Создание и развертывание ретранслятора Telegram каналов, используя Python и Heroku
В данной инструкции построим Python приложение для зеркалирования сообщений из Telegram каналов и развернем его на облачной платформе Heroku.
Подготовка
Идея проста — получить сообщение и тут же переотправить или переслать его.
Ввиду ограниченности Bot API, нет возможности добавить бота в канал, где вы не администратор. Поэтому будем использовать клиентский API, заходя на нужные каналы вручную (хотя и этот шаг можно автоматизировать).
По ходу действий нам понадобятся:
- Аккаунт Telegram. Лучше перестраховаться и завести отдельный аккаунт, благо на сервисах приема SMS его цена составляет ~6₽. Для таких аккаунтов лучше сразу ставить двухфакторную аутентификацию;
- Созданный Telegram канал, куда будем отправлять сообщения;
- Установленный интерпретатор Python версии 3. Инструкция по установке;
- Установленный Git клиент. Инструкция по установке;
- База данных Postgres;
- Аккаунт на облачной платформе Heroku. Регистрация.
- Heroku CLI для взаимодейсвия с платформой Heroku. Установка.
Взаимодействие с Telegram API
Для работы с клиентским API необходимо создать приложение Telegram. Сделать это можно по ссылке. Здесь нас интересуют два значения: api_id и api_hash.
Полученные значения api_id и api_hash занесем во вновь созданный файл переменных окружения .env:
При использовании системы контроля версий, не забудьте добавить файл .env в список исключения.
Создание Python приложения
Создадим папку проекта. Все последующие действия будем делать в ней.
Настроим виртуальное Python окружение для изоляции приложения от других глобальных зависимостей системы:
Активируем виртуальное окружение:
Последующая установка новых Python пакетов будет производиться в данном виртуальной окружении.
Авторизация клиента
В качестве библиотеки для взаимодействия с Telegram API будем использовать Telethon:
Для загрузки значений переменных окружения установим пакет python-dotenv:
Создадим файл config.py, где будем хранить и загружать переменные окружения в память приложения:
Скрипт авторизации, целью которого является получение идентификатора сессии, выглядит следующим образом:
Запустим его:
После ввода номера телефона и кода, пришедшего в приложение Telegram, получаем заветный идентификатор сессии, который добавим в файл .env.
Теперь, используя вновь полученный идентификатор сессии, авторизуемся для дальнейшего взаимодействия с Telegram API:
Получение обновлений от Telegram
Присоединившись к каналу, серверы Telegram начинают слать нам соответствующие обновления (новые сообщения, отредактированные сообщения и др.). Остается лишь перенаправить их в наш канал.
Определим идентификаторы канала-источника (SOURCE_CHANNEL) и канала-приемника (TARGET_CHANNEL):
Каналы имеют обыкновение менять свои ссылки, но что остается неизменным — это их идентификаторы (число с префиксом -100). Узнать идентификатор можно с помощью Telegram бота @messageinformationsbot, переслав ему любое сообщение из интересующего вас канала.
Получение новых сообщений
Следуя примеру из документации, добавим обработчик новых сообщений:
Для каждого нового сообщения в функцию handler_new_message приходит объект типа NewMessage, содержащий поле message с необходимой нам информацией. Т.к. нам необходимы обновления только от определенных каналов, при определении обработчика передадим ему параметр chats, содержащий идентификатор либо список идентификаторов каналов.
Для успешной отправки сообщений в Telegram канал необходимо, чтобы аккаунт, которым авторизовались в приложении, имел на это соответствующие права.
Редактирование сообщений
В случае переотправки пришедших сообщений (использование метода client.send_message), при отредактированном сообщении в канале-источнике, можем отредактировать его и в канале-приемнике.
Для реализации редактирования необходимо знать какое именно сообщение редактировать, поэтому при добавлении нового сообщения, нужно сохранить идентификаторы исходного и копии сообщения. В дальнейшем по идентификатору сообщения из источника сможем определить идентификатор сообщения в нашем канале.
Установим базу данных Postgres, если этого не было сделано ранее. Сама установка не должна вызвать сложностей:
- Для вашей операционной системы скачать установщик;
- Запустить его, оставив параметры по умолчанию. Единственное, что стоит запомнить это логин и пароль пользователя базы данных:
- Добавить к переменной окружения PATH путь к используемым файлам Postgres.
Для Windows: C:\Program Files\PostgreSQL\{ВЕРСИЯ}\bin, инструкция по установке переменных окружения.
Далее первоначальное взаимодействие с базой будем производить с помощью утилиты psql:
Для хранения соответствий идентификаторов определим простейшую таблицу BINDING_ID:
Создадим таблицу BINDING_ID в консоли утилиты psql командой:
Взаимодействие с базой данных будем производить с помощью пакета psycopg2:
В файл .env добавим строку подключения к базе данных вида:
В случае развертывания на Heroku и использования Postgresql дополнения, для приложения в облаке переменная окружения DATABASE_URL устанавливается автоматически. Поэтому при развертывании в облаке DATABASE_URL устанавливать не нужно.
Для работы с базой данных определим следующие базовые функции: добавление соответствия идентификаторов сообщений (insert) и поиск идентификатора сообщения-клона по идентификатору оригинального сообщения (find_by_id):
В функцию обработчика новых сообщений добавим сохранение идентификаторов сообщений в базе данных:
Тогда редактирование сообщений будет выглядеть следующим образом:
Ознакомиться с текущим вариантом проекта можно по ссылке ниже:
Для случая зеркалирования «один к одному» у нас все готово и можно переходить к развертыванию.
Зеркалирование «много к одному»
Когда каналов-источников и каналов, в которые необходимо пересылать сообщения, больше чем один, необходимо задать их взаимное соответствие.
Задавать карту соответствий прямо в коде — не вариант, поэтому для значения новой переменной окружения (CHANNELS_MAPPING) введем специальный формат записи:
Теперь вместо ранее заданных переменных SOURCE_CHANNEL и TARGET_CHANNEL имеем CHANNELS_MAPPING:
Для дальнейшей работы из строкового значения CHANNELS_MAPPING нужно получить представление в виде словаря (dict). Сделать это можно, разобрав строку по составляющим ее элементам с помощью регулярных выражений (модуль re) либо с помощью парсера грамматик (модуль pyparsing). В данном случае воспользуемся вторым вариантом.
Установим модуль pyparsing:
Так как входная строка для разбора состоит из отдельных элементов, для каждого такого элемента определим свою грамматику:
- Основной элемент — идентификатор канала, состоящий из префикса -100 и последующих цифр:
- Список каналов:
Полное выражение:
Подробнее о модуле pyparsing можно узнать в документации.
После составления грамматики, формируем результат разбора строки в виде словаря, где ключи — исходные каналы, значения — список целевых каналов (функция parse_string):
Инициализация переменной CHANNELS_MAPPING:
Расширим таблицу BINDING_ID двумя столбцами: идентификатор канала-зеркала (mirror_channel_id) и идентификатор канала-источника (channel_id):
И обновим функцию поиска сообщений-клонов в базе данных (find_by_id):
Тогда добавление новых сообщений будет выглядеть следующим образом:
Аналогично редактирование:
На вариант с множественным зеркалированием можно посмотреть по ссылке ниже:
Развертывание
Для развертывания приложения Heroku необходимо сделать следующее:
1. Зарегистрироваться на Heroku, если это не было сделано ранее;
2. Для бесплатного использования — увеличить число бесплатных часов работы до 1000;
3. Создать Heroku Procfile:
Procfile — файл описания команд, которые будут выполняться в приложении Heroku.
4. Создать локальный git репозиторий и сделать первый коммит, если это не было сделано ранее;
5. Создать новое Heroku приложение в Heroku CLI:
6. Добавить Postgres расширение к приложению Heroku:
7. Скопировать данные из локальной базы данных в удаленную базу данных Heroku:
Копирование данных может закончиться ошибкой, если таблица была пустой, но присоединившись к удаленной базе командой:
Убедимся, что таблица была перенесена:
8. Добавить удаленный git репозиторий Heroku:
9. Установить переменные окружения для приложения Heroku из файла .env с исключением переменной DATABASE_URL (установится автоматически):
Для пользователей Windows команду выше можно выполнить в Git Bash либо воспользоваться методами из статьи.
10. Создать файл requirements.txt со всеми зависимостями приложения:
11. Загрузить приложение на Heroku:
12. Запустить приложение:
Итог
В итоге получили инструмент зеркалирования Telegram каналов, конфигурируемый с помощью переменных окружения и работающий удаленно в облаке Heroku.
Полезная штука. Тут чувак сделал наподобие, только управление редиректами сделано через бота https://github.com/rumble-key/feed-bot-telegram
классный гайд, можешь написать в личку - есть подобная задача, вдруг возможно сотрудничество с тобой!
Спасибо! Настроил себе все)
Подскажите, а почему позникает ошибка "Cant adapt type message"?
Отличная работа! А вы не думали добавить фильтрацию измененных/удаленных сообщений?
Пример: добавляем новые сообщения в базу, и мониторим их. Если такое сообщение будет изменено/удалено - только тогда отправляем в "зеркальный" канал
По ссылке в конце статьи находится обновленный проект, где можно добиться такого поведения изменив реплицирующие методы в EventProcessor'e ( https://github.com/khoben/telemirror/blob/a555136d3844381016916794ff8c78b9879eebf6/telemirror/mirroring.py#L16 ): на событие NewMessage только пишем в БД без отправки, на остальные события по наличию в БД отправляем сообщение.
Heroku из России уже не доступен (не оплатить и т.д.). Можно все то-же самое сделать на Amvera Cloud, это отечественный аналог.