RubigramClient 1.6.6__py3-none-any.whl → 1.6.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 RubigramClient might be problematic. Click here for more details.

rubigram/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .client import Client
2
+ from .rubino import Rubino
2
3
  from .state import StateManager
3
4
  from . import enums
4
5
  from . import filters
rubigram/client.py CHANGED
@@ -1,15 +1,22 @@
1
- from typing import Optional, Callable, Literal, Union
1
+ from typing import Optional, Callable, Union
2
2
  from rubigram.types import Update, InlineMessage
3
3
  from rubigram.method import Method
4
4
  from rubigram.filters import Filter
5
5
  from rubigram.state import StateManager
6
6
  from datetime import datetime
7
- from aiohttp import web
7
+ from aiohttp.web import Application, Request, json_response, run_app
8
8
  import asyncio
9
9
  import logging
10
10
 
11
11
 
12
- logging.basicConfig(format=("%(levelname)s | %(message)s"))
12
+ logging.basicConfig(
13
+ format="%(asctime)s | %(levelname)s : %(message)s",
14
+ datefmt="%Y-%m-%d %H:%M:%S",
15
+ level=logging.DEBUG
16
+ )
17
+
18
+ logging.getLogger("asyncio").setLevel(logging.WARNING)
19
+ logging.getLogger("aiohttp").setLevel(logging.WARNING)
13
20
 
14
21
 
15
22
  class Client(Method):
@@ -25,106 +32,129 @@ class Client(Method):
25
32
  self.host = host
26
33
  self.port = port
27
34
  self.offset_id = None
35
+ self.set_endpoint = True
28
36
  self.ROUTES = []
29
37
  self.MESSAGE_HANDLER = []
30
38
  self.INLINE_HANDLER = []
31
39
  self.state = StateManager()
32
- super().__init__(token)
33
-
34
-
35
- def create_handler(self, type: Literal["message", "inline"], filters: Optional[Filter] = None):
40
+ super().__init__(token)
41
+
42
+
43
+ def on_message(self, filters: Optional[Filter] = None):
36
44
  def decorator(func: Callable) -> Callable:
37
45
  async def wrapper(client: Client, update: Update):
38
46
  if filters is None or await filters(update):
39
47
  await func(client, update)
40
48
  return True
41
49
  return False
42
- self.MESSAGE_HANDLER.append(wrapper) if type == "message" else self.INLINE_HANDLER.append(wrapper)
50
+ self.MESSAGE_HANDLER.append(wrapper)
43
51
  return func
44
52
  return decorator
45
-
46
- def on_message(self, filters: Optional[Filter] = None):
47
- return self.create_handler("message", filters)
48
53
 
49
54
  def on_inline_message(self, filters: Optional[Filter] = None):
50
- return self.create_handler("inline", filters)
51
-
52
- async def dispatch(self, update: Union[Update, InlineMessage], type: Literal["message", "inline"]):
53
- handlers = self.MESSAGE_HANDLER if type == "message" else self.INLINE_HANDLER
55
+ def decorator(func: Callable) -> Callable:
56
+ async def wrapper(client: Client, update: Update):
57
+ if filters is None or await filters(update):
58
+ await func(client, update)
59
+ return True
60
+ return False
61
+ self.INLINE_HANDLER.append(wrapper)
62
+ return func
63
+ return decorator
64
+
65
+ def on_create_app(self, path: str, method: str = "GET"):
66
+ def decorator(func):
67
+ self.ROUTES.append((path, func, method.upper()))
68
+ return func
69
+ return decorator
70
+
71
+ async def dispatch(self, update: Union[Update, InlineMessage]):
72
+ handlers = self.MESSAGE_HANDLER if isinstance(update, Update) else self.INLINE_HANDLER
54
73
  for handler in handlers:
55
74
  matched = await handler(self, update)
56
75
  if matched:
57
76
  return
58
-
77
+
59
78
  async def updater(self, data: dict):
60
79
  if "inline_message" in data:
61
80
  event = InlineMessage.from_dict(data["inline_message"])
62
- type = "inline"
63
81
  elif "update" in data:
64
82
  event = Update.from_dict(data["update"])
65
- type = "message"
66
83
  else: return
67
84
  event.client = self
68
- await self.dispatch(event, type)
69
-
85
+ await self.dispatch(event)
86
+
70
87
  async def set_endpoints(self):
71
88
  endpoint_type = ["ReceiveUpdate", "ReceiveInlineMessage"]
