Rubka 7.1.9__tar.gz → 7.1.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {rubka-7.1.9 → rubka-7.1.11}/PKG-INFO +1 -1
  2. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/PKG-INFO +1 -1
  3. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/SOURCES.txt +1 -0
  4. {rubka-7.1.9 → rubka-7.1.11}/rubka/api.py +2 -1
  5. {rubka-7.1.9 → rubka-7.1.11}/rubka/asynco.py +183 -39
  6. {rubka-7.1.9 → rubka-7.1.11}/rubka/context.py +32 -1
  7. rubka-7.1.11/rubka/metadata.py +92 -0
  8. {rubka-7.1.9 → rubka-7.1.11}/rubka/update.py +213 -19
  9. {rubka-7.1.9 → rubka-7.1.11}/setup.py +1 -1
  10. {rubka-7.1.9 → rubka-7.1.11}/README.md +0 -0
  11. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/dependency_links.txt +0 -0
  12. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/entry_points.txt +0 -0
  13. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/not-zip-safe +0 -0
  14. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/requires.txt +0 -0
  15. {rubka-7.1.9 → rubka-7.1.11}/Rubka.egg-info/top_level.txt +0 -0
  16. {rubka-7.1.9 → rubka-7.1.11}/rubka/__init__.py +0 -0
  17. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/__init__.py +0 -0
  18. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/client/__init__.py +0 -0
  19. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/client/client.py +0 -0
  20. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/crypto/__init__.py +0 -0
  21. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/crypto/crypto.py +0 -0
  22. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/enums.py +0 -0
  23. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/exceptions.py +0 -0
  24. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/methods/__init__.py +0 -0
  25. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/methods/methods.py +0 -0
  26. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/network/__init__.py +0 -0
  27. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/network/helper.py +0 -0
  28. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/network/network.py +0 -0
  29. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/network/socket.py +0 -0
  30. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/sessions/__init__.py +0 -0
  31. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/sessions/sessions.py +0 -0
  32. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/types/__init__.py +0 -0
  33. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/types/socket/__init__.py +0 -0
  34. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/types/socket/message.py +0 -0
  35. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/utils/__init__.py +0 -0
  36. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/utils/configs.py +0 -0
  37. {rubka-7.1.9 → rubka-7.1.11}/rubka/adaptorrubka/utils/utils.py +0 -0
  38. {rubka-7.1.9 → rubka-7.1.11}/rubka/button.py +0 -0
  39. {rubka-7.1.9 → rubka-7.1.11}/rubka/config.py +0 -0
  40. {rubka-7.1.9 → rubka-7.1.11}/rubka/decorators.py +0 -0
  41. {rubka-7.1.9 → rubka-7.1.11}/rubka/exceptions.py +0 -0
  42. {rubka-7.1.9 → rubka-7.1.11}/rubka/filters.py +0 -0
  43. {rubka-7.1.9 → rubka-7.1.11}/rubka/helpers.py +0 -0
  44. {rubka-7.1.9 → rubka-7.1.11}/rubka/jobs.py +0 -0
  45. {rubka-7.1.9 → rubka-7.1.11}/rubka/keyboards.py +0 -0
  46. {rubka-7.1.9 → rubka-7.1.11}/rubka/keypad.py +0 -0
  47. {rubka-7.1.9 → rubka-7.1.11}/rubka/logger.py +0 -0
  48. {rubka-7.1.9 → rubka-7.1.11}/rubka/rubino.py +0 -0
  49. {rubka-7.1.9 → rubka-7.1.11}/rubka/tv.py +0 -0
  50. {rubka-7.1.9 → rubka-7.1.11}/rubka/utils.py +0 -0
  51. {rubka-7.1.9 → rubka-7.1.11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 7.1.9
3
+ Version: 7.1.11
4
4
  Summary: Rubika: A Python library for interacting with the Rubika Bot API. This library provides an easy-to-use interface to send messages, polls, stickers, media files, manage groups and channels, handle inline keyboards, and implement advanced bot features like subscription management, user authentication, and message handling. Ideal for developers looking to automate and extend their Rubika bots with Python.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
6
  Download-URL: https://github.com/Mahdy-Ahmadi/rubka/archive/refs/tags/v6.6.4.zip
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 7.1.9
3
+ Version: 7.1.11
4
4
  Summary: Rubika: A Python library for interacting with the Rubika Bot API. This library provides an easy-to-use interface to send messages, polls, stickers, media files, manage groups and channels, handle inline keyboards, and implement advanced bot features like subscription management, user authentication, and message handling. Ideal for developers looking to automate and extend their Rubika bots with Python.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
6
  Download-URL: https://github.com/Mahdy-Ahmadi/rubka/archive/refs/tags/v6.6.4.zip
@@ -21,6 +21,7 @@ rubka/jobs.py
21
21
  rubka/keyboards.py
22
22
  rubka/keypad.py
23
23
  rubka/logger.py
24
+ rubka/metadata.py
24
25
  rubka/rubino.py
25
26
  rubka/tv.py
26
27
  rubka/update.py
@@ -1011,7 +1011,8 @@ class Robot:
1011
1011
  disable_notification: bool = False,
1012
1012
  reply_to_message_id: Optional[str] = None,
1013
1013
  chat_keypad_type: Optional[Literal["New", "Removed"]] = None,
1014
- delete_after = None
1014
+ delete_after = None,
1015
+ parse_mode = None
1015
1016
  ) -> Dict[str, Any]:
