Rubka 7.2.8__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.

Potentially problematic release.


This version of Rubka might be problematic. Click here for more details.

Files changed (45) hide show
  1. rubka/__init__.py +79 -0
  2. rubka/adaptorrubka/__init__.py +4 -0
  3. rubka/adaptorrubka/client/__init__.py +1 -0
  4. rubka/adaptorrubka/client/client.py +60 -0
  5. rubka/adaptorrubka/crypto/__init__.py +1 -0
  6. rubka/adaptorrubka/crypto/crypto.py +82 -0
  7. rubka/adaptorrubka/enums.py +36 -0
  8. rubka/adaptorrubka/exceptions.py +22 -0
  9. rubka/adaptorrubka/methods/__init__.py +1 -0
  10. rubka/adaptorrubka/methods/methods.py +90 -0
  11. rubka/adaptorrubka/network/__init__.py +3 -0
  12. rubka/adaptorrubka/network/helper.py +22 -0
  13. rubka/adaptorrubka/network/network.py +221 -0
  14. rubka/adaptorrubka/network/socket.py +31 -0
  15. rubka/adaptorrubka/sessions/__init__.py +1 -0
  16. rubka/adaptorrubka/sessions/sessions.py +72 -0
  17. rubka/adaptorrubka/types/__init__.py +1 -0
  18. rubka/adaptorrubka/types/socket/__init__.py +1 -0
  19. rubka/adaptorrubka/types/socket/message.py +187 -0
  20. rubka/adaptorrubka/utils/__init__.py +2 -0
  21. rubka/adaptorrubka/utils/configs.py +18 -0
  22. rubka/adaptorrubka/utils/utils.py +251 -0
  23. rubka/api.py +1723 -0
  24. rubka/asynco.py +2541 -0
  25. rubka/button.py +404 -0
  26. rubka/config.py +3 -0
  27. rubka/context.py +1077 -0
  28. rubka/decorators.py +30 -0
  29. rubka/exceptions.py +37 -0
  30. rubka/filters.py +330 -0
  31. rubka/helpers.py +1461 -0
  32. rubka/jobs.py +15 -0
  33. rubka/keyboards.py +16 -0
  34. rubka/keypad.py +298 -0
  35. rubka/logger.py +12 -0
  36. rubka/metadata.py +114 -0
  37. rubka/rubino.py +1271 -0
  38. rubka/tv.py +145 -0
  39. rubka/update.py +1038 -0
  40. rubka/utils.py +3 -0
  41. rubka-7.2.8.dist-info/METADATA +1047 -0
  42. rubka-7.2.8.dist-info/RECORD +45 -0
  43. rubka-7.2.8.dist-info/WHEEL +5 -0
  44. rubka-7.2.8.dist-info/entry_points.txt +2 -0
  45. rubka-7.2.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,72 @@
