RubigramClient 1.4.8__py3-none-any.whl → 1.4.9__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/client.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Optional, Callable, Awaitable
2
+ from typing import Optional, Callable, Literal
3
3
  from aiohttp import web
4
4
  from functools import wraps
5
5
  from rubigram.types import Update, InlineMessage
@@ -9,8 +9,10 @@ from datetime import datetime
9
9
  import asyncio
10
10
  import logging
11
11
 
12
+
12
13
  logger = logging.getLogger(__name__)
13
14
 
15
+
14
16
  class Client(Method):
15
17
  def __init__(self, token: str, endpoint: Optional[str] = None, host: str = "0.0.0.0", port: int = 8000):
16
18
  self.token = token
@@ -24,74 +26,67 @@ class Client(Method):
24
26
  super().__init__(token)
25
27
 
26
28
  def on_message(self, *filters: Filter):
27
- def decorator(func: Callable[[Client, Update], Awaitable]):
29
+ def decorator(func: Callable) -> Callable:
28
30
  @wraps(func)
29
31
  async def wrapper(client: Client, update: Update):
30
32
  try:
31
33
  combined_filter = filters[0] if filters else None
32
34
  for filter in filters[1:]:
33
35
  combined_filter = combined_filter & filter
34
-
36
+
35
37
  if combined_filter is None or await combined_filter(update):
36
38
  await func(client, update)
37
39
  return True
38
40
  return False
39
- except Exception as e:
40
- logger.exception(f"Error in message handler {func.__name__}: {e}")
41
- return False
41
+ except Exception as error:
42
+ logger.exception("Error {}: {}".format(func.__name__, error))
43
+
42
44
  self.message_handlers.append(wrapper)
43
45
  return func
44
46
  return decorator
45
47
 
46
-
47
48
  def on_inline_message(self, *filters: Filter):
48
- def decorator(func: Callable[[Client, InlineMessage], Awaitable]):
49
+ def decorator(func: Callable) -> Callable:
49
50
  @wraps(func)
50
51
  async def wrapper(client: Client, update: InlineMessage):
51
52
  try:
52
53
  combined_filter = filters[0] if filters else None
53
54
  for filter in filters[1:]:
54
55
  combined_filter = combined_filter & filter
55
-
56
+
56
57
  if combined_filter is None or await combined_filter(update):
57
58
  await func(client, update)
58
59
  return True
59
60
  return False
60
- except Exception as e:
61
- logger.exception(f"Error in inline handler {func.__name__}: {e}")
62
- return False
61
+ except Exception as error:
62
+ logger.exception("Error {}: {}".format(func.__name__, error))
63
+
63
64
  self.inline_handlers.append(wrapper)
64
65
  return func
65
66
  return decorator
66
67
 
67
-
68
- async def dispatch_update(self, update: Update):
69
- for handler in self.message_handlers:
68
+ async def dispatch(self, update: Update, type: Literal["update", "inline_message"] = "update"):
69
+ handlers = self.message_handlers if type == "update" else self.inline_handlers
70
+ for handler in handlers:
70
71
  try:
71
72
  matched = await handler(self, update)
72
73
  if matched:
73
74
  return
74
75
  except Exception as error:
75
- logger.exception(f"Error in handler dispatch: {error}")
76
-
77
- async def dispatch_inline(self, update: InlineMessage):
78
- for handler in self.inline_handlers:
79
- try:
80
- matched = await handler(self, update)
81
- if matched:
82
- return
83
- except Exception as error:
84
- logger.exception(f"Error in inline handler dispatch: {error}")
76
+ logger.exception(f"Dispatch Error in handler [ {handler.__name__} ] : {error}")
85
77
 
86
78
  async def updater(self, data: dict):
87
79
  if "inline_message" in data:
88
- event = InlineMessage.from_dict(data["inline_message"])
89
- await self.dispatch_inline(event)
80
+ event = InlineMessage.from_dict(data.get("inline_message", {}))
81
+ event.client = self
82
+ await self.dispatch(event, "inline_message")
90
83
  elif "update" in data:
91
- event = Update.from_dict(data["update"])
84
+ event = Update.from_dict(data.get("update", {}))
92
85
  event.client = self
93
- await self.dispatch_update(event)
94
-
86
+ await self.dispatch(event)
87
+ else:
88
+ return
89
+
95
90
  async def set_endpoints(self):
96
91
  if self.endpoint:
97
92
  await self.update_bot_endpoint(f"{self.endpoint}/ReceiveUpdate", "ReceiveUpdate")
@@ -113,37 +108,40 @@ class Client(Method):
113
108
 
114
109
  app = web.Application()
115
110
  app.add_routes(self.routes)
116
-
111
+
117
112
  async def on_startup(_):
118
113
  await self.set_endpoints()
119
114
  app.on_startup.append(on_startup)
120
115
  web.run_app(app, host=self.host, port=self.port)
121
-
116
+
122
117
  else:
123
118
  async def polling():
124
- while True:
125
- try:
126
- get_update = await self.get_update(100, self.offset_id)
127
- if get_update.updates:
128
- updates = get_update.updates
129
- for update in updates:
130
- if update.type == "NewMessage":
131
- message_time = int(update.new_message.time)
132
- elif update.type == "UpdatedMessage":
133
- message_time = int(update.updated_message.time)
134
- else:
135
- continue
136
-
137
- now = int(datetime.now().timestamp())
138
- if message_time >= now or message_time + 2 >= now:
139
- if isinstance(update, Update):
140
- update.client = self
141
- await self.dispatch_update(update)
142
-
143
- self.offset_id = get_update.next_offset_id
144
- except Exception as e:
145
- logger.exception(f"Polling error: {e}")
146
- await asyncio.sleep(1)
147
-
148
- try:asyncio.run(polling())
149
- except:pass
119
+ try:
120
+ while True:
121
+ try:
122
+ get_update = await self.get_update(100, self.offset_id)
123
+ if get_update.updates:
124
+ updates = get_update.updates
125
+ for update in updates:
126
+ if update.type == "NewMessage":
127
+ message_time = int(update.new_message.time)
128
+ elif update.type == "UpdatedMessage":
129
+ message_time = int(
130
+ update.updated_message.time)
131
+ else:
132
+ continue
133
+
134
+ now = int(datetime.now().timestamp())
135
+ if message_time >= now or message_time + 2 >= now:
136
+ if isinstance(update, Update):
137
+ update.client = self
138
+ await self.dispatch(update)
139
+
140
+ self.offset_id = get_update.next_offset_id
141
+ except Exception as error:
142
+ logger.exception("Polling Error : {}".format(error))
143
+ await asyncio.sleep(1)
144
+
145
+ except: pass
146
+ finally: await self.stop()
147
+ asyncio.run(polling())
rubigram/method.py CHANGED
@@ -3,10 +3,12 @@ from typing import Literal, Optional
3
3
  from .types import Bot, Chat, Keypad, MessageId, Updates
4
4
 
5
5
 
6
+
6
7
  class Method(Network):
7
8
  def __init__(self, token: str):
8
9
  super().__init__(token)
9
10
 
11
+
10
12
  async def get_me(self) -> "Bot":
11
13
  response = await self.request("getMe", {})
12
14
  return Bot.from_dict(response["bot"])
@@ -47,8 +49,7 @@ class Method(Network):
47
49
  await self.request("editMessageText", {"chat_id": chat_id, "message_id": message_id, "text": text})
48
50
 
49
51
  async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> "MessageId":
50
- data = {"from_chat_id": from_chat_id, "message_id": message_id,
51
- "to_chat_id": to_chat_id, "disable_notification": disable_notification}
52
+ data = {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification}
52
53
  response = await self.request("forwardMessage", data)
53
54
  return MessageId.from_dict(response)
54
55
 
@@ -181,12 +182,12 @@ class Method(Network):
181
182
 
182
183
  async def upload_file(self, file: str, name: str, type: str):
183
184
  upload_url = await self.request_send_file(type)
184
- response = await self.request_upload_file(upload_url, file, name)
185
+ response = await self.RequestUploadFile(upload_url, file, name)
185
186
  return response
186
187
 
187
188
  async def download_file(self, file_id: str, file_name: str):
188
189
  download_url = await self.get_file(file_id)
189
- response = await self.request_download_file(download_url, file_name)
190
+ response = await self.RequestDownloadFile(download_url, file_name)
190
191
  return response
191
192
 
192
193
  async def send_file(
@@ -194,8 +195,7 @@ class Method(Network):
194
195
  chat_id: str,
195
196
  file: str,
196
197
  file_name: str,
197
- type: Literal["File", "Image", "Voice",
198
- "Music", "Gif", "Video"] = "File",
198
+ type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
199
199
  chat_keypad: Keypad = None,
200
200
  inline_keypad: Keypad = None,
201
201
  chat_keypad_type: Literal["New", "Remove"] = None,
rubigram/network.py CHANGED
@@ -1,47 +1,65 @@
1
1
  from aiohttp import ClientSession, FormData
2
- from typing import Any, Optional, Dict, Union
3
- import aiofiles
4
- import re
5
- import os
2
+ from typing import Any, Optional
3
+ import aiofiles, re, os
4
+
6
5
 
7
6
 
8
7
  class Network:
9
8
  def __init__(self, token: str) -> None:
10
9
  self.token: str = token
11
10
  self.session: Optional[ClientSession] = None
12
- self.api: str = f"https://botapi.rubika.ir/v3/{self.token}/"
13
-
11
+ self.api: str = f"https://botapi.rubika.ir/v3/{token}/"
12
+
13
+ async def start(self):
14
+ if not self.session:
15
+ self.session = ClientSession()
16
+
17
+ async def stop(self):
18
+ if self.session:
19
+ await self.session.close()
20
+ self.session = None
21
+
22
+ async def __aenter__(self):
23
+ await self.start()
24
+ return self
25
+
26
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
27
+ await self.stop()
14
28
 
15
- async def request(self, method: str, json: Dict[str, Any]) -> Optional[Dict[str, Any]]:
16
- async with ClientSession() as session:
17
- async with session.post(self.api + method, json=json) as response:
29
+ async def request(self, method: str, json: dict[str, Any]):
30
+ await self.start()
31
+ async with self.session.post(self.api + method, json=json) as response:
32
+ response.raise_for_status()
33
+ data: dict = await response.json()
34
+ return data.get("data")
35
+
36
+ async def ContentFile(self, url: str) -> bytes:
37
+ await self.start()
38
+ async with self.session.get(url) as response:
39
+ response.raise_for_status()
40
+ return await response.read()
41
+
42
+ async def RequestUploadFile(self, upload_url: str, file: str, name: str):
43
+ if isinstance(file, str):
44
+ if re.match(r"^https?://", file):
45
+ file = await self.ContentFile(file)
46
+ elif os.path.isfile(file):
47
+ async with aiofiles.open(file, "rb") as f:
48
+ file = await f.read()
49
+ else:
50
+ raise Exception("file not found : {}".format(file))
51
+
52
+ form = FormData().add_field("file", filename=name, content_type="application/octet-stream")
53
+ await self.start()
54
+ async with self.session.post(upload_url, data=form) as response:
18
55
  response.raise_for_status()
19
56
  data: dict = await response.json()
20
- return data.get("data")
57
+ return data.get("data", {}).get("file_id")
21
58
 
22
- async def request_bytes_file(self, url: str) -> bytes:
23
- async with ClientSession() as session:
24
- async with session.get(url) as response:
25
- response.raise_for_status()
26
- return await response.read()
27
-
28
- async def request_upload_file(self, upload_url: str, file: Union[str], name: str) -> str:
29
- if isinstance(file, str) and re.match(r"^https?://", file):
30
- file = await self.request_bytes_file(file)
31
- elif isinstance(file, str) and os.path.isfile(file):
32
- async with aiofiles.open(file, "rb") as f:
33
- file = await f.read()
34
-
35
- form = FormData()
36
- form.add_field("file", file, filename=name, content_type="application/octet-stream")
37
- async with ClientSession() as session:
38
- async with session.post(upload_url, data=form) as response:
39
- response.raise_for_status()
40
- data = await response.json()
41
- return data["data"]["file_id"]
59
+ raise Exception("Format Of file is invalid")
42
60
 
43
- async def request_download_file(self, url: str, name: str) -> dict[str, Union[str, bool]]:
44
- file = await self.request_bytes_file(url)
61
+ async def RequestDownloadFile(self, url: str, name: str):
62
+ file = await self.ContentFile(url)
45
63
  async with aiofiles.open(name, "wb") as f:
46
64
  await f.write(file)
47
- return {"status": True, "file": name}
65
+ return name
rubigram/types.py CHANGED
@@ -334,6 +334,7 @@ class Message(Dict):
334
334
 
335
335
  @dataclass
336
336
  class InlineMessage(Dict):
337
+ client: Optional["rubigram.Client"] = None
337
338
  sender_id: Optional[str] = None
338
339
  text: Optional[str] = None
339
340
  message_id: Optional[str] = None
@@ -393,7 +394,7 @@ class Update(Dict):
393
394
  updated_message: Optional[Message] = None
394
395
  updated_payment: Optional[PaymentStatus] = None
395
396
 
396
- def __repr__(self):
397
+ def __str__(self):
397
398
  return self.to_json()
398
399
 
399
400
  async def send_text(self, text: str,) -> "MessageId":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RubigramClient
3
- Version: 1.4.8
3
+ Version: 1.4.9
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
@@ -28,7 +28,7 @@ from rubigram.types import Update
28
28
 
29
29
  bot = Client("your_bot_token", "you_endpoint_url")
30
30
 
31
- @bot.on_message(filters.command("start"))
31
+ @bot.on_message(filters.command("start") & filters.private)
32
32
  async def start_handler(client, message: Update):
33
33
  await message.reply("Hi, WELCOME TO RUBIGRAM")
34
34
 
@@ -68,3 +68,18 @@ async def button(_, message: InlineMessage):
68
68
 
69
69
  bot.run()
70
70
  ```
71
+
72
+ ## Contex Manager
73
+ ```python
74
+ from rubigram import Client
75
+ import asyncio
76
+
77
+ bot = Client("bot_token")
78
+
79
+ async def main():
80
+ async with bot:
81
+ data = await bot.get_me()
82
+ print(data.bot_id)
83
+
84
+ asyncio.run(main())
85
+ ```
@@ -0,0 +1,11 @@
1
+ rubigram/__init__.py,sha256=Z0foTm6Hpy4CDEH5am-IFWifi0bo4jLvpkfj3JQQHPc,107
2
+ rubigram/client.py,sha256=YV4epKXNH-WaqtioIuNdelDfO304Qk2MnSWJZie8bLE,6140
3
+ rubigram/filters.py,sha256=YS1fUmtZON8XTMc6IwzXUH3Qw58uMhXD9cbTeP8cjr4,5750
4
+ rubigram/method.py,sha256=bPK2VMtJtRnrN-wrEXH7ijcPzovm_pqFObmViNfWUHM,10364
5
+ rubigram/network.py,sha256=Dy2M4NSdN2iaNP0wwlmVcS408Omq6QhiWUWExrp9Ers,2340
6
+ rubigram/types.py,sha256=QJ-nzd0HNhxCwhCOC6_fuBmJBG2M6Cg9oWH9sIFRNdA,13603
7
+ rubigramclient-1.4.9.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ rubigramclient-1.4.9.dist-info/METADATA,sha256=uUw_-eLAT3z8PJOXRL_fHsBXbBcoXmRD4mJNN9NjllM,2292
9
+ rubigramclient-1.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ rubigramclient-1.4.9.dist-info/top_level.txt,sha256=Mhg5HfkL6rLec5sI4ClGmwoqYUANAZUz8sVa1sT_cas,9
11
+ rubigramclient-1.4.9.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- rubigram/__init__.py,sha256=Z0foTm6Hpy4CDEH5am-IFWifi0bo4jLvpkfj3JQQHPc,107
2
- rubigram/client.py,sha256=o-txKYvZJ8nyGeLTYXCUWjjSAHBySh9Hh4G7F3sUF_U,6242
3
- rubigram/filters.py,sha256=YS1fUmtZON8XTMc6IwzXUH3Qw58uMhXD9cbTeP8cjr4,5750
4
- rubigram/method.py,sha256=pKqpQY4ALrhPlJVtE4zBW2hZ659U8Fof7Sb5lAn0BtM,10404
5
- rubigram/network.py,sha256=BbHXfLxA8-GqTbZBWfJ3A_F8PYVSRydWKIOEC1J2gO8,2024
6
- rubigram/types.py,sha256=PIbj-unWGMQMGyT6eG6VQvSHzx0TWoQOc_Aq2PlSlK8,13556
7
- rubigramclient-1.4.8.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- rubigramclient-1.4.8.dist-info/METADATA,sha256=O6Xf39TCJnjnq7dwrNReFcdzhFKeadxhneevW1tG7yU,2035
9
- rubigramclient-1.4.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- rubigramclient-1.4.8.dist-info/top_level.txt,sha256=Mhg5HfkL6rLec5sI4ClGmwoqYUANAZUz8sVa1sT_cas,9
11
- rubigramclient-1.4.8.dist-info/RECORD,,