RubigramClient 1.5.4__tar.gz → 1.5.6__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.5.4
3
+ Version: 1.5.6
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
@@ -51,8 +51,8 @@ async def start(_, message: Update):
51
51
  rows=[
52
52
  KeypadRow(
53
53
  buttons=[
54
- Button("1", "Simple", "Button 1"),
55
- Button("2", "Simple", "Button 2")
54
+ Button("1", "Button 1"),
55
+ Button("2", "Button 2")
56
56
  ]
57
57
  )
58
58
  ]
@@ -35,8 +35,8 @@ async def start(_, message: Update):
35
35
  rows=[
36
36
  KeypadRow(
37
37
  buttons=[
38
- Button("1", "Simple", "Button 1"),
39
- Button("2", "Simple", "Button 2")
38
+ Button("1", "Button 1"),
39
+ Button("2", "Button 2")
40
40
  ]
41
41
  )
42
42
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RubigramClient
3
- Version: 1.5.4
3
+ Version: 1.5.6
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
@@ -51,8 +51,8 @@ async def start(_, message: Update):
51
51
  rows=[
52
52
  KeypadRow(
53
53
  buttons=[
54
- Button("1", "Simple", "Button 1"),
55
- Button("2", "Simple", "Button 2")
54
+ Button("1", "Button 1"),
55
+ Button("2", "Button 2")
56
56
  ]
57
57
  )
58
58
  ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "RubigramClient"
3
- version = "1.5.4"
3
+ version = "1.5.6"
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,115 @@
1
+ from typing import Optional, Callable, Literal, Union
2
+ from rubigram.models 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 import web
8
+ import asyncio
9
+
10
+ class Client(Method):
11
+ def __init__(
12
+ self,
13
+ token: str,
14
+ endpoint: Optional[str] = None,
15
+ host: str = "0.0.0.0",
16
+ port: int = 8000
17
+ ):
18
+ self.token = token
19
+ self.endpoint = endpoint
20
+ self.host = host
21
+ self.port = port
22
+ self.offset_id = None
23
+ self.MESSAGE_HANDLER = []
24
+ self.INLINE_HANDLER = []
25
+ self.state = StateManager()
26
+ super().__init__(token)
27
+
28
+
29
+ def create_handler(self, type: Literal["message", "inline"], filters: Optional[Filter] = None):
30
+ def decorator(func: Callable) -> Callable:
31
+ async def wrapper(client: Client, update: Update):
32
+ if filters is None or await filters(update):
33
+ await func(client, update)
34
+ return True
35
+ return False
36
+ self.MESSAGE_HANDLER.append(wrapper) if type == "message" else self.INLINE_HANDLER.append(wrapper)
37
+ return func
38
+ return decorator
39
+
40
+ def on_message(self, filters: Optional[Filter] = None):
41
+ return self.create_handler("message", filters)
42
+
43
+ def on_inline_message(self, filters: Optional[Filter] = None):
44
+ return self.create_handler("inline", filters)
45
+
46
+ async def dispatch(self, update: Union[Update, InlineMessage], type: Literal["message", "inline"]):
47
+ handlers = self.MESSAGE_HANDLER if type == "message" else self.INLINE_HANDLER
48
+ for handler in handlers:
49
+ matched = await handler(self, update)
50
+ if matched:
51
+ return
52
+
53
+ async def updater(self, data: dict):
54
+ if "inline_message" in data:
55
+ event = InlineMessage.from_dict(data["inline_message"])
56
+ type = "inline"
57
+ elif "update" in data:
58
+ event = Update.from_dict(data["update"])
59
+ type = "message"
60
+ else: return
61
+ event.client = self
62
+ await self.dispatch(event, type)
63
+
64
+ async def set_endpoints(self):
65
+ endpoint_type = ["ReceiveUpdate", "ReceiveInlineMessage"]
66
+ if self.endpoint:
67
+ for i in endpoint_type:
68
+ await self.update_bot_endpoint(f"{self.endpoint}/{i}", i)
69
+
70
+ async def on_startup(self, app):
71
+ await self.set_endpoints()
72
+ await self.state.start()
73
+ await self.start()
74
+
75
+ async def on_cleanup(self, app):
76
+ await self.state.stop()
77
+ await self.stop()
78
+
79
+ def create_request_handler(self):
80
+ async def wrapper(request: web.Request):
81
+ data = await request.json()
82
+ await self.updater(data)
83
+ return web.json_response({"status": "OK"})
84
+ return wrapper
85
+
86
+ async def runner(self):
87
+ try:
88
+ await self.state.start()
89
+ while True:
90
+ get_updates = await self.get_update(100, self.offset_id)
91
+ if get_updates.updates:
92
+ updates = get_updates.updates
93
+ for update in updates:
94
+ time = int(update.new_message.time) if update.type == "NewMessage" else int(update.updated_message.time) if update.type == "UpdatedMessage" else None
95
+ now = int(datetime.now().timestamp())
96
+ if time and time >= now or time + 2 >= now:
97
+ update.client = self
98
+ await self.dispatch(update, "message")
99
+ self.offset_id = get_updates.next_offset_id
100
+ except Exception as error:
101
+ print(f"ERROR : {error}")
102
+ finally:
103
+ await self.state.stop()
104
+ await self.stop()
105
+
106
+ def run(self):
107
+ if self.endpoint:
108
+ app = web.Application()
109
+ app.router.add_post("/ReceiveUpdate", self.create_request_handler())
110
+ app.router.add_post("/ReceiveInlineMessage", self.create_request_handler())
111
+ web.run_app(app, host=self.host, port=self.port)
112
+ else:
113
+ try:
114
+ asyncio.run(self.runner())
115
+ except:pass
@@ -3,6 +3,9 @@ from typing import Union, Callable, Awaitable
3
3
  import re
4
4
 
5
5
 
6
+
7
+
8
+
6
9
  class Filter:
7
10
  def __init__(self, func: Callable[[Union[Update, InlineMessage]], Union[bool, Awaitable[bool]]]):
8
11
  self.func = func
@@ -195,7 +195,7 @@ class Method(Network):
195
195
  chat_id: str,
196
196
  file: str,
197
197
  file_name: str,
198
- text: str = None,
198
+ caption: str = None,
199
199
  type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
200
200
  chat_keypad: Keypad = None,
201
201
  inline_keypad: Keypad = None,
@@ -207,7 +207,7 @@ class Method(Network):
207
207
  data = {
208
208
  "chat_id": chat_id,
209
209
  "file_id": file_id,
210
- "text": text,
210
+ "text": caption,
211
211
  "chat_keypad": chat_keypad.to_dict() if chat_keypad else None,
212
212
  "inline_keypad": inline_keypad.to_dict() if inline_keypad else None,
213
213
  "disable_notification": disable_notification,
@@ -218,20 +218,20 @@ class Method(Network):
218
218
  response = await self.request("sendFile", data)
219
219
  return MessageId.from_dict(response)
220
220
 
221
- async def send_document(self, chat_id: str, document: str, name: str, **kwargs):
222
- return await self.send_file(chat_id, document, name, "File", **kwargs)
221
+ async def send_document(self, chat_id: str, document: str, name: str, caption: str = None, **kwargs):
222
+ return await self.send_file(chat_id, document, name, caption, "File", **kwargs)
223
223
 
224
- async def send_photo(self, chat_id: str, photo: str, name: str, **kwargs):
225
- return await self.send_file(chat_id, photo, name, "Image", **kwargs)
224
+ async def send_photo(self, chat_id: str, photo: str, name: str, caption: str = None, **kwargs):
225
+ return await self.send_file(chat_id, photo, name, caption, "Image", **kwargs)
226
226
 
227
- async def send_video(self, chat_id: str, video: str, name: str, **kwargs):
228
- return await self.send_file(chat_id, video, name, "Video", **kwargs)
227
+ async def send_video(self, chat_id: str, video: str, name: str, caption: str = None, **kwargs):
228
+ return await self.send_file(chat_id, video, name, caption, "Video", **kwargs)
229
229
 
230
- async def send_gif(self, chat_id: str, gif: str, name: str, **kwargs):
231
- return await self.send_file(chat_id, gif, name, "Gif", **kwargs)
230
+ async def send_gif(self, chat_id: str, gif: str, name: str, caption: str = None, **kwargs):
231
+ return await self.send_file(chat_id, gif, name, caption, "Gif", **kwargs)
232
232
 
233
- async def send_music(self, chat_id: str, music: str, name: str, **kwargs):
234
- return await self.send_file(chat_id, music, name, "Music", **kwargs)
233
+ async def send_music(self, chat_id: str, music: str, name: str, caption: str = None, **kwargs):
234
+ return await self.send_file(chat_id, music, name, caption, "Music", **kwargs)
235
235
 
236
- async def send_voice(self, chat_id: str, voice: str, name: str, **kwargs):
237
- return await self.send_file(chat_id, voice, name, "Voice", **kwargs)
236
+ async def send_voice(self, chat_id: str, voice: str, name: str, caption: str = None, **kwargs):
237
+ return await self.send_file(chat_id, voice, name, caption, "Voice", **kwargs)
@@ -386,8 +386,7 @@ class MessageId(Dict):
386
386
  @dataclass
387
387
  class Update(Dict):
388
388
  client: Optional["rubigram.Client"] = None
389
- type: Optional[Literal["NewMessage", "UpdatedMessage", "RemovedMessage",
390
- "StartedBot", "StoppedBot", "UpdatedPayment"]] = None
389
+ type: Optional[Literal["NewMessage", "UpdatedMessage", "RemovedMessage", "StartedBot", "StoppedBot", "UpdatedPayment"]] = None
391
390
  chat_id: Optional[str] = None
392
391
  removed_message_id: Optional[str] = None
393
392
  new_message: Optional[Message] = None
@@ -417,32 +416,32 @@ class Update(Dict):
417
416
  self,
418
417
  file: str,
419
418
  file_name: str,
420
- text: str = None,
419
+ caption: str = None,
421
420
  type: Literal["File", "Image", "Voice", "Music", "Gif", "Video"] = "File",
422
421
  chat_keypad: Keypad = None,
423
422
  inline_keypad: Keypad = None,
424
423
  chat_keypad_type: Literal["New", "Remove"] = None,
425
424
  disable_notification: bool = False,
426
425
  ) -> "MessageId":
427
- return await self.client.send_file(self.chat_id, file, file_name, text, type, chat_keypad, inline_keypad, chat_keypad_type, disable_notification, self.new_message.message_id)
426
+ return await self.client.send_file(self.chat_id, file, file_name, caption, type, chat_keypad, inline_keypad, chat_keypad_type, disable_notification, self.new_message.message_id)
428
427
 
429
- async def reply_document(self, document: str, name: str, **kwargs) -> "MessageId":
430
- return await self.reply_file(document, name, "File", **kwargs)
428
+ async def reply_document(self, document: str, name: str, caption: Optional[str] = None, **kwargs) -> "MessageId":
429
+ return await self.reply_file(document, name, caption, "File", **kwargs)
431
430
 
432
- async def reply_photo(self, photo: str, name: str, **kwargs) -> "MessageId":
433
- return await self.reply_file(photo, name, "Image", **kwargs)
431
+ async def reply_photo(self, photo: str, name: str, caption: Optional[str] = None, **kwargs) -> "MessageId":
432
+ return await self.reply_file(photo, name, caption, "Image", **kwargs)
434
433
 
435
- async def reply_video(self, video: str, name: str, **kwargs) -> "MessageId":
436
- return await self.reply_file(video, name, "Video", **kwargs)
434
+ async def reply_video(self, video: str, name: str, caption: Optional[str] = None, **kwargs) -> "MessageId":
435
+ return await self.reply_file(video, name, caption, "Video", **kwargs)
437
436
 
438
- async def reply_gif(self, gif: str, name: str, **kwargs) -> "MessageId":
439
- return await self.reply_file(gif, name, "Gif", **kwargs)
437
+ async def reply_gif(self, gif: str, name: str, caption: Optional[str] = None, **kwargs) -> "MessageId":
438
+ return await self.reply_file(gif, name, caption, "Gif", **kwargs)
440
439
 
441
- async def reply_music(self, music: str, name: str, **kwargs) -> "MessageId":
442
- return await self.reply_file(music, name, "Music", **kwargs)
440
+ async def reply_music(self, music: str, name: str, caption: Optional[str] = None, **kwargs) -> "MessageId":
441
+ return await self.reply_file(music, name, caption, "Music", **kwargs)
443
442
 
444
- async def reply_voice(self, voice: str, name: str, **kwargs) -> "MessageId":
445
- return await self.reply_file(voice, name, "Voice", **kwargs)
443
+ async def reply_voice(self, voice: str, name: str, caption: Optional[str] = None, **kwargs) -> "MessageId":
444
+ return await self.reply_file(voice, name, caption, "Voice", **kwargs)
446
445
 
447
446
 
448
447
  @dataclass
@@ -1,159 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Optional, Callable, Literal
3
- from aiohttp import web
4
- from functools import wraps
5
- from rubigram.models import Update, InlineMessage
6
- from rubigram.method import Method
7
- from rubigram.filters import Filter
8
- from rubigram.state import StateManager
9
- from datetime import datetime
10
- import asyncio
11
- import logging
12
-
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class Client(Method):
18
- def __init__(self, token: str, endpoint: Optional[str] = None, host: str = "0.0.0.0", port: int = 8000):
19
- self.token = token
20
- self.endpoint = endpoint
21
- self.host = host
22
- self.port = port
23
- self.offset_id = None
24
- self.state = StateManager()
25
- self.routes = web.RouteTableDef()
26
- self.message_handlers = []
27
- self.inline_handlers = []
28
- super().__init__(token)
29
-
30
- def on_message(self, *filters: Filter):
31
- def decorator(func: Callable) -> Callable:
32
- @wraps(func)
33
- async def wrapper(client: Client, update: Update):
34
- try:
35
- combined_filter = filters[0] if filters else None
36
- for filter in filters[1:]:
37
- combined_filter = combined_filter & filter
38
-
39
- if combined_filter is None or await combined_filter(update):
40
- await func(client, update)
41
- return True
42
- return False
43
- except Exception as error:
44
- logger.exception("Error {}: {}".format(func.__name__, error))
45
-
46
- self.message_handlers.append(wrapper)
47
- return func
48
- return decorator
49
-
50
- def on_inline_message(self, *filters: Filter):
51
- def decorator(func: Callable) -> Callable:
52
- @wraps(func)
53
- async def wrapper(client: Client, update: InlineMessage):
54
- try:
55
- combined_filter = filters[0] if filters else None
56
- for filter in filters[1:]:
57
- combined_filter = combined_filter & filter
58
-
59
- if combined_filter is None or await combined_filter(update):
60
- await func(client, update)
61
- return True
62
- return False
63
- except Exception as error:
64
- logger.exception("Error {}: {}".format(func.__name__, error))
65
-
66
- self.inline_handlers.append(wrapper)
67
- return func
68
- return decorator
69
-
70
- async def dispatch(self, update: Update, type: Literal["update", "inline_message"] = "update"):
71
- handlers = self.message_handlers if type == "update" else self.inline_handlers
72
- for handler in handlers:
73
- try:
74
- matched = await handler(self, update)
75
- if matched:
76
- return
77
- except Exception as error:
78
- logger.exception(f"Dispatch Error in handler [ {handler.__name__} ] : {error}")
79
-
80
- async def updater(self, data: dict):
81
- if "inline_message" in data:
82
- event = InlineMessage.from_dict(data.get("inline_message", {}))
83
- event.client = self
84
- await self.dispatch(event, "inline_message")
85
- elif "update" in data:
86
- event = Update.from_dict(data.get("update", {}))
87
- event.client = self
88
- await self.dispatch(event)
89
- else:
90
- return
91
-
92
- async def set_endpoints(self):
93
- if self.endpoint:
94
- await self.update_bot_endpoint(f"{self.endpoint}/ReceiveUpdate", "ReceiveUpdate")
95
- await self.update_bot_endpoint(f"{self.endpoint}/ReceiveInlineMessage", "ReceiveInlineMessage")
96
-
97
- def run(self):
98
- if self.endpoint:
99
- @self.routes.post("/ReceiveUpdate")
100
- async def receive_update(request: web.Request):
101
- data = await request.json()
102
- await self.updater(data)
103
- return web.json_response({"status": "OK"})
104
-
105
- @self.routes.post("/ReceiveInlineMessage")
106
- async def receive_inline_message(request: web.Request):
107
- data = await request.json()
108
- await self.updater(data)
109
- return web.json_response({"status": "OK"})
110
-
111
- app = web.Application()
112
- app.add_routes(self.routes)
113
-
114
- async def on_startup(app):
115
- await self.set_endpoints()
116
- await self.state.start()
117
- await self.start()
118
-
119
- async def on_cleanup(app):
120
- await self.state.stop()
121
- await self.stop()
122
-
123
- app.on_startup.append(on_startup)
124
- app.on_cleanup.append(on_cleanup)
125
- web.run_app(app, host=self.host, port=self.port)
126
-
127
- else:
128
- async def polling():
129
- try:
130
- await self.state.start()
131
- while True:
132
- try:
133
- get_update = await self.get_update(100, self.offset_id)
134
- if get_update.updates:
135
- updates = get_update.updates
136
- for update in updates:
137
- if update.type == "NewMessage":
138
- message_time = int(update.new_message.time)
139
- elif update.type == "UpdatedMessage":
140
- message_time = int(update.updated_message.time)
141
- else:continue
142
-
143
- now = int(datetime.now().timestamp())
144
- if message_time >= now or message_time + 2 >= now:
145
- if isinstance(update, Update):
146
- update.client = self
147
- await self.dispatch(update)
148
-
149
- self.offset_id = get_update.next_offset_id
150
- except Exception as error:
151
- logger.exception("Polling Error : {}".format(error))
152
- await asyncio.sleep(1)
153
-
154
- except:
155
- pass
156
- finally:
157
- await self.state.stop()
158
- await self.stop()
159
- asyncio.run(polling())
File without changes
File without changes