1
+ from os.path import exists
2
+ from json import loads, dumps
3
+
4
+ class Sessions:
5
+
6
+ def __init__(self, client:object) -> None:
7
+ self.client = client
8
+
9
+ def cheackSessionExists(self):
10
+ return exists(f"{self.client.session}.rubka")
11
+
12
+ def loadSessionData(self):
13
+ return loads(open(f"{self.client.session}.rubka", encoding="UTF-8").read())
14
+
15
+ def createSession(self):
16
+ from ..methods import Methods
17
+ methods:object = Methods(
18
+ sessionData={},
19
+ platform=self.client.platform,
20
+ apiVersion=6,
21
+ proxy=self.client.proxy,
22
+ timeOut=self.client.timeOut,
23
+ showProgressBar=True
24
+ )
25
+
26
+ while True:
27
+ phoneNumber:str = input("\nphone number :\t")
28
+ try:
29
+ sendCodeData:dict = methods.sendCode(phoneNumber=phoneNumber)
30
+ except:
31
+ print("The phone number is invalid! Please try again.")
32
+ continue
33
+
34
+ if sendCodeData['status'] == 'SendPassKey':
35
+ while True:
36
+ passKey:str = input(f'\npass key [{sendCodeData["hint_pass_key"]}] : ')
37
+ sendCodeData:dict = methods.sendCode(phoneNumber=phoneNumber, passKey=passKey)
38
+
39
+ if sendCodeData['status'] == 'InvalidPassKey':
40
+ print(f'\nThe pass key({sendCodeData["hint_pass_key"]})try again.')
41
+ continue
42
+ break
43
+
44
+ while True:
45
+ phoneCode:str = input("\ncode : ").strip()
46
+ signInData:dict = methods.signIn(phoneNumber=phoneNumber, phoneCodeHash=sendCodeData['phone_code_hash'], phoneCode=phoneCode)
47
+ if signInData['status'] != 'OK':
48
+ print("The code is invalid! Please try again.")
49
+ continue
50
+ break
51
+
52
+ from ..crypto import Cryption
53
+
54
+ sessionData = {
55
+ 'auth': Cryption.decryptRsaOaep(signInData["private_key"], signInData['auth']),
56
+ 'private_key': signInData["private_key"],
57
+ 'user': signInData['user'],
58
+ }
59
+
60
+ open(f"{self.client.session}.rubka", "w", encoding="UTF-8").write(dumps(sessionData, indent=4))
61
+
62
+ Methods(
63
+ sessionData=sessionData,
64
+ platform=self.client.platform,
65
+ apiVersion=6,
66
+ proxy=self.client.proxy,
67
+ timeOut=self.client.timeOut,
68
+ showProgressBar=True
69
+ ).registerDevice(deviceModel=f"rubka-Api-{self.client.session}")
70
+ print(f"\nSign successful")
71
+
72
+ return sessionData
@@ -0,0 +1 @@
1
+ from .socket import Message
@@ -0,0 +1 @@
1
+ from .message import Message
@@ -0,0 +1,187 @@
1
+ class ReplyInfo:
2
+ def __init__(self, text, author_guid) -> None:
3
+ self.text = text
4
+ self.author_guid = author_guid
5
+ pass
6
+
7
+ @classmethod
8
+ def from_json(cls, json: dict):
9
+ return cls(json["text"], json["author_object_guid"])
10
+
11
+ class Message:
12
+ def __init__(self, data:dict, methods:object) -> None:
13
+ self.data = data
14
+ self.methods = methods
15
+
16
+ @property
17
+ def object_guid(self) -> str:
18
+ return self.data["chat_updates"][0].get("object_guid")
19
+
20
+ @property
21
+ def chat_type(self) -> str:
22
+ return self.data["chat_updates"][0].get("type")
23
+
24
+ @property
25
+ def count_unseen(self) -> int:
26
+ return int(self.data["chat_updates"][0]["chat"].get("count_unseen", 0))
27
+
28
+ @property
29
+ def last_seen_peer_mid(self) -> str:
30
+ return self.data["chat_updates"][0]["chat"].get("last_seen_peer_mid")
31
+
32
+ @property
33
+ def time_string(self) -> str:
34
+ return self.data["chat_updates"][0]["chat"].get("time_string")
35
+
36
+ @property
37
+ def is_mine(self) -> bool:
38
+ return self.data["chat_updates"][0]["chat"]["last_message"].get("is_mine")
39
+
40
+ @property
41
+ def time(self) -> str:
42
+ return self.data["chat_updates"][0]["chat"]["last_message"].get("time")
43
+
44
+ @property
45
+ def status(self) -> str:
46
+ return self.data["chat_updates"][0]["chat"].get("status")
47
+
48
+ @property
49
+ def last_message_id(self) -> str:
50
+ return self.data["chat_updates"][0]["chat"].get("last_message_id")
51
+
52
+ @property
53
+ def action(self) -> str:
54
+ return self.data["message_updates"][0].get("action")
55
+
56
+ @property
57
+ def message_id(self) -> str:
58
+ return self.data["message_updates"][0].get("message_id")
59
+
60
+ @property
61
+ def reply_message_id(self) -> str:
62
+ return self.data["message_updates"][0]["message"].get("reply_to_message_id")
63
+
64
+ @property
65
+ def text(self) -> str:
66
+ return str(self.data["message_updates"][0]["message"].get("text"))
67
+
68
+ @property
69
+ def is_edited(self) -> bool:
70
+ return self.data["message_updates"][0]["message"].get("is_edited")
71
+
72
+ @property
73
+ def message_type(self) -> str:
74
+ if self.file_inline:
75
+ return self.file_inline["type"]
76
+
77
+ return self.data["message_updates"][0]["message"].get("type")
78
+
79
+ @property
80
+ def author_type(self) -> str:
81
+ return self.data["message_updates"][0]["message"].get("author_type")
82
+
83
+ @property
84
+ def author_guid(self) -> str:
85
+ return self.data["message_updates"][0]["message"].get("author_object_guid")
86
+
87
+ @property
88
+ def prev_message_id(self) -> str:
89
+ return self.data["message_updates"][0].get("prev_message_id")
90
+
91
+ @property
92
+ def state(self) -> str:
93
+ return self.data["message_updates"][0].get("state")
94
+
95
+ @property
96
+ def title(self) -> str:
97
+ if self.data['show_notifications']:
98
+ return self.data['show_notifications'][0].get('title')
99
+
100
+ @property
101
+ def author_title(self) -> str:
102
+ return self.data['chat_updates'][0]['chat']['last_message'].get('author_title', self.title)
103
+
104
+ @property
105
+ def is_user(self) -> bool:
106
+ return self.chat_type == "User"
107
+
108
+ @property
109
+ def is_group(self) -> bool:
110
+ return self.chat_type == "Group"
111
+
112
+ @property
113
+ def is_forward(self) -> bool:
114
+ return "forwarded_from" in self.data["message_updates"][0]["message"].keys()
115
+
116
+ @property
117
+ def forward_from(self) -> str:
118
+ return self.data["message_updates"][0]["message"]["forwarded_from"].get("type_from") if self.is_forward else None
119
+
120
+ @property
121
+ def forward_object_guid(self) -> str:
122
+ return self.data["message_updates"][0]["message"]["forwarded_from"].get("object_guid") if self.is_forward else None
123
+
124
+ @property
125
+ def forward_message_id(self) -> str:
126
+ return self.data["message_updates"][0]["message"]["forwarded_from"].get("message_id") if self.is_forward else None
127
+
128
+ @property
129
+ def is_event(self) -> bool:
130
+ return 'event_data' in self.data['message_updates'][0]['message'].keys()
131
+
132
+ @property
133
+ def event_type(self) -> str:
134
+ return self.data['message_updates'][0]['message']['event_data'].get('type') if self.is_event else None
135
+
136
+ @property
137
+ def event_object_guid(self) -> str:
138
+ return self.data['message_updates'][0]['message']['event_data']['performer_object'].get('object_guid') if self.is_event else None
139
+
140
+ @property
141
+ def pinned_message_id(self) -> str:
142
+ return self.data['message_updates'][0]['message']['event_data'].get('pinned_message_id') if self.is_event else None
143
+
144
+ @property
145
+ def file_inline(self) -> dict:
146
+ return self.data["message_updates"][0]["message"].get("file_inline")
147
+
148
+ @property
149
+ def has_link(self) -> bool:
150
+ for link in ["http:/", "https:/", "www.", ".ir", ".com", ".net" "@"]:
151
+ if link in self.text.lower():
152
+ return True
153
+ return False
154
+
155
+ @property
156
+ def reply_info(self) -> ReplyInfo:
157
+ if not self.reply_message_id:
158
+ return
159
+
160
+ return ReplyInfo.from_json(self.methods.getMessagesById(self.object_guid, [self.reply_message_id])["messages"][0])
161
+
162
+ def reply(self, text:str) -> dict:
163
+ return self.methods.sendText(objectGuid=self.object_guid, text=text, messageId=self.message_id)
164
+
165
+ def seen(self) -> dict:
166
+ return self.methods.seenChats(seenList={self.object_guid: self.message_id})
167
+
168
+ def reaction(self, reaction:int) -> dict:
169
+ return self.methods.actionOnMessageReaction(objectGuid=self.object_guid, messageId=self.message_id, reactionId=reaction, action="Add")
170
+
171
+ def delete(self, delete_for_all:bool=True) -> dict:
172
+ return self.methods.deleteMessages(objectGuid=self.object_guid, messageIds=[self.message_id], deleteForAll=delete_for_all)
173
+
174
+ def pin(self) -> dict:
175
+ return self.methods.pinMessage(objectGuid=self.object_guid, messageId=self.message_id)
176
+
177
+ def forward(self, to_object_guid:str) -> dict:
178
+ return self.methods.forwardMessages(objectGuid=self.object_guid, message_ids=[self.message_id], toObjectGuid=to_object_guid)
179
+
180
+ def ban(self) -> dict:
181
+ return self.methods.banMember(objectGuid=self.object_guid, memberGuid=self.author_guid)
182
+
183
+ def check_join(self, object_guid:str) -> bool:
184
+ return self.methods.checkJoin(objectGuid=object_guid, userGuid=self.author_guid)
185
+
186
+ def download(self, save:bool=False, save_as:str=None) -> dict:
187
+ return self.methods.download(save=save, saveAs=save_as, fileInline=self.file_inline)
@@ -0,0 +1,2 @@
1
+ from .configs import Configs
2
+ from .utils import Utils
@@ -0,0 +1,18 @@
1
+ class Configs:
2
+ clients:dict = {
3
+ "web": {
4
+ "app_name": "Main",
5
+ "app_version": "4.4.6",
6
+ "lang_code": "fa",
7
+ "package": "web.rubika.ir",
8
+ "platform": "Web",
9
+ },
10
+ "android": {
11
+ "app_name": "Main",
12
+ "app_version": "3.5.7",
13
+ "lang_code": "fa",
14
+ "package": "app.rbmain.a",
15
+ "temp_code": "27",
16
+ "platform": "Android"
17
+ }
18
+ }
@@ -0,0 +1,251 @@
1
+
2
+ from random import choices, randint
3
+ from time import time
4
+ from re import finditer, sub
5
+ from base64 import b64encode
6
+ from io import BytesIO
7
+ from tempfile import NamedTemporaryFile
8
+ from mutagen import mp3, File
9
+ from filetype import guess
10
+ from os import system, chmod, remove
11
+ from .configs import Configs
12
+
13
+ class Utils:
14
+
15
+ def randomTmpSession() -> str:
16
+ return "".join(choices("abcdefghijklmnopqrstuvwxyz", k=32))
17
+
18
+ def randomDeviceHash() -> str:
19
+ return "".join(choices("0123456789", k=26))
20
+
21
+ def randomRnd() -> str:
22
+ return str(randint(-99999999, 99999999))
23
+
24
+ def privateParse(private:str) -> str:
25
+ if private:
26
+ if not private.startswith("-----BEGIN RSA PRIVATE KEY-----"):
27
+ private = "-----BEGIN RSA PRIVATE KEY-----\\n" + private
28
+
29
+ if not private.endswith("-----END RSA PRIVATE KEY-----"):
30
+ private += "\\n-----END RSA PRIVATE KEY-----"
31
+
32
+ return private.replace("\\n", "\n").strip()
33
+
34
+ def getState() -> int:
35
+ return int(time()) - 150
36
+
37
+ def phoneNumberParse(phoneNumber:str) -> str:
38
+ if str(phoneNumber).startswith("0"):
39
+ phoneNumber = phoneNumber[1:]
40
+
41
+ elif str(phoneNumber).startswith("98"):
42
+ phoneNumber = phoneNumber[2:]
43
+
44
+ elif str(phoneNumber).startswith("+98"):
45
+ phoneNumber = phoneNumber[3:]
46
+
47
+ return phoneNumber
48
+
49
+ def getChatTypeByGuid(objectGuid:str) -> str:
50
+ for chatType in [("u0", "User"), ("g0", "Group"), ("c0", "Channel"), ("s0", "Service"), ("b0", "Bot")]:
51
+ if objectGuid.startswith(chatType[0]):
52
+ return chatType[1]
53
+
54
+ def isChatType(chatType:str) -> bool:
55
+ chatType = chatType.lower()
56
+ for type in ["user", "group", "channel", "service", "bot"]:
57
+ if type == chatType:
58
+ return True
59
+
60
+ def isMessageType(messageType:str) -> bool:
61
+ messageType = messageType.lower()
62
+ for type in ["text", "image", "video", "gif", "video message", "voice", "music", "file"]:
63
+ if type == messageType:
64
+ return True
65
+
66
+
67
+ def getChatTypeByLink(link:str) -> str:
68
+ if "rubika.ir/joing" in link: return "Group"
69
+ elif "rubika.ir/joinc" in link: return "Channel"
70
+
71
+ def checkMetadata(text):
72
+ if text is None:
73
+ return [], ""
74
+
75
+ real_text = sub(r"``|\*\*|__|~~|--|@@|##|", "", text)
76
+ metadata = []
77
+ conflict = 0
78
+ mentionObjectIndex = 0
79
+ result = []
80
+
81
+ patterns = {
82
+ "Mono": r"\`\`([^``]*)\`\`",
83
+ "Bold": r"\*\*([^**]*)\*\*",
84
+ "Italic": r"\_\_([^__]*)\_\_",
85
+ "Strike": r"\~\~([^~~]*)\~\~",
86
+ "Underline": r"\-\-([^__]*)\-\-",
87
+ "Mention": r"\@\@([^@@]*)\@\@",
88
+ "Spoiler": r"\#\#([^##]*)\#\#",
89
+ }
90
+
91
+ for style, pattern in patterns.items():
92
+ for match in finditer(pattern, text):
93
+ metadata.append((match.start(), len(match.group(1)), style))
94
+
95
+ metadata.sort()
96
+
97
+ for start, length, style in metadata:
98
+ if not style == "Mention":
99
+ result.append({
100
+ "type": style,
101
+ "from_index": start - conflict,
102
+ "length": length,
103
+ })
104
+ conflict += 4
105
+ else:
106
+ mentionObjects = [i.group(1) for i in finditer(r"\@\(([^(]*)\)", text)]
107
+ mentionType = Utils.getChatTypeByGuid(objectGuid=mentionObjects[mentionObjectIndex]) or "Link"
108
+
109
+ if mentionType == "Link":
110
+ result.append(
111
+ {
112
+ "from_index": start - conflict,
113
+ "length": length,
114
+ "link": {
115
+ "hyperlink_data": {
116
+ "url": mentionObjects[mentionObjectIndex]
117
+ },
118
+ "type": "hyperlink",
119
+ },
120
+ "type": mentionType,
121
+ }
122
+ )
123
+ else:
124
+ result.append(
125
+ {
126
+ "type": "MentionText",
127
+ "from_index": start - conflict,
128
+ "length": length,
129
+ "mention_text_object_guid": mentionObjects[mentionObjectIndex],
130
+ "mention_text_object_type": mentionType
131
+ }
132
+ )
133
+ real_text = real_text.replace(f"({mentionObjects[mentionObjectIndex]})", "")
134
+ conflict += 6 + len(mentionObjects[mentionObjectIndex])
135
+ mentionObjectIndex += 1
136
+
137
+ return result, real_text
138
+
139
+ def checkLink(url:str) -> dict:
140
+ for i in ["http:/", "https://"]:
141
+ if url.startswith(i): return True
142
+
143
+ def getMimeFromByte(bytes:bytes) -> str:
144
+ mime = guess(bytes)
145
+ return "pyrubi" if mime is None else mime.extension
146
+
147
+ def generateFileName(mime:str) -> str:
148
+ return "Pyrubi Library {}.{}".format(randint(1, 1000), mime)
149
+
150
+ def getImageSize(bytes:bytes) -> str:
151
+ try:
152
+ from PIL import Image
153
+ except ImportError:
154
+ system("pip install pillow")
155
+ from PIL import Image
156
+
157
+ width, height = Image.open(BytesIO(bytes)).size
158
+ return width , height
159
+
160
+ def getImageThumbnail(bytes:bytes) -> str:
161
+ try:
162
+ from PIL import Image
163
+ except ImportError:
164
+ system("pip install pillow")
165
+ from PIL import Image
166
+
167
+ image = Image.open(BytesIO(bytes))
168
+ width, height = image.size
169
+ if height > width:
170
+ new_height = 40
171
+ new_width = round(new_height * width / height)
172
+ else:
173
+ new_width = 40
174
+ new_height = round(new_width * height / width)
175
+ image = image.resize((new_width, new_height), Image.LANCZOS)
176
+ changed_image = BytesIO()
177
+ image.save(changed_image, format="PNG")
178
+ return b64encode(changed_image.getvalue()).decode("UTF-8")
179
+
180
+ def getVideoData(bytes:bytes) -> list:
181
+ try:
182
+ from moviepy.editor import VideoFileClip
183
+
184
+ with NamedTemporaryFile(delete=False, dir=".") as temp_video:
185
+ temp_video.write(bytes)
186
+ temp_path = temp_video.name
187
+
188
+ chmod(temp_path, 0o777)
189
+
190
+ try:
191
+ from PIL import Image
192
+ except ImportError:
193
+ system("pip install pillow")
194
+ from PIL import Image
195
+
196
+ with VideoFileClip(temp_path) as clip:
197
+ duration = clip.duration
198
+ resolution = clip.size
199
+ thumbnail = clip.get_frame(0)
200
+ thumbnail_image = Image.fromarray(thumbnail)
201
+ thumbnail_buffer = BytesIO()
202
+ thumbnail_image.save(thumbnail_buffer, format="JPEG")
203
+ thumbnail_b64 = b64encode(thumbnail_buffer.getvalue()).decode("UTF-8")
204
+ clip.close()
205
+
206
+ remove(temp_path)
207
+
208
+ return thumbnail_b64, resolution, duration
209
+ except ImportError:
210
+ print(Colors.YELLOW + "Can't get video data! Please install the moviepy library by following command:\npip install moviepy" + Colors.RESET)
211
+ return Configs.defaultTumbInline, [900, 720], 1
212
+ except:
213
+ return Configs.defaultTumbInline, [900, 720], 1
214
+
215
+ def getVoiceDuration(bytes:bytes) -> int:
216
+ file = BytesIO()
217
+ file.write(bytes)
218
+ file.seek(0)
219
+ return mp3.MP3(file).info.length
220
+
221
+ def getMusicArtist(bytes:bytes) -> str:
222
+ try:
223
+ audio = File(BytesIO(bytes), easy=True)
224
+
225
+ if audio and "artist" in audio:
226
+ return audio["artist"][0]
227
+
228
+ return "pyrubi"
229
+ except Exception:
230
+ return "pyrubi"
231
+
232
+ class Colors:
233
+ RESET = "\033[0m"
234
+ BLACK = "\033[30m"
235
+ RED = "\033[31m"
236
+ GREEN = "\033[32m"
237
+ YELLOW = "\033[33m"
238
+ BLUE = "\033[34m"
239
+ MAGENTA = "\033[35m"
240
+ CYAN = "\033[36m"
241
+ WHITE = "\033[37m"
242
+
243
+ BG_RESET = "\033[49m"
244
+ BG_BLACK = "\033[40m"
245
+ BG_RED = "\033[41m"
246
+ BG_GREEN = "\033[42m"
247
+ BG_YELLOW = "\033[43m"
248
+ BG_BLUE = "\033[44m"
249
+ BG_MAGENTA = "\033[45m"
250
+ BG_CYAN = "\033[46m"
251
+ BG_WHITE = "\033[47m"