Pytdbot 0.9.4__tar.gz → 0.9.5__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.
- {pytdbot-0.9.4 → pytdbot-0.9.5}/PKG-INFO +2 -2
- {pytdbot-0.9.4 → pytdbot-0.9.5}/Pytdbot.egg-info/PKG-INFO +2 -2
- {pytdbot-0.9.4 → pytdbot-0.9.5}/Pytdbot.egg-info/SOURCES.txt +3 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/README.md +1 -1
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/__init__.py +1 -1
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/client.py +107 -12
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/handlers/decorators.py +45 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/methods/td_functions.py +296 -4
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/__init__.py +37 -3
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/types.py +899 -20
- pytdbot-0.9.5/pytdbot/types/tdserver/__init__.py +3 -0
- pytdbot-0.9.5/pytdbot/types/tdserver/schedule.py +96 -0
- pytdbot-0.9.5/pytdbot/types/tdserver/stats.py +65 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/__init__.py +4 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/text_format.py +44 -12
- {pytdbot-0.9.4 → pytdbot-0.9.5}/LICENSE +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/MANIFEST.in +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/Pytdbot.egg-info/dependency_links.txt +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/Pytdbot.egg-info/requires.txt +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/Pytdbot.egg-info/top_level.txt +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/client_manager.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/exception/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/filters.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/handlers/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/handlers/handler.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/handlers/td_updates.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/methods/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/methods/methods.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/tdjson/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/tdjson/tdjson.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/plugins/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/bound_methods/__init__.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/bound_methods/callback_query.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/bound_methods/chatActions.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/bound_methods/file.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/types/td_types/bound_methods/message.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/asyncio_utils.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/escape.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/json_utils.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/obj_encoder.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/strings.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/pytdbot/utils/webapps.py +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/requirements.txt +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/setup.cfg +0 -0
- {pytdbot-0.9.4 → pytdbot-0.9.5}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Pytdbot
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: Easy-to-use asynchronous TDLib wrapper for Python.
|
|
5
5
|
Home-page: https://github.com/pytdbot/client
|
|
6
6
|
Author: AYMEN Mohammed
|
|
@@ -30,7 +30,7 @@ Dynamic: requires-dist
|
|
|
30
30
|
Dynamic: requires-python
|
|
31
31
|
Dynamic: summary
|
|
32
32
|
|
|
33
|
-
# Pytdbot [](https://pypi.org/project/Pytdbot) [](https://pypi.org/project/Pytdbot) [](https://github.com/tdlib/td) [](https://pepy.tech/project/pytdbot) [](https://t.me/pytdbotchat)
|
|
34
34
|
|
|
35
35
|
Pytdbot (Python TDLib) is an asynchronous [**TDLib**](https://github.com/tdlib/td) wrapper for **Telegram** users/bots written in **Python**.
|
|
36
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Pytdbot
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: Easy-to-use asynchronous TDLib wrapper for Python.
|
|
5
5
|
Home-page: https://github.com/pytdbot/client
|
|
6
6
|
Author: AYMEN Mohammed
|
|
@@ -30,7 +30,7 @@ Dynamic: requires-dist
|
|
|
30
30
|
Dynamic: requires-python
|
|
31
31
|
Dynamic: summary
|
|
32
32
|
|
|
33
|
-
# Pytdbot [](https://pypi.org/project/Pytdbot) [](https://pypi.org/project/Pytdbot) [](https://github.com/tdlib/td) [](https://pepy.tech/project/pytdbot) [](https://t.me/pytdbotchat)
|
|
34
34
|
|
|
35
35
|
Pytdbot (Python TDLib) is an asynchronous [**TDLib**](https://github.com/tdlib/td) wrapper for **Telegram** users/bots written in **Python**.
|
|
36
36
|
|
|
@@ -31,6 +31,9 @@ pytdbot/types/td_types/bound_methods/callback_query.py
|
|
|
31
31
|
pytdbot/types/td_types/bound_methods/chatActions.py
|
|
32
32
|
pytdbot/types/td_types/bound_methods/file.py
|
|
33
33
|
pytdbot/types/td_types/bound_methods/message.py
|
|
34
|
+
pytdbot/types/tdserver/__init__.py
|
|
35
|
+
pytdbot/types/tdserver/schedule.py
|
|
36
|
+
pytdbot/types/tdserver/stats.py
|
|
34
37
|
pytdbot/utils/__init__.py
|
|
35
38
|
pytdbot/utils/asyncio_utils.py
|
|
36
39
|
pytdbot/utils/escape.py
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Pytdbot [](https://pypi.org/project/Pytdbot) [](https://pypi.org/project/Pytdbot) [](https://github.com/tdlib/td) [](https://pepy.tech/project/pytdbot) [](https://t.me/pytdbotchat)
|
|
2
2
|
|
|
3
3
|
Pytdbot (Python TDLib) is an asynchronous [**TDLib**](https://github.com/tdlib/td) wrapper for **Telegram** users/bots written in **Python**.
|
|
4
4
|
|
|
@@ -102,6 +102,9 @@ class Client(Decorators, Methods):
|
|
|
102
102
|
|
|
103
103
|
td_log (:class:`~pytdbot.types.LogStream`, *optional*):
|
|
104
104
|
Log stream. Default is ``None`` (Log to ``stdout``)
|
|
105
|
+
|
|
106
|
+
user_bot (``bool``, *optional*):
|
|
107
|
+
Pass ``True`` if this is a user-bot. Default is ``False``
|
|
105
108
|
"""
|
|
106
109
|
|
|
107
110
|
def __init__(
|
|
@@ -183,6 +186,7 @@ class Client(Decorators, Methods):
|
|
|
183
186
|
self._current_handlers = {}
|
|
184
187
|
self._results: Dict[str, asyncio.Future] = {}
|
|
185
188
|
self._workers_tasks = None
|
|
189
|
+
self.__rabbitmq_iterator_task = None
|
|
186
190
|
self.__authorization_state = None
|
|
187
191
|
self.__cache = {"is_coro_filter": {}}
|
|
188
192
|
self.__local_handlers = {
|
|
@@ -226,17 +230,72 @@ class Client(Decorators, Methods):
|
|
|
226
230
|
|
|
227
231
|
return self.__authorization_state
|
|
228
232
|
|
|
233
|
+
async def getServerStats(
|
|
234
|
+
self,
|
|
235
|
+
) -> Union["pytdbot.types.ServerStats", "pytdbot.types.Error"]:
|
|
236
|
+
"""Returns TDLib Server stats"""
|
|
237
|
+
|
|
238
|
+
self._check_rabbitmq()
|
|
239
|
+
|
|
240
|
+
return await self.invoke({"@type": "getServerStats"})
|
|
241
|
+
|
|
242
|
+
async def scheduleEvent(
|
|
243
|
+
self, data: dict, send_at: int
|
|
244
|
+
) -> Union["pytdbot.types.ScheduledEvent", "pytdbot.types.Error"]:
|
|
245
|
+
"""Schedule an event
|
|
246
|
+
|
|
247
|
+
Parameters:
|
|
248
|
+
data (:class:`dict`):
|
|
249
|
+
The event data to be scheduled
|
|
250
|
+
|
|
251
|
+
send_at (:class:`int`):
|
|
252
|
+
Unix timestamp when the event should be sent
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
self._check_rabbitmq()
|
|
256
|
+
|
|
257
|
+
if not isinstance(data, dict):
|
|
258
|
+
raise ValueError("data must be dict")
|
|
259
|
+
if not isinstance(send_at, (int, float)):
|
|
260
|
+
raise ValueError("send_at must be int")
|
|
261
|
+
|
|
262
|
+
return await self.invoke(
|
|
263
|
+
{"@type": "scheduleEvent", "data": data, "send_at": send_at}
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
async def cancelScheduledEvent(
|
|
267
|
+
self, event_id: str
|
|
268
|
+
) -> Union["pytdbot.types.Ok", "pytdbot.types.Error"]:
|
|
269
|
+
"""Cancel a scheduled event
|
|
270
|
+
|
|
271
|
+
Parameters:
|
|
272
|
+
event_id (:class:`str`):
|
|
273
|
+
Event ID to cancel
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
self._check_rabbitmq()
|
|
277
|
+
|
|
278
|
+
if not isinstance(event_id, str):
|
|
279
|
+
raise ValueError("event_id must be str")
|
|
280
|
+
|
|
281
|
+
return await self.invoke(
|
|
282
|
+
{"@type": "cancelScheduledEvent", "event_id": event_id}
|
|
283
|
+
)
|
|
284
|
+
|
|
229
285
|
async def start(self) -> None:
|
|
230
286
|
r"""Start pytdbot client"""
|
|
231
287
|
|
|
232
288
|
if not self.is_running:
|
|
233
289
|
self.logger.info("Starting pytdbot client...")
|
|
234
290
|
|
|
235
|
-
if
|
|
291
|
+
if self.is_rabbitmq:
|
|
292
|
+
await self.__start_rabbitmq()
|
|
293
|
+
elif not self.client_manager:
|
|
236
294
|
self.client_manager = ClientManager(
|
|
237
295
|
self, self.lib_path, self.td_verbosity, loop=self.loop
|
|
238
296
|
)
|
|
239
297
|
await self.client_manager.start()
|
|
298
|
+
self.is_running = True
|
|
240
299
|
|
|
241
300
|
if isinstance(self.td_log, LogStream) and not self.is_rabbitmq:
|
|
242
301
|
await self.__send(
|
|
@@ -245,21 +304,22 @@ class Client(Decorators, Methods):
|
|
|
245
304
|
|
|
246
305
|
if isinstance(self.workers, int):
|
|
247
306
|
self._workers_tasks = [
|
|
248
|
-
self.loop.create_task(
|
|
307
|
+
self.loop.create_task(
|
|
308
|
+
self._queue_update_worker()
|
|
309
|
+
if not self.is_rabbitmq
|
|
310
|
+
else self.__rabbitmq_worker()
|
|
311
|
+
)
|
|
249
312
|
for _ in range(self.workers)
|
|
250
313
|
]
|
|
251
314
|
self.__is_queue_worker = True
|
|
252
315
|
|
|
253
316
|
self.logger.info(f"Started with {self.workers} workers")
|
|
317
|
+
elif self.is_rabbitmq:
|
|
318
|
+
raise ValueError("workers must be an int when using TDLib Server")
|
|
254
319
|
else:
|
|
255
320
|
self.__is_queue_worker = False
|
|
256
321
|
self.logger.info("Started with unlimited updates processes")
|
|
257
322
|
|
|
258
|
-
if self.is_rabbitmq:
|
|
259
|
-
await self.__start_rabbitmq()
|
|
260
|
-
else: # client_manager
|
|
261
|
-
self.is_running = True
|
|
262
|
-
|
|
263
323
|
self.loop.create_task(
|
|
264
324
|
self.getOption("version")
|
|
265
325
|
) # Ping TDLib to start processing updates
|
|
@@ -535,7 +595,7 @@ class Client(Decorators, Methods):
|
|
|
535
595
|
|
|
536
596
|
self.__stop_client()
|
|
537
597
|
|
|
538
|
-
if not self.client_manager.start_clients_on_add:
|
|
598
|
+
if self.client_manager and not self.client_manager.start_clients_on_add:
|
|
539
599
|
await self.client_manager.close()
|
|
540
600
|
|
|
541
601
|
self.logger.info("Instance closed")
|
|
@@ -567,6 +627,9 @@ class Client(Decorators, Methods):
|
|
|
567
627
|
else:
|
|
568
628
|
self.client_manager.send(self.client_id, request)
|
|
569
629
|
|
|
630
|
+
def _check_rabbitmq(self):
|
|
631
|
+
assert self.is_rabbitmq, "This method is only available for TDLib Server"
|
|
632
|
+
|
|
570
633
|
def _check_init_args(self):
|
|
571
634
|
if self.user_bot:
|
|
572
635
|
return
|
|
@@ -696,7 +759,7 @@ class Client(Decorators, Methods):
|
|
|
696
759
|
if update_handler:
|
|
697
760
|
self.loop.create_task(update_handler(update))
|
|
698
761
|
|
|
699
|
-
if self.__is_queue_worker:
|
|
762
|
+
if not self.is_rabbitmq and self.__is_queue_worker:
|
|
700
763
|
self.queue.put_nowait(update)
|
|
701
764
|
else:
|
|
702
765
|
await self._handle_update(update)
|
|
@@ -884,7 +947,9 @@ class Client(Decorators, Methods):
|
|
|
884
947
|
f"{str(self.me.id) if not self.me.usernames else '@' + self.me.usernames.editable_username}"
|
|
885
948
|
)
|
|
886
949
|
|
|
887
|
-
if
|
|
950
|
+
if self.authorization_state == "authorizationStateClosing":
|
|
951
|
+
self.__is_closing = True
|
|
952
|
+
elif (
|
|
888
953
|
self.authorization_state == "authorizationStateClosed"
|
|
889
954
|
and self.__is_closing is False
|
|
890
955
|
):
|
|
@@ -955,6 +1020,8 @@ class Client(Decorators, Methods):
|
|
|
955
1020
|
)
|
|
956
1021
|
self.__rchannel = await self.__rconnection.channel()
|
|
957
1022
|
|
|
1023
|
+
self.logger.info("Connected to TDLib server via RabbitMQ")
|
|
1024
|
+
|
|
958
1025
|
updates_queue = await self.__get_updates_queue()
|
|
959
1026
|
|
|
960
1027
|
notify_queue = await self.__rchannel.declare_queue(
|
|
@@ -968,7 +1035,7 @@ class Client(Decorators, Methods):
|
|
|
968
1035
|
|
|
969
1036
|
self.__rqueues = {
|
|
970
1037
|
"updates": updates_queue,
|
|
971
|
-
"requests": await self.__rchannel.get_queue(self.my_id
|
|
1038
|
+
"requests": await self.__rchannel.get_queue(f"{self.my_id}_requests"),
|
|
972
1039
|
"notify": notify_queue,
|
|
973
1040
|
"responses": responses_queue,
|
|
974
1041
|
}
|
|
@@ -986,10 +1053,35 @@ class Client(Decorators, Methods):
|
|
|
986
1053
|
await self.process_update(obj_to_dict(update))
|
|
987
1054
|
|
|
988
1055
|
if not self.no_updates:
|
|
989
|
-
|
|
1056
|
+
self.__rabbitmq_iterator_task = self.loop.create_task(
|
|
1057
|
+
self.__rabbitmq_iterator()
|
|
1058
|
+
)
|
|
990
1059
|
|
|
991
1060
|
await self.__rqueues["notify"].consume(self.__on_update, no_ack=True)
|
|
992
1061
|
|
|
1062
|
+
async def __rabbitmq_iterator(self):
|
|
1063
|
+
async with self.__rqueues["updates"].iterator() as iterator:
|
|
1064
|
+
async for message in iterator:
|
|
1065
|
+
await self.queue.put(message)
|
|
1066
|
+
|
|
1067
|
+
async def __rabbitmq_worker(self):
|
|
1068
|
+
while self.is_running:
|
|
1069
|
+
message: aio_pika.IncomingMessage = await self.queue.get()
|
|
1070
|
+
|
|
1071
|
+
try:
|
|
1072
|
+
update = json_loads(message.body)
|
|
1073
|
+
if self.__is_closing and not isinstance(
|
|
1074
|
+
update, types.UpdateAuthorizationState
|
|
1075
|
+
):
|
|
1076
|
+
await message.nack(requeue=True)
|
|
1077
|
+
continue
|
|
1078
|
+
|
|
1079
|
+
await self.process_update(update)
|
|
1080
|
+
except Exception:
|
|
1081
|
+
self.logger.exception("Error processing message")
|
|
1082
|
+
|
|
1083
|
+
await message.ack() # ack after processing
|
|
1084
|
+
|
|
993
1085
|
async def __handle_rabbitmq_message(self, message: aio_pika.IncomingMessage):
|
|
994
1086
|
await self.process_update(json_loads(message.body))
|
|
995
1087
|
|
|
@@ -1026,6 +1118,9 @@ class Client(Decorators, Methods):
|
|
|
1026
1118
|
self.is_authenticated = False
|
|
1027
1119
|
self.is_running = False
|
|
1028
1120
|
|
|
1121
|
+
if self.__rabbitmq_iterator_task:
|
|
1122
|
+
self.__rabbitmq_iterator_task.cancel()
|
|
1123
|
+
|
|
1029
1124
|
if self.__is_queue_worker:
|
|
1030
1125
|
for worker_task in self._workers_tasks:
|
|
1031
1126
|
worker_task.cancel()
|
|
@@ -134,3 +134,48 @@ class Decorators(Updates):
|
|
|
134
134
|
return func
|
|
135
135
|
|
|
136
136
|
return decorator
|
|
137
|
+
|
|
138
|
+
def on_updateScheduledEvent(
|
|
139
|
+
self: "pytdbot.Client" = None,
|
|
140
|
+
filters: "pytdbot.filters.Filter" = None,
|
|
141
|
+
position: int = None,
|
|
142
|
+
inner_object: bool = False,
|
|
143
|
+
) -> None:
|
|
144
|
+
r"""A scheduled event has been triggered
|
|
145
|
+
|
|
146
|
+
Parameters:
|
|
147
|
+
filters (:class:`~pytdbot.filters.Filter`, *optional*):
|
|
148
|
+
An update filter
|
|
149
|
+
|
|
150
|
+
position (``int``, *optional*):
|
|
151
|
+
The function position in handlers list. Default is ``None`` (append)
|
|
152
|
+
|
|
153
|
+
inner_object (``bool``, *optional*):
|
|
154
|
+
Wether to pass an inner object of update or not; for example ``UpdateNewMessage.message``. Default is ``False``
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
:py:class:`TypeError`
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
def decorator(func: Callable) -> Callable:
|
|
161
|
+
if hasattr(func, "_handler"):
|
|
162
|
+
return func
|
|
163
|
+
elif isinstance(self, pytdbot.Client):
|
|
164
|
+
if iscoroutinefunction(func):
|
|
165
|
+
self.add_handler(
|
|
166
|
+
"updateScheduledEvent", func, filters, position, inner_object
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
raise TypeError("Handler must be async")
|
|
170
|
+
elif isinstance(self, pytdbot.filters.Filter):
|
|
171
|
+
func._handler = Handler(
|
|
172
|
+
func, "updateScheduledEvent", self, position, inner_object
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
func._handler = Handler(
|
|
176
|
+
func, "updateScheduledEvent", filters, position, inner_object
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return func
|
|
180
|
+
|
|
181
|
+
return decorator
|