Rubka 2.11.13__py3-none-any.whl → 7.1.17__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.
Files changed (41) hide show
  1. rubka/__init__.py +72 -3
  2. rubka/adaptorrubka/__init__.py +4 -0
  3. rubka/adaptorrubka/client/__init__.py +1 -0
  4. rubka/adaptorrubka/client/client.py +60 -0
  5. rubka/adaptorrubka/crypto/__init__.py +1 -0
  6. rubka/adaptorrubka/crypto/crypto.py +82 -0
  7. rubka/adaptorrubka/enums.py +36 -0
  8. rubka/adaptorrubka/exceptions.py +22 -0
  9. rubka/adaptorrubka/methods/__init__.py +1 -0
  10. rubka/adaptorrubka/methods/methods.py +90 -0
  11. rubka/adaptorrubka/network/__init__.py +3 -0
  12. rubka/adaptorrubka/network/helper.py +22 -0
  13. rubka/adaptorrubka/network/network.py +221 -0
  14. rubka/adaptorrubka/network/socket.py +31 -0
  15. rubka/adaptorrubka/sessions/__init__.py +1 -0
  16. rubka/adaptorrubka/sessions/sessions.py +72 -0
  17. rubka/adaptorrubka/types/__init__.py +1 -0
  18. rubka/adaptorrubka/types/socket/__init__.py +1 -0
  19. rubka/adaptorrubka/types/socket/message.py +187 -0
  20. rubka/adaptorrubka/utils/__init__.py +2 -0
  21. rubka/adaptorrubka/utils/configs.py +18 -0
  22. rubka/adaptorrubka/utils/utils.py +251 -0
  23. rubka/api.py +1450 -95
  24. rubka/asynco.py +2515 -0
  25. rubka/button.py +404 -0
  26. rubka/context.py +744 -34
  27. rubka/exceptions.py +35 -1
  28. rubka/filters.py +321 -0
  29. rubka/helpers.py +1461 -0
  30. rubka/keypad.py +255 -5
  31. rubka/metadata.py +117 -0
  32. rubka/rubino.py +1231 -0
  33. rubka/tv.py +145 -0
  34. rubka/update.py +1038 -0
  35. rubka-7.1.17.dist-info/METADATA +1048 -0
  36. rubka-7.1.17.dist-info/RECORD +45 -0
  37. rubka-7.1.17.dist-info/entry_points.txt +2 -0
  38. rubka-2.11.13.dist-info/METADATA +0 -315
  39. rubka-2.11.13.dist-info/RECORD +0 -15
  40. {rubka-2.11.13.dist-info → rubka-7.1.17.dist-info}/WHEEL +0 -0
  41. {rubka-2.11.13.dist-info → rubka-7.1.17.dist-info}/top_level.txt +0 -0
rubka/context.py CHANGED
@@ -1,5 +1,68 @@
1
- from typing import Any, Dict, List,Optional
1
+ from typing import Union
2
+ from pathlib import Path
3
+ from typing import List, Optional, Dict, Any, Literal, Callable, Union,Set
4
+ import re,inspect
5
+ import asyncio
6
+ _EMOJI_PATTERN = re.compile(
7
+ "["
8
+ "\U0001F300-\U0001F5FF"
9
+ "\U0001F600-\U0001F64F"
10
+ "\U0001F680-\U0001F6FF"
11
+ "\U0001F700-\U0001F77F"
12
+ "\U0001F780-\U0001F7FF"
13
+ "\U0001F800-\U0001F8FF"
14
+ "\U0001F900-\U0001F9FF"
15
+ "\U0001FA00-\U0001FA6F"
16
+ "\U0001FA70-\U0001FAFF"
17
+ "\U00002700-\U000027BF"
18
+ "\U00002600-\U000026FF"
19
+ "\U00002000-\U000023FF"
20
+ "]+",
21
+ flags=re.UNICODE
22
+ )
2
23
 
24
+ def contains_emoji(text: str) -> bool:
25
+ if not text:return False
26
+ return bool(_EMOJI_PATTERN.search(text))
27
+
28
+ def count_emojis(text: str) -> int:
29
+ if not text:return 0
30
+ return len(_EMOJI_PATTERN.findall(text))
31
+ def contains_link_or_mention(text: str) -> bool:
32
+ if not text:return False
33
+ pattern = re.compile(
34
+ r"(?i)\b("
35
+ r"https?://[^\s]+"
36
+ r"|www\.[^\s]+"
37
+ r"|[a-zA-Z0-9.-]+\.(com|net|org|ir|edu|gov|info|biz|io|me|co|xyz|in|us|uk|ru|tv|cc|app|dev|site|store|cloud|shop)"
38
+ r"|t\.me/[^\s]+"
39
+ r"|telegram\.me/[^\s]+"
40
+ r"|rubika\.(ir|app)/[^\s]+"
41
+ r"|whatsapp\.com/[^\s]+"
42
+ r"|wa\.me/[^\s]+"
43
+ r"|instagram\.com/[^\s]+"
44
+ r"|instagr\.am/[^\s]+"
45
+ r"|youtube\.com/[^\s]+"
46
+ r"|youtu\.be/[^\s]+"
47
+ r"|aparat\.com/[^\s]+"
48
+ r"|discord\.gg/[^\s]+"
49
+ r"|discord\.com/[^\s]+"
50
+ r"|splus\.ir/[^\s]+"
51
+ r"|eitaa\.com/[^\s]+"
52
+ r"|ble\.ir/[^\s]+"
53
+ r"|gap\.im/[^\s]+"
54
+ r"|rubino\.ir/[^\s]+"
55
+ r"|pin\.it/[^\s]+"
56
+ r"|twitter\.com/[^\s]+"
57
+ r"|x\.com/[^\s]+"
58
+ r"|facebook\.com/[^\s]+"
59
+ r"|threads\.net/[^\s]+"
60
+ r"|@\w+"
61
+ r"|\b\d{1,3}(?:\.\d{1,3}){3}\b"
62
+ r"|\[?[A-F0-9:]{2,39}\]?"
63
+ r")\b"
64
+ )
65
+ return bool(pattern.search(text))
3
66
  class File:
