Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit b2e1e41

Browse files
Added Conversation Support ❤️
1 parent 71f3125 commit b2e1e41

8 files changed

Lines changed: 339 additions & 3 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, 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,
@@ -80,6 +81,9 @@ def __init__(self, client: "pyrogram.Client"):
8081
self.updates_queue = asyncio.Queue()
8182
self.groups = OrderedDict()
8283

84+
self.conversation_handler = ConversationHandler()
85+
self.groups[0] = [self.conversation_handler]
86+
8387
async def message_parser(update, users, chats):
8488
return await pyrogram.types.Message._parse(
8589
self.client, update.message, users, chats,

pyrogram/handlers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@
2727
from .poll_handler import PollHandler
2828
from .raw_update_handler import RawUpdateHandler
2929
from .user_status_handler import UserStatusHandler
30+
from .conversation_handler import ConversationHandler
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
try:
35+
chat_id = update.chat.id if isinstance(update, Message) else update.message.chat.id
36+
except AttributeError:
37+
return False
38+
39+
waiter = self.waiters.get(chat_id)
40+
if not waiter or not isinstance(update, waiter['update_type']) or waiter['future'].done():
41+
return False
42+
43+
filters = waiter.get('filters')
44+
if callable(filters):
45+
if inspect.iscoroutinefunction(filters.__call__):
46+
filtered = await filters(client, update)
47+
else:
48+
filtered = await client.loop.run_in_executor(
49+
client.executor,
50+
filters,
51+
client, update
52+
)
53+
if not filtered or waiter['future'].done():
54+
return False
55+
56+
waiter['future'].set_result(update)
57+
return True
58+
59+
@staticmethod
60+
async def callback(_, __):
61+
pass
62+
63+
def delete_waiter(self, chat_id, future):
64+
if future == self.waiters[chat_id]['future']:
65+
del self.waiters[chat_id]

pyrogram/methods/messages/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@
6161
from .send_voice import SendVoice
6262
from .stop_poll import StopPoll
6363
from .vote_poll import VotePoll
64-
64+
from .wait_for_message import WaitForMessage
65+
from .wait_for_callback_query import WaitForCallbackQuery
6566

6667
class Messages(
6768
DeleteMessages,
@@ -108,6 +109,8 @@ class Messages(
108109
SearchMessagesCount,
109110
SearchGlobalCount,
110111
GetDiscussionMessage,
111-
SendReaction
112+
SendReaction,
113+
WaitForMessage,
114+
WaitForCallbackQuery
112115
):
113116
pass
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
from pyrogram.scaffold import Scaffold
26+
27+
28+
class WaitForCallbackQuery(Scaffold):
29+
async def wait_for_callback_query(
30+
self,
31+
chat_id: Union[int, str],
32+
filters: Filter = None,
33+
timeout: int = None
34+
) -> "types.CallbackQuery":
35+
"""Wait for callback query.
36+
37+
Parameters:
38+
chat_id (``int`` | ``str``):
39+
Unique identifier (int) or username (str) of the target chat.
40+
41+
filters (:obj:`Filters`):
42+
Pass one or more filters to allow only a subset of callback queries to be passed
43+
in your callback function.
44+
45+
timeout (``int``, *optional*):
46+
Timeout in seconds.
47+
48+
Returns:
49+
:obj:`~pyrogram.types.CallbackQuery`: On success, the callback query is returned.
50+
51+
Raises:
52+
asyncio.TimeoutError: In case callback query not received within the timeout.
53+
54+
Example:
55+
.. code-block:: python
56+
57+
# Simple example
58+
callback_query = app.wait_for_callback_query(chat_id)
59+
60+
# Example with filter
61+
callback_query = app.wait_for_callback_query(chat_id, filters=filters.user(user_id))
62+
63+
# Example with timeout
64+
callback_query = app.wait_for_callback_query(chat_id, timeout=60)
65+
"""
66+
67+
if not isinstance(chat_id, int):
68+
chat = await self.get_chat(chat_id)
69+
chat_id = chat.id
70+
71+
conversation_handler = self.dispatcher.conversation_handler
72+
future = self.loop.create_future()
73+
future.add_done_callback(
74+
partial(
75+
conversation_handler.delete_waiter,
76+
chat_id
77+
)
78+
)
79+
waiter = dict(future=future, filters=filters, update_type=types.CallbackQuery)
80+
conversation_handler.waiters[chat_id] = waiter
81+
return await asyncio.wait_for(future, timeout=timeout)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
from pyrogram.scaffold import Scaffold
26+
27+
28+
class WaitForMessage(Scaffold):
29+
async def wait_for_message(
30+
self,
31+
chat_id: Union[int, str],
32+
filters: Filter = None,
33+
timeout: int = None
34+
) -> "types.Message":
35+
"""Wait for message.
36+
37+
Parameters:
38+
chat_id (``int`` | ``str``):
39+
Unique identifier (int) or username (str) of the target chat.
40+
41+
filters (:obj:`Filters`):
42+
Pass one or more filters to allow only a subset of callback queries to be passed
43+
in your callback function.
44+
45+
timeout (``int``, *optional*):
46+
Timeout in seconds.
47+
48+
Returns:
49+
:obj:`~pyrogram.types.Message`: On success, the reply message is returned.
50+
51+
Raises:
52+
asyncio.TimeoutError: In case message not received within the timeout.
53+
54+
Example:
55+
.. code-block:: python
56+
57+
# Simple example
58+
reply_message = app.wait_for_message(chat_id)
59+
60+
# Example with filter
61+
reply_message = app.wait_for_message(chat_id, filters=filters.text)
62+
63+
# Example with timeout
64+
reply_message = app.wait_for_message(chat_id, timeout=60)
65+
"""
66+
67+
if not isinstance(chat_id, int):
68+
chat = await self.get_chat(chat_id)
69+
chat_id = chat.id
70+
71+
conversation_handler = self.dispatcher.conversation_handler
72+
future = self.loop.create_future()
73+
future.add_done_callback(
74+
partial(
75+
conversation_handler.delete_waiter,
76+
chat_id
77+
)
78+
)
79+
waiter = dict(future=future, filters=filters, update_type=types.Message)
80+
conversation_handler.waiters[chat_id] = waiter
81+
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
@@ -3525,3 +3525,103 @@ async def unpin(self) -> bool:
35253525
chat_id=self.chat.id,
35263526
message_id=self.message_id
35273527
)
3528+
3529+
async def ask(
3530+
self,
3531+
text: str,
3532+
quote: bool = None,
3533+
parse_mode: Optional[str] = object,
3534+
entities: List["types.MessageEntity"] = None,
3535+
disable_web_page_preview: bool = None,
3536+
disable_notification: bool = None,
3537+
reply_to_message_id: int = None,
3538+
reply_markup=None,
3539+
filters=None,
3540+
timeout: int = None
3541+
) -> "Message":
3542+
"""Bound method *ask* of :obj:`~pyrogram.types.Message`.
3543+
3544+
Use as a shortcut for:
3545+
3546+
.. code-block:: python
3547+
3548+
client.send_message(chat_id, "What is your name?")
3549+
client.wait_for_message(chat_id)
3550+
3551+
Example:
3552+
.. code-block:: python
3553+
3554+
message.ask("What is your name?")
3555+
3556+
Parameters:
3557+
text (``str``):
3558+
Text of the message to be sent.
3559+
3560+
quote (``bool``, *optional*):
3561+
If ``True``, the message will be sent as a reply to this message.
3562+
If *reply_to_message_id* is passed, this parameter will be ignored.
3563+
Defaults to ``True`` in group chats and ``False`` in private chats.
3564+
3565+
parse_mode (``str``, *optional*):
3566+
By default, texts are parsed using both Markdown and HTML styles.
3567+
You can combine both syntaxes together.
3568+
Pass "markdown" or "md" to enable Markdown-style parsing only.
3569+
Pass "html" to enable HTML-style parsing only.
3570+
Pass None to completely disable style parsing.
3571+
3572+
entities (List of :obj:`~pyrogram.types.MessageEntity`):
3573+
List of special entities that appear in message text, which can be specified instead of *parse_mode*.
3574+
3575+
disable_web_page_preview (``bool``, *optional*):
3576+
Disables link previews for links in this message.
3577+
3578+
disable_notification (``bool``, *optional*):
3579+
Sends the message silently.
3580+
Users will receive a notification with no sound.
3581+
3582+
reply_to_message_id (``int``, *optional*):
3583+
If the message is a reply, ID of the original message.
3584+
3585+
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
3586+
Additional interface options. An object for an inline keyboard, custom reply keyboard,
3587+
instructions to remove reply keyboard or to force a reply from the user.
3588+
3589+
filters (:obj:`Filters`):
3590+
Pass one or more filters to allow only a subset of callback queries to be passed
3591+
in your callback function.
3592+
3593+
timeout (``int``, *optional*):
3594+
Timeout in seconds.
3595+
3596+
Returns:
3597+
:obj:`~pyrogram.types.Message`: On success, the reply message is returned.
3598+
3599+
Raises:
3600+
RPCError: In case of a Telegram RPC error.
3601+
asyncio.TimeoutError: In case reply not received within the timeout.
3602+
"""
3603+
if quote is None:
3604+
quote = self.chat.type != "private"
3605+
3606+
if reply_to_message_id is None and quote:
3607+
reply_to_message_id = self.message_id
3608+
3609+
request = await self._client.send_message(
3610+
chat_id=self.chat.id,
3611+
text=text,
3612+
parse_mode=parse_mode,
3613+
entities=entities,
3614+
disable_web_page_preview=disable_web_page_preview,
3615+
disable_notification=disable_notification,
3616+
reply_to_message_id=reply_to_message_id,
3617+
reply_markup=reply_markup
3618+
)
3619+
3620+
reply_message = await self._client.wait_for_message(
3621+
self.chat.id,
3622+
filters=filters,
3623+
timeout=timeout
3624+
)
3625+
3626+
reply_message.request = request
3627+
return reply_message

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pyaes==1.6.1
22
pysocks==1.7.1
3+
async_lru==1.0.2

0 commit comments

Comments
 (0)