Skip to content

Commit

Permalink
Pyrofork: Add UpdateBotNewBusinessMessage updates handler
Browse files Browse the repository at this point in the history
Signed-off-by: wulan17 <wulan17@nusantararom.org>
  • Loading branch information
wulan17 committed Apr 6, 2024
1 parent 3cce2ca commit cb63f07
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 1 deletion.
32 changes: 31 additions & 1 deletion pyrogram/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,26 @@
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,
Expand All @@ -44,6 +60,7 @@

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)
Expand Down Expand Up @@ -77,6 +94,18 @@ async def message_parser(update, users, chats):
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)
Expand Down Expand Up @@ -154,6 +183,7 @@ async def message_bot_a_reaction_parser(update, users, chats):

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,
Expand Down
1 change: 1 addition & 0 deletions pyrogram/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>.

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
Expand Down
151 changes: 151 additions & 0 deletions pyrogram/handlers/bot_business_message_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/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 <http://www.gnu.org/licenses/>.
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)
2 changes: 2 additions & 0 deletions pyrogram/methods/decorators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>.

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
Expand All @@ -36,6 +37,7 @@

class Decorators(
OnMessage,
OnBotBusinessMessage,
OnEditedMessage,
OnDeletedMessages,
OnCallbackQuery,
Expand Down
62 changes: 62 additions & 0 deletions pyrogram/methods/decorators/on_bot_business_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/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 <http://www.gnu.org/licenses/>.

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.MessageHandler`.
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.MessageHandler(func, filters), group)
elif isinstance(self, Filter) or self is None:
if not hasattr(func, "handlers"):
func.handlers = []

func.handlers.append(
(
pyrogram.handlers.MessageHandler(func, self),
group if filters is None else filters
)
)

return func

return decorator
7 changes: 7 additions & 0 deletions pyrogram/types/messages_and_media/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ class Message(Object, Update):
reply_to_story (:obj:`~pyrogram.types.Story`, *optional*):
For replies, the original story.
business_connection_id (``int``, *optional*):
The business connection identifier.
mentioned (``bool``, *optional*):
The message contains a mention.
Expand Down Expand Up @@ -401,6 +404,7 @@ def __init__(
client: "pyrogram.Client" = None,
id: int,
message_thread_id: int = None,
business_connection_id: int = None,
from_user: "types.User" = None,
sender_chat: "types.Chat" = None,
sender_business_bot: "types.User" = None,
Expand Down Expand Up @@ -504,6 +508,7 @@ def __init__(

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
Expand Down Expand Up @@ -643,6 +648,7 @@ async def _parse(
chats: dict,
topics: dict = None,
is_scheduled: bool = False,
business_connection_id: int = None,
replies: int = 1
):
if isinstance(message, raw.types.MessageEmpty):
Expand Down Expand Up @@ -1039,6 +1045,7 @@ async def _parse(
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,
Expand Down

0 comments on commit cb63f07

Please sign in to comment.