4
67
  def __init__(self, data: dict):
5
68
  self.file_id: str = data.get("file_id")
@@ -14,9 +77,9 @@ class Sticker:
14
77
  self.file = File(data.get("file", {}))
15
78
 
16
79
 
17
- # =========================
18
- # Poll
19
- # =========================
80
+
81
+
82
+
20
83
  class PollStatus:
21
84
  def __init__(self, data: dict):
22
85
  self.state: str = data.get("state")
@@ -33,9 +96,9 @@ class Poll:
33
96
  self.poll_status = PollStatus(data.get("poll_status", {}))
34
97
 
35
98
 
36
- # =========================
37
- # Location & Contact & ForwardedFrom
38
- # =========================
99
+
100
+
101
+
39
102
  class Location:
40
103
  def __init__(self, data: dict):
41
104
  self.latitude: str = data.get("latitude")
@@ -67,18 +130,18 @@ class ForwardedFrom:
67
130
  self.from_sender_id: str = data.get("from_sender_id")
68
131
 
69
132
 
70
- # =========================
71
- # AuxData
72
- # =========================
133
+
134
+
135
+
73
136
  class AuxData:
74
137
  def __init__(self, data: dict):
75
138
  self.start_id: str = data.get("start_id")
76
139
  self.button_id: str = data.get("button_id")
77
140
 
78
141
 
79
- # =========================
80
- # Button Models
81
- # =========================
142
+
143
+
144
+
82
145
  class ButtonTextbox:
83
146
  def __init__(self, data: dict):
84
147
  self.type_line: str = data.get("type_line")
@@ -184,19 +247,90 @@ class Bot:
184
247
  self.username: str = data.get("username")
185
248
  self.start_message: str = data.get("start_message")
186
249
  self.share_url: str = data.get("share_url")
250
+
251
+ class hybrid_property:
252
+ def __init__(self, func):
253
+ self.func = func
254
+
255
+ def __get__(self, instance, owner):
256
+ if instance is None:
257
+ return self
258
+ coro = self.func(instance)
259
+ try:
260
+ loop = asyncio.get_running_loop()
261
+ return coro
262
+ except RuntimeError:
263
+ try:
264
+ loop = asyncio.get_event_loop()
265
+ except RuntimeError:
266
+ loop = asyncio.new_event_loop()
267
+ asyncio.set_event_loop(loop)
268
+ return loop.run_until_complete(coro)
187
269
  class Message:
188
270
  def __init__(self, bot, chat_id, message_id, sender_id, text=None, raw_data=None):
189
271
  self.bot = bot
190
- self.chat_id = chat_id
191
272
  self.raw_data = raw_data or {}
273
+ self.chat_id = chat_id
274
+ self.object_guid = chat_id
275
+ self.author_guid = self.raw_data.get("sender_id", sender_id)
192
276
  self.message_id: str = self.raw_data.get("message_id", message_id)
193
277
  self.text: str = self.raw_data.get("text", text)
278
+ self.has_link = contains_link_or_mention(self.raw_data.get("text", text))
279
+ self.is_link = self.has_link
280
+ self.is_mention = bool(re.search(r"@\w+", self.raw_data.get("text", text) or ""))
281
+ self.is_hashtag = bool(re.search(r"#\w+", text or ""))
282
+ self.is_number = bool(re.search(r"\d+", text or ""))
283
+ self.is_emoji = contains_emoji(self.raw_data.get("text", text))
284
+ self.emoji_count = count_emojis(self.raw_data.get("text", text))
285
+ self.is_pure_emoji = bool(self.is_emoji and self.emoji_count == len(self.text))
286
+ self.is_photo = None
287
+ self.is_video = None
288
+ self.is_audio = None
289
+ self.is_voice = None
290
+ self.is_document = None
291
+ self.is_archive = None
292
+ self.is_executable = None
293
+ self.is_font = None
294
+ self.metadata = self.raw_data.get("metadata", {})
295
+ self.is_metadata = self.metadata
296
+ self.meta_parts = self.metadata.get("meta_data_parts", [])
297
+ self.has_metadata = bool(self.meta_parts)
298
+ self.meta_types = [part.get("type") for part in self.meta_parts] if self.has_metadata else []
299
+ self.is_bold = "Bold" in self.meta_types
300
+ self.is_italic = "Italic" in self.meta_types
301
+ self.is_strike = "Strike" in self.meta_types
302
+ self.is_underline = "Underline" in self.meta_types
303
+ self.is_quote = "Quote" in self.meta_types
304
+ self.is_spoiler = "Spoiler" in self.meta_types
305
+ self.is_pre = "Pre" in self.meta_types
306
+ self.is_mono = "Mono" in self.meta_types
307
+ self.is_link_meta = "Link" in self.meta_types
308
+ self.meta_links = [part.get("link_url") for part in self.meta_parts if part.get("type") == "Link"]
309
+ self.meta_link_positions = [
310
+ {"from": part.get("from_index"), "length": part.get("length"), "url": part.get("link_url")}
311
+ for part in self.meta_parts if part.get("type") == "Link"
312
+ ]
313
+ self.has_link = contains_link_or_mention(self.text) or self.is_link_meta
314
+ self.is_formatted = any([
315
+ self.is_bold,
316
+ self.is_italic,
317
+ self.is_strike,
318
+ self.is_underline,
319
+ self.is_quote,
320
+ self.is_spoiler,
321
+ self.is_pre,
322
+ self.is_mono
323
+ ])
194
324
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
195
325
  self.time: str = self.raw_data.get("time")
196
326
  self.is_edited: bool = self.raw_data.get("is_edited", False)
197
327
  self.sender_type: str = self.raw_data.get("sender_type")
198
-
199
328
  self.args = []
329
+ self.is_command = bool(self.text and self.text.startswith("/"))
330
+ self.is_user = self.chat_id.startswith("b")
331
+ self.is_private = self.chat_id.startswith("b")
332
+ self.is_group = self.chat_id.startswith("g")
333
+ self.is_channel = self.chat_id.startswith("c")
200
334
  self.reply_to_message_id: Optional[str] = self.raw_data.get("reply_to_message_id")
201
335
  self.forwarded_from = ForwardedFrom(self.raw_data["forwarded_from"]) if "forwarded_from" in self.raw_data else None
202
336
  self.file = File(self.raw_data["file"]) if "file" in self.raw_data else None
@@ -206,28 +340,282 @@ class Message:
206
340
  self.location = Location(self.raw_data["location"]) if "location" in self.raw_data else None
207
341
  self.live_location = LiveLocation(self.raw_data["live_location"]) if "live_location" in self.raw_data else None
208
342
  self.aux_data = AuxData(self.raw_data["aux_data"]) if "aux_data" in self.raw_data else None
209
-
343
+ self.is_reply = self.reply_to_message_id is not None
344
+ self.has_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
345
+ self.is_forwarded = self.forwarded_from is not None
346
+ self.is_text = bool(self.text and not self.has_media)
347
+ self.is_media = self.has_media
348
+ self.is_poll = self.poll is not None
349
+ self.is_location = self.location is not None
350
+ self.is_live_location = self.live_location is not None
351
+ self.is_contact = self.contact_message is not None
352
+ self.has_any_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
353
+ self.edited_text = self.raw_data.get("edited_text") if self.is_edited else None
354
+ if self.file and self.file.file_name:
355
+ name = self.file.file_name.lower()
356
+ self.is_photo = name.endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))
357
+ self.is_video = name.endswith((".mp4", ".mov", ".avi", ".mkv", ".webm"))
358
+ self.is_audio = name.endswith((".mp3", ".wav", ".ogg", ".m4a", ".flac"))
359
+ self.is_voice = name.endswith((".ogg", ".m4a"))
360
+ self.is_document = name.endswith((".pdf", ".doc", ".docx", ".txt", ".xls", ".xlsx", ".ppt", ".pptx"))
361
+ self.is_archive = name.endswith((".zip", ".rar", ".7z", ".tar", ".gz"))
362
+ self.is_executable = name.endswith((".exe", ".msi", ".bat", ".sh"))
363
+ self.is_font = name.endswith((".ttf", ".otf", ".woff", ".woff2"))
210
364
  @property
211
365
  def session(self):
212
366
  if self.chat_id not in self.bot.sessions:
213
367
  self.bot.sessions[self.chat_id] = {}
214
368
  return self.bot.sessions[self.chat_id]
