PyRubikaBotAPI 1.0.0__py3-none-any.whl → 1.0.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyRubikaBotAPI
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: A Python library with a simple and familiar interface for working with the official Rubika bot API
5
5
  Home-page: https://github.com/alireza-sadeghian/PyRubikaBotAPI
6
6
  Author: Alireza Sadeghian
@@ -9,14 +9,14 @@ License: MIT
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.8
12
+ Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENCE
15
15
  Requires-Dist: requests
16
16
 
17
17
  # PyRubikaBotAPI
18
18
 
19
- یک کتابخانه برای ساخت ربات‌های روبیکا با پایتون
19
+ کتابخانه قدرتمند و آشنا برای ساخت ربات های تخصصی روبیکا
20
20
 
21
21
  ## نصب
22
22
  ```bash
@@ -38,17 +38,8 @@ def start(message):
38
38
  bot.polling()
39
39
  ```
40
40
 
41
- ## مثال پیشرفته تر
42
- ```py
43
- @bot.message_handler(content_types=['file'])
44
- def handle(msg):
45
- file_id = msg.file.id
46
- file_url = bot.get_file(file_id)
47
- file = bot.download_file(file_url)
48
- with open('file.format', 'wb') as f
49
- f.write(file)
50
- # استفاده از فایل ارسالی
51
- ```
41
+ برای استفاده دقیقتر حتما مستندات رو مطالعه بفرمایید و یا برای دریافت سورس کد ها
42
+ و مثال ها به کانال روبیکای ما سر بزنید
52
43
 
53
44
  ## مستندات:
54
45
 
@@ -0,0 +1,12 @@
1
+ rubibot/__init__.py,sha256=brh_qfPanBsFaTHIjtcX9GWJ7DgF3GujRy4scn4dqww,38036
2
+ rubibot/exceptions.py,sha256=68MnHRgJWi4asxeTfH2rrHeRvBI49LR8KkKxYEAYcxg,250
3
+ rubibot/metadata.py,sha256=eNUuFnRVQ_Ve8dh-VGMfcX0wxR0d06DiceE-sj1g_oQ,1413
4
+ rubibot/parse.py,sha256=5F-jq0l1mc0q8SHl3iby8dv9N2r4WTf-FzlPesrdzHQ,4837
5
+ rubibot/rubika_api.py,sha256=SgdvirBEXuocjKsFsvtdhXwK5JO-8t4jORZrGZPDiOY,9343
6
+ rubibot/types.py,sha256=mnLTbYaD727YkMDcRWssIa326TlpAL4hc5JbVHb7uko,10483
7
+ rubibot/updates.py,sha256=eXUkppIBhN-LDo0GS8VzkLi7t8U6v0kbb-Squy1BkZA,12366
8
+ pyrubikabotapi-1.0.1.dist-info/LICENCE,sha256=mds18XAwr8xSo4pIhwMfB9spNkvrm5KKmLl_KIKQky4,1095
9
+ pyrubikabotapi-1.0.1.dist-info/METADATA,sha256=Df-N0guE_VdzbGPKTtQZHpYxpz4WN8b-wccUHfUuS4o,1362
10
+ pyrubikabotapi-1.0.1.dist-info/WHEEL,sha256=BNRMDyzLkkcmlv0J8ppDQkk2VED33SesJDynr9ED1gc,91
11
+ pyrubikabotapi-1.0.1.dist-info/top_level.txt,sha256=TAhaSYH8WLX-dab28vY3_Wif-mrUw1pmS_RJOFaiogE,8
12
+ pyrubikabotapi-1.0.1.dist-info/RECORD,,
rubibot/__init__.py CHANGED
@@ -47,8 +47,8 @@ class RubiBot:
47
47
  commands: Optional[List[str]]=None,
48
48
  content_types: Optional[List[str]]=None,
49
49
  func=None,
50
- update_types: Optional[List[str]]=None
51
- ):
50
+ update_types: Optional[List[str]]=None,
51
+ ):
52
52
  """
53
53
  It is responsible for managing messages received from the user.
54
54
  It handles all types of messages such as text, video, Voice, Poll, etc.
@@ -134,7 +134,7 @@ class RubiBot:
134
134
  "commands": commands,
135
135
  "content_types": content_types,
136
136
  "func": func,
137
- "update_types": update_types
137
+ "update_types": update_types,
138
138
  }
139
139
  })
140
140
  return handler
@@ -151,12 +151,15 @@ class RubiBot:
151
151
  return decorator
152
152
 
153
153
  def register_message_handler(self, handler: Callable, **filters):
154
+ for filter in ['commands', 'content_types', 'func', 'update_types']:
155
+ filters.setdefault(filter, None)
156
+
154
157
  self._message_handlers.append({
155
158
  'handler': handler,
156
159
  'filters': filters
157
160
  })
158
161
 
159
- def register_next_step_message_handler_by_chat_id(self, chat_id: str, handler: callable):
162
+ def register_next_step_message_handler_by_chat_id(self, chat_id: str, handler: Callable):
160
163
  """ To register a handler for the next message in a specific chat """
161
164
 
162
165
  if chat_id in self._next_step_message_handlers_by_chat_id:
@@ -214,8 +217,16 @@ class RubiBot:
214
217
  res = requests.post(f"{self.BASE_URL}/getUpdates", json=params, timeout=15)
215
218
  except Exception as e:
216
219
  raise Exception("Error: {}".format(e))
217
- data = res.json()
218
- print(data)
220
+ data: dict = res.json()
221
+ if data.get('status') != 'OK':
222
+ if data["status"] == "INVALID_INPUT":
223
+ raise exceptions.RubiBotInputError(
224
+ "invalid inputs {}".format(data.get('dev_message', ''))
225
+ )
226
+ if data["status"] == "INVALID_ACCESS":
227
+ raise exceptions.RubiBotAccessError("not Access")
228
+ raise exceptions.RubiBotError(data["status"])
229
+
219
230
  if not res or res.status_code !=200:
220
231
  raise Exception("Error in receiving updates: {}".format(data.get("status")))
221
232
 
@@ -228,13 +239,13 @@ class RubiBot:
228
239
  def __load_offset(self) -> dict:
229
240
  if not os.path.exists(self.OFFSET_FILE):
230
241
  with open(self.OFFSET_FILE, 'w', encoding="utf-8") as f:
231
- json.dump({}, f, ensure_ascii=False, indent=4)
242
+ json.dump({self.token: None}, f, ensure_ascii=False, indent=4)
232
243
 
233
244
  with open(self.OFFSET_FILE, "r", encoding="utf-8") as f:
234
245
  return json.load(f)
235
246
 
236
- def __get_offset(self) -> str:
237
- return self.__load_offset()[self.token]
247
+ def __get_offset(self) -> Optional[str]:
248
+ return self.__load_offset().get(self.token)
238
249
 
239
250
  def __save_offset(self, offset_id):
240
251
  next_offset_ids = self.__load_offset()
@@ -286,10 +297,10 @@ class RubiBot:
286
297
  self.polling(*args, **kwargs)
287
298
  except requests.exceptions.JSONDecodeError:
288
299
  print('Json Decode Error')
289
- continue
300
+ time.sleep(1)
290
301
  except Exception as e:
291
302
  print(f"{e.__class__.__name__}: {e}")
292
- continue
303
+ time.sleep(1)
293
304
 
294
305
 
295
306
  def send_message(
@@ -350,6 +361,10 @@ class RubiBot:
350
361
  if parse_mode:
351
362
  parser = parse_mode()
352
363
  text, metadata_list = parser(text)
364
+
365
+ if not metadata_list:
366
+ metadata_list = []
367
+
353
368
  text = self._process_metadata(text, *metadata_list)
354
369
 
355
370
  return rubika_api.send_message(self.token, chat_id, text, chat_keypad, inline_keypad,
@@ -807,6 +822,7 @@ class RubiBot:
807
822
  if filters["content_types"]:
808
823
  if message.get_content_type() not in filters["content_types"]:
809
824
  return False
825
+
810
826
 
811
827
  if filters["func"]:
812
828
  return filters["func"](message)
@@ -850,22 +866,25 @@ class RubiBot:
850
866
 
851
867
  if isinstance(upd, rubibot.updates.Update):
852
868
  message = upd.to_message()
853
- message.token = self.token
854
-
855
- if message.type == "NewMessage":
856
- if message.chat_id in self._next_step_message_handlers_by_chat_id:
857
- self._next_step_message_handlers_by_chat_id.pop(message.chat_id)(message)
858
- return
859
- elif message.sender_id in self._next_step_message_handlers_by_sender_id:
860
- self._next_step_message_handlers_by_sender_id.pop(message.sender_id)(message)
861
- return
862
-
863
- for handler in self._message_handlers:
864
- if self._test_message_handler(handler, message):
865
- handler["handler"](message)
866
- break
869
+ if isinstance(message, rubibot.updates.InlineMessage):
870
+ upd = rubibot.updates.InlineUpdate(upd.dict)
871
+ else:
872
+ message.token = self.token
873
+
874
+ if message.type == "NewMessage":
875
+ if message.chat_id in self._next_step_message_handlers_by_chat_id:
876
+ self._next_step_message_handlers_by_chat_id.pop(message.chat_id)(message)
877
+ return
878
+ elif message.sender_id in self._next_step_message_handlers_by_sender_id:
879
+ self._next_step_message_handlers_by_sender_id.pop(message.sender_id)(message)
880
+ return
881
+
882
+ for handler in self._message_handlers:
883
+ if self._test_message_handler(handler, message):
884
+ handler["handler"](message)
885
+ break
867
886
 
868
- elif isinstance(upd, rubibot.updates.InlineUpdate):
887
+ if isinstance(upd, rubibot.updates.InlineUpdate):
869
888
  message = upd.to_inline_message()
870
889
  message.token = self.token
871
890
  for handler in self._inline_message_handlers:
@@ -875,5 +894,52 @@ class RubiBot:
875
894
  else: raise exceptions.RubiBotValueError("Invalid type for update")
876
895
 
877
896
 
897
+
898
+ class GroupBot(RubiBot):
899
+ def __init__(self, token, set_token_env=True):
900
+ super().__init__(token, set_token_env)
901
+
902
+ def command_handler(self, command: str):
903
+ def decorator(handler):
904
+ self.register_message_handler(handler, commands=[command])
905
+ return handler
906
+ return decorator
907
+
908
+ def in_text_handler(self, text: str):
909
+ def decorator(handler):
910
+ self.register_message_handler(
911
+ handler,
912
+ func= lambda m: text in m.text
913
+ )
914
+ return handler
915
+ return decorator
916
+
917
+ def text_equal_handler(self, text: str):
918
+ def decorator(handler):
919
+ self.register_message_handler(
920
+ handler,
921
+ func= lambda m: m.text==text
922
+ )
923
+ return handler
924
+ return decorator
925
+
926
+ def text_startswith_handler(self, text: str):
927
+ def decorator(handler):
928
+ self.register_message_handler(
929
+ handler,
930
+ func= lambda m: m.text.startswith(text)
931
+ )
932
+ return handler
933
+ return decorator
934
+
935
+ def text_endswith_handler(self, text: str):
936
+ def decorator(handler):
937
+ self.register_message_handler(
938
+ handler,
939
+ func= lambda m: m.text.endswith(text)
940
+ )
941
+ return handler
942
+ return decorator
943
+
878
944
  # soon...
879
945
  # This is not the end of our work... 😎
rubibot/exceptions.py CHANGED
@@ -1,14 +1,14 @@
1
1
  class RubiBotError(Exception):
2
2
  pass
3
3
 
4
- class RubiBotSyntaxError(Exception):
4
+ class RubiBotSyntaxError(RubiBotError):
5
5
  pass
6
6
 
7
- class RubiBotValueError(Exception):
7
+ class RubiBotValueError(RubiBotError):
8
8
  pass
9
9
 
10
- class RubiBotInputError(Exception):
10
+ class RubiBotInputError(RubiBotError):
11
11
  pass
12
12
 
13
- class RubiBotAccessError(Exception):
13
+ class RubiBotAccessError(RubiBotError):
14
14
  pass
rubibot/rubika_api.py CHANGED
@@ -1,4 +1,3 @@
1
- import time
2
1
  import requests
3
2
  from rubibot import exceptions, types, updates
4
3
 
rubibot/types.py CHANGED
@@ -281,7 +281,7 @@ class KeypadTextboxButton(BaseKeypadButton):
281
281
  dic['button_textbox']['default_value'] = self.default_value
282
282
  if self.title:
283
283
  dic['button_textbox']['title'] = self.title
284
- print(dic)
284
+
285
285
  return dic
286
286
 
287
287
  class KeypadRow:
@@ -293,16 +293,12 @@ class KeypadRow:
293
293
  self.btns.append(btn.to_dict())
294
294
  return self
295
295
 
296
- def remove(self, btn_or_btn_id):
297
- if isinstance(btn_or_btn_id, str):
298
- for btn in self.btns:
299
- if btn.id == btn_or_btn_id:
300
- buttton = self.btns.pop(btn)
301
- break
302
- return buttton
303
- if isinstance(btn_or_btn_id, KeypadSimpleButton):
304
- return self.btns.pop(btn_or_btn_id)
305
-
296
+ def remove(self, btn_id):
297
+ if isinstance(btn_id, str):
298
+ for i, btn in enumerate(self.btns):
299
+ if btn.get('id') == btn_id:
300
+ return self.btns.pop(i)
301
+ return None
306
302
 
307
303
  def to_dict(self):
308
304
  return {"buttons": self.btns}
@@ -319,9 +315,6 @@ class BaseKeypad:
319
315
  for row in rows:
320
316
  self.rows.append(row.to_dict())
321
317
  return self
322
-
323
- def remove(self, row):
324
- return self.rows.pop(row)
325
318
 
326
319
  def clear(self):
327
320
  self.rows = []
rubibot/updates.py CHANGED
@@ -1,10 +1,11 @@
1
+ from typing import Optional
1
2
  from rubibot import types
2
3
  from rubibot import rubika_api
3
4
  import json, os
4
5
 
5
6
 
6
7
  class BaseUpdate:
7
- def __init__(self, type: str, chat_id: str, update_time: str):
8
+ def __init__(self, type: str, chat_id: str, update_time: Optional[str]):
8
9
  self.type = type
9
10
  self.chat_id = chat_id
10
11
  self.update_time = update_time
@@ -18,7 +19,15 @@ class BaseUpdate:
18
19
  token = os.getenv('RUBIKA_BOT_API_TOKEN')
19
20
  return rubika_api.get_chat(token, self.chat_id)
20
21
 
21
-
22
+ @property
23
+ def chat_type(self):
24
+ if self.chat_id.startswith('b'):
25
+ return 'Private'
26
+ if self.chat_id.startswith('g'):
27
+ return 'Group'
28
+ if self.chat_id.startswith('c'):
29
+ return 'Channel'
30
+ return 'NotFound'
22
31
 
23
32
  class Message(BaseUpdate):
24
33
  def __init__(
@@ -78,17 +87,18 @@ class Message(BaseUpdate):
78
87
  return f"Message: from {self.chat.get_full_name()}"
79
88
 
80
89
 
81
- class InlineMessage:
90
+ class InlineMessage(BaseUpdate):
82
91
  def __init__(self, chat_id: str, sender_id: str, message_id: str, text: str, aux_data: types.AuxData,
83
92
  file: types.File, location: types.Location):
84
- self.chat_id = chat_id
93
+ super().__init__('InlineMessage', chat_id, None)
85
94
  self.sender_id = sender_id
86
95
  self.message_id = message_id
87
96
  self.text = text
88
97
  self.aux = aux_data
89
98
  self.file = file
90
99
  self.location = location
91
-
100
+ self.token = None
101
+
92
102
  def has_file(self):
93
103
  return self.file is not None
94
104
 
@@ -107,6 +117,7 @@ class InlineMessage:
107
117
  if self.has_location(): return "location"
108
118
 
109
119
 
120
+
110
121
  class RemovedMessage(BaseUpdate):
111
122
  def __init__(self, type, chat_id, update_time, removed_message_id):
112
123
  super().__init__(type, chat_id, update_time)
@@ -221,7 +232,7 @@ class Update:
221
232
  update_time=update_time,
222
233
  removed_message_id=update.get('removed_message_id')
223
234
  )
224
- elif type_ == "NewMessage":
235
+ elif type_ == "NewMessage" or type_ == "UpdatedMessage":
225
236
 
226
237
  new_message: dict = update.get("new_message")
227
238
  message_id = new_message.get("message_id")
@@ -1,12 +0,0 @@
1
- rubibot/__init__.py,sha256=dKqsh0sUKsfnykrycQLGnNH-Ba-su2tNRjQDDnFbpl0,35712
2
- rubibot/exceptions.py,sha256=dtKzNmDm1n3QfiRV6pdz3X3eHsrlLH9oa-0eHtfNaAc,238
3
- rubibot/metadata.py,sha256=eNUuFnRVQ_Ve8dh-VGMfcX0wxR0d06DiceE-sj1g_oQ,1413
4
- rubibot/parse.py,sha256=5F-jq0l1mc0q8SHl3iby8dv9N2r4WTf-FzlPesrdzHQ,4837
5
- rubibot/rubika_api.py,sha256=qAiyqynGvFp6ZItGpb_p08_yNw5FbFZENl0Vwb24IcQ,9356
6
- rubibot/types.py,sha256=mE-U7pOAbWOEaVKqEuwSZp6a0Ffnl2ce1T07Vru7nRs,10731
7
- rubibot/updates.py,sha256=n444wsom56FK9_dWpjaS4XTBuTPnKWcj7Vre5rYSvo0,11955
8
- pyrubikabotapi-1.0.0.dist-info/LICENCE,sha256=mds18XAwr8xSo4pIhwMfB9spNkvrm5KKmLl_KIKQky4,1095
9
- pyrubikabotapi-1.0.0.dist-info/METADATA,sha256=mQViVjdp9Idk7Axlcbo0nRC-7p6Uaw6xTcF37ZlmCSQ,1458
10
- pyrubikabotapi-1.0.0.dist-info/WHEEL,sha256=BNRMDyzLkkcmlv0J8ppDQkk2VED33SesJDynr9ED1gc,91
11
- pyrubikabotapi-1.0.0.dist-info/top_level.txt,sha256=TAhaSYH8WLX-dab28vY3_Wif-mrUw1pmS_RJOFaiogE,8
12
- pyrubikabotapi-1.0.0.dist-info/RECORD,,