Rubka 0.1.1__py3-none-any.whl → 1.2.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.
rubka/api.py CHANGED
@@ -2,9 +2,59 @@ import requests
2
2
  from typing import List, Optional, Dict, Any, Literal
3
3
  from .exceptions import APIRequestError
4
4
  from .logger import logger
5
-
5
+ from typing import Callable
6
+ from .context import Message,InlineMessage
6
7
  API_URL = "https://botapi.rubika.ir/v3"
8
+ import sys
9
+ import subprocess
10
+ import requests
11
+ def install_package(package_name):
12
+ try:
13
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
14
+ return True
15
+ except Exception:return False
16
+
17
+ def get_importlib_metadata():
18
+ try:
19
+ from importlib.metadata import version, PackageNotFoundError
20
+ return version, PackageNotFoundError
21
+ except ImportError:
22
+ if install_package("importlib-metadata"):
23
+ try:
24
+ from importlib_metadata import version, PackageNotFoundError
25
+ return version, PackageNotFoundError
26
+ except ImportError:
27
+ return None, None
28
+ return None, None
7
29
 
30
+ version, PackageNotFoundError = get_importlib_metadata()
31
+ def get_installed_version(package_name: str) -> str:
32
+ if version is None:return "unknown"
33
+ try:
34
+ return version(package_name)
35
+ except PackageNotFoundError:
36
+ return None
37
+ def get_latest_version(package_name: str) -> str:
38
+ url = f"https://pypi.org/pypi/{package_name}/json"
39
+ try:
40
+ resp = requests.get(url, timeout=5)
41
+ resp.raise_for_status()
42
+ data = resp.json()
43
+ return data["info"]["version"]
44
+ except Exception:return None
45
+ def check_rubka_version():
46
+ package_name = "rubka"
47
+ installed_version = get_installed_version(package_name)
48
+ if installed_version is None:return
49
+ latest_version = get_latest_version(package_name)
50
+ if latest_version is None:return
51
+ if installed_version != latest_version:
52
+ print(f"\n\n⚠️ WARNING: Your installed version of '{package_name}' is outdated!")
53
+ print(f"Installed version: {installed_version}")
54
+ print(f"Latest version: {latest_version}")
55
+ print(f"Please update it using:\n\npip install --upgrade {package_name}\n")
56
+
57
+ check_rubka_version()
8
58
  class Robot:
9
59
  """
10
60
  Main class to interact with Rubika Bot API.
@@ -13,7 +63,14 @@ class Robot:
13
63
 
14
64
  def __init__(self, token: str):
15
65
  self.token = token
66
+ self._offset_id = None
16
67
  self.session = requests.Session()
68
+ self.sessions: Dict[str, Dict[str, Any]] = {}
69
+ self._callback_handler = None
70
+ self._message_handler = None
71
+ self._inline_query_handler = None
72
+
73
+
17
74
  logger.info(f"Initialized RubikaBot with token: {token[:8]}***")
18
75
 
19
76
  def _post(self, method: str, data: Dict[str, Any]) -> Dict[str, Any]:
@@ -21,16 +78,111 @@ class Robot:
21
78
  try:
22
79
  response = self.session.post(url, json=data, timeout=10)
23
80
  response.raise_for_status()
24
- json_resp = response.json()
25
- logger.debug(f"API Response from {method}: {json_resp}")
81
+ try:
82
+ json_resp = response.json()
83
+ except ValueError:
84
+ logger.error(f"Invalid JSON response from {method}: {response.text}")
85
+ raise APIRequestError(f"Invalid JSON response: {response.text}")
86
+ if method != "getUpdates":logger.debug(f"API Response from {method}: {json_resp}")
87
+
26
88
  return json_resp
27
89
  except requests.RequestException as e:
28
90
  logger.error(f"API request failed: {e}")
29
91
  raise APIRequestError(f"API request failed: {e}") from e
30
92
 
93
+
31
94
  def get_me(self) -> Dict[str, Any]:
32
95
  """Get info about the bot itself."""
33
96
  return self._post("getMe", {})
97
+ def on_message(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
98
+ def decorator(func: Callable[[Any, Message], None]):
99
+ self._message_handler = {
100
+ "func": func,
101
+ "filters": filters,
102
+ "commands": commands
103
+ }
104
+ return func
105
+ return decorator
106
+
107
+
108
+ def on_inline_query(self):
109
+ def decorator(func: Callable[[Any, InlineMessage], None]):
110
+ self._inline_query_handler = func
111
+ return func
112
+ return decorator
113
+
114
+
115
+
116
+ def _process_update(self, update: Dict[str, Any]):
117
+ if update.get('type') == 'ReceiveQuery':
118
+ msg = update.get("inline_message", {})
119
+ if self._inline_query_handler:
120
+ context = InlineMessage(bot=self, raw_data=msg)
121
+ self._inline_query_handler(self, context)
122
+ if update.get('type') == 'NewMessage':
123
+ msg = update.get('new_message', {})
124
+ chat_id = update.get('chat_id')
125
+ message_id = msg.get('message_id')
126
+ sender_id = msg.get('sender_id')
127
+ text = msg.get('text')
128
+ try:
129
+ import time
130
+ if msg.get("time") and (time.time() - float(msg["time"])) > 10:return
131
+ except Exception as e:return
132
+ if self._message_handler:
133
+ handler = self._message_handler
134
+ context = Message(bot=self, chat_id=chat_id, message_id=message_id, sender_id=sender_id, text=text, raw_data=msg)
135
+ if handler["commands"]:
136
+ if not context.text or not context.text.startswith("/"):
137
+ return
138
+ parts = context.text.split()
139
+ cmd = parts[0][1:]
140
+ if cmd not in handler["commands"]:
141
+ return
142
+ context.args = parts[1:]
143
+
144
+ if handler["filters"]:
145
+ if not handler["filters"](context):
146
+ return
147
+
148
+ handler["func"](self, context)
149
+
150
+ elif update.get('type') == 'ReceiveQuery':
151
+ msg = update.get("inline_message", {})
152
+ chat_id = msg.get("chat_id")
153
+ message_id = msg.get("message_id")
154
+ sender_id = msg.get("sender_id")
155
+ text = msg.get("text")
156
+
157
+ if hasattr(self, "_callback_handler"):
158
+ context = Message(bot=self, chat_id=chat_id, message_id=message_id, sender_id=sender_id, text=text, raw_data=msg)
159
+ self._callback_handler(self, context)
160
+
161
+
162
+ def run(self):
163
+ print("Bot started running...")
164
+ if self._offset_id is None:
165
+ try:
166
+ latest = self.get_updates(limit=100)
167
+ if latest and latest.get("data") and latest["data"].get("updates"):
168
+ updates = latest["data"]["updates"]
169
+ last_update = updates[-1]
170
+ self._offset_id = latest["data"].get("next_offset_id")
171
+ print(f"Offset initialized to: {self._offset_id}")
172
+ else:
173
+ print("No updates found.")
174
+ except Exception as e:
175
+ print(f"Failed to fetch latest message: {e}")
176
+
177
+ while True:
178
+ try:
179
+ updates = self.get_updates(offset_id=self._offset_id, limit=1)
180
+ if updates and updates.get("data"):
181
+ for update in updates["data"].get("updates", []):
182
+ self._process_update(update)
183
+ self._offset_id = updates["data"].get("next_offset_id", self._offset_id)
184
+ except Exception as e:
185
+ print(f"Error in run loop: {e}")
34
186
 
35
187
  def send_message(
36
188
  self,
rubka/context.py ADDED
@@ -0,0 +1,328 @@
1
+ from typing import Any, Dict, List,Optional
2
+
3
+ class File:
4
+ def __init__(self, data: dict):
5
+ self.file_id: str = data.get("file_id")
6
+ self.file_name: str = data.get("file_name")
7
+ self.size: str = data.get("size")
8
+
9
+
10
+ class Sticker:
11
+ def __init__(self, data: dict):
12
+ self.sticker_id: str = data.get("sticker_id")
13
+ self.emoji_character: str = data.get("emoji_character")
14
+ self.file = File(data.get("file", {}))
15
+
16
+
17
+ # =========================
18
+ # Poll
19
+ # =========================
20
+ class PollStatus:
21
+ def __init__(self, data: dict):
22
+ self.state: str = data.get("state")
23
+ self.selection_index: int = data.get("selection_index")
24
+ self.percent_vote_options: List[int] = data.get("percent_vote_options", [])
25
+ self.total_vote: int = data.get("total_vote")
26
+ self.show_total_votes: bool = data.get("show_total_votes")
27
+
28
+
29
+ class Poll:
30
+ def __init__(self, data: dict):
31
+ self.question: str = data.get("question")
32
+ self.options: List[str] = data.get("options", [])
33
+ self.poll_status = PollStatus(data.get("poll_status", {}))
34
+
35
+
36
+ # =========================
37
+ # Location & Contact & ForwardedFrom
38
+ # =========================
39
+ class Location:
40
+ def __init__(self, data: dict):
41
+ self.latitude: str = data.get("latitude")
42
+ self.longitude: str = data.get("longitude")
43
+
44
+
45
+ class LiveLocation:
46
+ def __init__(self, data: dict):
47
+ self.start_time: str = data.get("start_time")
48
+ self.live_period: int = data.get("live_period")
49
+ self.current_location = Location(data.get("current_location", {}))
50
+ self.user_id: str = data.get("user_id")
51
+ self.status: str = data.get("status")
52
+ self.last_update_time: str = data.get("last_update_time")
53
+
54
+
55
+ class ContactMessage:
56
+ def __init__(self, data: dict):
57
+ self.phone_number: str = data.get("phone_number")
58
+ self.first_name: str = data.get("first_name")
59
+ self.last_name: str = data.get("last_name")
60
+
61
+
62
+ class ForwardedFrom:
63
+ def __init__(self, data: dict):
64
+ self.type_from: str = data.get("type_from")
65
+ self.message_id: str = data.get("message_id")
66
+ self.from_chat_id: str = data.get("from_chat_id")
67
+ self.from_sender_id: str = data.get("from_sender_id")
68
+
69
+
70
+ # =========================
71
+ # AuxData
72
+ # =========================
73
+ class AuxData:
74
+ def __init__(self, data: dict):
75
+ self.start_id: str = data.get("start_id")
76
+ self.button_id: str = data.get("button_id")
77
+
78
+
79
+ # =========================
80
+ # Button Models
81
+ # =========================
82
+ class ButtonTextbox:
83
+ def __init__(self, data: dict):
84
+ self.type_line: str = data.get("type_line")
85
+ self.type_keypad: str = data.get("type_keypad")
86
+ self.place_holder: Optional[str] = data.get("place_holder")
87
+ self.title: Optional[str] = data.get("title")
88
+ self.default_value: Optional[str] = data.get("default_value")
89
+
90
+
91
+ class ButtonNumberPicker:
92
+ def __init__(self, data: dict):
93
+ self.min_value: str = data.get("min_value")
94
+ self.max_value: str = data.get("max_value")
95
+ self.default_value: Optional[str] = data.get("default_value")
96
+ self.title: str = data.get("title")
97
+
98
+
99
+ class ButtonStringPicker:
100
+ def __init__(self, data: dict):
101
+ self.items: List[str] = data.get("items", [])
102
+ self.default_value: Optional[str] = data.get("default_value")
103
+ self.title: Optional[str] = data.get("title")
104
+
105
+
106
+ class ButtonCalendar:
107
+ def __init__(self, data: dict):
108
+ self.default_value: Optional[str] = data.get("default_value")
109
+ self.type: str = data.get("type")
110
+ self.min_year: str = data.get("min_year")
111
+ self.max_year: str = data.get("max_year")
112
+ self.title: str = data.get("title")
113
+
114
+
115
+ class ButtonLocation:
116
+ def __init__(self, data: dict):
117
+ self.default_pointer_location = Location(data.get("default_pointer_location", {}))
118
+ self.default_map_location = Location(data.get("default_map_location", {}))
119
+ self.type: str = data.get("type")
120
+ self.title: Optional[str] = data.get("title")
121
+ self.location_image_url: str = data.get("location_image_url")
122
+
123
+
124
+ class ButtonSelectionItem:
125
+ def __init__(self, data: dict):
126
+ self.text: str = data.get("text")
127
+ self.image_url: str = data.get("image_url")
128
+ self.type: str = data.get("type")
129
+
130
+
131
+ class ButtonSelection:
132
+ def __init__(self, data: dict):
133
+ self.selection_id: str = data.get("selection_id")
134
+ self.search_type: str = data.get("search_type")
135
+ self.get_type: str = data.get("get_type")
136
+ self.items: List[ButtonSelectionItem] = [ButtonSelectionItem(i) for i in data.get("items", [])]
137
+ self.is_multi_selection: bool = data.get("is_multi_selection")
138
+ self.columns_count: str = data.get("columns_count")
139
+ self.title: str = data.get("title")
140
+
141
+
142
+ class Button:
143
+ def __init__(self, data: dict):
144
+ self.id: str = data.get("id")
145
+ self.type: str = data.get("type")
146
+ self.button_text: str = data.get("button_text")
147
+ self.button_selection = ButtonSelection(data.get("button_selection", {})) if "button_selection" in data else None
148
+ self.button_calendar = ButtonCalendar(data.get("button_calendar", {})) if "button_calendar" in data else None
149
+ self.button_number_picker = ButtonNumberPicker(data.get("button_number_picker", {})) if "button_number_picker" in data else None
150
+ self.button_string_picker = ButtonStringPicker(data.get("button_string_picker", {})) if "button_string_picker" in data else None
151
+ self.button_location = ButtonLocation(data.get("button_location", {})) if "button_location" in data else None
152
+ self.button_textbox = ButtonTextbox(data.get("button_textbox", {})) if "button_textbox" in data else None
153
+
154
+
155
+ class KeypadRow:
156
+ def __init__(self, data: dict):
157
+ self.buttons: List[Button] = [Button(btn) for btn in data.get("buttons", [])]
158
+
159
+
160
+ class Keypad:
161
+ def __init__(self, data: dict):
162
+ self.rows: List[KeypadRow] = [KeypadRow(r) for r in data.get("rows", [])]
163
+ self.resize_keyboard: bool = data.get("resize_keyboard", False)
164
+ self.on_time_keyboard: bool = data.get("on_time_keyboard", False)
165
+
166
+
167
+ class Chat:
168
+ def __init__(self, data: dict):
169
+ self.chat_id: str = data.get("chat_id")
170
+ self.chat_type: str = data.get("chat_type")
171
+ self.user_id: str = data.get("user_id")
172
+ self.first_name: str = data.get("first_name")
173
+ self.last_name: str = data.get("last_name")
174
+ self.title: str = data.get("title")
175
+ self.username: str = data.get("username")
176
+
177
+
178
+ class Bot:
179
+ def __init__(self, data: dict):
180
+ self.bot_id: str = data.get("bot_id")
181
+ self.bot_title: str = data.get("bot_title")
182
+ self.avatar = File(data.get("avatar", {}))
183
+ self.description: str = data.get("description")
184
+ self.username: str = data.get("username")
185
+ self.start_message: str = data.get("start_message")
186
+ self.share_url: str = data.get("share_url")
187
+ class Message:
188
+ def __init__(self, bot, chat_id, message_id, sender_id, text=None, raw_data=None):
189
+ self.bot = bot
190
+ self.chat_id = chat_id
191
+ self.raw_data = raw_data or {}
192
+ self.message_id: str = self.raw_data.get("message_id", message_id)
193
+ self.text: str = self.raw_data.get("text", text)
194
+ self.sender_id: str = self.raw_data.get("sender_id", sender_id)
195
+ self.time: str = self.raw_data.get("time")
196
+ self.is_edited: bool = self.raw_data.get("is_edited", False)
197
+ self.sender_type: str = self.raw_data.get("sender_type")
198
+
199
+ self.args = []
200
+ self.reply_to_message_id: Optional[str] = self.raw_data.get("reply_to_message_id")
201
+ self.forwarded_from = ForwardedFrom(self.raw_data["forwarded_from"]) if "forwarded_from" in self.raw_data else None
202
+ self.file = File(self.raw_data["file"]) if "file" in self.raw_data else None
203
+ self.sticker = Sticker(self.raw_data["sticker"]) if "sticker" in self.raw_data else None
204
+ self.contact_message = ContactMessage(self.raw_data["contact_message"]) if "contact_message" in self.raw_data else None
205
+ self.poll = Poll(self.raw_data["poll"]) if "poll" in self.raw_data else None
206
+ self.location = Location(self.raw_data["location"]) if "location" in self.raw_data else None
207
+ self.live_location = LiveLocation(self.raw_data["live_location"]) if "live_location" in self.raw_data else None
208
+ self.aux_data = AuxData(self.raw_data["aux_data"]) if "aux_data" in self.raw_data else None
209
+
210
+ @property
211
+ def session(self):
212
+ if self.chat_id not in self.bot.sessions:
213
+ self.bot.sessions[self.chat_id] = {}
214
+ return self.bot.sessions[self.chat_id]
215
+ def reply(self, text: str, **kwargs):
216
+ return self.bot.send_message(
217
+ self.chat_id,
218
+ text,
219
+ reply_to_message_id=self.message_id,
220
+ **kwargs
221
+ )
222
+
223
+ def reply_poll(self, question: str, options: List[str], **kwargs) -> Dict[str, Any]:
224
+ return self.bot._post("sendPoll", {
225
+ "chat_id": self.chat_id,
226
+ "question": question,
227
+ "options": options,
228
+ "reply_to_message_id": self.message_id,
229
+ **kwargs
230
+ })
231
+
232
+ def reply_location(self, latitude: str, longitude: str, **kwargs) -> Dict[str, Any]:
233
+ return self.bot.send_location(
234
+ chat_id=self.chat_id,
235
+ latitude=latitude,
236
+ longitude=longitude,
237
+ reply_to_message_id=self.message_id,
238
+ **kwargs
239
+ )
240
+
241
+ def reply_contact(self, first_name: str, last_name: str, phone_number: str, **kwargs) -> Dict[str, Any]:
242
+ return self.bot.send_contact(
243
+ chat_id=self.chat_id,
244
+ first_name=first_name,
245
+ last_name=last_name,
246
+ phone_number=phone_number,
247
+ reply_to_message_id=self.message_id,
248
+ **kwargs
249
+ )
250
+
251
+ def reply_keypad(self, text: str, keypad: Dict[str, Any], **kwargs) -> Dict[str, Any]:
252
+ return self.bot.send_message(
253
+ chat_id=self.chat_id,
254
+ text=text,
255
+ chat_keypad_type="New",
256
+ chat_keypad=keypad,
257
+ reply_to_message_id=self.message_id,
258
+ **kwargs
259
+ )
260
+
261
+ def reply_inline(self, text: str, inline_keypad: Dict[str, Any], **kwargs) -> Dict[str, Any]:
262
+ return self.bot.send_message(
263
+ chat_id=self.chat_id,
264
+ text=text,
265
+ inline_keypad=inline_keypad,
266
+ reply_to_message_id=self.message_id,
267
+ **kwargs
268
+ )
269
+
270
+ def reply_sticker(self, sticker_id: str, **kwargs) -> Dict[str, Any]:
271
+ return self.bot._post("sendSticker", {
272
+ "chat_id": self.chat_id,
273
+ "sticker_id": sticker_id,
274
+ "reply_to_message_id": self.message_id,
275
+ **kwargs
276
+ })
277
+
278
+ def reply_file(self, file_id: str, **kwargs) -> Dict[str, Any]:
279
+ return self.bot._post("sendFile", {
280
+ "chat_id": self.chat_id,
281
+ "file_id": file_id,
282
+ "reply_to_message_id": self.message_id,
283
+ **kwargs
284
+ })
285
+
286
+ def edit(self, new_text: str) -> Dict[str, Any]:
287
+ return self.bot.edit_message_text(
288
+ chat_id=self.chat_id,
289
+ message_id=self.message_id,
290
+ text=new_text
291
+ )
292
+
293
+ def delete(self) -> Dict[str, Any]:
294
+ return self.bot.delete_message(
295
+ chat_id=self.chat_id,
296
+ message_id=self.message_id
297
+ )
298
+ class InlineMessage:
299
+ def __init__(self, bot, raw_data: dict):
300
+ self.bot = bot
301
+ self.raw_data = raw_data
302
+
303
+ self.chat_id: str = raw_data.get("chat_id")
304
+ self.message_id: str = raw_data.get("message_id")
305
+ self.sender_id: str = raw_data.get("sender_id")
306
+ self.text: str = raw_data.get("text")
307
+ self.aux_data = AuxData(raw_data.get("aux_data", {})) if "aux_data" in raw_data else None
308
+
309
+ def reply(self, text: str, **kwargs):
310
+ return self.bot.send_message(
311
+ chat_id=self.chat_id,
312
+ text=text,
313
+ reply_to_message_id=self.message_id,
314
+ **kwargs
315
+ )
316
+
317
+ def edit(self, new_text: str):
318
+ return self.bot.edit_message_text(
319
+ chat_id=self.chat_id,
320
+ message_id=self.message_id,
321
+ text=new_text
322
+ )
323
+
324
+ def delete(self):
325
+ return self.bot.delete_message(
326
+ chat_id=self.chat_id,
327
+ message_id=self.message_id
328
+ )
rubka/exceptions.py ADDED
@@ -0,0 +1,3 @@
1
+ class APIRequestError(Exception):
2
+ """Raised when an API request fails."""
3
+ pass
rubka/jobs.py ADDED
@@ -0,0 +1,15 @@
1
+ import threading
2
+ import time
3
+ from typing import Callable
4
+
5
+ class Job:
6
+ def __init__(self, delay: int, callback: Callable):
7
+ self.delay = delay
8
+ self.callback = callback
9
+ thread = threading.Thread(target=self.run)
10
+ thread.daemon = True
11
+ thread.start()
12
+
13
+ def run(self):
14
+ time.sleep(self.delay)
15
+ self.callback()
rubka/keypad.py ADDED
@@ -0,0 +1,48 @@
1
+ from typing import Dict
2
+
3
+ class InlineBuilder:
4
+ def __init__(self):
5
+ self.rows = []
6
+
7
+ def row(self, *buttons: Dict[str, str]):
8
+ self.rows.append({"buttons": list(buttons)})
9
+ return self
10
+
11
+ def button(self, id: str, text: str, type: str = "Simple") -> Dict[str, str]:
12
+ return {"id": id, "type": type, "button_text": text}
13
+
14
+ def build(self):
15
+ return {"rows": self.rows}
16
+ from typing import List, Dict, Optional
17
+
18
+ class ChatKeypadBuilder:
19
+ def __init__(self):
20
+ self.rows: List[Dict[str, List[Dict[str, str]]]] = []
21
+
22
+ def row(self, *buttons: Dict[str, str]) -> "ChatKeypadBuilder":
23
+ """
24
+ یک ردیف دکمه به کی‌پد اضافه می‌کند.
25
+ ورودی: چند دیکشنری که نماینده دکمه‌ها هستند.
26
+ """
27
+ self.rows.append({"buttons": list(buttons)})
28
+ return self
29
+
30
+ def button(self, id: str, text: str, type: str = "Simple") -> Dict[str, str]:
31
+ """
32
+ دیکشنری یک دکمه می‌سازد.
33
+ """
34
+ return {"id": id, "type": type, "button_text": text}
35
+
36
+ def build(
37
+ self,
38
+ resize_keyboard: bool = True,
39
+ on_time_keyboard: bool = False
40
+ ) -> Dict[str, object]:
41
+ """
42
+ ساختار نهایی chat_keypad را می‌سازد.
43
+ """
44
+ return {
45
+ "rows": self.rows,
46
+ "resize_keyboard": resize_keyboard,
47
+ "on_time_keyboard": on_time_keyboard
48
+ }
rubka/logger.py CHANGED
@@ -1,10 +1,10 @@
1
1
  import logging
2
2
 
3
3
  logger = logging.getLogger("rubka")
4
- logger.setLevel(logging.DEBUG)
4
+ logger.setLevel(logging.ERROR)
5
5
 
6
6
  ch = logging.StreamHandler()
7
- ch.setLevel(logging.DEBUG)
7
+ ch.setLevel(logging.ERROR)
8
8
 
9
9
  formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
10
10
  ch.setFormatter(formatter)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rubka
3
- Version: 0.1.1
3
+ Version: 1.2.9
4
4
  Summary: A Python library for interacting with Rubika Bot API.
5
5
  Home-page: https://github.com/Mahdy-Ahmadi/Rubka
6
6
  Download-URL: https://github.com/Mahdy-Ahmadi/Rubka/archive/refs/tags/v0.1.0.tar.gz
@@ -0,0 +1,15 @@
1
+ rubka/__init__.py,sha256=3f4H6Uj1ylrfBYlTHmTvzZSvaPJsdNhCocyX3Bbvuv0,254
2
+ rubka/api.py,sha256=VPlTu-k1-C3eth5OrTZP74QL2bTbzkrUrOv7NSZC94w,13169
3
+ rubka/config.py,sha256=Bck59xkOiqioLv0GkQ1qPGnBXVctz1hKk6LT4h2EPx0,78
4
+ rubka/context.py,sha256=5OMFjcnMWvkn3ZigRgJrjMiFt_tYgMwzHsTS6qXMGj8,12843
5
+ rubka/decorators.py,sha256=hGwUoE4q2ImrunJIGJ_kzGYYxQf1ueE0isadqraKEts,1157
6
+ rubka/exceptions.py,sha256=tujZt1XrhWaw-lmdeVadVceUptpw4XzNgE44sAAY0gs,90
7
+ rubka/jobs.py,sha256=GvLMLsVhcSEzRTgkvnPISPEBN71suW2xXI0hUaUZPTo,378
8
+ rubka/keyboards.py,sha256=7nr-dT2bQJVQnQ6RMWPTSjML6EEk6dsBx-4d8pab8xk,488
9
+ rubka/keypad.py,sha256=FHe0xVYhOXMdHZhbGKHsRxtsRB27qZv0DvNfb8NkNwI,1545
10
+ rubka/logger.py,sha256=J2I6NiK1z32lrAzC4H1Et6WPMBXxXGCVUsW4jgcAofs,289
11
+ rubka/utils.py,sha256=XUQUZxQt9J2f0X5hmAH_MH1kibTAfdT1T4AaBkBhBBs,148
12
+ rubka-1.2.9.dist-info/METADATA,sha256=0FzdDmgD9pYXBXNVB0oLTIsGLiHowjkH5SzaYyYy94w,8168
13
+ rubka-1.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ rubka-1.2.9.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
15
+ rubka-1.2.9.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- rubka/__init__.py,sha256=3f4H6Uj1ylrfBYlTHmTvzZSvaPJsdNhCocyX3Bbvuv0,254
2
- rubka/api.py,sha256=SJnuOXZdrj5llnkRltPBPNFXvixzaQDufWU5Zm7B8jk,6858
3
- rubka/config.py,sha256=Bck59xkOiqioLv0GkQ1qPGnBXVctz1hKk6LT4h2EPx0,78
4
- rubka/decorators.py,sha256=hGwUoE4q2ImrunJIGJ_kzGYYxQf1ueE0isadqraKEts,1157
5
- rubka/keyboards.py,sha256=7nr-dT2bQJVQnQ6RMWPTSjML6EEk6dsBx-4d8pab8xk,488
6
- rubka/logger.py,sha256=VhxaryxN_SqUmCX39nym2A6JiEPI-jEG22htPBP88Y4,289
7
- rubka/utils.py,sha256=XUQUZxQt9J2f0X5hmAH_MH1kibTAfdT1T4AaBkBhBBs,148
8
- rubka-0.1.1.dist-info/METADATA,sha256=_45Dxp2vgZ8trGFw8oy-NSGK42oX9t2yrUWldYfQI6I,8168
9
- rubka-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- rubka-0.1.1.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
11
- rubka-0.1.1.dist-info/RECORD,,
File without changes