Исправление: том ./mods:/lampac/mods в compose и копии 10 модулей с enable:true; конфиг без SkipModules

This commit is contained in:
2026-10-02 12:11:49 +03:00
parent 8dcbc6bc52
commit 7b8e28b3bf
151 changed files with 68625 additions and 44 deletions
+163
View File
@@ -0,0 +1,163 @@
# 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}` — хардкода адресов нет.