Rubka 5.2.0__py3-none-any.whl → 6.4.4__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
@@ -169,7 +169,326 @@ class Robot:
169
169
  """b"""
170
170
  if self.get_me()['status'] != "OK":
171
171
  raise InvalidTokenError("The provided bot token is invalid or expired.")
172
-
172
+ def on_message_private(
173
+ self,
174
+ chat_id: Optional[Union[str, List[str]]] = None,
175
+ commands: Optional[List[str]] = None,
176
+ filters: Optional[Callable[[Message], bool]] = None,
177
+ sender_id: Optional[Union[str, List[str]]] = None,
178
+ sender_type: Optional[str] = None,
179
+ allow_forwarded: bool = True,
180
+ allow_files: bool = True,
181
+ allow_stickers: bool = True,
182
+ allow_polls: bool = True,
183
+ allow_contacts: bool = True,
184
+ allow_locations: bool = True,
185
+ min_text_length: Optional[int] = None,
186
+ max_text_length: Optional[int] = None,
187
+ contains: Optional[str] = None,
188
+ startswith: Optional[str] = None,
189
+ endswith: Optional[str] = None,
190
+ case_sensitive: bool = False
191
+ ):
192
+ """
193
+ Sync decorator for handling only private messages with extended filters.
194
+ """
195
+
196
+ def decorator(func: Callable[[Any, Message], None]):
197
+ def wrapper(bot, message: Message):
198
+
199
+ if not message.is_private:
200
+ return
201
+
202
+
203
+ if chat_id:
204
+ if isinstance(chat_id, str) and message.chat_id != chat_id:
205
+ return
206
+ if isinstance(chat_id, list) and message.chat_id not in chat_id:
207
+ return
208
+
209
+
210
+ if sender_id:
211
+ if isinstance(sender_id, str) and message.sender_id != sender_id:
212
+ return
213
+ if isinstance(sender_id, list) and message.sender_id not in sender_id:
214
+ return
215
+
216
+
217
+ if sender_type and message.sender_type != sender_type:
218
+ return
219
+
220
+
221
+ if not allow_forwarded and message.forwarded_from:
222
+ return
223
+
224
+
225
+ if not allow_files and message.file:
226
+ return
227
+ if not allow_stickers and message.sticker:
228
+ return
229
+ if not allow_polls and message.poll:
230
+ return
231
+ if not allow_contacts and message.contact_message:
232
+ return
233
+ if not allow_locations and (message.location or message.live_location):
234
+ return
235
+
236
+
237
+ if message.text:
238
+ text = message.text if case_sensitive else message.text.lower()
239
+ if min_text_length and len(message.text) < min_text_length:
240
+ return
241
+ if max_text_length and len(message.text) > max_text_length:
242
+ return
243
+ if contains and (contains if case_sensitive else contains.lower()) not in text:
244
+ return
245
+ if startswith and not text.startswith(startswith if case_sensitive else startswith.lower()):
246
+ return
247
+ if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
248
+ return
249
+
250
+
251
+ if commands:
252
+ if not message.text:
253
+ return
254
+ parts = message.text.strip().split()
255
+ cmd = parts[0].lstrip("/")
256
+ if cmd not in commands:
257
+ return
258
+ message.args = parts[1:]
259
+
260
+
261
+ if filters and not filters(message):
262
+ return
263
+
264
+ return func(bot, message)
265
+
266
+ self._message_handlers.append({
267
+ "func": wrapper,
268
+ "filters": filters,
269
+ "commands": commands,
270
+ "chat_id": chat_id,
271
+ "private_only": True,
272
+ "sender_id": sender_id,
273
+ "sender_type": sender_type
274
+ })
275
+ return wrapper
276
+
277
+ return decorator
278
+ def on_message_channel(
279
+ self,
280
+ chat_id: Optional[Union[str, List[str]]] = None,
281
+ commands: Optional[List[str]] = None,
282
+ filters: Optional[Callable[[Message], bool]] = None,
283
+ sender_id: Optional[Union[str, List[str]]] = None,
284
+ sender_type: Optional[str] = None,
285
+ allow_forwarded: bool = True,
286
+ allow_files: bool = True,
287
+ allow_stickers: bool = True,
288
+ allow_polls: bool = True,
289
+ allow_contacts: bool = True,
290
+ allow_locations: bool = True,
291
+ min_text_length: Optional[int] = None,
292
+ max_text_length: Optional[int] = None,
293
+ contains: Optional[str] = None,
294
+ startswith: Optional[str] = None,
295
+ endswith: Optional[str] = None,
296
+ case_sensitive: bool = False
297
+ ):
298
+ """
299
+ Sync decorator for handling only private messages with extended filters.
300
+ """
301
+
302
+ def decorator(func: Callable[[Any, Message], None]):
303
+ def wrapper(bot, message: Message):
304
+
305
+ if not message.is_channel:
306
+ return
307
+
308
+
309
+ if chat_id:
310
+ if isinstance(chat_id, str) and message.chat_id != chat_id:
311
+ return
312
+ if isinstance(chat_id, list) and message.chat_id not in chat_id:
313
+ return
314
+
315
+
316
+ if sender_id:
317
+ if isinstance(sender_id, str) and message.sender_id != sender_id:
318
+ return
319
+ if isinstance(sender_id, list) and message.sender_id not in sender_id:
320
+ return
321
+
322
+
323
+ if sender_type and message.sender_type != sender_type:
324
+ return
325
+
326
+
327
+ if not allow_forwarded and message.forwarded_from:
328
+ return
329
+
330
+
331
+ if not allow_files and message.file:
332
+ return
333
+ if not allow_stickers and message.sticker:
334
+ return
335
+ if not allow_polls and message.poll:
336
+ return
337
+ if not allow_contacts and message.contact_message:
338
+ return
339
+ if not allow_locations and (message.location or message.live_location):
340
+ return
341
+
342
+
343
+ if message.text:
344
+ text = message.text if case_sensitive else message.text.lower()
345
+ if min_text_length and len(message.text) < min_text_length:
346
+ return
347
+ if max_text_length and len(message.text) > max_text_length:
348
+ return
349
+ if contains and (contains if case_sensitive else contains.lower()) not in text:
350
+ return
351
+ if startswith and not text.startswith(startswith if case_sensitive else startswith.lower()):
352
+ return
353
+ if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
354
+ return
355
+
356
+
357
+ if commands:
358
+ if not message.text:
359
+ return
360
+ parts = message.text.strip().split()
361
+ cmd = parts[0].lstrip("/")
362
+ if cmd not in commands:
363
+ return
364
+ message.args = parts[1:]
365
+
366
+
367
+ if filters and not filters(message):
368
+ return
369
+
370
+ return func(bot, message)
371
+
372
+ self._message_handlers.append({
373
+ "func": wrapper,
374
+ "filters": filters,
375
+ "commands": commands,
376
+ "chat_id": chat_id,
377
+ "private_only": True,
378
+ "sender_id": sender_id,
379
+ "sender_type": sender_type
380
+ })
381
+ return wrapper
382
+
383
+ return decorator
384
+
385
+ def on_message_group(
386
+ self,
387
+ chat_id: Optional[Union[str, List[str]]] = None,
388
+ commands: Optional[List[str]] = None,
389
+ filters: Optional[Callable[[Message], bool]] = None,
390
+ sender_id: Optional[Union[str, List[str]]] = None,
391
+ sender_type: Optional[str] = None,
392
+ allow_forwarded: bool = True,
393
+ allow_files: bool = True,
394
+ allow_stickers: bool = True,
395
+ allow_polls: bool = True,
396
+ allow_contacts: bool = True,
397
+ allow_locations: bool = True,
398
+ min_text_length: Optional[int] = None,
399
+ max_text_length: Optional[int] = None,
400
+ contains: Optional[str] = None,
401
+ startswith: Optional[str] = None,
402
+ endswith: Optional[str] = None,
403
+ case_sensitive: bool = False
404
+ ):
405
+ """
406
+ Sync decorator for handling only group messages with extended filters.
407
+ """
408
+
409
+ def decorator(func: Callable[[Any, Message], None]):
410
+ def wrapper(bot, message: Message):
411
+
412
+ if not message.is_group:
413
+ return
414
+
415
+
416
+ if chat_id:
417
+ if isinstance(chat_id, str) and message.chat_id != chat_id:
418
+ return
419
+ if isinstance(chat_id, list) and message.chat_id not in chat_id:
420
+ return
421
+
422
+
423
+ if sender_id:
424
+ if isinstance(sender_id, str) and message.sender_id != sender_id:
425
+ return
426
+ if isinstance(sender_id, list) and message.sender_id not in sender_id:
427
+ return
428
+
429
+
430
+ if sender_type and message.sender_type != sender_type:
431
+ return
432
+
433
+
434
+ if not allow_forwarded and message.forwarded_from:
435
+ return
436
+
437
+
438
+ if not allow_files and message.file:
439
+ return
440
+ if not allow_stickers and message.sticker:
441
+ return
442
+ if not allow_polls and message.poll:
443
+ return
444
+ if not allow_contacts and message.contact_message:
445
+ return
446
+ if not allow_locations and (message.location or message.live_location):
447
+ return
448
+
449
+
450
+ if message.text:
451
+ text = message.text if case_sensitive else message.text.lower()
452
+ if min_text_length and len(message.text) < min_text_length:
453
+ return
454
+ if max_text_length and len(message.text) > max_text_length:
455
+ return
456
+ if contains and (contains if case_sensitive else contains.lower()) not in text:
457
+ return
458
+ if startswith and not text.startswith(startswith if case_sensitive else startswith.lower()):
459
+ return
460
+ if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
461
+ return
462
+
463
+
464
+ if commands:
465
+ if not message.text:
466
+ return
467
+ parts = message.text.strip().split()
468
+ cmd = parts[0].lstrip("/")
469
+ if cmd not in commands:
470
+ return
471
+ message.args = parts[1:]
472
+
473
+
474
+ if filters and not filters(message):
475
+ return
476
+
477
+ return func(bot, message)
478
+
479
+ self._message_handlers.append({
480
+ "func": wrapper,
481
+ "filters": filters,
482
+ "commands": commands,
483
+ "chat_id": chat_id,
484
+ "group_only": True,
485
+ "sender_id": sender_id,
486
+ "sender_type": sender_type
487
+ })
488
+ return wrapper
489
+
490
+ return decorator
491
+
173
492
  def on_message(self, filters: Optional[Callable[[Message], bool]] = None, commands: Optional[List[str]] = None):
