Files
arestools/README.md
T
ros 15a03a77fd Переписать проект в личный MTProto-юзербот
Смена архитектуры с бизнес-бота на 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>
2026-08-09 19:16:52 +03:00

123 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# telegrambusiness
Личный Telegram-**юзербот** на Node.js + MTProto (GramJS). Работает от вашего аккаунта и сохраняет то, что Bot API недоступно:
- **antidelete** — сообщения, которые собеседник удалил в личке;
- **onetime** — одноразовые («просмотр один раз») фото и видео.
Всё управление и все перехваты приходят в **компаньон-бота** — отдельного бота от [@BotFather](https://t.me/BotFather), в личку владельца. Наружу юзербот ничего не пишет, слеш-команд в чатах нет.
> ⚠️ **Дисклеймер.** Инструмент для личного использования на **своём** аккаунте и своей переписке. Юзерботы нарушают ToS Telegram и аккаунт могут заблокировать — держите его на отдельном номере, не на основном. Одноразовые медиа отправляются с расчётом на удаление; сохраняя их, вы берёте ответственность на себя.
## Как устроено (важно понимать до запуска)
MTProto в событии удаления присылает **только id сообщений** — без текста и без медиа. Поэтому «сохранить удалённое» реактивно невозможно: юзербот **заранее кэширует каждое входящее** в личке, а при удалении достаёт его из своего кэша. То же с медиа — файл качается **в момент прихода**, после удаления ссылка мертва.
Отсюда следствия:
- тексты и метаданные пишутся в локальную БД (`node:sqlite`), **зашифрованы** (AES-256-GCM);
- медиа лежат отдельными **зашифрованными** файлами в `data/media/`;
- всё чистится по `RETENTION_DAYS`.
## Установка
Нужен Node.js ≥ 22.5 (для встроенного `node:sqlite`).
```bash
npm install
cp .env.example .env
```
Заполните `.env`:
1. **API_ID / API_HASH** — [my.telegram.org](https://my.telegram.org) → API development tools.
2. **BOT_TOKEN** — новый бот у [@BotFather](https://t.me/BotFather) (это и есть панель).
3. **OWNER_ID** — ваш id (напишите [@userinfobot](https://t.me/userinfobot)).
4. **ENCRYPTION_KEY** — сгенерируйте: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`.
Затем одноразовый вход в аккаунт (телефон, код, при наличии — пароль 2FA):
```bash
npm run login
```
Скрипт напечатает строку сессии — вставьте её в `.env` как `SESSION=...` и запускайте:
```bash
npm run dev
```
Откройте своего бота, нажмите **Start** — появится панель. При удалении сообщения собеседником или получении одноразового медиа бот пришлёт уведомление с кнопкой «Открыть».
`npm run dev` — с автоперезапуском, `npm start` — обычный запуск.
## Как добавить модуль
Модуль — файл в `src/modules/`. Загрузчик обходит папку рекурсивно; файлы на `_` игнорируются. Объявляйте только нужные хуки:
```js
// 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](src/core/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](src/userbot/events.js)).
- «Удалить у себя» на стороне собеседника юзерботу не приходит — ловится только «удалить у всех».
- `CACHE_MEDIA=1` качает медиа каждого входящего, чтобы восстанавливать удалённые картинки. Это ест диск — при `=0` останется только текст и пометка о типе медиа.
- Ключ шифрования — в `.env`. Потеряете ключ — расшифровать хранилище нельзя.
- Хранилище локальное; `data/` в `.gitignore`.
```