Pytdbot 0.9.5.dev0__tar.gz → 0.9.6__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.
Files changed (47) hide show
  1. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/PKG-INFO +2 -2
  2. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/Pytdbot.egg-info/PKG-INFO +2 -2
  3. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/Pytdbot.egg-info/SOURCES.txt +6 -7
  4. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/README.md +1 -1
  5. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/__init__.py +1 -1
  6. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/client.py +179 -63
  7. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/handlers/decorators.py +95 -15
  8. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/handlers/handler.py +3 -1
  9. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/handlers/td_updates.py +4072 -489
  10. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/methods/methods.py +6 -1
  11. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/methods/td_functions.py +444 -34
  12. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/tdjson/tdjson.py +15 -2
  13. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/types/__init__.py +115 -15
  14. {pytdbot-0.9.5.dev0/pytdbot/types/td_types → pytdbot-0.9.6/pytdbot/types}/bound_methods/__init__.py +4 -4
  15. {pytdbot-0.9.5.dev0/pytdbot/types/td_types → pytdbot-0.9.6/pytdbot/types}/bound_methods/callback_query.py +128 -110
  16. {pytdbot-0.9.5.dev0/pytdbot/types/td_types → pytdbot-0.9.6/pytdbot/types}/bound_methods/file.py +36 -36
  17. {pytdbot-0.9.5.dev0/pytdbot/types/td_types → pytdbot-0.9.6/pytdbot/types}/bound_methods/message.py +881 -881
  18. pytdbot-0.9.5.dev0/pytdbot/types/td_types/types.py → pytdbot-0.9.6/pytdbot/types/td_types.py +6113 -124
  19. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/types/tdserver/schedule.py +19 -11
  20. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/__init__.py +13 -1
  21. pytdbot-0.9.6/pytdbot/utils/json_utils.py +83 -0
  22. pytdbot-0.9.5.dev0/pytdbot/types/td_types/__init__.py +0 -1
  23. pytdbot-0.9.5.dev0/pytdbot/utils/json_utils.py +0 -27
  24. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/LICENSE +0 -0
  25. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/MANIFEST.in +0 -0
  26. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/Pytdbot.egg-info/dependency_links.txt +0 -0
  27. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/Pytdbot.egg-info/requires.txt +0 -0
  28. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/Pytdbot.egg-info/top_level.txt +0 -0
  29. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/client_manager.py +0 -0
  30. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/exception/__init__.py +0 -0
  31. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/filters.py +0 -0
  32. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/handlers/__init__.py +0 -0
  33. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/methods/__init__.py +0 -0
  34. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/tdjson/__init__.py +0 -0
  35. {pytdbot-0.9.5.dev0/pytdbot/types/td_types → pytdbot-0.9.6/pytdbot/types}/bound_methods/chatActions.py +0 -0
  36. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/types/plugins/__init__.py +0 -0
  37. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/types/tdserver/__init__.py +0 -0
  38. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/types/tdserver/stats.py +0 -0
  39. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/asyncio_utils.py +0 -0
  40. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/escape.py +0 -0
  41. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/obj_encoder.py +0 -0
  42. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/strings.py +0 -0
  43. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/text_format.py +0 -0
  44. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/pytdbot/utils/webapps.py +0 -0
  45. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/requirements.txt +0 -0
  46. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/setup.cfg +0 -0
  47. {pytdbot-0.9.5.dev0 → pytdbot-0.9.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Pytdbot
3
- Version: 0.9.5.dev0
3
+ Version: 0.9.6
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 [![Version](https://img.shields.io/pypi/v/Pytdbot?style=flat&logo=pypi)](https://pypi.org/project/Pytdbot) [![TDLib version](https://img.shields.io/badge/TDLib-v1.8.50-blue?logo=telegram)](https://github.com/tdlib/td) [![Downloads](https://static.pepy.tech/personalized-badge/pytdbot?period=month&units=none&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/pytdbot) [![Telegram Chat](https://img.shields.io/badge/Pytdbot%20chat-blue?logo=telegram&label=Telegram)](https://t.me/pytdbotchat)
33
+ # Pytdbot [![Version](https://img.shields.io/pypi/v/Pytdbot?style=flat&logo=pypi)](https://pypi.org/project/Pytdbot) [![TDLib version](https://img.shields.io/badge/TDLib-v1.8.52-blue?logo=telegram)](https://github.com/tdlib/td) [![Downloads](https://static.pepy.tech/personalized-badge/pytdbot?period=month&units=none&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/pytdbot) [![Telegram Chat](https://img.shields.io/badge/Pytdbot%20chat-blue?logo=telegram&label=Telegram)](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.5.dev0
3
+ Version: 0.9.6
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 [![Version](https://img.shields.io/pypi/v/Pytdbot?style=flat&logo=pypi)](https://pypi.org/project/Pytdbot) [![TDLib version](https://img.shields.io/badge/TDLib-v1.8.50-blue?logo=telegram)](https://github.com/tdlib/td) [![Downloads](https://static.pepy.tech/personalized-badge/pytdbot?period=month&units=none&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/pytdbot) [![Telegram Chat](https://img.shields.io/badge/Pytdbot%20chat-blue?logo=telegram&label=Telegram)](https://t.me/pytdbotchat)
33
+ # Pytdbot [![Version](https://img.shields.io/pypi/v/Pytdbot?style=flat&logo=pypi)](https://pypi.org/project/Pytdbot) [![TDLib version](https://img.shields.io/badge/TDLib-v1.8.52-blue?logo=telegram)](https://github.com/tdlib/td) [![Downloads](https://static.pepy.tech/personalized-badge/pytdbot?period=month&units=none&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/pytdbot) [![Telegram Chat](https://img.shields.io/badge/Pytdbot%20chat-blue?logo=telegram&label=Telegram)](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
 
@@ -23,14 +23,13 @@ pytdbot/methods/td_functions.py
23
23
  pytdbot/tdjson/__init__.py
24
24
  pytdbot/tdjson/tdjson.py
25
25
  pytdbot/types/__init__.py
26
+ pytdbot/types/td_types.py
27
+ pytdbot/types/bound_methods/__init__.py
28
+ pytdbot/types/bound_methods/callback_query.py
29
+ pytdbot/types/bound_methods/chatActions.py
30
+ pytdbot/types/bound_methods/file.py
31
+ pytdbot/types/bound_methods/message.py
26
32
  pytdbot/types/plugins/__init__.py
27
- pytdbot/types/td_types/__init__.py
28
- pytdbot/types/td_types/types.py
29
- pytdbot/types/td_types/bound_methods/__init__.py
30
- pytdbot/types/td_types/bound_methods/callback_query.py
31
- pytdbot/types/td_types/bound_methods/chatActions.py
32
- pytdbot/types/td_types/bound_methods/file.py
33
- pytdbot/types/td_types/bound_methods/message.py
34
33
  pytdbot/types/tdserver/__init__.py
35
34
  pytdbot/types/tdserver/schedule.py
36
35
  pytdbot/types/tdserver/stats.py
@@ -1,4 +1,4 @@
1
- # Pytdbot [![Version](https://img.shields.io/pypi/v/Pytdbot?style=flat&logo=pypi)](https://pypi.org/project/Pytdbot) [![TDLib version](https://img.shields.io/badge/TDLib-v1.8.50-blue?logo=telegram)](https://github.com/tdlib/td) [![Downloads](https://static.pepy.tech/personalized-badge/pytdbot?period=month&units=none&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/pytdbot) [![Telegram Chat](https://img.shields.io/badge/Pytdbot%20chat-blue?logo=telegram&label=Telegram)](https://t.me/pytdbotchat)
1
+ # Pytdbot [![Version](https://img.shields.io/pypi/v/Pytdbot?style=flat&logo=pypi)](https://pypi.org/project/Pytdbot) [![TDLib version](https://img.shields.io/badge/TDLib-v1.8.52-blue?logo=telegram)](https://github.com/tdlib/td) [![Downloads](https://static.pepy.tech/personalized-badge/pytdbot?period=month&units=none&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/pytdbot) [![Telegram Chat](https://img.shields.io/badge/Pytdbot%20chat-blue?logo=telegram&label=Telegram)](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
 
@@ -13,7 +13,7 @@ __all__ = [
13
13
  "Client",
14
14
  ]
15
15
 
16
- __version__ = "0.9.5.dev0"
16
+ __version__ = "0.9.6"
17
17
  __copyright__ = "Copyright (c) 2022-2025 Pytdbot, AYMENJD"
18
18
  __license__ = "MIT License"
19
19
 
@@ -7,7 +7,7 @@ from os.path import join as join_path
7
7
  from pathlib import Path
8
8
  from platform import python_implementation, python_version
9
9
  from threading import current_thread, main_thread
10
- from typing import Callable, Dict, Union
10
+ from typing import Callable, Dict, Type, Union
11
11
 
12
12
  import aio_pika
13
13
  from deepdiff import DeepDiff
@@ -94,6 +94,9 @@ class Client(Decorators, Methods):
94
94
  workers (``int``, *optional*):
95
95
  Number of workers to handle updates. Default is ``5``. If set to ``None``, updates will be immediately handled instead of being queued, which can impact performance.
96
96
 
97
+ default_handlers_timeout (``float``, *optional*):
98
+ Default timeout for handlers. If set, each handler will be awaited with this timeout (ignored if ``timeout`` is set when registering handler). Default is ``None`` (no timeout)
99
+
97
100
  no_updates (``bool``, *optional*):
98
101
  Whether the client should handle updates or not. Applicable only when using [TDLib Server](https://github.com/pytdbot/tdlib-server). Default is ``False``
99
102
 
@@ -128,10 +131,14 @@ class Client(Decorators, Methods):
128
131
  loop: asyncio.AbstractEventLoop = None,
129
132
  options: dict = None,
130
133
  workers: int = 5,
134
+ queue_size: int = 1000,
135
+ default_handlers_timeout: float = None,
131
136
  no_updates: bool = False,
137
+ load_messages_before_reply: bool = False,
132
138
  td_verbosity: int = 2,
133
139
  td_log: LogStream = None,
134
140
  user_bot: bool = False,
141
+ server_ack: bool = True,
135
142
  ) -> None:
136
143
  self.__api_id = api_id
137
144
  self.__api_hash = api_hash
@@ -158,9 +165,13 @@ class Client(Decorators, Methods):
158
165
  self.use_message_database = use_message_database
159
166
  self.td_options = options
160
167
  self.workers = workers
168
+ self.queue_size = queue_size
169
+ self.default_handlers_timeout = default_handlers_timeout
161
170
  self.no_updates = no_updates
171
+ self.load_messages_before_reply = load_messages_before_reply
162
172
  self.queue = asyncio.Queue()
163
173
  self.user_bot = user_bot
174
+ self.server_ack = server_ack
164
175
  self.my_id = (
165
176
  get_bot_id_from_token(self.__token)
166
177
  if isinstance(self.__token, str)
@@ -179,6 +190,12 @@ class Client(Decorators, Methods):
179
190
  self.is_rabbitmq = True if rabbitmq_url else False
180
191
  self.options = {}
181
192
  self.allow_outgoing_message_types: tuple = (types.MessagePaymentRefunded,)
193
+ self.get_message_methods = {
194
+ "getmessage",
195
+ "getmessagelocally",
196
+ "getrepliedmessage",
197
+ "getcallbackquerymessage",
198
+ } # TODO: improve this
182
199
 
183
200
  self._check_init_args()
184
201
 
@@ -240,13 +257,16 @@ class Client(Decorators, Methods):
240
257
  return await self.invoke({"@type": "getServerStats"})
241
258
 
242
259
  async def scheduleEvent(
243
- self, data: dict, send_at: int
260
+ self, name: str, payload: str, send_at: int
244
261
  ) -> Union["pytdbot.types.ScheduledEvent", "pytdbot.types.Error"]:
245
262
  """Schedule an event
246
263
 
247
264
  Parameters:
248
- data (:class:`dict`):
249
- The event data to be scheduled
265
+ name (:class:`str`):
266
+ Event name
267
+
268
+ payload (:class:`str`):
269
+ The event payload to be scheduled
250
270
 
251
271
  send_at (:class:`int`):
252
272
  Unix timestamp when the event should be sent
@@ -254,13 +274,20 @@ class Client(Decorators, Methods):
254
274
 
255
275
  self._check_rabbitmq()
256
276
 
257
- if not isinstance(data, dict):
258
- raise ValueError("data must be dict")
277
+ if not isinstance(name, str):
278
+ raise ValueError("name must be str")
279
+ if not isinstance(payload, str):
280
+ raise ValueError("payload must be str")
259
281
  if not isinstance(send_at, (int, float)):
260
282
  raise ValueError("send_at must be int")
261
283
 
262
284
  return await self.invoke(
263
- {"@type": "scheduleEvent", "data": data, "send_at": send_at}
285
+ {
286
+ "@type": "scheduleEvent",
287
+ "name": name,
288
+ "payload": payload,
289
+ "send_at": send_at,
290
+ }
264
291
  )
265
292
 
266
293
  async def cancelScheduledEvent(
@@ -326,17 +353,18 @@ class Client(Decorators, Methods):
326
353
 
327
354
  def add_handler(
328
355
  self,
329
- update_type: str,
356
+ update_type: Union[Type["pytdbot.types.Update"], str],
330
357
  func: Callable,
331
358
  filters: pytdbot.filters.Filter = None,
332
359
  position: int = None,
333
360
  inner_object: bool = False,
361
+ timeout: float = None,
334
362
  is_from_plugin: bool = False,
335
363
  ) -> None:
336
364
  r"""Add an update handler
337
365
 
338
366
  Parameters:
339
- update_type (``str``):
367
+ update_type (``str`` || :class:`~pytdbot.types.Update`):
340
368
  An update type
341
369
 
342
370
  func (``Callable``):
@@ -351,6 +379,9 @@ class Client(Decorators, Methods):
351
379
  inner_object (``bool``, *optional*):
352
380
  Wether to pass an inner object of update or not; for example ``UpdateNewMessage.message``. Default is ``False``
353
381
 
382
+ timeout (``float``, *optional*):
383
+ Max execution time for the handler before it timeout. If ``None``, ``Client.default_handlers_timeout`` is preferred. Default is ``None``
384
+
354
385
  is_from_plugin (``bool``, *optional*):
355
386
  Wether this handler is from a loaded plugin (this can help reloading plugin during runtime; for development only). Default is ``False``
356
387
 
@@ -359,23 +390,34 @@ class Client(Decorators, Methods):
359
390
  """
360
391
 
361
392
  if not isinstance(update_type, str):
362
- raise TypeError("update_type must be str")
363
- elif not isinstance(func, Callable):
393
+ if issubclass(update_type, types.Update):
394
+ update_type = update_type.getType()
395
+ else:
396
+ raise TypeError(
397
+ "update_type must be str or subclass of pytdbot.types.Update"
398
+ )
399
+ if not isinstance(func, Callable):
364
400
  raise TypeError("func must be callable")
365
- elif filters is not None and not isinstance(filters, Filter):
401
+ if filters is not None and not isinstance(filters, Filter):
366
402
  raise TypeError("filters must be instance of pytdbot.filters.Filter")
367
- else:
368
- handler = Handler(
369
- func, update_type, filters, position, inner_object, is_from_plugin
370
- )
371
403
 
372
- if update_type not in self._handlers:
373
- self._handlers[update_type] = []
404
+ handler = Handler(
405
+ func=func,
406
+ update_type=update_type,
407
+ filter=filters,
408
+ position=position,
409
+ inner_object=inner_object,
410
+ timeout=timeout,
411
+ is_from_plugin=is_from_plugin,
412
+ )
374
413
 
375
- if isinstance(position, int):
376
- self._handlers[update_type].insert(position, handler)
377
- else:
378
- self._handlers[update_type].append(handler)
414
+ if update_type not in self._handlers:
415
+ self._handlers[update_type] = []
416
+
417
+ if isinstance(position, int):
418
+ self._handlers[update_type].insert(position, handler)
419
+ else:
420
+ self._handlers[update_type].append(handler)
379
421
 
380
422
  self._update_handlers()
381
423
 
@@ -451,56 +493,78 @@ class Client(Decorators, Methods):
451
493
  """
452
494
 
453
495
  request = obj_to_dict(request)
454
-
455
496
  request["@extra"] = {"id": create_extra_id()}
456
-
457
- future = self._create_request_future(request)
497
+ request_method = request["@type"].lower()
458
498
 
459
499
  if (
460
500
  self.logger.root.level >= DEBUG or self.logger.level >= DEBUG
461
501
  ): # dumping all requests may create performance issues
462
502
  self.logger.debug(f"Sending: {dumps(request, indent=4)}")
463
503
 
464
- is_chat_attempted_load = request["@type"].lower() == "getchat"
504
+ is_chat_attempted_load = request_method == "getchat"
505
+ is_message_attempted_load = request_method in self.get_message_methods
465
506
 
466
507
  while True:
467
508
  future = self._create_request_future(request)
468
509
  await self.__send(request)
469
510
  result = await future
470
511
 
471
- if isinstance(result, types.Error):
472
- if result.code == 400:
473
- if result.message.startswith(
474
- "Failed to parse JSON object as TDLib request:"
475
- ):
476
- raise ValueError(result.message)
512
+ if not isinstance(result, types.Error):
513
+ break
514
+
515
+ error_code = result.code
516
+ error_message = result.message
477
517
 
478
- if not is_chat_attempted_load and (
479
- result.message == "Chat not found" and "chat_id" in request
480
- ):
481
- is_chat_attempted_load = True
518
+ if error_message.startswith(
519
+ "Failed to parse JSON object as TDLib request:"
520
+ ):
521
+ raise ValueError(error_message)
522
+
523
+ if error_code != 400:
524
+ break
525
+
526
+ chat_id = request.get("chat_id")
527
+ message_id = request.get("message_id")
528
+
529
+ if not is_message_attempted_load and (
530
+ error_message == "Message not found" and (chat_id and message_id)
531
+ ):
532
+ is_message_attempted_load = True
482
533
 
483
- chat_id = request["chat_id"]
534
+ self.logger.debug(f"Attempt to load message {message_id} in {chat_id}")
484
535
 
485
- self.logger.debug(f"Attempt to load chat {chat_id}")
536
+ message = await self.getMessage(chat_id=chat_id, message_id=message_id)
537
+ if message:
538
+ self.logger.debug(f"Message {message_id} in {chat_id} is loaded")
539
+ continue
540
+ else:
541
+ self.logger.debug(
542
+ f"Failed to load message {message_id} in {chat_id}"
543
+ )
486
544
 
487
- load_chat = await self.getChat(chat_id)
545
+ if not is_chat_attempted_load and (
546
+ error_message == "Chat not found" and chat_id
547
+ ):
548
+ is_chat_attempted_load = True
488
549
 
489
- if not isinstance(load_chat, types.Error):
490
- self.logger.debug(f"Chat {chat_id} is loaded")
550
+ self.logger.debug(f"Attempt to load chat {chat_id}")
491
551
 
492
- reply_to_message_id = (request.get("reply_to") or {}).get(
493
- "message_id", 0
494
- )
552
+ chat = await self.getChat(chat_id)
553
+ if not isinstance(chat, types.Error):
554
+ self.logger.debug(f"Chat {chat_id} is loaded")
495
555
 
496
- # if the request is a reply to another message
497
- # load the replied message to avoid "Message not found"
498
- if reply_to_message_id > 0:
499
- await self.getMessage(chat_id, reply_to_message_id)
556
+ reply_to_message_id = (request.get("reply_to") or {}).get(
557
+ "message_id", 0
558
+ )
500
559
 
501
- continue
560
+ # if the request is a reply to another message
561
+ # load the replied message to avoid "Message not found"
562
+ if reply_to_message_id > 0:
563
+ await self.getMessage(chat_id, reply_to_message_id)
502
564
 
503
- self.logger.error(f"Couldn't load chat {chat_id}")
565
+ continue
566
+ else:
567
+ self.logger.error(f"Couldn't load chat {chat_id}")
504
568
 
505
569
  break
506
570
 
@@ -701,12 +765,13 @@ class Client(Decorators, Methods):
701
765
  for handler in handlers_to_load:
702
766
  if asyncio.iscoroutinefunction(handler.func):
703
767
  self.add_handler(
704
- handler.update_type,
705
- handler.func,
706
- handler.filter,
707
- handler.position,
708
- handler.inner_object,
709
- True,
768
+ update_type=handler.update_type,
769
+ func=handler.func,
770
+ filters=handler.filter,
771
+ position=handler.position,
772
+ inner_object=handler.inner_object,
773
+ timeout=handler.timeout,
774
+ is_from_plugin=True,
710
775
  )
711
776
  handlers += 1
712
777
  plugin_handlers_count += 1
@@ -785,7 +850,22 @@ class Client(Decorators, Methods):
785
850
  elif not filter_function(self, handler_value):
786
851
  continue
787
852
 
788
- await initializer(self, handler_value)
853
+ if (
854
+ self.default_handlers_timeout is None
855
+ and initializer.timeout is None
856
+ ):
857
+ await initializer(self, handler_value)
858
+ else:
859
+ timeout = initializer.timeout or self.default_handlers_timeout
860
+ try:
861
+ await asyncio.wait_for(
862
+ initializer(self, handler_value),
863
+ timeout=timeout,
864
+ )
865
+ except asyncio.TimeoutError:
866
+ self.logger.warning(
867
+ f"Initializer {initializer} timed out after {timeout} seconds"
868
+ )
789
869
  except StopHandlers as e:
790
870
  raise e
791
871
  except Exception:
@@ -806,7 +886,18 @@ class Client(Decorators, Methods):
806
886
  elif not filter_function(self, handler_value):
807
887
  continue
808
888
 
809
- await handler(self, handler_value)
889
+ if self.default_handlers_timeout is None and handler.timeout is None:
890
+ await handler(self, handler_value)
891
+ else:
892
+ timeout = handler.timeout or self.default_handlers_timeout
893
+ try:
894
+ await asyncio.wait_for(
895
+ handler(self, handler_value), timeout=timeout
896
+ )
897
+ except asyncio.TimeoutError:
898
+ self.logger.warning(
899
+ f"Handler {handler} timed out after {timeout} seconds"
900
+ )
810
901
  except StopHandlers as e:
811
902
  raise e
812
903
  except Exception:
@@ -828,7 +919,19 @@ class Client(Decorators, Methods):
828
919
  elif not filter_function(self, handler_value):
829
920
  continue
830
921
 
831
- await finalizer(self, handler_value)
922
+ if self.default_handlers_timeout is None and finalizer.timeout is None:
923
+ await finalizer(self, handler_value)
924
+ else:
925
+ try:
926
+ timeout = finalizer.timeout or self.default_handlers_timeout
927
+ await asyncio.wait_for(
928
+ finalizer(self, handler_value),
929
+ timeout=timeout,
930
+ )
931
+ except asyncio.TimeoutError:
932
+ self.logger.warning(
933
+ f"Finalizer {finalizer} timed out after {timeout} seconds"
934
+ )
832
935
  except StopHandlers as e:
833
936
  raise e
834
937
  except Exception:
@@ -869,6 +972,9 @@ class Client(Decorators, Methods):
869
972
  `AuthorizationError`
870
973
  """
871
974
 
975
+ if self.is_rabbitmq:
976
+ return
977
+
872
978
  if isinstance(self.__database_encryption_key, str):
873
979
  self.__database_encryption_key = self.__database_encryption_key.encode(
874
980
  "utf-8"
@@ -1060,13 +1166,22 @@ class Client(Decorators, Methods):
1060
1166
  await self.__rqueues["notify"].consume(self.__on_update, no_ack=True)
1061
1167
 
1062
1168
  async def __rabbitmq_iterator(self):
1063
- async with self.__rqueues["updates"].iterator() as iterator:
1169
+ async with self.__rqueues["updates"].iterator(
1170
+ no_ack=not self.server_ack
1171
+ ) as iterator:
1064
1172
  async for message in iterator:
1065
- await self.queue.put(message)
1173
+ if self.queue.qsize() > self.queue_size:
1174
+ await message.nack(requeue=True)
1175
+ continue
1176
+
1177
+ self.queue.put_nowait(message)
1066
1178
 
1067
1179
  async def __rabbitmq_worker(self):
1068
1180
  while self.is_running:
1069
- message: aio_pika.IncomingMessage = await self.queue.get()
1181
+ try:
1182
+ message: aio_pika.IncomingMessage = self.queue.get_nowait()
1183
+ except asyncio.QueueEmpty:
1184
+ message: aio_pika.IncomingMessage = await self.queue.get()
1070
1185
 
1071
1186
  try:
1072
1187
  update = json_loads(message.body)
@@ -1103,7 +1218,8 @@ class Client(Decorators, Methods):
1103
1218
 
1104
1219
  async def __handle_authorization_state_wait_phone_number(self):
1105
1220
  if (
1106
- self.authorization_state != "authorizationStateWaitPhoneNumber"
1221
+ self.is_rabbitmq
1222
+ or self.authorization_state != "authorizationStateWaitPhoneNumber"
1107
1223
  or not self.__token
1108
1224
  ):
1109
1225
  return