RubigramClient 1.4.0__py3-none-any.whl → 1.4.1__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
- from .client import Client
1
+ from .network import Network
2
2
  from .method import Method
3
- from .network import NetWork
4
- from . import filters
3
+ from .client import Client
4
+ from . import filters
5
+ from . import state
rubigram/client.py CHANGED
@@ -55,11 +55,13 @@ class Client(Method):
55
55
 
56
56
  async def handle_update(self, data: dict):
57
57
  if "inline_message" in data:
58
- event = InlineMessage.read(data["inline_message"])
58
+ event = InlineMessage.from_dict(data["inline_message"])
59
59
  await asyncio.gather(*(h(self, event) for h in self.inlines_handler))
60
60
  elif "update" in data:
61
- event = Update.read(data["update"], self)
61
+ event = Update.from_dict(data["update"])
62
+ event.client = self
62
63
  await asyncio.gather(*(h(self, event) for h in self.messages_handler))
64
+
63
65
 
64
66
  async def set_endpoints(self):
65
67
  if not self.endpoint:
rubigram/filters.py CHANGED
@@ -1,14 +1,21 @@
1
1
  from rubigram.types import Update, InlineMessage
2
- # from rubigram.state import state_manager
2
+ from rubigram.state import state_manager
3
3
  from typing import Union
4
4
  import re
5
5
 
6
6
 
7
- # def state(states: Union[str, list[str]]):
8
- # def filter(message: Union[Update, InlineMessage]):
9
- # user_state = state_manager.get_state(message.chat_id)
10
- # return user_state in states if isinstance(states, list) else user_state == states
11
- # return filter
7
+ def state(states: Union[str, list[str]]):
8
+ def filter(message: Union[Update, InlineMessage]):
9
+ user_state = state_manager.get_state(message.chat_id)
10
+ return user_state in states if isinstance(states, list) else user_state == states
11
+ return filter
12
+
13
+ def edited():
14
+ def filter(message: Update):
15
+ if isinstance(message, Update) and message.updated_message:
16
+ return message.updated_message.is_edited
17
+ return False
18
+ return filter
12
19
 
13
20
  def command(commands: Union[str, list[str]], prefix: str = "/"):
14
21
  def filter(message: Update):
@@ -70,6 +77,7 @@ def private():
70
77
  return False
71
78
  return filter
72
79
 
80
+
73
81
  def forward():
74
82
  def filter(message: Update):
75
83
  if isinstance(message, Update) and message.type == "NewMessage":
@@ -77,6 +85,30 @@ def forward():
77
85
  return False
78
86
  return filter
79
87
 
88
+ def forward_channel():
89
+ def filter(message: Update):
90
+ if isinstance(message, Update) and message.type == "NewMessage" and message.new_message.forwarded_from:
91
+ return message.new_message.forwarded_from.from_sender_id == "Channel"
92
+ return False
93
+ return filter
94
+
95
+
96
+ def forward_user():
97
+ def filter(message: Update):
98
+ if isinstance(message, Update) and message.type == "NewMessage" and message.new_message.forwarded_from:
99
+ return message.new_message.forwarded_from.from_sender_id == "User"
100
+ return False
101
+ return filter
102
+
103
+
104
+ def forward_bot():
105
+ def filter(message: Update):
106
+ if isinstance(message, Update) and message.type == "NewMessage" and message.new_message.forwarded_from:
107
+ return message.new_message.forwarded_from.from_sender_id == "Bot"
108
+ return False
109
+ return filter
110
+
111
+
80
112
  def location():
81
113
  def filter(message: Update):
82
114
  if isinstance(message, Update) and message.type == "NewMessage":
rubigram/method.py CHANGED
@@ -1,26 +1,27 @@
1
- from rubigram.network import NetWork
1
+ from rubigram.network import Network
2
2
  from typing import Literal, Optional
3
3
  from rubigram.types import Bot, Chat, Update, Keypad, MessageId
4
4
 
5
- class Method(NetWork):
5
+
6
+ class Method(Network):
6
7
  def __init__(self, token: str):
7
8
  super().__init__(token)
8
9
 
9
10
  async def get_me(self) -> "Bot":
10
11
  response = await self.request("getMe", {})
11
- return Bot.read(response["data"]["bot"])
12
-
12
+ return Bot.from_dict(response["bot"])
13
+
13
14
  async def get_chat(self, chat_id: str) -> "Chat":
14
15
  response = await self.request("getChat", {"chat_id": chat_id})
15
- return Chat.read(response["data"]["chat"])
16
+ return Chat.from_dict(response["chat"])
16
17
 
17
18
  async def get_update(self, limit: int = 1, offset_id: Optional[int] = None) -> list[Update]:
18
19
  response = await self.request("getUpdates", {"limit": limit, "offset_id": offset_id})
19
- return [Update.read(update) for update in response["data"]["updates"]]
20
+ return [Update.from_dict(update) for update in response["updates"]]
20
21
 
21
22
  async def get_file(self, file_id: str) -> str:
22
23
  response = await self.request("getFile", {"file_id": file_id})
23
- return response["data"]["download_url"]
24
+ return response["download_url"]
24
25
 
25
26
  async def set_command(self, command: list):
26
27
  response = await self.request("setCommands", {"bot_commands": command})
@@ -30,19 +31,10 @@ class Method(NetWork):
30
31
  response = await self.request("updateBotEndpoints", {"url": url, "type": type})
31
32
  return response
32
33
 
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
34
  async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> "MessageId":
43
35
  data = {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification}
44
36
  response = await self.request("forwardMessage", data)
45
- return MessageId.read(response["data"])
37
+ return MessageId.from_dict(response)
46
38
 
47
39
  async def delete_message(self, chat_id: str, message_id: str):
48
40
  await self.request("deleteMessage", {"chat_id": chat_id, "message_id": message_id})
@@ -72,14 +64,14 @@ class Method(NetWork):
72
64
  data = {
73
65
  "chat_id": chat_id,
74
66
  "text": text,
75
- "chat_keypad": chat_keypad._dict() if chat_keypad else None,
76
- "inline_keypad": inline_keypad._dict() if inline_keypad else None,
67
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
68
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
77
69
  "chat_keypad_type": chat_keypad_type,
78
70
  "disable_notification": disable_notification,
79
71
  "reply_to_message_id": reply_to_message_id
80
72
  }
81
73
  response = await self.request("sendMessage", data)
82
- return MessageId.read(response["data"])
74
+ return MessageId.from_dict(response)
83
75
 
84
76
  async def send_poll(
85
77
  self,
@@ -96,14 +88,14 @@ class Method(NetWork):
96
88
  "chat_id": chat_id,
97
89
  "question": question,
98
90
  "options": options,
99
- "chat_keypad": chat_keypad._dict() if chat_keypad else None,
100
- "inline_keypad": inline_keypad._dict() if inline_keypad else None,
91
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
92
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
101
93
  "disable_notification": disable_notification,
102
94
  "reply_to_message_id": reply_to_message_id,
103
95
  "chat_keypad_type": chat_keypad_type
104
96
  }
105
97
  response = await self.request("sendPoll", data)
106
- return MessageId.read(response["data"])
98
+ return MessageId.from_dict(response)
107
99
 
108
100
  async def send_location(
109
101
  self,
@@ -120,14 +112,14 @@ class Method(NetWork):
120
112
  "chat_id": chat_id,
121
113
  "latitude": latitude,
122
114
  "longitude": longitude,
123
- "chat_keypad": chat_keypad._dict() if chat_keypad else None,
124
- "inline_keypad": inline_keypad._dict() if inline_keypad else None,
115
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
116
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
125
117
  "disable_notification": disable_notification,
126
118
  "reply_to_message_id": reply_to_message_id,
127
119
  "chat_keypad_type": chat_keypad_type
128
120
  }
129
121
  response = await self.request("sendLocation", data)
130
- return MessageId.read(response["data"])
122
+ return MessageId.from_dict(response)
131
123
 
132
124
  async def send_contact(
133
125
  self,
@@ -146,14 +138,14 @@ class Method(NetWork):
146
138
  "first_name": first_name,
147
139
  "last_name": last_name,
148
140
  "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,
141
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
142
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
151
143
  "disable_notification": disable_notification,
152
144
  "reply_to_message_id": reply_to_message_id,
153
145
  "chat_keypad_type": chat_keypad_type
154
146
  }
155
147
  response = await self.request("sendContact", data)
156
- return MessageId.read(response["data"])
148
+ return MessageId.from_dict(response)
157
149
 
158
150
  async def send_sticker(
159
151
  self,
@@ -168,41 +160,196 @@ class Method(NetWork):
168
160
  data = {
169
161
  "chat_id": chat_id,
170
162
  "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,
163
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
164
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
173
165
  "disable_notification": disable_notification,
174
166
  "reply_to_message_id": reply_to_message_id,
175
167
  "chat_keypad_type": chat_keypad_type
176
168
  }
177
169
  response = await self.request("sendSticker", data)
178
- return MessageId.read(response["data"])
170
+ return MessageId.from_dict(response)
171
+
172
+ async def request_send_file(self, type: str):
173
+ response = await self.request("requestSendFile", {"type": type})
174
+ return response["upload_url"]
175
+
176
+ async def upload_file(self, file: str, name: str, type: str):
177
+ upload_url = await self.request_send_file(type)
178
+ response = await self.request_upload_file(upload_url, file, name)
179
+ return response
180
+
181
+
182
+ async def download_file(self, file_id: str, file_name: str):
183
+ download_url = await self.get_file(file_id)
184
+ response = await self.request_download_file(download_url, file_name)
185
+ return response
179
186
 
180
187
  async def send_file(
181
188
  self,
182
189
  chat_id: str,
183
- path: str,
190
+ file: str,
184
191
  file_name: str,
185
192
  type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
186
193
  chat_keypad: Keypad = None,
187
194
  inline_keypad: Keypad = None,
195
+ chat_keypad_type: Literal["New", "Remove"] = None,
188
196
  disable_notification: bool = False,
189
197
  reply_to_message_id: str = None,
198
+ ) -> "MessageId":
199
+ file_id = await self.upload_file(file, file_name, type)
200
+ data = {
201
+ "chat_id": chat_id,
202
+ "file_id": file_id,
203
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
204
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
205
+ "disable_notification": disable_notification,
206
+ "reply_to_message_id": reply_to_message_id,
207
+ "chat_keypad_type": chat_keypad_type,
208
+ }
209
+ response = await self.request("sendFile", data)
210
+ return MessageId.from_dict(response)
211
+
212
+
213
+ async def send_document(
214
+ self,
215
+ chat_id: str,
216
+ document: str,
217
+ name: str,
218
+ chat_keypad: Keypad = None,
219
+ inline_keypad: Keypad = None,
190
220
  chat_keypad_type: Literal["New", "Remove"] = None,
221
+ disable_notification: bool = False,
222
+ reply_to_message_id: str = None,
191
223
  ) -> "MessageId":
192
- file_id = await self.upload_file(path, file_name, type)
224
+ file_id = await self.upload_file(document, name, "File")
193
225
  data = {
194
226
  "chat_id": chat_id,
195
227
  "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,
228
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
229
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
198
230
  "disable_notification": disable_notification,
199
231
  "reply_to_message_id": reply_to_message_id,
200
232
  "chat_keypad_type": chat_keypad_type,
201
233
  }
202
234
  response = await self.request("sendFile", data)
203
- return MessageId.read(response["data"])
235
+ return MessageId.from_dict(response)
204
236
 
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
237
+ async def send_photo(
238
+ self,
239
+ chat_id: str,
240
+ photo: str,
241
+ name: str,
242
+ chat_keypad: Keypad = None,
243
+ inline_keypad: Keypad = None,
244
+ chat_keypad_type: Literal["New", "Remove"] = None,
245
+ disable_notification: bool = False,
246
+ reply_to_message_id: str = None,
247
+ ) -> "MessageId":
248
+ file_id = await self.upload_file(photo, name, "Image")
249
+ data = {
250
+ "chat_id": chat_id,
251
+ "file_id": file_id,
252
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
253
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
254
+ "disable_notification": disable_notification,
255
+ "reply_to_message_id": reply_to_message_id,
256
+ "chat_keypad_type": chat_keypad_type,
257
+ }
258
+ response = await self.request("sendFile", data)
259
+ return MessageId.from_dict(response)
260
+
261
+ async def send_video(
262
+ self,
263
+ chat_id: str,
264
+ video: str,
265
+ name: str,
266
+ chat_keypad: Keypad = None,
267
+ inline_keypad: Keypad = None,
268
+ chat_keypad_type: Literal["New", "Remove"] = None,
269
+ disable_notification: bool = False,
270
+ reply_to_message_id: str = None,
271
+ ) -> "MessageId":
272
+ file_id = await self.upload_file(video, name, "Video")
273
+ data = {
274
+ "chat_id": chat_id,
275
+ "file_id": file_id,
276
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
277
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
278
+ "disable_notification": disable_notification,
279
+ "reply_to_message_id": reply_to_message_id,
280
+ "chat_keypad_type": chat_keypad_type,
281
+ }
282
+ response = await self.request("sendFile", data)
283
+ return MessageId.from_dict(response)
284
+
285
+ async def send_gif(
286
+ self,
287
+ chat_id: str,
288
+ gif: str,
289
+ name: str,
290
+ chat_keypad: Keypad = None,
291
+ inline_keypad: Keypad = None,
292
+ chat_keypad_type: Literal["New", "Remove"] = None,
293
+ disable_notification: bool = False,
294
+ reply_to_message_id: str = None,
295
+ ) -> "MessageId":
296
+ file_id = await self.upload_file(gif, name, "Gif")
297
+ data = {
298
+ "chat_id": chat_id,
299
+ "file_id": file_id,
300
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
301
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
302
+ "disable_notification": disable_notification,
303
+ "reply_to_message_id": reply_to_message_id,
304
+ "chat_keypad_type": chat_keypad_type,
305
+ }
306
+ response = await self.request("sendFile", data)
307
+ return MessageId.from_dict(response)
308
+
309
+ async def send_music(
310
+ self,
311
+ chat_id: str,
312
+ music: str,
313
+ name: str,
314
+ chat_keypad: Keypad = None,
315
+ inline_keypad: Keypad = None,
316
+ chat_keypad_type: Literal["New", "Remove"] = None,
317
+ disable_notification: bool = False,
318
+ reply_to_message_id: str = None,
319
+ ) -> "MessageId":
320
+ file_id = await self.upload_file(music, name, "Music")
321
+ data = {
322
+ "chat_id": chat_id,
323
+ "file_id": file_id,
324
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
325
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
326
+ "disable_notification": disable_notification,
327
+ "reply_to_message_id": reply_to_message_id,
328
+ "chat_keypad_type": chat_keypad_type,
329
+ }
330
+ response = await self.request("sendFile", data)
331
+ return MessageId.from_dict(response)
332
+
333
+ async def send_voice(
334
+ self,
335
+ chat_id: str,
336
+ voice: str,
337
+ name: str,
338
+ chat_keypad: Keypad = None,
339
+ inline_keypad: Keypad = None,
340
+ chat_keypad_type: Literal["New", "Remove"] = None,
341
+ disable_notification: bool = False,
342
+ reply_to_message_id: str = None,
343
+ ) -> "MessageId":
344
+ file_id = await self.upload_file(voice, name, "Voice")
345
+ data = {
346
+ "chat_id": chat_id,
347
+ "file_id": file_id,
348
+ "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
349
+ "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
350
+ "disable_notification": disable_notification,
351
+ "reply_to_message_id": reply_to_message_id,
352
+ "chat_keypad_type": chat_keypad_type,
353
+ }
354
+ response = await self.request("sendFile", data)
355
+ return MessageId.from_dict(response)
rubigram/network.py CHANGED
@@ -1,30 +1,58 @@
1
1
  from aiohttp import ClientSession, FormData
2
+ from typing import Any, Optional, Dict, Union
2
3
  import aiofiles
4
+ import re
5
+ import os
3
6
 
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()
7
+
8
+ class Network:
9
+ def __init__(self, token: str) -> None:
10
+ self.token: str = token
11
+ self.session: Optional[ClientSession] = None
12
+ self.api: str = f"https://botapi.rubika.ir/v3/{self.token}/"
13
+
14
+ async def get_session(self) -> ClientSession:
15
+ if self.session is None or self.session.closed:
16
+ self.session = ClientSession()
17
+ return self.session
18
+
19
+ async def close(self) -> None:
20
+ if self.session and not self.session.closed:
21
+ await self.session.close()
22
+
23
+ async def request(self, method: str, json: Dict[str, Any]) -> Optional[Dict[str, Any]]:
24
+ session = await self.get_session()
25
+ async with session.post(self.api + method, json=json) as response:
26
+ response.raise_for_status()
27
+ data: dict = await response.json()
28
+ return data.get("data")
29
+
30
+ async def request_bytes_file(self, url: str) -> bytes:
31
+ session = await self.get_session()
32
+ async with session.get(url) as response:
33
+ response.raise_for_status()
34
+ return await response.read()
35
+
36
+ async def request_upload_file(self, upload_url: str, file: Union[str], name: str) -> str:
37
+ session = await self.get_session()
38
+
39
+ if isinstance(file, str) and re.match(r"^https?://", file):
40
+ file = await self.request_bytes_file(file)
14
41
 
15
- async def request_upload_file(self, url: str, path: str, name: str) -> str:
42
+ elif isinstance(file, str) and os.path.isfile(file):
43
+ async with aiofiles.open(file, "rb") as f:
44
+ file = await f.read()
45
+
16
46
  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}
47
+ form.add_field("file", file, filename=name, content_type="application/octet-stream")
48
+
49
+ async with session.post(upload_url, data=form) as response:
50
+ response.raise_for_status()
51
+ data = await response.json()
52
+ return data["data"]["file_id"]
53
+
54
+ async def request_download_file(self, url: str, name: str) -> dict[str, Union[str, bool]]:
55
+ file = await self.request_bytes_file(url)
56
+ async with aiofiles.open(name, "wb") as f:
57
+ await f.write(file)
58
+ return {"status": True, "file": name}
rubigram/state.py ADDED
@@ -0,0 +1,30 @@
1
+ from typing import Any, Union, Optional
2
+
3
+ class StateManager:
4
+ def __init__(self):
5
+ self.states: dict[str, Any] = {}
6
+ self.datas: dict[str, dict[str, Any]] = {}
7
+
8
+ def set_state(self, user_id: str, state: Any):
9
+ self.states[user_id] = state
10
+
11
+ def get_state(self, user_id: str):
12
+ return self.states.get(user_id)
13
+
14
+ def clear_state(self, user_id: str):
15
+ self.states.pop(user_id, None)
16
+
17
+ def set_data(self, user_id: str, **data):
18
+ if user_id not in self.datas:
19
+ self.datas[user_id] = {}
20
+ self.datas[user_id].update(data)
21
+
22
+ def get_data(self, user_id: str, key: Optional[Union[str, int]] = None):
23
+ if key is not None:
24
+ return self.datas.get(user_id, {}).get(key)
25
+ return self.datas.get(user_id)
26
+
27
+ def clear_data(self, user_id: str):
28
+ self.datas.pop(user_id, None)
29
+
30
+ state_manager = StateManager()