215
- def reply(self, text: str, **kwargs):
369
+
370
+ def reply(self, text: str, delete_after: int = None,parse_mode : Optional[Literal["HTML", "Markdown"]] = None , **kwargs):
371
+ async def _reply_async():
372
+ send_func = self.bot.send_message
373
+ if inspect.iscoroutinefunction(send_func):
374
+ msg = await send_func(
375
+ self.chat_id,
376
+ text,
377
+ reply_to_message_id=self.message_id,
378
+ delete_after=delete_after,
379
+ parse_mode=parse_mode,
380
+ **kwargs
381
+ )
382
+ else:
383
+ msg = send_func(
384
+ self.chat_id,
385
+ text,
386
+ reply_to_message_id=self.message_id,
387
+ delete_after=delete_after,
388
+ **kwargs
389
+ )
390
+ class Pick:
391
+ def __init__(self, bot, chat_id, message_id):
392
+ self.bot = bot
393
+ self.chat_id = chat_id
394
+ self.message_id = message_id
395
+
396
+ def edit(self, new_text):
397
+ async def _edit():
398
+ func = self.bot.edit_message_text
399
+ if inspect.iscoroutinefunction(func):
400
+ await func(self.chat_id, self.message_id, new_text)
401
+ else:
402
+ func(self.chat_id, self.message_id, new_text)
403
+
404
+ try:
405
+ loop = asyncio.get_running_loop()
406
+ if loop.is_running():
407
+ return asyncio.create_task(_edit())
408
+ except RuntimeError:
409
+ return asyncio.run(_edit())
410
+
411
+ def delete(self):
412
+ async def _delete():
413
+ func = self.bot.delete_message
414
+ if inspect.iscoroutinefunction(func):
415
+ await func(self.chat_id, self.message_id)
416
+ else:
417
+ func(self.chat_id, self.message_id)
418
+ try:
419
+ loop = asyncio.get_running_loop()
420
+ if loop.is_running():
421
+ return asyncio.create_task(_delete())
422
+ except RuntimeError:
423
+ return asyncio.run(_delete())
424
+ chat_id = msg.get("chat_id") if isinstance(msg, dict) else getattr(msg, "chat_id", self.chat_id)
425
+ message_id = msg.get("message_id") if isinstance(msg, dict) else getattr(msg, "message_id", self.message_id)
426
+ return Pick(self.bot, chat_id, message_id)
427
+ try:
428
+ loop = asyncio.get_running_loop()
429
+ if loop.is_running():
430
+ return asyncio.create_task(_reply_async())
431
+ except RuntimeError:
432
+ return asyncio.run(_reply_async())
433
+ def answer(self, text: str, **kwargs):
216
434
  return self.bot.send_message(
217
435
  self.chat_id,
218
436
  text,
219
437
  reply_to_message_id=self.message_id,
220
438
  **kwargs
221
439
  )
440
+
222
441
 
