Rubka 1.7.2__tar.gz → 1.8.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 1.7.2
3
+ Version: 1.8.6
4
4
  Summary: A Python library for interacting with Rubika Bot API.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
6
  Download-URL: https://github.com/Mahdy-Ahmadi/Rubka/archive/refs/tags/v0.1.0.tar.gz
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 1.7.2
3
+ Version: 1.8.6
4
4
  Summary: A Python library for interacting with Rubika Bot API.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
6
  Download-URL: https://github.com/Mahdy-Ahmadi/Rubka/archive/refs/tags/v0.1.0.tar.gz
@@ -3,9 +3,58 @@ from typing import List, Optional, Dict, Any, Literal
3
3
  from .exceptions import APIRequestError
4
4
  from .logger import logger
5
5
  from typing import Callable
6
- from .context import Message
6
+ from .context import Message,InlineMessage
7
7
  API_URL = "https://botapi.rubika.ir/v3"
8
-
8
+ import sys
9
+ import subprocess
10
+ import requests
11
+ def install_package(package_name):
12
+ try:
13
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
14
+ return True
15
+ except Exception:return False
16
+
17
+ def get_importlib_metadata():
18
+ try:
19
+ from importlib.metadata import version, PackageNotFoundError
20
+ return version, PackageNotFoundError
21
+ except ImportError:
22
+ if install_package("importlib-metadata"):
23
+ try:
24
+ from importlib_metadata import version, PackageNotFoundError
25
+ return version, PackageNotFoundError
26
+ except ImportError:
27
+ return None, None
28
+ return None, None
29
+
30
+ version, PackageNotFoundError = get_importlib_metadata()
31
+ def get_installed_version(package_name: str) -> str:
32
+ if version is None:return "unknown"
33
+ try:
34
+ return version(package_name)
35
+ except PackageNotFoundError:
36
+ return None
37
+ def get_latest_version(package_name: str) -> str:
38
+ url = f"https://pypi.org/pypi/{package_name}/json"
39
+ try:
40
+ resp = requests.get(url, timeout=5)
41
+ resp.raise_for_status()
42
+ data = resp.json()
43
+ return data["info"]["version"]
44
+ except Exception:return None
45
+ def check_rubka_version():
46
+ package_name = "rubka"
47
+ installed_version = get_installed_version(package_name)
48
+ if installed_version is None:return
49
+ latest_version = get_latest_version(package_name)
50
+ if latest_version is None:return
51
+ if installed_version != latest_version:
52
+ print(f"\n\n⚠️ WARNING: Your installed version of '{package_name}' is outdated!")
53
+ print(f"Installed version: {installed_version}")
54
+ print(f"Latest version: {latest_version}")
55
+ print(f"Please update it using:\n\npip install --upgrade {package_name}\n")
56
+
57
+ check_rubka_version()
9
58
  class Robot:
10
59
  """
11
60
  Main class to interact with Rubika Bot API.
@@ -17,6 +66,11 @@ class Robot:
17
66
  self._offset_id = None
18
67
  self.session = requests.Session()
19
68
  self.sessions: Dict[str, Dict[str, Any]] = {}
69
+ self._callback_handler = None
70
+ self._message_handler = None
71
+ self._inline_query_handler = None
72
+
73
+
20
74
  logger.info(f"Initialized RubikaBot with token: {token[:8]}***")
21
75
 
22
76
  def _post(self, method: str, data: Dict[str, Any]) -> Dict[str, Any]:
@@ -51,8 +105,20 @@ class Robot:
51
105
  return decorator
52
106
 
53
107
 
108
+ def on_inline_query(self):
109
+ def decorator(func: Callable[[Any, InlineMessage], None]):
110
+ self._inline_query_handler = func
111
+ return func
112
+ return decorator
113
+
114
+
54
115
 
55
116
  def _process_update(self, update: Dict[str, Any]):
117
+ if update.get('type') == 'ReceiveQuery':
118
+ msg = update.get("inline_message", {})
119
+ if self._inline_query_handler:
120
+ context = InlineMessage(bot=self, raw_data=msg)
121
+ self._inline_query_handler(self, context)
56
122
  if update.get('type') == 'NewMessage':
57
123
  msg = update.get('new_message', {})
58
124
  chat_id = update.get('chat_id')
@@ -78,24 +144,41 @@ class Robot:
78
144
  return
79
145
 
80
146
  handler["func"](self, context)
81
-
82
-
83
-
84
-
147
+ elif update.get('type') == 'ReceiveQuery':
148
+ msg = update.get("inline_message", {})
149
+ chat_id = msg.get("chat_id")
150
+ message_id = msg.get("message_id")
151
+ sender_id = msg.get("sender_id")
152
+ text = msg.get("text")
153
+
154
+ if hasattr(self, "_callback_handler"):
155
+ context = Message(bot=self, chat_id=chat_id, message_id=message_id, sender_id=sender_id, text=text, raw_data=msg)
156
+ self._callback_handler(self, context)
85
157
 
86
158
  def run(self):
87
159
  print("Bot started running...")
160
+ if self._offset_id is None:
161
+ try:
162
+ latest = self.get_updates(limit=100)
163
+ if latest and latest.get("data") and latest["data"].get("updates"):
164
+ updates = latest["data"]["updates"]
165
+ last_update = updates[-1]
166
+ self._offset_id = latest["data"].get("next_offset_id")
167
+ print(f"Offset initialized to: {self._offset_id}")
168
+ else:
169
+ print("No updates found.")
170
+ except Exception as e:
171
+ print(f"Failed to fetch latest message: {e}")
172
+
88
173
  while True:
89
174
  try:
90
- updates = self.get_updates(offset_id=self._offset_id, limit=10)
91
- if updates and updates.get('data'):
92
- for update in updates['data'].get('updates', []):
175
+ updates = self.get_updates(offset_id=self._offset_id, limit=1)
176
+ if updates and updates.get("data"):
177
+ for update in updates["data"].get("updates", []):
93
178
  self._process_update(update)
94
- self._offset_id = updates['data'].get('next_offset_id', self._offset_id)
179
+ self._offset_id = updates["data"].get("next_offset_id", self._offset_id)
95
180
  except Exception as e:
96
181
  print(f"Error in run loop: {e}")
97
- #logger.error(f"API request failed: {e}")
98
- raise APIRequestError(f"API request failed: {e}") from e
99
182
 
100
183
  def send_message(
101
184
  self,
@@ -185,15 +185,18 @@ class Bot:
185
185
  self.start_message: str = data.get("start_message")
186
186
  self.share_url: str = data.get("share_url")
187
187
  class Message:
188
- def __init__(self, bot, chat_id, message_id, sender_id, text, raw_data=None):
188
+ def __init__(self, bot, chat_id, message_id, sender_id, text=None, raw_data=None):
189
189
  self.bot = bot
190
190
  self.chat_id = chat_id
191
- self.message_id = message_id
192
- self.sender_id = sender_id
193
- self.text = text
194
- self.args = []
195
191
  self.raw_data = raw_data or {}
192
+ self.message_id: str = self.raw_data.get("message_id", message_id)
193
+ self.text: str = self.raw_data.get("text", text)
194
+ self.sender_id: str = self.raw_data.get("sender_id", sender_id)
195
+ self.time: str = self.raw_data.get("time")
196
+ self.is_edited: bool = self.raw_data.get("is_edited", False)
197
+ self.sender_type: str = self.raw_data.get("sender_type")
196
198
 
199
+ self.args = []
197
200
  self.reply_to_message_id: Optional[str] = self.raw_data.get("reply_to_message_id")
198
201
  self.forwarded_from = ForwardedFrom(self.raw_data["forwarded_from"]) if "forwarded_from" in self.raw_data else None
199
202
  self.file = File(self.raw_data["file"]) if "file" in self.raw_data else None
@@ -292,3 +295,34 @@ class Message:
292
295
  chat_id=self.chat_id,
293
296
  message_id=self.message_id
294
297
  )
298
+ class InlineMessage:
299
+ def __init__(self, bot, raw_data: dict):
300
+ self.bot = bot
301
+ self.raw_data = raw_data
302
+
303
+ self.chat_id: str = raw_data.get("chat_id")
304
+ self.message_id: str = raw_data.get("message_id")
305
+ self.sender_id: str = raw_data.get("sender_id")
306
+ self.text: str = raw_data.get("text")
307
+ self.aux_data = AuxData(raw_data.get("aux_data", {})) if "aux_data" in raw_data else None
308
+
309
+ def reply(self, text: str, **kwargs):
310
+ return self.bot.send_message(
311
+ chat_id=self.chat_id,
312
+ text=text,
313
+ reply_to_message_id=self.message_id,
314
+ **kwargs
315
+ )
316
+
317
+ def edit(self, new_text: str):
318
+ return self.bot.edit_message_text(
319
+ chat_id=self.chat_id,
320
+ message_id=self.message_id,
321
+ text=new_text
322
+ )
323
+
324
+ def delete(self):
325
+ return self.bot.delete_message(
326
+ chat_id=self.chat_id,
327
+ message_id=self.message_id
328
+ )
@@ -0,0 +1,48 @@
1
+ from typing import Dict
2
+
3
+ class InlineBuilder:
4
+ def __init__(self):
5
+ self.rows = []
6
+
7
+ def row(self, *buttons: Dict[str, str]):
8
+ self.rows.append({"buttons": list(buttons)})
9
+ return self
10
+
11
+ def button(self, id: str, text: str, type: str = "Simple") -> Dict[str, str]:
12
+ return {"id": id, "type": type, "button_text": text}
13
+
14
+ def build(self):
15
+ return {"rows": self.rows}
16
+ from typing import List, Dict, Optional
17
+
18
+ class ChatKeypadBuilder:
19
+ def __init__(self):
20
+ self.rows: List[Dict[str, List[Dict[str, str]]]] = []
21
+
22
+ def row(self, *buttons: Dict[str, str]) -> "ChatKeypadBuilder":
23
+ """
24
+ یک ردیف دکمه به کی‌پد اضافه می‌کند.
25
+ ورودی: چند دیکشنری که نماینده دکمه‌ها هستند.
26
+ """
27
+ self.rows.append({"buttons": list(buttons)})
28
+ return self
29
+
30
+ def button(self, id: str, text: str, type: str = "Simple") -> Dict[str, str]:
31
+ """
32
+ دیکشنری یک دکمه می‌سازد.
33
+ """
34
+ return {"id": id, "type": type, "button_text": text}
35
+
36
+ def build(
37
+ self,
38
+ resize_keyboard: bool = True,
39
+ on_time_keyboard: bool = False
40
+ ) -> Dict[str, object]:
41
+ """
42
+ ساختار نهایی chat_keypad را می‌سازد.
43
+ """
44
+ return {
45
+ "rows": self.rows,
46
+ "resize_keyboard": resize_keyboard,
47
+ "on_time_keyboard": on_time_keyboard
48
+ }
@@ -1,10 +1,10 @@
1
1
  import logging
2
2
 
3
3
  logger = logging.getLogger("rubka")
4
- logger.setLevel(logging.DEBUG)
4
+ logger.setLevel(logging.ERROR)
5
5
 
6
6
  ch = logging.StreamHandler()
7
- ch.setLevel(logging.DEBUG)
7
+ ch.setLevel(logging.ERROR)
8
8
 
9
9
  formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
10
10
  ch.setFormatter(formatter)
@@ -8,7 +8,7 @@ except FileNotFoundError:
8
8
 
9
9
  setup(
10
10
  name='Rubka',
11
- version='1.7.2',
11
+ version='1.8.6',
12
12
  description='A Python library for interacting with Rubika Bot API.',
13
13
  long_description=long_description,
14
14
  long_description_content_type='text/markdown',
@@ -1,15 +0,0 @@
1
- from typing import Dict
2
-
3
- class InlineBuilder:
4
- def __init__(self):
5
- self.rows = []
6
-
7
- def row(self, *buttons: Dict[str, str]):
8
- self.rows.append({"buttons": list(buttons)})
9
- return self
10
-
11
- def button(self, id: str, text: str, type: str = "Simple") -> Dict[str, str]:
12
- return {"id": id, "type": type, "button_text": text}
13
-
14
- def build(self):
15
- return {"rows": self.rows}
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