diff --git a/docs/source/api/decorators.rst b/docs/source/api/decorators.rst
index 90a01cbe..c518891d 100644
--- a/docs/source/api/decorators.rst
+++ b/docs/source/api/decorators.rst
@@ -36,6 +36,7 @@ Index
:columns: 3
- :meth:`~Client.on_message`
+ - :meth:`~Client.on_bot_business_message`
- :meth:`~Client.on_edited_message`
- :meth:`~Client.on_callback_query`
- :meth:`~Client.on_message_reaction_updated`
@@ -58,6 +59,7 @@ Details
.. Decorators
.. autodecorator:: pyrogram.Client.on_message()
+.. autodecorator:: pyrogram.Client.on_bot_business_message()
.. autodecorator:: pyrogram.Client.on_edited_message()
.. autodecorator:: pyrogram.Client.on_callback_query()
.. autodecorator:: pyrogram.Client.on_message_reaction_updated()
diff --git a/docs/source/api/handlers.rst b/docs/source/api/handlers.rst
index cfe99bbe..6b39a3e6 100644
--- a/docs/source/api/handlers.rst
+++ b/docs/source/api/handlers.rst
@@ -36,6 +36,7 @@ Index
:columns: 3
- :class:`MessageHandler`
+ - :class:`BotBusinessMessageHandler`
- :class:`EditedMessageHandler`
- :class:`DeletedMessagesHandler`
- :class:`CallbackQueryHandler`
@@ -57,6 +58,7 @@ Details
.. Handlers
.. autoclass:: MessageHandler()
+.. autoclass:: BotBusinessMessageHandler()
.. autoclass:: EditedMessageHandler()
.. autoclass:: DeletedMessagesHandler()
.. autoclass:: CallbackQueryHandler()
diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py
index 696e52b1..5e0e4416 100644
--- a/pyrogram/dispatcher.py
+++ b/pyrogram/dispatcher.py
@@ -25,10 +25,26 @@ from collections import OrderedDict
import pyrogram
from pyrogram import utils
from pyrogram.handlers import (
- CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler, MessageReactionUpdatedHandler, MessageReactionCountUpdatedHandler, UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, ConversationHandler, ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler, StoryHandler
+ BotBusinessMessageHandler,
+ CallbackQueryHandler,
+ MessageHandler,
+ EditedMessageHandler,
+ DeletedMessagesHandler,
+ MessageReactionUpdatedHandler,
+ MessageReactionCountUpdatedHandler,
+ UserStatusHandler,
+ RawUpdateHandler,
+ InlineQueryHandler,
+ PollHandler,
+ ConversationHandler,
+ ChosenInlineResultHandler,
+ ChatMemberUpdatedHandler,
+ ChatJoinRequestHandler,
+ StoryHandler
)
from pyrogram.raw.types import (
UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage,
+ UpdateBotNewBusinessMessage,
UpdateEditMessage, UpdateEditChannelMessage,
UpdateDeleteMessages, UpdateDeleteChannelMessages,
UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery,
@@ -44,6 +60,7 @@ log = logging.getLogger(__name__)
class Dispatcher:
NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage)
+ NEW_BOT_BUSINESS_MESSAGE_UPDATES = (UpdateBotNewBusinessMessage,)
EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage)
DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages)
CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery)
@@ -77,6 +94,18 @@ class Dispatcher:
MessageHandler
)
+ async def bot_business_message_parser(update, users, chats):
+ return (
+ await pyrogram.types.Message._parse(
+ self.client,
+ update.message,
+ users,
+ chats,
+ business_connection_id=update.connection_id
+ ),
+ BotBusinessMessageHandler
+ )
+
async def edited_message_parser(update, users, chats):
# Edited messages are parsed the same way as new messages, but the handler is different
parsed, _ = await message_parser(update, users, chats)
@@ -154,6 +183,7 @@ class Dispatcher:
self.update_parsers = {
Dispatcher.NEW_MESSAGE_UPDATES: message_parser,
+ Dispatcher.NEW_BOT_BUSINESS_MESSAGE_UPDATES: bot_business_message_parser,
Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser,
Dispatcher.DELETE_MESSAGES_UPDATES: deleted_messages_parser,
Dispatcher.CALLBACK_QUERY_UPDATES: callback_query_parser,
diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py
index ab918668..ae118ad6 100644
--- a/pyrogram/handlers/__init__.py
+++ b/pyrogram/handlers/__init__.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see .
+from .bot_business_message_handler import BotBusinessMessageHandler
from .callback_query_handler import CallbackQueryHandler
from .chat_join_request_handler import ChatJoinRequestHandler
from .chat_member_updated_handler import ChatMemberUpdatedHandler
diff --git a/pyrogram/handlers/bot_business_message_handler.py b/pyrogram/handlers/bot_business_message_handler.py
new file mode 100644
index 00000000..1c1b4bb3
--- /dev/null
+++ b/pyrogram/handlers/bot_business_message_handler.py
@@ -0,0 +1,151 @@
+# Pyrofork - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+# Copyright (C) 2022-present Mayuri-Chan
+#
+# This file is part of Pyrofork.
+#
+# Pyrofork is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrofork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrofork. If not, see .
+from inspect import iscoroutinefunction
+from typing import Callable
+import pyrogram
+
+from pyrogram.types import Message, Identifier
+
+from .handler import Handler
+
+
+class BotBusinessMessageHandler(Handler):
+ """The Bot Business Message handler class. Used to handle new bot business messages.
+ It is intended to be used with :meth:`~pyrogram.Client.add_handler`
+
+ For a nicer way to register this handler, have a look at the
+ :meth:`~pyrogram.Client.on_bot_business_message` decorator.
+
+ Parameters:
+ callback (``Callable``):
+ Pass a function that will be called when a new Message arrives. It takes *(client, message)*
+ as positional arguments (look at the section below for a detailed description).
+
+ filters (:obj:`Filters`):
+ Pass one or more filters to allow only a subset of messages to be passed
+ in your callback function.
+
+ Other parameters:
+ client (:obj:`~pyrogram.Client`):
+ The Client itself, useful when you want to call other API methods inside the message handler.
+
+ message (:obj:`~pyrogram.types.Message`):
+ The received message.
+ """
+
+ def __init__(self, callback: Callable, filters=None):
+ self.original_callback = callback
+ super().__init__(self.resolve_future_or_callback, filters)
+
+ async def check_if_has_matching_listener(self, client: "pyrogram.Client", message: Message):
+ """
+ Checks if the message has a matching listener.
+
+ :param client: The Client object to check with.
+ :param message: The Message object to check with.
+ :return: A tuple of whether the message has a matching listener and its filters does match with the Message
+ and the matching listener;
+ """
+ from_user = message.from_user
+ from_user_id = from_user.id if from_user else None
+ from_user_username = from_user.username if from_user else None
+
+ message_id = getattr(message, "id", getattr(message, "message_id", None))
+
+ data = Identifier(
+ message_id=message_id,
+ chat_id=[message.chat.id, message.chat.username],
+ from_user_id=[from_user_id, from_user_username],
+ )
+
+ listener = client.get_listener_matching_with_data(data, pyrogram.enums.ListenerTypes.MESSAGE)
+
+ listener_does_match = False
+
+ if listener:
+ filters = listener.filters
+ if callable(filters):
+ if iscoroutinefunction(filters.__call__):
+ listener_does_match = await filters(client, message)
+ else:
+ listener_does_match = await client.loop.run_in_executor(
+ None, filters, client, message
+ )
+ else:
+ listener_does_match = True
+
+ return listener_does_match, listener
+
+ async def check(self, client: "pyrogram.Client", message: Message):
+ """
+ Checks if the message has a matching listener or handler and its filters does match with the Message.
+
+ :param client: Client object to check with.
+ :param message: Message object to check with.
+ :return: Whether the message has a matching listener or handler and its filters does match with the Message.
+ """
+ listener_does_match = (
+ await self.check_if_has_matching_listener(client, message)
+ )[0]
+
+ if callable(self.filters):
+ if iscoroutinefunction(self.filters.__call__):
+ handler_does_match = await self.filters(client, message)
+ else:
+ handler_does_match = await client.loop.run_in_executor(
+ None, self.filters, client, message
+ )
+ else:
+ handler_does_match = True
+
+ # let handler get the chance to handle if listener
+ # exists but its filters doesn't match
+ return listener_does_match or handler_does_match
+
+ async def resolve_future_or_callback(self, client: "pyrogram.Client", message: Message, *args):
+ """
+ Resolves the future or calls the callback of the listener if the message has a matching listener.
+
+ :param client: Client object to resolve or call with.
+ :param message: Message object to resolve or call with.
+ :param args: Arguments to call the callback with.
+ :return: None
+ """
+ listener_does_match, listener = await self.check_if_has_matching_listener(
+ client, message
+ )
+
+ if listener and listener_does_match:
+ client.remove_listener(listener)
+
+ if listener.future and not listener.future.done():
+ listener.future.set_result(message)
+
+ raise pyrogram.StopPropagation
+ elif listener.callback:
+ if iscoroutinefunction(listener.callback):
+ await listener.callback(client, message, *args)
+ else:
+ listener.callback(client, message, *args)
+
+ raise pyrogram.StopPropagation
+ else:
+ raise ValueError("Listener must have either a future or a callback")
+ else:
+ await self.original_callback(client, message, *args)
\ No newline at end of file
diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py
index 169b1ec3..b3b1dea5 100644
--- a/pyrogram/methods/decorators/__init__.py
+++ b/pyrogram/methods/decorators/__init__.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see .
+from .on_bot_business_message import OnBotBusinessMessage
from .on_callback_query import OnCallbackQuery
from .on_chat_join_request import OnChatJoinRequest
from .on_chat_member_updated import OnChatMemberUpdated
@@ -36,6 +37,7 @@ from .on_message_reaction_count_updated import OnMessageReactionCountUpdated
class Decorators(
OnMessage,
+ OnBotBusinessMessage,
OnEditedMessage,
OnDeletedMessages,
OnCallbackQuery,
diff --git a/pyrogram/methods/decorators/on_bot_business_message.py b/pyrogram/methods/decorators/on_bot_business_message.py
new file mode 100644
index 00000000..ba499484
--- /dev/null
+++ b/pyrogram/methods/decorators/on_bot_business_message.py
@@ -0,0 +1,62 @@
+# Pyrofork - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+# Copyright (C) 2022-present Mayuri-Chan
+#
+# This file is part of Pyrofork.
+#
+# Pyrofork is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrofork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrofork. If not, see .
+
+from typing import Callable
+
+import pyrogram
+from pyrogram.filters import Filter
+
+
+class OnBotBusinessMessage:
+ def on_bot_business_message(
+ self=None,
+ filters=None,
+ group: int = 0
+ ) -> Callable:
+ """Decorator for handling new bot business messages.
+
+ This does the same thing as :meth:`~pyrogram.Client.add_handler` using the
+ :obj:`~pyrogram.handlers.BotBusinessMessageHandler`.
+
+ Parameters:
+ filters (:obj:`~pyrogram.filters`, *optional*):
+ Pass one or more filters to allow only a subset of messages to be passed
+ in your function.
+
+ group (``int``, *optional*):
+ The group identifier, defaults to 0.
+ """
+
+ def decorator(func: Callable) -> Callable:
+ if isinstance(self, pyrogram.Client):
+ self.add_handler(pyrogram.handlers.BotBusinessMessageHandler(func, filters), group)
+ elif isinstance(self, Filter) or self is None:
+ if not hasattr(func, "handlers"):
+ func.handlers = []
+
+ func.handlers.append(
+ (
+ pyrogram.handlers.BotBusinessMessageHandler(func, self),
+ group if filters is None else filters
+ )
+ )
+
+ return func
+
+ return decorator
diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py
index 5eafa14b..a7612041 100644
--- a/pyrogram/types/messages_and_media/message.py
+++ b/pyrogram/types/messages_and_media/message.py
@@ -133,6 +133,9 @@ class Message(Object, Update):
reply_to_story (:obj:`~pyrogram.types.Story`, *optional*):
For replies, the original story.
+ business_connection_id (``str``, *optional*):
+ The business connection identifier.
+
mentioned (``bool``, *optional*):
The message contains a mention.
@@ -401,6 +404,7 @@ class Message(Object, Update):
client: "pyrogram.Client" = None,
id: int,
message_thread_id: int = None,
+ business_connection_id: str = None,
from_user: "types.User" = None,
sender_chat: "types.Chat" = None,
sender_business_bot: "types.User" = None,
@@ -504,6 +508,7 @@ class Message(Object, Update):
self.id = id
self.message_thread_id = message_thread_id
+ self.business_connection_id = business_connection_id
self.from_user = from_user
self.sender_chat = sender_chat
self.sender_business_bot = sender_business_bot
@@ -643,6 +648,7 @@ class Message(Object, Update):
chats: dict,
topics: dict = None,
is_scheduled: bool = False,
+ business_connection_id: str = None,
replies: int = 1
):
if isinstance(message, raw.types.MessageEmpty):
@@ -1039,6 +1045,7 @@ class Message(Object, Update):
parsed_message = Message(
id=message.id,
message_thread_id=message_thread_id,
+ business_connection_id=business_connection_id,
date=utils.timestamp_to_datetime(message.date),
chat=types.Chat._parse(client, message, users, chats, is_chat=True),
topics=None,
diff --git a/pyrogram/utils.py b/pyrogram/utils.py
index 4ed7fb20..007e3381 100644
--- a/pyrogram/utils.py
+++ b/pyrogram/utils.py
@@ -98,7 +98,8 @@ def get_input_media_from_file_id(
async def parse_messages(
client,
messages: "raw.types.messages.Messages",
- replies: int = 1
+ replies: int = 1,
+ business_connection_id: str = None
) -> List["types.Message"]:
users = {i.id: i for i in messages.users}
chats = {i.id: i for i in messages.chats}
@@ -112,7 +113,7 @@ async def parse_messages(
parsed_messages = []
for message in messages.messages:
- parsed_messages.append(await types.Message._parse(client, message, users, chats, topics, replies=0))
+ parsed_messages.append(await types.Message._parse(client, message, users, chats, topics, replies=0, business_connection_id=business_connection_id))
if replies:
messages_with_replies = {