Rubka 6.6.2__py3-none-any.whl → 7.2.2__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.
rubka/context.py CHANGED
@@ -1,5 +1,68 @@
1
- from typing import Any, Dict, List,Optional
2
-
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
+ )
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")
@@ -184,9 +247,25 @@ 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")
187
- from typing import Union
188
- from pathlib import Path
189
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)
190
269
  class Message:
191
270
  def __init__(self, bot, chat_id, message_id, sender_id, text=None, raw_data=None):
192
271
  self.bot = bot
@@ -196,6 +275,53 @@ class Message:
196
275
  self.author_guid = self.raw_data.get("sender_id", sender_id)
197
276
  self.message_id: str = self.raw_data.get("message_id", message_id)
198
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_music = None
292
+ self.is_archive = None
293
+ self.is_executable = None
294
+ self.is_font = None
295
+ self.metadata = self.raw_data.get("metadata", {})
296
+ self.is_metadata = self.metadata
297
+ self.meta_parts = self.metadata.get("meta_data_parts", [])
298
+ self.has_metadata = bool(self.meta_parts)
299
+ self.meta_types = [part.get("type") for part in self.meta_parts] if self.has_metadata else []
300
+ self.is_bold = "Bold" in self.meta_types
301
+ self.is_italic = "Italic" in self.meta_types
302
+ self.is_strike = "Strike" in self.meta_types
303
+ self.is_underline = "Underline" in self.meta_types
304
+ self.is_quote = "Quote" in self.meta_types
305
+ self.is_spoiler = "Spoiler" in self.meta_types
306
+ self.is_pre = "Pre" in self.meta_types
307
+ self.is_mono = "Mono" in self.meta_types
308
+ self.is_link_meta = "Link" in self.meta_types
309
+ self.meta_links = [part.get("link_url") for part in self.meta_parts if part.get("type") == "Link"]
310
+ self.meta_link_positions = [
311
+ {"from": part.get("from_index"), "length": part.get("length"), "url": part.get("link_url")}
312
+ for part in self.meta_parts if part.get("type") == "Link"
313
+ ]
314
+ self.has_link = contains_link_or_mention(self.text) or self.is_link_meta
315
+ self.is_formatted = any([
316
+ self.is_bold,
317
+ self.is_italic,
318
+ self.is_strike,
319
+ self.is_underline,
320
+ self.is_quote,
321
+ self.is_spoiler,
322
+ self.is_pre,
323
+ self.is_mono
324
+ ])
199
325
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
200
326
  self.time: str = self.raw_data.get("time")
201
327
  self.is_edited: bool = self.raw_data.get("is_edited", False)
@@ -226,19 +352,86 @@ class Message:
226
352
  self.is_contact = self.contact_message is not None
227
353
  self.has_any_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
228
354
  self.edited_text = self.raw_data.get("edited_text") if self.is_edited else None
229
- self.info = {attr: value for attr, value in vars(self).items()}
355
+ if self.file and self.file.file_name:
356
+ name = self.file.file_name.lower()
357
+ self.is_photo = name.endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))
358
+ self.is_video = name.endswith((".mp4", ".mov", ".avi", ".mkv", ".webm"))
359
+ self.is_audio = name.endswith((".mp3", ".wav", ".ogg", ".m4a", ".flac"))
360
+ self.is_music = name.endswith((".mp3", ".wav", ".ogg", ".m4a", ".flac"))
361
+ self.is_voice = name.endswith((".ogg", ".m4a"))
362
+ self.is_document = name.endswith((".pdf", ".doc", ".docx", ".txt", ".xls", ".xlsx", ".ppt", ".pptx"))
363
+ self.is_archive = name.endswith((".zip", ".rar", ".7z", ".tar", ".gz"))
364
+ self.is_executable = name.endswith((".exe", ".msi", ".bat", ".sh"))
365
+ self.is_font = name.endswith((".ttf", ".otf", ".woff", ".woff2"))
230
366
  @property
231
367
  def session(self):
232
368
  if self.chat_id not in self.bot.sessions:
233
369
  self.bot.sessions[self.chat_id] = {}
234
370
  return self.bot.sessions[self.chat_id]
235
- def reply(self, text: str, **kwargs):
236
- return self.bot.send_message(
237
- self.chat_id,
238
- text,
239
- reply_to_message_id=self.message_id,
240
- **kwargs
241
- )
371
+
372
+ def reply(self, text: str, delete_after: int = None,parse_mode : Optional[Literal["HTML", "Markdown"]] = None , **kwargs):
373
+ async def _reply_async():
374
+ send_func = self.bot.send_message
375
+ if inspect.iscoroutinefunction(send_func):
376
+ msg = await send_func(
377
+ self.chat_id,
378
+ text,
379
+ reply_to_message_id=self.message_id,
380
+ delete_after=delete_after,
381
+ parse_mode=parse_mode,
382
+ **kwargs
383
+ )
384
+ else:
385
+ msg = send_func(
386
+ self.chat_id,
387
+ text,
388
+ reply_to_message_id=self.message_id,
389
+ delete_after=delete_after,
390
+ **kwargs
391
+ )
392
+ class Pick:
393
+ def __init__(self, bot, chat_id, message_id):
394
+ self.bot = bot
395
+ self.chat_id = chat_id
396
+ self.message_id = message_id
397
+
398
+ def edit(self, new_text):
399
+ async def _edit():
400
+ func = self.bot.edit_message_text
401
+ if inspect.iscoroutinefunction(func):
402
+ await func(self.chat_id, self.message_id, new_text)
403
+ else:
404
+ func(self.chat_id, self.message_id, new_text)
405
+
406
+ try:
407
+ loop = asyncio.get_running_loop()
408
+ if loop.is_running():
409
+ return asyncio.create_task(_edit())
410
+ except RuntimeError:
411
+ return asyncio.run(_edit())
412
+
413
+ def delete(self):
414
+ async def _delete():
415
+ func = self.bot.delete_message
416
+ if inspect.iscoroutinefunction(func):
417
+ await func(self.chat_id, self.message_id)
418
+ else:
419
+ func(self.chat_id, self.message_id)
420
+ try:
421
+ loop = asyncio.get_running_loop()
422
+ if loop.is_running():
423
+ return asyncio.create_task(_delete())
424
+ except RuntimeError:
425
+ return asyncio.run(_delete())
426
+ chat_id = msg.get("chat_id") if isinstance(msg, dict) else getattr(msg, "chat_id", self.chat_id)
427
+ message_id = msg.get("message_id") if isinstance(msg, dict) else getattr(msg, "message_id", self.message_id)
428
+ return Pick(self.bot, chat_id, message_id)
429
+ try:
430
+ loop = asyncio.get_running_loop()
431
+ if loop.is_running():
432
+ return asyncio.create_task(_reply_async())
433
+ except RuntimeError:
434
+ return asyncio.run(_reply_async())
242
435
  def answer(self, text: str, **kwargs):
243
436
  return self.bot.send_message(
244
437
  self.chat_id,
@@ -246,15 +439,66 @@ class Message:
246
439
  reply_to_message_id=self.message_id,
247
440
  **kwargs
248
441
  )
249
-
250
- def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
251
- return self.bot._post("sendPoll", {
442
+ async def copy(self, to_chat_id: Optional[str], message_id: Optional[str] = None):await self.copy_message(to_chat_id, message_id)
443
+ async def copy_message(self, to_chat_id: Optional[str], message_id: Optional[str] = None):
444
+ try:
445
+ send_func = None
446
+ kwargs = {
447
+ "chat_id": to_chat_id,
448
+ "reply_to_message_id": message_id,
449
+ "meta_data": self.metadata
450
+ }
451
+ if getattr(self, "is_photo", False):
452
+ send_func = self.bot.send_image
453
+ elif getattr(self, "is_video", False):
454
+ send_func = self.bot.send_video
455
+ elif getattr(self, "is_document", False):
456
+ send_func = self.bot.send_document
457
+ elif getattr(self, "is_text", False):
458
+ send_func = self.bot.send_message
459
+ kwargs["text"] = self.text
460
+ if send_func:
461
+ if hasattr(self, "file") and self.file:
462
+ kwargs["path"] = await self.bot.get_url_file(self.file.file_id)
463
+ return await send_func(**kwargs)
464
+ else:
465
+ raise Exception("Unsupported message type.")
466
+ except Exception as e:
467
+ raise Exception(f"Error: {e}")
468
+ def reply_poll(
469
+ self,
470
+ question: str,
471
+ options: List[str],
472
+ type: Literal['Regular', 'Quiz'] = "Regular",
473
+ allows_multiple_answers: bool = False,
474
+ is_anonymous: bool = True,
475
+ correct_option_index: Optional[int] = None,
476
+ hint: Optional[str] = None,
477
+ reply_to_message_id: Optional[str] = None,
478
+ disable_notification: bool = False,
479
+ show_results: bool = False,
480
+ inline_keypad: Optional[Dict[str, Any]] = None,
481
+ chat_keypad: Optional[Dict[str, Any]] = None,
482
+ chat_keypad_type: Optional[Literal['New', 'Remove', 'None']] = None,
483
+ **kwargs
484
+ ) -> dict:
485
+ payload = {
252
486
  "chat_id": self.chat_id,
253
487
  "question": question,
254
488
  "options": options,
255
- "reply_to_message_id": self.message_id,
256
- **kwargs
257
- })
489
+ "type": type,
490
+ "allows_multiple_answers": allows_multiple_answers,
491
+ "is_anonymous": is_anonymous,
492
+ "correct_option_index": correct_option_index,
493
+ "hint": hint,
494
+ "reply_to_message_id": self.message_id if not reply_to_message_id else reply_to_message_id,
495
+ "disable_notification": disable_notification,
496
+ "show_results": show_results,
497
+ "inline_keypad": inline_keypad,
498
+ "chat_keypad": chat_keypad,
499
+ "chat_keypad_type": chat_keypad_type,
500
+ }
501
+ return self.bot._post("sendPoll", {key: value for key, value in payload.items() if value is not None}, **kwargs)
258
502
 
