tg-bot-1.md 23.07.2026

Бот для сбора лидов из Telegram-каналов и RSS

Бот для Telegram: читает каналы и RSS-ленты, фильтрует заказы по ключевым словам с весами и присылает подходящие в личку.

Задача

Источники заказов приходят в разные моменты: каналы — событием, RSS — раз в минуту. Нужен один поток лидов в личный чат и настройка фильтров без правки кода и передеплоя.

Как делал

Главная сложность в том, что два телеграм-транспорта умеют разное. Пришлось запустить оба в одном процессе, иначе либо команды, либо чтение каналов не работало бы:

ЗадачаBot APITelethon
Команды в чатеданет
Чтение каналов без подпискинетда
Что нужно от пользователятокен ботаapi_id, api_hash, сессия

offset лежит в SQLite, а не в памяти. Без этого после каждого редеплоя на Render long-poll откатывался бы назад и повторно обрабатывал старые команды.

Фильтр — не «есть слово», а вес: каждое ключевое слово кладёт в score своё значение, и текст проходит только когда сумма перевалила min_score. Стоп-слова проверяются первыми и отменяют всё, даже набранный score — один «букмекер» перекрывает десять совпадений. Нормализация приводит ё к е и схлопывает пробелы, иначе часть заказов отсеивалась бы из-за регистра.

Отдельный слой отвечает за то, что делать с самим текстом заказа. Из лида вытаскиваются контакты, бюджет и дата — их потом видно в сообщении, не открывая канал. Контакты ищутся тремя шаблонами: @username, почта и любая ссылка. Дубли отбрасываются, и в лид попадает максимум восемь находок, иначе длинный пост засорял бы сообщение. Бюджет ловится по ключевым словам с двоеточием или дефисом: «бюджет:», «цена —», «ставка -».

Самое полезное решение — доставка через SQLite как очередь с подтверждением. Запись о лиде создаётся до отправки с notified_at = NULL, и поле заполняется только после успешного sendMessage. Если отправка упала, запись осталась неподтверждённой, и следующий цикл отдаст её снова.

UNIQUE(source, message_id) в схеме закрывает дубли на уровне базы, а не приложения. Ключевые слова, стоп-слова и источники лежат там же, отдельными таблицами — поэтому фильтры меняются командами, а деплой для этого не нужен.

У RSS нет числового ID сообщения, а колонка объявлена INTEGER. Поэтому guid ленты хешается, и первые восемь hex-символов становятся ID. feedparser при этом синхронный, поэтому разбор уходит в executor и не встаёт event loop.

Остановка сделана через add_signal_handler на SIGINT и SIGTERM: деплой на Render — это обычный SIGTERM, и без обработчика база закрывалась бы в середине записи.

Итог

1221 строка, четыре зависимости, три среды. На старте бот добирает по пять последних сообщений каждого канала, иначе первый прогон молчал бы до первого нового поста. Фильтры, стоп-слова и источники правятся из чата.

Развёрнут на Render как Web Service через render.yaml, доступ к данным только через переменные окружения. Встроенный HTTP-сервер отдаёт /health на трёх путях, чтобы не угадывать, какой именно проверяет платформа. Пока инстанс не спит, cron в GitHub Actions пингует его каждые 10 минут.

Логика разложена по транспортам, а не по слоям: транспортный код в app.py, фильтры отдельно, разбор лент отдельно, база отдельно. Конфиг читается из переменных окружения прямо в app.py — свой модуль для шести os.getenv не окупался.

// похожие проекты

Ещё работы

// проект 2026
Нет изображения

Парсер публикаций ВКонтакте

Парсер для VK: находит подходящие посты в поиске новостей и ставит их в предложку своих групп, с защитой от повторов.

// проект 2026
Нет изображения

Парсер цены товара Ozon по ID

Скрипт по ID товара Ozon читает цену покупателя из зелёной плашки, отдаёт её в JSON и сам переживает антибот-проверку Ozon.

// проект 2026
Скриншот главной страницы лединга AROMA

Aroma. Лендинг кофейни

Одностраничный лендинг кофейни: атмосфера, меню с фильтрами и каруселью, форма бронирования с модалкой подтверждения.

// проект 2026
Скриншот главной страницы лендинга ИГРАЙ-КА

«Играй-ка». Лендинг магазина игрушек

Одностраничный лендинг магазина детских игрушек: витрина, категории, отзывы и форма заявки.

// проект 2026
Скриншот страницы сайта YTWrapped

YT-wrapped. Аналитика YouTube-каналов

Аналитика YouTube-каналов: поиск, разбор последних 50 видео, вовлечённость, частота загрузок и рейтинг роликов.

// проект 2026
Скриншот главной страницы лендинга Counter

Counter. Лендинг для типографии

Лендинг типографической студии: каскад размеров, живой образец шрифта и горизонтальная бегущая строка.

> все проекты

// contact.php

Ну что, работаем?

Опишите задачу — разберусь и предложу решение.