Remove filters factory, introduce docs translation (#978)

* Rewrite filters

* Update README.rst

* Fixed tests

* Small optimization of the Text filter (TY to @bomzheg)

* Remove dataclass slots argument in due to the only Python 3.10 has an slots argument

* Fixed mypy

* Update tests

* Disable Python 3.11

* Fixed #1013: Empty mention should be None instead of empty string.

* Added #990 to the changelog

* Added #942 to the changelog

* Fixed coverage

* Update poetry and dependencies

* Fixed mypy

* Remove deprecated code

* Added more tests, update pyproject.toml

* Partial update docs

* Added initial Docs translation files

* Added more changes

* Added log message when connection is established in polling process

* Fixed action

* Disable lint for PyPy

* Added changelog for docs translation
This commit is contained in:
Alex Root Junior 2022-10-02 00:04:31 +03:00 committed by GitHub
parent 94030903ec
commit f4251382e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
610 changed files with 61738 additions and 1687 deletions

View file

@ -3,7 +3,7 @@ CallbackQueryHandler
####################
.. automodule:: aiogram.handler.callback_query
.. automodule:: aiogram.handlers.callback_query
:members:
:member-order: bysource
:undoc-members: True

View file

@ -86,10 +86,10 @@ Handle user leave or join events
from aiogram.filters import IS_MEMBER, IS_NOT_MEMBER
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=IS_MEMBER >> IS_NOT_MEMBER))
@router.chat_member(ChatMemberUpdatedFilter(IS_MEMBER >> IS_NOT_MEMBER))
async def on_user_leave(event: ChatMemberUpdated): ...
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=IS_NOT_MEMBER >> IS_MEMBER))
@router.chat_member(ChatMemberUpdatedFilter(IS_NOT_MEMBER >> IS_MEMBER))
async def on_user_join(event: ChatMemberUpdated): ...
Or construct your own terms via using pre-defined set of statuses and transitions.

View file

@ -3,7 +3,7 @@ Command
=======
.. autoclass:: aiogram.filters.command.Command
:members:
:members: __init__
:member-order: bysource
:undoc-members: False
@ -18,10 +18,11 @@ When filter is passed the :class:`aiogram.filters.command.CommandObject` will be
Usage
=====
1. Filter single variant of commands: :code:`Command(commands=["start"])` or :code:`Command(commands="start")`
2. Handle command by regexp pattern: :code:`Command(commands=[re.compile(r"item_(\d+)")])`
3. Match command by multiple variants: :code:`Command(commands=["item", re.compile(r"item_(\d+)")])`
4. Handle commands in public chats intended for other bots: :code:`Command(commands=["command"], commands_ignore_mention=True)`
1. Filter single variant of commands: :code:`Command("start")`
2. Handle command by regexp pattern: :code:`Command(re.compile(r"item_(\d+)"))`
3. Match command by multiple variants: :code:`Command("item", re.compile(r"item_(\d+)"))`
4. Handle commands in public chats intended for other bots: :code:`Command("command", ignore_mention=True)`
5. Use :class:`aiogram.types.bot_command.BotCommand` object as command reference :code:`Command(BotCommand(command="command", description="My awesome command")`
.. warning::

View file

@ -1,33 +0,0 @@
==================
ContentTypesFilter
==================
.. autoclass:: aiogram.filters.content_types.ContentTypesFilter
:members:
:member-order: bysource
:undoc-members: False
Can be imported:
- :code:`from aiogram.filters.content_types import ContentTypesFilter`
- :code:`from aiogram.filters import ContentTypesFilter`
Or used from filters factory by passing corresponding arguments to handler registration line
Usage
=====
1. Single content type: :code:`ContentTypesFilter(content_types=["sticker"])` or :code:`ContentTypesFilter(content_types="sticker")`
2. Multiple content types: :code:`ContentTypesFilter(content_types=["sticker", "photo"])`
3. Recommended: With usage of `ContentType` helper: :code:`ContentTypesFilter(content_types=[ContentType.PHOTO])`
4. Any content type: :code:`ContentTypesFilter(content_types=[ContentType.ANY])`
Allowed handlers
================
Allowed update types for this filter:
- :code:`message`
- :code:`edited_message`
- :code:`channel_post`
- :code:`edited_channel_post`

View file

