Appearance
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")
Видим, что видео-сообщение успешно отправлено.