RubigramClient 1.6.5__py3-none-any.whl → 1.6.7__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,5 +1,3 @@
1
- from .network import Network
2
- from .method import Method
3
1
  from .client import Client
4
2
  from .state import StateManager
5
3
  from . import enums
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/method.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .network import Network
2
- from . import enums
3
- from typing import Optional
2
+ from rubigram import enums
3
+ from typing import Optional, Union
4
4
  from rubigram.types import Bot, Chat, Keypad, MessageId, Updates, BotCommand
5
5
 
6
6
 
@@ -28,7 +28,7 @@ class Method(Network):
28
28
  return response["download_url"]
29
29
 
30
30
  async def set_command(self, commands: list[BotCommand]):
31
- response = await self.request("setCommands", {"bot_commands": [command.to_dict() for command in commands]})
31
+ response = await self.request("setCommands", {"bot_commands": [command.asdict() for command in commands]})
32
32
  return response
33
33
 
34
34
  async def update_bot_endpoint(self, url: str, type: enums.UpdateEndpointType):
@@ -42,10 +42,10 @@ class Method(Network):
42
42
  await self.request("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "Remove"})
43
43
 
44
44
  async def edit_chat_keypad(self, chat_id: str, chat_keypad: Keypad):
45
- await self.request("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "New", "chat_keypad": chat_keypad.to_dict()})
45
+ await self.request("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "New", "chat_keypad": chat_keypad.asdict()})
46
46
 
47
47
  async def edit_message_keypad(self, chat_id: str, message_id: str, inline_keypad: Keypad):
48
- await self.request("editMessageKeypad", {"chat_id": chat_id, "message_id": message_id, "inline_keypad": inline_keypad.to_dict()})
48
+ await self.request("editMessageKeypad", {"chat_id": chat_id, "message_id": message_id, "inline_keypad": inline_keypad.asdict()})
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})
@@ -71,8 +71,8 @@ class Method(Network):
71
71
  data = {
72
72
  "chat_id": chat_id,
73
73
  "text": text,
74
- "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
75
- "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
74
+ "chat_keypad": chat_keypad.asdict() if chat_keypad else None,
75
+ "inline_keypad": inline_keypad.asdict() if inline_keypad else None,
76
76
  "chat_keypad_type": chat_keypad_type,
77
77
  "disable_notification": disable_notification,
78
78
  "reply_to_message_id": reply_to_message_id
@@ -98,8 +98,8 @@ class Method(Network):
98
98
  "chat_id": chat_id,
99
99
  "question": question,
100
100
  "options": options,
101
- "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
102
- "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
101
+ "chat_keypad": chat_keypad.asdict() if chat_keypad else None,
102
+ "inline_keypad": inline_keypad.asdict() if inline_keypad else None,
103
103
  "disable_notification": disable_notification,
104
104
  "reply_to_message_id": reply_to_message_id,
105
105
  "chat_keypad_type": chat_keypad_type
@@ -125,8 +125,8 @@ class Method(Network):
125
125
  "chat_id": chat_id,
126
126
  "latitude": latitude,
127
127
  "longitude": longitude,
128
- "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
129
- "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
128
+ "chat_keypad": chat_keypad.asdict() if chat_keypad else None,
129
+ "inline_keypad": inline_keypad.asdict() if inline_keypad else None,
130
130
  "disable_notification": disable_notification,
131
131
  "reply_to_message_id": reply_to_message_id,
132
132
  "chat_keypad_type": chat_keypad_type
@@ -154,8 +154,8 @@ class Method(Network):
154
154
  "first_name": first_name,
155
155
  "last_name": last_name,
156
156
  "phone_number": phone_number,
157
- "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
158
- "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
157
+ "chat_keypad": chat_keypad.asdict() if chat_keypad else None,
158
+ "inline_keypad": inline_keypad.asdict() if inline_keypad else None,
159
159
  "disable_notification": disable_notification,
160
160
  "reply_to_message_id": reply_to_message_id,
161
161
  "chat_keypad_type": chat_keypad_type
@@ -179,8 +179,8 @@ class Method(Network):
179
179
  data = {
180
180
  "chat_id": chat_id,
181
181
  "sticker_id": sticker_id,
182
- "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
183
- "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
182
+ "chat_keypad": chat_keypad.asdict() if chat_keypad else None,
183
+ "inline_keypad": inline_keypad.asdict() if inline_keypad else None,
184
184
  "disable_notification": disable_notification,
185
185
  "reply_to_message_id": reply_to_message_id,
186
186
  "chat_keypad_type": chat_keypad_type
@@ -195,36 +195,37 @@ class Method(Network):
195
195
  response = await self.request("requestSendFile", {"type": type})
196
196
  return response["upload_url"]
197
197
 
198
- async def upload_file(self, file: str, name: str, type: str):
198
+ async def upload_file(self, file: Union[str, bytes], name: Optional[str] = None, type: str = "File"):
199
199
  upload_url = await self.request_send_file(type)
200
- response = await self.RequestUploadFile(upload_url, file, name)
200
+ response = await self.requestUpload(upload_url, file, name)
201
201
  return response
202
202
 
203
- async def download_file(self, file_id: str, file_name: str):
203
+ async def download_file(self, file_id: str, filename: Optional[str] = None):
204
204
  download_url = await self.get_file(file_id)
205
- response = await self.RequestDownloadFile(download_url, file_name)
205
+ response = await self.requestDownload(download_url, filename)
206
206
  return response
207
207
 
208
208
  async def send_file(
209
209
  self,
210
210
  chat_id: str,
211
- file: str,
212
- file_name: str,
213
- caption: str = None,
211
+ file: Union[str, bytes],
212
+ caption: Optional[str] = None,
213
+ file_name: Optional[str] = None,
214
214
  type: enums.FileType = enums.FileType.File,
215
215
  chat_keypad: Keypad = None,
216
216
  inline_keypad: Keypad = None,
217
217
  chat_keypad_type: Optional[enums.ChatKeypadType] = None,
218
218
  disable_notification: bool = False,
219
- reply_to_message_id: str = None,
219
+ reply_to_message_id: Optional[str] = None,
220
220
  ) -> "MessageId":
221
221
  file_id = await self.upload_file(file, file_name, type)
222
+
222
223
  data = {
223
224
  "chat_id": chat_id,
224
225
  "file_id": file_id,
225
226
  "text": caption,
226
- "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
227
- "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
227
+ "chat_keypad": chat_keypad.asdict() if chat_keypad else None,
228
+ "inline_keypad": inline_keypad.asdict() if inline_keypad else None,
228
229
  "disable_notification": disable_notification,
229
230
  "reply_to_message_id": reply_to_message_id,
230
231
  "chat_keypad_type": chat_keypad_type,
@@ -236,20 +237,22 @@ class Method(Network):
236
237
  message.client = self
237
238
  return message
238
239
 
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
240
 
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
241
 
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)
242
+ async def send_document(self, chat_id: str, document: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
243
+ return await self.send_file(chat_id, document, caption, file_name, "File", **kwargs)
244
+
245
+ async def send_photo(self, chat_id: str, photo: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
246
+ return await self.send_file(chat_id, photo, caption, file_name, "Image", **kwargs)
247
+
248
+ async def send_video(self, chat_id: str, video: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
249
+ return await self.send_file(chat_id, video, caption, file_name, "Video", **kwargs)
247
250
 
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)
251
+ async def send_gif(self, chat_id: str, gif: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
252
+ return await self.send_file(chat_id, gif, caption, file_name, "Gif", **kwargs)
250
253
 
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)
254
+ async def send_music(self, chat_id: str, music: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
255
+ return await self.send_file(chat_id, music, caption, file_name, "Music", **kwargs)
253
256
 
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)
257
+ async def send_voice(self, chat_id: str, voice: Union[str, bytes], caption: Optional[str] = None, file_name: Optional[str] = None, **kwargs):
258
+ return await self.send_file(chat_id, voice, caption, file_name, "Voice", **kwargs)
rubigram/network.py CHANGED
@@ -1,6 +1,9 @@
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
+ import aiofiles
6
+ import os
4
7
 
5
8
 
6
9
  class Network:
@@ -32,34 +35,46 @@ class Network:
32
35
  data: dict = await response.json()
33
36
  return data.get("data")
34
37
 
35
- async def ContentFile(self, url: str) -> bytes:
38
+ async def getBytes(self, url: str) -> bytes:
36
39
  await self.start()
37
40
  async with self.session.get(url) as response:
38
41
  response.raise_for_status()
39
42
  return await response.read()
40
43
 
41
- async def RequestUploadFile(self, upload_url: str, file: str, name: str):
44
+ async def getName(self, url: str) -> str:
45
+ parser = urlparse(url)
46
+ return os.path.basename(parser.path)
47
+
48
+ async def requestUpload(self, upload_url: str, file: Union[str, bytes], name: Optional[str] = None):
49
+ data, filename = None, None
42
50
  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()
48
- else:
49
- raise Exception("file not found : {}".format(file))
51
+ path = Path(file)
52
+
53
+ if path.is_file():
54
+ data, filename = path.read_bytes(), name if name else path.name
50
55
 
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")
56
+ elif file.startswith("http"):
57
+ data, filename = await self.getBytes(file), name if name else await self.getName(file)
58
+
59
+ else:
60
+ raise Exception(f"Can't find this file : {file}")
58
61
 
59
- raise Exception("Format Of file is invalid")
62
+ elif isinstance(file, bytes):
63
+ if name:
64
+ data, filename = file, name
65
+ else:
66
+ raise Exception("choice name for bytes file")
67
+
68
+ form = FormData()
69
+ form.add_field("file", data, filename=filename, content_type="application/octet-stream")
70
+ await self.start()
71
+ async with self.session.post(upload_url, data=form) as response:
72
+ response.raise_for_status()
73
+ data: dict = await response.json()
74
+ return data.get("data", {})["file_id"]
60
75
 
61
- async def RequestDownloadFile(self, url: str, name: str):
62
- file = await self.ContentFile(url)
76
+ async def requestDownload(self, url: str, filename: Optional[str] = None):
77
+ file, name = await self.getBytes(url), filename if filename else await self.getName(url)
63
78
  async with aiofiles.open(name, "wb") as f:
64
79
  await f.write(file)
65
- return name
80
+ return name