telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev159__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 +129 -22
- telegrinder/api/__init__.py +11 -2
- telegrinder/api/abc.py +25 -9
- telegrinder/api/api.py +47 -28
- telegrinder/api/error.py +14 -4
- telegrinder/api/response.py +11 -7
- telegrinder/bot/__init__.py +68 -7
- telegrinder/bot/bot.py +30 -24
- telegrinder/bot/cute_types/__init__.py +11 -1
- telegrinder/bot/cute_types/base.py +138 -0
- telegrinder/bot/cute_types/callback_query.py +458 -15
- telegrinder/bot/cute_types/inline_query.py +30 -24
- telegrinder/bot/cute_types/message.py +2982 -78
- telegrinder/bot/cute_types/update.py +30 -0
- telegrinder/bot/cute_types/utils.py +794 -0
- telegrinder/bot/dispatch/__init__.py +56 -3
- telegrinder/bot/dispatch/abc.py +9 -7
- telegrinder/bot/dispatch/composition.py +74 -0
- telegrinder/bot/dispatch/context.py +71 -0
- telegrinder/bot/dispatch/dispatch.py +86 -49
- telegrinder/bot/dispatch/handler/__init__.py +3 -0
- telegrinder/bot/dispatch/handler/abc.py +11 -5
- telegrinder/bot/dispatch/handler/func.py +41 -32
- telegrinder/bot/dispatch/handler/message_reply.py +46 -0
- telegrinder/bot/dispatch/middleware/__init__.py +2 -0
- telegrinder/bot/dispatch/middleware/abc.py +10 -4
- telegrinder/bot/dispatch/process.py +53 -49
- telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
- telegrinder/bot/dispatch/return_manager/abc.py +95 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +25 -0
- telegrinder/bot/dispatch/view/__init__.py +14 -2
- telegrinder/bot/dispatch/view/abc.py +128 -2
- telegrinder/bot/dispatch/view/box.py +38 -0
- telegrinder/bot/dispatch/view/callback_query.py +13 -39
- telegrinder/bot/dispatch/view/inline_query.py +11 -39
- telegrinder/bot/dispatch/view/message.py +11 -47
- telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
- telegrinder/bot/polling/__init__.py +2 -0
- telegrinder/bot/polling/abc.py +11 -4
- telegrinder/bot/polling/polling.py +89 -40
- telegrinder/bot/rules/__init__.py +91 -5
- telegrinder/bot/rules/abc.py +81 -63
- telegrinder/bot/rules/adapter/__init__.py +11 -0
- telegrinder/bot/rules/adapter/abc.py +21 -0
- telegrinder/bot/rules/adapter/errors.py +5 -0
- telegrinder/bot/rules/adapter/event.py +49 -0
- telegrinder/bot/rules/adapter/raw_update.py +24 -0
- telegrinder/bot/rules/callback_data.py +159 -38
- telegrinder/bot/rules/command.py +116 -0
- telegrinder/bot/rules/enum_text.py +28 -0
- telegrinder/bot/rules/func.py +17 -17
- telegrinder/bot/rules/fuzzy.py +13 -10
- telegrinder/bot/rules/inline.py +61 -0
- telegrinder/bot/rules/integer.py +12 -7
- telegrinder/bot/rules/is_from.py +148 -7
- telegrinder/bot/rules/markup.py +21 -18
- telegrinder/bot/rules/mention.py +17 -0
- telegrinder/bot/rules/message_entities.py +33 -0
- telegrinder/bot/rules/regex.py +27 -19
- telegrinder/bot/rules/rule_enum.py +74 -0
- telegrinder/bot/rules/start.py +25 -13
- telegrinder/bot/rules/text.py +23 -14
- telegrinder/bot/scenario/__init__.py +2 -0
- telegrinder/bot/scenario/abc.py +12 -5
- telegrinder/bot/scenario/checkbox.py +48 -30
- telegrinder/bot/scenario/choice.py +16 -10
- telegrinder/client/__init__.py +3 -1
- telegrinder/client/abc.py +26 -16
- telegrinder/client/aiohttp.py +54 -32
- telegrinder/model.py +119 -40
- telegrinder/modules.py +189 -21
- telegrinder/msgspec_json.py +14 -0
- telegrinder/msgspec_utils.py +227 -0
- telegrinder/node/__init__.py +31 -0
- telegrinder/node/attachment.py +71 -0
- telegrinder/node/base.py +93 -0
- telegrinder/node/composer.py +71 -0
- telegrinder/node/container.py +22 -0
- telegrinder/node/message.py +18 -0
- telegrinder/node/rule.py +56 -0
- telegrinder/node/source.py +31 -0
- telegrinder/node/text.py +13 -0
- telegrinder/node/tools/__init__.py +3 -0
- telegrinder/node/tools/generator.py +40 -0
- telegrinder/node/update.py +12 -0
- telegrinder/rules.py +1 -1
- telegrinder/tools/__init__.py +138 -4
- telegrinder/tools/buttons.py +89 -51
- telegrinder/tools/error_handler/__init__.py +8 -0
- telegrinder/tools/error_handler/abc.py +30 -0
- telegrinder/tools/error_handler/error_handler.py +156 -0
- telegrinder/tools/formatting/__init__.py +81 -3
- telegrinder/tools/formatting/html.py +283 -37
- telegrinder/tools/formatting/links.py +32 -0
- telegrinder/tools/formatting/spec_html_formats.py +121 -0
- telegrinder/tools/global_context/__init__.py +12 -0
- telegrinder/tools/global_context/abc.py +66 -0
- telegrinder/tools/global_context/global_context.py +451 -0
- telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
- telegrinder/tools/i18n/__init__.py +12 -0
- telegrinder/tools/i18n/base.py +31 -0
- telegrinder/tools/i18n/middleware/__init__.py +3 -0
- telegrinder/tools/i18n/middleware/base.py +26 -0
- telegrinder/tools/i18n/simple.py +48 -0
- telegrinder/tools/kb_set/__init__.py +2 -0
- telegrinder/tools/kb_set/base.py +3 -0
- telegrinder/tools/kb_set/yaml.py +28 -17
- telegrinder/tools/keyboard.py +84 -62
- telegrinder/tools/loop_wrapper/__init__.py +4 -0
- telegrinder/tools/loop_wrapper/abc.py +18 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
- telegrinder/tools/magic.py +48 -23
- telegrinder/tools/parse_mode.py +1 -2
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +653 -0
- telegrinder/types/methods.py +4107 -1279
- telegrinder/types/objects.py +4771 -1745
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
- telegrinder-0.1.dev159.dist-info/METADATA +109 -0
- telegrinder-0.1.dev159.dist-info/RECORD +126 -0
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
- telegrinder/bot/dispatch/waiter.py +0 -38
- telegrinder/result.py +0 -38
- telegrinder/tools/formatting/abc.py +0 -52
- telegrinder/tools/formatting/markdown.py +0 -57
- telegrinder-0.1.dev20.dist-info/METADATA +0 -22
- telegrinder-0.1.dev20.dist-info/RECORD +0 -71
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.cute_types import UpdateCute
|
|
4
|
+
from telegrinder.node import Node
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NodeSession:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
value: typing.Any,
|
|
11
|
+
subnodes: dict[str, typing.Self],
|
|
12
|
+
generator: typing.AsyncGenerator[typing.Any, None] | None = None,
|
|
13
|
+
):
|
|
14
|
+
self.value = value
|
|
15
|
+
self.subnodes = subnodes
|
|
16
|
+
self.generator = generator
|
|
17
|
+
|
|
18
|
+
async def close(self, with_value: typing.Any | None = None) -> None:
|
|
19
|
+
for subnode in self.subnodes.values():
|
|
20
|
+
await subnode.close()
|
|
21
|
+
|
|
22
|
+
if self.generator is None:
|
|
23
|
+
return
|
|
24
|
+
try:
|
|
25
|
+
await self.generator.asend(with_value)
|
|
26
|
+
except StopAsyncIteration:
|
|
27
|
+
self.generator = None
|
|
28
|
+
|
|
29
|
+
def __repr__(self) -> str:
|
|
30
|
+
return f"<NodeSession {self.value}" + ("ACTIVE>" if self.generator else ">")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NodeCollection:
|
|
34
|
+
def __init__(self, sessions: dict[str, NodeSession]) -> None:
|
|
35
|
+
self.sessions = sessions
|
|
36
|
+
|
|
37
|
+
def values(self) -> dict[str, typing.Any]:
|
|
38
|
+
return {name: session.value for name, session in self.sessions.items()}
|
|
39
|
+
|
|
40
|
+
async def close_all(self, with_value: typing.Any | None = None) -> None:
|
|
41
|
+
for session in self.sessions.values():
|
|
42
|
+
await session.close(with_value)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def compose_node(
|
|
46
|
+
node: type[Node],
|
|
47
|
+
update: UpdateCute,
|
|
48
|
+
ready_context: dict[str, NodeSession] | None = None,
|
|
49
|
+
) -> NodeSession:
|
|
50
|
+
_node = node.as_node()
|
|
51
|
+
context = NodeCollection(ready_context.copy() if ready_context else {})
|
|
52
|
+
|
|
53
|
+
for name, subnode in _node.get_sub_nodes().items():
|
|
54
|
+
if subnode is UpdateCute:
|
|
55
|
+
context.sessions[name] = NodeSession(update, {})
|
|
56
|
+
else:
|
|
57
|
+
context.sessions[name] = await compose_node(subnode, update)
|
|
58
|
+
|
|
59
|
+
generator: typing.AsyncGenerator | None
|
|
60
|
+
|
|
61
|
+
if _node.is_generator():
|
|
62
|
+
generator = typing.cast(typing.AsyncGenerator, _node.compose(**context.values()))
|
|
63
|
+
value = await generator.asend(None)
|
|
64
|
+
else:
|
|
65
|
+
generator = None
|
|
66
|
+
value = await _node.compose(**context.values()) # type: ignore
|
|
67
|
+
|
|
68
|
+
return NodeSession(value, context.sessions, generator)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
__all__ = ("NodeCollection", "NodeSession", "compose_node")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from .base import Node
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ContainerNode(Node):
|
|
7
|
+
linked_nodes: typing.ClassVar[list[type[Node]]]
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
async def compose(cls, **kw) -> tuple["Node", ...]:
|
|
11
|
+
return tuple(t[1] for t in sorted(kw.items(), key=lambda t: t[0]))
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def get_sub_nodes(cls) -> dict[str, type["Node"]]:
|
|
15
|
+
return {f"node_{i}": node_t for i, node_t in enumerate(cls.linked_nodes)}
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def link_nodes(cls, linked_nodes: list[type[Node]]) -> type["ContainerNode"]:
|
|
19
|
+
return type("_ContainerNode", (cls,), {"linked_nodes": linked_nodes})
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ("ContainerNode",)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.cute_types import MessageCute
|
|
4
|
+
|
|
5
|
+
from .base import ComposeError, ScalarNode
|
|
6
|
+
from .update import UpdateNode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MessageNode(ScalarNode, MessageCute):
|
|
10
|
+
@classmethod
|
|
11
|
+
async def compose(cls, update: UpdateNode) -> typing.Self:
|
|
12
|
+
return cls(
|
|
13
|
+
**update.message.expect(ComposeError).to_dict(),
|
|
14
|
+
api=update.api,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = ("MessageNode",)
|
telegrinder/node/rule.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.bot.dispatch.process import check_rule
|
|
6
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
7
|
+
from telegrinder.node.base import ComposeError, Node
|
|
8
|
+
from telegrinder.node.update import UpdateNode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RuleContext(dict):
|
|
12
|
+
dataclass = dict
|
|
13
|
+
rules: tuple[ABCRule, ...] = ()
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
async def compose(cls, update: UpdateNode):
|
|
17
|
+
ctx = Context()
|
|
18
|
+
for rule in cls.rules:
|
|
19
|
+
if not await check_rule(update.api, rule, update, ctx):
|
|
20
|
+
raise ComposeError
|
|
21
|
+
try:
|
|
22
|
+
return cls.dataclass(**ctx) # type: ignore
|
|
23
|
+
except Exception as exc:
|
|
24
|
+
raise ComposeError(f"Dataclass validation error: {exc}")
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def as_node(cls) -> type[typing.Self]:
|
|
28
|
+
return cls
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def get_sub_nodes(cls) -> dict:
|
|
32
|
+
return {"update": UpdateNode}
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def is_generator(cls) -> typing.Literal[False]:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
def __new__(cls, *rules: ABCRule) -> type[Node]:
|
|
39
|
+
return type("_RuleNode", (cls,), {"dataclass": dict, "rules": rules}) # type: ignore
|
|
40
|
+
|
|
41
|
+
def __class_getitem__(cls, item: tuple[ABCRule, ...]) -> typing.Self:
|
|
42
|
+
if not isinstance(item, tuple):
|
|
43
|
+
item = (item,)
|
|
44
|
+
return cls(*item)
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def generate_dataclass(cls_: type["RuleContext"]): # noqa: ANN205
|
|
48
|
+
return dataclasses.dataclass(type(cls_.__name__, (object,), dict(cls_.__dict__)))
|
|
49
|
+
|
|
50
|
+
def __init_subclass__(cls) -> None:
|
|
51
|
+
if cls.__name__ == "_RuleNode":
|
|
52
|
+
return
|
|
53
|
+
cls.dataclass = cls.generate_dataclass(cls)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
__all__ = ("RuleContext",)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.api import API
|
|
5
|
+
from telegrinder.msgspec_utils import Nothing, Option
|
|
6
|
+
from telegrinder.types import Chat, Message
|
|
7
|
+
|
|
8
|
+
from .base import DataNode
|
|
9
|
+
from .message import MessageNode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclasses.dataclass
|
|
13
|
+
class Source(DataNode):
|
|
14
|
+
api: API
|
|
15
|
+
chat: Chat
|
|
16
|
+
thread_id: Option[int] = dataclasses.field(default_factory=lambda: Nothing)
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
async def compose(cls, message: MessageNode) -> typing.Self:
|
|
20
|
+
return cls(
|
|
21
|
+
api=message.ctx_api,
|
|
22
|
+
chat=message.chat,
|
|
23
|
+
thread_id=message.message_thread_id,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
async def send(self, text: str) -> Message:
|
|
27
|
+
result = await self.api.send_message(self.chat.id, message_thread_id=self.thread_id, text=text)
|
|
28
|
+
return result.unwrap()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = ("Source",)
|
telegrinder/node/text.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from .base import ComposeError, ScalarNode
|
|
4
|
+
from .message import MessageNode
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Text(ScalarNode, str):
|
|
8
|
+
@classmethod
|
|
9
|
+
async def compose(cls, message: MessageNode) -> typing.Self:
|
|
10
|
+
return cls(message.text.expect(ComposeError("Message has no text")))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
__all__ = ("Text",)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.node.base import ComposeError, Node
|
|
5
|
+
from telegrinder.node.container import ContainerNode
|
|
6
|
+
|
|
7
|
+
T = typing.TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cast_false_to_none(value: T) -> T | None:
|
|
11
|
+
if value is False:
|
|
12
|
+
return None
|
|
13
|
+
return value
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def error_on_none(value: T | None) -> T:
|
|
17
|
+
if value is None:
|
|
18
|
+
raise ComposeError
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def generate(
|
|
23
|
+
subnodes: tuple[type[Node], ...],
|
|
24
|
+
func: typing.Callable[..., typing.Any],
|
|
25
|
+
casts: tuple[typing.Callable, ...] = (cast_false_to_none, error_on_none),
|
|
26
|
+
) -> type[ContainerNode]:
|
|
27
|
+
async def compose(**kw: typing.Any) -> typing.Any:
|
|
28
|
+
args = await ContainerNode.compose(**kw)
|
|
29
|
+
result = func(*args)
|
|
30
|
+
if inspect.isawaitable(result):
|
|
31
|
+
result = await result
|
|
32
|
+
for cast in casts:
|
|
33
|
+
result = cast(result)
|
|
34
|
+
return result
|
|
35
|
+
|
|
36
|
+
container = ContainerNode.link_nodes(list(subnodes))
|
|
37
|
+
return type("_ContainerNode", (container,), {"compose": compose})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = ("generate",)
|
telegrinder/rules.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from .bot.rules import *
|
|
1
|
+
from .bot.rules import * # noqa: F403
|
telegrinder/tools/__init__.py
CHANGED
|
@@ -1,5 +1,139 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
1
|
+
from .buttons import BaseButton
|
|
2
|
+
from .error_handler import ABCErrorHandler, Catcher, ErrorHandler
|
|
3
|
+
from .formatting import (
|
|
4
|
+
BaseSpecFormat,
|
|
5
|
+
ChannelBoostLink,
|
|
6
|
+
FormatString,
|
|
7
|
+
HTMLFormatter,
|
|
8
|
+
InviteChatLink,
|
|
9
|
+
Link,
|
|
10
|
+
Mention,
|
|
11
|
+
PreCode,
|
|
12
|
+
ResolveDomain,
|
|
13
|
+
SpecialFormat,
|
|
14
|
+
StartBotLink,
|
|
15
|
+
StartGroupLink,
|
|
16
|
+
TgEmoji,
|
|
17
|
+
block_quote,
|
|
18
|
+
bold,
|
|
19
|
+
channel_boost_link,
|
|
20
|
+
code_inline,
|
|
21
|
+
escape,
|
|
22
|
+
get_channel_boost_link,
|
|
23
|
+
get_invite_chat_link,
|
|
24
|
+
get_mention_link,
|
|
25
|
+
get_resolve_domain_link,
|
|
26
|
+
get_start_bot_link,
|
|
27
|
+
get_start_group_link,
|
|
28
|
+
invite_chat_link,
|
|
29
|
+
italic,
|
|
30
|
+
link,
|
|
31
|
+
mention,
|
|
32
|
+
pre_code,
|
|
33
|
+
resolve_domain,
|
|
34
|
+
spoiler,
|
|
35
|
+
start_bot_link,
|
|
36
|
+
start_group_link,
|
|
37
|
+
strike,
|
|
38
|
+
tg_emoji,
|
|
39
|
+
underline,
|
|
40
|
+
)
|
|
41
|
+
from .global_context import (
|
|
42
|
+
ABCGlobalContext,
|
|
43
|
+
CtxVar,
|
|
44
|
+
GlobalContext,
|
|
45
|
+
GlobalCtxVar,
|
|
46
|
+
TelegrinderCtx,
|
|
47
|
+
ctx_var,
|
|
48
|
+
)
|
|
49
|
+
from .i18n import (
|
|
50
|
+
ABCI18n,
|
|
51
|
+
ABCTranslator,
|
|
52
|
+
ABCTranslatorMiddleware,
|
|
53
|
+
I18nEnum,
|
|
54
|
+
SimpleI18n,
|
|
55
|
+
SimpleTranslator,
|
|
56
|
+
)
|
|
3
57
|
from .kb_set import KeyboardSetBase, KeyboardSetYAML
|
|
4
|
-
from .
|
|
5
|
-
|
|
58
|
+
from .keyboard import (
|
|
59
|
+
AnyMarkup,
|
|
60
|
+
Button,
|
|
61
|
+
InlineButton,
|
|
62
|
+
InlineKeyboard,
|
|
63
|
+
Keyboard,
|
|
64
|
+
RowButtons,
|
|
65
|
+
keyboard_remove,
|
|
66
|
+
)
|
|
67
|
+
from .loop_wrapper import ABCLoopWrapper, DelayedTask, LoopWrapper
|
|
68
|
+
from .magic import magic_bundle, resolve_arg_names
|
|
69
|
+
from .parse_mode import ParseMode
|
|
70
|
+
|
|
71
|
+
__all__ = (
|
|
72
|
+
"ABCErrorHandler",
|
|
73
|
+
"ABCGlobalContext",
|
|
74
|
+
"ABCI18n",
|
|
75
|
+
"ABCLoopWrapper",
|
|
76
|
+
"ABCTranslator",
|
|
77
|
+
"ABCTranslatorMiddleware",
|
|
78
|
+
"AnyMarkup",
|
|
79
|
+
"BaseButton",
|
|
80
|
+
"BaseSpecFormat",
|
|
81
|
+
"Button",
|
|
82
|
+
"Catcher",
|
|
83
|
+
"ChannelBoostLink",
|
|
84
|
+
"CtxVar",
|
|
85
|
+
"DelayedTask",
|
|
86
|
+
"ErrorHandler",
|
|
87
|
+
"FormatString",
|
|
88
|
+
"GlobalContext",
|
|
89
|
+
"GlobalCtxVar",
|
|
90
|
+
"HTMLFormatter",
|
|
91
|
+
"I18nEnum",
|
|
92
|
+
"InlineButton",
|
|
93
|
+
"InlineKeyboard",
|
|
94
|
+
"InviteChatLink",
|
|
95
|
+
"Keyboard",
|
|
96
|
+
"KeyboardSetBase",
|
|
97
|
+
"KeyboardSetYAML",
|
|
98
|
+
"Link",
|
|
99
|
+
"LoopWrapper",
|
|
100
|
+
"Mention",
|
|
101
|
+
"ParseMode",
|
|
102
|
+
"PreCode",
|
|
103
|
+
"ResolveDomain",
|
|
104
|
+
"RowButtons",
|
|
105
|
+
"SimpleI18n",
|
|
106
|
+
"SimpleTranslator",
|
|
107
|
+
"SpecialFormat",
|
|
108
|
+
"StartBotLink",
|
|
109
|
+
"StartGroupLink",
|
|
110
|
+
"TelegrinderCtx",
|
|
111
|
+
"TgEmoji",
|
|
112
|
+
"block_quote",
|
|
113
|
+
"bold",
|
|
114
|
+
"channel_boost_link",
|
|
115
|
+
"code_inline",
|
|
116
|
+
"ctx_var",
|
|
117
|
+
"escape",
|
|
118
|
+
"get_channel_boost_link",
|
|
119
|
+
"get_invite_chat_link",
|
|
120
|
+
"get_mention_link",
|
|
121
|
+
"get_resolve_domain_link",
|
|
122
|
+
"get_start_bot_link",
|
|
123
|
+
"get_start_group_link",
|
|
124
|
+
"invite_chat_link",
|
|
125
|
+
"italic",
|
|
126
|
+
"keyboard_remove",
|
|
127
|
+
"link",
|
|
128
|
+
"magic_bundle",
|
|
129
|
+
"mention",
|
|
130
|
+
"pre_code",
|
|
131
|
+
"resolve_arg_names",
|
|
132
|
+
"resolve_domain",
|
|
133
|
+
"spoiler",
|
|
134
|
+
"start_bot_link",
|
|
135
|
+
"start_group_link",
|
|
136
|
+
"strike",
|
|
137
|
+
"tg_emoji",
|
|
138
|
+
"underline",
|
|
139
|
+
)
|
telegrinder/tools/buttons.py
CHANGED
|
@@ -1,51 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
self.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import msgspec
|
|
5
|
+
|
|
6
|
+
from telegrinder.model import encoder
|
|
7
|
+
from telegrinder.types import (
|
|
8
|
+
CallbackGame,
|
|
9
|
+
KeyboardButtonPollType,
|
|
10
|
+
KeyboardButtonRequestChat,
|
|
11
|
+
KeyboardButtonRequestUsers,
|
|
12
|
+
SwitchInlineQueryChosenChat,
|
|
13
|
+
WebAppInfo,
|
|
14
|
+
)
|
|
15
|
+
from telegrinder.types.objects import LoginUrl
|
|
16
|
+
|
|
17
|
+
ButtonT = typing.TypeVar("ButtonT", bound="BaseButton")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@typing.runtime_checkable
|
|
21
|
+
class DataclassInstance(typing.Protocol):
|
|
22
|
+
__dataclass_fields__: typing.ClassVar[dict[str, dataclasses.Field[typing.Any]]]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclasses.dataclass
|
|
26
|
+
class BaseButton:
|
|
27
|
+
def get_data(self) -> dict[str, typing.Any]:
|
|
28
|
+
return {
|
|
29
|
+
k: v
|
|
30
|
+
if k != "callback_data" or isinstance(v, str)
|
|
31
|
+
else encoder.encode(v)
|
|
32
|
+
for k, v in dataclasses.asdict(self).items()
|
|
33
|
+
if v is not None
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RowButtons(typing.Generic[ButtonT]):
|
|
38
|
+
buttons: list[ButtonT]
|
|
39
|
+
auto_row: bool
|
|
40
|
+
|
|
41
|
+
def __init__(self, *buttons: ButtonT, auto_row: bool = True) -> None:
|
|
42
|
+
self.buttons = list(buttons)
|
|
43
|
+
self.auto_row = auto_row
|
|
44
|
+
|
|
45
|
+
def get_data(self) -> list[dict[str, typing.Any]]:
|
|
46
|
+
return [b.get_data() for b in self.buttons]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclasses.dataclass
|
|
50
|
+
class Button(BaseButton):
|
|
51
|
+
text: str
|
|
52
|
+
_: dataclasses.KW_ONLY
|
|
53
|
+
request_contact: bool = False
|
|
54
|
+
request_location: bool = False
|
|
55
|
+
request_chat: dict[str, typing.Any] | KeyboardButtonRequestChat | None = None
|
|
56
|
+
request_user: dict[str, typing.Any] | KeyboardButtonRequestUsers | None = None
|
|
57
|
+
request_poll: dict[str, typing.Any] | KeyboardButtonPollType | None = None
|
|
58
|
+
web_app: dict[str, typing.Any] | WebAppInfo | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclasses.dataclass
|
|
62
|
+
class InlineButton(BaseButton):
|
|
63
|
+
text: str
|
|
64
|
+
_: dataclasses.KW_ONLY
|
|
65
|
+
url: str | None = None
|
|
66
|
+
login_url: dict[str, typing.Any] | LoginUrl | None = None
|
|
67
|
+
pay: bool | None = None
|
|
68
|
+
callback_data: typing.Union[
|
|
69
|
+
str,
|
|
70
|
+
dict[str, typing.Any],
|
|
71
|
+
DataclassInstance,
|
|
72
|
+
msgspec.Struct,
|
|
73
|
+
] | None = None
|
|
74
|
+
callback_game: dict[str, typing.Any] | CallbackGame | None = None
|
|
75
|
+
switch_inline_query: str | None = None
|
|
76
|
+
switch_inline_query_current_chat: str | None = None
|
|
77
|
+
switch_inline_query_chosen_chat: dict[
|
|
78
|
+
str, typing.Any
|
|
79
|
+
] | SwitchInlineQueryChosenChat | None = None
|
|
80
|
+
web_app: dict[str, typing.Any] | WebAppInfo | None = None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = (
|
|
84
|
+
"BaseButton",
|
|
85
|
+
"Button",
|
|
86
|
+
"DataclassInstance",
|
|
87
|
+
"InlineButton",
|
|
88
|
+
"RowButtons",
|
|
89
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
from fntypes.result import Result
|
|
5
|
+
|
|
6
|
+
from telegrinder.api import ABCAPI
|
|
7
|
+
from telegrinder.bot.cute_types import BaseCute
|
|
8
|
+
from telegrinder.bot.dispatch.context import Context
|
|
9
|
+
|
|
10
|
+
EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
11
|
+
Handler = typing.Callable[typing.Concatenate[EventT, ...], typing.Awaitable[typing.Any]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ABCErrorHandler(ABC, typing.Generic[EventT]):
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def catch(self) -> typing.Callable[[typing.Callable], typing.Callable]:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
async def run(
|
|
21
|
+
self,
|
|
22
|
+
handler: Handler[EventT],
|
|
23
|
+
event: EventT,
|
|
24
|
+
api: ABCAPI,
|
|
25
|
+
ctx: Context,
|
|
26
|
+
) -> Result[typing.Any, typing.Any]:
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ("ABCErrorHandler",)
|