pyrobale 0.3.9.1__py3-none-any.whl → 0.4.0__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.
@@ -1,4 +1,4 @@
1
- from typing import Optional, Union, List, Dict, Any, Callable, Awaitable, TypeVar
1
+ from typing import Optional, Union, List, Dict, Any, Callable, Awaitable
2
2
  from ..objects.animation import Animation
3
3
  from ..objects.audio import Audio
4
4
  from ..objects.callbackquery import CallbackQuery
@@ -39,7 +39,7 @@ from ..objects.webappinfo import WebAppInfo
39
39
  from ..objects.utils import *
40
40
  from ..objects.enums import UpdatesTypes, ChatAction, ChatType
41
41
  from ..objects.peerdata import PeerData
42
- from ..filters import Filters, equals
42
+ from ..filters import Filters
43
43
  from ..StateMachine import StateMachine
44
44
  from ..exceptions import NotFoundException, InvalidTokenException, PyroBaleException
45
45
 
@@ -47,6 +47,8 @@ from enum import Enum
47
47
  import asyncio
48
48
  from bs4 import BeautifulSoup
49
49
  from json import loads, JSONDecodeError
50
+ import aiohttp
51
+
50
52
 
51
53
  class Client:
52
54
  """A client for interacting with the Bale messenger API.
@@ -71,95 +73,56 @@ class Client:
71
73
  self.defined_messages = {}
72
74
 
73
75
  async def get_updates(
74
- self,
75
- offset: Optional[int] = None,
76
- limit: Optional[int] = None,
77
- timeout: Optional[int] = None,
76
+ self,
77
+ offset: Optional[int] = None,
78
+ limit: Optional[int] = None,
79
+ timeout: Optional[int] = None,
78
80
  ) -> List[Dict]:
79
- """Get updates from the Bale API.
80
-
81
- Args:
82
- offset (int, optional): The offset of the updates to get. Defaults to None.
83
- limit (int, optional): The maximum number of updates to get. Defaults to None.
84
- timeout (int, optional): The timeout for the request. Defaults to None.
85
- Returns:
86
- List[Dict]: The updates.
87
- """
81
+ """Get updates from the Bale API."""
88
82
  data = await make_get(
89
- self.requests_base
90
- + f"/getUpdates?offset={offset}&limit={limit}&timeout={timeout}"
83
+ self.requests_base + f"/getUpdates?offset={offset}&limit={limit}&timeout={timeout}"
91
84
  )
92
85
  if data['ok']:
93
- if 'result' in data.keys():
86
+ if 'result' in data:
94
87
  return data["result"]
95
88
  else:
96
- if data['error_code'] == 403:
89
+ if data.get('error_code') == 403:
97
90
  raise InvalidTokenException("Forbidden 403 : --ENTERED TOKEN IS NOT VALID--")
91
+ return []
98
92
 
99
93
  async def set_webhook(self, url: str) -> bool:
100
- """Set the webhook for the bot.
101
-
102
- Args:
103
- url (str): The URL to set the webhook to.
104
- Returns:
105
- bool: True if the webhook was set successfully, False otherwise.
106
- """
94
+ """Set the webhook for the bot."""
107
95
  data = await make_post(self.requests_base + "/setWebhook", data={"url": url})
108
- return data["result"]
96
+ return data.get("ok", False)
109
97
 
110
98
  async def get_webhook_info(self) -> Dict:
111
- """Get the webhook information for the bot.
112
-
113
- Returns:
114
- Dict: The webhook information.
115
- """
99
+ """Get the webhook information for the bot."""
116
100
  data = await make_get(self.requests_base + "/getWebhookInfo")
117
- return data["result"]
101
+ return data.get("result", {})
118
102
 
119
103
  async def get_me(self) -> User:
120
- """Get information about the bot.
121
-
122
- Returns:
123
- User: The information about the bot.
124
- """
104
+ """Get information about the bot."""
125
105
  data = await make_get(self.requests_base + "/getMe")
126
106
  return User(**data["result"])
127
107
 
128
108
  async def logout(self) -> bool:
129
- """Log out the bot.
130
-
131
- Returns:
132
- bool: True if the bot was logged out successfully, False otherwise.
133
- """
109
+ """Log out the bot."""
134
110
  data = await make_get(self.requests_base + "/logOut")
135
- return data["result"]
111
+ return data.get("ok", False)
136
112
 
137
113
  async def close(self) -> bool:
138
- """Close the bot.
139
-
140
- Returns:
141
- bool: True if the bot was closed successfully, False otherwise.
142
- """
114
+ """Close the bot."""
143
115
  data = await make_get(self.requests_base + "/close")
144
- return data["result"]
116
+ return data.get("ok", False)
145
117
 
146
118
  async def send_message(
147
- self,
148
- chat_id: int,
149
- text: str,
150
- reply_to_message_id: Optional[int] = None,
151
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
119
+ self,
120
+ chat_id: int,
121
+ text: str,
122
+ reply_to_message_id: Optional[int] = None,
123
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
152
124
  ) -> Message:
153
- """Send a message to a chat.
154
-
155
- Args:
156
- chat_id (int): The ID of the chat to send the message to.
157
- text (str): The text of the message.
158
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
159
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
160
- Returns:
161
- Message: The message that was sent.
162
- """
125
+ """Send a message to a chat."""
163
126
  data = await make_post(
164
127
  self.requests_base + "/sendMessage",
165
128
  data={
@@ -172,17 +135,9 @@ class Client:
172
135
  return Message(**pythonize(data["result"]))
173
136
 
174
137
  async def forward_message(
175
- self, chat_id: int, from_chat_id: int, message_id: int
138
+ self, chat_id: int, from_chat_id: int, message_id: int
176
139
  ) -> Message:
177
- """Forward a message to a chat.
178
-
179
- Args:
180
- chat_id (int): The ID of the chat to forward the message to.
181
- from_chat_id (int): The ID of the chat to forward the message from.
182
- message_id (int): The ID of the message to forward.
183
- Returns:
184
- Message: The message that was forwarded.
185
- """
140
+ """Forward a message to a chat."""
186
141
  data = await make_post(
187
142
  self.requests_base + "/forwardMessage",
188
143
  data={
@@ -194,17 +149,9 @@ class Client:
194
149
  return Message(**pythonize(data["result"]))
195
150
 
196
151
  async def copy_message(
197
- self, chat_id: int, from_chat_id: int, message_id: int
152
+ self, chat_id: int, from_chat_id: int, message_id: int
198
153
  ) -> Message:
199
- """Copy a message to a chat.
200
-
201
- Args:
202
- chat_id (int): The ID of the chat to copy the message to.
203
- from_chat_id (int): The ID of the chat to copy the message from.
204
- message_id (int): The ID of the message to copy.
205
- Returns:
206
- Message: The message that was copied.
207
- """
154
+ """Copy a message to a chat."""
208
155
  data = await make_post(
209
156
  self.requests_base + "/copyMessage",
210
157
  data={
@@ -216,24 +163,14 @@ class Client:
216
163
  return Message(**pythonize(data["result"]))
217
164
 
218
165
  async def send_photo(
219
- self,
220
- chat_id: Union[int, str],
221
- photo: Union[InputFile, str],
222
- caption: Optional[str] = None,
223
- reply_to_message_id: Optional[int] = None,
224
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
166
+ self,
167
+ chat_id: Union[int, str],
168
+ photo: Union[InputFile, str],
169
+ caption: Optional[str] = None,
170
+ reply_to_message_id: Optional[int] = None,
171
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
225
172
  ) -> Message:
226
- """Send a photo to a chat.
227
-
228
- Args:
229
- chat_id (Union[int, str]): The ID of the chat to send the photo to.
230
- photo (Union[InputFile, str]): The photo to send.
231
- caption (str, optional): The caption of the photo. Defaults to None.
232
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
233
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
234
- Returns:
235
- Message: The message that was sent.
236
- """
173
+ """Send a photo to a chat."""
237
174
  data = await make_post(
238
175
  self.requests_base + "/sendPhoto",
239
176
  data={
@@ -247,24 +184,14 @@ class Client:
247
184
  return Message(**pythonize(data["result"]))
248
185
 
249
186
  async def send_audio(
250
- self,
251
- chat_id: int,
252
- audio: Union[InputFile, str],
253
- caption: Optional[str] = None,
254
- reply_to_message_id: Optional[int] = None,
255
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
187
+ self,
188
+ chat_id: int,
189
+ audio: Union[InputFile, str],
190
+ caption: Optional[str] = None,
191
+ reply_to_message_id: Optional[int] = None,
192
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
256
193
  ) -> Message:
257
- """Send an audio to a chat.
258
-
259
- Args:
260
- chat_id (int): The ID of the chat to send the audio to.
261
- audio (Union[InputFile, str]): The audio to send.
262
- caption (str, optional): The caption of the audio. Defaults to None.
263
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
264
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
265
- Returns:
266
- Message: The message that was sent.
267
- """
194
+ """Send an audio to a chat."""
268
195
  data = await make_post(
269
196
  self.requests_base + "/sendAudio",
270
197
  data={
@@ -278,24 +205,14 @@ class Client:
278
205
  return Message(**pythonize(data["result"]))
279
206
 
280
207
  async def send_document(
281
- self,
282
- chat_id: int,
283
- document: Union[InputFile, str],
284
- caption: Optional[str] = None,
285
- reply_to_message_id: Optional[int] = None,
286
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
208
+ self,
209
+ chat_id: int,
210
+ document: Union[InputFile, str],
211
+ caption: Optional[str] = None,
212
+ reply_to_message_id: Optional[int] = None,
213
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
287
214
  ) -> Message:
288
- """Send a document to a chat.
289
-
290
- Args:
291
- chat_id (int): The ID of the chat to send the document to.
292
- document (Union[InputFile, str]): The document to send.
293
- caption (str, optional): The caption of the document. Defaults to None.
294
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
295
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
296
- Returns:
297
- Message: The message that was sent.
298
- """
215
+ """Send a document to a chat."""
299
216
  data = await make_post(
300
217
  self.requests_base + "/sendDocument",
301
218
  data={
@@ -309,24 +226,14 @@ class Client:
309
226
  return Message(**pythonize(data["result"]))
310
227
 
311
228
  async def send_video(
312
- self,
313
- chat_id: int,
314
- video: Union[InputFile, str],
315
- caption: Optional[str] = None,
316
- reply_to_message_id: Optional[int] = None,
317
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
229
+ self,
230
+ chat_id: int,
231
+ video: Union[InputFile, str],
232
+ caption: Optional[str] = None,
233
+ reply_to_message_id: Optional[int] = None,
234
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
318
235
  ) -> Message:
319
- """Send a video to a chat.
320
-
321
- Args:
322
- chat_id (int): The ID of the chat to send the video to.
323
- video (Union[InputFile, str]): The video to send.
324
- caption (str, optional): The caption of the video. Defaults to None.
325
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
326
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
327
- Returns:
328
- Message: The message that was sent.
329
- """
236
+ """Send a video to a chat."""
330
237
  data = await make_post(
331
238
  self.requests_base + "/sendVideo",
332
239
  data={
@@ -340,24 +247,14 @@ class Client:
340
247
  return Message(**pythonize(data["result"]))
341
248
 
342
249
  async def send_animation(
343
- self,
344
- chat_id: int,
345
- animation: Union[InputFile, str],
346
- caption: Optional[str] = None,
347
- reply_to_message_id: Optional[int] = None,
348
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
250
+ self,
251
+ chat_id: int,
252
+ animation: Union[InputFile, str],
253
+ caption: Optional[str] = None,
254
+ reply_to_message_id: Optional[int] = None,
255
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
349
256
  ) -> Message:
350
- """Send an animation to a chat.
351
-
352
- Args:
353
- chat_id (int): The ID of the chat to send the animation to.
354
- animation (Union[InputFile, str]): The animation to send.
355
- caption (str, optional): The caption of the animation. Defaults to None.
356
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
357
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
358
- Returns:
359
- Message: The message that was sent.
360
- """
257
+ """Send an animation to a chat."""
361
258
  data = await make_post(
362
259
  self.requests_base + "/sendAnimation",
363
260
  data={
@@ -371,24 +268,14 @@ class Client:
371
268
  return Message(**pythonize(data["result"]))
372
269
 
373
270
  async def send_voice(
374
- self,
375
- chat_id: int,
376
- voice: Union[InputFile, str],
377
- caption: Optional[str] = None,
378
- reply_to_message_id: Optional[int] = None,
379
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
271
+ self,
272
+ chat_id: int,
273
+ voice: Union[InputFile, str],
274
+ caption: Optional[str] = None,
275
+ reply_to_message_id: Optional[int] = None,
276
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
380
277
  ) -> Message:
381
- """Send a voice message to a chat.
382
-
383
- Args:
384
- chat_id (int): The ID of the chat to send the voice message to.
385
- voice (Union[InputFile, str]): The voice message to send.
386
- caption (str, optional): The caption of the voice message. Defaults to None.
387
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
388
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
389
- Returns:
390
- Message: The message that was sent.
391
- """
278
+ """Send a voice message to a chat."""
392
279
  data = await make_post(
393
280
  self.requests_base + "/sendVoice",
394
281
  data={
@@ -402,22 +289,13 @@ class Client:
402
289
  return Message(**pythonize(data["result"]))
403
290
 
404
291
  async def send_media_group(
405
- self,
406
- chat_id: int,
407
- media: List[Union[InputMediaPhoto, InputMediaVideo, InputMediaAudio]],
408
- reply_to_message_id: Optional[int] = None,
409
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
292
+ self,
293
+ chat_id: int,
294
+ media: List[Union[InputMediaPhoto, InputMediaVideo, InputMediaAudio]],
295
+ reply_to_message_id: Optional[int] = None,
296
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
410
297
  ) -> List[Message]:
411
- """Send a media group to a chat.
412
-
413
- Args:
414
- chat_id (int): The ID of the chat to send the media group to.
415
- media (List[Union[InputMediaPhoto, InputMediaVideo, InputMediaAudio]]): The media group to send.
416
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
417
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
418
- Returns:
419
- List[Message]: The messages that were sent.
420
- """
298
+ """Send a media group to a chat."""
421
299
  data = await make_post(
422
300
  self.requests_base + "/sendMediaGroup",
423
301
  data={
@@ -427,28 +305,18 @@ class Client:
427
305
  "reply_markup": reply_markup.to_dict() if reply_markup else None,
428
306
  },
429
307
  )
430
- return Message(**pythonize(data["result"]))
308
+ return [Message(**pythonize(msg)) for msg in data["result"]]
431
309
 
432
310
  async def send_location(
433
- self,
434
- chat_id: int,
435
- latitude: float,
436
- longitude: float,
437
- horizontal_accuracy: Optional[float] = None,
438
- reply_to_message_id: Optional[int] = None,
439
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
311
+ self,
312
+ chat_id: int,
313
+ latitude: float,
314
+ longitude: float,
315
+ horizontal_accuracy: Optional[float] = None,
316
+ reply_to_message_id: Optional[int] = None,
317
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
440
318
  ) -> Message:
441
- """Send a location to a chat.
442
-
443
- Args:
444
- chat_id (int): The ID of the chat to send the location to.
445
- latitude (float): The latitude of the location.
446
- longitude (float): The longitude of the location.
447
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
448
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
449
- Returns:
450
- Message: The message that was sent.
451
- """
319
+ """Send a location to a chat."""
452
320
  data = await make_post(
453
321
  self.requests_base + "/sendLocation",
454
322
  data={
@@ -463,26 +331,15 @@ class Client:
463
331
  return Message(**pythonize(data["result"]))
464
332
 
465
333
  async def send_contact(
466
- self,
467
- chat_id: int,
468
- phone_number: str,
469
- first_name: str,
470
- last_name: Optional[str] = None,
471
- reply_to_message_id: Optional[int] = None,
472
- reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
334
+ self,
335
+ chat_id: int,
336
+ phone_number: str,
337
+ first_name: str,
338
+ last_name: Optional[str] = None,
339
+ reply_to_message_id: Optional[int] = None,
340
+ reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
473
341
  ) -> Message:
474
- """Send a contact to a chat.
475
-
476
- Args:
477
- chat_id (int): The ID of the chat to send the contact to.
478
- phone_number (str): The phone number of the contact.
479
- first_name (str): The first name of the contact.
480
- last_name (str, optional): The last name of the contact. Defaults to None.
481
- reply_to_message_id (int, optional): The ID of the message to reply to. Defaults to None.
482
- reply_markup (Union[InlineKeyboardMarkup, ReplyKeyboardMarkup], optional): The reply markup to use. Defaults to None.
483
- Returns:
484
- Message: The message that was sent.
485
- """
342
+ """Send a contact to a chat."""
486
343
  data = await make_post(
487
344
  self.requests_base + "/sendContact",
488
345
  data={
@@ -497,34 +354,18 @@ class Client:
497
354
  return Message(**pythonize(data["result"]))
498
355
 
499
356
  async def send_invoice(
500
- self,
501
- chat_id: Union[str, int],
502
- title: str,
503
- description: str,
504
- payload: str,
505
- provider_token: str,
506
- prices: list[LabeledPrice],
507
- photo_url: Optional[str] = None,
508
- reply_to_message_id: Optional[int] = None,
357
+ self,
358
+ chat_id: Union[str, int],
359
+ title: str,
360
+ description: str,
361
+ payload: str,
362
+ provider_token: str,
363
+ prices: list[LabeledPrice],
364
+ photo_url: Optional[str] = None,
365
+ reply_to_message_id: Optional[int] = None,
509
366
  ) -> Message:
510
- """Sends a message including a invoice for user to pay.
511
-
512
- Args:
513
- chat_id (string OR integer): unique chat id to send the invoice
514
- title (string): the title of invoice
515
- description (string): desciption of invoice, you can explain the invoice here
516
- payload (string): payload of invoice, user will not see this, it'll be returned after successful payment
517
- provider_token (string): Wallet token or card number of receiver
518
- prices (list of LabledPrice): a list of prices that user must pay
519
- photo_url (Optional: string): url of a photo that will be sent with invoice
520
- reply_to_message_id (Optional: int): message id to reply that
521
-
522
- Returns:
523
- Message: returns the sent message with invoice
524
- """
525
- new_prices = []
526
- for price in prices:
527
- new_prices.append(price.json)
367
+ """Sends a message including a invoice for user to pay."""
368
+ new_prices = [price.json for price in prices]
528
369
  data = await make_post(
529
370
  self.requests_base + "/sendInvoice",
530
371
  data={
@@ -541,33 +382,19 @@ class Client:
541
382
  return Message(**pythonize(data["result"]))
542
383
 
543
384
  async def get_file(self, file_id: str) -> File:
544
- """Get a file from the Bale servers.
545
-
546
- Args:
547
- file_id (str): The ID of the file to get.
548
- Returns:
549
- File: The file that was retrieved.
550
- """
385
+ """Get a file from the Bale servers."""
551
386
  data = await make_post(
552
387
  self.requests_base + "/getFile", data={"file_id": file_id}
553
388
  )
554
389
  return File(**pythonize(data["result"]))
555
390
 
556
391
  async def answer_callback_query(
557
- self,
558
- callback_query_id: str,
559
- text: Optional[str] = None,
560
- show_alert: Optional[bool] = None,
561
- ):
562
- """Answer a callback query.
563
-
564
- Args:
565
- callback_query_id (str): The ID of the callback query to answer.
566
- text (str, optional): The text to show to the user. Defaults to None.
567
- show_alert (bool, optional): Whether to show an alert to the user. Defaults to None.
568
- Returns:
569
- bool: Whether the callback query was answered successfully.
570
- """
392
+ self,
393
+ callback_query_id: str,
394
+ text: Optional[str] = None,
395
+ show_alert: Optional[bool] = None,
396
+ ) -> bool:
397
+ """Answer a callback query."""
571
398
  data = await make_post(
572
399
  self.requests_base + "/answerCallbackQuery",
573
400
  data={
@@ -579,15 +406,7 @@ class Client:
579
406
  return data.get("ok", False)
580
407
 
581
408
  async def ban_chat_member(self, chat_id: int, user_id: int) -> bool:
582
- """Ban a user from a chat.
583
-
584
- Args:
585
- chat_id (int): The ID of the chat to ban the user from.
586
- user_id (int): The ID of the user to ban.
587
- Returns:
588
- bool: Whether the user was banned successfully
589
- """
590
-
409
+ """Ban a user from a chat."""
591
410
  data = await make_post(
592
411
  self.requests_base + "/banChatMember",
593
412
  data={"chat_id": chat_id, "user_id": user_id},
@@ -595,14 +414,7 @@ class Client:
595
414
  return data.get("ok", False)
596
415
 
597
416
  async def unban_chat_member(self, chat_id: int, user_id: int) -> bool:
598
- """Unban a user from a chat.
599
-
600
- Args:
601
- chat_id (int): The ID of the chat to unban the user from.
602
- user_id (int): The ID of the user to unban.
603
- Returns:
604
- bool: Whether the user was unbanned successfully.
605
- """
417
+ """Unban a user from a chat."""
606
418
  data = await make_post(
607
419
  self.requests_base + "/unbanChatMember",
608
420
  data={"chat_id": chat_id, "user_id": user_id},
@@ -610,14 +422,7 @@ class Client:
610
422
  return data.get("ok", False)
611
423
 
612
424
  async def get_chat_member(self, chat_id: int, user_id: int) -> ChatMember:
613
- """Get a chat member.
614
-
615
- Args:
616
- chat_id (int): The ID of the chat to get the member from.
617
- user_id (int): The ID of the user to get.
618
- Returns:
619
- ChatMember: The chat member that was retrieved.
620
- """
425
+ """Get a chat member."""
621
426
  data = await make_post(
622
427
  self.requests_base + "/getChatMember",
623
428
  data={"chat_id": chat_id, "user_id": user_id},
@@ -627,34 +432,19 @@ class Client:
627
432
  )
628
433
 
629
434
  async def promote_chat_member(
630
- self,
631
- chat_id: int,
632
- user_id: int,
633
- can_change_info: Optional[bool] = None,
634
- can_post_messages: Optional[bool] = None,
635
- can_edit_messages: Optional[bool] = None,
636
- can_delete_messages: Optional[bool] = None,
637
- can_invite_users: Optional[bool] = None,
638
- can_restrict_members: Optional[bool] = None,
639
- can_pin_messages: Optional[bool] = None,
640
- can_promote_members: Optional[bool] = None,
641
- ):
642
- """Promote a user in a chat.
643
-
644
- Args:
645
- chat_id (int): The ID of the chat to promote the user in.
646
- user_id (int): The ID of the user to promote.
647
- can_change_info (bool, optional): Whether the user can change the chat info. Defaults to None.
648
- can_post_messages (bool, optional): Whether the user can post messages. Defaults to None.
649
- can_edit_messages (bool, optional): Whether the user can edit messages. Defaults to None.
650
- can_delete_messages (bool, optional): Whether the user can delete messages. Defaults to None.
651
- can_invite_users (bool, optional): Whether the user can invite users. Defaults to None.
652
- can_restrict_members (bool, optional): Whether the user can restrict members. Defaults to None.
653
- can_pin_messages (bool, optional): Whether the user can pin messages. Defaults to None.
654
- can_promote_members (bool, optional): Whether the user can promote members. Defaults to None.
655
- Returns:
656
- bool: Whether the user was promoted successfully.
657
- """
435
+ self,
436
+ chat_id: int,
437
+ user_id: int,
438
+ can_change_info: Optional[bool] = None,
439
+ can_post_messages: Optional[bool] = None,
440
+ can_edit_messages: Optional[bool] = None,
441
+ can_delete_messages: Optional[bool] = None,
442
+ can_invite_users: Optional[bool] = None,
443
+ can_restrict_members: Optional[bool] = None,
444
+ can_pin_messages: Optional[bool] = None,
445
+ can_promote_members: Optional[bool] = None,
446
+ ) -> bool:
447
+ """Promote a user in a chat."""
658
448
  data = await make_post(
659
449
  self.requests_base + "/promoteChatMember",
660
450
  data={
@@ -673,15 +463,7 @@ class Client:
673
463
  return data.get("ok", False)
674
464
 
675
465
  async def set_chat_photo(self, chat_id: int, photo: InputFile) -> bool:
676
- """Set a new profile photo for the chat.
677
-
678
- Args:
679
- chat_id (int): Unique identifier for the target chat
680
- photo (InputFile): New chat photo
681
-
682
- Returns:
683
- bool: True on success
684
- """
466
+ """Set a new profile photo for the chat."""
685
467
  data = await make_post(
686
468
  self.requests_base + "/setChatPhoto",
687
469
  data={"chat_id": chat_id, "photo": photo},
@@ -689,29 +471,14 @@ class Client:
689
471
  return data.get("ok", False)
690
472
 
691
473
  async def leave_chat(self, chat_id: int) -> bool:
692
- """Leave a group, supergroup or channel.
693
-
694
- Args:
695
- chat_id (int): Unique identifier for the target chat
696
-
697
- Returns:
698
- bool: True on success
699
- """
474
+ """Leave a group, supergroup or channel."""
700
475
  data = await make_post(
701
476
  self.requests_base + "/leaveChat", data={"chat_id": chat_id}
702
477
  )
703
478
  return data.get("ok", False)
704
-
479
+
705
480
  async def is_joined(self, user_id: int, chat_id: int) -> bool:
706
- """Check if a user is joined to a chat.
707
-
708
- Args:
709
- user_id (int): Unique identifier for the target chat
710
- chat_id (int): Unique identifier for the target chat
711
-
712
- Returns:
713
- bool: True if the user is joined to the chat, False otherwise
714
- """
481
+ """Check if a user is joined to a chat."""
715
482
  data = await make_post(
716
483
  self.requests_base + "/getChatMember",
717
484
  data={"chat_id": chat_id, "user_id": user_id},
@@ -719,53 +486,39 @@ class Client:
719
486
  return data.get("result", {}).get("status") in ["member", "creator", "administrator"]
720
487
 
721
488
  async def get_chat(self, chat_id: int) -> Chat:
722
- """Get up to date information about the chat.
723
-
724
- Args:
725
- chat_id (int): Unique identifier for the target chat
726
-
727
- Returns:
728
- Chat: Chat object with information about the chat
729
- """
489
+ """Get up to date information about the chat."""
730
490
  data = await make_post(
731
491
  self.requests_base + "/getChat", data={"chat_id": chat_id}
732
492
  )
733
493
  return Chat(**pythonize(data["result"]))
734
-
494
+
735
495
  @staticmethod
736
- async def get_ble_ir_page(username_or_phone_number: str) -> Union[dict, PeerData]:
737
- """Get BleIR user/group information.
738
-
739
- Args:
740
- username_or_phone_number (str): Username or phone number
741
-
742
- Returns:
743
- Union[dict, PeerData]: User/group information or error dict
744
- """
496
+ async def get_ble_ir_page(username_or_phone_number: str) -> PeerData:
497
+ """Get BleIR user/group information."""
745
498
  url = f"https://ble.ir/{username_or_phone_number}"
746
-
499
+
747
500
  async with aiohttp.ClientSession() as session:
748
501
  async with session.get(url) as response:
749
502
  req = await response.text()
750
-
503
+
751
504
  if """<p class="__404_title__lxIKL">گفتگوی مورد نظر وجود ندارد.</p>""" in req:
752
505
  return PeerData(
753
- is_ok=False,
754
- avatar=None,
755
- description=None,
756
- name=None,
757
- is_bot=None,
758
- is_verified=None,
759
- is_private=None,
760
- members=None,
761
- last_message=None,
762
- user_id=None,
763
- username=None,
764
- )
765
-
506
+ is_ok=False,
507
+ avatar=None,
508
+ description=None,
509
+ name=None,
510
+ is_bot=None,
511
+ is_verified=None,
512
+ is_private=None,
513
+ members=None,
514
+ last_message=None,
515
+ user_id=None,
516
+ username=None,
517
+ )
518
+
766
519
  soup = BeautifulSoup(req, "html.parser")
767
520
  json_data = {}
768
-
521
+
769
522
  try:
770
523
  json_script = soup.find("script", id="__NEXT_DATA__").text
771
524
  json_data = loads(json_script)
@@ -803,8 +556,8 @@ class Client:
803
556
  try:
804
557
  last_msg = messages[-1]["message"]
805
558
  last_message = (
806
- last_msg.get("documentMessage", {}).get("caption", {}).get("text")
807
- or last_msg.get("textMessage", {}).get("text")
559
+ last_msg.get("documentMessage", {}).get("caption", {}).get("text")
560
+ or last_msg.get("textMessage", {}).get("text")
808
561
  )
809
562
  if last_message:
810
563
  last_message = last_message.replace("&zwnj;", "")
@@ -824,31 +577,16 @@ class Client:
824
577
  user_id,
825
578
  username
826
579
  )
827
-
828
- async def get_chat_members_count(self, chat_id: int) -> int:
829
- """Get the number of members in a chat.
830
-
831
- Args:
832
- chat_id (int): Unique identifier for the target chat
833
580
 
834
- Returns:
835
- int: Number of members in the chat
836
- """
581
+ async def get_chat_members_count(self, chat_id: int) -> int:
582
+ """Get the number of members in a chat."""
837
583
  data = await make_post(
838
584
  self.requests_base + "/getChatMembersCount", data={"chat_id": chat_id}
839
585
  )
840
586
  return data.get("result", 0)
841
587
 
842
588
  async def pin_chat_message(self, chat_id: int, message_id: int) -> bool:
843
- """Pin a message in a chat.
844
-
845
- Args:
846
- chat_id (int): Unique identifier for the target chat
847
- message_id (int): Identifier of a message to pin
848
-
849
- Returns:
850
- bool: True on success
851
- """
589
+ """Pin a message in a chat."""
852
590
  data = await make_post(
853
591
  self.requests_base + "/pinChatMessage",
854
592
  data={"chat_id": chat_id, "message_id": message_id},
@@ -856,43 +594,21 @@ class Client:
856
594
  return data.get("ok", False)
857
595
 
858
596
  async def unpin_chat_message(self, chat_id: int) -> bool:
859
- """Unpin a message in a chat.
860
-
861
- Args:
862
- chat_id (int): Unique identifier for the target chat
863
-
864
- Returns:
865
- bool: True on success
866
- """
597
+ """Unpin a message in a chat."""
867
598
  data = await make_post(
868
599
  self.requests_base + "/unpinChatMessage", data={"chat_id": chat_id}
869
600
  )
870
601
  return data.get("ok", False)
871
602
 
872
603
  async def unpin_all_chat_messages(self, chat_id: int) -> bool:
873
- """Unpin all messages in a chat.
874
-
875
- Args:
876
- chat_id (int): Unique identifier for the target chat
877
-
878
- Returns:
879
- bool: True on success
880
- """
604
+ """Unpin all messages in a chat."""
881
605
  data = await make_post(
882
606
  self.requests_base + "/unpinAllChatMessages", data={"chat_id": chat_id}
883
607
  )
884
608
  return data.get("ok", False)
885
609
 
886
610
  async def set_chat_title(self, chat_id: int, title: str) -> bool:
887
- """Change the title of a chat.
888
-
889
- Args:
890
- chat_id (int): Unique identifier for the target chat
891
- title (str): New chat title, 1-255 characters
892
-
893
- Returns:
894
- bool: True on success
895
- """
611
+ """Change the title of a chat."""
896
612
  data = await make_post(
897
613
  self.requests_base + "/setChatTitle",
898
614
  data={"chat_id": chat_id, "title": title},
@@ -900,15 +616,7 @@ class Client:
900
616
  return data.get("ok", False)
901
617
 
902
618
  async def set_chat_description(self, chat_id: int, description: str) -> bool:
903
- """Change the description of a chat.
904
-
905
- Args:
906
- chat_id (int): Unique identifier for the target chat
907
- description (str): New chat description, 0-255 characters
908
-
909
- Returns:
910
- bool: True on success
911
- """
619
+ """Change the description of a chat."""
912
620
  data = await make_post(
913
621
  self.requests_base + "/setChatDescription",
914
622
  data={"chat_id": chat_id, "description": description},
@@ -916,38 +624,20 @@ class Client:
916
624
  return data.get("ok", False)
917
625
 
918
626
  async def delete_chat_photo(self, chat_id: int) -> bool:
919
- """Delete a chat photo.
920
-
921
- Args:
922
- chat_id (int): Unique identifier for the target chat
923
-
924
- Returns:
925
- bool: True on success
926
- """
627
+ """Delete a chat photo."""
927
628
  data = await make_post(
928
629
  self.requests_base + "/deleteChatPhoto", data={"chat_id": chat_id}
929
630
  )
930
631
  return data.get("ok", False)
931
632
 
932
633
  async def edit_message(
933
- self,
934
- chat_id: Union[int, str],
935
- message_id: int,
936
- text: str,
937
- reply_markup: Optional[InlineKeyboardMarkup] = None,
634
+ self,
635
+ chat_id: Union[int, str],
636
+ message_id: int,
637
+ text: str,
638
+ reply_markup: Optional[InlineKeyboardMarkup] = None,
938
639
  ) -> Message:
939
- """Edits a message in a specified chat
940
-
941
- Args:
942
- chat_id (int OR str): Unique identifier for the target chat
943
- message_id (int): Unique indentifier for the message you want to edit
944
- text (str): New text of message
945
- reply_markup (InlineKeyboardMarkup): Inline markup you can add or change in message
946
-
947
- Returns:
948
- Message: The object of edited message
949
- """
950
-
640
+ """Edits a message in a specified chat"""
951
641
  data = await make_post(
952
642
  self.requests_base + "/editMessageText",
953
643
  data={
@@ -960,29 +650,14 @@ class Client:
960
650
  return Message(**pythonize(data["result"]))
961
651
 
962
652
  async def create_chat_invite_link(self, chat_id: int) -> str:
963
- """Create an additional invite link for a chat.
964
-
965
- Args:
966
- chat_id (int): Unique identifier for the target chat
967
-
968
- Returns:
969
- str: The new invite link
970
- """
653
+ """Create an additional invite link for a chat."""
971
654
  data = await make_post(
972
655
  self.requests_base + "/createChatInviteLink", data={"chat_id": chat_id}
973
656
  )
974
657
  return data.get("result", "")
975
658
 
976
659
  async def revoke_chat_invite_link(self, chat_id: int, invite_link: str) -> str:
977
- """Revoke an invite link created by the bot.
978
-
979
- Args:
980
- chat_id (int): Unique identifier for the target chat
981
- invite_link (str): The invite link to revoke
982
-
983
- Returns:
984
- str: The revoked invite link
985
- """
660
+ """Revoke an invite link created by the bot."""
986
661
  data = await make_post(
987
662
  self.requests_base + "/revokeChatInviteLink",
988
663
  data={"chat_id": chat_id, "invite_link": invite_link},
@@ -990,29 +665,14 @@ class Client:
990
665
  return data.get("result", "")
991
666
 
992
667
  async def export_chat_invite_link(self, chat_id: int) -> str:
993
- """Generate a new primary invite link for a chat.
994
-
995
- Args:
996
- chat_id (int): Unique identifier for the target chat
997
-
998
- Returns:
999
- str: The new invite link
1000
- """
668
+ """Generate a new primary invite link for a chat."""
1001
669
  data = await make_post(
1002
670
  self.requests_base + "/exportChatInviteLink", data={"chat_id": chat_id}
1003
671
  )
1004
672
  return data.get("result", "")
1005
673
 
1006
674
  async def send_chat_action(self, chat_id: int, action: ChatAction) -> bool:
1007
- """Tell the user that something is happening on the bot's side.
1008
-
1009
- Args:
1010
- chat_id (int): Unique identifier for the target chat
1011
- action (ChatAction): Type of action to broadcast
1012
-
1013
- Returns:
1014
- bool: True on success
1015
- """
675
+ """Tell the user that something is happening on the bot's side."""
1016
676
  data = await make_post(
1017
677
  self.requests_base + "/sendChatAction",
1018
678
  data={"chat_id": str(chat_id), "action": action.value},
@@ -1020,269 +680,208 @@ class Client:
1020
680
  return data.get("ok", False)
1021
681
 
1022
682
  async def wait_for(self, update_type: UpdatesTypes, check=None):
1023
- """Wait until a specified update
1024
-
1025
- Args:
1026
- update_type (UpdatesTypes): The type of update you're waiting for it.
1027
- check: a condition that will be checked before passing.
1028
- """
683
+ """Wait until a specified update"""
1029
684
  future = asyncio.get_running_loop().create_future()
1030
685
  self._waiters.append((update_type, check, future))
1031
686
  return await future
1032
687
 
1033
688
  async def process_update(self, update: Dict[str, Any]) -> None:
1034
- """Process a single update and call registered handlers.
689
+ """Process a single update and call registered handlers."""
690
+ # print(f"Processing update: {update}")
1035
691
 
1036
- Args:
1037
- update (Dict[str, Any]): The update to process
1038
- """
1039
692
  update_id = update.get("update_id")
1040
693
  if update_id:
1041
694
  self.last_update_id = update_id + 1
1042
-
1043
695
  if self.check_defined_message:
1044
696
  try:
1045
- update_raw = update['message']
1046
- if update_raw.get("text") in self.defined_messages.keys():
1047
- await self.send_message(update_raw.get('chat').get('id'), self.defined_messages.get(update_raw.get("text")), update.get('message_id'))
1048
- except KeyError:
1049
- pass
697
+ update_raw = update.get('message', {})
698
+ if update_raw.get("text") in self.defined_messages:
699
+ await self.send_message(
700
+ update_raw.get('chat', {}).get('id'),
701
+ self.defined_messages.get(update_raw.get("text")),
702
+ update_raw.get('message_id')
703
+ )
1050
704
  except Exception as e:
1051
- print(e)
1052
-
705
+ print(f"Error processing defined message: {e}")
1053
706
  for waiter in list(self._waiters):
1054
707
  w_type, check, future = waiter
1055
708
  if w_type.value in update:
1056
- event = update[w_type.value]
1057
- event = self._convert_event(w_type, event)
1058
- if check is None or check(event):
1059
- if not future.done():
1060
- future.set_result(event)
1061
- self._waiters.remove(waiter)
1062
- return
1063
-
709
+ event_data = update[w_type.value]
710
+ try:
711
+ event = self._convert_event(w_type, event_data)
712
+ if check is None or check(event):
713
+ if not future.done():
714
+ future.set_result(event)
715
+ self._waiters.remove(waiter)
716
+ return
717
+ except Exception as e:
718
+ print(f"Error in waiter conversion: {e}")
719
+ continue
1064
720
  for handler in self.handlers:
1065
- update_type = handler["type"].value
1066
- if update_type in update:
1067
- raw_event = update[update_type]
721
+ handler_type = handler["type"]
722
+ update_type_key = handler_type.value
1068
723
 
724
+ if update_type_key not in update:
725
+ continue
1069
726
 
1070
- event = self._convert_event(handler["type"], raw_event)
1071
-
1072
- if handler["type"] == UpdatesTypes.COMMAND:
1073
- if hasattr(event, 'text') and event.text and event.text.startswith('/'):
1074
- command_text = event.text[1:]
1075
- command_parts = command_text.split()
1076
- if command_parts:
1077
- actual_command = command_parts[0]
1078
- expected_command = handler.get("command", "")
1079
-
1080
- if actual_command != expected_command:
1081
- continue
1082
-
1083
- flt = handler.get("filter")
1084
- if flt is not None:
1085
- if callable(flt):
1086
- try:
1087
- if not flt(event):
1088
- continue
1089
- except Exception as e:
1090
- print(f"[Filter Error] {e}")
1091
- continue
1092
- elif isinstance(flt, Filters):
1093
- if not hasattr(event, flt.value):
727
+ raw_event = update[update_type_key]
728
+
729
+ try:
730
+ event = self._convert_event(handler_type, raw_event)
731
+ except Exception as e:
732
+ print(f"Error converting event for handler {handler_type}: {e}")
733
+ continue
734
+ if handler_type == UpdatesTypes.COMMAND:
735
+ if not hasattr(event, 'text') or not event.text or not event.text.startswith('/'):
736
+ continue
737
+
738
+ command_text = event.text[1:].split('@')[0]
739
+ command_parts = command_text.split()
740
+ if not command_parts:
741
+ continue
742
+
743
+ actual_command = command_parts[0]
744
+ expected_command = handler.get("command", "")
745
+
746
+ if actual_command != expected_command:
747
+ continue
748
+ flt = handler.get("filter")
749
+ if flt is not None:
750
+ if callable(flt):
751
+ try:
752
+ if not flt(event):
1094
753
  continue
1095
-
754
+ except Exception as e:
755
+ print(f"[Filter Error] {e}")
756
+ continue
757
+ elif isinstance(flt, Filters):
758
+ if not hasattr(event, flt.value):
759
+ continue
760
+ try:
1096
761
  if asyncio.iscoroutinefunction(handler["callback"]):
1097
762
  asyncio.create_task(handler["callback"](event))
1098
763
  else:
1099
764
  handler["callback"](event)
765
+ except Exception as e:
766
+ print(f"Error executing handler: {e}")
767
+
768
+ def _convert_event(self, handler_type: UpdatesTypes, event_data: Dict[str, Any]) -> Any:
769
+ """Convert raw event data to appropriate object type."""
770
+ kwargs = {"client": self}
1100
771
 
772
+ try:
773
+ if handler_type == UpdatesTypes.MESSAGE:
774
+ return Message(kwargs=kwargs, **pythonize(event_data))
1101
775
 
776
+ elif handler_type == UpdatesTypes.MESSAGE_EDITED:
777
+ return Message(kwargs=kwargs, **pythonize(event_data))
1102
778
 
1103
- def base_handler_decorator(self, update_type: UpdatesTypes):
1104
- """Base decorator for handling different types of updates.
779
+ elif handler_type == UpdatesTypes.CALLBACK_QUERY:
780
+ return CallbackQuery(kwargs=kwargs, **pythonize(event_data))
781
+
782
+ elif handler_type == UpdatesTypes.PRE_CHECKOUT_QUERY:
783
+ return PreCheckoutQuery(kwargs=kwargs, **pythonize(event_data))
784
+
785
+ elif handler_type == UpdatesTypes.MEMBER_JOINED:
786
+ if "new_chat_member" in event_data:
787
+ return (
788
+ ChatMember(kwargs=kwargs, **pythonize(event_data["new_chat_member"])),
789
+ Chat(kwargs=kwargs, **pythonize(event_data.get("chat", {}))),
790
+ Message(kwargs=kwargs, **pythonize(event_data))
791
+ )
792
+ return Message(kwargs=kwargs, **pythonize(event_data))
793
+
794
+ elif handler_type == UpdatesTypes.MEMBER_LEFT:
795
+ if "left_chat_member" in event_data:
796
+ return (
797
+ ChatMember(kwargs=kwargs, **pythonize(event_data["left_chat_member"])),
798
+ Chat(kwargs=kwargs, **pythonize(event_data.get("chat", {}))),
799
+ Message(kwargs=kwargs, **pythonize(event_data))
800
+ )
801
+ return Message(kwargs=kwargs, **pythonize(event_data))
802
+
803
+ elif handler_type == UpdatesTypes.SUCCESSFUL_PAYMENT:
804
+ if "successful_payment" in event_data:
805
+ return SuccessfulPayment(kwargs=kwargs, **pythonize(event_data["successful_payment"]))
806
+ return Message(kwargs=kwargs, **pythonize(event_data))
807
+
808
+ elif handler_type == UpdatesTypes.COMMAND:
809
+ return Message(kwargs=kwargs, **pythonize(event_data))
810
+
811
+ elif handler_type == UpdatesTypes.PHOTO:
812
+ return Message(kwargs=kwargs, **pythonize(event_data))
813
+
814
+ else: return event_data
1105
815
 
1106
- Args:
1107
- update_type (UpdatesTypes): The type of update to handle.
816
+ except Exception as e:
817
+ print(f"Error converting event {handler_type}: {e}")
818
+ return event_data
819
+
820
+ def base_handler_decorator(self, update_type: UpdatesTypes):
821
+ """Base decorator for handling different types of updates."""
1108
822
 
1109
- Returns:
1110
- Callable: A decorator function that registers the callback for the specified update type.
1111
- """
1112
823
  def wrapper(filter: Optional[Filters] = None):
1113
824
  def decorator(callback: Callable[[Any], Union[None, Awaitable[None]]]):
1114
825
  self.add_handler(update_type, callback, filter)
1115
826
  return callback
827
+
1116
828
  return decorator
829
+
1117
830
  return wrapper
1118
-
831
+
1119
832
  def on_command(self, command: str, filter: Optional[Filters] = None):
1120
- """Decorator for handling command updates.
1121
-
1122
- Args:
1123
- command (str): The command to handle.
1124
- filter (Optional[Filters]): An optional filter to apply to the command.
1125
- Returns:
1126
- Callable: A decorator function that registers the callback for the specified command.
1127
- """
833
+ """Decorator for handling command updates."""
834
+
1128
835
  def decorator(callback: Callable[[Any], Union[None, Awaitable[None]]]):
1129
836
  self.add_handler(UpdatesTypes.COMMAND, callback, filter, command=command)
1130
837
  return callback
1131
- return decorator
1132
838
 
839
+ return decorator
1133
840
 
1134
841
  def on_message(self, filter: Optional[Filters] = None):
1135
- """Decorator for handling new message updates.
1136
-
1137
- Returns:
1138
- Callable: A decorator function that registers the callback for message updates.
1139
- """
842
+ """Decorator for handling new message updates."""
1140
843
  return self.base_handler_decorator(UpdatesTypes.MESSAGE)(filter)
1141
844
 
845
+ def on_edited_message(self, filter: Optional[Filters] = None):
846
+ """Decorator for handling edited message updates."""
847
+ return self.base_handler_decorator(UpdatesTypes.MESSAGE_EDITED)(filter)
1142
848
 
1143
- def on_edited_message(self):
1144
- """Decorator for handling edited message updates.
1145
-
1146
- Returns:
1147
- Callable: A decorator function that registers the callback for edited message updates.
1148
- """
1149
- return self.base_handler_decorator(UpdatesTypes.MESSAGE_EDITED)
1150
-
1151
- def on_callback_query(self):
1152
- """Decorator for handling callback query updates.
1153
-
1154
- Returns:
1155
- Callable: A decorator function that registers the callback for callback query updates.
1156
- """
1157
- return self.base_handler_decorator(UpdatesTypes.CALLBACK_QUERY)
1158
-
1159
- def on_new_members(self):
1160
- """Decorator for handling new chat members updates.
1161
-
1162
- Returns:
1163
- Callable: A decorator function that registers the callback for new members updates.
1164
- """
1165
- return self.base_handler_decorator(UpdatesTypes.MEMBER_JOINED)
1166
-
1167
- def on_members_left(self):
1168
- return self.base_handler_decorator(UpdatesTypes.MEMBER_LEFT)
1169
-
1170
- def on_pre_checkout_query(self):
1171
- """Decorator for handling pre-checkout query updates.
1172
-
1173
- Returns:
1174
- Callable: A decorator function that registers the callback for pre-checkout query updates.
1175
- """
1176
- return self.base_handler_decorator(UpdatesTypes.PRE_CHECKOUT_QUERY)
1177
-
1178
- def on_photo(self):
1179
- """Decorator for handling photo updates.
1180
-
1181
- Returns:
1182
- Callable: A decorator function that registers the callback for photo updates.
1183
- """
1184
- return self.base_handler_decorator(UpdatesTypes.PHOTO)
1185
-
1186
- def on_successful_payment(self):
1187
- """Decorator for handling successful payment updates.
1188
-
1189
- Returns:
1190
- Callable: A decorator function that registers the callback for successful payment updates.
1191
- """
1192
- return self.base_handler_decorator(UpdatesTypes.SUCCESSFUL_PAYMENT)
1193
-
1194
- def _convert_event(self, handler_type: UpdatesTypes, event: Dict[str, Any]) -> Any:
1195
- """Convert raw event data to appropriate object type.
1196
-
1197
- Args:
1198
- handler_type (UpdatesTypes): Type of the update
1199
- event (Dict[str, Any]): Raw event data
1200
-
1201
- Returns:
1202
- Any: Converted event object
1203
- """
1204
- if handler_type in (
1205
- UpdatesTypes.MESSAGE,
1206
- UpdatesTypes.MESSAGE_EDITED,
1207
- UpdatesTypes.MEMBER_JOINED,
1208
- UpdatesTypes.MEMBER_LEFT,
1209
- UpdatesTypes.SUCCESSFUL_PAYMENT,
1210
- UpdatesTypes.COMMAND
1211
- ):
1212
- if (
1213
- event.get("new_chat_member", False)
1214
- and handler_type == UpdatesTypes.MEMBER_JOINED
1215
- ):
1216
- return (
1217
- ChatMember(
1218
- kwargs={"client": self},
1219
- **pythonize(event.get("new_chat_member", {})),
1220
- ),
1221
- Chat(kwargs={"client": self}, **pythonize(event.get("chat", {}))),
1222
- Message(
1223
- kwargs={"client": self}, **pythonize(event.get("message", {}))
1224
- ),
1225
- )
1226
- elif (
1227
- event.get("left_chat_member", False)
1228
- and handler_type == UpdatesTypes.MEMBER_LEFT
1229
- ):
1230
- return (
1231
- ChatMember(
1232
- kwargs={"client": self},
1233
- **pythonize(event.get("left_chat_member", {})),
1234
- ),
1235
- Chat(kwargs={"client": self}, **pythonize(event.get("chat", {}))),
1236
- Message(
1237
- kwargs={"client": self}, **pythonize(event.get("message", {}))
1238
- ),
1239
- )
849
+ def on_callback_query(self, filter: Optional[Filters] = None):
850
+ """Decorator for handling callback query updates."""
851
+ return self.base_handler_decorator(UpdatesTypes.CALLBACK_QUERY)(filter)
1240
852
 
1241
- elif (
1242
- event.get("successful_payment", False)
1243
- and handler_type == UpdatesTypes.SUCCESSFUL_PAYMENT
1244
- ):
1245
- return Message(
1246
- **pythonize(event.get("successful_payment", {})),
1247
- kwargs={"client": self},
1248
- )
853
+ def on_new_members(self, filter: Optional[Filters] = None):
854
+ """Decorator for handling new chat members updates."""
855
+ return self.base_handler_decorator(UpdatesTypes.MEMBER_JOINED)(filter)
1249
856
 
1250
- else:
1251
- return Message(kwargs={"client": self}, **pythonize(event))
857
+ def on_members_left(self, filter: Optional[Filters] = None):
858
+ """Decorator for handling members left updates."""
859
+ return self.base_handler_decorator(UpdatesTypes.MEMBER_LEFT)(filter)
1252
860
 
1253
- elif handler_type == UpdatesTypes.CALLBACK_QUERY:
1254
- return CallbackQuery(kwargs={"client": self}, **pythonize(event))
861
+ def on_pre_checkout_query(self, filter: Optional[Filters] = None):
862
+ """Decorator for handling pre-checkout query updates."""
863
+ return self.base_handler_decorator(UpdatesTypes.PRE_CHECKOUT_QUERY)(filter)
1255
864
 
1256
- elif handler_type == UpdatesTypes.PRE_CHECKOUT_QUERY:
1257
- return PreCheckoutQuery(kwargs={"client": self}, **pythonize(event))
865
+ def on_photo(self, filter: Optional[Filters] = None):
866
+ """Decorator for handling photo updates."""
867
+ return self.base_handler_decorator(UpdatesTypes.PHOTO)(filter)
1258
868
 
1259
- return event
869
+ def on_successful_payment(self, filter: Optional[Filters] = None):
870
+ """Decorator for handling successful payment updates."""
871
+ return self.base_handler_decorator(UpdatesTypes.SUCCESSFUL_PAYMENT)(filter)
1260
872
 
1261
-
1262
- def add_handler(self, update_type, callback, filter: Optional[Filters] = None, **kwargs):
1263
- """Register a handler for specific update type.
1264
-
1265
- Args:
1266
- update_type (UpdatesTypes): Type of update to handle
1267
- callback (Callable): Function to call when update is received
1268
- """
1269
- data = {
873
+ def add_handler(self, update_type: UpdatesTypes, callback: Callable, filter: Optional[Filters] = None, **kwargs):
874
+ """Register a handler for specific update type."""
875
+ handler_data = {
1270
876
  "type": update_type,
1271
877
  "callback": callback,
1272
878
  "filter": filter,
1273
879
  }
1274
- data.update(kwargs)
1275
- self.handlers.append(data)
1276
-
880
+ handler_data.update(kwargs)
881
+ self.handlers.append(handler_data)
1277
882
 
1278
- def remove_handler(
1279
- self, callback: Callable[[Any], Union[None, Awaitable[None]]]
1280
- ) -> None:
1281
- """Remove a handler from the list of handlers.
1282
-
1283
- Args:
1284
- callback (Callable): Handler function to remove
1285
- """
883
+ def remove_handler(self, callback: Callable) -> None:
884
+ """Remove a handler from the list of handlers."""
1286
885
  self.handlers = [
1287
886
  handler for handler in self.handlers if handler["callback"] != callback
1288
887
  ]
@@ -1292,15 +891,7 @@ class Client:
1292
891
  self.handlers = []
1293
892
 
1294
893
  async def start_polling(self, timeout: int = 30, limit: int = 100) -> None:
1295
- """Start polling updates from the server.
1296
-
1297
- Args:
1298
- timeout (int, optional): Timeout in seconds for long polling. Defaults to 30.
1299
- limit (int, optional): Maximum number of updates to retrieve. Defaults to 100.
1300
-
1301
- Raises:
1302
- RuntimeError: If client is already running
1303
- """
894
+ """Start polling updates from the server."""
1304
895
  if self.running:
1305
896
  raise RuntimeError("Client is already running")
1306
897
 
@@ -1313,33 +904,26 @@ class Client:
1313
904
 
1314
905
  for update in updates:
1315
906
  await self.process_update(update)
1316
-
1317
- except Exception as e:
1318
- raise e
1319
907
 
908
+ except Exception as e:
909
+ print(f"Error in polling: {e}")
910
+ await asyncio.sleep(1)
911
+
1320
912
  async def stop_polling(self) -> None:
1321
913
  """Stop polling updates."""
1322
914
  self.running = False
1323
915
 
1324
916
  def run(self, timeout: int = 30, limit: int = 100) -> None:
1325
- """Run the client.
1326
-
1327
- Args:
1328
- timeout (int, optional): Timeout in seconds for long polling. Defaults to 30.
1329
- limit (int, optional): Maximum number of updates to retrieve. Defaults to 100.
1330
- """
1331
- loop = asyncio.get_event_loop()
1332
- loop.run_until_complete(self.start_polling(timeout, limit))
917
+ """Run the client."""
918
+ try:
919
+ asyncio.run(self.start_polling(timeout, limit))
920
+ except KeyboardInterrupt:
921
+ print("Bot stopped by user")
1333
922
 
1334
- def stop(self) -> None:
923
+ async def stop(self) -> None:
1335
924
  """Stop the client."""
1336
- loop = asyncio.get_event_loop()
1337
- loop.run_until_complete(self.stop_polling())
925
+ await self.stop_polling()
1338
926
 
1339
927
  async def handle_webhook_update(self, update_data: Dict[str, Any]) -> None:
1340
- """Process an update received via webhook.
1341
-
1342
- Args:
1343
- update_data (Dict[str, Any]): Update data received from webhook
1344
- """
1345
- await self.process_update(update_data)
928
+ """Process an update received via webhook."""
929
+ await self.process_update(update_data)