patchpyro 2.0.5__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- patchpyro-2.1.1/PKG-INFO +112 -0
- patchpyro-2.1.1/README.md +83 -0
- {patchpyro-2.0.5/patchpyro/listen → patchpyro-2.1.1/patchpyro}/__init__.py +7 -4
- patchpyro-2.0.5/patchpyro/utils/utils.py → patchpyro-2.1.1/patchpyro/listen/__init__.py +9 -19
- patchpyro-2.1.1/patchpyro/listen/listen.py +186 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/patchpyro/utils/__init__.py +5 -4
- patchpyro-2.1.1/patchpyro/utils/utils.py +90 -0
- patchpyro-2.1.1/patchpyro.egg-info/PKG-INFO +112 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/setup.py +1 -1
- patchpyro-2.0.5/PKG-INFO +0 -113
- patchpyro-2.0.5/README.md +0 -84
- patchpyro-2.0.5/patchpyro/__init__.py +0 -28
- patchpyro-2.0.5/patchpyro/listen/listen.py +0 -151
- patchpyro-2.0.5/patchpyro.egg-info/PKG-INFO +0 -113
- {patchpyro-2.0.5 → patchpyro-2.1.1}/COPYING +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/COPYING.lesser +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/NOTICE +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/patchpyro.egg-info/SOURCES.txt +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/patchpyro.egg-info/dependency_links.txt +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/patchpyro.egg-info/requires.txt +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/patchpyro.egg-info/top_level.txt +0 -0
- {patchpyro-2.0.5 → patchpyro-2.1.1}/setup.cfg +0 -0
patchpyro-2.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: patchpyro
|
|
3
|
+
Version: 2.1.1
|
|
4
|
+
Summary: A modified pyromod by a.devh.in
|
|
5
|
+
Home-page: https://github.com/adityaprasad502/patchpyro
|
|
6
|
+
Author: Cezar H. & adityaprasad502
|
|
7
|
+
Author-email: plutoniumx502@gmail.com
|
|
8
|
+
License: LGPLv3+
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: COPYING
|
|
15
|
+
License-File: COPYING.lesser
|
|
16
|
+
License-File: NOTICE
|
|
17
|
+
Requires-Dist: kurigram>=2.0.69
|
|
18
|
+
Dynamic: author
|
|
19
|
+
Dynamic: author-email
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: home-page
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
# PatchPyro
|
|
31
|
+
|
|
32
|
+
A fork of pyromod (renamed as patchpyro) providing conversation patches for Pyrogram-based clients.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
```text
|
|
36
|
+
kurigram>=2.0.69
|
|
37
|
+
python>=3.9
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
```bash
|
|
42
|
+
pip install patchpyro
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
Import `patchpyro` at least once in your script so you can use the modified Pyrogram in all files of the same process.
|
|
47
|
+
|
|
48
|
+
### Example
|
|
49
|
+
```python
|
|
50
|
+
# config.py
|
|
51
|
+
from patchpyro import listen # or import patchpyro.listen
|
|
52
|
+
from pyrogram import Client
|
|
53
|
+
|
|
54
|
+
listen.thank() # use this if your linter/IDE flags patchpyro as an unused import.
|
|
55
|
+
|
|
56
|
+
mybot = Client("mysession")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# any other .py
|
|
61
|
+
from config import mybot
|
|
62
|
+
# no need to import patchpyro again; Pyrogram is already monkeypatched globally (in the same process)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Available Methods
|
|
66
|
+
Just importing `patchpyro.listen` will automatically do the monkeypatch and you'll get these new methods:
|
|
67
|
+
|
|
68
|
+
### `Chat.listen`, `User.listen`
|
|
69
|
+
- `await mybot.listen(chat_id, filters=None, timeout=30)`
|
|
70
|
+
- Awaits a new message in the specified chat and returns it.
|
|
71
|
+
- Raises `asyncio.TimeoutError` if timeout (optional parameter) occurs.
|
|
72
|
+
- You can pass Update Filters to the filters parameter just like you do for the update handlers.
|
|
73
|
+
- E.g. `filters=filters.photo & filters.bot`
|
|
74
|
+
|
|
75
|
+
### `Chat.ask`, `User.ask`
|
|
76
|
+
- `await mybot.ask(text, chat_id, filters=None, timeout=30)`
|
|
77
|
+
- Same as `.listen()` above, but sends a message before awaiting.
|
|
78
|
+
- You can pass custom parameters to its internal `send_message()` call. Check the example below.
|
|
79
|
+
|
|
80
|
+
### `Chat.asker`, `User.asker`
|
|
81
|
+
- `await mybot.asker(chat_id, filters=None, timeout=36)`
|
|
82
|
+
- Same as `.listen()` but `.asker()` returns `None` instead of raising `asyncio.TimeoutError`.
|
|
83
|
+
- Useful for graceful timeout handling, `.asker()` has a default timeout of 2 minutes (adjustable via `timeout` argument).
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
### `asker()`
|
|
88
|
+
```python
|
|
89
|
+
# ...
|
|
90
|
+
sendx = await client.send_message(chat_id, "`Send me your name:`")
|
|
91
|
+
answer = await client.asker(chat_id, filters=None, timeout=60)
|
|
92
|
+
if not answer: # `None` if timeout reached with no reply.
|
|
93
|
+
return await sendx.reply_text("How long should I wait? Bye!")
|
|
94
|
+
await answer.reply_text(f"{answer.text}, That's a cool name!")
|
|
95
|
+
# ...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `ask()`
|
|
99
|
+
```python
|
|
100
|
+
# ...
|
|
101
|
+
answer = await client.ask(chat_id, '*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
|
|
102
|
+
await client.send_message(chat_id, f'Your name is: {answer.text}')
|
|
103
|
+
# ...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Copyright & License
|
|
107
|
+
This project includes code from:
|
|
108
|
+
- **[kurimod](https://github.com/ohmyarthur/kurimod)**: Monkeypatcher logic and modern async compatibility fixes.
|
|
109
|
+
- **Pyrogram**: Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>
|
|
110
|
+
- **pyromod**: Original conversation patch logic.
|
|
111
|
+
|
|
112
|
+
Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# PatchPyro
|
|
2
|
+
|
|
3
|
+
A fork of pyromod (renamed as patchpyro) providing conversation patches for Pyrogram-based clients.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
```text
|
|
7
|
+
kurigram>=2.0.69
|
|
8
|
+
python>=3.9
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
```bash
|
|
13
|
+
pip install patchpyro
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
Import `patchpyro` at least once in your script so you can use the modified Pyrogram in all files of the same process.
|
|
18
|
+
|
|
19
|
+
### Example
|
|
20
|
+
```python
|
|
21
|
+
# config.py
|
|
22
|
+
from patchpyro import listen # or import patchpyro.listen
|
|
23
|
+
from pyrogram import Client
|
|
24
|
+
|
|
25
|
+
listen.thank() # use this if your linter/IDE flags patchpyro as an unused import.
|
|
26
|
+
|
|
27
|
+
mybot = Client("mysession")
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# any other .py
|
|
32
|
+
from config import mybot
|
|
33
|
+
# no need to import patchpyro again; Pyrogram is already monkeypatched globally (in the same process)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Available Methods
|
|
37
|
+
Just importing `patchpyro.listen` will automatically do the monkeypatch and you'll get these new methods:
|
|
38
|
+
|
|
39
|
+
### `Chat.listen`, `User.listen`
|
|
40
|
+
- `await mybot.listen(chat_id, filters=None, timeout=30)`
|
|
41
|
+
- Awaits a new message in the specified chat and returns it.
|
|
42
|
+
- Raises `asyncio.TimeoutError` if timeout (optional parameter) occurs.
|
|
43
|
+
- You can pass Update Filters to the filters parameter just like you do for the update handlers.
|
|
44
|
+
- E.g. `filters=filters.photo & filters.bot`
|
|
45
|
+
|
|
46
|
+
### `Chat.ask`, `User.ask`
|
|
47
|
+
- `await mybot.ask(text, chat_id, filters=None, timeout=30)`
|
|
48
|
+
- Same as `.listen()` above, but sends a message before awaiting.
|
|
49
|
+
- You can pass custom parameters to its internal `send_message()` call. Check the example below.
|
|
50
|
+
|
|
51
|
+
### `Chat.asker`, `User.asker`
|
|
52
|
+
- `await mybot.asker(chat_id, filters=None, timeout=36)`
|
|
53
|
+
- Same as `.listen()` but `.asker()` returns `None` instead of raising `asyncio.TimeoutError`.
|
|
54
|
+
- Useful for graceful timeout handling, `.asker()` has a default timeout of 2 minutes (adjustable via `timeout` argument).
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### `asker()`
|
|
59
|
+
```python
|
|
60
|
+
# ...
|
|
61
|
+
sendx = await client.send_message(chat_id, "`Send me your name:`")
|
|
62
|
+
answer = await client.asker(chat_id, filters=None, timeout=60)
|
|
63
|
+
if not answer: # `None` if timeout reached with no reply.
|
|
64
|
+
return await sendx.reply_text("How long should I wait? Bye!")
|
|
65
|
+
await answer.reply_text(f"{answer.text}, That's a cool name!")
|
|
66
|
+
# ...
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `ask()`
|
|
70
|
+
```python
|
|
71
|
+
# ...
|
|
72
|
+
answer = await client.ask(chat_id, '*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
|
|
73
|
+
await client.send_message(chat_id, f'Your name is: {answer.text}')
|
|
74
|
+
# ...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Copyright & License
|
|
78
|
+
This project includes code from:
|
|
79
|
+
- **[kurimod](https://github.com/ohmyarthur/kurimod)**: Monkeypatcher logic and modern async compatibility fixes.
|
|
80
|
+
- **Pyrogram**: Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>
|
|
81
|
+
- **pyromod**: Original conversation patch logic.
|
|
82
|
+
|
|
83
|
+
Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>
|
|
1
|
+
"""patchpyro - A monkeypatcher add-on for Pyrogram
|
|
2
|
+
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>.
|
|
4
3
|
|
|
5
4
|
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
6
5
|
|
|
@@ -18,4 +17,8 @@ You should have received a copy of the GNU General Public License
|
|
|
18
17
|
along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
19
18
|
"""
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
|
|
21
|
+
__version__ = "2.1.1"
|
|
22
|
+
# change in setup.py aswell
|
|
23
|
+
|
|
24
|
+
from . import listen
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>
|
|
1
|
+
"""patchpyro - A monkeypatcher add-on for Pyrogram
|
|
2
|
+
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>.
|
|
4
3
|
|
|
5
4
|
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
6
5
|
|
|
@@ -19,20 +18,11 @@ along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
|
19
18
|
"""
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
old = getattr(obj, name, None)
|
|
29
|
-
setattr(obj, f"old{name}", old)
|
|
30
|
-
setattr(obj, name, func)
|
|
31
|
-
return container
|
|
32
|
-
|
|
33
|
-
return wrapper
|
|
34
|
-
|
|
21
|
+
from .listen import Chat, Client, MessageHandler, User
|
|
22
|
+
def thank() -> None:
|
|
23
|
+
"""A dummy function to prevent patchpyro.listen from being removed by formatters and linters."""
|
|
24
|
+
from patchpyro import __version__
|
|
25
|
+
import logging
|
|
26
|
+
logging.debug(f"Thank you for using patchpyro v{__version__}!")
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
func.patchable = True
|
|
38
|
-
return func
|
|
28
|
+
__all__ = ["Chat", "Client", "MessageHandler", "User", "thank"]
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""patchpyro - A monkeypatcher add-on for Pyrogram
|
|
2
|
+
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>.
|
|
3
|
+
|
|
4
|
+
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
5
|
+
Additional patching logic was adapted from kurimod (C) Dias Arthur.
|
|
6
|
+
|
|
7
|
+
patchpyro is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
patchpyro is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU General Public License
|
|
18
|
+
along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import asyncio
|
|
22
|
+
import functools
|
|
23
|
+
from contextlib import suppress
|
|
24
|
+
from inspect import iscoroutinefunction
|
|
25
|
+
|
|
26
|
+
import pyrogram
|
|
27
|
+
import pyrogram.client
|
|
28
|
+
import pyrogram.handlers.message_handler
|
|
29
|
+
import pyrogram.types.user_and_chats.chat
|
|
30
|
+
import pyrogram.types.user_and_chats.user
|
|
31
|
+
|
|
32
|
+
from patchpyro.utils import patch_into, should_patch
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ListenerCanceled(Exception):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
pyrogram.errors.ListenerCanceled = ListenerCanceled
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@patch_into(pyrogram.client.Client)
|
|
43
|
+
class Client:
|
|
44
|
+
@should_patch()
|
|
45
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
46
|
+
self.listening = {}
|
|
47
|
+
self.using_mod = True
|
|
48
|
+
|
|
49
|
+
self.old__init__(*args, **kwargs)
|
|
50
|
+
|
|
51
|
+
@should_patch()
|
|
52
|
+
async def listen(
|
|
53
|
+
self,
|
|
54
|
+
chat_id: int | str,
|
|
55
|
+
filters: pyrogram.filters.Filter | None = None,
|
|
56
|
+
timeout: int | None = None,
|
|
57
|
+
) -> pyrogram.types.Message:
|
|
58
|
+
if not isinstance(chat_id, int):
|
|
59
|
+
chat = await self.get_chat(chat_id)
|
|
60
|
+
chat_id = chat.id
|
|
61
|
+
|
|
62
|
+
# Cancel existing listener in this chat if any
|
|
63
|
+
self.cancel_listener(chat_id)
|
|
64
|
+
|
|
65
|
+
loop = asyncio.get_running_loop()
|
|
66
|
+
future = loop.create_future()
|
|
67
|
+
future.add_done_callback(functools.partial(self.clear_listener, chat_id))
|
|
68
|
+
self.listening[chat_id] = {"future": future, "filters": filters}
|
|
69
|
+
|
|
70
|
+
return await asyncio.wait_for(future, timeout)
|
|
71
|
+
|
|
72
|
+
@should_patch()
|
|
73
|
+
async def ask(
|
|
74
|
+
self,
|
|
75
|
+
chat_id: int | str,
|
|
76
|
+
text: str,
|
|
77
|
+
filters: pyrogram.filters.Filter | None = None,
|
|
78
|
+
timeout: int | None = None,
|
|
79
|
+
*args,
|
|
80
|
+
**kwargs,
|
|
81
|
+
) -> pyrogram.types.Message:
|
|
82
|
+
request = await self.send_message(chat_id, text, *args, **kwargs)
|
|
83
|
+
response = await self.listen(chat_id, filters, timeout)
|
|
84
|
+
response.request = request
|
|
85
|
+
return response
|
|
86
|
+
|
|
87
|
+
@should_patch()
|
|
88
|
+
async def asker(
|
|
89
|
+
self,
|
|
90
|
+
chat_id: int | str,
|
|
91
|
+
filters: pyrogram.filters.Filter | None = None,
|
|
92
|
+
timeout: int = 119,
|
|
93
|
+
) -> pyrogram.types.Message | None:
|
|
94
|
+
try:
|
|
95
|
+
return await self.listen(chat_id, filters, timeout)
|
|
96
|
+
except asyncio.TimeoutError:
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
@should_patch()
|
|
100
|
+
def clear_listener(self, chat_id: int, future: asyncio.Future) -> None:
|
|
101
|
+
with suppress(KeyError):
|
|
102
|
+
if (
|
|
103
|
+
chat_id in self.listening
|
|
104
|
+
and future == self.listening[chat_id]["future"]
|
|
105
|
+
):
|
|
106
|
+
self.listening.pop(chat_id, None)
|
|
107
|
+
|
|
108
|
+
@should_patch()
|
|
109
|
+
def cancel_listener(self, chat_id: int) -> None:
|
|
110
|
+
listener = self.listening.get(chat_id)
|
|
111
|
+
if not listener or listener["future"].done():
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
if not listener["future"].done():
|
|
115
|
+
listener["future"].set_exception(ListenerCanceled())
|
|
116
|
+
self.clear_listener(chat_id, listener["future"])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@patch_into(pyrogram.handlers.message_handler.MessageHandler)
|
|
120
|
+
class MessageHandler:
|
|
121
|
+
@should_patch()
|
|
122
|
+
def __init__(self, callback: callable, filters=None) -> None:
|
|
123
|
+
self.user_callback = callback
|
|
124
|
+
self.old__init__(self.resolve_listener, filters)
|
|
125
|
+
|
|
126
|
+
@should_patch()
|
|
127
|
+
async def resolve_listener(self, client: "pyrogram.Client", message: pyrogram.types.Message, *args) -> None:
|
|
128
|
+
listener = client.listening.get(message.chat.id)
|
|
129
|
+
if listener and not listener["future"].done():
|
|
130
|
+
listener["future"].set_result(message)
|
|
131
|
+
raise pyrogram.StopPropagation
|
|
132
|
+
|
|
133
|
+
if listener and listener["future"].done():
|
|
134
|
+
client.clear_listener(message.chat.id, listener["future"])
|
|
135
|
+
|
|
136
|
+
await self.user_callback(client, message, *args)
|
|
137
|
+
|
|
138
|
+
@should_patch()
|
|
139
|
+
async def check(self, client: "pyrogram.Client", update: pyrogram.types.Message):
|
|
140
|
+
listener = client.listening.get(update.chat.id)
|
|
141
|
+
|
|
142
|
+
if listener and not listener["future"].done():
|
|
143
|
+
filters = listener["filters"]
|
|
144
|
+
if callable(filters):
|
|
145
|
+
if iscoroutinefunction(filters.__call__):
|
|
146
|
+
return await filters(client, update)
|
|
147
|
+
loop = asyncio.get_running_loop()
|
|
148
|
+
return await loop.run_in_executor(None, filters, client, update)
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
if callable(self.filters):
|
|
152
|
+
if iscoroutinefunction(self.filters.__call__):
|
|
153
|
+
return await self.filters(client, update)
|
|
154
|
+
loop = asyncio.get_running_loop()
|
|
155
|
+
return await loop.run_in_executor(None, self.filters, client, update)
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@patch_into(pyrogram.types.user_and_chats.chat.Chat)
|
|
160
|
+
class Chat(pyrogram.types.Chat):
|
|
161
|
+
@should_patch()
|
|
162
|
+
async def listen(self, *args, **kwargs) -> pyrogram.types.Message:
|
|
163
|
+
return await self._client.listen(self.id, *args, **kwargs)
|
|
164
|
+
|
|
165
|
+
@should_patch()
|
|
166
|
+
async def ask(self, *args, **kwargs) -> pyrogram.types.Message:
|
|
167
|
+
return await self._client.ask(self.id, *args, **kwargs)
|
|
168
|
+
|
|
169
|
+
@should_patch()
|
|
170
|
+
def cancel_listener(self) -> None:
|
|
171
|
+
return self._client.cancel_listener(self.id)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@patch_into(pyrogram.types.user_and_chats.user.User)
|
|
175
|
+
class User(pyrogram.types.User):
|
|
176
|
+
@should_patch()
|
|
177
|
+
async def listen(self, *args, **kwargs) -> pyrogram.types.Message:
|
|
178
|
+
return await self._client.listen(self.id, *args, **kwargs)
|
|
179
|
+
|
|
180
|
+
@should_patch()
|
|
181
|
+
async def ask(self, *args, **kwargs) -> pyrogram.types.Message:
|
|
182
|
+
return await self._client.ask(self.id, *args, **kwargs)
|
|
183
|
+
|
|
184
|
+
@should_patch()
|
|
185
|
+
def cancel_listener(self) -> None:
|
|
186
|
+
return self._client.cancel_listener(self.id)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>
|
|
1
|
+
"""patchpyro - A monkeypatcher add-on for Pyrogram
|
|
2
|
+
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>.
|
|
4
3
|
|
|
5
4
|
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
6
5
|
|
|
@@ -18,4 +17,6 @@ You should have received a copy of the GNU General Public License
|
|
|
18
17
|
along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
19
18
|
"""
|
|
20
19
|
|
|
21
|
-
from .utils import patch, patchable
|
|
20
|
+
from .utils import patch, patch_into, patchable, should_patch
|
|
21
|
+
|
|
22
|
+
__all__ = ["patch", "patch_into", "patchable", "should_patch"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""patchpyro - A monkeypatcher add-on for Pyrogram
|
|
2
|
+
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>.
|
|
3
|
+
|
|
4
|
+
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
5
|
+
Additional patching logic was adapted from kurimod (C) Dias Arthur.
|
|
6
|
+
|
|
7
|
+
patchpyro is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
patchpyro is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU General Public License
|
|
18
|
+
along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
"""
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from contextlib import asynccontextmanager, contextmanager
|
|
22
|
+
from inspect import iscoroutinefunction
|
|
23
|
+
from typing import TypeVar
|
|
24
|
+
|
|
25
|
+
from pyrogram.sync import async_to_sync
|
|
26
|
+
|
|
27
|
+
T = TypeVar("T")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def patch(target_class):
|
|
31
|
+
def is_patchable(item):
|
|
32
|
+
func = item[1]
|
|
33
|
+
return getattr(func, "should_patch", False) or getattr(func, "patchable", False)
|
|
34
|
+
|
|
35
|
+
def wrapper(container: type[T]) -> T:
|
|
36
|
+
for name, func in filter(is_patchable, container.__dict__.items()):
|
|
37
|
+
old = getattr(target_class, name, None)
|
|
38
|
+
if old is not None:
|
|
39
|
+
setattr(target_class, "old" + name, old)
|
|
40
|
+
|
|
41
|
+
tempConf = {
|
|
42
|
+
i: getattr(func, i, False)
|
|
43
|
+
for i in ["is_property", "is_static", "is_context"]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if not iscoroutinefunction(func):
|
|
47
|
+
async_to_sync(container, name)
|
|
48
|
+
func = getattr(container, name)
|
|
49
|
+
|
|
50
|
+
for tKey, tValue in tempConf.items():
|
|
51
|
+
setattr(func, tKey, tValue)
|
|
52
|
+
|
|
53
|
+
if func.is_property:
|
|
54
|
+
func = property(func)
|
|
55
|
+
elif func.is_static:
|
|
56
|
+
func = staticmethod(func)
|
|
57
|
+
elif func.is_context:
|
|
58
|
+
if iscoroutinefunction(func.__call__):
|
|
59
|
+
func = asynccontextmanager(func)
|
|
60
|
+
else:
|
|
61
|
+
func = contextmanager(func)
|
|
62
|
+
|
|
63
|
+
setattr(target_class, name, func)
|
|
64
|
+
return container
|
|
65
|
+
|
|
66
|
+
return wrapper
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def patchable(
|
|
70
|
+
func: Callable | None = None,
|
|
71
|
+
*,
|
|
72
|
+
is_property: bool = False,
|
|
73
|
+
is_static: bool = False,
|
|
74
|
+
is_context: bool = False,
|
|
75
|
+
) -> Callable:
|
|
76
|
+
def wrapper(f: Callable) -> Callable:
|
|
77
|
+
f.should_patch = True
|
|
78
|
+
f.patchable = True
|
|
79
|
+
f.is_property = is_property
|
|
80
|
+
f.is_static = is_static
|
|
81
|
+
f.is_context = is_context
|
|
82
|
+
return f
|
|
83
|
+
|
|
84
|
+
if func is not None:
|
|
85
|
+
return wrapper(func)
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
patch_into = patch
|
|
90
|
+
should_patch = patchable
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: patchpyro
|
|
3
|
+
Version: 2.1.1
|
|
4
|
+
Summary: A modified pyromod by a.devh.in
|
|
5
|
+
Home-page: https://github.com/adityaprasad502/patchpyro
|
|
6
|
+
Author: Cezar H. & adityaprasad502
|
|
7
|
+
Author-email: plutoniumx502@gmail.com
|
|
8
|
+
License: LGPLv3+
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: COPYING
|
|
15
|
+
License-File: COPYING.lesser
|
|
16
|
+
License-File: NOTICE
|
|
17
|
+
Requires-Dist: kurigram>=2.0.69
|
|
18
|
+
Dynamic: author
|
|
19
|
+
Dynamic: author-email
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: home-page
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
# PatchPyro
|
|
31
|
+
|
|
32
|
+
A fork of pyromod (renamed as patchpyro) providing conversation patches for Pyrogram-based clients.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
```text
|
|
36
|
+
kurigram>=2.0.69
|
|
37
|
+
python>=3.9
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
```bash
|
|
42
|
+
pip install patchpyro
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
Import `patchpyro` at least once in your script so you can use the modified Pyrogram in all files of the same process.
|
|
47
|
+
|
|
48
|
+
### Example
|
|
49
|
+
```python
|
|
50
|
+
# config.py
|
|
51
|
+
from patchpyro import listen # or import patchpyro.listen
|
|
52
|
+
from pyrogram import Client
|
|
53
|
+
|
|
54
|
+
listen.thank() # use this if your linter/IDE flags patchpyro as an unused import.
|
|
55
|
+
|
|
56
|
+
mybot = Client("mysession")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# any other .py
|
|
61
|
+
from config import mybot
|
|
62
|
+
# no need to import patchpyro again; Pyrogram is already monkeypatched globally (in the same process)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Available Methods
|
|
66
|
+
Just importing `patchpyro.listen` will automatically do the monkeypatch and you'll get these new methods:
|
|
67
|
+
|
|
68
|
+
### `Chat.listen`, `User.listen`
|
|
69
|
+
- `await mybot.listen(chat_id, filters=None, timeout=30)`
|
|
70
|
+
- Awaits a new message in the specified chat and returns it.
|
|
71
|
+
- Raises `asyncio.TimeoutError` if timeout (optional parameter) occurs.
|
|
72
|
+
- You can pass Update Filters to the filters parameter just like you do for the update handlers.
|
|
73
|
+
- E.g. `filters=filters.photo & filters.bot`
|
|
74
|
+
|
|
75
|
+
### `Chat.ask`, `User.ask`
|
|
76
|
+
- `await mybot.ask(text, chat_id, filters=None, timeout=30)`
|
|
77
|
+
- Same as `.listen()` above, but sends a message before awaiting.
|
|
78
|
+
- You can pass custom parameters to its internal `send_message()` call. Check the example below.
|
|
79
|
+
|
|
80
|
+
### `Chat.asker`, `User.asker`
|
|
81
|
+
- `await mybot.asker(chat_id, filters=None, timeout=36)`
|
|
82
|
+
- Same as `.listen()` but `.asker()` returns `None` instead of raising `asyncio.TimeoutError`.
|
|
83
|
+
- Useful for graceful timeout handling, `.asker()` has a default timeout of 2 minutes (adjustable via `timeout` argument).
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
### `asker()`
|
|
88
|
+
```python
|
|
89
|
+
# ...
|
|
90
|
+
sendx = await client.send_message(chat_id, "`Send me your name:`")
|
|
91
|
+
answer = await client.asker(chat_id, filters=None, timeout=60)
|
|
92
|
+
if not answer: # `None` if timeout reached with no reply.
|
|
93
|
+
return await sendx.reply_text("How long should I wait? Bye!")
|
|
94
|
+
await answer.reply_text(f"{answer.text}, That's a cool name!")
|
|
95
|
+
# ...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `ask()`
|
|
99
|
+
```python
|
|
100
|
+
# ...
|
|
101
|
+
answer = await client.ask(chat_id, '*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
|
|
102
|
+
await client.send_message(chat_id, f'Your name is: {answer.text}')
|
|
103
|
+
# ...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Copyright & License
|
|
107
|
+
This project includes code from:
|
|
108
|
+
- **[kurimod](https://github.com/ohmyarthur/kurimod)**: Monkeypatcher logic and modern async compatibility fixes.
|
|
109
|
+
- **Pyrogram**: Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>
|
|
110
|
+
- **pyromod**: Original conversation patch logic.
|
|
111
|
+
|
|
112
|
+
Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)
|
patchpyro-2.0.5/PKG-INFO
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: patchpyro
|
|
3
|
-
Version: 2.0.5
|
|
4
|
-
Summary: A modified pyromod by a.devh.in
|
|
5
|
-
Home-page: https://github.com/adityaprasad502/patchpyro
|
|
6
|
-
Author: Cezar H. & adityaprasad502
|
|
7
|
-
Author-email: plutoniumx502@gmail.com
|
|
8
|
-
License: LGPLv3+
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.9
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: COPYING
|
|
15
|
-
License-File: COPYING.lesser
|
|
16
|
-
License-File: NOTICE
|
|
17
|
-
Requires-Dist: kurigram>=2.0.69
|
|
18
|
-
Dynamic: author
|
|
19
|
-
Dynamic: author-email
|
|
20
|
-
Dynamic: classifier
|
|
21
|
-
Dynamic: description
|
|
22
|
-
Dynamic: description-content-type
|
|
23
|
-
Dynamic: home-page
|
|
24
|
-
Dynamic: license
|
|
25
|
-
Dynamic: license-file
|
|
26
|
-
Dynamic: requires-dist
|
|
27
|
-
Dynamic: requires-python
|
|
28
|
-
Dynamic: summary
|
|
29
|
-
|
|
30
|
-
# PatchPyro
|
|
31
|
-
### This is a fork of pyromod (renamed as patchpyro) for personal usecases.
|
|
32
|
-
|
|
33
|
-
### Remember that this fork contains only conversation patch.
|
|
34
|
-
|
|
35
|
-
# Requirements:
|
|
36
|
-
~~~python
|
|
37
|
-
kurigram>=2.0.69
|
|
38
|
-
python>=3.9
|
|
39
|
-
~~~
|
|
40
|
-
|
|
41
|
-
# Installation:
|
|
42
|
-
```
|
|
43
|
-
python -m pip install patchpyro
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
# Usage:
|
|
47
|
-
Import `patchpyro` at least one time in your script, so you'll be able to use modified pyrogram in all files of the same proccess.
|
|
48
|
-
Example:
|
|
49
|
-
|
|
50
|
-
```python
|
|
51
|
-
# config.py
|
|
52
|
-
from patchpyro import listen # or import patchpyro.listen
|
|
53
|
-
from pyrogram import Client
|
|
54
|
-
|
|
55
|
-
patchpyro.thank() # use this if ur linters/ide is removing patchpyro as unused import.
|
|
56
|
-
|
|
57
|
-
mybot = Client("mysession")
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
# any other .py
|
|
62
|
-
from config import mybot
|
|
63
|
-
# no need to import patchpyro again pyrogram is already monkeypatched globally (at the same proccess)
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## `patchpyro.listen`
|
|
67
|
-
Just import it, it will automatically do the monkeypatch and you'll get these new methods:
|
|
68
|
-
|
|
69
|
-
- Available bound methods::
|
|
70
|
-
- `Chat.listen, User.listen`
|
|
71
|
-
|
|
72
|
-
- `await mybot.listen(chat_id, filters=None, timeout=30)`
|
|
73
|
-
- raises `asyncio.TimeoutError` if timeout (optional parameter)
|
|
74
|
-
- Awaits for a new message in the specified chat and returns it.
|
|
75
|
-
- You can pass Update Filters to the filters parameter just like you do for the update handlers.
|
|
76
|
-
- E.g. `filters=filters.photo & filters.bot`
|
|
77
|
-
- `Chat.ask, User.ask`
|
|
78
|
-
|
|
79
|
-
- `await mybot.ask(text, chat_id, filters=None, timeout=30)`
|
|
80
|
-
- Same of `.listen()` above, but sends a message before awaiting.
|
|
81
|
-
- You can pass custom parameters to its send_message() call. Check the example below.
|
|
82
|
-
|
|
83
|
-
- `Chat.asker, User.asker`
|
|
84
|
-
- `await mybot.asker(chat_id, filters=None, timeout=36)`
|
|
85
|
-
- same as `.listen()` but `.asker()` returns `None` instead of raising `asyncio.TimeoutError`.
|
|
86
|
-
- Found useful in some cases for me, `.asker()` has a default timeout of 2 minutes.
|
|
87
|
-
- You can adjust it by passing as a argument. Refer the example code given below.
|
|
88
|
-
|
|
89
|
-
# Examples:
|
|
90
|
-
### For .asker():
|
|
91
|
-
```python
|
|
92
|
-
...
|
|
93
|
-
sendx = await client.send_message(chat_id, "`Send me your name:`")
|
|
94
|
-
answer = await client.asker(chat_id, filters=None, timeout=60)
|
|
95
|
-
if not answer: # `None` if timeout if no reply received.
|
|
96
|
-
return await sendx.reply_text("How long should I wait?, Eh! Bye!")
|
|
97
|
-
await answer.reply_text(f"{answer.text}, That's a cool name!")
|
|
98
|
-
...
|
|
99
|
-
```
|
|
100
|
-
### For .ask():
|
|
101
|
-
```python
|
|
102
|
-
...
|
|
103
|
-
answer = await client.ask(chat_id, '*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
|
|
104
|
-
await client.send_message(chat_id, f'Your name is: {answer.text}')
|
|
105
|
-
...
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
### Copyright & License
|
|
110
|
-
This project may include snippets of Pyrogram code
|
|
111
|
-
- Pyrogram - Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>
|
|
112
|
-
|
|
113
|
-
Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)
|
patchpyro-2.0.5/README.md
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# PatchPyro
|
|
2
|
-
### This is a fork of pyromod (renamed as patchpyro) for personal usecases.
|
|
3
|
-
|
|
4
|
-
### Remember that this fork contains only conversation patch.
|
|
5
|
-
|
|
6
|
-
# Requirements:
|
|
7
|
-
~~~python
|
|
8
|
-
kurigram>=2.0.69
|
|
9
|
-
python>=3.9
|
|
10
|
-
~~~
|
|
11
|
-
|
|
12
|
-
# Installation:
|
|
13
|
-
```
|
|
14
|
-
python -m pip install patchpyro
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
# Usage:
|
|
18
|
-
Import `patchpyro` at least one time in your script, so you'll be able to use modified pyrogram in all files of the same proccess.
|
|
19
|
-
Example:
|
|
20
|
-
|
|
21
|
-
```python
|
|
22
|
-
# config.py
|
|
23
|
-
from patchpyro import listen # or import patchpyro.listen
|
|
24
|
-
from pyrogram import Client
|
|
25
|
-
|
|
26
|
-
patchpyro.thank() # use this if ur linters/ide is removing patchpyro as unused import.
|
|
27
|
-
|
|
28
|
-
mybot = Client("mysession")
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
```python
|
|
32
|
-
# any other .py
|
|
33
|
-
from config import mybot
|
|
34
|
-
# no need to import patchpyro again pyrogram is already monkeypatched globally (at the same proccess)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## `patchpyro.listen`
|
|
38
|
-
Just import it, it will automatically do the monkeypatch and you'll get these new methods:
|
|
39
|
-
|
|
40
|
-
- Available bound methods::
|
|
41
|
-
- `Chat.listen, User.listen`
|
|
42
|
-
|
|
43
|
-
- `await mybot.listen(chat_id, filters=None, timeout=30)`
|
|
44
|
-
- raises `asyncio.TimeoutError` if timeout (optional parameter)
|
|
45
|
-
- Awaits for a new message in the specified chat and returns it.
|
|
46
|
-
- You can pass Update Filters to the filters parameter just like you do for the update handlers.
|
|
47
|
-
- E.g. `filters=filters.photo & filters.bot`
|
|
48
|
-
- `Chat.ask, User.ask`
|
|
49
|
-
|
|
50
|
-
- `await mybot.ask(text, chat_id, filters=None, timeout=30)`
|
|
51
|
-
- Same of `.listen()` above, but sends a message before awaiting.
|
|
52
|
-
- You can pass custom parameters to its send_message() call. Check the example below.
|
|
53
|
-
|
|
54
|
-
- `Chat.asker, User.asker`
|
|
55
|
-
- `await mybot.asker(chat_id, filters=None, timeout=36)`
|
|
56
|
-
- same as `.listen()` but `.asker()` returns `None` instead of raising `asyncio.TimeoutError`.
|
|
57
|
-
- Found useful in some cases for me, `.asker()` has a default timeout of 2 minutes.
|
|
58
|
-
- You can adjust it by passing as a argument. Refer the example code given below.
|
|
59
|
-
|
|
60
|
-
# Examples:
|
|
61
|
-
### For .asker():
|
|
62
|
-
```python
|
|
63
|
-
...
|
|
64
|
-
sendx = await client.send_message(chat_id, "`Send me your name:`")
|
|
65
|
-
answer = await client.asker(chat_id, filters=None, timeout=60)
|
|
66
|
-
if not answer: # `None` if timeout if no reply received.
|
|
67
|
-
return await sendx.reply_text("How long should I wait?, Eh! Bye!")
|
|
68
|
-
await answer.reply_text(f"{answer.text}, That's a cool name!")
|
|
69
|
-
...
|
|
70
|
-
```
|
|
71
|
-
### For .ask():
|
|
72
|
-
```python
|
|
73
|
-
...
|
|
74
|
-
answer = await client.ask(chat_id, '*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
|
|
75
|
-
await client.send_message(chat_id, f'Your name is: {answer.text}')
|
|
76
|
-
...
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
### Copyright & License
|
|
81
|
-
This project may include snippets of Pyrogram code
|
|
82
|
-
- Pyrogram - Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>
|
|
83
|
-
|
|
84
|
-
Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
patchpyro - A monkeypatcher add-on for Pyrogram
|
|
3
|
-
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>
|
|
4
|
-
|
|
5
|
-
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
6
|
-
|
|
7
|
-
patchpyro is free software: you can redistribute it and/or modify
|
|
8
|
-
it under the terms of the GNU General Public License as published by
|
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
-
(at your option) any later version.
|
|
11
|
-
|
|
12
|
-
patchpyro is distributed in the hope that it will be useful,
|
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
-
GNU General Public License for more details.
|
|
16
|
-
|
|
17
|
-
You should have received a copy of the GNU General Public License
|
|
18
|
-
along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
__version__ = "2.0.5"
|
|
23
|
-
# change in setup.py aswell
|
|
24
|
-
|
|
25
|
-
def thank():
|
|
26
|
-
"A dummy function to prevent patchpyro from being removed by formatters and linters. It does nothing, just prints a message to the console to indicate that patchpyro is not an unused import."
|
|
27
|
-
# print("Thank you for using patchpyro! If you see this message. author: a.devh.in, version: " + __version__)
|
|
28
|
-
print("PatchPyro " + __version__ + " is imported! Thank you for using it! Author: a.devh.in")
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
patchpyro - A monkeypatcher add-on for Pyrogram
|
|
3
|
-
Copyright (C) 2026 Aditya Prasad S <https://github.com/adityaprasad502>
|
|
4
|
-
|
|
5
|
-
This file is part of patchpyro and was forked from usernein/pyromod.
|
|
6
|
-
|
|
7
|
-
patchpyro is free software: you can redistribute it and/or modify
|
|
8
|
-
it under the terms of the GNU General Public License as published by
|
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
-
(at your option) any later version.
|
|
11
|
-
|
|
12
|
-
patchpyro is distributed in the hope that it will be useful,
|
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
-
GNU General Public License for more details.
|
|
16
|
-
|
|
17
|
-
You should have received a copy of the GNU General Public License
|
|
18
|
-
along with patchpyro. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
import asyncio
|
|
22
|
-
import functools
|
|
23
|
-
from contextlib import suppress
|
|
24
|
-
|
|
25
|
-
import pyrogram
|
|
26
|
-
|
|
27
|
-
from ..utils import patch, patchable
|
|
28
|
-
|
|
29
|
-
loop = asyncio.get_event_loop()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class ListenerCanceled(Exception):
|
|
33
|
-
pass
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
pyrogram.errors.ListenerCanceled = ListenerCanceled
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@patch(pyrogram.client.Client)
|
|
40
|
-
class Client:
|
|
41
|
-
@patchable
|
|
42
|
-
def __init__(self, *args, **kwargs):
|
|
43
|
-
self.listening = {}
|
|
44
|
-
self.using_mod = True
|
|
45
|
-
|
|
46
|
-
self.old__init__(*args, **kwargs)
|
|
47
|
-
|
|
48
|
-
@patchable
|
|
49
|
-
async def listen(self, chat_id, filters=None, timeout=None):
|
|
50
|
-
if type(chat_id) != int:
|
|
51
|
-
chat = await self.get_chat(chat_id)
|
|
52
|
-
chat_id = chat.id
|
|
53
|
-
|
|
54
|
-
future = loop.create_future()
|
|
55
|
-
future.add_done_callback(functools.partial(self.clear_listener, chat_id))
|
|
56
|
-
self.listening.update({chat_id: {"future": future, "filters": filters}})
|
|
57
|
-
return await asyncio.wait_for(future, timeout)
|
|
58
|
-
|
|
59
|
-
@patchable
|
|
60
|
-
async def ask(self, chat_id, text, filters=None, timeout=None, *args, **kwargs):
|
|
61
|
-
request = await self.send_message(chat_id, text, *args, **kwargs)
|
|
62
|
-
response = await self.listen(chat_id, filters, timeout)
|
|
63
|
-
response.request = request
|
|
64
|
-
return response
|
|
65
|
-
|
|
66
|
-
@patchable
|
|
67
|
-
async def asker(self, chat_id, filters=None, timeout=119):
|
|
68
|
-
try:
|
|
69
|
-
response = await self.listen(chat_id, filters, timeout)
|
|
70
|
-
except asyncio.TimeoutError:
|
|
71
|
-
response = None
|
|
72
|
-
return response
|
|
73
|
-
|
|
74
|
-
@patchable
|
|
75
|
-
def clear_listener(self, chat_id, future):
|
|
76
|
-
with suppress(KeyError):
|
|
77
|
-
if (
|
|
78
|
-
chat_id in self.listening
|
|
79
|
-
and future == self.listening[chat_id]["future"]
|
|
80
|
-
):
|
|
81
|
-
self.listening.pop(chat_id, None)
|
|
82
|
-
|
|
83
|
-
@patchable
|
|
84
|
-
def cancel_listener(self, chat_id):
|
|
85
|
-
listener = self.listening.get(chat_id)
|
|
86
|
-
if not listener or listener["future"].done():
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
listener["future"].set_exception(ListenerCanceled())
|
|
90
|
-
self.clear_listener(chat_id, listener["future"])
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@patch(pyrogram.handlers.message_handler.MessageHandler)
|
|
94
|
-
class MessageHandler:
|
|
95
|
-
@patchable
|
|
96
|
-
def __init__(self, callback: callable, filters=None):
|
|
97
|
-
self.user_callback = callback
|
|
98
|
-
self.old__init__(self.resolve_listener, filters)
|
|
99
|
-
|
|
100
|
-
@patchable
|
|
101
|
-
async def resolve_listener(self, client, message, *args):
|
|
102
|
-
listener = client.listening.get(message.chat.id)
|
|
103
|
-
if listener and not listener["future"].done():
|
|
104
|
-
listener["future"].set_result(message)
|
|
105
|
-
else:
|
|
106
|
-
if listener and listener["future"].done():
|
|
107
|
-
client.clear_listener(message.chat.id, listener["future"])
|
|
108
|
-
await self.user_callback(client, message, *args)
|
|
109
|
-
|
|
110
|
-
@patchable
|
|
111
|
-
async def check(self, client, update):
|
|
112
|
-
listener = client.listening.get(update.chat.id)
|
|
113
|
-
|
|
114
|
-
if listener and not listener["future"].done():
|
|
115
|
-
return (
|
|
116
|
-
await listener["filters"](client, update)
|
|
117
|
-
if callable(listener["filters"])
|
|
118
|
-
else True
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
return await self.filters(client, update) if callable(self.filters) else True
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
@patch(pyrogram.types.user_and_chats.chat.Chat)
|
|
125
|
-
class Chat(pyrogram.types.Chat):
|
|
126
|
-
@patchable
|
|
127
|
-
def listen(self, *args, **kwargs):
|
|
128
|
-
return self._client.listen(self.id, *args, **kwargs)
|
|
129
|
-
|
|
130
|
-
@patchable
|
|
131
|
-
def ask(self, *args, **kwargs):
|
|
132
|
-
return self._client.ask(self.id, *args, **kwargs)
|
|
133
|
-
|
|
134
|
-
@patchable
|
|
135
|
-
def cancel_listener(self):
|
|
136
|
-
return self._client.cancel_listener(self.id)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
@patch(pyrogram.types.user_and_chats.user.User)
|
|
140
|
-
class User(pyrogram.types.User):
|
|
141
|
-
@patchable
|
|
142
|
-
def listen(self, *args, **kwargs):
|
|
143
|
-
return self._client.listen(self.id, *args, **kwargs)
|
|
144
|
-
|
|
145
|
-
@patchable
|
|
146
|
-
def ask(self, *args, **kwargs):
|
|
147
|
-
return self._client.ask(self.id, *args, **kwargs)
|
|
148
|
-
|
|
149
|
-
@patchable
|
|
150
|
-
def cancel_listener(self):
|
|
151
|
-
return self._client.cancel_listener(self.id)
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: patchpyro
|
|
3
|
-
Version: 2.0.5
|
|
4
|
-
Summary: A modified pyromod by a.devh.in
|
|
5
|
-
Home-page: https://github.com/adityaprasad502/patchpyro
|
|
6
|
-
Author: Cezar H. & adityaprasad502
|
|
7
|
-
Author-email: plutoniumx502@gmail.com
|
|
8
|
-
License: LGPLv3+
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.9
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: COPYING
|
|
15
|
-
License-File: COPYING.lesser
|
|
16
|
-
License-File: NOTICE
|
|
17
|
-
Requires-Dist: kurigram>=2.0.69
|
|
18
|
-
Dynamic: author
|
|
19
|
-
Dynamic: author-email
|
|
20
|
-
Dynamic: classifier
|
|
21
|
-
Dynamic: description
|
|
22
|
-
Dynamic: description-content-type
|
|
23
|
-
Dynamic: home-page
|
|
24
|
-
Dynamic: license
|
|
25
|
-
Dynamic: license-file
|
|
26
|
-
Dynamic: requires-dist
|
|
27
|
-
Dynamic: requires-python
|
|
28
|
-
Dynamic: summary
|
|
29
|
-
|
|
30
|
-
# PatchPyro
|
|
31
|
-
### This is a fork of pyromod (renamed as patchpyro) for personal usecases.
|
|
32
|
-
|
|
33
|
-
### Remember that this fork contains only conversation patch.
|
|
34
|
-
|
|
35
|
-
# Requirements:
|
|
36
|
-
~~~python
|
|
37
|
-
kurigram>=2.0.69
|
|
38
|
-
python>=3.9
|
|
39
|
-
~~~
|
|
40
|
-
|
|
41
|
-
# Installation:
|
|
42
|
-
```
|
|
43
|
-
python -m pip install patchpyro
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
# Usage:
|
|
47
|
-
Import `patchpyro` at least one time in your script, so you'll be able to use modified pyrogram in all files of the same proccess.
|
|
48
|
-
Example:
|
|
49
|
-
|
|
50
|
-
```python
|
|
51
|
-
# config.py
|
|
52
|
-
from patchpyro import listen # or import patchpyro.listen
|
|
53
|
-
from pyrogram import Client
|
|
54
|
-
|
|
55
|
-
patchpyro.thank() # use this if ur linters/ide is removing patchpyro as unused import.
|
|
56
|
-
|
|
57
|
-
mybot = Client("mysession")
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
# any other .py
|
|
62
|
-
from config import mybot
|
|
63
|
-
# no need to import patchpyro again pyrogram is already monkeypatched globally (at the same proccess)
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## `patchpyro.listen`
|
|
67
|
-
Just import it, it will automatically do the monkeypatch and you'll get these new methods:
|
|
68
|
-
|
|
69
|
-
- Available bound methods::
|
|
70
|
-
- `Chat.listen, User.listen`
|
|
71
|
-
|
|
72
|
-
- `await mybot.listen(chat_id, filters=None, timeout=30)`
|
|
73
|
-
- raises `asyncio.TimeoutError` if timeout (optional parameter)
|
|
74
|
-
- Awaits for a new message in the specified chat and returns it.
|
|
75
|
-
- You can pass Update Filters to the filters parameter just like you do for the update handlers.
|
|
76
|
-
- E.g. `filters=filters.photo & filters.bot`
|
|
77
|
-
- `Chat.ask, User.ask`
|
|
78
|
-
|
|
79
|
-
- `await mybot.ask(text, chat_id, filters=None, timeout=30)`
|
|
80
|
-
- Same of `.listen()` above, but sends a message before awaiting.
|
|
81
|
-
- You can pass custom parameters to its send_message() call. Check the example below.
|
|
82
|
-
|
|
83
|
-
- `Chat.asker, User.asker`
|
|
84
|
-
- `await mybot.asker(chat_id, filters=None, timeout=36)`
|
|
85
|
-
- same as `.listen()` but `.asker()` returns `None` instead of raising `asyncio.TimeoutError`.
|
|
86
|
-
- Found useful in some cases for me, `.asker()` has a default timeout of 2 minutes.
|
|
87
|
-
- You can adjust it by passing as a argument. Refer the example code given below.
|
|
88
|
-
|
|
89
|
-
# Examples:
|
|
90
|
-
### For .asker():
|
|
91
|
-
```python
|
|
92
|
-
...
|
|
93
|
-
sendx = await client.send_message(chat_id, "`Send me your name:`")
|
|
94
|
-
answer = await client.asker(chat_id, filters=None, timeout=60)
|
|
95
|
-
if not answer: # `None` if timeout if no reply received.
|
|
96
|
-
return await sendx.reply_text("How long should I wait?, Eh! Bye!")
|
|
97
|
-
await answer.reply_text(f"{answer.text}, That's a cool name!")
|
|
98
|
-
...
|
|
99
|
-
```
|
|
100
|
-
### For .ask():
|
|
101
|
-
```python
|
|
102
|
-
...
|
|
103
|
-
answer = await client.ask(chat_id, '*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
|
|
104
|
-
await client.send_message(chat_id, f'Your name is: {answer.text}')
|
|
105
|
-
...
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
### Copyright & License
|
|
110
|
-
This project may include snippets of Pyrogram code
|
|
111
|
-
- Pyrogram - Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>
|
|
112
|
-
|
|
113
|
-
Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|