RubigramClient 1.3.3__py3-none-any.whl → 1.3.5__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,2 +1,4 @@
1
1
  from .client import Client
2
- from .filters import filter
2
+ from .method import Method
3
+ from .network import NetWork
4
+ from . import filters
rubigram/client.py CHANGED
@@ -1,317 +1,72 @@
1
- from rubigram.types import Update, MessageId, InlineMessage, Keypad, Chat, Bot
2
- from aiohttp import ClientSession, FormData, web
3
- from typing import Literal, Optional
4
- import aiofiles
1
+ from rubigram.types import Update, InlineMessage
2
+ from rubigram.method import Method
3
+ from aiohttp import web
5
4
 
6
5
 
7
- class Client:
8
- def __init__(self, token: str):
6
+ class Client(Method):
7
+ def __init__(self, token: str, endpoint: str = None, host: str = "0.0.0.0", port: int = 5000):
9
8
  self.token = token
9
+ self.port = port
10
+ self.host = host
11
+ self.endpoint = endpoint
10
12
  self.messages_handler = []
11
- self.api = f"https://botapi.rubika.ir/v3/{self.token}"
12
-
13
- async def request(self, method: str, data: dict):
14
- async with ClientSession() as session:
15
- async with session.post(f"{self.api}/{method}", json=data) as response:
16
- response.raise_for_status()
17
- return await response.json()
13
+ self.inlines_handler = []
14
+ self.routes = web.RouteTableDef()
15
+ self.api = f"https://botapi.rubika.ir/v3/{self.token}/"
16
+ super().__init__(token)
17
+
18
18
 
19
19
  def on_message(self, *filters):
20
20
  def decorator(func):
21
- async def wrapped(client, update):
21
+ async def wrapper(client, update):
22
22
  if all(f(update) for f in filters):
23
23
  await func(client, update)
24
- self.messages_handler.append(wrapped)
24
+ self.messages_handler.append(wrapper)
25
25
  return func
26
26
  return decorator
27
27
 
28
- async def update(self, data: dict):
29
- event = Update.read(data["update"], self) if data.get("update") else InlineMessage.read(data["inline_message"])
30
- for handler in self.messages_handler:
31
- await handler(self, event)
32
-
33
-
34
- async def get_me(self) -> "Bot":
35
- response = await self.request("getMe", {})
36
- return Bot.read(response["data"]["bot"])
37
-
38
-
39
- async def get_chat(self, chat_id: str) -> "Chat":
40
- response = await self.request("getChat", {"chat_id": chat_id})
41
- return Chat.read(response["data"]["chat"])
42
-
43
-
44
- async def get_updates(self, limit: int = 1, offset_id: str = None) -> list["Update"]:
45
- response = await self.request("getUpdates", {"limit": limit, "offset_id": offset_id})
46
- updates = [update for update in response["data"]["updates"]]
47
- return [Update.read(update) for update in updates]
48
-
49
-
50
- async def set_command(self, commands: list):
51
- response = await self.request("setCommands", {"bot_commands": commands})
52
- return response
53
-
54
-
55
- async def update_bot_endpoint(
56
- self,
57
- url: str,
58
- type: Literal["ReceiveUpdate", "ReceiveInlineMessage", "ReceiveQuery", "GetSelectionItem", "SearchSelectionItems"]
59
- ):
60
- response = await self.request("updateBotEndpoints", {"url": url, "type": type})
61
- return response
62
-
63
-
64
- async def send_message(
65
- self,
66
- chat_id: str,
67
- text: str,
68
- chat_keypad : Optional[Keypad] = None,
69
- inline_keypad: Optional[Keypad] = None,
70
- chat_keypad_type: Literal["New", "Remove"] = None,
71
- disable_notification: bool = None,
72
- reply_to_message_id = None
73
- ) -> "MessageId":
74
- data = {
75
- "chat_id": chat_id,
76
- "text": text,
77
- "chat_keypad": chat_keypad._dict() if chat_keypad else None,
78
- "inline_keypad": inline_keypad._dict() if inline_keypad else None,
79
- "chat_keypad_type": chat_keypad_type,
80
- "disable_notification": disable_notification,
81
- "reply_to_message_id": reply_to_message_id
82
- }
83
- response = await self.request("sendMessage", data)
84
- return MessageId.read(response["data"])
85
-
86
-
87
- async def send_poll(
88
- self,
89
- chat_id: str,
90
- question: str,
91
- options: list[str],
92
- chat_keypad: Keypad = None,
93
- inline_keypad: Keypad = None,
94
- disable_notification: bool = False,
95
- reply_to_message_id: str = None,
96
- chat_keypad_type: Literal[None, "New", "Remove"] = None
97
- ) -> "MessageId":
98
- data = {
99
- "chat_id": chat_id,
100
- "question": question,
101
- "options": options,
102
- "chat_keypad": chat_keypad,
103
- "inline_keypad": inline_keypad,
104
- "disable_notification": disable_notification,
105
- "reply_to_message_id": reply_to_message_id,
106
- "chat_keypad_type": chat_keypad_type
107
- }
108
- response = await self.request("sendPoll", data)
109
- return MessageId.read(response["data"])
110
-
111
-
112
- async def send_location(
113
- self,
114
- chat_id: str,
115
- latitude: str,
116
- longitude: str,
117
- chat_keypad: Keypad = None,
118
- inline_keypad: Keypad = None,
119
- disable_notification: bool = False,
120
- reply_to_message_id: str = None,
121
- chat_keypad_type: Literal[None, "New", "Remove"] = None
122
- ) -> "MessageId":
123
- data = {
124
- "chat_id": chat_id,
125
- "latitude": latitude,
126
- "longitude": longitude,
127
- "chat_keypad": chat_keypad,
128
- "inline_keypad": inline_keypad,
129
- "disable_notification": disable_notification,
130
- "reply_to_message_id": reply_to_message_id,
131
- "chat_keypad_type": chat_keypad_type
132
- }
133
- response = await self.request("sendLocation", data)
134
- return MessageId.read(response["data"])
135
-
136
-
137
- async def send_contact(
138
- self,
139
- chat_id: str,
140
- first_name: str,
141
- last_name: str,
142
- phone_number: str,
143
- chat_keypad: Keypad = None,
144
- inline_keypad: Keypad = None,
145
- disable_notification: bool = False,
146
- reply_to_message_id: str = None,
147
- chat_keypad_type: Literal[None, "New", "Remove"] = None
148
- ) -> "MessageId":
149
- data = {
150
- "chat_id": chat_id,
151
- "first_name": first_name,
152
- "last_name": last_name,
153
- "phone_number": phone_number,
154
- "chat_keypad": chat_keypad,
155
- "inline_keypad": inline_keypad,
156
- "disable_notification": disable_notification,
157
- "reply_to_message_id": reply_to_message_id,
158
- "chat_keypad_type": chat_keypad_type
159
- }
160
- response = await self.request("sendContact", data)
161
- return MessageId.read(response["data"])
162
-
163
-
164
- async def send_sticker(
165
- self,
166
- chat_id: str,
167
- sticker_id: str,
168
- chat_keypad: Keypad = None,
169
- inline_keypad: Keypad = None,
170
- disable_notification: bool = False,
171
- reply_to_message_id: str = None,
172
- chat_keypad_type: Literal[None, "New", "Remove"] = None,
173
- ) -> "MessageId":
174
- data = {
175
- "chat_id": chat_id,
176
- "sticker_id": sticker_id,
177
- "chat_keypad": chat_keypad,
178
- "disable_notification": disable_notification,
179
- "inline_keypad": inline_keypad,
180
- "reply_to_message_id": reply_to_message_id,
181
- "chat_keypad_type": chat_keypad_type
182
- }
183
- response = await self.request("sendSticker", data)
184
- return MessageId.read(response["data"])
185
-
186
-
187
- async def forward_message(
188
- self,
189
- from_chat_id: str,
190
- message_id: str,
191
- to_chat_id: str,
192
- disable_notification: bool = False
193
- ) -> "MessageId":
194
- data = {
195
- "from_chat_id": from_chat_id,
196
- "message_id": message_id,
197
- "to_chat_id": to_chat_id,
198
- "disable_notification": disable_notification
199
- }
200
- response = await self.request("forwardMessage", data)
201
- return MessageId.read(response["data"])
202
-
203
-
204
- async def edit_message_text(
205
- self,
206
- chat_id: str,
207
- message_id: str,
208
- text: str
209
- ) -> None:
210
- data = {"chat_id": chat_id, "message_id": message_id, "text": text}
211
- await self.request("editMessageText", data)
212
-
213
-
214
- async def edit_message_keypad(
215
- self,
216
- chat_id: str,
217
- message_id: str,
218
- inline_keypad: Keypad
219
- ) -> None:
220
- data = {"chat_id": chat_id, "message_id": message_id, "inline_keypad": inline_keypad}
221
- await self.request("editMessageKeypad", data)
222
-
223
-
224
- async def edit_chat_keypad(
225
- self,
226
- chat_id: str,
227
- chat_keypad: Keypad
228
- ) -> None:
229
- data = {"chat_id": chat_id, "chat_keypad_type": "New", "chat_keypad": chat_keypad}
230
- await self.request("editChatKeypad", data)
231
-
232
-
233
- async def remove_chat_keypad(
234
- self,
235
- chat_id: str
236
- ) -> None:
237
- data = {"chat_id": chat_id, "chat_keypad_type": "Remove"}
238
- await self.request("editChatKeypad", data)
239
-
240
-
241
- async def delete_message(
242
- self,
243
- chat_id: str,
244
- message_id: str
245
- ) -> None:
246
- data = {"chat_id": chat_id, "message_id": message_id}
247
- await self.request("deleteMessage", data)
248
-
249
-
250
- async def get_file(self, file_id: str) -> str:
251
- response = await self.request("getFile", {"file_id": file_id})
252
- return response["data"]["download_url"]
253
-
254
-
255
- async def request_send_file(self, type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"]) -> str:
256
- response = await self.request("requestSendFile", {"type": type})
257
- return response["data"]["upload_url"]
258
-
259
-
260
- async def upload_file(self, path: str, file_name: str, type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"]) -> str:
261
- upload_url = await self.request_send_file(type)
262
- form = FormData()
263
- form.add_field("file", open(path, "rb"), filename=file_name, content_type="application/octet-stream")
264
- async with ClientSession() as session:
265
- async with session.post(upload_url, data=form) as response:
266
- result = await response.json()
267
- return result["data"]["file_id"]
268
-
28
+ def on_inline_message(self, *filters):
29
+ def decorator(func):
30
+ async def wrapper(client, update):
31
+ if all(f(update) for f in filters):
32
+ await func(client, update)
33
+ self.inlines_handler.append(wrapper)
34
+ return func
35
+ return decorator
269
36
 
270
- async def send_file(
271
- self,
272
- chat_id: str,
273
- path: str,
274
- file_name: str,
275
- type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
276
- chat_keypad = None,
277
- inline_keypad = None,
278
- disable_notification: bool = False,
279
- reply_to_message_id: str = None,
280
- chat_keypad_type: Literal["New", "Remove"] = None,
281
- ) -> str:
282
- file_id = await self.upload_file(path, file_name, type)
283
- data = {
284
- "chat_id": chat_id,
285
- "file_id": file_id,
286
- "chat_keypad": chat_keypad,
287
- "disable_notification": disable_notification,
288
- "inline_keypad": inline_keypad,
289
- "reply_to_message_id": reply_to_message_id,
290
- "chat_keypad_type": chat_keypad_type,
291
- }
292
- response = await self.request("sendFile", data)
293
- return MessageId.read(response["data"])
37
+ async def update(self, data: dict):
38
+ if "inline_message" in data:
39
+ event = InlineMessage.read(data["inline_message"])
40
+ for handler in self.inlines_handler:
41
+ await handler(self, event)
42
+ else:
43
+ event = Update.read(data["update"])
44
+ for handler in self.messages_handler:
45
+ await handler(self, event)
46
+
47
+ async def set_endpoints(self):
48
+ await self.update_bot_endpoint(self.endpoint + "/ReceiveUpdate", "ReceiveUpdate")
49
+ await self.update_bot_endpoint(self.endpoint + "/ReceiveInlineMessage", "ReceiveInlineMessage")
294
50
 
295
-
296
- async def download_file(self, file_id: str, file_name: str):
297
- download_url = await self.get_file(file_id)
298
- async with ClientSession() as session:
299
- async with session.get(download_url) as response:
300
- if response.status == 200:
301
- async with aiofiles.open(file_name, "wb") as file:
302
- await file.write(await response.read())
303
- return {"status": "OK", "file": file_name}
304
- raise Exception(f"Download Error | status_code : {response.status}")
305
-
306
-
307
51
  def run(self):
308
- routes = web.RouteTableDef()
309
- @routes.post("/receiveUpdate")
310
- @routes.post("/receiveInlineMessage")
311
- async def webhook(request):
52
+ @self.routes.post("/ReceiveUpdate")
53
+ async def receive_update(request):
54
+ data = await request.json()
55
+ await self.update(data)
56
+ return web.json_response({"status": "OK"})
57
+
58
+ @self.routes.post("/receiveInlineMessage")
59
+ async def receive_inline_message(request):
312
60
  data = await request.json()
313
61
  await self.update(data)
314
62
  return web.json_response({"status": "ok"})
63
+
315
64
  app = web.Application()
316
- app.add_routes(routes)
317
- web.run_app(app, host="0.0.0.0", port=8000)
65
+ app.add_routes(self.routes)
66
+
67
+ async def on_startup(app):
68
+ if self.endpoint:
69
+ await self.set_endpoints()
70
+
71
+ app.on_startup.append(on_startup)
72
+ web.run_app(app, host = self.host, port = self.port)
rubigram/filters.py CHANGED
@@ -1,58 +1,53 @@
1
1
  from rubigram.types import Update, InlineMessage
2
2
  from typing import Union
3
3
 
4
+ def command(commands: Union[str, list[str]], prefixe: str = "/"):
5
+ def filter(message: Update):
6
+ if isinstance(message, Update) and message.type == "NewMessage" and message.new_message.text:
7
+ text = message.new_message.text
8
+ COMMANDS = commands if isinstance(commands, list) else [commands]
9
+ for cmd in COMMANDS:
10
+ if text.lower().startswith(prefixe + cmd):
11
+ return True
12
+ return False
13
+ return filter
4
14
 
5
- class filter:
6
- @staticmethod
7
- def command(command: Union[str, list[str]], prefixes: Union[str, list[str]] = "/"):
8
- def inner(update: Update):
9
- if isinstance(update, Update) and update.new_message and update.new_message.text:
10
- message = update.new_message
11
- commands = command if isinstance(command, list) else [command]
12
- commands = [c.lower() for c in commands]
13
- prefix_list = [] if prefixes is None else prefixes
14
- prefix_list = prefix_list if isinstance(prefix_list, list) else [prefix_list]
15
- for prefix in prefix_list:
16
- if message.text.startswith(prefix) and commands:
17
- return message.text[len(prefix):].split()[0].lower() in commands
18
- return False
19
- return False
20
- return inner
21
-
22
- @staticmethod
23
- def text():
24
- def inner(update: Update):
25
- if isinstance(update, Update) and update.new_message:
26
- return update.new_message.text is not None
27
- return False
28
- return inner
15
+ def button(id: Union[str, list[str]]):
16
+ def filter(message: InlineMessage):
17
+ if isinstance(message, InlineMessage):
18
+ button_id = message.aux_data.button_id
19
+ ID = id if isinstance(id, list) else [id]
20
+ for i in ID:
21
+ if button_id == i:
22
+ return True
23
+ return False
24
+ return filter
29
25
 
30
- @staticmethod
31
- def private():
32
- def inner(update: Update):
33
- if isinstance(update, Update) and update.new_message:
34
- return update.new_message.sender_type == "User"
35
- return False
36
- return inner
37
-
38
- @staticmethod
39
- def chat(chat_id: str):
40
- def inner(update: Update):
41
- return getattr(update, "chat_id", None) == chat_id
42
- return inner
43
-
44
- @staticmethod
45
- def file():
46
- def inner(update: Update):
47
- if isinstance(update, Update) and update.new_message:
48
- return update.new_message.file is not None
49
- return False
50
- return inner
51
-
52
- @staticmethod
53
- def button(id: str):
54
- def inner(update: InlineMessage):
55
- if isinstance(update, InlineMessage):
56
- return update.aux_data.button_id == id
57
- return False
58
- return inner
26
+ def chat(chat_id: Union[str, list[str]]):
27
+ def filter(message: Union[Update, InlineMessage]):
28
+ chat_ids = chat_id if isinstance(chat_id, list) else [chat_id]
29
+ if isinstance(message, Update) or isinstance(message, InlineMessage):
30
+ return message.chat_id in chat_ids
31
+ return False
32
+ return filter
33
+
34
+ def text():
35
+ def filter(message: Update):
36
+ if isinstance(message, Update) and message.type == "NewMessage":
37
+ return bool(message.new_message.text)
38
+ return False
39
+ return filter
40
+
41
+ def file():
42
+ def filter(message: Update):
43
+ if isinstance(message, Update) and message.type == "NewMessage":
44
+ return bool(message.new_message.file)
45
+ return False
46
+ return filter
47
+
48
+ def private():
49
+ def filter(message: Update):
50
+ if isinstance(message, Update) and message.type == "NewMessage":
51
+ return message.new_message.sender_type in ["User", "Bot"]
52
+ return False
53
+ return filter
rubigram/method.py ADDED
@@ -0,0 +1,208 @@
1
+ from rubigram.network import NetWork
2
+ from typing import Literal, Optional
3
+ from rubigram.types import Bot, Chat, Update, Keypad, MessageId
4
+
5
+ class Method(NetWork):
6
+ def __init__(self, token: str):
7
+ super().__init__(token)
8
+
9
+ async def get_me(self) -> "Bot":
10
+ response = await self.request("getMe", {})
11
+ return Bot.read(response["data"]["bot"])
12
+
13
+ async def get_chat(self, chat_id: str) -> "Chat":
14
+ response = await self.request("getChat", {"chat_id": chat_id})
15
+ return Chat.read(response["data"]["chat"])
16
+
17
+ async def get_update(self, limit: int = 1, offset_id: Optional[int] = None) -> list[Update]:
18
+ response = await self.request("getUpdates", {"limit": limit, "offset_id": offset_id})
19
+ return [Update.read(update) for update in response["data"]["updates"]]
20
+
21
+ async def get_file(self, file_id: str) -> str:
22
+ response = await self.request("getFile", {"file_id": file_id})
23
+ return response["data"]["download_url"]
24
+
25
+ async def set_command(self, command: list):
26
+ response = await self.request("setCommands", {"bot_commands": command})
27
+ return response
28
+
29
+ async def update_bot_endpoint(self, url: str, type: Literal["ReceiveUpdate", "ReceiveInlineMessage", "ReceiveQuery", "GetSelectionItem", "SearchSelectionItems"]):
30
+ response = await self.request("updateBotEndpoints", {"url": url, "type": type})
31
+ return response
32
+
33
+ async def request_send_file(self, type: str):
34
+ response = await self.request("requestSendFile", {"type": type})
35
+ return response["data"]["upload_url"]
36
+
37
+ async def upload_file(self, path: str, name: str, type: str):
38
+ upload_url = await self.request_send_file(type)
39
+ response = await self.request_upload_file(upload_url, path, name)
40
+ return response
41
+
42
+ async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> "MessageId":
43
+ data = {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification}
44
+ response = await self.request("forwardMessage", data)
45
+ return MessageId.read(response["data"])
46
+
47
+ async def delete_message(self, chat_id: str, message_id: str):
48
+ await self.request("deleteMessage", {"chat_id": chat_id, "message_id": message_id})
49
+
50
+ async def remove_chat_keypad(self, chat_id: str):
51
+ await self.request("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "Remove"})
52
+
53
+ async def edit_chat_keypad(self, chat_id: str, chat_keypad):
54
+ await self.request("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "New", "chat_keypad": chat_keypad})
55
+
56
+ async def edit_message_keypad(self, chat_id: str, message_id: str, inline_keypad):
57
+ await self.request("editMessageKeypad", {"chat_id": chat_id, "message_id": message_id, "inline_keypad": inline_keypad})
58
+
59
+ async def edit_message_text(self, chat_id: str, message_id: str, text: str):
60
+ await self.request("editMessageText", {"chat_id": chat_id, "message_id": message_id, "text": text})
61
+
62
+ async def send_message(
63
+ self,
64
+ chat_id: str,
65
+ text: str,
66
+ chat_keypad: Keypad = None,
67
+ inline_keypad: Keypad= None,
68
+ chat_keypad_type: Literal["New", "Remove"] = None,
69
+ disable_notification: bool = None,
70
+ reply_to_message_id = None
71
+ ) -> "MessageId":
72
+ data = {
73
+ "chat_id": chat_id,
74
+ "text": text,
75
+ "chat_keypad": chat_keypad._dict() if chat_keypad else None,
76
+ "inline_keypad": inline_keypad._dict() if inline_keypad else None,
77
+ "chat_keypad_type": chat_keypad_type,
78
+ "disable_notification": disable_notification,
79
+ "reply_to_message_id": reply_to_message_id
80
+ }
81
+ response = await self.request("sendMessage", data)
82
+ return MessageId.read(response["data"])
83
+
84
+ async def send_poll(
85
+ self,
86
+ chat_id: str,
87
+ question: str,
88
+ options: list[str],
89
+ chat_keypad: Keypad = None,
90
+ inline_keypad: Keypad = None,
91
+ disable_notification: bool = False,
92
+ reply_to_message_id: str = None,
93
+ chat_keypad_type: Literal["New", "Remove"] = None
94
+ ) -> "MessageId":
95
+ data = {
96
+ "chat_id": chat_id,
97
+ "question": question,
98
+ "options": options,
99
+ "chat_keypad": chat_keypad._dict() if chat_keypad else None,
100
+ "inline_keypad": inline_keypad._dict() if inline_keypad else None,
101
+ "disable_notification": disable_notification,
102
+ "reply_to_message_id": reply_to_message_id,
103
+ "chat_keypad_type": chat_keypad_type
104
+ }
105
+ response = await self.request("sendPoll", data)
106
+ return MessageId.read(response["data"])
107
+
108
+ async def send_location(
109
+ self,
110
+ chat_id: str,
111
+ latitude: str,
112
+ longitude: str,
113
+ chat_keypad: Keypad = None,
114
+ inline_keypad: Keypad = None,
115
+ disable_notification: bool = False,
116
+ reply_to_message_id: str = None,
117
+ chat_keypad_type: Literal["New", "Remove"] = None
118
+ ) -> "MessageId":
119
+ data = {
120
+ "chat_id": chat_id,
121
+ "latitude": latitude,
122
+ "longitude": longitude,
123
+ "chat_keypad": chat_keypad._dict() if chat_keypad else None,
124
+ "inline_keypad": inline_keypad._dict() if inline_keypad else None,
125
+ "disable_notification": disable_notification,
126
+ "reply_to_message_id": reply_to_message_id,
127
+ "chat_keypad_type": chat_keypad_type
128
+ }
129
+ response = await self.request("sendLocation", data)
130
+ return MessageId.read(response["data"])
131
+
132
+ async def send_contact(
133
+ self,
134
+ chat_id: str,
135
+ first_name: str,
136
+ last_name: str,
137
+ phone_number: str,
138
+ chat_keypad: Keypad = None,
139
+ inline_keypad: Keypad = None,
140
+ disable_notification: bool = False,
141
+ reply_to_message_id: str = None,
142
+ chat_keypad_type: Literal["New", "Remove"] = None
143
+ ) -> "MessageId":
144
+ data = {
145
+ "chat_id": chat_id,
146
+ "first_name": first_name,
147
+ "last_name": last_name,
148
+ "phone_number": phone_number,
149
+ "chat_keypad": chat_keypad._dict() if chat_keypad else None,
150
+ "inline_keypad": inline_keypad._dict() if inline_keypad else None,
151
+ "disable_notification": disable_notification,
152
+ "reply_to_message_id": reply_to_message_id,
153
+ "chat_keypad_type": chat_keypad_type
154
+ }
155
+ response = await self.request("sendContact", data)
156
+ return MessageId.read(response["data"])
157
+
158
+ async def send_sticker(
159
+ self,
160
+ chat_id: str,
161
+ sticker_id: str,
162
+ chat_keypad: Keypad = None,
163
+ inline_keypad: Keypad = None,
164
+ disable_notification: bool = False,
165
+ reply_to_message_id: str = None,
166
+ chat_keypad_type: Literal["New", "Remove"] = None,
167
+ ) -> "MessageId":
168
+ data = {
169
+ "chat_id": chat_id,
170
+ "sticker_id": sticker_id,
171
+ "chat_keypad": chat_keypad._dict() if chat_keypad else None,
172
+ "inline_keypad": inline_keypad._dict() if inline_keypad else None,
173
+ "disable_notification": disable_notification,
174
+ "reply_to_message_id": reply_to_message_id,
175
+ "chat_keypad_type": chat_keypad_type
176
+ }
177
+ response = await self.request("sendSticker", data)
178
+ return MessageId.read(response["data"])
179
+
180
+ async def send_file(
181
+ self,
182
+ chat_id: str,
183
+ path: str,
184
+ file_name: str,
185
+ type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
186
+ chat_keypad: Keypad = None,
187
+ inline_keypad: Keypad = None,
188
+ disable_notification: bool = False,
189
+ reply_to_message_id: str = None,
190
+ chat_keypad_type: Literal["New", "Remove"] = None,
191
+ ) -> "MessageId":
192
+ file_id = await self.upload_file(path, file_name, type)
193
+ data = {
194
+ "chat_id": chat_id,
195
+ "file_id": file_id,
196
+ "chat_keypad": chat_keypad._dict() if chat_keypad else None,
197
+ "inline_keypad": inline_keypad._dict() if inline_keypad else None,
198
+ "disable_notification": disable_notification,
199
+ "reply_to_message_id": reply_to_message_id,
200
+ "chat_keypad_type": chat_keypad_type,
201
+ }
202
+ response = await self.request("sendFile", data)
203
+ return MessageId.read(response["data"])
204
+
205
+ async def download_file(self, file_id: str, file_name: str):
206
+ download_url = await self.get_file(file_id)
207
+ response = await self.request_download_file(download_url, file_name)
208
+ return response
rubigram/network.py ADDED
@@ -0,0 +1,30 @@
1
+ from aiohttp import ClientSession, FormData
2
+ import aiofiles
3
+
4
+ class NetWork:
5
+ def __init__(self, token: str):
6
+ self.token = token
7
+ self.api = f"https://botapi.rubika.ir/v3/{self.token}/"
8
+
9
+ async def request(self, method: str, json: dict) -> dict:
10
+ async with ClientSession() as session:
11
+ async with session.post(self.api + method, json=json) as response:
12
+ response.raise_for_status()
13
+ return await response.json()
14
+
15
+ async def request_upload_file(self, url: str, path: str, name: str) -> str:
16
+ form = FormData()
17
+ form.add_field("file", open(path, "rb"), filename=name, content_type="application/octet-stream")
18
+ async with ClientSession() as session:
19
+ async with session.post(url, data=form) as response:
20
+ response.raise_for_status()
21
+ result = await response.json()
22
+ return result["data"]["file_id"]
23
+
24
+ async def request_download_file(self, url: str, name: str):
25
+ async with ClientSession() as session:
26
+ async with session.get(url) as response:
27
+ response.raise_for_status()
28
+ async with aiofiles.open(name, mode="wb") as file:
29
+ await file.write(await response.read())
30
+ return {"status": True, "file": name}
rubigram/types.py CHANGED
@@ -3,7 +3,6 @@ from typing import Any, Dict, List, Literal, Optional
3
3
  import rubigram
4
4
 
5
5
 
6
-
7
6
  @dataclass
8
7
  class Location:
9
8
  longitude: Optional[str] = None
@@ -62,8 +61,8 @@ class ButtonLocation:
62
61
 
63
62
  def _dict(self) -> Dict:
64
63
  return {
65
- "default_pointer_location": self.default_pointer_location._dict(),
66
- "default_map_location": self.default_map_location._dict(),
64
+ "default_pointer_location": self.default_pointer_location._dict() if self.default_pointer_location else None,
65
+ "default_map_location": self.default_map_location._dict() if self.default_map_location else None,
67
66
  "type": self.type,
68
67
  "title": self.title,
69
68
  "location_image_url": self.location_image_url
@@ -200,6 +199,7 @@ class MessageId:
200
199
  message_id = data.get("message_id"),
201
200
  file_id = data.get("file_id")
202
201
  )
202
+
203
203
 
204
204
  @dataclass
205
205
  class PollStatus:
@@ -270,14 +270,14 @@ class Poll:
270
270
  @dataclass
271
271
  class ContactMessage:
272
272
  phone_number: Optional[str] = None
273
- first_name: Optional[File] = None
273
+ first_name: Optional[str] = None
274
274
  last_name: Optional[str] = None
275
275
 
276
276
  @classmethod
277
277
  def read(cls, data: dict[str, Any]) -> "ContactMessage":
278
278
  return cls(
279
279
  phone_number = data.get("phone_number"),
280
- first_name = File.read(data.get("first_name")) if "first_name" in data else None,
280
+ first_name = data.get("first_name"),
281
281
  last_name = data.get("last_name")
282
282
  )
283
283
 
@@ -458,7 +458,7 @@ class Update:
458
458
  type = data["type"],
459
459
  chat_id = data["chat_id"],
460
460
  removed_message_id = data.get("removed_message_id"),
461
- new_message = Message.read(data["new_message"]),
461
+ new_message = Message.read(data["new_message"]) if "new_message" in data else None,
462
462
  updated_message = Message.read(data["updated_message"]) if "updated_message" in data else None,
463
463
  updated_payment = PaymentStatus.read(data["updated_payment"]) if "updated_payment" in data else None
464
464
  )
@@ -467,4 +467,7 @@ class Update:
467
467
  return await self.client.send_message(self.chat_id, text, reply_to_message_id=self.new_message.message_id)
468
468
 
469
469
  async def reply_file(self, path: str, file_name: str, type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File") -> "MessageId":
470
- return await self.client.send_file(self.chat_id, path, file_name, type, reply_to_message_id=self.new_message.message_id)
470
+ return await self.client.send_file(self.chat_id, path, file_name, type, reply_to_message_id=self.new_message.message_id)
471
+
472
+ async def download(self, file_name: str):
473
+ return await self.client.download_file(self.new_message.file.file_id, file_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RubigramClient
3
- Version: 1.3.3
3
+ Version: 1.3.5
4
4
  Summary: A simple and flexible Python library for building advanced Rubika bots with powerful message handling, inline buttons, and custom filters.
5
5
  Author-email: Javad RZ <Javad.Py1385@gmail.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -9,7 +9,8 @@ Classifier: Operating System :: OS Independent
9
9
  Requires-Python: >=3.7
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
- Requires-Dist: aiohttp>=3.8.0
12
+ Requires-Dist: aiohttp
13
+ Requires-Dist: aiofiles
13
14
  Dynamic: license-file
14
15
 
15
16
  # Rubigram
@@ -24,7 +25,7 @@ pip install RubigramClient
24
25
  from rubigram import Client, filters
25
26
  from rubigram.types import Update
26
27
 
27
- bot = Client("your_bot_token")
28
+ bot = Client("your_bot_token", "you_endpoint_url")
28
29
 
29
30
  @bot.on_message(filters.command("start"))
30
31
  async def start_handler(client, message: Update):
@@ -0,0 +1,11 @@
1
+ rubigram/__init__.py,sha256=4JZ6q8ukflz_izRmJNYcndcKZFCsyJdYoYVoAPwN5V0,107
2
+ rubigram/client.py,sha256=F5yEanmYEh2WXMO_Jtwkl1vHa40K1rxHupwFNks7a7E,2635
3
+ rubigram/filters.py,sha256=VU9Nd5ALWQAXafH1mXi19hm-E5H20JgTswTS4usK3ro,1955
4
+ rubigram/method.py,sha256=nm2qfzkanpzDo3yusIXmjYgCYmLzV6nVOyaxPDDqqz4,9074
5
+ rubigram/network.py,sha256=K7vvGikXStvUBqHX2gogy3hJ1eQjLAJ7kdwLbfFuZIg,1399
6
+ rubigram/types.py,sha256=evelh8GGttOHFQjiwXOzwXYFwLXKJSHit60T0T-9VIE,17035
7
+ rubigramclient-1.3.5.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ rubigramclient-1.3.5.dist-info/METADATA,sha256=ZGaxld0O20t7CbavDp6AXPfPJYfib9haVLEuQnMWwBc,1022
9
+ rubigramclient-1.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ rubigramclient-1.3.5.dist-info/top_level.txt,sha256=Mhg5HfkL6rLec5sI4ClGmwoqYUANAZUz8sVa1sT_cas,9
11
+ rubigramclient-1.3.5.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- rubigram/__init__.py,sha256=K1WXGcBx9-1cLmImtnruqFk91nb_6XuhY_Ud0KS_IUk,55
2
- rubigram/client.py,sha256=bwt5209q4wcT9nUP67KLWryWF4TWmz1r1PR1lNuM1S8,11591
3
- rubigram/filters.py,sha256=nvROwHxBdjokWezfAZBjRgBdJdcOcjz0IuR3BShyiJw,2153
4
- rubigram/types.py,sha256=GIGvTGdDURuwJ3oHPvRR7B8oJ3V1f8MDaMAqjvOOO-0,16813
5
- rubigramclient-1.3.3.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- rubigramclient-1.3.3.dist-info/METADATA,sha256=JAm-gGFFLj-ea9Br1sx8GhREUYLGx-JO7dBTSbsLxKA,984
7
- rubigramclient-1.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- rubigramclient-1.3.3.dist-info/top_level.txt,sha256=Mhg5HfkL6rLec5sI4ClGmwoqYUANAZUz8sVa1sT_cas,9
9
- rubigramclient-1.3.3.dist-info/RECORD,,