Rubka 4.4.6__py3-none-any.whl → 4.4.19__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.
@@ -1,12 +1,19 @@
1
+ import sys
2
+ import subprocess
1
3
 
2
- try:
3
- from websocket import WebSocketApp
4
- except:
5
- import os
4
+ def install_and_import(package_name):
6
5
  try:
7
- os.system("pip install websocket-client")
8
- except:
9
- os.system("python -m pip install websocket-client")
6
+ __import__(package_name)
7
+ except ModuleNotFoundError:
8
+ print(f"Module '{package_name}' not found. Installing...")
9
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
10
+ finally:
11
+ globals()[package_name] = __import__(package_name)
12
+
13
+ install_and_import("websocket")
14
+
15
+ from websocket import WebSocketApp
16
+
10
17
  from .helper import Helper
11
18
  from json import dumps, loads
12
19
  from threading import Thread
@@ -21,4 +28,4 @@ class Socket:
21
28
  self.methods = methods
22
29
  self.handlers = {}
23
30
 
24
- ...
31
+ ...
rubka/api.py CHANGED
@@ -8,7 +8,8 @@ from .context import Message,InlineMessage
8
8
  from typing import Optional, Union, Literal, Dict, Any
9
9
  from pathlib import Path
10
10
  import requests
11
-
11
+ import time
12
+ import datetime
12
13
  API_URL = "https://botapi.rubika.ir/v3"
13
14
  import sys
14
15
  import subprocess
@@ -54,10 +55,13 @@ def check_rubka_version():
54
55
  latest_version = get_latest_version(package_name)
55
56
  if latest_version is None:return
56
57
  if installed_version != latest_version:
57
- print(f"\n\n⚠️ WARNING: Your installed version of '{package_name}' is outdated!")
58
- print(f"Installed version: {installed_version}")
59
- print(f"Latest version: {latest_version}")
60
- print(f"Please update it using:\n\npip install --upgrade {package_name}\n")
58
+ print(f"\n\nWARNING: Your installed version of '{package_name}' is OUTDATED and may cause errors or security risks!")
59
+ print(f"Installed version : {installed_version}")
60
+ print(f"Latest available version : {latest_version}")
61
+ print(f"Please update IMMEDIATELY by running:")
62
+ print(f"\npip install {package_name}=={latest_version}\n")
63
+ print(f"Not updating may lead to malfunctions or incompatibility.")
64
+ print(f"To see new methods : @rubka_library\n\n")
61
65
 
62
66
  check_rubka_version()
63
67
  def show_last_six_words(text):
@@ -74,7 +78,7 @@ class Robot:
74
78
  Initialized with bot token.
75
79
  """
76
80
 
77
- def __init__(self, token: str,session_name : str = None,auth : str = None , Key : str = None,platform : str ="web",timeout : int =10):
81
+ 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):
78
82
  """
79
83
  راه‌اندازی اولیه ربات روبیکا و پیکربندی پارامترهای پایه.
80
84
 
@@ -95,11 +99,14 @@ class Robot:
95
99
  >>> bot = Robot(token="BOT_TOKEN", platform="android", timeout=15)
