feat(api): derive stable placeholder names for hidden profiles (#3224)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-05 16:57:25 +02:00
1 parent 02c82f0038
commit 3093e7334b
30 files changed
+629 -65

No files matched your search

+26 -6
View File
@@ -8,19 +8,17 @@ import {UsernameType} from '@fluxer/schema/src/primitives/UserValidators';
const scales = readFileSync(resolveAssetPath('words', 'scales.txt'), 'utf-8').trim().split('\n').filter(Boolean);
const tails = readFileSync(resolveAssetPath('words', 'tails.txt'), 'utf-8').trim().split('\n').filter(Boolean);
type IndexPicker = (size: number) => number;
function capitalize(word: string): string {
return word.charAt(0).toUpperCase() + word.slice(1);
}
function pickRandom(words: Array<string>): string {
return words[randomInt(words.length)];
}
export function generateRandomUsername(): string {
function generateUsername(pick: IndexPicker): string {
const MAX_LENGTH = 32;
const MAX_ATTEMPTS = 100;
for (let i = 0; i < MAX_ATTEMPTS; i++) {
const username = capitalize(pickRandom(scales)) + capitalize(pickRandom(tails));
const username = capitalize(scales[pick(scales.length)]) + capitalize(tails[pick(tails.length)]);
if (username.length <= MAX_LENGTH && UsernameType.safeParse(username).success) {
return username;
}
@@ -33,3 +31,25 @@ export function generateRandomUsername(): string {
}
return 'BotUser';
}
function seededPicker(seed: Uint8Array): IndexPicker {
const view = new DataView(seed.buffer, seed.byteOffset, seed.byteLength);
const words = Math.floor(seed.byteLength / 4);
let next = 0;
return (size) => {
const value = view.getUint32((next % words) * 4);
next++;
return value % size;
};
}
export function generateRandomUsername(): string {
return generateUsername((size) => randomInt(size));
}
export function generateSeededUsername(seed: Uint8Array): string {
if (seed.byteLength < 4) {
throw new Error('Username seed must hold at least four bytes');
}
return generateUsername(seededPicker(seed));
}