Rubka 6.4.4__tar.gz → 6.4.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {rubka-6.4.4 → rubka-6.4.8}/PKG-INFO +1 -1
  2. {rubka-6.4.4 → rubka-6.4.8}/Rubka.egg-info/PKG-INFO +1 -1
  3. {rubka-6.4.4 → rubka-6.4.8}/rubka/api.py +182 -12
  4. {rubka-6.4.4 → rubka-6.4.8}/rubka/asynco.py +218 -38
  5. {rubka-6.4.4 → rubka-6.4.8}/rubka/context.py +16 -2
  6. {rubka-6.4.4 → rubka-6.4.8}/rubka/update.py +16 -2
  7. {rubka-6.4.4 → rubka-6.4.8}/setup.py +1 -1
  8. {rubka-6.4.4 → rubka-6.4.8}/README.md +0 -0
  9. {rubka-6.4.4 → rubka-6.4.8}/Rubka.egg-info/SOURCES.txt +0 -0
  10. {rubka-6.4.4 → rubka-6.4.8}/Rubka.egg-info/dependency_links.txt +0 -0
  11. {rubka-6.4.4 → rubka-6.4.8}/Rubka.egg-info/requires.txt +0 -0
  12. {rubka-6.4.4 → rubka-6.4.8}/Rubka.egg-info/top_level.txt +0 -0
  13. {rubka-6.4.4 → rubka-6.4.8}/rubka/__init__.py +0 -0
  14. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/__init__.py +0 -0
  15. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/client/__init__.py +0 -0
  16. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/client/client.py +0 -0
  17. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/crypto/__init__.py +0 -0
  18. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/crypto/crypto.py +0 -0
  19. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/enums.py +0 -0
  20. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/exceptions.py +0 -0
  21. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/methods/__init__.py +0 -0
  22. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/methods/methods.py +0 -0
  23. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/network/__init__.py +0 -0
  24. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/network/helper.py +0 -0
  25. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/network/network.py +0 -0
  26. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/network/socket.py +0 -0
  27. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/sessions/__init__.py +0 -0
  28. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/sessions/sessions.py +0 -0
  29. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/types/__init__.py +0 -0
  30. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/types/socket/__init__.py +0 -0
  31. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/types/socket/message.py +0 -0
  32. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/utils/__init__.py +0 -0
  33. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/utils/configs.py +0 -0
  34. {rubka-6.4.4 → rubka-6.4.8}/rubka/adaptorrubka/utils/utils.py +0 -0
  35. {rubka-6.4.4 → rubka-6.4.8}/rubka/button.py +0 -0
  36. {rubka-6.4.4 → rubka-6.4.8}/rubka/config.py +0 -0
  37. {rubka-6.4.4 → rubka-6.4.8}/rubka/decorators.py +0 -0
  38. {rubka-6.4.4 → rubka-6.4.8}/rubka/exceptions.py +0 -0
  39. {rubka-6.4.4 → rubka-6.4.8}/rubka/jobs.py +0 -0
  40. {rubka-6.4.4 → rubka-6.4.8}/rubka/keyboards.py +0 -0
  41. {rubka-6.4.4 → rubka-6.4.8}/rubka/keypad.py +0 -0
  42. {rubka-6.4.4 → rubka-6.4.8}/rubka/logger.py +0 -0
  43. {rubka-6.4.4 → rubka-6.4.8}/rubka/rubino.py +0 -0
  44. {rubka-6.4.4 → rubka-6.4.8}/rubka/utils.py +0 -0
  45. {rubka-6.4.4 → rubka-6.4.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 6.4.4
3
+ Version: 6.4.8
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 6.4.4
3
+ Version: 6.4.8
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
@@ -498,6 +498,168 @@ class Robot:
498
498
  })
499
499
  return func
500
500
  return decorator
