Дорогой дневник, мне не подобрать слов, чтобы описать ту боль и унижения…
Короче, столкнулся я тут с вопросом: можно ли клонировать канал из телеграмма в MAX? Сразу скажу - функционалом ботов нельзя. Боты не видят историю сообщений, поэтому при необходимости нужно использовать user-agent библиотеки типа telethon. Я таким заниматься желанием не горю, да и вопрос сегодня про кросс-постинг.
Если вкратце: есть канал в телеграмм, есть канал в MAX. В каналы отправляются посты с картинкой (может и не одной), либо видео и текстом. Предположим, стандартные новостные посты.
Нужно сделать так, чтобы при добавлении поста в телеграмм, он автоматически добавлялся в канал MAX. Также, неплохо бы добавить и обратную схему на случай блокировок (отправлять из MAX в телеграмм).
Немного вводных:
В этой статье мы не будем писать код, он уже написан и выложен мной на github.
Я буду объяснять структуру проекта, и как функционал работает. Также напишу, как правильно поднять все окружение и настроить каналы.
Для запуска проекта нужно:
- Бот в телеграмме с его токеном.
- Канал в телеграмме, в который наш бот добавлен администратором.
- Бот в MAX с полученным токеном.
- Канал в MAX, в который бот добавлен администратором.
Настройка
- После добавления бота, нам необходимо узнать chat_id нашего канала (всегда будет отрицательным).
- Создаем бота в MAX и получаем его токен.
- Аналогично телеграмму, создаем канал в MAX, добавляем туда бота и делаем его администратором.
Отправка сообщений
В файле message_sender.py хранится две функции: для отправки сообщения в MAX и в телеграмм.
Отправлять сообщения в MAX будет тг бот, и наоборот соответственно.
- Отправка в MAX
В этой статье мы уже разбирались, как правильно отправлять сообщения с вложениями. Теперь мы немного доработаем функцию под нашу задачу.
В функцию будет приходить текст, список вложений и ID группы в MAX.
Благодаря структуре сообщений в MAX, все просто и лаконично.
- Отправка в телеграмм
С телеграммом немного сложнее: текст сообщения (caption) должен прикрепляться именно к первой картинке, и только к ней. Поэтому нам необходимо отлавливать этот момент.
Также, если мы потом захотим дополнительно обрабатывать и видео, нужно будет дописать отдельное условие на него, т.к. вместо InputMediaPhoto придется использовать InputMediaVideo.
В остальном, логика та же: мы принимаем текст, список вложений и ID группы.
Бот MAX
Бот состоит всего из одной функции:
Давайте разбираться, как это работает.
- @dp.message_created(F.chat.chat_id == max_group_id) - декоратор, который обрабатывает сообщения только из указанной группы.
- event.message.body.attachments - список вложений в полученном сообщении. Если у сообщения не будет вложений - вернет пустой список.
- match attach.type - проверка вложений по типу файла (если необходимо скачивать и видео - достаточно добавить:
case 'video':
file_name = f'{attach.payload.token[0:15]}.mp4'
- send_tg_message() - вызывает написанную нами функцию отправки сообщения в телеграмм.
Бот в телеграмм
А теперь немного личной боли.
Уже не в первый раз я сталкиваюсь с обработкой сообщений с фото, видео и т.д. А в частности - с обработкой медиа групп (когда в сообщении приходит сразу несколько файлов). Проблема заключается в том, что медиа группа приходит не одним сообщением, а каждый файл прилетает отдельно. Как правильно это обрабатывать, я так и не придумал, да и смысла уже не особо много, поэтому использую решение с таймаутами.
Для работы с файлами нам понадобятся функции:
- Скачивание ботом файла через API телеграмм
- Сохранение картинок и видео
- Получение списка файлов медиа группы
- Функция, обрабатывающая сообщения (расскажу на примере фото, видео идентично):
@bot.message_handler(content_types=['photo', 'video']) - обработка сообщений, в которых прикреплено фото, либо видео.
if message.chat.id == tg_group_id - обработка сообщений только из нашего канала.
if message.media_group_id is not None - если мы видим, что у полученного сообщения есть тег медиа группы.
save_image(f"{fileid}_{message.media_group_id}", downloaded_file) - сохраняем картинку с тегом медиа группы в названии.
if message.caption is not None - и если у сообщения есть подпись (первое сообщение из медиа группы)
text = message.caption - запоминаем текст сообщения и ждем 10 секунд (надеемся, что за это время дойдут и остальные сообщения, в случае больших задержек - увеличьте таймаут до 20-30 сек.)
list_of_files = get_list_of_files(message.media_group_id) - ищем все файлы в папке с тегом медиа группы в названии и формируем из них список.
asyncio.run(send_max_message(chat_id=max_group_id, message=text, attach=list_of_files)) - Отправляем сообщение в MAX.
Если же нам пришло сообщение без тега медиа группы, сразу обрабатываем его и передаем дальше на отправку в MAX.
Весь проект выложен здесь. Можете свободно использовать его для своих нужд. При необходимости - доработайте под себя, а все вопросы задавайте в комментариях, по возможности с радостью отвечу.