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.

Files changed (132) hide show
  1. telegrinder/__init__.py +129 -22
  2. telegrinder/api/__init__.py +11 -2
  3. telegrinder/api/abc.py +25 -9
  4. telegrinder/api/api.py +47 -28
  5. telegrinder/api/error.py +14 -4
  6. telegrinder/api/response.py +11 -7
  7. telegrinder/bot/__init__.py +68 -7
  8. telegrinder/bot/bot.py +30 -24
  9. telegrinder/bot/cute_types/__init__.py +11 -1
  10. telegrinder/bot/cute_types/base.py +138 -0
  11. telegrinder/bot/cute_types/callback_query.py +458 -15
  12. telegrinder/bot/cute_types/inline_query.py +30 -24
  13. telegrinder/bot/cute_types/message.py +2982 -78
  14. telegrinder/bot/cute_types/update.py +30 -0
  15. telegrinder/bot/cute_types/utils.py +794 -0
  16. telegrinder/bot/dispatch/__init__.py +56 -3
  17. telegrinder/bot/dispatch/abc.py +9 -7
  18. telegrinder/bot/dispatch/composition.py +74 -0
  19. telegrinder/bot/dispatch/context.py +71 -0
  20. telegrinder/bot/dispatch/dispatch.py +86 -49
  21. telegrinder/bot/dispatch/handler/__init__.py +3 -0
  22. telegrinder/bot/dispatch/handler/abc.py +11 -5
  23. telegrinder/bot/dispatch/handler/func.py +41 -32
  24. telegrinder/bot/dispatch/handler/message_reply.py +46 -0
  25. telegrinder/bot/dispatch/middleware/__init__.py +2 -0
  26. telegrinder/bot/dispatch/middleware/abc.py +10 -4
  27. telegrinder/bot/dispatch/process.py +53 -49
  28. telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
  29. telegrinder/bot/dispatch/return_manager/abc.py +95 -0
  30. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  31. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  32. telegrinder/bot/dispatch/return_manager/message.py +25 -0
  33. telegrinder/bot/dispatch/view/__init__.py +14 -2
  34. telegrinder/bot/dispatch/view/abc.py +128 -2
  35. telegrinder/bot/dispatch/view/box.py +38 -0
  36. telegrinder/bot/dispatch/view/callback_query.py +13 -39
  37. telegrinder/bot/dispatch/view/inline_query.py +11 -39
  38. telegrinder/bot/dispatch/view/message.py +11 -47
  39. telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
  40. telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
  41. telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
  42. telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
  43. telegrinder/bot/polling/__init__.py +2 -0
  44. telegrinder/bot/polling/abc.py +11 -4
  45. telegrinder/bot/polling/polling.py +89 -40
  46. telegrinder/bot/rules/__init__.py +91 -5
  47. telegrinder/bot/rules/abc.py +81 -63
  48. telegrinder/bot/rules/adapter/__init__.py +11 -0
  49. telegrinder/bot/rules/adapter/abc.py +21 -0
  50. telegrinder/bot/rules/adapter/errors.py +5 -0
  51. telegrinder/bot/rules/adapter/event.py +49 -0
  52. telegrinder/bot/rules/adapter/raw_update.py +24 -0
  53. telegrinder/bot/rules/callback_data.py +159 -38
  54. telegrinder/bot/rules/command.py +116 -0
  55. telegrinder/bot/rules/enum_text.py +28 -0
  56. telegrinder/bot/rules/func.py +17 -17
  57. telegrinder/bot/rules/fuzzy.py +13 -10
  58. telegrinder/bot/rules/inline.py +61 -0
  59. telegrinder/bot/rules/integer.py +12 -7
  60. telegrinder/bot/rules/is_from.py +148 -7
  61. telegrinder/bot/rules/markup.py +21 -18
  62. telegrinder/bot/rules/mention.py +17 -0
  63. telegrinder/bot/rules/message_entities.py +33 -0
  64. telegrinder/bot/rules/regex.py +27 -19
  65. telegrinder/bot/rules/rule_enum.py +74 -0
  66. telegrinder/bot/rules/start.py +25 -13
  67. telegrinder/bot/rules/text.py +23 -14
  68. telegrinder/bot/scenario/__init__.py +2 -0
  69. telegrinder/bot/scenario/abc.py +12 -5
  70. telegrinder/bot/scenario/checkbox.py +48 -30
  71. telegrinder/bot/scenario/choice.py +16 -10
  72. telegrinder/client/__init__.py +3 -1
  73. telegrinder/client/abc.py +26 -16
  74. telegrinder/client/aiohttp.py +54 -32
  75. telegrinder/model.py +119 -40
  76. telegrinder/modules.py +189 -21
  77. telegrinder/msgspec_json.py +14 -0
  78. telegrinder/msgspec_utils.py +227 -0
  79. telegrinder/node/__init__.py +31 -0
  80. telegrinder/node/attachment.py +71 -0
  81. telegrinder/node/base.py +93 -0
  82. telegrinder/node/composer.py +71 -0
  83. telegrinder/node/container.py +22 -0
  84. telegrinder/node/message.py +18 -0
  85. telegrinder/node/rule.py +56 -0
  86. telegrinder/node/source.py +31 -0
  87. telegrinder/node/text.py +13 -0
  88. telegrinder/node/tools/__init__.py +3 -0
  89. telegrinder/node/tools/generator.py +40 -0
  90. telegrinder/node/update.py +12 -0
  91. telegrinder/rules.py +1 -1
  92. telegrinder/tools/__init__.py +138 -4
  93. telegrinder/tools/buttons.py +89 -51
  94. telegrinder/tools/error_handler/__init__.py +8 -0
  95. telegrinder/tools/error_handler/abc.py +30 -0
  96. telegrinder/tools/error_handler/error_handler.py +156 -0
  97. telegrinder/tools/formatting/__init__.py +81 -3
  98. telegrinder/tools/formatting/html.py +283 -37
  99. telegrinder/tools/formatting/links.py +32 -0
  100. telegrinder/tools/formatting/spec_html_formats.py +121 -0
  101. telegrinder/tools/global_context/__init__.py +12 -0
  102. telegrinder/tools/global_context/abc.py +66 -0
  103. telegrinder/tools/global_context/global_context.py +451 -0
  104. telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
  105. telegrinder/tools/i18n/__init__.py +12 -0
  106. telegrinder/tools/i18n/base.py +31 -0
  107. telegrinder/tools/i18n/middleware/__init__.py +3 -0
  108. telegrinder/tools/i18n/middleware/base.py +26 -0
  109. telegrinder/tools/i18n/simple.py +48 -0
  110. telegrinder/tools/kb_set/__init__.py +2 -0
  111. telegrinder/tools/kb_set/base.py +3 -0
  112. telegrinder/tools/kb_set/yaml.py +28 -17
  113. telegrinder/tools/keyboard.py +84 -62
  114. telegrinder/tools/loop_wrapper/__init__.py +4 -0
  115. telegrinder/tools/loop_wrapper/abc.py +18 -0
  116. telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
  117. telegrinder/tools/magic.py +48 -23
  118. telegrinder/tools/parse_mode.py +1 -2
  119. telegrinder/types/__init__.py +1 -0
  120. telegrinder/types/enums.py +653 -0
  121. telegrinder/types/methods.py +4107 -1279
  122. telegrinder/types/objects.py +4771 -1745
  123. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
  124. telegrinder-0.1.dev159.dist-info/METADATA +109 -0
  125. telegrinder-0.1.dev159.dist-info/RECORD +126 -0
  126. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
  127. telegrinder/bot/dispatch/waiter.py +0 -38
  128. telegrinder/result.py +0 -38
  129. telegrinder/tools/formatting/abc.py +0 -52
  130. telegrinder/tools/formatting/markdown.py +0 -57
  131. telegrinder-0.1.dev20.dist-info/METADATA +0 -22
  132. 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",)
