telegrinder 0.1.dev164__py3-none-any.whl → 0.1.dev166__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 +58 -0
- telegrinder/api/abc.py +1 -1
- telegrinder/api/api.py +8 -6
- telegrinder/api/error.py +2 -3
- telegrinder/bot/__init__.py +14 -0
- telegrinder/bot/bot.py +13 -2
- telegrinder/bot/cute_types/__init__.py +4 -0
- telegrinder/bot/cute_types/base.py +22 -13
- telegrinder/bot/cute_types/chat_join_request.py +63 -0
- telegrinder/bot/cute_types/chat_member_updated.py +244 -0
- telegrinder/bot/cute_types/message.py +33 -6
- telegrinder/bot/cute_types/update.py +5 -4
- telegrinder/bot/cute_types/utils.py +39 -17
- telegrinder/bot/dispatch/__init__.py +9 -1
- telegrinder/bot/dispatch/composition.py +10 -8
- telegrinder/bot/dispatch/context.py +9 -10
- telegrinder/bot/dispatch/dispatch.py +29 -19
- telegrinder/bot/dispatch/handler/abc.py +1 -0
- telegrinder/bot/dispatch/handler/func.py +29 -6
- 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 +39 -21
- 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 +12 -6
- telegrinder/bot/dispatch/view/__init__.py +8 -2
- telegrinder/bot/dispatch/view/abc.py +26 -20
- telegrinder/bot/dispatch/view/box.py +72 -1
- 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 +116 -0
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -1
- telegrinder/bot/dispatch/waiter_machine/machine.py +73 -19
- telegrinder/bot/dispatch/waiter_machine/middleware.py +3 -3
- telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -3
- telegrinder/bot/polling/polling.py +17 -1
- telegrinder/bot/rules/__init__.py +20 -12
- telegrinder/bot/rules/abc.py +0 -9
- telegrinder/bot/rules/adapter/event.py +31 -22
- telegrinder/bot/rules/callback_data.py +15 -20
- telegrinder/bot/rules/chat_join.py +47 -0
- telegrinder/bot/rules/enum_text.py +7 -2
- telegrinder/bot/rules/inline.py +3 -3
- telegrinder/bot/rules/is_from.py +36 -50
- telegrinder/bot/rules/markup.py +1 -1
- telegrinder/bot/rules/message.py +17 -0
- telegrinder/bot/rules/message_entities.py +1 -1
- telegrinder/bot/rules/start.py +6 -4
- telegrinder/bot/rules/text.py +2 -1
- telegrinder/bot/rules/update.py +16 -0
- telegrinder/bot/scenario/checkbox.py +9 -7
- telegrinder/client/aiohttp.py +4 -4
- telegrinder/model.py +43 -19
- telegrinder/modules.py +16 -32
- telegrinder/msgspec_utils.py +48 -36
- telegrinder/node/__init__.py +12 -12
- telegrinder/node/attachment.py +15 -5
- telegrinder/node/base.py +24 -16
- telegrinder/node/composer.py +8 -6
- telegrinder/node/container.py +1 -1
- telegrinder/node/rule.py +4 -4
- telegrinder/node/source.py +4 -2
- telegrinder/node/tools/generator.py +1 -1
- telegrinder/tools/__init__.py +2 -1
- telegrinder/tools/error_handler/abc.py +4 -3
- telegrinder/tools/error_handler/error_handler.py +22 -16
- telegrinder/tools/formatting/html.py +20 -18
- telegrinder/tools/formatting/links.py +12 -6
- telegrinder/tools/formatting/spec_html_formats.py +5 -6
- telegrinder/tools/global_context/abc.py +5 -1
- telegrinder/tools/global_context/global_context.py +2 -2
- telegrinder/tools/global_context/telegrinder_ctx.py +1 -1
- telegrinder/tools/i18n/base.py +4 -3
- telegrinder/tools/i18n/simple.py +1 -3
- telegrinder/tools/keyboard.py +1 -1
- telegrinder/tools/loop_wrapper/__init__.py +2 -2
- telegrinder/tools/loop_wrapper/abc.py +1 -4
- telegrinder/tools/loop_wrapper/loop_wrapper.py +90 -36
- telegrinder/tools/magic.py +1 -1
- telegrinder/types/__init__.py +206 -0
- telegrinder/types/enums.py +34 -0
- telegrinder/types/methods.py +52 -47
- telegrinder/types/objects.py +531 -88
- telegrinder/verification_utils.py +33 -0
- {telegrinder-0.1.dev164.dist-info → telegrinder-0.1.dev166.dist-info}/METADATA +1 -1
- telegrinder-0.1.dev166.dist-info/RECORD +136 -0
- telegrinder-0.1.dev164.dist-info/RECORD +0 -127
- {telegrinder-0.1.dev164.dist-info → telegrinder-0.1.dev166.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev164.dist-info → telegrinder-0.1.dev166.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,
|
|
@@ -1213,10 +1214,12 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
1213
1214
|
async def answer_poll(
|
|
1214
1215
|
self,
|
|
1215
1216
|
question: str,
|
|
1216
|
-
options: list[
|
|
1217
|
+
options: list[InputPollOption],
|
|
1217
1218
|
chat_id: int | str | None = None,
|
|
1218
1219
|
business_connection_id: str | None = None,
|
|
1219
1220
|
message_thread_id: int | None = None,
|
|
1221
|
+
question_parse_mode: str | None = None,
|
|
1222
|
+
question_entities: list[MessageEntity] | None = None,
|
|
1220
1223
|
is_anonymous: bool | None = None,
|
|
1221
1224
|
type: typing.Literal["quiz", "regular"] | None = None,
|
|
1222
1225
|
allows_multiple_answers: bool | None = None,
|
|
@@ -1246,10 +1249,15 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
1246
1249
|
:param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for \
|
|
1247
1250
|
forum supergroups only.
|
|
1248
1251
|
|
|
1252
|
+
:param question_parse_mode: Mode for parsing entities in the question. See formatting options for more \
|
|
1253
|
+
details. Currently, only custom emoji entities are allowed.
|
|
1254
|
+
|
|
1255
|
+
:param question_entities: A JSON-serialized list of special entities that appear in the poll question. \
|
|
1256
|
+
It can be specified instead of question_parse_mode.
|
|
1257
|
+
|
|
1249
1258
|
:param question: Poll question, 1-300 characters.
|
|
1250
1259
|
|
|
1251
|
-
:param options: A JSON-serialized list of
|
|
1252
|
-
each.
|
|
1260
|
+
:param options: A JSON-serialized list of 2-10 answer options.
|
|
1253
1261
|
|
|
1254
1262
|
:param is_anonymous: True, if the poll needs to be anonymous, defaults to True.
|
|
1255
1263
|
|
|
@@ -2365,10 +2373,12 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2365
2373
|
async def reply_poll(
|
|
2366
2374
|
self,
|
|
2367
2375
|
question: str,
|
|
2368
|
-
options: list[
|
|
2376
|
+
options: list[InputPollOption],
|
|
2369
2377
|
chat_id: int | str | None = None,
|
|
2370
2378
|
business_connection_id: str | None = None,
|
|
2371
2379
|
message_thread_id: int | None = None,
|
|
2380
|
+
question_parse_mode: str | None = None,
|
|
2381
|
+
question_entities: list[MessageEntity] | None = None,
|
|
2372
2382
|
is_anonymous: bool | None = None,
|
|
2373
2383
|
type: typing.Literal["quiz", "regular"] | None = None,
|
|
2374
2384
|
allows_multiple_answers: bool | None = None,
|
|
@@ -2398,10 +2408,15 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2398
2408
|
:param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for \
|
|
2399
2409
|
forum supergroups only.
|
|
2400
2410
|
|
|
2411
|
+
:param question_parse_mode: Mode for parsing entities in the question. See formatting options for more \
|
|
2412
|
+
details. Currently, only custom emoji entities are allowed.
|
|
2413
|
+
|
|
2414
|
+
:param question_entities: A JSON-serialized list of special entities that appear in the poll question. \
|
|
2415
|
+
It can be specified instead of question_parse_mode.
|
|
2416
|
+
|
|
2401
2417
|
:param question: Poll question, 1-300 characters.
|
|
2402
2418
|
|
|
2403
|
-
:param options: A JSON-serialized list of
|
|
2404
|
-
each.
|
|
2419
|
+
:param options: A JSON-serialized list of 2-10 answer options.
|
|
2405
2420
|
|
|
2406
2421
|
:param is_anonymous: True, if the poll needs to be anonymous, defaults to True.
|
|
2407
2422
|
|
|
@@ -2910,6 +2925,8 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2910
2925
|
chat_id: int | str | None = None,
|
|
2911
2926
|
message_id: int | None = None,
|
|
2912
2927
|
message_thread_id: int | None = None,
|
|
2928
|
+
inline_message_id: str | None = None,
|
|
2929
|
+
live_period: int | None = None,
|
|
2913
2930
|
horizontal_accuracy: float | None = None,
|
|
2914
2931
|
heading: int | None = None,
|
|
2915
2932
|
proximity_alert_radius: int | None = None,
|
|
@@ -2932,6 +2949,16 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
2932
2949
|
:param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for \
|
|
2933
2950
|
forum supergroups only.
|
|
2934
2951
|
|
|
2952
|
+
:param live_period: New period in seconds during which the location can be updated, starting \
|
|
2953
|
+
from the message send date. If 0x7FFFFFFF is specified, then the location \
|
|
2954
|
+
can be updated forever. Otherwise, the new value must not exceed the current \
|
|
2955
|
+
live_period by more than a day, and the live location expiration date must \
|
|
2956
|
+
remain within the next 90 days. If not specified, then live_period remains \
|
|
2957
|
+
unchanged.
|
|
2958
|
+
|
|
2959
|
+
:param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the \
|
|
2960
|
+
inline message.
|
|
2961
|
+
|
|
2935
2962
|
:param latitude: Latitude of new location.
|
|
2936
2963
|
|
|
2937
2964
|
: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,
|
|
@@ -115,6 +116,26 @@ def compose_link_preview_options(
|
|
|
115
116
|
return LinkPreviewOptions(**get_params(locals()))
|
|
116
117
|
|
|
117
118
|
|
|
119
|
+
def compose_chat_permissions(
|
|
120
|
+
*,
|
|
121
|
+
can_send_messages: bool | None = None,
|
|
122
|
+
can_send_audios: bool | None = None,
|
|
123
|
+
can_send_documents: bool | None = None,
|
|
124
|
+
can_send_photos: bool | None = None,
|
|
125
|
+
can_send_videos: bool | None = None,
|
|
126
|
+
can_send_video_notes: bool | None = None,
|
|
127
|
+
can_send_voice_notes: bool | None = None,
|
|
128
|
+
can_send_polls: bool | None = None,
|
|
129
|
+
can_send_other_messages: bool | None = None,
|
|
130
|
+
can_add_web_page_previews: bool | None = None,
|
|
131
|
+
can_change_info: bool | None = None,
|
|
132
|
+
can_invite_users: bool | None = None,
|
|
133
|
+
can_pin_messages: bool | None = None,
|
|
134
|
+
can_manage_topics: bool | None = None,
|
|
135
|
+
) -> ChatPermissions:
|
|
136
|
+
return ChatPermissions(**get_params(locals()))
|
|
137
|
+
|
|
138
|
+
|
|
118
139
|
def input_media(
|
|
119
140
|
type: typing.Literal["animation", "audio", "document", "photo", "video"],
|
|
120
141
|
media: str | InputFile,
|
|
@@ -510,33 +531,34 @@ def inline_query_cached_photo(
|
|
|
510
531
|
|
|
511
532
|
|
|
512
533
|
__all__ = (
|
|
534
|
+
"compose_chat_permissions",
|
|
513
535
|
"compose_link_preview_options",
|
|
514
536
|
"compose_reactions",
|
|
515
537
|
"compose_reply_params",
|
|
516
538
|
"inline_query_article",
|
|
517
|
-
"inline_query_photo",
|
|
518
|
-
"inline_query_mpeg4_gif",
|
|
519
|
-
"inline_query_gif",
|
|
520
|
-
"inline_query_video",
|
|
521
539
|
"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
540
|
"inline_query_cached_audio",
|
|
531
|
-
"
|
|
541
|
+
"inline_query_cached_document",
|
|
532
542
|
"inline_query_cached_gif",
|
|
533
543
|
"inline_query_cached_mpeg4_gif",
|
|
534
|
-
"inline_query_cached_voice",
|
|
535
544
|
"inline_query_cached_photo",
|
|
545
|
+
"inline_query_cached_sticker",
|
|
546
|
+
"inline_query_cached_video",
|
|
547
|
+
"inline_query_cached_voice",
|
|
548
|
+
"inline_query_contact",
|
|
549
|
+
"inline_query_document",
|
|
550
|
+
"inline_query_game",
|
|
551
|
+
"inline_query_gif",
|
|
552
|
+
"inline_query_location",
|
|
553
|
+
"inline_query_mpeg4_gif",
|
|
554
|
+
"inline_query_photo",
|
|
555
|
+
"inline_query_venue",
|
|
556
|
+
"inline_query_video",
|
|
557
|
+
"inline_query_voice",
|
|
558
|
+
"input_contact_message_content",
|
|
559
|
+
"input_invoice_message_content",
|
|
560
|
+
"input_location_message_content",
|
|
536
561
|
"input_media",
|
|
537
562
|
"input_text_message_content",
|
|
538
|
-
"input_location_message_content",
|
|
539
563
|
"input_venue_message_content",
|
|
540
|
-
"input_contact_message_content",
|
|
541
|
-
"input_invoice_message_content",
|
|
542
564
|
)
|
|
@@ -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, ShortStateStorage, 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,9 @@ __all__ = (
|
|
|
49
54
|
"MessageReplyHandler",
|
|
50
55
|
"MessageReturnManager",
|
|
51
56
|
"MessageView",
|
|
57
|
+
"RawEventView",
|
|
58
|
+
"ShortState",
|
|
59
|
+
"ShortStateStorage",
|
|
52
60
|
"TelegrinderCtx",
|
|
53
61
|
"ViewBox",
|
|
54
62
|
"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
|
|
|
@@ -79,9 +78,12 @@ class CompositionDispatch(ABCDispatch):
|
|
|
79
78
|
def wrapper(func: typing.Callable):
|
|
80
79
|
composition = Composition(func, is_blocking)
|
|
81
80
|
if container_nodes:
|
|
82
|
-
composition.nodes["container"] = ContainerNode.link_nodes(
|
|
81
|
+
composition.nodes["container"] = ContainerNode.link_nodes(
|
|
82
|
+
list(container_nodes)
|
|
83
|
+
)
|
|
83
84
|
self.compositions.append(composition)
|
|
84
85
|
return func
|
|
86
|
+
|
|
85
87
|
return wrapper
|
|
86
88
|
|
|
87
89
|
|
|
@@ -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(
|
|
@@ -9,15 +9,23 @@ 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[
|
|
22
|
+
typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]
|
|
23
|
+
],
|
|
24
|
+
)
|
|
19
25
|
EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
20
|
-
ErrorHandlerT = typing.TypeVar(
|
|
26
|
+
ErrorHandlerT = typing.TypeVar(
|
|
27
|
+
"ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler
|
|
28
|
+
)
|
|
21
29
|
|
|
22
30
|
|
|
23
31
|
@dataclasses.dataclass(repr=False)
|
|
@@ -31,6 +39,7 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
31
39
|
default_factory=lambda: typing.cast(ErrorHandlerT, ErrorHandler()),
|
|
32
40
|
)
|
|
33
41
|
preset_context: Context = dataclasses.field(default_factory=lambda: Context())
|
|
42
|
+
update_type: UpdateType | None = dataclasses.field(default=None)
|
|
34
43
|
|
|
35
44
|
def __repr__(self) -> str:
|
|
36
45
|
return "<{}: {}={!r} with rules={!r}, dataclass={!r}, error_handler={!r}>".format(
|
|
@@ -41,8 +50,13 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
41
50
|
self.dataclass,
|
|
42
51
|
self.error_handler,
|
|
43
52
|
)
|
|
44
|
-
|
|
53
|
+
|
|
45
54
|
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
55
|
+
if (
|
|
56
|
+
self.update_type is not None
|
|
57
|
+
and self.update_type != event.update_type.unwrap_or_none()
|
|
58
|
+
):
|
|
59
|
+
return False
|
|
46
60
|
ctx = ctx or Context()
|
|
47
61
|
temp_ctx = ctx.copy()
|
|
48
62
|
temp_ctx |= self.preset_context
|
|
@@ -51,14 +65,23 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
51
65
|
if not await check_rule(api, rule, event, temp_ctx):
|
|
52
66
|
logger.debug("Rule {!r} failed!", rule)
|
|
53
67
|
return False
|
|
54
|
-
|
|
68
|
+
|
|
55
69
|
ctx |= temp_ctx
|
|
56
70
|
return True
|
|
57
71
|
|
|
58
72
|
async def run(self, event: EventT, ctx: Context) -> typing.Any:
|
|
73
|
+
api = event.api
|
|
59
74
|
if self.dataclass is not None:
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
if self.update_type is not None:
|
|
76
|
+
update = event.to_dict()[self.update_type.value].unwrap()
|
|
77
|
+
event = (
|
|
78
|
+
self.dataclass.from_update(update, bound_api=api) # type: ignore
|
|
79
|
+
if issubclass(self.dataclass, BaseCute)
|
|
80
|
+
else self.dataclass(**update.to_dict())
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
event = self.dataclass(**event.to_dict())
|
|
84
|
+
return (await self.error_handler.run(self.func, event, api, ctx)).unwrap()
|
|
62
85
|
|
|
63
86
|
|
|
64
87
|
__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,
|
|
@@ -8,11 +8,9 @@ T = typing.TypeVar("T", bound=BaseCute)
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ABCMiddleware(ABC, typing.Generic[T]):
|
|
11
|
-
async def pre(self, event: T, ctx: Context) -> bool:
|
|
12
|
-
...
|
|
11
|
+
async def pre(self, event: T, ctx: Context) -> bool: ...
|
|
13
12
|
|
|
14
|
-
async def post(self, event: T, responses: list[typing.Any], ctx: Context) -> None:
|
|
15
|
-
...
|
|
13
|
+
async def post(self, event: T, responses: list[typing.Any], ctx: Context) -> None: ...
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
__all__ = ("ABCMiddleware",)
|
|
@@ -25,7 +25,7 @@ async def process_inner(
|
|
|
25
25
|
raw_event: Update,
|
|
26
26
|
middlewares: list[ABCMiddleware[T]],
|
|
27
27
|
handlers: list["ABCHandler[T]"],
|
|
28
|
-
return_manager: ABCReturnManager[T],
|
|
28
|
+
return_manager: ABCReturnManager[T] | None = None,
|
|
29
29
|
) -> bool:
|
|
30
30
|
logger.debug("Processing {!r}...", event.__class__.__name__)
|
|
31
31
|
ctx = Context(raw_update=raw_event)
|
|
@@ -43,7 +43,8 @@ async def process_inner(
|
|
|
43
43
|
found = True
|
|
44
44
|
response = await handler.run(event, ctx)
|
|
45
45
|
responses.append(response)
|
|
46
|
-
|
|
46
|
+
if return_manager is not None:
|
|
47
|
+
await return_manager.run(response, event, ctx)
|
|
47
48
|
if handler.is_blocking:
|
|
48
49
|
break
|
|
49
50
|
ctx = ctx_copy
|
|
@@ -56,7 +57,7 @@ async def process_inner(
|
|
|
56
57
|
|
|
57
58
|
async def check_rule(
|
|
58
59
|
api: ABCAPI,
|
|
59
|
-
rule: "ABCRule",
|
|
60
|
+
rule: "ABCRule[T]",
|
|
60
61
|
update: Update,
|
|
61
62
|
ctx: Context,
|
|
62
63
|
) -> bool:
|