RubigramClient 1.7.17__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.
- rubigram/__init__.py +15 -0
- rubigram/client.py +294 -0
- rubigram/enums/__init__.py +52 -0
- rubigram/enums/buttons/__init__.py +13 -0
- rubigram/enums/buttons/button_calendar_type.py +20 -0
- rubigram/enums/buttons/button_location_type.py +20 -0
- rubigram/enums/buttons/button_selection_get_type.py +20 -0
- rubigram/enums/buttons/button_selection_search_type.py +20 -0
- rubigram/enums/buttons/button_selection_type.py +22 -0
- rubigram/enums/buttons/button_textbox_type_keypad.py +20 -0
- rubigram/enums/buttons/button_textbox_type_line.py +20 -0
- rubigram/enums/buttons/button_type.py +58 -0
- rubigram/enums/chat_action_type.py +22 -0
- rubigram/enums/chat_keypad_type.py +20 -0
- rubigram/enums/chat_type.py +24 -0
- rubigram/enums/enum.py +6 -0
- rubigram/enums/file_type.py +28 -0
- rubigram/enums/forwarded_from_type.py +22 -0
- rubigram/enums/live_location_status.py +20 -0
- rubigram/enums/message_sender_type.py +20 -0
- rubigram/enums/metadata_type.py +18 -0
- rubigram/enums/parse_mode.py +20 -0
- rubigram/enums/payment_status_type.py +20 -0
- rubigram/enums/poll_status_type.py +20 -0
- rubigram/enums/update_endpoint_type.py +26 -0
- rubigram/enums/update_type.py +28 -0
- rubigram/errors.py +16 -0
- rubigram/filters.py +865 -0
- rubigram/http_session.py +96 -0
- rubigram/methods/__init__.py +26 -0
- rubigram/methods/chats/__init__.py +10 -0
- rubigram/methods/chats/get_chat.py +53 -0
- rubigram/methods/decorators/__init__.py +25 -0
- rubigram/methods/decorators/on_inline_message.py +70 -0
- rubigram/methods/decorators/on_message.py +62 -0
- rubigram/methods/decorators/on_remove_message.py +65 -0
- rubigram/methods/decorators/on_start.py +65 -0
- rubigram/methods/decorators/on_started_bot.py +70 -0
- rubigram/methods/decorators/on_stop.py +65 -0
- rubigram/methods/decorators/on_stopped_bot.py +70 -0
- rubigram/methods/decorators/on_update_message.py +70 -0
- rubigram/methods/files/__init__.py +32 -0
- rubigram/methods/files/download_file.py +118 -0
- rubigram/methods/files/get_file.py +41 -0
- rubigram/methods/files/get_file_name.py +41 -0
- rubigram/methods/files/request_send_file.py +49 -0
- rubigram/methods/files/send_file.py +133 -0
- rubigram/methods/files/send_gif.py +51 -0
- rubigram/methods/files/send_music.py +97 -0
- rubigram/methods/files/send_photo.py +95 -0
- rubigram/methods/files/send_video.py +96 -0
- rubigram/methods/files/send_voice.py +96 -0
- rubigram/methods/files/upload_file.py +114 -0
- rubigram/methods/messages/__init__.py +34 -0
- rubigram/methods/messages/delete_messages.py +84 -0
- rubigram/methods/messages/edit_chat_keypad.py +68 -0
- rubigram/methods/messages/edit_message.py +82 -0
- rubigram/methods/messages/edit_message_keypad.py +72 -0
- rubigram/methods/messages/edit_message_text.py +68 -0
- rubigram/methods/messages/forward_message.py +78 -0
- rubigram/methods/messages/remove_chat_keypad.py +46 -0
- rubigram/methods/messages/send_contact.py +114 -0
- rubigram/methods/messages/send_location.py +108 -0
- rubigram/methods/messages/send_message.py +115 -0
- rubigram/methods/messages/send_poll.py +104 -0
- rubigram/methods/messages/send_sticker.py +98 -0
- rubigram/methods/network/__init__.py +12 -0
- rubigram/methods/network/request.py +129 -0
- rubigram/methods/settings/__init__.py +16 -0
- rubigram/methods/settings/set_command.py +50 -0
- rubigram/methods/settings/setup_endpoints.py +48 -0
- rubigram/methods/settings/update_bot_endpoint.py +62 -0
- rubigram/methods/updates/__init__.py +14 -0
- rubigram/methods/updates/get_me.py +35 -0
- rubigram/methods/updates/get_update.py +62 -0
- rubigram/methods/utilities/__init__.py +14 -0
- rubigram/methods/utilities/dispatcher.py +66 -0
- rubigram/methods/utilities/run.py +118 -0
- rubigram/rubino/__init__.py +6 -0
- rubigram/rubino/client.py +374 -0
- rubigram/rubino/network.py +129 -0
- rubigram/server/__init__.py +6 -0
- rubigram/server/server.py +245 -0
- rubigram/state/__init__.py +7 -0
- rubigram/state/state.py +121 -0
- rubigram/state/storage.py +131 -0
- rubigram/types/__init__.py +66 -0
- rubigram/types/aux_data.py +28 -0
- rubigram/types/bot.py +51 -0
- rubigram/types/bot_command.py +26 -0
- rubigram/types/buttons/__init__.py +13 -0
- rubigram/types/buttons/button.py +59 -0
- rubigram/types/buttons/button_calendar.py +40 -0
- rubigram/types/buttons/button_location.py +40 -0
- rubigram/types/buttons/button_number_picker.py +34 -0
- rubigram/types/buttons/button_selection.py +48 -0
- rubigram/types/buttons/button_selection_item.py +32 -0
- rubigram/types/buttons/button_string_picker.py +29 -0
- rubigram/types/buttons/button_text_box.py +40 -0
- rubigram/types/chat.py +86 -0
- rubigram/types/config/__init__.py +6 -0
- rubigram/types/config/object.py +442 -0
- rubigram/types/contact_message.py +29 -0
- rubigram/types/file.py +78 -0
- rubigram/types/forwarded_from.py +39 -0
- rubigram/types/keypads/__init__.py +7 -0
- rubigram/types/keypads/keypad.py +31 -0
- rubigram/types/keypads/keypad_row.py +23 -0
- rubigram/types/live_location.py +44 -0
- rubigram/types/location.py +24 -0
- rubigram/types/messages/__init__.py +8 -0
- rubigram/types/messages/inline_message.py +78 -0
- rubigram/types/messages/message.py +117 -0
- rubigram/types/messages/update_message.py +341 -0
- rubigram/types/metadata/__init__.py +7 -0
- rubigram/types/metadata/metadata.py +43 -0
- rubigram/types/metadata/metadata_parts.py +42 -0
- rubigram/types/payment_status.py +30 -0
- rubigram/types/poll.py +32 -0
- rubigram/types/poll_status.py +40 -0
- rubigram/types/sticker.py +33 -0
- rubigram/types/updates/__init__.py +7 -0
- rubigram/types/updates/update.py +917 -0
- rubigram/types/updates/updates.py +56 -0
- rubigram/utils/__init__.py +14 -0
- rubigram/utils/auto_delete.py +93 -0
- rubigram/utils/parser.py +99 -0
- rubigramclient-1.7.17.dist-info/METADATA +215 -0
- rubigramclient-1.7.17.dist-info/RECORD +132 -0
- rubigramclient-1.7.17.dist-info/WHEEL +5 -0
- rubigramclient-1.7.17.dist-info/licenses/LICENSE +21 -0
- rubigramclient-1.7.17.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from aiohttp.web import Application, AppRunner, RouteTableDef, TCPSite, Request, json_response
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import rubigram
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Server:
|
|
16
|
+
"""
|
|
17
|
+
HTTP webhook server for receiving Rubika Bot API updates.
|
|
18
|
+
|
|
19
|
+
This class implements a web server that listens for incoming updates
|
|
20
|
+
from Rubika and dispatches them to the client's handlers. It supports
|
|
21
|
+
all update types defined in the API and integrates with the client's
|
|
22
|
+
lifecycle management.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
client (rubigram.Client):
|
|
26
|
+
The bot client instance that will process updates.
|
|
27
|
+
host (str, optional):
|
|
28
|
+
Host address to bind the server to. Use "0.0.0.0" to listen
|
|
29
|
+
on all interfaces. Defaults to "0.0.0.0".
|
|
30
|
+
port (int, optional):
|
|
31
|
+
Port number to listen on. Defaults to 8000.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
client (rubigram.Client): The bot client instance.
|
|
35
|
+
host (str): Server host address.
|
|
36
|
+
port (int): Server port number.
|
|
37
|
+
app (Application): aiohttp web application.
|
|
38
|
+
routes (RouteTableDef): HTTP route definitions.
|
|
39
|
+
runner (Optional[AppRunner]): aiohttp application runner.
|
|
40
|
+
site (Optional[TCPSite]): TCP site for the server.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
.. code-block:: python
|
|
44
|
+
# Create client with webhook
|
|
45
|
+
client = Client(
|
|
46
|
+
token="YOUR_BOT_TOKEN",
|
|
47
|
+
webhook="https://example.com/webhook"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Create and start server
|
|
51
|
+
server = Server(client, host="127.0.0.1", port=8080)
|
|
52
|
+
|
|
53
|
+
# Run server (blocks until interrupted)
|
|
54
|
+
server.run_server()
|
|
55
|
+
|
|
56
|
+
# Or manage server lifecycle manually
|
|
57
|
+
await server.start()
|
|
58
|
+
# Server is now running...
|
|
59
|
+
await server.stop()
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
client: "rubigram.Client",
|
|
65
|
+
host: str = "0.0.0.0",
|
|
66
|
+
port: int = 8000
|
|
67
|
+
):
|
|
68
|
+
self.client = client
|
|
69
|
+
self.host = host
|
|
70
|
+
self.port = port
|
|
71
|
+
|
|
72
|
+
self.app = Application()
|
|
73
|
+
self.routes = RouteTableDef()
|
|
74
|
+
|
|
75
|
+
self.app.on_startup.append(self.client.startup)
|
|
76
|
+
self.app.on_cleanup.append(self.client.cleanup)
|
|
77
|
+
|
|
78
|
+
self.runner = None
|
|
79
|
+
self.site = None
|
|
80
|
+
|
|
81
|
+
async def process_update(self, data: dict):
|
|
82
|
+
"""
|
|
83
|
+
Parse and dispatch an incoming update.
|
|
84
|
+
|
|
85
|
+
Parameters:
|
|
86
|
+
data (dict):
|
|
87
|
+
Raw JSON data received from Rubika API.
|
|
88
|
+
|
|
89
|
+
Note:
|
|
90
|
+
- For inline messages, creates an InlineMessage object
|
|
91
|
+
- For other updates, creates an Update object
|
|
92
|
+
- Dispatches the parsed update to client's handlers
|
|
93
|
+
- Binds the client to the parsed object
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
.. code-block:: python
|
|
97
|
+
# Manual processing (for testing)
|
|
98
|
+
await server.process_update({
|
|
99
|
+
"update": {
|
|
100
|
+
"chat_id": "b0123456789",
|
|
101
|
+
"new_message": {...}
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
"""
|
|
105
|
+
if "inline_message" in data:
|
|
106
|
+
update = rubigram.types.InlineMessage.parse(
|
|
107
|
+
data["inline_message"], self.client
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
update = rubigram.types.Update.parse(data["update"], self.client)
|
|
111
|
+
|
|
112
|
+
await self.client.dispatcher(update)
|
|
113
|
+
|
|
114
|
+
def receive_data(self):
|
|
115
|
+
"""
|
|
116
|
+
Create a request handler for incoming webhook data.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
callable: An async function that handles HTTP POST requests.
|
|
120
|
+
|
|
121
|
+
Note:
|
|
122
|
+
The handler:
|
|
123
|
+
1. Parses JSON from request body
|
|
124
|
+
2. Logs the received data
|
|
125
|
+
3. Processes the update asynchronously
|
|
126
|
+
4. Returns JSON response with status
|
|
127
|
+
5. Catches and logs any processing errors
|
|
128
|
+
"""
|
|
129
|
+
async def wrapper(request: Request):
|
|
130
|
+
try:
|
|
131
|
+
data = await request.json()
|
|
132
|
+
logger.debug("Receive data from webhook, data=%s", data)
|
|
133
|
+
await self.process_update(data)
|
|
134
|
+
return json_response({"status": "OK", "data": data})
|
|
135
|
+
except Exception as error:
|
|
136
|
+
logger.error(
|
|
137
|
+
"Error receive data from webhook, error=%s", error)
|
|
138
|
+
return json_response({"status": "ERROR", "errcor": error})
|
|
139
|
+
return wrapper
|
|
140
|
+
|
|
141
|
+
def setup_routes(self):
|
|
142
|
+
"""
|
|
143
|
+
Configure HTTP routes for all update types.
|
|
144
|
+
|
|
145
|
+
Creates POST endpoints for each update type defined in
|
|
146
|
+
`rubigram.enums.UpdateEndpointType`. Each endpoint uses the
|
|
147
|
+
same handler to receive and process updates.
|
|
148
|
+
|
|
149
|
+
Note:
|
|
150
|
+
Endpoint paths correspond to the enum values (e.g., "/newMessage",
|
|
151
|
+
"/updateMessage", "/inlineMessage").
|
|
152
|
+
"""
|
|
153
|
+
for i in rubigram.enums.UpdateEndpointType:
|
|
154
|
+
handler = self.receive_data()
|
|
155
|
+
self.routes.post("/{}".format(i.value))(handler)
|
|
156
|
+
self.app.add_routes(self.routes)
|
|
157
|
+
|
|
158
|
+
async def start(self):
|
|
159
|
+
"""
|
|
160
|
+
Start the webhook server.
|
|
161
|
+
|
|
162
|
+
This method:
|
|
163
|
+
1. Sets up all routes
|
|
164
|
+
2. Creates and configures the AppRunner
|
|
165
|
+
3. Starts the TCP site
|
|
166
|
+
4. Logs server startup information
|
|
167
|
+
|
|
168
|
+
Note:
|
|
169
|
+
Also triggers the client's startup handlers via the
|
|
170
|
+
app.on_startup callback.
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
.. code-block:: python
|
|
174
|
+
await server.start()
|
|
175
|
+
print(f"Server running on {server.host}:{server.port}")
|
|
176
|
+
"""
|
|
177
|
+
self.setup_routes()
|
|
178
|
+
self.runner = AppRunner(self.app)
|
|
179
|
+
await self.runner.setup()
|
|
180
|
+
self.site = TCPSite(self.runner, self.host, self.port)
|
|
181
|
+
await self.site.start()
|
|
182
|
+
|
|
183
|
+
async def stop(self):
|
|
184
|
+
"""
|
|
185
|
+
Stop the webhook server gracefully.
|
|
186
|
+
|
|
187
|
+
This method:
|
|
188
|
+
1. Cleans up the AppRunner
|
|
189
|
+
2. Logs server shutdown
|
|
190
|
+
3. Sets runner and site to None
|
|
191
|
+
|
|
192
|
+
Note:
|
|
193
|
+
Also triggers the client's cleanup handlers via the
|
|
194
|
+
app.on_cleanup callback.
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
.. code-block:: python
|
|
198
|
+
await server.stop()
|
|
199
|
+
print("Server stopped")
|
|
200
|
+
"""
|
|
201
|
+
if self.runner:
|
|
202
|
+
await self.runner.cleanup()
|
|
203
|
+
|
|
204
|
+
async def run(self):
|
|
205
|
+
"""
|
|
206
|
+
Run the server indefinitely until interrupted.
|
|
207
|
+
|
|
208
|
+
This method:
|
|
209
|
+
1. Starts the server
|
|
210
|
+
2. Waits on an event (blocks forever)
|
|
211
|
+
3. Handles cancellation gracefully
|
|
212
|
+
4. Ensures server is stopped on exit
|
|
213
|
+
|
|
214
|
+
Note:
|
|
215
|
+
Used internally by `run_server()`.
|
|
216
|
+
"""
|
|
217
|
+
await self.start()
|
|
218
|
+
try:
|
|
219
|
+
await asyncio.Event().wait()
|
|
220
|
+
except asyncio.CancelledError:
|
|
221
|
+
pass
|
|
222
|
+
finally:
|
|
223
|
+
await self.stop()
|
|
224
|
+
|
|
225
|
+
def run_server(self, set_endpoint: bool = True):
|
|
226
|
+
"""
|
|
227
|
+
Run the server in the main thread (blocking call).
|
|
228
|
+
|
|
229
|
+
This method:
|
|
230
|
+
1. Creates a new asyncio event loop
|
|
231
|
+
2. Runs the server indefinitely
|
|
232
|
+
3. Handles KeyboardInterrupt for graceful shutdown
|
|
233
|
+
4. Closes the event loop on exit
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
.. code-block:: python
|
|
237
|
+
# This blocks until Ctrl+C is pressed
|
|
238
|
+
server.run_server()
|
|
239
|
+
"""
|
|
240
|
+
self.client.set_new_endpoint = set_endpoint
|
|
241
|
+
try:
|
|
242
|
+
logger.info("Start server in %s", self.client.webhook)
|
|
243
|
+
asyncio.run(self.run())
|
|
244
|
+
except KeyboardInterrupt:
|
|
245
|
+
logger.info("Stop server in %s", self.client.webhook)
|
rubigram/state/state.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from .storage import Storage
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class State:
|
|
14
|
+
"""
|
|
15
|
+
Convenience wrapper for managing individual user conversation states.
|
|
16
|
+
|
|
17
|
+
This class provides a user-specific interface to the Storage class,
|
|
18
|
+
simplifying state management for individual users in conversation flows.
|
|
19
|
+
It automatically handles user identification and delegates operations
|
|
20
|
+
to the underlying storage system.
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
storage (Storage):
|
|
24
|
+
The storage instance where states are persisted.
|
|
25
|
+
user_id (str):
|
|
26
|
+
Unique identifier for the user (typically chat_id).
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
storage (Storage): Reference to the storage backend.
|
|
30
|
+
user_id (str): The user identifier for this state instance.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
.. code-block:: python
|
|
34
|
+
# Get a State instance for a specific user
|
|
35
|
+
user_state = State(storage, user_id="b0123456789")
|
|
36
|
+
|
|
37
|
+
# Set the user's state with additional data
|
|
38
|
+
await user_state.set(
|
|
39
|
+
state="collecting_name",
|
|
40
|
+
attempts=2,
|
|
41
|
+
last_prompt="Please enter your full name:"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Retrieve the user's current state
|
|
45
|
+
current_state = await user_state.get()
|
|
46
|
+
|
|
47
|
+
# Clear the user's state (e.g., after conversation completion)
|
|
48
|
+
await user_state.delete()
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
storage: "Storage",
|
|
54
|
+
user_id: str
|
|
55
|
+
):
|
|
56
|
+
self.storage = storage
|
|
57
|
+
self.user_id = user_id
|
|
58
|
+
|
|
59
|
+
async def set(self, state: str, **kwargs):
|
|
60
|
+
"""
|
|
61
|
+
Set or update the conversation state for this user.
|
|
62
|
+
|
|
63
|
+
Parameters:
|
|
64
|
+
state (str):
|
|
65
|
+
The conversation state identifier (e.g., "awaiting_input",
|
|
66
|
+
"processing_data", "completed").
|
|
67
|
+
**kwargs:
|
|
68
|
+
Arbitrary key-value data to store with the state.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
bool: True if the state was successfully set.
|
|
72
|
+
|
|
73
|
+
Note:
|
|
74
|
+
This method overwrites any existing state for this user.
|
|
75
|
+
Use `get()` first if you need to preserve existing data.
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
.. code-block:: python
|
|
79
|
+
await user_state.set(
|
|
80
|
+
state="collecting_payment",
|
|
81
|
+
amount=10000,
|
|
82
|
+
currency="IRT",
|
|
83
|
+
invoice_id="inv_12345"
|
|
84
|
+
)
|
|
85
|
+
"""
|
|
86
|
+
await self.storage.set_state(self.user_id, state, **kwargs)
|
|
87
|
+
|
|
88
|
+
async def get(self):
|
|
89
|
+
"""
|
|
90
|
+
Retrieve the current conversation state and data for this user.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Optional[dict]: The stored state data if found, None otherwise.
|
|
94
|
+
Structure: {"state": str, "data": dict}
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
.. code-block:: python
|
|
98
|
+
data = await user_state.get()
|
|
99
|
+
if data:
|
|
100
|
+
current_state = data["state"] # e.g., "awaiting_confirmation"
|
|
101
|
+
user_data = data["data"] # Additional stored data
|
|
102
|
+
"""
|
|
103
|
+
return await self.storage.get_state(self.user_id)
|
|
104
|
+
|
|
105
|
+
async def delete(self):
|
|
106
|
+
"""
|
|
107
|
+
Remove the conversation state for this user from storage.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
bool: True if the state was deleted, False if it didn't exist.
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
.. code-block:: python
|
|
114
|
+
# Reset user's conversation
|
|
115
|
+
await user_state.delete()
|
|
116
|
+
|
|
117
|
+
# Or after completing a workflow
|
|
118
|
+
if workflow_completed:
|
|
119
|
+
await user_state.delete()
|
|
120
|
+
"""
|
|
121
|
+
await self.storage.delete_state(self.user_id)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from aiocache import Cache
|
|
7
|
+
from aiocache.serializers import JsonSerializer
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Storage:
|
|
15
|
+
"""
|
|
16
|
+
Asynchronous storage for managing user conversation states.
|
|
17
|
+
|
|
18
|
+
This class provides a simple key-value storage using aiocache to persist
|
|
19
|
+
user conversation states and associated data. States are stored with
|
|
20
|
+
configurable TTL (time-to-live) and automatically serialized to JSON.
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
ttl (int, optional):
|
|
24
|
+
Time-to-live for stored states in seconds. After this time,
|
|
25
|
+
states are automatically evicted from cache. Defaults to 3600
|
|
26
|
+
(1 hour).
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
cache (Cache):
|
|
30
|
+
The underlying aiocache instance configured for memory storage
|
|
31
|
+
with JSON serialization.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
.. code-block:: python
|
|
35
|
+
# Create storage with custom TTL
|
|
36
|
+
storage = Storage(ttl=7200) # 2 hours TTL
|
|
37
|
+
|
|
38
|
+
# Set user state with additional data
|
|
39
|
+
await storage.set_state(
|
|
40
|
+
user_id="b0123456789",
|
|
41
|
+
state="waiting_for_name",
|
|
42
|
+
name="John",
|
|
43
|
+
age=25
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Get user state
|
|
47
|
+
state_data = await storage.get_state("b0123456789")
|
|
48
|
+
# Returns: {"state": "waiting_for_name", "data": {"name": "John", "age": 25}}
|
|
49
|
+
|
|
50
|
+
# Delete user state
|
|
51
|
+
await storage.delete_state("b0123456789")
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, ttl: int = 3600):
|
|
55
|
+
self.cache = Cache(
|
|
56
|
+
Cache.MEMORY,
|
|
57
|
+
serializer=JsonSerializer(),
|
|
58
|
+
ttl=ttl
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def set_state(self, user_id: str, state: str, **kwargs):
|
|
62
|
+
"""
|
|
63
|
+
Set or update a user's conversation state with optional data.
|
|
64
|
+
|
|
65
|
+
Parameters:
|
|
66
|
+
user_id (str):
|
|
67
|
+
Unique identifier for the user (typically chat_id).
|
|
68
|
+
state (str):
|
|
69
|
+
The conversation state identifier (e.g., "waiting_for_name",
|
|
70
|
+
"collecting_data").
|
|
71
|
+
**kwargs:
|
|
72
|
+
Arbitrary key-value pairs to store alongside the state.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
bool: True if the state was successfully set.
|
|
76
|
+
|
|
77
|
+
Note:
|
|
78
|
+
The state is stored with the key format: "state:{user_id}"
|
|
79
|
+
Example: "state:b0123456789"
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
.. code-block:: python
|
|
83
|
+
await storage.set_state(
|
|
84
|
+
user_id="b0123456789",
|
|
85
|
+
state="collecting_preferences",
|
|
86
|
+
language="fa",
|
|
87
|
+
theme="dark",
|
|
88
|
+
step=3
|
|
89
|
+
)
|
|
90
|
+
"""
|
|
91
|
+
payload = {"state": state, "data": kwargs}
|
|
92
|
+
return await self.cache.set("state:{}".format(user_id), payload)
|
|
93
|
+
|
|
94
|
+
async def get_state(self, user_id: str):
|
|
95
|
+
"""
|
|
96
|
+
Retrieve a user's conversation state and associated data.
|
|
97
|
+
|
|
98
|
+
Parameters:
|
|
99
|
+
user_id (str):
|
|
100
|
+
Unique identifier for the user.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Optional[dict]: The stored state data if found, None otherwise.
|
|
104
|
+
Structure: {"state": str, "data": dict}
|
|
105
|
+
|
|
106
|
+
Example:
|
|
107
|
+
.. code-block:: python
|
|
108
|
+
data = await storage.get_state("b0123456789")
|
|
109
|
+
if data:
|
|
110
|
+
current_state = data["state"] # e.g., "waiting_for_name"
|
|
111
|
+
user_data = data["data"] # e.g., {"name": "John"}
|
|
112
|
+
"""
|
|
113
|
+
await self.cache.get("state:{}".format(user_id))
|
|
114
|
+
|
|
115
|
+
async def delete_state(self, user_id: str):
|
|
116
|
+
"""
|
|
117
|
+
Delete a user's conversation state from storage.
|
|
118
|
+
|
|
119
|
+
Parameters:
|
|
120
|
+
user_id (str):
|
|
121
|
+
Unique identifier for the user.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
bool: True if the state was deleted, False if it didn't exist.
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
.. code-block:: python
|
|
128
|
+
# Clear user's conversation state
|
|
129
|
+
await storage.delete_state("b0123456789")
|
|
130
|
+
"""
|
|
131
|
+
await self.cache.delete("state:{}".format(user_id))
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from .bot import Bot
|
|
7
|
+
from .chat import Chat
|
|
8
|
+
from .file import File
|
|
9
|
+
from .bot_command import BotCommand
|
|
10
|
+
from .keypads import Keypad, KeypadRow
|
|
11
|
+
from .messages import Message, UMessage, InlineMessage
|
|
12
|
+
from .contact_message import ContactMessage
|
|
13
|
+
from .forwarded_from import ForwardedFrom
|
|
14
|
+
from .live_location import LiveLocation
|
|
15
|
+
from .location import Location
|
|
16
|
+
from .payment_status import PaymentStatus
|
|
17
|
+
from .poll_status import PollStatus
|
|
18
|
+
from .poll import Poll
|
|
19
|
+
from .sticker import Sticker
|
|
20
|
+
from .aux_data import AuxData
|
|
21
|
+
from .updates import Updates, Update
|
|
22
|
+
from .metadata import Metadata, MetadataParts
|
|
23
|
+
from .buttons import (
|
|
24
|
+
Button,
|
|
25
|
+
ButtonTextbox,
|
|
26
|
+
ButtonStringPicker,
|
|
27
|
+
ButtonSelection,
|
|
28
|
+
ButtonSelectionItem,
|
|
29
|
+
ButtonNumberPicker,
|
|
30
|
+
ButtonLocation,
|
|
31
|
+
ButtonCalendar
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"Bot",
|
|
37
|
+
"Chat",
|
|
38
|
+
"File",
|
|
39
|
+
"BotCommand",
|
|
40
|
+
"Button",
|
|
41
|
+
"ButtonCalendar",
|
|
42
|
+
"ButtonLocation",
|
|
43
|
+
"ButtonNumberPicker",
|
|
44
|
+
"ButtonSelection",
|
|
45
|
+
"ButtonSelectionItem",
|
|
46
|
+
"ButtonTextbox",
|
|
47
|
+
"ButtonStringPicker",
|
|
48
|
+
"KeypadRow",
|
|
49
|
+
"Keypad",
|
|
50
|
+
"Message",
|
|
51
|
+
"UMessage",
|
|
52
|
+
"InlineMessage",
|
|
53
|
+
"Sticker",
|
|
54
|
+
"Poll",
|
|
55
|
+
"PollStatus",
|
|
56
|
+
"PaymentStatus",
|
|
57
|
+
"Location",
|
|
58
|
+
"LiveLocation",
|
|
59
|
+
"ForwardedFrom",
|
|
60
|
+
"ContactMessage",
|
|
61
|
+
"AuxData",
|
|
62
|
+
"Updates",
|
|
63
|
+
"Update",
|
|
64
|
+
"Metadata",
|
|
65
|
+
"MetadataParts"
|
|
66
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from .config import Object
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class AuxData(Object):
|
|
13
|
+
"""
|
|
14
|
+
**Represents auxiliary data attached to a message.**
|
|
15
|
+
`from rubigram.types import AuxData`
|
|
16
|
+
|
|
17
|
+
AuxData is typically used to store metadata related to
|
|
18
|
+
buttons, interactions, or other inline components in a chat.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
start_id (`Optional[str]`):
|
|
22
|
+
An identifier for the start action, if applicable.
|
|
23
|
+
|
|
24
|
+
button_id (`Optional[str]`):
|
|
25
|
+
The ID of a button that triggered this auxiliary data, if applicable.
|
|
26
|
+
"""
|
|
27
|
+
start_id: Optional[str] = None
|
|
28
|
+
button_id: Optional[str] = None
|
rubigram/types/bot.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from .config import Object
|
|
11
|
+
import rubigram
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Bot(Object):
|
|
16
|
+
"""
|
|
17
|
+
**Represents a bot in Rubigram.**
|
|
18
|
+
`from rubigram.types import Bot`
|
|
19
|
+
|
|
20
|
+
Contains basic information about a bot, including its identifiers,
|
|
21
|
+
description, avatar, and sharing information.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
bot_id (`str`):
|
|
25
|
+
Unique identifier for the bot.
|
|
26
|
+
|
|
27
|
+
bot_title (`str`):
|
|
28
|
+
Display title of the bot.
|
|
29
|
+
|
|
30
|
+
avatar (`Optional[rubigram.types.File]`):
|
|
31
|
+
The bot's avatar file object.
|
|
32
|
+
|
|
33
|
+
description (`Optional[str]`):
|
|
34
|
+
Description of the bot.
|
|
35
|
+
|
|
36
|
+
username (`str`):
|
|
37
|
+
The bot's username.
|
|
38
|
+
|
|
39
|
+
start_message (`Optional[str]`):
|
|
40
|
+
Default start message of the bot.
|
|
41
|
+
|
|
42
|
+
share_url (`str`):
|
|
43
|
+
Public URL for sharing the bot.
|
|
44
|
+
"""
|
|
45
|
+
bot_id: str = None
|
|
46
|
+
bot_title: Optional[str] = None
|
|
47
|
+
avatar: Optional[rubigram.types.File] = None
|
|
48
|
+
description: Optional[str] = None
|
|
49
|
+
username: str = None
|
|
50
|
+
start_message: Optional[str] = None
|
|
51
|
+
share_url: str = None
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from .config import Object
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class BotCommand(Object):
|
|
12
|
+
"""
|
|
13
|
+
**Represents a command for a bot in Rubigram.**
|
|
14
|
+
`from rubigram.types import Bot`
|
|
15
|
+
|
|
16
|
+
Contains the command text and its description. Typically used
|
|
17
|
+
to show available commands in the bot interface.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
command (`str`):
|
|
21
|
+
The command text (e.g., '/start').
|
|
22
|
+
description (`str`):
|
|
23
|
+
Description of what the command does.
|
|
24
|
+
"""
|
|
25
|
+
command: str
|
|
26
|
+
description: str
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from .button_calendar import ButtonCalendar
|
|
7
|
+
from .button_location import ButtonLocation
|
|
8
|
+
from .button_number_picker import ButtonNumberPicker
|
|
9
|
+
from .button_selection_item import ButtonSelectionItem
|
|
10
|
+
from .button_selection import ButtonSelection
|
|
11
|
+
from .button_string_picker import ButtonStringPicker
|
|
12
|
+
from .button_text_box import ButtonTextbox
|
|
13
|
+
from .button import Button
|