223
- def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
224
- return self.bot._post("sendPoll", {
442
+ def reply_poll(
443
+ self,
444
+ question: str,
445
+ options: List[str],
446
+ type: Literal['Regular', 'Quiz'] = "Regular",
447
+ allows_multiple_answers: bool = False,
448
+ is_anonymous: bool = True,
449
+ correct_option_index: Optional[int] = None,
450
+ hint: Optional[str] = None,
451
+ reply_to_message_id: Optional[str] = None,
452
+ disable_notification: bool = False,
453
+ show_results: bool = False,
454
+ inline_keypad: Optional[Dict[str, Any]] = None,
455
+ chat_keypad: Optional[Dict[str, Any]] = None,
456
+ chat_keypad_type: Optional[Literal['New', 'Remove', 'None']] = None,
457
+ **kwargs
458
+ ) -> dict:
459
+ payload = {
225
460
  "chat_id": self.chat_id,
226
461
  "question": question,
227
462
  "options": options,
228
- "reply_to_message_id": self.message_id,
229
- **kwargs
230
- })
463
+ "type": type,
464
+ "allows_multiple_answers": allows_multiple_answers,
465
+ "is_anonymous": is_anonymous,
466
+ "correct_option_index": correct_option_index,
467
+ "hint": hint,
468
+ "reply_to_message_id": self.message_id if not reply_to_message_id else reply_to_message_id,
469
+ "disable_notification": disable_notification,
470
+ "show_results": show_results,
471
+ "inline_keypad": inline_keypad,
472
+ "chat_keypad": chat_keypad,
473
+ "chat_keypad_type": chat_keypad_type,
474
+ }
475
+ return self.bot._post("sendPoll", {key: value for key, value in payload.items() if value is not None}, **kwargs)
476
+
477
+
478
+ def reply_document(
479
+ self,
480
+ path: Optional[Union[str, Path]] = None,
481
+ file_id: Optional[str] = None,
482
+ text: Optional[str] = None,
483
+ chat_keypad: Optional[Dict[str, Any]] = None,
484
+ inline_keypad: Optional[Dict[str, Any]] = None,
485
+ chat_keypad_type: Optional[str] = "None",
486
+ disable_notification: bool = False
487
+ ):
488
+ if chat_keypad and chat_keypad_type == "none":chat_keypad_type == "New"
489
+ return self.bot.send_document(
490
+ chat_id=self.chat_id,
491
+ path=path,
492
+ file_id=file_id,
493
+ text=text,
494
+ chat_keypad=chat_keypad,
495
+ inline_keypad=inline_keypad,
496
+ chat_keypad_type=chat_keypad_type,
497
+ disable_notification=disable_notification,
498
+ reply_to_message_id=self.message_id
499
+ )
500
+ def reply_file(
501
+ self,
502
+ path: Optional[Union[str, Path]] = None,
503
+ file_id: Optional[str] = None,
504
+ text: Optional[str] = None,
505
+ chat_keypad: Optional[Dict[str, Any]] = None,
506
+ inline_keypad: Optional[Dict[str, Any]] = None,
507
+ chat_keypad_type: Optional[str] = "None",
508
+ disable_notification: bool = False
509
+ ):
510
+ if chat_keypad and chat_keypad_type == "none":
511
+ chat_keypad_type == "New"
512
+
513
+ return self.bot.send_document(
514
+ chat_id=self.chat_id,
515
+ path=path,
516
+ file_id=file_id,
517
+ text=text,
518
+ chat_keypad=chat_keypad,
519
+ inline_keypad=inline_keypad,
520
+ chat_keypad_type=chat_keypad_type,
521
+ disable_notification=disable_notification,
522
+ reply_to_message_id=self.message_id
523
+ )
524
+
525
+ def reply_image(
526
+ self,
527
+ path: Optional[Union[str, Path]] = None,
528
+ file_id: Optional[str] = None,
529
+ text: Optional[str] = None,
530
+ chat_keypad: Optional[Dict[str, Any]] = None,
531
+ inline_keypad: Optional[Dict[str, Any]] = None,
532
+ chat_keypad_type: Optional[str] = "None",
533
+ disable_notification: bool = False
534
+ ):
535
+ if chat_keypad and chat_keypad_type == "none":
536
+ chat_keypad_type == "New"
537
+ return self.bot.send_image(
538
+ chat_id=self.chat_id,
539
+ path=path,
540
+ file_id=file_id,
541
+ text=text,
542
+ chat_keypad=chat_keypad,
543
+ inline_keypad=inline_keypad,
544
+ chat_keypad_type=chat_keypad_type,
545
+ disable_notification=disable_notification,
546
+ reply_to_message_id=self.message_id
547
+ )
548
+
549
+ def reply_music(
550
+ self,
551
+ path: Optional[Union[str, Path]] = None,
552
+ file_id: Optional[str] = None,
553
+ text: Optional[str] = None,
554
+ chat_keypad: Optional[Dict[str, Any]] = None,
555
+ inline_keypad: Optional[Dict[str, Any]] = None,
556
+ chat_keypad_type: Optional[str] = "None",
557
+ disable_notification: bool = False
558
+ ):
559
+ if chat_keypad and chat_keypad_type == "none":
560
+ chat_keypad_type == "New"
561
+ return self.bot.send_music(
562
+ chat_id=self.chat_id,
563
+ path=path,
564
+ file_id=file_id,
565
+ text=text,
566
+ chat_keypad=chat_keypad,
567
+ inline_keypad=inline_keypad,
568
+ chat_keypad_type=chat_keypad_type,
569
+ disable_notification=disable_notification,
570
+ reply_to_message_id=self.message_id
571
+ )
572
+
573
+ def reply_voice(
574
+ self,
575
+ path: Optional[Union[str, Path]] = None,
576
+ file_id: Optional[str] = None,
577
+ text: Optional[str] = None,
578
+ chat_keypad: Optional[Dict[str, Any]] = None,
579
+ inline_keypad: Optional[Dict[str, Any]] = None,
580
+ chat_keypad_type: Optional[str] = "None",
581
+ disable_notification: bool = False
582
+ ):
583
+ if chat_keypad and chat_keypad_type == "none":
584
+ chat_keypad_type == "New"
585
+ return self.bot.send_voice(
586
+ chat_id=self.chat_id,
587
+ path=path,
588
+ file_id=file_id,
589
+ text=text,
590
+ chat_keypad=chat_keypad,
591
+ inline_keypad=inline_keypad,
592
+ chat_keypad_type=chat_keypad_type,
593
+ disable_notification=disable_notification,
594
+ reply_to_message_id=self.message_id
595
+ )
596
+
597
+ def reply_gif(
598
+ self,
599
+ path: Optional[Union[str, Path]] = None,
600
+ file_id: Optional[str] = None,
601
+ text: Optional[str] = None,
602
+ chat_keypad: Optional[Dict[str, Any]] = None,
603
+ inline_keypad: Optional[Dict[str, Any]] = None,
604
+ chat_keypad_type: Optional[str] = "None",
605
+ disable_notification: bool = False
606
+ ):
607
+ if chat_keypad and chat_keypad_type == "none":chat_keypad_type == "New"
608
+ return self.bot.send_gif(
609
+ chat_id=self.chat_id,
610
+ path=path,
611
+ file_id=file_id,
612
+ text=text,
613
+ chat_keypad=chat_keypad,
614
+ inline_keypad=inline_keypad,
615
+ chat_keypad_type=chat_keypad_type,
616
+ disable_notification=disable_notification,
617
+ reply_to_message_id=self.message_id
618
+ )
231
619
 
232
620
  def reply_location(self, latitude: str, longitude: str, **kwargs) -> Dict[str, Any]:
233
621
  return self.bot.send_location(
@@ -275,14 +663,6 @@ class Message:
275
663
  **kwargs
276
664
  })
277
665
 
278
- def reply_file(self, file_id: str, **kwargs) -> Dict[str, Any]:
279
- return self.bot._post("sendFile", {
280
- "chat_id": self.chat_id,
281
- "file_id": file_id,
282
- "reply_to_message_id": self.message_id,
283
- **kwargs
284
- })
285
-
286
666
  def edit(self, new_text: str) -> Dict[str, Any]:
287
667
  return self.bot.edit_message_text(
288
668
  chat_id=self.chat_id,
@@ -295,34 +675,364 @@ class Message:
295
675
  chat_id=self.chat_id,
296
676
  message_id=self.message_id
297
677
  )
678
+ @hybrid_property
679
+ async def author_name(self):return await self.bot.get_name(self.chat_id)
680
+ @hybrid_property
681
+ async def name(self):return await self.bot.get_name(self.chat_id)
682
+ @hybrid_property
683
+ async def username(self):return await self.bot.get_username(self.chat_id)
684
+ @hybrid_property
685
+ async def author_info(self):return await self.bot.get_chat(self.chat_id)
686
+ class AuxData:
687
+ def __init__(self, data: dict):
688
+ self.start_id = data.get("start_id")
689
+ self.button_id = data.get("button_id")
690
+
691
+
298
692
  class InlineMessage:
299
693
  def __init__(self, bot, raw_data: dict):
300
694
  self.bot = bot
301
695
  self.raw_data = raw_data
302
-
696
+ chat_id : str = raw_data.get("chat_id")
697
+ sender_id : str = raw_data.get("sender_id")
303
698
  self.chat_id: str = raw_data.get("chat_id")
304
699
  self.message_id: str = raw_data.get("message_id")
305
700
  self.sender_id: str = raw_data.get("sender_id")
306
701
  self.text: str = raw_data.get("text")
307
702
  self.aux_data = AuxData(raw_data.get("aux_data", {})) if "aux_data" in raw_data else None
703
+ self.bot = bot
704
+ self.raw_data = raw_data or {}
705
+ self.object_guid = chat_id
706
+ self.author_guid = self.raw_data.get("sender_id", sender_id)
707
+ self.has_link = bool(re.search(r"(https?://[^\s]+ | www\.[^\s]+ | [a-zA-Z0-9.-]+\.(com|net|org|ir|edu|gov|info|biz|io|me|co) | t\.me/[^\s]+ | telegram\.me/[^\s]+ | @\w+ | \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)", self.text))
708
+ self.sender_id: str = self.raw_data.get("sender_id", sender_id)
709
+ self.time: str = self.raw_data.get("time")
710
+ self.is_edited: bool = self.raw_data.get("is_edited", False)
711
+ self.sender_type: str = self.raw_data.get("sender_type")
712
+ self.args = []
713
+ self.is_command = bool(self.text and self.text.startswith("/"))
714
+ self.is_user = self.chat_id.startswith("b")
715
+ self.is_private = self.chat_id.startswith("b")
716
+ self.is_group = self.chat_id.startswith("g")
717
+ self.is_channel = self.chat_id.startswith("c")
718
+ self.reply_to_message_id: Optional[str] = self.raw_data.get("reply_to_message_id")
719
+ self.forwarded_from = ForwardedFrom(self.raw_data["forwarded_from"]) if "forwarded_from" in self.raw_data else None
720
+ self.file = File(self.raw_data["file"]) if "file" in self.raw_data else None
721
+ self.sticker = Sticker(self.raw_data["sticker"]) if "sticker" in self.raw_data else None
722
+ self.contact_message = ContactMessage(self.raw_data["contact_message"]) if "contact_message" in self.raw_data else None
723
+ self.aux_data = AuxData(self.raw_data["aux_data"]) if "aux_data" in self.raw_data else None
724
+ self.is_reply = self.reply_to_message_id is not None
725
+ self.has_media = any([self.file, self.sticker])
726
+ self.is_forwarded = self.forwarded_from is not None
727
+ self.is_text = bool(self.text and not self.has_media)
728
+ self.is_media = self.has_media
729
+ self.is_contact = self.contact_message is not None
730
+ self.has_any_media = any([self.file, self.sticker,])
731
+ self.edited_text = self.raw_data.get("edited_text") if self.is_edited else None
732
+ if self.file and self.file.file_name:
733
+ name = self.file.file_name.lower()
734
+ self.is_photo = name.endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))
735
+ self.is_video = name.endswith((".mp4", ".mov", ".avi", ".mkv", ".webm"))
736
+ self.is_audio = name.endswith((".mp3", ".wav", ".ogg", ".m4a", ".flac"))
737
+ self.is_voice = name.endswith((".ogg", ".m4a"))
738
+ self.is_document = name.endswith((".pdf", ".doc", ".docx", ".txt", ".xls", ".xlsx", ".ppt", ".pptx"))
739
+ self.is_archive = name.endswith((".zip", ".rar", ".7z", ".tar", ".gz"))
740
+ self.is_executable = name.endswith((".exe", ".msi", ".bat", ".sh"))
741
+ self.is_font = name.endswith((".ttf", ".otf", ".woff", ".woff2"))
742
+
743
+
744
+ @property
745
+ def session(self):
746
+ if self.chat_id not in self.bot.sessions:
747
+ self.bot.sessions[self.chat_id] = {}
748
+ return self.bot.sessions[self.chat_id]
749
+
750
+ def reply(self, text: str, delete_after: int = None, **kwargs):
751
+ async def _reply_async():
752
+ send_func = self.bot.send_message
753
+ if inspect.iscoroutinefunction(send_func):
754
+ msg = await send_func(
755
+ self.chat_id,
756
+ text,
757
+ reply_to_message_id=self.message_id,
758
+ delete_after=delete_after,
759
+ **kwargs
760
+ )
761
+ else:
762
+ msg = send_func(
763
+ self.chat_id,
764
+ text,
765
+ reply_to_message_id=self.message_id,
766
+ delete_after=delete_after,
767
+ **kwargs
768
+ )
769
+ class Pick:
770
+ def __init__(self, bot, chat_id, message_id):
771
+ self.bot = bot
772
+ self.chat_id = chat_id
773
+ self.message_id = message_id
774
+
775
+ def edit(self, new_text):
776
+ async def _edit():
777
+ func = self.bot.edit_message_text
778
+ if inspect.iscoroutinefunction(func):
779
+ await func(self.chat_id, self.message_id, new_text)
780
+ else:
781
+ func(self.chat_id, self.message_id, new_text)
782
+
783
+ try:
784
+ loop = asyncio.get_running_loop()
785
+ if loop.is_running():
786
+ return asyncio.create_task(_edit())
787
+ except RuntimeError:
788
+ return asyncio.run(_edit())
308
789
 
