telegrinder 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of telegrinder might be problematic. Click here for more details.
- telegrinder/__init__.py +144 -144
- telegrinder/api/__init__.py +8 -8
- telegrinder/api/api.py +93 -93
- telegrinder/api/error.py +16 -16
- telegrinder/api/response.py +20 -20
- telegrinder/api/token.py +36 -36
- telegrinder/bot/__init__.py +66 -66
- telegrinder/bot/bot.py +76 -76
- telegrinder/bot/cute_types/__init__.py +11 -11
- telegrinder/bot/cute_types/base.py +258 -234
- telegrinder/bot/cute_types/callback_query.py +382 -382
- telegrinder/bot/cute_types/chat_join_request.py +61 -61
- telegrinder/bot/cute_types/chat_member_updated.py +160 -160
- telegrinder/bot/cute_types/inline_query.py +53 -53
- telegrinder/bot/cute_types/message.py +2631 -2631
- telegrinder/bot/cute_types/update.py +75 -75
- telegrinder/bot/cute_types/utils.py +92 -92
- telegrinder/bot/dispatch/__init__.py +55 -55
- telegrinder/bot/dispatch/abc.py +77 -77
- telegrinder/bot/dispatch/context.py +92 -92
- telegrinder/bot/dispatch/dispatch.py +202 -201
- telegrinder/bot/dispatch/handler/__init__.py +13 -13
- telegrinder/bot/dispatch/handler/abc.py +24 -24
- telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
- telegrinder/bot/dispatch/handler/base.py +57 -57
- telegrinder/bot/dispatch/handler/document_reply.py +44 -44
- telegrinder/bot/dispatch/handler/func.py +128 -123
- telegrinder/bot/dispatch/handler/media_group_reply.py +43 -43
- telegrinder/bot/dispatch/handler/message_reply.py +36 -36
- telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
- telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
- telegrinder/bot/dispatch/handler/video_reply.py +44 -44
- telegrinder/bot/dispatch/middleware/__init__.py +3 -3
- telegrinder/bot/dispatch/middleware/abc.py +16 -16
- telegrinder/bot/dispatch/process.py +132 -132
- telegrinder/bot/dispatch/return_manager/__init__.py +13 -13
- telegrinder/bot/dispatch/return_manager/abc.py +108 -108
- telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
- telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
- telegrinder/bot/dispatch/return_manager/message.py +36 -36
- telegrinder/bot/dispatch/view/__init__.py +13 -13
- telegrinder/bot/dispatch/view/abc.py +41 -41
- telegrinder/bot/dispatch/view/base.py +200 -211
- telegrinder/bot/dispatch/view/box.py +129 -129
- telegrinder/bot/dispatch/view/callback_query.py +17 -17
- telegrinder/bot/dispatch/view/chat_join_request.py +16 -16
- telegrinder/bot/dispatch/view/chat_member.py +39 -39
- telegrinder/bot/dispatch/view/inline_query.py +17 -17
- telegrinder/bot/dispatch/view/message.py +44 -44
- telegrinder/bot/dispatch/view/raw.py +114 -118
- telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
- telegrinder/bot/dispatch/waiter_machine/actions.py +13 -13
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +57 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +53 -53
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
- telegrinder/bot/dispatch/waiter_machine/machine.py +168 -170
- telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
- telegrinder/bot/dispatch/waiter_machine/short_state.py +65 -65
- telegrinder/bot/polling/__init__.py +4 -4
- telegrinder/bot/polling/abc.py +25 -25
- telegrinder/bot/polling/polling.py +131 -131
- telegrinder/bot/rules/__init__.py +62 -62
- telegrinder/bot/rules/abc.py +238 -238
- telegrinder/bot/rules/adapter/__init__.py +9 -9
- telegrinder/bot/rules/adapter/abc.py +29 -29
- telegrinder/bot/rules/adapter/errors.py +5 -5
- telegrinder/bot/rules/adapter/event.py +76 -76
- telegrinder/bot/rules/adapter/node.py +48 -48
- telegrinder/bot/rules/adapter/raw_update.py +30 -30
- telegrinder/bot/rules/callback_data.py +171 -171
- telegrinder/bot/rules/chat_join.py +48 -48
- telegrinder/bot/rules/command.py +126 -126
- telegrinder/bot/rules/enum_text.py +36 -36
- telegrinder/bot/rules/func.py +26 -26
- telegrinder/bot/rules/fuzzy.py +24 -24
- telegrinder/bot/rules/inline.py +60 -60
- telegrinder/bot/rules/integer.py +20 -20
- telegrinder/bot/rules/is_from.py +146 -146
- telegrinder/bot/rules/markup.py +43 -43
- telegrinder/bot/rules/mention.py +14 -14
- telegrinder/bot/rules/message.py +17 -17
- telegrinder/bot/rules/message_entities.py +35 -35
- telegrinder/bot/rules/node.py +27 -27
- telegrinder/bot/rules/regex.py +37 -37
- telegrinder/bot/rules/rule_enum.py +72 -72
- telegrinder/bot/rules/start.py +42 -42
- telegrinder/bot/rules/state.py +37 -37
- telegrinder/bot/rules/text.py +33 -33
- telegrinder/bot/rules/update.py +15 -15
- telegrinder/bot/scenario/__init__.py +5 -5
- telegrinder/bot/scenario/abc.py +19 -19
- telegrinder/bot/scenario/checkbox.py +167 -147
- telegrinder/bot/scenario/choice.py +46 -44
- telegrinder/client/__init__.py +4 -4
- telegrinder/client/abc.py +75 -75
- telegrinder/client/aiohttp.py +130 -130
- telegrinder/model.py +244 -244
- telegrinder/modules.py +237 -237
- telegrinder/msgspec_json.py +14 -14
- telegrinder/msgspec_utils.py +410 -410
- telegrinder/node/__init__.py +20 -20
- telegrinder/node/attachment.py +92 -92
- telegrinder/node/base.py +143 -144
- telegrinder/node/callback_query.py +14 -14
- telegrinder/node/command.py +33 -33
- telegrinder/node/composer.py +196 -184
- telegrinder/node/container.py +27 -27
- telegrinder/node/event.py +71 -73
- telegrinder/node/me.py +16 -16
- telegrinder/node/message.py +14 -14
- telegrinder/node/polymorphic.py +48 -52
- telegrinder/node/rule.py +76 -76
- telegrinder/node/scope.py +38 -38
- telegrinder/node/source.py +71 -71
- telegrinder/node/text.py +21 -21
- telegrinder/node/tools/__init__.py +3 -3
- telegrinder/node/tools/generator.py +40 -40
- telegrinder/node/update.py +15 -15
- telegrinder/rules.py +0 -0
- telegrinder/tools/__init__.py +74 -74
- telegrinder/tools/buttons.py +79 -79
- telegrinder/tools/error_handler/__init__.py +7 -7
- telegrinder/tools/error_handler/abc.py +33 -33
- telegrinder/tools/error_handler/error.py +9 -9
- telegrinder/tools/error_handler/error_handler.py +193 -193
- telegrinder/tools/formatting/__init__.py +46 -46
- telegrinder/tools/formatting/html.py +308 -308
- telegrinder/tools/formatting/links.py +33 -33
- telegrinder/tools/formatting/spec_html_formats.py +111 -111
- telegrinder/tools/functional.py +12 -12
- telegrinder/tools/global_context/__init__.py +7 -7
- telegrinder/tools/global_context/abc.py +63 -63
- telegrinder/tools/global_context/global_context.py +412 -412
- telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
- telegrinder/tools/i18n/__init__.py +12 -12
- telegrinder/tools/i18n/abc.py +32 -32
- telegrinder/tools/i18n/middleware/__init__.py +3 -3
- telegrinder/tools/i18n/middleware/abc.py +25 -25
- telegrinder/tools/i18n/simple.py +43 -43
- telegrinder/tools/kb_set/__init__.py +4 -4
- telegrinder/tools/kb_set/base.py +15 -15
- telegrinder/tools/kb_set/yaml.py +63 -63
- telegrinder/tools/keyboard.py +128 -128
- telegrinder/tools/limited_dict.py +37 -37
- telegrinder/tools/loop_wrapper/__init__.py +4 -4
- telegrinder/tools/loop_wrapper/abc.py +15 -15
- telegrinder/tools/loop_wrapper/loop_wrapper.py +216 -216
- telegrinder/tools/magic.py +168 -168
- telegrinder/tools/parse_mode.py +6 -6
- telegrinder/tools/state_storage/__init__.py +4 -4
- telegrinder/tools/state_storage/abc.py +35 -35
- telegrinder/tools/state_storage/memory.py +25 -25
- telegrinder/types/__init__.py +6 -6
- telegrinder/types/enums.py +672 -672
- telegrinder/types/methods.py +4633 -4633
- telegrinder/types/objects.py +6317 -6317
- telegrinder/verification_utils.py +32 -32
- {telegrinder-0.3.1.dist-info → telegrinder-0.3.2.dist-info}/LICENSE +22 -22
- {telegrinder-0.3.1.dist-info → telegrinder-0.3.2.dist-info}/METADATA +1 -1
- telegrinder-0.3.2.dist-info/RECORD +164 -0
- telegrinder-0.3.1.dist-info/RECORD +0 -164
- {telegrinder-0.3.1.dist-info → telegrinder-0.3.2.dist-info}/WHEEL +0 -0
|
@@ -1,193 +1,193 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import typing
|
|
3
|
-
|
|
4
|
-
from fntypes.result import Error, Ok, Result
|
|
5
|
-
|
|
6
|
-
from telegrinder.api.api import API
|
|
7
|
-
from telegrinder.bot.dispatch.context import Context
|
|
8
|
-
from telegrinder.modules import logger
|
|
9
|
-
from telegrinder.node.base import is_node
|
|
10
|
-
from telegrinder.tools.error_handler.abc import ABCErrorHandler, Event, Handler
|
|
11
|
-
from telegrinder.tools.error_handler.error import CatcherError
|
|
12
|
-
from telegrinder.tools.magic import magic_bundle
|
|
13
|
-
|
|
14
|
-
F = typing.TypeVar("F", bound="FuncCatcher")
|
|
15
|
-
ExceptionT = typing.TypeVar("ExceptionT", bound=BaseException, contravariant=True)
|
|
16
|
-
FuncCatcher = typing.Callable[typing.Concatenate[ExceptionT, ...], typing.Awaitable[typing.Any]]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async def run_handler(
|
|
20
|
-
handler: Handler,
|
|
21
|
-
event: typing.Any,
|
|
22
|
-
ctx: dict[str, typing.Any],
|
|
23
|
-
) -> typing.Any:
|
|
24
|
-
annotations = tuple(handler.
|
|
25
|
-
start_idx = 0 if is_node(None if not annotations else annotations[0]) else 1
|
|
26
|
-
context = magic_bundle(handler, ctx, start_idx=start_idx)
|
|
27
|
-
return await (handler(event, **context) if start_idx == 1 else handler(**context))
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclasses.dataclass(frozen=True, repr=False, slots=True)
|
|
31
|
-
class Catcher(typing.Generic[Event]):
|
|
32
|
-
func: FuncCatcher[BaseException]
|
|
33
|
-
exceptions: list[type[BaseException] | BaseException] = dataclasses.field(
|
|
34
|
-
default_factory=lambda: [],
|
|
35
|
-
kw_only=True,
|
|
36
|
-
)
|
|
37
|
-
logging: bool = dataclasses.field(default=False, kw_only=True)
|
|
38
|
-
raise_exception: bool = dataclasses.field(default=False, kw_only=True)
|
|
39
|
-
ignore_errors: bool = dataclasses.field(default=False, kw_only=True)
|
|
40
|
-
|
|
41
|
-
def __repr__(self) -> str:
|
|
42
|
-
return "<Catcher: function={!r}, logging={}, raise_exception={}, ignore_errors={}>".format(
|
|
43
|
-
self.func.__name__,
|
|
44
|
-
self.logging,
|
|
45
|
-
self.raise_exception,
|
|
46
|
-
self.ignore_errors,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
async def __call__(
|
|
50
|
-
self,
|
|
51
|
-
handler: Handler,
|
|
52
|
-
event: Event,
|
|
53
|
-
api: API,
|
|
54
|
-
ctx: Context,
|
|
55
|
-
) -> Result[typing.Any, BaseException]:
|
|
56
|
-
try:
|
|
57
|
-
return Ok(await run_handler(handler, event, ctx))
|
|
58
|
-
except BaseException as exc:
|
|
59
|
-
return await self.run(api, event, ctx, exc, handler.__name__)
|
|
60
|
-
|
|
61
|
-
def match_exception(self, exception: BaseException) -> bool:
|
|
62
|
-
for exc in self.exceptions:
|
|
63
|
-
if isinstance(exc, type) and type(exception) is exc:
|
|
64
|
-
return True
|
|
65
|
-
if isinstance(exc, object) and type(exception) is type(exc):
|
|
66
|
-
return True if not exc.args else exc.args == exception.args
|
|
67
|
-
|
|
68
|
-
return False
|
|
69
|
-
|
|
70
|
-
async def run(
|
|
71
|
-
self,
|
|
72
|
-
api: API,
|
|
73
|
-
event: Event,
|
|
74
|
-
ctx: Context,
|
|
75
|
-
exception: BaseException,
|
|
76
|
-
handler_name: str,
|
|
77
|
-
) -> Result[typing.Any, BaseException]:
|
|
78
|
-
if self.match_exception(exception):
|
|
79
|
-
logger.debug(
|
|
80
|
-
"Error handler caught an exception {!r} in handler {!r}, running catcher {!r}...".format(
|
|
81
|
-
exception, handler_name, self.func.__name__
|
|
82
|
-
)
|
|
83
|
-
)
|
|
84
|
-
return Ok(await run_handler(self.func, event, {"event": event, "api": api} | ctx))
|
|
85
|
-
|
|
86
|
-
logger.debug("Failed to match exception {!r}.", exception.__class__.__name__)
|
|
87
|
-
return Error(exception)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class ErrorHandler(ABCErrorHandler[Event]):
|
|
91
|
-
def __init__(self, catcher: Catcher[Event] | None = None, /) -> None:
|
|
92
|
-
self.catcher = catcher
|
|
93
|
-
|
|
94
|
-
def __repr__(self) -> str:
|
|
95
|
-
return (
|
|
96
|
-
"<{}: exceptions_handled=[{}], catcher={!r}>".format(
|
|
97
|
-
self.__class__.__name__,
|
|
98
|
-
", ".join(e.__name__ if isinstance(e, type) else repr(e) for e in self.catcher.exceptions),
|
|
99
|
-
self.catcher,
|
|
100
|
-
)
|
|
101
|
-
if self.catcher is not None
|
|
102
|
-
else "<{}()>".format(self.__class__.__name__)
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
def __call__(
|
|
106
|
-
self,
|
|
107
|
-
*exceptions: type[BaseException] | BaseException,
|
|
108
|
-
logging: bool = False,
|
|
109
|
-
raise_exception: bool = False,
|
|
110
|
-
ignore_errors: bool = False,
|
|
111
|
-
):
|
|
112
|
-
"""Register the catcher.
|
|
113
|
-
|
|
114
|
-
:param logging: Logging the result of the catcher at the level `DEBUG`.
|
|
115
|
-
:param raise_exception: Raise an exception if the catcher has not started.
|
|
116
|
-
:param ignore_errors: Ignore errors that may occur.
|
|
117
|
-
"""
|
|
118
|
-
|
|
119
|
-
def decorator(func: F) -> F:
|
|
120
|
-
if not self.catcher:
|
|
121
|
-
self.catcher = Catcher(
|
|
122
|
-
func,
|
|
123
|
-
exceptions=list(exceptions),
|
|
124
|
-
logging=logging,
|
|
125
|
-
raise_exception=raise_exception,
|
|
126
|
-
ignore_errors=ignore_errors,
|
|
127
|
-
)
|
|
128
|
-
return func
|
|
129
|
-
|
|
130
|
-
return decorator
|
|
131
|
-
|
|
132
|
-
def _process_catcher_error(self, error: CatcherError) -> Result[None, BaseException]:
|
|
133
|
-
assert self.catcher is not None
|
|
134
|
-
|
|
135
|
-
if self.catcher.raise_exception:
|
|
136
|
-
raise error.exc from None
|
|
137
|
-
if self.catcher.logging:
|
|
138
|
-
logger.error(error.message)
|
|
139
|
-
if not self.catcher.ignore_errors:
|
|
140
|
-
return Error(error.exc)
|
|
141
|
-
|
|
142
|
-
return Ok(None)
|
|
143
|
-
|
|
144
|
-
async def process(
|
|
145
|
-
self,
|
|
146
|
-
handler: Handler,
|
|
147
|
-
event: Event,
|
|
148
|
-
api: API,
|
|
149
|
-
ctx: Context,
|
|
150
|
-
) -> Result[typing.Any, BaseException]:
|
|
151
|
-
assert self.catcher is not None
|
|
152
|
-
logger.debug("Processing the error handler for handler {!r}...", handler.__name__)
|
|
153
|
-
|
|
154
|
-
try:
|
|
155
|
-
return await self.catcher(handler, event, api, ctx)
|
|
156
|
-
except BaseException as exc:
|
|
157
|
-
return Error(
|
|
158
|
-
CatcherError(
|
|
159
|
-
exc,
|
|
160
|
-
"Exception {} was occurred during the running catcher {!r}.".format(
|
|
161
|
-
repr(exc), self.catcher.func.__name__
|
|
162
|
-
),
|
|
163
|
-
)
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
async def run(
|
|
167
|
-
self,
|
|
168
|
-
handler: Handler,
|
|
169
|
-
event: Event,
|
|
170
|
-
api: API,
|
|
171
|
-
ctx: Context,
|
|
172
|
-
) -> Result[typing.Any, BaseException]:
|
|
173
|
-
if not self.catcher:
|
|
174
|
-
return Ok(await run_handler(handler, event, ctx))
|
|
175
|
-
|
|
176
|
-
match await self.process(handler, event, api, ctx):
|
|
177
|
-
case Ok(value) as ok:
|
|
178
|
-
if self.catcher.logging:
|
|
179
|
-
logger.debug(
|
|
180
|
-
"Catcher {!r} returned: {!r}",
|
|
181
|
-
self.catcher.func.__name__,
|
|
182
|
-
value,
|
|
183
|
-
)
|
|
184
|
-
return ok
|
|
185
|
-
case Error(exc) as err:
|
|
186
|
-
if isinstance(exc, CatcherError):
|
|
187
|
-
return self._process_catcher_error(exc)
|
|
188
|
-
if self.catcher.ignore_errors:
|
|
189
|
-
return Ok(None)
|
|
190
|
-
return err
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
__all__ = ("Catcher", "ErrorHandler")
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from fntypes.result import Error, Ok, Result
|
|
5
|
+
|
|
6
|
+
from telegrinder.api.api import API
|
|
7
|
+
from telegrinder.bot.dispatch.context import Context
|
|
8
|
+
from telegrinder.modules import logger
|
|
9
|
+
from telegrinder.node.base import is_node
|
|
10
|
+
from telegrinder.tools.error_handler.abc import ABCErrorHandler, Event, Handler
|
|
11
|
+
from telegrinder.tools.error_handler.error import CatcherError
|
|
12
|
+
from telegrinder.tools.magic import get_annotations, magic_bundle
|
|
13
|
+
|
|
14
|
+
F = typing.TypeVar("F", bound="FuncCatcher[typing.Any]")
|
|
15
|
+
ExceptionT = typing.TypeVar("ExceptionT", bound=BaseException, contravariant=True)
|
|
16
|
+
FuncCatcher = typing.Callable[typing.Concatenate[ExceptionT, ...], typing.Awaitable[typing.Any]]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def run_handler(
|
|
20
|
+
handler: Handler,
|
|
21
|
+
event: typing.Any,
|
|
22
|
+
ctx: dict[str, typing.Any],
|
|
23
|
+
) -> typing.Any:
|
|
24
|
+
annotations = tuple(get_annotations(handler).values())
|
|
25
|
+
start_idx = 0 if is_node(None if not annotations else annotations[0]) else 1
|
|
26
|
+
context = magic_bundle(handler, ctx, start_idx=start_idx)
|
|
27
|
+
return await (handler(event, **context) if start_idx == 1 else handler(**context))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclasses.dataclass(frozen=True, repr=False, slots=True)
|
|
31
|
+
class Catcher(typing.Generic[Event]):
|
|
32
|
+
func: FuncCatcher[BaseException]
|
|
33
|
+
exceptions: list[type[BaseException] | BaseException] = dataclasses.field(
|
|
34
|
+
default_factory=lambda: [],
|
|
35
|
+
kw_only=True,
|
|
36
|
+
)
|
|
37
|
+
logging: bool = dataclasses.field(default=False, kw_only=True)
|
|
38
|
+
raise_exception: bool = dataclasses.field(default=False, kw_only=True)
|
|
39
|
+
ignore_errors: bool = dataclasses.field(default=False, kw_only=True)
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return "<Catcher: function={!r}, logging={}, raise_exception={}, ignore_errors={}>".format(
|
|
43
|
+
self.func.__name__,
|
|
44
|
+
self.logging,
|
|
45
|
+
self.raise_exception,
|
|
46
|
+
self.ignore_errors,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
async def __call__(
|
|
50
|
+
self,
|
|
51
|
+
handler: Handler,
|
|
52
|
+
event: Event,
|
|
53
|
+
api: API,
|
|
54
|
+
ctx: Context,
|
|
55
|
+
) -> Result[typing.Any, BaseException]:
|
|
56
|
+
try:
|
|
57
|
+
return Ok(await run_handler(handler, event, ctx))
|
|
58
|
+
except BaseException as exc:
|
|
59
|
+
return await self.run(api, event, ctx, exc, handler.__name__)
|
|
60
|
+
|
|
61
|
+
def match_exception(self, exception: BaseException) -> bool:
|
|
62
|
+
for exc in self.exceptions:
|
|
63
|
+
if isinstance(exc, type) and type(exception) is exc:
|
|
64
|
+
return True
|
|
65
|
+
if isinstance(exc, object) and type(exception) is type(exc):
|
|
66
|
+
return True if not exc.args else exc.args == exception.args
|
|
67
|
+
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
async def run(
|
|
71
|
+
self,
|
|
72
|
+
api: API,
|
|
73
|
+
event: Event,
|
|
74
|
+
ctx: Context,
|
|
75
|
+
exception: BaseException,
|
|
76
|
+
handler_name: str,
|
|
77
|
+
) -> Result[typing.Any, BaseException]:
|
|
78
|
+
if self.match_exception(exception):
|
|
79
|
+
logger.debug(
|
|
80
|
+
"Error handler caught an exception {!r} in handler {!r}, running catcher {!r}...".format(
|
|
81
|
+
exception, handler_name, self.func.__name__
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
return Ok(await run_handler(self.func, event, {"event": event, "api": api} | ctx))
|
|
85
|
+
|
|
86
|
+
logger.debug("Failed to match exception {!r}.", exception.__class__.__name__)
|
|
87
|
+
return Error(exception)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ErrorHandler(ABCErrorHandler[Event]):
|
|
91
|
+
def __init__(self, catcher: Catcher[Event] | None = None, /) -> None:
|
|
92
|
+
self.catcher = catcher
|
|
93
|
+
|
|
94
|
+
def __repr__(self) -> str:
|
|
95
|
+
return (
|
|
96
|
+
"<{}: exceptions_handled=[{}], catcher={!r}>".format(
|
|
97
|
+
self.__class__.__name__,
|
|
98
|
+
", ".join(e.__name__ if isinstance(e, type) else repr(e) for e in self.catcher.exceptions),
|
|
99
|
+
self.catcher,
|
|
100
|
+
)
|
|
101
|
+
if self.catcher is not None
|
|
102
|
+
else "<{}()>".format(self.__class__.__name__)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def __call__(
|
|
106
|
+
self,
|
|
107
|
+
*exceptions: type[BaseException] | BaseException,
|
|
108
|
+
logging: bool = False,
|
|
109
|
+
raise_exception: bool = False,
|
|
110
|
+
ignore_errors: bool = False,
|
|
111
|
+
):
|
|
112
|
+
"""Register the catcher.
|
|
113
|
+
|
|
114
|
+
:param logging: Logging the result of the catcher at the level `DEBUG`.
|
|
115
|
+
:param raise_exception: Raise an exception if the catcher has not started.
|
|
116
|
+
:param ignore_errors: Ignore errors that may occur.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def decorator(func: F) -> F:
|
|
120
|
+
if not self.catcher:
|
|
121
|
+
self.catcher = Catcher(
|
|
122
|
+
func,
|
|
123
|
+
exceptions=list(exceptions),
|
|
124
|
+
logging=logging,
|
|
125
|
+
raise_exception=raise_exception,
|
|
126
|
+
ignore_errors=ignore_errors,
|
|
127
|
+
)
|
|
128
|
+
return func
|
|
129
|
+
|
|
130
|
+
return decorator
|
|
131
|
+
|
|
132
|
+
def _process_catcher_error(self, error: CatcherError) -> Result[None, BaseException]:
|
|
133
|
+
assert self.catcher is not None
|
|
134
|
+
|
|
135
|
+
if self.catcher.raise_exception:
|
|
136
|
+
raise error.exc from None
|
|
137
|
+
if self.catcher.logging:
|
|
138
|
+
logger.error(error.message)
|
|
139
|
+
if not self.catcher.ignore_errors:
|
|
140
|
+
return Error(error.exc)
|
|
141
|
+
|
|
142
|
+
return Ok(None)
|
|
143
|
+
|
|
144
|
+
async def process(
|
|
145
|
+
self,
|
|
146
|
+
handler: Handler,
|
|
147
|
+
event: Event,
|
|
148
|
+
api: API,
|
|
149
|
+
ctx: Context,
|
|
150
|
+
) -> Result[typing.Any, BaseException]:
|
|
151
|
+
assert self.catcher is not None
|
|
152
|
+
logger.debug("Processing the error handler for handler {!r}...", handler.__name__)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
return await self.catcher(handler, event, api, ctx)
|
|
156
|
+
except BaseException as exc:
|
|
157
|
+
return Error(
|
|
158
|
+
CatcherError(
|
|
159
|
+
exc,
|
|
160
|
+
"Exception {} was occurred during the running catcher {!r}.".format(
|
|
161
|
+
repr(exc), self.catcher.func.__name__
|
|
162
|
+
),
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
async def run(
|
|
167
|
+
self,
|
|
168
|
+
handler: Handler,
|
|
169
|
+
event: Event,
|
|
170
|
+
api: API,
|
|
171
|
+
ctx: Context,
|
|
172
|
+
) -> Result[typing.Any, BaseException]:
|
|
173
|
+
if not self.catcher:
|
|
174
|
+
return Ok(await run_handler(handler, event, ctx))
|
|
175
|
+
|
|
176
|
+
match await self.process(handler, event, api, ctx):
|
|
177
|
+
case Ok(value) as ok:
|
|
178
|
+
if self.catcher.logging:
|
|
179
|
+
logger.debug(
|
|
180
|
+
"Catcher {!r} returned: {!r}",
|
|
181
|
+
self.catcher.func.__name__,
|
|
182
|
+
value,
|
|
183
|
+
)
|
|
184
|
+
return ok
|
|
185
|
+
case Error(exc) as err:
|
|
186
|
+
if isinstance(exc, CatcherError):
|
|
187
|
+
return self._process_catcher_error(exc)
|
|
188
|
+
if self.catcher.ignore_errors:
|
|
189
|
+
return Ok(None)
|
|
190
|
+
return err
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
__all__ = ("Catcher", "ErrorHandler")
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
from .html import (
|
|
2
|
-
FormatString,
|
|
3
|
-
HTMLFormatter,
|
|
4
|
-
block_quote,
|
|
5
|
-
bold,
|
|
6
|
-
channel_boost_link,
|
|
7
|
-
code_inline,
|
|
8
|
-
escape,
|
|
9
|
-
invite_chat_link,
|
|
10
|
-
italic,
|
|
11
|
-
link,
|
|
12
|
-
mention,
|
|
13
|
-
pre_code,
|
|
14
|
-
resolve_domain,
|
|
15
|
-
spoiler,
|
|
16
|
-
start_bot_link,
|
|
17
|
-
start_group_link,
|
|
18
|
-
strike,
|
|
19
|
-
tg_emoji,
|
|
20
|
-
underline,
|
|
21
|
-
)
|
|
22
|
-
from .links import (
|
|
23
|
-
get_channel_boost_link,
|
|
24
|
-
get_invite_chat_link,
|
|
25
|
-
get_mention_link,
|
|
26
|
-
get_resolve_domain_link,
|
|
27
|
-
get_start_bot_link,
|
|
28
|
-
get_start_group_link,
|
|
29
|
-
)
|
|
30
|
-
from .spec_html_formats import (
|
|
31
|
-
BaseSpecFormat,
|
|
32
|
-
ChannelBoostLink,
|
|
33
|
-
InviteChatLink,
|
|
34
|
-
Link,
|
|
35
|
-
Mention,
|
|
36
|
-
PreCode,
|
|
37
|
-
ResolveDomain,
|
|
38
|
-
SpecialFormat,
|
|
39
|
-
StartBotLink,
|
|
40
|
-
StartGroupLink,
|
|
41
|
-
TgEmoji,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
__all__ = (
|
|
1
|
+
from .html import (
|
|
2
|
+
FormatString,
|
|
3
|
+
HTMLFormatter,
|
|
4
|
+
block_quote,
|
|
5
|
+
bold,
|
|
6
|
+
channel_boost_link,
|
|
7
|
+
code_inline,
|
|
8
|
+
escape,
|
|
9
|
+
invite_chat_link,
|
|
10
|
+
italic,
|
|
11
|
+
link,
|
|
12
|
+
mention,
|
|
13
|
+
pre_code,
|
|
14
|
+
resolve_domain,
|
|
15
|
+
spoiler,
|
|
16
|
+
start_bot_link,
|
|
17
|
+
start_group_link,
|
|
18
|
+
strike,
|
|
19
|
+
tg_emoji,
|
|
20
|
+
underline,
|
|
21
|
+
)
|
|
22
|
+
from .links import (
|
|
23
|
+
get_channel_boost_link,
|
|
24
|
+
get_invite_chat_link,
|
|
25
|
+
get_mention_link,
|
|
26
|
+
get_resolve_domain_link,
|
|
27
|
+
get_start_bot_link,
|
|
28
|
+
get_start_group_link,
|
|
29
|
+
)
|
|
30
|
+
from .spec_html_formats import (
|
|
31
|
+
BaseSpecFormat,
|
|
32
|
+
ChannelBoostLink,
|
|
33
|
+
InviteChatLink,
|
|
34
|
+
Link,
|
|
35
|
+
Mention,
|
|
36
|
+
PreCode,
|
|
37
|
+
ResolveDomain,
|
|
38
|
+
SpecialFormat,
|
|
39
|
+
StartBotLink,
|
|
40
|
+
StartGroupLink,
|
|
41
|
+
TgEmoji,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = (
|
|
45
45
|
"BaseSpecFormat",
|
|
46
46
|
"ChannelBoostLink",
|
|
47
47
|
"FormatString",
|
|
@@ -77,5 +77,5 @@ __all__ = (
|
|
|
77
77
|
"start_group_link",
|
|
78
78
|
"strike",
|
|
79
79
|
"tg_emoji",
|
|
80
|
-
"underline",
|
|
81
|
-
)
|
|
80
|
+
"underline",
|
|
81
|
+
)
|