1016
1017
  """
1017
1018
  Send a text message to a chat.
@@ -1,9 +1,10 @@
1
- import asyncio,aiohttp,aiofiles,time,datetime,json,tempfile,os,sys,subprocess,mimetypes,time, hashlib,sqlite3
1
+ import asyncio,aiohttp,aiofiles,time,datetime,json,tempfile,os,sys,subprocess,mimetypes,time, hashlib,sqlite3,re
2
2
  from typing import List, Optional, Dict, Any, Literal, Callable, Union,Set
3
3
  from collections import OrderedDict
4
4
  from .exceptions import APIRequestError,raise_for_status,InvalidAccessError,InvalidInputError,TooRequestError,InvalidTokenError
5
5
  from .adaptorrubka import Client as Client_get
6
6
  from .logger import logger
7
+ from .metadata import Track_parsed as GlyphWeaver
7
8
  from .rubino import Bot as Rubino
8
9
  from . import filters
9
10
  try:from .context import Message, InlineMessage
@@ -27,6 +28,127 @@ def install_package(package_name: str) -> bool:
27
28
  except Exception:
28
29
  return False
29
30
 
31
+ def metadata_CallBack(text: str, mode: str) -> Dict[str, Any]:
32
+ meta_data_parts = []
33
+ if mode == "Markdown":
34
+ for match in re.finditer(r"\*\*(.*?)\*\*", text):
35
+ meta_data_parts.append({
36
+ "type": "Bold",
37
+ "from_index": match.start(1),
38
+ "length": len(match.group(1))
39
+ })
40
+ for match in re.finditer(r"(?<!\*)\*(?!\*)(.*?)\*(?!\*)", text):
41
+ meta_data_parts.append({
42
+ "type": "Italic",
43
+ "from_index": match.start(1),
44
+ "length": len(match.group(1))
45
+ })
46
+ for match in re.finditer(r"~~(.*?)~~", text):
47
+ meta_data_parts.append({
48
+ "type": "Strike",
49
+ "from_index": match.start(1),
50
+ "length": len(match.group(1))
51
+ })
52
+ for match in re.finditer(r"__(.*?)__", text):
53
+ meta_data_parts.append({
54
+ "type": "Underline",
55
+ "from_index": match.start(1),
56
+ "length": len(match.group(1))
57
+ })
58
+ for match in re.finditer(r"^> (.*)$", text, re.MULTILINE):
59
+ meta_data_parts.append({
60
+ "type": "Quote",
61
+ "from_index": match.start(1),
62
+ "length": len(match.group(1))
63
+ })
64
+ for match in re.finditer(r"\|\|(.*?)\|\|", text):
65
+ meta_data_parts.append({
66
+ "type": "Spoiler",
67
+ "from_index": match.start(1),
68
+ "length": len(match.group(1))
69
+ })
70
+ for match in re.finditer(r"`(.*?)`", text):
71
+ meta_data_parts.append({
72
+ "type": "Mono",
73
+ "from_index": match.start(1),
74
+ "length": len(match.group(1))
75
+ })
76
+
77
+ for match in re.finditer(r"```(.*?)```", text, re.DOTALL):
78
+ meta_data_parts.append({
79
+ "type": "Pre",
80
+ "from_index": match.start(1),
81
+ "length": len(match.group(1))
82
+ })
83
+ for match in re.finditer(r"\[(.*?)\]\((.*?)\)", text):
84
+ meta_data_parts.append({
85
+ "type": "Link",
86
+ "from_index": match.start(1),
87
+ "length": len(match.group(1)),
88
+ "link_url": match.group(2)
89
+ })
90
+
91
+ elif mode == "HTML":
92
+ for match in re.finditer(r"<b>(.*?)</b>", text):
93
+ meta_data_parts.append({
94
+ "type": "Bold",
95
+ "from_index": match.start(1),
96
+ "length": len(match.group(1))
97
+ })
98
+ for match in re.finditer(r"<i>(.*?)</i>", text):
99
+ meta_data_parts.append({
100
+ "type": "Italic",
101
+ "from_index": match.start(1),
102
+ "length": len(match.group(1))
103
+ })
104
+ for match in re.finditer(r"<u>(.*?)</u>", text):
105
+ meta_data_parts.append({
106
+ "type": "Underline",
107
+ "from_index": match.start(1),
108
+ "length": len(match.group(1))
109
+ })
110
+ for match in re.finditer(r"<s>(.*?)</s>|<strike>(.*?)</strike>", text):
111
+ inner = match.group(1) or match.group(2)
112
+ meta_data_parts.append({
113
+ "type": "Strike",
114
+ "from_index": match.start(),
115
+ "length": len(inner)
116
+ })
117
+ for match in re.finditer(r"<blockquote>(.*?)</blockquote>", text, re.DOTALL):
118
+ meta_data_parts.append({
119
+ "type": "Quote",
120
+ "from_index": match.start(1),
121
+ "length": len(match.group(1))
122
+ })
123
+ for match in re.finditer(r'<span class="spoiler">(.*?)</span>', text):
124
+ meta_data_parts.append({
125
+ "type": "Spoiler",
126
+ "from_index": match.start(1),
127
+ "length": len(match.group(1))
128
+ })
129
+ for match in re.finditer(r"<code>(.*?)</code>", text):
130
+ meta_data_parts.append({
131
+ "type": "Pre",
132
+ "from_index": match.start(1),
133
+ "length": len(match.group(1))
134
+ })
135
+
136
+ for match in re.finditer(r"<pre>(.*?)</pre>", text, re.DOTALL):
137
+ meta_data_parts.append({
138
+ "type": "Pre",
139
+ "from_index": match.start(1),
140
+ "length": len(match.group(1))
141
+ })
142
+ for match in re.finditer(r'<a href="(.*?)">(.*?)</a>', text):
143
+ meta_data_parts.append({
144
+ "type": "Link",
145
+ "from_index": match.start(2),
146
+ "length": len(match.group(2)),
147
+ "link_url": match.group(1)
148
+ })
149
+
150
+ return {"meta_data_parts": meta_data_parts} if meta_data_parts else {}
151
+
30
152
  def get_importlib_metadata():
31
153
  try:
32
154
  from importlib.metadata import version, PackageNotFoundError
@@ -143,7 +265,7 @@ safeSendMode ensures reliable message sending even if replying by message_id fai
143
265
  max_cache_size and max_msg_age help manage duplicate message processing efficiently.
144
266
  """
