Pytdbot 0.10.0.dev0__py3-none-any.whl → 0.10.0.dev2__py3-none-any.whl
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/__init__.py +1 -1
- pytdbot/client.py +56 -45
- pytdbot/filters.py +1 -1
- pytdbot/handlers/td_updates.py +178 -1
- pytdbot/methods/methods.py +277 -1
- pytdbot/methods/td_functions.py +461 -35
- pytdbot/types/__init__.py +65 -1
- pytdbot/types/bound_methods/message.py +108 -0
- pytdbot/types/td_types.py +2003 -97
- pytdbot/types/tdserver/schedule.py +16 -11
- pytdbot/types/tdserver/stats.py +10 -7
- pytdbot/utils/__init__.py +2 -2
- pytdbot/utils/albums.py +17 -0
- pytdbot/utils/text_format.py +1 -1
- pytdbot-0.10.0.dev2.dist-info/METADATA +146 -0
- {pytdbot-0.10.0.dev0.dist-info → pytdbot-0.10.0.dev2.dist-info}/RECORD +19 -19
- pytdbot/utils/asyncio_utils.py +0 -10
- pytdbot-0.10.0.dev0.dist-info/METADATA +0 -129
- {pytdbot-0.10.0.dev0.dist-info → pytdbot-0.10.0.dev2.dist-info}/WHEEL +0 -0
- {pytdbot-0.10.0.dev0.dist-info → pytdbot-0.10.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {pytdbot-0.10.0.dev0.dist-info → pytdbot-0.10.0.dev2.dist-info}/top_level.txt +0 -0
pytdbot/__init__.py
CHANGED
pytdbot/client.py
CHANGED
|
@@ -5,6 +5,7 @@ import signal
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from importlib import import_module
|
|
7
7
|
from importlib import reload as reload_module
|
|
8
|
+
from inspect import iscoroutinefunction
|
|
8
9
|
from json import dumps
|
|
9
10
|
from logging import DEBUG, getLogger
|
|
10
11
|
from os.path import join as join_path
|
|
@@ -28,7 +29,6 @@ from .utils import (
|
|
|
28
29
|
create_extra_id,
|
|
29
30
|
dict_to_obj,
|
|
30
31
|
get_bot_id_from_token,
|
|
31
|
-
get_running_loop,
|
|
32
32
|
json_dumps,
|
|
33
33
|
json_loads,
|
|
34
34
|
obj_to_dict,
|
|
@@ -88,9 +88,6 @@ class Client(Decorators, Methods):
|
|
|
88
88
|
use_message_database (``bool``, *optional*):
|
|
89
89
|
If set to true, the library will maintain a cache of chats and messages. Implies use_chat_info_database. Default is ``True``
|
|
90
90
|
|
|
91
|
-
loop (:py:class:`asyncio.AbstractEventLoop`, *optional*):
|
|
92
|
-
Event loop. Default is ``None`` (auto-detect)
|
|
93
|
-
|
|
94
91
|
options (``dict``, *optional*):
|
|
95
92
|
Pass key-value dictionary to set TDLib options. Check the list of available options at https://core.telegram.org/tdlib/options
|
|
96
93
|
|
|
@@ -131,7 +128,6 @@ class Client(Decorators, Methods):
|
|
|
131
128
|
use_file_database: bool = True,
|
|
132
129
|
use_chat_info_database: bool = True,
|
|
133
130
|
use_message_database: bool = True,
|
|
134
|
-
loop: asyncio.AbstractEventLoop | None = None,
|
|
135
131
|
options: dict | None = None,
|
|
136
132
|
workers: int = 5,
|
|
137
133
|
queue_size: int = 1000,
|
|
@@ -220,15 +216,15 @@ class Client(Decorators, Methods):
|
|
|
220
216
|
}
|
|
221
217
|
self.__is_queue_worker = False
|
|
222
218
|
self.__is_closing = False
|
|
219
|
+
self.__idle_event: asyncio.Event = None
|
|
220
|
+
self.__closed_event: asyncio.Event = None
|
|
223
221
|
|
|
224
222
|
# RabbitMQ
|
|
225
223
|
self.__rqueues = None
|
|
226
224
|
self.__rconnection = None
|
|
227
225
|
self.__rchannel = None
|
|
228
226
|
|
|
229
|
-
self.loop =
|
|
230
|
-
loop if isinstance(loop, asyncio.AbstractEventLoop) else get_running_loop()
|
|
231
|
-
)
|
|
227
|
+
self.loop = None
|
|
232
228
|
|
|
233
229
|
if plugins is not None:
|
|
234
230
|
self._load_plugins()
|
|
@@ -324,6 +320,10 @@ class Client(Decorators, Methods):
|
|
|
324
320
|
if not self.is_running:
|
|
325
321
|
self.logger.info("Starting pytdbot client...")
|
|
326
322
|
|
|
323
|
+
self.loop = asyncio.get_running_loop()
|
|
324
|
+
self.__idle_event = asyncio.Event()
|
|
325
|
+
self.__closed_event = asyncio.Event()
|
|
326
|
+
|
|
327
327
|
if self.is_rabbitmq:
|
|
328
328
|
await self.__start_rabbitmq()
|
|
329
329
|
elif not self.client_manager:
|
|
@@ -611,12 +611,13 @@ class Client(Decorators, Methods):
|
|
|
611
611
|
|
|
612
612
|
return await self.invoke(kwargs)
|
|
613
613
|
|
|
614
|
-
def run(self) -> None:
|
|
614
|
+
async def run(self) -> None:
|
|
615
615
|
r"""Start the client and block until the client is stopped
|
|
616
616
|
|
|
617
617
|
Example:
|
|
618
618
|
.. code-block:: python
|
|
619
619
|
|
|
620
|
+
import asyncio
|
|
620
621
|
from pytdbot import Client
|
|
621
622
|
|
|
622
623
|
client = Client(...)
|
|
@@ -625,19 +626,22 @@ class Client(Decorators, Methods):
|
|
|
625
626
|
async def new_message(c,update):
|
|
626
627
|
await update.reply_text('Hello!')
|
|
627
628
|
|
|
628
|
-
client.run()
|
|
629
|
+
asyncio.run(client.run())
|
|
629
630
|
"""
|
|
630
631
|
|
|
632
|
+
await self.start()
|
|
633
|
+
|
|
631
634
|
self._register_signal_handlers()
|
|
632
635
|
|
|
633
|
-
self.
|
|
634
|
-
self.loop.run_until_complete(self.idle())
|
|
636
|
+
await self.idle()
|
|
635
637
|
|
|
636
638
|
async def idle(self):
|
|
637
|
-
r"""Idle and wait until the client is stopped
|
|
639
|
+
r"""Idle and wait until the client is stopped"""
|
|
638
640
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
+
if not self.__idle_event:
|
|
642
|
+
self.__idle_event = asyncio.Event()
|
|
643
|
+
|
|
644
|
+
await self.__idle_event.wait()
|
|
641
645
|
|
|
642
646
|
async def stop(self) -> bool:
|
|
643
647
|
r"""Stop the client
|
|
@@ -666,8 +670,7 @@ class Client(Decorators, Methods):
|
|
|
666
670
|
}:
|
|
667
671
|
await self.close()
|
|
668
672
|
|
|
669
|
-
|
|
670
|
-
await asyncio.sleep(0.1)
|
|
673
|
+
await self.__closed_event.wait()
|
|
671
674
|
|
|
672
675
|
if self.is_rabbitmq:
|
|
673
676
|
await self.__rchannel.close()
|
|
@@ -679,6 +682,7 @@ class Client(Decorators, Methods):
|
|
|
679
682
|
await self.client_manager.close()
|
|
680
683
|
|
|
681
684
|
self.logger.info("Instance closed")
|
|
685
|
+
self.__idle_event.set()
|
|
682
686
|
|
|
683
687
|
return True
|
|
684
688
|
|
|
@@ -778,7 +782,7 @@ class Client(Decorators, Methods):
|
|
|
778
782
|
]
|
|
779
783
|
|
|
780
784
|
for handler in handlers_to_load:
|
|
781
|
-
if
|
|
785
|
+
if iscoroutinefunction(handler.func):
|
|
782
786
|
self.add_handler(
|
|
783
787
|
update_type=handler.update_type,
|
|
784
788
|
func=handler.func,
|
|
@@ -810,39 +814,45 @@ class Client(Decorators, Methods):
|
|
|
810
814
|
if func in self.__cache["is_coro_filter"]:
|
|
811
815
|
return self.__cache["is_coro_filter"][func]
|
|
812
816
|
else:
|
|
813
|
-
is_coro =
|
|
817
|
+
is_coro = iscoroutinefunction(func)
|
|
814
818
|
self.__cache["is_coro_filter"][func] = is_coro
|
|
815
819
|
return is_coro
|
|
816
820
|
|
|
817
|
-
async def process_update(self, update):
|
|
821
|
+
async def process_update(self, update: dict) -> None:
|
|
818
822
|
if not update:
|
|
819
823
|
self.logger.warning("Received None update")
|
|
820
824
|
return
|
|
821
825
|
|
|
822
|
-
|
|
823
|
-
self.logger.root.level >= DEBUG or self.logger.level >= DEBUG
|
|
824
|
-
): # dumping all results may create performance issues
|
|
825
|
-
self.logger.debug(f"Received: {dumps(update, indent=4)}")
|
|
826
|
+
is_debug = self.logger.isEnabledFor(DEBUG)
|
|
826
827
|
|
|
827
|
-
if "@extra"
|
|
828
|
-
|
|
829
|
-
obj = dict_to_obj(update, self)
|
|
828
|
+
if extra := update.get("@extra"):
|
|
829
|
+
result_id = extra["id"]
|
|
830
830
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
831
|
+
if is_debug:
|
|
832
|
+
self.logger.debug(
|
|
833
|
+
f"Received result for {result_id}: {dumps(update, indent=4)}"
|
|
834
|
+
)
|
|
834
835
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
update = dict_to_obj(update, self)
|
|
836
|
+
if result_id and (result := self._results.pop(result_id, None)):
|
|
837
|
+
result.set_result(dict_to_obj(update, self))
|
|
838
838
|
|
|
839
|
-
|
|
840
|
-
self.
|
|
839
|
+
elif update["@type"] == "error" and "option" in extra:
|
|
840
|
+
self.logger.error(f"{extra['option']}: {update['message']}")
|
|
841
841
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
842
|
+
return
|
|
843
|
+
|
|
844
|
+
if is_debug:
|
|
845
|
+
self.logger.debug(f"Received: {dumps(update, indent=4)}")
|
|
846
|
+
|
|
847
|
+
update_obj = dict_to_obj(update, self)
|
|
848
|
+
|
|
849
|
+
if handler := self.__local_handlers.get(update.get("@type")):
|
|
850
|
+
self.loop.create_task(handler(update_obj))
|
|
851
|
+
|
|
852
|
+
if not self.is_rabbitmq and self.__is_queue_worker:
|
|
853
|
+
self.queue.put_nowait(update_obj)
|
|
854
|
+
else:
|
|
855
|
+
await self._handle_update(update_obj)
|
|
846
856
|
|
|
847
857
|
def get_inner_object(self, update: types.TlObject):
|
|
848
858
|
if isinstance(update, types.UpdateNewMessage):
|
|
@@ -1073,11 +1083,10 @@ class Client(Decorators, Methods):
|
|
|
1073
1083
|
|
|
1074
1084
|
if self.authorization_state == "authorizationStateClosing":
|
|
1075
1085
|
self.__is_closing = True
|
|
1076
|
-
elif
|
|
1077
|
-
self.
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
await self.stop()
|
|
1086
|
+
elif self.authorization_state == "authorizationStateClosed":
|
|
1087
|
+
self.__closed_event.set()
|
|
1088
|
+
if self.__is_closing is False:
|
|
1089
|
+
await self.stop()
|
|
1081
1090
|
|
|
1082
1091
|
async def __handle_connection_state(self, update: types.UpdateConnectionState):
|
|
1083
1092
|
self.connection_state: str = update.state.getType()
|
|
@@ -1099,7 +1108,9 @@ class Client(Decorators, Methods):
|
|
|
1099
1108
|
m_id = f"{update.message.chat_id}:{update.old_message_id}"
|
|
1100
1109
|
|
|
1101
1110
|
if result := self._results.pop(m_id, None):
|
|
1102
|
-
result.set_result(
|
|
1111
|
+
result.set_result(
|
|
1112
|
+
update.error if not update.message.media_album_id else update.message
|
|
1113
|
+
)
|
|
1103
1114
|
|
|
1104
1115
|
async def __handle_update_option(self, update: types.UpdateOption):
|
|
1105
1116
|
if isinstance(update.value, types.OptionValueBoolean):
|
pytdbot/filters.py
CHANGED
pytdbot/handlers/td_updates.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from asyncio import iscoroutinefunction
|
|
4
3
|
from collections.abc import Callable
|
|
4
|
+
from inspect import iscoroutinefunction
|
|
5
5
|
from logging import getLogger
|
|
6
6
|
|
|
7
7
|
import pytdbot
|
|
@@ -2433,6 +2433,65 @@ class Updates:
|
|
|
2433
2433
|
|
|
2434
2434
|
return decorator
|
|
2435
2435
|
|
|
2436
|
+
def on_updateChatUnreadPollVoteCount(
|
|
2437
|
+
self: pytdbot.Client | None = None,
|
|
2438
|
+
filters: pytdbot.filters.Filter | None = None,
|
|
2439
|
+
position: int | None = None,
|
|
2440
|
+
timeout: float | None = None,
|
|
2441
|
+
) -> Callable:
|
|
2442
|
+
r"""The chat unread\_poll\_vote\_count has changed
|
|
2443
|
+
|
|
2444
|
+
Parameters:
|
|
2445
|
+
filters (:class:`pytdbot.filters.Filter`, *optional*):
|
|
2446
|
+
An update filter
|
|
2447
|
+
|
|
2448
|
+
position (``int``, *optional*):
|
|
2449
|
+
The function position in handlers list. Default is ``None`` (append)
|
|
2450
|
+
|
|
2451
|
+
timeout (``float``, *optional*):
|
|
2452
|
+
Max execution time for the handler before it timeout. Default is ``None``
|
|
2453
|
+
|
|
2454
|
+
Raises:
|
|
2455
|
+
:py:class:`TypeError`
|
|
2456
|
+
"""
|
|
2457
|
+
|
|
2458
|
+
def decorator(func: Callable) -> Callable:
|
|
2459
|
+
if hasattr(func, "_handler"):
|
|
2460
|
+
return func
|
|
2461
|
+
elif isinstance(self, pytdbot.Client):
|
|
2462
|
+
if iscoroutinefunction(func):
|
|
2463
|
+
self.add_handler(
|
|
2464
|
+
update_type="updateChatUnreadPollVoteCount",
|
|
2465
|
+
func=func,
|
|
2466
|
+
filters=filters,
|
|
2467
|
+
position=position,
|
|
2468
|
+
inner_object=False,
|
|
2469
|
+
timeout=timeout,
|
|
2470
|
+
)
|
|
2471
|
+
else:
|
|
2472
|
+
raise TypeError("Handler must be async")
|
|
2473
|
+
elif isinstance(self, pytdbot.filters.Filter):
|
|
2474
|
+
func._handler = Handler(
|
|
2475
|
+
func=func,
|
|
2476
|
+
update_type="updateChatUnreadPollVoteCount",
|
|
2477
|
+
filter=self,
|
|
2478
|
+
position=position,
|
|
2479
|
+
inner_object=False,
|
|
2480
|
+
timeout=timeout,
|
|
2481
|
+
)
|
|
2482
|
+
else:
|
|
2483
|
+
func._handler = Handler(
|
|
2484
|
+
func=func,
|
|
2485
|
+
update_type="updateChatUnreadPollVoteCount",
|
|
2486
|
+
filter=filters,
|
|
2487
|
+
position=position,
|
|
2488
|
+
inner_object=False,
|
|
2489
|
+
timeout=timeout,
|
|
2490
|
+
)
|
|
2491
|
+
return func
|
|
2492
|
+
|
|
2493
|
+
return decorator
|
|
2494
|
+
|
|
2436
2495
|
def on_updateChatVideoChat(
|
|
2437
2496
|
self: pytdbot.Client | None = None,
|
|
2438
2497
|
filters: pytdbot.filters.Filter | None = None,
|
|
@@ -8864,6 +8923,65 @@ class Updates:
|
|
|
8864
8923
|
|
|
8865
8924
|
return decorator
|
|
8866
8925
|
|
|
8926
|
+
def on_updateTextCompositionStyles(
|
|
8927
|
+
self: pytdbot.Client | None = None,
|
|
8928
|
+
filters: pytdbot.filters.Filter | None = None,
|
|
8929
|
+
position: int | None = None,
|
|
8930
|
+
timeout: float | None = None,
|
|
8931
|
+
) -> Callable:
|
|
8932
|
+
r"""The styles supported for text composition have changed
|
|
8933
|
+
|
|
8934
|
+
Parameters:
|
|
8935
|
+
filters (:class:`pytdbot.filters.Filter`, *optional*):
|
|
8936
|
+
An update filter
|
|
8937
|
+
|
|
8938
|
+
position (``int``, *optional*):
|
|
8939
|
+
The function position in handlers list. Default is ``None`` (append)
|
|
8940
|
+
|
|
8941
|
+
timeout (``float``, *optional*):
|
|
8942
|
+
Max execution time for the handler before it timeout. Default is ``None``
|
|
8943
|
+
|
|
8944
|
+
Raises:
|
|
8945
|
+
:py:class:`TypeError`
|
|
8946
|
+
"""
|
|
8947
|
+
|
|
8948
|
+
def decorator(func: Callable) -> Callable:
|
|
8949
|
+
if hasattr(func, "_handler"):
|
|
8950
|
+
return func
|
|
8951
|
+
elif isinstance(self, pytdbot.Client):
|
|
8952
|
+
if iscoroutinefunction(func):
|
|
8953
|
+
self.add_handler(
|
|
8954
|
+
update_type="updateTextCompositionStyles",
|
|
8955
|
+
func=func,
|
|
8956
|
+
filters=filters,
|
|
8957
|
+
position=position,
|
|
8958
|
+
inner_object=False,
|
|
8959
|
+
timeout=timeout,
|
|
8960
|
+
)
|
|
8961
|
+
else:
|
|
8962
|
+
raise TypeError("Handler must be async")
|
|
8963
|
+
elif isinstance(self, pytdbot.filters.Filter):
|
|
8964
|
+
func._handler = Handler(
|
|
8965
|
+
func=func,
|
|
8966
|
+
update_type="updateTextCompositionStyles",
|
|
8967
|
+
filter=self,
|
|
8968
|
+
position=position,
|
|
8969
|
+
inner_object=False,
|
|
8970
|
+
timeout=timeout,
|
|
8971
|
+
)
|
|
8972
|
+
else:
|
|
8973
|
+
func._handler = Handler(
|
|
8974
|
+
func=func,
|
|
8975
|
+
update_type="updateTextCompositionStyles",
|
|
8976
|
+
filter=filters,
|
|
8977
|
+
position=position,
|
|
8978
|
+
inner_object=False,
|
|
8979
|
+
timeout=timeout,
|
|
8980
|
+
)
|
|
8981
|
+
return func
|
|
8982
|
+
|
|
8983
|
+
return decorator
|
|
8984
|
+
|
|
8867
8985
|
def on_updateSuggestedActions(
|
|
8868
8986
|
self: pytdbot.Client | None = None,
|
|
8869
8987
|
filters: pytdbot.filters.Filter | None = None,
|
|
@@ -9985,6 +10103,65 @@ class Updates:
|
|
|
9985
10103
|
|
|
9986
10104
|
return decorator
|
|
9987
10105
|
|
|
10106
|
+
def on_updateManagedBot(
|
|
10107
|
+
self: pytdbot.Client | None = None,
|
|
10108
|
+
filters: pytdbot.filters.Filter | None = None,
|
|
10109
|
+
position: int | None = None,
|
|
10110
|
+
timeout: float | None = None,
|
|
10111
|
+
) -> Callable:
|
|
10112
|
+
r"""A bot that can be managed by the current bot was created or updated; for bots only
|
|
10113
|
+
|
|
10114
|
+
Parameters:
|
|
10115
|
+
filters (:class:`pytdbot.filters.Filter`, *optional*):
|
|
10116
|
+
An update filter
|
|
10117
|
+
|
|
10118
|
+
position (``int``, *optional*):
|
|
10119
|
+
The function position in handlers list. Default is ``None`` (append)
|
|
10120
|
+
|
|
10121
|
+
timeout (``float``, *optional*):
|
|
10122
|
+
Max execution time for the handler before it timeout. Default is ``None``
|
|
10123
|
+
|
|
10124
|
+
Raises:
|
|
10125
|
+
:py:class:`TypeError`
|
|
10126
|
+
"""
|
|
10127
|
+
|
|
10128
|
+
def decorator(func: Callable) -> Callable:
|
|
10129
|
+
if hasattr(func, "_handler"):
|
|
10130
|
+
return func
|
|
10131
|
+
elif isinstance(self, pytdbot.Client):
|
|
10132
|
+
if iscoroutinefunction(func):
|
|
10133
|
+
self.add_handler(
|
|
10134
|
+
update_type="updateManagedBot",
|
|
10135
|
+
func=func,
|
|
10136
|
+
filters=filters,
|
|
10137
|
+
position=position,
|
|
10138
|
+
inner_object=False,
|
|
10139
|
+
timeout=timeout,
|
|
10140
|
+
)
|
|
10141
|
+
else:
|
|
10142
|
+
raise TypeError("Handler must be async")
|
|
10143
|
+
elif isinstance(self, pytdbot.filters.Filter):
|
|
10144
|
+
func._handler = Handler(
|
|
10145
|
+
func=func,
|
|
10146
|
+
update_type="updateManagedBot",
|
|
10147
|
+
filter=self,
|
|
10148
|
+
position=position,
|
|
10149
|
+
inner_object=False,
|
|
10150
|
+
timeout=timeout,
|
|
10151
|
+
)
|
|
10152
|
+
else:
|
|
10153
|
+
func._handler = Handler(
|
|
10154
|
+
func=func,
|
|
10155
|
+
update_type="updateManagedBot",
|
|
10156
|
+
filter=filters,
|
|
10157
|
+
position=position,
|
|
10158
|
+
inner_object=False,
|
|
10159
|
+
timeout=timeout,
|
|
10160
|
+
)
|
|
10161
|
+
return func
|
|
10162
|
+
|
|
10163
|
+
return decorator
|
|
10164
|
+
|
|
9988
10165
|
def on_updateChatMember(
|
|
9989
10166
|
self: pytdbot.Client | None = None,
|
|
9990
10167
|
filters: pytdbot.filters.Filter | None = None,
|