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.
7.6 KiB
Бот расписания в 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:канал показывает
программу. Действие в первом поле, а не «угадаем по числу частей»: кнопок со временем станет
больше, и разбор по длине строки сломался бы на первой же новой.
Границы
- Бот не отвечает на произвольные вопросы и не показывает программу передач — только подписки.
- Групповые чаты не поддерживаются: подписка привязана к учётке, а в группе её владельца нет.
- Один чат — один подписчик; повторная привязка по новому коду меняет учётку, а не плодит копии.