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,442 @@
|
|
|
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 (
|
|
7
|
+
Optional,
|
|
8
|
+
TypeVar,
|
|
9
|
+
Union,
|
|
10
|
+
Type,
|
|
11
|
+
Any,
|
|
12
|
+
get_type_hints,
|
|
13
|
+
get_origin,
|
|
14
|
+
get_args
|
|
15
|
+
)
|
|
16
|
+
from dataclasses import fields
|
|
17
|
+
from json import dumps
|
|
18
|
+
import sys
|
|
19
|
+
import rubigram
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
T = TypeVar("T", bound="Object")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
FIELDS_CACHE: dict[type, tuple] = {}
|
|
26
|
+
TYPE_HINTS_CACHE: dict[type, dict] = {}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_fields(cls: type):
|
|
30
|
+
"""
|
|
31
|
+
Get cached dataclass fields for a class.
|
|
32
|
+
|
|
33
|
+
This function caches the result of `dataclasses.fields()` to avoid
|
|
34
|
+
repeated computation for the same class.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
cls (type):
|
|
38
|
+
The dataclass type to get fields for.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
tuple[Field]: A tuple of field objects for the dataclass.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
.. code-block:: python
|
|
45
|
+
fields = get_fields(User)
|
|
46
|
+
for field in fields:
|
|
47
|
+
print(field.name, field.type)
|
|
48
|
+
"""
|
|
49
|
+
cached = FIELDS_CACHE.get(cls)
|
|
50
|
+
if cached is None:
|
|
51
|
+
cached = fields(cls)
|
|
52
|
+
FIELDS_CACHE[cls] = cached
|
|
53
|
+
return cached
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_cached_type_hints(cls: type) -> dict:
|
|
57
|
+
"""
|
|
58
|
+
Get cached type hints for a class.
|
|
59
|
+
|
|
60
|
+
This function retrieves and caches type annotations for a class,
|
|
61
|
+
respecting the module's global namespace for forward references.
|
|
62
|
+
|
|
63
|
+
Parameters:
|
|
64
|
+
cls (type):
|
|
65
|
+
The class to get type hints for.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
dict[str, type]: A dictionary mapping field names to their types.
|
|
69
|
+
|
|
70
|
+
Note:
|
|
71
|
+
If type hints cannot be retrieved (e.g., due to forward references
|
|
72
|
+
in a different module), an empty dictionary is returned and cached.
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
.. code-block:: python
|
|
76
|
+
hints = get_cached_type_hints(User)
|
|
77
|
+
print(hints["user_id"]) # <class 'int'>
|
|
78
|
+
"""
|
|
79
|
+
cached = TYPE_HINTS_CACHE.get(cls)
|
|
80
|
+
if cached is None:
|
|
81
|
+
try:
|
|
82
|
+
module = sys.modules[cls.__module__]
|
|
83
|
+
cached = get_type_hints(cls, globalns=module.__dict__)
|
|
84
|
+
except Exception:
|
|
85
|
+
cached = {}
|
|
86
|
+
TYPE_HINTS_CACHE[cls] = cached
|
|
87
|
+
return cached
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def strip_optional(tp):
|
|
91
|
+
"""
|
|
92
|
+
Extract the non-None type from an Optional annotation.
|
|
93
|
+
|
|
94
|
+
Given a Union type (typically from Optional[T]), returns T.
|
|
95
|
+
If no non-None type is found, returns the original type.
|
|
96
|
+
|
|
97
|
+
Parameters:
|
|
98
|
+
tp (type):
|
|
99
|
+
The type annotation, e.g., Optional[int] or Union[int, None].
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
type: The non-None type from the union, or the original type.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
.. code-block:: python
|
|
106
|
+
t = strip_optional(Optional[int]) # returns <class 'int'>
|
|
107
|
+
t = strip_optional(Union[int, str, None]) # returns <class 'int'>
|
|
108
|
+
"""
|
|
109
|
+
for arg in get_args(tp):
|
|
110
|
+
if arg is not type(None):
|
|
111
|
+
return arg
|
|
112
|
+
return tp
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def is_object_type(tp) -> bool:
|
|
116
|
+
"""
|
|
117
|
+
Check if a type is a subclass of Object.
|
|
118
|
+
|
|
119
|
+
Parameters:
|
|
120
|
+
tp (type):
|
|
121
|
+
The type to check.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
bool: True if tp is a class and subclass of Object, False otherwise.
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
.. code-block:: python
|
|
128
|
+
is_object_type(User) # True if User extends Object
|
|
129
|
+
is_object_type(int) # False
|
|
130
|
+
"""
|
|
131
|
+
return isinstance(tp, type) and issubclass(tp, Object)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def clear_none(data: Any):
|
|
135
|
+
"""
|
|
136
|
+
Recursively remove None values from dictionaries and lists.
|
|
137
|
+
|
|
138
|
+
This function deeply traverses nested structures and removes:
|
|
139
|
+
- Keys with None values from dictionaries
|
|
140
|
+
- None items from lists
|
|
141
|
+
|
|
142
|
+
Parameters:
|
|
143
|
+
data (Any):
|
|
144
|
+
The data structure to clean. Can be dict, list, or any other type.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Any: The cleaned data structure with None values removed.
|
|
148
|
+
|
|
149
|
+
Note:
|
|
150
|
+
Returns non-dict/list values unchanged.
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
.. code-block:: python
|
|
154
|
+
data = {"a": 1, "b": None, "c": [1, None, {"d": None}]}
|
|
155
|
+
cleaned = clear_none(data)
|
|
156
|
+
# Result: {"a": 1, "c": [1, {}]}
|
|
157
|
+
"""
|
|
158
|
+
if isinstance(data, dict):
|
|
159
|
+
return {
|
|
160
|
+
key: clear_none(value)
|
|
161
|
+
for key, value in data.items()
|
|
162
|
+
if value is not None
|
|
163
|
+
}
|
|
164
|
+
if isinstance(data, list):
|
|
165
|
+
return [
|
|
166
|
+
clear_none(i)
|
|
167
|
+
for i in data
|
|
168
|
+
if i is not None
|
|
169
|
+
]
|
|
170
|
+
return data
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class Object:
|
|
174
|
+
"""
|
|
175
|
+
Base class for all API data models in Rubigram.
|
|
176
|
+
|
|
177
|
+
This class provides serialization, deserialization, and client binding
|
|
178
|
+
functionality for dataclass-based models. It handles complex type
|
|
179
|
+
annotations including Optional, List, and nested Object types.
|
|
180
|
+
|
|
181
|
+
The class is designed to work with Rubika API responses, automatically
|
|
182
|
+
parsing JSON data into Python objects and vice versa.
|
|
183
|
+
|
|
184
|
+
Attributes:
|
|
185
|
+
client (Optional[rubigram.Client]):
|
|
186
|
+
The client instance bound to this object. Used for making
|
|
187
|
+
subsequent API calls from within the object context.
|
|
188
|
+
|
|
189
|
+
Slots:
|
|
190
|
+
__slots__ = ("client",): Optimizes memory usage by restricting
|
|
191
|
+
dynamic attribute creation.
|
|
192
|
+
|
|
193
|
+
Example:
|
|
194
|
+
.. code-block:: python
|
|
195
|
+
@dataclass
|
|
196
|
+
class User(Object):
|
|
197
|
+
user_id: int
|
|
198
|
+
name: str
|
|
199
|
+
profile: Optional[Profile] = None
|
|
200
|
+
|
|
201
|
+
# Parse from API response
|
|
202
|
+
user = User.parse({"user_id": 123, "name": "John"})
|
|
203
|
+
|
|
204
|
+
# Serialize to JSON
|
|
205
|
+
json_str = user.jsonify()
|
|
206
|
+
|
|
207
|
+
# Bind to client for API operations
|
|
208
|
+
user.bind(client)
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
__slots__ = ("client",)
|
|
212
|
+
|
|
213
|
+
def bind(self, client: "rubigram.Client") -> None:
|
|
214
|
+
"""
|
|
215
|
+
Bind a client instance to this object and all nested Object instances.
|
|
216
|
+
|
|
217
|
+
This method recursively traverses the object's fields and binds the
|
|
218
|
+
client to any nested Object instances or lists containing Objects.
|
|
219
|
+
|
|
220
|
+
Parameters:
|
|
221
|
+
client (rubigram.Client):
|
|
222
|
+
The client instance to bind.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
None
|
|
226
|
+
|
|
227
|
+
Note:
|
|
228
|
+
The client is stored using `object.__setattr__` to bypass
|
|
229
|
+
the restrictions of `__slots__`.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
.. code-block:: python
|
|
233
|
+
user.bind(client)
|
|
234
|
+
# Now user and all nested objects can use client for API calls
|
|
235
|
+
"""
|
|
236
|
+
object.__setattr__(self, "client", client)
|
|
237
|
+
|
|
238
|
+
for field in get_fields(self.__class__):
|
|
239
|
+
value = getattr(self, field.name)
|
|
240
|
+
|
|
241
|
+
if isinstance(value, Object):
|
|
242
|
+
value.bind(client)
|
|
243
|
+
|
|
244
|
+
elif isinstance(value, list):
|
|
245
|
+
for item in value:
|
|
246
|
+
if isinstance(item, Object):
|
|
247
|
+
item.bind(client)
|
|
248
|
+
|
|
249
|
+
def as_dict(self) -> dict:
|
|
250
|
+
"""
|
|
251
|
+
Convert the object to a dictionary representation suitable for JSON serialization.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
dict: A dictionary where:
|
|
255
|
+
- Object instances are converted to dict with "_" type marker
|
|
256
|
+
- Lists are recursively processed
|
|
257
|
+
- Simple values are included as-is
|
|
258
|
+
|
|
259
|
+
Note:
|
|
260
|
+
This method adds a "_" key with the class name for Object instances,
|
|
261
|
+
matching Rubika API's type indicator convention.
|
|
262
|
+
|
|
263
|
+
Example:
|
|
264
|
+
.. code-block:: python
|
|
265
|
+
user = User(user_id=123, name="John")
|
|
266
|
+
data = user.as_dict()
|
|
267
|
+
# Result: {"user_id": 123, "name": "John"}
|
|
268
|
+
|
|
269
|
+
profile = Profile(avatar="url")
|
|
270
|
+
user.profile = profile
|
|
271
|
+
data = user.as_dict()
|
|
272
|
+
# Result: {"user_id": 123, "name": "John",
|
|
273
|
+
# "profile": {"_": "Profile", "avatar": "url"}}
|
|
274
|
+
"""
|
|
275
|
+
result = {}
|
|
276
|
+
|
|
277
|
+
for field in get_fields(self.__class__):
|
|
278
|
+
value = getattr(self, field.name)
|
|
279
|
+
|
|
280
|
+
if isinstance(value, Object):
|
|
281
|
+
result[field.name] = {
|
|
282
|
+
"_": value.__class__.__name__,
|
|
283
|
+
**value.as_dict()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
elif isinstance(value, list):
|
|
287
|
+
result[field.name] = [
|
|
288
|
+
(
|
|
289
|
+
{"_": v.__class__.__name__, **v.as_dict()}
|
|
290
|
+
if isinstance(v, Object)
|
|
291
|
+
else v
|
|
292
|
+
)
|
|
293
|
+
for v in value
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
else:
|
|
297
|
+
result[field.name] = value
|
|
298
|
+
|
|
299
|
+
return result
|
|
300
|
+
|
|
301
|
+
def jsonify(self, exclude_none: bool = True) -> str:
|
|
302
|
+
"""
|
|
303
|
+
Serialize the object to a JSON string.
|
|
304
|
+
|
|
305
|
+
Parameters:
|
|
306
|
+
exclude_none (bool, optional):
|
|
307
|
+
If True (default), removes None values from the output.
|
|
308
|
+
If False, includes None values in the JSON.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
str: A formatted JSON string with:
|
|
312
|
+
- ASCII characters preserved
|
|
313
|
+
- 4-space indentation
|
|
314
|
+
- Type marker "_" included
|
|
315
|
+
|
|
316
|
+
Note:
|
|
317
|
+
Uses `default=str` to handle non-serializable types by
|
|
318
|
+
converting them to strings.
|
|
319
|
+
|
|
320
|
+
Example:
|
|
321
|
+
.. code-block:: python
|
|
322
|
+
user = User(user_id=123, name="John", profile=None)
|
|
323
|
+
json_str = user.jsonify()
|
|
324
|
+
# Result: '{"_": "User", "user_id": 123, "name": "John"}'
|
|
325
|
+
|
|
326
|
+
json_str = user.jsonify(exclude_none=False)
|
|
327
|
+
# Result: '{"_": "User", "user_id": 123, "name": "John", "profile": null}'
|
|
328
|
+
"""
|
|
329
|
+
data = self.as_dict()
|
|
330
|
+
|
|
331
|
+
if exclude_none:
|
|
332
|
+
data = clear_none(data)
|
|
333
|
+
|
|
334
|
+
return dumps(
|
|
335
|
+
{"_": self.__class__.__name__, **data},
|
|
336
|
+
ensure_ascii=False,
|
|
337
|
+
indent=4,
|
|
338
|
+
default=str
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
@classmethod
|
|
342
|
+
def parse(cls: Type[T], data: dict, client: Optional["rubigram.Client"] = None) -> T:
|
|
343
|
+
"""
|
|
344
|
+
Parse a dictionary (typically from JSON API response) into an Object instance.
|
|
345
|
+
|
|
346
|
+
This is the primary factory method for creating Object instances from
|
|
347
|
+
Rubika API responses. It handles:
|
|
348
|
+
- Type conversion based on field annotations
|
|
349
|
+
- Nested Object parsing
|
|
350
|
+
- Optional and List type handling
|
|
351
|
+
- Client binding
|
|
352
|
+
|
|
353
|
+
Parameters:
|
|
354
|
+
data (dict):
|
|
355
|
+
The dictionary data to parse, typically from `response.json()`.
|
|
356
|
+
client (Optional[rubigram.Client], optional):
|
|
357
|
+
Client instance to bind to the created object and its children.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
T: An instance of the calling class, populated with data.
|
|
361
|
+
|
|
362
|
+
Raises:
|
|
363
|
+
TypeError: If data cannot be parsed according to field types.
|
|
364
|
+
KeyError: If required fields are missing (depending on dataclass defaults).
|
|
365
|
+
|
|
366
|
+
Note:
|
|
367
|
+
- The "_" key (type marker) is automatically stripped from input data
|
|
368
|
+
- For lists of Objects, each item is parsed recursively
|
|
369
|
+
- Missing fields are set to None
|
|
370
|
+
|
|
371
|
+
Example:
|
|
372
|
+
.. code-block:: python
|
|
373
|
+
json_data = {
|
|
374
|
+
"_": "User",
|
|
375
|
+
"user_id": 123,
|
|
376
|
+
"name": "John",
|
|
377
|
+
"profile": {"_": "Profile", "avatar": "url"}
|
|
378
|
+
}
|
|
379
|
+
user = User.parse(json_data, client=my_client)
|
|
380
|
+
print(user.user_id) # 123
|
|
381
|
+
print(user.profile.avatar) # "url"
|
|
382
|
+
"""
|
|
383
|
+
if not data:
|
|
384
|
+
obj = cls()
|
|
385
|
+
if client:
|
|
386
|
+
obj.bind(client)
|
|
387
|
+
return obj
|
|
388
|
+
|
|
389
|
+
data = {key: value for key, value in data.items() if key != "_"}
|
|
390
|
+
init_data = {}
|
|
391
|
+
|
|
392
|
+
fields_ = get_fields(cls)
|
|
393
|
+
type_hints = get_cached_type_hints(cls)
|
|
394
|
+
|
|
395
|
+
for field in fields_:
|
|
396
|
+
raw = data.get(field.name)
|
|
397
|
+
|
|
398
|
+
if raw is None:
|
|
399
|
+
init_data[field.name] = None
|
|
400
|
+
continue
|
|
401
|
+
|
|
402
|
+
field_type = type_hints.get(field.name, field.type)
|
|
403
|
+
origin = get_origin(field_type)
|
|
404
|
+
|
|
405
|
+
if isinstance(raw, dict) and "_" in raw:
|
|
406
|
+
raw = {k: v for k, v in raw.items() if k != "_"}
|
|
407
|
+
|
|
408
|
+
if origin is list and isinstance(raw, list):
|
|
409
|
+
inner = get_args(field_type)[0]
|
|
410
|
+
inner_origin = get_origin(inner)
|
|
411
|
+
|
|
412
|
+
if inner_origin is Union:
|
|
413
|
+
inner = strip_optional(inner)
|
|
414
|
+
|
|
415
|
+
if is_object_type(inner):
|
|
416
|
+
init_data[field.name] = [
|
|
417
|
+
inner.parse(v, client) if isinstance(v, dict) else v
|
|
418
|
+
for v in raw
|
|
419
|
+
]
|
|
420
|
+
else:
|
|
421
|
+
init_data[field.name] = raw
|
|
422
|
+
|
|
423
|
+
elif origin is Union:
|
|
424
|
+
inner = strip_optional(field_type)
|
|
425
|
+
|
|
426
|
+
if is_object_type(inner) and isinstance(raw, dict):
|
|
427
|
+
init_data[field.name] = inner.parse(raw, client)
|
|
428
|
+
else:
|
|
429
|
+
init_data[field.name] = raw
|
|
430
|
+
|
|
431
|
+
else:
|
|
432
|
+
init_data[field.name] = raw
|
|
433
|
+
|
|
434
|
+
obj = cls(**init_data)
|
|
435
|
+
|
|
436
|
+
if client:
|
|
437
|
+
obj.bind(client)
|
|
438
|
+
|
|
439
|
+
return obj
|
|
440
|
+
|
|
441
|
+
def __str__(self) -> str:
|
|
442
|
+
return self.jsonify()
|
|
@@ -0,0 +1,29 @@
|
|
|
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 ContactMessage(Object):
|
|
13
|
+
"""
|
|
14
|
+
**Represents a contact message containing user contact information.**
|
|
15
|
+
`from rubigram.types import ContactMessage`
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
phone_number (`str`):
|
|
19
|
+
The contact's phone number.
|
|
20
|
+
|
|
21
|
+
first_name (`Optional[str]`):
|
|
22
|
+
The contact's first name.
|
|
23
|
+
|
|
24
|
+
last_name (`Optional[str]`):
|
|
25
|
+
The contact's last name.
|
|
26
|
+
"""
|
|
27
|
+
phone_number: str
|
|
28
|
+
first_name: Optional[str] = None
|
|
29
|
+
last_name: Optional[str] = None
|
rubigram/types/file.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
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 mimetypes
|
|
12
|
+
import rubigram
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class File(Object):
|
|
18
|
+
"""
|
|
19
|
+
**Represents a file object in Rubigram.**
|
|
20
|
+
`from rubigram.types import File`
|
|
21
|
+
|
|
22
|
+
Contains metadata about a file, such as its unique identifier,
|
|
23
|
+
name, size, and MIME type.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
file_id (`str`):
|
|
27
|
+
Unique identifier for the file.
|
|
28
|
+
|
|
29
|
+
file_name (`Optional[str]`):
|
|
30
|
+
Name of the file (e.g., 'photo.png').
|
|
31
|
+
|
|
32
|
+
size (`Optional[int]`):
|
|
33
|
+
Size of the file in bytes.
|
|
34
|
+
|
|
35
|
+
file_type (`Optional[str]`):
|
|
36
|
+
MIME type of the file (e.g., 'image/png', 'video/mp4').
|
|
37
|
+
Automatically detected from the file name if not provided.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
file_id: str
|
|
41
|
+
file_name: Optional[str] = None
|
|
42
|
+
size: Optional[int] = None
|
|
43
|
+
file_type: Optional[str] = None
|
|
44
|
+
|
|
45
|
+
def __post_init__(self):
|
|
46
|
+
if not self.file_name:
|
|
47
|
+
self.file_type = rubigram.enums.FileType.FILE
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
mime_type, _ = mimetypes.guess_type(self.file_name)
|
|
51
|
+
if not mime_type:
|
|
52
|
+
self.file_type = rubigram.enums.FileType.FILE
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
mime_main = mime_type.split("/")[0]
|
|
56
|
+
mime_sub = mime_type.split("/")[1]
|
|
57
|
+
|
|
58
|
+
if mime_main == "image":
|
|
59
|
+
|
|
60
|
+
if mime_sub == "gif":
|
|
61
|
+
self.file_type = rubigram.enums.FileType.GIF
|
|
62
|
+
|
|
63
|
+
else:
|
|
64
|
+
self.file_type = rubigram.enums.FileType.IMAGE
|
|
65
|
+
|
|
66
|
+
elif mime_main == "video":
|
|
67
|
+
self.file_type = rubigram.enums.FileType.VIDEO
|
|
68
|
+
|
|
69
|
+
elif mime_main == "audio":
|
|
70
|
+
|
|
71
|
+
if mime_sub in ["ogg", "amr", "m4a"]:
|
|
72
|
+
self.file_type = rubigram.enums.FileType.VOICE
|
|
73
|
+
|
|
74
|
+
else:
|
|
75
|
+
self.file_type = rubigram.enums.FileType.MUSIC
|
|
76
|
+
|
|
77
|
+
else:
|
|
78
|
+
self.file_type = rubigram.enums.FileType.FILE
|
|
@@ -0,0 +1,39 @@
|
|
|
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, Union
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from .config import Object
|
|
11
|
+
import rubigram
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ForwardedFrom(Object):
|
|
16
|
+
"""
|
|
17
|
+
**Represents information about the original sender of a forwarded message.**
|
|
18
|
+
`from rubigram.types import ForwardedFrom`
|
|
19
|
+
|
|
20
|
+
Includes the type of sender, the original message ID, and identifiers
|
|
21
|
+
for the chat or sender from which the message was forwarded.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
type_from (`Optional[rubigram.enums.ForwardedFromType]`):
|
|
25
|
+
Type of the original sender (User, Bot, Channel).
|
|
26
|
+
|
|
27
|
+
message_id (`Optional[str]`):
|
|
28
|
+
ID of the original message.
|
|
29
|
+
|
|
30
|
+
from_chat_id (`Optional[str]`):
|
|
31
|
+
Chat ID where the original message was sent.
|
|
32
|
+
|
|
33
|
+
from_sender_id (`Optional[str]`):
|
|
34
|
+
Sender ID of the original message.
|
|
35
|
+
"""
|
|
36
|
+
type_from: Optional[Union[str, rubigram.enums.ForwardedFromType]] = None
|
|
37
|
+
message_id: Optional[str] = None
|
|
38
|
+
from_chat_id: Optional[str] = None
|
|
39
|
+
from_sender_id: Optional[str] = None
|
|
@@ -0,0 +1,31 @@
|
|
|
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 dataclasses import dataclass, field
|
|
9
|
+
from ..config import Object
|
|
10
|
+
import rubigram
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Keypad(Object):
|
|
15
|
+
"""
|
|
16
|
+
**Represents a chat keypad, which may contain multiple rows of buttons.**
|
|
17
|
+
`from rubigram.types import Keypad`
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
rows (`list[rubigram.types.KeypadRow]`):
|
|
21
|
+
A list of KeypadRow objects representing the keypad layout.
|
|
22
|
+
|
|
23
|
+
resize_keyboard (`bool`):
|
|
24
|
+
Whether the keyboard should be resized to fit the screen. Defaults to True.
|
|
25
|
+
|
|
26
|
+
on_time_keyboard (`bool`):
|
|
27
|
+
Whether the keyboard should appear temporarily and disappear after use. Defaults to False.
|
|
28
|
+
"""
|
|
29
|
+
rows: list[rubigram.types.KeypadRow] = field(default_factory=list)
|
|
30
|
+
resize_keyboard: bool = True
|
|
31
|
+
on_time_keyboard: bool = False
|
|
@@ -0,0 +1,23 @@
|
|
|
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 dataclasses import dataclass, field
|
|
9
|
+
from ..config import Object
|
|
10
|
+
import rubigram
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class KeypadRow(Object):
|
|
15
|
+
"""
|
|
16
|
+
**Represents a single row of buttons in a chat keypad.**
|
|
17
|
+
`from rubigram.types import KeypadRow`
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
buttons (`list[rubigram.types.Button]`):
|
|
21
|
+
A list of Button objects in this row.
|
|
22
|
+
"""
|
|
23
|
+
buttons: list[rubigram.types.Button] = field(default_factory=list)
|
|
@@ -0,0 +1,44 @@
|
|
|
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, Union
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from .config import Object
|
|
11
|
+
import rubigram
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class LiveLocation(Object):
|
|
16
|
+
"""
|
|
17
|
+
**Represents a live location shared by a user.**
|
|
18
|
+
`from rubigram.types import LiveLocation`
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
start_time (`Optional[str]`):
|
|
22
|
+
The start time of the live location sharing.
|
|
23
|
+
|
|
24
|
+
live_period (`Optional[int]`):
|
|
25
|
+
Duration of the live location in seconds.
|
|
26
|
+
|
|
27
|
+
current_location (`Optional[rubigram.types.Location]`):
|
|
28
|
+
Current coordinates of the live location.
|
|
29
|
+
|
|
30
|
+
user_id (`Optional[str]`):
|
|
31
|
+
User ID of the person sharing the location.
|
|
32
|
+
|
|
33
|
+
status (`Optional[Union[str, rubigram.enums.LiveLocationStatus]]`):
|
|
34
|
+
Status of the live location (Live or Stopped).
|
|
35
|
+
|
|
36
|
+
last_update_time (`Optional[str]`):
|
|
37
|
+
Timestamp of the last location update.
|
|
38
|
+
"""
|
|
39
|
+
start_time: Optional[str] = None
|
|
40
|
+
live_period: Optional[int] = None
|
|
41
|
+
current_location: Optional[rubigram.types.Location] = None
|
|
42
|
+
user_id: Optional[str] = None
|
|
43
|
+
status: Optional[Union[str, rubigram.enums.LiveLocationStatus]] = None
|
|
44
|
+
last_update_time: Optional[str] = None
|