Files
TeleWave/docs/telegram-bot.md
T
Leonid Pershin 8a439f1351
ci / build-backend (push) Successful in 2m38s
ci / build-frontend (push) Successful in 58s
ci / tests (push) Successful in 4m27s
ci / sonar (push) Successful in 9m26s
Enhance TelegramBotService with schedule handling and subscription toggling
Updated the TelegramBotService to support new actions for managing subscriptions and retrieving channel schedules. Introduced constants for action types and improved the callback data structure for better clarity. Added a method to send channel schedules, consolidating upcoming program information into a user-friendly format. Updated related tests to ensure proper functionality of the new features and adjusted documentation to reflect changes in button actions.
2026-07-31 08:16:37 +03:00

72 lines
7.6 KiB
Markdown

# Бот расписания в Telegram
Оповещения зрителю: «на канале началось шоу» и «скоро начнётся». Настраивается в админке
(вкладка «Телеграм»), подписки собирает сам зритель кнопками в чате.
## Что решено и почему
**Подписка только по одноразовому коду.** Кнопка «Открыть чат с ботом» на странице эфира выдаёт
ссылку `t.me/бот?start=КОД`; код живёт 15 минут, сгорает при первом использовании и привязывает
чат к учётной записи TeleWave. Анонимный бот («кто нашёл, тот и подписался») означал бы рассылку
неизвестно кому и невозможность отписать чат вместе с блокировкой пользователя. Ссылка выдаётся
по нажатию, а не лежит в разметке: в ней пропуск к чужим оповещениям.
**Два транспорта, переключателем.** `Polling` — сервер сам ходит за обновлениями: работает из-за
NAT и через прокси, и это единственный вариант там, где публичного адреса нет. `Webhook` — Telegram
стучится на наш HTTPS-эндпоинт: дешевле и быстрее, но требует доступного извне домена, и прокси
здесь не поможет — соединение устанавливает Telegram. Разговор с ботом при этом один и тот же:
оба транспорта приносят одинаковое обновление и разбираются общим кодом.
**Прокси — часть настроек, а не окружения.** HTTP или SOCKS5 с логином и паролем; клиент собирается
на каждый вызов, поэтому смена прокси не требует перезапуска контейнера. Кривой адрес — ошибка, а
не повод молча пойти напрямую: «работает в обход прокси» на канале, ради которого прокси и заводили,
хуже отказа.
**Секреты в базе, наружу не отдаются.** Токен и пароль прокси лежат открытым текстом: шифровать их
нечем — Data Protection в проекте сознательно не заводили (см. CLAUDE.md). Наружу уходит только
признак «задан», а пустое поле в форме означает «оставить как есть». Секрет вебхука генерируется
сам и проверяется заголовком `X-Telegram-Bot-Api-Secret-Token`: адрес рано или поздно окажется
в чужих логах, и без секрета туда постучится кто угодно.
**Проверка связи — на сохранении.** Настройки сразу спрашивают у Telegram `getMe`: неверный токен
или мёртвый прокси иначе всплыли бы через сутки молчания бота. Оттуда же приходит имя бота — без
него не собрать ссылку на чат, поэтому кнопка у зрителя появляется только после успешного контакта.
Вебхук ставится и снимается тем же сохранением: пока он стоит, Telegram не отдаёт обновления
опросом, и переключение режима без этого оставило бы бота немым.
## Оповещения
Считаются по материализованной ленте, а не по факту раздачи: лента и есть эфир, а ждать
подтверждения от плеера не от кого — зрителей может не быть вовсе.
- **Начало шоу** — на смену шоу, а не на каждую запись: четыре серии подряд это одна программа
в глазах зрителя, и четыре сообщения о ней читаются как спам.
- **Скоро в эфире** — за пять минут до следующей программы.
Граница разобранного (`TelegramSettings.NotifiedUntil`) двигается на каждом тике, даже когда
подписчиков нет: иначе первый же подписавшийся получил бы пачку сообщений про всё, что успело
пройти. Догон ограничен пятнадцатью минутами — после долгого простоя бот не станет пересказывать
вчерашний эфир.
## Меню в чате
`/start` с кодом привязывает чат и открывает меню; `/start` без кода — открывает меню уже
привязанному. Заголовок канала в меню — это кнопка «программа передач»: ближайшие восемь блоков
в часовом поясе канала, идущее сейчас помечено. Подряд идущие серии одного шоу схлопываются
в один блок — зритель спрашивает «что дальше», а не «в каком порядке лежат файлы». Программа
приходит отдельным сообщением, меню остаётся на месте: иначе после просмотра пришлось бы звать
`/start`, чтобы вернуть кнопки. Меню живёт **одним сообщением**: нажатие кнопки переписывает его на месте, иначе
чат заполняется одинаковыми «ваши подписки» после каждого клика. Кнопка — тумблер: повторное
нажатие снимает подписку. `/stop` останавливает рассылку, не стирая подписки: вернувшись, зритель
получит свои каналы, а не пустой список.
Данные кнопок несут действие: `sub:канал:вид` переключает подписку, `epg:канал` показывает
программу. Действие в первом поле, а не «угадаем по числу частей»: кнопок со временем станет
больше, и разбор по длине строки сломался бы на первой же новой.
## Границы
- Бот не отвечает на произвольные вопросы и не показывает программу передач — только подписки.
- Групповые чаты не поддерживаются: подписка привязана к учётке, а в группе её владельца нет.
- Один чат — один подписчик; повторная привязка по новому коду меняет учётку, а не плодит копии.