309
- def reply(self, text: str, **kwargs):
790
+ def delete(self):
791
+ async def _delete():
792
+ func = self.bot.delete_message
793
+ if inspect.iscoroutinefunction(func):
794
+ await func(self.chat_id, self.message_id)
795
+ else:
796
+ func(self.chat_id, self.message_id)
797
+ try:
798
+ loop = asyncio.get_running_loop()
799
+ if loop.is_running():
800
+ return asyncio.create_task(_delete())
801
+ except RuntimeError:
802
+ return asyncio.run(_delete())
803
+ chat_id = msg.get("chat_id") if isinstance(msg, dict) else getattr(msg, "chat_id", self.chat_id)
804
+ message_id = msg.get("message_id") if isinstance(msg, dict) else getattr(msg, "message_id", self.message_id)
805
+ return Pick(self.bot, chat_id, message_id)
806
+ try:
807
+ loop = asyncio.get_running_loop()
808
+ if loop.is_running():
809
+ return asyncio.create_task(_reply_async())
810
+ except RuntimeError:
811
+ return asyncio.run(_reply_async())
812
+ def answer(self, text: str, **kwargs):
310
813
  return self.bot.send_message(
814
+ self.chat_id,
815
+ text,
816
+ reply_to_message_id=self.message_id,
817
+ **kwargs
818
+ )
819
+
820
+
821
+ def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
822
+ return self.bot._post("sendPoll", {
823
+ "chat_id": self.chat_id,
824
+ "question": question,
825
+ "options": options,
826
+ "reply_to_message_id": self.message_id,
827
+ **kwargs
828
+ })
829
+
830
+
831
+ def reply_document(
832
+ self,
833
+ path: Optional[Union[str, Path]] = None,
834
+ file_id: Optional[str] = None,
835
+ text: Optional[str] = None,
836
+ chat_keypad: Optional[Dict[str, Any]] = None,
837
+ inline_keypad: Optional[Dict[str, Any]] = None,
838
+ chat_keypad_type: Optional[str] = "None",
839
+ disable_notification: bool = False
840
+ ):
841
+ if chat_keypad and chat_keypad_type == "none":chat_keypad_type == "New"
842
+ return self.bot.send_document(
843
+ chat_id=self.chat_id,
844
+ path=path,
845
+ file_id=file_id,
846
+ text=text,
847
+ chat_keypad=chat_keypad,
848
+ inline_keypad=inline_keypad,
849
+ chat_keypad_type=chat_keypad_type,
850
+ disable_notification=disable_notification,
851
+ reply_to_message_id=self.message_id
852
+ )
853
+ def reply_file(
854
+ self,
855
+ path: Optional[Union[str, Path]] = None,
856
+ file_id: Optional[str] = None,
857
+ text: Optional[str] = None,
858
+ chat_keypad: Optional[Dict[str, Any]] = None,
859
+ inline_keypad: Optional[Dict[str, Any]] = None,
860
+ chat_keypad_type: Optional[str] = "None",
861
+ disable_notification: bool = False
862
+ ):
863
+ if chat_keypad and chat_keypad_type == "none":
864
+ chat_keypad_type == "New"
865
+
866
+ return self.bot.send_document(
867
+ chat_id=self.chat_id,
868
+ path=path,
869
+ file_id=file_id,
870
+ text=text,
871
+ chat_keypad=chat_keypad,
872
+ inline_keypad=inline_keypad,
873
+ chat_keypad_type=chat_keypad_type,
874
+ disable_notification=disable_notification,
875
+ reply_to_message_id=self.message_id
876
+ )
877
+
878
+ def reply_image(
879
+ self,
880
+ path: Optional[Union[str, Path]] = None,
881
+ file_id: Optional[str] = None,
882
+ text: Optional[str] = None,
883
+ chat_keypad: Optional[Dict[str, Any]] = None,
884
+ inline_keypad: Optional[Dict[str, Any]] = None,
885
+ chat_keypad_type: Optional[str] = "None",
886
+ disable_notification: bool = False
887
+ ):
888
+ if chat_keypad and chat_keypad_type == "none":
889
+ chat_keypad_type == "New"
890
+ return self.bot.send_image(
891
+ chat_id=self.chat_id,
892
+ path=path,
893
+ file_id=file_id,
894
+ text=text,
895
+ chat_keypad=chat_keypad,
896
+ inline_keypad=inline_keypad,
897
+ chat_keypad_type=chat_keypad_type,
898
+ disable_notification=disable_notification,
899
+ reply_to_message_id=self.message_id
900
+ )
901
+
902
+ def reply_music(
903
+ self,
904
+ path: Optional[Union[str, Path]] = None,
905
+ file_id: Optional[str] = None,
906
+ text: Optional[str] = None,
907
+ chat_keypad: Optional[Dict[str, Any]] = None,
908
+ inline_keypad: Optional[Dict[str, Any]] = None,
909
+ chat_keypad_type: Optional[str] = "None",
910
+ disable_notification: bool = False
911
+ ):
912
+ if chat_keypad and chat_keypad_type == "none":
913
+ chat_keypad_type == "New"
914
+ return self.bot.send_music(
915
+ chat_id=self.chat_id,
916
+ path=path,
917
+ file_id=file_id,
918
+ text=text,
919
+ chat_keypad=chat_keypad,
920
+ inline_keypad=inline_keypad,
921
+ chat_keypad_type=chat_keypad_type,
922
+ disable_notification=disable_notification,
923
+ reply_to_message_id=self.message_id
924
+ )
925
+
926
+ def reply_voice(
927
+ self,
928
+ path: Optional[Union[str, Path]] = None,
929
+ file_id: Optional[str] = None,
930
+ text: Optional[str] = None,
931
+ chat_keypad: Optional[Dict[str, Any]] = None,
932
+ inline_keypad: Optional[Dict[str, Any]] = None,
933
+ chat_keypad_type: Optional[str] = "None",
934
+ disable_notification: bool = False
935
+ ):
936
+ if chat_keypad and chat_keypad_type == "none":
937
+ chat_keypad_type == "New"
938
+ return self.bot.send_voice(
311
939
  chat_id=self.chat_id,
940
+ path=path,
941
+ file_id=file_id,
312
942
  text=text,
943
+ chat_keypad=chat_keypad,
944
+ inline_keypad=inline_keypad,
945
+ chat_keypad_type=chat_keypad_type,
946
+ disable_notification=disable_notification,
947
+ reply_to_message_id=self.message_id
948
+ )
949
+
950
+ def reply_gif(
951
+ self,
952
+ path: Optional[Union[str, Path]] = None,
953
+ file_id: Optional[str] = None,
954
+ text: Optional[str] = None,
955
+ chat_keypad: Optional[Dict[str, Any]] = None,
956
+ inline_keypad: Optional[Dict[str, Any]] = None,
957
+ chat_keypad_type: Optional[str] = "None",
958
+ disable_notification: bool = False
959
+ ):
960
+ if chat_keypad and chat_keypad_type == "none":chat_keypad_type == "New"
961
+ return self.bot.send_gif(
962
+ chat_id=self.chat_id,
963
+ path=path,
964
+ file_id=file_id,
965
+ text=text,
966
+ chat_keypad=chat_keypad,
967
+ inline_keypad=inline_keypad,
968
+ chat_keypad_type=chat_keypad_type,
969
+ disable_notification=disable_notification,
970
+ reply_to_message_id=self.message_id
971
+ )
972
+
973
+ def reply_location(self, latitude: str, longitude: str, **kwargs) -> Dict[str, Any]:
974
+ return self.bot.send_location(
975
+ chat_id=self.chat_id,
976
+ latitude=latitude,
977
+ longitude=longitude,
978
+ reply_to_message_id=self.message_id,
979
+ **kwargs
980
+ )
981
+
982
+ def reply_contact(self, first_name: str, last_name: str, phone_number: str, **kwargs) -> Dict[str, Any]:
983
+ return self.bot.send_contact(
984
+ chat_id=self.chat_id,
985
+ first_name=first_name,
986
+ last_name=last_name,
987
+ phone_number=phone_number,
313
988
  reply_to_message_id=self.message_id,
314
989
  **kwargs
315
990
  )