145
267
 
146
- def __init__(self, token: str, session_name: str = None, auth: str = None, Key: str = None, platform: str = "web", web_hook: str = None, timeout: int = 10, show_progress: bool = False, raise_errors: bool = True,proxy: str = None,retries: int = 2,retry_delay: float = 0.5,user_agent: str = None,safeSendMode = False,max_cache_size: int = 2000,max_msg_age : int = 60,chunk_size : int = 64 * 1024):
268
+ def __init__(self, token: str, session_name: str = None, auth: str = None, Key: str = None, platform: str = "web", web_hook: str = None, timeout: int = 10, show_progress: bool = False, raise_errors: bool = True,proxy: str = None,retries: int = 2,retry_delay: float = 0.5,user_agent: str = None,safeSendMode = False,max_cache_size: int = 2000,max_msg_age : int = 60,chunk_size : int = 64 * 1024,parse_mode: Optional[Literal["HTML", "Markdown"]] = "Markdown"):
147
269
  self.token = token
148
270
  self._inline_query_handlers: List[dict] = []
149
271
  self.timeout = timeout
@@ -161,6 +283,7 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
161
283
  self.Key = Key
162
284
  self.platform = platform
163
285
  self.web_hook = web_hook
286
+ self.parse_mode = parse_mode
164
287
  self._offset_id: Optional[str] = None