72
- for i in endpoint_type: await self.update_bot_endpoint(f"{self.endpoint}/{i}", i)
73
-
89
+ for i in endpoint_type:
90
+ set_endpoint = await self.update_bot_endpoint(f"{self.endpoint}/{i}", i)
91
+ logging.info(f"status set endpoint for {i} : {set_endpoint["status"]}")
92
+
74
93
  async def on_startup(self, app):
75
- await self.set_endpoints()
94
+ if self.set_endpoint:
95
+ await self.set_endpoints()
76
96
  await self.start()
77
97
 
78
98
  async def on_cleanup(self, app):
79
99
  await self.stop()
80
100
 
81
101
  def create_request_handler(self):
82
- async def wrapper(request: web.Request):
102
+ async def wrapper(request: Request):
83
103
  data = await request.json()
84
104
  await self.updater(data)
85
- return web.json_response({"status": "OK"})
105
+ return json_response({"status": "OK"})
86
106
  return wrapper
87
-
88
- async def runner(self):
107
+
108
+
109
+ async def update_runner(self):
89
110
  try:
90
111
  while True:
91
- get_updates = await self.get_update(100, self.offset_id)
92
- if get_updates.updates:
93
- updates = get_updates.updates
112
+ get_update = await self.get_update(100, self.offset_id)
113
+ updates = get_update.updates
114
+ if updates:
94
115
  for update in updates:
95
- time = int(update.new_message.time) if update.type == "NewMessage" else int(update.updated_message.time) if update.type == "UpdatedMessage" else None
116
+ time = update.new_message.time if update.type == "NewMessage" else update.updated_message.time if update.type == "UpdatedMessage" else None
117
+ time = int(time)
96
118
  now = int(datetime.now().timestamp())
97
- if time and time >= now or time + 2 >= now:
119
+ if time and (time >= now or time + 2 >= now):
98
120
  update.client = self
99
- await self.dispatch(update, "message")
100
- self.offset_id = get_updates.next_offset_id
121
+ await self.dispatch(update)
122
+ self.offset_id = get_update.next_offset_id
101
123
  except Exception as error:
102
124
  logging.error(error)
103
125
  finally:
104
126
  await self.stop()
105
127
 
106
- def create_app(self, path: str, method: str = "Get"):
107
- def decorator(func):
108
- self.ROUTES.append((path, func, method))
109
- return func
110
- return decorator
111
-
112
- def run(self):
128
+ def run(self, set_endpoint = True):
129
+ self.set_endpoint = set_endpoint
113
130
  if self.endpoint:
114
- app = web.Application()
131
+ app = Application()
115
132
  app.on_startup(self.on_startup)
116
133
  app.on_cleanup(self.on_cleanup)
117
- for path, func, method in self.ROUTES:
118
- if method.upper() == "GET":
119
- app.router.add_get(path, func)
120
- elif method.upper() == "POST":
121
- app.router.add_post(path, func)
134
+
122
135
  app.router.add_post("/ReceiveUpdate", self.create_request_handler())
123
136
  app.router.add_post("/ReceiveInlineMessage", self.create_request_handler())
124
- web.run_app(app, host=self.host, port=self.port)
137
+
138
+ for path, func, method in self.ROUTES:
139
+ match method:
140
+ case "GET":
141
+ app.router.add_get(path, func)
142
+ case "POST":
143
+ app.router.add_post(path, func)
144
+ case "DELETE":
145
+ app.router.add_delete(path, func)
146
+ case "PUT":
147
+ app.router.add_put(path, func)
148
+ case "PATCH":
149
+ app.router.add_patch(path, func)
150
+
151
+ run_app(app, host=self.host, port=self.port)
152
+
125
153
  else:
126
154
  try:
127
- asyncio.run(self.runner())
128
- except KeyboardInterrupt:pass
155
+ logging.info("Start Bot")
156
+ asyncio.run(self.update_runner())
157
+ except KeyboardInterrupt:
158
+ logging.info("Stop Bot")
129
159
  except Exception as error:
130
160
  logging.error(error)
rubigram/enums.py CHANGED
@@ -126,4 +126,12 @@ class FileType(str, Enum):
126
126
  Video = "Video"
127
127
  Gif = "Gif"
128
128
  Music = "Music"
129
- Voice = "Voice"
129
+ Voice = "Voice"
130
+
131
+
132
+ class ChatAction(str, Enum):
133
+ Typing = "Typing"
134
+ Uploading = "Uploading"
135
+ Recording = "Recording"
136
+
137
+
rubigram/method.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .network import Network
2
2
  from rubigram import enums