316
991
 
317
- def edit(self, new_text: str):
992
+ def reply_keypad(self, text: str, keypad: Dict[str, Any], **kwargs) -> Dict[str, Any]:
993
+ return self.bot.send_message(
994
+ chat_id=self.chat_id,
995
+ text=text,
996
+ chat_keypad_type="New",
997
+ chat_keypad=keypad,
998
+ reply_to_message_id=self.message_id,
999
+ **kwargs
1000
+ )
1001
+
1002
+ def reply_inline(self, text: str, inline_keypad: Dict[str, Any], **kwargs) -> Dict[str, Any]:
1003
+ return self.bot.send_message(
1004
+ chat_id=self.chat_id,
1005
+ text=text,
1006
+ inline_keypad=inline_keypad,
1007
+ reply_to_message_id=self.message_id,
1008
+ **kwargs
1009
+ )
1010
+
1011
+ def reply_sticker(self, sticker_id: str, **kwargs) -> Dict[str, Any]:
1012
+ return self.bot._post("sendSticker", {
1013
+ "chat_id": self.chat_id,
1014
+ "sticker_id": sticker_id,
1015
+ "reply_to_message_id": self.message_id,
1016
+ **kwargs
1017
+ })
1018
+
1019
+ def edit(self, new_text: str) -> Dict[str, Any]:
318
1020
  return self.bot.edit_message_text(
319
1021
  chat_id=self.chat_id,
320
1022
  message_id=self.message_id,
321
1023
  text=new_text
322
1024
  )
323
1025
 
324
- def delete(self):
1026
+ def delete(self) -> Dict[str, Any]:
325
1027
  return self.bot.delete_message(
326
1028
  chat_id=self.chat_id,
327
1029
  message_id=self.message_id
328
1030
  )
1031
+ @hybrid_property
1032
+ async def author_name(self):return await self.bot.get_name(self.chat_id)
1033
+ @hybrid_property
1034
+ async def name(self):return await self.bot.get_name(self.chat_id)
1035
+ @hybrid_property
1036
+ async def username(self):return await self.bot.get_username(self.chat_id)
1037
+ @hybrid_property
1038
+ async def author_info(self):return await self.bot.get_chat(self.chat_id)