Как создать Telegram-канал с автоматическим постингом: мой опыт сборки пайплайна с Codex на Python и GitHub Actions
Я хотел собрать Telegram-канал, который сам каждый день решает за меня вечную проблему: что сегодня посмотреть и как превратить это в аккуратный автопостинг без ручной рутины.
Не просто канал, где нужно каждый день вручную искать тему, собирать пост и следить за временем публикации, а систему, которая сама формирует контентный план на день и публикует его по расписанию.
Темой я выбрал кино и сериалы. Причина очень простая: я сам трачу довольно много времени на поиск того, что посмотреть, и мне казалось, что этот формат хорошо подходит для автоматизации. У него понятный источник данных, регулярный поток новинок и достаточно структурированная карточка контента: название, описание, рейтинг, постер, актеры, жанры.
Я прекрасно понимаю, что сама по себе идея канала про кино и сериалы не выглядит как что-то уникальное. Таких каналов в Telegram много. Авторского контента вокруг этой темы тоже более чем достаточно.
Но моя задача была не в том, чтобы придумать новую медиа-нишу. Мне было интереснее собрать полностью автоматизированный workflow, который каждый день выдает рекомендации без ручного участия. А еще это был хороший способ научиться новому и на практике поработать с Codex не на абстрактной демо-задаче, а на живом проекте.
Если захотите посмотреть, как это устроено в коде, вот репозиторий проекта: github
Почему я не хотел делать просто “бота, который постит”
Самый очевидный путь в такой задаче выглядит так: взять API, написать скрипт, который раз в день получает несколько фильмов, собирает текст и отправляет его в Telegram.
На уровне MVP это действительно работает. Но довольно быстро выясняется, что этого недостаточно.
Если просто брать данные из внешнего источника и сразу публиковать их, почти сразу появляются проблемы:
- одни и те же тайтлы начинают повторяться
- часть карточек приходит без нормального русского описания
- в постах могут быть странные имена актеров или неаккуратные жанры
- план на день может получаться неполным
- уже собранную очередь легко случайно пересобрать или испортить
- если публикации размазаны по времени, нужен отдельный слой состояния: что уже вышло, что еще нет и что делать, если наступил новый день
Поэтому довольно быстро стало понятно, что нужен не “скрипт для отправки постов”, а маленький контентный пайплайн.
Что в итоге получилось
Я собрал проект на Python, который работает в два этапа.
Сначала утром он готовит очередь на день:
- собирает кандидатов из TMDb
- фильтрует их
- выбирает лучшие
- раскладывает их по слотам публикации
Потом в течение дня система проверяет время и публикует только следующий пост, когда наступает нужный слот.
Сейчас логика такая:
- 3 фильма в день
- 3 сериала в день
- чередование фильм/сериал
- фиксированные временные слоты по Москве
То есть в ленте получается предсказуемый ритм, а не случайный набор карточек.
Откуда берутся данные
В качестве источника я использовал TMDb.
Для фильмов сейчас используется movie/popular, для сериалов tv/on_the_air.
Важно, что проект не парсит HTML-страницы сайта, а работает через API. Для автоматизации это намного надежнее: меньше хрупких мест, проще поддержка, понятнее структура данных.
Кандидаты ищутся не по одной странице, а глубже:
- до 10 страниц по фильмам
- до 10 страниц по сериалам
Но если нужное количество уже найдено, дальнейший поиск останавливается. Это позволяет не тратить лишние запросы и не раздувать выполнение workflow без пользы.
Технически проект собран на простом и понятном стеке: Python отвечает за бизнес-логику пайплайна, TMDb API дает данные по фильмам и сериалам, Telegram Bot API публикует посты, GitHub Actions запускает подготовку очереди и публикации по расписанию, а Codex я использовал как рабочий инструмент для анализа, правок, отладки и постепенной эволюции проекта.
Почему пришлось добавлять quality gate
Красивый API не означает, что все данные в нем одинаково пригодны для публикации.
Если не фильтровать карточки, в канал довольно быстро начинает попадать то, что выглядит неаккуратно:
- нет русского описания
- нет нормального русского названия
- слишком низкий рейтинг
- нет постера
- смешанный язык в метаданных
Поэтому тайтл проходит в очередь только если:
- есть постер
- есть русское название
- есть русское описание
- рейтинг выше заданного порога
- по фильму нормально определяется дата релиза
- это не adult-контент
Отдельно пришлось поработать и с сериалами. Изначально в подборку попадали проекты из разных стран, потому что tv/on_the_air сам по себе не означает “только США”. В итоге я добавил безопасный фильтр по origin_country = US, чтобы сильнее сфокусировать выдачу на американском рынке.
Почему важна дедупликация
Если просто брать популярные тайтлы из API, одни и те же фильмы и сериалы будут возвращаться снова и снова.
Чтобы канал не зацикливался, я сделал дедупликацию в двух слоях:
- по точному событию
- по самому тайтлу в рамках временного окна
Сейчас логика такая:
- фильмы не повторяются раньше, чем через 120 дней
- сериалы не повторяются раньше, чем через 60 дней
За счет этого канал не выглядит так, будто он каждый раз советует одно и то же, если TMDb снова поднимает те же карточки в выдаче.
Зачем вообще понадобилась отдельная очередь на день
Это, пожалуй, одна из самых полезных частей проекта.
Я не хотел, чтобы система каждый раз в момент публикации заново пересчитывала весь мир. Если утром мы уже нашли хорошие карточки, логичнее один раз зафиксировать план на день и потом спокойно исполнять его по времени.
Поэтому проект сначала собирает очередь и сохраняет ее в отдельный state-файл. Внутри для каждого элемента есть:
- слот публикации
- сама карточка
- флаг публикации
- время, когда пост реально вышел
Это дает несколько плюсов:
- план на день можно проверить заранее
- в течение дня публикация не зависит от изменений в TMDb
- проще понимать, что уже вышло, а что еще ждет своей очереди
- если что-то нужно отлаживать, state лежит прозрачно и читаемо
Как публикуются посты
Для отправки используется Telegram Bot API через sendPhoto.
То есть каждый пост — это постер плюс caption. В caption я оставил только самую полезную структуру:
- тип: фильм или сериал
- оригинальное и русское название
- tagline
- дата
- рейтинг
- жанры
- актеры
- длительность или количество сезонов
- короткое описание
По пути пришлось отдельно полировать формат:
- перевести дату в привычный европейский вид
- убрать лишнее из подписи к рейтингу
- привести жанры к нижнему регистру
- сделать их списком через запятую
- добавить транслитерацию имен актеров, если TMDb отдает их латиницей
То есть проект постепенно эволюционировал из “что-то уже постится” в более аккуратный редакционный инструмент.
Почему я использовал GitHub Actions
Вместо отдельного сервера я решил построить все на GitHub Actions.
Для такого проекта это оказалось очень удобным вариантом:
- код и логика workflow лежат рядом
- не нужно поднимать лишнюю инфраструктуру
- проще видеть историю запусков
- можно хранить состояние публикаций прямо в репозитории
Сейчас у проекта два workflow.
Первый утром собирает очередь на день:
- поднимает окружение
- прогоняет тесты
- запускает сборку очереди
- проверяет, что очередь создана именно на сегодняшнюю дату
- коммитит обновленный queue-state обратно в репозиторий
Второй workflow запускается каждые 5 минут:
- проверяет, не наступил ли следующий слот
- если время еще не пришло, ничего не делает
- если пришло, публикует ровно один пост
- сохраняет обновленное состояние очереди и историю публикаций
Но именно здесь и всплыл важный практический нюанс.
На бумаге cron в GitHub Actions выглядит очень удобно. Можно поставить workflow хоть на каждые 5 минут, и документация GitHub это допускает. Но на практике scheduled jobs не стоит воспринимать как жесткую гарантию запуска минута в минуту.
У меня были случаи, когда workflow, который должен был срабатывать каждые 5 минут, по факту запускался раз в 1–2 часа, а иногда и реже. И это важное ограничение, которое нужно учитывать заранее, если вы строите что-то похожее.
Для меня это стало отдельным уроком. Автоматизация — это не только написать код, но и понимать ограничения платформы, на которой этот код живет.
То есть GitHub Actions отлично подходит для недорогой и простой автоматизации, но если проекту нужна жесткая гарантия публикации в точную минуту, это уже стоит учитывать отдельно на уровне архитектуры.
Почему для меня это был полезный проект
Для меня это был не просто еще один маленький pet-проект.
Во-первых, он решал реальную задачу, которая мне самому была интересна: как организовать контентный поток без ручного ежедневного участия.
Во-вторых, это был удобный полигон для того, чтобы учиться новому. Я использовал Codex не как “генератор кода ради генерации”, а как рабочий инструмент в живом цикле:
- разобраться в проблеме
- проверить гипотезу
- поправить логику
- прогнать dry run
- не сломать state
- аккуратно закоммитить и отправить изменения
Для обучения такой формат оказался гораздо полезнее абстрактных упражнений. Когда у тебя есть настоящий workflow, реальное расписание, живое состояние и понятный результат на выходе, ты намного лучше начинаешь понимать, что именно строишь и зачем.
Что я понял по итогам
Главная мысль у меня сейчас такая: автоматизация Telegram-канала — это не про “подключить API и codex+бот сам все сделает”.
На самом деле самая ценная часть тут — это процесс:
- как отбирать тайтлы
- как не допускать дублей
- как не публиковать мусор
- как раскладывать контент по времени
- как переживать смену дня
- как хранить и обновлять состояние без хаоса
Сделать “бота, который умеет постить” несложно. Намного интереснее сделать систему, которая несколько дней подряд ведет себя предсказуемо и не требует постоянного ручного вмешательства.
Именно это мне и хотелось собрать.
Что дальше
Если энтузиазм не угаснет, то впереди у проекта еще много вещей, которые можно улучшать:
- формат длинных постов
- более умный отбор рекомендаций
- дополнительные фильтры и источники
- улучшение оформления
- новые сценарии автоматизации
Если вам интересно, что в итоге получилось, можно посмотреть по ссылке на канал:
Ну и конечно, пробуйте сами, процесс довольно интересный.
Всем добра!