96
100
  """
97
101
  self.token = token
102
+ self._inline_query_handlers = []
103
+ self._inline_query_handlers: List[dict] = []
98
104
  self.timeout = timeout
99
105
  self.auth = auth
100
106
  self.session_name = session_name
101
107
  self.Key = Key
102
108
  self.platform = platform
109
+ self.web_hook = web_hook
103
110
  self._offset_id = None
104
111
  self.session = requests.Session()
105
112
  self.sessions: Dict[str, Dict[str, Any]] = {}
@@ -108,6 +115,11 @@ class Robot:
108
115
  self._inline_query_handler = None
109
116
  self._callback_handlers = None
110
117
  self._callback_handlers = [] # ✅ این خط مهمه
118
+ json_url = requests.get(web_hook).json()['url']
119
+ if web_hook:
120
+ for endpoint_type in ["ReceiveUpdate", "ReceiveInlineMessage", "ReceiveQuery", "GetSelectionItem", "SearchSelectionItems"]:
121
+ print(self.update_bot_endpoint(web_hook, endpoint_type))
122
+ self.web_hook = json_url
111
123
 
112
124
 
113
125
  logger.info(f"Initialized RubikaBot with token: {token[:8]}***")
@@ -153,48 +165,65 @@ class Robot:
153
165
  })
154
166
  return func
155
167
  return decorator
156
- def on_inline_query(self):
168
+ def _handle_inline_query(self, inline_message: InlineMessage):
169
+ aux_button_id = inline_message.aux_data.button_id if inline_message.aux_data else None
170
+
171
+ for handler in self._inline_query_handlers:
172
+ if handler["button_id"] is None or handler["button_id"] == aux_button_id:
173
+ try:
174
+ handler["func"](self, inline_message)
175
+ except Exception as e:
176
+ print(f"Error in inline query handler: {e}")
177
+
178
+ def on_inline_query(self, button_id: Optional[str] = None):
157
179
  def decorator(func: Callable[[Any, InlineMessage], None]):
158
- self._inline_query_handler = func
180
+ self._inline_query_handlers.append({
181
+ "func": func,
182
+ "button_id": button_id
183
+ })
159
184
  return func
160
185
  return decorator
186
+
161
187
 
162
- def _process_update(self, update: Dict[str, Any]):
188
+ def _process_update(self, update: dict):
163
189
  import threading, time
164
190
 
165
- if update.get('type') == 'ReceiveQuery':
191
+ # هندل پیام inline (معمولاً type = ReceiveQuery)
192
+ if update.get("type") == "ReceiveQuery":
166
193
  msg = update.get("inline_message", {})
167
- if self._inline_query_handler:
194
+ if update.get("type") == "ReceiveQuery":
195
+ msg = update.get("inline_message", {})
168
196
  context = InlineMessage(bot=self, raw_data=msg)
169
- threading.Thread(target=self._inline_query_handler, args=(self, context), daemon=True).start()
170
- return # اگر inline بود ادامه نده
197
+ threading.Thread(target=self._handle_inline_query, args=(context,), daemon=True).start()
198
+ return
171
199
 
172
- if update.get('type') == 'NewMessage':
173
- msg = update.get('new_message', {})
174
- chat_id = update.get('chat_id')
175
- message_id = msg.get('message_id')
176
- sender_id = msg.get('sender_id')
177
- text = msg.get('text')
200
+ # هندل پیام جدید متنی
201
+ if update.get("type") == "NewMessage":
202
+ msg = update.get("new_message", {})
203
+ chat_id = update.get("chat_id")
204
+ message_id = msg.get("message_id")
205
+ sender_id = msg.get("sender_id")
206
+ text = msg.get("text")
178
207
 
179
- # فیلتر زمان قدیمی
208
+ # فیلتر زمان پیام (مثلاً پیام‌های خیلی قدیمی)
180
209
  try:
181
210
  if msg.get("time") and (time.time() - float(msg["time"])) > 20:
182
211
  return
183
212
  except Exception:
184
213
  return
185
214
 
186
- # ساخت context یکجا
215
+ # ساخت context پیام
187
216
  context = Message(bot=self, chat_id=chat_id, message_id=message_id, sender_id=sender_id, text=text, raw_data=msg)
188
217
 
189
- # 💠 اول بررسی دکمه‌ها (callback)
218
+ # هندل callback ها (دکمه‌ها)
190
219
  if context.aux_data and hasattr(self, "_callback_handlers"):
191
220
  for handler in self._callback_handlers:
192
221
  if not handler["button_id"] or context.aux_data.button_id == handler["button_id"]:
193
222
  threading.Thread(target=handler["func"], args=(self, context), daemon=True).start()
194
- return # فقط اولین callback اجرا شود
223
+ return # فقط یک callback اجرا شود
195
224
 
196
- # 💠 بعد بررسی پیام‌های متنی معمولی
197
- if self._message_handler:
225
+ # هندل پیام‌های متنی معمولی
226
+ if hasattr(self, "_message_handler") and self._message_handler:
198
227
  handler = self._message_handler
199
228
 
200
229
  if handler["commands"]:
@@ -214,30 +243,137 @@ class Robot:
214
243
 
215
244
 
216
245
 
246
+
247
+ def get_updates(
248
+ self,
249
+ offset_id: Optional[str] = None,
250
+ limit: Optional[int] = None
251
+ ) -> Dict[str, Any]:
252
+ """Get updates."""
253
+ data = {}
254
+ if offset_id:
255
+ data["offset_id"] = offset_id
256
+ if limit:
257
+ data["limit"] = limit
258
+ return self._post("getUpdates", data)
259
+ def update_webhook(
260
+ self,
261
+ offset_id: Optional[str] = None,
262
+ limit: Optional[int] = None
263
+ ) -> Dict[str, Any]:
264
+ data = {}
265
+ if offset_id:
266
+ data["offset_id"] = offset_id
267
+ if limit:
268
+ data["limit"] = limit
269
+ return list(requests.get(self.web_hook).json())
270
+ def _is_duplicate(self, message_id: str, max_age_sec: int = 300) -> bool:
271
+ now = time.time()
272
+
273
+ # حذف پیام‌های قدیمی‌تر از max_age_sec
274
+ expired = [mid for mid, ts in self._processed_message_ids.items() if now - ts > max_age_sec]
275
+ for mid in expired:
276
+ del self._processed_message_ids[mid]
277
+
278
+ # بررسی تکراری بودن پیام
279
+ if message_id in self._processed_message_ids:
280
+ return True
281
+
282
+ self._processed_message_ids[message_id] = now
283
+ return False
284
+
285
+
286
+ import time
287
+
288
+
217
289
  def run(self):
218
290
  print("Bot started running...")
219
- if self._offset_id is None:
220
- try:
221
- latest = self.get_updates(limit=100)
222
- if latest and latest.get("data") and latest["data"].get("updates"):
223
- updates = latest["data"]["updates"]
224
- last_update = updates[-1]
225
- self._offset_id = latest["data"].get("next_offset_id")
226
- print(f"Offset initialized to: {self._offset_id}")
227
- else:
228
- print("No updates found.")
229
- except Exception as e:
230
- print(f"Failed to fetch latest message: {e}")
291
+ self._processed_message_ids: Dict[str, float] = {}
292
+ # self._offset_id می‌تواند قبلاً مقداردهی شده باشد
231
293
 
232
294
  while True:
233
295
  try:
234
- updates = self.get_updates(offset_id=self._offset_id, limit=100)
235
- if updates and updates.get("data"):
236
- for update in updates["data"].get("updates", []):
237
- self._process_update(update)
238
- self._offset_id = updates["data"].get("next_offset_id", self._offset_id)
296
+ if self.web_hook:
297
+ updates = self.update_webhook()
298
+
299
+ if isinstance(updates, list):
300
+ for item in updates:
301
+ data = item.get("data", {})
302
+
303
+ # بررسی زمان دریافت پیام و رد کردن پیام‌های قدیمی (بیش از 20 ثانیه)
304
+ received_at_str = item.get("received_at")
305
+ if received_at_str:
306
+ received_at_ts = datetime.datetime.strptime(received_at_str, "%Y-%m-%d %H:%M:%S").timestamp()
307
+ if time.time() - received_at_ts > 20:
308
+ continue # رد کردن پیام قدیمی
309
+
310
+ update = None
311
+ if "update" in data:
312
+ update = data["update"]
313
+ elif "inline_message" in data:
314
+ update = {
315
+ "type": "ReceiveQuery",
316
+ "inline_message": data["inline_message"]
317
+ }
318
+ else:
319
+ continue
320
+
321
+ message_id = None
322
+ if update.get("type") == "NewMessage":
323
+ message_id = update.get("new_message", {}).get("message_id")
324
+ elif update.get("type") == "ReceiveQuery":
325
+ message_id = update.get("inline_message", {}).get("message_id")
326
+ elif "message_id" in update:
327
+ message_id = update.get("message_id")
328
+
329
+ if message_id is not None:
330
+ message_id = str(message_id)
331
+
332
+ if message_id and self._is_duplicate(message_id):
333
+ continue
334
+
335
+ self._process_update(update)
336
+
337
+ # ثبت پیام به عنوان پردازش شده
338
+ if message_id:
339
+ self._processed_message_ids[message_id] = time.time()
340
+
341
+ else:
342
+ updates = self.get_updates(offset_id=self._offset_id, limit=100)
343
+
344
+ if updates and updates.get("data"):
345
+ for update in updates["data"].get("updates", []):
346
+ message_id = None
347
+ if update.get("type") == "NewMessage":
348
+ message_id = update.get("new_message", {}).get("message_id")
349
+ elif update.get("type") == "ReceiveQuery":
350
+ message_id = update.get("inline_message", {}).get("message_id")
351
+ elif "message_id" in update:
352
+ message_id = update.get("message_id")
353
+
354
+ if message_id is not None:
355
+ message_id = str(message_id)
356
+
357
+ if message_id and self._is_duplicate(message_id):
358
+ continue
359
+
360
+ self._process_update(update)
361
+
362
+ if message_id:
363
+ self._processed_message_ids[message_id] = time.time()
364
+
365
+ self._offset_id = updates["data"].get("next_offset_id", self._offset_id)
366
+
239
367
  except Exception as e:
240
- print(f"Error in run loop: {e}")
368
+ print(f"Error in run loop: {e}")
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
241
377
 
242
378
  def send_message(
243
379
  self,
@@ -573,18 +709,7 @@ class Robot:
573
709
  chat_keypad_type=chat_keypad_type
574
710
  )
575
711
 
576
- def get_updates(
577
- self,
578
- offset_id: Optional[str] = None,
579
- limit: Optional[int] = None
580
- ) -> Dict[str, Any]:
581
- """Get updates."""
582
- data = {}
583
- if offset_id:
584
- data["offset_id"] = offset_id
585
- if limit:
586
- data["limit"] = limit
587
- return self._post("getUpdates", data)
712
+
588
713
 
589
714
  def forward_message(
590
715
  self,
@@ -621,7 +746,7 @@ class Robot:
621
746
  inline_keypad: Dict[str, Any]
622
747
  ) -> Dict[str, Any]:
623
748
  """Edit inline keypad of a message."""
624
- return self._post("editInlineKeypad", {
749
+ return self._post("editMessageKeypad", {
625
750
  "chat_id": chat_id,
626
751
  "message_id": message_id,
627
752
  "inline_keypad": inline_keypad
rubka/context.py CHANGED
@@ -408,6 +408,12 @@ class Message:
408
408
  chat_id=self.chat_id,
409
409
  message_id=self.message_id
410
410
  )
411
+ class AuxData:
412
+ def __init__(self, data: dict):
413
+ self.start_id = data.get("start_id")
414
+ self.button_id = data.get("button_id")
415
+
416
+ # نمونه کلاس InlineMessage با ویژگی aux_data که شامل دکمه است
411
417
  class InlineMessage:
412
418
  def __init__(self, bot, raw_data: dict):
413
419
  self.bot = bot
@@ -438,4 +444,4 @@ class InlineMessage:
438
444
  return self.bot.delete_message(
439
445
  chat_id=self.chat_id,
440
446
  message_id=self.message_id
441
- )
447
+ )