Files

164 lines
12 KiB
Markdown
Raw Permalink 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.
# Tg-notify.bot
Фоновый **Telegram-бот уведомлений о новых сериях и озвучках** (long polling). Пользователь подписывается на сериал прямо из карточки Lampa, а бот сам отслеживает выход новых эпизодов и появление выбранной озвучки и шлёт уведомление в Telegram с обложкой, номером серии и описанием из TMDB.
Проект: **`Modules/Tg-notify.bot/`**. C#-namespace модуля — `TelegramBot`.
Бот объединяет три подсистемы:
1. **Трекер новых серий** — Trakt API (приоритет) с фоллбэком на TMDB.
2. **Трекер озвучек** — параллельный опрос балансеров **Mirage**, **Collaps** и **VideoHub** (поиск по названию или kinopoisk_id, мультиозвучка, чистый JSON). Берётся максимальный эпизод из всех источников — кто первым выложил серию в нужной озвучке, от того и уведомление.
3. **Клиентский плагин Lampa** (`tg-notify.js`) — вкладка «TG Подписки», перехват кнопки 🔔 в карточке, меню выбора озвучки.
---
## Включение
1. В шаблоне [`config/base.conf`](../../../config/base.conf) при необходимости проверьте, что **`TelegramBot`** не попал в **`BaseModule.SkipModules`** (если попал — уберите имя из списка, иначе модуль не загрузится).
2. [`manifest.json`](manifest.json): выставьте **`"enable": true`** (в репозитории по умолчанию `false`).
3. `init.conf`: добавьте секцию **`TelegramBot`**. Пример — [`init.merge.example.json`](init.merge.example.json).
4. Перезапустите сервис: `systemctl restart lampac` (или имя вашего systemd-юнита).
При `enable: true` и пустом `bot_token` long polling **не стартует** — в лог пишется предупреждение.
---
## Конфигурация (секция `init.conf`)
```json
"TelegramBot": {
"enable": true,
"bot_token": "ТОКЕН_ОТ_BOTFATHER",
"tmdb_api_key": "ВАШ_КЛЮЧ_TMDB",
"trakt_client_id": "",
"lampac_host": "http://127.0.0.1:9118",
"lampac_token": "",
"kp_api_key": "",
"check_interval_minutes": 60,
"tmdb_lang": "ru-RU",
"data_dir": "database/tgnotify"
}
```
| Поле | Назначение |
|------|-----------|
| `bot_token` | Токен бота от [@BotFather](https://t.me/BotFather). **Обязателен.** |
| `tmdb_api_key` | Ключ TMDB v3 — обложки, описания эпизодов, определение актуального сезона. |
| `trakt_client_id` | Client ID приложения Trakt. Если задан — основной источник новых серий; если пуст — используется только TMDB. |
| `lampac_host` | Базовый URL самого Lampac для запросов к балансерам (`/lite/mirage`, `/lite/collaps`). Обычно локальный: `http://127.0.0.1:9118`. |
| `lampac_token` | Токен **accsdb**. Обязателен, если у вас включена защита `accsdb` — без него балансеры вернут `{"accsdb":true,"msg":"Войдите в аккаунт"}`. |
| `kp_api_key` | Ключ [kinopoiskapiunofficial.tech](https://kinopoiskapiunofficial.tech/) — резолвинг `tmdb_id → kinopoisk_id` по названию для источника **VideoHub**. Опционально: если пусто, `kinopoisk_id` берётся только из встроенного моста Lampac `/externalids` (покрытие ~40%); с ключом — фоллбэк через поиск по названию поднимает покрытие почти до полного. |
| `check_interval_minutes` | Период фоновой проверки подписок. По умолчанию 60. Первый прогон — через 2 минуты после старта. |
| `tmdb_lang` | Язык метаданных TMDB. По умолчанию `ru-RU` (с фоллбэком на `en-US` при пустом описании). |
| `data_dir` | Каталог хранения `users.json` / `subscriptions.json`. По умолчанию `database/tgnotify`. |
---
## Зависимости
- **`Telegram.Bot` 22.4.4** — кладётся в [`references/`](references/) (для рантайм-компиляции Roslyn) и подтягивается через `PackageReference` при локальной сборке.
- **TMDB API** — ключ v3 (`tmdb_api_key`).
- **Trakt API** (опционально) — `trakt_client_id` для более точного и быстрого трекинга выхода серий.
- Балансеры **Mirage**, **Collaps** и **VideoHub** (`/lite/cdnvideohub`) должны быть доступны по `lampac_host`.
- **kinopoiskapiunofficial.tech** (опционально) — `kp_api_key`. Нужен только для VideoHub: резолвит `kinopoisk_id` по названию, когда встроенный мост Lampac его не знает.
---
## Структура модуля
```
Tg-notify.bot/
├── manifest.json # dynamic:true, references+tree
├── Tg-notify.bot.csproj # только для локальной сборки; на сервере не используется
├── init.merge.example.json # пример секции init.conf
├── ModInit.cs # ядро: polling, трекинг серий, парсеры озвучек, HTTP API
├── Models/
│ └── TelegramBotConf.cs # конфиг (наследует ModuleBaseConf)
├── Controllers/
│ └── TgNotifyController.cs # HTTP API + раздача tg-notify.js
├── references/
│ └── Telegram.Bot.dll # 22.4.4
└── tg-notify.js # клиентский плагин Lampa
```
> На сервере модуль компилируется рантайм-движком **Roslyn** по `manifest.json` (`dynamic: true`) — `.csproj` нужен только для локальной разработки. Поля `references` и `tree` обязательны.
---
## Как работает
### Привязка аккаунта
UID пользователя определяется так же, как для закладок и тайм-кодов: плагин передаёт `token`, `account_email` и `uid` устройства, сервер берёт первое непустое (`RequestInfo.getuid`). Вошёл в аккаунт CUB — подписки общие для всех его устройств.
Из вкладки «TG Подписки» в Lampa пользователь получает deep-link на бота. Telegram пропускает в параметре только `A-Za-z0-9_-` (до 64 символов), поэтому UID вида email уходит как `link64_{base64url(uid)}`, остальные — как раньше, `link_{uid}`. Бот сохраняет связку `chat_id ↔ lampac_uid` (`users.json`), после чего подписки этого UID становятся «его». Привязка, сделанная раньше по `uid` устройства, продолжает работать: если под основным UID привязки нет, API ищет её по `uid` устройства.
### Подписка
В карточке сериала плагин перехватывает кнопку 🔔 и показывает меню озвучек (`/api/tg/voices` → Mirage + Collaps + VideoHub). Выбор отправляется в `/api/tg/subscribe` и сохраняется в `subscriptions.json`.
### Фоновый цикл `CheckAll`
Раз в `check_interval_minutes` (и вручную командой `/check`) по каждой подписке:
1. **Новые серии** — `CheckNewEpisodes` (Trakt → TMDB). Если у подписки не выбрана конкретная озвучка — уведомление шлётся о самом факте выхода серии.
2. **Озвучки** (если озвучка выбрана) — **параллельно** (`Task.WhenAll`) опрашиваются Mirage, Collaps и VideoHub; берётся максимальный доступный эпизод; кто первый нашёл новую серию — от того уведомление. На каждую новую серию шлётся отдельное сообщение с инфой из TMDB.
> **VideoHub и kinopoisk_id.** VideoHub ищет контент только по `kinopoisk_id`, тогда как бот работает по `tmdb_id`. Резолвинг выполняется лениво и кешируется в самой подписке: сначала через встроенный мост Lampac `/externalids`, при неудаче — поиском по названию через `kp_api_key`. Имя озвучки в запросе URL-кодируется (кириллица иначе даёт HTTP 400). Если `kinopoisk_id` не нашёлся, VideoHub для этой подписки молча пропускается — Mirage и Collaps продолжают работать.
Формат уведомления:
```
🎬 Название
🎙 Озвучка
📺 S05E08 — Название эпизода
Описание (до 300 символов)
```
С обложкой кадра (`SendPhoto`), если у эпизода есть `still_path`.
---
## Команды бота
| Команда / кнопка | Действие |
|------------------|----------|
| `/start` | Приветствие; `/start link_{uid}` или `/start link64_{base64url(uid)}` — привязка аккаунта |
| `/list` · «📋 Подписки» | Список активных подписок |
| `/check` · «🔍 Проверить» | Принудительная проверка (в фоне) |
| `/help` · «📖 Помощь» | Справка |
| `/unlink` · «🔓 Отвязать» | Отвязать аккаунт |
---
## HTTP API (`TgNotifyController : BaseController`)
| Метод | Маршрут | Назначение |
|-------|---------|-----------|
| `GET` | `/tg-notify.js` | Раздаёт клиентский плагин с автозаменой `{localhost}` → реальный хост. `[AllowAnonymous]` |
| `POST` | `/api/tg/subscribe` | Подписка на сериал/озвучку |
| `POST` | `/api/tg/unsubscribe` | Отписка |
| `GET` | `/api/tg/status?tmdb_id=` | Статус подписки текущего пользователя |
| `GET` | `/api/tg/voices?title=&year=&season=&tmdb_id=` | Список доступных озвучек |
| `GET` | `/api/tg/link` | Ссылка привязки бота |
| `GET` | `/api/tg/subscriptions` | Все подписки пользователя с метаданными |
Все маршруты, кроме `/tg-notify.js` и `/api/tg/voices`, требуют `requestInfo.user_uid`.
---
## Клиентский плагин (`tg-notify.js`)
Подключается через `customPlugins` в `init.conf` либо раздаётся самим модулем по `/tg-notify.js`:
```json
"LampaWeb": {
"customPlugins": [
{ "url": "{localhost}/tg-notify.js", "status": 1 }
]
}
```
Возможности: вкладка «TG Подписки» (сетка карточек с бейджем `S5 E4`), перехват кнопки 🔔 в полной карточке, меню выбора озвучки (`Lampa.Select.show`, метки `Mirage [—]` / `Collaps [C]` / `VideoHub [V]`), загрузка метаданных через `Lampa.TMDB` / `Lampa.Network.silent` с кешем. Хост подставляется через `{localhost}` — хардкода адресов нет.