259
503
 
260
504
  def reply_document(
@@ -457,6 +701,14 @@ class Message:
457
701
  chat_id=self.chat_id,
458
702
  message_id=self.message_id
459
703
  )
704
+ @hybrid_property
705
+ async def author_name(self):return await self.bot.get_name(self.chat_id)
706
+ @hybrid_property
707
+ async def name(self):return await self.bot.get_name(self.chat_id)
708
+ @hybrid_property
709
+ async def username(self):return await self.bot.get_username(self.chat_id)
710
+ @hybrid_property
711
+ async def author_info(self):return await self.bot.get_chat(self.chat_id)
460
712
  class AuxData:
461
713
  def __init__(self, data: dict):
462
714
  self.start_id = data.get("start_id")
@@ -467,37 +719,346 @@ class InlineMessage:
467
719
  def __init__(self, bot, raw_data: dict):
468
720
  self.bot = bot
469
721
  self.raw_data = raw_data
470
-
722
+ chat_id : str = raw_data.get("chat_id")
723
+ sender_id : str = raw_data.get("sender_id")
471
724
  self.chat_id: str = raw_data.get("chat_id")
472
725
  self.message_id: str = raw_data.get("message_id")
473
726
  self.sender_id: str = raw_data.get("sender_id")
474
727
  self.text: str = raw_data.get("text")
475
728
  self.aux_data = AuxData(raw_data.get("aux_data", {})) if "aux_data" in raw_data else None
729
+ self.bot = bot
730
+ self.raw_data = raw_data or {}
731
+ self.object_guid = chat_id
732
+ self.author_guid = self.raw_data.get("sender_id", sender_id)
733
+ 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))
734
+ self.sender_id: str = self.raw_data.get("sender_id", sender_id)
735
+ self.time: str = self.raw_data.get("time")
736
+ self.is_edited: bool = self.raw_data.get("is_edited", False)
737
+ self.sender_type: str = self.raw_data.get("sender_type")
738
+ self.args = []
739
+ self.is_command = bool(self.text and self.text.startswith("/"))
740
+ self.is_user = self.chat_id.startswith("b")
741
+ self.is_private = self.chat_id.startswith("b")
742
+ self.is_group = self.chat_id.startswith("g")
743
+ self.is_channel = self.chat_id.startswith("c")
744
+ self.reply_to_message_id: Optional[str] = self.raw_data.get("reply_to_message_id")
745
+ self.forwarded_from = ForwardedFrom(self.raw_data["forwarded_from"]) if "forwarded_from" in self.raw_data else None
746
+ self.file = File(self.raw_data["file"]) if "file" in self.raw_data else None
747
+ self.sticker = Sticker(self.raw_data["sticker"]) if "sticker" in self.raw_data else None
748
+ self.contact_message = ContactMessage(self.raw_data["contact_message"]) if "contact_message" in self.raw_data else None
749
+ self.aux_data = AuxData(self.raw_data["aux_data"]) if "aux_data" in self.raw_data else None
750
+ self.is_reply = self.reply_to_message_id is not None
751
+ self.has_media = any([self.file, self.sticker])
752
+ self.is_forwarded = self.forwarded_from is not None
753
+ self.is_text = bool(self.text and not self.has_media)
754
+ self.is_media = self.has_media
755
+ self.is_contact = self.contact_message is not None
756
+ self.has_any_media = any([self.file, self.sticker,])
757
+ self.edited_text = self.raw_data.get("edited_text") if self.is_edited else None
758
+ if self.file and self.file.file_name:
759
+ name = self.file.file_name.lower()
760
+ self.is_photo = name.endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))
761
+ self.is_video = name.endswith((".mp4", ".mov", ".avi", ".mkv", ".webm"))
762
+ self.is_audio = name.endswith((".mp3", ".wav", ".ogg", ".m4a", ".flac"))
763
+ self.is_voice = name.endswith((".ogg", ".m4a"))
764
+ self.is_document = name.endswith((".pdf", ".doc", ".docx", ".txt", ".xls", ".xlsx", ".ppt", ".pptx"))
765
+ self.is_archive = name.endswith((".zip", ".rar", ".7z", ".tar", ".gz"))
766
+ self.is_executable = name.endswith((".exe", ".msi", ".bat", ".sh"))
767
+ self.is_font = name.endswith((".ttf", ".otf", ".woff", ".woff2"))
768
+
476
769
 