501
+ def on_message_file(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
502
+ def decorator(func: Callable[[Any, Message], None]):
503
+ def wrapper(bot, message: Message):
504
+ if not message.file:
505
+ return
506
+ if filters and not filters(message):
507
+ return
508
+ return func(bot, message)
509
+
510
+ self._message_handlers.append({
511
+ "func": wrapper,
512
+ "filters": filters,
513
+ "file_only": True,
514
+ "commands":commands
515
+ })
516
+ return wrapper
517
+ return decorator
518
+
519
+ def on_message_forwarded(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
520
+ def decorator(func: Callable[[Any, Message], None]):
521
+ def wrapper(bot, message: Message):
522
+ if not message.is_forwarded:
523
+ return
524
+ if filters and not filters(message):
525
+ return
526
+ return func(bot, message)
527
+
528
+ self._message_handlers.append({
529
+ "func": wrapper,
530
+ "filters": filters,
531
+ "forwarded_only": True,
532
+ "commands":commands
533
+ })
534
+ return wrapper
535
+ return decorator
536
+
537
+ def on_message_reply(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
538
+ def decorator(func: Callable[[Any, Message], None]):
539
+ def wrapper(bot, message: Message):
540
+ if not message.is_reply:
541
+ return
542
+ if filters and not filters(message):
543
+ return
544
+ return func(bot, message)
545
+
546
+ self._message_handlers.append({
547
+ "func": wrapper,
548
+ "filters": filters,
549
+ "reply_only": True,
550
+ "commands":commands
551
+ })
552
+ return wrapper
553
+ return decorator
554
+
555
+ def on_message_text(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
556
+ def decorator(func: Callable[[Any, Message], None]):
557
+ def wrapper(bot, message: Message):
558
+ if not message.text:
559
+ return
560
+ if filters and not filters(message):
561
+ return
562
+ return func(bot, message)
563
+
564
+ self._message_handlers.append({
565
+ "func": wrapper,
566
+ "filters": filters,
567
+ "text_only": True,
568
+ "commands":commands
569
+ })
570
+ return wrapper
571
+ return decorator
572
+
573
+ def on_message_media(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
574
+ def decorator(func: Callable[[Any, Message], None]):
575
+ def wrapper(bot, message: Message):
576
+ if not message.is_media:
577
+ return
578
+ if filters and not filters(message):
579
+ return
580
+ return func(bot, message)
581
+
582
+ self._message_handlers.append({
583
+ "func": wrapper,
584
+ "filters": filters,
585
+ "media_only": True,
586
+ "commands":commands
587
+ })
588
+ return wrapper
589
+ return decorator
590
+
591
+ def on_message_sticker(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
592
+ def decorator(func: Callable[[Any, Message], None]):
593
+ def wrapper(bot, message: Message):
594
+ if not message.sticker:
595
+ return
596
+ if filters and not filters(message):
597
+ return
598
+ return func(bot, message)
599
+
600
+ self._message_handlers.append({
601
+ "func": wrapper,
602
+ "filters": filters,
603
+ "sticker_only": True,
604
+ "commands":commands
605
+ })
606
+ return wrapper
607
+ return decorator
608
+
609
+ def on_message_contact(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
610
+ def decorator(func: Callable[[Any, Message], None]):
611
+ def wrapper(bot, message: Message):
612
+ if not message.is_contact:
613
+ return
614
+ if filters and not filters(message):
615
+ return
616
+ return func(bot, message)
617
+
618
+ self._message_handlers.append({
619
+ "func": wrapper,
620
+ "filters": filters,
621
+ "contact_only": True,
622
+ "commands":commands
623
+ })
624
+ return wrapper
625
+ return decorator
626
+
627
+ def on_message_location(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
628
+ def decorator(func: Callable[[Any, Message], None]):
629
+ def wrapper(bot, message: Message):
630
+ if not message.is_location:
631
+ return
632
+ if filters and not filters(message):
633
+ return
634
+ return func(bot, message)
635
+
636
+ self._message_handlers.append({
637
+ "func": wrapper,
638
+ "filters": filters,
639
+ "location_only": True,
640
+ "commands":commands
641
+ })
642
+ return wrapper
643
+ return decorator
644
+
645
+ def on_message_poll(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
646
+ def decorator(func: Callable[[Any, Message], None]):
647
+ def wrapper(bot, message: Message):
648
+ if not message.is_poll:
649
+ return
650
+ if filters and not filters(message):
651
+ return
652
+ return func(bot, message)
653
+
654
+ self._message_handlers.append({
655
+ "func": wrapper,
656
+ "filters": filters,
657
+ "poll_only": True,
658
+ "commands":commands
659
+ })
660
+ return wrapper
661
+ return decorator
662
+
501
663
  def message_handler(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
502
664
  def decorator(func: Callable[[Any, Message], None]):
503
665
  self._message_handlers.append({
@@ -1191,17 +1353,7 @@ class Robot:
1191
1353
  raise ValueError(f"Invalid media type. Must be one of {allowed}")
1192
1354
  result = self._post("requestSendFile", {"type": media_type})
1193
1355
  return result.get("data", {}).get("upload_url")
1194
- def _send_uploaded_file(
1195
- self,
1196
- chat_id: str,
1197
- file_id: str,
1198
- text: Optional[str] = None,
1199
- chat_keypad: Optional[Dict[str, Any]] = None,
1200
- inline_keypad: Optional[Dict[str, Any]] = None,
1201
- disable_notification: bool = False,
1202
- reply_to_message_id: Optional[str] = None,
1203
- chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None"
1204
- ) -> Dict[str, Any]:
1356
+ def _send_uploaded_file(self, chat_id: str, file_id: str,type_file : str = "file",text: Optional[str] = None, chat_keypad: Optional[Dict[str, Any]] = None, inline_keypad: Optional[Dict[str, Any]] = None, disable_notification: bool = False, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
1205
1357
  payload = {
1206
1358
  "chat_id": chat_id,
1207
1359
  "file_id": file_id,
@@ -1216,7 +1368,25 @@ class Robot:
1216
1368
  if reply_to_message_id:
1217
1369
  payload["reply_to_message_id"] = str(reply_to_message_id)
1218
1370
 
1219
- return self._post("sendFile", payload)
1371
+ resp = self._post("sendFile", payload)
1372
+ message_id_put = resp["data"]["message_id"]
1373
+ result = {
1374
+ "status": resp.get("status"),
1375
+ "status_det": resp.get("status_det"),
1376
+ "file_id": file_id,
1377
+ "text":text,
1378
+ "message_id": message_id_put,
1379
+ "send_to_chat_id": chat_id,
1380
+ "reply_to_message_id": reply_to_message_id,
1381
+ "disable_notification": disable_notification,
1382
+ "type_file": type_file,
1383
+ "raw_response": resp,
1384
+ "chat_keypad":chat_keypad,
1385
+ "inline_keypad":inline_keypad,
1386
+ "chat_keypad_type":chat_keypad_type
1387
+ }
1388
+ import json
1389
+ return json.dumps(result, ensure_ascii=False, indent=4)
1220
1390
  def send_file(
1221
1391
  self,
1222
1392
  chat_id: str,
@@ -9,6 +9,8 @@ try:
9
9
  from .context import Message, InlineMessage
10
10
  except (ImportError, ModuleNotFoundError):
11
11
  from context import Message, InlineMessage
12
+ class FeatureNotAvailableError(Exception):
13
+ pass
12
14
 
13
15
  from tqdm.asyncio import tqdm
14
16
  from urllib.parse import urlparse, parse_qs
@@ -16,7 +18,7 @@ class InvalidTokenError(Exception):pass
16
18
  import mimetypes
17
19
  from pathlib import Path
18
20
  import time
19
- import datetime
21
+ import datetime,json
20
22
  import tempfile
21
23
  from tqdm import tqdm
22
24
  import os
@@ -32,7 +34,31 @@ def install_package(package_name: str) -> bool:
32
34
  return True
33
35
  except Exception:
34
36
  return False
35
-
37
+ class MessageResponse:
38
+ def __init__(self, resp: dict, chat_id: str, file_id: str,
39
+ type_file: str, reply_to_message_id: str,
40
+ disable_notification: bool, text: str = None,
41
+ chat_keypad=None, inline_keypad=None, chat_keypad_type="None"):
42
+
43
+ self.status = resp.get("status")
44
+ self.status_det = resp.get("status_det")
45
+ self.file_id = file_id
46
+ self.message_id = resp["data"].get("message_id")
47
+ self.send_to_chat_id = chat_id
48
+ self.reply_to_message_id = reply_to_message_id
49
+ self.disable_notification = disable_notification
50
+ self.type_file = type_file
51
+ self.text = text
52
+ self.chat_keypad = chat_keypad
53
+ self.inline_keypad = inline_keypad
54
+ self.chat_keypad_type = chat_keypad_type
55
+ self.raw_response = resp
56
+
57
+ def to_dict(self):
58
+ return self.__dict__
59
+
60
+ def to_json(self):
61
+ return json.dumps(self.__dict__, ensure_ascii=False, indent=4)
36
62
  def get_importlib_metadata():
37
63
  """Dynamically imports and returns metadata functions from importlib."""
38
64
  try:
@@ -192,6 +218,9 @@ class Robot:
192
218
  raise InvalidTokenError("The provided bot token is invalid or expired.")
193
219
  from typing import Callable, Any, Optional, List
194
220
 
221
+
222
+ #decorator#
223
+
195
224
  def on_message_private(
196
225
  self,
197
226
  chat_id: Optional[Union[str, List[str]]] = None,
@@ -221,30 +250,20 @@ class Robot:
221
250
 
222
251
  if not message.is_private:
223
252
  return
224
-
225
-
226
253
  if chat_id:
227
254
  if isinstance(chat_id, str) and message.chat_id != chat_id:
228
255
  return
229
256
  if isinstance(chat_id, list) and message.chat_id not in chat_id:
230
257
  return
231
-
232
-
233
258
  if sender_id:
234
259
  if isinstance(sender_id, str) and message.sender_id != sender_id:
235
260
  return
236
261
  if isinstance(sender_id, list) and message.sender_id not in sender_id:
237
262
  return
238
-
239
-
240
263
  if sender_type and message.sender_type != sender_type:
241
264
  return
242
-
243
-
244
265
  if not allow_forwarded and message.forwarded_from:
245
266
  return
246
-
247
-
248
267
  if not allow_files and message.file:
249
268
  return
250
269
  if not allow_stickers and message.sticker:
@@ -517,6 +536,142 @@ class Robot:
517
536
  })
518
537
  return func
519
538
  return decorator
539
+
540
+ def on_message_file(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
541
+ def decorator(func: Callable[[Any, Message], None]):
542
+ async def wrapper(bot, message: Message):
543
+ if not message.file:return
544
+ if filters and not filters(message):return
545
+ return await func(bot, message)
546
+
547
+ self._message_handlers.append({
548
+ "func": wrapper,
549
+ "filters": filters,
550
+ "file_only": True,
551
+ "commands": commands
552
+ })
553
+ return wrapper
554
+ return decorator
555
+ def on_message_forwarded(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
556
+ def decorator(func: Callable[[Any, Message], None]):
557
+ async def wrapper(bot, message: Message):
558
+ if not message.is_forwarded:return
559
+ if filters and not filters(message):return
560
+ return await func(bot, message)
561
+
562
+ self._message_handlers.append({
563
+ "func": wrapper,
564
+ "filters": filters,
565
+ "forwarded_only": True,
566
+ "commands": commands
567
+ })
568
+ return wrapper
569
+ return decorator
570
+ def on_message_reply(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
571
+ def decorator(func: Callable[[Any, Message], None]):
572
+ async def wrapper(bot, message: Message):
573
+ if not message.is_reply:return
574
+ if filters and not filters(message):return
575
+ return await func(bot, message)
576
+
577
+ self._message_handlers.append({
578
+ "func": wrapper,
579
+ "filters": filters,
580
+ "reply_only": True,
581
+ "commands": commands
582
+ })
583
+ return wrapper
584
+ return decorator
585
+ def on_message_text(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
586
+ def decorator(func: Callable[[Any, Message], None]):
587
+ async def wrapper(bot, message: Message):
588
+ if not message.text:return
589
+ if filters and not filters(message):return
590
+ return await func(bot, message)
591
+
592
+ self._message_handlers.append({
593
+ "func": wrapper,
594
+ "filters": filters,
595
+ "text_only": True,
596
+ "commands": commands
597
+ })
598
+ return wrapper
599
+ return decorator
600
+ def on_message_media(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
601
+ def decorator(func: Callable[[Any, Message], None]):
602
+ async def wrapper(bot, message: Message):
603
+ if not message.is_media:return
604
+ if filters and not filters(message):return
605
+ return await func(bot, message)
606
+
607
+ self._message_handlers.append({
608
+ "func": wrapper,
609
+ "filters": filters,
610
+ "media_only": True,
611
+ "commands": commands
612
+ })
613
+ return wrapper
614
+ return decorator
615
+ def on_message_sticker(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
616
+ def decorator(func: Callable[[Any, Message], None]):
617
+ async def wrapper(bot, message: Message):
618
+ if not message.sticker:return
619
+ if filters and not filters(message):return
620
+ return await func(bot, message)
621
+
622
+ self._message_handlers.append({
623
+ "func": wrapper,
624
+ "filters": filters,
625
+ "sticker_only": True,
626
+ "commands": commands
627
+ })
628
+ return wrapper
629
+ return decorator
630
+ def on_message_contact(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
631
+ def decorator(func: Callable[[Any, Message], None]):
632
+ async def wrapper(bot, message: Message):
633
+ if not message.is_contact:return
634
+ if filters and not filters(message):return
635
+ return await func(bot, message)
636
+
637
+ self._message_handlers.append({
638
+ "func": wrapper,
639
+ "filters": filters,
640
+ "contact_only": True,
641
+ "commands": commands
642
+ })
643
+ return wrapper
644
+ return decorator
645
+ def on_message_location(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
646
+ def decorator(func: Callable[[Any, Message], None]):
647
+ async def wrapper(bot, message: Message):
648
+ if not message.is_location:return
649
+ if filters and not filters(message):return
650
+ return await func(bot, message)
651
+
652
+ self._message_handlers.append({
653
+ "func": wrapper,
654
+ "filters": filters,
655
+ "location_only": True,
656
+ "commands": commands
657
+ })
658
+ return wrapper
659
+ return decorator
660
+ def on_message_poll(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
661
+ def decorator(func: Callable[[Any, Message], None]):
662
+ async def wrapper(bot, message: Message):
663
+ if not message.is_poll:return
664
+ if filters and not filters(message):return
665
+ return await func(bot, message)
666
+
667
+ self._message_handlers.append({
668
+ "func": wrapper,
669
+ "filters": filters,
670
+ "poll_only": True,
671
+ "commands": commands
672
+ })
673
+ return wrapper
674
+ return decorator
520
675
  def on_update(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
521
676
  def decorator(func: Callable[[Any, Message], None]):
522
677
  self._message_handlers.append({
@@ -1481,17 +1636,18 @@ class Robot:
1481
1636
 
1482
1637
  data = aiohttp.FormData()
1483
1638
  data.add_field('file', file_progress_generator(path), filename=name, content_type='application/octet-stream')
1484
-
1485
- async with session.post(upload_url, data=data) as response:
1486
- progress_bar.close()
1487
- if response.status != 200:
1488
- raise Exception(f"Upload failed ({response.status}): {await response.text()}")
1489
-
1490
- json_data = await response.json()
1491
- if is_temp_file:
1492
- os.remove(path)
1493
- print(json_data)
1494
- return json_data.get('data', {}).get('file_id')
1639
+ try:
1640
+ async with session.post(upload_url, data=data) as response:
1641
+ progress_bar.close()
1642
+ if response.status != 200:
1643
+ raise Exception(f"Upload failed ({response.status}): {await response.text()}")
1644
+
1645
+ json_data = await response.json()
1646
+ if is_temp_file:
1647
+ os.remove(path)
1648
+ return json_data.get('data', {}).get('file_id')
1649
+ except :
1650
+ raise FeatureNotAvailableError(f"files is not currently supported by the server.")
1495
1651
 
1496
1652
 
1497
1653
  def get_extension(content_type: str) -> str:
@@ -1584,19 +1740,37 @@ class Robot:
1584
1740
  except Exception as e:
1585
1741
  raise Exception(f"An error occurred while downloading the file: {e}")
1586
1742
 
1587
- async def get_upload_url(self, media_type: Literal['File', 'Image', 'Voice', 'Music', 'Gif', 'Video']) -> str:
1588
- allowed = ['File', 'Image', 'Voice', 'Music', 'Gif', 'Video']
1743
+ async def get_upload_url(self, media_type: Literal['File', 'Image', 'voice', 'Music', 'Gif', 'Video']) -> str:
1744
+ allowed = ['File', 'Image', 'voice', 'Music', 'Gif', 'Video']
1589
1745
  if media_type not in allowed:
1590
1746
  raise ValueError(f"Invalid media type. Must be one of {allowed}")
1591
1747
  result = await self._post("requestSendFile", {"type": media_type})
1592
1748
  return result.get("data", {}).get("upload_url")
1593
1749
 
1594
- async def _send_uploaded_file(self, chat_id: str, file_id: str, text: Optional[str] = None, chat_keypad: Optional[Dict[str, Any]] = None, inline_keypad: Optional[Dict[str, Any]] = None, disable_notification: bool = False, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
1750
+ async def _send_uploaded_file(self, chat_id: str, file_id: str,type_file : str = "file",text: Optional[str] = None, chat_keypad: Optional[Dict[str, Any]] = None, inline_keypad: Optional[Dict[str, Any]] = None, disable_notification: bool = False, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
1595
1751
  payload = {"chat_id": chat_id, "file_id": file_id, "text": text, "disable_notification": disable_notification, "chat_keypad_type": chat_keypad_type}
1596
1752
  if chat_keypad: payload["chat_keypad"] = chat_keypad
1597
1753
  if inline_keypad: payload["inline_keypad"] = inline_keypad
1598
1754
  if reply_to_message_id: payload["reply_to_message_id"] = str(reply_to_message_id)
1599
- return await self._post("sendFile", payload)
1755
+ payload["time"] = "10"
1756
+ resp = await self._post("sendFile", payload)
1757
+ message_id_put = resp["data"]["message_id"]
1758
+ result = {
1759
+ "status": resp.get("status"),
1760
+ "status_det": resp.get("status_det"),
1761
+ "file_id": file_id,
1762
+ "text":text,
1763
+ "message_id": message_id_put,
1764
+ "send_to_chat_id": chat_id,
1765
+ "reply_to_message_id": reply_to_message_id,
1766
+ "disable_notification": disable_notification,
1767
+ "type_file": type_file,
1768
+ "raw_response": resp,
1769
+ "chat_keypad":chat_keypad,
1770
+ "inline_keypad":inline_keypad,
1771
+ "chat_keypad_type":chat_keypad_type
1772
+ }
1773
+ return result
1600
1774
 
1601
1775
  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):
1602
1776
  if path:
@@ -1605,7 +1779,7 @@ class Robot:
1605
1779
  file_id = await self.upload_media_file(upload_url, file_name, path)
1606
1780
  if not file_id:
1607
1781
  raise ValueError("Either path or file_id must be provided.")
1608
- return await self._send_uploaded_file(chat_id=chat_id, file_id=file_id, text=text, inline_keypad=inline_keypad, chat_keypad=chat_keypad, reply_to_message_id=reply_to_message_id, disable_notification=disable_notification, chat_keypad_type=chat_keypad_type)
1782
+ return await self._send_uploaded_file(chat_id=chat_id, file_id=file_id, text=text, inline_keypad=inline_keypad, chat_keypad=chat_keypad, reply_to_message_id=reply_to_message_id, disable_notification=disable_notification, chat_keypad_type=chat_keypad_type,type_file=media_type)
1609
1783
 
1610
1784
  async def send_document(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]:
1611
1785
  return await self._send_file_generic("File", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
@@ -1613,14 +1787,14 @@ class Robot:
1613
1787
  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)
1614
1788
  async def re_send(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, caption: 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]:
1615
1789
  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)
1616
- 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] = 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]:
1617
- return await self._send_file_generic("Music", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
1790
+ 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]:
1791
+ 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)
1618
1792
 
1619
1793
  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]:
1620
1794
  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)
1621
1795
 
1622
1796
  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]:
1623
- 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)
1797
+ 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)
1624
1798
 
1625
1799
  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]:
1626
1800
  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)
@@ -1656,13 +1830,19 @@ class Robot:
1656
1830
  try:
1657
1831
  chat = await self.get_chat(chat_id)
1658
1832
  chat_info = chat.get("data", {}).get("chat", {})
1659
- first_name = chat_info.get("first_name", "")
1660
- last_name = chat_info.get("last_name", "")
1661
-
1662
- full_name = f"{first_name} {last_name}".strip()
1663
- return full_name if full_name else "Unknown"
1664
- except Exception:
1665
- return "Unknown"
1833
+ chat_type = chat_info.get("chat_type", "").lower()
1834
+ if chat_type == "user":
1835
+ first_name = chat_info.get("first_name", "")
1836
+ last_name = chat_info.get("last_name", "")
1837
+ full_name = f"{first_name} {last_name}".strip()
1838
+ return full_name if full_name else "null"
1839
+ elif chat_type in ["group", "channel"]:
1840
+ title = chat_info.get("title", "")
1841
+ return title if title else "null"
1842
+ else:return "null"
1843
+ except Exception:return "null"
1844
+
1845
+
1666
1846
 
1667
1847
  async def get_username(self, chat_id: str) -> str:
1668
1848
  chat_info = await self.get_chat(chat_id)
@@ -190,8 +190,10 @@ from pathlib import Path
190
190
  class Message:
191
191
  def __init__(self, bot, chat_id, message_id, sender_id, text=None, raw_data=None):
192
192
  self.bot = bot
193
- self.chat_id = chat_id
194
193
  self.raw_data = raw_data or {}
194
+ self.chat_id = chat_id
195
+ self.object_guid = chat_id
196
+ self.author_guid = self.raw_data.get("sender_id", sender_id)
195
197
  self.message_id: str = self.raw_data.get("message_id", message_id)
196
198
  self.text: str = self.raw_data.get("text", text)
197
199
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
@@ -199,6 +201,7 @@ class Message:
199
201
  self.is_edited: bool = self.raw_data.get("is_edited", False)
200
202
  self.sender_type: str = self.raw_data.get("sender_type")
201
203
  self.args = []
204
+ self.is_command = bool(self.text and self.text.startswith("/"))
202
205
  self.is_user = self.chat_id.startswith("b")
203
206
  self.is_private = self.chat_id.startswith("b")
204
207
  self.is_group = self.chat_id.startswith("g")
@@ -212,7 +215,18 @@ class Message:
212
215
  self.location = Location(self.raw_data["location"]) if "location" in self.raw_data else None
213
216
  self.live_location = LiveLocation(self.raw_data["live_location"]) if "live_location" in self.raw_data else None
214
217
  self.aux_data = AuxData(self.raw_data["aux_data"]) if "aux_data" in self.raw_data else None
215
-
218
+ self.is_reply = self.reply_to_message_id is not None
219
+ self.has_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
220
+ self.is_forwarded = self.forwarded_from is not None
221
+ self.is_text = bool(self.text and not self.has_media)
222
+ self.is_media = self.has_media
223
+ self.is_poll = self.poll is not None
224
+ self.is_location = self.location is not None
225
+ self.is_live_location = self.live_location is not None
226
+ self.is_contact = self.contact_message is not None
227
+ self.has_any_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
228
+ self.edited_text = self.raw_data.get("edited_text") if self.is_edited else None
229
+ self.info = {attr: value for attr, value in vars(self).items()}
216
230
  @property
217
231
  def session(self):
218
232
  if self.chat_id not in self.bot.sessions:
@@ -190,8 +190,10 @@ from pathlib import Path
190
190
  class Message:
191
191
  def __init__(self, bot, chat_id, message_id, sender_id, text=None, raw_data=None):
192
192
  self.bot = bot
193
- self.chat_id = chat_id
194
193
  self.raw_data = raw_data or {}
194
+ self.chat_id = chat_id
195
+ self.object_guid = chat_id
196
+ self.author_guid = self.raw_data.get("sender_id", sender_id)
195
197
  self.message_id: str = self.raw_data.get("message_id", message_id)
196
198
  self.text: str = self.raw_data.get("text", text)
197
199
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
@@ -199,6 +201,7 @@ class Message:
199
201
  self.is_edited: bool = self.raw_data.get("is_edited", False)
200
202
  self.sender_type: str = self.raw_data.get("sender_type")
201
203
  self.args = []
204
+ self.is_command = bool(self.text and self.text.startswith("/"))
202
205
  self.is_user = self.chat_id.startswith("b")
203
206
  self.is_private = self.chat_id.startswith("b")
204
207
  self.is_group = self.chat_id.startswith("g")
@@ -212,7 +215,18 @@ class Message:
212
215
  self.location = Location(self.raw_data["location"]) if "location" in self.raw_data else None
213
216
  self.live_location = LiveLocation(self.raw_data["live_location"]) if "live_location" in self.raw_data else None
214
217
  self.aux_data = AuxData(self.raw_data["aux_data"]) if "aux_data" in self.raw_data else None
215
-
218
+ self.is_reply = self.reply_to_message_id is not None
219
+ self.has_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
220
+ self.is_forwarded = self.forwarded_from is not None
221
+ self.is_text = bool(self.text and not self.has_media)
222
+ self.is_media = self.has_media
223
+ self.is_poll = self.poll is not None
224
+ self.is_location = self.location is not None
225
+ self.is_live_location = self.live_location is not None
226
+ self.is_contact = self.contact_message is not None
227
+ self.has_any_media = any([self.file, self.sticker, self.poll, self.location, self.live_location])
228
+ self.edited_text = self.raw_data.get("edited_text") if self.is_edited else None
229
+ self.info = {attr: value for attr, value in vars(self).items()}
216
230
  @property
217
231
  def session(self):
218
232
  if self.chat_id not in self.bot.sessions:
@@ -8,7 +8,7 @@ except FileNotFoundError:
8
8
 
9
9
  setup(
10
10
  name='Rubka',
11
- version='6.4.4',
11
+ version='6.4.8',
12
12
  description='A Python library for interacting with Rubika Bot API.',
13
13
  long_description=long_description,
14
14
  long_description_content_type='text/markdown',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes