Смена архитектуры с бизнес-бота на Bot API (команды со слешем) на личный юзербот на GramJS/MTProto с управлением через компаньон-бота. Возможности: - antidelete — кэш входящих в личке, восстановление удалённых «у всех» - onetime — перехват «просмотр один раз» фото/видео до истечения ссылки - локальное хранилище node:sqlite + шифрование AES-256-GCM, чистка по сроку - компаньон-бот (grammY): инлайн-панель, лента с пагинацией, выдача медиа, доступ только владельцу по OWNER_ID Устройство: - core/ — crypto, db, media, loader, registry, logger - userbot/ — MTProto-клиент из строки сессии, подписка на события - modules/ — фичи как модули с хуками setup/onMessage/onEdited/onDeleted - bot/ — компаньон-бот (единственный UI) - scripts/login.js — генерация строки сессии Удалён старый код Bot API (commands/, handlers/, middlewares/, dispatcher, store). Обновлены README и .env.example. Hcrgram/ (сторонний референс) добавлен в .gitignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.6 KiB
telegrambusiness
Личный Telegram-юзербот на Node.js + MTProto (GramJS). Работает от вашего аккаунта и сохраняет то, что Bot API недоступно:
- antidelete — сообщения, которые собеседник удалил в личке;
- onetime — одноразовые («просмотр один раз») фото и видео.
Всё управление и все перехваты приходят в компаньон-бота — отдельного бота от @BotFather, в личку владельца. Наружу юзербот ничего не пишет, слеш-команд в чатах нет.
⚠️ Дисклеймер. Инструмент для личного использования на своём аккаунте и своей переписке. Юзерботы нарушают ToS Telegram и аккаунт могут заблокировать — держите его на отдельном номере, не на основном. Одноразовые медиа отправляются с расчётом на удаление; сохраняя их, вы берёте ответственность на себя.
Как устроено (важно понимать до запуска)
MTProto в событии удаления присылает только id сообщений — без текста и без медиа. Поэтому «сохранить удалённое» реактивно невозможно: юзербот заранее кэширует каждое входящее в личке, а при удалении достаёт его из своего кэша. То же с медиа — файл качается в момент прихода, после удаления ссылка мертва.
Отсюда следствия:
- тексты и метаданные пишутся в локальную БД (
node:sqlite), зашифрованы (AES-256-GCM); - медиа лежат отдельными зашифрованными файлами в
data/media/; - всё чистится по
RETENTION_DAYS.
Установка
Нужен Node.js ≥ 22.5 (для встроенного node:sqlite).
npm install
cp .env.example .env
Заполните .env:
- API_ID / API_HASH — my.telegram.org → API development tools.
- BOT_TOKEN — новый бот у @BotFather (это и есть панель).
- OWNER_ID — ваш id (напишите @userinfobot).
- ENCRYPTION_KEY — сгенерируйте:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))".
Затем одноразовый вход в аккаунт (телефон, код, при наличии — пароль 2FA):
npm run login
Скрипт напечатает строку сессии — вставьте её в .env как SESSION=... и запускайте:
npm run dev
Откройте своего бота, нажмите Start — появится панель. При удалении сообщения собеседником или получении одноразового медиа бот пришлёт уведомление с кнопкой «Открыть».
npm run dev — с автоперезапуском, npm start — обычный запуск.
Как добавить модуль
Модуль — файл в src/modules/. Загрузчик обходит папку рекурсивно; файлы на _ игнорируются. Объявляйте только нужные хуки:
// src/modules/keywords.js
export default {
name: 'keywords',
description: 'Уведомлять, если во входящем есть ключевое слово',
async onMessage(message, ctx) {
if (message.out) return; // не своё
if (/срочно/i.test(message.text ?? '')) {
await ctx.capture('deleted', { // переиспользуем ленту перехватов
chatId: message.chatId,
senderId: message.senderId,
sender: (await message.getSender().catch(() => null))?.username ?? '',
text: message.text,
});
}
},
};
| Хук | Когда вызывается |
|---|---|
setup(_, ctx) |
Один раз при старте |
onMessage(message, ctx) |
Новое сообщение (NewMessage) |
onEdited(message, ctx) |
Правка (EditedMessage) |
onDeleted(event, ctx) |
Удаление; event.deletedIds — массив id |
ctx даёт: client (GramJS), me, registry и capture(kind, data) — сохранить перехват и уведомить владельца. Ошибка в одном модуле не роняет остальные (registry.js — runHook).
Устройство
src/
index.js сборка: юзербот + бот + модули + чистка
config.js .env, проверка на старте
core/
crypto.js AES-256-GCM (шифр текста и медиа)
db.js node:sqlite: кэш входящих + лента перехватов
media.js тип медиа, детект one-time, скачать+шифровать
loader.js обход src/modules
registry.js реестр модулей, runHook
logger.js
userbot/
client.js MTProto-клиент из строки сессии
events.js подписка NewMessage/Edited/Deleted → модули
context.js ctx.capture — мост модуль↔БД↔бот
modules/
cache.js кэширует входящие (основа antidelete)
antidelete.js удаление → достать из кэша → перехват
onetime.js one-time медиа → скачать → перехват
bot/
panel.js компаньон-бот: инлайн-панель, лента, выдача медиа
scripts/
login.js генерация строки сессии
Поток: входящее → cache/onetime (кэш и/или скачивание) → при удалении antidelete поднимает из кэша → ctx.capture пишет в БД и шлёт уведомление → панель показывает и отдаёт расшифрованный файл.
Что стоит учесть
- Перехватываются только входящие в личных диалогах. Группы, каналы и свои сообщения — нет (см.
isPrivateIncomingв events.js). - «Удалить у себя» на стороне собеседника юзерботу не приходит — ловится только «удалить у всех».
CACHE_MEDIA=1качает медиа каждого входящего, чтобы восстанавливать удалённые картинки. Это ест диск — при=0останется только текст и пометка о типе медиа.- Ключ шифрования — в
.env. Потеряете ключ — расшифровать хранилище нельзя. - Хранилище локальное;
data/в.gitignore.