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.
- rubka/adaptorrubka/network/socket.py +15 -8
- rubka/api.py +181 -56
- rubka/context.py +7 -1
- rubka-4.4.19.dist-info/METADATA +1025 -0
- {rubka-4.4.6.dist-info → rubka-4.4.19.dist-info}/RECORD +7 -7
- rubka-4.4.6.dist-info/METADATA +0 -315
- {rubka-4.4.6.dist-info → rubka-4.4.19.dist-info}/WHEEL +0 -0
- {rubka-4.4.6.dist-info → rubka-4.4.19.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import subprocess
|
|
1
3
|
|
|
2
|
-
|
|
3
|
-
from websocket import WebSocketApp
|
|
4
|
-
except:
|
|
5
|
-
import os
|
|
4
|
+
def install_and_import(package_name):
|
|
6
5
|
try:
|
|
7
|
-
|
|
8
|
-
except:
|
|
9
|
-
|
|
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\
|
|
58
|
-
print(f"Installed version: {installed_version}")
|
|
59
|
-
print(f"Latest version:
|
|
60
|
-
print(f"Please update
|
|
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
|
|
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.
|
|
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:
|
|
188
|
+
def _process_update(self, update: dict):
|
|
163
189
|
import threading, time
|
|
164
190
|
|
|
165
|
-
|
|
191
|
+
# هندل پیام inline (معمولاً type = ReceiveQuery)
|
|
192
|
+
if update.get("type") == "ReceiveQuery":
|
|
166
193
|
msg = update.get("inline_message", {})
|
|
167
|
-
if
|
|
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.
|
|
170
|
-
|
|
197
|
+
threading.Thread(target=self._handle_inline_query, args=(context,), daemon=True).start()
|
|
198
|
+
return
|
|
171
199
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
#
|
|
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 # فقط
|
|
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
|
-
|
|
220
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
+
)
|