telegrinder 0.2.1__py3-none-any.whl → 0.3.0__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 +45 -12
- telegrinder/bot/__init__.py +32 -4
- telegrinder/bot/cute_types/callback_query.py +60 -146
- telegrinder/bot/cute_types/chat_join_request.py +12 -16
- telegrinder/bot/cute_types/chat_member_updated.py +15 -105
- telegrinder/bot/cute_types/inline_query.py +5 -14
- telegrinder/bot/cute_types/message.py +623 -1238
- telegrinder/bot/dispatch/__init__.py +40 -3
- telegrinder/bot/dispatch/abc.py +8 -1
- telegrinder/bot/dispatch/context.py +2 -2
- telegrinder/bot/dispatch/dispatch.py +8 -1
- telegrinder/bot/dispatch/handler/__init__.py +17 -1
- telegrinder/bot/dispatch/handler/audio_reply.py +44 -0
- telegrinder/bot/dispatch/handler/base.py +57 -0
- telegrinder/bot/dispatch/handler/document_reply.py +44 -0
- telegrinder/bot/dispatch/handler/func.py +3 -3
- telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
- telegrinder/bot/dispatch/handler/message_reply.py +12 -35
- telegrinder/bot/dispatch/handler/photo_reply.py +44 -0
- telegrinder/bot/dispatch/handler/sticker_reply.py +37 -0
- telegrinder/bot/dispatch/handler/video_reply.py +44 -0
- telegrinder/bot/dispatch/process.py +2 -2
- telegrinder/bot/dispatch/return_manager/abc.py +11 -8
- telegrinder/bot/dispatch/return_manager/callback_query.py +2 -2
- telegrinder/bot/dispatch/return_manager/inline_query.py +2 -2
- telegrinder/bot/dispatch/return_manager/message.py +3 -3
- telegrinder/bot/dispatch/view/__init__.py +2 -1
- telegrinder/bot/dispatch/view/abc.py +2 -181
- telegrinder/bot/dispatch/view/base.py +200 -0
- telegrinder/bot/dispatch/view/callback_query.py +3 -3
- telegrinder/bot/dispatch/view/chat_join_request.py +2 -2
- telegrinder/bot/dispatch/view/chat_member.py +2 -3
- telegrinder/bot/dispatch/view/inline_query.py +2 -2
- telegrinder/bot/dispatch/view/message.py +5 -4
- telegrinder/bot/dispatch/view/raw.py +4 -3
- telegrinder/bot/dispatch/waiter_machine/__init__.py +18 -0
- telegrinder/bot/dispatch/waiter_machine/actions.py +10 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +15 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +60 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +49 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +54 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +88 -101
- telegrinder/bot/dispatch/waiter_machine/middleware.py +23 -41
- telegrinder/bot/dispatch/waiter_machine/short_state.py +9 -9
- telegrinder/bot/polling/polling.py +5 -2
- telegrinder/bot/rules/__init__.py +3 -3
- telegrinder/bot/rules/abc.py +6 -5
- telegrinder/bot/rules/adapter/__init__.py +1 -1
- telegrinder/bot/rules/integer.py +1 -1
- telegrinder/bot/rules/is_from.py +19 -0
- telegrinder/bot/rules/state.py +9 -6
- telegrinder/bot/scenario/checkbox.py +5 -5
- telegrinder/bot/scenario/choice.py +2 -2
- telegrinder/client/aiohttp.py +5 -7
- telegrinder/model.py +6 -11
- telegrinder/modules.py +16 -25
- telegrinder/msgspec_json.py +1 -1
- telegrinder/msgspec_utils.py +56 -5
- telegrinder/node/base.py +2 -2
- telegrinder/node/composer.py +5 -9
- telegrinder/node/container.py +6 -1
- telegrinder/node/event.py +2 -0
- telegrinder/node/polymorphic.py +7 -7
- telegrinder/node/rule.py +6 -4
- telegrinder/node/scope.py +3 -3
- telegrinder/node/source.py +4 -2
- telegrinder/node/tools/generator.py +7 -6
- telegrinder/rules.py +2 -2
- telegrinder/tools/__init__.py +10 -10
- telegrinder/tools/functional.py +9 -0
- telegrinder/tools/keyboard.py +6 -1
- telegrinder/tools/loop_wrapper/loop_wrapper.py +4 -5
- telegrinder/tools/magic.py +17 -19
- telegrinder/tools/state_storage/__init__.py +3 -3
- telegrinder/tools/state_storage/abc.py +12 -10
- telegrinder/tools/state_storage/memory.py +9 -6
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/methods.py +10 -2
- telegrinder/types/objects.py +47 -5
- {telegrinder-0.2.1.dist-info → telegrinder-0.3.0.dist-info}/METADATA +4 -5
- telegrinder-0.3.0.dist-info/RECORD +164 -0
- telegrinder-0.2.1.dist-info/RECORD +0 -149
- {telegrinder-0.2.1.dist-info → telegrinder-0.3.0.dist-info}/LICENSE +0 -0
- {telegrinder-0.2.1.dist-info → telegrinder-0.3.0.dist-info}/WHEEL +0 -0
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
from telegrinder.bot.dispatch.abc import ABCDispatch
|
|
2
2
|
from telegrinder.bot.dispatch.context import Context
|
|
3
3
|
from telegrinder.bot.dispatch.dispatch import Dispatch, TelegrinderContext
|
|
4
|
-
from telegrinder.bot.dispatch.handler import
|
|
4
|
+
from telegrinder.bot.dispatch.handler import (
|
|
5
|
+
ABCHandler,
|
|
6
|
+
AudioReplyHandler,
|
|
7
|
+
DocumentReplyHandler,
|
|
8
|
+
FuncHandler,
|
|
9
|
+
MediaGroupReplyHandler,
|
|
10
|
+
MessageReplyHandler,
|
|
11
|
+
PhotoReplyHandler,
|
|
12
|
+
StickerReplyHandler,
|
|
13
|
+
VideoReplyHandler,
|
|
14
|
+
)
|
|
5
15
|
from telegrinder.bot.dispatch.middleware import ABCMiddleware
|
|
6
16
|
from telegrinder.bot.dispatch.process import check_rule, process_inner
|
|
7
17
|
from telegrinder.bot.dispatch.return_manager import (
|
|
@@ -26,7 +36,19 @@ from telegrinder.bot.dispatch.view import (
|
|
|
26
36
|
RawEventView,
|
|
27
37
|
ViewBox,
|
|
28
38
|
)
|
|
29
|
-
from telegrinder.bot.dispatch.waiter_machine import
|
|
39
|
+
from telegrinder.bot.dispatch.waiter_machine import (
|
|
40
|
+
CALLBACK_QUERY_FOR_MESSAGE,
|
|
41
|
+
CALLBACK_QUERY_FROM_CHAT,
|
|
42
|
+
CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE,
|
|
43
|
+
MESSAGE_FROM_USER,
|
|
44
|
+
MESSAGE_FROM_USER_IN_CHAT,
|
|
45
|
+
MESSAGE_IN_CHAT,
|
|
46
|
+
Hasher,
|
|
47
|
+
ShortState,
|
|
48
|
+
StateViewHasher,
|
|
49
|
+
WaiterMachine,
|
|
50
|
+
clear_wm_storage_worker,
|
|
51
|
+
)
|
|
30
52
|
|
|
31
53
|
__all__ = (
|
|
32
54
|
"ABCDispatch",
|
|
@@ -35,29 +57,44 @@ __all__ = (
|
|
|
35
57
|
"ABCReturnManager",
|
|
36
58
|
"ABCStateView",
|
|
37
59
|
"ABCView",
|
|
60
|
+
"AudioReplyHandler",
|
|
38
61
|
"BaseReturnManager",
|
|
39
62
|
"BaseStateView",
|
|
40
63
|
"BaseView",
|
|
64
|
+
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
65
|
+
"CALLBACK_QUERY_FROM_CHAT",
|
|
66
|
+
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
41
67
|
"CallbackQueryReturnManager",
|
|
42
68
|
"CallbackQueryView",
|
|
43
69
|
"ChatJoinRequestView",
|
|
44
70
|
"ChatMemberView",
|
|
45
71
|
"Context",
|
|
46
72
|
"Dispatch",
|
|
73
|
+
"DocumentReplyHandler",
|
|
47
74
|
"FuncHandler",
|
|
75
|
+
"Hasher",
|
|
48
76
|
"InlineQueryReturnManager",
|
|
49
77
|
"InlineQueryView",
|
|
78
|
+
"MESSAGE_FROM_USER",
|
|
79
|
+
"MESSAGE_FROM_USER_IN_CHAT",
|
|
80
|
+
"MESSAGE_IN_CHAT",
|
|
50
81
|
"Manager",
|
|
82
|
+
"MediaGroupReplyHandler",
|
|
51
83
|
"MessageReplyHandler",
|
|
52
84
|
"MessageReturnManager",
|
|
53
85
|
"MessageView",
|
|
86
|
+
"PhotoReplyHandler",
|
|
54
87
|
"RawEventView",
|
|
55
88
|
"ShortState",
|
|
89
|
+
"StateViewHasher",
|
|
90
|
+
"StickerReplyHandler",
|
|
56
91
|
"TelegrinderContext",
|
|
92
|
+
"VideoReplyHandler",
|
|
57
93
|
"ViewBox",
|
|
58
94
|
"WaiterMachine",
|
|
59
95
|
"check_rule",
|
|
96
|
+
"clear_wm_storage_worker",
|
|
97
|
+
"clear_wm_storage_worker",
|
|
60
98
|
"process_inner",
|
|
61
99
|
"register_manager",
|
|
62
|
-
"clear_wm_storage_worker",
|
|
63
100
|
)
|
telegrinder/bot/dispatch/abc.py
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from fntypes import Option
|
|
5
|
+
|
|
6
|
+
from telegrinder.api.api import API
|
|
5
7
|
from telegrinder.tools.global_context.abc import ABCGlobalContext
|
|
6
8
|
from telegrinder.types.objects import Update
|
|
7
9
|
|
|
10
|
+
T = typing.TypeVar("T")
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
class ABCDispatch(ABC):
|
|
10
14
|
@property
|
|
@@ -24,5 +28,8 @@ class ABCDispatch(ABC):
|
|
|
24
28
|
for external in externals:
|
|
25
29
|
self.load(external)
|
|
26
30
|
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_view(self, of_type: type[T]) -> Option[T]: ...
|
|
33
|
+
|
|
27
34
|
|
|
28
35
|
__all__ = ("ABCDispatch",)
|
|
@@ -2,7 +2,7 @@ import enum
|
|
|
2
2
|
import typing
|
|
3
3
|
from reprlib import recursive_repr
|
|
4
4
|
|
|
5
|
-
from telegrinder.types import Update
|
|
5
|
+
from telegrinder.types.objects import Update
|
|
6
6
|
|
|
7
7
|
T = typing.TypeVar("T")
|
|
8
8
|
|
|
@@ -63,7 +63,7 @@ class Context(dict[str, AnyValue]):
|
|
|
63
63
|
return key if isinstance(key, str) else str(key.value)
|
|
64
64
|
|
|
65
65
|
def copy(self) -> typing.Self:
|
|
66
|
-
return self.__class__(**self)
|
|
66
|
+
return self.__class__(**dict.copy(self))
|
|
67
67
|
|
|
68
68
|
def set(self, key: Key, value: AnyValue) -> None:
|
|
69
69
|
self[key] = value
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
+
from fntypes import Nothing, Option, Some
|
|
4
5
|
from vbml.patcher import Patcher
|
|
5
6
|
|
|
6
|
-
from telegrinder.api import API
|
|
7
|
+
from telegrinder.api.api import API
|
|
7
8
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
8
9
|
from telegrinder.bot.cute_types.update import UpdateCute
|
|
9
10
|
from telegrinder.bot.dispatch.abc import ABCDispatch
|
|
@@ -188,6 +189,12 @@ class Dispatch(
|
|
|
188
189
|
view.load(view_external[name])
|
|
189
190
|
setattr(external, name, view)
|
|
190
191
|
|
|
192
|
+
def get_view(self, of_type: type[T]) -> Option[T]:
|
|
193
|
+
for view in self.get_views().values():
|
|
194
|
+
if isinstance(view, of_type):
|
|
195
|
+
return Some(view)
|
|
196
|
+
return Nothing()
|
|
197
|
+
|
|
191
198
|
__call__ = handle
|
|
192
199
|
|
|
193
200
|
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
2
|
+
from telegrinder.bot.dispatch.handler.audio_reply import AudioReplyHandler
|
|
3
|
+
from telegrinder.bot.dispatch.handler.document_reply import DocumentReplyHandler
|
|
2
4
|
from telegrinder.bot.dispatch.handler.func import FuncHandler
|
|
5
|
+
from telegrinder.bot.dispatch.handler.media_group_reply import MediaGroupReplyHandler
|
|
3
6
|
from telegrinder.bot.dispatch.handler.message_reply import MessageReplyHandler
|
|
7
|
+
from telegrinder.bot.dispatch.handler.photo_reply import PhotoReplyHandler
|
|
8
|
+
from telegrinder.bot.dispatch.handler.sticker_reply import StickerReplyHandler
|
|
9
|
+
from telegrinder.bot.dispatch.handler.video_reply import VideoReplyHandler
|
|
4
10
|
|
|
5
|
-
__all__ = (
|
|
11
|
+
__all__ = (
|
|
12
|
+
"ABCHandler",
|
|
13
|
+
"AudioReplyHandler",
|
|
14
|
+
"DocumentReplyHandler",
|
|
15
|
+
"FuncHandler",
|
|
16
|
+
"MediaGroupReplyHandler",
|
|
17
|
+
"MessageReplyHandler",
|
|
18
|
+
"PhotoReplyHandler",
|
|
19
|
+
"StickerReplyHandler",
|
|
20
|
+
"VideoReplyHandler",
|
|
21
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.types.objects import InputFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AudioReplyHandler(BaseReplyHandler):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
audio: InputFile | str,
|
|
15
|
+
*rules: ABCRule,
|
|
16
|
+
caption: str | None = None,
|
|
17
|
+
parse_mode: str | None = None,
|
|
18
|
+
is_blocking: bool = True,
|
|
19
|
+
as_reply: bool = False,
|
|
20
|
+
preset_context: Context | None = None,
|
|
21
|
+
**default_params: typing.Any,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.audio = audio
|
|
24
|
+
self.parse_mode = parse_mode
|
|
25
|
+
self.caption = caption
|
|
26
|
+
super().__init__(
|
|
27
|
+
*rules,
|
|
28
|
+
is_blocking=is_blocking,
|
|
29
|
+
as_reply=as_reply,
|
|
30
|
+
preset_context=preset_context,
|
|
31
|
+
**default_params,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
35
|
+
method = event.answer_audio if not self.as_reply else event.reply_audio
|
|
36
|
+
await method(
|
|
37
|
+
audio=self.audio,
|
|
38
|
+
parse_mode=self.parse_mode,
|
|
39
|
+
caption=self.caption,
|
|
40
|
+
**self.default_params,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ("AudioReplyHandler",)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from fntypes.result import Result
|
|
5
|
+
|
|
6
|
+
from telegrinder.api.api import API
|
|
7
|
+
from telegrinder.api.error import APIError
|
|
8
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
9
|
+
from telegrinder.bot.dispatch.context import Context
|
|
10
|
+
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
11
|
+
from telegrinder.bot.dispatch.process import check_rule
|
|
12
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
13
|
+
from telegrinder.modules import logger
|
|
14
|
+
from telegrinder.types.objects import Update
|
|
15
|
+
|
|
16
|
+
APIMethod: typing.TypeAlias = typing.Callable[
|
|
17
|
+
typing.Concatenate[MessageCute, ...], typing.Awaitable[Result[typing.Any, APIError]]
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseReplyHandler(ABCHandler[MessageCute], abc.ABC):
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
*rules: ABCRule,
|
|
25
|
+
is_blocking: bool = True,
|
|
26
|
+
as_reply: bool = False,
|
|
27
|
+
preset_context: Context | None = None,
|
|
28
|
+
**default_params: typing.Any,
|
|
29
|
+
) -> None:
|
|
30
|
+
self.rules = list(rules)
|
|
31
|
+
self.as_reply = as_reply
|
|
32
|
+
self.is_blocking = is_blocking
|
|
33
|
+
self.default_params = default_params
|
|
34
|
+
self.preset_context = preset_context or Context()
|
|
35
|
+
|
|
36
|
+
def __repr__(self) -> str:
|
|
37
|
+
return f"<{self.__class__.__qualname__}>"
|
|
38
|
+
|
|
39
|
+
async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
|
|
40
|
+
ctx = Context(raw_update=event) if ctx is None else ctx
|
|
41
|
+
temp_ctx = ctx.copy()
|
|
42
|
+
temp_ctx |= self.preset_context
|
|
43
|
+
|
|
44
|
+
for rule in self.rules:
|
|
45
|
+
if not await check_rule(api, rule, event, ctx):
|
|
46
|
+
logger.debug("Rule {!r} failed!", rule)
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
ctx |= temp_ctx
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
@abc.abstractmethod
|
|
53
|
+
async def run(self, api: API, event: MessageCute, ctx: Context) -> typing.Any:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
__all__ = ("BaseReplyHandler",)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.types.objects import InputFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DocumentReplyHandler(BaseReplyHandler):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
document: InputFile | str,
|
|
15
|
+
*rules: ABCRule,
|
|
16
|
+
caption: str | None = None,
|
|
17
|
+
parse_mode: str | None = None,
|
|
18
|
+
is_blocking: bool = True,
|
|
19
|
+
as_reply: bool = False,
|
|
20
|
+
preset_context: Context | None = None,
|
|
21
|
+
**default_params: typing.Any,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.document = document
|
|
24
|
+
self.parse_mode = parse_mode
|
|
25
|
+
self.caption = caption
|
|
26
|
+
super().__init__(
|
|
27
|
+
*rules,
|
|
28
|
+
is_blocking=is_blocking,
|
|
29
|
+
as_reply=as_reply,
|
|
30
|
+
preset_context=preset_context,
|
|
31
|
+
**default_params,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
35
|
+
method = event.answer_document if not self.as_reply else event.reply_document
|
|
36
|
+
await method(
|
|
37
|
+
document=self.document,
|
|
38
|
+
parse_mode=self.parse_mode,
|
|
39
|
+
caption=self.caption,
|
|
40
|
+
**self.default_params,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ("DocumentReplyHandler",)
|
|
@@ -3,7 +3,7 @@ from functools import cached_property
|
|
|
3
3
|
|
|
4
4
|
import typing_extensions as typing
|
|
5
5
|
|
|
6
|
-
from telegrinder.api import API
|
|
6
|
+
from telegrinder.api.api import API
|
|
7
7
|
from telegrinder.bot.cute_types import BaseCute, UpdateCute
|
|
8
8
|
from telegrinder.bot.dispatch.context import Context
|
|
9
9
|
from telegrinder.bot.dispatch.process import check_rule
|
|
@@ -72,7 +72,7 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
|
|
|
72
72
|
if nodes:
|
|
73
73
|
result = await compose_nodes(nodes, ctx, data={Update: event, API: api})
|
|
74
74
|
if not result:
|
|
75
|
-
logger.debug(f"Cannot compose nodes for handler. {result.error}")
|
|
75
|
+
logger.debug(f"Cannot compose nodes for handler. Error: {result.error!r}")
|
|
76
76
|
return False
|
|
77
77
|
|
|
78
78
|
node_col = result.value
|
|
@@ -95,7 +95,7 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
|
|
|
95
95
|
return True
|
|
96
96
|
|
|
97
97
|
async def run(self, api: API, event: Event, ctx: Context) -> typing.Any:
|
|
98
|
-
logger.debug(f"Running func handler {self.func}")
|
|
98
|
+
logger.debug(f"Running func handler {self.func.__qualname__!r}")
|
|
99
99
|
dataclass_type = typing.get_origin(self.dataclass) or self.dataclass
|
|
100
100
|
|
|
101
101
|
if dataclass_type is Update and (event_node := ctx.pop(EVENT_NODE_KEY, None)) is not None:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.message import InputMediaType, MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MediaGroupReplyHandler(BaseReplyHandler):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
media: InputMediaType | list[InputMediaType],
|
|
14
|
+
*rules: ABCRule,
|
|
15
|
+
caption: str | list[str] | None = None,
|
|
16
|
+
parse_mode: str | list[str] | None = None,
|
|
17
|
+
is_blocking: bool = True,
|
|
18
|
+
as_reply: bool = False,
|
|
19
|
+
preset_context: Context | None = None,
|
|
20
|
+
**default_params: typing.Any,
|
|
21
|
+
) -> None:
|
|
22
|
+
self.media = media
|
|
23
|
+
self.parse_mode = parse_mode
|
|
24
|
+
self.caption = caption
|
|
25
|
+
super().__init__(
|
|
26
|
+
*rules,
|
|
27
|
+
is_blocking=is_blocking,
|
|
28
|
+
as_reply=as_reply,
|
|
29
|
+
preset_context=preset_context,
|
|
30
|
+
**default_params,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
34
|
+
method = event.answer_media_group if not self.as_reply else event.reply_media_group
|
|
35
|
+
await method(
|
|
36
|
+
media=self.media,
|
|
37
|
+
parse_mode=self.parse_mode,
|
|
38
|
+
caption=self.caption,
|
|
39
|
+
**self.default_params,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = ("MediaGroupReplyHandler",)
|
|
@@ -3,57 +3,34 @@ import typing
|
|
|
3
3
|
from telegrinder.api.api import API
|
|
4
4
|
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
|
-
from telegrinder.bot.dispatch.handler.
|
|
7
|
-
from telegrinder.bot.dispatch.process import check_rule
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
8
7
|
from telegrinder.bot.rules.abc import ABCRule
|
|
9
|
-
from telegrinder.modules import logger
|
|
10
|
-
from telegrinder.types.objects import ReplyParameters, Update
|
|
11
8
|
|
|
12
9
|
|
|
13
|
-
class MessageReplyHandler(
|
|
10
|
+
class MessageReplyHandler(BaseReplyHandler):
|
|
14
11
|
def __init__(
|
|
15
12
|
self,
|
|
16
13
|
text: str,
|
|
17
14
|
*rules: ABCRule,
|
|
15
|
+
parse_mode: str | None = None,
|
|
18
16
|
is_blocking: bool = True,
|
|
19
17
|
as_reply: bool = False,
|
|
20
18
|
preset_context: Context | None = None,
|
|
21
19
|
**default_params: typing.Any,
|
|
22
20
|
) -> None:
|
|
23
21
|
self.text = text
|
|
24
|
-
self.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return "<{}: with rules={!r}, {}: {!r}>".format(
|
|
32
|
-
("blocking " if self.is_blocking else "") + self.__class__.__name__,
|
|
33
|
-
self.rules,
|
|
34
|
-
"answer text as reply" if self.as_reply else "answer text",
|
|
35
|
-
self.text,
|
|
22
|
+
self.parse_mode = parse_mode
|
|
23
|
+
super().__init__(
|
|
24
|
+
*rules,
|
|
25
|
+
is_blocking=is_blocking,
|
|
26
|
+
as_reply=as_reply,
|
|
27
|
+
preset_context=preset_context,
|
|
28
|
+
**default_params,
|
|
36
29
|
)
|
|
37
30
|
|
|
38
|
-
async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
|
|
39
|
-
ctx = Context(raw_update=event) if ctx is None else ctx
|
|
40
|
-
temp_ctx = ctx.copy()
|
|
41
|
-
temp_ctx |= self.preset_context
|
|
42
|
-
|
|
43
|
-
for rule in self.rules:
|
|
44
|
-
if not await check_rule(api, rule, event, ctx):
|
|
45
|
-
logger.debug("Rule {!r} failed!", rule)
|
|
46
|
-
return False
|
|
47
|
-
|
|
48
|
-
ctx |= temp_ctx
|
|
49
|
-
return True
|
|
50
|
-
|
|
51
31
|
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
|
|
55
|
-
**self.default_params,
|
|
56
|
-
)
|
|
32
|
+
method = event.answer if not self.as_reply else event.reply
|
|
33
|
+
await method(text=self.text, parse_mode=self.parse_mode, **self.default_params)
|
|
57
34
|
|
|
58
35
|
|
|
59
36
|
__all__ = ("MessageReplyHandler",)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.types.objects import InputFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PhotoReplyHandler(BaseReplyHandler):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
photo: InputFile | str,
|
|
15
|
+
*rules: ABCRule,
|
|
16
|
+
caption: str | None = None,
|
|
17
|
+
parse_mode: str | None = None,
|
|
18
|
+
is_blocking: bool = True,
|
|
19
|
+
as_reply: bool = False,
|
|
20
|
+
preset_context: Context | None = None,
|
|
21
|
+
**default_params: typing.Any,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.photo = photo
|
|
24
|
+
self.parse_mode = parse_mode
|
|
25
|
+
self.caption = caption
|
|
26
|
+
super().__init__(
|
|
27
|
+
*rules,
|
|
28
|
+
is_blocking=is_blocking,
|
|
29
|
+
as_reply=as_reply,
|
|
30
|
+
preset_context=preset_context,
|
|
31
|
+
**default_params,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
35
|
+
method = event.answer_photo if not self.as_reply else event.reply_photo
|
|
36
|
+
await method(
|
|
37
|
+
photo=self.photo,
|
|
38
|
+
parse_mode=self.parse_mode,
|
|
39
|
+
caption=self.caption,
|
|
40
|
+
**self.default_params,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ("PhotoReplyHandler",)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.types.objects import InputFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StickerReplyHandler(BaseReplyHandler):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
sticker: InputFile | str,
|
|
15
|
+
*rules: ABCRule,
|
|
16
|
+
emoji: str | None = None,
|
|
17
|
+
is_blocking: bool = True,
|
|
18
|
+
as_reply: bool = False,
|
|
19
|
+
preset_context: Context | None = None,
|
|
20
|
+
**default_params: typing.Any,
|
|
21
|
+
) -> None:
|
|
22
|
+
self.sticker = sticker
|
|
23
|
+
self.emoji = emoji
|
|
24
|
+
super().__init__(
|
|
25
|
+
*rules,
|
|
26
|
+
is_blocking=is_blocking,
|
|
27
|
+
as_reply=as_reply,
|
|
28
|
+
preset_context=preset_context,
|
|
29
|
+
**default_params,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
33
|
+
method = event.answer_sticker if not self.as_reply else event.reply_sticker
|
|
34
|
+
await method(sticker=self.sticker, emoji=self.emoji, **self.default_params)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ("StickerReplyHandler",)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.types.objects import InputFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VideoReplyHandler(BaseReplyHandler):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
video: InputFile | str,
|
|
15
|
+
*rules: ABCRule,
|
|
16
|
+
caption: str | None = None,
|
|
17
|
+
parse_mode: str | None = None,
|
|
18
|
+
is_blocking: bool = True,
|
|
19
|
+
as_reply: bool = False,
|
|
20
|
+
preset_context: Context | None = None,
|
|
21
|
+
**default_params: typing.Any,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.video = video
|
|
24
|
+
self.parse_mode = parse_mode
|
|
25
|
+
self.caption = caption
|
|
26
|
+
super().__init__(
|
|
27
|
+
*rules,
|
|
28
|
+
is_blocking=is_blocking,
|
|
29
|
+
as_reply=as_reply,
|
|
30
|
+
preset_context=preset_context,
|
|
31
|
+
**default_params,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
|
|
35
|
+
method = event.answer_video if not self.as_reply else event.reply_video
|
|
36
|
+
await method(
|
|
37
|
+
video=self.video,
|
|
38
|
+
parse_mode=self.parse_mode,
|
|
39
|
+
caption=self.caption,
|
|
40
|
+
**self.default_params,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ("VideoReplyHandler",)
|
|
@@ -35,7 +35,7 @@ async def process_inner(
|
|
|
35
35
|
logger.debug("Run pre middlewares...")
|
|
36
36
|
for middleware in middlewares:
|
|
37
37
|
middleware_result = await middleware.pre(event, ctx)
|
|
38
|
-
logger.debug("Middleware {!r} returned {!r}", middleware.__class__.
|
|
38
|
+
logger.debug("Middleware {!r} returned: {!r}", middleware.__class__.__qualname__, middleware_result)
|
|
39
39
|
if middleware_result is False:
|
|
40
40
|
return False
|
|
41
41
|
|
|
@@ -59,7 +59,7 @@ async def process_inner(
|
|
|
59
59
|
|
|
60
60
|
logger.debug("Run post middlewares...")
|
|
61
61
|
for middleware in middlewares:
|
|
62
|
-
logger.debug("Run post middleware {!r}", middleware.__class__.
|
|
62
|
+
logger.debug("Run post middleware {!r}", middleware.__class__.__qualname__)
|
|
63
63
|
await middleware.post(event, responses, ctx)
|
|
64
64
|
|
|
65
65
|
for session in ctx.get(CONTEXT_STORE_NODES_KEY, {}).values():
|
|
@@ -27,13 +27,10 @@ def register_manager(return_type: type[typing.Any] | types.UnionType):
|
|
|
27
27
|
@dataclasses.dataclass(frozen=True, slots=True)
|
|
28
28
|
class Manager:
|
|
29
29
|
types: tuple[type, ...]
|
|
30
|
-
callback: typing.Callable[..., typing.Awaitable]
|
|
30
|
+
callback: typing.Callable[..., typing.Awaitable[typing.Any]]
|
|
31
31
|
|
|
32
32
|
async def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
|
33
|
-
|
|
34
|
-
await self.callback(*args, **kwargs)
|
|
35
|
-
except BaseException as ex:
|
|
36
|
-
logger.exception(ex)
|
|
33
|
+
await self.callback(*args, **kwargs)
|
|
37
34
|
|
|
38
35
|
|
|
39
36
|
class ABCReturnManager(ABC, typing.Generic[Event]):
|
|
@@ -51,11 +48,16 @@ class BaseReturnManager(ABCReturnManager[Event]):
|
|
|
51
48
|
|
|
52
49
|
@property
|
|
53
50
|
def managers(self) -> list[Manager]:
|
|
54
|
-
|
|
51
|
+
managers = self.__dict__.get("managers")
|
|
52
|
+
if managers is not None:
|
|
53
|
+
return managers
|
|
54
|
+
managers_lst = [
|
|
55
55
|
manager
|
|
56
56
|
for manager in (vars(BaseReturnManager) | vars(self.__class__)).values()
|
|
57
57
|
if isinstance(manager, Manager)
|
|
58
58
|
]
|
|
59
|
+
self.__dict__["managers"] = managers_lst
|
|
60
|
+
return managers_lst
|
|
59
61
|
|
|
60
62
|
@register_manager(Context)
|
|
61
63
|
@staticmethod
|
|
@@ -73,7 +75,8 @@ class BaseReturnManager(ABCReturnManager[Event]):
|
|
|
73
75
|
|
|
74
76
|
@typing.overload
|
|
75
77
|
def register_manager(
|
|
76
|
-
self,
|
|
78
|
+
self,
|
|
79
|
+
return_type: type[T],
|
|
77
80
|
) -> typing.Callable[[typing.Callable[[T, Event, Context], typing.Awaitable[typing.Any]]], Manager]: ...
|
|
78
81
|
|
|
79
82
|
@typing.overload
|
|
@@ -94,7 +97,7 @@ class BaseReturnManager(ABCReturnManager[Event]):
|
|
|
94
97
|
]:
|
|
95
98
|
def wrapper(func: typing.Callable[[T, Event, Context], typing.Awaitable]) -> Manager:
|
|
96
99
|
manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
|
|
97
|
-
|
|
100
|
+
self.managers.append(manager)
|
|
98
101
|
return manager
|
|
99
102
|
|
|
100
103
|
return wrapper
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
3
|
+
from telegrinder.bot.cute_types.callback_query import CallbackQueryCute
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
5
|
from telegrinder.bot.dispatch.return_manager.abc import BaseReturnManager, register_manager
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ class CallbackQueryReturnManager(BaseReturnManager[CallbackQueryCute]):
|
|
|
11
11
|
async def str_manager(value: str, event: CallbackQueryCute, ctx: Context) -> None:
|
|
12
12
|
await event.answer(value)
|
|
13
13
|
|
|
14
|
-
@register_manager(dict)
|
|
14
|
+
@register_manager(dict[str, typing.Any])
|
|
15
15
|
@staticmethod
|
|
16
16
|
async def dict_manager(value: dict[str, typing.Any], event: CallbackQueryCute, ctx: Context) -> None:
|
|
17
17
|
await event.answer(**value)
|