Pytdbot 0.9.5__tar.gz → 0.9.6.dev0__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 (46) hide show
  1. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/PKG-INFO +1 -1
  2. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/Pytdbot.egg-info/PKG-INFO +1 -1
  3. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/__init__.py +1 -1
  4. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/client.py +85 -50
  5. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/methods/methods.py +6 -1
  6. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/types.py +3585 -3
  7. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/tdserver/schedule.py +11 -11
  8. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/LICENSE +0 -0
  9. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/MANIFEST.in +0 -0
  10. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/Pytdbot.egg-info/SOURCES.txt +0 -0
  11. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/Pytdbot.egg-info/dependency_links.txt +0 -0
  12. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/Pytdbot.egg-info/requires.txt +0 -0
  13. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/Pytdbot.egg-info/top_level.txt +0 -0
  14. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/README.md +0 -0
  15. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/client_manager.py +0 -0
  16. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/exception/__init__.py +0 -0
  17. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/filters.py +0 -0
  18. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/handlers/__init__.py +0 -0
  19. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/handlers/decorators.py +0 -0
  20. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/handlers/handler.py +0 -0
  21. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/handlers/td_updates.py +0 -0
  22. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/methods/__init__.py +0 -0
  23. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/methods/td_functions.py +0 -0
  24. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/tdjson/__init__.py +0 -0
  25. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/tdjson/tdjson.py +0 -0
  26. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/__init__.py +0 -0
  27. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/plugins/__init__.py +0 -0
  28. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/__init__.py +0 -0
  29. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/bound_methods/__init__.py +0 -0
  30. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/bound_methods/callback_query.py +0 -0
  31. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/bound_methods/chatActions.py +0 -0
  32. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/bound_methods/file.py +0 -0
  33. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/td_types/bound_methods/message.py +0 -0
  34. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/tdserver/__init__.py +0 -0
  35. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/types/tdserver/stats.py +0 -0
  36. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/__init__.py +0 -0
  37. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/asyncio_utils.py +0 -0
  38. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/escape.py +0 -0
  39. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/json_utils.py +0 -0
  40. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/obj_encoder.py +0 -0
  41. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/strings.py +0 -0
  42. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/text_format.py +0 -0
  43. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/pytdbot/utils/webapps.py +0 -0
  44. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/requirements.txt +0 -0
  45. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/setup.cfg +0 -0
  46. {pytdbot-0.9.5 → pytdbot-0.9.6.dev0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Pytdbot
3
- Version: 0.9.5
3
+ Version: 0.9.6.dev0
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Pytdbot
3
- Version: 0.9.5
3
+ Version: 0.9.6.dev0
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
@@ -13,7 +13,7 @@ __all__ = [
13
13
  "Client",
14
14
  ]
15
15
 
16
- __version__ = "0.9.5"
16
+ __version__ = "0.9.6.dev0"
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
@@ -129,6 +129,7 @@ class Client(Decorators, Methods):
129
129
  options: dict = None,
130
130
  workers: int = 5,
131
131
  no_updates: bool = False,
132
+ load_messages_before_reply: bool = False,
132
133
  td_verbosity: int = 2,
133
134
  td_log: LogStream = None,
134
135
  user_bot: bool = False,
@@ -159,6 +160,7 @@ class Client(Decorators, Methods):
159
160
  self.td_options = options
160
161
  self.workers = workers
161
162
  self.no_updates = no_updates
163
+ self.load_messages_before_reply = load_messages_before_reply
162
164
  self.queue = asyncio.Queue()
163
165
  self.user_bot = user_bot
164
166
  self.my_id = (
@@ -179,6 +181,12 @@ class Client(Decorators, Methods):
179
181
  self.is_rabbitmq = True if rabbitmq_url else False
180
182
  self.options = {}
181
183
  self.allow_outgoing_message_types: tuple = (types.MessagePaymentRefunded,)
184
+ self.get_message_methods = {
185
+ "getmessage",
186
+ "getmessagelocally",
187
+ "getrepliedmessage",
188
+ "getcallbackquerymessage",
189
+ } # TODO: improve this
182
190
 
183
191
  self._check_init_args()
184
192
 
@@ -240,13 +248,13 @@ class Client(Decorators, Methods):
240
248
  return await self.invoke({"@type": "getServerStats"})
241
249
 
242
250
  async def scheduleEvent(
243
- self, data: dict, send_at: int
251
+ self, payload: str, send_at: int
244
252
  ) -> Union["pytdbot.types.ScheduledEvent", "pytdbot.types.Error"]:
245
253
  """Schedule an event
246
254
 
247
255
  Parameters:
248
- data (:class:`dict`):
249
- The event data to be scheduled
256
+ payload (:class:`str`):
257
+ The event payload to be scheduled
250
258
 
251
259
  send_at (:class:`int`):
252
260
  Unix timestamp when the event should be sent
@@ -254,13 +262,13 @@ class Client(Decorators, Methods):
254
262
 
255
263
  self._check_rabbitmq()
256
264
 
257
- if not isinstance(data, dict):
258
- raise ValueError("data must be dict")
265
+ if not isinstance(payload, str):
266
+ raise ValueError("payload must be str")
259
267
  if not isinstance(send_at, (int, float)):
260
268
  raise ValueError("send_at must be int")
261
269
 
262
270
  return await self.invoke(
263
- {"@type": "scheduleEvent", "data": data, "send_at": send_at}
271
+ {"@type": "scheduleEvent", "payload": payload, "send_at": send_at}
264
272
  )
265
273
 
266
274
  async def cancelScheduledEvent(
@@ -326,7 +334,7 @@ class Client(Decorators, Methods):
326
334
 
327
335
  def add_handler(
328
336
  self,
329
- update_type: str,
337
+ update_type: Union[Type["pytdbot.types.Update"], str],
330
338
  func: Callable,
331
339
  filters: pytdbot.filters.Filter = None,
332
340
  position: int = None,
@@ -336,7 +344,7 @@ class Client(Decorators, Methods):
336
344
  r"""Add an update handler
337
345
 
338
346
  Parameters:
339
- update_type (``str``):
347
+ update_type (``str`` || :class:`~pytdbot.types.Update`):
340
348
  An update type
341
349
 
342
350
  func (``Callable``):
@@ -359,23 +367,28 @@ class Client(Decorators, Methods):
359
367
  """
360
368
 
361
369
  if not isinstance(update_type, str):
362
- raise TypeError("update_type must be str")
363
- elif not isinstance(func, Callable):
370
+ if issubclass(update_type, types.Update):
371
+ update_type = update_type.getType()
372
+ else:
373
+ raise TypeError(
374
+ "update_type must be str or subclass of pytdbot.types.Update"
375
+ )
376
+ if not isinstance(func, Callable):
364
377
  raise TypeError("func must be callable")
365
- elif filters is not None and not isinstance(filters, Filter):
378
+ if filters is not None and not isinstance(filters, Filter):
366
379
  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
380
 
372
- if update_type not in self._handlers:
373
- self._handlers[update_type] = []
381
+ handler = Handler(
382
+ func, update_type, filters, position, inner_object, is_from_plugin
383
+ )
374
384
 
375
- if isinstance(position, int):
376
- self._handlers[update_type].insert(position, handler)
377
- else:
378
- self._handlers[update_type].append(handler)
385
+ if update_type not in self._handlers:
386
+ self._handlers[update_type] = []
387
+
388
+ if isinstance(position, int):
389
+ self._handlers[update_type].insert(position, handler)
390
+ else:
391
+ self._handlers[update_type].append(handler)
379
392
 
380
393
  self._update_handlers()
381
394
 
@@ -451,56 +464,78 @@ class Client(Decorators, Methods):
451
464
  """
452
465
 
453
466
  request = obj_to_dict(request)
454
-
455
467
  request["@extra"] = {"id": create_extra_id()}
456
-
457
- future = self._create_request_future(request)
468
+ request_method = request["@type"].lower()
458
469
 
459
470
  if (
460
471
  self.logger.root.level >= DEBUG or self.logger.level >= DEBUG
461
472
  ): # dumping all requests may create performance issues
462
473
  self.logger.debug(f"Sending: {dumps(request, indent=4)}")
463
474
 
464
- is_chat_attempted_load = request["@type"].lower() == "getchat"
475
+ is_chat_attempted_load = request_method == "getchat"
476
+ is_message_attempted_load = request_method in self.get_message_methods
465
477
 
466
478
  while True:
467
479
  future = self._create_request_future(request)
468
480
  await self.__send(request)
469
481
  result = await future
470
482
 
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)
483
+ if not isinstance(result, types.Error):
484
+ break
485
+
486
+ error_code = result.code
487
+ error_message = result.message
488
+
489
+ if error_message.startswith(
490
+ "Failed to parse JSON object as TDLib request:"
491
+ ):
492
+ raise ValueError(error_message)
477
493
 
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
494
+ if error_code != 400:
495
+ break
482
496
 
483
- chat_id = request["chat_id"]
497
+ chat_id = request.get("chat_id")
498
+ message_id = request.get("message_id")
484
499
 
485
- self.logger.debug(f"Attempt to load chat {chat_id}")
500
+ if not is_message_attempted_load and (
501
+ error_message == "Message not found" and (chat_id and message_id)
502
+ ):
503
+ is_message_attempted_load = True
486
504
 
487
- load_chat = await self.getChat(chat_id)
505
+ self.logger.debug(f"Attempt to load message {message_id} in {chat_id}")
488
506
 
489
- if not isinstance(load_chat, types.Error):
490
- self.logger.debug(f"Chat {chat_id} is loaded")
507
+ message = await self.getMessage(chat_id=chat_id, message_id=message_id)
508
+ if message:
509
+ self.logger.debug(f"Message {message_id} in {chat_id} is loaded")
510
+ continue
511
+ else:
512
+ self.logger.debug(
513
+ f"Failed to load message {message_id} in {chat_id}"
514
+ )
491
515
 
492
- reply_to_message_id = (request.get("reply_to") or {}).get(
493
- "message_id", 0
494
- )
516
+ if not is_chat_attempted_load and (
517
+ error_message == "Chat not found" and chat_id
518
+ ):
519
+ is_chat_attempted_load = True
495
520
 
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)
521
+ self.logger.debug(f"Attempt to load chat {chat_id}")
500
522
 
501
- continue
523
+ chat = await self.getChat(chat_id)
524
+ if not isinstance(chat, types.Error):
525
+ self.logger.debug(f"Chat {chat_id} is loaded")
526
+
527
+ reply_to_message_id = (request.get("reply_to") or {}).get(
528
+ "message_id", 0
529
+ )
502
530
 
503
- self.logger.error(f"Couldn't load chat {chat_id}")
531
+ # if the request is a reply to another message
532
+ # load the replied message to avoid "Message not found"
533
+ if reply_to_message_id > 0:
534
+ await self.getMessage(chat_id, reply_to_message_id)
535
+
536
+ continue
537
+ else:
538
+ self.logger.error(f"Couldn't load chat {chat_id}")
504
539
 
505
540
  break
506
541
 
@@ -1027,7 +1027,7 @@ class Methods(TDLibFunctions):
1027
1027
  return await self.sendMessageWithContent(
1028
1028
  chat_id=chat_id,
1029
1029
  content=InputMessageVoiceNote(
1030
- voice=voice,
1030
+ voice_note=voice,
1031
1031
  waveform=waveform,
1032
1032
  duration=duration,
1033
1033
  caption=caption,
@@ -1389,6 +1389,11 @@ class Methods(TDLibFunctions):
1389
1389
  message_id=reply_to_message_id, quote=quote
1390
1390
  )
1391
1391
 
1392
+ if self.load_messages_before_reply and isinstance(
1393
+ reply_to, InputMessageReplyToMessage
1394
+ ):
1395
+ await self.getMessage(chat_id=chat_id, message_id=reply_to.message_id)
1396
+
1392
1397
  res = await self.sendMessage(
1393
1398
  chat_id=chat_id,
1394
1399
  message_thread_id=message_thread_id,