3
- from typing import Optional
3
+ from typing import Optional, Union
4
4
  from rubigram.types import Bot, Chat, Keypad, MessageId, Updates, BotCommand
5
5
 
6
6
 
@@ -49,6 +49,21 @@ class Method(Network):
49
49
 
50
50
  async def edit_message_text(self, chat_id: str, message_id: str, text: str):
51
51
  await self.request("editMessageText", {"chat_id": chat_id, "message_id": message_id, "text": text})
52
+
53
+ async def edit_message(
54
+ self,
55
+ chat_id: str,
56
+ message_id: str,
57
+ text: Optional[str] = None,
58
+ chat_keypad: Optional[Keypad] = None,
59
+ inline_keypad: Optional[Keypad] = None
60
+ ):
61
+ if text:
62
+ await self.edit_message_text(chat_id, message_id, text)
63
+ if chat_keypad:
64
+ await self.edit_chat_keypad(chat_id, chat_keypad)
65
+ if inline_keypad:
66
+ await self.edit_message_keypad(chat_id, message_id, inline_keypad)
52
67
 
53
68
  async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> "MessageId":
54
69
  data = {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification}
@@ -195,30 +210,31 @@ class Method(Network):
195
210
  response = await self.request("requestSendFile", {"type": type})
196
211
  return response["upload_url"]
197
212
 
198
- async def upload_file(self, file: str, name: str, type: str):
213
+ async def upload_file(self, file: Union[str, bytes], name: Optional[str] = None, type: str = "File"):
199
214
  upload_url = await self.request_send_file(type)
200
- response = await self.RequestUploadFile(upload_url, file, name)
215
+ response = await self.requestUpload(upload_url, file, name)
201
216
  return response
202
217
 
203
- async def download_file(self, file_id: str, file_name: str):
218
+ async def download_file(self, file_id: str, filename: Optional[str] = None):
204
219
  download_url = await self.get_file(file_id)
205
- response = await self.RequestDownloadFile(download_url, file_name)
220
+ response = await self.requestDownload(download_url, filename)
206
221
  return response
207
222
 
208
223
  async def send_file(
209
224
  self,
210
225
  chat_id: str,
211
- file: str,
212
- file_name: str,
213
- caption: str = None,
226
+ file: Union[str, bytes],
227
+ caption: Optional[str] = None,
228
+ file_name: Optional[str] = None,
214
229
  type: enums.FileType = enums.FileType.File,
215
230
  chat_keypad: Keypad = None,
216
231
  inline_keypad: Keypad = None,
217
232
  chat_keypad_type: Optional[enums.ChatKeypadType] = None,
218
233
  disable_notification: bool = False,
219
- reply_to_message_id: str = None,
234
+ reply_to_message_id: Optional[str] = None,
220
235
  ) -> "MessageId":
221
236
  file_id = await self.upload_file(file, file_name, type)
