telegrinder 0.3.3.post1__py3-none-any.whl → 0.3.4.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.

Files changed (165) hide show
  1. telegrinder/__init__.py +144 -144
  2. telegrinder/api/__init__.py +8 -8
  3. telegrinder/api/api.py +93 -93
  4. telegrinder/api/error.py +16 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +66 -66
  8. telegrinder/bot/bot.py +76 -76
  9. telegrinder/bot/cute_types/__init__.py +17 -17
  10. telegrinder/bot/cute_types/base.py +258 -258
  11. telegrinder/bot/cute_types/callback_query.py +385 -385
  12. telegrinder/bot/cute_types/chat_join_request.py +61 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +160 -160
  14. telegrinder/bot/cute_types/inline_query.py +43 -43
  15. telegrinder/bot/cute_types/message.py +2637 -2637
  16. telegrinder/bot/cute_types/update.py +104 -109
  17. telegrinder/bot/cute_types/utils.py +95 -95
  18. telegrinder/bot/dispatch/__init__.py +55 -55
  19. telegrinder/bot/dispatch/abc.py +77 -77
  20. telegrinder/bot/dispatch/context.py +98 -98
  21. telegrinder/bot/dispatch/dispatch.py +202 -202
  22. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  23. telegrinder/bot/dispatch/handler/abc.py +24 -24
  24. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  25. telegrinder/bot/dispatch/handler/base.py +57 -57
  26. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  27. telegrinder/bot/dispatch/handler/func.py +135 -135
  28. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -43
  29. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  30. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  31. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  32. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  33. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  34. telegrinder/bot/dispatch/middleware/abc.py +22 -16
  35. telegrinder/bot/dispatch/process.py +157 -132
  36. telegrinder/bot/dispatch/return_manager/__init__.py +13 -13
  37. telegrinder/bot/dispatch/return_manager/abc.py +108 -108
  38. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  39. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  40. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  41. telegrinder/bot/dispatch/view/__init__.py +13 -13
  42. telegrinder/bot/dispatch/view/abc.py +41 -41
  43. telegrinder/bot/dispatch/view/base.py +200 -200
  44. telegrinder/bot/dispatch/view/box.py +129 -129
  45. telegrinder/bot/dispatch/view/callback_query.py +17 -17
  46. telegrinder/bot/dispatch/view/chat_join_request.py +16 -16
  47. telegrinder/bot/dispatch/view/chat_member.py +39 -39
  48. telegrinder/bot/dispatch/view/inline_query.py +17 -17
  49. telegrinder/bot/dispatch/view/message.py +44 -44
  50. telegrinder/bot/dispatch/view/raw.py +114 -114
  51. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  52. telegrinder/bot/dispatch/waiter_machine/actions.py +13 -13
  53. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  54. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
  55. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
  56. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
  57. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
  58. telegrinder/bot/dispatch/waiter_machine/machine.py +172 -167
  59. telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
  60. telegrinder/bot/dispatch/waiter_machine/short_state.py +68 -68
  61. telegrinder/bot/polling/__init__.py +4 -4
  62. telegrinder/bot/polling/abc.py +25 -25
  63. telegrinder/bot/polling/polling.py +131 -131
  64. telegrinder/bot/rules/__init__.py +62 -62
  65. telegrinder/bot/rules/abc.py +213 -213
  66. telegrinder/bot/rules/adapter/__init__.py +12 -9
  67. telegrinder/bot/rules/adapter/abc.py +31 -29
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +65 -67
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_event.py +27 -0
  72. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  73. telegrinder/bot/rules/callback_data.py +170 -170
  74. telegrinder/bot/rules/chat_join.py +46 -46
  75. telegrinder/bot/rules/command.py +126 -126
  76. telegrinder/bot/rules/enum_text.py +36 -36
  77. telegrinder/bot/rules/func.py +26 -26
  78. telegrinder/bot/rules/fuzzy.py +24 -24
  79. telegrinder/bot/rules/inline.py +60 -60
  80. telegrinder/bot/rules/integer.py +20 -20
  81. telegrinder/bot/rules/is_from.py +127 -127
  82. telegrinder/bot/rules/markup.py +43 -43
  83. telegrinder/bot/rules/mention.py +14 -14
  84. telegrinder/bot/rules/message.py +17 -17
  85. telegrinder/bot/rules/message_entities.py +35 -35
  86. telegrinder/bot/rules/node.py +27 -27
  87. telegrinder/bot/rules/regex.py +37 -37
  88. telegrinder/bot/rules/rule_enum.py +72 -72
  89. telegrinder/bot/rules/start.py +42 -42
  90. telegrinder/bot/rules/state.py +37 -37
  91. telegrinder/bot/rules/text.py +33 -33
  92. telegrinder/bot/rules/update.py +15 -15
  93. telegrinder/bot/scenario/__init__.py +5 -5
  94. telegrinder/bot/scenario/abc.py +19 -19
  95. telegrinder/bot/scenario/checkbox.py +176 -167
  96. telegrinder/bot/scenario/choice.py +51 -46
  97. telegrinder/client/__init__.py +4 -4
  98. telegrinder/client/abc.py +75 -75
  99. telegrinder/client/aiohttp.py +130 -130
  100. telegrinder/model.py +320 -295
  101. telegrinder/modules.py +237 -237
  102. telegrinder/msgspec_json.py +14 -14
  103. telegrinder/msgspec_utils.py +410 -410
  104. telegrinder/node/__init__.py +0 -0
  105. telegrinder/node/attachment.py +87 -87
  106. telegrinder/node/base.py +166 -166
  107. telegrinder/node/callback_query.py +53 -53
  108. telegrinder/node/command.py +33 -33
  109. telegrinder/node/composer.py +198 -198
  110. telegrinder/node/container.py +27 -27
  111. telegrinder/node/event.py +65 -65
  112. telegrinder/node/me.py +16 -16
  113. telegrinder/node/message.py +14 -14
  114. telegrinder/node/polymorphic.py +48 -48
  115. telegrinder/node/rule.py +76 -76
  116. telegrinder/node/scope.py +38 -38
  117. telegrinder/node/source.py +71 -71
  118. telegrinder/node/text.py +41 -41
  119. telegrinder/node/tools/__init__.py +3 -3
  120. telegrinder/node/tools/generator.py +40 -40
  121. telegrinder/node/update.py +15 -15
  122. telegrinder/rules.py +0 -0
  123. telegrinder/tools/__init__.py +74 -74
  124. telegrinder/tools/buttons.py +79 -79
  125. telegrinder/tools/error_handler/__init__.py +7 -7
  126. telegrinder/tools/error_handler/abc.py +33 -33
  127. telegrinder/tools/error_handler/error.py +9 -9
  128. telegrinder/tools/error_handler/error_handler.py +193 -193
  129. telegrinder/tools/formatting/__init__.py +46 -46
  130. telegrinder/tools/formatting/html.py +283 -283
  131. telegrinder/tools/formatting/links.py +33 -33
  132. telegrinder/tools/formatting/spec_html_formats.py +111 -111
  133. telegrinder/tools/functional.py +12 -12
  134. telegrinder/tools/global_context/__init__.py +7 -7
  135. telegrinder/tools/global_context/abc.py +63 -63
  136. telegrinder/tools/global_context/global_context.py +412 -412
  137. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  138. telegrinder/tools/i18n/__init__.py +7 -7
  139. telegrinder/tools/i18n/abc.py +30 -30
  140. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  141. telegrinder/tools/i18n/middleware/abc.py +25 -25
  142. telegrinder/tools/i18n/simple.py +43 -43
  143. telegrinder/tools/kb_set/__init__.py +4 -4
  144. telegrinder/tools/kb_set/base.py +15 -15
  145. telegrinder/tools/kb_set/yaml.py +63 -63
  146. telegrinder/tools/keyboard.py +132 -132
  147. telegrinder/tools/limited_dict.py +37 -37
  148. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  149. telegrinder/tools/loop_wrapper/abc.py +15 -15
  150. telegrinder/tools/loop_wrapper/loop_wrapper.py +224 -224
  151. telegrinder/tools/magic.py +157 -157
  152. telegrinder/tools/parse_mode.py +6 -6
  153. telegrinder/tools/state_storage/__init__.py +4 -4
  154. telegrinder/tools/state_storage/abc.py +35 -35
  155. telegrinder/tools/state_storage/memory.py +25 -25
  156. telegrinder/types/__init__.py +260 -260
  157. telegrinder/types/enums.py +701 -701
  158. telegrinder/types/methods.py +4633 -4633
  159. telegrinder/types/objects.py +6950 -8561
  160. telegrinder/verification_utils.py +32 -32
  161. {telegrinder-0.3.3.post1.dist-info → telegrinder-0.3.4.post1.dist-info}/LICENSE +22 -22
  162. {telegrinder-0.3.3.post1.dist-info → telegrinder-0.3.4.post1.dist-info}/METADATA +1 -1
  163. telegrinder-0.3.4.post1.dist-info/RECORD +165 -0
  164. telegrinder-0.3.3.post1.dist-info/RECORD +0 -164
  165. {telegrinder-0.3.3.post1.dist-info → telegrinder-0.3.4.post1.dist-info}/WHEEL +0 -0