@ -2,13 +2,6 @@
Filtering events
================
.. danger::
Note that the design of filters will be changed in 3.0b5
`Read more >> <https://github.com/aiogram/aiogram/issues/942>`_
Filters is needed for routing updates to the specific handler.
Searching of handler is always stops on first match set of filters are pass.
@ -23,91 +16,40 @@ Here is list of builtin filters:
:maxdepth: 1
command
content_types
text
chat_member_updated
exception
magic_filters
magic_data
callback_data
exception
Own filters specification
Writing own filters
=========================
Filters can be:
- Asynchronous function (:code:`async def my_filter(*args, **kwargs): pass`)
- Synchronous function (:code:`def my_filter(*args, **kwargs): pass`)
- Anonymous function (:code:`lambda event: True`)
- Any awaitable object
- Subclass of :class:`aiogram.filters.base.BaseFilter`
- Subclass of :class:`aiogram.filters.base.Filter`
- Instances of :ref:`MagicFilter <magic-filters>`
Filters should return bool or dict.
and should return bool or dict.
If the dictionary is passed as result of filter - resulted data will be propagated to the next
filters and handler as keywords arguments.
Writing bound filters
=====================
Base class for own filters
--------------------------
.. autoclass:: aiogram.filters.base.BaseFilter
:members: __call__
.. autoclass:: aiogram.filters.base.Filter
:members: __call__,update_handler_flags
:member-order: bysource
:undoc-members: False
Own filter example
------------------
For example if you need to make simple text filter:
.. code-block:: python
from aiogram.filters import BaseFilter
class MyText(BaseFilter):
my_text: str
async def __call__(self, message: Message) -> bool:
return message.text == self.my_text
router.message.bind_filter(MyText)
@router.message(my_text="hello")
async def my_handler(message: Message): ...
.. note::
Bound filters is always recursive propagates to the nested routers but will be available
in nested routers only after attaching routers so that's mean you will need to
include routers before registering handlers.
Resolving filters with default value
====================================
Bound Filters with only default arguments will be automatically applied with default values
to each handler in the router and nested routers to which this filter is bound.
For example, although we do not specify :code:`chat_type` in the handler filters,
but since the filter has a default value, the filter will be applied to the handler
with a default value :code:`private`:
.. code-block:: python
class ChatType(BaseFilter):
chat_type: str = "private"
async def __call__(self, message: Message , event_chat: Chat) -> bool:
if event_chat:
return event_chat.type == self.chat_type
else:
return False
router.message.bind_filter(ChatType)
@router.message()
async def my_handler(message: Message): ...
.. literalinclude:: ../../../examples/own_filter.py

View file

@ -1,6 +1,6 @@
====
=========
MagicData
====
=========
.. autoclass:: aiogram.filters.magic_data.MagicData
:members:
@ -9,7 +9,6 @@ MagicData
Can be imported:
- :code:`from aiogram.filters.magic_data import MagicData`
- :code:`from aiogram.filters import MagicData`
Or used from filters factory by passing corresponding arguments to handler registration line
@ -17,7 +16,7 @@ Or used from filters factory by passing corresponding arguments to handler regis
Usage
=====
#. :code:`MagicData(magic_data=F.event.from_user.id == F.config.admin_id)` (Note that :code:`config` should be passed from middleware)
#. :code:`MagicData(F.event.from_user.id == F.config.admin_id)` (Note that :code:`config` should be passed from middleware)
Allowed handlers

View file

@ -11,7 +11,6 @@ Can be imported:
- :code:`from aiogram.filters.text import Text`
- :code:`from aiogram.filters import Text`
- :code:`from.filters import Text`
Or used from filters factory by passing corresponding arguments to handler registration line
@ -19,11 +18,11 @@ Usage
=====
#. Text equals with the specified value: :code:`Text(text="text") # value == 'text'`
#. Text starts with the specified value: :code:`Text(text_startswith="text") # value.startswith('text')`
#. Text ends with the specified value: :code:`Text(text_endswith="text") # value.endswith('text')`
#. Text contains the specified value: :code:`Text(text_contains="text") # value in 'text'`
#. Text starts with the specified value: :code:`Text(startswith="text") # value.startswith('text')`
#. Text ends with the specified value: :code:`Text(endswith="text") # value.endswith('text')`
#. Text contains the specified value: :code:`Text(contains="text") # value in 'text'`
#. Any of previous listed filters can be list, set or tuple of strings that's mean any of listed value should be equals/startswith/endswith/contains: :code:`Text(text=["text", "spam"])`
#. Ignore case can be combined with any previous listed filter: :code:`Text(text="Text", text_ignore_case=True) # value.lower() == 'text'.lower()`
#. Ignore case can be combined with any previous listed filter: :code:`Text(text="Text", ignore_case=True) # value.lower() == 'text'.lower()`
Allowed handlers
================

View file

@ -42,7 +42,7 @@ Via filters
.. code-block:: python
class Command(BaseFilter):
class Command(Filter):
...
def update_handler_flags(self, flags: Dict[str, Any]) -> None:

View file

@ -44,8 +44,8 @@ Message
(For example text, sticker and document are always of different content types of message)
Recommended way to check field availability before usage or use
:class:`aiogram.filters.content_types.ContentTypesFilter`
Recommended way to check field availability before usage, for example via :ref:`magic filter <magic-filters>`:
:code:`F.text` to handle text, :code:`F.sticker` to handle stickers only and etc.
.. code-block:: python
@ -141,7 +141,7 @@ Errors
.. code-block:: python
@router.errors()
async def error_handler(exception: Exception) -> Any: pass
async def error_handler(exception: ErrorEvent) -> Any: pass
Is useful for handling errors from other handlers
@ -152,9 +152,8 @@ Nested routers
.. warning::
Routers by the way can be nested to an another routers with some limitations:
1. Router **CAN NOT** include itself
1. Routers **CAN NOT** be used for circular including (router 1 include router 2, router 2 include router 3, router 3 include router 1)
1. Router **CAN NOT** include itself
1. Routers **CAN NOT** be used for circular including (router 1 include router 2, router 2 include router 3, router 3 include router 1)
Example:
@ -170,7 +169,7 @@ Example:
.. code-block:: python
:caption: module_12.py
:caption: module_2.py
:name: module_1
from module_2 import router2