Rubka 7.1.8__tar.gz → 7.1.10__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.
- {rubka-7.1.8 → rubka-7.1.10}/PKG-INFO +1 -2
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/PKG-INFO +1 -2
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/requires.txt +0 -1
- rubka-7.1.8/rubka/update.py → rubka-7.1.10/rubka/context.py +211 -18
- rubka-7.1.8/rubka/context.py → rubka-7.1.10/rubka/update.py +105 -4
- {rubka-7.1.8 → rubka-7.1.10}/setup.py +1 -2
- {rubka-7.1.8 → rubka-7.1.10}/README.md +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/SOURCES.txt +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/dependency_links.txt +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/entry_points.txt +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/not-zip-safe +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/Rubka.egg-info/top_level.txt +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/client/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/client/client.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/crypto/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/crypto/crypto.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/enums.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/exceptions.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/methods/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/methods/methods.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/network/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/network/helper.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/network/network.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/network/socket.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/sessions/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/sessions/sessions.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/types/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/types/socket/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/types/socket/message.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/utils/__init__.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/utils/configs.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/adaptorrubka/utils/utils.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/api.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/asynco.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/button.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/config.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/decorators.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/exceptions.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/filters.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/helpers.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/jobs.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/keyboards.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/keypad.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/logger.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/rubino.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/tv.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/rubka/utils.py +0 -0
- {rubka-7.1.8 → rubka-7.1.10}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Rubka
|
|
3
|
-
Version: 7.1.
|
|
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
|
|
@@ -27,7 +27,6 @@ Classifier: Natural Language :: Persian
|
|
|
27
27
|
Requires-Python: >=3.6
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
Requires-Dist: requests
|
|
30
|
-
Requires-Dist: Pillow
|
|
31
30
|
Requires-Dist: websocket-client
|
|
32
31
|
Requires-Dist: pycryptodome
|
|
33
32
|
Requires-Dist: aiohttp
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Rubka
|
|
3
|
-
Version: 7.1.
|
|
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
|
|
@@ -27,7 +27,6 @@ Classifier: Natural Language :: Persian
|
|
|
27
27
|
Requires-Python: >=3.6
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
Requires-Dist: requests
|
|
30
|
-
Requires-Dist: Pillow
|
|
31
30
|
Requires-Dist: websocket-client
|
|
32
31
|
Requires-Dist: pycryptodome
|
|
33
32
|
Requires-Dist: aiohttp
|
|
@@ -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,52 @@ 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 =
|
|
219
|
-
|
|
220
|
-
|
|
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.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
|
+
])
|
|
222
324
|
self.sender_id: str = self.raw_data.get("sender_id", sender_id)
|
|
223
325
|
self.time: str = self.raw_data.get("time")
|
|
224
326
|
self.is_edited: bool = self.raw_data.get("is_edited", False)
|
|
@@ -336,14 +438,40 @@ class Message:
|
|
|
336
438
|
)
|
|
337
439
|
|
|
338
440
|
|
|
339
|
-
def reply_poll(
|
|
340
|
-
|
|
441
|
+
def reply_poll(
|
|
442
|
+
self,
|
|
443
|
+
question: str,
|
|
444
|
+
options: List[str],
|
|
445
|
+
type: Literal['Regular', 'Quiz'] = "Regular",
|
|
446
|
+
allows_multiple_answers: bool = False,
|
|
447
|
+
is_anonymous: bool = True,
|
|
448
|
+
correct_option_index: Optional[int] = None,
|
|
449
|
+
hint: Optional[str] = None,
|
|
450
|
+
reply_to_message_id: Optional[str] = None,
|
|
451
|
+
disable_notification: bool = False,
|
|
452
|
+
show_results: bool = False,
|
|
453
|
+
inline_keypad: Optional[Dict[str, Any]] = None,
|
|
454
|
+
chat_keypad: Optional[Dict[str, Any]] = None,
|
|
455
|
+
chat_keypad_type: Optional[Literal['New', 'Remove', 'None']] = None,
|
|
456
|
+
**kwargs
|
|
457
|
+
) -> dict:
|
|
458
|
+
payload = {
|
|
341
459
|
"chat_id": self.chat_id,
|
|
342
460
|
"question": question,
|
|
343
461
|
"options": options,
|
|
344
|
-
"
|
|
345
|
-
|
|
346
|
-
|
|
462
|
+
"type": type,
|
|
463
|
+
"allows_multiple_answers": allows_multiple_answers,
|
|
464
|
+
"is_anonymous": is_anonymous,
|
|
465
|
+
"correct_option_index": correct_option_index,
|
|
466
|
+
"hint": hint,
|
|
467
|
+
"reply_to_message_id": self.message_id if not reply_to_message_id else reply_to_message_id,
|
|
468
|
+
"disable_notification": disable_notification,
|
|
469
|
+
"show_results": show_results,
|
|
470
|
+
"inline_keypad": inline_keypad,
|
|
471
|
+
"chat_keypad": chat_keypad,
|
|
472
|
+
"chat_keypad_type": chat_keypad_type,
|
|
473
|
+
}
|
|
474
|
+
return self.bot._post("sendPoll", {key: value for key, value in payload.items() if value is not None}, **kwargs)
|
|
347
475
|
|
|
348
476
|
|
|
349
477
|
def reply_document(
|
|
@@ -617,13 +745,69 @@ class InlineMessage:
|
|
|
617
745
|
if self.chat_id not in self.bot.sessions:
|
|
618
746
|
self.bot.sessions[self.chat_id] = {}
|
|
619
747
|
return self.bot.sessions[self.chat_id]
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
748
|
+
|
|
749
|
+
def reply(self, text: str, delete_after: int = None, **kwargs):
|
|
750
|
+
async def _reply_async():
|
|
751
|
+
send_func = self.bot.send_message
|
|
752
|
+
if inspect.iscoroutinefunction(send_func):
|
|
753
|
+
msg = await send_func(
|
|
754
|
+
self.chat_id,
|
|
755
|
+
text,
|
|
756
|
+
reply_to_message_id=self.message_id,
|
|
757
|
+
delete_after=delete_after,
|
|
758
|
+
**kwargs
|
|
759
|
+
)
|
|
760
|
+
else:
|
|
761
|
+
msg = send_func(
|
|
762
|
+
self.chat_id,
|
|
763
|
+
text,
|
|
764
|
+
reply_to_message_id=self.message_id,
|
|
765
|
+
delete_after=delete_after,
|
|
766
|
+
**kwargs
|
|
767
|
+
)
|
|
768
|
+
class Pick:
|
|
769
|
+
def __init__(self, bot, chat_id, message_id):
|
|
770
|
+
self.bot = bot
|
|
771
|
+
self.chat_id = chat_id
|
|
772
|
+
self.message_id = message_id
|
|
773
|
+
|
|
774
|
+
def edit(self, new_text):
|
|
775
|
+
async def _edit():
|
|
776
|
+
func = self.bot.edit_message_text
|
|
777
|
+
if inspect.iscoroutinefunction(func):
|
|
778
|
+
await func(self.chat_id, self.message_id, new_text)
|
|
779
|
+
else:
|
|
780
|
+
func(self.chat_id, self.message_id, new_text)
|
|
781
|
+
|
|
782
|
+
try:
|
|
783
|
+
loop = asyncio.get_running_loop()
|
|
784
|
+
if loop.is_running():
|
|
785
|
+
return asyncio.create_task(_edit())
|
|
786
|
+
except RuntimeError:
|
|
787
|
+
return asyncio.run(_edit())
|
|
788
|
+
|
|
789
|
+
def delete(self):
|
|
790
|
+
async def _delete():
|
|
791
|
+
func = self.bot.delete_message
|
|
792
|
+
if inspect.iscoroutinefunction(func):
|
|
793
|
+
await func(self.chat_id, self.message_id)
|
|
794
|
+
else:
|
|
795
|
+
func(self.chat_id, self.message_id)
|
|
796
|
+
try:
|
|
797
|
+
loop = asyncio.get_running_loop()
|
|
798
|
+
if loop.is_running():
|
|
799
|
+
return asyncio.create_task(_delete())
|
|
800
|
+
except RuntimeError:
|
|
801
|
+
return asyncio.run(_delete())
|
|
802
|
+
chat_id = msg.get("chat_id") if isinstance(msg, dict) else getattr(msg, "chat_id", self.chat_id)
|
|
803
|
+
message_id = msg.get("message_id") if isinstance(msg, dict) else getattr(msg, "message_id", self.message_id)
|
|
804
|
+
return Pick(self.bot, chat_id, message_id)
|
|
805
|
+
try:
|
|
806
|
+
loop = asyncio.get_running_loop()
|
|
807
|
+
if loop.is_running():
|
|
808
|
+
return asyncio.create_task(_reply_async())
|
|
809
|
+
except RuntimeError:
|
|
810
|
+
return asyncio.run(_reply_async())
|
|
627
811
|
def answer(self, text: str, **kwargs):
|
|
628
812
|
return self.bot.send_message(
|
|
629
813
|
self.chat_id,
|
|
@@ -631,6 +815,7 @@ class InlineMessage:
|
|
|
631
815
|
reply_to_message_id=self.message_id,
|
|
632
816
|
**kwargs
|
|
633
817
|
)
|
|
818
|
+
|
|
634
819
|
|
|
635
820
|
def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
|
|
636
821
|
return self.bot._post("sendPoll", {
|
|
@@ -841,4 +1026,12 @@ class InlineMessage:
|
|
|
841
1026
|
return self.bot.delete_message(
|
|
842
1027
|
chat_id=self.chat_id,
|
|
843
1028
|
message_id=self.message_id
|
|
844
|
-
)
|
|
1029
|
+
)
|
|
1030
|
+
@hybrid_property
|
|
1031
|
+
async def author_name(self):return await self.bot.get_name(self.chat_id)
|
|
1032
|
+
@hybrid_property
|
|
1033
|
+
async def name(self):return await self.bot.get_name(self.chat_id)
|
|
1034
|
+
@hybrid_property
|
|
1035
|
+
async def username(self):return await self.bot.get_username(self.chat_id)
|
|
1036
|
+
@hybrid_property
|
|
1037
|
+
async def author_info(self):return await self.bot.get_chat(self.chat_id)
|
|
@@ -3,6 +3,66 @@ from pathlib import Path
|
|
|
3
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 =
|
|
219
|
-
|
|
220
|
-
|
|
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)
|
|
@@ -13,7 +13,7 @@ with open("README.md", "r", encoding="utf-8") as f:
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name='Rubka',
|
|
16
|
-
version='7.1.
|
|
16
|
+
version='7.1.10',
|
|
17
17
|
description=(
|
|
18
18
|
"Rubika: A Python library for interacting with the Rubika Bot API. "
|
|
19
19
|
"This library provides an easy-to-use interface to send messages, polls, "
|
|
@@ -49,7 +49,6 @@ setup(
|
|
|
49
49
|
python_requires='>=3.6',
|
|
50
50
|
install_requires=[
|
|
51
51
|
"requests",
|
|
52
|
-
"Pillow",
|
|
53
52
|
"websocket-client",
|
|
54
53
|
'pycryptodome',
|
|
55
54
|
'aiohttp',
|
|
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
|
|
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
|
|
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
|