Skip to content

Commit 23fd547

Browse files
Mahesh0253alissonlauffer
authored andcommitted
Add conversation support
Squashed commit of the following: commit e5f79a0 Author: Mahesh <44301650+Mahesh0253@users.noreply.github.com> Date: Sun, 21 May 2023 20:30:24 +0530 Remove Scaffold commit d76bb8d Author: Mahesh0253 <maheshmalekar0253@gmail.com> Date: Mon Mar 21 18:19:14 2022 +0530 Update copyright year commit 47193f4 Author: Mahesh0253 <maheshmalekar0253@gmail.com> Date: Mon Mar 21 18:18:20 2022 +0530 Skip outgoing messages commit c818f70 Author: Mahesh <44301650+Mahesh0253@users.noreply.github.com> Date: Mon, 12 Jul 2021 13:49:00 +0530 update commit c34f085 Author: Mahesh0253 <maheshmalekar0253@gmail.com> Date: Mon, 12 Jul 2021 11:36:28 +0530 Add conversation support
1 parent 8a83bf1 commit 23fd547

7 files changed

Lines changed: 339 additions & 2 deletions

File tree

pyrogram/dispatcher.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
from pyrogram.handlers import (
2727
CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler,
2828
UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler,
29-
ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler
29+
ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler,
30+
ConversationHandler
3031
)
3132
from pyrogram.raw.types import (
3233
UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage,
@@ -63,6 +64,9 @@ def __init__(self, client: "pyrogram.Client"):
6364
self.updates_queue = asyncio.Queue()
6465
self.groups = OrderedDict()
6566

67+
self.conversation_handler = ConversationHandler()
68+
self.groups[0] = [self.conversation_handler]
69+
6670
async def message_parser(update, users, chats):
6771
return (
6872
await pyrogram.types.Message._parse(self.client, update.message, users, chats,

pyrogram/handlers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from .callback_query_handler import CallbackQueryHandler
2020
from .chat_join_request_handler import ChatJoinRequestHandler
2121
from .chat_member_updated_handler import ChatMemberUpdatedHandler
22+
from .conversation_handler import ConversationHandler
2223
from .chosen_inline_result_handler import ChosenInlineResultHandler
2324
from .deleted_messages_handler import DeletedMessagesHandler
2425
from .disconnect_handler import DisconnectHandler
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Pyrogram - Telegram MTProto API Client Library for Python
2+
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
3+
#
4+
# This file is part of Pyrogram.
5+
#
6+
# Pyrogram is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Lesser General Public License as published
8+
# by the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# Pyrogram is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public License
17+
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
18+
19+
import inspect
20+
from typing import Union
21+
22+
import pyrogram
23+
from pyrogram.types import Message, CallbackQuery
24+
from .message_handler import MessageHandler
25+
from .callback_query_handler import CallbackQueryHandler
26+
27+
28+
class ConversationHandler(MessageHandler, CallbackQueryHandler):
29+
"""The Conversation handler class."""
30+
def __init__(self):
31+
self.waiters = {}
32+
33+
async def check(self, client: "pyrogram.Client", update: Union[Message, CallbackQuery]):
34+
if isinstance(update, Message) and update.outgoing:
35+
return False
36+
37+
try:
38+
chat_id = update.chat.id if isinstance(update, Message) else update.message.chat.id
39+
except AttributeError:
40+
return False
41+
42+
waiter = self.waiters.get(chat_id)
43+
if not waiter or not isinstance(update, waiter['update_type']) or waiter['future'].done():
44+
return False
45+
46+
filters = waiter.get('filters')
47+
if callable(filters):
48+
if inspect.iscoroutinefunction(filters.__call__):
49+
filtered = await filters(client, update)
50+
else:
51+
filtered = await client.loop.run_in_executor(
52+
client.executor,
53+
filters,
54+
client, update
55+
)
56+
if not filtered or waiter['future'].done():
57+
return False
58+
59+
waiter['future'].set_result(update)
60+
return True
61+
62+
@staticmethod
63+
async def callback(_, __):
64+
pass
65+
66+
def delete_waiter(self, chat_id, future):
67+
if future == self.waiters[chat_id]['future']:
68+
del self.waiters[chat_id]

pyrogram/methods/messages/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
from .stop_poll import StopPoll
6565
from .stream_media import StreamMedia
6666
from .vote_poll import VotePoll
67+
from .wait_for_message import WaitForMessage
68+
from .wait_for_callback_query import WaitForCallbackQuery
6769

6870

6971
class Messages(
@@ -114,6 +116,8 @@ class Messages(
114116
GetDiscussionReplies,
115117
GetDiscussionRepliesCount,
116118
StreamMedia,
117-
GetCustomEmojiStickers
119+
GetCustomEmojiStickers,
120+
WaitForMessage,
121+
WaitForCallbackQuery
118122
):
119123
pass
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Pyrogram - Telegram MTProto API Client Library for Python
2+
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
3+
#
4+
# This file is part of Pyrogram.
5+
#
6+
# Pyrogram is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Lesser General Public License as published
8+
# by the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# Pyrogram is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public License
17+
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
18+
19+
import asyncio
20+
from typing import Union
21+
from functools import partial
22+
23+
from pyrogram import types
24+
from pyrogram.filters import Filter
25+
26+
27+
class WaitForCallbackQuery:
28+
async def wait_for_callback_query(
29+
self,
30+
chat_id: Union[int, str],
31+
filters: Filter = None,
32+
timeout: int = None
33+
) -> "types.CallbackQuery":
34+
"""Wait for callback query.
35+
36+
Parameters:
37+
chat_id (``int`` | ``str``):
38+
Unique identifier (int) or username (str) of the target chat.
39+
40+
filters (:obj:`Filters`):
41+
Pass one or more filters to allow only a subset of callback queries to be passed
42+
in your callback function.
43+
44+
timeout (``int``, *optional*):
45+
Timeout in seconds.
46+
47+
Returns:
48+
:obj:`~pyrogram.types.CallbackQuery`: On success, the callback query is returned.
49+
50+
Raises:
51+
asyncio.TimeoutError: In case callback query not received within the timeout.
52+
53+
Example:
54+
.. code-block:: python
55+
56+
# Simple example
57+
callback_query = app.wait_for_callback_query(chat_id)
58+
59+
# Example with filter
60+
callback_query = app.wait_for_callback_query(chat_id, filters=filters.user(user_id))
61+
62+
# Example with timeout
63+
callback_query = app.wait_for_callback_query(chat_id, timeout=60)
64+
"""
65+
66+
if not isinstance(chat_id, int):
67+
chat = await self.get_chat(chat_id)
68+
chat_id = chat.id
69+
70+
conversation_handler = self.dispatcher.conversation_handler
71+
future = self.loop.create_future()
72+
future.add_done_callback(
73+
partial(
74+
conversation_handler.delete_waiter,
75+
chat_id
76+
)
77+
)
78+
waiter = dict(future=future, filters=filters, update_type=types.CallbackQuery)
79+
conversation_handler.waiters[chat_id] = waiter
80+
return await asyncio.wait_for(future, timeout=timeout)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Pyrogram - Telegram MTProto API Client Library for Python
2+
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
3+
#
4+
# This file is part of Pyrogram.
5+
#
6+
# Pyrogram is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Lesser General Public License as published
8+
# by the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# Pyrogram is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public License
17+
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
18+
19+
import asyncio
20+
from typing import Union
21+
from functools import partial
22+
23+
from pyrogram import types
24+
from pyrogram.filters import Filter
25+
26+
27+
class WaitForMessage:
28+
async def wait_for_message(
29+
self,
30+
chat_id: Union[int, str],
31+
filters: Filter = None,
32+
timeout: int = None
33+
) -> "types.Message":
34+
"""Wait for message.
35+
36+
Parameters:
37+
chat_id (``int`` | ``str``):
38+
Unique identifier (int) or username (str) of the target chat.
39+
40+
filters (:obj:`Filters`):
41+
Pass one or more filters to allow only a subset of callback queries to be passed
42+
in your callback function.
43+
44+
timeout (``int``, *optional*):
45+
Timeout in seconds.
46+
47+
Returns:
48+
:obj:`~pyrogram.types.Message`: On success, the reply message is returned.
49+
50+
Raises:
51+
asyncio.TimeoutError: In case message not received within the timeout.
52+
53+
Example:
54+
.. code-block:: python
55+
56+
# Simple example
57+
reply_message = app.wait_for_message(chat_id)
58+
59+
# Example with filter
60+
reply_message = app.wait_for_message(chat_id, filters=filters.text)
61+
62+
# Example with timeout
63+
reply_message = app.wait_for_message(chat_id, timeout=60)
64+
"""
65+
66+
if not isinstance(chat_id, int):
67+
chat = await self.get_chat(chat_id)
68+
chat_id = chat.id
69+
70+
conversation_handler = self.dispatcher.conversation_handler
71+
future = self.loop.create_future()
72+
future.add_done_callback(
73+
partial(
74+
conversation_handler.delete_waiter,
75+
chat_id
76+
)
77+
)
78+
waiter = dict(future=future, filters=filters, update_type=types.Message)
79+
conversation_handler.waiters[chat_id] = waiter
80+
return await asyncio.wait_for(future, timeout=timeout)

pyrogram/types/messages_and_media/message.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3604,3 +3604,103 @@ async def unpin(self) -> bool:
36043604
chat_id=self.chat.id,
36053605
message_id=self.id
36063606
)
3607+
3608+
async def ask(
3609+
self,
3610+
text: str,
3611+
quote: bool = None,
3612+
parse_mode: Optional[str] = object,
3613+
entities: List["types.MessageEntity"] = None,
3614+
disable_web_page_preview: bool = None,
3615+
disable_notification: bool = None,
3616+
reply_to_message_id: int = None,
3617+
reply_markup=None,
3618+
filters=None,
3619+
timeout: int = None
3620+
) -> "Message":
3621+
"""Bound method *ask* of :obj:`~pyrogram.types.Message`.
3622+
3623+
Use as a shortcut for:
3624+
3625+
.. code-block:: python
3626+
3627+
client.send_message(chat_id, "What is your name?")
3628+
client.wait_for_message(chat_id)
3629+
3630+
Example:
3631+
.. code-block:: python
3632+
3633+
message.ask("What is your name?")
3634+
3635+
Parameters:
3636+
text (``str``):
3637+
Text of the message to be sent.
3638+
3639+
quote (``bool``, *optional*):
3640+
If ``True``, the message will be sent as a reply to this message.
3641+
If *reply_to_message_id* is passed, this parameter will be ignored.
3642+
Defaults to ``True`` in group chats and ``False`` in private chats.
3643+
3644+
parse_mode (``str``, *optional*):
3645+
By default, texts are parsed using both Markdown and HTML styles.
3646+
You can combine both syntaxes together.
3647+
Pass "markdown" or "md" to enable Markdown-style parsing only.
3648+
Pass "html" to enable HTML-style parsing only.
3649+
Pass None to completely disable style parsing.
3650+
3651+
entities (List of :obj:`~pyrogram.types.MessageEntity`):
3652+
List of special entities that appear in message text, which can be specified instead of *parse_mode*.
3653+
3654+
disable_web_page_preview (``bool``, *optional*):
3655+
Disables link previews for links in this message.
3656+
3657+
disable_notification (``bool``, *optional*):
3658+
Sends the message silently.
3659+
Users will receive a notification with no sound.
3660+
3661+
reply_to_message_id (``int``, *optional*):
3662+
If the message is a reply, ID of the original message.
3663+
3664+
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
3665+
Additional interface options. An object for an inline keyboard, custom reply keyboard,
3666+
instructions to remove reply keyboard or to force a reply from the user.
3667+
3668+
filters (:obj:`Filters`):
3669+
Pass one or more filters to allow only a subset of callback queries to be passed
3670+
in your callback function.
3671+
3672+
timeout (``int``, *optional*):
3673+
Timeout in seconds.
3674+
3675+
Returns:
3676+
:obj:`~pyrogram.types.Message`: On success, the reply message is returned.
3677+
3678+
Raises:
3679+
RPCError: In case of a Telegram RPC error.
3680+
asyncio.TimeoutError: In case reply not received within the timeout.
3681+
"""
3682+
if quote is None:
3683+
quote = self.chat.type != "private"
3684+
3685+
if reply_to_message_id is None and quote:
3686+
reply_to_message_id = self.id
3687+
3688+
request = await self._client.send_message(
3689+
chat_id=self.chat.id,
3690+
text=text,
3691+
parse_mode=parse_mode,
3692+
entities=entities,
3693+
disable_web_page_preview=disable_web_page_preview,
3694+
disable_notification=disable_notification,
3695+
reply_to_message_id=reply_to_message_id,
3696+
reply_markup=reply_markup
3697+
)
3698+
3699+
reply_message = await self._client.wait_for_message(
3700+
self.chat.id,
3701+
filters=filters,
3702+
timeout=timeout
3703+
)
3704+
3705+
reply_message.request = request
3706+
return reply_message

0 commit comments

Comments
 (0)