Rubka 6.4.6__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.6 → rubka-6.4.8}/PKG-INFO +1 -1
  2. {rubka-6.4.6 → rubka-6.4.8}/Rubka.egg-info/PKG-INFO +1 -1
  3. {rubka-6.4.6 → rubka-6.4.8}/rubka/api.py +162 -0
  4. {rubka-6.4.6 → rubka-6.4.8}/rubka/asynco.py +153 -19
  5. {rubka-6.4.6 → rubka-6.4.8}/rubka/context.py +16 -2
  6. {rubka-6.4.6 → rubka-6.4.8}/rubka/update.py +16 -2
  7. {rubka-6.4.6 → rubka-6.4.8}/setup.py +1 -1
  8. {rubka-6.4.6 → rubka-6.4.8}/README.md +0 -0
  9. {rubka-6.4.6 → rubka-6.4.8}/Rubka.egg-info/SOURCES.txt +0 -0
  10. {rubka-6.4.6 → rubka-6.4.8}/Rubka.egg-info/dependency_links.txt +0 -0
  11. {rubka-6.4.6 → rubka-6.4.8}/Rubka.egg-info/requires.txt +0 -0
  12. {rubka-6.4.6 → rubka-6.4.8}/Rubka.egg-info/top_level.txt +0 -0
  13. {rubka-6.4.6 → rubka-6.4.8}/rubka/__init__.py +0 -0
  14. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/__init__.py +0 -0
  15. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/client/__init__.py +0 -0
  16. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/client/client.py +0 -0
  17. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/crypto/__init__.py +0 -0
  18. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/crypto/crypto.py +0 -0
  19. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/enums.py +0 -0
  20. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/exceptions.py +0 -0
  21. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/methods/__init__.py +0 -0
  22. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/methods/methods.py +0 -0
  23. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/network/__init__.py +0 -0
  24. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/network/helper.py +0 -0
  25. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/network/network.py +0 -0
  26. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/network/socket.py +0 -0
  27. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/sessions/__init__.py +0 -0
  28. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/sessions/sessions.py +0 -0
  29. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/types/__init__.py +0 -0
  30. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/types/socket/__init__.py +0 -0
  31. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/types/socket/message.py +0 -0
  32. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/utils/__init__.py +0 -0
  33. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/utils/configs.py +0 -0
  34. {rubka-6.4.6 → rubka-6.4.8}/rubka/adaptorrubka/utils/utils.py +0 -0
  35. {rubka-6.4.6 → rubka-6.4.8}/rubka/button.py +0 -0
  36. {rubka-6.4.6 → rubka-6.4.8}/rubka/config.py +0 -0
  37. {rubka-6.4.6 → rubka-6.4.8}/rubka/decorators.py +0 -0
  38. {rubka-6.4.6 → rubka-6.4.8}/rubka/exceptions.py +0 -0
  39. {rubka-6.4.6 → rubka-6.4.8}/rubka/jobs.py +0 -0
  40. {rubka-6.4.6 → rubka-6.4.8}/rubka/keyboards.py +0 -0
  41. {rubka-6.4.6 → rubka-6.4.8}/rubka/keypad.py +0 -0
  42. {rubka-6.4.6 → rubka-6.4.8}/rubka/logger.py +0 -0
  43. {rubka-6.4.6 → rubka-6.4.8}/rubka/rubino.py +0 -0
  44. {rubka-6.4.6 → rubka-6.4.8}/rubka/utils.py +0 -0
  45. {rubka-6.4.6 → 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.6
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.6
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({
@@ -218,6 +218,9 @@ class Robot:
218
218
  raise InvalidTokenError("The provided bot token is invalid or expired.")
219
219
  from typing import Callable, Any, Optional, List
220
220
 
221
+
222
+ #decorator#
223
+
221
224
  def on_message_private(
222
225
  self,
223
226
  chat_id: Optional[Union[str, List[str]]] = None,
@@ -247,30 +250,20 @@ class Robot:
247
250
 
248
251
  if not message.is_private:
249
252
  return
250
-
251
-
252
253
  if chat_id:
253
254
  if isinstance(chat_id, str) and message.chat_id != chat_id:
254
255
  return
255
256
  if isinstance(chat_id, list) and message.chat_id not in chat_id:
256
257
  return
257
-
258
-
259
258
  if sender_id:
260
259
  if isinstance(sender_id, str) and message.sender_id != sender_id:
261
260
  return
262
261
  if isinstance(sender_id, list) and message.sender_id not in sender_id:
263
262
  return
264
-
265
-
266
263
  if sender_type and message.sender_type != sender_type:
267
264
  return
268
-
269
-
270
265
  if not allow_forwarded and message.forwarded_from:
271
266
  return
272
-
273
-
274
267
  if not allow_files and message.file:
275
268
  return
276
269
  if not allow_stickers and message.sticker:
@@ -543,6 +536,142 @@ class Robot:
543
536
  })
544
537
  return func
545
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
546
675
  def on_update(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
547
676
  def decorator(func: Callable[[Any, Message], None]):
548
677
  self._message_handlers.append({
@@ -1516,7 +1645,6 @@ class Robot:
1516
1645
  json_data = await response.json()
1517
1646
  if is_temp_file:
1518
1647
  os.remove(path)
1519
- print(json_data)
1520
1648
  return json_data.get('data', {}).get('file_id')
1521
1649
  except :
1522
1650
  raise FeatureNotAvailableError(f"files is not currently supported by the server.")
@@ -1642,7 +1770,7 @@ class Robot:
1642
1770
  "inline_keypad":inline_keypad,
1643
1771
  "chat_keypad_type":chat_keypad_type
1644
1772
  }
1645
- return json.dumps(result, ensure_ascii=False, indent=4)
1773
+ return result
1646
1774
 
1647
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):
1648
1776
  if path:
@@ -1702,13 +1830,19 @@ class Robot:
1702
1830
  try:
1703
1831
  chat = await self.get_chat(chat_id)
1704
1832
  chat_info = chat.get("data", {}).get("chat", {})
1705
- first_name = chat_info.get("first_name", "")
1706
- last_name = chat_info.get("last_name", "")
1707
-
1708
- full_name = f"{first_name} {last_name}".strip()
1709
- return full_name if full_name else "Unknown"
1710
- except Exception:
1711
- 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
+
1712
1846
 
1713
1847
  async def get_username(self, chat_id: str) -> str:
1714
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.6',
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