Rubka 7.1.9__py3-none-any.whl → 7.1.10__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/context.py CHANGED
@@ -291,6 +291,36 @@ class Message:
291
291
  self.is_archive = None
292
292
  self.is_executable = None
293
293
  self.is_font = None
294
+ self.metadata = self.raw_data.get("metadata", {})
295
+ self.is_metadata = self.metadata
296
+ self.meta_parts = self.metadata.get("meta_data_parts", [])
297
+ self.has_metadata = bool(self.meta_parts)
298
+ self.meta_types = [part.get("type") for part in self.meta_parts] if self.has_metadata else []
299
+ self.is_bold = "Bold" in self.meta_types
300
+ self.is_italic = "Italic" in self.meta_types
301
+ self.is_strike = "Strike" in self.meta_types
302
+ self.is_underline = "Underline" in self.meta_types
303
+ self.is_quote = "Quote" in self.meta_types
304
+ self.is_spoiler = "Spoiler" in self.meta_types
305
+ self.is_pre = "Pre" in self.meta_types
306
+ self.is_mono = "Mono" in self.meta_types
307
+ self.is_link_meta = "Link" in self.meta_types
308
+ self.meta_links = [part.get("link_url") for part in self.meta_parts if part.get("type") == "Link"]
309
+ self.meta_link_positions = [
310
+ {"from": part.get("from_index"), "length": part.get("length"), "url": part.get("link_url")}
311
+ for part in self.meta_parts if part.get("type") == "Link"
312
+ ]
313
+ self.has_link = contains_link_or_mention(self.text) or self.is_link_meta
314
+ self.is_formatted = any([
315
+ self.is_bold,
316
+ self.is_italic,
317
+ self.is_strike,
318
+ self.is_underline,
319
+ self.is_quote,
320
+ self.is_spoiler,
321
+ self.is_pre,
322
+ self.is_mono
323
+ ])
294
324
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
295
325
  self.time: str = self.raw_data.get("time")
296
326
  self.is_edited: bool = self.raw_data.get("is_edited", False)
rubka/update.py CHANGED
@@ -1,8 +1,68 @@
1
- from typing import Any, Dict, List,Optional
2
1
  from typing import Union
3
2
  from pathlib import Path
3
+ from typing import List, Optional, Dict, Any, Literal, Callable, Union,Set
4
4
  import re,inspect
5
5
  import asyncio
6
+ _EMOJI_PATTERN = re.compile(
7
+ "["
8
+ "\U0001F300-\U0001F5FF"
9
+ "\U0001F600-\U0001F64F"
10
+ "\U0001F680-\U0001F6FF"
11
+ "\U0001F700-\U0001F77F"
12
+ "\U0001F780-\U0001F7FF"
13
+ "\U0001F800-\U0001F8FF"
14
+ "\U0001F900-\U0001F9FF"
15
+ "\U0001FA00-\U0001FA6F"
16
+ "\U0001FA70-\U0001FAFF"
17
+ "\U00002700-\U000027BF"
18
+ "\U00002600-\U000026FF"
19
+ "\U00002000-\U000023FF"
20
+ "]+",
21
+ flags=re.UNICODE
22
+ )
23
+
24
+ def contains_emoji(text: str) -> bool:
25
+ if not text:return False
26
+ return bool(_EMOJI_PATTERN.search(text))
27
+
28
+ def count_emojis(text: str) -> int:
29
+ if not text:return 0
30
+ return len(_EMOJI_PATTERN.findall(text))
31
+ def contains_link_or_mention(text: str) -> bool:
32
+ if not text:return False
33
+ pattern = re.compile(
34
+ r"(?i)\b("
35
+ r"https?://[^\s]+"
36
+ r"|www\.[^\s]+"
37
+ r"|[a-zA-Z0-9.-]+\.(com|net|org|ir|edu|gov|info|biz|io|me|co|xyz|in|us|uk|ru|tv|cc|app|dev|site|store|cloud|shop)"
38
+ r"|t\.me/[^\s]+"
39
+ r"|telegram\.me/[^\s]+"
40
+ r"|rubika\.(ir|app)/[^\s]+"
41
+ r"|whatsapp\.com/[^\s]+"
42
+ r"|wa\.me/[^\s]+"
43
+ r"|instagram\.com/[^\s]+"
44
+ r"|instagr\.am/[^\s]+"
45
+ r"|youtube\.com/[^\s]+"
46
+ r"|youtu\.be/[^\s]+"
47
+ r"|aparat\.com/[^\s]+"
48
+ r"|discord\.gg/[^\s]+"
49
+ r"|discord\.com/[^\s]+"
50
+ r"|splus\.ir/[^\s]+"
51
+ r"|eitaa\.com/[^\s]+"
52
+ r"|ble\.ir/[^\s]+"
53
+ r"|gap\.im/[^\s]+"
54
+ r"|rubino\.ir/[^\s]+"
55
+ r"|pin\.it/[^\s]+"
56
+ r"|twitter\.com/[^\s]+"
57
+ r"|x\.com/[^\s]+"
58
+ r"|facebook\.com/[^\s]+"
59
+ r"|threads\.net/[^\s]+"
60
+ r"|@\w+"
61
+ r"|\b\d{1,3}(?:\.\d{1,3}){3}\b"
62
+ r"|\[?[A-F0-9:]{2,39}\]?"
63
+ r")\b"
64
+ )
65
+ return bool(pattern.search(text))
6
66
  class File:
7
67
  def __init__(self, data: dict):
8
68
  self.file_id: str = data.get("file_id")
@@ -215,10 +275,51 @@ class Message:
215
275
  self.author_guid = self.raw_data.get("sender_id", sender_id)
216
276
  self.message_id: str = self.raw_data.get("message_id", message_id)
217
277
  self.text: str = self.raw_data.get("text", text)
218
- self.has_link = bool(re.search(
219
- r"(https?://[^\s]+|www\.[^\s]+|[a-zA-Z0-9.-]+\.(com|net|org|ir|edu|gov|info|biz|io|me|co)|t\.me/[^\s]+|telegram\.me/[^\s]+|@\w+|\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)",
220
- self.raw_data.get("text", text) or ""
221
- ))
278
+ self.has_link = contains_link_or_mention(self.raw_data.get("text", text))
279
+ self.is_link = self.has_link
280
+ self.is_mention = bool(re.search(r"@\w+", self.raw_data.get("text", text) or ""))
281
+ self.is_hashtag = bool(re.search(r"#\w+", text or ""))
282
+ self.is_number = bool(re.search(r"\d+", text or ""))
283
+ self.is_emoji = contains_emoji(self.raw_data.get("text", text))
284
+ self.emoji_count = count_emojis(self.raw_data.get("text", text))
285
+ self.is_pure_emoji = bool(self.is_emoji and self.emoji_count == len(self.text))
286
+ self.is_photo = None
287
+ self.is_video = None
288
+ self.is_audio = None
289
+ self.is_voice = None
290
+ self.is_document = None
291
+ self.is_archive = None
292
+ self.is_executable = None
293
+ self.is_font = None
294
+ self.metadata = self.raw_data.get("metadata", {})
295
+ self.meta_parts = self.metadata.get("meta_data_parts", [])
296
+ self.has_metadata = bool(self.meta_parts)
297
+ self.meta_types = [part.get("type") for part in self.meta_parts] if self.has_metadata else []
298
+ self.is_bold = "Bold" in self.meta_types
299
+ self.is_italic = "Italic" in self.meta_types
300
+ self.is_strike = "Strike" in self.meta_types
301
+ self.is_underline = "Underline" in self.meta_types
302
+ self.is_quote = "Quote" in self.meta_types
303
+ self.is_spoiler = "Spoiler" in self.meta_types
304
+ self.is_pre = "Pre" in self.meta_types
305
+ self.is_mono = "Mono" in self.meta_types
306
+ self.is_link_meta = "Link" in self.meta_types
307
+ self.meta_links = [part.get("link_url") for part in self.meta_parts if part.get("type") == "Link"]
308
+ self.meta_link_positions = [
309
+ {"from": part.get("from_index"), "length": part.get("length"), "url": part.get("link_url")}
310
+ for part in self.meta_parts if part.get("type") == "Link"
311
+ ]
312
+ self.has_link = contains_link_or_mention(self.text) or self.is_link_meta
313
+ self.is_formatted = any([
314
+ self.is_bold,
315
+ self.is_italic,
316
+ self.is_strike,
317
+ self.is_underline,
318
+ self.is_quote,
319
+ self.is_spoiler,
320
+ self.is_pre,
321
+ self.is_mono
322
+ ])
222
323
  self.sender_id: str = self.raw_data.get("sender_id", sender_id)
223
324
  self.time: str = self.raw_data.get("time")
224
325
  self.is_edited: bool = self.raw_data.get("is_edited", False)
@@ -336,14 +437,40 @@ class Message:
336
437
  )
337
438
 
338
439
 
339
- def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
340
- return self.bot._post("sendPoll", {
440
+ def reply_poll(
441
+ self,
442
+ question: str,
443
+ options: List[str],
444
+ type: Literal['Regular', 'Quiz'] = "Regular",
445
+ allows_multiple_answers: bool = False,
446
+ is_anonymous: bool = True,
447
+ correct_option_index: Optional[int] = None,
448
+ hint: Optional[str] = None,
449
+ reply_to_message_id: Optional[str] = None,
450
+ disable_notification: bool = False,
451
+ show_results: bool = False,
452
+ inline_keypad: Optional[Dict[str, Any]] = None,
453
+ chat_keypad: Optional[Dict[str, Any]] = None,
454
+ chat_keypad_type: Optional[Literal['New', 'Remove', 'None']] = None,
455
+ **kwargs
456
+ ) -> dict:
457
+ payload = {
341
458
  "chat_id": self.chat_id,
342
459
  "question": question,
343
460
  "options": options,
344
- "reply_to_message_id": self.message_id,
345
- **kwargs
346
- })
461
+ "type": type,
462
+ "allows_multiple_answers": allows_multiple_answers,
463
+ "is_anonymous": is_anonymous,
464
+ "correct_option_index": correct_option_index,
465
+ "hint": hint,
466
+ "reply_to_message_id": self.message_id if not reply_to_message_id else reply_to_message_id,
467
+ "disable_notification": disable_notification,
468
+ "show_results": show_results,
469
+ "inline_keypad": inline_keypad,
470
+ "chat_keypad": chat_keypad,
471
+ "chat_keypad_type": chat_keypad_type,
472
+ }
473
+ return self.bot._post("sendPoll", {key: value for key, value in payload.items() if value is not None}, **kwargs)
347
474
 
348
475
 
349
476
  def reply_document(
@@ -617,13 +744,69 @@ class InlineMessage:
617
744
  if self.chat_id not in self.bot.sessions:
618
745
  self.bot.sessions[self.chat_id] = {}
619
746
  return self.bot.sessions[self.chat_id]
620
- def reply(self, text: str, **kwargs):
621
- return self.bot.send_message(
622
- self.chat_id,
623
- text,
624
- reply_to_message_id=self.message_id,
625
- **kwargs
626
- )
747
+
748
+ def reply(self, text: str, delete_after: int = None, **kwargs):
749
+ async def _reply_async():
750
+ send_func = self.bot.send_message
751
+ if inspect.iscoroutinefunction(send_func):
752
+ msg = await send_func(
753
+ self.chat_id,
754
+ text,
755
+ reply_to_message_id=self.message_id,
756
+ delete_after=delete_after,
757
+ **kwargs
758
+ )
759
+ else:
760
+ msg = send_func(
761
+ self.chat_id,
762
+ text,
763
+ reply_to_message_id=self.message_id,
764
+ delete_after=delete_after,
765
+ **kwargs
766
+ )
767
+ class Pick:
768
+ def __init__(self, bot, chat_id, message_id):
769
+ self.bot = bot
770
+ self.chat_id = chat_id
771
+ self.message_id = message_id
772
+
773
+ def edit(self, new_text):
774
+ async def _edit():
775
+ func = self.bot.edit_message_text
776
+ if inspect.iscoroutinefunction(func):
777
+ await func(self.chat_id, self.message_id, new_text)
778
+ else:
779
+ func(self.chat_id, self.message_id, new_text)
780
+
781
+ try:
782
+ loop = asyncio.get_running_loop()
783
+ if loop.is_running():
784
+ return asyncio.create_task(_edit())
785
+ except RuntimeError:
786
+ return asyncio.run(_edit())
787
+
788
+ def delete(self):
789
+ async def _delete():
790
+ func = self.bot.delete_message
791
+ if inspect.iscoroutinefunction(func):
792
+ await func(self.chat_id, self.message_id)
793
+ else:
794
+ func(self.chat_id, self.message_id)
795
+ try:
796
+ loop = asyncio.get_running_loop()
797
+ if loop.is_running():
798
+ return asyncio.create_task(_delete())
799
+ except RuntimeError:
800
+ return asyncio.run(_delete())
801
+ chat_id = msg.get("chat_id") if isinstance(msg, dict) else getattr(msg, "chat_id", self.chat_id)
802
+ message_id = msg.get("message_id") if isinstance(msg, dict) else getattr(msg, "message_id", self.message_id)
803
+ return Pick(self.bot, chat_id, message_id)
804
+ try:
805
+ loop = asyncio.get_running_loop()
806
+ if loop.is_running():
807
+ return asyncio.create_task(_reply_async())
808
+ except RuntimeError:
809
+ return asyncio.run(_reply_async())
627
810
  def answer(self, text: str, **kwargs):
628
811
  return self.bot.send_message(
629
812
  self.chat_id,
@@ -631,6 +814,7 @@ class InlineMessage:
631
814
  reply_to_message_id=self.message_id,
632
815
  **kwargs
633
816
  )
817
+
634
818
 
635
819
  def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
636
820
  return self.bot._post("sendPoll", {
@@ -841,4 +1025,12 @@ class InlineMessage:
841
1025
  return self.bot.delete_message(
842
1026
  chat_id=self.chat_id,
843
1027
  message_id=self.message_id
844
- )
1028
+ )
1029
+ @hybrid_property
1030
+ async def author_name(self):return await self.bot.get_name(self.chat_id)
1031
+ @hybrid_property
1032
+ async def name(self):return await self.bot.get_name(self.chat_id)
1033
+ @hybrid_property
1034
+ async def username(self):return await self.bot.get_username(self.chat_id)
1035
+ @hybrid_property
1036
+ async def author_info(self):return await self.bot.get_chat(self.chat_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 7.1.9
3
+ Version: 7.1.10
4
4
  Summary: Rubika: A Python library for interacting with the Rubika Bot API. This library provides an easy-to-use interface to send messages, polls, stickers, media files, manage groups and channels, handle inline keyboards, and implement advanced bot features like subscription management, user authentication, and message handling. Ideal for developers looking to automate and extend their Rubika bots with Python.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
6
  Download-URL: https://github.com/Mahdy-Ahmadi/rubka/archive/refs/tags/v6.6.4.zip
@@ -3,7 +3,7 @@ rubka/api.py,sha256=wa1gQj7NDc7QEbmNNRz-TIOdVqfMWFC362tndRKdqig,68449
3
3
  rubka/asynco.py,sha256=uuqhWLPrhPVIlb4l7umwryYFD_x2tDFfcYxo-Yb49Uc,112784
4
4
  rubka/button.py,sha256=woSzZVd5MtTqOrP-YgkH5b0GS9y4DuKBsFSc9-KuLnk,13320
5
5
  rubka/config.py,sha256=Bck59xkOiqioLv0GkQ1qPGnBXVctz1hKk6LT4h2EPx0,78
6
- rubka/context.py,sha256=VSbIpVVgfl6f2JXl4jgQsc5rMgd3u5t816ogKDWagq4,40898
6
+ rubka/context.py,sha256=mYyYybwe_vqhp5ublxByYTmFUbsemXWiK3huRBO8V98,42389
7
7
  rubka/decorators.py,sha256=hGwUoE4q2ImrunJIGJ_kzGYYxQf1ueE0isadqraKEts,1157
8
8
  rubka/exceptions.py,sha256=DDOGIHEMoliHNW5E7C_s38WZgqqMBv9812fcJGvj7TY,1173
9
9
  rubka/filters.py,sha256=DY1bdkpRKIiLtVcy6X3hOnlGPcVOK4HFb3QgmaPx6Oo,12116
@@ -14,7 +14,7 @@ rubka/keypad.py,sha256=yGsNt8W5HtUFBzVF1m_p7GySlu1hwIcSvXZ4BTdrlvg,9558
14
14
  rubka/logger.py,sha256=J2I6NiK1z32lrAzC4H1Et6WPMBXxXGCVUsW4jgcAofs,289
15
15
  rubka/rubino.py,sha256=HOILsm2zOIRe9EW1hxhLinhjwE_TvFrOAxBsg9T_L5E,61701
16
16
  rubka/tv.py,sha256=rBoyCadCH3I3YqQGrQYv_dLtTg1I63AzVf1orn-hbko,5724
17
- rubka/update.py,sha256=-oC9h7U_H3CrtqUCDCnFXAvC7zdSmwxlc0CNwL1XLxM,34314
17
+ rubka/update.py,sha256=ky4FvHMAnAX6VAZCQ1xsjwlvCVTHubmaoWsTOrIefEM,42347
18
18
  rubka/utils.py,sha256=XUQUZxQt9J2f0X5hmAH_MH1kibTAfdT1T4AaBkBhBBs,148
19
19
  rubka/adaptorrubka/__init__.py,sha256=6o2tCXnVeES7nx-LjnzsuMqjKcWIm9qwKficLE54s-U,83
20
20
  rubka/adaptorrubka/enums.py,sha256=cyiakExmZi-QQpYuf_A93HQvfZVmyG_0uVuvTTNT5To,1053
@@ -37,8 +37,8 @@ rubka/adaptorrubka/types/socket/message.py,sha256=0WgLMZh4eow8Zn7AiSX4C3GZjQTkIg
37
37
  rubka/adaptorrubka/utils/__init__.py,sha256=OgCFkXdNFh379quNwIVOAWY2NP5cIOxU5gDRRALTk4o,54
38
38
  rubka/adaptorrubka/utils/configs.py,sha256=nMUEOJh1NqDJsf9W9PurkN_DLYjO6kKPMm923i4Jj_A,492
39
39
  rubka/adaptorrubka/utils/utils.py,sha256=5-LioLNYX_TIbQGDeT50j7Sg9nAWH2LJUUs-iEXpsUY,8816
40
- rubka-7.1.9.dist-info/METADATA,sha256=Vgu6f_KuvBIdcMXOb_rqloVJd-XEYSJ_rxVW0whgDCw,34644
41
- rubka-7.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
- rubka-7.1.9.dist-info/entry_points.txt,sha256=4aESuUmuUOALMUy7Kucv_Gb5YlqhsJmTmdXLlZU9sJ0,46
43
- rubka-7.1.9.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
44
- rubka-7.1.9.dist-info/RECORD,,
40
+ rubka-7.1.10.dist-info/METADATA,sha256=iNaufLcqGhGb6uLiUsOD4dGUQUtd9VusDZgJDLjK8BI,34645
41
+ rubka-7.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
+ rubka-7.1.10.dist-info/entry_points.txt,sha256=4aESuUmuUOALMUy7Kucv_Gb5YlqhsJmTmdXLlZU9sJ0,46
43
+ rubka-7.1.10.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
44
+ rubka-7.1.10.dist-info/RECORD,,
File without changes