@@ -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",)
@@ -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,3 @@
1
+ from .generator import generate
2
+
3
+ __all__ = ("generate",)
@@ -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",)
@@ -0,0 +1,12 @@
1
+ from telegrinder.bot.cute_types import UpdateCute
2
+
3
+ from .base import ScalarNode
4
+
5
+
6
+ class UpdateNode(ScalarNode, UpdateCute):
7
+ @classmethod
8
+ async def compose(cls, update: UpdateCute) -> UpdateCute:
9
+ return update
10
+
11
+
12
+ __all__ = ("UpdateNode",)
telegrinder/rules.py CHANGED
@@ -1 +1 @@
1
- from .bot.rules import *
1
+ from .bot.rules import * # noqa: F403
@@ -1,5 +1,139 @@
1
- from .keyboard import Keyboard, Button, InlineButton, InlineKeyboard, AnyMarkup
2
- from .magic import VarUnset, resolve_arg_names, magic_bundle
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 .parse_mode import ParseMode, get_mention_link
5
- from .formatting import ABCFormatter, HTMLFormatter, MarkdownFormatter
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
+ )
@@ -1,51 +1,89 @@
1
- from typing import Optional
2
- from abc import ABC, abstractmethod
3
-
4
-
5
- class ABCButton(ABC):
6
- @abstractmethod
7
- def __init__(self, **kwargs):
8
- pass
9
-
10
- def get_data(self) -> dict:
11
- return {k: v for k, v in self.__dict__.items() if v is not None}
12
-
13
-
14
- class Button(ABCButton):
15
- def __init__(
16
- self,
17
- text: str,
18
- request_contact: Optional[bool] = False,
19
- request_location: Optional[bool] = False,
20
- request_poll: Optional[dict] = None,
21
- web_app: Optional[dict] = None,
22
- ):
23
- self.text = text
24
- self.request_contact = request_contact
25
- self.request_location = request_location
26
- self.request_poll = request_poll
27
- self.web_app = web_app
28
-
29
-
30
- class InlineButton(ABCButton):
31
- def __init__(
32
- self,
33
- text: str,
34
- url: str = None,
35
- login_url: dict = None,
36
- pay: bool = None,
37
- callback_data: str = None,
38
- callback_game: dict = None,
39
- switch_inline_query: str = None,
40
- switch_inline_query_current_chat: str = None,
41
- web_app: Optional[dict] = None,
42
- ):
43
- self.text = text
44
- self.url = url
45
- self.login_url = login_url
46
- self.pay = pay
47
- self.callback_data = callback_data
48
- self.callback_game = callback_game
49
- self.switch_inline_query = switch_inline_query
50
- self.switch_inline_query_current_chat = switch_inline_query_current_chat
51
- self.web_app = web_app
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,8 @@
1
+ from .abc import ABCErrorHandler
2
+ from .error_handler import Catcher, ErrorHandler
3
+
4
+ __all__ = (
5
+ "ABCErrorHandler",
6
+ "Catcher",
7
+ "ErrorHandler",
8
+ )
@@ -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",)