Telegram боты. Загружаем файлы больше 50мб

  • Tutorial

Telegram боты позволяют автоматизировать многие процессы. Их использование не ограничивается одним чатом, по сути — бот это всего лишь интерфейс ввода-вывода, который помимо текста также может принимать и передавать файлы: изображения, видео, аудио, документы…

  • Для пользователей максимальный размер файла — 1.5Гб
  • Боты ограничены размером всего лишь в 50мб

Как обойти данное ограничение — под катом.

Telegram API


Раз пользователи могут загружать файлы до 1.5Гб — значит и мы можем — для этого создадим агента (назвал чтобы не путать с ботами) который будет работать в связке с нашим Telegram ботом. Для этого потребуется отдельный аккаунт и Telegram API.

Для начала идем на https://core.telegram.org и по инструкции регистрируем приложение, в итоге вы должны получить api_id и api_hash



Что делает агент?


Бот не может загружать файлы больше 50мб, но если у него есть file_id уже загруженного на сервера Telegram файла — то он может его пересылать. Так что алгоритм следующий

  1. Приложение, работающее на сервере через Bot API формирует файл для отправки
  2. Вызывает агента для загрузки файла на сервера Telegram
  3. Получает от агента file_id
  4. Пользуется загруженным файлом

Пример кода


Потребность загружать большие файлы появилась при написании @AudioTubeBot — изначально аудиофайл разбивался на части и отправлялся по частям. Функционал загрузки больших файлов было решено вынести в отдельное приложение, которое вызывается через subprocess.check_call

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from telethon import TelegramClient
from telethon.tl.types import DocumentAttributeAudio
import mimetypes
entity = 'AudioTube_bot' #имя сессии - все равно какое
api_id = 1959
api_hash = '88b68d6da53fe68c1c3541bbefc'
phone =  '+79620181488'
client = TelegramClient(entity, api_id, api_hash, update_workers=None, spawn_read_thread=False)
client.connect()
if not client.is_user_authorized():
    # client.send_code_request(phone) #при первом запуске - раскомментить, после авторизации для избежания FloodWait советую закомментить
    client.sign_in(phone, input('Enter code: '))
client.start()
def main(argv):
    file_path = argv[1]
    file_name = argv[2]
    chat_id = argv[3]
    object_id = argv[4]
    bot_name = argv[5]
    duration = argv[6]
    mimetypes.add_type('audio/aac','.aac')
    mimetypes.add_type('audio/ogg','.ogg')
    msg = client.send_file(
                           str(bot_name),
                           file_path,
                           caption=str(chat_id + ':' + object_id + ':' + duration),
                           file_name=str(file_name),
                           use_cache=False,
                           part_size_kb=512,
                           attributes=[DocumentAttributeAudio(
                                                      int(duration),
                                                      voice=None,
                                                      title=file_name[:-4],
                                                      performer='')]
                           )
    client.disconnect()
    return 0

if __name__ == '__main__':
    import sys
    main(sys.argv[0:])

Комментарии:


Вот и весь код — тут используется библиотека Telethon — при запуске программе передается путь к файлу для отправки, имя файла, chat_id — для кого предназначается данный файл), имя бота, который вызвал агента(например у меня это beta и release боты).

client.send_file


Просто загрузить файл на сервер через upload, получить file_id и передать его боту — не выйдет, file_id работает только внутри чата, в котором он был создан — чтобы наш бот мог переслать файл пользователю по file_id — агент должен переслать ему этот файл
— тогда бот получит свой file_id для этого файла и сможет распоряжаться им.

caption=str(...) — wat?!


Агент пересылает файлы только боту, добавляя комментарий в caption-у меня это:

  • chat_id конечного пользователя
  • длительность трека
  • object_id в базе данных, к которому нужно привязать file_id, чтобы повторно не загружать файл(индексация, оптимизация и все такое)



Пример вызова в коде бота


На диске в path_file_mp3 уже сохранен файл для загрузки, вызываем подпрограмму и ждем ее завершения.

код
status = subprocess.check_call(
             "python3.6 audiotubeagent36/main.py " +
             path_file_mp3 + ' ' +
             audio_title + '.'+ us_audio_codec +
             ' ' + str(chat_id) + 
             ' ' + str(pool_object['_id']) +
             ' ' + config.BOT_NAME + 
             ' ' + str(duration),shell=True)


В обработчике входящих сообщений делаем что то подобное

код
    if message.content_type in ['document','audio']:
        user_id = message.from_user.id
        bot_settings = SafeConfigParser()
        bot_settings.read(config.PATH_SETTINGS_FILE)
        c_type = message.content_type
        if functions.check_is_admin(bot_settings, user_id):
            if c_type == 'audio':
                file_id = message.audio.file_id
                audio_title = message.audio.title
            else:
                file_id = message.document.file_id
                audio_title = message.document.file_name[:-4]
            client_chat_id = message.caption
            if client_chat_id.find(u':') != -1:
                client_chat_id, q_pool_obj_id, duration_s = re.split(r':',client_chat_id)
#добавляем file_id в базу
                q_pool.update_request_file_id(str(q_pool_obj_id), str(file_id))
#пересылаем конечному адресату
            bot.send_audio(int(client_chat_id), 
                                   file_id,caption='',
                                   duration=int(duration_s),
                                   title=audio_title,
                                   performer='')
        return


Вопросы/предложения пишите в комментариях или в чате.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 7

    0
    Туториал годный, но если им будут пользоваться новички, то лучше сразу приучать их к прекрасному :)

    Можно использовать f-string вместо кучи сложений:
    call = f"python3.6 audiotubeagent36/main.py {path_file_mp3} {audio_title} " \
           f"{us_audio_codec} {chat_id} {pool_object.get('_id')} {config.BOT_NAME} " \ 
           f"{duration}"
    


    Или через .join:
    params = us_audio_codec, str(chat_id), str(pool_object.get('_id')), \ 
             config.BOT_NAME, str(dutation)
    call = ' '.join(params)


    Можно использовать передачу словаря или json вместо строки и её парсинга.
    Можно использовать двух ботов в одном коде, вместо разделения.
    Можно использовать таски asyncio вместо запуска cопрограмм.
    Можно использовать .get() в словарях, чтобы не ловить Exception в случае недоступности ключа.
      0
      спасибо за совет
      0
      Здесь у тебя дублируется запрос, поэтому и возникает FloodWait:
      client.connect()
      if not client.is_user_authorized():
          # client.send_code_request(phone) #при первом запуске - раскомментить, после авторизации для избежания FloodWait советую закомментить
          client.sign_in(phone, input('Enter code: '))
      client.start()


      Вижу, что пример ты брал из документации, однако в методе .start() клиент и так выполняет все действия, необходимые для авторизации.
      Если требуется двухфакторка, то необходимо в конфиге указать пароль.

      Простой и рабочий пример:
      client.connect()
      client.start(phone=config.PHONE, password=config.PASSWORD)
        0

        Не в этом дело-дело в том, что если не авторизован-можно случайно запросить много раз смс и вызвать flood wait-когда строчка закомментирована-нельзя

          0
          авторизация требуется для одной сессии, а когда отлаживаешь на разных машинах — легко случайно на новой машине запросить несколько раз смс, получив блок на сутки — поэтому так вот обезапасил — на новой машине раскомментировал, залогинился, закомментировал
        • НЛО прилетело и опубликовало эту надпись здесь
          • НЛО прилетело и опубликовало эту надпись здесь

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое