RubigramClient 1.7.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rubigram/__init__.py +15 -0
- rubigram/client.py +294 -0
- rubigram/enums/__init__.py +52 -0
- rubigram/enums/buttons/__init__.py +13 -0
- rubigram/enums/buttons/button_calendar_type.py +20 -0
- rubigram/enums/buttons/button_location_type.py +20 -0
- rubigram/enums/buttons/button_selection_get_type.py +20 -0
- rubigram/enums/buttons/button_selection_search_type.py +20 -0
- rubigram/enums/buttons/button_selection_type.py +22 -0
- rubigram/enums/buttons/button_textbox_type_keypad.py +20 -0
- rubigram/enums/buttons/button_textbox_type_line.py +20 -0
- rubigram/enums/buttons/button_type.py +58 -0
- rubigram/enums/chat_action_type.py +22 -0
- rubigram/enums/chat_keypad_type.py +20 -0
- rubigram/enums/chat_type.py +24 -0
- rubigram/enums/enum.py +6 -0
- rubigram/enums/file_type.py +28 -0
- rubigram/enums/forwarded_from_type.py +22 -0
- rubigram/enums/live_location_status.py +20 -0
- rubigram/enums/message_sender_type.py +20 -0
- rubigram/enums/metadata_type.py +18 -0
- rubigram/enums/parse_mode.py +20 -0
- rubigram/enums/payment_status_type.py +20 -0
- rubigram/enums/poll_status_type.py +20 -0
- rubigram/enums/update_endpoint_type.py +26 -0
- rubigram/enums/update_type.py +28 -0
- rubigram/errors.py +16 -0
- rubigram/filters.py +865 -0
- rubigram/http_session.py +96 -0
- rubigram/methods/__init__.py +26 -0
- rubigram/methods/chats/__init__.py +10 -0
- rubigram/methods/chats/get_chat.py +53 -0
- rubigram/methods/decorators/__init__.py +25 -0
- rubigram/methods/decorators/on_inline_message.py +70 -0
- rubigram/methods/decorators/on_message.py +62 -0
- rubigram/methods/decorators/on_remove_message.py +65 -0
- rubigram/methods/decorators/on_start.py +65 -0
- rubigram/methods/decorators/on_started_bot.py +70 -0
- rubigram/methods/decorators/on_stop.py +65 -0
- rubigram/methods/decorators/on_stopped_bot.py +70 -0
- rubigram/methods/decorators/on_update_message.py +70 -0
- rubigram/methods/files/__init__.py +32 -0
- rubigram/methods/files/download_file.py +118 -0
- rubigram/methods/files/get_file.py +41 -0
- rubigram/methods/files/get_file_name.py +41 -0
- rubigram/methods/files/request_send_file.py +49 -0
- rubigram/methods/files/send_file.py +133 -0
- rubigram/methods/files/send_gif.py +51 -0
- rubigram/methods/files/send_music.py +97 -0
- rubigram/methods/files/send_photo.py +95 -0
- rubigram/methods/files/send_video.py +96 -0
- rubigram/methods/files/send_voice.py +96 -0
- rubigram/methods/files/upload_file.py +114 -0
- rubigram/methods/messages/__init__.py +34 -0
- rubigram/methods/messages/delete_messages.py +84 -0
- rubigram/methods/messages/edit_chat_keypad.py +68 -0
- rubigram/methods/messages/edit_message.py +82 -0
- rubigram/methods/messages/edit_message_keypad.py +72 -0
- rubigram/methods/messages/edit_message_text.py +68 -0
- rubigram/methods/messages/forward_message.py +78 -0
- rubigram/methods/messages/remove_chat_keypad.py +46 -0
- rubigram/methods/messages/send_contact.py +114 -0
- rubigram/methods/messages/send_location.py +108 -0
- rubigram/methods/messages/send_message.py +115 -0
- rubigram/methods/messages/send_poll.py +104 -0
- rubigram/methods/messages/send_sticker.py +98 -0
- rubigram/methods/network/__init__.py +12 -0
- rubigram/methods/network/request.py +129 -0
- rubigram/methods/settings/__init__.py +16 -0
- rubigram/methods/settings/set_command.py +50 -0
- rubigram/methods/settings/setup_endpoints.py +48 -0
- rubigram/methods/settings/update_bot_endpoint.py +62 -0
- rubigram/methods/updates/__init__.py +14 -0
- rubigram/methods/updates/get_me.py +35 -0
- rubigram/methods/updates/get_update.py +62 -0
- rubigram/methods/utilities/__init__.py +14 -0
- rubigram/methods/utilities/dispatcher.py +66 -0
- rubigram/methods/utilities/run.py +118 -0
- rubigram/rubino/__init__.py +6 -0
- rubigram/rubino/client.py +374 -0
- rubigram/rubino/network.py +129 -0
- rubigram/server/__init__.py +6 -0
- rubigram/server/server.py +245 -0
- rubigram/state/__init__.py +7 -0
- rubigram/state/state.py +121 -0
- rubigram/state/storage.py +131 -0
- rubigram/types/__init__.py +66 -0
- rubigram/types/aux_data.py +28 -0
- rubigram/types/bot.py +51 -0
- rubigram/types/bot_command.py +26 -0
- rubigram/types/buttons/__init__.py +13 -0
- rubigram/types/buttons/button.py +59 -0
- rubigram/types/buttons/button_calendar.py +40 -0
- rubigram/types/buttons/button_location.py +40 -0
- rubigram/types/buttons/button_number_picker.py +34 -0
- rubigram/types/buttons/button_selection.py +48 -0
- rubigram/types/buttons/button_selection_item.py +32 -0
- rubigram/types/buttons/button_string_picker.py +29 -0
- rubigram/types/buttons/button_text_box.py +40 -0
- rubigram/types/chat.py +86 -0
- rubigram/types/config/__init__.py +6 -0
- rubigram/types/config/object.py +442 -0
- rubigram/types/contact_message.py +29 -0
- rubigram/types/file.py +78 -0
- rubigram/types/forwarded_from.py +39 -0
- rubigram/types/keypads/__init__.py +7 -0
- rubigram/types/keypads/keypad.py +31 -0
- rubigram/types/keypads/keypad_row.py +23 -0
- rubigram/types/live_location.py +44 -0
- rubigram/types/location.py +24 -0
- rubigram/types/messages/__init__.py +8 -0
- rubigram/types/messages/inline_message.py +78 -0
- rubigram/types/messages/message.py +117 -0
- rubigram/types/messages/update_message.py +341 -0
- rubigram/types/metadata/__init__.py +7 -0
- rubigram/types/metadata/metadata.py +43 -0
- rubigram/types/metadata/metadata_parts.py +42 -0
- rubigram/types/payment_status.py +30 -0
- rubigram/types/poll.py +32 -0
- rubigram/types/poll_status.py +40 -0
- rubigram/types/sticker.py +33 -0
- rubigram/types/updates/__init__.py +7 -0
- rubigram/types/updates/update.py +917 -0
- rubigram/types/updates/updates.py +56 -0
- rubigram/utils/__init__.py +14 -0
- rubigram/utils/auto_delete.py +93 -0
- rubigram/utils/parser.py +99 -0
- rubigramclient-1.7.17.dist-info/METADATA +215 -0
- rubigramclient-1.7.17.dist-info/RECORD +132 -0
- rubigramclient-1.7.17.dist-info/WHEEL +5 -0
- rubigramclient-1.7.17.dist-info/licenses/LICENSE +21 -0
- rubigramclient-1.7.17.dist-info/top_level.txt +1 -0
rubigram/filters.py
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
# RubigramClient - Rubika API library for python
|
|
2
|
+
# Copyright (C) 2025-present Javad <https://github.com/DevJavad>
|
|
3
|
+
# Github - https://github.com/DevJavad/rubigram
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import re
|
|
8
|
+
from typing import Callable, Optional, Union
|
|
9
|
+
from rubigram.types import Update, InlineMessage
|
|
10
|
+
import rubigram
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
CUSTOM_FILTER_NAME = "CustomFilter"
|
|
14
|
+
URL_PATTERN = re.compile(
|
|
15
|
+
r"(?:(?:https?|ftp):\/\/)?(?:www\.)?[a-z0-9]+(?:[.\-][a-z0-9]+)*\.[a-z]{2,}(?:\/[^\s]*)?",
|
|
16
|
+
re.IGNORECASE
|
|
17
|
+
)
|
|
18
|
+
USERNAME_PATTERN = re.compile(r"@[A-Za-z0-9_]{3,32}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Filter:
|
|
22
|
+
"""
|
|
23
|
+
Asynchronous filter for processing updates in Rubigram.
|
|
24
|
+
|
|
25
|
+
This class provides a flexible way to create and combine filters for
|
|
26
|
+
message handling. Filters can be logically combined using AND (&),
|
|
27
|
+
OR (|), and NOT (~) operators.
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
func (callable):
|
|
31
|
+
An async function that takes (client, update) as arguments
|
|
32
|
+
and returns a boolean.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
.. code-block:: python
|
|
36
|
+
async def my_filter(client, update):
|
|
37
|
+
return update.text == "hello"
|
|
38
|
+
|
|
39
|
+
filter_obj = Filter(my_filter)
|
|
40
|
+
result = await filter_obj(client, update)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
__slots__ = ("func",)
|
|
44
|
+
|
|
45
|
+
def __init__(self, func):
|
|
46
|
+
self.func = func
|
|
47
|
+
|
|
48
|
+
async def __call__(self, client, update):
|
|
49
|
+
"""
|
|
50
|
+
Execute the filter.
|
|
51
|
+
|
|
52
|
+
Parameters:
|
|
53
|
+
client (rubigram.Client):
|
|
54
|
+
The client instance.
|
|
55
|
+
update (Update | InlineMessage):
|
|
56
|
+
The update to check.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
bool: True if the filter matches, False otherwise.
|
|
60
|
+
"""
|
|
61
|
+
return await self.func(client, update)
|
|
62
|
+
|
|
63
|
+
def __and__(self, other):
|
|
64
|
+
"""
|
|
65
|
+
Create a new filter that is the logical AND of two filters.
|
|
66
|
+
|
|
67
|
+
Parameters:
|
|
68
|
+
other (Filter):
|
|
69
|
+
Another filter to combine with.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Filter: A new filter that returns True only if both filters match.
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
.. code-block:: python
|
|
76
|
+
combined = text & private # Matches text messages in private chats
|
|
77
|
+
"""
|
|
78
|
+
async def func(client, update):
|
|
79
|
+
return await self(client, update) and await other(client, update)
|
|
80
|
+
return Filter(func)
|
|
81
|
+
|
|
82
|
+
def __or__(self, other):
|
|
83
|
+
"""
|
|
84
|
+
Create a new filter that is the logical OR of two filters.
|
|
85
|
+
|
|
86
|
+
Parameters:
|
|
87
|
+
other (Filter):
|
|
88
|
+
Another filter to combine with.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Filter: A new filter that returns True if either filter matches.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
.. code-block:: python
|
|
95
|
+
combined = text | file # Matches text OR file messages
|
|
96
|
+
"""
|
|
97
|
+
async def func(client, update):
|
|
98
|
+
return await self(client, update) or await other(client, update)
|
|
99
|
+
return Filter(func)
|
|
100
|
+
|
|
101
|
+
def __invert__(self):
|
|
102
|
+
"""
|
|
103
|
+
Create a new filter that is the logical NOT of this filter.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Filter: A new filter that returns True when the original returns False.
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
.. code-block:: python
|
|
110
|
+
not_text = ~text # Matches non-text messages
|
|
111
|
+
"""
|
|
112
|
+
async def func(client, update):
|
|
113
|
+
return not await self(client, update)
|
|
114
|
+
return Filter(func)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def create(func: Callable, name: Optional[str] = None, **kwargs) -> Filter:
|
|
118
|
+
"""
|
|
119
|
+
**Create a custom Rubigram filter.**
|
|
120
|
+
`filters.create(my_filter_func)`
|
|
121
|
+
|
|
122
|
+
Custom filters let you control which updates your handlers receive.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
func (`Callable`):
|
|
126
|
+
Async function that takes (filter, client, update) and returns bool.
|
|
127
|
+
|
|
128
|
+
name (`Optional[str]`):
|
|
129
|
+
Filter class name. Defaults to 'CustomFilter'.
|
|
130
|
+
|
|
131
|
+
**kwargs: Extra parameters accessible inside the filter.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Filter: A custom filter instance.
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
.. code-block:: python
|
|
138
|
+
|
|
139
|
+
from rubigram import filters
|
|
140
|
+
|
|
141
|
+
async def is_admin(client, update):
|
|
142
|
+
return update.chat_id in ADMIN_IDS
|
|
143
|
+
|
|
144
|
+
admin = filters.create(is_admin)
|
|
145
|
+
|
|
146
|
+
@client.on_message(admin)
|
|
147
|
+
async def handle_admin(client, update):
|
|
148
|
+
await update.reply(text="Admin command received!")
|
|
149
|
+
"""
|
|
150
|
+
return type(
|
|
151
|
+
name or func.__name__ or CUSTOM_FILTER_NAME,
|
|
152
|
+
(Filter,),
|
|
153
|
+
{"__call__": func, **kwargs}
|
|
154
|
+
)()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async def gif_filter(client, update: "Update") -> bool:
|
|
158
|
+
message = update.new_message
|
|
159
|
+
if not message:
|
|
160
|
+
return False
|
|
161
|
+
file = message.file
|
|
162
|
+
if not file:
|
|
163
|
+
return False
|
|
164
|
+
return bool(file.size and file.size < 1024 * 1024)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def caption_filter(client, update: "Update") -> bool:
|
|
168
|
+
message = update.new_message
|
|
169
|
+
if not message:
|
|
170
|
+
return False
|
|
171
|
+
file = message.file
|
|
172
|
+
return bool(file and update.new_message.text)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
async def reply_filter(client, update: "Update") -> bool:
|
|
176
|
+
message = update.new_message
|
|
177
|
+
return bool(message and getattr(message, "reply_to_message_id", None))
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
async def text_filter(client, update: "Update") -> bool:
|
|
181
|
+
"""
|
|
182
|
+
Filter for text messages.
|
|
183
|
+
|
|
184
|
+
Parameters:
|
|
185
|
+
client (rubigram.Client): The client instance.
|
|
186
|
+
update (Update): The update to check.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
bool: True if the message contains text, False otherwise.
|
|
190
|
+
"""
|
|
191
|
+
return bool(getattr(update, "text", None))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
async def file_filter(client, update: "Update") -> bool:
|
|
195
|
+
"""
|
|
196
|
+
Filter for file messages.
|
|
197
|
+
|
|
198
|
+
Parameters:
|
|
199
|
+
client (rubigram.Client): The client instance.
|
|
200
|
+
update (Update): The update to check.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
bool: True if the message contains a file, False otherwise.
|
|
204
|
+
"""
|
|
205
|
+
message = update.new_message
|
|
206
|
+
return bool(message and getattr(message, "file", None))
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
async def live_filter(client, update: "Update") -> bool:
|
|
210
|
+
"""
|
|
211
|
+
Filter for live location messages.
|
|
212
|
+
|
|
213
|
+
Parameters:
|
|
214
|
+
client (rubigram.Client): The client instance.
|
|
215
|
+
update (Update): The update to check.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
bool: True if the message contains live location, False otherwise.
|
|
219
|
+
"""
|
|
220
|
+
message = update.new_message
|
|
221
|
+
return bool(message and getattr(message, "live_location", None))
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
async def poll_filter(client, update: "Update") -> bool:
|
|
225
|
+
"""
|
|
226
|
+
Filter for poll messages.
|
|
227
|
+
|
|
228
|
+
Parameters:
|
|
229
|
+
client (rubigram.Client): The client instance.
|
|
230
|
+
update (Update): The update to check.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
bool: True if the message contains a poll, False otherwise.
|
|
234
|
+
"""
|
|
235
|
+
message = update.new_message
|
|
236
|
+
return bool(message and getattr(message, "poll", None))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async def contact_filter(client, update: "Update") -> bool:
|
|
240
|
+
"""
|
|
241
|
+
Filter for contact messages.
|
|
242
|
+
|
|
243
|
+
Parameters:
|
|
244
|
+
client (rubigram.Client): The client instance.
|
|
245
|
+
update (Update): The update to check.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
bool: True if the message contains contact information, False otherwise.
|
|
249
|
+
"""
|
|
250
|
+
message = update.new_message
|
|
251
|
+
return bool(message and getattr(message, "contact_message", None))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def sticker_filter(client, update: "Update") -> bool:
|
|
255
|
+
"""
|
|
256
|
+
Filter for sticker messages.
|
|
257
|
+
|
|
258
|
+
Parameters:
|
|
259
|
+
client (rubigram.Client): The client instance.
|
|
260
|
+
update (Update): The update to check.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
bool: True if the message contains a sticker, False otherwise.
|
|
264
|
+
"""
|
|
265
|
+
message = update.new_message
|
|
266
|
+
return bool(message and getattr(message, "sticker", None))
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
async def location_filter(client, update: "Update") -> bool:
|
|
270
|
+
"""
|
|
271
|
+
Filter for location messages.
|
|
272
|
+
|
|
273
|
+
Parameters:
|
|
274
|
+
client (rubigram.Client): The client instance.
|
|
275
|
+
update (Update): The update to check.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
bool: True if the message contains a location, False otherwise.
|
|
279
|
+
"""
|
|
280
|
+
message = update.new_message
|
|
281
|
+
return bool(message and getattr(message, "location", None))
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
async def forward_filter(client, update: "Update") -> bool:
|
|
285
|
+
"""
|
|
286
|
+
Filter for forwarded messages.
|
|
287
|
+
|
|
288
|
+
Parameters:
|
|
289
|
+
client (rubigram.Client): The client instance.
|
|
290
|
+
update (Update): The update to check.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
bool: True if the message is forwarded, False otherwise.
|
|
294
|
+
"""
|
|
295
|
+
message = update.new_message
|
|
296
|
+
return bool(message and getattr(message, "forwarded_from", None))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
async def edited_filter(client, update: "Update") -> bool:
|
|
300
|
+
"""
|
|
301
|
+
Filter for edited messages.
|
|
302
|
+
|
|
303
|
+
Parameters:
|
|
304
|
+
client (rubigram.Client): The client instance.
|
|
305
|
+
update (Update): The update to check.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
bool: True if the message is edited, False otherwise.
|
|
309
|
+
"""
|
|
310
|
+
return bool(update.updated_message)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
async def group_filter(client, update: Union["Update", "InlineMessage"]) -> bool:
|
|
314
|
+
"""
|
|
315
|
+
Filter for group chats.
|
|
316
|
+
|
|
317
|
+
Parameters:
|
|
318
|
+
client (rubigram.Client): The client instance.
|
|
319
|
+
update (Update | InlineMessage): The update to check.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
bool: True if the chat is a group, False otherwise.
|
|
323
|
+
|
|
324
|
+
Note:
|
|
325
|
+
Group chat IDs start with "g0".
|
|
326
|
+
"""
|
|
327
|
+
return update.chat_id.startswith("g0")
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
async def channel_filter(client, update: Union["Update", "InlineMessage"]) -> bool:
|
|
331
|
+
"""
|
|
332
|
+
Filter for channel chats.
|
|
333
|
+
|
|
334
|
+
Parameters:
|
|
335
|
+
client (rubigram.Client): The client instance.
|
|
336
|
+
update (Update | InlineMessage): The update to check.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
bool: True if the chat is a channel, False otherwise.
|
|
340
|
+
|
|
341
|
+
Note:
|
|
342
|
+
Channel chat IDs start with "c0".
|
|
343
|
+
"""
|
|
344
|
+
return update.chat_id.startswith("c0")
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
async def private_filter(client, update: Union["Update", "InlineMessage"]) -> bool:
|
|
348
|
+
"""
|
|
349
|
+
Filter for private chats.
|
|
350
|
+
|
|
351
|
+
Parameters:
|
|
352
|
+
client (rubigram.Client): The client instance.
|
|
353
|
+
update (Update | InlineMessage): The update to check.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
bool: True if the chat is private, False otherwise.
|
|
357
|
+
|
|
358
|
+
Note:
|
|
359
|
+
Private chat IDs start with "b0".
|
|
360
|
+
"""
|
|
361
|
+
return update.chat_id.startswith("b0")
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def file_type_filter(type: str):
|
|
365
|
+
"""
|
|
366
|
+
Create a filter for specific file types.
|
|
367
|
+
|
|
368
|
+
Parameters:
|
|
369
|
+
type (str):
|
|
370
|
+
The file type to filter for (e.g., "Gif", "Image", "Video").
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Filter: A filter that matches messages with the specified file type.
|
|
374
|
+
|
|
375
|
+
Example:
|
|
376
|
+
.. code-block:: python
|
|
377
|
+
gif_filter = file_type_filter("Gif")
|
|
378
|
+
# Equivalent to using the pre-defined `gif` filter
|
|
379
|
+
"""
|
|
380
|
+
async def wrapper(client, update: Update):
|
|
381
|
+
message = update.new_message
|
|
382
|
+
file = message.file or None
|
|
383
|
+
return bool(file and file.file_type == type)
|
|
384
|
+
return Filter(wrapper)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def forwarded_filter(type: str):
|
|
388
|
+
"""
|
|
389
|
+
Create a filter for messages forwarded from specific sources.
|
|
390
|
+
|
|
391
|
+
Parameters:
|
|
392
|
+
type (str):
|
|
393
|
+
The source type ("Bot", "User", "Channel").
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Filter: A filter that matches messages forwarded from the specified source.
|
|
397
|
+
|
|
398
|
+
Example:
|
|
399
|
+
.. code-block:: python
|
|
400
|
+
from_bot = forwarded_filter("Bot")
|
|
401
|
+
# Matches messages forwarded from bots
|
|
402
|
+
"""
|
|
403
|
+
async def wrapper(client, update: Update):
|
|
404
|
+
message = update.new_message
|
|
405
|
+
forwarded = message.forwarded_from or None
|
|
406
|
+
return bool(forwarded and forwarded.type_from == type)
|
|
407
|
+
return Filter(wrapper)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
async def url_filter(client, update: Update) -> bool:
|
|
411
|
+
"""
|
|
412
|
+
Filter for messages containing URLs.
|
|
413
|
+
|
|
414
|
+
Parameters:
|
|
415
|
+
client (rubigram.Client): The client instance.
|
|
416
|
+
update (Update): The update to check.
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
bool: True if the message text contains a URL, False otherwise.
|
|
420
|
+
"""
|
|
421
|
+
text = update.text or ""
|
|
422
|
+
if not text:
|
|
423
|
+
return False
|
|
424
|
+
return bool(URL_PATTERN.search(text))
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
async def hyperlink_filter(client, update: Update):
|
|
428
|
+
"""
|
|
429
|
+
Filter for messages with clickable hyperlinks.
|
|
430
|
+
|
|
431
|
+
Parameters:
|
|
432
|
+
client (rubigram.Client): The client instance.
|
|
433
|
+
update (Update): The update to check.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
bool: True if the message contains hyperlink metadata, False otherwise.
|
|
437
|
+
|
|
438
|
+
Note:
|
|
439
|
+
Checks for "Link" type in message metadata, not plain text URLs.
|
|
440
|
+
"""
|
|
441
|
+
message = update.new_message or update.updated_message
|
|
442
|
+
if not message:
|
|
443
|
+
return False
|
|
444
|
+
metadata = message.metadata
|
|
445
|
+
if not metadata:
|
|
446
|
+
return False
|
|
447
|
+
for i in metadata.meta_data_parts:
|
|
448
|
+
if i.type == "Link":
|
|
449
|
+
return True
|
|
450
|
+
return False
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
async def mention_filter(client, update: Update):
|
|
454
|
+
"""
|
|
455
|
+
Filter for messages containing mentions.
|
|
456
|
+
|
|
457
|
+
Parameters:
|
|
458
|
+
client (rubigram.Client): The client instance.
|
|
459
|
+
update (Update): The update to check.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
bool: True if the message contains user mentions, False otherwise.
|
|
463
|
+
"""
|
|
464
|
+
message = update.new_message or update.updated_message
|
|
465
|
+
if not message:
|
|
466
|
+
return False
|
|
467
|
+
metadata = message.metadata
|
|
468
|
+
if not metadata:
|
|
469
|
+
return False
|
|
470
|
+
for i in metadata.meta_data_parts:
|
|
471
|
+
if i.type == "MentionText":
|
|
472
|
+
return True
|
|
473
|
+
return False
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
async def text_bold_filter(client, update: "Update"):
|
|
477
|
+
message = update.new_message or update.updated_message
|
|
478
|
+
if not message:
|
|
479
|
+
return False
|
|
480
|
+
metadata = message.metadata
|
|
481
|
+
if not metadata:
|
|
482
|
+
return False
|
|
483
|
+
for i in metadata.meta_data_parts:
|
|
484
|
+
if i.type == "Bold":
|
|
485
|
+
return True
|
|
486
|
+
return False
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
async def text_mono_filter(client, update: "Update"):
|
|
490
|
+
message = update.new_message or update.updated_message
|
|
491
|
+
if not message:
|
|
492
|
+
return False
|
|
493
|
+
metadata = message.metadata
|
|
494
|
+
if not metadata:
|
|
495
|
+
return False
|
|
496
|
+
for i in metadata.meta_data_parts:
|
|
497
|
+
if i.type == "Mono":
|
|
498
|
+
return True
|
|
499
|
+
return False
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
async def text_quote_filter(client, update: "Update"):
|
|
503
|
+
message = update.new_message or update.updated_message
|
|
504
|
+
if not message:
|
|
505
|
+
return False
|
|
506
|
+
metadata = message.metadata
|
|
507
|
+
if not metadata:
|
|
508
|
+
return False
|
|
509
|
+
for i in metadata.meta_data_parts:
|
|
510
|
+
if i.type == "Quote":
|
|
511
|
+
return True
|
|
512
|
+
return False
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
async def text_italic_filter(client, update: "Update"):
|
|
516
|
+
message = update.new_message or update.updated_message
|
|
517
|
+
if not message:
|
|
518
|
+
return False
|
|
519
|
+
metadata = message.metadata
|
|
520
|
+
if not metadata:
|
|
521
|
+
return False
|
|
522
|
+
for i in metadata.meta_data_parts:
|
|
523
|
+
if i.type == "Italic":
|
|
524
|
+
return True
|
|
525
|
+
return False
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
async def text_strike_filter(client, update: "Update"):
|
|
529
|
+
message = update.new_message or update.updated_message
|
|
530
|
+
if not message:
|
|
531
|
+
return False
|
|
532
|
+
metadata = message.metadata
|
|
533
|
+
if not metadata:
|
|
534
|
+
return False
|
|
535
|
+
for i in metadata.meta_data_parts:
|
|
536
|
+
if i.type == "Strike":
|
|
537
|
+
return True
|
|
538
|
+
return False
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
async def text_spoiler_filter(client, update: "Update"):
|
|
542
|
+
message = update.new_message or update.updated_message
|
|
543
|
+
if not message:
|
|
544
|
+
return False
|
|
545
|
+
metadata = message.metadata
|
|
546
|
+
if not metadata:
|
|
547
|
+
return False
|
|
548
|
+
for i in metadata.meta_data_parts:
|
|
549
|
+
if i.type == "Spoiler":
|
|
550
|
+
return True
|
|
551
|
+
return False
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
async def text_underline_filter(client, update: "Update"):
|
|
555
|
+
message = update.new_message or update.updated_message
|
|
556
|
+
if not message:
|
|
557
|
+
return False
|
|
558
|
+
metadata = message.metadata
|
|
559
|
+
if not metadata:
|
|
560
|
+
return False
|
|
561
|
+
for i in metadata.meta_data_parts:
|
|
562
|
+
if i.type == "Underline":
|
|
563
|
+
return True
|
|
564
|
+
return False
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
async def metadata_filter(client, update: "Update"):
|
|
568
|
+
message = update.new_message or update.updated_message
|
|
569
|
+
return bool(message and message.metadata)
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
async def username_filter(client, update: "Update"):
|
|
573
|
+
return bool(USERNAME_PATTERN.search(update.text or ""))
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
# Pre-defined filter instances for common use cases
|
|
577
|
+
username = Filter(username_filter)
|
|
578
|
+
|
|
579
|
+
metadata = Filter(metadata_filter)
|
|
580
|
+
|
|
581
|
+
text_bold = Filter(text_bold_filter)
|
|
582
|
+
|
|
583
|
+
text_mono = Filter(text_mono_filter)
|
|
584
|
+
|
|
585
|
+
text_quote = Filter(text_quote_filter)
|
|
586
|
+
|
|
587
|
+
text_italic = Filter(text_italic_filter)
|
|
588
|
+
|
|
589
|
+
text_strike = Filter(text_strike_filter)
|
|
590
|
+
|
|
591
|
+
text_spoiler = Filter(text_spoiler_filter)
|
|
592
|
+
|
|
593
|
+
text_underline = Filter(text_underline_filter)
|
|
594
|
+
|
|
595
|
+
caption = Filter(caption_filter)
|
|
596
|
+
|
|
597
|
+
reply = Filter(reply_filter)
|
|
598
|
+
|
|
599
|
+
url = Filter(url_filter)
|
|
600
|
+
"""Filter for messages containing URLs in text."""
|
|
601
|
+
|
|
602
|
+
hyperlink = Filter(hyperlink_filter)
|
|
603
|
+
"""Filter for messages with clickable hyperlinks."""
|
|
604
|
+
|
|
605
|
+
mention = Filter(mention_filter)
|
|
606
|
+
"""Filter for messages containing user mentions."""
|
|
607
|
+
|
|
608
|
+
text = Filter(text_filter)
|
|
609
|
+
"""Filter for text messages."""
|
|
610
|
+
|
|
611
|
+
file = Filter(file_filter)
|
|
612
|
+
"""Filter for file messages."""
|
|
613
|
+
|
|
614
|
+
live = Filter(live_filter)
|
|
615
|
+
"""Filter for live location messages."""
|
|
616
|
+
|
|
617
|
+
poll = Filter(poll_filter)
|
|
618
|
+
"""Filter for poll messages."""
|
|
619
|
+
|
|
620
|
+
contact = Filter(contact_filter)
|
|
621
|
+
"""Filter for contact messages."""
|
|
622
|
+
|
|
623
|
+
sticker = Filter(sticker_filter)
|
|
624
|
+
"""Filter for sticker messages."""
|
|
625
|
+
|
|
626
|
+
location = Filter(location_filter)
|
|
627
|
+
"""Filter for location messages."""
|
|
628
|
+
|
|
629
|
+
forward = Filter(forward_filter)
|
|
630
|
+
"""Filter for forwarded messages."""
|
|
631
|
+
|
|
632
|
+
edited = Filter(edited_filter)
|
|
633
|
+
"""Filter for edited messages."""
|
|
634
|
+
|
|
635
|
+
group = Filter(group_filter)
|
|
636
|
+
"""Filter for group chats."""
|
|
637
|
+
|
|
638
|
+
channel = Filter(channel_filter)
|
|
639
|
+
"""Filter for channel chats."""
|
|
640
|
+
|
|
641
|
+
private = Filter(private_filter)
|
|
642
|
+
"""Filter for private chats."""
|
|
643
|
+
|
|
644
|
+
gif = Filter(gif_filter)
|
|
645
|
+
"""Filter for GIF file messages."""
|
|
646
|
+
|
|
647
|
+
photo = file_type_filter("Image")
|
|
648
|
+
"""Filter for image file messages."""
|
|
649
|
+
|
|
650
|
+
video = file_type_filter("Video")
|
|
651
|
+
"""Filter for video file messages."""
|
|
652
|
+
|
|
653
|
+
music = file_type_filter("Music")
|
|
654
|
+
"""Filter for music/audio file messages."""
|
|
655
|
+
|
|
656
|
+
voice = file_type_filter("Voice")
|
|
657
|
+
"""Filter for voice message files."""
|
|
658
|
+
|
|
659
|
+
document = file_type_filter("File")
|
|
660
|
+
"""Filter for document file messages."""
|
|
661
|
+
|
|
662
|
+
forwarded_bot = forwarded_filter("Bot")
|
|
663
|
+
"""Filter for messages forwarded from bots."""
|
|
664
|
+
|
|
665
|
+
forwarded_user = forwarded_filter("User")
|
|
666
|
+
"""Filter for messages forwarded from users."""
|
|
667
|
+
|
|
668
|
+
forwarded_channel = forwarded_filter("Channel")
|
|
669
|
+
"""Filter for messages forwarded from channels."""
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def command(
|
|
673
|
+
command: Union[str, list[str]],
|
|
674
|
+
prefix: Union[str, list[str]] = "/",
|
|
675
|
+
case_sensitive: bool = False,
|
|
676
|
+
start_with: bool = False
|
|
677
|
+
) -> bool:
|
|
678
|
+
"""
|
|
679
|
+
Create a filter for command messages.
|
|
680
|
+
|
|
681
|
+
Parameters:
|
|
682
|
+
command (str | list[str]):
|
|
683
|
+
The command(s) to match (without prefix).
|
|
684
|
+
prefix (str | list[str], optional):
|
|
685
|
+
The prefix(es) to use. Defaults to "/".
|
|
686
|
+
case_sensitive (bool, optional):
|
|
687
|
+
Whether the match should be case-sensitive. Defaults to False.
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
Filter: A filter that matches command messages.
|
|
691
|
+
|
|
692
|
+
Example:
|
|
693
|
+
.. code-block:: python
|
|
694
|
+
start_cmd = command("start")
|
|
695
|
+
# Matches "/start"
|
|
696
|
+
|
|
697
|
+
multi_cmd = command(["start", "help"], prefix=["/", "!"])
|
|
698
|
+
# Matches "/start", "/help", "!start", "!help"
|
|
699
|
+
"""
|
|
700
|
+
commands = command if isinstance(command, list) else [command]
|
|
701
|
+
prefixs = prefix if isinstance(prefix, list) else [prefix]
|
|
702
|
+
|
|
703
|
+
if not case_sensitive:
|
|
704
|
+
commands = [c.lower() for c in commands]
|
|
705
|
+
|
|
706
|
+
command_list = tuple(p + c for p in prefixs for c in commands)
|
|
707
|
+
|
|
708
|
+
async def wrapper(client, update: Update):
|
|
709
|
+
if update.text is None:
|
|
710
|
+
return False
|
|
711
|
+
|
|
712
|
+
text = update.text if case_sensitive else update.text.lower()
|
|
713
|
+
return any(text.startswith(c) if start_with else text == c for c in command_list)
|
|
714
|
+
|
|
715
|
+
return Filter(wrapper)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def chat(chat_id: Union[str, list[str]]) -> bool:
|
|
719
|
+
"""
|
|
720
|
+
Create a filter for specific chat IDs.
|
|
721
|
+
|
|
722
|
+
Parameters:
|
|
723
|
+
chat_id (str | list[str]):
|
|
724
|
+
The chat ID(s) to match.
|
|
725
|
+
|
|
726
|
+
Returns:
|
|
727
|
+
Filter: A filter that matches messages from specific chats.
|
|
728
|
+
|
|
729
|
+
Example:
|
|
730
|
+
.. code-block:: python
|
|
731
|
+
specific_chat = chat("b0123456789")
|
|
732
|
+
# Matches messages from chat ID "b0123456789"
|
|
733
|
+
|
|
734
|
+
multiple_chats = chat(["b0123456789", "g0987654321"])
|
|
735
|
+
# Matches messages from either chat
|
|
736
|
+
"""
|
|
737
|
+
async def wrapper(client, update: Union["Update", "InlineMessage"]):
|
|
738
|
+
chat_ids = chat_id if isinstance(chat_id, list) else [chat_id]
|
|
739
|
+
return update.chat_id in chat_ids
|
|
740
|
+
return Filter(wrapper)
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
def regex(
|
|
744
|
+
pattern: Union[str, list[str]],
|
|
745
|
+
flags: int = re.IGNORECASE
|
|
746
|
+
) -> bool:
|
|
747
|
+
"""
|
|
748
|
+
Create a filter for messages matching regular expressions.
|
|
749
|
+
|
|
750
|
+
Parameters:
|
|
751
|
+
pattern (str | list[str]):
|
|
752
|
+
The regex pattern(s) to match.
|
|
753
|
+
flags (int, optional):
|
|
754
|
+
Regex flags. Defaults to re.IGNORECASE.
|
|
755
|
+
|
|
756
|
+
Returns:
|
|
757
|
+
Filter: A filter that matches messages based on regex patterns.
|
|
758
|
+
|
|
759
|
+
Example:
|
|
760
|
+
.. code-block:: python
|
|
761
|
+
hello_filter = regex(r"^hello.*")
|
|
762
|
+
# Matches messages starting with "hello"
|
|
763
|
+
|
|
764
|
+
number_filter = regex([r"\d+", r"number"])
|
|
765
|
+
# Matches messages containing digits or the word "number"
|
|
766
|
+
"""
|
|
767
|
+
patterns = pattern if isinstance(pattern, list) else [pattern]
|
|
768
|
+
compiled_patterns = [re.compile(p, flags) for p in patterns]
|
|
769
|
+
|
|
770
|
+
async def wrapper(client, update: Union["Update", "InlineMessage"]):
|
|
771
|
+
text = getattr(update, "text", None)
|
|
772
|
+
if not text:
|
|
773
|
+
return False
|
|
774
|
+
return any(p.search(text) for p in compiled_patterns)
|
|
775
|
+
|
|
776
|
+
return Filter(wrapper)
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def button(
|
|
780
|
+
button_id: Union[str, list[str]],
|
|
781
|
+
prefix: Union[str, list[str]] = "",
|
|
782
|
+
case_sensitive: bool = False
|
|
783
|
+
) -> bool:
|
|
784
|
+
"""
|
|
785
|
+
Create a filter for inline button clicks.
|
|
786
|
+
|
|
787
|
+
Parameters:
|
|
788
|
+
button_id (str | list[str]):
|
|
789
|
+
The button ID(s) to match.
|
|
790
|
+
prefix (str | list[str], optional):
|
|
791
|
+
Prefix(es) for button IDs. Defaults to "".
|
|
792
|
+
case_sensitive (bool, optional):
|
|
793
|
+
Whether the match should be case-sensitive. Defaults to False.
|
|
794
|
+
|
|
795
|
+
Returns:
|
|
796
|
+
Filter: A filter that matches inline button clicks.
|
|
797
|
+
|
|
798
|
+
Note:
|
|
799
|
+
Only works with InlineMessage updates.
|
|
800
|
+
|
|
801
|
+
Example:
|
|
802
|
+
.. code-block:: python
|
|
803
|
+
vote_btn = button("vote_yes", prefix="poll_")
|
|
804
|
+
# Matches button with ID "poll_vote_yes"
|
|
805
|
+
"""
|
|
806
|
+
button_ids = button_id if isinstance(button_id, list) else [button_id]
|
|
807
|
+
prefixs = prefix if isinstance(prefix, list) else [prefix]
|
|
808
|
+
|
|
809
|
+
if not case_sensitive:
|
|
810
|
+
button_ids = [b.lower() for b in button_ids]
|
|
811
|
+
|
|
812
|
+
button_id_list = tuple(p + b for p in prefixs for b in button_ids)
|
|
813
|
+
|
|
814
|
+
async def wrapper(client, update: "InlineMessage"):
|
|
815
|
+
btn_id = update.aux_data.button_id if case_sensitive else update.aux_data.button_id.lower()
|
|
816
|
+
return any(btn_id.startswith(b) for b in button_id_list)
|
|
817
|
+
return Filter(wrapper)
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
def state(
|
|
821
|
+
state: Union[str, list[str]],
|
|
822
|
+
prefix: Union[str, list[str]] = "",
|
|
823
|
+
case_sensitive: bool = False
|
|
824
|
+
) -> bool:
|
|
825
|
+
"""
|
|
826
|
+
Create a filter based on user conversation state.
|
|
827
|
+
|
|
828
|
+
Parameters:
|
|
829
|
+
state (str | list[str]):
|
|
830
|
+
The state value(s) to match.
|
|
831
|
+
prefix (str | list[str], optional):
|
|
832
|
+
Prefix(es) for state values. Defaults to "".
|
|
833
|
+
case_sensitive (bool, optional):
|
|
834
|
+
Whether the match should be case-sensitive. Defaults to False.
|
|
835
|
+
|
|
836
|
+
Returns:
|
|
837
|
+
Filter: A filter that matches when the user is in a specific state.
|
|
838
|
+
|
|
839
|
+
Example:
|
|
840
|
+
.. code-block:: python
|
|
841
|
+
waiting_state = state("waiting_for_name")
|
|
842
|
+
# Matches when user's state is "waiting_for_name"
|
|
843
|
+
"""
|
|
844
|
+
states = state if isinstance(state, list) else [state]
|
|
845
|
+
prefixs = prefix if isinstance(prefix, list) else [prefix]
|
|
846
|
+
|
|
847
|
+
if not case_sensitive:
|
|
848
|
+
states = [s.lower() for s in states]
|
|
849
|
+
|
|
850
|
+
state_list = tuple(p + s for p in prefixs for s in states)
|
|
851
|
+
|
|
852
|
+
async def wrapper(client: "rubigram.Client", update: Union["Update", "InlineMessage"]):
|
|
853
|
+
stmt = client.state(update.chat_id)
|
|
854
|
+
data = await stmt.get()
|
|
855
|
+
return data.get("state") in state_list
|
|
856
|
+
return Filter(wrapper)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def sender_id(sender_id: Union[str, list[str]]) -> bool:
|
|
860
|
+
sender_ids = sender_id if isinstance(sender_id, list) else [sender_id]
|
|
861
|
+
|
|
862
|
+
async def wrapper(client, update: "Update"):
|
|
863
|
+
message = update.new_message or update.updated_message
|
|
864
|
+
return bool(message and message.sender_id in sender_ids)
|
|
865
|
+
return Filter(wrapper)
|