Rubka 7.1.10__tar.gz → 7.1.12__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.10 → rubka-7.1.12}/PKG-INFO +1 -1
  2. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/PKG-INFO +1 -1
  3. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/SOURCES.txt +1 -0
  4. {rubka-7.1.10 → rubka-7.1.12}/rubka/api.py +2 -1
  5. {rubka-7.1.10 → rubka-7.1.12}/rubka/asynco.py +183 -39
  6. {rubka-7.1.10 → rubka-7.1.12}/rubka/context.py +2 -1
  7. rubka-7.1.12/rubka/metadata.py +102 -0
  8. {rubka-7.1.10 → rubka-7.1.12}/rubka/update.py +3 -1
  9. {rubka-7.1.10 → rubka-7.1.12}/setup.py +1 -1
  10. {rubka-7.1.10 → rubka-7.1.12}/README.md +0 -0
  11. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/dependency_links.txt +0 -0
  12. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/entry_points.txt +0 -0
  13. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/not-zip-safe +0 -0
  14. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/requires.txt +0 -0
  15. {rubka-7.1.10 → rubka-7.1.12}/Rubka.egg-info/top_level.txt +0 -0
  16. {rubka-7.1.10 → rubka-7.1.12}/rubka/__init__.py +0 -0
  17. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/__init__.py +0 -0
  18. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/client/__init__.py +0 -0
  19. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/client/client.py +0 -0
  20. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/crypto/__init__.py +0 -0
  21. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/crypto/crypto.py +0 -0
  22. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/enums.py +0 -0
  23. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/exceptions.py +0 -0
  24. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/methods/__init__.py +0 -0
  25. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/methods/methods.py +0 -0
  26. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/network/__init__.py +0 -0
  27. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/network/helper.py +0 -0
  28. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/network/network.py +0 -0
  29. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/network/socket.py +0 -0
  30. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/sessions/__init__.py +0 -0
  31. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/sessions/sessions.py +0 -0
  32. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/types/__init__.py +0 -0
  33. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/types/socket/__init__.py +0 -0
  34. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/types/socket/message.py +0 -0
  35. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/utils/__init__.py +0 -0
  36. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/utils/configs.py +0 -0
  37. {rubka-7.1.10 → rubka-7.1.12}/rubka/adaptorrubka/utils/utils.py +0 -0
  38. {rubka-7.1.10 → rubka-7.1.12}/rubka/button.py +0 -0
  39. {rubka-7.1.10 → rubka-7.1.12}/rubka/config.py +0 -0
  40. {rubka-7.1.10 → rubka-7.1.12}/rubka/decorators.py +0 -0
  41. {rubka-7.1.10 → rubka-7.1.12}/rubka/exceptions.py +0 -0
  42. {rubka-7.1.10 → rubka-7.1.12}/rubka/filters.py +0 -0
  43. {rubka-7.1.10 → rubka-7.1.12}/rubka/helpers.py +0 -0
  44. {rubka-7.1.10 → rubka-7.1.12}/rubka/jobs.py +0 -0
  45. {rubka-7.1.10 → rubka-7.1.12}/rubka/keyboards.py +0 -0
  46. {rubka-7.1.10 → rubka-7.1.12}/rubka/keypad.py +0 -0
  47. {rubka-7.1.10 → rubka-7.1.12}/rubka/logger.py +0 -0
  48. {rubka-7.1.10 → rubka-7.1.12}/rubka/rubino.py +0 -0
  49. {rubka-7.1.10 → rubka-7.1.12}/rubka/tv.py +0 -0
  50. {rubka-7.1.10 → rubka-7.1.12}/rubka/utils.py +0 -0
  51. {rubka-7.1.10 → rubka-7.1.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 7.1.10
3
+ Version: 7.1.12
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.10
3
+ Version: 7.1.12
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)
@@ -367,7 +367,7 @@ class Message:
367
367
  self.bot.sessions[self.chat_id] = {}
368
368
  return self.bot.sessions[self.chat_id]
369
369
 
370
- 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):
371
371
  async def _reply_async():
372
372
  send_func = self.bot.send_message
373
373
  if inspect.iscoroutinefunction(send_func):
@@ -376,6 +376,7 @@ class Message:
376
376
  text,
377
377
  reply_to_message_id=self.message_id,
378
378
  delete_after=delete_after,
379
+ parse_mode=parse_mode,
379
380
  **kwargs
380
381
  )
381
382
  else:
@@ -0,0 +1,102 @@
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<link>\[(?P<link_text>.*?)\]\((?P<link_url>\S+?)\))"
12
+ r"|(?P<quote>\$(?P<quote_c>[\s\S]*?)\$)"
13
+ r"|(?P<strike>~~(?P<strike_c>.*?)~~)"
14
+ r"|(?P<spoiler>\|\|(?P<spoiler_c>.*?)\|\|)",
15
+ flags=re.DOTALL,
16
+ )
17
+
18
+
19
+
20
+ _TYPE_MAP = {
21
+ "pre": "Pre",
22
+ "bold": "Bold",
23
+ "mono": "Mono",
24
+ "italic": "Italic",
25
+ "underline": "Underline",
26
+ "strike": "Strike",
27
+ "spoiler": "Spoiler",
28
+ "quote": "Quote",
29
+ "link": "Link",
30
+ }
31
+
32
+ @staticmethod
33
+ def _utf16_len_java_style(s: str) -> int:
34
+ return len(s.encode("utf-16-be")) // 2
35
+
36
+ @staticmethod
37
+ def _html2md(src: str) -> str:
38
+ src = re.sub(r'<i>(.*?)</i>', r'||\1||', src, flags=re.DOTALL)
39
+ src = re.sub(r'<span class="spoiler">(.*?)</span>', r'||\1||', src, flags=re.DOTALL)
40
+ src = markdownify.markdownify(html=src).strip()
41
+ src = src.replace("@@SPOILER@@", "||")
42
+ return src
43
+
44
+ def transcribe(self, src: str, mode: str = "MARKDOWN") -> Dict[str, Any]:
45
+ if mode and mode.upper() == "HTML":src = self._html2md(src)
46
+
47
+ payload_parts: List[Dict[str, Any]] = []
48
+
49
+ normalized_text = src
50
+ byte_offset = 0
51
+ char_offset = 0
52
+
53
+ matches = list(self._PATT.finditer(src))
54
+ for m in matches:
55
+ whole = m.group(0)
56
+ start, end = m.span()
57
+ adj_from = self._utf16_len_java_style(src[:start]) - byte_offset
58
+ adj_char_from = start - char_offset
59
+
60
+ gname = m.lastgroup
61
+ if not gname:
62
+ continue
63
+
64
+ if gname == "link":
65
+ inner = m.group("link_text") or ""
66
+ link_href = m.group("link_url")
67
+ else:
68
+ inner = m.group(f"{gname}_c") or ""
69
+ link_href = None
70
+ if gname == "quote":
71
+ inner_metadata = self.transcribe(inner, mode="MARKDOWN")
72
+ inner = inner_metadata["text"]
73
+ if "metadata" in inner_metadata:
74
+ for part in inner_metadata["metadata"]["meta_data_parts"]:
75
+ part["from_index"] += adj_from
76
+ payload_parts.append(part)
77
+ if inner == "":
78
+ continue
79
+ content_utf16_len = self._utf16_len_java_style(inner)
80
+ part: Dict[str, Any] = {
81
+ "type": self._TYPE_MAP.get(gname, "Unknown"),
82
+ "from_index": adj_from,
83
+ "length": content_utf16_len,
84
+ }
85
+ if link_href:
86
+ part["link_url"] = link_href
87
+ payload_parts.append(part)
88
+ normalized_text = (
89
+ normalized_text[:adj_char_from] + inner + normalized_text[end - char_offset :]
90
+ )
91
+ byte_offset += self._utf16_len_java_style(whole) - content_utf16_len
92
+ char_offset += (end - start) - len(inner)
93
+
94
+ result: Dict[str, Any] = {"text": normalized_text.strip()}
95
+ if payload_parts:
96
+ result["metadata"] = {"meta_data_parts": payload_parts}
97
+
98
+ return result
99
+
100
+
101
+ def parse(self, text: str, parse_mode: str = "MARKDOWN") -> Dict[str, Any]:
102
+ return self.transcribe(text, mode=parse_mode)
@@ -292,6 +292,7 @@ class Message:
292
292
  self.is_executable = None
293
293
  self.is_font = None
294
294
  self.metadata = self.raw_data.get("metadata", {})
295
+ self.is_metadata = self.metadata
295
296
  self.meta_parts = self.metadata.get("meta_data_parts", [])
296
297
  self.has_metadata = bool(self.meta_parts)
297
298
  self.meta_types = [part.get("type") for part in self.meta_parts] if self.has_metadata else []
@@ -366,7 +367,7 @@ class Message:
366
367
  self.bot.sessions[self.chat_id] = {}
367
368
  return self.bot.sessions[self.chat_id]
368
369
 
369
- 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):
370
371
  async def _reply_async():
371
372
  send_func = self.bot.send_message
372
373
  if inspect.iscoroutinefunction(send_func):
@@ -375,6 +376,7 @@ class Message:
375
376
  text,
376
377
  reply_to_message_id=self.message_id,
377
378
  delete_after=delete_after,
379
+ parse_mode=parse_mode,
378
380
  **kwargs
379
381
  )
380
382
  else:
@@ -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.10',
16
+ version='7.1.12',
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