Initial commit: Telegram password generator bot (telegraf, inline menu, crypto.randomInt)
This commit is contained in:
commit
c76676713f
7 files changed
+607
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
BOT_TOKEN=your-bot-token-here
|
||||||
|
OWNER_ID=your-telegram-user-id
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# Секреты — никогда не коммитим
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Зависимости
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Логи
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# ОС / редакторы
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# tg_bot_gen
|
||||||
|
|
||||||
|
Telegram-бот — полноценный генератор паролей с инлайн-меню (telegraf v4, Node.js).
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- `/start` — всё меню на инлайн-кнопках
|
||||||
|
- 🔑 Генерация паролей через `crypto.randomInt` (без `Math.random`)
|
||||||
|
- ⚙️ Настройки на кнопках:
|
||||||
|
- длина 4–128 (кнопки −5 / −1 / +1 / +5)
|
||||||
|
- наборы: A-Z, a-z, 0-9, символы
|
||||||
|
- «без похожих символов» (il1Lo0O)
|
||||||
|
- «произносимый» режим (CV-паттерн)
|
||||||
|
- 📊 Оценка стойкости: энтропия в битах + уровень
|
||||||
|
- 📋 История последних 5 паролей (в памяти, на диск не пишется)
|
||||||
|
- 🔒 Доступ только для владельца (`OWNER_ID` из `.env`)
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm start # или npm run dev (с --watch)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка `.env`
|
||||||
|
|
||||||
|
```
|
||||||
|
BOT_TOKEN=***
|
||||||
|
OWNER_ID=ваш_user_id
|
||||||
|
```
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
```
|
||||||
|
index.js # бот: меню, хэндлеры кнопок
|
||||||
|
lib/generator.js # генератор + оценка энтропии
|
||||||
|
```
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { Telegraf, Markup } from 'telegraf';
|
||||||
|
import { generatePassword, strength } from './lib/generator.js';
|
||||||
|
|
||||||
|
const TOKEN = process.env.BOT_TOKEN;
|
||||||
|
const OWNER_ID = Number(process.env.OWNER_ID);
|
||||||
|
|
||||||
|
if (!TOKEN) {
|
||||||
|
console.error('BOT_TOKEN not set in .env');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bot = new Telegraf(TOKEN, { handlerTimeout: 5_000 });
|
||||||
|
|
||||||
|
/* ---------- User settings state (in-memory) ---------- */
|
||||||
|
const defaults = () => ({
|
||||||
|
length: 16,
|
||||||
|
upper: true,
|
||||||
|
lower: true,
|
||||||
|
digits: true,
|
||||||
|
symbols: true,
|
||||||
|
excludeAmbiguous: false,
|
||||||
|
pronounceable: false,
|
||||||
|
});
|
||||||
|
const users = new Map();
|
||||||
|
const history = new Map(); // userId -> [passwords]
|
||||||
|
|
||||||
|
const state = (id) => {
|
||||||
|
if (!users.has(id)) users.set(id, defaults());
|
||||||
|
return users.get(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addHistory = (id, pwd) => {
|
||||||
|
if (!history.has(id)) history.set(id, []);
|
||||||
|
const h = history.get(id);
|
||||||
|
h.unshift(pwd);
|
||||||
|
if (h.length > 5) h.pop();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- Keyboard builders ---------- */
|
||||||
|
const mainMenu = () =>
|
||||||
|
Markup.inlineKeyboard([
|
||||||
|
[Markup.button.callback('🔑 Сгенерировать пароль', 'gen')],
|
||||||
|
[Markup.button.callback('⚙️ Настройки', 'settings')],
|
||||||
|
[Markup.button.callback('📋 История', 'history')],
|
||||||
|
[Markup.button.callback('ℹ️ О боте', 'about')],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const settingsKb = (s) => {
|
||||||
|
const on = (v) => (v ? '✅' : '❎');
|
||||||
|
return Markup.inlineKeyboard([
|
||||||
|
[
|
||||||
|
Markup.button.callback('➖ 1', 'len:-1'),
|
||||||
|
Markup.button.callback(`📏 Длина: ${s.length}`, 'noop'),
|
||||||
|
Markup.button.callback('➕ 1', 'len:+1'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Markup.button.callback('➖ 5', 'len:-5'),
|
||||||
|
Markup.button.callback('➕ 5', 'len:+5'),
|
||||||
|
],
|
||||||
|
[Markup.button.callback(`${on(s.upper)} Заглавные (A-Z)`, 'set:upper')],
|
||||||
|
[Markup.button.callback(`${on(s.lower)} Строчные (a-z)`, 'set:lower')],
|
||||||
|
[Markup.button.callback(`${on(s.digits)} Цифры (0-9)`, 'set:digits')],
|
||||||
|
[Markup.button.callback(`${on(s.symbols)} Символы (!@#$)`, 'set:symbols')],
|
||||||
|
[Markup.button.callback(`${on(s.excludeAmbiguous)} Без похожих (il1Lo0O)`, 'set:excludeAmbiguous')],
|
||||||
|
[Markup.button.callback(`${on(s.pronounceable)} Произносимый`, 'set:pronounceable')],
|
||||||
|
[Markup.button.callback('🔑 Сгенерировать', 'gen'), Markup.button.callback('⬅️ Назад', 'main')],
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resultKb = () =>
|
||||||
|
Markup.inlineKeyboard([
|
||||||
|
[Markup.button.callback('🔁 Ещё раз', 'gen')],
|
||||||
|
[Markup.button.callback('⚙️ Настройки', 'settings'), Markup.button.callback('⬅️ Меню', 'main')],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const backKb = (cb = 'main') =>
|
||||||
|
Markup.inlineKeyboard([[Markup.button.callback('⬅️ Назад', cb)]]);
|
||||||
|
|
||||||
|
const passwordText = (pwd, s) => {
|
||||||
|
const st = strength(pwd);
|
||||||
|
return [
|
||||||
|
'🔑 Ваш пароль:',
|
||||||
|
'',
|
||||||
|
'`' + pwd + '`',
|
||||||
|
'',
|
||||||
|
`${st.bar} ${st.level} · ~${st.bits} бит энтропии`,
|
||||||
|
`⚙️ Длина ${s.length} · ${modeLabel(s)}`,
|
||||||
|
'',
|
||||||
|
'🛡 crypto.randomInt, без Math.random.',
|
||||||
|
].join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
const modeLabel = (s) => {
|
||||||
|
if (s.pronounceable) return 'произносимый';
|
||||||
|
const parts = [];
|
||||||
|
if (s.upper) parts.push('A-Z');
|
||||||
|
if (s.lower) parts.push('a-z');
|
||||||
|
if (s.digits) parts.push('0-9');
|
||||||
|
if (s.symbols) parts.push('!@#');
|
||||||
|
return parts.join(' + ') || 'a-z';
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- Access restriction: OWNER_ID only ---------- */
|
||||||
|
const ownerOnly = async (ctx, next) => {
|
||||||
|
if (!Number.isFinite(OWNER_ID) || ctx.from?.id === OWNER_ID) return next();
|
||||||
|
if (ctx.callbackQuery) await ctx.answerCbQuery('Доступ запрещен').catch(() => {});
|
||||||
|
return ctx.reply('⛔ Доступ разрешен только владельцу бота.').catch(() => {});
|
||||||
|
};
|
||||||
|
bot.use(ownerOnly);
|
||||||
|
|
||||||
|
/* ---------- Handlers ---------- */
|
||||||
|
bot.start((ctx) => {
|
||||||
|
state(ctx.from.id);
|
||||||
|
return ctx.reply(
|
||||||
|
'👋 Привет! Я генератор паролей.\n\n' +
|
||||||
|
'🔑 Жми «Сгенерировать» — пароль создаётся криптографически стойким генератором Node.js (crypto.randomInt).\n' +
|
||||||
|
'⚙️ В настройках — длина, наборы символов, режимы.',
|
||||||
|
mainMenu()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action('noop', (ctx) => ctx.answerCbQuery());
|
||||||
|
|
||||||
|
bot.action('main', (ctx) =>
|
||||||
|
ctx.editMessageText('Выбери действие:', mainMenu()).catch(() => {})
|
||||||
|
);
|
||||||
|
|
||||||
|
bot.action('settings', (ctx) => {
|
||||||
|
const s = state(ctx.from.id);
|
||||||
|
return ctx.editMessageText('⚙️ Настройки генерации:', settingsKb(s)).catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action(/^len:(-?\d+)$/, (ctx) => {
|
||||||
|
const delta = Number(ctx.match[1]);
|
||||||
|
const s = state(ctx.from.id);
|
||||||
|
s.length = Math.min(128, Math.max(4, s.length + delta));
|
||||||
|
return ctx
|
||||||
|
.editMessageText(`⚙️ Настройки генерации:\nДлина: ${s.length} (4–128)`, settingsKb(s))
|
||||||
|
.then(() => ctx.answerCbQuery(`Длина: ${s.length}`))
|
||||||
|
.catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action(/^set:(\w+)$/, (ctx) => {
|
||||||
|
const key = ctx.match[1];
|
||||||
|
if (!(key in defaults())) return ctx.answerCbQuery('Неизвестная настройка');
|
||||||
|
const s = state(ctx.from.id);
|
||||||
|
if (key === 'pronounceable' || key === 'excludeAmbiguous') {
|
||||||
|
s[key] = !s[key];
|
||||||
|
} else {
|
||||||
|
const others = ['upper', 'lower', 'digits', 'symbols'].filter((k) => k !== key);
|
||||||
|
if (s[key] && !others.some((k) => s[k])) {
|
||||||
|
return ctx.answerCbQuery('Нужен хотя бы один набор символов');
|
||||||
|
}
|
||||||
|
s[key] = !s[key];
|
||||||
|
}
|
||||||
|
return ctx.editMessageText('⚙️ Настройки генерации:', settingsKb(s)).catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action('gen', async (ctx) => {
|
||||||
|
const s = state(ctx.from.id);
|
||||||
|
const pwd = generatePassword(s);
|
||||||
|
addHistory(ctx.from.id, pwd);
|
||||||
|
await ctx.answerCbQuery('Готово').catch(() => {});
|
||||||
|
return ctx.replyWithMarkdown(passwordText(pwd, s), resultKb()).catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action('history', (ctx) => {
|
||||||
|
const h = history.get(ctx.from.id) || [];
|
||||||
|
const text = h.length
|
||||||
|
? '📋 Последние 5 паролей:\n\n' + h.map((p, i) => `${i + 1}. \`${p}\``).join('\n')
|
||||||
|
: '📋 История пуста — сначала сгенерируй пароль.';
|
||||||
|
const kb = Markup.inlineKeyboard([
|
||||||
|
[Markup.button.callback('🧹 Очистить', 'clearhist')],
|
||||||
|
[Markup.button.callback('⬅️ Назад', 'main')],
|
||||||
|
]);
|
||||||
|
return ctx.replyWithMarkdown(text, kb).catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action('clearhist', (ctx) => {
|
||||||
|
history.delete(ctx.from.id);
|
||||||
|
return ctx.editMessageText('🧹 История очищена.', backKb()).catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.action('about', (ctx) =>
|
||||||
|
ctx
|
||||||
|
.editMessageText(
|
||||||
|
'ℹ️ tg_bot_gen — генератор паролей в Telegram\n\n' +
|
||||||
|
'🛡 Node.js crypto (crypto.randomInt), Fisher–Yates перемешивание, гарантия минимум одного символа из каждого набора.\n' +
|
||||||
|
'📏 Длина 4–128 символов.\n' +
|
||||||
|
'🔤 Наборы: A-Z, a-z, 0-9, символы; опции «без похожих» и «произносимый».\n' +
|
||||||
|
'🔒 Пароли не отправляются на серверы и не сохраняются на диске.',
|
||||||
|
backKb()
|
||||||
|
)
|
||||||
|
.catch(() => {})
|
||||||
|
);
|
||||||
|
|
||||||
|
bot.on(['text', 'message'], (ctx) => {
|
||||||
|
const t = ctx.message?.text;
|
||||||
|
if (t === '/start' || t === '/help' || t === '/menu') return bot.start(ctx);
|
||||||
|
return ctx.reply('Используй /start 🙂', mainMenu());
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.catch((err) => console.error('Bot error:', err?.response?.description || err));
|
||||||
|
|
||||||
|
/* ---------- Start (long polling) ---------- */
|
||||||
|
bot
|
||||||
|
.launch({ dropPendingUpdates: true })
|
||||||
|
.then(() => console.log(`Bot @${bot.botInfo?.username ?? '?'} started (polling)`))
|
||||||
|
.catch((e) => {
|
||||||
|
console.error('Failed to launch:', e?.response?.description || e.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
const shutdown = () => {
|
||||||
|
bot.stop('shutting down');
|
||||||
|
process.exit(0);
|
||||||
|
};
|
||||||
|
process.once('SIGINT', shutdown);
|
||||||
|
process.once('SIGTERM', shutdown);
|
||||||
@@ -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 };
|
||||||
|
}
|
||||||
Generated
+204
@@ -0,0 +1,204 @@
|
|||||||
|
{
|
||||||
|
"name": "tg_bot_gen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "tg_bot_gen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"telegraf": "^4.16.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@telegraf/types": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer-alloc": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-alloc-unsafe": "^1.1.0",
|
||||||
|
"buffer-fill": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer-alloc-unsafe": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/buffer-fill": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "17.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
|
||||||
|
"integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mri": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-timeout": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-compare": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-alloc": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sandwich-stream": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/telegraf": {
|
||||||
|
"version": "4.16.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz",
|
||||||
|
"integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@telegraf/types": "^7.1.0",
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"mri": "^1.2.0",
|
||||||
|
"node-fetch": "^2.7.0",
|
||||||
|
"p-timeout": "^4.1.0",
|
||||||
|
"safe-compare": "^1.1.4",
|
||||||
|
"sandwich-stream": "^2.0.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"telegraf": "lib/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || >=14.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "tg_bot_gen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Telegram bot: генератор паролей с инлайн-меню",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js",
|
||||||
|
"dev": "node --watch index.js"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"telegram",
|
||||||
|
"bot",
|
||||||
|
"password",
|
||||||
|
"generator"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"telegraf": "^4.16.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user