Pytdbot 0.9.3__tar.gz → 0.9.4__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 (43) hide show
  1. {pytdbot-0.9.3 → pytdbot-0.9.4}/PKG-INFO +2 -2
  2. {pytdbot-0.9.3 → pytdbot-0.9.4}/Pytdbot.egg-info/PKG-INFO +2 -2
  3. {pytdbot-0.9.3 → pytdbot-0.9.4}/README.md +1 -1
  4. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/__init__.py +1 -1
  5. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/client.py +61 -14
  6. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/handlers/handler.py +3 -1
  7. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/handlers/td_updates.py +76 -0
  8. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/methods/td_functions.py +378 -45
  9. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/__init__.py +19 -1
  10. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/types.py +583 -42
  11. {pytdbot-0.9.3 → pytdbot-0.9.4}/LICENSE +0 -0
  12. {pytdbot-0.9.3 → pytdbot-0.9.4}/MANIFEST.in +0 -0
  13. {pytdbot-0.9.3 → pytdbot-0.9.4}/Pytdbot.egg-info/SOURCES.txt +0 -0
  14. {pytdbot-0.9.3 → pytdbot-0.9.4}/Pytdbot.egg-info/dependency_links.txt +0 -0
  15. {pytdbot-0.9.3 → pytdbot-0.9.4}/Pytdbot.egg-info/requires.txt +0 -0
  16. {pytdbot-0.9.3 → pytdbot-0.9.4}/Pytdbot.egg-info/top_level.txt +0 -0
  17. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/client_manager.py +0 -0
  18. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/exception/__init__.py +0 -0
  19. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/filters.py +0 -0
  20. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/handlers/__init__.py +0 -0
  21. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/handlers/decorators.py +0 -0
  22. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/methods/__init__.py +0 -0
  23. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/methods/methods.py +0 -0
  24. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/tdjson/__init__.py +0 -0
  25. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/tdjson/tdjson.py +0 -0
  26. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/plugins/__init__.py +0 -0
  27. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/__init__.py +0 -0
  28. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/bound_methods/__init__.py +0 -0
  29. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/bound_methods/callback_query.py +0 -0
  30. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/bound_methods/chatActions.py +0 -0
  31. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/bound_methods/file.py +0 -0
  32. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/types/td_types/bound_methods/message.py +0 -0
  33. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/__init__.py +0 -0
  34. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/asyncio_utils.py +0 -0
  35. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/escape.py +0 -0
  36. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/json_utils.py +0 -0
  37. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/obj_encoder.py +0 -0
  38. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/strings.py +0 -0
  39. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/text_format.py +0 -0
  40. {pytdbot-0.9.3 → pytdbot-0.9.4}/pytdbot/utils/webapps.py +0 -0
  41. {pytdbot-0.9.3 → pytdbot-0.9.4}/requirements.txt +0 -0
  42. {pytdbot-0.9.3 → pytdbot-0.9.4}/setup.cfg +0 -0
  43. {pytdbot-0.9.3 → pytdbot-0.9.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Pytdbot
3
- Version: 0.9.3
3
+ Version: 0.9.4
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.49-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.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)
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
3
+ Version: 0.9.4
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.49-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.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)
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,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.49-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.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)
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.3"
16
+ __version__ = "0.9.4"
17
17
  __copyright__ = "Copyright (c) 2022-2025 Pytdbot, AYMENJD"
18
18
  __license__ = "MIT License"
19
19
 
@@ -1,6 +1,6 @@
1
1
  import asyncio
2
2
  import signal
3
- from importlib import import_module
3
+ from importlib import import_module, reload as reload_module
4
4
  from json import dumps
5
5
  from logging import DEBUG, getLogger
6
6
  from os.path import join as join_path
@@ -172,12 +172,15 @@ class Client(Decorators, Methods):
172
172
  self.is_running = None
173
173
  self.me: types.User = None
174
174
  self.is_authenticated = False
175
+ self.is_reloading_plugins = False
175
176
  self.is_rabbitmq = True if rabbitmq_url else False
176
177
  self.options = {}
178
+ self.allow_outgoing_message_types: tuple = (types.MessagePaymentRefunded,)
177
179
 
178
180
  self._check_init_args()
179
181
 
180
182
  self._handlers = {"initializer": [], "finalizer": []}
183
+ self._current_handlers = {}
181
184
  self._results: Dict[str, asyncio.Future] = {}
182
185
  self._workers_tasks = None
183
186
  self.__authorization_state = None
@@ -268,6 +271,7 @@ class Client(Decorators, Methods):
268
271
  filters: pytdbot.filters.Filter = None,
269
272
  position: int = None,
270
273
  inner_object: bool = False,
274
+ is_from_plugin: bool = False,
271
275
  ) -> None:
272
276
  r"""Add an update handler
273
277
 
@@ -287,6 +291,9 @@ class Client(Decorators, Methods):
287
291
  inner_object (``bool``, *optional*):
288
292
  Wether to pass an inner object of update or not; for example ``UpdateNewMessage.message``. Default is ``False``
289
293
 
294
+ is_from_plugin (``bool``, *optional*):
295
+ Wether this handler is from a loaded plugin (this can help reloading plugin during runtime; for development only). Default is ``False``
296
+
290
297
  Raises:
291
298
  TypeError
292
299
  """
@@ -298,14 +305,38 @@ class Client(Decorators, Methods):
298
305
  elif filters is not None and not isinstance(filters, Filter):
299
306
  raise TypeError("filters must be instance of pytdbot.filters.Filter")
300
307
  else:
301
- func = Handler(func, update_type, filters, position, inner_object)
308
+ handler = Handler(
309
+ func, update_type, filters, position, inner_object, is_from_plugin
310
+ )
311
+
302
312
  if update_type not in self._handlers:
303
313
  self._handlers[update_type] = []
314
+
304
315
  if isinstance(position, int):
305
- self._handlers[update_type].insert(position, func)
316
+ self._handlers[update_type].insert(position, handler)
306
317
  else:
307
- self._handlers[update_type].append(func)
308
- self._handlers[update_type].sort(key=lambda x: (x.position is None, x.position))
318
+ self._handlers[update_type].append(handler)
319
+
320
+ self._update_handlers()
321
+
322
+ def reload_plugins(self):
323
+ """Reload all plugins, non-plugin handlers are not ``reloaded``
324
+ .. note::
325
+ This is for ``development purposes only`` and should not be used
326
+ in production environments
327
+ """
328
+
329
+ if self.is_reloading_plugins:
330
+ return
331
+
332
+ self.is_reloading_plugins = True
333
+ for handlers in self._handlers.values():
334
+ for handler in handlers.copy():
335
+ if handler.is_from_plugin:
336
+ self.remove_handler(handler.func)
337
+
338
+ self._load_plugins(reload_plugins=True)
339
+ self.is_reloading_plugins = False
309
340
 
310
341
  def remove_handler(self, func: Callable) -> bool:
311
342
  r"""Remove an update handler
@@ -323,13 +354,17 @@ class Client(Decorators, Methods):
323
354
 
324
355
  if not isinstance(func, Callable):
325
356
  raise TypeError("func must be callable")
326
- for _, handlers in self._handlers.items():
357
+
358
+ removed = False
359
+ for handlers in self._handlers.values():
327
360
  for handler in handlers.copy():
328
361
  if handler.func == func:
329
362
  handlers.remove(handler)
330
- handlers.sort(key=lambda x: (x.position is None, x.position))
331
- return True
332
- return False
363
+ removed = True
364
+
365
+ if removed:
366
+ self._update_handlers()
367
+ return removed
333
368
 
334
369
  async def invoke(
335
370
  self,
@@ -554,7 +589,13 @@ class Client(Decorators, Methods):
554
589
  if isinstance(self.workers, int) and self.workers < 1:
555
590
  raise ValueError("workers must be greater than 0")
556
591
 
557
- def _load_plugins(self):
592
+ def _update_handlers(self):
593
+ self._current_handlers = {
594
+ k: tuple(sorted(v, key=lambda x: (x.position is None, x.position)))
595
+ for k, v in self._handlers.items()
596
+ }
597
+
598
+ def _load_plugins(self, reload_plugins: bool = False):
558
599
  count = 0
559
600
  handlers = 0
560
601
  plugin_paths = sorted(Path(self.plugins.folder).rglob("*.py"))
@@ -578,6 +619,8 @@ class Client(Decorators, Methods):
578
619
 
579
620
  try:
580
621
  module = import_module(module_path)
622
+ if reload_plugins:
623
+ reload_module(module)
581
624
  except Exception:
582
625
  self.logger.exception(f"Failed to import plugin {module_path}")
583
626
  continue
@@ -600,6 +643,7 @@ class Client(Decorators, Methods):
600
643
  handler.filter,
601
644
  handler.position,
602
645
  handler.inner_object,
646
+ True,
603
647
  )
604
648
  handlers += 1
605
649
  plugin_handlers_count += 1
@@ -665,7 +709,7 @@ class Client(Decorators, Methods):
665
709
  async def __run_initializers(self, update):
666
710
  inner_object = self.get_inner_object(update)
667
711
 
668
- for initializer in self._handlers["initializer"]:
712
+ for initializer in self._current_handlers["initializer"]:
669
713
  try:
670
714
  handler_value = inner_object if initializer.inner_object else update
671
715
 
@@ -687,7 +731,7 @@ class Client(Decorators, Methods):
687
731
  async def __run_handlers(self, update):
688
732
  inner_object = self.get_inner_object(update)
689
733
 
690
- for handler in self._handlers[update.getType()]:
734
+ for handler in self._current_handlers[update.getType()]:
691
735
  try:
692
736
  handler_value = inner_object if handler.inner_object else update
693
737
 
@@ -708,7 +752,7 @@ class Client(Decorators, Methods):
708
752
  async def __run_finalizers(self, update):
709
753
  inner_object = self.get_inner_object(update)
710
754
 
711
- for finalizer in self._handlers["finalizer"]:
755
+ for finalizer in self._current_handlers["finalizer"]:
712
756
  try:
713
757
  handler_value = inner_object if finalizer.inner_object else update
714
758
 
@@ -728,10 +772,13 @@ class Client(Decorators, Methods):
728
772
  self.logger.exception(f"Finalizer {finalizer} failed")
729
773
 
730
774
  async def _handle_update(self, update):
731
- if update.getType() in self._handlers:
775
+ if update.getType() in self._current_handlers:
732
776
  if (
733
777
  not self.user_bot
734
778
  and isinstance(update, types.UpdateNewMessage)
779
+ and not isinstance(
780
+ update.message.content, self.allow_outgoing_message_types
781
+ )
735
782
  and update.message.is_outgoing
736
783
  ):
737
784
  return
@@ -15,18 +15,20 @@ class Handler:
15
15
  filter: Filter = None,
16
16
  position: int = None,
17
17
  inner_object: bool = False,
18
+ is_from_plugin: bool = False,
18
19
  ) -> None:
19
20
  self.func = func
20
21
  self.update_type = update_type
21
22
  self.filter = filter
22
23
  self.position = position
23
24
  self.inner_object = inner_object
25
+ self.is_from_plugin = is_from_plugin
24
26
 
25
27
  def __call__(self, client: "pytdbot.Client", update: "pytdbot.types.Update"):
26
28
  return self.func(client, update)
27
29
 
28
30
  def __str__(self) -> str:
29
- return f"Handler(func={self.func}, update_type={self.update_type}, filter={self.filter}, position={self.position}, inner_object={self.inner_object})"
31
+ return f"Handler(func={self.func}, update_type={self.update_type}, filter={self.filter}, position={self.position}, inner_object={self.inner_object}, is_from_plugin={self.is_from_plugin})"
30
32
 
31
33
  def __repr__(self) -> str:
32
34
  return str(self)
@@ -1947,6 +1947,82 @@ class Updates:
1947
1947
 
1948
1948
  return decorator
1949
1949
 
1950
+ def on_updateDirectMessagesChatTopic(
1951
+ self: "pytdbot.Client" = None,
1952
+ filters: "pytdbot.filters.Filter" = None,
1953
+ position: int = None,
1954
+ ) -> Callable:
1955
+ r"""Basic information about a topic in a channel direct messages chat administered by the current user has changed\. This update is guaranteed to come before the topic identifier is returned to the application
1956
+
1957
+ Parameters:
1958
+ filters (:class:`pytdbot.filters.Filter`, *optional*):
1959
+ An update filter
1960
+
1961
+ position (``int``, *optional*):
1962
+ The function position in handlers list. Default is ``None`` (append)
1963
+
1964
+ Raises:
1965
+ :py:class:`TypeError`
1966
+ """
1967
+
1968
+ def decorator(func: Callable) -> Callable:
1969
+ if hasattr(func, "_handler"):
1970
+ return func
1971
+ elif isinstance(self, pytdbot.Client):
1972
+ if iscoroutinefunction(func):
1973
+ self.add_handler(
1974
+ "updateDirectMessagesChatTopic", func, filters, position
1975
+ )
1976
+ else:
1977
+ raise TypeError("Handler must be async")
1978
+ elif isinstance(self, pytdbot.filters.Filter):
1979
+ func._handler = Handler(
1980
+ func, "updateDirectMessagesChatTopic", self, position
1981
+ )
1982
+ else:
1983
+ func._handler = Handler(
1984
+ func, "updateDirectMessagesChatTopic", filters, position
1985
+ )
1986
+ return func
1987
+
1988
+ return decorator
1989
+
1990
+ def on_updateTopicMessageCount(
1991
+ self: "pytdbot.Client" = None,
1992
+ filters: "pytdbot.filters.Filter" = None,
1993
+ position: int = None,
1994
+ ) -> Callable:
1995
+ r"""Number of messages in a topic has changed; for Saved Messages and channel direct messages chat topics only
1996
+
1997
+ Parameters:
1998
+ filters (:class:`pytdbot.filters.Filter`, *optional*):
1999
+ An update filter
2000
+
2001
+ position (``int``, *optional*):
2002
+ The function position in handlers list. Default is ``None`` (append)
2003
+
2004
+ Raises:
2005
+ :py:class:`TypeError`
2006
+ """
2007
+
2008
+ def decorator(func: Callable) -> Callable:
2009
+ if hasattr(func, "_handler"):
2010
+ return func
2011
+ elif isinstance(self, pytdbot.Client):
2012
+ if iscoroutinefunction(func):
2013
+ self.add_handler("updateTopicMessageCount", func, filters, position)
2014
+ else:
2015
+ raise TypeError("Handler must be async")
2016
+ elif isinstance(self, pytdbot.filters.Filter):
2017
+ func._handler = Handler(func, "updateTopicMessageCount", self, position)
2018
+ else:
2019
+ func._handler = Handler(
2020
+ func, "updateTopicMessageCount", filters, position
2021
+ )
2022
+ return func
2023
+
2024
+ return decorator
2025
+
1950
2026
  def on_updateQuickReplyShortcut(
1951
2027
  self: "pytdbot.Client" = None,
1952
2028
  filters: "pytdbot.filters.Filter" = None,