telegrinder/node/me.py CHANGED
@@ -1,16 +1,16 @@
1
- from telegrinder.api.api import API
2
- from telegrinder.node.base import ComposeError, ScalarNode
3
- from telegrinder.node.scope import GLOBAL
4
- from telegrinder.types.objects import User
5
-
6
-
7
- class Me(ScalarNode, User):
8
- scope = GLOBAL
9
-
10
- @classmethod
11
- async def compose(cls, api: API) -> User:
12
- me = await api.get_me()
13
- return me.expect(ComposeError("Can't complete get_me request"))
14
-
15
-
16
- __all__ = ("Me",)
1
+ from telegrinder.api.api import API
2
+ from telegrinder.node.base import ComposeError, ScalarNode
3
+ from telegrinder.node.scope import GLOBAL
4
+ from telegrinder.types.objects import User
5
+
6
+
7
+ class Me(ScalarNode, User):
8
+ scope = GLOBAL
9
+
10
+ @classmethod
11
+ async def compose(cls, api: API) -> User:
12
+ me = await api.get_me()
13
+ return me.expect(ComposeError("Can't complete get_me request"))
14
+
15
+
16
+ __all__ = ("Me",)
@@ -1,14 +1,14 @@
1
- from telegrinder.bot.cute_types.message import MessageCute
2
- from telegrinder.node.base import ComposeError, ScalarNode
3
- from telegrinder.node.update import UpdateNode
4
-
5
-
6
- class MessageNode(ScalarNode, MessageCute):
7
- @classmethod
8
- def compose(cls, update: UpdateNode) -> MessageCute:
9
- if not update.message:
10
- raise ComposeError("Update is not a message.")
11
- return update.message.unwrap()
12
-
13
-
14
- __all__ = ("MessageNode",)
1
+ from telegrinder.bot.cute_types.message import MessageCute
2
+ from telegrinder.node.base import ComposeError, ScalarNode
3
+ from telegrinder.node.update import UpdateNode
4
+
5
+
6
+ class MessageNode(ScalarNode, MessageCute):
7
+ @classmethod
8
+ def compose(cls, update: UpdateNode) -> MessageCute:
9
+ if not update.message:
10
+ raise ComposeError("Update is not a message.")
11
+ return update.message.unwrap()
12
+
13
+
14
+ __all__ = ("MessageNode",)
@@ -1,48 +1,48 @@
1
- import typing
2
-
3
- from telegrinder.bot.dispatch.context import Context
4
- from telegrinder.modules import logger
5
- from telegrinder.node.base import ComposeError, Node
6
- from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, Composition, NodeSession
7
- from telegrinder.node.scope import NodeScope
8
- from telegrinder.node.update import UpdateNode
9
- from telegrinder.tools.magic import get_impls, impl
10
-
11
-
12
- class Polymorphic(Node):
13
- @classmethod
14
- async def compose(cls, update: UpdateNode, context: Context) -> typing.Any:
15
- logger.debug(f"Composing polymorphic node {cls.__name__!r}...")
16
- scope = getattr(cls, "scope", None)
17
- node_ctx = context.get_or_set(CONTEXT_STORE_NODES_KEY, {})
18
-
19
- for i, impl_ in enumerate(get_impls(cls)):
20
- logger.debug("Checking impl {!r}...", impl_.__name__)
21
- composition = Composition(impl_, True)
22
- node_collection = await composition.compose_nodes(update, context)
23
- if node_collection is None:
24
- logger.debug("Impl {!r} composition failed!", impl_.__name__)
25
- continue
26
-
27
- # To determine whether this is a right morph, all subnodes must be resolved
28
- if scope is NodeScope.PER_EVENT and (cls, i) in node_ctx:
29
- logger.debug(
30
- "Morph is already cached as per_event node, using its value. Impl {!r} succeeded!",
31
- impl_.__name__,
32
- )
33
- res: NodeSession = node_ctx[(cls, i)]
34
- await node_collection.close_all()
35
- return res.value
36
-
37
- result = await composition(cls, **node_collection.values)
38
- if scope is NodeScope.PER_EVENT:
39
- node_ctx[(cls, i)] = NodeSession(cls, result, {})
40
-
41
- await node_collection.close_all(with_value=result)
42
- logger.debug("Impl {!r} succeeded with value: {!r}", impl_.__name__, result)
43
- return result
44
-
45
- raise ComposeError("No implementation found.")
46
-
47
-
48
- __all__ = ("Polymorphic", "impl")
1
+ import typing
2
+
3
+ from telegrinder.bot.dispatch.context import Context
4
+ from telegrinder.modules import logger
5
+ from telegrinder.node.base import ComposeError, Node
6
+ from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, Composition, NodeSession
7
+ from telegrinder.node.scope import NodeScope
8
+ from telegrinder.node.update import UpdateNode
9
+ from telegrinder.tools.magic import get_impls, impl
10
+
11
+
12
+ class Polymorphic(Node):
13
+ @classmethod
14
+ async def compose(cls, update: UpdateNode, context: Context) -> typing.Any:
15
+ logger.debug(f"Composing polymorphic node {cls.__name__!r}...")
16
+ scope = getattr(cls, "scope", None)
17
+ node_ctx = context.get_or_set(CONTEXT_STORE_NODES_KEY, {})
18
+
19
+ for i, impl_ in enumerate(get_impls(cls)):
20
+ logger.debug("Checking impl {!r}...", impl_.__name__)
21
+ composition = Composition(impl_, True)
22
+ node_collection = await composition.compose_nodes(update, context)
23
+ if node_collection is None:
24
+ logger.debug("Impl {!r} composition failed!", impl_.__name__)
25
+ continue
26
+
27
+ # To determine whether this is a right morph, all subnodes must be resolved
28
+ if scope is NodeScope.PER_EVENT and (cls, i) in node_ctx:
29
+ logger.debug(
30
+ "Morph is already cached as per_event node, using its value. Impl {!r} succeeded!",
31
+ impl_.__name__,
32
+ )
33
+ res: NodeSession = node_ctx[(cls, i)]
34
+ await node_collection.close_all()
35
+ return res.value
36
+
37
+ result = await composition(cls, **node_collection.values)
38
+ if scope is NodeScope.PER_EVENT:
39
+ node_ctx[(cls, i)] = NodeSession(cls, result, {})
40
+
41
+ await node_collection.close_all(with_value=result)
42
+ logger.debug("Impl {!r} succeeded with value: {!r}", impl_.__name__, result)
43
+ return result
44
+
45
+ raise ComposeError("No implementation found.")
46
+
47
+
48
+ __all__ = ("Polymorphic", "impl")
telegrinder/node/rule.py CHANGED
@@ -1,76 +1,76 @@
1
- import dataclasses
2
- import importlib
3
- import typing
4
-
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.node.base import ComposeError, Node
7
- from telegrinder.node.update import UpdateNode
8
-
9
- if typing.TYPE_CHECKING:
10
- from telegrinder.bot.dispatch.process import check_rule
11
- from telegrinder.bot.rules.abc import ABCRule
12
-
13
-
14
- class RuleChain(dict[str, typing.Any], Node):
15
- dataclass: type[typing.Any] = dict
16
- rules: tuple["ABCRule", ...] = ()
17
-
18
- def __init_subclass__(cls, *args: typing.Any, **kwargs: typing.Any) -> None:
19
- super().__init_subclass__(*args, **kwargs)
20
-
21
- if cls.__name__ == "_RuleNode":
22
- return
23
- cls.dataclass = cls.generate_node_dataclass(cls)
24
-
25
- def __new__(cls, *rules: "ABCRule") -> type[Node]:
26
- return type("_RuleNode", (cls,), {"dataclass": dict, "rules": rules}) # type: ignore
27
-
28
- def __class_getitem__(cls, items: "ABCRule | tuple[ABCRule, ...]", /) -> typing.Self:
29
- if not isinstance(items, tuple):
30
- items = (items,)
31
- return cls(*items)
32
-
33
- @staticmethod
34
- def generate_node_dataclass(cls_: type["RuleChain"]): # noqa: ANN205
35
- return dataclasses.dataclass(type(cls_.__name__, (object,), dict(cls_.__dict__)))
36
-
37
- @classmethod
38
- async def compose(cls, update: UpdateNode) -> typing.Any:
39
- # Hack to avoid circular import
40
- globalns = globals()
41
- if "check_rule" not in globalns:
42
- globalns.update(
43
- {
44
- "check_rule": getattr(
45
- importlib.import_module("telegrinder.bot.dispatch.process"),
46
- "check_rule",
47
- ),
48
- },
49
- )
50
-
51
- ctx = Context()
52
- for rule in cls.rules:
53
- if not await check_rule(update.api, rule, update, ctx):
54
- raise ComposeError(f"Rule {rule!r} failed!")
55
-
56
- try:
57
- if dataclasses.is_dataclass(cls.dataclass):
58
- return cls.dataclass(**{k: ctx[k] for k in cls.__annotations__})
59
- return cls.dataclass(**ctx)
60
- except Exception as exc:
61
- raise ComposeError(f"Dataclass validation error: {exc}")
62
-
63
- @classmethod
64
- def as_node(cls) -> type[typing.Self]:
65
- return cls
66
-
67
- @classmethod
68
- def get_subnodes(cls) -> dict[typing.Literal["update"], type[UpdateNode]]:
69
- return {"update": UpdateNode}
70
-
71
- @classmethod
72
- def is_generator(cls) -> typing.Literal[False]:
73
- return False
74
-
75
-
76
- __all__ = ("RuleChain",)
1
+ import dataclasses
2
+ import importlib
3
+ import typing
4
+
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.node.base import ComposeError, Node
7
+ from telegrinder.node.update import UpdateNode
8
+
9
+ if typing.TYPE_CHECKING:
10
+ from telegrinder.bot.dispatch.process import check_rule
11
+ from telegrinder.bot.rules.abc import ABCRule
12
+
13
+
14
+ class RuleChain(dict[str, typing.Any], Node):
15
+ dataclass: type[typing.Any] = dict
16
+ rules: tuple["ABCRule", ...] = ()
17
+
18
+ def __init_subclass__(cls, *args: typing.Any, **kwargs: typing.Any) -> None:
19
+ super().__init_subclass__(*args, **kwargs)
20
+
21
+ if cls.__name__ == "_RuleNode":
22
+ return
23
+ cls.dataclass = cls.generate_node_dataclass(cls)
24
+
25
+ def __new__(cls, *rules: "ABCRule") -> type[Node]:
26
+ return type("_RuleNode", (cls,), {"dataclass": dict, "rules": rules}) # type: ignore
27
+
28
+ def __class_getitem__(cls, items: "ABCRule | tuple[ABCRule, ...]", /) -> typing.Self:
29
+ if not isinstance(items, tuple):
30
+ items = (items,)
31
+ return cls(*items)
32
+
33
+ @staticmethod
34
+ def generate_node_dataclass(cls_: type["RuleChain"]): # noqa: ANN205
35
+ return dataclasses.dataclass(type(cls_.__name__, (object,), dict(cls_.__dict__)))
36
+
37
+ @classmethod
38
+ async def compose(cls, update: UpdateNode) -> typing.Any:
39
+ # Hack to avoid circular import
40
+ globalns = globals()
41
+ if "check_rule" not in globalns:
42
+ globalns.update(
43
+ {
44
+ "check_rule": getattr(
45
+ importlib.import_module("telegrinder.bot.dispatch.process"),
46
+ "check_rule",
47
+ ),
48
+ },
49
+ )
50
+
51
+ ctx = Context()
52
+ for rule in cls.rules:
53
+ if not await check_rule(update.api, rule, update, ctx):
54
+ raise ComposeError(f"Rule {rule!r} failed!")
55
+
56
+ try:
57
+ if dataclasses.is_dataclass(cls.dataclass):
58
+ return cls.dataclass(**{k: ctx[k] for k in cls.__annotations__})
59
+ return cls.dataclass(**ctx)
60
+ except Exception as exc:
61
+ raise ComposeError(f"Dataclass validation error: {exc}")
62
+
63
+ @classmethod
64
+ def as_node(cls) -> type[typing.Self]:
65
+ return cls
66
+
67
+ @classmethod
68
+ def get_subnodes(cls) -> dict[typing.Literal["update"], type[UpdateNode]]:
69
+ return {"update": UpdateNode}
70
+
71
+ @classmethod
72
+ def is_generator(cls) -> typing.Literal[False]:
73
+ return False
74
+
75
+
76
+ __all__ = ("RuleChain",)
telegrinder/node/scope.py CHANGED
@@ -1,44 +1,44 @@
1
- import enum
2
- import typing
3
-
4
- if typing.TYPE_CHECKING:
5
- from .base import Node
6
-
7
- T = typing.TypeVar("T", bound=type["Node"])
8
-
9
-
10
- class NodeScope(enum.Enum):
11
- GLOBAL = enum.auto()
12
- PER_EVENT = enum.auto()
13
- PER_CALL = enum.auto()
14
-
15
-
16
- PER_EVENT = NodeScope.PER_EVENT
17
- PER_CALL = NodeScope.PER_CALL
18
- GLOBAL = NodeScope.GLOBAL
19
-
20
-
21
- def per_call(node: T) -> T:
22
- setattr(node, "scope", PER_CALL)
23
- return node
24
-
25
-
26
- def per_event(node: T) -> T:
27
- setattr(node, "scope", PER_EVENT)
28
- return node
29
-
30
-
31
- def global_node(node: T) -> T:
32
- setattr(node, "scope", GLOBAL)
33
- return node
34
-
35
-
36
- __all__ = (
1
+ import enum
2
+ import typing
3
+
4
+ if typing.TYPE_CHECKING:
5
+ from .base import Node
6
+
7
+ T = typing.TypeVar("T", bound=type["Node"])
8
+
9
+
10
+ class NodeScope(enum.Enum):
11
+ GLOBAL = enum.auto()
12
+ PER_EVENT = enum.auto()
13
+ PER_CALL = enum.auto()
14
+
15
+
16
+ PER_EVENT = NodeScope.PER_EVENT
17
+ PER_CALL = NodeScope.PER_CALL
18
+ GLOBAL = NodeScope.GLOBAL
19
+
20
+
21
+ def per_call(node: T) -> T:
22
+ setattr(node, "scope", PER_CALL)
23
+ return node
24
+
25
+
26
+ def per_event(node: T) -> T:
27
+ setattr(node, "scope", PER_EVENT)
28
+ return node
29
+
30
+
31
+ def global_node(node: T) -> T:
32
+ setattr(node, "scope", GLOBAL)
33
+ return node
34
+
35
+
36
+ __all__ = (
37
37
  "GLOBAL",
38
38
  "NodeScope",
39
39
  "PER_CALL",
40
40
  "PER_EVENT",
41
41
  "global_node",
42
42
  "per_call",
43
- "per_event",
44
- )
43
+ "per_event",
44
+ )
@@ -1,71 +1,71 @@
1
- import dataclasses
2
- import typing
3
-
4
- from fntypes.option import Nothing, Option
5
-
6
- from telegrinder.api.api import API
7
- from telegrinder.bot.cute_types import ChatJoinRequestCute
8
- from telegrinder.node.base import ComposeError, DataNode, ScalarNode
9
- from telegrinder.node.callback_query import CallbackQueryNode
10
- from telegrinder.node.event import EventNode
11
- from telegrinder.node.message import MessageNode
12
- from telegrinder.node.polymorphic import Polymorphic, impl
13
- from telegrinder.types.objects import Chat, Message, User
14
-
15
-
16
- @dataclasses.dataclass(kw_only=True, slots=True)
17
- class Source(Polymorphic, DataNode):
18
- api: API
19
- chat: Chat
20
- from_user: User
21
- thread_id: Option[int] = dataclasses.field(default_factory=lambda: Nothing())
22
-
23
- @impl
24
- def compose_message(cls, message: MessageNode) -> typing.Self:
25
- return cls(
26
- api=message.ctx_api,
27
- chat=message.chat,
28
- from_user=message.from_.expect(ComposeError("MessageNode has no from_user")),
29
- thread_id=message.message_thread_id,
30
- )
31
-
32
- @impl
33
- def compose_callback_query(cls, callback_query: CallbackQueryNode) -> typing.Self:
34
- return cls(
35
- api=callback_query.ctx_api,
36
- chat=callback_query.chat.expect(ComposeError("CallbackQueryNode has no chat")),
37
- from_user=callback_query.from_user,
38
- thread_id=callback_query.message_thread_id,
39
- )
40
-
41
- @impl
42
- def compose_chat_join_request(cls, chat_join_request: EventNode[ChatJoinRequestCute]) -> typing.Self:
43
- return cls(
44
- api=chat_join_request.ctx_api,
45
- chat=chat_join_request.chat,
46
- from_user=chat_join_request.from_user,
47
- thread_id=Nothing(),
48
- )
49
-
50
- async def send(self, text: str) -> Message:
51
- result = await self.api.send_message(
52
- chat_id=self.chat.id,
53
- message_thread_id=self.thread_id.unwrap_or_none(),
54
- text=text,
55
- )
56
- return result.unwrap()
57
-
58
-
59
- class ChatSource(ScalarNode, Chat):
60
- @classmethod
61
- def compose(cls, source: Source) -> Chat:
62
- return source.chat
63
-
64
-
65
- class UserSource(ScalarNode, User):
66
- @classmethod
67
- def compose(cls, source: Source) -> User:
68
- return source.from_user
69
-
70
-
71
- __all__ = ("ChatSource", "Source", "UserSource")
1
+ import dataclasses
2
+ import typing
3
+
4
+ from fntypes.option import Nothing, Option
5
+
6
+ from telegrinder.api.api import API
7
+ from telegrinder.bot.cute_types import ChatJoinRequestCute
8
+ from telegrinder.node.base import ComposeError, DataNode, ScalarNode
9
+ from telegrinder.node.callback_query import CallbackQueryNode
10
+ from telegrinder.node.event import EventNode
11
+ from telegrinder.node.message import MessageNode
12
+ from telegrinder.node.polymorphic import Polymorphic, impl
13
+ from telegrinder.types.objects import Chat, Message, User
14
+
15
+
16
+ @dataclasses.dataclass(kw_only=True, slots=True)
17
+ class Source(Polymorphic, DataNode):
18
+ api: API
19
+ chat: Chat
20
+ from_user: User
21
+ thread_id: Option[int] = dataclasses.field(default_factory=lambda: Nothing())
22
+
23
+ @impl
24
+ def compose_message(cls, message: MessageNode) -> typing.Self:
25
+ return cls(
26
+ api=message.ctx_api,
27
+ chat=message.chat,
28
+ from_user=message.from_.expect(ComposeError("MessageNode has no from_user")),
29
+ thread_id=message.message_thread_id,
30
+ )
31
+
32
+ @impl
33
+ def compose_callback_query(cls, callback_query: CallbackQueryNode) -> typing.Self:
34
+ return cls(
35
+ api=callback_query.ctx_api,
36
+ chat=callback_query.chat.expect(ComposeError("CallbackQueryNode has no chat")),
37
+ from_user=callback_query.from_user,
38
+ thread_id=callback_query.message_thread_id,
39
+ )
40
+
41
+ @impl
42
+ def compose_chat_join_request(cls, chat_join_request: EventNode[ChatJoinRequestCute]) -> typing.Self:
43
+ return cls(
44
+ api=chat_join_request.ctx_api,
45
+ chat=chat_join_request.chat,
46
+ from_user=chat_join_request.from_user,
47
+ thread_id=Nothing(),
48
+ )
49
+
50
+ async def send(self, text: str) -> Message:
51
+ result = await self.api.send_message(
52
+ chat_id=self.chat.id,
53
+ message_thread_id=self.thread_id.unwrap_or_none(),
54
+ text=text,
55
+ )
56
+ return result.unwrap()
57
+
58
+
59
+ class ChatSource(ScalarNode, Chat):
60
+ @classmethod
61
+ def compose(cls, source: Source) -> Chat:
62
+ return source.chat
63
+
64
+
65
+ class UserSource(ScalarNode, User):
66
+ @classmethod
67
+ def compose(cls, source: Source) -> User:
68
+ return source.from_user
69
+
70
+
71
+ __all__ = ("ChatSource", "Source", "UserSource")
telegrinder/node/text.py CHANGED
@@ -1,41 +1,41 @@
1
- import typing
2
-
3
- from telegrinder.node.base import ComposeError, FactoryNode, ScalarNode
4
- from telegrinder.node.message import MessageNode
5
-
6
-
7
- class Text(ScalarNode, str):
8
- @classmethod
9
- def compose(cls, message: MessageNode) -> str:
10
- if not message.text:
11
- raise ComposeError("Message has no text.")
12
- return message.text.unwrap()
13
-
14
-
15
- class TextInteger(ScalarNode, int):
16
- @classmethod
17
- def compose(cls, text: Text) -> int:
18
- if not text.isdigit():
19
- raise ComposeError("Text is not digit.")
20
- return int(text)
21
-
22
-
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")
1
+ import typing
2
+
3
+ from telegrinder.node.base import ComposeError, FactoryNode, ScalarNode
4
+ from telegrinder.node.message import MessageNode
5
+
6
+
7
+ class Text(ScalarNode, str):
8
+ @classmethod
9
+ def compose(cls, message: MessageNode) -> str:
10
+ if not message.text:
11
+ raise ComposeError("Message has no text.")
12
+ return message.text.unwrap()
13
+
14
+
15
+ class TextInteger(ScalarNode, int):
16
+ @classmethod
17
+ def compose(cls, text: Text) -> int:
18
+ if not text.isdigit():
19
+ raise ComposeError("Text is not digit.")
20
+ return int(text)
21
+
22
+
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")
@@ -1,3 +1,3 @@
1
- from .generator import generate_node
2
-
3
- __all__ = ("generate_node",)
1
+ from .generator import generate_node
2
+
3
+ __all__ = ("generate_node",)