165
288
  self._aiohttp_session: aiohttp.ClientSession = None
166
289
  self.sessions: Dict[str, Dict[str, Any]] = {}
@@ -1692,23 +1815,35 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
1692
1815
 
1693
1816
  task = loop.create_task(_task())
1694
1817
  return task
1818
+ def _parse_text_metadata(self, text: str, parse_mode: str):
1819
+ formatter = GlyphWeaver()
1820
+ parsed = formatter.parse(text, parse_mode)
1821
+ return parsed.get("text"), parsed.get("metadata")
1695
1822
 
1696
1823
  async def send_message(
1697
- self,
1698
- chat_id: str,
1699
- text: str,
1700
- chat_keypad: Optional[Dict[str, Any]] = None,
1701
- inline_keypad: Optional[Dict[str, Any]] = None,
1702
- disable_notification: bool = False,
1703
- reply_to_message_id: Optional[str] = None,
1704
- chat_keypad_type: Optional[Literal["New", "Remove"]] = None,
1705
- delete_after : int = None
1706
- ) -> Dict[str, Any]:
1824
+ self,
1825
+ chat_id: str,
1826
+ text: str,
1827
+ chat_keypad: Optional[Dict[str, Any]] = None,
1828
+ inline_keypad: Optional[Dict[str, Any]] = None,
1829
+ disable_notification: bool = False,
1830
+ reply_to_message_id: Optional[str] = None,
1831
+ chat_keypad_type: Optional[Literal["New", "Remove"]] = None,
1832
+ delete_after: Optional[int] = None,
1833
+ parse_mode: Optional[Literal["HTML", "Markdown"]] = None
1834
+ ) -> Dict[str, Any]:
1835
+
1707
1836
  payload = {
1708
1837
  "chat_id": chat_id,
1709
1838
  "text": text,
1710
1839
  "disable_notification": disable_notification,
1711
1840
  }
1841
+ parse_mode_to_use = parse_mode or self.parse_mode
1842
+ if text:
1843
+ text, metadata = self._parse_text_metadata(text, parse_mode_to_use)
1844
+ payload["text"] = text
1845
+ if metadata:
1846
+ payload["metadata"] = metadata
1712
1847
  if chat_keypad:
1713
1848
  payload["chat_keypad"] = chat_keypad
1714
1849
  payload["chat_keypad_type"] = chat_keypad_type or "New"
@@ -1716,19 +1851,18 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
1716
1851
  payload["inline_keypad"] = inline_keypad
1717
1852
  if reply_to_message_id:
1718
1853
  payload["reply_to_message_id"] = reply_to_message_id
1719
- if self.safeSendMode and reply_to_message_id:
1720
- try:
1721
- state = await self._post("sendMessage", payload)
1722
- except Exception:
1854
+ try:
1855
+ state = await self._post("sendMessage", payload)
1856
+ except Exception:
1857
+ if self.safeSendMode and reply_to_message_id:
1723
1858
  payload.pop("reply_to_message_id", None)
1724
1859
  state = await self._post("sendMessage", payload)
1725
- else:
1726
- state = await self._post("sendMessage", payload)
1860
+ else:
1861
+ raise
1727
1862
  if delete_after:
1728
1863
  await self.delete_after(chat_id, state.message_id, delete_after)
1729
- return state
1730
1864
  return state
1731
-
1865
+
1732
1866
 
1733
1867
  async def send_sticker(
1734
1868
  self,
@@ -1996,11 +2130,16 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
1996
2130
  raise ValueError(f"Invalid media type. Must be one of {allowed}")
1997
2131
  result = await self._post("requestSendFile", {"type": media_type})
1998
2132
  return result.get("data", {}).get("upload_url")
1999
- async def _send_uploaded_file(self, chat_id: str, file_id: str,type_file : str = "file",text: Optional[str] = None, chat_keypad: Optional[Dict[str, Any]] = None, inline_keypad: Optional[Dict[str, Any]] = None, disable_notification: bool = False, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2133
+ async def _send_uploaded_file(self, chat_id: str, file_id: str,type_file : str = "file",text: Optional[str] = None, chat_keypad: Optional[Dict[str, Any]] = None, inline_keypad: Optional[Dict[str, Any]] = None, disable_notification: bool = False, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2000
2134
  payload = {"chat_id": chat_id, "file_id": file_id, "text": text, "disable_notification": disable_notification, "chat_keypad_type": chat_keypad_type}
2001
2135
  if chat_keypad: payload["chat_keypad"] = chat_keypad
2002
2136
  if inline_keypad: payload["inline_keypad"] = inline_keypad
2003
2137
  if reply_to_message_id: payload["reply_to_message_id"] = str(reply_to_message_id)
2138
+ parse_mode_to_use = parse_mode or self.parse_mode
2139
+ if text:
2140
+ text, metadata = self._parse_text_metadata(text, parse_mode_to_use)
2141
+ payload["text"] = text
2142
+ if metadata:payload["metadata"] = metadata
2004
2143
  payload["time"] = "10"
2005
2144
  resp = await self._post("sendFile", payload)
2006
2145
  message_id_put = resp["data"]["message_id"]
@@ -2020,20 +2159,26 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
2020
2159
  "chat_keypad_type":chat_keypad_type
2021
2160
  }
2022
2161
  return AttrDict(result)
2023
- async def _send_file_generic(self, media_type, chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type):
2162
+ async def _send_file_generic(self, media_type, chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode: Optional[Literal["HTML", "Markdown"]] = None):
2024
2163
  if path:
2025
2164
  file_name = file_name or Path(path).name
2026
2165
  upload_url = await self.get_upload_url(media_type)
2027
2166
  file_id = await self.upload_media_file(upload_url, file_name, path)
2028
2167
  if not file_id:
2029
2168
  raise ValueError("Either path or file_id must be provided.")
2030
- return await self._send_uploaded_file(chat_id=chat_id, file_id=file_id, text=text, inline_keypad=inline_keypad, chat_keypad=chat_keypad, reply_to_message_id=reply_to_message_id, disable_notification=disable_notification, chat_keypad_type=chat_keypad_type,type_file=media_type)
2031
- async def send_document(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2032
- return await self._send_file_generic("File", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
2033
- async def send_file(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, caption: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2034
- return await self._send_file_generic("File", chat_id, path, file_id, caption, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
2035
- async def re_send(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, caption: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2036
- return await self._send_file_generic("File", chat_id, path, file_id, caption, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
2169
+ return await self._send_uploaded_file(chat_id=chat_id, file_id=file_id, text=text, inline_keypad=inline_keypad, chat_keypad=chat_keypad, reply_to_message_id=reply_to_message_id, disable_notification=disable_notification, chat_keypad_type=chat_keypad_type,type_file=media_type,parse_mode=parse_mode)
2170
+ async def send_document(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2171
+ return await self._send_file_generic("File", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode=parse_mode)
2172
+ async def send_file(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, caption: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2173
+ return await self._send_file_generic("File", chat_id, path, file_id, caption, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode=parse_mode)
2174
+ async def re_send(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, caption: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2175
+ return await self._send_file_generic("File", chat_id, path, file_id, caption, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode=parse_mode)
2176
+ async def send_video(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2177
+ return await self._send_file_generic("Video", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode=parse_mode)
2178
+ async def send_voice(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2179
+ return await self._send_file_generic("voice", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode=parse_mode)
2180
+ async def send_image(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",parse_mode: Optional[Literal["HTML", "Markdown"]] = None) -> Dict[str, Any]:
2181
+ return await self._send_file_generic("Image", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type,parse_mode=parse_mode)
2037
2182
  async def send_music(
2038
2183
  self,
2039
2184
  chat_id: str,
@@ -2045,7 +2190,8 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
2045
2190
  chat_keypad: Optional[Dict[str, Any]] = None,
2046
2191
  reply_to_message_id: Optional[str] = None,
2047
2192
  disable_notification: bool = False,
2048
- chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None"
2193
+ chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",
2194
+ parse_mode: Optional[Literal["HTML", "Markdown"]] = None
2049
2195
  ) -> Dict[str, Any]:
2050
2196
  valid_extensions = {"ogg", "oga", "opus", "flac"}
2051
2197
  extension = "flac"
@@ -2077,7 +2223,8 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
2077
2223
  chat_keypad,
2078
2224
  reply_to_message_id,
2079
2225
  disable_notification,
2080
- chat_keypad_type
2226
+ chat_keypad_type,
2227
+ parse_mode=parse_mode
2081
2228
  )
2082
2229
  async def send_gif(
2083
2230
  self,
@@ -2090,7 +2237,8 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
2090
2237
  chat_keypad: Optional[Dict[str, Any]] = None,
2091
2238
  reply_to_message_id: Optional[str] = None,
2092
2239
  disable_notification: bool = False,
2093
- chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None"
2240
+ chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None",
2241
+ parse_mode: Optional[Literal["HTML", "Markdown"]] = None
2094
2242
  ) -> Dict[str, Any]:
2095
2243
  valid_extensions = {"gif"}
2096
2244
  extension = "gif"
@@ -2122,7 +2270,8 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
2122
2270
  chat_keypad,
2123
2271
  reply_to_message_id,
2124
2272
  disable_notification,
2125
- chat_keypad_type
2273
+ chat_keypad_type,
2274
+ parse_mode=parse_mode
2126
2275
  )
2127
2276
 
2128
2277
  async def get_avatar_me(self, save_as: str = None) -> str:
@@ -2285,12 +2434,7 @@ max_cache_size and max_msg_age help manage duplicate message processing efficien
2285
2434
  return await self._post("sendContact", {"chat_id": chat_id, "first_name": first_name, "last_name": last_name, "phone_number": phone_number,"inline_keypad": inline_keypad,"chat_keypad": chat_keypad,"chat_keypad_type": chat_keypad_type})
2286
2435
  async def get_chat(self, chat_id: str) -> Dict[str, Any]:
2287
2436
  return await self._post("getChat", {"chat_id": chat_id})
2288
- async def send_video(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2289
- return await self._send_file_generic("Video", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
2290
- async def send_voice(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2291
- return await self._send_file_generic("voice", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
2292
- async def send_image(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Remove", "None"]] = "None") -> Dict[str, Any]:
2293
- return await self._send_file_generic("Image", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
2437
+
2294
2438
  def get_all_member(self, channel_guid: str, search_text: str = None, start_id: str = None, just_get_guids: bool = False):
2295
2439
  client = self._get_client()
2296
2440
  return client.get_all_members(channel_guid, search_text, start_id, just_get_guids)
@@ -291,6 +291,36 @@ class Message:
291
291
  self.is_archive = None
292
292
  self.is_executable = None
293
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
+ ])
294
324
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
295
325
  self.time: str = self.raw_data.get("time")
296
326
  self.is_edited: bool = self.raw_data.get("is_edited", False)
@@ -337,7 +367,7 @@ class Message:
337
367
  self.bot.sessions[self.chat_id] = {}
338
368
  return self.bot.sessions[self.chat_id]
339
369
 
340
- def reply(self, text: str, delete_after: int = None, **kwargs):
370
+ def reply(self, text: str, delete_after: int = None,parse_mode : Optional[Literal["HTML", "Markdown"]] = None , **kwargs):
341
371
  async def _reply_async():
342
372
  send_func = self.bot.send_message
343
373
  if inspect.iscoroutinefunction(send_func):
@@ -346,6 +376,7 @@ class Message:
346
376
  text,
347
377
  reply_to_message_id=self.message_id,
348
378
  delete_after=delete_after,
379
+ parse_mode=parse_mode,
349
380
  **kwargs
350
381
  )
351
382
  else:
@@ -0,0 +1,92 @@
1
+ import re
2
+ from typing import Any, Dict, List
3
+ import markdownify
4
+ class Track_parsed:
5
+ _PATT = re.compile(
6
+ r"(?P<pre>```(?P<pre_c>[\s\S]*?)```)"
7
+ r"|(?P<bold>\*\*(?P<bold_c>.*?)\*\*)"
8
+ r"|(?P<mono>`(?P<mono_c>.*?)`)"
9
+ r"|(?P<italic>__(?P<italic_c>.*?)__)"
10
+ r"|(?P<underline>--(?P<underline_c>.*?)--)"
11
+ r"|(?P<strike>~~(?P<strike_c>.*?)~~)"
12
+ r"|(?P<spoiler>\|\|(?P<spoiler_c>.*?)\|\|)"
13
+ r"|(?P<link>\[(?P<link_text>.*?)\]\((?P<link_url>\S+?)\))",
14
+ flags=re.DOTALL,
15
+ )
16
+ _TYPE_MAP = {
17
+ "pre": "Pre",
18
+ "bold": "Bold",
19
+ "mono": "Mono",
20
+ "italic": "Italic",
21
+ "underline": "Underline",
22
+ "strike": "Strike",
23
+ "spoiler": "Spoiler",
24
+ "link": "Link",
25
+ }
26
+
27
+ @staticmethod
28
+ def _utf16_len_java_style(s: str) -> int:
29
+ return len(s.encode("utf-16-be")) // 2
30
+
31
+ @staticmethod
32
+ def _html2md(src: str) -> str:
33
+ src = re.sub(r'<i>(.*?)</i>', r'||\1||', src, flags=re.DOTALL)
34
+ src = re.sub(r'<span class="spoiler">(.*?)</span>', r'||\1||', src, flags=re.DOTALL)
35
+ src = markdownify.markdownify(html=src).strip()
36
+ src = src.replace("@@SPOILER@@", "||")
37
+ return src
38
+
39
+ def transcribe(self, src: str, mode: str = "MARKDOWN") -> Dict[str, Any]:
40
+ if mode and mode.upper() == "HTML":src = self._html2md(src)
41
+
42
+ payload_parts: List[Dict[str, Any]] = []
43
+
44
+ normalized_text = src
45
+ byte_offset = 0
46
+ char_offset = 0
47
+
48
+ matches = list(self._PATT.finditer(src))
49
+ for m in matches:
50
+ whole = m.group(0)
51
+ start, end = m.span()
52
+ adj_from = self._utf16_len_java_style(src[:start]) - byte_offset
53
+ adj_char_from = start - char_offset
54
+
55
+ gname = m.lastgroup
56
+ if not gname:
57
+ continue
58
+ if gname == "link":
59
+ inner = m.group("link_text") or ""
60
+ link_href = m.group("link_url")
61
+ else:
62
+ inner = m.group(f"{gname}_c") or ""
63
+ link_href = None
64
+
65
+ if inner == "":
66
+ continue
67
+
68
+ content_utf16_len = self._utf16_len_java_style(inner)
69
+
70
+ part: Dict[str, Any] = {
71
+ "type": self._TYPE_MAP.get(gname, "Unknown"),
72
+ "from_index": adj_from,
73
+ "length": content_utf16_len,
74
+ }
75
+ if link_href:
76
+ part["link_url"] = link_href
77
+ payload_parts.append(part)
78
+ normalized_text = (
79
+ normalized_text[:adj_char_from] + inner + normalized_text[end - char_offset :]
80
+ )
81
+ byte_offset += self._utf16_len_java_style(whole) - content_utf16_len
82
+ char_offset += (end - start) - len(inner)
83
+
84
+ result: Dict[str, Any] = {"text": normalized_text.strip()}
85
+ if payload_parts:
86
+ result["metadata"] = {"meta_data_parts": payload_parts}
87
+
88
+ return result
89
+
90
+
91
+ def parse(self, text: str, parse_mode: str = "MARKDOWN") -> Dict[str, Any]:
92
+ return self.transcribe(text, mode=parse_mode)
@@ -1,8 +1,68 @@
1
- from typing import Any, Dict, List,Optional
2
1
  from typing import Union
3
2
  from pathlib import Path
3
+ from typing import List, Optional, Dict, Any, Literal, Callable, Union,Set
4
4
  import re,inspect
5
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))
6
66
  class File:
7
67
  def __init__(self, data: dict):
8
68
  self.file_id: str = data.get("file_id")
@@ -215,10 +275,52 @@ class Message:
215
275
  self.author_guid = self.raw_data.get("sender_id", sender_id)
216
276
  self.message_id: str = self.raw_data.get("message_id", message_id)
217
277
  self.text: str = self.raw_data.get("text", text)
218
- self.has_link = bool(re.search(
219
- 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)",
220
- self.raw_data.get("text", text) or ""
221
- ))
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
+ ])
222
324
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
223
325
  self.time: str = self.raw_data.get("time")
224
326
  self.is_edited: bool = self.raw_data.get("is_edited", False)
@@ -265,7 +367,7 @@ class Message:
265
367
  self.bot.sessions[self.chat_id] = {}
266
368
  return self.bot.sessions[self.chat_id]
267
369
 
268
- def reply(self, text: str, delete_after: int = None, **kwargs):
370
+ def reply(self, text: str, delete_after: int = None,parse_mode : Optional[Literal["HTML", "Markdown"]] = None , **kwargs):
269
371
  async def _reply_async():
270
372
  send_func = self.bot.send_message
271
373
  if inspect.iscoroutinefunction(send_func):
@@ -274,6 +376,7 @@ class Message:
274
376
  text,
275
377
  reply_to_message_id=self.message_id,
276
378
  delete_after=delete_after,
379
+ parse_mode=parse_mode,
277
380
  **kwargs
278
381
  )
279
382
  else:
@@ -336,14 +439,40 @@ class Message:
336
439
  )
337
440
 
338
441
 
339
- def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
340
- 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 = {
341
460
  "chat_id": self.chat_id,
342
461
  "question": question,
343
462
  "options": options,
344
- "reply_to_message_id": self.message_id,
345
- **kwargs
346
- })
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)
347
476
 
348
477
 
349
478
  def reply_document(
@@ -617,13 +746,69 @@ class InlineMessage:
617
746
  if self.chat_id not in self.bot.sessions:
618
747
  self.bot.sessions[self.chat_id] = {}
619
748
  return self.bot.sessions[self.chat_id]
620
- def reply(self, text: str, **kwargs):
621
- return self.bot.send_message(
622
- self.chat_id,
623
- text,
624
- reply_to_message_id=self.message_id,
625
- **kwargs
626
- )
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())
789
+
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())
627
812
  def answer(self, text: str, **kwargs):
628
813
  return self.bot.send_message(
629
814
  self.chat_id,
@@ -631,6 +816,7 @@ class InlineMessage:
631
816
  reply_to_message_id=self.message_id,
632
817
  **kwargs
633
818
  )
819
+
634
820
 
635
821
  def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
636
822
  return self.bot._post("sendPoll", {
@@ -841,4 +1027,12 @@ class InlineMessage:
841
1027
  return self.bot.delete_message(
842
1028
  chat_id=self.chat_id,
843
1029
  message_id=self.message_id
844
- )
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)
@@ -13,7 +13,7 @@ with open("README.md", "r", encoding="utf-8") as f:
13
13
 
14
14
  setup(
15
15
  name='Rubka',
16
- version='7.1.9',
16
+ version='7.1.11',
17
17
  description=(
18
18
  "Rubika: A Python library for interacting with the Rubika Bot API. "
19
19
  "This library provides an easy-to-use interface to send messages, polls, "
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes