Files
lampac/mods/Tg-notify.bot
..

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 при необходимости проверьте, что TelegramBot не попал в BaseModule.SkipModules (если попал — уберите имя из списка, иначе модуль не загрузится).
  2. manifest.json: выставьте "enable": true (в репозитории по умолчанию false).
  3. init.conf: добавьте секцию TelegramBot. Пример — init.merge.example.json.
  4. Перезапустите сервис: systemctl restart lampac (или имя вашего systemd-юнита).

При enable: true и пустом bot_token long polling не стартует — в лог пишется предупреждение.


Конфигурация (секция init.conf)

"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. Обязателен.
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 — резолвинг 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/ (для рантайм-компиляции 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:

"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} — хардкода адресов нет.