telegrinder 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of telegrinder might be problematic. Click here for more details.

Files changed (164) 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 +11 -11
  10. telegrinder/bot/cute_types/base.py +258 -234
  11. telegrinder/bot/cute_types/callback_query.py +382 -382
  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 +53 -53
  15. telegrinder/bot/cute_types/message.py +2631 -2631
  16. telegrinder/bot/cute_types/update.py +75 -75
  17. telegrinder/bot/cute_types/utils.py +92 -92
  18. telegrinder/bot/dispatch/__init__.py +55 -55
  19. telegrinder/bot/dispatch/abc.py +77 -77
  20. telegrinder/bot/dispatch/context.py +92 -92
  21. telegrinder/bot/dispatch/dispatch.py +202 -201
  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 +128 -123
  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 +16 -16
  35. telegrinder/bot/dispatch/process.py +132 -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 -211
  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 -118
  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 +57 -57
  55. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
  56. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +53 -53
  57. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
  58. telegrinder/bot/dispatch/waiter_machine/machine.py +168 -170
  59. telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
  60. telegrinder/bot/dispatch/waiter_machine/short_state.py +65 -65
  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 +238 -238
  66. telegrinder/bot/rules/adapter/__init__.py +9 -9
  67. telegrinder/bot/rules/adapter/abc.py +29 -29
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +76 -76
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  72. telegrinder/bot/rules/callback_data.py +171 -171
  73. telegrinder/bot/rules/chat_join.py +48 -48
  74. telegrinder/bot/rules/command.py +126 -126
  75. telegrinder/bot/rules/enum_text.py +36 -36
  76. telegrinder/bot/rules/func.py +26 -26
  77. telegrinder/bot/rules/fuzzy.py +24 -24
  78. telegrinder/bot/rules/inline.py +60 -60
  79. telegrinder/bot/rules/integer.py +20 -20
  80. telegrinder/bot/rules/is_from.py +146 -146
  81. telegrinder/bot/rules/markup.py +43 -43
  82. telegrinder/bot/rules/mention.py +14 -14
  83. telegrinder/bot/rules/message.py +17 -17
  84. telegrinder/bot/rules/message_entities.py +35 -35
  85. telegrinder/bot/rules/node.py +27 -27
  86. telegrinder/bot/rules/regex.py +37 -37
  87. telegrinder/bot/rules/rule_enum.py +72 -72
  88. telegrinder/bot/rules/start.py +42 -42
  89. telegrinder/bot/rules/state.py +37 -37
  90. telegrinder/bot/rules/text.py +33 -33
  91. telegrinder/bot/rules/update.py +15 -15
  92. telegrinder/bot/scenario/__init__.py +5 -5
  93. telegrinder/bot/scenario/abc.py +19 -19
  94. telegrinder/bot/scenario/checkbox.py +167 -147
  95. telegrinder/bot/scenario/choice.py +46 -44
  96. telegrinder/client/__init__.py +4 -4
  97. telegrinder/client/abc.py +75 -75
  98. telegrinder/client/aiohttp.py +130 -130
  99. telegrinder/model.py +244 -244
  100. telegrinder/modules.py +237 -237
  101. telegrinder/msgspec_json.py +14 -14
  102. telegrinder/msgspec_utils.py +410 -410
  103. telegrinder/node/__init__.py +20 -20
  104. telegrinder/node/attachment.py +92 -92
  105. telegrinder/node/base.py +143 -144
  106. telegrinder/node/callback_query.py +14 -14
  107. telegrinder/node/command.py +33 -33
  108. telegrinder/node/composer.py +196 -184
  109. telegrinder/node/container.py +27 -27
  110. telegrinder/node/event.py +71 -73
  111. telegrinder/node/me.py +16 -16
  112. telegrinder/node/message.py +14 -14
  113. telegrinder/node/polymorphic.py +48 -52
  114. telegrinder/node/rule.py +76 -76
  115. telegrinder/node/scope.py +38 -38
  116. telegrinder/node/source.py +71 -71
  117. telegrinder/node/text.py +21 -21
  118. telegrinder/node/tools/__init__.py +3 -3
  119. telegrinder/node/tools/generator.py +40 -40
  120. telegrinder/node/update.py +15 -15
  121. telegrinder/rules.py +0 -0
  122. telegrinder/tools/__init__.py +74 -74
  123. telegrinder/tools/buttons.py +79 -79
  124. telegrinder/tools/error_handler/__init__.py +7 -7
  125. telegrinder/tools/error_handler/abc.py +33 -33
  126. telegrinder/tools/error_handler/error.py +9 -9
  127. telegrinder/tools/error_handler/error_handler.py +193 -193
  128. telegrinder/tools/formatting/__init__.py +46 -46
  129. telegrinder/tools/formatting/html.py +308 -308
  130. telegrinder/tools/formatting/links.py +33 -33
  131. telegrinder/tools/formatting/spec_html_formats.py +111 -111
  132. telegrinder/tools/functional.py +12 -12
  133. telegrinder/tools/global_context/__init__.py +7 -7
  134. telegrinder/tools/global_context/abc.py +63 -63
  135. telegrinder/tools/global_context/global_context.py +412 -412
  136. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  137. telegrinder/tools/i18n/__init__.py +12 -12
  138. telegrinder/tools/i18n/abc.py +32 -32
  139. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  140. telegrinder/tools/i18n/middleware/abc.py +25 -25
  141. telegrinder/tools/i18n/simple.py +43 -43
  142. telegrinder/tools/kb_set/__init__.py +4 -4
  143. telegrinder/tools/kb_set/base.py +15 -15
  144. telegrinder/tools/kb_set/yaml.py +63 -63
  145. telegrinder/tools/keyboard.py +128 -128
  146. telegrinder/tools/limited_dict.py +37 -37
  147. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  148. telegrinder/tools/loop_wrapper/abc.py +15 -15
  149. telegrinder/tools/loop_wrapper/loop_wrapper.py +216 -216
  150. telegrinder/tools/magic.py +168 -168
  151. telegrinder/tools/parse_mode.py +6 -6
  152. telegrinder/tools/state_storage/__init__.py +4 -4
  153. telegrinder/tools/state_storage/abc.py +35 -35
  154. telegrinder/tools/state_storage/memory.py +25 -25
  155. telegrinder/types/__init__.py +6 -6
  156. telegrinder/types/enums.py +672 -672
  157. telegrinder/types/methods.py +4633 -4633
  158. telegrinder/types/objects.py +6317 -6317
  159. telegrinder/verification_utils.py +32 -32
  160. {telegrinder-0.3.1.dist-info → telegrinder-0.3.2.dist-info}/LICENSE +22 -22
  161. {telegrinder-0.3.1.dist-info → telegrinder-0.3.2.dist-info}/METADATA +1 -1
  162. telegrinder-0.3.2.dist-info/RECORD +164 -0
  163. telegrinder-0.3.1.dist-info/RECORD +0 -164
  164. {telegrinder-0.3.1.dist-info → telegrinder-0.3.2.dist-info}/WHEEL +0 -0
