Pyrofork: Add UpdateBotNewBusinessMessage updates handler

Signed-off-by: wulan17 <wulan17@nusantararom.org>
This commit is contained in:
wulan17 2024-04-06 11:15:19 +07:00
parent 05c2770ca7
commit 48fed9347a
No known key found for this signature in database
GPG key ID: 318CD6CD3A6AC0A5
9 changed files with 261 additions and 3 deletions

View file

@ -36,6 +36,7 @@ Index
:columns: 3 :columns: 3
- :meth:`~Client.on_message` - :meth:`~Client.on_message`
- :meth:`~Client.on_bot_business_message`
- :meth:`~Client.on_edited_message` - :meth:`~Client.on_edited_message`
- :meth:`~Client.on_callback_query` - :meth:`~Client.on_callback_query`
- :meth:`~Client.on_message_reaction_updated` - :meth:`~Client.on_message_reaction_updated`
@ -58,6 +59,7 @@ Details
.. Decorators .. Decorators
.. autodecorator:: pyrogram.Client.on_message() .. autodecorator:: pyrogram.Client.on_message()
.. autodecorator:: pyrogram.Client.on_bot_business_message()
.. autodecorator:: pyrogram.Client.on_edited_message() .. autodecorator:: pyrogram.Client.on_edited_message()
.. autodecorator:: pyrogram.Client.on_callback_query() .. autodecorator:: pyrogram.Client.on_callback_query()
.. autodecorator:: pyrogram.Client.on_message_reaction_updated() .. autodecorator:: pyrogram.Client.on_message_reaction_updated()

View file

@ -36,6 +36,7 @@ Index
:columns: 3 :columns: 3
- :class:`MessageHandler` - :class:`MessageHandler`
- :class:`BotBusinessMessageHandler`
- :class:`EditedMessageHandler` - :class:`EditedMessageHandler`
- :class:`DeletedMessagesHandler` - :class:`DeletedMessagesHandler`
- :class:`CallbackQueryHandler` - :class:`CallbackQueryHandler`
@ -57,6 +58,7 @@ Details
.. Handlers .. Handlers
.. autoclass:: MessageHandler() .. autoclass:: MessageHandler()
.. autoclass:: BotBusinessMessageHandler()
.. autoclass:: EditedMessageHandler() .. autoclass:: EditedMessageHandler()
.. autoclass:: DeletedMessagesHandler() .. autoclass:: DeletedMessagesHandler()
.. autoclass:: CallbackQueryHandler() .. autoclass:: CallbackQueryHandler()

View file

@ -25,10 +25,26 @@ from collections import OrderedDict
import pyrogram import pyrogram
from pyrogram import utils from pyrogram import utils
from pyrogram.handlers import ( 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 ( from pyrogram.raw.types import (
UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage,
UpdateBotNewBusinessMessage,
UpdateEditMessage, UpdateEditChannelMessage, UpdateEditMessage, UpdateEditChannelMessage,
UpdateDeleteMessages, UpdateDeleteChannelMessages, UpdateDeleteMessages, UpdateDeleteChannelMessages,
UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery,
@ -44,6 +60,7 @@ log = logging.getLogger(__name__)
class Dispatcher: class Dispatcher:
NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage) NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage)
NEW_BOT_BUSINESS_MESSAGE_UPDATES = (UpdateBotNewBusinessMessage,)
EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage) EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage)
DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages) DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages)
CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery) CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery)
@ -77,6 +94,18 @@ class Dispatcher:
MessageHandler 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): async def edited_message_parser(update, users, chats):
# Edited messages are parsed the same way as new messages, but the handler is different # Edited messages are parsed the same way as new messages, but the handler is different
parsed, _ = await message_parser(update, users, chats) parsed, _ = await message_parser(update, users, chats)
@ -154,6 +183,7 @@ class Dispatcher:
self.update_parsers = { self.update_parsers = {
Dispatcher.NEW_MESSAGE_UPDATES: message_parser, Dispatcher.NEW_MESSAGE_UPDATES: message_parser,
Dispatcher.NEW_BOT_BUSINESS_MESSAGE_UPDATES: bot_business_message_parser,
Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser, Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser,
Dispatcher.DELETE_MESSAGES_UPDATES: deleted_messages_parser, Dispatcher.DELETE_MESSAGES_UPDATES: deleted_messages_parser,
Dispatcher.CALLBACK_QUERY_UPDATES: callback_query_parser, Dispatcher.CALLBACK_QUERY_UPDATES: callback_query_parser,

View file

@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>. # 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 .callback_query_handler import CallbackQueryHandler
from .chat_join_request_handler import ChatJoinRequestHandler from .chat_join_request_handler import ChatJoinRequestHandler
from .chat_member_updated_handler import ChatMemberUpdatedHandler from .chat_member_updated_handler import ChatMemberUpdatedHandler

View file

@ -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)

View file

@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>. # 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_callback_query import OnCallbackQuery
from .on_chat_join_request import OnChatJoinRequest from .on_chat_join_request import OnChatJoinRequest
from .on_chat_member_updated import OnChatMemberUpdated from .on_chat_member_updated import OnChatMemberUpdated
@ -36,6 +37,7 @@ from .on_message_reaction_count_updated import OnMessageReactionCountUpdated
class Decorators( class Decorators(
OnMessage, OnMessage,
OnBotBusinessMessage,
OnEditedMessage, OnEditedMessage,
OnDeletedMessages, OnDeletedMessages,
OnCallbackQuery, OnCallbackQuery,

View file

@ -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.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

View file

@ -133,6 +133,9 @@ class Message(Object, Update):
reply_to_story (:obj:`~pyrogram.types.Story`, *optional*): reply_to_story (:obj:`~pyrogram.types.Story`, *optional*):
For replies, the original story. For replies, the original story.
business_connection_id (``str``, *optional*):
The business connection identifier.
mentioned (``bool``, *optional*): mentioned (``bool``, *optional*):
The message contains a mention. The message contains a mention.
@ -401,6 +404,7 @@ class Message(Object, Update):
client: "pyrogram.Client" = None, client: "pyrogram.Client" = None,
id: int, id: int,
message_thread_id: int = None, message_thread_id: int = None,
business_connection_id: str = None,
from_user: "types.User" = None, from_user: "types.User" = None,
sender_chat: "types.Chat" = None, sender_chat: "types.Chat" = None,
sender_business_bot: "types.User" = None, sender_business_bot: "types.User" = None,
@ -504,6 +508,7 @@ class Message(Object, Update):
self.id = id self.id = id
self.message_thread_id = message_thread_id self.message_thread_id = message_thread_id
self.business_connection_id = business_connection_id
self.from_user = from_user self.from_user = from_user
self.sender_chat = sender_chat self.sender_chat = sender_chat
self.sender_business_bot = sender_business_bot self.sender_business_bot = sender_business_bot
@ -643,6 +648,7 @@ class Message(Object, Update):
chats: dict, chats: dict,
topics: dict = None, topics: dict = None,
is_scheduled: bool = False, is_scheduled: bool = False,
business_connection_id: str = None,
replies: int = 1 replies: int = 1
): ):
if isinstance(message, raw.types.MessageEmpty): if isinstance(message, raw.types.MessageEmpty):
@ -1039,6 +1045,7 @@ class Message(Object, Update):
parsed_message = Message( parsed_message = Message(
id=message.id, id=message.id,
message_thread_id=message_thread_id, message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
date=utils.timestamp_to_datetime(message.date), date=utils.timestamp_to_datetime(message.date),
chat=types.Chat._parse(client, message, users, chats, is_chat=True), chat=types.Chat._parse(client, message, users, chats, is_chat=True),
topics=None, topics=None,

View file

@ -98,7 +98,8 @@ def get_input_media_from_file_id(
async def parse_messages( async def parse_messages(
client, client,
messages: "raw.types.messages.Messages", messages: "raw.types.messages.Messages",
replies: int = 1 replies: int = 1,
business_connection_id: str = None
) -> List["types.Message"]: ) -> List["types.Message"]:
users = {i.id: i for i in messages.users} users = {i.id: i for i in messages.users}
chats = {i.id: i for i in messages.chats} chats = {i.id: i for i in messages.chats}
@ -112,7 +113,7 @@ async def parse_messages(
parsed_messages = [] parsed_messages = []
for message in messages.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: if replies:
messages_with_replies = { messages_with_replies = {