Docs for filters (Not fully)

This commit is contained in:
Alex RootJunior 2019-04-21 01:16:42 +03:00
parent 0f53e5fbd7
commit 2af930149c
6 changed files with 263 additions and 25 deletions

View file

@ -38,5 +38,5 @@ __all__ = [
'utils'
]
__version__ = '2.1'
__version__ = '2.1.1.dev1'
__api_version__ = '4.2'

View file

@ -10,12 +10,15 @@ from babel.support import LazyProxy
from aiogram import types
from aiogram.dispatcher.filters.filters import BoundFilter, Filter
from aiogram.types import CallbackQuery, Message, InlineQuery, Poll
from aiogram.utils.deprecated import warn_deprecated
class Command(Filter):
"""
You can handle commands by using this filter
You can handle commands by using this filter.
If filter is successful processed the :obj:`Command.CommandObj` will be passed to the handler arguments.
By default this filter is registered for messages and edited messages handlers.
"""
def __init__(self, commands: Union[Iterable, str],
@ -23,12 +26,22 @@ class Command(Filter):
ignore_case: bool = True,
ignore_mention: bool = False):
"""
Filter can be initialized from filters factory or by simply creating instance of this class
Filter can be initialized from filters factory or by simply creating instance of this class.
:param commands: command or list of commands
:param prefixes:
:param ignore_case:
:param ignore_mention:
Examples:
.. code-block:: python
@dp.message_handler(commands=['myCommand'])
@dp.message_handler(Command(['myCommand']))
@dp.message_handler(commands=['myCommand'], commands_prefix='!/')
:param commands: Command or list of commands always without leading slashes (prefix)
:param prefixes: Allowed commands prefix. By default is slash.
If you change the default behavior pass the list of prefixes to this argument.
:param ignore_case: Ignore case of the command
:param ignore_mention: Ignore mention in command
(By default this filter pass only the commands addressed to current bot)
"""
if isinstance(commands, str):
commands = (commands,)
@ -43,15 +56,21 @@ class Command(Filter):
"""
Validator for filters factory
From filters factory this filter can be registered with arguments:
- ``command``
- ``commands_prefix`` (will be passed as ``prefixes``)
- ``commands_ignore_mention`` (will be passed as ``ignore_mention``
:param full_config:
:return: config or empty dict
"""
config = {}
if 'commands' in full_config:
config['commands'] = full_config.pop('commands')
if 'commands_prefix' in full_config:
if config and 'commands_prefix' in full_config:
config['prefixes'] = full_config.pop('commands_prefix')
if 'commands_ignore_mention' in full_config:
if config and 'commands_ignore_mention' in full_config:
config['ignore_mention'] = full_config.pop('commands_ignore_mention')
return config
@ -74,17 +93,37 @@ class Command(Filter):
@dataclass
class CommandObj:
"""
Instance of this object is always has command and it prefix.
Can be passed as keyword argument ``command`` to the handler
"""
"""Command prefix"""
prefix: str = '/'
"""Command without prefix and mention"""
command: str = ''
"""Mention (if available)"""
mention: str = None
"""Command argument"""
args: str = field(repr=False, default=None)
@property
def mentioned(self) -> bool:
"""
This command has mention?
:return:
"""
return bool(self.mention)
@property
def text(self) -> str:
"""
Generate original text from object
:return:
"""
line = self.prefix + self.command
if self.mentioned:
line += '@' + self.mention
@ -94,11 +133,32 @@ class Command(Filter):
class CommandStart(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/start`` command.
"""
def __init__(self, deep_link: typing.Optional[typing.Union[str, re.Pattern]] = None):
"""
Also this filter can handle `deep-linking <https://core.telegram.org/bots#deep-linking>`_ arguments.
Example:
.. code-block:: python
@dp.message_handler(CommandStart(re.compile(r'ref-([\\d]+)')))
:param deep_link: string or compiled regular expression (by ``re.compile(...)``).
"""
super(CommandStart, self).__init__(['start'])
self.deep_link = deep_link
async def check(self, message: types.Message):
"""
If deep-linking is passed to the filter result of the matching will be passed as ``deep_link`` to the handler
:param message:
:return:
"""
check = await super(CommandStart, self).check(message)
if check and self.deep_link is not None:
@ -114,16 +174,28 @@ class CommandStart(Command):
class CommandHelp(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/help`` command.
"""
def __init__(self):
super(CommandHelp, self).__init__(['help'])
class CommandSettings(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/settings`` command.
"""
def __init__(self):
super(CommandSettings, self).__init__(['settings'])
class CommandPrivacy(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/privacy`` command.
"""
def __init__(self):
super(CommandPrivacy, self).__init__(['privacy'])

View file

@ -6,7 +6,7 @@ from ..handler import Handler
class FiltersFactory:
"""
Default filters factory
Filters factory
"""
def __init__(self, dispatcher):

View file

@ -128,24 +128,29 @@ class FilterRecord:
class AbstractFilter(abc.ABC):
"""
Abstract class for custom filters
Abstract class for custom filters.
"""
@classmethod
@abc.abstractmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]:
"""
Validate and parse config
Validate and parse config.
:param full_config:
:return: config
This method will be called by the filters factory when you bind this filter.
Must be overridden.
:param full_config: dict with arguments passed to handler registrar
:return: Current filter config
"""
pass
@abc.abstractmethod
async def check(self, *args) -> bool:
"""
Check object
Will be called when filters checks.
This method must be overridden.
:param args:
:return:
@ -173,24 +178,46 @@ class AbstractFilter(abc.ABC):
class Filter(AbstractFilter):
"""
You can make subclasses of that class for custom filters
You can make subclasses of that class for custom filters.
Method ``check`` must be overridden
"""
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]:
"""
Here method ``validate`` is optional.
If you need to use filter from filters factory you need to override this method.
:param full_config: dict with arguments passed to handler registrar
:return: Current filter config
"""
pass
class BoundFilter(Filter):
"""
Base class for filters with default validator
To easily create your own filters with one parameter, you can inherit from this filter.
You need to implement ``__init__`` method with single argument related with key attribute
and ``check`` method where you need to implement filter logic.
"""
"""Unique name of the filter argument. You need to override this attribute."""
key = None
"""If :obj:`True` this filter will be added to the all of the registered handlers"""
required = False
"""Default value for configure required filters"""
default = None
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]:
"""
If ``cls.key`` is not :obj:`None` and that is in config returns config with that argument.
:param full_config:
:return:
"""
if cls.key is not None:
if cls.key in full_config:
return {cls.key: full_config[cls.key]}

View file

@ -4,16 +4,155 @@ Filters
Basics
======
Coming soon...
Filter factory greatly simplifies the reuse of filters when registering handlers.
Filters factory
===============
Coming soon...
.. autoclass:: aiogram.dispatcher.filters.factory.FiltersFactory
:members:
:show-inheritance:
Builtin filters
===============
Coming soon...
``aiogram`` has some builtin filters. Here you can see all of them:
Command
-------
.. autoclass:: aiogram.dispatcher.filters.builtin.Command
:members:
:show-inheritance:
CommandStart
------------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandStart
:members:
:show-inheritance:
CommandHelp
-----------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandHelp
:members:
:show-inheritance:
CommandSettings
---------------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandSettings
:members:
:show-inheritance:
CommandPrivacy
--------------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandPrivacy
:members:
:show-inheritance:
Text
----
.. autoclass:: aiogram.dispatcher.filters.builtin.Text
:members:
:show-inheritance:
HashTag
-------
.. autoclass:: aiogram.dispatcher.filters.builtin.HashTag
:members:
:show-inheritance:
Regexp
------
.. autoclass:: aiogram.dispatcher.filters.builtin.Regexp
:members:
:show-inheritance:
RegexpCommandsFilter
--------------------
.. autoclass:: aiogram.dispatcher.filters.builtin.RegexpCommandsFilter
:members:
:show-inheritance:
ContentTypeFilter
-----------------
.. autoclass:: aiogram.dispatcher.filters.builtin.ContentTypeFilter
:members:
:show-inheritance:
StateFilter
-----------
.. autoclass:: aiogram.dispatcher.filters.builtin.StateFilter
:members:
:show-inheritance:
ExceptionsFilter
----------------
.. autoclass:: aiogram.dispatcher.filters.builtin.ExceptionsFilter
:members:
:show-inheritance:
Making own filters (Custom filters)
===================================
Coming soon...
Own filter can be:
- any callable object
- any async function
- any anonymous function (Example: ``lambda msg: msg.text == 'spam'``)
- Subclass of :obj:`AbstractFilter`, :obj:`Filter` or :obj:`BoundFilter`
AbstractFilter
--------------
.. autoclass:: aiogram.dispatcher.filters.filters.AbstractFilter
:members:
:show-inheritance:
Filter
------
.. autoclass:: aiogram.dispatcher.filters.filters.Filter
:members:
:show-inheritance:
BoundFilter
-----------
.. autoclass:: aiogram.dispatcher.filters.filters.BoundFilter
:members:
:show-inheritance:
.. code-block:: python
class ChatIdFilter(BoundFilter):
key = 'chat_id'
def __init__(self, chat_id: typing.Union[typing.Iterable, int]):
if isinstance(chat_id, int):
chat_id = [chat_id]
self.chat_id = chat_id
def check(self, message: types.Message) -> bool:
return message.chat.id in self.chat_id
dp.filters_factory.bind(ChatIdFilter, event_handlers=[dp.message_handlers])

View file

@ -22,19 +22,19 @@ Next step: interaction with bots starts with one command. Register your first co
.. literalinclude:: ../../examples/echo_bot.py
:language: python
:lines: 21-25
:lines: 20-25
If you want to handle all messages in the chat simply add handler without filters:
.. literalinclude:: ../../examples/echo_bot.py
:language: python
:lines: 28-30
:lines: 35-37
Last step: run long polling.
.. literalinclude:: ../../examples/echo_bot.py
:language: python
:lines: 33-34
:lines: 40-41
Summary
-------