Rubka 6.5.2__py3-none-any.whl → 6.6.4__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
@@ -1669,9 +1669,11 @@ class Robot:
1669
1669
  self,
1670
1670
  chat_id: str,
1671
1671
  message_id: str,
1672
- inline_keypad: Dict[str, Any]
1672
+ inline_keypad: Dict[str, Any],
1673
+ text : str = None
1673
1674
  ) -> Dict[str, Any]:
1674
1675
  """Edit inline keypad of a message."""
1676
+ if text is not None:self._post("editMessageText", {"chat_id": chat_id,"message_id": message_id,"text": text})
1675
1677
  return self._post("editMessageKeypad", {
1676
1678
  "chat_id": chat_id,
1677
1679
  "message_id": message_id,
rubka/asynco.py CHANGED
@@ -1,31 +1,20 @@
1
- import asyncio
2
- import aiohttp
3
- import aiofiles
4
- from typing import List, Optional, Dict, Any, Literal, Callable, Union
1
+ import asyncio,aiohttp,aiofiles,time,datetime,json,tempfile,os,sys,subprocess,mimetypes
2
+ from typing import List, Optional, Dict, Any, Literal, Callable, Union,Set
5
3
  from .exceptions import APIRequestError
6
4
  from .adaptorrubka import Client as Client_get
7
5
  from .logger import logger
6
+ from .rubino import Bot as Rubino
8
7
  from . import filters
9
- try:
10
- from .context import Message, InlineMessage
11
- except (ImportError, ModuleNotFoundError):
12
- from context import Message, InlineMessage
8
+ try:from .context import Message, InlineMessage
9
+ except (ImportError, ModuleNotFoundError):from context import Message, InlineMessage
13
10
  class FeatureNotAvailableError(Exception):
14
11
  pass
15
12
 
16
13
  from tqdm.asyncio import tqdm
17
14
  from urllib.parse import urlparse, parse_qs
18
15
  class InvalidTokenError(Exception):pass
19
- import mimetypes
20
16
  from pathlib import Path
21
- import time
22
- import datetime,json
23
- import tempfile
24
17
  from tqdm import tqdm
25
- import os
26
- import sys
27
- import subprocess
28
-
29
18
  API_URL = "https://botapi.rubika.ir/v3"
30
19
 
31
20
  def install_package(package_name: str) -> bool:
@@ -35,31 +24,7 @@ def install_package(package_name: str) -> bool:
35
24
  return True
36
25
  except Exception:
37
26
  return False
38
- class MessageResponse:
39
- def __init__(self, resp: dict, chat_id: str, file_id: str,
40
- type_file: str, reply_to_message_id: str,
41
- disable_notification: bool, text: str = None,
42
- chat_keypad=None, inline_keypad=None, chat_keypad_type="None"):
43
-
44
- self.status = resp.get("status")
45
- self.status_det = resp.get("status_det")
46
- self.file_id = file_id
47
- self.message_id = resp["data"].get("message_id")
48
- self.send_to_chat_id = chat_id
49
- self.reply_to_message_id = reply_to_message_id
50
- self.disable_notification = disable_notification
51
- self.type_file = type_file
52
- self.text = text
53
- self.chat_keypad = chat_keypad
54
- self.inline_keypad = inline_keypad
55
- self.chat_keypad_type = chat_keypad_type
56
- self.raw_response = resp
57
-
58
- def to_dict(self):
59
- return self.__dict__
60
-
61
- def to_json(self):
62
- return json.dumps(self.__dict__, ensure_ascii=False, indent=4)
27
+
63
28
  def get_importlib_metadata():
64
29
  """Dynamically imports and returns metadata functions from importlib."""
65
30
  try:
@@ -124,12 +89,40 @@ def show_last_six_words(text: str) -> str:
124
89
  """Returns the last 6 characters of a stripped string."""
125
90
  text = text.strip()
126
91
  return text[-6:]
127
-
92
+ class AttrDict(dict):
93
+ def __getattr__(self, item):
94
+ value = self.get(item)
95
+ if isinstance(value, dict):
96
+ return AttrDict(value)
97
+ return value
128
98
 
129
99
  class Robot:
130
100
  """
131
- Main async class to interact with Rubika Bot API.
132
- Initialized with a bot token.
101
+ Main asynchronous class to interact with the Rubika Bot API.
102
+
103
+ This class handles sending and receiving messages, inline queries, callbacks,
104
+ and manages sessions and API interactions. It is initialized with a bot token
105
+ and provides multiple optional parameters for configuration.
106
+
107
+ Attributes:
108
+ token (str): Bot token used for authentication with Rubika Bot API.
109
+ session_name (str | None): Optional session name for storing session data.
110
+ auth (str | None): Optional authentication string for advanced features.
111
+ Key (str | None): Optional key for additional authorization if required.
112
+ platform (str): Platform type, default is 'web'.
113
+ web_hook (str | None): Optional webhook URL for receiving updates.
114
+ timeout (int): Timeout for API requests in seconds (default 10).
115
+ show_progress (bool): Whether to show progress for long operations (default False).
116
+ Example:
117
+ ```python
118
+ import asyncio
119
+ from rubka.asynco import Robot,filters,Message
120
+ bot = Robot(token="YOUR_BOT_TOKEN")
121
+ @bot.on_message(filters.is_command.start)
122
+ async def start_command(bot: Robot, message: Message):
123
+ await message.reply("Hello!")
124
+ asyncio.run(bot.run())
125
+
133
126
  """
134
127
 
135
128
  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):
@@ -173,7 +166,6 @@ class Robot:
173
166
  print(data)
174
167
  json_url = data.get('url', self.web_hook)
175
168
  print(self.web_hook)
176
-
177
169
  for endpoint_type in [
178
170
  "ReceiveUpdate",
179
171
  "ReceiveInlineMessage",
@@ -187,8 +179,6 @@ class Robot:
187
179
  except Exception as e:
188
180
  logger.error(f"Failed to set webhook from {self.web_hook}: {e}")
189
181
  self.web_hook = None
190
-
191
-
192
182
  async def _post(self, method: str, data: Dict[str, Any]) -> Dict[str, Any]:
193
183
  url = f"{API_URL}/{self.token}/{method}"
194
184
  session = await self._get_session()
@@ -201,11 +191,9 @@ class Robot:
201
191
  text_resp = await response.text()
202
192
  logger.error(f"Invalid JSON response from {method}: {text_resp}")
203
193
  raise APIRequestError(f"Invalid JSON response: {text_resp}")
204
-
205
194
  if method != "getUpdates":
206
195
  logger.debug(f"API Response from {method}: {json_resp}")
207
-
208
- return json_resp
196
+ return AttrDict(json_resp)
209
197
  except aiohttp.ClientError as e:
210
198
  logger.error(f"API request failed: {e}")
211
199
  raise APIRequestError(f"API request failed: {e}") from e
@@ -275,8 +263,6 @@ class Robot:
275
263
  return
276
264
  if not allow_locations and (message.location or message.live_location):
277
265
  return
278
-
279
-
280
266
  if message.text:
281
267
  text = message.text if case_sensitive else message.text.lower()
282
268
  if min_text_length and len(message.text) < min_text_length:
@@ -289,8 +275,6 @@ class Robot:
289
275
  return
290
276
  if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
291
277
  return
292
-
293
-
294
278
  if commands:
295
279
  if not message.text:
296
280
  return
@@ -299,13 +283,9 @@ class Robot:
299
283
  if cmd not in commands:
300
284
  return
301
285
  message.args = parts[1:]
302
-
303
-
304
286
  if filters and not filters(message):
305
287
  return
306
-
307
288
  return await func(bot, message)
308
-
309
289
  self._message_handlers.append({
310
290
  "func": wrapper,
311
291
  "filters": filters,
@@ -346,30 +326,20 @@ class Robot:
346
326
 
347
327
  if not message.is_channel:
348
328
  return
349
-
350
-
351
329
  if chat_id:
352
330
  if isinstance(chat_id, str) and message.chat_id != chat_id:
353
331
  return
354
332
  if isinstance(chat_id, list) and message.chat_id not in chat_id:
355
333
  return
356
-
357
-
358
334
  if sender_id:
359
335
  if isinstance(sender_id, str) and message.sender_id != sender_id:
360
336
  return
361
337
  if isinstance(sender_id, list) and message.sender_id not in sender_id:
362
338
  return
363
-
364
-
365
339
  if sender_type and message.sender_type != sender_type:
366
340
  return
367
-
368
-
369
341
  if not allow_forwarded and message.forwarded_from:
370
342
  return
371
-
372
-
373
343
  if not allow_files and message.file:
374
344
  return
375
345
  if not allow_stickers and message.sticker:
@@ -380,8 +350,6 @@ class Robot:
380
350
  return
381
351
  if not allow_locations and (message.location or message.live_location):
382
352
  return
383
-
384
-
385
353
  if message.text:
386
354
  text = message.text if case_sensitive else message.text.lower()
387
355
  if min_text_length and len(message.text) < min_text_length:
@@ -394,8 +362,6 @@ class Robot:
394
362
  return
395
363
  if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
396
364
  return
397
-
398
-
399
365
  if commands:
400
366
  if not message.text:
401
367
  return
@@ -403,14 +369,10 @@ class Robot:
403
369
  cmd = parts[0].lstrip("/")
404
370
  if cmd not in commands:
405
371
  return
406
- message.args = parts[1:]
407
-
408
-
372
+ message.args = parts[1:]
409
373
  if filters and not filters(message):
410
374
  return
411
-
412
375
  return await func(bot, message)
413
-
414
376
  self._message_handlers.append({
415
377
  "func": wrapper,
416
378
  "filters": filters,
@@ -448,33 +410,22 @@ class Robot:
448
410
 
449
411
  def decorator(func: Callable[[Any, Message], None]):
450
412
  async def wrapper(bot, message: Message):
451
-
452
413
  if not message.is_group:
453
414
  return
454
-
455
-
456
415
  if chat_id:
457
416
  if isinstance(chat_id, str) and message.chat_id != chat_id:
458
417
  return
459
418
  if isinstance(chat_id, list) and message.chat_id not in chat_id:
460
419
  return
461
-
462
-
463
420
  if sender_id:
464
421
  if isinstance(sender_id, str) and message.sender_id != sender_id:
465
422
  return
466
423
  if isinstance(sender_id, list) and message.sender_id not in sender_id:
467
424
  return
468
-
469
-
470
425
  if sender_type and message.sender_type != sender_type:
471
426
  return
472
-
473
-
474
427
  if not allow_forwarded and message.forwarded_from:
475
428
  return
476
-
477
-
478
429
  if not allow_files and message.file:
479
430
  return
480
431
  if not allow_stickers and message.sticker:
@@ -485,8 +436,6 @@ class Robot:
485
436
  return
486
437
  if not allow_locations and (message.location or message.live_location):
487
438
  return
488
-
489
-
490
439
  if message.text:
491
440
  text = message.text if case_sensitive else message.text.lower()
492
441
  if min_text_length and len(message.text) < min_text_length:
@@ -499,8 +448,6 @@ class Robot:
499
448
  return
500
449
  if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
501
450
  return
502
-
503
-
504
451
  if commands:
505
452
  if not message.text:
506
453
  return
@@ -509,13 +456,9 @@ class Robot:
509
456
  if cmd not in commands:
510
457
  return
511
458
  message.args = parts[1:]
512
-
513
-
514
459
  if filters and not filters(message):
515
460
  return
516
-
517
461
  return await func(bot, message)
518
-
519
462
  self._message_handlers.append({
520
463
  "func": wrapper,
521
464
  "filters": filters,
@@ -738,13 +681,12 @@ class Robot:
738
681
 
739
682
  async def _handle_inline_query(self, inline_message: InlineMessage):
740
683
  aux_button_id = inline_message.aux_data.button_id if inline_message.aux_data else None
741
-
742
684
  for handler in self._inline_query_handlers:
743
685
  if handler["button_id"] is None or handler["button_id"] == aux_button_id:
744
686
  try:
745
687
  await handler["func"](self, inline_message)
746
688
  except Exception as e:
747
- print(f"Error in inline query handler: {e}")
689
+ raise Exception(f"Error in inline query handler: {e}")
748
690
 
749
691
  def on_inline_query(self, button_id: Optional[str] = None):
750
692
  def decorator(func: Callable[[Any, InlineMessage], None]):
@@ -757,33 +699,19 @@ class Robot:
757
699
  def on_inline_query_prefix(self, prefix: str, button_id: Optional[str] = None):
758
700
  if not prefix.startswith('/'):
759
701
  prefix = '/' + prefix
760
-
761
702
  def decorator(func: Callable[[Any, InlineMessage], None]):
762
-
763
703
  async def handler_wrapper(bot_instance, inline_message: InlineMessage):
764
-
765
704
  if not inline_message.raw_data or 'text' not in inline_message.raw_data:
766
705
  return
767
-
768
706
  query_text = inline_message.raw_data['text']
769
-
770
-
771
707
  if query_text.startswith(prefix):
772
-
773
-
774
-
775
708
  try:
776
709
  await func(bot_instance, inline_message)
777
710
  except Exception as e:
778
- print(f"Error in inline query prefix handler '{prefix}': {e}")
779
-
780
-
711
+ raise Exception(f"Error in inline query prefix handler '{prefix}': {e}")
781
712
  self._inline_query_handlers.append({
782
713
  "func": handler_wrapper,
783
- "button_id": button_id
784
-
785
-
786
-
714
+ "button_id": button_id
787
715
  })
788
716
  return func
789
717
  return decorator
@@ -791,40 +719,29 @@ class Robot:
791
719
  if update.get("type") == "ReceiveQuery":
792
720
  msg = update.get("inline_message", {})
793
721
  context = InlineMessage(bot=self, raw_data=msg)
794
-
795
-
796
722
  if hasattr(self, "_callback_handlers"):
797
723
  for handler in self._callback_handlers:
798
724
  if not handler["button_id"] or getattr(context.aux_data, "button_id", None) == handler["button_id"]:
799
725
  asyncio.create_task(handler["func"](self, context))
800
-
801
-
802
726
  asyncio.create_task(self._handle_inline_query(context))
803
727
  return
804
728
 
805
729
  if update.get("type") == "NewMessage":
806
730
  msg = update.get("new_message", {})
807
731
  try:
808
- if msg.get("time") and (time.time() - float(msg["time"])) > 20:
809
- return
810
- except (ValueError, TypeError):
811
- return
812
-
732
+ if msg.get("time") and (time.time() - float(msg["time"])) > 20:return
733
+ except (ValueError, TypeError):return
813
734
  context = Message(bot=self,
814
735
  chat_id=update.get("chat_id"),
815
736
  message_id=msg.get("message_id"),
816
737
  sender_id=msg.get("sender_id"),
817
738
  text=msg.get("text"),
818
739
  raw_data=msg)
819
-
820
-
821
740
  if context.aux_data and self._callback_handlers:
822
741
  for handler in self._callback_handlers:
823
742
  if not handler["button_id"] or context.aux_data.button_id == handler["button_id"]:
824
743
  asyncio.create_task(handler["func"](self, context))
825
744
  return
826
-
827
-
828
745
  if self._message_handlers:
829
746
  for handler_info in self._message_handlers:
830
747
 
@@ -836,18 +753,12 @@ class Robot:
836
753
  if cmd not in handler_info["commands"]:
837
754
  continue
838
755
  context.args = parts[1:]
839
-
840
-
841
756
  if handler_info["filters"]:
842
757
  if not handler_info["filters"](context):
843
758
  continue
844
-
845
-
846
759
  if not handler_info["commands"] and not handler_info["filters"]:
847
760
  asyncio.create_task(handler_info["func"](self, context))
848
761
  continue
849
-
850
-
851
762
  if handler_info["commands"] or handler_info["filters"]:
852
763
  asyncio.create_task(handler_info["func"](self, context))#jaq
853
764
  continue
@@ -864,72 +775,65 @@ class Robot:
864
775
  if offset_id: params['offset_id'] = offset_id
865
776
  if limit: params['limit'] = limit
866
777
  async with session.get(self.web_hook, params=params) as response:
867
- response.raise_for_status()
868
-
778
+ response.raise_for_status()
869
779
  return await response.json()
870
780
 
871
781
  def _is_duplicate(self, message_id: str, max_age_sec: int = 300) -> bool:
872
782
  now = time.time()
873
783
  expired = [mid for mid, ts in self._processed_message_ids.items() if now - ts > max_age_sec]
874
- for mid in expired:
875
- del self._processed_message_ids[mid]
876
-
877
- if message_id in self._processed_message_ids:
878
- return True
879
-
784
+ for mid in expired:del self._processed_message_ids[mid]
785
+ if message_id in self._processed_message_ids:return True
880
786
  self._processed_message_ids[message_id] = now
881
787
  return False
882
788
 
883
789
 
884
-
885
-
886
790
  async def run(
887
- self,
888
- debug: bool = False,
889
- sleep_time: float = 0.2,
890
- webhook_timeout: int = 20,
891
- update_limit: int = 100,
892
- retry_delay: float = 5.0,
893
- stop_on_error: bool = False,
894
- max_errors: int = 0,
895
- auto_restart: bool = False,
896
- max_runtime: float | None = None,
897
- loop_forever: bool = True,
898
- allowed_update_types: list[str] | None = None,
899
- ignore_duplicate_messages: bool = True,
900
- skip_inline_queries: bool = False,
901
- skip_channel_posts: bool = False,
902
- skip_service_messages: bool = False,
903
- skip_edited_messages: bool = False,
904
- skip_bot_messages: bool = False,
905
- log_file: str | None = None,
906
- log_level: str = "info",
907
- print_exceptions: bool = True,
908
- error_handler=None,
909
- shutdown_hook=None,
910
- save_unprocessed_updates: bool = False,
911
- log_to_console: bool = True,
912
- rate_limit: float | None = None,
913
- max_message_size: int | None = None,
914
- ignore_users: set[str] | None = None,
915
- ignore_groups: set[str] | None = None,
916
- require_auth_token: bool = False,
917
- only_private_chats: bool = False,
918
- only_groups: bool = False,
919
- require_admin_rights: bool = False,
920
- custom_update_fetcher=None,
921
- custom_update_processor=None,
922
- process_in_background: bool = False,
923
- max_queue_size: int = 1000,
924
- thread_workers: int = 3,
925
- message_filter=None,
926
- pause_on_idle: bool = False,
927
- max_concurrent_tasks: int | None = None,
928
- metrics_enabled: bool = False,
929
- metrics_handler=None,
930
- notify_on_error: bool = False,
931
- notification_handler=None,
932
- watchdog_timeout: float | None = None,
791
+ self,
792
+ debug: bool = False,
793
+ sleep_time: float = 0.2,
794
+ webhook_timeout: int = 20,
795
+ update_limit: int = 100,
796
+ retry_delay: float = 5.0,
797
+ stop_on_error: bool = False,
798
+ max_errors: int = 0,
799
+ auto_restart: bool = False,
800
+ max_runtime: Optional[float] = None,
801
+ loop_forever: bool = True,
802
+ allowed_update_types: Optional[List[str]] = None,
803
+ ignore_duplicate_messages: bool = True,
804
+ skip_inline_queries: bool = False,
805
+ skip_channel_posts: bool = False,
806
+ skip_service_messages: bool = False,
807
+ skip_edited_messages: bool = False,
808
+ skip_bot_messages: bool = False,
809
+ log_file: Optional[str] = None,
810
+ log_level: str = "info",
811
+ print_exceptions: bool = True,
812
+ error_handler: Optional[Callable[[Exception], Any]] = None,
813
+ shutdown_hook: Optional[Callable[[], Any]] = None,
814
+ save_unprocessed_updates: bool = False,
815
+ log_to_console: bool = True,
816
+ rate_limit: Optional[float] = None,
817
+ max_message_size: Optional[int] = None,
818
+ ignore_users: Optional[Set[str]] = None,
819
+ ignore_groups: Optional[Set[str]] = None,
820
+ require_auth_token: bool = False,
821
+ only_private_chats: bool = False,
822
+ only_groups: bool = False,
823
+ require_admin_rights: bool = False,
824
+ custom_update_fetcher: Optional[Callable[[], Any]] = None,
825
+ custom_update_processor: Optional[Callable[[Any], Any]] = None,
826
+ process_in_background: bool = False,
827
+ max_queue_size: int = 1000,
828
+ thread_workers: int = 3,
829
+ message_filter: Optional[Callable[[Any], bool]] = None,
830
+ pause_on_idle: bool = False,
831
+ max_concurrent_tasks: Optional[int] = None,
832
+ metrics_enabled: bool = False,
833
+ metrics_handler: Optional[Callable[[dict], Any]] = None,
834
+ notify_on_error: bool = False,
835
+ notification_handler: Optional[Callable[[str], Any]] = None,
836
+ watchdog_timeout: Optional[float] = None,
933
837
  ):
934
838
  """
935
839
  Starts the bot's main execution loop with extensive configuration options.
@@ -1449,8 +1353,7 @@ class Robot:
1449
1353
 
1450
1354
 
1451
1355
  _log("Auto-restart requested. You can call run(...) again as needed.", "warning")
1452
-
1453
-
1356
+
1454
1357
  async def send_message(
1455
1358
  self,
1456
1359
  chat_id: str,
@@ -1460,27 +1363,21 @@ class Robot:
1460
1363
  disable_notification: bool = False,
1461
1364
  reply_to_message_id: Optional[str] = None,
1462
1365
  chat_keypad_type: Optional[Literal["New", "Removed"]] = None
1463
- ) -> Dict[str, Any]:
1366
+ ) -> Dict[str, Any]:
1464
1367
  payload = {
1465
1368
  "chat_id": chat_id,
1466
1369
  "text": text,
1467
1370
  "disable_notification": disable_notification,
1468
1371
  }
1469
-
1470
1372
  if chat_keypad:
1471
1373
  payload["chat_keypad"] = chat_keypad
1472
1374
  payload["chat_keypad_type"] = chat_keypad_type or "New"
1473
-
1474
1375
  if inline_keypad:
1475
1376
  payload["inline_keypad"] = inline_keypad
1476
-
1477
1377
  if reply_to_message_id:
1478
1378
  payload["reply_to_message_id"] = reply_to_message_id
1479
-
1480
1379
  return await self._post("sendMessage", payload)
1481
1380
 
1482
-
1483
-
1484
1381
  async def get_url_file(self,file_id):
1485
1382
  data = await self._post("getFile", {'file_id': file_id})
1486
1383
  return data.get("data").get("download_url")
@@ -1493,14 +1390,11 @@ class Robot:
1493
1390
 
1494
1391
  async def check_join(self, channel_guid: str, chat_id: str = None) -> Union[bool, list[str]]:
1495
1392
  client = self._get_client()
1496
-
1497
1393
  if chat_id:
1498
1394
  chat_info_data = await self.get_chat(chat_id)
1499
1395
  chat_info = chat_info_data.get('data', {}).get('chat', {})
1500
1396
  username = chat_info.get('username')
1501
1397
  user_id = chat_info.get('user_id')
1502
-
1503
-
1504
1398
  if username:
1505
1399
  result = await asyncio.to_thread(self.get_all_member, channel_guid, search_text=username)
1506
1400
  members = result.get('in_chat_members', [])
@@ -1519,7 +1413,6 @@ class Robot:
1519
1413
  id="None"):
1520
1414
  from .button import InlineBuilder
1521
1415
  builder = InlineBuilder()
1522
-
1523
1416
  if isinstance(username, (list, tuple)) and isinstance(title_button, (list, tuple)):
1524
1417
  for t, u in zip(title_button, username):
1525
1418
  builder = builder.row(
@@ -1589,55 +1482,41 @@ class Robot:
1589
1482
  url=url
1590
1483
  )
1591
1484
  )
1592
-
1593
1485
  return await self.send_message(
1594
1486
  chat_id=chat_id,
1595
1487
  text=text,
1596
1488
  inline_keypad=builder.build(),
1597
1489
  reply_to_message_id=reply_to_message_id
1598
1490
  )
1599
-
1600
1491
  def get_all_member(self, channel_guid: str, search_text: str = None, start_id: str = None, just_get_guids: bool = False):
1601
-
1602
1492
  client = self._get_client()
1603
1493
  return client.get_all_members(channel_guid, search_text, start_id, just_get_guids)
1604
-
1605
1494
  async def send_poll(self, chat_id: str, question: str, options: List[str]) -> Dict[str, Any]:
1606
1495
  return await self._post("sendPoll", {"chat_id": chat_id, "question": question, "options": options})
1607
-
1608
1496
  async def send_location(self, chat_id: str, latitude: str, longitude: str, disable_notification: bool = False, inline_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Removed"]] = None) -> Dict[str, Any]:
1609
1497
  payload = {"chat_id": chat_id, "latitude": latitude, "longitude": longitude, "disable_notification": disable_notification}
1610
1498
  if inline_keypad: payload["inline_keypad"] = inline_keypad
1611
1499
  if reply_to_message_id: payload["reply_to_message_id"] = reply_to_message_id
1612
1500
  if chat_keypad_type: payload["chat_keypad_type"] = chat_keypad_type
1613
1501
  return await self._post("sendLocation", {k: v for k, v in payload.items() if v is not None})
1614
-
1615
1502
  async def send_contact(self, chat_id: str, first_name: str, last_name: str, phone_number: str) -> Dict[str, Any]:
1616
1503
  return await self._post("sendContact", {"chat_id": chat_id, "first_name": first_name, "last_name": last_name, "phone_number": phone_number})
1617
-
1618
1504
  async def get_chat(self, chat_id: str) -> Dict[str, Any]:
1619
1505
  return await self._post("getChat", {"chat_id": chat_id})
1620
-
1621
1506
  async def upload_media_file(self, upload_url: str, name: str, path: Union[str, Path]) -> str:
1622
1507
  is_temp_file = False
1623
1508
  session = await self._get_session()
1624
-
1625
1509
  if isinstance(path, str) and path.startswith("http"):
1626
1510
  async with session.get(path) as response:
1627
1511
  if response.status != 200:
1628
1512
  raise Exception(f"Failed to download file from URL ({response.status})")
1629
-
1630
1513
  content = await response.read()
1631
-
1632
1514
  with tempfile.NamedTemporaryFile(delete=False) as temp_file:
1633
1515
  temp_file.write(content)
1634
1516
  path = temp_file.name
1635
1517
  is_temp_file = True
1636
-
1637
1518
  file_size = os.path.getsize(path)
1638
-
1639
1519
  progress_bar = tqdm(total=file_size, unit='B', unit_scale=True, unit_divisor=1024, desc=f'Uploading : {name}', bar_format='{l_bar}{bar:100}{r_bar}', colour='cyan', disable=not self.show_progress)
1640
-
1641
1520
  async def file_progress_generator(file_path, chunk_size=8192):
1642
1521
  async with aiofiles.open(file_path, 'rb') as f:
1643
1522
  while True:
@@ -1646,7 +1525,6 @@ class Robot:
1646
1525
  break
1647
1526
  progress_bar.update(len(chunk))
1648
1527
  yield chunk
1649
-
1650
1528
  data = aiohttp.FormData()
1651
1529
  data.add_field('file', file_progress_generator(path), filename=name, content_type='application/octet-stream')
1652
1530
  try:
@@ -1661,12 +1539,9 @@ class Robot:
1661
1539
  return json_data.get('data', {}).get('file_id')
1662
1540
  except :
1663
1541
  raise FeatureNotAvailableError(f"files is not currently supported by the server.")
1664
-
1665
-
1666
1542
  def get_extension(content_type: str) -> str:
1667
1543
  ext = mimetypes.guess_extension(content_type)
1668
1544
  return ext if ext else ''
1669
-
1670
1545
  async def download(self, file_id: str, save_as: str = None, chunk_size: int = 1024 * 512,timeout_sec: int = 60, verbose: bool = False):
1671
1546
  """
1672
1547
  Download a file from server using its file_id with chunked transfer,
@@ -1685,16 +1560,11 @@ class Robot:
1685
1560
  Returns:
1686
1561
  bool: True if success, raises exceptions otherwise.
1687
1562
  """
1688
-
1689
1563
  try:
1690
1564
  url = await self.get_url_file(file_id)
1691
- if not url:
1692
- raise ValueError("Download URL not found in response.")
1693
- except Exception as e:
1694
- raise ValueError(f"Failed to get download URL: {e}")
1695
-
1565
+ if not url:raise ValueError("Download URL not found in response.")
1566
+ except Exception as e:raise ValueError(f"Failed to get download URL: {e}")
1696
1567
  timeout = aiohttp.ClientTimeout(total=timeout_sec)
1697
-
1698
1568
  try:
1699
1569
  async with aiohttp.ClientSession(timeout=timeout) as session:
1700
1570
  async with session.get(url) as resp:
@@ -1706,21 +1576,17 @@ class Robot:
1706
1576
  message="Failed to download file.",
1707
1577
  headers=resp.headers
1708
1578
  )
1709
-
1710
1579
  if not save_as:
1711
1580
  content_disp = resp.headers.get("Content-Disposition", "")
1712
1581
  import re
1713
1582
  match = re.search(r'filename="?([^\";]+)"?', content_disp)
1714
- if match:
1715
- save_as = match.group(1)
1583
+ if match:save_as = match.group(1)
1716
1584
  else:
1717
1585
  content_type = resp.headers.get("Content-Type", "").split(";")[0]
1718
1586
  extension = mimetypes.guess_extension(content_type) or ".bin"
1719
1587
  save_as = f"{file_id}{extension}"
1720
-
1721
1588
  total_size = int(resp.headers.get("Content-Length", 0))
1722
1589
  progress = tqdm(total=total_size, unit="B", unit_scale=True, disable=not verbose)
1723
-
1724
1590
  async with aiofiles.open(save_as, "wb") as f:
1725
1591
  async for chunk in resp.content.iter_chunked(chunk_size):
1726
1592
  await f.write(chunk)
@@ -1783,7 +1649,7 @@ class Robot:
1783
1649
  "inline_keypad":inline_keypad,
1784
1650
  "chat_keypad_type":chat_keypad_type
1785
1651
  }
1786
- return result
1652
+ return AttrDict(result)
1787
1653
 
1788
1654
  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):
1789
1655
  if path:
@@ -1802,43 +1668,31 @@ class Robot:
1802
1668
  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)
1803
1669
  async def send_music(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = "music", 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", "Removed", "None"]] = "None") -> Dict[str, Any]:
1804
1670
  return await self._send_file_generic("File", chat_id, path, file_id, text, f"{file_name}.ogg", inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
1805
-
1806
1671
  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", "Removed", "None"]] = "None") -> Dict[str, Any]:
1807
1672
  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)
1808
-
1809
1673
  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", "Removed", "None"]] = "None") -> Dict[str, Any]:
1810
1674
  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)
1811
-
1812
1675
  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", "Removed", "None"]] = "None") -> Dict[str, Any]:
1813
1676
  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)
1814
-
1815
1677
  async def send_gif(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", "Removed", "None"]] = "None") -> Dict[str, Any]:
1816
1678
  return await self._send_file_generic("Gif", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
1817
-
1818
1679
  async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> Dict[str, Any]:
1819
1680
  return await self._post("forwardMessage", {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification})
1820
-
1821
1681
  async def edit_message_text(self, chat_id: str, message_id: str, text: str) -> Dict[str, Any]:
1822
1682
  return await self._post("editMessageText", {"chat_id": chat_id, "message_id": message_id, "text": text})
1823
-
1824
- async def edit_inline_keypad(self, chat_id: str, message_id: str, inline_keypad: Dict[str, Any]) -> Dict[str, Any]:
1825
- return await self._post("editMessageKeypad", {"chat_id": chat_id, "message_id": message_id, "inline_keypad": inline_keypad})
1826
-
1683
+ async def edit_inline_keypad(self,chat_id: str,message_id: str,inline_keypad: Dict[str, Any],text: str = None) -> Dict[str, Any]:
1684
+ if text is not None:await self._post("editMessageText", {"chat_id": chat_id,"message_id": message_id,"text": text})
1685
+ return await self._post("editMessageKeypad", {"chat_id": chat_id,"message_id": message_id,"inline_keypad": inline_keypad})
1827
1686
  async def delete_message(self, chat_id: str, message_id: str) -> Dict[str, Any]:
1828
1687
  return await self._post("deleteMessage", {"chat_id": chat_id, "message_id": message_id})
1829
-
1830
1688
  async def set_commands(self, bot_commands: List[Dict[str, str]]) -> Dict[str, Any]:
1831
1689
  return await self._post("setCommands", {"bot_commands": bot_commands})
1832
-
1833
1690
  async def update_bot_endpoint(self, url: str, type: str) -> Dict[str, Any]:
1834
1691
  return await self._post("updateBotEndpoints", {"url": url, "type": type})
1835
-
1836
1692
  async def remove_keypad(self, chat_id: str) -> Dict[str, Any]:
1837
1693
  return await self._post("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "Removed"})
1838
-
1839
1694
  async def edit_chat_keypad(self, chat_id: str, chat_keypad: Dict[str, Any]) -> Dict[str, Any]:
1840
1695
  return await self._post("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "New", "chat_keypad": chat_keypad})
1841
-
1842
1696
  async def get_name(self, chat_id: str) -> str:
1843
1697
  try:
1844
1698
  chat = await self.get_chat(chat_id)
@@ -1854,9 +1708,6 @@ class Robot:
1854
1708
  return title if title else "null"
1855
1709
  else:return "null"
1856
1710
  except Exception:return "null"
1857
-
1858
-
1859
-
1860
1711
  async def get_username(self, chat_id: str) -> str:
1861
1712
  chat_info = await self.get_chat(chat_id)
1862
1713
  return chat_info.get("data", {}).get("chat", {}).get("username", "None")
rubka/filters.py CHANGED
@@ -1,6 +1,11 @@
1
1
  from typing import Callable
2
2
  import re
3
-
3
+ class TextFilter:
4
+ def __call__(self, keyword=None):
5
+ if keyword is None:
6
+ return Filter(lambda m: getattr(m, "is_text", False))
7
+ else:
8
+ return Filter(lambda m: getattr(m, "is_text", False) and keyword in getattr(m, "text", ""))
4
9
  class Filter:
5
10
  def __init__(self, func: Callable):
6
11
  self.func = func
@@ -49,8 +54,32 @@ class Filter:
49
54
 
50
55
  def __truediv__(self, other):
51
56
  return Filter(lambda m: self(m) / (other(m) if callable(other) else other))
52
-
53
- is_text = Filter(lambda m: getattr(m, "is_text", False))
57
+ class IsCommand(Filter):
58
+ def __init__(self, commands=None):
59
+ if commands is None:
60
+ func = lambda m: getattr(m, "is_command", False)
61
+ else:
62
+ if isinstance(commands, str):
63
+ commands = [commands]
64
+ func = lambda m: getattr(m, "is_command", False) and getattr(m, "text", "").lstrip("/").split()[0] in commands
65
+
66
+ super().__init__(func)
67
+
68
+ def __getattr__(self, name: str):
69
+ return IsCommand([name])
70
+
71
+
72
+ class IsText:
73
+ def __call__(self, text=None):
74
+ if text is None:
75
+ func = lambda m: m.is_text is True and m.is_file is False
76
+ else:
77
+ if isinstance(text, str):
78
+ text = [text]
79
+ func = lambda m: m.is_text is True and m.is_file is False and m.text in text
80
+
81
+ return Filter(func)
82
+ #is_text = Filter(lambda m: getattr(m, "is_text", False))
54
83
  is_file = Filter(lambda m: getattr(m, "file", None) is not None)
55
84
  is_sticker = Filter(lambda m: getattr(m, "sticker", None) is not None)
56
85
  is_contact = Filter(lambda m: getattr(m, "contact_message", None) is not None)
@@ -59,7 +88,8 @@ is_location = Filter(lambda m: getattr(m, "location", None) is not None)
59
88
  is_live_location = Filter(lambda m: getattr(m, "live_location", None) is not None)
60
89
  has_any_media = Filter(lambda m: getattr(m, "has_any_media", False))
61
90
  has_media = Filter(lambda m: getattr(m, "has_media", False))
62
- is_command = Filter(lambda m: getattr(m, "is_command", False))
91
+ is_text = IsText()
92
+ is_command = IsCommand()
63
93
  is_user = Filter(lambda m: getattr(m, "is_user", False))
64
94
  is_private = Filter(lambda m: getattr(m, "is_private", False))
65
95
  is_group = Filter(lambda m: getattr(m, "is_group", False))
@@ -67,13 +97,8 @@ is_channel = Filter(lambda m: getattr(m, "is_channel", False))
67
97
  is_reply = Filter(lambda m: getattr(m, "is_reply", False))
68
98
  is_forwarded = Filter(lambda m: getattr(m, "is_forwarded", False))
69
99
  is_edited = Filter(lambda m: getattr(m, "is_edited", False))
70
-
71
-
72
-
73
-
74
100
  def text(keyword: str):
75
101
  return Filter(lambda m: getattr(m, "text", "") and keyword in m.text)
76
-
77
102
  def text_length(min_len: int = 0, max_len: int = None):
78
103
  def _filter(m):
79
104
  t = getattr(m, "text", "")
@@ -82,26 +107,19 @@ def text_length(min_len: int = 0, max_len: int = None):
82
107
  if max_len is not None and len(t) > max_len: return False
83
108
  return True
84
109
  return Filter(_filter)
85
-
86
110
  def text_regex(pattern: str):
87
111
  regex = re.compile(pattern)
88
112
  return Filter(lambda m: getattr(m, "text", "") and regex.search(m.text))
89
-
90
113
  def text_startswith(prefix: str):
91
114
  return Filter(lambda m: getattr(m, "text", "").startswith(prefix) if getattr(m, "text", None) else False)
92
-
93
115
  def text_endswith(suffix: str):
94
116
  return Filter(lambda m: getattr(m, "text", "").endswith(suffix) if getattr(m, "text", None) else False)
95
-
96
117
  def text_upper():
97
118
  return Filter(lambda m: getattr(m, "text", "").isupper() if getattr(m, "text", None) else False)
98
-
99
119
  def text_lower():
100
120
  return Filter(lambda m: getattr(m, "text", "").islower() if getattr(m, "text", None) else False)
101
-
102
121
  def text_digit():
103
122
  return Filter(lambda m: getattr(m, "text", "").isdigit() if getattr(m, "text", None) else False)
104
-
105
123
  def text_word_count(min_words: int = 1, max_words: int = None):
106
124
  def _filter(m):
107
125
  t = getattr(m, "text", "")
@@ -111,49 +129,28 @@ def text_word_count(min_words: int = 1, max_words: int = None):
111
129
  if max_words is not None and wc > max_words: return False
112
130
  return True
113
131
  return Filter(_filter)
114
-
115
132
  def text_contains_any(keywords: list):
116
133
  return Filter(lambda m: getattr(m, "text", "") and any(k in m.text for k in keywords))
117
-
118
134
  def text_equals(value: str):
119
135
  return Filter(lambda m: getattr(m, "text", None) == value)
120
-
121
136
  def text_not_equals(value: str):
122
137
  return Filter(lambda m: getattr(m, "text", None) != value)
123
-
124
-
125
-
126
-
127
138
  def file_size_gt(size: int):
128
139
  return Filter(lambda m: m.file and getattr(m.file, "size", 0) > size)
129
-
130
140
  def file_size_lt(size: int):
131
141
  return Filter(lambda m: m.file and getattr(m.file, "size", 0) < size)
132
-
133
142
  def file_name_contains(substring: str):
134
143
  return Filter(lambda m: m.file and substring in getattr(m.file, "file_name", ""))
135
-
136
144
  def file_extension(ext: str):
137
145
  return Filter(lambda m: m.file and getattr(m.file, "file_name", "").endswith(ext))
138
-
139
146
  def file_id_is(file_id: str):
140
147
  return Filter(lambda m: m.file and getattr(m.file, "file_id", None) == file_id)
141
-
142
-
143
-
144
-
145
148
  def sticker_id_is(sid: str):
146
149
  return Filter(lambda m: m.sticker and getattr(m.sticker, "sticker_id", None) == sid)
147
-
148
150
  def sticker_emoji_is(emoji: str):
149
151
  return Filter(lambda m: m.sticker and getattr(m.sticker, "emoji", None) == emoji)
150
-
151
-
152
-
153
-
154
152
  def poll_question_contains(keyword: str):
155
153
  return Filter(lambda m: m.poll and keyword in getattr(m.poll, "question", ""))
156
-
157
154
  def poll_option_count(min_options: int = 1, max_options: int = None):
158
155
  def _filter(m):
159
156
  if not getattr(m, "poll", None): return False
@@ -162,27 +159,18 @@ def poll_option_count(min_options: int = 1, max_options: int = None):
162
159
  if max_options is not None and len(options) > max_options: return False
163
160
  return True
164
161
  return Filter(_filter)
165
-
166
-
167
-
168
-
169
162
  def location_within(lat_min, lat_max, long_min, long_max):
170
163
  def _filter(m):
171
164
  loc = getattr(m, "location", None)
172
165
  if not loc: return False
173
166
  return lat_min <= getattr(loc, "lat", 0) <= lat_max and long_min <= getattr(loc, "long", 0) <= long_max
174
167
  return Filter(_filter)
175
-
176
168
  def live_location_within(lat_min, lat_max, long_min, long_max):
177
169
  def _filter(m):
178
170
  loc = getattr(m, "live_location", None)
179
171
  if not loc: return False
180
172
  return lat_min <= getattr(loc, "lat", 0) <= lat_max and long_min <= getattr(loc, "long", 0) <= long_max
181
173
  return Filter(_filter)
182
-
183
-
184
-
185
-
186
174
  def has_media_types(types: list):
187
175
  return Filter(lambda m: any(getattr(m, t, None) for t in types))
188
176
 
rubka/rubino.py CHANGED
@@ -446,7 +446,7 @@ class Bot():
446
446
  "api_version":"0",
447
447
  "client":{
448
448
  "app_name":"Main",
449
- "app_version":"2.2.4",
449
+ "app_version":"2.4.7",
450
450
  "package":"m.rubika.ir",
451
451
  "platform":"PWA"
452
452
  },
@@ -926,6 +926,20 @@ class Bot():
926
926
  "profile_id": profile_id
927
927
  },methode="getProfileFollowers")
928
928
 
929
+ def get_Highlight_StoryIds(self,highlight_id:str,profile_id:str,target_profile_id:str):
930
+ return self._reuests_post(data={
931
+ "highlight_id": highlight_id,
932
+ "profile_id": profile_id,
933
+ "target_profile_id": target_profile_id
934
+ },methode="getHighlightStoryIds")
935
+ def get_Highlight_Stories(self,highlight_id:str,profile_id:str,target_profile_id:str,story_ids:list):
936
+ return self._reuests_post(data={
937
+ "highlight_id": highlight_id,
938
+ "profile_id": profile_id,
939
+ "target_profile_id": target_profile_id,
940
+ "story_ids":story_ids
941
+ },methode="getHighlightStories")
942
+
929
943
  def get_NewFollow_Requests(self,sort:str="FromMax",limit:int=50,equal:bool=False,profile_id:str=None):
930
944
  return self._reuests_post(data={
931
945
  "equal": equal,
@@ -955,14 +969,37 @@ class Bot():
955
969
  "track_id": "Related",
956
970
  "profile_id": profile_id
957
971
  },methode="postBookmarkAction")
958
-
959
- def get_Saved_Posts(self,sort:str="FromMax",limit:int=51,equal:bool=False,profile_id:str=None):
960
- return self._reuests_post(data={
961
- "equal": equal,
972
+ def get_saved_posts(
973
+ self,
974
+ max_id: str,
975
+ limit: int = 20,
976
+ profile_id: Optional[str] = None,
977
+ sort: str = "FromMax") -> Dict[str, Any]:
978
+ """
979
+ دریافت پست‌های ذخیره‌شده (بوکمارک‌شده)
980
+
981
+ Args:
982
+ max_id (str): شناسه آخرین پست برای صفحه‌بندی.
983
+ limit (int, optional): تعداد پست‌ها در هر درخواست. پیش‌فرض 20.
984
+ profile_id (str, optional): شناسه پروفایل (در صورت نیاز).
985
+ sort (str, optional): نوع مرتب‌سازی. پیش‌فرض "FromMax".
986
+
987
+ Returns:
988
+ Dict[str, Any]: پاسخ سرور شامل لیست پست‌های ذخیره‌شده.
989
+ """
990
+ payload = {
962
991
  "limit": limit,
963
- "sort": sort,
964
- "profile_id": profile_id
965
- },methode="getBookmarkedPosts")
992
+ "max_id": max_id,
993
+ "sort": sort
994
+ }
995
+
996
+ if profile_id is not None:
997
+ payload["profile_id"] = profile_id
998
+
999
+ return self._reuests_post(
1000
+ data=payload,
1001
+ methode="getBookmarkedPosts"
1002
+ )
966
1003
 
967
1004
  def search_Page(self,username:str,sort:str="FromMax",limit:int=50,equal:bool=False,profile_id:str=None):
968
1005
  return self._reuests_post(data={
@@ -994,6 +1031,14 @@ class Bot():
994
1031
  "record_id": post_id,
995
1032
  "profile_id": profile_id
996
1033
  },methode="setReportRecord")
1034
+ def report_Post(self,post_profile_id,post_id:str,reason:int=2,profile_id:str=None):
1035
+ return self._reuests_post(data={
1036
+ "model": "Post",
1037
+ "reason": reason,
1038
+ "post_profile_id":post_profile_id,
1039
+ "record_id": post_id,
1040
+ "profile_id": profile_id
1041
+ },methode="setReportRecord")
997
1042
 
998
1043
  def delete_Post(self,post_id:str,profile_id:str=None):
999
1044
  return self._reuests_post(data={
@@ -1013,6 +1058,12 @@ class Bot():
1013
1058
  "profile_status": profile_status,
1014
1059
  "profile_id": profile_id
1015
1060
  },methode="updateProfile")
1061
+ def get_New_Events(self,limit:int=20,sort:str="FromMax",profile_id:str=None):
1062
+ return self._reuests_post(data={
1063
+ "limit": limit,
1064
+ "profile_id": profile_id,
1065
+ "sort":sort
1066
+ },methode="updateProfile")
1016
1067
 
1017
1068
  def allow_Send_MessagePv(self,is_message_allowed:bool=False,profile_id:str=None):
1018
1069
  return self._reuests_post(data={
@@ -1,18 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 6.5.2
4
- Summary: A Python library for interacting with Rubika Bot API.
3
+ Version: 6.6.4
4
+ Summary: rubika A Python library for interacting with Rubika Bot API.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
- Download-URL: https://github.com/Mahdy-Ahmadi/rubka/blob/main/project_library.zip
6
+ Download-URL: https://github.com/Mahdy-Ahmadi/rubka/archive/refs/tags/v6.6.4.zip
7
7
  Author: Mahdi Ahmadi
8
8
  Author-email: mahdiahmadi.1208@gmail.com
9
9
  Maintainer: Mahdi Ahmadi
10
10
  Maintainer-email: mahdiahmadi.1208@gmail.com
11
+ License: MIT
12
+ Project-URL: Bug Tracker, https://t.me/Bprogrammer
13
+ Project-URL: Documentation, https://github.com/Mahdy-Ahmadi/rubka/blob/main/README.md
14
+ Project-URL: Source Code, https://github.com/Mahdy-Ahmadi/Rubka
15
+ Keywords: rubika bot api library chat messaging rubpy pyrubi rubigram
16
+ Classifier: Development Status :: 5 - Production/Stable
17
+ Classifier: Intended Audience :: Developers
11
18
  Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
12
22
  Classifier: License :: OSI Approved :: MIT License
13
23
  Classifier: Operating System :: OS Independent
14
24
  Classifier: Topic :: Communications :: Chat
15
25
  Classifier: Topic :: Software Development :: Libraries
26
+ Classifier: Natural Language :: Persian
16
27
  Requires-Python: >=3.6
17
28
  Description-Content-Type: text/markdown
18
29
  Requires-Dist: requests
@@ -31,8 +42,11 @@ Dynamic: description
31
42
  Dynamic: description-content-type
32
43
  Dynamic: download-url
33
44
  Dynamic: home-page
45
+ Dynamic: keywords
46
+ Dynamic: license
34
47
  Dynamic: maintainer
35
48
  Dynamic: maintainer-email
49
+ Dynamic: project-url
36
50
  Dynamic: requires-dist
37
51
  Dynamic: requires-python
38
52
  Dynamic: summary
@@ -62,16 +76,16 @@ If `importlib.metadata` is not available, it installs `importlib-metadata` autom
62
76
  ## 🚀 Getting Started
63
77
 
64
78
  ```python
65
- from rubka import Robot
79
+ from rubka.asynco import Robot
66
80
  from rubka.context import Message
67
-
81
+ import asyncio
68
82
  bot = Robot(token="YOUR_TOKEN_HERE")
69
83
 
70
84
  @bot.on_message(commands=["start"])
71
- def start(bot: Robot, message: Message):
72
- message.reply("سلام! خوش آمدید!")
85
+ async def start(bot: Robot, message: Message):
86
+ await message.reply("سلام! خوش آمدید!")
73
87
 
74
- bot.run()
88
+ asyncio.run(bot.run())
75
89
  ```
76
90
 
77
91
  ---
@@ -82,8 +96,8 @@ You can handle incoming text messages using `@bot.on_message()`:
82
96
 
83
97
  ```python
84
98
  @bot.on_message(commands=["hello"])
85
- def greet(bot: Robot, message: Message):
86
- message.reply("سلام کاربر عزیز 👋")
99
+ async def greet(bot: Robot, message: Message):
100
+ await message.reply("سلام کاربر عزیز 👋")
87
101
  ```
88
102
 
89
103
  You can also add filters.
@@ -1,17 +1,17 @@
1
1
  rubka/__init__.py,sha256=TR1DABU5Maz2eO62ZEFiwOqNU0dH6l6HZfqRUxeo4eY,194
2
- rubka/api.py,sha256=Nu9bX0Vv0AGLEHfUDtS7_SQDbaavk8bZghc-pRH5zRk,68691
3
- rubka/asynco.py,sha256=8zpKldan71TQLcZk7W8h3G2Aya1OSGZs5oqhn9jNlrM,84336
2
+ rubka/api.py,sha256=FkvXi6lGyDhy-GStfT9IPtmnpir75ggNsC4bsgy2tKw,68838
3
+ rubka/asynco.py,sha256=V0PW63HpBZJ1B39aKRPKU0gHDqriH7u9Z-6LFiFaWQg,83562
4
4
  rubka/button.py,sha256=vU9OvWXCD4MRrTJ8Xmivd4L471-06zrD2qpZBTw5vjY,13305
5
5
  rubka/config.py,sha256=Bck59xkOiqioLv0GkQ1qPGnBXVctz1hKk6LT4h2EPx0,78
6
6
  rubka/context.py,sha256=4YZs7DiZD_HWOqY76hwwajG0J-bLy6wjeKtQT3EatZU,19341
7
7
  rubka/decorators.py,sha256=hGwUoE4q2ImrunJIGJ_kzGYYxQf1ueE0isadqraKEts,1157
8
8
  rubka/exceptions.py,sha256=tujZt1XrhWaw-lmdeVadVceUptpw4XzNgE44sAAY0gs,90
9
- rubka/filters.py,sha256=hvH48ejEEXX5KEt9KlzZWBep6O8F9c2o1agwg-y7mMc,11101
9
+ rubka/filters.py,sha256=DY1bdkpRKIiLtVcy6X3hOnlGPcVOK4HFb3QgmaPx6Oo,12116
10
10
  rubka/jobs.py,sha256=GvLMLsVhcSEzRTgkvnPISPEBN71suW2xXI0hUaUZPTo,378
11
11
  rubka/keyboards.py,sha256=7nr-dT2bQJVQnQ6RMWPTSjML6EEk6dsBx-4d8pab8xk,488
12
12
  rubka/keypad.py,sha256=yGsNt8W5HtUFBzVF1m_p7GySlu1hwIcSvXZ4BTdrlvg,9558
13
13
  rubka/logger.py,sha256=J2I6NiK1z32lrAzC4H1Et6WPMBXxXGCVUsW4jgcAofs,289
14
- rubka/rubino.py,sha256=uOhvljK_tms_767pX1TSxryyq71rKDyCgiv36ssNXEg,56674
14
+ rubka/rubino.py,sha256=GuXnEUTTSZUw68FMTQBYsB2zG_3rzdDa-krsrl4ib8w,58755
15
15
  rubka/update.py,sha256=4YZs7DiZD_HWOqY76hwwajG0J-bLy6wjeKtQT3EatZU,19341
16
16
  rubka/utils.py,sha256=XUQUZxQt9J2f0X5hmAH_MH1kibTAfdT1T4AaBkBhBBs,148
17
17
  rubka/adaptorrubka/__init__.py,sha256=6o2tCXnVeES7nx-LjnzsuMqjKcWIm9qwKficLE54s-U,83
@@ -35,7 +35,8 @@ rubka/adaptorrubka/types/socket/message.py,sha256=0WgLMZh4eow8Zn7AiSX4C3GZjQTkIg
35
35
  rubka/adaptorrubka/utils/__init__.py,sha256=OgCFkXdNFh379quNwIVOAWY2NP5cIOxU5gDRRALTk4o,54
36
36
  rubka/adaptorrubka/utils/configs.py,sha256=nMUEOJh1NqDJsf9W9PurkN_DLYjO6kKPMm923i4Jj_A,492
37
37
  rubka/adaptorrubka/utils/utils.py,sha256=5-LioLNYX_TIbQGDeT50j7Sg9nAWH2LJUUs-iEXpsUY,8816
38
- rubka-6.5.2.dist-info/METADATA,sha256=ijWtJKisum3EsKK72ILJ9e8B2LL4Z_kqbgqaPO82PFg,33335
39
- rubka-6.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- rubka-6.5.2.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
41
- rubka-6.5.2.dist-info/RECORD,,
38
+ rubka-6.6.4.dist-info/METADATA,sha256=FH4BjESYZfSWjk76Us_1LFKez1zxqv72MRv6-cINDCE,34043
39
+ rubka-6.6.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ rubka-6.6.4.dist-info/entry_points.txt,sha256=4aESuUmuUOALMUy7Kucv_Gb5YlqhsJmTmdXLlZU9sJ0,46
41
+ rubka-6.6.4.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
42
+ rubka-6.6.4.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ rubka = rubka.__main__:main
File without changes