Skip to content

Aiogram 3: Отправка и обработка фото

https://habr.com/ru/articles/822061/

Для понимания работы с медиа сообщениями, так как для текстовых сообщений, используются схожие методы с небольшими отличиями, зависящими от типа сообщения. Например, для работы с фото используются методы answer_photo, reply_photo, send_photo, а для документов — answer_document, reply_document, send_document и так далее.

Также важно помнить, что правила, применяемые к текстовым сообщениям, распространяются и на медиа сообщения, за исключением того, что в медиа отсутствует объект текста message.text. Там, где это возможно (например, при добавлении подписи к фото или видео), используется элемент caption.

Методы для редактирования текста в медиа заменяются на методы для редактирования подписей edit_caption, а для замены медиа контента используется метод edit_media. Замена клавиатур для всех поддерживающих их медиа сообщений ничем не отличается от замены клавиатур в текстовых сообщениях.

Отправка и обработка фото

Фотографии в Telegram можно отправлять со сжатием и без сжатия. Фотографии имеют свои превью (маленького размера). За счет всех этих особенностей у каждого файла фотографии всегда несколько идентификаторов, и чтобы забрать фото наилучшего качества, нам необходимо сделать следующее:

python
msg_id.photo[-1].file_id

То есть отправляя одно фото, отправляется целый список фотографий, а фото самого лучшего качества в этом списке будет стоять последним (индекс -1). Давайте посмотрим на примере:

python
@start_router.message(Command('send_photo'))
async def cmd_start(message: Message, state: FSMContext):
    photo_file = FSInputFile(path=os.path.join(all_media_dir, 'photo_2024-06-14_20-13-40.jpg'))
    msg_id = await message.answer_photo(photo=photo_file, reply_markup=main_kb(message.from_user.id),
                                        caption='Моя <u>отформатированная</u> подпись к <b>фото</b>')
    print(msg_id.photo[-1].file_id)

Мы видим, что фото отправилось, а в консоли я получил идентификатор фото:

shell
AgACAgIAAxkDAAIBwGZshp7dSSQi0VKxt6RKJgseyMHxAALM4DEbGKhoS4tvyaZWY29DAQADAgADeAADNQQ

Копируем и пробуем отправить уже через идентификатор.

python
@start_router.message(Command('send_photo'))
async def cmd_start(message: Message, state: FSMContext):
    # photo_file = FSInputFile(path=os.path.join(all_media_dir, 'photo_2024-06-14_20-13-40.jpg'))
    photo_id = 'AgACAgIAAxkDAAIBwGZshp7dSSQi0VKxt6RKJgseyMHxAALM4DEbGKhoS4tvyaZWY29DAQADAgADeAADNQQ'
    msg_id = await message.answer_photo(photo=photo_id, reply_markup=main_kb(message.from_user.id),
                                        caption='Моя <u>отформатированная</u> подпись к <b>фото</b>')
    print(msg_id.photo[-1].file_id)

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

python
@start_router.message(Command('send_photo'))
async def cmd_start(message: Message, state: FSMContext):
    # photo_file = FSInputFile(path=os.path.join(all_media_dir, 'photo_2024-06-14_20-13-40.jpg'))
    # photo_id = 'AgACAgIAAxkDAAIBwGZshp7dSSQi0VKxt6RKJgseyMHxAALM4DEbGKhoS4tvyaZWY29DAQADAgADeAADNQQ'
    photo_url = 'https://indirimlerce.com/wp-content/uploads/2023/02/phyton-ile-neler-yapilabilir.jpg'
    msg_id = await message.answer_photo(photo=photo_url, reply_markup=main_kb(message.from_user.id),
                                        caption='Моя <u>отформатированная</u> подпись к <b>фото</b>')
    print(msg_id.photo[-1].file_id)

Все получилось. Выбирайте формат отправки файла, который вам удобен.

Теперь давайте рассмотрим методы edit_caption (перезапись описания к медиа) и edit_media. Редактирование медиа и описания edit_caption будет невозможным, если вы привязали к своему медиа-сообщению текстовую клавиатуру.

То есть, допустим, вы отправляете видео с подписью и текстовой клавиатурой. Проблем никаких нет, все отправляется, но при попытке вызвать метод edit_caption или edit_media вы получите ошибку «Невозможно изменить сообщение».

В данном контексте возможны следующие варианты решения:

Менять описание, клавиатуру или медиа, когда вы не вызывали изначально никакой клавиатуры или вызывали с медиа инлайн-клавиатуру (тогда проблем не будет).

Сохранять объект отправленного сообщения с медиа (достаем ID медиа и описание, копируем, удаляем медиа и делаем повторную отправку). Попробуем:

python
@start_router.message(Command('send_video'))
async def cmd_start(message: Message, state: FSMContext):
    video_file = FSInputFile(path=os.path.join(all_media_dir, 'IMG_3998.MP4'))
    msg = await message.answer_video(video=video_file, reply_markup=main_kb(message.from_user.id),
                                     caption='Моя отформатированная подпись к файлу')
    await asyncio.sleep(2)
    await msg.edit_caption(caption='Новое описание к моему видео.')

Получаем ошибку:

python
aiogram.exceptions.TelegramBadRequest: Telegram server says - Bad Request: message can't be edited

Просто уберем клавиатуру и повторим попытку:

python
@start_router.message(Command('send_video'))
async def cmd_start(message: Message, state: FSMContext):
    video_file = FSInputFile(path=os.path.join(all_media_dir, 'IMG_3998.MP4'))
    msg = await message.answer_video(video=video_file, caption='Моя отформатированная подпись к файлу')
    await asyncio.sleep(2)
    await msg.edit_caption(caption='Новое описание к моему видео.')

И все прекрасно отрабатывает (с инлайн клавиатурой так же все будет корректно работать).

Теперь рассмотрим, как обойти проблему с невозможностью изменить описание при наличии текстовой клавиатуры:

python
@start_router.message(Command('send_video'))
async def cmd_start(message: Message, state: FSMContext):
    video_file = FSInputFile(path=os.path.join(all_media_dir, 'IMG_3998.MP4'))
    msg = await message.answer_video(video=video_file, reply_markup=main_kb(message.from_user.id),
                                     caption='Моя отформатированная подпись к файлу')
    await asyncio.sleep(2)
    await message.answer_video(video=msg.video.file_id, caption='Новое описание к тому же видосу',
                               reply_markup=main_kb(message.from_user.id))
    await msg.delete()

Обратите внимание: метод edit_caption также перезаписывает клавиатуру. То есть, если была инлайн-клавиатура с медиа-сообщением, то в случае, если не передадите в edit_caption reply_markup, клавиатура удалится.

Метод edit_media

Этот метод принимает один обязательный аргумент: media. Там должен быть один из классов: InputMediaAnimation, InputMediaDocument, InputMediaAudio, InputMediaPhoto или InputMediaVideo. Импортируются все они из aiogram.types.

Есть интересный момент: можно без проблем заменять один тип файла на другой. К примеру, было фото с описанием, а вместо него будет видео с описанием. Все зависит от вашей фантазии.

Записывается по такой конструкции на примере видео:

python
new_video_file = FSInputFile(path=os.path.join(all_media_dir, 'IMG_4044.MP4'))
media = InputMediaVideo(media=new_video_file, caption='Новое видео и у него новое описание.')

Обратите внимание, что внутри можно передать описание, но тут нужно быть внимательным. Описание передается как аргумент к InputMediaVideo.

Давайте посмотрим на конкретном примере, чтобы было понятно:

python
@start_router.message(Command('send_video'))
async def cmd_start(message: Message, state: FSMContext):
    video_file = FSInputFile(path=os.path.join(all_media_dir, 'IMG_3998.MP4'))
    msg_1 = await message.answer_video(video=video_file,
                                       caption='Моя <b>отформатированная подпись</b> к файлу')
    await asyncio.sleep(2)
    await msg_1.edit_caption(caption='Новое описание к видео 1')

    await asyncio.sleep(2)
    new_video_file = FSInputFile(path=os.path.join(all_media_dir, 'IMG_4044.MP4'))
    await msg_1.edit_media(media=InputMediaVideo(media=new_video_file, caption='Новое видео и у него новое описание.'),
                           reply_markup=inline_kb())

Тут скомбинировали answer_video, edit_caption и edit_media. Советую сохранить этот код где-то. Такую информацию в контексте aiogram 3 вряд ли где-то найдете.

Пример отправки голосового и видео сообщения:

python
@start_router.message(Command('send_voice'))
async def cmd_start(message: Message, state: FSMContext):
    await message.answer_voice(voice=FSInputFile(
        path=os.path.join(all_media_dir, 'krasivyie-snyi-nevinnost-zvezdnyiy-fon-zvukovyie-effektyi-43378.mp3')))

@start_router.message(Command('send_video_note'))
async def cmd_start(message: Message, state: FSMContext):
    await message.answer_video_note(video_note=FSInputFile(path=os.path.join(all_media_dir, 'IMG_4044.MP4')))

Для того чтобы видео сообщение отправилось кругляшом – оно должно быть изначально квадратным, но, из личного опыта, если хотите имитацию делать видео-сообщений – лучше их записать отдельно и через админку сохранить.

Вот небольшой пример. Без админки, надеюсь поймете в чем смысл.

python
@start_router.message(F.video_note)
async def cmd_start(message: Message, state: FSMContext):
    print(message.video_note.file_id)

Понятное дело, что тут должен быть FSM и подключенная база данных, но до этого дойдем. Сейчас просто «поймаем» через магический фильтр F.video_note видео-сообщение и выведем его идентификатор в консоль.

Получился такой ID:

shell
DQACAgIAAxkBAAICKGZspGExG2ZPTe6cxgrHFgl9V8caAALvSgACGKhoS8XEd0xdU4AKNQQ

Отправляем:

python
@start_router.message(Command('send_video_note'))
async def cmd_start(message: Message, state: FSMContext):
    await message.answer_video_note(video_note="DQACAgIAAxkBAAICKGZspGExG2ZPTe6cxgrHFgl9V8caAALvSgACGKhoS8XEd0xdU4AKNQQ")

Видим, что видео-сообщение успешно отправлено.

Contacts: teffal@mail.ru