pyrobale 0.3.5__py3-none-any.whl → 0.3.8__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.
- pyrobale/StateMachine/__init__.py +66 -0
- pyrobale/__init__.py +122 -0
- pyrobale/client/__init__.py +679 -35
- pyrobale/exceptions/__init__.py +1 -0
- pyrobale/exceptions/common.py +6 -0
- pyrobale/filters/__init__.py +2 -0
- pyrobale/filters/enum_filters.py +12 -0
- pyrobale/filters/func_filters.py +17 -0
- pyrobale/objects/__init__.py +3 -0
- pyrobale/objects/callbackquery.py +19 -24
- pyrobale/objects/chatmember.py +5 -4
- pyrobale/objects/enums.py +8 -0
- pyrobale/objects/inlinekeyboardmarkup.py +11 -4
- pyrobale/objects/message.py +33 -16
- pyrobale/objects/user.py +11 -9
- pyrobale/objects/utils.py +23 -3
- pyrobale-0.3.8.dist-info/METADATA +152 -0
- {pyrobale-0.3.5.dist-info → pyrobale-0.3.8.dist-info}/RECORD +20 -16
- {pyrobale-0.3.5.dist-info → pyrobale-0.3.8.dist-info}/licenses/LICENSE +2 -2
- pyrobale-0.3.5.dist-info/METADATA +0 -176
- {pyrobale-0.3.5.dist-info → pyrobale-0.3.8.dist-info}/WHEEL +0 -0
pyrobale/client/__init__.py
CHANGED
@@ -38,16 +38,19 @@ from ..objects.webappdata import WebAppData
|
|
38
38
|
from ..objects.webappinfo import WebAppInfo
|
39
39
|
from ..objects.utils import *
|
40
40
|
import asyncio
|
41
|
-
import aiohttp
|
42
|
-
import bale
|
43
41
|
from enum import Enum
|
44
42
|
from ..objects.enums import UpdatesTypes, ChatAction, ChatType
|
43
|
+
from ..filters import Filters, equals
|
44
|
+
from ..StateMachine import StateMachine
|
45
|
+
from ..exceptions import NotFoundException, InvalidTokenException, PyroBaleException
|
45
46
|
|
46
47
|
|
47
48
|
class Client:
|
48
49
|
"""A client for interacting with the Bale messenger API.
|
49
50
|
|
50
|
-
|
51
|
+
Args:
|
52
|
+
token (str): The bot token.
|
53
|
+
base_url (str, optional): The base URL for the API. Defaults to "https://tapi.bale.ai/bot".
|
51
54
|
"""
|
52
55
|
|
53
56
|
def __init__(self, token: str, base_url: str = "https://tapi.bale.ai/bot"):
|
@@ -56,8 +59,10 @@ class Client:
|
|
56
59
|
self.requests_base = base_url + token
|
57
60
|
|
58
61
|
self.handlers = []
|
62
|
+
self._waiters = []
|
59
63
|
self.running = False
|
60
64
|
self.last_update_id = 0
|
65
|
+
self.state_machine = StateMachine()
|
61
66
|
|
62
67
|
async def get_updates(
|
63
68
|
self,
|
@@ -65,29 +70,70 @@ class Client:
|
|
65
70
|
limit: Optional[int] = None,
|
66
71
|
timeout: Optional[int] = None,
|
67
72
|
) -> List[Dict]:
|
73
|
+
"""Get updates from the Bale API.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
offset (int, optional): The offset of the updates to get. Defaults to None.
|
77
|
+
limit (int, optional): The maximum number of updates to get. Defaults to None.
|
78
|
+
timeout (int, optional): The timeout for the request. Defaults to None.
|
79
|
+
Returns:
|
80
|
+
List[Dict]: The updates.
|
81
|
+
"""
|
68
82
|
data = await make_get(
|
69
83
|
self.requests_base
|
70
84
|
+ f"/getUpdates?offset={offset}&limit={limit}&timeout={timeout}"
|
71
85
|
)
|
72
|
-
|
86
|
+
if data['ok']:
|
87
|
+
if 'result' in data.keys():
|
88
|
+
return data["result"]
|
89
|
+
else:
|
90
|
+
if data['error_code'] == 403:
|
91
|
+
raise InvalidTokenException("Forbidden 403 : --ENTERED TOKEN IS NOT VALID--")
|
73
92
|
|
74
93
|
async def set_webhook(self, url: str) -> bool:
|
94
|
+
"""Set the webhook for the bot.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
url (str): The URL to set the webhook to.
|
98
|
+
Returns:
|
99
|
+
bool: True if the webhook was set successfully, False otherwise.
|
100
|
+
"""
|
75
101
|
data = await make_post(self.requests_base + "/setWebhook", data={"url": url})
|
76
102
|
return data["result"]
|
77
103
|
|
78
104
|
async def get_webhook_info(self) -> Dict:
|
105
|
+
"""Get the webhook information for the bot.
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
Dict: The webhook information.
|
109
|
+
"""
|
79
110
|
data = await make_get(self.requests_base + "/getWebhookInfo")
|
80
111
|
return data["result"]
|
81
112
|
|
82
113
|
async def get_me(self) -> User:
|
114
|
+
"""Get information about the bot.
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
User: The information about the bot.
|
118
|
+
"""
|
83
119
|
data = await make_get(self.requests_base + "/getMe")
|
84
120
|
return User(**data["result"])
|
85
121
|
|
86
122
|
async def logout(self) -> bool:
|
123
|
+
"""Log out the bot.
|
124
|
+
|
125
|
+
Returns:
|
126
|
+
bool: True if the bot was logged out successfully, False otherwise.
|
127
|
+
"""
|
87
128
|
data = await make_get(self.requests_base + "/logOut")
|
88
129
|
return data["result"]
|
89
130
|
|
90
131
|
async def close(self) -> bool:
|
132
|
+
"""Close the bot.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
bool: True if the bot was closed successfully, False otherwise.
|
136
|
+
"""
|
91
137
|
data = await make_get(self.requests_base + "/close")
|
92
138
|
return data["result"]
|
93
139
|
|
@@ -98,6 +144,16 @@ class Client:
|
|
98
144
|
reply_to_message_id: Optional[int] = None,
|
99
145
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
100
146
|
) -> Message:
|
147
|
+
"""Send a message to a chat.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
chat_id (int): The ID of the chat to send the message to.
|
151
|
+
text (str): The text of the message.
|
152
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
153
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
154
|
+
Returns:
|
155
|
+
Message: The message that was sent.
|
156
|
+
"""
|
101
157
|
data = await make_post(
|
102
158
|
self.requests_base + "/sendMessage",
|
103
159
|
data={
|
@@ -112,6 +168,15 @@ class Client:
|
|
112
168
|
async def forward_message(
|
113
169
|
self, chat_id: int, from_chat_id: int, message_id: int
|
114
170
|
) -> Message:
|
171
|
+
"""Forward a message to a chat.
|
172
|
+
|
173
|
+
Args:
|
174
|
+
chat_id (int): The ID of the chat to forward the message to.
|
175
|
+
from_chat_id (int): The ID of the chat to forward the message from.
|
176
|
+
message_id (int): The ID of the message to forward.
|
177
|
+
Returns:
|
178
|
+
Message: The message that was forwarded.
|
179
|
+
"""
|
115
180
|
data = await make_post(
|
116
181
|
self.requests_base + "/forwardMessage",
|
117
182
|
data={
|
@@ -125,6 +190,15 @@ class Client:
|
|
125
190
|
async def copy_message(
|
126
191
|
self, chat_id: int, from_chat_id: int, message_id: int
|
127
192
|
) -> Message:
|
193
|
+
"""Copy a message to a chat.
|
194
|
+
|
195
|
+
Args:
|
196
|
+
chat_id (int): The ID of the chat to copy the message to.
|
197
|
+
from_chat_id (int): The ID of the chat to copy the message from.
|
198
|
+
message_id (int): The ID of the message to copy.
|
199
|
+
Returns:
|
200
|
+
Message: The message that was copied.
|
201
|
+
"""
|
128
202
|
data = await make_post(
|
129
203
|
self.requests_base + "/copyMessage",
|
130
204
|
data={
|
@@ -143,6 +217,17 @@ class Client:
|
|
143
217
|
reply_to_message_id: Optional[int] = None,
|
144
218
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
145
219
|
) -> Message:
|
220
|
+
"""Send a photo to a chat.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
chat_id (Union[int, str]): The ID of the chat to send the photo to.
|
224
|
+
photo (Union[InputFile, str]): The photo to send.
|
225
|
+
caption (str, optional): The caption of the photo. Defaults to None.
|
226
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
227
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
228
|
+
Returns:
|
229
|
+
Message: The message that was sent.
|
230
|
+
"""
|
146
231
|
data = await make_post(
|
147
232
|
self.requests_base + "/sendPhoto",
|
148
233
|
data={
|
@@ -163,6 +248,17 @@ class Client:
|
|
163
248
|
reply_to_message_id: Optional[int] = None,
|
164
249
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
165
250
|
) -> Message:
|
251
|
+
"""Send an audio to a chat.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
chat_id (int): The ID of the chat to send the audio to.
|
255
|
+
audio (Union[InputFile, str]): The audio to send.
|
256
|
+
caption (str, optional): The caption of the audio. Defaults to None.
|
257
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
258
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
259
|
+
Returns:
|
260
|
+
Message: The message that was sent.
|
261
|
+
"""
|
166
262
|
data = await make_post(
|
167
263
|
self.requests_base + "/sendAudio",
|
168
264
|
data={
|
@@ -183,6 +279,17 @@ class Client:
|
|
183
279
|
reply_to_message_id: Optional[int] = None,
|
184
280
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
185
281
|
) -> Message:
|
282
|
+
"""Send a document to a chat.
|
283
|
+
|
284
|
+
Args:
|
285
|
+
chat_id (int): The ID of the chat to send the document to.
|
286
|
+
document (Union[InputFile, str]): The document to send.
|
287
|
+
caption (str, optional): The caption of the document. Defaults to None.
|
288
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
289
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
290
|
+
Returns:
|
291
|
+
Message: The message that was sent.
|
292
|
+
"""
|
186
293
|
data = await make_post(
|
187
294
|
self.requests_base + "/sendDocument",
|
188
295
|
data={
|
@@ -203,6 +310,17 @@ class Client:
|
|
203
310
|
reply_to_message_id: Optional[int] = None,
|
204
311
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
205
312
|
) -> Message:
|
313
|
+
"""Send a video to a chat.
|
314
|
+
|
315
|
+
Args:
|
316
|
+
chat_id (int): The ID of the chat to send the video to.
|
317
|
+
video (Union[InputFile, str]): The video to send.
|
318
|
+
caption (str, optional): The caption of the video. Defaults to None.
|
319
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
320
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
321
|
+
Returns:
|
322
|
+
Message: The message that was sent.
|
323
|
+
"""
|
206
324
|
data = await make_post(
|
207
325
|
self.requests_base + "/sendVideo",
|
208
326
|
data={
|
@@ -223,6 +341,17 @@ class Client:
|
|
223
341
|
reply_to_message_id: Optional[int] = None,
|
224
342
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
225
343
|
) -> Message:
|
344
|
+
"""Send an animation to a chat.
|
345
|
+
|
346
|
+
Args:
|
347
|
+
chat_id (int): The ID of the chat to send the animation to.
|
348
|
+
animation (Union[InputFile, str]): The animation to send.
|
349
|
+
caption (str, optional): The caption of the animation. Defaults to None.
|
350
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
351
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
352
|
+
Returns:
|
353
|
+
Message: The message that was sent.
|
354
|
+
"""
|
226
355
|
data = await make_post(
|
227
356
|
self.requests_base + "/sendAnimation",
|
228
357
|
data={
|
@@ -243,6 +372,17 @@ class Client:
|
|
243
372
|
reply_to_message_id: Optional[int] = None,
|
244
373
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
245
374
|
) -> Message:
|
375
|
+
"""Send a voice message to a chat.
|
376
|
+
|
377
|
+
Args:
|
378
|
+
chat_id (int): The ID of the chat to send the voice message to.
|
379
|
+
voice (Union[InputFile, str]): The voice message to send.
|
380
|
+
caption (str, optional): The caption of the voice message. Defaults to None.
|
381
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
382
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
383
|
+
Returns:
|
384
|
+
Message: The message that was sent.
|
385
|
+
"""
|
246
386
|
data = await make_post(
|
247
387
|
self.requests_base + "/sendVoice",
|
248
388
|
data={
|
@@ -262,6 +402,16 @@ class Client:
|
|
262
402
|
reply_to_message_id: Optional[int] = None,
|
263
403
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
264
404
|
) -> List[Message]:
|
405
|
+
"""Send a media group to a chat.
|
406
|
+
|
407
|
+
Args:
|
408
|
+
chat_id (int): The ID of the chat to send the media group to.
|
409
|
+
media (List[Union[InputMediaPhoto, InputMediaVideo, InputMediaAudio]]): The media group to send.
|
410
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
411
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
412
|
+
Returns:
|
413
|
+
List[Message]: The messages that were sent.
|
414
|
+
"""
|
265
415
|
data = await make_post(
|
266
416
|
self.requests_base + "/sendMediaGroup",
|
267
417
|
data={
|
@@ -278,15 +428,28 @@ class Client:
|
|
278
428
|
chat_id: int,
|
279
429
|
latitude: float,
|
280
430
|
longitude: float,
|
431
|
+
horizontal_accuracy: Optional[float] = None,
|
281
432
|
reply_to_message_id: Optional[int] = None,
|
282
433
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
283
434
|
) -> Message:
|
435
|
+
"""Send a location to a chat.
|
436
|
+
|
437
|
+
Args:
|
438
|
+
chat_id (int): The ID of the chat to send the location to.
|
439
|
+
latitude (float): The latitude of the location.
|
440
|
+
longitude (float): The longitude of the location.
|
441
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
442
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
443
|
+
Returns:
|
444
|
+
Message: The message that was sent.
|
445
|
+
"""
|
284
446
|
data = await make_post(
|
285
447
|
self.requests_base + "/sendLocation",
|
286
448
|
data={
|
287
449
|
"chat_id": chat_id,
|
288
450
|
"latitude": latitude,
|
289
451
|
"longitude": longitude,
|
452
|
+
"horizontal_accuracy": horizontal_accuracy,
|
290
453
|
"reply_to_message_id": reply_to_message_id,
|
291
454
|
"reply_markup": reply_markup.to_dict() if reply_markup else None,
|
292
455
|
},
|
@@ -302,6 +465,18 @@ class Client:
|
|
302
465
|
reply_to_message_id: Optional[int] = None,
|
303
466
|
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
|
304
467
|
) -> Message:
|
468
|
+
"""Send a contact to a chat.
|
469
|
+
|
470
|
+
Args:
|
471
|
+
chat_id (int): The ID of the chat to send the contact to.
|
472
|
+
phone_number (str): The phone number of the contact.
|
473
|
+
first_name (str): The first name of the contact.
|
474
|
+
last_name (str, optional): The last name of the contact. Defaults to None.
|
475
|
+
reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
|
476
|
+
reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
|
477
|
+
Returns:
|
478
|
+
Message: The message that was sent.
|
479
|
+
"""
|
305
480
|
data = await make_post(
|
306
481
|
self.requests_base + "/sendContact",
|
307
482
|
data={
|
@@ -315,7 +490,58 @@ class Client:
|
|
315
490
|
)
|
316
491
|
return Message(**pythonize(data["result"]))
|
317
492
|
|
493
|
+
async def send_invoice(
|
494
|
+
self,
|
495
|
+
chat_id: Union[str, int],
|
496
|
+
title: str,
|
497
|
+
description: str,
|
498
|
+
payload: str,
|
499
|
+
provider_token: str,
|
500
|
+
prices: list[LabeledPrice],
|
501
|
+
photo_url: Optional[str] = None,
|
502
|
+
reply_to_message_id: Optional[int] = None,
|
503
|
+
) -> Message:
|
504
|
+
"""Sends a message including a invoice for user to pay.
|
505
|
+
|
506
|
+
Args:
|
507
|
+
chat_id (string OR integer): unique chat id to send the invoice
|
508
|
+
title (string): the title of invoice
|
509
|
+
description (string): desciption of invoice, you can explain the invoice here
|
510
|
+
payload (string): payload of invoice, user will not see this, it'll be returned after successful payment
|
511
|
+
provider_token (string): Wallet token or card number of receiver
|
512
|
+
prices (list of LabledPrice): a list of prices that user must pay
|
513
|
+
photo_url (Optional: string): url of a photo that will be sent with invoice
|
514
|
+
reply_to_message_id (Optional: int): message id to reply that
|
515
|
+
|
516
|
+
Returns:
|
517
|
+
Message: returns the sent message with invoice
|
518
|
+
"""
|
519
|
+
new_prices = []
|
520
|
+
for price in prices:
|
521
|
+
new_prices.append(price.json)
|
522
|
+
data = await make_post(
|
523
|
+
self.requests_base + "/sendInvoice",
|
524
|
+
data={
|
525
|
+
"chat_id": chat_id,
|
526
|
+
"title": title,
|
527
|
+
"description": description,
|
528
|
+
"payload": payload,
|
529
|
+
"provider_token": provider_token,
|
530
|
+
"prices": new_prices,
|
531
|
+
"photo_url": photo_url,
|
532
|
+
"reply_to_message_id": reply_to_message_id,
|
533
|
+
},
|
534
|
+
)
|
535
|
+
return Message(**pythonize(data["result"]))
|
536
|
+
|
318
537
|
async def get_file(self, file_id: str) -> File:
|
538
|
+
"""Get a file from the Bale servers.
|
539
|
+
|
540
|
+
Args:
|
541
|
+
file_id (str): The ID of the file to get.
|
542
|
+
Returns:
|
543
|
+
File: The file that was retrieved.
|
544
|
+
"""
|
319
545
|
data = await make_post(
|
320
546
|
self.requests_base + "/getFile", data={"file_id": file_id}
|
321
547
|
)
|
@@ -327,6 +553,15 @@ class Client:
|
|
327
553
|
text: Optional[str] = None,
|
328
554
|
show_alert: Optional[bool] = None,
|
329
555
|
):
|
556
|
+
"""Answer a callback query.
|
557
|
+
|
558
|
+
Args:
|
559
|
+
callback_query_id (str): The ID of the callback query to answer.
|
560
|
+
text (str, optional): The text to show to the user. Defaults to None.
|
561
|
+
show_alert (bool, optional): Whether to show an alert to the user. Defaults to None.
|
562
|
+
Returns:
|
563
|
+
bool: Whether the callback query was answered successfully.
|
564
|
+
"""
|
330
565
|
data = await make_post(
|
331
566
|
self.requests_base + "/answerCallbackQuery",
|
332
567
|
data={
|
@@ -338,6 +573,15 @@ class Client:
|
|
338
573
|
return data.get("ok", False)
|
339
574
|
|
340
575
|
async def ban_chat_member(self, chat_id: int, user_id: int) -> bool:
|
576
|
+
"""Ban a user from a chat.
|
577
|
+
|
578
|
+
Args:
|
579
|
+
chat_id (int): The ID of the chat to ban the user from.
|
580
|
+
user_id (int): The ID of the user to ban.
|
581
|
+
Returns:
|
582
|
+
bool: Whether the user was banned successfully
|
583
|
+
"""
|
584
|
+
|
341
585
|
data = await make_post(
|
342
586
|
self.requests_base + "/banChatMember",
|
343
587
|
data={"chat_id": chat_id, "user_id": user_id},
|
@@ -345,6 +589,14 @@ class Client:
|
|
345
589
|
return data.get("ok", False)
|
346
590
|
|
347
591
|
async def unban_chat_member(self, chat_id: int, user_id: int) -> bool:
|
592
|
+
"""Unban a user from a chat.
|
593
|
+
|
594
|
+
Args:
|
595
|
+
chat_id (int): The ID of the chat to unban the user from.
|
596
|
+
user_id (int): The ID of the user to unban.
|
597
|
+
Returns:
|
598
|
+
bool: Whether the user was unbanned successfully.
|
599
|
+
"""
|
348
600
|
data = await make_post(
|
349
601
|
self.requests_base + "/unbanChatMember",
|
350
602
|
data={"chat_id": chat_id, "user_id": user_id},
|
@@ -352,6 +604,14 @@ class Client:
|
|
352
604
|
return data.get("ok", False)
|
353
605
|
|
354
606
|
async def get_chat_member(self, chat_id: int, user_id: int) -> ChatMember:
|
607
|
+
"""Get a chat member.
|
608
|
+
|
609
|
+
Args:
|
610
|
+
chat_id (int): The ID of the chat to get the member from.
|
611
|
+
user_id (int): The ID of the user to get.
|
612
|
+
Returns:
|
613
|
+
ChatMember: The chat member that was retrieved.
|
614
|
+
"""
|
355
615
|
data = await make_post(
|
356
616
|
self.requests_base + "/getChatMember",
|
357
617
|
data={"chat_id": chat_id, "user_id": user_id},
|
@@ -373,6 +633,22 @@ class Client:
|
|
373
633
|
can_pin_messages: Optional[bool] = None,
|
374
634
|
can_promote_members: Optional[bool] = None,
|
375
635
|
):
|
636
|
+
"""Promote a user in a chat.
|
637
|
+
|
638
|
+
Args:
|
639
|
+
chat_id (int): The ID of the chat to promote the user in.
|
640
|
+
user_id (int): The ID of the user to promote.
|
641
|
+
can_change_info (bool, optional): Whether the user can change the chat info. Defaults to None.
|
642
|
+
can_post_messages (bool, optional): Whether the user can post messages. Defaults to None.
|
643
|
+
can_edit_messages (bool, optional): Whether the user can edit messages. Defaults to None.
|
644
|
+
can_delete_messages (bool, optional): Whether the user can delete messages. Defaults to None.
|
645
|
+
can_invite_users (bool, optional): Whether the user can invite users. Defaults to None.
|
646
|
+
can_restrict_members (bool, optional): Whether the user can restrict members. Defaults to None.
|
647
|
+
can_pin_messages (bool, optional): Whether the user can pin messages. Defaults to None.
|
648
|
+
can_promote_members (bool, optional): Whether the user can promote members. Defaults to None.
|
649
|
+
Returns:
|
650
|
+
bool: Whether the user was promoted successfully.
|
651
|
+
"""
|
376
652
|
data = await make_post(
|
377
653
|
self.requests_base + "/promoteChatMember",
|
378
654
|
data={
|
@@ -391,6 +667,15 @@ class Client:
|
|
391
667
|
return data.get("ok", False)
|
392
668
|
|
393
669
|
async def set_chat_photo(self, chat_id: int, photo: InputFile) -> bool:
|
670
|
+
"""Set a new profile photo for the chat.
|
671
|
+
|
672
|
+
Args:
|
673
|
+
chat_id (int): Unique identifier for the target chat
|
674
|
+
photo (InputFile): New chat photo
|
675
|
+
|
676
|
+
Returns:
|
677
|
+
bool: True on success
|
678
|
+
"""
|
394
679
|
data = await make_post(
|
395
680
|
self.requests_base + "/setChatPhoto",
|
396
681
|
data={"chat_id": chat_id, "photo": photo},
|
@@ -398,24 +683,57 @@ class Client:
|
|
398
683
|
return data.get("ok", False)
|
399
684
|
|
400
685
|
async def leave_chat(self, chat_id: int) -> bool:
|
686
|
+
"""Leave a group, supergroup or channel.
|
687
|
+
|
688
|
+
Args:
|
689
|
+
chat_id (int): Unique identifier for the target chat
|
690
|
+
|
691
|
+
Returns:
|
692
|
+
bool: True on success
|
693
|
+
"""
|
401
694
|
data = await make_post(
|
402
695
|
self.requests_base + "/leaveChat", data={"chat_id": chat_id}
|
403
696
|
)
|
404
697
|
return data.get("ok", False)
|
405
698
|
|
406
699
|
async def get_chat(self, chat_id: int) -> Chat:
|
700
|
+
"""Get up to date information about the chat.
|
701
|
+
|
702
|
+
Args:
|
703
|
+
chat_id (int): Unique identifier for the target chat
|
704
|
+
|
705
|
+
Returns:
|
706
|
+
Chat: Chat object with information about the chat
|
707
|
+
"""
|
407
708
|
data = await make_post(
|
408
709
|
self.requests_base + "/getChat", data={"chat_id": chat_id}
|
409
710
|
)
|
410
711
|
return Chat(**pythonize(data["result"]))
|
411
712
|
|
412
713
|
async def get_chat_members_count(self, chat_id: int) -> int:
|
714
|
+
"""Get the number of members in a chat.
|
715
|
+
|
716
|
+
Args:
|
717
|
+
chat_id (int): Unique identifier for the target chat
|
718
|
+
|
719
|
+
Returns:
|
720
|
+
int: Number of members in the chat
|
721
|
+
"""
|
413
722
|
data = await make_post(
|
414
723
|
self.requests_base + "/getChatMembersCount", data={"chat_id": chat_id}
|
415
724
|
)
|
416
725
|
return data.get("result", 0)
|
417
726
|
|
418
727
|
async def pin_chat_message(self, chat_id: int, message_id: int) -> bool:
|
728
|
+
"""Pin a message in a chat.
|
729
|
+
|
730
|
+
Args:
|
731
|
+
chat_id (int): Unique identifier for the target chat
|
732
|
+
message_id (int): Identifier of a message to pin
|
733
|
+
|
734
|
+
Returns:
|
735
|
+
bool: True on success
|
736
|
+
"""
|
419
737
|
data = await make_post(
|
420
738
|
self.requests_base + "/pinChatMessage",
|
421
739
|
data={"chat_id": chat_id, "message_id": message_id},
|
@@ -423,18 +741,43 @@ class Client:
|
|
423
741
|
return data.get("ok", False)
|
424
742
|
|
425
743
|
async def unpin_chat_message(self, chat_id: int) -> bool:
|
744
|
+
"""Unpin a message in a chat.
|
745
|
+
|
746
|
+
Args:
|
747
|
+
chat_id (int): Unique identifier for the target chat
|
748
|
+
|
749
|
+
Returns:
|
750
|
+
bool: True on success
|
751
|
+
"""
|
426
752
|
data = await make_post(
|
427
753
|
self.requests_base + "/unpinChatMessage", data={"chat_id": chat_id}
|
428
754
|
)
|
429
755
|
return data.get("ok", False)
|
430
756
|
|
431
757
|
async def unpin_all_chat_messages(self, chat_id: int) -> bool:
|
758
|
+
"""Unpin all messages in a chat.
|
759
|
+
|
760
|
+
Args:
|
761
|
+
chat_id (int): Unique identifier for the target chat
|
762
|
+
|
763
|
+
Returns:
|
764
|
+
bool: True on success
|
765
|
+
"""
|
432
766
|
data = await make_post(
|
433
767
|
self.requests_base + "/unpinAllChatMessages", data={"chat_id": chat_id}
|
434
768
|
)
|
435
769
|
return data.get("ok", False)
|
436
770
|
|
437
771
|
async def set_chat_title(self, chat_id: int, title: str) -> bool:
|
772
|
+
"""Change the title of a chat.
|
773
|
+
|
774
|
+
Args:
|
775
|
+
chat_id (int): Unique identifier for the target chat
|
776
|
+
title (str): New chat title, 1-255 characters
|
777
|
+
|
778
|
+
Returns:
|
779
|
+
bool: True on success
|
780
|
+
"""
|
438
781
|
data = await make_post(
|
439
782
|
self.requests_base + "/setChatTitle",
|
440
783
|
data={"chat_id": chat_id, "title": title},
|
@@ -442,6 +785,15 @@ class Client:
|
|
442
785
|
return data.get("ok", False)
|
443
786
|
|
444
787
|
async def set_chat_description(self, chat_id: int, description: str) -> bool:
|
788
|
+
"""Change the description of a chat.
|
789
|
+
|
790
|
+
Args:
|
791
|
+
chat_id (int): Unique identifier for the target chat
|
792
|
+
description (str): New chat description, 0-255 characters
|
793
|
+
|
794
|
+
Returns:
|
795
|
+
bool: True on success
|
796
|
+
"""
|
445
797
|
data = await make_post(
|
446
798
|
self.requests_base + "/setChatDescription",
|
447
799
|
data={"chat_id": chat_id, "description": description},
|
@@ -449,18 +801,73 @@ class Client:
|
|
449
801
|
return data.get("ok", False)
|
450
802
|
|
451
803
|
async def delete_chat_photo(self, chat_id: int) -> bool:
|
804
|
+
"""Delete a chat photo.
|
805
|
+
|
806
|
+
Args:
|
807
|
+
chat_id (int): Unique identifier for the target chat
|
808
|
+
|
809
|
+
Returns:
|
810
|
+
bool: True on success
|
811
|
+
"""
|
452
812
|
data = await make_post(
|
453
813
|
self.requests_base + "/deleteChatPhoto", data={"chat_id": chat_id}
|
454
814
|
)
|
455
815
|
return data.get("ok", False)
|
456
816
|
|
817
|
+
async def edit_message(
|
818
|
+
self,
|
819
|
+
chat_id: Union[int, str],
|
820
|
+
message_id: int,
|
821
|
+
text: str,
|
822
|
+
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
823
|
+
) -> Message:
|
824
|
+
"""Edits a message in a specified chat
|
825
|
+
|
826
|
+
Args:
|
827
|
+
chat_id (int OR str): Unique identifier for the target chat
|
828
|
+
message_id (int): Unique indentifier for the message you want to edit
|
829
|
+
text (str): New text of message
|
830
|
+
reply_markup (InlineKeyboardMarkup): Inline markup you can add or change in message
|
831
|
+
|
832
|
+
Returns:
|
833
|
+
Message: The object of edited message
|
834
|
+
"""
|
835
|
+
|
836
|
+
data = await make_post(
|
837
|
+
self.requests_base + "/editMessageText",
|
838
|
+
data={
|
839
|
+
"chat_id": chat_id,
|
840
|
+
"message_id": message_id,
|
841
|
+
"text": text,
|
842
|
+
"reply_markup": reply_markup.to_dict() if reply_markup else None,
|
843
|
+
},
|
844
|
+
)
|
845
|
+
return Message(**pythonize(data["result"]))
|
846
|
+
|
457
847
|
async def create_chat_invite_link(self, chat_id: int) -> str:
|
848
|
+
"""Create an additional invite link for a chat.
|
849
|
+
|
850
|
+
Args:
|
851
|
+
chat_id (int): Unique identifier for the target chat
|
852
|
+
|
853
|
+
Returns:
|
854
|
+
str: The new invite link
|
855
|
+
"""
|
458
856
|
data = await make_post(
|
459
857
|
self.requests_base + "/createChatInviteLink", data={"chat_id": chat_id}
|
460
858
|
)
|
461
859
|
return data.get("result", "")
|
462
860
|
|
463
861
|
async def revoke_chat_invite_link(self, chat_id: int, invite_link: str) -> str:
|
862
|
+
"""Revoke an invite link created by the bot.
|
863
|
+
|
864
|
+
Args:
|
865
|
+
chat_id (int): Unique identifier for the target chat
|
866
|
+
invite_link (str): The invite link to revoke
|
867
|
+
|
868
|
+
Returns:
|
869
|
+
str: The revoked invite link
|
870
|
+
"""
|
464
871
|
data = await make_post(
|
465
872
|
self.requests_base + "/revokeChatInviteLink",
|
466
873
|
data={"chat_id": chat_id, "invite_link": invite_link},
|
@@ -468,66 +875,287 @@ class Client:
|
|
468
875
|
return data.get("result", "")
|
469
876
|
|
470
877
|
async def export_chat_invite_link(self, chat_id: int) -> str:
|
878
|
+
"""Generate a new primary invite link for a chat.
|
879
|
+
|
880
|
+
Args:
|
881
|
+
chat_id (int): Unique identifier for the target chat
|
882
|
+
|
883
|
+
Returns:
|
884
|
+
str: The new invite link
|
885
|
+
"""
|
471
886
|
data = await make_post(
|
472
887
|
self.requests_base + "/exportChatInviteLink", data={"chat_id": chat_id}
|
473
888
|
)
|
474
889
|
return data.get("result", "")
|
475
890
|
|
476
891
|
async def send_chat_action(self, chat_id: int, action: ChatAction) -> bool:
|
892
|
+
"""Tell the user that something is happening on the bot's side.
|
893
|
+
|
894
|
+
Args:
|
895
|
+
chat_id (int): Unique identifier for the target chat
|
896
|
+
action (ChatAction): Type of action to broadcast
|
897
|
+
|
898
|
+
Returns:
|
899
|
+
bool: True on success
|
900
|
+
"""
|
477
901
|
data = await make_post(
|
478
902
|
self.requests_base + "/sendChatAction",
|
479
903
|
data={"chat_id": str(chat_id), "action": action.value},
|
480
904
|
)
|
481
905
|
return data.get("ok", False)
|
482
906
|
|
907
|
+
async def wait_for(self, update_type: UpdatesTypes, check=None):
|
908
|
+
"""Wait until a specified update
|
909
|
+
|
910
|
+
Args:
|
911
|
+
update_type (UpdatesTypes): The type of update you're waiting for it.
|
912
|
+
check: a condition that will be checked before passing.
|
913
|
+
"""
|
914
|
+
future = asyncio.get_running_loop().create_future()
|
915
|
+
self._waiters.append((update_type, check, future))
|
916
|
+
return await future
|
917
|
+
|
483
918
|
async def process_update(self, update: Dict[str, Any]) -> None:
|
484
|
-
"""Process a single update and call registered handlers.
|
919
|
+
"""Process a single update and call registered handlers.
|
920
|
+
|
921
|
+
Args:
|
922
|
+
update (Dict[str, Any]): The update to process
|
923
|
+
"""
|
485
924
|
update_id = update.get("update_id")
|
486
925
|
if update_id:
|
487
926
|
self.last_update_id = update_id + 1
|
488
927
|
|
928
|
+
for waiter in list(self._waiters):
|
929
|
+
w_type, check, future = waiter
|
930
|
+
if w_type.value in update:
|
931
|
+
event = update[w_type.value]
|
932
|
+
event = self._convert_event(w_type, event)
|
933
|
+
if check is None or check(event):
|
934
|
+
if not future.done():
|
935
|
+
future.set_result(event)
|
936
|
+
self._waiters.remove(waiter)
|
937
|
+
return
|
938
|
+
|
489
939
|
for handler in self.handlers:
|
490
940
|
update_type = handler["type"].value
|
491
941
|
if update_type in update:
|
492
|
-
|
493
|
-
|
494
|
-
|
942
|
+
raw_event = update[update_type]
|
943
|
+
event = self._convert_event(handler["type"], raw_event)
|
944
|
+
|
945
|
+
if handler["type"] == UpdatesTypes.COMMAND:
|
946
|
+
if hasattr(event, 'text') and event.text and event.text.startswith('/'):
|
947
|
+
command_text = event.text[1:]
|
948
|
+
command_parts = command_text.split()
|
949
|
+
if command_parts:
|
950
|
+
actual_command = command_parts[0]
|
951
|
+
expected_command = handler.get("command", "")
|
952
|
+
|
953
|
+
if actual_command != expected_command:
|
954
|
+
continue
|
495
955
|
|
956
|
+
flt = handler.get("filter")
|
957
|
+
if flt is not None:
|
958
|
+
if callable(flt):
|
959
|
+
try:
|
960
|
+
if not flt(event):
|
961
|
+
continue
|
962
|
+
except Exception as e:
|
963
|
+
print(f"[Filter Error] {e}")
|
964
|
+
continue
|
965
|
+
elif isinstance(flt, Filters):
|
966
|
+
if not hasattr(event, flt.value):
|
967
|
+
continue
|
968
|
+
|
496
969
|
if asyncio.iscoroutinefunction(handler["callback"]):
|
497
970
|
asyncio.create_task(handler["callback"](event))
|
498
971
|
else:
|
499
972
|
handler["callback"](event)
|
500
973
|
|
974
|
+
|
975
|
+
|
976
|
+
def base_handler_decorator(self, update_type: UpdatesTypes):
|
977
|
+
"""Base decorator for handling different types of updates.
|
978
|
+
|
979
|
+
Args:
|
980
|
+
update_type (UpdatesTypes): The type of update to handle.
|
981
|
+
|
982
|
+
Returns:
|
983
|
+
Callable: A decorator function that registers the callback for the specified update type.
|
984
|
+
"""
|
985
|
+
def wrapper(filter: Optional[Filters] = None):
|
986
|
+
def decorator(callback: Callable[[Any], Union[None, Awaitable[None]]]):
|
987
|
+
self.add_handler(update_type, callback, filter)
|
988
|
+
return callback
|
989
|
+
return decorator
|
990
|
+
return wrapper
|
991
|
+
|
992
|
+
def on_command(self, command: str, filter: Optional[Filters] = None):
|
993
|
+
"""Decorator for handling command updates.
|
994
|
+
|
995
|
+
Args:
|
996
|
+
command (str): The command to handle.
|
997
|
+
filter (Optional[Filters]): An optional filter to apply to the command.
|
998
|
+
Returns:
|
999
|
+
Callable: A decorator function that registers the callback for the specified command.
|
1000
|
+
"""
|
1001
|
+
def decorator(callback: Callable[[Any], Union[None, Awaitable[None]]]):
|
1002
|
+
self.add_handler(UpdatesTypes.COMMAND, callback, filter, command=command)
|
1003
|
+
return callback
|
1004
|
+
return decorator
|
1005
|
+
|
1006
|
+
|
1007
|
+
def on_message(self, filter: Optional[Filters] = None):
|
1008
|
+
"""Decorator for handling new message updates.
|
1009
|
+
|
1010
|
+
Returns:
|
1011
|
+
Callable: A decorator function that registers the callback for message updates.
|
1012
|
+
"""
|
1013
|
+
return self.base_handler_decorator(UpdatesTypes.MESSAGE)(filter)
|
1014
|
+
|
1015
|
+
|
1016
|
+
def on_edited_message(self):
|
1017
|
+
"""Decorator for handling edited message updates.
|
1018
|
+
|
1019
|
+
Returns:
|
1020
|
+
Callable: A decorator function that registers the callback for edited message updates.
|
1021
|
+
"""
|
1022
|
+
return self.base_handler_decorator(UpdatesTypes.MESSAGE_EDITED)
|
1023
|
+
|
1024
|
+
def on_callback_query(self):
|
1025
|
+
"""Decorator for handling callback query updates.
|
1026
|
+
|
1027
|
+
Returns:
|
1028
|
+
Callable: A decorator function that registers the callback for callback query updates.
|
1029
|
+
"""
|
1030
|
+
return self.base_handler_decorator(UpdatesTypes.CALLBACK_QUERY)
|
1031
|
+
|
1032
|
+
def on_new_members(self):
|
1033
|
+
"""Decorator for handling new chat members updates.
|
1034
|
+
|
1035
|
+
Returns:
|
1036
|
+
Callable: A decorator function that registers the callback for new members updates.
|
1037
|
+
"""
|
1038
|
+
return self.base_handler_decorator(UpdatesTypes.MEMBER_JOINED)
|
1039
|
+
|
1040
|
+
def on_members_left(self):
|
1041
|
+
return self.base_handler_decorator(UpdatesTypes.MEMBER_LEFT)
|
1042
|
+
|
1043
|
+
def on_pre_checkout_query(self):
|
1044
|
+
"""Decorator for handling pre-checkout query updates.
|
1045
|
+
|
1046
|
+
Returns:
|
1047
|
+
Callable: A decorator function that registers the callback for pre-checkout query updates.
|
1048
|
+
"""
|
1049
|
+
return self.base_handler_decorator(UpdatesTypes.PRE_CHECKOUT_QUERY)
|
1050
|
+
|
1051
|
+
def on_photo(self):
|
1052
|
+
"""Decorator for handling photo updates.
|
1053
|
+
|
1054
|
+
Returns:
|
1055
|
+
Callable: A decorator function that registers the callback for photo updates.
|
1056
|
+
"""
|
1057
|
+
return self.base_handler_decorator(UpdatesTypes.PHOTO)
|
1058
|
+
|
1059
|
+
def on_successful_payment(self):
|
1060
|
+
"""Decorator for handling successful payment updates.
|
1061
|
+
|
1062
|
+
Returns:
|
1063
|
+
Callable: A decorator function that registers the callback for successful payment updates.
|
1064
|
+
"""
|
1065
|
+
return self.base_handler_decorator(UpdatesTypes.SUCCESSFUL_PAYMENT)
|
1066
|
+
|
501
1067
|
def _convert_event(self, handler_type: UpdatesTypes, event: Dict[str, Any]) -> Any:
|
502
|
-
"""Convert raw event data to appropriate object type.
|
503
|
-
|
504
|
-
|
1068
|
+
"""Convert raw event data to appropriate object type.
|
1069
|
+
|
1070
|
+
Args:
|
1071
|
+
handler_type (UpdatesTypes): Type of the update
|
1072
|
+
event (Dict[str, Any]): Raw event data
|
1073
|
+
|
1074
|
+
Returns:
|
1075
|
+
Any: Converted event object
|
1076
|
+
"""
|
1077
|
+
if handler_type in (
|
1078
|
+
UpdatesTypes.MESSAGE,
|
1079
|
+
UpdatesTypes.MESSAGE_EDITED,
|
1080
|
+
UpdatesTypes.MEMBER_JOINED,
|
1081
|
+
UpdatesTypes.MEMBER_LEFT,
|
1082
|
+
UpdatesTypes.SUCCESSFUL_PAYMENT,
|
1083
|
+
UpdatesTypes.COMMAND
|
1084
|
+
):
|
1085
|
+
if (
|
1086
|
+
event.get("new_chat_member", False)
|
1087
|
+
and handler_type == UpdatesTypes.MEMBER_JOINED
|
1088
|
+
):
|
1089
|
+
return (
|
1090
|
+
ChatMember(
|
1091
|
+
kwargs={"client": self},
|
1092
|
+
**pythonize(event.get("new_chat_member", {})),
|
1093
|
+
),
|
1094
|
+
Chat(kwargs={"client": self}, **pythonize(event.get("chat", {}))),
|
1095
|
+
Message(
|
1096
|
+
kwargs={"client": self}, **pythonize(event.get("message", {}))
|
1097
|
+
),
|
1098
|
+
)
|
1099
|
+
elif (
|
1100
|
+
event.get("left_chat_member", False)
|
1101
|
+
and handler_type == UpdatesTypes.MEMBER_LEFT
|
1102
|
+
):
|
1103
|
+
return (
|
1104
|
+
ChatMember(
|
1105
|
+
kwargs={"client": self},
|
1106
|
+
**pythonize(event.get("left_chat_member", {})),
|
1107
|
+
),
|
1108
|
+
Chat(kwargs={"client": self}, **pythonize(event.get("chat", {}))),
|
1109
|
+
Message(
|
1110
|
+
kwargs={"client": self}, **pythonize(event.get("message", {}))
|
1111
|
+
),
|
1112
|
+
)
|
1113
|
+
|
1114
|
+
elif (
|
1115
|
+
event.get("successful_payment", False)
|
1116
|
+
and handler_type == UpdatesTypes.SUCCESSFUL_PAYMENT
|
1117
|
+
):
|
1118
|
+
return Message(
|
1119
|
+
**pythonize(event.get("successful_payment", {})),
|
1120
|
+
kwargs={"client": self},
|
1121
|
+
)
|
1122
|
+
|
1123
|
+
else:
|
1124
|
+
return Message(kwargs={"client": self}, **pythonize(event))
|
1125
|
+
|
505
1126
|
elif handler_type == UpdatesTypes.CALLBACK_QUERY:
|
506
1127
|
return CallbackQuery(kwargs={"client": self}, **pythonize(event))
|
1128
|
+
|
507
1129
|
elif handler_type == UpdatesTypes.PRE_CHECKOUT_QUERY:
|
508
1130
|
return PreCheckoutQuery(kwargs={"client": self}, **pythonize(event))
|
509
|
-
|
510
|
-
return ChatMember(
|
511
|
-
kwargs={"client": self}, **pythonize(event.get("new_chat_member", {}))
|
512
|
-
)
|
513
|
-
elif handler_type == UpdatesTypes.MEMBER_LEFT:
|
514
|
-
return ChatMember(
|
515
|
-
kwargs={"client": self}, **pythonize(event.get("left_chat_member", {}))
|
516
|
-
)
|
1131
|
+
|
517
1132
|
return event
|
518
1133
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
1134
|
+
|
1135
|
+
def add_handler(self, update_type, callback, filter: Optional[Filters] = None, **kwargs):
|
1136
|
+
"""Register a handler for specific update type.
|
1137
|
+
|
1138
|
+
Args:
|
1139
|
+
update_type (UpdatesTypes): Type of update to handle
|
1140
|
+
callback (Callable): Function to call when update is received
|
1141
|
+
"""
|
1142
|
+
data = {
|
1143
|
+
"type": update_type,
|
1144
|
+
"callback": callback,
|
1145
|
+
"filter": filter,
|
1146
|
+
}
|
1147
|
+
data.update(kwargs)
|
1148
|
+
self.handlers.append(data)
|
1149
|
+
|
526
1150
|
|
527
1151
|
def remove_handler(
|
528
1152
|
self, callback: Callable[[Any], Union[None, Awaitable[None]]]
|
529
1153
|
) -> None:
|
530
|
-
"""Remove a handler from the list of handlers.
|
1154
|
+
"""Remove a handler from the list of handlers.
|
1155
|
+
|
1156
|
+
Args:
|
1157
|
+
callback (Callable): Handler function to remove
|
1158
|
+
"""
|
531
1159
|
self.handlers = [
|
532
1160
|
handler for handler in self.handlers if handler["callback"] != callback
|
533
1161
|
]
|
@@ -537,30 +1165,42 @@ class Client:
|
|
537
1165
|
self.handlers = []
|
538
1166
|
|
539
1167
|
async def start_polling(self, timeout: int = 30, limit: int = 100) -> None:
|
540
|
-
"""Start polling updates from the server.
|
1168
|
+
"""Start polling updates from the server.
|
1169
|
+
|
1170
|
+
Args:
|
1171
|
+
timeout (int, optional): Timeout in seconds for long polling. Defaults to 30.
|
1172
|
+
limit (int, optional): Maximum number of updates to retrieve. Defaults to 100.
|
1173
|
+
|
1174
|
+
Raises:
|
1175
|
+
RuntimeError: If client is already running
|
1176
|
+
"""
|
541
1177
|
if self.running:
|
542
1178
|
raise RuntimeError("Client is already running")
|
543
1179
|
|
544
1180
|
self.running = True
|
545
1181
|
while self.running:
|
546
|
-
|
1182
|
+
try:
|
547
1183
|
updates = await self.get_updates(
|
548
1184
|
offset=self.last_update_id, limit=limit, timeout=timeout
|
549
1185
|
)
|
550
1186
|
|
551
1187
|
for update in updates:
|
552
1188
|
await self.process_update(update)
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
await asyncio.sleep(5)
|
1189
|
+
|
1190
|
+
except Exception as e:
|
1191
|
+
raise e
|
557
1192
|
|
558
1193
|
async def stop_polling(self) -> None:
|
559
1194
|
"""Stop polling updates."""
|
560
1195
|
self.running = False
|
561
1196
|
|
562
1197
|
def run(self, timeout: int = 30, limit: int = 100) -> None:
|
563
|
-
"""Run the client.
|
1198
|
+
"""Run the client.
|
1199
|
+
|
1200
|
+
Args:
|
1201
|
+
timeout (int, optional): Timeout in seconds for long polling. Defaults to 30.
|
1202
|
+
limit (int, optional): Maximum number of updates to retrieve. Defaults to 100.
|
1203
|
+
"""
|
564
1204
|
loop = asyncio.get_event_loop()
|
565
1205
|
loop.run_until_complete(self.start_polling(timeout, limit))
|
566
1206
|
|
@@ -570,5 +1210,9 @@ class Client:
|
|
570
1210
|
loop.run_until_complete(self.stop_polling())
|
571
1211
|
|
572
1212
|
async def handle_webhook_update(self, update_data: Dict[str, Any]) -> None:
|
573
|
-
"""Process an update received via webhook.
|
1213
|
+
"""Process an update received via webhook.
|
1214
|
+
|
1215
|
+
Args:
|
1216
|
+
update_data (Dict[str, Any]): Update data received from webhook
|
1217
|
+
"""
|
574
1218
|
await self.process_update(update_data)
|