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