237
+
222
238
  data = {
223
239
  "chat_id": chat_id,
224
240
  "file_id": file_id,
@@ -236,20 +252,58 @@ class Method(Network):
236
252
  message.client = self
237
253
  return message
238
254
 
239
- async def send_document(self, chat_id: str, document: str, name: str, caption: str = None, **kwargs):
240
- return await self.send_file(chat_id, document, name, caption, "File", **kwargs)
241
255
 
242
- async def send_photo(self, chat_id: str, photo: str, name: str, caption: str = None, **kwargs):
243
- return await self.send_file(chat_id, photo, name, caption, "Image", **kwargs)
244
256
 
245
- async def send_video(self, chat_id: str, video: str, name: str, caption: str = None, **kwargs):
246
- return await self.send_file(chat_id, video, name, caption, "Video", **kwargs)
257
+ async def send_document(self, chat_id: str, document: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
258
+ return await self.send_file(chat_id, document, caption, file_name, "File", **kwargs)
247
259
 
248
- async def send_gif(self, chat_id: str, gif: str, name: str, caption: str = None, **kwargs):
249
- return await self.send_file(chat_id, gif, name, caption, "Gif", **kwargs)
260
+ async def send_photo(self, chat_id: str, photo: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
261
+ return await self.send_file(chat_id, photo, caption, file_name, "Image", **kwargs)
250
262
 
251
- async def send_music(self, chat_id: str, music: str, name: str, caption: str = None, **kwargs):
252
- return await self.send_file(chat_id, music, name, caption, "Music", **kwargs)
263
+ async def send_video(self, chat_id: str, video: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
264
+ return await self.send_file(chat_id, video, caption, file_name, "Video", **kwargs)
265
+
266
+ async def send_gif(self, chat_id: str, gif: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
267
+ return await self.send_file(chat_id, gif, caption, file_name, "Gif", **kwargs)
268
+
269
+ async def send_music(self, chat_id: str, music: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
270
+ return await self.send_file(chat_id, music, caption, file_name, "Music", **kwargs)
271
+
272
+ async def send_voice(self, chat_id: str, voice: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
273
+ return await self.send_file(chat_id, voice, caption, file_name, "Voice", **kwargs)
274
+
275
+ # async def get_messages(self, chat_id: str, message_ids: Optional[list[str]] = None):
276
+ # return await self.request("getMessages", {"chat_id": chat_id, "message_ids": message_ids})
277
+
278
+ # async def get_bot_command(self):
279
+ # return await self.request("getBotCommands", {})
280
+
281
+ # async def send_chat_action(self, chat_id: str, action: enums.ChatAction):
282
+ # return await self.request("sendChatAction", {"chat_id": chat_id, "action": action})
283
+
284
+ # async def set_username(self, username: str):
285
+ # return await self.request("setUsername", {"username": username})
286
+
287
+ async def get_bot_name(self):
288
+ bot = await self.get_me()
289
+ return bot.bot_title
253
290
 
254
- async def send_voice(self, chat_id: str, voice: str, name: str, caption: str = None, **kwargs):
255
- return await self.send_file(chat_id, voice, name, caption, "Voice", **kwargs)
291
+ async def get_bot_id(self):
292
+ bot = await self.get_me()
293
+ return bot.bot_id
294
+
295
+ async def get_bot_username(self):
296
+ bot = await self.get_me()
297
+ return bot.username
298
+
299
+ async def get_bot_share_url(self):
300
+ bot = await self.get_me()
301
+ return bot.share_url
302
+
303
+ async def get_bot_descriptionl(self):
304
+ bot = await self.get_me()
305
+ return bot.description
306
+
307
+ async def get_bot_start_messagel(self):
308
+ bot = await self.get_me()
309
+ return bot.start_message
rubigram/network.py CHANGED
@@ -1,6 +1,10 @@
1
1
  from aiohttp import ClientSession, FormData
2
- from typing import Any, Optional
3
- import aiofiles, re, os
2
+ from typing import Any, Optional, Union
3
+ from pathlib import Path
4
+ from urllib.parse import urlparse
5
+ from random import randint
6
+ import aiofiles
7
+ import os
4
8
 
5
9
 
6
10
  class Network:
@@ -32,34 +36,46 @@ class Network:
32
36
  data: dict = await response.json()
33
37
  return data.get("data")
34
38
 
35
- async def ContentFile(self, url: str) -> bytes:
39
+ async def getBytes(self, url: str) -> bytes:
36
40
  await self.start()
37
41
  async with self.session.get(url) as response:
38
42
  response.raise_for_status()
39
43
  return await response.read()
40
44
 
41
- async def RequestUploadFile(self, upload_url: str, file: str, name: str):
45
+ async def getName(self, url: str) -> str:
46
+ parser = urlparse(url)
47
+ return os.path.basename(parser.path)
48
+
49
+ async def requestUpload(self, upload_url: str, file: Union[str, bytes], name: Optional[str] = None):
50
+ data, filename = None, None
42
51
  if isinstance(file, str):
43
- if re.match(r"^https://", file):
44
- file = await self.ContentFile(file)
45
- elif os.path.isfile(file):
46
- async with aiofiles.open(file, "rb") as f:
47
- file = await f.read()
52
+ path = Path(file)
53
+
54
+ if path.is_file():
55
+ data, filename = path.read_bytes(), name if name else path.name
56
+
57
+ elif file.startswith("http"):
58
+ data, filename = await self.getBytes(file), name if name else await self.getName(file)
59
+
48
60
  else:
49
- raise Exception("file not found : {}".format(file))
61
+ raise Exception(f"Can't find this file : {file}")
50
62
 
51
- form = FormData()
52
- form.add_field("file", file, filename=name, content_type="application/octet-stream")
53
- await self.start()
54
- async with self.session.post(upload_url, data=form) as response:
55
- response.raise_for_status()
56
- data: dict = await response.json()
57
- return data.get("data", {}).get("file_id")
63
+ elif isinstance(file, bytes):
64
+ if name:
65
+ data, filename = file, name
66
+ else:
67
+ raise Exception("choice name for bytes file")
58
68
 
59
- raise Exception("Format Of file is invalid")
69
+ form = FormData()
70
+ form.add_field("file", data, filename=filename, content_type="application/octet-stream")
71
+ await self.start()
72
+ async with self.session.post(upload_url, data=form) as response:
73
+ response.raise_for_status()
74
+ data: dict = await response.json()
75
+ return data.get("data", {})["file_id"]
60
76
 
61
- async def RequestDownloadFile(self, url: str, name: str):
62
- file = await self.ContentFile(url)
77
+ async def requestDownload(self, url: str, filename: Optional[str] = None):
78
+ file, name = await self.getBytes(url), filename if filename else await self.getName(url)
63
79
  async with aiofiles.open(name, "wb") as f:
64
80
  await f.write(file)
65
- return name
81
+ return name
@@ -0,0 +1 @@
1
+ from .client import Rubino
@@ -0,0 +1,82 @@
1
+ from aiohttp import ClientSession
2
+ from typing import Literal, Optional, Union, Any
3
+ from random import randint
4
+
5
+
6
+ class Rubino:
7
+ def __init__(self, auth: str):
8
+ self.auth = auth
9
+ self.api = f"https://rubino{randint(1, 30)}.iranlms.ir"
10
+ self.client = {
11
+ "app_name": "Main",
12
+ "app_version": "3.0.2",
13
+ "lang_code": "fa",
14
+ "package": "app.rbmain.a",
15
+ "platform": "Android"
16
+ }
17
+
18
+
19
+ async def reauest(self, method: str, data: dict[str, Any]):
20
+ json = {
21
+ "api_version": "0",
22
+ "auth": self.auth,
23
+ "client": self.client,
24
+ "data": data,
25
+ "method": method
26
+ }
27
+ async with ClientSession() as session:
28
+ async with session.post(self.api, json=json) as response:
29
+ response.raise_for_status()
30
+ return await response.json()
31
+
32
+
33
+ async def get_post_by_share_link(self, post_link: str):
34
+ return await self.reauest("getPostByShareLink", {"share_string": post_link.split("/")[-1]})
35
+
36
+
37
+ async def add_post_view_count(self, post_id: str, post_profile_id: str):
38
+ data = {"post_id": post_id, "post_profile_id": post_profile_id}
39
+ return await self.reauest("addPostViewCount", data)
40
+
41
+
42
+ async def add_view_story(self, story_profile_id: str, story_ids: Union[str, list[str]], profile_id: Optional[str] = None):
43
+ story_ids = story_ids if isinstance(story_ids, list) else [story_ids]
44
+ data = {"story_profile_id": story_profile_id, "story_ids": story_ids, "profile_id": profile_id}
45
+ return await self.reauest("addViewStory", data)
46
+
47
+
48
+ async def is_exist_username(self, username: str):
49
+ username = username.replace("@", "")
50
+ return await self.reauest("isExistUsername", {"username": username})
51
+
52
+
53
+ async def create_page(self, username: str, name: str, bio: Optional[str] = None):
54
+ return await self.reauest("createPage", {"username": username, "name": name, "bio": bio})
55
+
56
+
57
+ async def add_comment(self, content: str, post_id: str, post_profile_id: str, profile_id: Optional[str] = None):
58
+ rnd = randint(100000, 999999999)
59
+ data = {"content": content, "post_id": post_id, "post_profile_id": post_profile_id, "profile_id": profile_id, "rnd": rnd}
60
+ return await self.reauest("addComment", data)
61
+
62
+
63
+ async def request_follow(self, followee_id: str, f_type: Literal["Follow", "Unfollow"] = "Follow", profile_id: Optional[str] = None):
64
+ data = {"f_type": f_type, "followee_id": followee_id, "profile_id": profile_id}
65
+ return self.reauest("requestFollow", data)
66
+
67
+
68
+ async def set_block_profile(self, block_id: str, action: Literal["Block", "Unblock"] = "Block", profile_id: Optional[str] = None):
69
+ data = {"block_id": block_id, "action": action, "profile_id": profile_id}
70
+ return self.reauest("setBlockProfile", data)
71
+
72
+
73
+ async def get_comments(self, post_id: str, post_profile_id: str, limit: Optional[int] = 100, profile_id: Optional[str] = None):
74
+ data = {
75
+ "post_id": post_id,
76
+ "post_profile_id": post_profile_id,
77
+ "limit": limit,
78
+ "profile_id": profile_id,
79
+ "equal": False,
80
+ "sort": "FromMax"
81
+ }
82
+ return await self.reauest("getComments", data)