Перейти к содержанию

Фильтры⚓︎

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

Tip

Наши фильтры, также как и фильтры aiogram, построены на библиотеке magic-filter.

Объединение фильтров⚓︎

Если указать несколько фильтров подряд, они будут проверены с условием «и»:

@<router>.message(F.from_user.id == "bots_ru@video.example.com", F.text == 'admin')

Кроме того, если вы хотите использовать два альтернативных способа запуска одного и того же обработчика (условие «или»), вы можете зарегистрировать обработчик два или более раз по вашему усмотрению.

@<router>.message(F.text == "hi")
@<router>.message(Command("/start"))

MagicFilter⚓︎

MagicFilter — это мощный и гибкий инструмент из пакета magic-filter, интегрированный в python-trueconf-bot. Он позволяет строить выразительные и цепочечные фильтры, упрощающие маршрутизацию сообщений в вашем боте. Вместо того чтобы вручную проверять поля апдейта внутри обработчика, условия можно задать прямо в декораторе.

Фильтр работает «лениво»: при объявлении обработчика сохраняется сама цепочка проверок, а не её результат. Проверка выполняется только в момент, когда приходит новое событие, поэтому фильтры легко комбинируются и остаются читаемыми. Такой подход делает код короче и понятнее: сразу видно, какие именно апдейты пройдут через конкретный обработчик.

Идея MagicFilter проста: вы описываете цепочку атрибутов и условие, а затем применяете её к объекту. Представьте, что у вас есть объект с вложенными полями. Вместо ручной проверки вида if obj.foo.bar.baz == "spam": ... можно собрать фильтр декларативно:

F.foo.bar.baz == "spam"

Получившийся фильтр — это не мгновенная проверка, а объект, который «запоминает» условие. При обработке апдейта этот фильтр автоматически применяется к объекту (роутер сам выполняет проверку под капотом). Технически для этого используется метод .resolve(obj), но напрямую вызывать его в коде не требуется — достаточно описать условие в декораторе, и оно будет выполнено при маршрутизации.

@r.message(F.text == "ping")
async def ping_handler(message):
    await message.answer("pong")

Здесь фильтр F.text == "ping" будет автоматически проверен для каждого входящего сообщения. Если условие совпадает, сработает обработчик.

Возможные действия⚓︎

Объект MagicFilter поддерживает базовые логические операции над атрибутами объектов.

Существование или not None⚓︎

Действие по умолчанию:

F.photo  # message.photo

Равенство⚓︎

F.text == "hello"        # message.text == "hello"
F.from_user.id == 42     # message.from_user.id == 42
F.text != "spam"         # message.text != "spam"

Принадлежность множеству⚓︎

# query.from_user.id in {42, 1000, 123123}
F.from_user.id.in_({42, 1000, 123123})

# query.data in {"foo", "bar", "baz"}
F.data.in_({"foo", "bar", "baz"})       

Содержит⚓︎

F.text.contains("foo")  # "foo" in message.text

Начинается/заканчивается строкой⚓︎

F.text.startswith("foo")  # message.text.startswith("foo")
F.text.endswith("bar")    # message.text.endswith("bar")

Регулярные выражения⚓︎

F.text.regexp(r"Hello, .+")  # re.match(r"Hello, .+", message.text)

Пользовательская функция⚓︎

# (lambda chat: chat.id == -42)(message.chat)
F.chat.func(lambda chat: chat.id == -42)  

Инверсия результата⚓︎

~F.text                     # not message.text
~F.text.startswith("spam")  # not message.text.startswith("spam")

Комбинирование условий⚓︎

(F.from_user.id == 42) & (F.text == "admin")

F.text.startswith("a") | F.text.endswith("b")

(F.from_user.id.in_({42, 777, 911})) & (F.text.startswith("!") | F.text.startswith("/")) & F.text.contains("ban")   

Модификаторы атрибутов (строки)⚓︎

F.text.lower() == "test"           # message.text.lower() == "test"
F.text.upper().in_({"FOO", "BAR"}) # message.text.upper() in {"FOO", "BAR"}
F.text.len() == 5                  # len(message.text) == 5