@@ -1,52 +1,48 @@
1
- import inspect
2
- import typing
3
-
4
- from telegrinder.bot.dispatch.context import Context
5
- from telegrinder.modules import logger
6
- from telegrinder.node.base import ComposeError, Node
7
- from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, Composition, NodeSession
8
- from telegrinder.node.scope import NodeScope
9
- from telegrinder.node.update import UpdateNode
10
- from telegrinder.tools.magic import get_impls, impl
11
-
12
-
13
- class Polymorphic(Node):
14
- @classmethod
15
- async def compose(cls, update: UpdateNode, context: Context) -> typing.Any:
16
- logger.debug(f"Composing polymorphic node {cls.__name__!r}...")
17
- scope = getattr(cls, "scope", None)
18
- node_ctx = context.get_or_set(CONTEXT_STORE_NODES_KEY, {})
19
-
20
- for i, impl_ in enumerate(get_impls(cls)):
21
- logger.debug("Checking impl {!r}...", impl_.__name__)
22
- composition = Composition(impl_, True)
23
- node_collection = await composition.compose_nodes(update, context)
24
- if node_collection is None:
25
- logger.debug("Impl {!r} composition failed!", impl_.__name__)
26
- continue
27
-
28
- # To determine whether this is a right morph, all subnodes must be resolved
29
- if scope is NodeScope.PER_EVENT and (cls, i) in node_ctx:
30
- logger.debug(
31
- "Morph is already cached as per_event node, using its value. Impl {!r} succeeded!",
32
- impl_.__name__,
33
- )
34
- res: NodeSession = node_ctx[(cls, i)]
35
- await node_collection.close_all()
36
- return res.value
37
-
38
- result = composition.func(cls, **node_collection.values)
39
- if inspect.isawaitable(result):
40
- result = await result
41
-
42
- if scope is NodeScope.PER_EVENT:
43
- node_ctx[(cls, i)] = NodeSession(cls, result, {})
44
-
45
- await node_collection.close_all(with_value=result)
46
- logger.debug("Impl {!r} succeeded with value: {!r}", impl_.__name__, result)
47
- return result
48
-
49
- raise ComposeError("No implementation found.")
50
-
51
-
52
- __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,21 +1,21 @@
1
- from telegrinder.node.base import ComposeError, ScalarNode
2
- from telegrinder.node.message import MessageNode
3
-
4
-
5
- class Text(ScalarNode, str):
6
- @classmethod
7
- def compose(cls, message: MessageNode) -> str:
8
- if not message.text:
9
- raise ComposeError("Message has no text.")
10
- return message.text.unwrap()
11
-
12
-
13
- class TextInteger(ScalarNode, int):
14
- @classmethod
15
- def compose(cls, text: Text) -> int:
16
- if not text.isdigit():
17
- raise ComposeError("Text is not digit.")
18
- return int(text)
19
-
20
-
21
- __all__ = ("Text", "TextInteger")
1
+ from telegrinder.node.base import ComposeError, ScalarNode
2
+ from telegrinder.node.message import MessageNode
3
+
4
+
5
+ class Text(ScalarNode, str):
6
+ @classmethod
7
+ def compose(cls, message: MessageNode) -> str:
8
+ if not message.text:
9
+ raise ComposeError("Message has no text.")
10
+ return message.text.unwrap()
11
+
12
+
13
+ class TextInteger(ScalarNode, int):
14
+ @classmethod
15
+ def compose(cls, text: Text) -> int:
16
+ if not text.isdigit():
17
+ raise ComposeError("Text is not digit.")
18
+ return int(text)
19
+
20
+
21
+ __all__ = ("Text", "TextInteger")
@@ -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",)
@@ -1,40 +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_node(
23
- subnodes: tuple[type[Node], ...],
24
- func: typing.Callable[..., typing.Any],
25
- casts: tuple[typing.Callable[[typing.Any], typing.Any], ...] = (cast_false_to_none, error_on_none),
26
- ) -> type[Node]:
27
- async def compose(cls, **kw) -> typing.Any:
28
- result = func(*ContainerNode.compose(**kw)) # type: ignore
29
- if inspect.isawaitable(result):
30
- result = await result
31
- for cast in casts:
32
- result = cast(result)
33
- return result
34
-
35
- container = ContainerNode.link_nodes(list(subnodes))
36
- compose.__annotations__ = container.get_subnodes()
37
- return type("_ContainerNode", (container,), {"compose": classmethod(compose)})
38
-
39
-
40
- __all__ = ("generate_node",)
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_node(
23
+ subnodes: tuple[type[Node], ...],
24
+ func: typing.Callable[..., typing.Any],
25
+ casts: tuple[typing.Callable[[typing.Any], typing.Any], ...] = (cast_false_to_none, error_on_none),
26
+ ) -> type[Node]:
27
+ async def compose(cls, **kw) -> typing.Any:
28
+ result = func(*ContainerNode.compose(**kw)) # type: ignore
29
+ if inspect.isawaitable(result):
30
+ result = await result
31
+ for cast in casts:
32
+ result = cast(result)
33
+ return result
34
+
35
+ container = ContainerNode.link_nodes(list(subnodes))
36
+ compose.__annotations__ = container.get_subnodes()
37
+ return type("_ContainerNode", (container,), {"compose": classmethod(compose)})
38
+
39
+
40
+ __all__ = ("generate_node",)
@@ -1,15 +1,15 @@
1
- from telegrinder.api.api import API
2
- from telegrinder.bot.cute_types import UpdateCute
3
- from telegrinder.node.base import ScalarNode
4
- from telegrinder.types.objects import Update
5
-
6
-
7
- class UpdateNode(ScalarNode, UpdateCute):
8
- @classmethod
9
- def compose(cls, update: Update, api: API) -> UpdateCute:
10
- if isinstance(update, UpdateCute):
11
- return update
12
- return UpdateCute.from_update(update, api)
13
-
14
-
15
- __all__ = ("UpdateNode",)
1
+ from telegrinder.api.api import API
2
+ from telegrinder.bot.cute_types import UpdateCute
3
+ from telegrinder.node.base import ScalarNode
4
+ from telegrinder.types.objects import Update
5
+
6
+
7
+ class UpdateNode(ScalarNode, UpdateCute):
8
+ @classmethod
9
+ def compose(cls, update: Update, api: API) -> UpdateCute:
10
+ if isinstance(update, UpdateCute):
11
+ return update
12
+ return UpdateCute.from_update(update, api)
13
+
14
+
15
+ __all__ = ("UpdateNode",)
telegrinder/rules.py CHANGED
File without changes