From 64393048dd82a9a3c6fe7e54f068bf4aaba000e2 Mon Sep 17 00:00:00 2001 From: Fenicu Date: Sun, 7 Nov 2021 03:49:31 +0300 Subject: [PATCH 1/5] custom filter for filters_factory example (#688) * custom filter for filters_factory example * Shortened the code * added new example of filter registration * simplifying Filters and more handlers * upgrade example * black reformat --- examples/custom_filter_example.py | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 examples/custom_filter_example.py diff --git a/examples/custom_filter_example.py b/examples/custom_filter_example.py new file mode 100644 index 00000000..beebbfcf --- /dev/null +++ b/examples/custom_filter_example.py @@ -0,0 +1,125 @@ +from typing import List, Union +from aiogram import Bot, Dispatcher, executor, types +from aiogram.dispatcher.filters import BoundFilter + +API_TOKEN = "BOT_TOKEN_HERE" + + +ADMIN_IDS = [ + 000000000, + 111111111, + 222222222, + 333333333, + 444444444, +] + + +bot = Bot(token=API_TOKEN) +dp = Dispatcher(bot) + + +class GlobalAdminFilter(BoundFilter): + """ + Check if the user is a bot admin + """ + + key = "global_admin" + + def __init__(self, global_admin: bool): + self.global_admin = global_admin + + async def check(self, obj: Union[types.Message, types.CallbackQuery]): + user = obj.from_user + if user.id in ADMIN_IDS: + return self.global_admin is True + return self.global_admin is False + + +class MimeTypeFilter(BoundFilter): + """ + Check document mime_type + """ + + key = "mime_type" + + def __init__(self, mime_type: Union[str, List[str]]): + if isinstance(mime_type, str): + self.mime_types = [mime_type] + + elif isinstance(mime_type, list): + self.mime_types = mime_type + + else: + raise ValueError( + f"filter mime_types must be a str or list of str, not {type(mime_type).__name__}" + ) + + async def check(self, obj: types.Message): + if not obj.document: + return False + + if obj.document.mime_type in self.mime_types: + return True + + return False + + +class LettersInMessageFilter(BoundFilter): + """ + Checking for the number of characters in a message/callback_data + """ + + key = "letters" + + def __init__(self, letters: int): + if isinstance(letters, int): + self.letters = letters + else: + raise ValueError( + f"filter letters must be a int, not {type(letters).__name__}" + ) + + async def check(self, obj: Union[types.Message, types.CallbackQuery]): + data = obj.text or obj.data + if data: + letters_in_message = len(data) + if letters_in_message > self.letters: + return False + return {"letters": letters_in_message} + return False + + +# Binding filters +dp.filters_factory.bind( + GlobalAdminFilter, + exclude_event_handlers=[dp.channel_post_handlers, dp.edited_channel_post_handlers], +) +dp.filters_factory.bind(MimeTypeFilter, event_handlers=[dp.message_handlers]) +dp.filters_factory.bind(LettersInMessageFilter) + + +@dp.message_handler(letters=5) +async def handle_letters_in_message(message: types.Message, letters: int): + await message.answer(f"Message too short!\nYou sent only {letters} letters") + + +@dp.message_handler(content_types=types.ContentTypes.DOCUMENT, mime_type="text/plain") +async def handle_txt_documents(message: types.Message): + await message.answer("This is a text file!") + + +@dp.message_handler( + content_types=types.ContentTypes.DOCUMENT, mime_type=["image/jpeg", "image/png"] +) +async def handle_photo_documents(message: types.Message): + await message.answer("This is a photo file!") + + +@dp.message_handler(global_admin=True) +async def handle_admins(message: types.Message): + await message.answer("Congratulations, you are global admin!") + + +if __name__ == "__main__": + allowed_updates = types.AllowedUpdates.MESSAGE | types.AllowedUpdates.CALLBACK_QUERY + executor.start_polling(dp, allowed_updates=allowed_updates, skip_updates=True) From bccfca7caefe784f2baadb7251753374060a0fc5 Mon Sep 17 00:00:00 2001 From: barbashovtd Date: Tue, 9 Nov 2021 00:28:30 +0300 Subject: [PATCH 2/5] Project typos fix (#691) * Fixed typo in fields.py * Update fields.py * Whole project typos fix --- aiogram/dispatcher/webhook.py | 2 +- aiogram/types/fields.py | 4 ++-- aiogram/utils/helper.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aiogram/dispatcher/webhook.py b/aiogram/dispatcher/webhook.py index db5efd7b..e8246b59 100644 --- a/aiogram/dispatcher/webhook.py +++ b/aiogram/dispatcher/webhook.py @@ -74,7 +74,7 @@ allow_ip(TELEGRAM_SUBNET_1, TELEGRAM_SUBNET_2) class WebhookRequestHandler(web.View): """ - Simple Wehhook request handler for aiohttp web server. + Simple Webhook request handler for aiohttp web server. You need to register that in app: diff --git a/aiogram/types/fields.py b/aiogram/types/fields.py index 11c83eab..d7a1d8ca 100644 --- a/aiogram/types/fields.py +++ b/aiogram/types/fields.py @@ -118,7 +118,7 @@ class Field(BaseField): class ListField(Field): """ - Field contains list ob objects + The field contains a list of objects """ def __init__(self, *args, **kwargs): @@ -162,7 +162,7 @@ class ListOfLists(Field): class DateTimeField(Field): """ - In this field st_ored datetime + In this field stored datetime in: unixtime out: datetime diff --git a/aiogram/utils/helper.py b/aiogram/utils/helper.py index 55a134a3..3a9df56e 100644 --- a/aiogram/utils/helper.py +++ b/aiogram/utils/helper.py @@ -79,7 +79,7 @@ class HelperMode(Helper): @classmethod def _snake_case(cls, text): """ - Transform text to snake cale (Based on SCREAMING_SNAKE_CASE) + Transform text to snake case (Based on SCREAMING_SNAKE_CASE) :param text: :return: From d646e198520e4076f713ca841f45f4e07e2391eb Mon Sep 17 00:00:00 2001 From: Cyril Margorin Date: Wed, 1 Dec 2021 03:43:12 +0300 Subject: [PATCH 3/5] Fix fsmcontextproxy update (#755) * Create test case for invalid 'FSMContextProxy.update()' function * Fix invalid 'FSMContextProxy.update()' function * Verify that written data has been stored and read back correctly --- aiogram/dispatcher/storage.py | 2 ++ tests/test_dispatcher/test_fsm_context.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/test_dispatcher/test_fsm_context.py diff --git a/aiogram/dispatcher/storage.py b/aiogram/dispatcher/storage.py index 340b6352..63ce25b2 100644 --- a/aiogram/dispatcher/storage.py +++ b/aiogram/dispatcher/storage.py @@ -408,6 +408,8 @@ class FSMContextProxy: def update(self, data=None, **kwargs): self._check_closed() + if data is None: + data = {} self._data.update(data, **kwargs) def pop(self, key, default=None): diff --git a/tests/test_dispatcher/test_fsm_context.py b/tests/test_dispatcher/test_fsm_context.py new file mode 100644 index 00000000..3189e4e6 --- /dev/null +++ b/tests/test_dispatcher/test_fsm_context.py @@ -0,0 +1,14 @@ +import pytest +from aiogram.contrib.fsm_storage.memory import MemoryStorage +from aiogram.dispatcher import FSMContext + + +class TestFSMContext: + @pytest.mark.asyncio + async def test_update_data(self): + context = FSMContext(MemoryStorage(), chat=1, user=1) + async with context.proxy() as data: + data.update(key1="value1", key2="value2") + async with context.proxy() as data: + assert data['key1'] == "value1" + assert data['key2'] == "value2" From 5bcbcedebad98ccae0f8bd1c97c81fb2e05041e9 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Tue, 7 Dec 2021 01:41:07 +0300 Subject: [PATCH 4/5] fix: header values must be str instances (#765) #762 --- aiogram/dispatcher/webhook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiogram/dispatcher/webhook.py b/aiogram/dispatcher/webhook.py index e8246b59..11e3238c 100644 --- a/aiogram/dispatcher/webhook.py +++ b/aiogram/dispatcher/webhook.py @@ -145,7 +145,7 @@ class WebhookRequestHandler(web.View): web_response = web.Response(text='ok') if self.request.app.get('RETRY_AFTER', None): - web_response.headers['Retry-After'] = self.request.app['RETRY_AFTER'] + web_response.headers['Retry-After'] = str(self.request.app['RETRY_AFTER']) return web_response From 910e4d778443436f895326472ab03ab9b4deebb7 Mon Sep 17 00:00:00 2001 From: Oleg A Date: Tue, 7 Dec 2021 21:03:13 +0300 Subject: [PATCH 5/5] Bot API 5.5 (#768) * chore: bump version 5.5 * feat: add banChatSenderChat and unbanChatSenderChat Added the methods banChatSenderChat and unbanChatSenderChat for banning and unbanning channel chats in supergroups and channels. * feat: add has_private_forwards Added the field has_private_forwards to the class Chat for private chats, which can be used to check the possibility of mentioning the user by their ID. * feat: add has_protected_content Added the field has_protected_content to the classes Chat and Message. * feat: add is_automatic_forward Added the field is_automatic_forward to the class Message. * feat: add shortcuts Added Chat.ban_sender_chat() and Chat.unban_sender_chat() shortcuts. * docs: add new methods Source --- README.md | 2 +- aiogram/__init__.py | 4 +-- aiogram/bot/api.py | 4 ++- aiogram/bot/bot.py | 56 ++++++++++++++++++++++++++++++++++++++++ aiogram/types/chat.py | 26 +++++++++++++++++++ aiogram/types/message.py | 2 ++ docs/source/index.rst | 2 +- 7 files changed, 91 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2dd5394a..f9052665 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![PyPi status](https://img.shields.io/pypi/status/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) [![Downloads](https://img.shields.io/pypi/dm/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) [![Supported python versions](https://img.shields.io/pypi/pyversions/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) -[![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-5.4-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api) +[![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-5.5-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api) [![Documentation Status](https://img.shields.io/readthedocs/aiogram?style=flat-square)](http://docs.aiogram.dev/en/latest/?badge=latest) [![Github issues](https://img.shields.io/github/issues/aiogram/aiogram.svg?style=flat-square)](https://github.com/aiogram/aiogram/issues) [![MIT License](https://img.shields.io/pypi/l/aiogram.svg?style=flat-square)](https://opensource.org/licenses/MIT) diff --git a/aiogram/__init__.py b/aiogram/__init__.py index a9581081..5216689a 100644 --- a/aiogram/__init__.py +++ b/aiogram/__init__.py @@ -43,5 +43,5 @@ __all__ = ( 'utils', ) -__version__ = '2.16' -__api_version__ = '5.4' +__version__ = '2.17' +__api_version__ = '5.5' diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index b0bb790c..fca5b738 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -189,7 +189,7 @@ class Methods(Helper): """ Helper for Telegram API Methods listed on https://core.telegram.org/bots/api - List is updated to Bot API 5.4 + List is updated to Bot API 5.5 """ mode = HelperMode.lowerCamelCase @@ -230,6 +230,8 @@ class Methods(Helper): RESTRICT_CHAT_MEMBER = Item() # restrictChatMember PROMOTE_CHAT_MEMBER = Item() # promoteChatMember SET_CHAT_ADMINISTRATOR_CUSTOM_TITLE = Item() # setChatAdministratorCustomTitle + BAN_CHAT_SENDER_CHAT = Item() # banChatSenderChat + UNBAN_CHAT_SENDER_CHAT = Item() # unbanChatSenderChat SET_CHAT_PERMISSIONS = Item() # setChatPermissions EXPORT_CHAT_INVITE_LINK = Item() # exportChatInviteLink CREATE_CHAT_INVITE_LINK = Item() # createChatInviteLink diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 436b83a4..d5aa5d65 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1814,6 +1814,62 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return await self.request(api.Methods.SET_CHAT_ADMINISTRATOR_CUSTOM_TITLE, payload) + async def ban_chat_sender_chat( + self, + chat_id: typing.Union[base.Integer, base.String], + sender_chat_id: base.Integer, + until_date: typing.Union[ + base.Integer, datetime.datetime, datetime.timedelta, None + ] = None, + ): + """Ban a channel chat in a supergroup or a channel. + + The owner of the chat will not be able to send messages and join + live streams on behalf of the chat, unless it is unbanned first. + The bot must be an administrator in the supergroup or channel + for this to work and must have the appropriate administrator + rights. Returns True on success. + + Source: https://core.telegram.org/bots/api#banchatsenderchat + + :param chat_id: Unique identifier for the target chat or + username of the target channel (in the format + @channelusername) + :param sender_chat_id: Unique identifier of the target sender + chat + :param until_date: Date when the sender chat will be unbanned, + unix time. If the chat is banned for more than 366 days or + less than 30 seconds from the current time they are + considered to be banned forever. + """ + until_date = prepare_arg(until_date) + payload = generate_payload(**locals()) + + return await self.request(api.Methods.BAN_CHAT_SENDER_CHAT, payload) + + async def unban_chat_sender_chat( + self, + chat_id: typing.Union[base.Integer, base.String], + sender_chat_id: base.Integer, + ): + """Unban a previously banned channel chat in a supergroup or + channel. + + The bot must be an administrator for this to work and must have + the appropriate administrator rights. Returns True on success. + + Source: https://core.telegram.org/bots/api#unbanchatsenderchat + + :param chat_id: Unique identifier for the target chat or + username of the target channel (in the format + @channelusername) + :param sender_chat_id: Unique identifier of the target sender + chat + """ + payload = generate_payload(**locals()) + + return await self.request(api.Methods.UNBAN_CHAT_SENDER_CHAT, payload) + async def set_chat_permissions(self, chat_id: typing.Union[base.Integer, base.String], permissions: types.ChatPermissions) -> base.Boolean: """ diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index a2487b59..00b7a656 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -30,12 +30,14 @@ class Chat(base.TelegramObject): all_members_are_administrators: base.Boolean = fields.Field() photo: ChatPhoto = fields.Field(base=ChatPhoto) bio: base.String = fields.Field() + has_private_forwards: base.Boolean = fields.Field() description: base.String = fields.Field() invite_link: base.String = fields.Field() pinned_message: 'Message' = fields.Field(base='Message') permissions: ChatPermissions = fields.Field(base=ChatPermissions) slow_mode_delay: base.Integer = fields.Field() message_auto_delete_time: base.Integer = fields.Field() + has_protected_content: base.Boolean = fields.Field() sticker_set_name: base.String = fields.Field() can_set_sticker_set: base.Boolean = fields.Field() linked_chat_id: base.Integer = fields.Field() @@ -621,6 +623,30 @@ class Chat(base.TelegramObject): message_id=message_id, ) + async def ban_sender_chat( + self, + sender_chat_id: base.Integer, + until_date: typing.Union[ + base.Integer, datetime.datetime, datetime.timedelta, None + ] = None, + ): + """Shortcut for banChatSenderChat method.""" + return await self.bot.ban_chat_sender_chat( + chat_id=self.id, + sender_chat_id=sender_chat_id, + until_date=until_date, + ) + + async def unban_sender_chat( + self, + sender_chat_id: base.Integer, + ): + """Shortcut for unbanChatSenderChat method.""" + return await self.bot.unban_chat_sender_chat( + chat_id=self.id, + sender_chat_id=sender_chat_id, + ) + def __int__(self): return self.id diff --git a/aiogram/types/message.py b/aiogram/types/message.py index 403ef954..fa73f2e4 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -58,9 +58,11 @@ class Message(base.TelegramObject): forward_from_message_id: base.Integer = fields.Field() forward_signature: base.String = fields.Field() forward_date: datetime.datetime = fields.DateTimeField() + is_automatic_forward: base.Boolean = fields.Field() reply_to_message: Message = fields.Field(base="Message") via_bot: User = fields.Field(base=User) edit_date: datetime.datetime = fields.DateTimeField() + has_protected_content: base.Boolean = fields.Field() media_group_id: base.String = fields.Field() author_signature: base.String = fields.Field() forward_sender_name: base.String = fields.Field() diff --git a/docs/source/index.rst b/docs/source/index.rst index 1b9c752d..a4ce2354 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,7 +22,7 @@ Welcome to aiogram's documentation! :target: https://pypi.python.org/pypi/aiogram :alt: Supported python versions - .. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.4-blue.svg?style=flat-square&logo=telegram + .. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.5-blue.svg?style=flat-square&logo=telegram :target: https://core.telegram.org/bots/api :alt: Telegram Bot API