477
- def reply(self, text: str, **kwargs):
770
+ @property
771
+ def session(self):
772
+ if self.chat_id not in self.bot.sessions:
773
+ self.bot.sessions[self.chat_id] = {}
774
+ return self.bot.sessions[self.chat_id]
775
+
776
+ def reply(self, text: str, delete_after: int = None, **kwargs):
777
+ async def _reply_async():
778
+ send_func = self.bot.send_message
779
+ if inspect.iscoroutinefunction(send_func):
780
+ msg = await send_func(
781
+ self.chat_id,
782
+ text,
783
+ reply_to_message_id=self.message_id,
784
+ delete_after=delete_after,
785
+ **kwargs
786
+ )
787
+ else:
788
+ msg = send_func(
789
+ self.chat_id,
790
+ text,
791
+ reply_to_message_id=self.message_id,
792
+ delete_after=delete_after,
793
+ **kwargs
794
+ )
795
+ class Pick:
796
+ def __init__(self, bot, chat_id, message_id):
797
+ self.bot = bot
798
+ self.chat_id = chat_id
799
+ self.message_id = message_id
800
+
801
+ def edit(self, new_text):
802
+ async def _edit():
803
+ func = self.bot.edit_message_text
804
+ if inspect.iscoroutinefunction(func):
805
+ await func(self.chat_id, self.message_id, new_text)
806
+ else:
807
+ func(self.chat_id, self.message_id, new_text)
808
+
809
+ try:
810
+ loop = asyncio.get_running_loop()
811
+ if loop.is_running():
812
+ return asyncio.create_task(_edit())
813
+ except RuntimeError:
814
+ return asyncio.run(_edit())
815
+
816
+ def delete(self):
817
+ async def _delete():
818
+ func = self.bot.delete_message
819
+ if inspect.iscoroutinefunction(func):
820
+ await func(self.chat_id, self.message_id)
821
+ else:
822
+ func(self.chat_id, self.message_id)
823
+ try:
824
+ loop = asyncio.get_running_loop()
825
+ if loop.is_running():
826
+ return asyncio.create_task(_delete())
827
+ except RuntimeError:
828
+ return asyncio.run(_delete())
829
+ chat_id = msg.get("chat_id") if isinstance(msg, dict) else getattr(msg, "chat_id", self.chat_id)
830
+ message_id = msg.get("message_id") if isinstance(msg, dict) else getattr(msg, "message_id", self.message_id)
831
+ return Pick(self.bot, chat_id, message_id)
832
+ try:
833
+ loop = asyncio.get_running_loop()
834
+ if loop.is_running():
835
+ return asyncio.create_task(_reply_async())
836
+ except RuntimeError:
837
+ return asyncio.run(_reply_async())
838
+ def answer(self, text: str, **kwargs):
478
839
  return self.bot.send_message(
840
+ self.chat_id,
841
+ text,
842
+ reply_to_message_id=self.message_id,
843
+ **kwargs
844
+ )
845
+
846
+
847
+ def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
848
+ return self.bot._post("sendPoll", {
849
+ "chat_id": self.chat_id,
850
+ "question": question,
851
+ "options": options,
852
+ "reply_to_message_id": self.message_id,
853
+ **kwargs
854
+ })
855
+
856
+
857
+ def reply_document(
858
+ self,
859
+ path: Optional[Union[str, Path]] = None,
860
+ file_id: Optional[str] = None,
861
+ text: Optional[str] = None,
862
+ chat_keypad: Optional[Dict[str, Any]] = None,
863
+ inline_keypad: Optional[Dict[str, Any]] = None,
864
+ chat_keypad_type: Optional[str] = "None",
865
+ disable_notification: bool = False
866
+ ):
867
+ if chat_keypad and chat_keypad_type == "none":chat_keypad_type == "New"
868
+ return self.bot.send_document(
869
+ chat_id=self.chat_id,
870
+ path=path,
871
+ file_id=file_id,
872
+ text=text,
873
+ chat_keypad=chat_keypad,
874
+ inline_keypad=inline_keypad,
875
+ chat_keypad_type=chat_keypad_type,
876
+ disable_notification=disable_notification,
877
+ reply_to_message_id=self.message_id
878
+ )
879
+ def reply_file(
880
+ self,
881
+ path: Optional[Union[str, Path]] = None,
882
+ file_id: Optional[str] = None,
883
+ text: Optional[str] = None,
884
+ chat_keypad: Optional[Dict[str, Any]] = None,
885
+ inline_keypad: Optional[Dict[str, Any]] = None,
886
+ chat_keypad_type: Optional[str] = "None",
887
+ disable_notification: bool = False
888
+ ):
889
+ if chat_keypad and chat_keypad_type == "none":
890
+ chat_keypad_type == "New"
891
+
892
+ return self.bot.send_document(
479
893
  chat_id=self.chat_id,
894
+ path=path,
895
+ file_id=file_id,
480
896
  text=text,
897
+ chat_keypad=chat_keypad,
898
+ inline_keypad=inline_keypad,
899
+ chat_keypad_type=chat_keypad_type,
900
+ disable_notification=disable_notification,
901
+ reply_to_message_id=self.message_id
902
+ )
903
+
904
+ def reply_image(
905
+ self,
906
+ path: Optional[Union[str, Path]] = None,
907
+ file_id: Optional[str] = None,
908
+ text: Optional[str] = None,
909
+ chat_keypad: Optional[Dict[str, Any]] = None,
910
+ inline_keypad: Optional[Dict[str, Any]] = None,
911
+ chat_keypad_type: Optional[str] = "None",
912
+ disable_notification: bool = False
913
+ ):
914
+ if chat_keypad and chat_keypad_type == "none":
915
+ chat_keypad_type == "New"
916
+ return self.bot.send_image(
917
+ chat_id=self.chat_id,
918
+ path=path,
919
+ file_id=file_id,
920
+ text=text,
921
+ chat_keypad=chat_keypad,
922
+ inline_keypad=inline_keypad,
923
+ chat_keypad_type=chat_keypad_type,
924
+ disable_notification=disable_notification,
925
+ reply_to_message_id=self.message_id
926
+ )
927
+
928
+ def reply_music(
929
+ self,
930
+ path: Optional[Union[str, Path]] = None,
931
+ file_id: Optional[str] = None,
932
+ text: Optional[str] = None,
933
+ chat_keypad: Optional[Dict[str, Any]] = None,
934
+ inline_keypad: Optional[Dict[str, Any]] = None,
935
+ chat_keypad_type: Optional[str] = "None",
936
+ disable_notification: bool = False
937
+ ):
938
+ if chat_keypad and chat_keypad_type == "none":
939
+ chat_keypad_type == "New"
940
+ return self.bot.send_music(
941
+ chat_id=self.chat_id,
942
+ path=path,
943
+ file_id=file_id,
944
+ text=text,
945
+ chat_keypad=chat_keypad,
946
+ inline_keypad=inline_keypad,
947
+ chat_keypad_type=chat_keypad_type,
948
+ disable_notification=disable_notification,
949
+ reply_to_message_id=self.message_id
950
+ )
951
+
952
+ def reply_voice(
953
+ self,
954
+ path: Optional[Union[str, Path]] = None,
955
+ file_id: Optional[str] = None,
956
+ text: Optional[str] = None,
957
+ chat_keypad: Optional[Dict[str, Any]] = None,
958
+ inline_keypad: Optional[Dict[str, Any]] = None,
959
+ chat_keypad_type: Optional[str] = "None",
960
+ disable_notification: bool = False
961
+ ):
962
+ if chat_keypad and chat_keypad_type == "none":
963
+ chat_keypad_type == "New"
964
+ return self.bot.send_voice(
965
+ chat_id=self.chat_id,
966
+ path=path,
967
+ file_id=file_id,
968
+ text=text,
969
+ chat_keypad=chat_keypad,
970
+ inline_keypad=inline_keypad,
971
+ chat_keypad_type=chat_keypad_type,
972
+ disable_notification=disable_notification,
973
+ reply_to_message_id=self.message_id
974
+ )
975
+
976
+ def reply_gif(
977
+ self,
978
+ path: Optional[Union[str, Path]] = None,
979
+ file_id: Optional[str] = None,
980
+ text: Optional[str] = None,
981
+ chat_keypad: Optional[Dict[str, Any]] = None,
982
+ inline_keypad: Optional[Dict[str, Any]] = None,
983
+ chat_keypad_type: Optional[str] = "None",
984
+ disable_notification: bool = False
985
+ ):
986
+ if chat_keypad and chat_keypad_type == "none":chat_keypad_type == "New"
987
+ return self.bot.send_gif(
988
+ chat_id=self.chat_id,
989
+ path=path,
990
+ file_id=file_id,
991
+ text=text,
992
+ chat_keypad=chat_keypad,
993
+ inline_keypad=inline_keypad,
994
+ chat_keypad_type=chat_keypad_type,
995
+ disable_notification=disable_notification,
996
+ reply_to_message_id=self.message_id
997
+ )
998
+
999
+ def reply_location(self, latitude: str, longitude: str, **kwargs) -> Dict[str, Any]:
1000
+ return self.bot.send_location(
1001
+ chat_id=self.chat_id,
1002
+ latitude=latitude,
1003
+ longitude=longitude,
481
1004
  reply_to_message_id=self.message_id,
482
1005
  **kwargs
483
1006
  )
