telegrinder 0.3.2__py3-none-any.whl → 0.3.3.post1__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/bot/cute_types/__init__.py +6 -6
- telegrinder/bot/cute_types/callback_query.py +5 -2
- telegrinder/bot/cute_types/inline_query.py +3 -13
- telegrinder/bot/cute_types/message.py +9 -3
- telegrinder/bot/cute_types/update.py +45 -11
- telegrinder/bot/cute_types/utils.py +5 -5
- telegrinder/bot/dispatch/context.py +6 -0
- telegrinder/bot/dispatch/handler/func.py +10 -3
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +2 -2
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +2 -2
- telegrinder/bot/dispatch/waiter_machine/machine.py +7 -8
- telegrinder/bot/dispatch/waiter_machine/short_state.py +4 -1
- telegrinder/bot/rules/abc.py +2 -27
- telegrinder/bot/rules/adapter/abc.py +1 -1
- telegrinder/bot/rules/adapter/event.py +22 -31
- telegrinder/bot/rules/adapter/node.py +1 -1
- telegrinder/bot/rules/adapter/raw_update.py +1 -1
- telegrinder/bot/rules/callback_data.py +1 -2
- telegrinder/bot/rules/chat_join.py +1 -3
- telegrinder/bot/rules/inline.py +1 -1
- telegrinder/bot/rules/is_from.py +19 -19
- telegrinder/bot/rules/message.py +2 -2
- telegrinder/model.py +51 -0
- telegrinder/msgspec_utils.py +2 -2
- telegrinder/node/__init__.py +24 -20
- telegrinder/node/attachment.py +5 -5
- telegrinder/node/base.py +30 -7
- telegrinder/node/callback_query.py +41 -2
- telegrinder/node/composer.py +6 -4
- telegrinder/node/event.py +3 -9
- telegrinder/node/text.py +22 -2
- telegrinder/tools/formatting/html.py +25 -25
- telegrinder/tools/i18n/__init__.py +5 -5
- telegrinder/tools/i18n/abc.py +2 -2
- telegrinder/tools/keyboard.py +5 -5
- telegrinder/tools/loop_wrapper/loop_wrapper.py +17 -9
- telegrinder/tools/magic.py +11 -11
- telegrinder/types/__init__.py +254 -254
- telegrinder/types/enums.py +29 -29
- telegrinder/types/objects.py +3723 -1703
- {telegrinder-0.3.2.dist-info → telegrinder-0.3.3.post1.dist-info}/METADATA +1 -1
- {telegrinder-0.3.2.dist-info → telegrinder-0.3.3.post1.dist-info}/RECORD +44 -44
- {telegrinder-0.3.2.dist-info → telegrinder-0.3.3.post1.dist-info}/LICENSE +0 -0
- {telegrinder-0.3.2.dist-info → telegrinder-0.3.3.post1.dist-info}/WHEEL +0 -0
telegrinder/model.py
CHANGED
|
@@ -60,6 +60,57 @@ def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
|
|
|
60
60
|
return validated_params
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
class From(typing.Generic[T]):
|
|
64
|
+
def __new__(cls, _: T, /) -> typing.Any: ...
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if typing.TYPE_CHECKING:
|
|
68
|
+
|
|
69
|
+
@typing.overload
|
|
70
|
+
def field(*, name: str | None = ...) -> typing.Any: ...
|
|
71
|
+
|
|
72
|
+
@typing.overload
|
|
73
|
+
def field(*, default: typing.Any, name: str | None = ...) -> typing.Any: ...
|
|
74
|
+
|
|
75
|
+
@typing.overload
|
|
76
|
+
def field(
|
|
77
|
+
*,
|
|
78
|
+
converter: typing.Callable[[typing.Any], typing.Any],
|
|
79
|
+
name: str | None = ...,
|
|
80
|
+
) -> typing.Any: ...
|
|
81
|
+
|
|
82
|
+
@typing.overload
|
|
83
|
+
def field(
|
|
84
|
+
*,
|
|
85
|
+
default: typing.Any,
|
|
86
|
+
converter: typing.Callable[[typing.Any], typing.Any],
|
|
87
|
+
name: str | None = ...,
|
|
88
|
+
) -> typing.Any: ...
|
|
89
|
+
|
|
90
|
+
@typing.overload
|
|
91
|
+
def field(
|
|
92
|
+
*,
|
|
93
|
+
default_factory: typing.Callable[[], typing.Any],
|
|
94
|
+
converter: typing.Callable[[typing.Any], typing.Any],
|
|
95
|
+
name: str | None = None,
|
|
96
|
+
) -> typing.Any: ...
|
|
97
|
+
|
|
98
|
+
def field(
|
|
99
|
+
*,
|
|
100
|
+
default=...,
|
|
101
|
+
default_factory=...,
|
|
102
|
+
name=...,
|
|
103
|
+
converter=...,
|
|
104
|
+
) -> typing.Any: ...
|
|
105
|
+
else:
|
|
106
|
+
from msgspec import field as _field
|
|
107
|
+
|
|
108
|
+
def field(**kwargs):
|
|
109
|
+
kwargs.pop("converter", None)
|
|
110
|
+
return _field(**kwargs)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@typing.dataclass_transform(field_specifiers=(field,))
|
|
63
114
|
class Model(msgspec.Struct, **MODEL_CONFIG):
|
|
64
115
|
@classmethod
|
|
65
116
|
def from_data(cls, data: dict[str, typing.Any]) -> typing.Self:
|
telegrinder/msgspec_utils.py
CHANGED
|
@@ -235,7 +235,7 @@ class Decoder:
|
|
|
235
235
|
)
|
|
236
236
|
yield dec_obj
|
|
237
237
|
|
|
238
|
-
def add_dec_hook(self, t: T): # type: ignore
|
|
238
|
+
def add_dec_hook(self, t: type[T]): # type: ignore
|
|
239
239
|
def decorator(func: DecHook[T]) -> DecHook[T]:
|
|
240
240
|
return self.dec_hooks.setdefault(get_origin(t), func) # type: ignore
|
|
241
241
|
|
|
@@ -346,7 +346,7 @@ class Encoder:
|
|
|
346
346
|
enc_obj = msgspec.json.Encoder(enc_hook=self.enc_hook)
|
|
347
347
|
yield enc_obj
|
|
348
348
|
|
|
349
|
-
def
|
|
349
|
+
def add_enc_hook(self, t: type[T]):
|
|
350
350
|
def decorator(func: EncHook[T]) -> EncHook[T]:
|
|
351
351
|
encode_hook = self.enc_hooks.setdefault(get_origin(t), func)
|
|
352
352
|
return func if encode_hook is not func else encode_hook
|
telegrinder/node/__init__.py
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
from .attachment import Attachment, Audio, Photo, Video
|
|
2
|
-
from .base import ComposeError, DataNode, Node, ScalarNode, is_node
|
|
3
|
-
from .callback_query import CallbackQueryNode
|
|
4
|
-
from .command import CommandInfo
|
|
5
|
-
from .composer import Composition, NodeCollection, NodeSession, compose_node, compose_nodes
|
|
6
|
-
from .container import ContainerNode
|
|
7
|
-
from .event import EventNode
|
|
8
|
-
from .me import Me
|
|
9
|
-
from .message import MessageNode
|
|
10
|
-
from .polymorphic import Polymorphic, impl
|
|
11
|
-
from .rule import RuleChain
|
|
12
|
-
from .scope import GLOBAL, PER_CALL, PER_EVENT, NodeScope, global_node, per_call, per_event
|
|
13
|
-
from .source import ChatSource, Source, UserSource
|
|
14
|
-
from .text import Text, TextInteger
|
|
15
|
-
from .tools import generate_node
|
|
16
|
-
from .update import UpdateNode
|
|
17
|
-
|
|
18
|
-
__all__ = (
|
|
1
|
+
from .attachment import Attachment, Audio, Photo, Video
|
|
2
|
+
from .base import ComposeError, DataNode, Name, Node, ScalarNode, is_node
|
|
3
|
+
from .callback_query import CallbackQueryData, CallbackQueryNode, Field
|
|
4
|
+
from .command import CommandInfo
|
|
5
|
+
from .composer import Composition, NodeCollection, NodeSession, compose_node, compose_nodes
|
|
6
|
+
from .container import ContainerNode
|
|
7
|
+
from .event import EventNode
|
|
8
|
+
from .me import Me
|
|
9
|
+
from .message import MessageNode
|
|
10
|
+
from .polymorphic import Polymorphic, impl
|
|
11
|
+
from .rule import RuleChain
|
|
12
|
+
from .scope import GLOBAL, PER_CALL, PER_EVENT, NodeScope, global_node, per_call, per_event
|
|
13
|
+
from .source import ChatSource, Source, UserSource
|
|
14
|
+
from .text import Text, TextInteger, TextLiteral
|
|
15
|
+
from .tools import generate_node
|
|
16
|
+
from .update import UpdateNode
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
19
|
"Attachment",
|
|
20
20
|
"Audio",
|
|
21
|
+
"CallbackQueryData",
|
|
21
22
|
"CallbackQueryNode",
|
|
22
23
|
"ChatSource",
|
|
23
24
|
"CommandInfo",
|
|
@@ -26,9 +27,11 @@ __all__ = (
|
|
|
26
27
|
"ContainerNode",
|
|
27
28
|
"DataNode",
|
|
28
29
|
"EventNode",
|
|
30
|
+
"Field",
|
|
29
31
|
"GLOBAL",
|
|
30
32
|
"Me",
|
|
31
33
|
"MessageNode",
|
|
34
|
+
"Name",
|
|
32
35
|
"Node",
|
|
33
36
|
"NodeCollection",
|
|
34
37
|
"NodeScope",
|
|
@@ -42,6 +45,7 @@ __all__ = (
|
|
|
42
45
|
"Source",
|
|
43
46
|
"Text",
|
|
44
47
|
"TextInteger",
|
|
48
|
+
"TextLiteral",
|
|
45
49
|
"UpdateNode",
|
|
46
50
|
"UserSource",
|
|
47
51
|
"Video",
|
|
@@ -52,5 +56,5 @@ __all__ = (
|
|
|
52
56
|
"impl",
|
|
53
57
|
"is_node",
|
|
54
58
|
"per_call",
|
|
55
|
-
"per_event",
|
|
56
|
-
)
|
|
59
|
+
"per_event",
|
|
60
|
+
)
|
telegrinder/node/attachment.py
CHANGED
telegrinder/node/base.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import inspect
|
|
3
|
-
import typing
|
|
4
3
|
from types import AsyncGeneratorType
|
|
5
4
|
|
|
5
|
+
import typing_extensions as typing
|
|
6
|
+
|
|
6
7
|
from telegrinder.node.scope import NodeScope
|
|
7
8
|
from telegrinder.tools.magic import cache_magic_value, get_annotations
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
T = typing.TypeVar("T", default=typing.Any)
|
|
11
|
+
|
|
12
|
+
ComposeResult: typing.TypeAlias = T | typing.Awaitable[T] | typing.AsyncGenerator[T, None]
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def is_node(maybe_node: typing.Any) -> typing.TypeGuard[type["Node"]]:
|
|
@@ -77,20 +78,35 @@ class Node(abc.ABC):
|
|
|
77
78
|
return is_generator(cls.compose)
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
@typing.dataclass_transform(kw_only_default=True)
|
|
82
|
+
class FactoryNode(Node, abc.ABC):
|
|
83
|
+
node = "factory"
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
@abc.abstractmethod
|
|
87
|
+
def compose(cls, *args, **kwargs) -> ComposeResult:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
def __new__(cls, **context: typing.Any) -> typing.Self:
|
|
91
|
+
namespace = dict(**cls.__dict__)
|
|
92
|
+
namespace.pop("__new__", None)
|
|
93
|
+
return type(cls.__name__, (cls,), context | namespace) # type: ignore
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@typing.dataclass_transform()
|
|
80
97
|
class DataNode(Node, abc.ABC):
|
|
81
98
|
node = "data"
|
|
82
99
|
|
|
83
|
-
@typing.dataclass_transform()
|
|
84
100
|
@classmethod
|
|
85
101
|
@abc.abstractmethod
|
|
86
|
-
|
|
102
|
+
def compose(cls, *args, **kwargs) -> ComposeResult[typing.Self]:
|
|
87
103
|
pass
|
|
88
104
|
|
|
89
105
|
|
|
90
106
|
class ScalarNodeProto(Node, abc.ABC):
|
|
91
107
|
@classmethod
|
|
92
108
|
@abc.abstractmethod
|
|
93
|
-
|
|
109
|
+
def compose(cls, *args, **kwargs) -> ComposeResult:
|
|
94
110
|
pass
|
|
95
111
|
|
|
96
112
|
|
|
@@ -131,9 +147,16 @@ else:
|
|
|
131
147
|
pass
|
|
132
148
|
|
|
133
149
|
|
|
150
|
+
class Name(ScalarNode, str):
|
|
151
|
+
@classmethod
|
|
152
|
+
def compose(cls) -> str: ...
|
|
153
|
+
|
|
154
|
+
|
|
134
155
|
__all__ = (
|
|
135
156
|
"ComposeError",
|
|
157
|
+
"FactoryNode",
|
|
136
158
|
"DataNode",
|
|
159
|
+
"Name",
|
|
137
160
|
"Node",
|
|
138
161
|
"SCALAR_NODE",
|
|
139
162
|
"ScalarNode",
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from fntypes.result import Error, Ok
|
|
4
|
+
|
|
1
5
|
from telegrinder.bot.cute_types.callback_query import CallbackQueryCute
|
|
2
|
-
from telegrinder.
|
|
6
|
+
from telegrinder.msgspec_utils import msgspec_convert
|
|
7
|
+
from telegrinder.node.base import ComposeError, FactoryNode, Name, ScalarNode
|
|
3
8
|
from telegrinder.node.update import UpdateNode
|
|
4
9
|
|
|
10
|
+
FieldType = typing.TypeVar("FieldType")
|
|
11
|
+
|
|
5
12
|
|
|
6
13
|
class CallbackQueryNode(ScalarNode, CallbackQueryCute):
|
|
7
14
|
@classmethod
|
|
@@ -11,4 +18,36 @@ class CallbackQueryNode(ScalarNode, CallbackQueryCute):
|
|
|
11
18
|
return update.callback_query.unwrap()
|
|
12
19
|
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
class CallbackQueryData(ScalarNode, dict[str, typing.Any]):
|
|
22
|
+
@classmethod
|
|
23
|
+
def compose(cls, callback_query: CallbackQueryNode) -> dict[str, typing.Any]:
|
|
24
|
+
return callback_query.decode_callback_data().expect(
|
|
25
|
+
ComposeError("Cannot complete decode callback query data.")
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _Field(FactoryNode):
|
|
30
|
+
field_type: type[typing.Any]
|
|
31
|
+
|
|
32
|
+
def __class_getitem__(cls, field_type: type[typing.Any], /) -> typing.Self:
|
|
33
|
+
return cls(field_type=field_type)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def compose(cls, callback_query_data: CallbackQueryData, data_name: Name) -> typing.Any:
|
|
37
|
+
if data := callback_query_data.get(data_name):
|
|
38
|
+
match msgspec_convert(data, cls.field_type):
|
|
39
|
+
case Ok(value):
|
|
40
|
+
return value
|
|
41
|
+
case Error(err):
|
|
42
|
+
raise ComposeError(err)
|
|
43
|
+
|
|
44
|
+
raise ComposeError(f"Cannot find callback data with name {data_name!r}.")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if typing.TYPE_CHECKING:
|
|
48
|
+
Field = typing.Annotated[FieldType, ...]
|
|
49
|
+
else:
|
|
50
|
+
Field = _Field
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = ("CallbackQueryData", "CallbackQueryNode", "Field")
|
telegrinder/node/composer.py
CHANGED
|
@@ -11,6 +11,7 @@ from telegrinder.bot.dispatch.context import Context
|
|
|
11
11
|
from telegrinder.modules import logger
|
|
12
12
|
from telegrinder.node.base import (
|
|
13
13
|
ComposeError,
|
|
14
|
+
Name,
|
|
14
15
|
Node,
|
|
15
16
|
NodeScope,
|
|
16
17
|
get_node_calc_lst,
|
|
@@ -53,13 +54,14 @@ async def compose_nodes(
|
|
|
53
54
|
parent_nodes: dict[type[Node], NodeSession] = {}
|
|
54
55
|
event_nodes: dict[type[Node], NodeSession] = ctx.get_or_set(CONTEXT_STORE_NODES_KEY, {})
|
|
55
56
|
# TODO: optimize flattened list calculation via caching key = tuple of node types
|
|
56
|
-
calculation_nodes: dict[type[Node], tuple[type[Node], ...]] = {
|
|
57
|
-
node_t: tuple(get_node_calc_lst(node_t)) for node_t in nodes.
|
|
57
|
+
calculation_nodes: dict[tuple[str, type[Node]], tuple[type[Node], ...]] = {
|
|
58
|
+
(node_name, node_t): tuple(get_node_calc_lst(node_t)) for node_name, node_t in nodes.items()
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
for
|
|
61
|
+
for (parent_node_name, parent_node_t), linked_nodes in calculation_nodes.items():
|
|
61
62
|
local_nodes = {}
|
|
62
63
|
subnodes = {}
|
|
64
|
+
data[Name] = parent_node_name
|
|
63
65
|
|
|
64
66
|
for node_t in linked_nodes:
|
|
65
67
|
scope = getattr(node_t, "scope", None)
|
|
@@ -88,7 +90,7 @@ async def compose_nodes(
|
|
|
88
90
|
elif scope is NodeScope.GLOBAL:
|
|
89
91
|
setattr(node_t, GLOBAL_VALUE_KEY, local_nodes[node_t])
|
|
90
92
|
|
|
91
|
-
parent_nodes[
|
|
93
|
+
parent_nodes[parent_node_t] = local_nodes[parent_node_t]
|
|
92
94
|
|
|
93
95
|
node_sessions = {k: parent_nodes[t] for k, t in nodes.items()}
|
|
94
96
|
return Ok(NodeCollection(node_sessions))
|
telegrinder/node/event.py
CHANGED
|
@@ -7,7 +7,7 @@ from telegrinder.api.api import API
|
|
|
7
7
|
from telegrinder.bot.cute_types import BaseCute
|
|
8
8
|
from telegrinder.bot.dispatch.context import Context
|
|
9
9
|
from telegrinder.msgspec_utils import DataclassInstance, decoder
|
|
10
|
-
from telegrinder.node.base import ComposeError,
|
|
10
|
+
from telegrinder.node.base import ComposeError, FactoryNode
|
|
11
11
|
from telegrinder.node.update import UpdateNode
|
|
12
12
|
|
|
13
13
|
if typing.TYPE_CHECKING:
|
|
@@ -18,17 +18,11 @@ if typing.TYPE_CHECKING:
|
|
|
18
18
|
EVENT_NODE_KEY = "_event_node"
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class _EventNode(
|
|
21
|
+
class _EventNode(FactoryNode):
|
|
22
22
|
dataclass: type["DataclassType"]
|
|
23
23
|
|
|
24
|
-
def __new__(cls, dataclass: type["DataclassType"], /) -> type[typing.Self]:
|
|
25
|
-
namespace = dict(**cls.__dict__)
|
|
26
|
-
namespace.pop("__new__", None)
|
|
27
|
-
new_cls = type("EventNode", (cls,), {"dataclass": dataclass, **namespace})
|
|
28
|
-
return new_cls # type: ignore
|
|
29
|
-
|
|
30
24
|
def __class_getitem__(cls, dataclass: type["DataclassType"], /) -> typing.Self:
|
|
31
|
-
return cls(dataclass)
|
|
25
|
+
return cls(dataclass=dataclass)
|
|
32
26
|
|
|
33
27
|
@classmethod
|
|
34
28
|
def compose(cls, raw_update: UpdateNode, ctx: Context, api: API) -> "DataclassType":
|
telegrinder/node/text.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.node.base import ComposeError, FactoryNode, ScalarNode
|
|
2
4
|
from telegrinder.node.message import MessageNode
|
|
3
5
|
|
|
4
6
|
|
|
@@ -18,4 +20,22 @@ class TextInteger(ScalarNode, int):
|
|
|
18
20
|
return int(text)
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
if typing.TYPE_CHECKING:
|
|
24
|
+
from typing import Literal as TextLiteral
|
|
25
|
+
|
|
26
|
+
else:
|
|
27
|
+
|
|
28
|
+
class TextLiteral(FactoryNode):
|
|
29
|
+
texts: tuple[str, ...]
|
|
30
|
+
|
|
31
|
+
def __class_getitem__(cls, texts, /):
|
|
32
|
+
return cls(texts=(texts,) if not isinstance(texts, tuple) else texts)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def compose(cls, text: Text) -> str:
|
|
36
|
+
if text in cls.texts:
|
|
37
|
+
return text
|
|
38
|
+
raise ComposeError("Text matching failed.")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
__all__ = ("Text", "TextInteger", "TextLiteral")
|
|
@@ -279,30 +279,30 @@ def underline(string: str) -> TagFormat:
|
|
|
279
279
|
|
|
280
280
|
|
|
281
281
|
__all__ = (
|
|
282
|
-
"FormatString",
|
|
283
|
-
"HTMLFormatter",
|
|
284
|
-
"SpecialFormat",
|
|
285
|
-
"block_quote",
|
|
286
|
-
"bold",
|
|
287
|
-
"channel_boost_link",
|
|
288
|
-
"code_inline",
|
|
289
|
-
"escape",
|
|
290
|
-
"get_channel_boost_link",
|
|
291
|
-
"get_invite_chat_link",
|
|
292
|
-
"get_mention_link",
|
|
293
|
-
"get_resolve_domain_link",
|
|
294
|
-
"get_start_bot_link",
|
|
295
|
-
"get_start_group_link",
|
|
296
|
-
"invite_chat_link",
|
|
297
|
-
"italic",
|
|
298
|
-
"link",
|
|
299
|
-
"mention",
|
|
300
|
-
"pre_code",
|
|
301
|
-
"resolve_domain",
|
|
302
|
-
"spoiler",
|
|
303
|
-
"start_bot_link",
|
|
304
|
-
"start_group_link",
|
|
305
|
-
"strike",
|
|
306
|
-
"tg_emoji",
|
|
282
|
+
"FormatString",
|
|
283
|
+
"HTMLFormatter",
|
|
284
|
+
"SpecialFormat",
|
|
285
|
+
"block_quote",
|
|
286
|
+
"bold",
|
|
287
|
+
"channel_boost_link",
|
|
288
|
+
"code_inline",
|
|
289
|
+
"escape",
|
|
290
|
+
"get_channel_boost_link",
|
|
291
|
+
"get_invite_chat_link",
|
|
292
|
+
"get_mention_link",
|
|
293
|
+
"get_resolve_domain_link",
|
|
294
|
+
"get_start_bot_link",
|
|
295
|
+
"get_start_group_link",
|
|
296
|
+
"invite_chat_link",
|
|
297
|
+
"italic",
|
|
298
|
+
"link",
|
|
299
|
+
"mention",
|
|
300
|
+
"pre_code",
|
|
301
|
+
"resolve_domain",
|
|
302
|
+
"spoiler",
|
|
303
|
+
"start_bot_link",
|
|
304
|
+
"start_group_link",
|
|
305
|
+
"strike",
|
|
306
|
+
"tg_emoji",
|
|
307
307
|
"underline",
|
|
308
308
|
)
|
|
@@ -3,10 +3,10 @@ from .middleware import ABCTranslatorMiddleware
|
|
|
3
3
|
from .simple import SimpleI18n, SimpleTranslator
|
|
4
4
|
|
|
5
5
|
__all__ = (
|
|
6
|
-
"ABCI18n",
|
|
7
|
-
"ABCTranslator",
|
|
8
|
-
"ABCTranslatorMiddleware",
|
|
9
|
-
"I18nEnum",
|
|
10
|
-
"SimpleI18n",
|
|
6
|
+
"ABCI18n",
|
|
7
|
+
"ABCTranslator",
|
|
8
|
+
"ABCTranslatorMiddleware",
|
|
9
|
+
"I18nEnum",
|
|
10
|
+
"SimpleI18n",
|
|
11
11
|
"SimpleTranslator",
|
|
12
12
|
)
|
telegrinder/tools/i18n/abc.py
CHANGED
telegrinder/tools/keyboard.py
CHANGED
|
@@ -106,7 +106,7 @@ class Keyboard(ABCMarkup[Button], KeyboardModel):
|
|
|
106
106
|
return ReplyKeyboardMarkup(**self.dict())
|
|
107
107
|
|
|
108
108
|
def keyboard_remove(self, *, selective: bool = False) -> ReplyKeyboardRemove:
|
|
109
|
-
return ReplyKeyboardRemove(remove_keyboard=True, selective=
|
|
109
|
+
return ReplyKeyboardRemove(remove_keyboard=True, selective=selective)
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
class InlineKeyboard(ABCMarkup[InlineButton]):
|
|
@@ -124,9 +124,9 @@ class InlineKeyboard(ABCMarkup[InlineButton]):
|
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
__all__ = (
|
|
127
|
-
"ABCMarkup",
|
|
128
|
-
"InlineKeyboard",
|
|
129
|
-
"Keyboard",
|
|
130
|
-
"KeyboardModel",
|
|
127
|
+
"ABCMarkup",
|
|
128
|
+
"InlineKeyboard",
|
|
129
|
+
"Keyboard",
|
|
130
|
+
"KeyboardModel",
|
|
131
131
|
"copy_keyboard",
|
|
132
132
|
)
|
|
@@ -5,16 +5,17 @@ import datetime
|
|
|
5
5
|
import typing
|
|
6
6
|
|
|
7
7
|
from telegrinder.modules import logger
|
|
8
|
-
|
|
9
|
-
from .abc import ABCLoopWrapper
|
|
8
|
+
from telegrinder.tools.loop_wrapper.abc import ABCLoopWrapper
|
|
10
9
|
|
|
11
10
|
T = typing.TypeVar("T")
|
|
12
11
|
P = typing.ParamSpec("P")
|
|
13
|
-
CoroFunc = typing.TypeVar("CoroFunc", bound="CoroutineFunc")
|
|
12
|
+
CoroFunc = typing.TypeVar("CoroFunc", bound="CoroutineFunc[..., typing.Any]")
|
|
14
13
|
|
|
15
14
|
CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, T]
|
|
16
15
|
CoroutineFunc: typing.TypeAlias = typing.Callable[P, CoroutineTask[T]]
|
|
17
|
-
Task: typing.TypeAlias =
|
|
16
|
+
Task: typing.TypeAlias = (
|
|
17
|
+
"CoroutineFunc[P, T] | CoroutineTask[T] | DelayedTask[typing.Callable[P, CoroutineTask[T]]]"
|
|
18
|
+
)
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def run_tasks(
|
|
@@ -90,7 +91,12 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
90
91
|
) -> None:
|
|
91
92
|
self.tasks: list[CoroutineTask[typing.Any]] = tasks or []
|
|
92
93
|
self.lifespan = lifespan or Lifespan()
|
|
93
|
-
self._loop = event_loop
|
|
94
|
+
self._loop = event_loop
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def loop(self) -> asyncio.AbstractEventLoop:
|
|
98
|
+
assert self._loop is not None
|
|
99
|
+
return self._loop
|
|
94
100
|
|
|
95
101
|
def __repr__(self) -> str:
|
|
96
102
|
return "<{}: loop={!r} with tasks={!r}, lifespan={!r}>".format(
|
|
@@ -104,11 +110,13 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
104
110
|
if not self.tasks:
|
|
105
111
|
logger.warning("You run loop with 0 tasks!")
|
|
106
112
|
|
|
113
|
+
self._loop = asyncio.new_event_loop() if self._loop is None else self._loop
|
|
107
114
|
self.lifespan.start(self._loop)
|
|
115
|
+
|
|
108
116
|
while self.tasks:
|
|
109
117
|
self._loop.create_task(self.tasks.pop(0))
|
|
110
|
-
tasks = asyncio.all_tasks(self._loop)
|
|
111
118
|
|
|
119
|
+
tasks = asyncio.all_tasks(self._loop)
|
|
112
120
|
try:
|
|
113
121
|
while tasks:
|
|
114
122
|
tasks_results, _ = self._loop.run_until_complete(
|
|
@@ -132,17 +140,17 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
132
140
|
def add_task(self, task: Task) -> None:
|
|
133
141
|
task = to_coroutine_task(task)
|
|
134
142
|
|
|
135
|
-
if self._loop and self._loop.is_running():
|
|
143
|
+
if self._loop is not None and self._loop.is_running():
|
|
136
144
|
self._loop.create_task(task)
|
|
137
145
|
else:
|
|
138
146
|
self.tasks.append(task)
|
|
139
147
|
|
|
140
148
|
def complete_tasks(self, tasks: set[asyncio.Task[typing.Any]]) -> None:
|
|
141
|
-
tasks = tasks | asyncio.all_tasks(self.
|
|
149
|
+
tasks = tasks | asyncio.all_tasks(self.loop)
|
|
142
150
|
task_to_cancel = asyncio.gather(*tasks, return_exceptions=True)
|
|
143
151
|
task_to_cancel.cancel()
|
|
144
152
|
with contextlib.suppress(asyncio.CancelledError):
|
|
145
|
-
self.
|
|
153
|
+
self.loop.run_until_complete(task_to_cancel)
|
|
146
154
|
|
|
147
155
|
@typing.overload
|
|
148
156
|
def timer(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
|
telegrinder/tools/magic.py
CHANGED
|
@@ -153,16 +153,16 @@ def get_impls(cls: type["Polymorphic"]) -> list[typing.Callable[..., typing.Any]
|
|
|
153
153
|
|
|
154
154
|
|
|
155
155
|
__all__ = (
|
|
156
|
-
"TRANSLATIONS_KEY",
|
|
157
|
-
"cache_magic_value",
|
|
158
|
-
"cache_translation",
|
|
159
|
-
"get_annotations",
|
|
160
|
-
"get_cached_translation",
|
|
161
|
-
"get_default_args",
|
|
162
|
-
"get_default_args",
|
|
163
|
-
"get_impls",
|
|
164
|
-
"impl",
|
|
165
|
-
"magic_bundle",
|
|
166
|
-
"resolve_arg_names",
|
|
156
|
+
"TRANSLATIONS_KEY",
|
|
157
|
+
"cache_magic_value",
|
|
158
|
+
"cache_translation",
|
|
159
|
+
"get_annotations",
|
|
160
|
+
"get_cached_translation",
|
|
161
|
+
"get_default_args",
|
|
162
|
+
"get_default_args",
|
|
163
|
+
"get_impls",
|
|
164
|
+
"impl",
|
|
165
|
+
"magic_bundle",
|
|
166
|
+
"resolve_arg_names",
|
|
167
167
|
"to_str",
|
|
168
168
|
)
|