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