Как отправить фотографию в бота телеграмм aiogram
Создайте объект InputFile , в котором хранится путь к файлу и воспользуйтесь методом send_photo() :
photo = InputFile("files/test.png") await bot.send_photo(chat_id=message.chat.id, photo=photo)
Отслеживать
ответ дан 4 июл 2021 в 14:43
2,818 1 1 золотой знак 11 11 серебряных знаков 19 19 бронзовых знаков
а чем ваш ответ принципиально отличается от предыдущего ответа?
4 июл 2021 в 17:28
Мой ответ более соответствует фреймворку aiogram. Аргумент photo должен принимать объект InputFile.
4 июл 2021 в 17:53
Там происходит то что человек водит код который генерируется ,как сделать чтобы открывалось именно то фото которое до этого сгенерировалось с этим кодом
4 июл 2021 в 19:49
photo = InputFile(f’./data/tmp/emulator_.png') await bot.send_photo(chat_id=message.chat.id, photo=photo) Таким способом можно будет вывести
4 июл 2021 в 19:49
InputFile(f’data/tmp/emulator_.png')
4 июл 2021 в 19:59
photo = open('Путь к фото', 'rb') await bot.send_photo(chat_id, photo)
Отслеживать
ответ дан 4 июл 2021 в 14:01
665 1 1 золотой знак 3 3 серебряных знака 18 18 бронзовых знаков
Не понял, работает же
4 июл 2021 в 14:40
Про with open . главное не забывать
10 июн 2022 в 21:02
with open('Путь к фото', 'rb') as photo: await bot.send_photo(chat_id=message.chat.id, photo)
Отслеживать
67.9k 216 216 золотых знаков 77 77 серебряных знаков 219 219 бронзовых знаков
ответ дан 30 янв в 12:04
Длиато Райкаут Длиато Райкаут
Нужно импортировать InputFile
from aiogram.types import InputFile
Затем создать переменную, через которую указываем путь к фото и указываем нашу переменную photo при отправке через bot.send_photo()
photo = InputFile("files/any_picture.png") await bot.send_photo(chat_id=message.chat.id, photo)
Работа с сообщениями¶
В этой главе мы разберёмся, как применять различные типы форматирования к сообщениям и работать с медиафайлами.
Текст¶
Обработка текстовых сообщений — это, пожалуй, одно из важнейших действий у большинства ботов. Текстом можно выразить практически что угодно и при этом подавать информацию хочется красиво. В распоряжении у разработчика имеется три способа разметки текста: HTML, Markdown и MarkdownV2. Наиболее продвинутыми из них считаются HTML и MarkdownV2, «классический» Markdown поддерживает меньше возможностей и более не используется в aiogram.
Прежде, чем мы рассмотрим способы работы с текстом в aiogram, необходимо упомянуть важное отличие aiogram 3.x от 2.x: в «двойке» по умолчанию обрабатывались только текстовые сообщения, а в «тройке» — любого типа. Если точнее, вот как теперь надо принимать исключительно текстовые сообщения:
# было (декоратором) @dp.message_handler() async def func_name(. ) # было (функцией-регистратором) dp.register_message_handler(func_name) # стало (декоратором) from aiogram import F @dp.message(F.text) async def func_name(. ) # стало (функцией-регистратором) dp.message.register(func_name, F.text)
Про «магический фильтр» F мы поговорим в другой главе.
Форматированный вывод¶
За выбор форматирования при отправке сообщений отвечает аргумент parse_mode , например:
from aiogram import types from aiogram.filters import Command # Если не указать фильтр F.text, # то хэндлер сработает даже на картинку с подписью /test, # но пока нам это не важно и рассматриваем только текстовые сообщения @dp.message(Command("test")) async def any_message(message: types.Message): await message.answer("Hello, world!", parse_mode="HTML") await message.answer("Hello, *world*\!", parse_mode="MarkdownV2")
Если в боте повсеместно используется определённое форматирование, то каждый раз указывать аргумент parse_mode довольно накладно. К счастью, в aiogram можно передать необходимый тип прямо в объект Bot, а если в каком-то конкретном случае нужно обойтись без этих ваших разметок, то просто укажите parse_mode=None :
bot = Bot(token="123:abcxyz", parse_mode="HTML") # где-то в функции. await message.answer("Сообщение с HTML-разметкой") await message.answer("Сообщение без какой-либо разметки", parse_mode=None)
Экранирование ввода¶
Нередко бывают ситуации, когда окончательный текст сообщения бота заранее неизвестен и формируется исходя из каких-то внешних данных: имя пользователя, его ввод и т.д. Напишем хэндлер на команду /name , который будет отвечать пользователю текстом, указанным после команды, например, /name Иван Иванов :
from aiogram.filters import CommandObject @dp.message(Command("name")) async def cmd_name(message: types.Message, command: CommandObject): if command.args: await message.answer(f"Привет, command.args> ") else: await message.answer("Пожалуйста, укажи своё имя после команды /name!")
Почему мы не воспользовались просто message.text ? В противном случае бот бы ответил: «Привет, /name Иван Иванов», а нам нужен только текст после команды. Если вы используете встроенный фильтр Command, то можно добавить в хэндлер аргумент command с типом CommandObject и достать оттуда текст после команды, который aiogram уже распарсил за вас. Если после команды ничего не указано, то command.args будет иметь значение None .
И, вроде бы, всё хорошо, но тут приходит хитрый юзер и пишет /name . В этом случае бот не ответит, а в консоли появится ошибка:
aiogram.exceptions.TelegramBadRequest: Bad Request: can’t parse entities: Unsupported start tag «славик777» at byte offset 17
Что же делать? К счастью, проблема решается просто: экранированием. Для этого в модуле html (в markdown аналогично) есть метод quote() для экранирования, а также различные методы для форматирования: bold() , italic() , link() и т.д.
Исправим код, чтобы всё заработало:
# новый импорт! from aiogram import html # В функции cmd_name await message.answer(f"Привет, html.bold(html.quote(command.args))>", parse_mode="HTML")
Подробнее о различных способах форматирования и поддерживаемых тегах можно узнать в документации Bot API.
Сохранение форматирования¶
Представим, что бот должен получить форматированный текст от пользователя и добавить туда что-то своё, например, отметку времени. Напишем простой код:
# новый импорт! from datetime import datetime @dp.message(F.text) async def echo_with_time(message: types.Message): # Получаем текущее время в часовом поясе ПК time_now = datetime.now().strftime('%H:%M') # Создаём подчёркнутый текст added_text = html.underline(f"Создано в time_now>") # Отправляем новое сообщение с добавленным текстом await message.answer(f"message.text>\n\nadded_text>", parse_mode="HTML")
Мда, что-то пошло не так, почему сбилось форматирование исходного сообщения? Это происходит из-за того, что message.text возвращает просто текст, без каких-либо оформлений. Чтобы получить текст в нужном форматировании, воспользуемся альтернативными свойствами: message.html_text или message.md_text . Сейчас нам нужен первый вариант. Заменяем в примере выше message.text на message.html_text и получаем корректный результат:
Работа с entities¶
Telegram, на самом деле, очень много обработки делает вместо пользователя, сильно упрощая жизнь. Например, некоторые сущности, типа e-mail, номера телефона, юзернейма и др. можно не доставать регулярными выражениями, а извлечь напрямую из объекта Message и поля entities , содержащего массив объектов типа MessageEntity. В качестве примера напишем хэндлер, который извлекает ссылку, e-mail и моноширинный текст из сообщения (по одной штуке).
Здесь кроется важный подвох. Telegram возвращает не сами значения, а их начало в тексте и длину. Более того, текст считается в символах UTF-8, а entities работают с UTF-16, из-за этого, если просто взять позицию и длину, то при наличии UTF-16 символов (например, эмодзи) ваш обработанный текст просто съедет.
Лучше всего это демонстрирует пример ниже. На скриншоте первый ответ бота есть результат парсинга «в лоб», а второй — результат применения аиограмного метода extract_from() над entity. На вход ему передаётся весь исходный текст:
@dp.message(F.text) async def extract_data(message: types.Message): data = "url": "", "email": "", "code": "" > entities = message.entities or [] for item in entities: if item.type in data.keys(): # Неправильно # data[item.type] = message.text[item.offset : item.offset+item.length] # Правильно data[item.type] = item.extract_from(message.text) await message.reply( "Вот что я нашёл:\n" f"URL: html.quote(data['url'])>\n" f"E-mail: html.quote(data['email'])>\n" f"Пароль: html.quote(data['code'])>" )
Медиафайлы¶
Отправка файлов¶
Помимо обычных текстовых сообщений Telegram позволяет обмениваться медиафайлами различных типов: фото, видео, гифки, геолокации, стикеры и т.д. У большинства медиафайлов есть свойства file_id и file_unique_id . Первый можно использовать для повторной отправки одного и того же файла много раз, причём отправка будет мгновенной, т.к. сам файл уже лежит на серверах Telegram. Это самый предпочтительный способ.
К примеру, следующий код заставит бота моментально ответить пользователю той же гифкой, что была прислана:
@dp.message(F.animation) async def echo_gif(message: types.Message): await message.reply_animation(message.animation.file_id)
Всегда используйте правильные file_id!
Бот должен использовать для отправки только те file_id , которые получил напрямую сам, например, в личке от пользователя или «увидев» медиафайл в группе/канале. При этом, если попытаться использовать file_id от другого бота, то это может сработать, но через какое-то время вы получите ошибку wrong url/file_id specified. Поэтому — только свои file_id !
В отличие от file_id , идентификатор file_unique_id нельзя использовать для повторной отправки или скачивания медиафайла, но зато он одинаковый у всех ботов для конкретного медиа. Нужен file_unique_id обычно тогда, когда нескольким ботам требуется знать, что их собственные file_id односятся к одному и тому же файлу.
Если файл ещё не существует на сервере Telegram, бот может загрузить его тремя различными способами: как файл в файловой системе, по ссылке и напрямую набор байтов. Для ускорения отправки и в целом для более бережного отношения к серверам мессенджера, загрузку (upload) файлов Telegram правильнее производить один раз, а в дальнейшем использовать file_id , который будет доступен после первой загрузки медиа.
В aiogram 3.x присутствуют 3 класса для отправки изображений — FSInputFile , BufferedInputFile , URLInputFile , с ними можно ознакомиться в документации.
Рассмотрим простой пример отправки изображений всеми различными способами:
from aiogram.types import FSInputFile, URLInputFile, BufferedInputFile @dp.message(Command('images')) async def upload_photo(message: types.Message): # Сюда будем помещать file_id отправленных файлов, чтобы потом ими воспользоваться file_ids = [] # Чтобы продемонстрировать BufferedInputFile, воспользуемся "классическим" # открытием файла через `open()`. Но, вообще говоря, этот способ # лучше всего подходит для отправки байтов из оперативной памяти # после проведения каких-либо манипуляций, например, редактированием через Pillow with open("buffer_emulation.jpg", "rb") as image_from_buffer: result = await message.answer_photo( BufferedInputFile( image_from_buffer.read(), filename="image from buffer.jpg" ), caption="Изображение из буфера" ) file_ids.append(result.photo[-1].file_id) # Отправка файла из файловой системы image_from_pc = FSInputFile("image_from_pc.jpg") result = await message.answer_photo( image_from_pc, caption="Изображение из файла на компьютере" ) file_ids.append(result.photo[-1].file_id) # Отправка файла по ссылке image_from_url = URLInputFile("https://picsum.photos/seed/groosha/400/300") result = await message.answer_photo( image_from_url, caption="Изображение по ссылке" ) file_ids.append(result.photo[-1].file_id) await message.answer("Отправленные файлы:\n"+"\n".join(file_ids))
Скачивание файлов¶
Помимо переиспользования для отправки, бот может скачать медиа к себе на компьютер/сервер. Для этого у объекта типа Bot есть метод download() . В примерах ниже файлы скачиваются сразу в файловую систему, но никто не мешает вместо этого сохранить в объект BytesIO в памяти, чтобы передать в какое-то приложение дальше (например, pillow).
@dp.message(F.photo) async def download_photo(message: types.Message, bot: Bot): await bot.download( message.photo[-1], destination=f"/tmp/message.photo[-1].file_id>.jpg" ) @dp.message(F.sticker) async def download_sticker(message: types.Message, bot: Bot): await bot.download( message.sticker, # для Windows пути надо подправить destination=f"/tmp/message.sticker.file_id>.webp" )
В случае с изображениями мы использовали не message.photo , а message.photo[-1] , почему? Фотографии в Telegram в сообщении приходят сразу в нескольких экземплярах; это одно и то же изображение с разным размером. Соответственно, если мы берём последний элемент (индекс -1), то работаем с максимально доступным размером фото.
Скачивание больших файлов
Боты, использующие Telegram Bot API, могут скачивать файлы размером не более 20 мегабайт. Если вы планируете скачивать/заливать большие файлы, лучше рассмотрите библиотеки, взаимодействующие с Telegram Client API, а не с Telegram Bot API, например, Telethon или Pyrogram.
Немногие знают, но Client API могут использовать не только обычные аккаунты, но ещё и боты.
А начиная с Bot API версии 5.0, можно использовать собственный сервер Bot API для работы с большими файлами.
Сервисные (служебные) сообщения¶
Сообщения в Telegram делятся на текстовые, медиафайлы и служебные (они же — сервисные). Настало время поговорить о последних.
Несмотря на то, что они выглядят необычно и взаимодействие с ними ограничено, это всё ещё сообщения, у которых есть свои айдишники и даже владелец. Стоит отметить, что спектр применения сервисных сообщений с годами менялся и сейчас, скорее всего, ваш бот с ними работать не будет, либо только удалять.
Не будем сильно углубляться в детали и рассмотрим один конкретный пример: отправка приветственного сообщения вошедшему участнику. У такого служебного сообщения будет content_type равный «new_chat_members», но вообще это объект Message, у которого заполнено одноимённое поле.
@dp.message(F.new_chat_members) async def somebody_added(message: types.Message): for user in message.new_chat_members: # проперти full_name берёт сразу имя И фамилию # (на скриншоте выше у юзеров нет фамилии) await message.reply(f"Привет, user.full_name>")
Важно помнить, что message.new_chat_members является списком, потому что один пользователь может добавить сразу нескольких участников. Также не надо путать поля message.from_user и message.new_chat_members . Первое — это субъект, т.е. тот, кто совершил действие. Второе — это объекты действия. Т.е. если вы видите сообщение вида «Анна добавила Бориса и Виктора», то message.from_user — это информация об Анне, а список message.new_chat_members содержит информацию о Борисе с Виктором.
Не стоит целиком полагаться на сервисные сообщения!
У служебных сообщений о добавлении (new_chat_members) и выходе (left_chat_member) есть одна неприятная особенность: они ненадёжны, т.е. они могут не создаваться вообще.
К примеру, сообщение о new_chat_members перестаёт создаваться при ~10k участников в группе, а left_chat_member уже при 50 (но при написании этой главы я столкнулся с тем, что в одной из групп left_chat_member не появился и при 9 участниках. А через полчаса там же появился при выходе другого человека).
С выходом Bot API 5.0 у разработчиков появился гораздо более надёжный способ видеть входы/выходы участников в группах любого размера, а также в каналах. Но об этом поговорим в другой раз.
Бонус: прячем ссылку в тексте¶
Бывают ситуации, когда хочется отправить длинное сообщение с картинкой, но лимит на подписи к медиафайлам составляет всего 1024 символа против 4096 у обычного текстового, а вставлять внизу ссылку на медиа — выглядит некрасиво. Более того, когда Telegram делает предпросмотр ссылок, он берёт первую из них и считывает метатеги, в результате сообщение может отправиться не с тем превью, которое хочется увидеть.
Для решения этой проблемы ещё много лет назад придумали подход со «скрытыми ссылками» в HTML-разметке. Суть в том, что можно поместить ссылку в пробел нулевой ширины и вставить всю эту конструкцию в начало сообщения. Для наблюдателя в сообщении никаких ссылок нет, а сервер Telegram всё видит и честно добавляет предпросмотр.
Разработчики aiogram для этого даже сделали специальный вспомогательный метод hide_link() :
# новый импорт! from aiogram.utils.markdown import hide_link @dp.message(Command("hidden_link")) async def cmd_hidden_link(message: types.Message): await message.answer( f"hide_link('https://telegra.ph/file/562a512448876923e28c3.png')>" f"Документация Telegram: *существует*\n" f"Пользователи: *не читают документацию*\n" f"Груша:" )
На этом всё. До следующих глав! Ставьте лайки, подписывайтесь, прожимайте колокольчик
Урок 2. Медиа, разметка, эмоджи и щепотка логирования
А также воспользуемся логированием, дабы посмотреть, что происходит под капотом.
Если вы собираетесь работать с отправкой эмоджи, и делать это не по их коду, например ‘\U0001F31A’ , а пользоваться наглядными названиями как :new_moon_with_face: , то необходимо установить библиотеку emoji , для этого воспользуемся командой pip install emoji (или командой python3.6 -m pip install emoji , если у вас установлено несколько интерпретаторов питона).
Зачем хранить айди файлов, которые мы отправляем?
Например, мы хотим добавить в нашего бота возможность отправлять картинки. Скорее всего, эти картинки будут отправлены неоднократно. Поэтому Телеграм даёт нам возможность отправлять файлы по айди. Вероятно, вы уже замечали, как это работает. Например, вы отправляете фото другу, оно отправляется пару секунд, а потом решаете отправить это же фото подруге, выбираете, жмете отправить, а оно тут же отправляется — это происходит потому, что ваш телеграм клиент запомнил, что вы уже отправляли это фото, поэтому в следующий раз шлёт его по айди. Боты могут то же самое (и даже лучше). Отправляя файл по айди мы в первую очередь ускоряем отправку сообщения пользователю, так как медиа уже загружено на сервер телеграм.
Итак, допустим, наш бот будет отправлять различные типы медиа. В этом уроке мы рассмотрим отправку изображений по одному, медиагруппами, отправку аудиосообщений, файлов, видео и видео в кружочке. Предполагая, что все файлы, отсылаемые нашим ботом, будут когда-либо отправлены пользователю, сделаем выгрузку данных заранее. Для того, чтобы не вносить данные в базу данных вручную, автоматизируем этот процесс. В данном случае нам подойдет база данных на движке SQLite. Для новичков скажу, что этот тип баз данных по умолчанию поддерживается языком Python (проверить это можно сделав в интерпретаторе import sqlite3 ). В своих проектах вы можете пользоваться любимыми ORM или писать чистые SQL запросы (вам всего доброго, хорошего настроения и здоровья). Мой выбор пал на SQLAlchemy. Так как работа с данной библиотекой — не тема наших уроков, я не буду углубляться в подробности и поверхностно расскажу, что происходит коде, относящемся к работе с базой данных (далее — БД). Напоминаю, что весь исходный код, продемонстрированный в уроке, доступен по ссылке. Отмечу, что база данных намерено представлена в максимально простом виде — в целях наглядности.
Ещё вам необходимо знать, что каждый бот видит разные айди у всех медиа файлов. По айди, который получил один бот, другой бот не сможет отправить ничего, а при попытке сделать это получит ошибку.
Наконец-то код!
Для начала создаем модель таблицы для нашей базы данных. Файл db_map.py:
from sqlalchemy import Column, Integer, String from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class MediaIds(Base): __tablename__ = 'Media ids' primary_key=True) file_id = Column(String(255)) filename = Column(String(255))
Теперь загружаем в Телеграм файлы и сохраняем возвращаемые айди в базу данных. Для этого я написал небольшой скрипт, который доступен по ссылке. Если будете исполнять его на своём компьютере, можете обратить внимание на то самое логирование библиотеки aiogram.
Вот результат выполнения скрипта:
Представим, что в базе данных было ещё несколько полей, по которым вы в коде будете получать необходимые данные. Теперь переходим к основному коду бота. Так как создание логики запросов к БД — не тема этих уроков, допустим, что вы получили данные при старте программы и записали их в переменные (строки кода 17 — 26).
Создаем хэндлер команд /start и /help :
@dp.message_handler(commands=['start']) async def process_start_command(message: types.Message): await message.reply('Привет!\nИспользуй /help, ' 'чтобы узнать список доступных команд!') @dp.message_handler(commands=['help']) async def process_help_command(message: types.Message): msg = text(bold('Я могу ответить на следующие команды:'), '/voice', '/photo', '/group', '/note', '/file, /testpre', sep='\n') await message.reply(msg, parse_mode=ParseMode.MARKDOWN)
Обращу внимание читателя на то, что в ответе на команду /help мы воспользовались новыми методами: text и bold . Эти на первый взгляд простые функции сильно упрощают работу с генерацией текста. Первая склеивает в одно целое все передаваемые ей строчки, перемежая сепаратором (по умолчанию пробел, в таких случаях переменную sep вообще не нужно указывать), а вторая обрамляет входящую строчку метками для жирного текста (в данном примере используется разметка Markdown, поэтому это символы * . Для HTML теги будут другие). По аналогии работают методы italic , code и pre для Markdown и аналогичные для HTML. А чтобы Телеграм понял, что эти символы здесь не просто так, но обозначают разметку, мы явно указываем тип этой самой разметки: parse_mode=ParseMode.MARKDOWN . Напомню, что все необходимые импорты можно посмотреть по ссылке.
Затем добавляем обработчики всех остальных команд
О каждой по отдельности (за одно разберем эмоджи):
Отправка аудио + ответ на определенное сообщение:
@dp.message_handler(commands=['voice']) async def process_voice_command(message: types.Message): await bot.send_voice(message.from_user.id, VOICE, reply_to_message_id=message.message_id)
Параметр reply_to_message_id отвечает за ответ на конкретное сообщение — айди можно указать любой доступный в этом чате (хоть просто поставить единицу). В данном случае берем айди сообщения, которое вызвало эту функцию.
Отправка фото с комментарием + эмоджи:
@dp.message_handler(commands=['photo']) async def process_photo_command(message: types.Message): caption = 'Какие глазки! :eyes:' await bot.send_photo(message.from_user.id, CAT_BIG_EYES, caption=emojize(caption), reply_to_message_id=message.message_id)
Тут мы дополнительно передаем данные в параметр caption , предварительно преобразовав текст, чтобы получить эмоджи из его кода. Теперь от простого переходим к более сложному.
Отправка медиагруппы (где смешались кони, люди фото и видео):
@dp.message_handler(commands=['group']) async def process_group_command(message: types.Message): media = [InputMediaVideo(VIDEO, 'ёжик и котятки')] for photo_id in KITTENS: media.append(InputMediaPhoto(photo_id)) await bot.send_media_group(message.from_user.id, media)
Внимание! На момент публикации заметки при использовании релизной версии библиотеки невозможно отправить медиагруппу представленным выше способом из-за ошибки в коде. Недочёт исправлен в этом коммите. Версия 1.1, которая устанавливается через pip, уже несет в себе это исправление.
Итак, что же произошло в коде? Для начала создаем массив и кладем в него один элемент типа InputMediaVideo . Первый входной параметр — само видео (в данном случае айди), а второй элемент — caption (его передавать не обязательно). Дальше в цикле заполняем массив элементами типа InputMediaPhoto . Их создаем без подписи, тем самым делаем так, что всей медиагруппе принадлежит комментарий, оставленный к первому медиа. Затем просто в нужный метод передаем массив элементов. Можно, конечно, ещё было передать сразу медиагруппу, сделав MediaGroup(media) , но так как библиотека делает это сама, то какой смысл писать лишний код и делать лишний импорт?
Отправка видеозаметки (видео в кружочке):
@dp.message_handler(commands=['note']) async def process_note_command(message: types.Message): user_id = message.from_user.id await bot.send_chat_action(user_id, ChatActions.RECORD_VIDEO_NOTE) await asyncio.sleep(1) # конвертируем видео и отправляем его пользователю await bot.send_video_note(message.from_user.id, VIDEO_NOTE)
Представим, что вы собираетесь отправить пользователю видео в кружочке, которое ещё ни разу не было отправлено. Так как загрузка займет какое-то время, можно передать пользователю информацию, что бот получил команду и в данный момент обрабатывает её. Для этого существует метод sendChatAction . После отправки этого уведомления, приложение будет показывать пользователю, что бот сейчас что-то делает. Телеграм уберет этот статус как только бот отправит следующее сообщение, но не позже, чем через пять секунд. Для имитации каких-то действий со стороны бота просто выставляем ожидание в секунду.
Отправка файла:
@dp.message_handler(commands=['file']) async def process_file_command(message: types.Message): user_id = message.from_user.id await bot.send_chat_action(user_id, ChatActions.UPLOAD_DOCUMENT) await asyncio.sleep(1) # скачиваем файл и отправляем его пользователю await bot.send_document(user_id, TEXT_FILE, caption='Этот файл специально для тебя!')
Тут как и в предыдущем примере предполагаем, что файл нужно ещё откуда-то достать или, может, сгенерировать, поэтому снова отправляем chatAction , выжидаем драматичную паузу и отправляем сам файл.
Преформатированный текст:
@dp.message_handler(commands=['testpre']) async def process_testpre_command(message: types.Message): message_text = pre(emojize('''@dp.message_handler(commands=['testpre']) async def process_testpre_command(message: types.Message): message_text = pre(emojize('Ха! Не в этот раз :smirk:')) await bot.send_message(message.from_user.id, message_text)''')) await bot.send_message(message.from_user.id, message_text, parse_mode=ParseMode.MARKDOWN)
Так как разметка тут немного шалит, приложу ещё и скриншот кода с более понятной подсветкой синтаксиса: Тут мы отправляем преформатированный текст. Обычно такая разметка необходима при отправке блоков кода. По сути, это тот же моноширинный шрифт как и при использовании code (рассмотрим его ниже), однако pre работает для многострочных вставок и автоматически отделяется переносом строки до и после. В качестве примера отправим данную же функцию с её хэндлером. Получаем такой результат:
Ну и напоследок немного улучшим знания, полученные в предыдущем уроке и закрепим их на деле:
Оставим для нерадивого пользователя пару хэндлеров для нежданных сообщений:
@dp.message_handler() async def echo_message(msg: types.Message): await bot.send_message(msg.from_user.id, msg.text) @dp.message_handler(content_types=ContentType.ANY) async def unknown_message(msg: types.Message): message_text = text(emojize('Я не знаю, что с этим делать :astonished:'), italic('\nЯ просто напомню,'), 'что есть', code('команда'), '/help') await msg.reply(message_text, parse_mode=ParseMode.MARKDOWN)
И если первый должен быть понятен ещё с первого урока, то о втором расскажу поподробнее:
Так как по умолчанию в пустой хэндлер прилетают только текстовые сообщения, то наш бот будет игнорировать все остальные типы сообщений. Специально для этого мы создаем «уловитель» этих самых «всех остальных типов» сообщений, передавая content_types=ContentType.ANY . Обращу внимание читателя на то, что данная библиотека работает так, что сообщение передается в первый подходящий по условиям message_handler , поэтому необходимо соблюдать логический порядок. И именно по этой причине нельзя ставить хэндлер команд выше хэндлера текстовых сообщений — из-за того, что команды — это тоже текстовые сообщения, в вашей программе будет нарушена логика.
Домашнее задание
В качестве домашнего задания предлагаю вам поэкспериментировать и выполнить следующие действия:
- отправить видео;
- отправить геолокацию;
- отправить аудиофайл (музыку);
- отправить группу из фото (видео опционально), где у каждого элемента будет свой уникальный caption ;
- отправить фото по ссылке со внешнего источника — просто передайте не файл, не айди, а ссылку на файл;
- поставить хэндлер текста выше хэндлера команд, посмотреть, что будет.
sendPhoto#
Returns: Message class aiogram.methods.send_photo. SendPhoto ( * , chat_id : int | str , photo : InputFile | str , message_thread_id : int | None = None , caption : str | None = None , parse_mode : str | None = sentinel.UNSET_PARSE_MODE , caption_entities : List [ MessageEntity ] | None = None , has_spoiler : bool | None = None , disable_notification : bool | None = None , protect_content : bool | None = sentinel.UNSET_PROTECT_CONTENT , reply_to_message_id : int | None = None , allow_sending_without_reply : bool | None = None , reply_markup : InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove | ForceReply | None = None , ** extra_data : Any ) [source] # Use this method to send photos. On success, the sent aiogram.types.message.Message is returned. Source: https://core.telegram.org/bots/api#sendphoto chat_id : int | str # Unique identifier for the target chat or username of the target channel (in the format @channelusername ) photo : InputFile | str # Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. The photo must be at most 10 MB in size. The photo’s width and height must not exceed 10000 in total. Width and height ratio must be at most 20. More information on Sending Files » message_thread_id : int | None # Unique identifier for the target message thread (topic) of the forum; for forum supergroups only caption : str | None # Photo caption (may also be used when resending photos by file_id), 0-1024 characters after entities parsing parse_mode : str | None # Mode for parsing entities in the photo caption. See formatting options for more details. caption_entities : List [ MessageEntity ] | None # A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode has_spoiler : bool | None # Pass True if the photo needs to be covered with a spoiler animation disable_notification : bool | None # Sends the message silently. Users will receive a notification with no sound. protect_content : bool | None # Protects the contents of the sent message from forwarding and saving reply_to_message_id : int | None # If the message is a reply, ID of the original message allow_sending_without_reply : bool | None # Pass True if the message should be sent even if the specified replied-to message is not found reply_markup : InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove | ForceReply | None # Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
Usage#
As bot method#
result: Message = await bot.send_photo(. )
Method as object#
- from aiogram.methods.send_photo import SendPhoto
- alias: from aiogram.methods import SendPhoto
With specific bot#
result: Message = await bot(SendPhoto(. ))