174
493
  def decorator(func: Callable[[Any, Message], None]):
175
494
  self._message_handlers.append({
@@ -302,7 +621,7 @@ class Robot:
302
621
  continue
303
622
 
304
623
  threading.Thread(target=handler["func"], args=(self, context), daemon=True).start()
305
- return
624
+ continue
306
625
 
307
626
  def get_updates(
308
627
  self,
@@ -762,39 +1081,110 @@ class Robot:
762
1081
  data = response.json()
763
1082
  return data.get('data', {}).get('file_id')
764
1083
 
765
- def send_button_join(self, chat_id, title_button, username, text, reply_to_message_id=None, id="None"):
1084
+ def send_button_join(
1085
+ self,
1086
+ chat_id,
1087
+ title_button : Union[str, list],
1088
+ username : Union[str, list],
1089
+ text,
1090
+ reply_to_message_id=None,
1091
+ id="None"
1092
+ ):
766
1093
  from .button import InlineBuilder
767
- return self.send_message(
768
- chat_id=chat_id,
769
- text=text,
770
- inline_keypad=InlineBuilder()
771
- .row(
772
- InlineBuilder()
773
- .button_join_channel(
1094
+ builder = InlineBuilder()
1095
+
1096
+
1097
+ if isinstance(username, (list, tuple)) and isinstance(title_button, (list, tuple)):
1098
+ for t, u in zip(title_button, username):
1099
+ builder = builder.row(
1100
+ InlineBuilder().button_join_channel(
1101
+ text=t,
1102
+ id=id,
1103
+ username=u
1104
+ )
1105
+ )
1106
+
1107
+
1108
+ elif isinstance(username, (list, tuple)) and isinstance(title_button, str):
1109
+ for u in username:
1110
+ builder = builder.row(
1111
+ InlineBuilder().button_join_channel(
1112
+ text=title_button,
1113
+ id=id,
1114
+ username=u
1115
+ )
1116
+ )
1117
+
1118
+
1119
+ else:
1120
+ builder = builder.row(
1121
+ InlineBuilder().button_join_channel(
774
1122
  text=title_button,
775
1123
  id=id,
776
1124
  username=username
777
1125
  )
778
- ).build(),
779
- reply_to_message_id=reply_to_message_id
780
- )
781
- def send_button_url(self, chat_id, title_button, url, text, reply_to_message_id=None, id="None"):
782
- from .button import InlineBuilder
1126
+ )
1127
+
783
1128
  return self.send_message(
784
1129
  chat_id=chat_id,
785
1130
  text=text,
786
- inline_keypad=InlineBuilder()
787
- .row(
788
- InlineBuilder()
789
- .button_url_link(
1131
+ inline_keypad=builder.build(),
1132
+ reply_to_message_id=reply_to_message_id
1133
+ )
1134
+
1135
+
1136
+ def send_button_url(
1137
+ self,
1138
+ chat_id,
1139
+ title_button : Union[str, list],
1140
+ url : Union[str, list],
1141
+ text,
1142
+ reply_to_message_id=None,
1143
+ id="None"
1144
+ ):
1145
+ from .button import InlineBuilder
1146
+ builder = InlineBuilder()
1147
+
1148
+
1149
+ if isinstance(url, (list, tuple)) and isinstance(title_button, (list, tuple)):
1150
+ for t, u in zip(title_button, url):
1151
+ builder = builder.row(
1152
+ InlineBuilder().button_url_link(
1153
+ text=t,
1154
+ id=id,
1155
+ url=u
1156
+ )
1157
+ )
1158
+
1159
+
1160
+ elif isinstance(url, (list, tuple)) and isinstance(title_button, str):
1161
+ for u in url:
1162
+ builder = builder.row(
1163
+ InlineBuilder().button_url_link(
1164
+ text=title_button,
1165
+ id=id,
1166
+ url=u
1167
+ )
1168
+ )
1169
+
1170
+
1171
+ else:
1172
+ builder = builder.row(
1173
+ InlineBuilder().button_url_link(
790
1174
  text=title_button,
791
1175
  id=id,
792
1176
  url=url
793
1177
  )
794
- ).build(),
1178
+ )
1179
+
1180
+ return self.send_message(
1181
+ chat_id=chat_id,
1182
+ text=text,
1183
+ inline_keypad=builder.build(),
795
1184
  reply_to_message_id=reply_to_message_id
796
1185
  )
797
1186
 
1187
+
798
1188
  def get_upload_url(self, media_type: Literal['File', 'Image', 'Voice', 'Music', 'Gif','Video']) -> str:
799
1189
  allowed = ['File', 'Image', 'Voice', 'Music', 'Gif','Video']
800
1190
  if media_type not in allowed: