From 538ba2bab1d71d95f3eb742a14aeb928e2887369 Mon Sep 17 00:00:00 2001 From: cybernet Date: Wed, 1 Jan 2020 20:20:09 +0000 Subject: [PATCH 01/11] aiogram dev --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04a95017..53abcd3c 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ You can [read the docs here](http://docs.aiogram.dev/en/latest/). - Community: [@aiogram](https://t.me/aiogram) - Russian community: [@aiogram_ru](https://t.me/aiogram_ru) - Pip: [aiogram](https://pypi.python.org/pypi/aiogram) - - Docs: [ReadTheDocs](http://docs.aiogram.dev) + - Docs: [AiOGram Dev](https://docs.aiogram.dev/en/latest/) - Source: [Github repo](https://github.com/aiogram/aiogram) - Issues/Bug tracker: [Github issues tracker](https://github.com/aiogram/aiogram/issues) - Test bot: [@aiogram_bot](https://t.me/aiogram_bot) From 29aa4daf2c3cc29073d44f59e61609facc9bc2d6 Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 8 Jan 2020 00:09:56 +0500 Subject: [PATCH 02/11] Update i18n_example.py --- examples/i18n_example.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/i18n_example.py b/examples/i18n_example.py index 2d65655a..b626d048 100644 --- a/examples/i18n_example.py +++ b/examples/i18n_example.py @@ -83,7 +83,6 @@ def get_likes() -> int: def increase_likes() -> int: LIKES_STORAGE['count'] += 1 return get_likes() -# @dp.message_handler(commands='like') From 13b4f345a6abc13a00e8021ea93ff88d1c0f1bc3 Mon Sep 17 00:00:00 2001 From: cybernet Date: Wed, 8 Jan 2020 15:14:31 +0100 Subject: [PATCH 03/11] Update README.md as sugested by @JrooTJunior Co-Authored-By: Alex Root Junior --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 53abcd3c..4bea22bc 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ You can [read the docs here](http://docs.aiogram.dev/en/latest/). - Community: [@aiogram](https://t.me/aiogram) - Russian community: [@aiogram_ru](https://t.me/aiogram_ru) - Pip: [aiogram](https://pypi.python.org/pypi/aiogram) - - Docs: [AiOGram Dev](https://docs.aiogram.dev/en/latest/) + - Docs: [aiogram site](https://docs.aiogram.dev/) - Source: [Github repo](https://github.com/aiogram/aiogram) - Issues/Bug tracker: [Github issues tracker](https://github.com/aiogram/aiogram/issues) - Test bot: [@aiogram_bot](https://t.me/aiogram_bot) From a0261003535c771919ad3f11b36fc4af29bc814d Mon Sep 17 00:00:00 2001 From: gabbhack <43146729+gabbhack@users.noreply.github.com> Date: Thu, 16 Jan 2020 17:14:57 +0500 Subject: [PATCH 04/11] Fix ContentTypeFilter Now the ContentTypeFilter works correctly with single elements. --- aiogram/dispatcher/filters/builtin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aiogram/dispatcher/filters/builtin.py b/aiogram/dispatcher/filters/builtin.py index b80448c9..683ee841 100644 --- a/aiogram/dispatcher/filters/builtin.py +++ b/aiogram/dispatcher/filters/builtin.py @@ -444,6 +444,8 @@ class ContentTypeFilter(BoundFilter): default = types.ContentTypes.TEXT def __init__(self, content_types): + if isinstance(content_types, str): + content_types = (content_types,) self.content_types = content_types async def check(self, message): From ee803303aa07dee68266d89a430914d699478cd3 Mon Sep 17 00:00:00 2001 From: gabbhack <43146729+gabbhack@users.noreply.github.com> Date: Thu, 23 Jan 2020 20:23:08 +0500 Subject: [PATCH 05/11] Bot API 4.6 --- aiogram/bot/bot.py | 15 +++++++ aiogram/dispatcher/dispatcher.py | 76 ++++++++++++++++++++++++++++++++ aiogram/types/message_entity.py | 1 + aiogram/types/poll.py | 27 ++++++++++++ aiogram/types/reply_keyboard.py | 16 ++++++- aiogram/types/update.py | 5 ++- aiogram/types/user.py | 3 ++ 7 files changed, 141 insertions(+), 2 deletions(-) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 81a15603..30974f3a 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -863,6 +863,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): async def send_poll(self, chat_id: typing.Union[base.Integer, base.String], question: base.String, options: typing.List[base.String], + is_anonymous: typing.Optional[base.Boolean] = None, + type: typing.Optional[base.String] = None, + allows_multiple_answers: typing.Optional[base.Boolean] = None, + correct_option_id: typing.Optional[base.Integer] = None, + is_closed: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, @@ -881,6 +886,16 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :type question: :obj:`base.String` :param options: List of answer options, 2-10 strings 1-100 characters each :param options: :obj:`typing.List[base.String]` + :param is_anonymous: True, if the poll needs to be anonymous, defaults to True + :param is_anonymous: :obj:`typing.Optional[base.Boolean]` + :param type: Poll type, “quiz” or “regular”, defaults to “regular” + :param type: :obj:`typing.Optional[base.String]` + :param allows_multiple_answers: True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False + :param allows_multiple_answers: :obj:`typing.Optional[base.Boolean]` + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param correct_option_id: :obj:`typing.Optional[base.Integer]` + :param is_closed: Pass True, if the poll needs to be immediately closed + :param is_closed: :obj:`typing.Optional[base.Boolean]` :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`typing.Optional[Boolean]` :param reply_to_message_id: If the message is a reply, ID of the original message diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index 600e25ba..af39bbc7 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -69,6 +69,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): self.shipping_query_handlers = Handler(self, middleware_key='shipping_query') self.pre_checkout_query_handlers = Handler(self, middleware_key='pre_checkout_query') self.poll_handlers = Handler(self, middleware_key='poll') + self.poll_answer_handlers = Handler(self, middleware_key='poll_answer') self.errors_handlers = Handler(self, once=False, middleware_key='error') self.middleware = MiddlewareManager(self) @@ -87,6 +88,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): filters_factory.bind(StateFilter, exclude_event_handlers=[ self.errors_handlers, self.poll_handlers, + self.poll_answer_handlers, ]) filters_factory.bind(ContentTypeFilter, event_handlers=[ self.message_handlers, @@ -226,6 +228,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin): return await self.pre_checkout_query_handlers.notify(update.pre_checkout_query) if update.poll: return await self.poll_handlers.notify(update.poll) + if update.poll_answer: + return await self.poll_answer_handlers.notify(update.poll_answer) except Exception as e: err = await self.errors_handlers.notify(update, e) if err: @@ -853,18 +857,90 @@ class Dispatcher(DataMixin, ContextInstanceMixin): return decorator def register_poll_handler(self, callback, *custom_filters, run_task=None, **kwargs): + """ + Register handler for poll + + Example: + + .. code-block:: python3 + + dp.register_poll_handler(some_poll_handler) + + :param callback: + :param custom_filters: + :param run_task: run callback in task (no wait results) + :param kwargs: + """ filters_set = self.filters_factory.resolve(self.poll_handlers, *custom_filters, **kwargs) self.poll_handlers.register(self._wrap_async_task(callback, run_task), filters_set) def poll_handler(self, *custom_filters, run_task=None, **kwargs): + """ + Decorator for poll handler + + Example: + + .. code-block:: python3 + + @dp.poll_handler() + async def some_poll_handler(poll: types.Poll) + + :param custom_filters: + :param run_task: run callback in task (no wait results) + :param kwargs: + """ + def decorator(callback): self.register_poll_handler(callback, *custom_filters, run_task=run_task, **kwargs) return callback return decorator + + def register_poll_answer_handler(self, callback, *custom_filters, run_task=None, **kwargs): + """ + Register handler for poll_answer + + Example: + + .. code-block:: python3 + + dp.register_poll_answer_handler(some_poll_answer_handler) + + :param callback: + :param custom_filters: + :param run_task: run callback in task (no wait results) + :param kwargs: + """ + filters_set = self.filters_factory.resolve(self.poll_answer_handlers, + *custom_filters, + **kwargs) + self.poll_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + + def poll_answer_handler(self, *custom_filters, run_task=None, **kwargs): + """ + Decorator for poll_answer handler + + Example: + + .. code-block:: python3 + + @dp.poll_answer_handler() + async def some_poll_answer_handler(poll: types.Poll) + + :param custom_filters: + :param run_task: run callback in task (no wait results) + :param kwargs: + """ + + def decorator(callback): + self.register_poll_answer_handler(callback, *custom_filters, run_task=run_task, + **kwargs) + return callback + + return decorator def register_errors_handler(self, callback, *custom_filters, exception=None, run_task=None, **kwargs): """ diff --git a/aiogram/types/message_entity.py b/aiogram/types/message_entity.py index 98191e43..d0dce7c5 100644 --- a/aiogram/types/message_entity.py +++ b/aiogram/types/message_entity.py @@ -18,6 +18,7 @@ class MessageEntity(base.TelegramObject): length: base.Integer = fields.Field() url: base.String = fields.Field() user: User = fields.Field(base=User) + langugage: base.String = fields.Field() def get_text(self, text): """ diff --git a/aiogram/types/poll.py b/aiogram/types/poll.py index 316bca2d..16f83634 100644 --- a/aiogram/types/poll.py +++ b/aiogram/types/poll.py @@ -2,15 +2,42 @@ import typing from . import base from . import fields +from .user import User class PollOption(base.TelegramObject): + """ + This object contains information about one answer option in a poll. + + https://core.telegram.org/bots/api#polloption + """ text: base.String = fields.Field() voter_count: base.Integer = fields.Field() +class PollAnswer(base.TelegramObject): + """ + This object represents an answer of a user in a non-anonymous poll. + + https://core.telegram.org/bots/api#pollanswer + """ + poll_id: base.String = fields.Field() + user: User = fields.Field() + option_ids: typing.List[base.Integer] = fields.ListField() + + class Poll(base.TelegramObject): + """ + This object contains information about a poll. + + https://core.telegram.org/bots/api#poll + """ id: base.String = fields.Field() question: base.String = fields.Field() options: typing.List[PollOption] = fields.ListField(base=PollOption) + total_voter_count: base.Integer = fields.Field() is_closed: base.Boolean = fields.Field() + is_anonymous: base.Boolean = fields.Field() + poll_type: base.String = fields.Field(alias="type") + allows_multiple_answers: base.Boolean = fields.Field() + correct_option_id: base.Integer = fields.Field() diff --git a/aiogram/types/reply_keyboard.py b/aiogram/types/reply_keyboard.py index 8eda21f9..4f4c2404 100644 --- a/aiogram/types/reply_keyboard.py +++ b/aiogram/types/reply_keyboard.py @@ -4,6 +4,18 @@ from . import base from . import fields +class KeyboardButtonPollType(base.TelegramObject): + """ + This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed. + + https://core.telegram.org/bots/api#keyboardbuttonpolltype + """ + poll_type: base.String = fields.Field(alias="type") + + def __init__(self, poll_type: base.String): + super(KeyboardButtonPollType, self).__init__(poll_type=poll_type) + + class ReplyKeyboardMarkup(base.TelegramObject): """ This object represents a custom keyboard with reply options (see Introduction to bots for details and examples). @@ -81,14 +93,16 @@ class ReplyKeyboardMarkup(base.TelegramObject): class KeyboardButton(base.TelegramObject): """ - This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields are mutually exclusive. + This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields request_contact, request_location, and request_poll are mutually exclusive. Note: request_contact and request_location options will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + Note: request_poll option will only work in Telegram versions released after 23 January, 2020. Older clients will receive unsupported message. https://core.telegram.org/bots/api#keyboardbutton """ text: base.String = fields.Field() request_contact: base.Boolean = fields.Field() request_location: base.Boolean = fields.Field() + request_poll: KeyboardButtonPollType = fields.Field() def __init__(self, text: base.String, request_contact: base.Boolean = None, diff --git a/aiogram/types/update.py b/aiogram/types/update.py index 9f8ce0fb..2146cb9d 100644 --- a/aiogram/types/update.py +++ b/aiogram/types/update.py @@ -6,7 +6,7 @@ from .callback_query import CallbackQuery from .chosen_inline_result import ChosenInlineResult from .inline_query import InlineQuery from .message import Message -from .poll import Poll +from .poll import Poll, PollAnswer from .pre_checkout_query import PreCheckoutQuery from .shipping_query import ShippingQuery from ..utils import helper @@ -30,6 +30,7 @@ class Update(base.TelegramObject): shipping_query: ShippingQuery = fields.Field(base=ShippingQuery) pre_checkout_query: PreCheckoutQuery = fields.Field(base=PreCheckoutQuery) poll: Poll = fields.Field(base=Poll) + poll_answer: PollAnswer = fields.Field(base=PollAnswer) def __hash__(self): return self.update_id @@ -58,3 +59,5 @@ class AllowedUpdates(helper.Helper): CALLBACK_QUERY = helper.ListItem() # callback_query SHIPPING_QUERY = helper.ListItem() # shipping_query PRE_CHECKOUT_QUERY = helper.ListItem() # pre_checkout_query + POLL = helper.ListItem() # poll + POLL_ANSWER = helper.ListItem() # poll_answer diff --git a/aiogram/types/user.py b/aiogram/types/user.py index 2bcdd032..8263cfc2 100644 --- a/aiogram/types/user.py +++ b/aiogram/types/user.py @@ -22,6 +22,9 @@ class User(base.TelegramObject): last_name: base.String = fields.Field() username: base.String = fields.Field() language_code: base.String = fields.Field() + can_join_groups: base.Boolean = fields.Field() + can_read_all_group_messages: base.Boolean = fields.Field() + supports_inline_queries: base.Boolean = fields.Field() @property def full_name(self): From caf9f9e411e94970fefcb6f035bde43862ce4d16 Mon Sep 17 00:00:00 2001 From: gabbhack <43146729+gabbhack@users.noreply.github.com> Date: Thu, 23 Jan 2020 20:49:43 +0500 Subject: [PATCH 06/11] typofix --- aiogram/types/message_entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiogram/types/message_entity.py b/aiogram/types/message_entity.py index d0dce7c5..77b23c5c 100644 --- a/aiogram/types/message_entity.py +++ b/aiogram/types/message_entity.py @@ -18,7 +18,7 @@ class MessageEntity(base.TelegramObject): length: base.Integer = fields.Field() url: base.String = fields.Field() user: User = fields.Field(base=User) - langugage: base.String = fields.Field() + language: base.String = fields.Field() def get_text(self, text): """ From a8debbba0462bd2d789b34e5d4869929b6274ba2 Mon Sep 17 00:00:00 2001 From: gabbhack <43146729+gabbhack@users.noreply.github.com> Date: Thu, 23 Jan 2020 20:51:25 +0500 Subject: [PATCH 07/11] poll_type to type --- aiogram/types/poll.py | 2 +- aiogram/types/reply_keyboard.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aiogram/types/poll.py b/aiogram/types/poll.py index 16f83634..84fde48d 100644 --- a/aiogram/types/poll.py +++ b/aiogram/types/poll.py @@ -38,6 +38,6 @@ class Poll(base.TelegramObject): total_voter_count: base.Integer = fields.Field() is_closed: base.Boolean = fields.Field() is_anonymous: base.Boolean = fields.Field() - poll_type: base.String = fields.Field(alias="type") + type: base.String = fields.Field() allows_multiple_answers: base.Boolean = fields.Field() correct_option_id: base.Integer = fields.Field() diff --git a/aiogram/types/reply_keyboard.py b/aiogram/types/reply_keyboard.py index 4f4c2404..7d2283ec 100644 --- a/aiogram/types/reply_keyboard.py +++ b/aiogram/types/reply_keyboard.py @@ -10,10 +10,10 @@ class KeyboardButtonPollType(base.TelegramObject): https://core.telegram.org/bots/api#keyboardbuttonpolltype """ - poll_type: base.String = fields.Field(alias="type") + type: base.String = fields.Field() - def __init__(self, poll_type: base.String): - super(KeyboardButtonPollType, self).__init__(poll_type=poll_type) + def __init__(self, type: base.String): + super(KeyboardButtonPollType, self).__init__(type=type) class ReplyKeyboardMarkup(base.TelegramObject): From ca9a4440d1542267f07bc33fe9096a3965a7b6ef Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Thu, 23 Jan 2020 22:37:54 +0200 Subject: [PATCH 08/11] - Update docstring of KeyboardButton - Fix PollAnswer model and export it from `aiogram.types` - Fix dispatcher poll_answer handlers registration - Add actions to LoggingMiddleware --- aiogram/contrib/middlewares/logging.py | 14 ++++++++++++++ aiogram/dispatcher/dispatcher.py | 2 +- aiogram/types/__init__.py | 3 ++- aiogram/types/poll.py | 2 +- aiogram/types/reply_keyboard.py | 10 +++++++--- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/aiogram/contrib/middlewares/logging.py b/aiogram/contrib/middlewares/logging.py index 9f389b60..308d0e10 100644 --- a/aiogram/contrib/middlewares/logging.py +++ b/aiogram/contrib/middlewares/logging.py @@ -146,6 +146,20 @@ class LoggingMiddleware(BaseMiddleware): if timeout > 0: self.logger.info(f"Process update [ID:{update.update_id}]: [failed] (in {timeout} ms)") + async def on_pre_process_poll(self, poll, data): + self.logger.info(f"Received poll [ID:{poll.id}]") + + async def on_post_process_poll(self, poll, results, data): + self.logger.debug(f"{HANDLED_STR[bool(len(results))]} poll [ID:{poll.id}]") + + async def on_pre_process_poll_answer(self, poll_answer, data): + self.logger.info(f"Received poll answer [ID:{poll_answer.poll_id}] " + f"from user [ID:{poll_answer.user.id}]") + + async def on_post_process_poll_answer(self, poll_answer, results, data): + self.logger.debug(f"{HANDLED_STR[bool(len(results))]} poll answer [ID:{poll_answer.poll_id}] " + f"from user [ID:{poll_answer.user.id}]") + class LoggingFilter(logging.Filter): """ diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index af39bbc7..dc793b76 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -917,7 +917,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): filters_set = self.filters_factory.resolve(self.poll_answer_handlers, *custom_filters, **kwargs) - self.poll_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + self.poll_answer_handlers.register(self._wrap_async_task(callback, run_task), filters_set) def poll_answer_handler(self, *custom_filters, run_task=None, **kwargs): """ diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 37dc4b3e..018c593a 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -45,7 +45,7 @@ from .passport_element_error import PassportElementError, PassportElementErrorDa PassportElementErrorSelfie from .passport_file import PassportFile from .photo_size import PhotoSize -from .poll import PollOption, Poll +from .poll import PollOption, Poll, PollAnswer from .pre_checkout_query import PreCheckoutQuery from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove from .response_parameters import ResponseParameters @@ -147,6 +147,7 @@ __all__ = ( 'PassportFile', 'PhotoSize', 'Poll', + 'PollAnswer', 'PollOption', 'PreCheckoutQuery', 'ReplyKeyboardMarkup', diff --git a/aiogram/types/poll.py b/aiogram/types/poll.py index 84fde48d..e5a485d4 100644 --- a/aiogram/types/poll.py +++ b/aiogram/types/poll.py @@ -22,7 +22,7 @@ class PollAnswer(base.TelegramObject): https://core.telegram.org/bots/api#pollanswer """ poll_id: base.String = fields.Field() - user: User = fields.Field() + user: User = fields.Field(base=User) option_ids: typing.List[base.Integer] = fields.ListField() diff --git a/aiogram/types/reply_keyboard.py b/aiogram/types/reply_keyboard.py index 7d2283ec..59804f08 100644 --- a/aiogram/types/reply_keyboard.py +++ b/aiogram/types/reply_keyboard.py @@ -93,9 +93,13 @@ class ReplyKeyboardMarkup(base.TelegramObject): class KeyboardButton(base.TelegramObject): """ - This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields request_contact, request_location, and request_poll are mutually exclusive. - Note: request_contact and request_location options will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. - Note: request_poll option will only work in Telegram versions released after 23 January, 2020. Older clients will receive unsupported message. + This object represents one button of the reply keyboard. + For simple text buttons String can be used instead of this object to specify text of the button. + Optional fields request_contact, request_location, and request_poll are mutually exclusive. + Note: request_contact and request_location options will only work in Telegram versions released after 9 April, 2016. + Older clients will ignore them. + Note: request_poll option will only work in Telegram versions released after 23 January, 2020. + Older clients will receive unsupported message. https://core.telegram.org/bots/api#keyboardbutton """ From d1ff0046a519a2245869edf08656ae294244aa3c Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Thu, 23 Jan 2020 22:44:24 +0200 Subject: [PATCH 09/11] Export KeyboardButtonPollType --- aiogram/types/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 018c593a..e4e27e1a 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -47,7 +47,7 @@ from .passport_file import PassportFile from .photo_size import PhotoSize from .poll import PollOption, Poll, PollAnswer from .pre_checkout_query import PreCheckoutQuery -from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove +from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, KeyboardButtonPollType from .response_parameters import ResponseParameters from .shipping_address import ShippingAddress from .shipping_option import ShippingOption @@ -126,6 +126,7 @@ __all__ = ( 'InputVenueMessageContent', 'Invoice', 'KeyboardButton', + 'KeyboardButtonPollType', 'LabeledPrice', 'Location', 'LoginUrl', From 7c8006d742d26ddba8088be26f85f02f3a703798 Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Thu, 23 Jan 2020 22:49:35 +0200 Subject: [PATCH 10/11] #262: Fix aiohttp-socks version in setup.py --- dev_requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index be2c8f7d..40a74f81 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -13,6 +13,6 @@ wheel>=0.31.1 sphinx>=2.0.1 sphinx-rtd-theme>=0.4.3 sphinxcontrib-programoutput>=0.14 -aiohttp-socks>=0.3.3 +aiohttp-socks>=0.3.4 rethinkdb>=2.4.1 coverage==4.5.3 diff --git a/setup.py b/setup.py index c63094b9..b21b4e57 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ setup( ], extras_require={ 'proxy': [ - 'aiohttp-socks>=3.3,<4.0.0', + 'aiohttp-socks>=0.3.4,<0.4.0', ], 'fast': [ 'uvloop>=0.14.0,<0.15.0', From 5db726d7585c5252343642f1201c4775ac47bfeb Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Thu, 23 Jan 2020 23:13:07 +0200 Subject: [PATCH 11/11] Add IsSenderContact filter --- aiogram/dispatcher/dispatcher.py | 7 +++++++ aiogram/dispatcher/filters/__init__.py | 3 ++- aiogram/dispatcher/filters/builtin.py | 22 ++++++++++++++++++++++ docs/source/dispatcher/filters.rst | 6 ++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index dc793b76..ec4a3fa8 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -11,6 +11,7 @@ from aiohttp.helpers import sentinel from aiogram.utils.deprecated import renamed_argument from .filters import Command, ContentTypeFilter, ExceptionsFilter, FiltersFactory, HashTag, Regexp, \ RegexpCommandsFilter, StateFilter, Text, IDFilter, AdminFilter, IsReplyFilter +from .filters.builtin import IsSenderContact from .handler import Handler from .middlewares import MiddlewareManager from .storage import BaseStorage, DELTA, DisabledStorage, EXCEEDED_COUNT, FSMContext, \ @@ -153,6 +154,12 @@ class Dispatcher(DataMixin, ContextInstanceMixin): self.channel_post_handlers, self.edited_channel_post_handlers, ]) + filters_factory.bind(IsSenderContact, event_handlers=[ + self.message_handlers, + self.edited_message_handlers, + self.channel_post_handlers, + self.edited_channel_post_handlers, + ]) def __del__(self): self.stop_polling() diff --git a/aiogram/dispatcher/filters/__init__.py b/aiogram/dispatcher/filters/__init__.py index 67c13872..6de3cc7a 100644 --- a/aiogram/dispatcher/filters/__init__.py +++ b/aiogram/dispatcher/filters/__init__.py @@ -1,6 +1,6 @@ from .builtin import Command, CommandHelp, CommandPrivacy, CommandSettings, CommandStart, ContentTypeFilter, \ ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, \ - Text, IDFilter, AdminFilter, IsReplyFilter + Text, IDFilter, AdminFilter, IsReplyFilter, IsSenderContact from .factory import FiltersFactory from .filters import AbstractFilter, BoundFilter, Filter, FilterNotPassed, FilterRecord, execute_filter, \ check_filters, get_filter_spec, get_filters_spec @@ -26,6 +26,7 @@ __all__ = [ 'Text', 'IDFilter', 'IsReplyFilter', + 'IsSenderContact', 'AdminFilter', 'get_filter_spec', 'get_filters_spec', diff --git a/aiogram/dispatcher/filters/builtin.py b/aiogram/dispatcher/filters/builtin.py index 683ee841..0a81998a 100644 --- a/aiogram/dispatcher/filters/builtin.py +++ b/aiogram/dispatcher/filters/builtin.py @@ -453,6 +453,28 @@ class ContentTypeFilter(BoundFilter): message.content_type in self.content_types +class IsSenderContact(BoundFilter): + """ + Filter check that the contact matches the sender + + `is_sender_contact=True` - contact matches the sender + `is_sender_contact=False` - result will be inverted + """ + key = 'is_sender_contact' + + def __init__(self, is_sender_contact: bool): + self.is_sender_contact = is_sender_contact + + async def check(self, message: types.Message) -> bool: + if not message.contact: + return False + is_sender_contact = message.contact.user_id == message.from_user.id + if self.is_sender_contact: + return is_sender_contact + else: + return not is_sender_contact + + class StateFilter(BoundFilter): """ Check user state diff --git a/docs/source/dispatcher/filters.rst b/docs/source/dispatcher/filters.rst index b174f1ef..af06b73e 100644 --- a/docs/source/dispatcher/filters.rst +++ b/docs/source/dispatcher/filters.rst @@ -94,6 +94,12 @@ ContentTypeFilter :members: :show-inheritance: +IsSenderContact +--------------- + +.. autoclass:: aiogram.dispatcher.filters.builtin.IsSenderContact + :members: + :show-inheritance: StateFilter -----------