2022-07-02 11:44:57 +00:00
|
|
|
"""
|
|
|
|
pyromod - A monkeypatcher add-on for Pyrogram
|
|
|
|
Copyright (C) 2020 Cezar H. <https://github.com/usernein>
|
|
|
|
|
|
|
|
This file is part of pyromod.
|
|
|
|
|
|
|
|
pyromod is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
pyromod 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import asyncio
|
2024-08-07 15:33:57 +00:00
|
|
|
import contextlib
|
2022-07-02 11:44:57 +00:00
|
|
|
import functools
|
|
|
|
|
|
|
|
import pyrogram
|
|
|
|
|
|
|
|
from sticker.scheduler import add_delete_message_job
|
|
|
|
from ..utils import patch, patchable
|
|
|
|
from ..utils.errors import ListenerCanceled, TimeoutConversationError
|
|
|
|
|
|
|
|
pyrogram.errors.ListenerCanceled = ListenerCanceled
|
2024-08-07 15:33:57 +00:00
|
|
|
LOCK = asyncio.Lock()
|
|
|
|
DONE = []
|
2024-11-17 13:14:22 +00:00
|
|
|
DONE_MAP = {}
|
2022-07-02 11:44:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
@patch(pyrogram.client.Client)
|
|
|
|
class Client:
|
|
|
|
@patchable
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.listening = {}
|
|
|
|
self.using_mod = True
|
|
|
|
|
|
|
|
self.old__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
async def listen(self, chat_id, filters=None, timeout=None):
|
|
|
|
if type(chat_id) != int:
|
|
|
|
chat = await self.get_chat(chat_id)
|
|
|
|
chat_id = chat.id
|
|
|
|
|
|
|
|
future = self.loop.create_future()
|
2023-09-11 13:05:50 +00:00
|
|
|
future.add_done_callback(functools.partial(self.clear_listener, chat_id))
|
|
|
|
self.listening.update({chat_id: {"future": future, "filters": filters}})
|
2022-07-02 11:44:57 +00:00
|
|
|
try:
|
|
|
|
return await asyncio.wait_for(future, timeout)
|
|
|
|
except asyncio.exceptions.TimeoutError as e:
|
|
|
|
raise TimeoutConversationError() from e
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
async def ask(self, chat_id, text, filters=None, timeout=None, *args, **kwargs):
|
|
|
|
request = await self.send_message(chat_id, text, *args, **kwargs)
|
|
|
|
response = await self.listen(chat_id, filters, timeout)
|
|
|
|
response.request = request
|
|
|
|
return response
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
def clear_listener(self, chat_id, future):
|
2024-08-07 15:33:57 +00:00
|
|
|
with contextlib.suppress(KeyError):
|
|
|
|
if future == self.listening[chat_id]["future"]:
|
|
|
|
self.listening.pop(chat_id, None)
|
2022-07-02 11:44:57 +00:00
|
|
|
|
|
|
|
@patchable
|
|
|
|
def cancel_listener(self, chat_id):
|
|
|
|
listener = self.listening.get(chat_id)
|
2023-09-11 13:05:50 +00:00
|
|
|
if not listener or listener["future"].done():
|
2022-07-02 11:44:57 +00:00
|
|
|
return
|
|
|
|
|
2023-09-11 13:05:50 +00:00
|
|
|
listener["future"].set_exception(ListenerCanceled())
|
|
|
|
self.clear_listener(chat_id, listener["future"])
|
2022-07-02 11:44:57 +00:00
|
|
|
|
|
|
|
@patchable
|
|
|
|
def cancel_all_listener(self):
|
|
|
|
for chat_id in self.listening:
|
|
|
|
self.cancel_listener(chat_id)
|
|
|
|
|
|
|
|
|
|
|
|
@patch(pyrogram.handlers.message_handler.MessageHandler)
|
|
|
|
class MessageHandler:
|
|
|
|
@patchable
|
|
|
|
def __init__(self, callback: callable, filters=None):
|
|
|
|
self.user_callback = callback
|
|
|
|
self.old__init__(self.resolve_listener, filters)
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
async def resolve_listener(self, client, message, *args):
|
2024-08-07 15:33:57 +00:00
|
|
|
global LOCK, DONE
|
|
|
|
async with LOCK:
|
|
|
|
listener = client.listening.get(message.chat.id)
|
2024-11-17 13:14:22 +00:00
|
|
|
if listener and DONE_MAP.get(id(self)) == id(listener):
|
2024-08-07 15:33:57 +00:00
|
|
|
with contextlib.suppress(ValueError):
|
|
|
|
DONE.remove(listener)
|
2024-11-17 13:14:22 +00:00
|
|
|
del DONE_MAP[id(self)]
|
|
|
|
if not listener["future"].done():
|
|
|
|
listener["future"].set_result(message)
|
|
|
|
return
|
|
|
|
if listener["future"].done():
|
|
|
|
client.clear_listener(message.chat.id, listener["future"])
|
2024-08-07 15:33:57 +00:00
|
|
|
await self.user_callback(client, message, *args)
|
2022-07-02 11:44:57 +00:00
|
|
|
|
|
|
|
@patchable
|
|
|
|
async def check(self, client, update):
|
2024-08-07 15:33:57 +00:00
|
|
|
global LOCK, DONE
|
|
|
|
async with LOCK:
|
|
|
|
listener = client.listening.get(update.chat.id)
|
|
|
|
if listener and (listener not in DONE) and (not listener["future"].done()):
|
|
|
|
if callable(listener["filters"]):
|
|
|
|
result = await listener["filters"](client, update)
|
|
|
|
if result:
|
|
|
|
DONE.append(listener)
|
2024-11-17 13:14:22 +00:00
|
|
|
DONE_MAP[id(self)] = id(listener)
|
|
|
|
return True
|
2024-08-07 15:33:57 +00:00
|
|
|
else:
|
|
|
|
DONE.append(listener)
|
2024-11-17 13:14:22 +00:00
|
|
|
DONE_MAP[id(self)] = id(listener)
|
2024-08-07 15:33:57 +00:00
|
|
|
return True
|
2023-09-11 13:05:50 +00:00
|
|
|
return await self.filters(client, update) if callable(self.filters) else True
|
2022-07-02 11:44:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
@patch(pyrogram.types.user_and_chats.chat.Chat)
|
|
|
|
class Chat(pyrogram.types.Chat):
|
|
|
|
@patchable
|
|
|
|
def listen(self, *args, **kwargs):
|
|
|
|
return self._client.listen(self.id, *args, **kwargs)
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
def ask(self, *args, **kwargs):
|
|
|
|
return self._client.ask(self.id, *args, **kwargs)
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
def cancel_listener(self):
|
|
|
|
return self._client.cancel_listener(self.id)
|
|
|
|
|
|
|
|
|
|
|
|
@patch(pyrogram.types.user_and_chats.user.User)
|
|
|
|
class User(pyrogram.types.User):
|
|
|
|
@patchable
|
|
|
|
def listen(self, *args, **kwargs):
|
|
|
|
return self._client.listen(self.id, *args, **kwargs)
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
def ask(self, *args, **kwargs):
|
|
|
|
return self._client.ask(self.id, *args, **kwargs)
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
def cancel_listener(self):
|
|
|
|
return self._client.cancel_listener(self.id)
|
|
|
|
|
|
|
|
|
|
|
|
@patch(pyrogram.types.messages_and_media.Message)
|
|
|
|
class Message(pyrogram.types.Message):
|
|
|
|
@patchable
|
|
|
|
async def safe_delete(self, revoke: bool = True):
|
|
|
|
try:
|
|
|
|
return await self._client.delete_messages(
|
2023-09-11 13:05:50 +00:00
|
|
|
chat_id=self.chat.id, message_ids=self.id, revoke=revoke
|
2022-07-02 11:44:57 +00:00
|
|
|
)
|
|
|
|
except Exception as e: # noqa
|
|
|
|
return False
|
|
|
|
|
|
|
|
@patchable
|
|
|
|
async def delay_delete(self, delay: int = 60):
|
|
|
|
add_delete_message_job(self, delay)
|