484
- def answer(self, text: str, **kwargs):
1007
+
1008
+ def reply_contact(self, first_name: str, last_name: str, phone_number: str, **kwargs) -> Dict[str, Any]:
1009
+ return self.bot.send_contact(
1010
+ chat_id=self.chat_id,
1011
+ first_name=first_name,
1012
+ last_name=last_name,
1013
+ phone_number=phone_number,
1014
+ reply_to_message_id=self.message_id,
1015
+ **kwargs
1016
+ )
1017
+
1018
+ def reply_keypad(self, text: str, keypad: Dict[str, Any], **kwargs) -> Dict[str, Any]:
485
1019
  return self.bot.send_message(
486
- self.chat_id,
487
- text,
1020
+ chat_id=self.chat_id,
1021
+ text=text,
1022
+ chat_keypad_type="New",
1023
+ chat_keypad=keypad,
1024
+ reply_to_message_id=self.message_id,
1025
+ **kwargs
1026
+ )
1027
+
1028
+ def reply_inline(self, text: str, inline_keypad: Dict[str, Any], **kwargs) -> Dict[str, Any]:
1029
+ return self.bot.send_message(
1030
+ chat_id=self.chat_id,
1031
+ text=text,
1032
+ inline_keypad=inline_keypad,
488
1033
  reply_to_message_id=self.message_id,
489
1034
  **kwargs
490
1035
  )
491
1036
 
492
- def edit(self, new_text: str):
1037
+ def reply_sticker(self, sticker_id: str, **kwargs) -> Dict[str, Any]:
1038
+ return self.bot._post("sendSticker", {
1039
+ "chat_id": self.chat_id,
1040
+ "sticker_id": sticker_id,
1041
+ "reply_to_message_id": self.message_id,
1042
+ **kwargs
1043
+ })
1044
+
1045
+ def edit(self, new_text: str) -> Dict[str, Any]:
493
1046
  return self.bot.edit_message_text(
494
1047
  chat_id=self.chat_id,
495
1048
  message_id=self.message_id,
496
1049
  text=new_text
497
1050
  )
498
1051
 
499
- def delete(self):
1052
+ def delete(self) -> Dict[str, Any]:
500
1053
  return self.bot.delete_message(
501
1054
  chat_id=self.chat_id,
502
1055
  message_id=self.message_id
503
- )
1056
+ )
1057
+ @hybrid_property
1058
+ async def author_name(self):return await self.bot.get_name(self.chat_id)
1059
+ @hybrid_property
1060
+ async def name(self):return await self.bot.get_name(self.chat_id)
1061
+ @hybrid_property
1062
+ async def username(self):return await self.bot.get_username(self.chat_id)
1063
+ @hybrid_property
1064
+ async def author_info(self):return await self.bot.get_chat(self.chat_id)