15 Working with Files and Media
Kyryh edited this page 2024-03-16 20:21:43 +01:00

Bots interacting with users in plain text messages is often times not enough for a pleasant user experience. Providing the users with images, videos, files and other media is therefore a common use case for bot programmers and the Bot API provides several ways to do this. On this wiki page, we explain how files and media are handled in the python-telegram-bot framework.

Sending files

If you want to send a file (e.g. send a document or a photo) with the bot, you have three options:

  • Upload the file
  • Send an HTTP URL that leads to the file
  • Send a file_id of a file that has already been sent.

Note that not every method is supported everywhere (e.g. for thumbnails you can't pass a file_id). Make sure to check out the documentation of the corresponding bot method for details.

Please also check out the official Telegram API docs on sending files.

Let's have a look at how sending a document can be done. In these examples, we'll be using Bot's send_document() method.

Note

In discussion and examples below, we will be using methods of Bot, but most of them (including send_document()) have shortcut methods in classes like User, Chat or Message that can be more convenient to use in your particular situation. Documentation for every method in Bot contains links to shortcut methods in other classes.

  1. Uploading a file

    await bot.send_document(chat_id=chat_id, document=open('tests/test.png', 'rb'))
    

    or even just

    await bot.send_document(chat_id=chat_id, document='tests/test.png')
    

    When you pass a file path (note that both str and pathlib.Path are accepted as document parameter), PTB will automatically check if your bot is running in local mode. If it is, the file does not need to be uploaded. Otherwise, the file is read in binary mode, so just as when you pass open('tests/test.png', 'rb').

  2. Sending an HTTP URL

    await bot.send_document(chat_id=chat_id, document='https://python-telegram-bot.org/static/testfiles/telegram.gif')
    
  3. Sending by file_id:

    await bot.send_document(chat_id=chat_id, document=file_id)
    

    Two further notes on this:

    1. Each bot has its own file_ids, i.e. you can't use a file_id from a different bot to send a photo

    2. How do you get a file_id of a photo you sent? Read it from the return value of bot.send_document() (or any other Message object you get your hands on):

      message = await bot.send_document(...)
      file_id = message.document.file_id
      

This pretty much works the same way for all the other send_<media_type>() methods like send_photo(), send_video() etc. There is one exception, though: send_media_group().

Sending a media group

A call to send_media_group() looks like this:

await bot.send_media_group(chat_id=chat_id, media=[media_1, media_2, ...])

Each of the items in the media sequence (list or tuple) must be an instances of InputMediaAudio, InputMediaDocument, InputMediaPhoto or InputMediaVideo. The media comes into play like so:

media_1 = InputMediaDocument(media=open('tests/test.png', 'rb'), ...)
media_1 = InputMediaDocument(media='https://python-telegram-bot.org/static/testfiles/telegram.gif', ...)
media_1 = InputMediaDocument(media=file_id, ...)

Caution

For the InputMedia* classes, passing a file path only works if your bot is running in local mode.

Sending files via inline mode

You may want to allow users to send media via your bots inline mode. This works a little bit different than posting media via send_*. Most notably, you can't upload files for inline mode! You must provide either an HTTP URL or a file_id.

Let's stick to example of sending a document. You have to provide bot.answer_inline_query() with an InlineQueryResult that represents that document. There are two ways of doing that:

  1. HTTP URL:

    result = InlineQueryResultDocument(document_url='https://python-telegram-bot.org/static/testfiles/telegram.gif', ...)
    
  2. file_id:

    result = InlineQueryResultCachedDocument(document_file_id=file_id, ...)
    

In this example, we are using InlineQueryResultDocument for option #1 and InlineQueryResultCachedDocument for option #2. The scheme InlineQueryResult<media_type> vs InlineQueryResultCached<media_type> is similar for the other media types. Again, please check out the docs for details on required and optional arguments.

Editing a file

When you have sent a file, you may want to edit it. This works similarly as send_media_group, i.e. the media must be wrapped into a InputMedia<media_type> object. Again, with document as example, we'll call bot.edit_message_media() and pass an instance of InputMediaDocument as media:

await bot.edit_message_media(chat_id=chat_id, message_id=message_id, media=InputMediaDocument(media=open('tests/test.png'), ...))

Please check out the restrictions on editing media in the official docs of editMessageMedia.

Downloading a file

When you receive files from a user, you sometimes want to download and save them. If it's a document, that could look like this:

file_id = message.document.file_id
new_file = await bot.get_file(file_id)
await new_file.download_to_drive()

For a received video/voice/... change message.document to message.video/voice/.... However, there is one exception: message.photo is a list of PhotoSize objects, which represent different sizes of the same photo. Use message.photo[-1].file_id to get the largest size.

See also:

Documentation for Bot.get_file()

Moreover, the above snippet can be shortened by using PTB's built-in utility shortcuts:

new_file = await message.effective_attachment.get_file()
await new_file.download_to_drive('file_name')

message.effective_attachment automatically contains whichever media attachment the message has. In case of a photo, you'll again have to use e.g. message.effective_attachment[-1].get_file().

See also:

Documentation for File.download_to_drive() and File.download_to_memory()