RubigramClient 1.6.6__tar.gz → 1.6.7__tar.gz

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RubigramClient
3
- Version: 1.6.6
3
+ Version: 1.6.7
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 <MrJavad.Email@gmail.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -31,7 +31,8 @@ bot = Client(token="YOUR_TOKEN_BOT")
31
31
 
32
32
  @bot.on_message(filters.private)
33
33
  async def welcome_message(client, message: Update):
34
- await message.reply("Hi, WELCOME TO RUBIGRAM")
34
+ send = await message.reply("Hi, WELCOME TO RUBIGRAM")
35
+ await send.edit_text("message was edited")
35
36
 
36
37
  bot.run()
37
38
  ```
@@ -84,3 +85,22 @@ async def main():
84
85
 
85
86
  asyncio.run(main())
86
87
  ```
88
+
89
+ ## Implementation of multiple programs
90
+ ```python
91
+ from rubigram import Client
92
+ import asyncio
93
+
94
+ tokens = [
95
+ "TOKEN_1",
96
+ "TOKEN_2"
97
+ ]
98
+
99
+ async def main():
100
+ for token in tokens:
101
+ async with Client(token) as bot:
102
+ get_me = await bot.get_me()
103
+ print(get_me.asjson())
104
+
105
+ asyncio.run(main())
106
+ ```
@@ -15,7 +15,8 @@ bot = Client(token="YOUR_TOKEN_BOT")
15
15
 
16
16
  @bot.on_message(filters.private)
17
17
  async def welcome_message(client, message: Update):
18
- await message.reply("Hi, WELCOME TO RUBIGRAM")
18
+ send = await message.reply("Hi, WELCOME TO RUBIGRAM")
19
+ await send.edit_text("message was edited")
19
20
 
20
21
  bot.run()
21
22
  ```
@@ -66,5 +67,24 @@ async def main():
66
67
  data = await bot.get_me()
67
68
  print(data.bot_id)
68
69
 
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Implementation of multiple programs
74
+ ```python
75
+ from rubigram import Client
76
+ import asyncio
77
+
78
+ tokens = [
79
+ "TOKEN_1",
80
+ "TOKEN_2"
81
+ ]
82
+
83
+ async def main():
84
+ for token in tokens:
85
+ async with Client(token) as bot:
86
+ get_me = await bot.get_me()
87
+ print(get_me.asjson())
88
+
69
89
  asyncio.run(main())
70
90
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RubigramClient
3
- Version: 1.6.6
3
+ Version: 1.6.7
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 <MrJavad.Email@gmail.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -31,7 +31,8 @@ bot = Client(token="YOUR_TOKEN_BOT")
31
31
 
32
32
  @bot.on_message(filters.private)
33
33
  async def welcome_message(client, message: Update):
34
- await message.reply("Hi, WELCOME TO RUBIGRAM")
34
+ send = await message.reply("Hi, WELCOME TO RUBIGRAM")
35
+ await send.edit_text("message was edited")
35
36
 
36
37
  bot.run()
37
38
  ```
@@ -84,3 +85,22 @@ async def main():
84
85
 
85
86
  asyncio.run(main())
86
87
  ```
88
+
89
+ ## Implementation of multiple programs
90
+ ```python
91
+ from rubigram import Client
92
+ import asyncio
93
+
94
+ tokens = [
95
+ "TOKEN_1",
96
+ "TOKEN_2"
97
+ ]
98
+
99
+ async def main():
100
+ for token in tokens:
101
+ async with Client(token) as bot:
102
+ get_me = await bot.get_me()
103
+ print(get_me.asjson())
104
+
105
+ asyncio.run(main())
106
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "RubigramClient"
3
- version = "1.6.6"
3
+ version = "1.6.7"
4
4
  description = "A simple and flexible Python library for building advanced Rubika bots with powerful message handling, inline buttons, and custom filters."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.7"
@@ -0,0 +1,160 @@
1
+ from typing import Optional, Callable, Union
2
+ from rubigram.types import Update, InlineMessage
3
+ from rubigram.method import Method
4
+ from rubigram.filters import Filter
5
+ from rubigram.state import StateManager
6
+ from datetime import datetime
7
+ from aiohttp.web import Application, Request, json_response, run_app
8
+ import asyncio
9
+ import logging
10
+
11
+
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)
20
+
21
+
22
+ class Client(Method):
23
+ def __init__(
24
+ self,
25
+ token: str,
26
+ endpoint: Optional[str] = None,
27
+ host: str = "0.0.0.0",
28
+ port: int = 8000
29
+ ):
30
+ self.token = token
31
+ self.endpoint = endpoint
32
+ self.host = host
33
+ self.port = port
34
+ self.offset_id = None
35
+ self.set_endpoint = True
36
+ self.ROUTES = []
37
+ self.MESSAGE_HANDLER = []
38
+ self.INLINE_HANDLER = []
39
+ self.state = StateManager()
40
+ super().__init__(token)
41
+
42
+
43
+ def on_message(self, filters: Optional[Filter] = None):
44
+ def decorator(func: Callable) -> Callable:
45
+ async def wrapper(client: Client, update: Update):
46
+ if filters is None or await filters(update):
47
+ await func(client, update)
48
+ return True
49
+ return False
50
+ self.MESSAGE_HANDLER.append(wrapper)
51
+ return func
52
+ return decorator
53
+
54
+ def on_inline_message(self, filters: Optional[Filter] = None):
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
73
+ for handler in handlers:
74
+ matched = await handler(self, update)
75
+ if matched:
76
+ return
77
+
78
+ async def updater(self, data: dict):
79
+ if "inline_message" in data:
80
+ event = InlineMessage.from_dict(data["inline_message"])
81
+ elif "update" in data:
82
+ event = Update.from_dict(data["update"])
83
+ else: return
84
+ event.client = self
85
+ await self.dispatch(event)
86
+
87
+ async def set_endpoints(self):
88
+ endpoint_type = ["ReceiveUpdate", "ReceiveInlineMessage"]
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
+
93
+ async def on_startup(self, app):
94
+ if self.set_endpoint:
95
+ await self.set_endpoints()
96
+ await self.start()
97
+
98
+ async def on_cleanup(self, app):
99
+ await self.stop()
100
+
101
+ def create_request_handler(self):
102
+ async def wrapper(request: Request):
103
+ data = await request.json()
104
+ await self.updater(data)
105
+ return json_response({"status": "OK"})
106
+ return wrapper
107
+
108
+
109
+ async def update_runner(self):
110
+ try:
111
+ while True:
112
+ get_update = await self.get_update(100, self.offset_id)
113
+ updates = get_update.updates
114
+ if updates:
115
+ for update in updates:
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)
118
+ now = int(datetime.now().timestamp())
119
+ if time and (time >= now or time + 2 >= now):
120
+ update.client = self
121
+ await self.dispatch(update)
122
+ self.offset_id = get_update.next_offset_id
123
+ except Exception as error:
124
+ logging.error(error)
125
+ finally:
126
+ await self.stop()
127
+
128
+ def run(self, set_endpoint = True):
129
+ self.set_endpoint = set_endpoint
130
+ if self.endpoint:
131
+ app = Application()
132
+ app.on_startup(self.on_startup)
133
+ app.on_cleanup(self.on_cleanup)
134
+
135
+ app.router.add_post("/ReceiveUpdate", self.create_request_handler())
136
+ app.router.add_post("/ReceiveInlineMessage", self.create_request_handler())
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
+
153
+ else:
154
+ try:
155
+ logging.info("Start Bot")
156
+ asyncio.run(self.update_runner())
157
+ except KeyboardInterrupt:
158
+ logging.info("Stop Bot")
159
+ except Exception as error:
160
+ logging.error(error)
@@ -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
 
@@ -195,30 +195,31 @@ 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,
@@ -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)
@@ -0,0 +1,80 @@
1
+ from aiohttp import ClientSession, FormData
2
+ from typing import Any, Optional, Union
3
+ from pathlib import Path
4
+ from urllib.parse import urlparse
5
+ import aiofiles
6
+ import os
7
+
8
+
9
+ class Network:
10
+ def __init__(self, token: str) -> None:
11
+ self.token: str = token
12
+ self.session: Optional[ClientSession] = None
13
+ self.api: str = f"https://botapi.rubika.ir/v3/{token}/"
14
+
15
+ async def start(self):
16
+ if not self.session:
17
+ self.session = ClientSession()
18
+
19
+ async def stop(self):
20
+ if self.session:
21
+ await self.session.close()
22
+ self.session = None
23
+
24
+ async def __aenter__(self):
25
+ await self.start()
26
+ return self
27
+
28
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
29
+ await self.stop()
30
+
31
+ async def request(self, method: str, json: dict[str, Any]):
32
+ await self.start()
33
+ async with self.session.post(self.api + method, json=json) as response:
34
+ response.raise_for_status()
35
+ data: dict = await response.json()
36
+ return data.get("data")
37
+
38
+ async def getBytes(self, url: str) -> bytes:
39
+ await self.start()
40
+ async with self.session.get(url) as response:
41
+ response.raise_for_status()
42
+ return await response.read()
43
+
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
50
+ if isinstance(file, str):
51
+ path = Path(file)
52
+
53
+ if path.is_file():
54
+ data, filename = path.read_bytes(), name if name else path.name
55
+
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}")
61
+
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"]
75
+
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)
78
+ async with aiofiles.open(name, "wb") as f:
79
+ await f.write(file)
80
+ return name