Magic filters

Note

This page still in progress. Has many incorrectly worded sentences.

Is external package maintained by aiogram core team.

By default installs with aiogram and also is available on PyPi - magic-filter. That’s mean you can install it and use with any other libraries and in own projects without depending aiogram installed.

Usage

The magic_filter package implements class shortly named magic_filter.F that’s mean F can be imported from aiogram or magic_filter. F is alias for MagicFilter.

Note

Note that aiogram has an small extension over magic-filter and if you want to use this extension you should import magic from aiogram instead of magic_filter package

The MagicFilter object is callable, supports some actions and memorize the attributes chain and the action which should be checked on demand.

So that’s mean you can chain attribute getters, describe simple data validations and then call the resulted object passing single object as argument, for example make attributes chain F.foo.bar.baz then add action ‘F.foo.bar.baz == 'spam' and then call the resulted object - (F.foo.bar.baz == 'spam').resolve(obj)

Possible actions

Magic filter object supports some of basic logical operations over object attributes

Exists or not None

Default actions.

F.photo  # lambda message: message.photo

Equals

F.text == 'hello'  # lambda message: message.text == 'hello'
F.from_user.id == 42  # lambda message: message.from_user.id == 42
F.text != 'spam'  # lambda message: message.text != 'spam'

Is one of

Can be used as method named in_ or as matmul operator @ with any iterable

F.from_user.id.in_({42, 1000, 123123})  # lambda query: query.from_user.id in {42, 1000, 123123}
F.data.in_({'foo', 'bar', 'baz'})  # lambda query: query.data in {'foo', 'bar', 'baz'}

Contains

F.text.contains('foo')  # lambda message: 'foo' in message.text

String startswith/endswith

Can be applied only for text attributes

F.text.startswith('foo')  # lambda message: message.text.startswith('foo')
F.text.endswith('bar')  # lambda message: message.text.startswith('bar')

Regexp

F.text.regexp(r'Hello, .+')  # lambda message: re.match(r'Hello, .+', message.text)

Custom function

Accepts any callable. Callback will be called when filter checks result

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

Inverting result

Any of available operation can be inverted by bitwise inversion - ~

~F.text  # lambda message: not message.text
~F.text.startswith('spam')  # lambda message: not message.text.startswith('spam')

Combining

All operations can be combined via bitwise and/or operators - &/|

(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')

Attribute modifiers - string manipulations

Make text upper- or lower-case

Can be used only with string attributes.

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

Get filter result as handler argument

This part is not available in magic-filter directly but can be used with aiogram

from aiogram import F

...

@router.message(F.text.regexp(r"^(\d+)$").as_("digits"))
async def any_digits_handler(message: Message, digits: Match[str]):
    await message.answer(html.quote(str(digits)))

Usage in aiogram

@router.message(F.text == 'hello')
@router.inline_query(F.data == 'button:1')
@router.message(F.text.startswith('foo'))
@router.message(F.content_type.in_({'text', 'sticker'}))
@router.message(F.text.regexp(r'\d+'))

...

# Many others cases when you will need to check any of available event attribute