Rubka 4.4.8__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.
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
@@ -77,7 +78,7 @@ class Robot:
77
78
  Initialized with bot token.
78
79
  """
79
80
 
80
- 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):
81
82
  """
82
83
  راه‌اندازی اولیه ربات روبیکا و پیکربندی پارامترهای پایه.
83
84
 
@@ -98,11 +99,14 @@ class Robot:
98
99
  >>> bot = Robot(token="BOT_TOKEN", platform="android", timeout=15)
99
100
  """
100
101
  self.token = token
102
+ self._inline_query_handlers = []
103
+ self._inline_query_handlers: List[dict] = []
101
104
  self.timeout = timeout
102
105
  self.auth = auth
103
106
  self.session_name = session_name
104
107
  self.Key = Key
105
108
  self.platform = platform
109
+ self.web_hook = web_hook
106
110
  self._offset_id = None
107
111
  self.session = requests.Session()
108
112
  self.sessions: Dict[str, Dict[str, Any]] = {}
@@ -111,6 +115,11 @@ class Robot:
111
115
  self._inline_query_handler = None
112
116
  self._callback_handlers = None
113
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
114
123
 
115
124
 
116
125
  logger.info(f"Initialized RubikaBot with token: {token[:8]}***")
@@ -156,48 +165,65 @@ class Robot:
156
165
  })
157
166
  return func
158
167
  return decorator
159
- 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):
160
179
  def decorator(func: Callable[[Any, InlineMessage], None]):
161
- self._inline_query_handler = func
180
+ self._inline_query_handlers.append({
181
+ "func": func,
182
+ "button_id": button_id
183
+ })
162
184
  return func
163
185
  return decorator
186
+
164
187
 
165
- def _process_update(self, update: Dict[str, Any]):
188
+ def _process_update(self, update: dict):
166
189
  import threading, time
167
190
 
168
- if update.get('type') == 'ReceiveQuery':
191
+ # هندل پیام inline (معمولاً type = ReceiveQuery)
192
+ if update.get("type") == "ReceiveQuery":
169
193
  msg = update.get("inline_message", {})
170
- if self._inline_query_handler:
194
+ if update.get("type") == "ReceiveQuery":
195
+ msg = update.get("inline_message", {})
171
196
  context = InlineMessage(bot=self, raw_data=msg)
172
- threading.Thread(target=self._inline_query_handler, args=(self, context), daemon=True).start()
173
- return # اگر inline بود ادامه نده
197
+ threading.Thread(target=self._handle_inline_query, args=(context,), daemon=True).start()
198
+ return
174
199
 
175
- if update.get('type') == 'NewMessage':
176
- msg = update.get('new_message', {})
177
- chat_id = update.get('chat_id')
178
- message_id = msg.get('message_id')
179
- sender_id = msg.get('sender_id')
180
- 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")
181
207
 
182
- # فیلتر زمان قدیمی
208
+ # فیلتر زمان پیام (مثلاً پیام‌های خیلی قدیمی)
183
209
  try:
184
210
  if msg.get("time") and (time.time() - float(msg["time"])) > 20:
185
211
  return
186
212
  except Exception:
187
213
  return
188
214
 
189
- # ساخت context یکجا
215
+ # ساخت context پیام
190
216
  context = Message(bot=self, chat_id=chat_id, message_id=message_id, sender_id=sender_id, text=text, raw_data=msg)
191
217
 
192
- # 💠 اول بررسی دکمه‌ها (callback)
218
+ # هندل callback ها (دکمه‌ها)
193
219
  if context.aux_data and hasattr(self, "_callback_handlers"):
194
220
  for handler in self._callback_handlers:
195
221
  if not handler["button_id"] or context.aux_data.button_id == handler["button_id"]:
196
222
  threading.Thread(target=handler["func"], args=(self, context), daemon=True).start()
197
- return # فقط اولین callback اجرا شود
223
+ return # فقط یک callback اجرا شود
198
224
 
199
- # 💠 بعد بررسی پیام‌های متنی معمولی
200
- if self._message_handler:
225
+ # هندل پیام‌های متنی معمولی
226
+ if hasattr(self, "_message_handler") and self._message_handler:
201
227
  handler = self._message_handler
202
228
 
203
229
  if handler["commands"]:
@@ -217,30 +243,137 @@ class Robot:
217
243
 
218
244
 
219
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
+
220
289
  def run(self):
221
290
  print("Bot started running...")
222
- if self._offset_id is None:
223
- try:
224
- latest = self.get_updates(limit=100)
225
- if latest and latest.get("data") and latest["data"].get("updates"):
226
- updates = latest["data"]["updates"]
227
- last_update = updates[-1]
228
- self._offset_id = latest["data"].get("next_offset_id")
229
- print(f"Offset initialized to: {self._offset_id}")
230
- else:
231
- print("No updates found.")
232
- except Exception as e:
233
- print(f"Failed to fetch latest message: {e}")
291
+ self._processed_message_ids: Dict[str, float] = {}
292
+ # self._offset_id می‌تواند قبلاً مقداردهی شده باشد
234
293
 
235
294
  while True:
236
295
  try:
237
- updates = self.get_updates(offset_id=self._offset_id, limit=100)
238
- if updates and updates.get("data"):
239
- for update in updates["data"].get("updates", []):
240
- self._process_update(update)
241
- 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
+
242
367
  except Exception as e:
243
- print(f"Error in run loop: {e}")
368
+ print(f"Error in run loop: {e}")
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
244
377
 
245
378
  def send_message(
246
379
  self,
@@ -576,18 +709,7 @@ class Robot:
576
709
  chat_keypad_type=chat_keypad_type
577
710
  )
578
711
 
579
- def get_updates(
580
- self,
581
- offset_id: Optional[str] = None,
582
- limit: Optional[int] = None
583
- ) -> Dict[str, Any]:
584
- """Get updates."""
585
- data = {}
586
- if offset_id:
587
- data["offset_id"] = offset_id
588
- if limit:
589
- data["limit"] = limit
590
- return self._post("getUpdates", data)
712
+
591
713
 
592
714
  def forward_message(
593
715
  self,
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
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 4.4.8
3
+ Version: 4.4.19
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/blob/main/project_library.zip
@@ -18,6 +18,7 @@ Description-Content-Type: text/markdown
18
18
  Requires-Dist: requests
19
19
  Requires-Dist: Pillow
20
20
  Requires-Dist: websocket-client
21
+ Requires-Dist: pycryptodome
21
22
  Dynamic: author
22
23
  Dynamic: author-email
23
24
  Dynamic: classifier
@@ -32,6 +33,11 @@ Dynamic: requires-python
32
33
  Dynamic: summary
33
34
 
34
35
  # 📚 Rubka Bot Python Library Documentation
36
+ # نمونه تنظیم وب‌هوک (Webhook) در کتابخونه rubla
37
+
38
+ برای مشاهده مستندات کامل و آخرین نسخه راهنما، لطفاً به آدرس زیر مراجعه کنید:
39
+ [github.com/Mahdy-Ahmadi](https://github.com/Mahdy-Ahmadi/rubka/blob/main/webhook.md))
40
+
35
41
 
36
42
  ## 🧠 Introduction
37
43
  `rubka` is a Python library to interact with the [Rubika Bot API](https://rubika.ir/). This library helps you create Telegram-like bots with support for messages, inline buttons, chat keypads, and callback handling.
@@ -1,8 +1,8 @@
1
1
  rubka/__init__.py,sha256=TR1DABU5Maz2eO62ZEFiwOqNU0dH6l6HZfqRUxeo4eY,194
2
- rubka/api.py,sha256=9TFrpgHNnfh3XuzARJXXquqB_wNmuhgqjyrmSO-c6Y4,27290
2
+ rubka/api.py,sha256=h818Eoi84-odyf2Ed1H_suL8VSAHCN-TYM1Jaww2LD4,32370
3
3
  rubka/button.py,sha256=4fMSZR7vUADxSmw1R3_pZ4dw5uMLZX5sOkwPPyNTBDE,8437
4
4
  rubka/config.py,sha256=Bck59xkOiqioLv0GkQ1qPGnBXVctz1hKk6LT4h2EPx0,78
5
- rubka/context.py,sha256=L8ofVuK-aYSFR5m9tBRe1IMMEaCYdfzAqITwD9Q26GI,16778
5
+ rubka/context.py,sha256=j1scXTy_wBY52MmMixfZyIQnB0sdYjRwx17-8ZZmyB4,17017
6
6
  rubka/decorators.py,sha256=hGwUoE4q2ImrunJIGJ_kzGYYxQf1ueE0isadqraKEts,1157
7
7
  rubka/exceptions.py,sha256=tujZt1XrhWaw-lmdeVadVceUptpw4XzNgE44sAAY0gs,90
8
8
  rubka/jobs.py,sha256=GvLMLsVhcSEzRTgkvnPISPEBN71suW2xXI0hUaUZPTo,378
@@ -32,7 +32,7 @@ rubka/adaptorrubka/types/socket/message.py,sha256=0WgLMZh4eow8Zn7AiSX4C3GZjQTkIg
32
32
  rubka/adaptorrubka/utils/__init__.py,sha256=OgCFkXdNFh379quNwIVOAWY2NP5cIOxU5gDRRALTk4o,54
33
33
  rubka/adaptorrubka/utils/configs.py,sha256=nMUEOJh1NqDJsf9W9PurkN_DLYjO6kKPMm923i4Jj_A,492
34
34
  rubka/adaptorrubka/utils/utils.py,sha256=5-LioLNYX_TIbQGDeT50j7Sg9nAWH2LJUUs-iEXpsUY,8816
35
- rubka-4.4.8.dist-info/METADATA,sha256=3SXBpAPJJxYkgO1QjfYNlvXOcOT72Q9thdRLIq7r-AU,32876
36
- rubka-4.4.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- rubka-4.4.8.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
38
- rubka-4.4.8.dist-info/RECORD,,
35
+ rubka-4.4.19.dist-info/METADATA,sha256=HUXTIlzStSyrHVnjRxFCByBBrZd6TH8RpFYa9_tpe_s,33217
36
+ rubka-4.4.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ rubka-4.4.19.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
38
+ rubka-4.4.19.dist-info/RECORD,,
File without changes