Как изменить сообщение aiogram

Работа с сообщениями

Работа с сообщениями¶

О совместимости версий

Код в главах сейчас использует aiogram 3.0 beta3. Возможна несовместимость с другими версиями.

В этой главе мы разберёмся, как применять различные типы форматирования к сообщениям и работать с медиафайлами.

Текст¶

Обработка текстовых сообщений — это, пожалуй, одно из важнейших действий у большинства ботов. Текстом можно выразить
практически что угодно и при этом подавать информацию хочется красиво. В распоряжении у разработчика имеется три способа
разметки текста: 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)

# стало (декоратором)
@dp.message(content_types=types.ContentType.TEXT)
# или 
@dp.message(content_types="text")
async def func_name(...)

# стало (функцией-регистратором)
dp.message.register(func_name, content_types=types.ContentType.TEXT) # или "text"

Форматированный вывод¶

За выбор форматирования при отправке сообщений отвечает аргумент parse_mode, например:

from aiogram import types

# Если не указать фильтр content_types, 
# то хэндлер сработает даже на картинку с подписью /test,
# но пока нам это не важно и рассматриваем только текстовые сообщения
@dp.message(commands=["test"])
async def any_message(message: types.Message):
    await message.answer("Hello, <b>world</b>!", parse_mode="HTML")
    await message.answer("Hello, *world*!", parse_mode="MarkdownV2")

Hello world с разным форматированием

Если в боте повсеместно используется определённое форматирование, то каждый раз указывать аргумент parse_mode довольно
накладно. К счастью, в aiogram можно передать необходимый тип прямо в объект Bot, а если в каком-то конкретном случае
нужно обойтись без этих ваших разметок, то просто укажите parse_mode=None:

bot = Bot(token="123:abcxyz", parse_mode="HTML")

# где-то в функции...
await message.answer("Сообщение с <u>HTML-разметкой</u>")
await message.answer("Сообщение без <s>какой-либо разметки</s>", parse_mode=None)

Настройка типа разметки по умолчанию

Нередко бывают ситуации, когда окончательный текст сообщения бота заранее неизвестен
и формируется исходя из каких-то внешних данных: имя пользователя, его ввод и т.д.
Напишем хэндлер на команду /name, который будет отвечать пользователю текстом,
указанным после команды, например, /name Иван Иванов:

from aiogram.dispatcher.filters import CommandObject

@dp.message(commands=["name"])
async def cmd_name(message: types.Message, command: CommandObject):
    if command.args:
        await message.answer(f"Привет, <b>{command.args}</b>")
    else:
        await message.answer("Пожалуйста, укажи своё имя после команды /name!")

Почему мы не воспользовались просто message.text? В противном случае бот бы ответил:
«Привет, /name Иван Иванов», а нам нужен только текст после команды. Если вы
используете встроенный фильтр Command (или его алиас commands, как в примере выше),
то можно добавить в хэндлер аргумент command с типом CommandObject, и достать оттуда
текст после команды, который aiogram уже распарсил за вас. Если после команды ничего не указано,
то command.args будет иметь значение None.

Проверим:

Работа команды /name

И, вроде бы, всё хорошо, но тут приходит хитрый юзер и пишет /name <славик777>. В этом случае
бот не ответит, а в консоли появится ошибка:
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")

Работа команды /name

Подробнее о различных способах форматирования и поддерживаемых тегах можно узнать
в документации Bot API.

Сохранение форматирования¶

Представим, что бот должен получить форматированный текст от пользователя и добавить туда что-то
своё, например, отметку времени. Напишем простой код:

# новый импорт!
from datetime import datetime

@dp.message(content_types="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}nn{added_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() над entity. На вход ему передаётся весь исходный текст:

@dp.message(content_types="text")
async def extract_data(message: types.Message):
    data = {
        "url": "<N/A>",
        "email": "<N/A>",
        "code": "<N/A>"
    }
    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(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'])}"
    )

Парсинг entities

Помимо обычных текстовых сообщений Telegram позволяет обмениваться медиафайлами различных типов: фото, видео, гифки,
геолокации, стикеры и т.д. У большинства медиафайлов есть свойства file_id и file_unique_id. Первый можно использовать
для повторной отправки одного и того же файла много раз, причём отправка будет мгновенной, т.к. сам файл уже лежит
на серверах Telegram. Это самый предпочтительный способ.
К примеру, следующий код заставит бота моментально ответить пользователю той же гифкой, что была прислана:

@dp.message(content_types=[types.ContentType.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 односятся
к одному и тому же файлу.

Помимо переиспользования для отправки, бот может скачать медиа к себе на компьютер/сервер. Для этого у объекта типа Bot
есть метод download(). В примерах ниже файлы скачиваются сразу в файловую систему, но никто не мешает
вместо этого сохранить в объект BytesIO в памяти, чтобы передать в какое-то приложение дальше
(например, pillow).

@dp.message(content_types="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(content_types=types.ContentType.STICKER)
async def download_sticker(message: types.Message, bot: Bot):
    await bot.download(
        message.sticker,
        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.
Немногие знают, но Client API могут использовать не только обычные аккаунты, но ещё и
боты.

А начиная с Bot API версии 5.0, можно использовать
собственный сервер Bot API для работы с
большими файлами.

Сервисные (служебные) сообщения¶

Сообщения в Telegram делятся на текстовые, медиафайлы и служебные (они же — сервисные).
Настало время поговорить о последних.

Сервисные сообщения

Несмотря на то, что они выглядят необычно и взаимодействие с ними ограничено, это всё ещё
сообщения, у которых есть свои айдишники и даже владелец. Стоит отметить, что спектр применения
сервисных сообщений с годами менялся и сейчас, скорее всего, ваш бот с ними работать не будет,
либо только удалять.

Не будем сильно углубляться в детали и рассмотрим один конкретный пример: отправка
приветственного сообщения вошедшему участнику. У такого служебного сообщения будет content_type
равный «new_chat_members», но вообще это объект Message, у которого заполнено одноимённое поле.

@dp.message(content_types=types.ContentType.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(commands=["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"Груша:"
    )

Изображение со скрытой ссылкой

На этом всё. До следующих глав!
Ставьте лайки, подписывайтесь, прожимайте колокольчик

aiogram

aiogram

AxeLiX-



AxeLiX


Хочу задать тупой вопрос.
Как изменить сообщение отправленное ботом ранее?
Либо

поменять инлайн — клавиатуру этого сообщения

russian

programming

aiogram

08:23 15.12.2021


3

ответов

Andrey-Yankovsky



Andrey
Yankovsky

message.edit_reply_markup

08:23 15.12.2021

Dentlyk-[10$/240$]



Dentlyk
[10$/240$]

CallbackQuery.message.message_id?

08:27 15.12.2021

AxeLiX-



AxeLiX



Автор вопроса


Dentlyk [10$/240$]

CallbackQuery.message.message_id?

Спасибо огромное!

08:35 15.12.2021

Похожие вопросы

aiogram

aiogram

влад-

влад

screenshot

народ а для бота с аудиторией 0-5000 какой примерно сервачок брать?

programming

aiogram

russian

chat
4

00:06 05.11.2022

aiogram

aiogram

Левый Тип-Тип

Левый Тип
Тип

screenshot

максимально глуппый вопрос наверное…но почему у мя бот на админку реагирует только со 2 раза? каждый раз а на другие сообщение с 1

programming

aiogram

russian

chat
4

03:26 05.11.2022

aiogram

aiogram

Evgene-

Evgene

Всем привет

Вопрос, скорее к самому Telegram bot api, но вдруг кто знает
Столкнулся со следующим

Средствами бота отправляю сообщение в канал

await bot.send_photo(
c…

programming

aiogram

russian

chat
2

04:32 05.11.2022

aiogram

aiogram

Axel-

Axel

а ещё что думаешь?

programming

aiogram

russian

chat
2

08:36 05.11.2022

aiogram

aiogram

Georgy-Karpov

Georgy
Karpov

Вечер добрый, aiogram совместим с schedule?

programming

aiogram

russian

chat
2

16:00 05.11.2022

aiogram

aiogram

chekavo-

chekavo

здравствуйте. Подскажите пожалуйста , как эта реализовать ? Вот эта Меню синяя кнопка.

programming

aiogram

russian

chat
1

01:02 05.11.2022

aiogram

aiogram

🥀⃤Furkus🥀⃤-

🥀⃤Furkus🥀⃤

Можно ли спарсить всех подписчиков канала с помощью бота?

programming

aiogram

russian

chat
1

08:13 05.11.2022

aiogram

aiogram

kindr 🤍-

kindr 🤍

вопрос про прокидывание sqlalchemy сессий через мидлвари. Считаю что это не правильно. Для хайлоад проекта такой вариант не особо проходит, может есть у кого какие то другие и…

programming

aiogram

russian

chat
1

16:34 05.11.2022

aiogram

aiogram

Firuz-

Firuz

Ребят, написал бота. Запустил на сервак. Происходит постоянный рост потребления оперативной памяти. С некоторым интервалом. И причина не в пользователях, которые нагружают бот…

programming

aiogram

russian

chat
3

00:52 04.11.2022

aiogram

aiogram

👨🏻‍🚀Bro_From_Space🚀-

👨🏻‍🚀Bro_From_Space🚀

Подскажите пожалуйста в callback query user id доставать через from_user.id или message.from_user.id?

programming

aiogram

russian

chat
3

22:57 04.11.2022

Смотреть еще

Обновлено: 09.02.2023

Или тебе какие-то алиасы

Да это понятно, я уже гуглил.

забей это в доки docs.aiogram.dev

ну это уже в доках про кнопки, по крайней мере в телетоне это просто делается

Этим сайтом я тоже пользуюсь.

Похожие вопросы по теме IT talks

«назад», то его на предыдущее вытаскивало. допустим несколько пунктов, где находится человек: А: кнопка1 и кнопка2 B: кнопка3 и кнопка «назад» при нажатии на кнопку1 C: кнопка4 и кнопка «назад» при нажатии на кнопку2 D: кнопка5 и кнопка «назад» при нажатии на кнопку3 теперь как сделать, чтобы при нажатии на «назад» в D перемещало в В, а при нажатии этой же кнопки в В или С перемещало в А?

на эту кнопку несколько раз подряд, ловить только первое нажатие?

интернете вообще нет инфы. ERROR — TeleBot: «A request to the Telegram API was unsuccessful. Error code: 400. Description: Bad Request: IMAGE_PROCESS_FAILED»

проверить что человек отсоединился? Будет ли ошибка с каким нибудь кодом?

отправлялся ответ пользователю что картинка не верная? node:52305 UnhandledPromiseRejectionWarning: Error: 400: Bad Request: there is no photo in the request

Не забывайте своевременно обновлять библиотеку командой python3.6 -m pip install aiogram -U ! Урок проводится с использованием версии 1.2.3

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

Для начала стоит понять, в чем основное различие ReplyKeyboardMarkup и InlineKeyboardMarkup :

Переходим к коду

Создадим отдельный модуль с клавиатурами, которые будем использовать в основной программе.

В первую очередь импортируем необходимые нам модули и создаём первую клавиатуру:

При инициализации класса KeyboardButton необходимо передать один обязательный параметр — текст, который пользователь будет отправлять по нажатию на эту кнопку. У объекта класса ReplyKeyboardMarkup есть несколько методов, позволяющих добавить кнопку, в данном случае мы используем add . Таким образом мы получили первую готовую клавиатуру.

Запускаем и проверяем:

Отлично, клавиатура появилась! Но эта одна кнопка с маленьким текстом занимает очень много места. Телеграм позволяет автоматически уменьшить размер, для этого необходимо передать в инициализатор класса ReplyKeyboardMarkup параметру resize_keyboard значение True . Создадим новую клавиатуру:

Мы передали параметр в инициализатор и следом сразу добавили уже существующую кнопку. Отправляем новую версию клавиатуры:

Эти параметры можно комбинировать. Вследствие чего мы получили маленькую клавиатуру, которая скрывается после одной отправки. Но пользователь может снова её открыть нажатием на соответствующую кнопку в поле ввода.

Добавляем больше кнопок

Рассмотрим подробно работу встроенных методов для создания более сложных клавиатур, а именно row , insert и add . Создаём кнопки, которые мы сможем использовать повторно и генерируем несколько разных клавиатур:

Важно: сейчас мы отталкиваемся от того, что клавиатура по умолчанию имеет 3 кнопки в ряд. О том, как изменить это значение, будет в примере с инлайн клавиатурами — этот параметр идентичен для обоих видов.

  • Метод add принимает в себя любое количество кнопок, всегда начинает добавление с новой строки и переносит ряд при достижении значения установленной ширины.
  • Метод row тоже принимает любое количество, но при этом не переносит кнопки на новый ряд, а добавляет всё полученное в одну строчку.
  • Метод insert работает по схеме схожей с add , но только начинает добавлять к последнему ряду. И только если там уже достигнута максимальная ширина, начинает новую строку. Взглянем на него ещё раз при создании следующей клавиатуры.

Отправляем все готовые кнопки и смотрим на результат:

Думаю, здесь достаточно наглядно видно работу методов добавления кнопок в разметку.

И последнее по этому виду клавиатур. ReplyKeyboardMarkup позволяет запросить у пользователя его контакт или локацию. Это те самые два исключения из правила, когда при нажатии кнопки будет отправлено не то, что написано на ней. Их можно отправлять как по одной, так и в составе более сложной клавиатуры. Добавим обе кнопки и посмотрим, что будет (обращу внимание читателя на то, что нельзя одной кнопкой запросить сразу и то и то):

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

Запрос контакта

Запрос текущей геолокации

Данные пользователя

Рассмотрим подробнее последний метод для составления клавиатур — insert . Он похож на метод add , но начинает добавлять кнопки не с нового ряда, а сначала проверяет, заполнен ли до конца последний ряд. И если нет, то сначала добавляет кнопки туда, а переносит строку только при достижении указанного лимита.

Инлайн клавиатуры

Здесь мы делаем проверку прямо в хэндлере. Аргумент func ожидает функцию, которая принимает один параметр (туда передаётся апдейт), и хэндлер срабатывает, если возвращается истина. Этот аргумент можно использовать и в других хэндлерах, мы уже использовали его в уроке по FSM.

Внутри мы можем делать вообще что угодно, так как работаем с обычным питоном. Подача корма для кошки? Управление светом? Пределом является только ваша фантазия.

В этот раз установим ширину клавиатуры 2 и посмотрим, что будет.
Ну и так как методы добавления новых элементов мы уже разобрали, добавим сразу все доступные кнопки и отправим получившуюся клавиатуру, не забыв добавить хэндлер кнопок:

Пройдёмся по строчкам по порядку, чтобы не осталось вопросов:

Так как параметр клавиатуры row_width равен двум, то кнопки автоматически расставились соответствующе. Рассмотрим реакцию на кнопки по порядку: При нажатии первой срабатывает наш первый колбек, так как не важно, в какую клавиатуру добавлена кнопка, важно, какая у неё callback_data ☝️. Поэтому добавлять инлайн кнопку можно сколько угодно раз в любые инлайн клавиатуры.

Кнопки со второй по пятую имеют схожую структуру в callback_data , поэтому внутри хэндлера проверяем, какой код у нажатой кнопки и:

Для начала давайте создадим каталог для бота, организуем там virtual environment (далее venv) и установим библиотеку aiogram.
Проверим, что установлен Python версии 3.7 (если вы знаете, что установлен 3.8 и выше, можете пропустить этот кусок):

О версиях aiogram

В этой главе используется aiogram версии 2.9.2, но перед началом работы рекомендую заглянуть в канал релизов библиотеки и проверить наличие более новой версии. Подойдёт любая более новая, начинающаяся с цифры 2, поскольку в будущем ожидается релиз aiogram 3.0 с заметными изменениями и без обратной совместимости.
Чтобы избежать неприятностей, зафиксируемся на 2.9.2 и далее будем обновляться вручную.

Обратите внимание на префикс «venv» в терминале. Он указывает, что мы находимся в виртуальном окружении с именем «venv». Проверим, что внутри venv вызов команды python указывает на всё тот же Python 3.7:

Последней командой deactivate мы вышли из venv, чтобы он нам не мешал.

Первый бот¶

Давайте создадим файл bot.py с базовым шаблоном бота на aiogram:

Первое, на что нужно обратить внимание: aiogram — асинхронная библиотека, поэтому ваши функции тоже должны быть асинхронными, а перед вызовами методов API нужно ставить ключевое слово await, т.к. эти вызовы возвращают корутины.

Асинхронное программирование в Python

Не стоит пренебрегать официальной документацией!
Прекрасный туториал по asyncio доступен на сайте Python.

Рассмотрим следующий код:

Команда /test2 не работает

Давайте запустим с ним бота:

Функция cmd_test2 не работает, т.к. диспетчер о ней не знает. Исправим эту ошибку и отдельно зарегистрируем функцию:

Обе команды работают

Снова запустим бота:

Обработка ошибок¶

За эти 10 секунд пользователь может успеть заблокировать бота со своей стороны и попытка вызвать метод reply приведёт к появлению исключения BotBlocked . Напишем специальный хэндлер для этого исключения:

Аналогично пишутся обработчики и на другие исключения. Таким образом, если одна и та же непредвиденная ситуация может возникнуть в различных хэндлерах, то можно вынести её обработку в отдельный хэндлер ошибок. Кода будет меньше, а оставшийся станет читабельнее.

У errors_handler есть одна особенность, из-за которой его использование может быть нежелательно. Дело в том, что после срабатывания и завершения хэндлера, управление в исходную функцию не возвращается. Проще говоря, если, например, 57-я итерация цикла из 100 привела к срабатыванию errors_handler, остальные итерации выполнены не будут, как и весь остальной код исходной функции. В этом случае ничего не остаётся, кроме как использовать try..except .

Синтаксический сахар¶

Разница между message.answer() и message.reply()

что значит ‘message: types.Message’ ?

Python является интерпретируемым языком с сильной, но динамической типизацией, поэтому встроенная проверка типов, как, например, в C++ или Java, отсутствует. Однако начиная с версии 3.5 в языке появилась поддержка подсказок типов, благодаря которой различные чекеры и IDE вроде PyCharm анализируют типы используемых значений и подсказывают программисту, если он передаёт что-то не то. В данном случае подсказка types.Message соообщает PyCharm-у, что переменная message имеет тип Message , описанный в модуле types библиотеки aiogram (см. импорты в начале кода). Благодаря этому IDE может на лету подсказывать атрибуты и функции.

Всё хорошо, но если вдруг вы захотите поделиться с кем-то кодом, то придётся каждый раз помнить об удалении из исходников токена бота, иначе придётся его перевыпускать у @BotFather. Чтобы обезопасить себя, давайте перестанем указывать токен прямо в коде, а вынесем его как переменную окружения.
Замените следующие строчки из начала файла:

Но теперь ваш бот не запустится, т.к. будет сразу завершаться с ошибкой Error: no token provided . Чтобы передать переменную окружения в PyCharm, откройте сверху раздел Run -> Edit Configurations и добавьте в окне Environment Variables переменную с именем BOT_TOKEN и значением токена.

Разница между message.answer() и message.reply()

Запустите снова бота и убедитесь, что он работает. Получившийся код можно смело сохранять в PyCharm в File Templates.

На этом мы закончим знакомство с библиотекой, а в следующих главах рассмотрим другие «фишки» aiogram и Telegram Bot API.

F0rzend

Используя aiogram вы получаете множество дополнительных штук, которых нет в других библиотеках, как например встроенные фильтры. Вместо того, чтобы лепить страшную func=lambda message: message.text==»Текст» , как в pyTelegramBotApi, в aiogram вы пропишете просто text=»Текст» . Но это далеко не все. При этом встроенные фильтры постоянно добавляются, но это нигде нормально не освещено (кроме источников), это подтолкнуло меня к написанию данной статьи. Сегодня мы полазим в исходниках aiogram и посмотрим, какие же фильтры у нас есть «из коробки«.

Начнём, как не удивительно, с открытия репозиторий aiogram на github. Не сложно догадаться, что все фильтры находятся в пакете filters. Пара минут поиска приводят нас к aiogram / dispatcher / filters / . Далеко ходить не будем, сразу заглянем в файл __init__.py

aiogram/dispatcher/filters/__init__.py

В этом файле находятся просто импорты. Но это даёт нам очень важную информацию — всё самое интересное находится в файле .builtin. Таким образом составим небольшой список всех фильтров в текущей версии aiogram 2.10.2 :

IDFilter — фильтр для проверки id чата или пользователя

AdminFilter — проверка на то, является ли пользователь администратором чата

IsSenderContact — проверяет, что пользователь отправил именно свой контакт

Дальше мы рассмотрим подробнее каждый из фильтров.

Command фильтры

Все command фильтры применяются исключительно на обработчики message_handler и edited_message_handler.
Может использоваться как аргумент обработчика commands. Т.е. вы можете использовать данный фильтр двумя способами:

Наверное самый часто используемый фильтр — Command. Наверняка все о нём знают, но я уверен, что не все знают о том, на сколько это «крутой» фильтр. Кроме всем известного аргумента commands он принимает prefixes, ignore_case, ignore_mention, ignore_caption.

  • prefixes — префиксы команды, т.е. то, с чего команда начинается. Самыми частыми префиксами являются стандартный » / «, а также » ! «.
  • ignore_case — игнорировать регистр команды. Проверяется с помощью str.lower() .
  • ignore_mention — игнорировать упоминание бота. По умолчанию False. Таким образом, когда бот получает команду с mention другого бота, он её не обрабатывает. Если передать True, в независимости от mention в /command@mention команда попадёт в обработчик, даже если это команда с упоминанием другого бота. Помните, что бот с включенным privacy mode не получит команду с упоминанием другого бота.
  • ignore_caption — игнорировать команды, которые написаны под изображением. По умолчанию True.

CommandStart

Этот фильтр, для проверки команды /start . Это фильтр Command, в который передаётся команда ‘start’, а остальные аргументы остаются по умолчанию. Фильтр CommandStart принимает лишь два аргумента:

  • deep_link — строка или регулярное выражение, для обработки deep_link.
  • encoded — обрабатывать закодированную ссылку deep_link (по умолчанию False).

Пример использования можно взять следующий:

CommandHelp, CommandPrivacy и CommandSettings

Фильтры CommandHelp, CommandPrivacy и CommandSettings не представляют из себя ничего особо интересного. Это просто фильтр Command с переданной в него командой ‘help’ , ‘ privacy’ или ‘ settings’ соответственно. Особенность этих команд заключается в том, что это глобальные команды, наличие которые добавляет дополнительные кнопки в профиле бота.

ContentTypeFilter

Этот фильтр проверяет тип контента, будь то фото, текст или что-нибудь другое. Должен использоваться исключительно как аргумент content_types. По умолчанию проверяет на текст. Принимает либо строку, либо aiogram.types.ContentTypes (что строкой и является).

Можно придумать много примеров использования, но возьмём самый простой: пусть бот хвалит картиночки.

ExceptionsFilter

Фильтр, используемый в error_handlers. Принимает исключение. Работает исключительно в качестве аргумента по ключевому слову exception. Пример использования можно придумать следующий:

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

HashTag

Regexp

Фильтр, проверяющий регулярное выражение. Подключён на большинство обработчиков. Для примера напишем обработчик, который будет обрабатывать ссылку на изображения:

Этот фильтр может использоваться также и как аргумент. Используйте для этого ключевое слово regexp :

RegexpCommandsFilter

Фильтр, проверяющий команду через регулярное выражение. Может использоваться как аргумент regexp_commands (Именно поэтому у меня функция с двумя декораторами, вам стоит использовать один из приведенных вариантов).

Возьмём идентичный пример. Реализуем команду image с несколькими префиксами, которые проверяют ссылку на изображение:

StateFilter

Все, кто использовал FSM знакомы с этим фильтром. Этот фильтр проверяет состояние, в котором находится пользователь. Он может использоваться только как аргумент функции state . Для примера опишем 2 обработчика: для присвоения состояния и для его сброса.

    — строка, которой текст должен быть идентичен — строка, которую должен содержаться текст — строка, которой текст должен начинаться — строка, которой текст должен заканчиваться — игнорировать регистр текст. Проверяется с помощью str.lower() .

Как я и сказал ранее, данный фильтр может использоваться как аргумент. Для этого используйте text, text_contains, text_startswith или text_endswith.

Рассмотрим следующий пример: допустим нам нужно ругать в чате участника за какую-нибудь фразу:

Результат работы фильтра

IDFilter

Ещё один фильтр, который заслуживает внимания — фильтр для проверки идентификатора. Он может использоваться как аргумент user_id, chat_id, так и как callable объект IDFilter(user_id=12345789)

Сам фильтр имеет два аргумента:

  • user_id — проверяет ID пользователя
  • chat_id — проверяет ID чата

AdminFilter

Один из любимых фильтров разработчиков чат модераторов. Как понятно из названия проверяет, что запрос прилетел от администратора чата. Может использоваться как callable объект, или аргумент функции.

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

Ещё раз хочу вам напомнить, что я использую два декоратора исключительно для демонстрации. Вам стоит выбрать одно из решений. Также обратите внимание, что вперёд я поставил фильтр на команду, потому что AdminFilter делает запрос в API, что требует времени, а значит замедляет работу бота.

Кроме логического типа данных, этот фильтр может также содержать ID чата. В этом случае он будет проверять, что запрос пришёл именно от администратора чата с конкретным идентификатором, а не текущего чата.

Этот фильтр не может быть False.

Если при использовании этого фильтра как callable не передавать аргументы, проверяться будет администрация текущего чата.

IsReplyFilter

IsSenderContact

Этот фильтр проверяет, что пользователь отправил именно свой контакт. Может использоваться как callable или как аргумент is_sender_contact. Думаю никому не нужно объяснять, где это необходимо.

ForwardedMessageFilter

ChatTypeFilter

Итоги

В этой статье мы разобрали абсолютно все фильтры фреймворка aiogram и разобрали примеры их использования, а так же задачи, в которых они могут применяться. Все примеры вы можете найти в моём github репозитории.

Читайте также:

      

  • Сообщение по истории туляки декабристы
  •   

  • Сообщение на тему проблема народонаселения
  •   

  • Сообщение которое напугает друга
  •   

  • Какое сообщение icmpv6 отправляется если поле hop limit ipv6 пакета уменьшается до нуля
  •   

  • Портрет половцев сообщение по музыке

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Как изменить сонный режим на виндовс 7
  • Как изменить сонный режим на windows 10
  • Как изменить сон человека
  • Как изменить сон на айфоне
  • Как изменить сом порт на компьютере виндовс 10

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии