telegrinder 0.1.dev165__py3-none-any.whl → 0.1.dev167__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 telegrinder might be problematic. Click here for more details.
- telegrinder/__init__.py +20 -0
- telegrinder/api/abc.py +1 -1
- telegrinder/api/api.py +8 -6
- telegrinder/api/error.py +2 -3
- telegrinder/bot/__init__.py +12 -0
- telegrinder/bot/bot.py +2 -2
- telegrinder/bot/cute_types/__init__.py +4 -0
- telegrinder/bot/cute_types/base.py +10 -10
- telegrinder/bot/cute_types/callback_query.py +1 -3
- telegrinder/bot/cute_types/chat_join_request.py +65 -0
- telegrinder/bot/cute_types/chat_member_updated.py +246 -0
- telegrinder/bot/cute_types/message.py +44 -38
- telegrinder/bot/cute_types/update.py +5 -4
- telegrinder/bot/cute_types/utils.py +40 -20
- telegrinder/bot/dispatch/__init__.py +8 -1
- telegrinder/bot/dispatch/composition.py +7 -7
- telegrinder/bot/dispatch/context.py +9 -10
- telegrinder/bot/dispatch/dispatch.py +30 -22
- telegrinder/bot/dispatch/handler/abc.py +1 -0
- telegrinder/bot/dispatch/handler/func.py +21 -5
- telegrinder/bot/dispatch/handler/message_reply.py +2 -3
- telegrinder/bot/dispatch/middleware/abc.py +2 -4
- telegrinder/bot/dispatch/process.py +4 -3
- telegrinder/bot/dispatch/return_manager/__init__.py +1 -1
- telegrinder/bot/dispatch/return_manager/abc.py +28 -20
- telegrinder/bot/dispatch/return_manager/callback_query.py +4 -2
- telegrinder/bot/dispatch/return_manager/inline_query.py +4 -2
- telegrinder/bot/dispatch/return_manager/message.py +8 -4
- telegrinder/bot/dispatch/view/__init__.py +8 -2
- telegrinder/bot/dispatch/view/abc.py +27 -23
- telegrinder/bot/dispatch/view/box.py +74 -11
- telegrinder/bot/dispatch/view/callback_query.py +1 -3
- telegrinder/bot/dispatch/view/chat_join_request.py +17 -0
- telegrinder/bot/dispatch/view/chat_member.py +26 -0
- telegrinder/bot/dispatch/view/message.py +23 -1
- telegrinder/bot/dispatch/view/raw.py +112 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +41 -26
- telegrinder/bot/dispatch/waiter_machine/middleware.py +14 -7
- telegrinder/bot/dispatch/waiter_machine/short_state.py +10 -7
- telegrinder/bot/polling/polling.py +2 -4
- telegrinder/bot/rules/__init__.py +20 -12
- telegrinder/bot/rules/abc.py +0 -9
- telegrinder/bot/rules/adapter/event.py +29 -22
- telegrinder/bot/rules/callback_data.py +15 -18
- telegrinder/bot/rules/chat_join.py +47 -0
- telegrinder/bot/rules/enum_text.py +7 -2
- telegrinder/bot/rules/fuzzy.py +1 -2
- telegrinder/bot/rules/inline.py +3 -3
- telegrinder/bot/rules/is_from.py +39 -51
- telegrinder/bot/rules/markup.py +1 -2
- telegrinder/bot/rules/mention.py +1 -4
- telegrinder/bot/rules/message.py +17 -0
- telegrinder/bot/rules/message_entities.py +1 -1
- telegrinder/bot/rules/regex.py +1 -2
- telegrinder/bot/rules/rule_enum.py +1 -3
- telegrinder/bot/rules/start.py +7 -7
- telegrinder/bot/rules/text.py +2 -1
- telegrinder/bot/rules/update.py +16 -0
- telegrinder/bot/scenario/checkbox.py +5 -7
- telegrinder/client/aiohttp.py +5 -7
- telegrinder/model.py +37 -22
- telegrinder/modules.py +15 -33
- telegrinder/msgspec_utils.py +34 -35
- telegrinder/node/__init__.py +12 -12
- telegrinder/node/attachment.py +21 -7
- telegrinder/node/base.py +14 -13
- telegrinder/node/composer.py +5 -5
- telegrinder/node/container.py +1 -1
- telegrinder/node/message.py +3 -1
- telegrinder/node/rule.py +4 -4
- telegrinder/node/source.py +6 -2
- telegrinder/node/text.py +3 -1
- telegrinder/node/tools/generator.py +1 -1
- telegrinder/tools/__init__.py +3 -1
- telegrinder/tools/buttons.py +4 -6
- telegrinder/tools/error_handler/abc.py +1 -2
- telegrinder/tools/error_handler/error.py +3 -6
- telegrinder/tools/error_handler/error_handler.py +34 -24
- telegrinder/tools/formatting/html.py +9 -5
- telegrinder/tools/formatting/links.py +1 -3
- telegrinder/tools/formatting/spec_html_formats.py +1 -1
- telegrinder/tools/global_context/abc.py +3 -1
- telegrinder/tools/global_context/global_context.py +13 -31
- telegrinder/tools/global_context/telegrinder_ctx.py +1 -1
- telegrinder/tools/i18n/base.py +4 -3
- telegrinder/tools/i18n/middleware/base.py +1 -3
- telegrinder/tools/i18n/simple.py +1 -3
- telegrinder/tools/keyboard.py +1 -1
- telegrinder/tools/limited_dict.py +27 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +18 -14
- telegrinder/tools/magic.py +1 -1
- telegrinder/types/__init__.py +236 -0
- telegrinder/types/enums.py +34 -0
- telegrinder/types/methods.py +52 -47
- telegrinder/types/objects.py +533 -90
- telegrinder/verification_utils.py +4 -1
- {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev167.dist-info}/METADATA +1 -1
- telegrinder-0.1.dev167.dist-info/RECORD +137 -0
- telegrinder-0.1.dev165.dist-info/RECORD +0 -128
- {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev167.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev167.dist-info}/WHEEL +0 -0
|
@@ -14,6 +14,7 @@ from telegrinder.types import (
|
|
|
14
14
|
InlineKeyboardMarkup,
|
|
15
15
|
InputFile,
|
|
16
16
|
InputMedia,
|
|
17
|
+
InputPollOption,
|
|
17
18
|
LabeledPrice,
|
|
18
19
|
LinkPreviewOptions,
|
|
19
20
|
Message,
|
|
@@ -70,17 +71,13 @@ async def execute_method_answer(
|
|
|
70
71
|
link_preview_options = params.get("link_preview_options")
|
|
71
72
|
|
|
72
73
|
if reply_parameters is not None and isinstance(reply_parameters, dict):
|
|
73
|
-
reply_parameters.setdefault(
|
|
74
|
-
"message_id", params.get("message_id", message.message_id)
|
|
75
|
-
)
|
|
74
|
+
reply_parameters.setdefault("message_id", params.get("message_id", message.message_id))
|
|
76
75
|
reply_parameters.setdefault("chat_id", params.get("chat_id"))
|
|
77
76
|
params["reply_parameters"] = compose_reply_params(**reply_parameters)
|
|
78
77
|
|
|
79
78
|
if link_preview_options is not None and isinstance(link_preview_options, dict):
|
|
80
|
-
params["link_preview_options"] = compose_link_preview_options(
|
|
81
|
-
|
|
82
|
-
)
|
|
83
|
-
|
|
79
|
+
params["link_preview_options"] = compose_link_preview_options(**link_preview_options)
|
|
80
|
+
|
|
84
81
|
result = await getattr(message.ctx_api, method_name)(**params)
|
|
85
82
|
return result.map(
|
|
86
83
|
lambda x: (
|
|
@@ -123,8 +120,7 @@ async def execute_method_edit(
|
|
|
123
120
|
"message_thread_id": lambda x: (
|
|
124
121
|
x.is_topic_message.unwrap_or(False)
|
|
125
122
|
if isinstance(x, MessageCute)
|
|
126
|
-
else bool(x.message)
|
|
127
|
-
and getattr(x.message.unwrap().v, "is_topic_message", False)
|
|
123
|
+
else bool(x.message) and getattr(x.message.unwrap().v, "is_topic_message", False)
|
|
128
124
|
),
|
|
129
125
|
},
|
|
130
126
|
)
|
|
@@ -329,9 +325,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
329
325
|
params=get_params(locals()),
|
|
330
326
|
update=self,
|
|
331
327
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
332
|
-
validators={
|
|
333
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
334
|
-
},
|
|
328
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
335
329
|
)
|
|
336
330
|
return await self.ctx_api.delete_message(**params)
|
|
337
331
|
|
|
@@ -449,9 +443,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
449
443
|
("from_chat_id", "chat_id"),
|
|
450
444
|
"message_thread_id",
|
|
451
445
|
},
|
|
452
|
-
validators={
|
|
453
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
454
|
-
},
|
|
446
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
455
447
|
)
|
|
456
448
|
if isinstance(reply_parameters, dict):
|
|
457
449
|
reply_parameters.setdefault("message_id", params.get("message_id"))
|
|
@@ -466,11 +458,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
466
458
|
async def react(
|
|
467
459
|
self,
|
|
468
460
|
reaction: (
|
|
469
|
-
str
|
|
470
|
-
| ReactionEmoji
|
|
471
|
-
| ReactionType
|
|
472
|
-
| list[str | ReactionEmoji | ReactionType]
|
|
473
|
-
| None
|
|
461
|
+
str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType] | None
|
|
474
462
|
) = None,
|
|
475
463
|
chat_id: int | str | None = None,
|
|
476
464
|
message_thread_id: int | None = None,
|
|
@@ -506,9 +494,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
506
494
|
params=get_params(locals()),
|
|
507
495
|
update=self,
|
|
508
496
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
509
|
-
validators={
|
|
510
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
511
|
-
},
|
|
497
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
512
498
|
)
|
|
513
499
|
if reaction:
|
|
514
500
|
params["reaction"] = compose_reactions(
|
|
@@ -556,9 +542,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
556
542
|
"message_id",
|
|
557
543
|
"message_thread_id",
|
|
558
544
|
},
|
|
559
|
-
validators={
|
|
560
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
561
|
-
},
|
|
545
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
562
546
|
)
|
|
563
547
|
return (await self.ctx_api.forward_message(**params)).map(
|
|
564
548
|
lambda message: MessageCute.from_update(message, bound_api=self.api),
|
|
@@ -598,9 +582,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
598
582
|
params=get_params(locals()),
|
|
599
583
|
update=self,
|
|
600
584
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
601
|
-
validators={
|
|
602
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
603
|
-
},
|
|
585
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
604
586
|
)
|
|
605
587
|
return await self.ctx_api.pin_chat_message(**params)
|
|
606
588
|
|
|
@@ -631,9 +613,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
631
613
|
params=get_params(locals()),
|
|
632
614
|
update=self,
|
|
633
615
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
634
|
-
validators={
|
|
635
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
636
|
-
},
|
|
616
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
637
617
|
)
|
|
638
618
|
return await self.ctx_api.pin_chat_message(**params)
|
|
639
619
|
|
|
@@ -1213,10 +1193,12 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
1213
1193
|
async def answer_poll(
|
|
1214
1194
|
self,
|
|
1215
1195
|
question: str,
|
|
1216
|
-
options: list[
|
|
1196
|
+
options: list[InputPollOption],
|
|
1217
1197
|
chat_id: int | str | None = None,
|
|
1218
1198
|
business_connection_id: str | None = None,
|
|
1219
1199
|
message_thread_id: int | None = None,
|
|
1200
|
+
question_parse_mode: str | None = None,
|
|
1201
|
+
question_entities: list[MessageEntity] | None = None,
|
|
1220
1202
|
is_anonymous: bool | None = None,
|
|
1221
1203
|
type: typing.Literal["quiz", "regular"] | None = None,
|
|
1222
1204
|
allows_multiple_answers: bool | None = None,
|
|
@@ -1246,10 +1228,15 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
1246
1228
|
:param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for \
|
|
1247
1229
|
forum supergroups only.
|
|
1248
1230
|
|
|
1231
|
+
:param question_parse_mode: Mode for parsing entities in the question. See formatting options for more \
|
|
1232
|
+
details. Currently, only custom emoji entities are allowed.
|
|
1233
|
+
|
|
1234
|
+
:param question_entities: A JSON-serialized list of special entities that appear in the poll question. \
|
|
1235
|
+
It can be specified instead of question_parse_mode.
|
|
1236
|
+
|
|
1249
1237
|
:param question: Poll question, 1-300 characters.
|
|
1250
1238
|
|
|
1251
|
-
:param options: A JSON-serialized list of
|
|
1252
|
-
each.
|
|
1239
|
+
:param options: A JSON-serialized list of 2-10 answer options.
|
|
1253
1240
|
|
|
1254
1241
|
:param is_anonymous: True, if the poll needs to be anonymous, defaults to True.
|
|
1255
1242
|
|
|
@@ -2365,10 +2352,12 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2365
2352
|
async def reply_poll(
|
|
2366
2353
|
self,
|
|
2367
2354
|
question: str,
|
|
2368
|
-
options: list[
|
|
2355
|
+
options: list[InputPollOption],
|
|
2369
2356
|
chat_id: int | str | None = None,
|
|
2370
2357
|
business_connection_id: str | None = None,
|
|
2371
2358
|
message_thread_id: int | None = None,
|
|
2359
|
+
question_parse_mode: str | None = None,
|
|
2360
|
+
question_entities: list[MessageEntity] | None = None,
|
|
2372
2361
|
is_anonymous: bool | None = None,
|
|
2373
2362
|
type: typing.Literal["quiz", "regular"] | None = None,
|
|
2374
2363
|
allows_multiple_answers: bool | None = None,
|
|
@@ -2398,10 +2387,15 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2398
2387
|
:param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for \
|
|
2399
2388
|
forum supergroups only.
|
|
2400
2389
|
|
|
2390
|
+
:param question_parse_mode: Mode for parsing entities in the question. See formatting options for more \
|
|
2391
|
+
details. Currently, only custom emoji entities are allowed.
|
|
2392
|
+
|
|
2393
|
+
:param question_entities: A JSON-serialized list of special entities that appear in the poll question. \
|
|
2394
|
+
It can be specified instead of question_parse_mode.
|
|
2395
|
+
|
|
2401
2396
|
:param question: Poll question, 1-300 characters.
|
|
2402
2397
|
|
|
2403
|
-
:param options: A JSON-serialized list of
|
|
2404
|
-
each.
|
|
2398
|
+
:param options: A JSON-serialized list of 2-10 answer options.
|
|
2405
2399
|
|
|
2406
2400
|
:param is_anonymous: True, if the poll needs to be anonymous, defaults to True.
|
|
2407
2401
|
|
|
@@ -2910,6 +2904,8 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2910
2904
|
chat_id: int | str | None = None,
|
|
2911
2905
|
message_id: int | None = None,
|
|
2912
2906
|
message_thread_id: int | None = None,
|
|
2907
|
+
inline_message_id: str | None = None,
|
|
2908
|
+
live_period: int | None = None,
|
|
2913
2909
|
horizontal_accuracy: float | None = None,
|
|
2914
2910
|
heading: int | None = None,
|
|
2915
2911
|
proximity_alert_radius: int | None = None,
|
|
@@ -2932,6 +2928,16 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2932
2928
|
:param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for \
|
|
2933
2929
|
forum supergroups only.
|
|
2934
2930
|
|
|
2931
|
+
:param live_period: New period in seconds during which the location can be updated, starting \
|
|
2932
|
+
from the message send date. If 0x7FFFFFFF is specified, then the location \
|
|
2933
|
+
can be updated forever. Otherwise, the new value must not exceed the current \
|
|
2934
|
+
live_period by more than a day, and the live location expiration date must \
|
|
2935
|
+
remain within the next 90 days. If not specified, then live_period remains \
|
|
2936
|
+
unchanged.
|
|
2937
|
+
|
|
2938
|
+
:param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the \
|
|
2939
|
+
inline message.
|
|
2940
|
+
|
|
2935
2941
|
:param latitude: Latitude of new location.
|
|
2936
2942
|
|
|
2937
2943
|
:param longitude: Longitude of new location.
|
|
@@ -15,15 +15,16 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
15
15
|
api: ABCAPI
|
|
16
16
|
|
|
17
17
|
@property
|
|
18
|
-
def incoming_update(self) -> Model:
|
|
18
|
+
def incoming_update(self) -> Option[Model]:
|
|
19
19
|
return getattr(
|
|
20
20
|
self,
|
|
21
21
|
self.update_type.expect("Update object has no incoming update.").value,
|
|
22
22
|
)
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def get_event(self, event_model: type[ModelT]) -> Option[ModelT]:
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
match self.incoming_update:
|
|
26
|
+
case Some(event) if isinstance(event, event_model):
|
|
27
|
+
return Some(event)
|
|
27
28
|
return Nothing()
|
|
28
29
|
|
|
29
30
|
|
|
@@ -2,6 +2,7 @@ import typing
|
|
|
2
2
|
|
|
3
3
|
from telegrinder.model import get_params
|
|
4
4
|
from telegrinder.types import (
|
|
5
|
+
ChatPermissions,
|
|
5
6
|
InlineKeyboardMarkup,
|
|
6
7
|
InlineQueryResultArticle,
|
|
7
8
|
InlineQueryResultAudio,
|
|
@@ -68,9 +69,7 @@ INPUT_MEDIA_TYPES: typing.Final[dict[str, type[InputMedia]]] = {
|
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
def compose_reactions(
|
|
71
|
-
reactions: (
|
|
72
|
-
str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType]
|
|
73
|
-
),
|
|
72
|
+
reactions: (str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType]),
|
|
74
73
|
/,
|
|
75
74
|
) -> list[ReactionType]:
|
|
76
75
|
if not isinstance(reactions, list):
|
|
@@ -115,6 +114,26 @@ def compose_link_preview_options(
|
|
|
115
114
|
return LinkPreviewOptions(**get_params(locals()))
|
|
116
115
|
|
|
117
116
|
|
|
117
|
+
def compose_chat_permissions(
|
|
118
|
+
*,
|
|
119
|
+
can_send_messages: bool | None = None,
|
|
120
|
+
can_send_audios: bool | None = None,
|
|
121
|
+
can_send_documents: bool | None = None,
|
|
122
|
+
can_send_photos: bool | None = None,
|
|
123
|
+
can_send_videos: bool | None = None,
|
|
124
|
+
can_send_video_notes: bool | None = None,
|
|
125
|
+
can_send_voice_notes: bool | None = None,
|
|
126
|
+
can_send_polls: bool | None = None,
|
|
127
|
+
can_send_other_messages: bool | None = None,
|
|
128
|
+
can_add_web_page_previews: bool | None = None,
|
|
129
|
+
can_change_info: bool | None = None,
|
|
130
|
+
can_invite_users: bool | None = None,
|
|
131
|
+
can_pin_messages: bool | None = None,
|
|
132
|
+
can_manage_topics: bool | None = None,
|
|
133
|
+
) -> ChatPermissions:
|
|
134
|
+
return ChatPermissions(**get_params(locals()))
|
|
135
|
+
|
|
136
|
+
|
|
118
137
|
def input_media(
|
|
119
138
|
type: typing.Literal["animation", "audio", "document", "photo", "video"],
|
|
120
139
|
media: str | InputFile,
|
|
@@ -510,33 +529,34 @@ def inline_query_cached_photo(
|
|
|
510
529
|
|
|
511
530
|
|
|
512
531
|
__all__ = (
|
|
532
|
+
"compose_chat_permissions",
|
|
513
533
|
"compose_link_preview_options",
|
|
514
534
|
"compose_reactions",
|
|
515
535
|
"compose_reply_params",
|
|
516
536
|
"inline_query_article",
|
|
517
|
-
"inline_query_photo",
|
|
518
|
-
"inline_query_mpeg4_gif",
|
|
519
|
-
"inline_query_gif",
|
|
520
|
-
"inline_query_video",
|
|
521
537
|
"inline_query_audio",
|
|
522
|
-
"inline_query_voice",
|
|
523
|
-
"inline_query_document",
|
|
524
|
-
"inline_query_location",
|
|
525
|
-
"inline_query_venue",
|
|
526
|
-
"inline_query_contact",
|
|
527
|
-
"inline_query_game",
|
|
528
|
-
"inline_query_cached_sticker",
|
|
529
|
-
"inline_query_cached_document",
|
|
530
538
|
"inline_query_cached_audio",
|
|
531
|
-
"
|
|
539
|
+
"inline_query_cached_document",
|
|
532
540
|
"inline_query_cached_gif",
|
|
533
541
|
"inline_query_cached_mpeg4_gif",
|
|
534
|
-
"inline_query_cached_voice",
|
|
535
542
|
"inline_query_cached_photo",
|
|
543
|
+
"inline_query_cached_sticker",
|
|
544
|
+
"inline_query_cached_video",
|
|
545
|
+
"inline_query_cached_voice",
|
|
546
|
+
"inline_query_contact",
|
|
547
|
+
"inline_query_document",
|
|
548
|
+
"inline_query_game",
|
|
549
|
+
"inline_query_gif",
|
|
550
|
+
"inline_query_location",
|
|
551
|
+
"inline_query_mpeg4_gif",
|
|
552
|
+
"inline_query_photo",
|
|
553
|
+
"inline_query_venue",
|
|
554
|
+
"inline_query_video",
|
|
555
|
+
"inline_query_voice",
|
|
556
|
+
"input_contact_message_content",
|
|
557
|
+
"input_invoice_message_content",
|
|
558
|
+
"input_location_message_content",
|
|
536
559
|
"input_media",
|
|
537
560
|
"input_text_message_content",
|
|
538
|
-
"input_location_message_content",
|
|
539
561
|
"input_venue_message_content",
|
|
540
|
-
"input_contact_message_content",
|
|
541
|
-
"input_invoice_message_content",
|
|
542
562
|
)
|
|
@@ -20,11 +20,14 @@ from .view import (
|
|
|
20
20
|
BaseStateView,
|
|
21
21
|
BaseView,
|
|
22
22
|
CallbackQueryView,
|
|
23
|
+
ChatJoinRequestView,
|
|
24
|
+
ChatMemberView,
|
|
23
25
|
InlineQueryView,
|
|
24
26
|
MessageView,
|
|
27
|
+
RawEventView,
|
|
25
28
|
ViewBox,
|
|
26
29
|
)
|
|
27
|
-
from .waiter_machine import WaiterMachine
|
|
30
|
+
from .waiter_machine import ShortState, WaiterMachine
|
|
28
31
|
|
|
29
32
|
__all__ = (
|
|
30
33
|
"ABCDispatch",
|
|
@@ -39,6 +42,8 @@ __all__ = (
|
|
|
39
42
|
"BaseView",
|
|
40
43
|
"CallbackQueryReturnManager",
|
|
41
44
|
"CallbackQueryView",
|
|
45
|
+
"ChatJoinRequestView",
|
|
46
|
+
"ChatMemberView",
|
|
42
47
|
"CompositionDispatch",
|
|
43
48
|
"Context",
|
|
44
49
|
"Dispatch",
|
|
@@ -49,6 +54,8 @@ __all__ = (
|
|
|
49
54
|
"MessageReplyHandler",
|
|
50
55
|
"MessageReturnManager",
|
|
51
56
|
"MessageView",
|
|
57
|
+
"RawEventView",
|
|
58
|
+
"ShortState",
|
|
52
59
|
"TelegrinderCtx",
|
|
53
60
|
"ViewBox",
|
|
54
61
|
"WaiterMachine",
|
|
@@ -26,15 +26,14 @@ class Composition:
|
|
|
26
26
|
for name, parameter in inspect.signature(func).parameters.items()
|
|
27
27
|
}
|
|
28
28
|
self.is_blocking = is_blocking
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
def __repr__(self) -> str:
|
|
31
31
|
return "<{}: for function={!r} with nodes={}>".format(
|
|
32
|
-
("blocking " if self.is_blocking else "")
|
|
33
|
-
+ self.__class__.__name__,
|
|
32
|
+
("blocking " if self.is_blocking else "") + self.__class__.__name__,
|
|
34
33
|
self.func.__name__,
|
|
35
34
|
self.nodes,
|
|
36
35
|
)
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
async def compose_nodes(self, update: UpdateCute) -> NodeCollection | None:
|
|
39
38
|
nodes: dict[str, NodeSession] = {}
|
|
40
39
|
for name, node_t in self.nodes.items():
|
|
@@ -44,7 +43,7 @@ class Composition:
|
|
|
44
43
|
await NodeCollection(nodes).close_all()
|
|
45
44
|
return None
|
|
46
45
|
return NodeCollection(nodes)
|
|
47
|
-
|
|
46
|
+
|
|
48
47
|
async def __call__(self, **kwargs: typing.Any) -> typing.Any:
|
|
49
48
|
return await self.func(**magic_bundle(self.func, kwargs, start_idx=0, bundle_ctx=False)) # type: ignore
|
|
50
49
|
|
|
@@ -52,7 +51,7 @@ class Composition:
|
|
|
52
51
|
class CompositionDispatch(ABCDispatch):
|
|
53
52
|
def __init__(self) -> None:
|
|
54
53
|
self.compositions: list[Composition] = []
|
|
55
|
-
|
|
54
|
+
|
|
56
55
|
def __repr__(self) -> str:
|
|
57
56
|
return "<{}: with compositions={!r}>".format(
|
|
58
57
|
self.__class__.__name__,
|
|
@@ -71,7 +70,7 @@ class CompositionDispatch(ABCDispatch):
|
|
|
71
70
|
return True
|
|
72
71
|
is_found = True
|
|
73
72
|
return is_found
|
|
74
|
-
|
|
73
|
+
|
|
75
74
|
def load(self, external: typing.Self):
|
|
76
75
|
self.compositions.extend(external.compositions)
|
|
77
76
|
|
|
@@ -82,6 +81,7 @@ class CompositionDispatch(ABCDispatch):
|
|
|
82
81
|
composition.nodes["container"] = ContainerNode.link_nodes(list(container_nodes))
|
|
83
82
|
self.compositions.append(composition)
|
|
84
83
|
return func
|
|
84
|
+
|
|
85
85
|
return wrapper
|
|
86
86
|
|
|
87
87
|
|
|
@@ -33,45 +33,44 @@ class Context(dict[str, AnyValue]):
|
|
|
33
33
|
defaults[k] = cls_vars[k]
|
|
34
34
|
delattr(self.__class__, k)
|
|
35
35
|
dict.__init__(self, **defaults | kwargs)
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
@recursive_repr()
|
|
38
38
|
def __repr__(self) -> str:
|
|
39
39
|
return "{}({})".format(
|
|
40
|
-
self.__class__.__name__,
|
|
41
|
-
", ".join(f"{k}={v!r}" for k, v in self.items())
|
|
40
|
+
self.__class__.__name__, ", ".join(f"{k}={v!r}" for k, v in self.items())
|
|
42
41
|
)
|
|
43
42
|
|
|
44
43
|
def __setitem__(self, __key: Key, __value: AnyValue) -> None:
|
|
45
44
|
dict.__setitem__(self, self.key_to_str(__key), __value)
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
def __getitem__(self, __key: Key) -> AnyValue:
|
|
48
47
|
return dict.__getitem__(self, self.key_to_str(__key))
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
def __delitem__(self, __key: Key) -> None:
|
|
51
50
|
dict.__delitem__(self, self.key_to_str(__key))
|
|
52
51
|
|
|
53
52
|
def __setattr__(self, __name: str, __value: AnyValue) -> None:
|
|
54
53
|
self.__setitem__(__name, __value)
|
|
55
|
-
|
|
54
|
+
|
|
56
55
|
def __getattr__(self, __name: str) -> AnyValue:
|
|
57
56
|
return self.__getitem__(__name)
|
|
58
|
-
|
|
57
|
+
|
|
59
58
|
def __delattr__(self, __name: str) -> None:
|
|
60
59
|
self.__delitem__(__name)
|
|
61
60
|
|
|
62
61
|
@staticmethod
|
|
63
62
|
def key_to_str(key: Key) -> str:
|
|
64
63
|
return key if isinstance(key, str) else str(key.value)
|
|
65
|
-
|
|
64
|
+
|
|
66
65
|
def copy(self) -> typing.Self:
|
|
67
66
|
return self.__class__(**self)
|
|
68
67
|
|
|
69
68
|
def set(self, key: Key, value: AnyValue) -> None:
|
|
70
69
|
self[key] = value
|
|
71
|
-
|
|
70
|
+
|
|
72
71
|
def get(self, key: Key, default: T | None = None) -> T | AnyValue:
|
|
73
72
|
return dict.get(self, key, default)
|
|
74
|
-
|
|
73
|
+
|
|
75
74
|
def delete(self, key: Key) -> None:
|
|
76
75
|
del self[key]
|
|
77
76
|
|
|
@@ -16,12 +16,22 @@ from telegrinder.types import Update
|
|
|
16
16
|
from .abc import ABCDispatch
|
|
17
17
|
from .handler import ABCHandler, FuncHandler
|
|
18
18
|
from .handler.func import ErrorHandlerT
|
|
19
|
-
from .view.box import
|
|
19
|
+
from .view.box import (
|
|
20
|
+
CallbackQueryViewT,
|
|
21
|
+
ChatJoinRequestViewT,
|
|
22
|
+
ChatMemberViewT,
|
|
23
|
+
InlineQueryViewT,
|
|
24
|
+
MessageViewT,
|
|
25
|
+
RawEventViewT,
|
|
26
|
+
ViewBox,
|
|
27
|
+
)
|
|
20
28
|
|
|
21
29
|
T = typing.TypeVar("T")
|
|
22
30
|
R = typing.TypeVar("R")
|
|
23
31
|
P = typing.ParamSpec("P")
|
|
24
|
-
Handler = typing.Callable[
|
|
32
|
+
Handler = typing.Callable[
|
|
33
|
+
typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]
|
|
34
|
+
]
|
|
25
35
|
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
26
36
|
|
|
27
37
|
DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
|
|
@@ -30,7 +40,14 @@ DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
|
|
|
30
40
|
@dataclasses.dataclass(repr=False, kw_only=True)
|
|
31
41
|
class Dispatch(
|
|
32
42
|
ABCDispatch,
|
|
33
|
-
ViewBox[
|
|
43
|
+
ViewBox[
|
|
44
|
+
CallbackQueryViewT,
|
|
45
|
+
ChatJoinRequestViewT,
|
|
46
|
+
ChatMemberViewT,
|
|
47
|
+
InlineQueryViewT,
|
|
48
|
+
MessageViewT,
|
|
49
|
+
RawEventViewT,
|
|
50
|
+
],
|
|
34
51
|
):
|
|
35
52
|
global_context: TelegrinderCtx = dataclasses.field(
|
|
36
53
|
init=False,
|
|
@@ -42,29 +59,25 @@ class Dispatch(
|
|
|
42
59
|
)
|
|
43
60
|
|
|
44
61
|
def __repr__(self) -> str:
|
|
45
|
-
return "Dispatch(%s)" % ", ".join(
|
|
46
|
-
f"{k}={v!r}" for k, v in self.__dict__.items()
|
|
47
|
-
)
|
|
62
|
+
return "Dispatch(%s)" % ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
|
|
48
63
|
|
|
49
64
|
@property
|
|
50
65
|
def patcher(self) -> Patcher:
|
|
51
66
|
"""Alias `patcher` to get `vbml.Patcher` from the global context"""
|
|
52
67
|
return self.global_context.vbml_patcher
|
|
53
|
-
|
|
68
|
+
|
|
54
69
|
@typing.overload
|
|
55
70
|
def handle(
|
|
56
71
|
self,
|
|
57
72
|
*rules: ABCRule[Event],
|
|
58
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
|
|
59
|
-
...
|
|
73
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]: ...
|
|
60
74
|
|
|
61
75
|
@typing.overload
|
|
62
76
|
def handle(
|
|
63
77
|
self,
|
|
64
78
|
*rules: ABCRule[Event],
|
|
65
79
|
is_blocking: bool = True,
|
|
66
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
|
|
67
|
-
...
|
|
80
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]: ...
|
|
68
81
|
|
|
69
82
|
@typing.overload
|
|
70
83
|
def handle(
|
|
@@ -72,8 +85,7 @@ class Dispatch(
|
|
|
72
85
|
*rules: ABCRule[Event],
|
|
73
86
|
dataclass: type[T],
|
|
74
87
|
is_blocking: bool = True,
|
|
75
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
|
|
76
|
-
...
|
|
88
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]: ...
|
|
77
89
|
|
|
78
90
|
@typing.overload
|
|
79
91
|
def handle( # type: ignore
|
|
@@ -81,8 +93,7 @@ class Dispatch(
|
|
|
81
93
|
*rules: ABCRule[Event],
|
|
82
94
|
error_handler: ErrorHandlerT,
|
|
83
95
|
is_blocking: bool = True,
|
|
84
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandlerT]]:
|
|
85
|
-
...
|
|
96
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandlerT]]: ...
|
|
86
97
|
|
|
87
98
|
@typing.overload
|
|
88
99
|
def handle(
|
|
@@ -91,8 +102,7 @@ class Dispatch(
|
|
|
91
102
|
dataclass: type[T],
|
|
92
103
|
error_handler: ErrorHandlerT,
|
|
93
104
|
is_blocking: bool = True,
|
|
94
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandlerT]]:
|
|
95
|
-
...
|
|
105
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandlerT]]: ...
|
|
96
106
|
|
|
97
107
|
@typing.overload
|
|
98
108
|
def handle(
|
|
@@ -101,8 +111,7 @@ class Dispatch(
|
|
|
101
111
|
dataclass: type[T] = DEFAULT_DATACLASS,
|
|
102
112
|
error_handler: typing.Literal[None] = None,
|
|
103
113
|
is_blocking: bool = True,
|
|
104
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
|
|
105
|
-
...
|
|
114
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]: ...
|
|
106
115
|
|
|
107
116
|
def handle( # type: ignore
|
|
108
117
|
self,
|
|
@@ -126,6 +135,7 @@ class Dispatch(
|
|
|
126
135
|
|
|
127
136
|
async def feed(self, event: Update, api: ABCAPI) -> bool:
|
|
128
137
|
logger.debug("Processing update (update_id={})", event.update_id)
|
|
138
|
+
await self.raw_event.process(event, api)
|
|
129
139
|
for view in self.get_views().values():
|
|
130
140
|
if await view.check(event):
|
|
131
141
|
logger.debug(
|
|
@@ -150,9 +160,7 @@ class Dispatch(
|
|
|
150
160
|
def load(self, external: typing.Self) -> None:
|
|
151
161
|
view_external = external.get_views()
|
|
152
162
|
for name, view in self.get_views().items():
|
|
153
|
-
assert
|
|
154
|
-
name in view_external
|
|
155
|
-
), f"View {name!r} is undefined in external dispatch."
|
|
163
|
+
assert name in view_external, f"View {name!r} is undefined in external dispatch."
|
|
156
164
|
view.load(view_external[name])
|
|
157
165
|
setattr(external, name, view)
|
|
158
166
|
|
|
@@ -9,13 +9,17 @@ from telegrinder.bot.dispatch.process import check_rule
|
|
|
9
9
|
from telegrinder.modules import logger
|
|
10
10
|
from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
|
|
11
11
|
from telegrinder.types import Update
|
|
12
|
+
from telegrinder.types.enums import UpdateType
|
|
12
13
|
|
|
13
14
|
from .abc import ABCHandler
|
|
14
15
|
|
|
15
16
|
if typing.TYPE_CHECKING:
|
|
16
17
|
from telegrinder.bot.rules import ABCRule
|
|
17
18
|
|
|
18
|
-
F = typing.TypeVar(
|
|
19
|
+
F = typing.TypeVar(
|
|
20
|
+
"F",
|
|
21
|
+
bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]],
|
|
22
|
+
)
|
|
19
23
|
EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
20
24
|
ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
|
|
21
25
|
|
|
@@ -31,6 +35,7 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
31
35
|
default_factory=lambda: typing.cast(ErrorHandlerT, ErrorHandler()),
|
|
32
36
|
)
|
|
33
37
|
preset_context: Context = dataclasses.field(default_factory=lambda: Context())
|
|
38
|
+
update_type: UpdateType | None = dataclasses.field(default=None)
|
|
34
39
|
|
|
35
40
|
def __repr__(self) -> str:
|
|
36
41
|
return "<{}: {}={!r} with rules={!r}, dataclass={!r}, error_handler={!r}>".format(
|
|
@@ -41,8 +46,10 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
41
46
|
self.dataclass,
|
|
42
47
|
self.error_handler,
|
|
43
48
|
)
|
|
44
|
-
|
|
49
|
+
|
|
45
50
|
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
51
|
+
if self.update_type is not None and self.update_type != event.update_type.unwrap_or_none():
|
|
52
|
+
return False
|
|
46
53
|
ctx = ctx or Context()
|
|
47
54
|
temp_ctx = ctx.copy()
|
|
48
55
|
temp_ctx |= self.preset_context
|
|
@@ -51,14 +58,23 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
51
58
|
if not await check_rule(api, rule, event, temp_ctx):
|
|
52
59
|
logger.debug("Rule {!r} failed!", rule)
|
|
53
60
|
return False
|
|
54
|
-
|
|
61
|
+
|
|
55
62
|
ctx |= temp_ctx
|
|
56
63
|
return True
|
|
57
64
|
|
|
58
65
|
async def run(self, event: EventT, ctx: Context) -> typing.Any:
|
|
66
|
+
api = event.api
|
|
59
67
|
if self.dataclass is not None:
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
if self.update_type is not None:
|
|
69
|
+
update = event.to_dict()[self.update_type.value].unwrap()
|
|
70
|
+
event = (
|
|
71
|
+
self.dataclass.from_update(update, bound_api=api) # type: ignore
|
|
72
|
+
if issubclass(self.dataclass, BaseCute)
|
|
73
|
+
else self.dataclass(**update.to_dict())
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
event = self.dataclass(**event.to_dict())
|
|
77
|
+
return (await self.error_handler.run(self.func, event, api, ctx)).unwrap()
|
|
62
78
|
|
|
63
79
|
|
|
64
80
|
__all__ = ("FuncHandler",)
|
|
@@ -25,11 +25,10 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
|
|
|
25
25
|
self.as_reply = as_reply
|
|
26
26
|
self.is_blocking = is_blocking
|
|
27
27
|
self.preset_context = Context()
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
def __repr__(self) -> str:
|
|
30
30
|
return "<{}: with rules={!r}, {}: {!r}>".format(
|
|
31
|
-
("blocking " if self.is_blocking else "")
|
|
32
|
-
+ self.__class__.__name__,
|
|
31
|
+
("blocking " if self.is_blocking else "") + self.__class__.__name__,
|
|
33
32
|
self.rules,
|
|
34
33
|
"answer text as reply" if self.as_reply else "answer text",
|
|
35
34
|
self.text,
|