Search code examples
python-3.xpython-imaging-librarybytesioaiogram

send_media_group with BytesIO to telegram bot


I'm creating an image with PIL and storing it in the clipboard with BytesIO.I want to send an image album to telegram bot, but I get an error:

Code:

from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
from aiogram import Bot, Dispatcher, executor, types
import logging

API_TOKEN = 'xxxxxx'
logging.basicConfig(level=logging.INFO)
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)

def createimg()
    im = Image.new('RGB', (256, 256), color='black')
    bio = BytesIO()
    bio.name = 'res.png'
    im.save(bio, 'PNG')
    bio.seek(0)
    return bio

@dp.message_handler(commands=['start'])
async def send_welcome(message: types.Message):
    await bot.send_media_group(message.from_user.id, [createimg(), createimg()])

Error:

   raise TypeError(f"Media must be an instance of InputMedia or dict, not {type(media).__name__}")
TypeError: Media must be an instance of InputMedia or dict, not BytesIO

Solution

  • You have to use subclasses of aiogram.types.InputMedia objects in your list, not plain io.BytesIO objects. In your case it must be aiogram.types.InputMediaPhoto, which accepts aiogram.types.InputFile object as first argument, where you can put your plain io.BytesIO objects directly.

    Also, consider using shortcuts in your code as it makes your code cleaner and more readable. For example, you can .reply() or .answer() to a aiogram.types.Message, which is a shortcut for aiogram.Bot.send_message(). In your case you should use aiogram.types.Message.answer_media_group.

    So, your code should be like this:

    # Some code is omitted in favor of brievity
    
    def wrap_media(bytesio, **kwargs):
        """Wraps plain BytesIO objects into InputMediaPhoto"""
        # First, rewind internal file pointer to the beginning so the contents
        #  can be read by InputFile class
        bytesio.seek(0)
        return types.InputMediaPhoto(types.InputFile(bytesio), **kwargs)
    
    @dp.message_handler(commands=['start'])
    async def send_welcome(message: types.Message):
        await message.answer_media_group([wrap_media(createimg()), wrap_media(createimg())])
    

    For more info of what aiogram.types.InputMediaPhoto accepts, refer to aiogram documentation.