From 2c6d8a44349c46255133ad55010f5661136993a7 Mon Sep 17 00:00:00 2001 From: payacom <59262811+payacom@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:23:55 +0330 Subject: [PATCH] Initial commit --- utils/usernameSuggestions.js | 69 +++++++++++++++++++++++++----------- utils/usernameValidation.js | 4 +-- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/utils/usernameSuggestions.js b/utils/usernameSuggestions.js index 315dcaa..92c9833 100644 --- a/utils/usernameSuggestions.js +++ b/utils/usernameSuggestions.js @@ -12,28 +12,46 @@ const isUsernameTaken = async (UserModel, userName, excludeUserId) => { return true } -const buildCandidates = (base, attempt) => { - const random2 = () => String(Math.floor(Math.random() * 90 + 10)) - const random3 = () => String(Math.floor(Math.random() * 900 + 100)) - const random4 = () => String(Math.floor(Math.random() * 9000 + 1000)) +const splitBaseAndSuffix = (value) => { + const match = value.match(/^(.*?)(\d+)$/) + if (!match || !match[1]) { + return { core: value, suffix: null } + } + return { core: match[1], suffix: Number.parseInt(match[2], 10) } +} - const builders = [ - (b) => `${b}${random2()}`, - (b) => `${b}_${random3()}`, - (b) => `${b}.${random2()}`, - (b) => `${b}-${random2()}`, - (b) => `${b}${random4()}`, - (b) => `${b}_m${random2()}`, - (b) => `${b}${attempt}`, - ] +const trimToMax = (value) => + value.length > USERNAME_MAX_LENGTH ? value.slice(0, USERNAME_MAX_LENGTH) : value - return builders.map((fn) => { - let candidate = fn(base) - if (candidate.length > USERNAME_MAX_LENGTH) { - candidate = candidate.slice(0, USERNAME_MAX_LENGTH) - } - return candidate +/** پیشنهادهای مشابه: amiri2 → amiri3, amiri5, amiiri3, amiri334 */ +const buildCandidates = (base) => { + const { core, suffix } = splitBaseAndSuffix(base) + const candidates = [] + + if (suffix !== null && !Number.isNaN(suffix)) { + candidates.push(`${core}${suffix + 1}`) + candidates.push(`${core}${suffix + 3}`) + candidates.push(`${core}${suffix}${Math.floor(Math.random() * 9 + 1)}`) + } + + const numericSuffixes = [3, 5, 7, 12, 21, 34, 99, 334, 567] + numericSuffixes.forEach((num) => { + candidates.push(`${core}${num}`) }) + + for (let i = 1; i < Math.min(core.length, 5); i += 1) { + const ch = core[i] + if (!ch) continue + const doubled = `${core.slice(0, i)}${ch}${core.slice(i)}` + candidates.push(`${doubled}${suffix !== null ? suffix + 1 : 3}`) + candidates.push(`${doubled}${Math.floor(Math.random() * 90 + 10)}`) + } + + candidates.push(`${core}_${suffix !== null ? suffix + 1 : 1}`) + candidates.push(`${core}.${Math.floor(Math.random() * 900 + 100)}`) + candidates.push(`${core}-${Math.floor(Math.random() * 90 + 10)}`) + + return [...new Set(candidates.map(trimToMax))] } const generateUsernameSuggestions = async ( @@ -46,8 +64,8 @@ const generateUsernameSuggestions = async ( const suggestions = [] const tried = new Set([base]) - for (let attempt = 1; attempt <= 60 && suggestions.length < count; attempt++) { - const candidates = buildCandidates(base, attempt) + for (let attempt = 0; attempt < 80 && suggestions.length < count; attempt += 1) { + const candidates = buildCandidates(base) for (const candidate of candidates) { if (suggestions.length >= count) break @@ -58,6 +76,15 @@ const generateUsernameSuggestions = async ( const taken = await isUsernameTaken(UserModel, candidate, excludeUserId) if (!taken) suggestions.push(candidate) } + + if (suggestions.length < count) { + const fallback = trimToMax(`${base}${Math.floor(Math.random() * 9000 + 1000)}`) + if (!tried.has(fallback) && !validateUsername(fallback)) { + tried.add(fallback) + const taken = await isUsernameTaken(UserModel, fallback, excludeUserId) + if (!taken) suggestions.push(fallback) + } + } } return suggestions diff --git a/utils/usernameValidation.js b/utils/usernameValidation.js index be99b38..78d1319 100644 --- a/utils/usernameValidation.js +++ b/utils/usernameValidation.js @@ -1,7 +1,7 @@ /** حروف انگلیسی، اعداد و . _ - — بدون فارسی و کاراکترهای غیرمعمول */ -const USERNAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._-]{5,99}$/ +const USERNAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._-]{7,99}$/ -const USERNAME_MIN_LENGTH = 6 +const USERNAME_MIN_LENGTH = 8 const USERNAME_MAX_LENGTH = 100 const validateUsername = (user_name) => {