Initial commit: Telegram password generator bot (telegraf, inline menu, crypto.randomInt)

This commit is contained in:
kromskii2 committed 2026-09-10 14:30:10 +07:00
commit c76676713f
7 files changed
+607

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
// Генератор паролей на crypto API (cryptographically secure)
import crypto from 'node:crypto';
const SETS = {
upper: 'ABCDEFGHJKLMNPQRSTUVWXYZ', // без I O (при excludeAmbiguous)
upperFull: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
lower: 'abcdefghijkmnpqrstuvwxyz', // без l o
lowerFull: 'abcdefghijklmnopqrstuvwxyz',
digits: '23456789', // без 0 1
digitsFull: '0123456789',
symbols: '!@#$%^&*()_+-=[]{};:,.<>?/~',
};
const VOWELS = 'aeiou';
const CONSONANTS = 'bcdfghjkmnpqrstvz';
function randInt(max) {
return crypto.randomIntRange ? crypto.randomIntRange(max) : crypto.randomInt(0, max);
}
function pick(str) {
return str[randInt(str.length)];
}
/**
* @param {object} opts
* @param {number} opts.length
* @param {boolean} opts.upper
* @param {boolean} opts.lower
* @param {boolean} opts.digits
* @param {boolean} opts.symbols
* @param {boolean} opts.excludeAmbiguous - убрать похожие символы (il1Lo0O)
* @param {boolean} opts.pronounceable - произносимый режим (CV-паттерн)
*/
export function generatePassword(opts) {
const {
length = 16,
upper = true,
lower = true,
digits = true,
symbols = true,
excludeAmbiguous = false,
pronounceable = false,
} = opts || {};
const len = Math.min(Math.max ? Math.max(Number(length) || 16, 4) : 16, 128);
if (pronounceable) return pronounceablePassword(len);
const active = [];
if (upper) active.push(excludeAmbiguous ? SETS.upper : SETS.upperFull);
if (lower) active.push(excludeAmbiguous ? SETS.lower : SETS.lowerFull);
if (digits) active.push(excludeAmbiguous ? SETS.digits : SETS.digitsFull);
if (symbols) active.push(SETS.symbols);
if (active.length === 0) active.push(SETS.lowerFull);
const pool = active.join('');
// Гарантируем минимум один символ из каждого выбранного набора
const chars = active.map(pick);
while (chars.length < len) chars.push(pick(pool));
// Перемешиваем (Fisher–Yates на crypto)
for (let i = chars.length - 1; i > 0; i--) {
const j = randInt(i + 1);
[chars[i], chars[j]] = [chars[j], chars[i]];
}
return chars.slice(0, len).join('');
}
function pronounceablePassword(len) {
const out = [];
while (out.length < len) {
const c = pick(CONSONANTS);
const v = pick(VOWELS);
out.push(randInt(2) === 0 ? c : c.toUpperCase(), v);
if (randInt(10) < 3 && out.length < len) out.push(pick('0123456789'));
}
return out.slice(0, len).join('');
}
/**
* Оценка пароля: энтропия (бит) + уровень.
*/
export function strength(password) {
let poolSize = 0;
if (/[a-z]/.test(password)) poolSize += 26;
if (/[A-Z]/.test(password)) poolSize += 26;
if (/[0-9]/.test(password)) poolSize += 10;
if (/[^a-zA-Z0-9]/.test(password)) poolSize += 32;
const bits = Math.round(password.length * Math.log2(poolSize || 1));
let level, bar;
if (bits < 40) { level = 'Слабый'; bar = '🟥⬜⬜⬜⬜'; }
else if (bits < 60) { level = 'Средний'; bar = '🟨🟨⬜⬜⬜'; }
else if (bits < 80) { level = 'Хороший'; bar = '🟩🟩🟩⬜⬜'; }
else if (bits < 120) { level = 'Отличный'; bar = '🟩🟩🟩🟩⬜'; }
else { level = 'Максимальный'; bar = '🟩🟩🟩🟩🟩'; }
return { bits, level, bar };
}