Rubka 6.5.2__py3-none-any.whl → 6.6.2__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.
Potentially problematic release.
This version of Rubka might be problematic. Click here for more details.
- rubka/api.py +3 -1
- rubka/asynco.py +54 -204
- rubka/filters.py +34 -46
- {rubka-6.5.2.dist-info → rubka-6.6.2.dist-info}/METADATA +1 -1
- {rubka-6.5.2.dist-info → rubka-6.6.2.dist-info}/RECORD +7 -7
- {rubka-6.5.2.dist-info → rubka-6.6.2.dist-info}/WHEEL +0 -0
- {rubka-6.5.2.dist-info → rubka-6.6.2.dist-info}/top_level.txt +0 -0
rubka/api.py
CHANGED
|
@@ -1669,9 +1669,11 @@ class Robot:
|
|
|
1669
1669
|
self,
|
|
1670
1670
|
chat_id: str,
|
|
1671
1671
|
message_id: str,
|
|
1672
|
-
inline_keypad: Dict[str, Any]
|
|
1672
|
+
inline_keypad: Dict[str, Any],
|
|
1673
|
+
text : str = None
|
|
1673
1674
|
) -> Dict[str, Any]:
|
|
1674
1675
|
"""Edit inline keypad of a message."""
|
|
1676
|
+
if text is not None:self._post("editMessageText", {"chat_id": chat_id,"message_id": message_id,"text": text})
|
|
1675
1677
|
return self._post("editMessageKeypad", {
|
|
1676
1678
|
"chat_id": chat_id,
|
|
1677
1679
|
"message_id": message_id,
|
rubka/asynco.py
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import aiohttp
|
|
3
|
-
import aiofiles
|
|
1
|
+
import asyncio,aiohttp,aiofiles,time,datetime,json,tempfile,os,sys,subprocess,mimetypes
|
|
4
2
|
from typing import List, Optional, Dict, Any, Literal, Callable, Union
|
|
5
3
|
from .exceptions import APIRequestError
|
|
6
4
|
from .adaptorrubka import Client as Client_get
|
|
7
5
|
from .logger import logger
|
|
8
6
|
from . import filters
|
|
9
|
-
try:
|
|
10
|
-
|
|
11
|
-
except (ImportError, ModuleNotFoundError):
|
|
12
|
-
from context import Message, InlineMessage
|
|
7
|
+
try:from .context import Message, InlineMessage
|
|
8
|
+
except (ImportError, ModuleNotFoundError):from context import Message, InlineMessage
|
|
13
9
|
class FeatureNotAvailableError(Exception):
|
|
14
10
|
pass
|
|
15
11
|
|
|
16
12
|
from tqdm.asyncio import tqdm
|
|
17
13
|
from urllib.parse import urlparse, parse_qs
|
|
18
14
|
class InvalidTokenError(Exception):pass
|
|
19
|
-
import mimetypes
|
|
20
15
|
from pathlib import Path
|
|
21
|
-
import time
|
|
22
|
-
import datetime,json
|
|
23
|
-
import tempfile
|
|
24
16
|
from tqdm import tqdm
|
|
25
|
-
import os
|
|
26
|
-
import sys
|
|
27
|
-
import subprocess
|
|
28
|
-
|
|
29
17
|
API_URL = "https://botapi.rubika.ir/v3"
|
|
30
18
|
|
|
31
19
|
def install_package(package_name: str) -> bool:
|
|
@@ -35,31 +23,7 @@ def install_package(package_name: str) -> bool:
|
|
|
35
23
|
return True
|
|
36
24
|
except Exception:
|
|
37
25
|
return False
|
|
38
|
-
|
|
39
|
-
def __init__(self, resp: dict, chat_id: str, file_id: str,
|
|
40
|
-
type_file: str, reply_to_message_id: str,
|
|
41
|
-
disable_notification: bool, text: str = None,
|
|
42
|
-
chat_keypad=None, inline_keypad=None, chat_keypad_type="None"):
|
|
43
|
-
|
|
44
|
-
self.status = resp.get("status")
|
|
45
|
-
self.status_det = resp.get("status_det")
|
|
46
|
-
self.file_id = file_id
|
|
47
|
-
self.message_id = resp["data"].get("message_id")
|
|
48
|
-
self.send_to_chat_id = chat_id
|
|
49
|
-
self.reply_to_message_id = reply_to_message_id
|
|
50
|
-
self.disable_notification = disable_notification
|
|
51
|
-
self.type_file = type_file
|
|
52
|
-
self.text = text
|
|
53
|
-
self.chat_keypad = chat_keypad
|
|
54
|
-
self.inline_keypad = inline_keypad
|
|
55
|
-
self.chat_keypad_type = chat_keypad_type
|
|
56
|
-
self.raw_response = resp
|
|
57
|
-
|
|
58
|
-
def to_dict(self):
|
|
59
|
-
return self.__dict__
|
|
60
|
-
|
|
61
|
-
def to_json(self):
|
|
62
|
-
return json.dumps(self.__dict__, ensure_ascii=False, indent=4)
|
|
26
|
+
|
|
63
27
|
def get_importlib_metadata():
|
|
64
28
|
"""Dynamically imports and returns metadata functions from importlib."""
|
|
65
29
|
try:
|
|
@@ -124,12 +88,40 @@ def show_last_six_words(text: str) -> str:
|
|
|
124
88
|
"""Returns the last 6 characters of a stripped string."""
|
|
125
89
|
text = text.strip()
|
|
126
90
|
return text[-6:]
|
|
127
|
-
|
|
91
|
+
class AttrDict(dict):
|
|
92
|
+
def __getattr__(self, item):
|
|
93
|
+
value = self.get(item)
|
|
94
|
+
if isinstance(value, dict):
|
|
95
|
+
return AttrDict(value)
|
|
96
|
+
return value
|
|
128
97
|
|
|
129
98
|
class Robot:
|
|
130
99
|
"""
|
|
131
|
-
Main
|
|
132
|
-
|
|
100
|
+
Main asynchronous class to interact with the Rubika Bot API.
|
|
101
|
+
|
|
102
|
+
This class handles sending and receiving messages, inline queries, callbacks,
|
|
103
|
+
and manages sessions and API interactions. It is initialized with a bot token
|
|
104
|
+
and provides multiple optional parameters for configuration.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
token (str): Bot token used for authentication with Rubika Bot API.
|
|
108
|
+
session_name (str | None): Optional session name for storing session data.
|
|
109
|
+
auth (str | None): Optional authentication string for advanced features.
|
|
110
|
+
Key (str | None): Optional key for additional authorization if required.
|
|
111
|
+
platform (str): Platform type, default is 'web'.
|
|
112
|
+
web_hook (str | None): Optional webhook URL for receiving updates.
|
|
113
|
+
timeout (int): Timeout for API requests in seconds (default 10).
|
|
114
|
+
show_progress (bool): Whether to show progress for long operations (default False).
|
|
115
|
+
Example:
|
|
116
|
+
```python
|
|
117
|
+
import asyncio
|
|
118
|
+
from rubka.asynco import Robot,filters,Message
|
|
119
|
+
bot = Robot(token="YOUR_BOT_TOKEN")
|
|
120
|
+
@bot.on_message(filters.is_command.start)
|
|
121
|
+
async def start_command(bot: Robot, message: Message):
|
|
122
|
+
await message.reply("Hello!")
|
|
123
|
+
asyncio.run(bot.run())
|
|
124
|
+
|
|
133
125
|
"""
|
|
134
126
|
|
|
135
127
|
def __init__(self, token: str, session_name: str = None, auth: str = None, Key: str = None, platform: str = "web", web_hook: str = None, timeout: int = 10, show_progress: bool = False):
|
|
@@ -173,7 +165,6 @@ class Robot:
|
|
|
173
165
|
print(data)
|
|
174
166
|
json_url = data.get('url', self.web_hook)
|
|
175
167
|
print(self.web_hook)
|
|
176
|
-
|
|
177
168
|
for endpoint_type in [
|
|
178
169
|
"ReceiveUpdate",
|
|
179
170
|
"ReceiveInlineMessage",
|
|
@@ -187,8 +178,6 @@ class Robot:
|
|
|
187
178
|
except Exception as e:
|
|
188
179
|
logger.error(f"Failed to set webhook from {self.web_hook}: {e}")
|
|
189
180
|
self.web_hook = None
|
|
190
|
-
|
|
191
|
-
|
|
192
181
|
async def _post(self, method: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
193
182
|
url = f"{API_URL}/{self.token}/{method}"
|
|
194
183
|
session = await self._get_session()
|
|
@@ -201,11 +190,9 @@ class Robot:
|
|
|
201
190
|
text_resp = await response.text()
|
|
202
191
|
logger.error(f"Invalid JSON response from {method}: {text_resp}")
|
|
203
192
|
raise APIRequestError(f"Invalid JSON response: {text_resp}")
|
|
204
|
-
|
|
205
193
|
if method != "getUpdates":
|
|
206
194
|
logger.debug(f"API Response from {method}: {json_resp}")
|
|
207
|
-
|
|
208
|
-
return json_resp
|
|
195
|
+
return AttrDict(json_resp)
|
|
209
196
|
except aiohttp.ClientError as e:
|
|
210
197
|
logger.error(f"API request failed: {e}")
|
|
211
198
|
raise APIRequestError(f"API request failed: {e}") from e
|
|
@@ -275,8 +262,6 @@ class Robot:
|
|
|
275
262
|
return
|
|
276
263
|
if not allow_locations and (message.location or message.live_location):
|
|
277
264
|
return
|
|
278
|
-
|
|
279
|
-
|
|
280
265
|
if message.text:
|
|
281
266
|
text = message.text if case_sensitive else message.text.lower()
|
|
282
267
|
if min_text_length and len(message.text) < min_text_length:
|
|
@@ -289,8 +274,6 @@ class Robot:
|
|
|
289
274
|
return
|
|
290
275
|
if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
|
|
291
276
|
return
|
|
292
|
-
|
|
293
|
-
|
|
294
277
|
if commands:
|
|
295
278
|
if not message.text:
|
|
296
279
|
return
|
|
@@ -299,13 +282,9 @@ class Robot:
|
|
|
299
282
|
if cmd not in commands:
|
|
300
283
|
return
|
|
301
284
|
message.args = parts[1:]
|
|
302
|
-
|
|
303
|
-
|
|
304
285
|
if filters and not filters(message):
|
|
305
286
|
return
|
|
306
|
-
|
|
307
287
|
return await func(bot, message)
|
|
308
|
-
|
|
309
288
|
self._message_handlers.append({
|
|
310
289
|
"func": wrapper,
|
|
311
290
|
"filters": filters,
|
|
@@ -346,30 +325,20 @@ class Robot:
|
|
|
346
325
|
|
|
347
326
|
if not message.is_channel:
|
|
348
327
|
return
|
|
349
|
-
|
|
350
|
-
|
|
351
328
|
if chat_id:
|
|
352
329
|
if isinstance(chat_id, str) and message.chat_id != chat_id:
|
|
353
330
|
return
|
|
354
331
|
if isinstance(chat_id, list) and message.chat_id not in chat_id:
|
|
355
332
|
return
|
|
356
|
-
|
|
357
|
-
|
|
358
333
|
if sender_id:
|
|
359
334
|
if isinstance(sender_id, str) and message.sender_id != sender_id:
|
|
360
335
|
return
|
|
361
336
|
if isinstance(sender_id, list) and message.sender_id not in sender_id:
|
|
362
337
|
return
|
|
363
|
-
|
|
364
|
-
|
|
365
338
|
if sender_type and message.sender_type != sender_type:
|
|
366
339
|
return
|
|
367
|
-
|
|
368
|
-
|
|
369
340
|
if not allow_forwarded and message.forwarded_from:
|
|
370
341
|
return
|
|
371
|
-
|
|
372
|
-
|
|
373
342
|
if not allow_files and message.file:
|
|
374
343
|
return
|
|
375
344
|
if not allow_stickers and message.sticker:
|
|
@@ -380,8 +349,6 @@ class Robot:
|
|
|
380
349
|
return
|
|
381
350
|
if not allow_locations and (message.location or message.live_location):
|
|
382
351
|
return
|
|
383
|
-
|
|
384
|
-
|
|
385
352
|
if message.text:
|
|
386
353
|
text = message.text if case_sensitive else message.text.lower()
|
|
387
354
|
if min_text_length and len(message.text) < min_text_length:
|
|
@@ -394,8 +361,6 @@ class Robot:
|
|
|
394
361
|
return
|
|
395
362
|
if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
|
|
396
363
|
return
|
|
397
|
-
|
|
398
|
-
|
|
399
364
|
if commands:
|
|
400
365
|
if not message.text:
|
|
401
366
|
return
|
|
@@ -403,14 +368,10 @@ class Robot:
|
|
|
403
368
|
cmd = parts[0].lstrip("/")
|
|
404
369
|
if cmd not in commands:
|
|
405
370
|
return
|
|
406
|
-
message.args = parts[1:]
|
|
407
|
-
|
|
408
|
-
|
|
371
|
+
message.args = parts[1:]
|
|
409
372
|
if filters and not filters(message):
|
|
410
373
|
return
|
|
411
|
-
|
|
412
374
|
return await func(bot, message)
|
|
413
|
-
|
|
414
375
|
self._message_handlers.append({
|
|
415
376
|
"func": wrapper,
|
|
416
377
|
"filters": filters,
|
|
@@ -448,33 +409,22 @@ class Robot:
|
|
|
448
409
|
|
|
449
410
|
def decorator(func: Callable[[Any, Message], None]):
|
|
450
411
|
async def wrapper(bot, message: Message):
|
|
451
|
-
|
|
452
412
|
if not message.is_group:
|
|
453
413
|
return
|
|
454
|
-
|
|
455
|
-
|
|
456
414
|
if chat_id:
|
|
457
415
|
if isinstance(chat_id, str) and message.chat_id != chat_id:
|
|
458
416
|
return
|
|
459
417
|
if isinstance(chat_id, list) and message.chat_id not in chat_id:
|
|
460
418
|
return
|
|
461
|
-
|
|
462
|
-
|
|
463
419
|
if sender_id:
|
|
464
420
|
if isinstance(sender_id, str) and message.sender_id != sender_id:
|
|
465
421
|
return
|
|
466
422
|
if isinstance(sender_id, list) and message.sender_id not in sender_id:
|
|
467
423
|
return
|
|
468
|
-
|
|
469
|
-
|
|
470
424
|
if sender_type and message.sender_type != sender_type:
|
|
471
425
|
return
|
|
472
|
-
|
|
473
|
-
|
|
474
426
|
if not allow_forwarded and message.forwarded_from:
|
|
475
427
|
return
|
|
476
|
-
|
|
477
|
-
|
|
478
428
|
if not allow_files and message.file:
|
|
479
429
|
return
|
|
480
430
|
if not allow_stickers and message.sticker:
|
|
@@ -485,8 +435,6 @@ class Robot:
|
|
|
485
435
|
return
|
|
486
436
|
if not allow_locations and (message.location or message.live_location):
|
|
487
437
|
return
|
|
488
|
-
|
|
489
|
-
|
|
490
438
|
if message.text:
|
|
491
439
|
text = message.text if case_sensitive else message.text.lower()
|
|
492
440
|
if min_text_length and len(message.text) < min_text_length:
|
|
@@ -499,8 +447,6 @@ class Robot:
|
|
|
499
447
|
return
|
|
500
448
|
if endswith and not text.endswith(endswith if case_sensitive else endswith.lower()):
|
|
501
449
|
return
|
|
502
|
-
|
|
503
|
-
|
|
504
450
|
if commands:
|
|
505
451
|
if not message.text:
|
|
506
452
|
return
|
|
@@ -509,13 +455,9 @@ class Robot:
|
|
|
509
455
|
if cmd not in commands:
|
|
510
456
|
return
|
|
511
457
|
message.args = parts[1:]
|
|
512
|
-
|
|
513
|
-
|
|
514
458
|
if filters and not filters(message):
|
|
515
459
|
return
|
|
516
|
-
|
|
517
460
|
return await func(bot, message)
|
|
518
|
-
|
|
519
461
|
self._message_handlers.append({
|
|
520
462
|
"func": wrapper,
|
|
521
463
|
"filters": filters,
|
|
@@ -738,13 +680,12 @@ class Robot:
|
|
|
738
680
|
|
|
739
681
|
async def _handle_inline_query(self, inline_message: InlineMessage):
|
|
740
682
|
aux_button_id = inline_message.aux_data.button_id if inline_message.aux_data else None
|
|
741
|
-
|
|
742
683
|
for handler in self._inline_query_handlers:
|
|
743
684
|
if handler["button_id"] is None or handler["button_id"] == aux_button_id:
|
|
744
685
|
try:
|
|
745
686
|
await handler["func"](self, inline_message)
|
|
746
687
|
except Exception as e:
|
|
747
|
-
|
|
688
|
+
raise Exception(f"Error in inline query handler: {e}")
|
|
748
689
|
|
|
749
690
|
def on_inline_query(self, button_id: Optional[str] = None):
|
|
750
691
|
def decorator(func: Callable[[Any, InlineMessage], None]):
|
|
@@ -757,33 +698,19 @@ class Robot:
|
|
|
757
698
|
def on_inline_query_prefix(self, prefix: str, button_id: Optional[str] = None):
|
|
758
699
|
if not prefix.startswith('/'):
|
|
759
700
|
prefix = '/' + prefix
|
|
760
|
-
|
|
761
701
|
def decorator(func: Callable[[Any, InlineMessage], None]):
|
|
762
|
-
|
|
763
702
|
async def handler_wrapper(bot_instance, inline_message: InlineMessage):
|
|
764
|
-
|
|
765
703
|
if not inline_message.raw_data or 'text' not in inline_message.raw_data:
|
|
766
704
|
return
|
|
767
|
-
|
|
768
705
|
query_text = inline_message.raw_data['text']
|
|
769
|
-
|
|
770
|
-
|
|
771
706
|
if query_text.startswith(prefix):
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
707
|
try:
|
|
776
708
|
await func(bot_instance, inline_message)
|
|
777
709
|
except Exception as e:
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
710
|
+
raise Exception(f"Error in inline query prefix handler '{prefix}': {e}")
|
|
781
711
|
self._inline_query_handlers.append({
|
|
782
712
|
"func": handler_wrapper,
|
|
783
|
-
"button_id": button_id
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
713
|
+
"button_id": button_id
|
|
787
714
|
})
|
|
788
715
|
return func
|
|
789
716
|
return decorator
|
|
@@ -791,40 +718,29 @@ class Robot:
|
|
|
791
718
|
if update.get("type") == "ReceiveQuery":
|
|
792
719
|
msg = update.get("inline_message", {})
|
|
793
720
|
context = InlineMessage(bot=self, raw_data=msg)
|
|
794
|
-
|
|
795
|
-
|
|
796
721
|
if hasattr(self, "_callback_handlers"):
|
|
797
722
|
for handler in self._callback_handlers:
|
|
798
723
|
if not handler["button_id"] or getattr(context.aux_data, "button_id", None) == handler["button_id"]:
|
|
799
724
|
asyncio.create_task(handler["func"](self, context))
|
|
800
|
-
|
|
801
|
-
|
|
802
725
|
asyncio.create_task(self._handle_inline_query(context))
|
|
803
726
|
return
|
|
804
727
|
|
|
805
728
|
if update.get("type") == "NewMessage":
|
|
806
729
|
msg = update.get("new_message", {})
|
|
807
730
|
try:
|
|
808
|
-
if msg.get("time") and (time.time() - float(msg["time"])) > 20:
|
|
809
|
-
|
|
810
|
-
except (ValueError, TypeError):
|
|
811
|
-
return
|
|
812
|
-
|
|
731
|
+
if msg.get("time") and (time.time() - float(msg["time"])) > 20:return
|
|
732
|
+
except (ValueError, TypeError):return
|
|
813
733
|
context = Message(bot=self,
|
|
814
734
|
chat_id=update.get("chat_id"),
|
|
815
735
|
message_id=msg.get("message_id"),
|
|
816
736
|
sender_id=msg.get("sender_id"),
|
|
817
737
|
text=msg.get("text"),
|
|
818
738
|
raw_data=msg)
|
|
819
|
-
|
|
820
|
-
|
|
821
739
|
if context.aux_data and self._callback_handlers:
|
|
822
740
|
for handler in self._callback_handlers:
|
|
823
741
|
if not handler["button_id"] or context.aux_data.button_id == handler["button_id"]:
|
|
824
742
|
asyncio.create_task(handler["func"](self, context))
|
|
825
743
|
return
|
|
826
|
-
|
|
827
|
-
|
|
828
744
|
if self._message_handlers:
|
|
829
745
|
for handler_info in self._message_handlers:
|
|
830
746
|
|
|
@@ -836,18 +752,12 @@ class Robot:
|
|
|
836
752
|
if cmd not in handler_info["commands"]:
|
|
837
753
|
continue
|
|
838
754
|
context.args = parts[1:]
|
|
839
|
-
|
|
840
|
-
|
|
841
755
|
if handler_info["filters"]:
|
|
842
756
|
if not handler_info["filters"](context):
|
|
843
757
|
continue
|
|
844
|
-
|
|
845
|
-
|
|
846
758
|
if not handler_info["commands"] and not handler_info["filters"]:
|
|
847
759
|
asyncio.create_task(handler_info["func"](self, context))
|
|
848
760
|
continue
|
|
849
|
-
|
|
850
|
-
|
|
851
761
|
if handler_info["commands"] or handler_info["filters"]:
|
|
852
762
|
asyncio.create_task(handler_info["func"](self, context))#jaq
|
|
853
763
|
continue
|
|
@@ -864,25 +774,18 @@ class Robot:
|
|
|
864
774
|
if offset_id: params['offset_id'] = offset_id
|
|
865
775
|
if limit: params['limit'] = limit
|
|
866
776
|
async with session.get(self.web_hook, params=params) as response:
|
|
867
|
-
response.raise_for_status()
|
|
868
|
-
|
|
777
|
+
response.raise_for_status()
|
|
869
778
|
return await response.json()
|
|
870
779
|
|
|
871
780
|
def _is_duplicate(self, message_id: str, max_age_sec: int = 300) -> bool:
|
|
872
781
|
now = time.time()
|
|
873
782
|
expired = [mid for mid, ts in self._processed_message_ids.items() if now - ts > max_age_sec]
|
|
874
|
-
for mid in expired:
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
if message_id in self._processed_message_ids:
|
|
878
|
-
return True
|
|
879
|
-
|
|
783
|
+
for mid in expired:del self._processed_message_ids[mid]
|
|
784
|
+
if message_id in self._processed_message_ids:return True
|
|
880
785
|
self._processed_message_ids[message_id] = now
|
|
881
786
|
return False
|
|
882
787
|
|
|
883
788
|
|
|
884
|
-
|
|
885
|
-
|
|
886
789
|
async def run(
|
|
887
790
|
self,
|
|
888
791
|
debug: bool = False,
|
|
@@ -1449,8 +1352,7 @@ class Robot:
|
|
|
1449
1352
|
|
|
1450
1353
|
|
|
1451
1354
|
_log("Auto-restart requested. You can call run(...) again as needed.", "warning")
|
|
1452
|
-
|
|
1453
|
-
|
|
1355
|
+
|
|
1454
1356
|
async def send_message(
|
|
1455
1357
|
self,
|
|
1456
1358
|
chat_id: str,
|
|
@@ -1460,27 +1362,21 @@ class Robot:
|
|
|
1460
1362
|
disable_notification: bool = False,
|
|
1461
1363
|
reply_to_message_id: Optional[str] = None,
|
|
1462
1364
|
chat_keypad_type: Optional[Literal["New", "Removed"]] = None
|
|
1463
|
-
) -> Dict[str, Any]:
|
|
1365
|
+
) -> Dict[str, Any]:
|
|
1464
1366
|
payload = {
|
|
1465
1367
|
"chat_id": chat_id,
|
|
1466
1368
|
"text": text,
|
|
1467
1369
|
"disable_notification": disable_notification,
|
|
1468
1370
|
}
|
|
1469
|
-
|
|
1470
1371
|
if chat_keypad:
|
|
1471
1372
|
payload["chat_keypad"] = chat_keypad
|
|
1472
1373
|
payload["chat_keypad_type"] = chat_keypad_type or "New"
|
|
1473
|
-
|
|
1474
1374
|
if inline_keypad:
|
|
1475
1375
|
payload["inline_keypad"] = inline_keypad
|
|
1476
|
-
|
|
1477
1376
|
if reply_to_message_id:
|
|
1478
1377
|
payload["reply_to_message_id"] = reply_to_message_id
|
|
1479
|
-
|
|
1480
1378
|
return await self._post("sendMessage", payload)
|
|
1481
1379
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
1380
|
async def get_url_file(self,file_id):
|
|
1485
1381
|
data = await self._post("getFile", {'file_id': file_id})
|
|
1486
1382
|
return data.get("data").get("download_url")
|
|
@@ -1493,14 +1389,11 @@ class Robot:
|
|
|
1493
1389
|
|
|
1494
1390
|
async def check_join(self, channel_guid: str, chat_id: str = None) -> Union[bool, list[str]]:
|
|
1495
1391
|
client = self._get_client()
|
|
1496
|
-
|
|
1497
1392
|
if chat_id:
|
|
1498
1393
|
chat_info_data = await self.get_chat(chat_id)
|
|
1499
1394
|
chat_info = chat_info_data.get('data', {}).get('chat', {})
|
|
1500
1395
|
username = chat_info.get('username')
|
|
1501
1396
|
user_id = chat_info.get('user_id')
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
1397
|
if username:
|
|
1505
1398
|
result = await asyncio.to_thread(self.get_all_member, channel_guid, search_text=username)
|
|
1506
1399
|
members = result.get('in_chat_members', [])
|
|
@@ -1519,7 +1412,6 @@ class Robot:
|
|
|
1519
1412
|
id="None"):
|
|
1520
1413
|
from .button import InlineBuilder
|
|
1521
1414
|
builder = InlineBuilder()
|
|
1522
|
-
|
|
1523
1415
|
if isinstance(username, (list, tuple)) and isinstance(title_button, (list, tuple)):
|
|
1524
1416
|
for t, u in zip(title_button, username):
|
|
1525
1417
|
builder = builder.row(
|
|
@@ -1589,55 +1481,41 @@ class Robot:
|
|
|
1589
1481
|
url=url
|
|
1590
1482
|
)
|
|
1591
1483
|
)
|
|
1592
|
-
|
|
1593
1484
|
return await self.send_message(
|
|
1594
1485
|
chat_id=chat_id,
|
|
1595
1486
|
text=text,
|
|
1596
1487
|
inline_keypad=builder.build(),
|
|
1597
1488
|
reply_to_message_id=reply_to_message_id
|
|
1598
1489
|
)
|
|
1599
|
-
|
|
1600
1490
|
def get_all_member(self, channel_guid: str, search_text: str = None, start_id: str = None, just_get_guids: bool = False):
|
|
1601
|
-
|
|
1602
1491
|
client = self._get_client()
|
|
1603
1492
|
return client.get_all_members(channel_guid, search_text, start_id, just_get_guids)
|
|
1604
|
-
|
|
1605
1493
|
async def send_poll(self, chat_id: str, question: str, options: List[str]) -> Dict[str, Any]:
|
|
1606
1494
|
return await self._post("sendPoll", {"chat_id": chat_id, "question": question, "options": options})
|
|
1607
|
-
|
|
1608
1495
|
async def send_location(self, chat_id: str, latitude: str, longitude: str, disable_notification: bool = False, inline_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, chat_keypad_type: Optional[Literal["New", "Removed"]] = None) -> Dict[str, Any]:
|
|
1609
1496
|
payload = {"chat_id": chat_id, "latitude": latitude, "longitude": longitude, "disable_notification": disable_notification}
|
|
1610
1497
|
if inline_keypad: payload["inline_keypad"] = inline_keypad
|
|
1611
1498
|
if reply_to_message_id: payload["reply_to_message_id"] = reply_to_message_id
|
|
1612
1499
|
if chat_keypad_type: payload["chat_keypad_type"] = chat_keypad_type
|
|
1613
1500
|
return await self._post("sendLocation", {k: v for k, v in payload.items() if v is not None})
|
|
1614
|
-
|
|
1615
1501
|
async def send_contact(self, chat_id: str, first_name: str, last_name: str, phone_number: str) -> Dict[str, Any]:
|
|
1616
1502
|
return await self._post("sendContact", {"chat_id": chat_id, "first_name": first_name, "last_name": last_name, "phone_number": phone_number})
|
|
1617
|
-
|
|
1618
1503
|
async def get_chat(self, chat_id: str) -> Dict[str, Any]:
|
|
1619
1504
|
return await self._post("getChat", {"chat_id": chat_id})
|
|
1620
|
-
|
|
1621
1505
|
async def upload_media_file(self, upload_url: str, name: str, path: Union[str, Path]) -> str:
|
|
1622
1506
|
is_temp_file = False
|
|
1623
1507
|
session = await self._get_session()
|
|
1624
|
-
|
|
1625
1508
|
if isinstance(path, str) and path.startswith("http"):
|
|
1626
1509
|
async with session.get(path) as response:
|
|
1627
1510
|
if response.status != 200:
|
|
1628
1511
|
raise Exception(f"Failed to download file from URL ({response.status})")
|
|
1629
|
-
|
|
1630
1512
|
content = await response.read()
|
|
1631
|
-
|
|
1632
1513
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
|
1633
1514
|
temp_file.write(content)
|
|
1634
1515
|
path = temp_file.name
|
|
1635
1516
|
is_temp_file = True
|
|
1636
|
-
|
|
1637
1517
|
file_size = os.path.getsize(path)
|
|
1638
|
-
|
|
1639
1518
|
progress_bar = tqdm(total=file_size, unit='B', unit_scale=True, unit_divisor=1024, desc=f'Uploading : {name}', bar_format='{l_bar}{bar:100}{r_bar}', colour='cyan', disable=not self.show_progress)
|
|
1640
|
-
|
|
1641
1519
|
async def file_progress_generator(file_path, chunk_size=8192):
|
|
1642
1520
|
async with aiofiles.open(file_path, 'rb') as f:
|
|
1643
1521
|
while True:
|
|
@@ -1646,7 +1524,6 @@ class Robot:
|
|
|
1646
1524
|
break
|
|
1647
1525
|
progress_bar.update(len(chunk))
|
|
1648
1526
|
yield chunk
|
|
1649
|
-
|
|
1650
1527
|
data = aiohttp.FormData()
|
|
1651
1528
|
data.add_field('file', file_progress_generator(path), filename=name, content_type='application/octet-stream')
|
|
1652
1529
|
try:
|
|
@@ -1661,12 +1538,9 @@ class Robot:
|
|
|
1661
1538
|
return json_data.get('data', {}).get('file_id')
|
|
1662
1539
|
except :
|
|
1663
1540
|
raise FeatureNotAvailableError(f"files is not currently supported by the server.")
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
1541
|
def get_extension(content_type: str) -> str:
|
|
1667
1542
|
ext = mimetypes.guess_extension(content_type)
|
|
1668
1543
|
return ext if ext else ''
|
|
1669
|
-
|
|
1670
1544
|
async def download(self, file_id: str, save_as: str = None, chunk_size: int = 1024 * 512,timeout_sec: int = 60, verbose: bool = False):
|
|
1671
1545
|
"""
|
|
1672
1546
|
Download a file from server using its file_id with chunked transfer,
|
|
@@ -1685,16 +1559,11 @@ class Robot:
|
|
|
1685
1559
|
Returns:
|
|
1686
1560
|
bool: True if success, raises exceptions otherwise.
|
|
1687
1561
|
"""
|
|
1688
|
-
|
|
1689
1562
|
try:
|
|
1690
1563
|
url = await self.get_url_file(file_id)
|
|
1691
|
-
if not url:
|
|
1692
|
-
|
|
1693
|
-
except Exception as e:
|
|
1694
|
-
raise ValueError(f"Failed to get download URL: {e}")
|
|
1695
|
-
|
|
1564
|
+
if not url:raise ValueError("Download URL not found in response.")
|
|
1565
|
+
except Exception as e:raise ValueError(f"Failed to get download URL: {e}")
|
|
1696
1566
|
timeout = aiohttp.ClientTimeout(total=timeout_sec)
|
|
1697
|
-
|
|
1698
1567
|
try:
|
|
1699
1568
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
1700
1569
|
async with session.get(url) as resp:
|
|
@@ -1706,21 +1575,17 @@ class Robot:
|
|
|
1706
1575
|
message="Failed to download file.",
|
|
1707
1576
|
headers=resp.headers
|
|
1708
1577
|
)
|
|
1709
|
-
|
|
1710
1578
|
if not save_as:
|
|
1711
1579
|
content_disp = resp.headers.get("Content-Disposition", "")
|
|
1712
1580
|
import re
|
|
1713
1581
|
match = re.search(r'filename="?([^\";]+)"?', content_disp)
|
|
1714
|
-
if match:
|
|
1715
|
-
save_as = match.group(1)
|
|
1582
|
+
if match:save_as = match.group(1)
|
|
1716
1583
|
else:
|
|
1717
1584
|
content_type = resp.headers.get("Content-Type", "").split(";")[0]
|
|
1718
1585
|
extension = mimetypes.guess_extension(content_type) or ".bin"
|
|
1719
1586
|
save_as = f"{file_id}{extension}"
|
|
1720
|
-
|
|
1721
1587
|
total_size = int(resp.headers.get("Content-Length", 0))
|
|
1722
1588
|
progress = tqdm(total=total_size, unit="B", unit_scale=True, disable=not verbose)
|
|
1723
|
-
|
|
1724
1589
|
async with aiofiles.open(save_as, "wb") as f:
|
|
1725
1590
|
async for chunk in resp.content.iter_chunked(chunk_size):
|
|
1726
1591
|
await f.write(chunk)
|
|
@@ -1783,7 +1648,7 @@ class Robot:
|
|
|
1783
1648
|
"inline_keypad":inline_keypad,
|
|
1784
1649
|
"chat_keypad_type":chat_keypad_type
|
|
1785
1650
|
}
|
|
1786
|
-
return result
|
|
1651
|
+
return AttrDict(result)
|
|
1787
1652
|
|
|
1788
1653
|
async def _send_file_generic(self, media_type, chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type):
|
|
1789
1654
|
if path:
|
|
@@ -1802,43 +1667,31 @@ class Robot:
|
|
|
1802
1667
|
return await self._send_file_generic("File", chat_id, path, file_id, caption, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
|
|
1803
1668
|
async def send_music(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = "music", inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
|
|
1804
1669
|
return await self._send_file_generic("File", chat_id, path, file_id, text, f"{file_name}.ogg", inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
|
|
1805
|
-
|
|
1806
1670
|
async def send_video(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
|
|
1807
1671
|
return await self._send_file_generic("Video", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
|
|
1808
|
-
|
|
1809
1672
|
async def send_voice(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
|
|
1810
1673
|
return await self._send_file_generic("voice", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
|
|
1811
|
-
|
|
1812
1674
|
async def send_image(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
|
|
1813
1675
|
return await self._send_file_generic("Image", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
|
|
1814
|
-
|
|
1815
1676
|
async def send_gif(self, chat_id: str, path: Optional[Union[str, Path]] = None, file_id: Optional[str] = None, text: Optional[str] = None, file_name: Optional[str] = None, inline_keypad: Optional[Dict[str, Any]] = None, chat_keypad: Optional[Dict[str, Any]] = None, reply_to_message_id: Optional[str] = None, disable_notification: bool = False, chat_keypad_type: Optional[Literal["New", "Removed", "None"]] = "None") -> Dict[str, Any]:
|
|
1816
1677
|
return await self._send_file_generic("Gif", chat_id, path, file_id, text, file_name, inline_keypad, chat_keypad, reply_to_message_id, disable_notification, chat_keypad_type)
|
|
1817
|
-
|
|
1818
1678
|
async def forward_message(self, from_chat_id: str, message_id: str, to_chat_id: str, disable_notification: bool = False) -> Dict[str, Any]:
|
|
1819
1679
|
return await self._post("forwardMessage", {"from_chat_id": from_chat_id, "message_id": message_id, "to_chat_id": to_chat_id, "disable_notification": disable_notification})
|
|
1820
|
-
|
|
1821
1680
|
async def edit_message_text(self, chat_id: str, message_id: str, text: str) -> Dict[str, Any]:
|
|
1822
1681
|
return await self._post("editMessageText", {"chat_id": chat_id, "message_id": message_id, "text": text})
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
return await self._post("editMessageKeypad", {"chat_id": chat_id,
|
|
1826
|
-
|
|
1682
|
+
async def edit_inline_keypad(self,chat_id: str,message_id: str,inline_keypad: Dict[str, Any],text: str = None) -> Dict[str, Any]:
|
|
1683
|
+
if text is not None:await self._post("editMessageText", {"chat_id": chat_id,"message_id": message_id,"text": text})
|
|
1684
|
+
return await self._post("editMessageKeypad", {"chat_id": chat_id,"message_id": message_id,"inline_keypad": inline_keypad})
|
|
1827
1685
|
async def delete_message(self, chat_id: str, message_id: str) -> Dict[str, Any]:
|
|
1828
1686
|
return await self._post("deleteMessage", {"chat_id": chat_id, "message_id": message_id})
|
|
1829
|
-
|
|
1830
1687
|
async def set_commands(self, bot_commands: List[Dict[str, str]]) -> Dict[str, Any]:
|
|
1831
1688
|
return await self._post("setCommands", {"bot_commands": bot_commands})
|
|
1832
|
-
|
|
1833
1689
|
async def update_bot_endpoint(self, url: str, type: str) -> Dict[str, Any]:
|
|
1834
1690
|
return await self._post("updateBotEndpoints", {"url": url, "type": type})
|
|
1835
|
-
|
|
1836
1691
|
async def remove_keypad(self, chat_id: str) -> Dict[str, Any]:
|
|
1837
1692
|
return await self._post("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "Removed"})
|
|
1838
|
-
|
|
1839
1693
|
async def edit_chat_keypad(self, chat_id: str, chat_keypad: Dict[str, Any]) -> Dict[str, Any]:
|
|
1840
1694
|
return await self._post("editChatKeypad", {"chat_id": chat_id, "chat_keypad_type": "New", "chat_keypad": chat_keypad})
|
|
1841
|
-
|
|
1842
1695
|
async def get_name(self, chat_id: str) -> str:
|
|
1843
1696
|
try:
|
|
1844
1697
|
chat = await self.get_chat(chat_id)
|
|
@@ -1854,9 +1707,6 @@ class Robot:
|
|
|
1854
1707
|
return title if title else "null"
|
|
1855
1708
|
else:return "null"
|
|
1856
1709
|
except Exception:return "null"
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
1710
|
async def get_username(self, chat_id: str) -> str:
|
|
1861
1711
|
chat_info = await self.get_chat(chat_id)
|
|
1862
1712
|
return chat_info.get("data", {}).get("chat", {}).get("username", "None")
|
rubka/filters.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
from typing import Callable
|
|
2
2
|
import re
|
|
3
|
-
|
|
3
|
+
class TextFilter:
|
|
4
|
+
def __call__(self, keyword=None):
|
|
5
|
+
if keyword is None:
|
|
6
|
+
return Filter(lambda m: getattr(m, "is_text", False))
|
|
7
|
+
else:
|
|
8
|
+
return Filter(lambda m: getattr(m, "is_text", False) and keyword in getattr(m, "text", ""))
|
|
4
9
|
class Filter:
|
|
5
10
|
def __init__(self, func: Callable):
|
|
6
11
|
self.func = func
|
|
@@ -49,8 +54,32 @@ class Filter:
|
|
|
49
54
|
|
|
50
55
|
def __truediv__(self, other):
|
|
51
56
|
return Filter(lambda m: self(m) / (other(m) if callable(other) else other))
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
class IsCommand(Filter):
|
|
58
|
+
def __init__(self, commands=None):
|
|
59
|
+
if commands is None:
|
|
60
|
+
func = lambda m: getattr(m, "is_command", False)
|
|
61
|
+
else:
|
|
62
|
+
if isinstance(commands, str):
|
|
63
|
+
commands = [commands]
|
|
64
|
+
func = lambda m: getattr(m, "is_command", False) and getattr(m, "text", "").lstrip("/").split()[0] in commands
|
|
65
|
+
|
|
66
|
+
super().__init__(func)
|
|
67
|
+
|
|
68
|
+
def __getattr__(self, name: str):
|
|
69
|
+
return IsCommand([name])
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class IsText:
|
|
73
|
+
def __call__(self, text=None):
|
|
74
|
+
if text is None:
|
|
75
|
+
func = lambda m: m.is_text is True and m.is_file is False
|
|
76
|
+
else:
|
|
77
|
+
if isinstance(text, str):
|
|
78
|
+
text = [text]
|
|
79
|
+
func = lambda m: m.is_text is True and m.is_file is False and m.text in text
|
|
80
|
+
|
|
81
|
+
return Filter(func)
|
|
82
|
+
#is_text = Filter(lambda m: getattr(m, "is_text", False))
|
|
54
83
|
is_file = Filter(lambda m: getattr(m, "file", None) is not None)
|
|
55
84
|
is_sticker = Filter(lambda m: getattr(m, "sticker", None) is not None)
|
|
56
85
|
is_contact = Filter(lambda m: getattr(m, "contact_message", None) is not None)
|
|
@@ -59,7 +88,8 @@ is_location = Filter(lambda m: getattr(m, "location", None) is not None)
|
|
|
59
88
|
is_live_location = Filter(lambda m: getattr(m, "live_location", None) is not None)
|
|
60
89
|
has_any_media = Filter(lambda m: getattr(m, "has_any_media", False))
|
|
61
90
|
has_media = Filter(lambda m: getattr(m, "has_media", False))
|
|
62
|
-
|
|
91
|
+
is_text = IsText()
|
|
92
|
+
is_command = IsCommand()
|
|
63
93
|
is_user = Filter(lambda m: getattr(m, "is_user", False))
|
|
64
94
|
is_private = Filter(lambda m: getattr(m, "is_private", False))
|
|
65
95
|
is_group = Filter(lambda m: getattr(m, "is_group", False))
|
|
@@ -67,13 +97,8 @@ is_channel = Filter(lambda m: getattr(m, "is_channel", False))
|
|
|
67
97
|
is_reply = Filter(lambda m: getattr(m, "is_reply", False))
|
|
68
98
|
is_forwarded = Filter(lambda m: getattr(m, "is_forwarded", False))
|
|
69
99
|
is_edited = Filter(lambda m: getattr(m, "is_edited", False))
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
100
|
def text(keyword: str):
|
|
75
101
|
return Filter(lambda m: getattr(m, "text", "") and keyword in m.text)
|
|
76
|
-
|
|
77
102
|
def text_length(min_len: int = 0, max_len: int = None):
|
|
78
103
|
def _filter(m):
|
|
79
104
|
t = getattr(m, "text", "")
|
|
@@ -82,26 +107,19 @@ def text_length(min_len: int = 0, max_len: int = None):
|
|
|
82
107
|
if max_len is not None and len(t) > max_len: return False
|
|
83
108
|
return True
|
|
84
109
|
return Filter(_filter)
|
|
85
|
-
|
|
86
110
|
def text_regex(pattern: str):
|
|
87
111
|
regex = re.compile(pattern)
|
|
88
112
|
return Filter(lambda m: getattr(m, "text", "") and regex.search(m.text))
|
|
89
|
-
|
|
90
113
|
def text_startswith(prefix: str):
|
|
91
114
|
return Filter(lambda m: getattr(m, "text", "").startswith(prefix) if getattr(m, "text", None) else False)
|
|
92
|
-
|
|
93
115
|
def text_endswith(suffix: str):
|
|
94
116
|
return Filter(lambda m: getattr(m, "text", "").endswith(suffix) if getattr(m, "text", None) else False)
|
|
95
|
-
|
|
96
117
|
def text_upper():
|
|
97
118
|
return Filter(lambda m: getattr(m, "text", "").isupper() if getattr(m, "text", None) else False)
|
|
98
|
-
|
|
99
119
|
def text_lower():
|
|
100
120
|
return Filter(lambda m: getattr(m, "text", "").islower() if getattr(m, "text", None) else False)
|
|
101
|
-
|
|
102
121
|
def text_digit():
|
|
103
122
|
return Filter(lambda m: getattr(m, "text", "").isdigit() if getattr(m, "text", None) else False)
|
|
104
|
-
|
|
105
123
|
def text_word_count(min_words: int = 1, max_words: int = None):
|
|
106
124
|
def _filter(m):
|
|
107
125
|
t = getattr(m, "text", "")
|
|
@@ -111,49 +129,28 @@ def text_word_count(min_words: int = 1, max_words: int = None):
|
|
|
111
129
|
if max_words is not None and wc > max_words: return False
|
|
112
130
|
return True
|
|
113
131
|
return Filter(_filter)
|
|
114
|
-
|
|
115
132
|
def text_contains_any(keywords: list):
|
|
116
133
|
return Filter(lambda m: getattr(m, "text", "") and any(k in m.text for k in keywords))
|
|
117
|
-
|
|
118
134
|
def text_equals(value: str):
|
|
119
135
|
return Filter(lambda m: getattr(m, "text", None) == value)
|
|
120
|
-
|
|
121
136
|
def text_not_equals(value: str):
|
|
122
137
|
return Filter(lambda m: getattr(m, "text", None) != value)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
138
|
def file_size_gt(size: int):
|
|
128
139
|
return Filter(lambda m: m.file and getattr(m.file, "size", 0) > size)
|
|
129
|
-
|
|
130
140
|
def file_size_lt(size: int):
|
|
131
141
|
return Filter(lambda m: m.file and getattr(m.file, "size", 0) < size)
|
|
132
|
-
|
|
133
142
|
def file_name_contains(substring: str):
|
|
134
143
|
return Filter(lambda m: m.file and substring in getattr(m.file, "file_name", ""))
|
|
135
|
-
|
|
136
144
|
def file_extension(ext: str):
|
|
137
145
|
return Filter(lambda m: m.file and getattr(m.file, "file_name", "").endswith(ext))
|
|
138
|
-
|
|
139
146
|
def file_id_is(file_id: str):
|
|
140
147
|
return Filter(lambda m: m.file and getattr(m.file, "file_id", None) == file_id)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
148
|
def sticker_id_is(sid: str):
|
|
146
149
|
return Filter(lambda m: m.sticker and getattr(m.sticker, "sticker_id", None) == sid)
|
|
147
|
-
|
|
148
150
|
def sticker_emoji_is(emoji: str):
|
|
149
151
|
return Filter(lambda m: m.sticker and getattr(m.sticker, "emoji", None) == emoji)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
152
|
def poll_question_contains(keyword: str):
|
|
155
153
|
return Filter(lambda m: m.poll and keyword in getattr(m.poll, "question", ""))
|
|
156
|
-
|
|
157
154
|
def poll_option_count(min_options: int = 1, max_options: int = None):
|
|
158
155
|
def _filter(m):
|
|
159
156
|
if not getattr(m, "poll", None): return False
|
|
@@ -162,27 +159,18 @@ def poll_option_count(min_options: int = 1, max_options: int = None):
|
|
|
162
159
|
if max_options is not None and len(options) > max_options: return False
|
|
163
160
|
return True
|
|
164
161
|
return Filter(_filter)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
162
|
def location_within(lat_min, lat_max, long_min, long_max):
|
|
170
163
|
def _filter(m):
|
|
171
164
|
loc = getattr(m, "location", None)
|
|
172
165
|
if not loc: return False
|
|
173
166
|
return lat_min <= getattr(loc, "lat", 0) <= lat_max and long_min <= getattr(loc, "long", 0) <= long_max
|
|
174
167
|
return Filter(_filter)
|
|
175
|
-
|
|
176
168
|
def live_location_within(lat_min, lat_max, long_min, long_max):
|
|
177
169
|
def _filter(m):
|
|
178
170
|
loc = getattr(m, "live_location", None)
|
|
179
171
|
if not loc: return False
|
|
180
172
|
return lat_min <= getattr(loc, "lat", 0) <= lat_max and long_min <= getattr(loc, "long", 0) <= long_max
|
|
181
173
|
return Filter(_filter)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
174
|
def has_media_types(types: list):
|
|
187
175
|
return Filter(lambda m: any(getattr(m, t, None) for t in types))
|
|
188
176
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Rubka
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.6.2
|
|
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/blob/main/project_library.zip
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
rubka/__init__.py,sha256=TR1DABU5Maz2eO62ZEFiwOqNU0dH6l6HZfqRUxeo4eY,194
|
|
2
|
-
rubka/api.py,sha256=
|
|
3
|
-
rubka/asynco.py,sha256=
|
|
2
|
+
rubka/api.py,sha256=FkvXi6lGyDhy-GStfT9IPtmnpir75ggNsC4bsgy2tKw,68838
|
|
3
|
+
rubka/asynco.py,sha256=O5sr6Ev7LCsC5egjDk5uzJyeZEPqmxOH27lUBgzHM8A,83517
|
|
4
4
|
rubka/button.py,sha256=vU9OvWXCD4MRrTJ8Xmivd4L471-06zrD2qpZBTw5vjY,13305
|
|
5
5
|
rubka/config.py,sha256=Bck59xkOiqioLv0GkQ1qPGnBXVctz1hKk6LT4h2EPx0,78
|
|
6
6
|
rubka/context.py,sha256=4YZs7DiZD_HWOqY76hwwajG0J-bLy6wjeKtQT3EatZU,19341
|
|
7
7
|
rubka/decorators.py,sha256=hGwUoE4q2ImrunJIGJ_kzGYYxQf1ueE0isadqraKEts,1157
|
|
8
8
|
rubka/exceptions.py,sha256=tujZt1XrhWaw-lmdeVadVceUptpw4XzNgE44sAAY0gs,90
|
|
9
|
-
rubka/filters.py,sha256=
|
|
9
|
+
rubka/filters.py,sha256=DY1bdkpRKIiLtVcy6X3hOnlGPcVOK4HFb3QgmaPx6Oo,12116
|
|
10
10
|
rubka/jobs.py,sha256=GvLMLsVhcSEzRTgkvnPISPEBN71suW2xXI0hUaUZPTo,378
|
|
11
11
|
rubka/keyboards.py,sha256=7nr-dT2bQJVQnQ6RMWPTSjML6EEk6dsBx-4d8pab8xk,488
|
|
12
12
|
rubka/keypad.py,sha256=yGsNt8W5HtUFBzVF1m_p7GySlu1hwIcSvXZ4BTdrlvg,9558
|
|
@@ -35,7 +35,7 @@ rubka/adaptorrubka/types/socket/message.py,sha256=0WgLMZh4eow8Zn7AiSX4C3GZjQTkIg
|
|
|
35
35
|
rubka/adaptorrubka/utils/__init__.py,sha256=OgCFkXdNFh379quNwIVOAWY2NP5cIOxU5gDRRALTk4o,54
|
|
36
36
|
rubka/adaptorrubka/utils/configs.py,sha256=nMUEOJh1NqDJsf9W9PurkN_DLYjO6kKPMm923i4Jj_A,492
|
|
37
37
|
rubka/adaptorrubka/utils/utils.py,sha256=5-LioLNYX_TIbQGDeT50j7Sg9nAWH2LJUUs-iEXpsUY,8816
|
|
38
|
-
rubka-6.
|
|
39
|
-
rubka-6.
|
|
40
|
-
rubka-6.
|
|
41
|
-
rubka-6.
|
|
38
|
+
rubka-6.6.2.dist-info/METADATA,sha256=-XVEYYI0-PnUjEzLHgnEifIpRem8h7RqL3V7sA07jhQ,33335
|
|
39
|
+
rubka-6.6.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
rubka-6.6.2.dist-info/top_level.txt,sha256=vy2A4lot11cRMdQS-F4HDCIXL3JK8RKfu7HMDkezJW4,6
|
|
41
|
+
rubka-6.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|