Pytdbot 0.10.0.dev2__tar.gz → 0.10.0.dev4__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.10.0.dev2 → pytdbot-0.10.0.dev4}/PKG-INFO +4 -4
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/Pytdbot.egg-info/PKG-INFO +4 -4
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/Pytdbot.egg-info/requires.txt +3 -1
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/README.md +1 -2
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pyproject.toml +2 -1
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/__init__.py +1 -1
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/client.py +103 -138
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/handlers/td_updates.py +118 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/methods/td_functions.py +438 -18
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/__init__.py +37 -3
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/td_types.py +967 -74
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/LICENSE +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/MANIFEST.in +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/Pytdbot.egg-info/SOURCES.txt +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/Pytdbot.egg-info/dependency_links.txt +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/Pytdbot.egg-info/top_level.txt +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/client_manager.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/exception/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/filters.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/handlers/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/handlers/decorators.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/handlers/handler.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/methods/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/methods/methods.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/tdjson/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/tdjson/tdjson.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/bound_methods/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/bound_methods/callback_query.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/bound_methods/chatActions.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/bound_methods/file.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/bound_methods/message.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/bound_methods/sender_id.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/plugins/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/tdserver/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/tdserver/schedule.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/types/tdserver/stats.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/__init__.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/albums.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/escape.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/json_utils.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/obj_encoder.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/strings.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/text_format.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/pytdbot/utils/webapps.py +0 -0
- {pytdbot-0.10.0.dev2 → pytdbot-0.10.0.dev4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Pytdbot
|
|
3
|
-
Version: 0.10.0.
|
|
3
|
+
Version: 0.10.0.dev4
|
|
4
4
|
Summary: Easy-to-use asynchronous TDLib wrapper for Python.
|
|
5
5
|
Author-email: AYMEN A <let.me.code.safe@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,15 +23,16 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: deepdiff
|
|
26
|
-
Requires-Dist: aio_pika
|
|
27
26
|
Provides-Extra: tdjson
|
|
28
27
|
Requires-Dist: tdjson; extra == "tdjson"
|
|
28
|
+
Provides-Extra: nats
|
|
29
|
+
Requires-Dist: nats-py; extra == "nats"
|
|
29
30
|
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
# Pytdbot
|
|
32
33
|
|
|
33
34
|
[](https://pypi.org/project/Pytdbot)
|
|
34
|
-
[](https://github.com/tdlib/td)
|
|
35
36
|
[](https://pepy.tech/project/pytdbot)
|
|
36
37
|
[](https://t.me/pytdbotchat)
|
|
37
38
|
|
|
@@ -60,7 +61,6 @@ Pytdbot is an asynchronous [**TDLib**](https://github.com/tdlib/td) wrapper for
|
|
|
60
61
|
- Telegram [API key](https://my.telegram.org/apps)
|
|
61
62
|
- [tdjson](https://github.com/AYMENJD/tdjson) or [TDLib](https://github.com/tdlib/td#building)
|
|
62
63
|
- [deepdiff](https://github.com/seperman/deepdiff)
|
|
63
|
-
- [aio-pika](https://github.com/mosquito/aio-pika)
|
|
64
64
|
|
|
65
65
|
### Installation
|
|
66
66
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Pytdbot
|
|
3
|
-
Version: 0.10.0.
|
|
3
|
+
Version: 0.10.0.dev4
|
|
4
4
|
Summary: Easy-to-use asynchronous TDLib wrapper for Python.
|
|
5
5
|
Author-email: AYMEN A <let.me.code.safe@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,15 +23,16 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: deepdiff
|
|
26
|
-
Requires-Dist: aio_pika
|
|
27
26
|
Provides-Extra: tdjson
|
|
28
27
|
Requires-Dist: tdjson; extra == "tdjson"
|
|
28
|
+
Provides-Extra: nats
|
|
29
|
+
Requires-Dist: nats-py; extra == "nats"
|
|
29
30
|
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
# Pytdbot
|
|
32
33
|
|
|
33
34
|
[](https://pypi.org/project/Pytdbot)
|
|
34
|
-
[](https://github.com/tdlib/td)
|
|
35
36
|
[](https://pepy.tech/project/pytdbot)
|
|
36
37
|
[](https://t.me/pytdbotchat)
|
|
37
38
|
|
|
@@ -60,7 +61,6 @@ Pytdbot is an asynchronous [**TDLib**](https://github.com/tdlib/td) wrapper for
|
|
|
60
61
|
- Telegram [API key](https://my.telegram.org/apps)
|
|
61
62
|
- [tdjson](https://github.com/AYMENJD/tdjson) or [TDLib](https://github.com/tdlib/td#building)
|
|
62
63
|
- [deepdiff](https://github.com/seperman/deepdiff)
|
|
63
|
-
- [aio-pika](https://github.com/mosquito/aio-pika)
|
|
64
64
|
|
|
65
65
|
### Installation
|
|
66
66
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Pytdbot
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/Pytdbot)
|
|
4
|
-
[](https://github.com/tdlib/td)
|
|
5
5
|
[](https://pepy.tech/project/pytdbot)
|
|
6
6
|
[](https://t.me/pytdbotchat)
|
|
7
7
|
|
|
@@ -30,7 +30,6 @@ Pytdbot is an asynchronous [**TDLib**](https://github.com/tdlib/td) wrapper for
|
|
|
30
30
|
- Telegram [API key](https://my.telegram.org/apps)
|
|
31
31
|
- [tdjson](https://github.com/AYMENJD/tdjson) or [TDLib](https://github.com/tdlib/td#building)
|
|
32
32
|
- [deepdiff](https://github.com/seperman/deepdiff)
|
|
33
|
-
- [aio-pika](https://github.com/mosquito/aio-pika)
|
|
34
33
|
|
|
35
34
|
### Installation
|
|
36
35
|
|
|
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
|
|
|
11
11
|
license = "MIT"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
13
|
authors = [{ name = "AYMEN A", email = "let.me.code.safe@gmail.com" }]
|
|
14
|
-
dependencies = ["deepdiff"
|
|
14
|
+
dependencies = ["deepdiff"]
|
|
15
15
|
keywords = [
|
|
16
16
|
"telegram",
|
|
17
17
|
"tdlib",
|
|
@@ -43,6 +43,7 @@ Tracker = "https://github.com/pytdbot/client/issues"
|
|
|
43
43
|
|
|
44
44
|
[project.optional-dependencies]
|
|
45
45
|
tdjson = ["tdjson"]
|
|
46
|
+
nats = ["nats-py"]
|
|
46
47
|
|
|
47
48
|
[tool.setuptools.packages.find]
|
|
48
49
|
exclude = ["examples"]
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import signal
|
|
5
|
+
import sys
|
|
5
6
|
from collections.abc import Callable
|
|
6
7
|
from importlib import import_module
|
|
7
8
|
from importlib import reload as reload_module
|
|
@@ -13,7 +14,11 @@ from pathlib import Path
|
|
|
13
14
|
from platform import python_implementation, python_version
|
|
14
15
|
from threading import current_thread, main_thread
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
try:
|
|
18
|
+
import nats
|
|
19
|
+
except ImportError:
|
|
20
|
+
nats = None
|
|
21
|
+
|
|
17
22
|
from deepdiff import DeepDiff
|
|
18
23
|
|
|
19
24
|
import pytdbot
|
|
@@ -48,11 +53,11 @@ class Client(Decorators, Methods):
|
|
|
48
53
|
api_hash (``str``, *optional*):
|
|
49
54
|
Identifier hash for Telegram API access, which can be obtained at https://my.telegram.org
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
URL for
|
|
56
|
+
nats_url (``str``, *optional*):
|
|
57
|
+
URL for NATS server connection
|
|
53
58
|
|
|
54
59
|
instance_id (``str``, *optional*):
|
|
55
|
-
Instance ID for
|
|
60
|
+
Instance ID for NATS connections and queues. Default is ``None`` (random)
|
|
56
61
|
|
|
57
62
|
lib_path (``str``, *optional*):
|
|
58
63
|
Path to TDLib library. Default is ``None`` (auto-detect)
|
|
@@ -115,7 +120,7 @@ class Client(Decorators, Methods):
|
|
|
115
120
|
token: str | None = None,
|
|
116
121
|
api_id: int | None = None,
|
|
117
122
|
api_hash: str | None = None,
|
|
118
|
-
|
|
123
|
+
nats_url: str | None = None,
|
|
119
124
|
instance_id: str | None = None,
|
|
120
125
|
lib_path: str | None = None,
|
|
121
126
|
plugins: Plugins | None = None,
|
|
@@ -137,12 +142,11 @@ class Client(Decorators, Methods):
|
|
|
137
142
|
td_verbosity: int = 2,
|
|
138
143
|
td_log: LogStream | None = None,
|
|
139
144
|
user_bot: bool = False,
|
|
140
|
-
server_ack: bool = True,
|
|
141
145
|
) -> None:
|
|
142
146
|
self.__api_id = api_id
|
|
143
147
|
self.__api_hash = api_hash
|
|
144
|
-
self.
|
|
145
|
-
self.
|
|
148
|
+
self.__nats_url = nats_url
|
|
149
|
+
self._nats_instance_id = (
|
|
146
150
|
instance_id if isinstance(instance_id, str) else create_extra_id(4)
|
|
147
151
|
)
|
|
148
152
|
self.__token = token
|
|
@@ -170,7 +174,6 @@ class Client(Decorators, Methods):
|
|
|
170
174
|
self.load_messages_before_reply = load_messages_before_reply
|
|
171
175
|
self.queue = asyncio.Queue()
|
|
172
176
|
self.user_bot = user_bot
|
|
173
|
-
self.server_ack = server_ack
|
|
174
177
|
self.my_id = (
|
|
175
178
|
get_bot_id_from_token(self.__token)
|
|
176
179
|
if isinstance(self.__token, str)
|
|
@@ -186,7 +189,7 @@ class Client(Decorators, Methods):
|
|
|
186
189
|
self.me: types.User = None
|
|
187
190
|
self.is_authenticated = False
|
|
188
191
|
self.is_reloading_plugins = False
|
|
189
|
-
self.
|
|
192
|
+
self.is_nats = True if nats_url else False
|
|
190
193
|
self.options = {}
|
|
191
194
|
self.allow_outgoing_message_types: tuple = (types.MessagePaymentRefunded,)
|
|
192
195
|
self.get_message_methods = {
|
|
@@ -203,7 +206,6 @@ class Client(Decorators, Methods):
|
|
|
203
206
|
self._results: dict[str, asyncio.Future] = {}
|
|
204
207
|
self._workers_tasks = None
|
|
205
208
|
self.__wait_login: asyncio.Event = None
|
|
206
|
-
self.__rabbitmq_iterator_task = None
|
|
207
209
|
self.__authorization_state: str = None
|
|
208
210
|
self.__cache = {"is_coro_filter": {}}
|
|
209
211
|
self.__local_handlers = {
|
|
@@ -219,10 +221,11 @@ class Client(Decorators, Methods):
|
|
|
219
221
|
self.__idle_event: asyncio.Event = None
|
|
220
222
|
self.__closed_event: asyncio.Event = None
|
|
221
223
|
|
|
222
|
-
#
|
|
223
|
-
self.
|
|
224
|
-
self.
|
|
225
|
-
self.
|
|
224
|
+
# NATS
|
|
225
|
+
self.__nc = None
|
|
226
|
+
self.__requests_subject = f"bot.{self.my_id}.requests"
|
|
227
|
+
self.__updates_subject = f"bot.{self.my_id}.updates"
|
|
228
|
+
self.__broadcast_subject = f"bot.{self.my_id}.broadcast"
|
|
226
229
|
|
|
227
230
|
self.loop = None
|
|
228
231
|
|
|
@@ -252,7 +255,7 @@ class Client(Decorators, Methods):
|
|
|
252
255
|
) -> pytdbot.types.ServerStats | pytdbot.types.Error:
|
|
253
256
|
"""Returns TDLib Server stats"""
|
|
254
257
|
|
|
255
|
-
self.
|
|
258
|
+
self._check_nats()
|
|
256
259
|
|
|
257
260
|
return await self.invoke({"@type": "getServerStats"})
|
|
258
261
|
|
|
@@ -272,7 +275,7 @@ class Client(Decorators, Methods):
|
|
|
272
275
|
Unix timestamp when the event should be sent
|
|
273
276
|
"""
|
|
274
277
|
|
|
275
|
-
self.
|
|
278
|
+
self._check_nats()
|
|
276
279
|
|
|
277
280
|
if not isinstance(name, str):
|
|
278
281
|
raise ValueError("name must be str")
|
|
@@ -300,7 +303,7 @@ class Client(Decorators, Methods):
|
|
|
300
303
|
Event ID to cancel
|
|
301
304
|
"""
|
|
302
305
|
|
|
303
|
-
self.
|
|
306
|
+
self._check_nats()
|
|
304
307
|
|
|
305
308
|
if not isinstance(event_id, str):
|
|
306
309
|
raise ValueError("event_id must be str")
|
|
@@ -324,8 +327,8 @@ class Client(Decorators, Methods):
|
|
|
324
327
|
self.__idle_event = asyncio.Event()
|
|
325
328
|
self.__closed_event = asyncio.Event()
|
|
326
329
|
|
|
327
|
-
if self.
|
|
328
|
-
await self.
|
|
330
|
+
if self.is_nats:
|
|
331
|
+
await self.__start_nats()
|
|
329
332
|
elif not self.client_manager:
|
|
330
333
|
self.__wait_login = asyncio.Event() if not self.user_bot else None
|
|
331
334
|
|
|
@@ -335,25 +338,19 @@ class Client(Decorators, Methods):
|
|
|
335
338
|
await self.client_manager.start()
|
|
336
339
|
self.is_running = True
|
|
337
340
|
|
|
338
|
-
if isinstance(self.td_log, LogStream) and not self.
|
|
341
|
+
if isinstance(self.td_log, LogStream) and not self.is_nats:
|
|
339
342
|
await self.__send(
|
|
340
343
|
{"@type": "setLogStream", "log_stream": obj_to_dict(self.td_log)}
|
|
341
344
|
)
|
|
342
345
|
|
|
343
|
-
if isinstance(self.workers, int):
|
|
346
|
+
if isinstance(self.workers, int) and not self.is_nats:
|
|
344
347
|
self._workers_tasks = [
|
|
345
|
-
self.loop.create_task(
|
|
346
|
-
self._queue_update_worker()
|
|
347
|
-
if not self.is_rabbitmq
|
|
348
|
-
else self.__rabbitmq_worker()
|
|
349
|
-
)
|
|
348
|
+
self.loop.create_task(self._queue_update_worker())
|
|
350
349
|
for _ in range(self.workers)
|
|
351
350
|
]
|
|
352
351
|
self.__is_queue_worker = True
|
|
353
352
|
|
|
354
353
|
self.logger.info(f"Started with {self.workers} workers")
|
|
355
|
-
elif self.is_rabbitmq:
|
|
356
|
-
raise ValueError("workers must be an int when using TDLib Server")
|
|
357
354
|
else:
|
|
358
355
|
self.__is_queue_worker = False
|
|
359
356
|
self.logger.info("Started with unlimited updates processes")
|
|
@@ -670,11 +667,27 @@ class Client(Decorators, Methods):
|
|
|
670
667
|
}:
|
|
671
668
|
await self.close()
|
|
672
669
|
|
|
670
|
+
if self.is_nats:
|
|
671
|
+
# fake closing because TDLib Server doesn't allow close() from workers
|
|
672
|
+
await self.process_update(
|
|
673
|
+
obj_to_dict(
|
|
674
|
+
types.UpdateAuthorizationState(
|
|
675
|
+
authorization_state=types.AuthorizationStateClosing()
|
|
676
|
+
)
|
|
677
|
+
)
|
|
678
|
+
)
|
|
679
|
+
await self.process_update(
|
|
680
|
+
obj_to_dict(
|
|
681
|
+
types.UpdateAuthorizationState(
|
|
682
|
+
authorization_state=types.AuthorizationStateClosed()
|
|
683
|
+
)
|
|
684
|
+
)
|
|
685
|
+
)
|
|
686
|
+
|
|
673
687
|
await self.__closed_event.wait()
|
|
674
688
|
|
|
675
|
-
if self.
|
|
676
|
-
await self.
|
|
677
|
-
await self.__rconnection.close()
|
|
689
|
+
if self.is_nats:
|
|
690
|
+
await self.__nc.close()
|
|
678
691
|
|
|
679
692
|
self.__stop_client()
|
|
680
693
|
|
|
@@ -700,25 +713,23 @@ class Client(Decorators, Methods):
|
|
|
700
713
|
return result
|
|
701
714
|
|
|
702
715
|
async def __send(self, request: dict) -> None:
|
|
703
|
-
if self.
|
|
704
|
-
await self.
|
|
705
|
-
|
|
706
|
-
json_dumps(request, encode=True)
|
|
707
|
-
|
|
708
|
-
),
|
|
709
|
-
routing_key=self.__rqueues["requests"].name,
|
|
716
|
+
if self.is_nats:
|
|
717
|
+
await self.__on_update(
|
|
718
|
+
await self.__nc.request(
|
|
719
|
+
self.__requests_subject, json_dumps(request, encode=True)
|
|
720
|
+
)
|
|
710
721
|
)
|
|
711
722
|
else:
|
|
712
723
|
self.client_manager.send(self.client_id, request)
|
|
713
724
|
|
|
714
|
-
def
|
|
715
|
-
assert self.
|
|
725
|
+
def _check_nats(self):
|
|
726
|
+
assert self.is_nats, "This method is only available for TDLib Server"
|
|
716
727
|
|
|
717
728
|
def _check_init_args(self):
|
|
718
729
|
if self.user_bot:
|
|
719
730
|
return
|
|
720
731
|
|
|
721
|
-
if not self.
|
|
732
|
+
if not self.is_nats:
|
|
722
733
|
if not isinstance(self.__api_id, int):
|
|
723
734
|
raise TypeError("api_id must be an int")
|
|
724
735
|
if not isinstance(self.__api_hash, str):
|
|
@@ -849,7 +860,7 @@ class Client(Decorators, Methods):
|
|
|
849
860
|
if handler := self.__local_handlers.get(update.get("@type")):
|
|
850
861
|
self.loop.create_task(handler(update_obj))
|
|
851
862
|
|
|
852
|
-
if not self.
|
|
863
|
+
if not self.is_nats and self.__is_queue_worker:
|
|
853
864
|
self.queue.put_nowait(update_obj)
|
|
854
865
|
else:
|
|
855
866
|
await self._handle_update(update_obj)
|
|
@@ -997,7 +1008,7 @@ class Client(Decorators, Methods):
|
|
|
997
1008
|
`AuthorizationError`
|
|
998
1009
|
"""
|
|
999
1010
|
|
|
1000
|
-
if self.
|
|
1011
|
+
if self.is_nats:
|
|
1001
1012
|
return
|
|
1002
1013
|
|
|
1003
1014
|
if isinstance(self.__database_encryption_key, str):
|
|
@@ -1041,13 +1052,15 @@ class Client(Decorators, Methods):
|
|
|
1041
1052
|
else:
|
|
1042
1053
|
raise ValueError(f"Option {k} has unsupported type {v_type}")
|
|
1043
1054
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1055
|
+
self.loop.create_task(
|
|
1056
|
+
self.__send(
|
|
1057
|
+
{
|
|
1058
|
+
"@type": "setOption",
|
|
1059
|
+
"name": k,
|
|
1060
|
+
"value": data,
|
|
1061
|
+
"@extra": {"option": k, "value": v, "id": ""},
|
|
1062
|
+
}
|
|
1063
|
+
)
|
|
1051
1064
|
)
|
|
1052
1065
|
self.logger.debug(f"Option {k} sent with value {v}")
|
|
1053
1066
|
|
|
@@ -1068,6 +1081,7 @@ class Client(Decorators, Methods):
|
|
|
1068
1081
|
await self.__handle_authorization_state_wait_phone_number()
|
|
1069
1082
|
elif self.authorization_state == "authorizationStateReady":
|
|
1070
1083
|
self.is_authenticated = True
|
|
1084
|
+
self.__idle_event.clear()
|
|
1071
1085
|
|
|
1072
1086
|
self.me = await self.getMe()
|
|
1073
1087
|
if isinstance(self.me, types.Error):
|
|
@@ -1130,56 +1144,49 @@ class Client(Decorators, Methods):
|
|
|
1130
1144
|
f"Option {update.name} changed to {self.options[update.name]}"
|
|
1131
1145
|
)
|
|
1132
1146
|
|
|
1133
|
-
async def
|
|
1134
|
-
|
|
1135
|
-
try:
|
|
1136
|
-
return await self.__rchannel.get_queue(self.my_id + "_updates")
|
|
1137
|
-
except aio_pika.exceptions.ChannelNotFoundEntity:
|
|
1138
|
-
self.logger.warning(
|
|
1139
|
-
f"Attempt {attempt + 1}: TDLib Server is not running. Retrying in {delay} seconds..."
|
|
1140
|
-
)
|
|
1141
|
-
await asyncio.sleep(delay)
|
|
1142
|
-
self.logger.error(
|
|
1143
|
-
f"Could not connect to TDLib Server after {retries} attempts."
|
|
1144
|
-
)
|
|
1145
|
-
raise AuthorizationError(
|
|
1146
|
-
f"Could not connect to TDLib Server after {delay * retries} seconds timeout"
|
|
1147
|
-
)
|
|
1147
|
+
async def __nc_error_handler(self, e):
|
|
1148
|
+
self.logger.error(f"NATS connection error: {e}")
|
|
1148
1149
|
|
|
1149
|
-
async def
|
|
1150
|
-
self.
|
|
1151
|
-
self.__rabbitmq_url,
|
|
1152
|
-
client_properties={
|
|
1153
|
-
"connection_name": f"Pytdbot instance {self._rabbitmq_instance_id}"
|
|
1154
|
-
},
|
|
1155
|
-
)
|
|
1156
|
-
self.__rchannel = await self.__rconnection.channel()
|
|
1150
|
+
async def __nc_reconnect_handler(self):
|
|
1151
|
+
self.logger.info("Reconnected to NATS server")
|
|
1157
1152
|
|
|
1158
|
-
|
|
1153
|
+
async def __nc_disconnect_handler(self):
|
|
1154
|
+
self.logger.info("Disconnected from NATS server")
|
|
1159
1155
|
|
|
1160
|
-
|
|
1156
|
+
async def __nc_closed_handler(self):
|
|
1157
|
+
self.logger.info("Closed connection to NATS server")
|
|
1161
1158
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
)
|
|
1159
|
+
async def __start_nats(self):
|
|
1160
|
+
if not nats:
|
|
1161
|
+
raise ImportError(
|
|
1162
|
+
f"nats-py is not installed, please install it with `{sys.executable} -m pip install --upgrade nats-py`"
|
|
1163
|
+
)
|
|
1168
1164
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1165
|
+
self.__nc = await nats.connect(
|
|
1166
|
+
self.__nats_url,
|
|
1167
|
+
name=f"Pytdbot instance {self._nats_instance_id}",
|
|
1168
|
+
error_cb=self.__nc_error_handler,
|
|
1169
|
+
reconnected_cb=self.__nc_reconnect_handler,
|
|
1170
|
+
closed_cb=self.__nc_closed_handler,
|
|
1171
|
+
disconnected_cb=self.__nc_disconnect_handler,
|
|
1172
|
+
max_reconnect_attempts=-1,
|
|
1171
1173
|
)
|
|
1172
1174
|
|
|
1173
|
-
self.
|
|
1174
|
-
"
|
|
1175
|
-
|
|
1176
|
-
"
|
|
1177
|
-
"responses": responses_queue,
|
|
1178
|
-
}
|
|
1175
|
+
if self.__nc.is_connected:
|
|
1176
|
+
self.logger.info("Connected to TDLib server via NATS")
|
|
1177
|
+
else:
|
|
1178
|
+
raise AuthorizationError("Failed to connect to TDLib server via NATS")
|
|
1179
1179
|
|
|
1180
1180
|
self.is_running = True
|
|
1181
1181
|
|
|
1182
|
-
|
|
1182
|
+
if not self.no_updates:
|
|
1183
|
+
await self.__nc.subscribe(
|
|
1184
|
+
self.__updates_subject,
|
|
1185
|
+
queue="updates",
|
|
1186
|
+
cb=self.__on_update,
|
|
1187
|
+
)
|
|
1188
|
+
|
|
1189
|
+
await self.__nc.subscribe(self.__broadcast_subject, cb=self.__on_update)
|
|
1183
1190
|
|
|
1184
1191
|
await self._set_options()
|
|
1185
1192
|
|
|
@@ -1189,50 +1196,11 @@ class Client(Decorators, Methods):
|
|
|
1189
1196
|
# since it's not part of the object
|
|
1190
1197
|
await self.process_update(obj_to_dict(update))
|
|
1191
1198
|
|
|
1192
|
-
if not self.no_updates:
|
|
1193
|
-
self.__rabbitmq_iterator_task = self.loop.create_task(
|
|
1194
|
-
self.__rabbitmq_iterator()
|
|
1195
|
-
)
|
|
1196
|
-
|
|
1197
|
-
await self.__rqueues["notify"].consume(self.__on_update, no_ack=True)
|
|
1198
|
-
|
|
1199
|
-
async def __rabbitmq_iterator(self):
|
|
1200
|
-
async with self.__rqueues["updates"].iterator(
|
|
1201
|
-
no_ack=not self.server_ack
|
|
1202
|
-
) as iterator:
|
|
1203
|
-
async for message in iterator:
|
|
1204
|
-
if self.queue.qsize() > self.queue_size:
|
|
1205
|
-
await message.nack(requeue=True)
|
|
1206
|
-
continue
|
|
1207
|
-
|
|
1208
|
-
self.queue.put_nowait(message)
|
|
1209
|
-
|
|
1210
|
-
async def __rabbitmq_worker(self):
|
|
1211
|
-
while self.is_running:
|
|
1212
|
-
try:
|
|
1213
|
-
message: aio_pika.IncomingMessage = self.queue.get_nowait()
|
|
1214
|
-
except asyncio.QueueEmpty:
|
|
1215
|
-
message: aio_pika.IncomingMessage = await self.queue.get()
|
|
1216
|
-
|
|
1217
|
-
try:
|
|
1218
|
-
update = json_loads(message.body)
|
|
1219
|
-
if self.__is_closing and not isinstance(
|
|
1220
|
-
update, types.UpdateAuthorizationState
|
|
1221
|
-
):
|
|
1222
|
-
await message.nack(requeue=True)
|
|
1223
|
-
continue
|
|
1224
|
-
|
|
1225
|
-
await self.process_update(update)
|
|
1226
|
-
except Exception:
|
|
1227
|
-
self.logger.exception("Error processing message")
|
|
1228
|
-
|
|
1229
|
-
await message.ack() # ack after processing
|
|
1230
|
-
|
|
1231
|
-
async def __handle_rabbitmq_message(self, message: aio_pika.IncomingMessage):
|
|
1232
|
-
await self.process_update(json_loads(message.body))
|
|
1233
|
-
|
|
1234
1199
|
async def __on_update(self, update):
|
|
1235
|
-
|
|
1200
|
+
try:
|
|
1201
|
+
await self.process_update(json_loads(update.data))
|
|
1202
|
+
except Exception:
|
|
1203
|
+
self.logger.exception("Failed to process update")
|
|
1236
1204
|
|
|
1237
1205
|
async def __handle_update_user(self, update: types.UpdateUser):
|
|
1238
1206
|
if self.is_authenticated and self.me and update.user.id == self.me.id:
|
|
@@ -1249,7 +1217,7 @@ class Client(Decorators, Methods):
|
|
|
1249
1217
|
|
|
1250
1218
|
async def __handle_authorization_state_wait_phone_number(self):
|
|
1251
1219
|
if (
|
|
1252
|
-
self.
|
|
1220
|
+
self.is_nats
|
|
1253
1221
|
or self.authorization_state != "authorizationStateWaitPhoneNumber"
|
|
1254
1222
|
or not self.__token
|
|
1255
1223
|
):
|
|
@@ -1265,9 +1233,6 @@ class Client(Decorators, Methods):
|
|
|
1265
1233
|
self.is_authenticated = False
|
|
1266
1234
|
self.is_running = False
|
|
1267
1235
|
|
|
1268
|
-
if self.__rabbitmq_iterator_task:
|
|
1269
|
-
self.__rabbitmq_iterator_task.cancel()
|
|
1270
|
-
|
|
1271
1236
|
if self.__is_queue_worker:
|
|
1272
1237
|
for worker_task in self._workers_tasks:
|
|
1273
1238
|
worker_task.cancel()
|