telegrinder 1.0.0rc1__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.
Files changed (215) hide show
  1. telegrinder/__init__.py +258 -0
  2. telegrinder/__meta__.py +1 -0
  3. telegrinder/api/__init__.py +15 -0
  4. telegrinder/api/api.py +175 -0
  5. telegrinder/api/error.py +50 -0
  6. telegrinder/api/response.py +23 -0
  7. telegrinder/api/token.py +30 -0
  8. telegrinder/api/validators.py +30 -0
  9. telegrinder/bot/__init__.py +144 -0
  10. telegrinder/bot/bot.py +70 -0
  11. telegrinder/bot/cute_types/__init__.py +41 -0
  12. telegrinder/bot/cute_types/base.py +228 -0
  13. telegrinder/bot/cute_types/base.pyi +49 -0
  14. telegrinder/bot/cute_types/business_connection.py +9 -0
  15. telegrinder/bot/cute_types/business_messages_deleted.py +9 -0
  16. telegrinder/bot/cute_types/callback_query.py +248 -0
  17. telegrinder/bot/cute_types/chat_boost_removed.py +9 -0
  18. telegrinder/bot/cute_types/chat_boost_updated.py +9 -0
  19. telegrinder/bot/cute_types/chat_join_request.py +59 -0
  20. telegrinder/bot/cute_types/chat_member_updated.py +158 -0
  21. telegrinder/bot/cute_types/chosen_inline_result.py +11 -0
  22. telegrinder/bot/cute_types/inline_query.py +41 -0
  23. telegrinder/bot/cute_types/message.py +2809 -0
  24. telegrinder/bot/cute_types/message_reaction_count_updated.py +9 -0
  25. telegrinder/bot/cute_types/message_reaction_updated.py +9 -0
  26. telegrinder/bot/cute_types/paid_media_purchased.py +11 -0
  27. telegrinder/bot/cute_types/poll.py +9 -0
  28. telegrinder/bot/cute_types/poll_answer.py +9 -0
  29. telegrinder/bot/cute_types/pre_checkout_query.py +36 -0
  30. telegrinder/bot/cute_types/shipping_query.py +11 -0
  31. telegrinder/bot/cute_types/update.py +209 -0
  32. telegrinder/bot/cute_types/utils.py +141 -0
  33. telegrinder/bot/dispatch/__init__.py +99 -0
  34. telegrinder/bot/dispatch/abc.py +74 -0
  35. telegrinder/bot/dispatch/action.py +99 -0
  36. telegrinder/bot/dispatch/context.py +162 -0
  37. telegrinder/bot/dispatch/dispatch.py +362 -0
  38. telegrinder/bot/dispatch/handler/__init__.py +23 -0
  39. telegrinder/bot/dispatch/handler/abc.py +25 -0
  40. telegrinder/bot/dispatch/handler/audio_reply.py +43 -0
  41. telegrinder/bot/dispatch/handler/base.py +34 -0
  42. telegrinder/bot/dispatch/handler/document_reply.py +43 -0
  43. telegrinder/bot/dispatch/handler/func.py +73 -0
  44. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
  45. telegrinder/bot/dispatch/handler/message_reply.py +35 -0
  46. telegrinder/bot/dispatch/handler/photo_reply.py +43 -0
  47. telegrinder/bot/dispatch/handler/sticker_reply.py +36 -0
  48. telegrinder/bot/dispatch/handler/video_reply.py +43 -0
  49. telegrinder/bot/dispatch/middleware/__init__.py +13 -0
  50. telegrinder/bot/dispatch/middleware/abc.py +112 -0
  51. telegrinder/bot/dispatch/middleware/box.py +32 -0
  52. telegrinder/bot/dispatch/middleware/filter.py +88 -0
  53. telegrinder/bot/dispatch/middleware/media_group.py +69 -0
  54. telegrinder/bot/dispatch/process.py +93 -0
  55. telegrinder/bot/dispatch/return_manager/__init__.py +21 -0
  56. telegrinder/bot/dispatch/return_manager/abc.py +107 -0
  57. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  58. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  59. telegrinder/bot/dispatch/return_manager/message.py +34 -0
  60. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +19 -0
  61. telegrinder/bot/dispatch/return_manager/utils.py +20 -0
  62. telegrinder/bot/dispatch/router/__init__.py +4 -0
  63. telegrinder/bot/dispatch/router/abc.py +15 -0
  64. telegrinder/bot/dispatch/router/base.py +154 -0
  65. telegrinder/bot/dispatch/view/__init__.py +15 -0
  66. telegrinder/bot/dispatch/view/abc.py +15 -0
  67. telegrinder/bot/dispatch/view/base.py +226 -0
  68. telegrinder/bot/dispatch/view/box.py +207 -0
  69. telegrinder/bot/dispatch/view/media_group.py +25 -0
  70. telegrinder/bot/dispatch/waiter_machine/__init__.py +25 -0
  71. telegrinder/bot/dispatch/waiter_machine/actions.py +16 -0
  72. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +13 -0
  73. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +53 -0
  74. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +61 -0
  75. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +49 -0
  76. telegrinder/bot/dispatch/waiter_machine/machine.py +264 -0
  77. telegrinder/bot/dispatch/waiter_machine/middleware.py +77 -0
  78. telegrinder/bot/dispatch/waiter_machine/short_state.py +105 -0
  79. telegrinder/bot/polling/__init__.py +4 -0
  80. telegrinder/bot/polling/abc.py +25 -0
  81. telegrinder/bot/polling/error_handler.py +93 -0
  82. telegrinder/bot/polling/polling.py +167 -0
  83. telegrinder/bot/polling/utils.py +12 -0
  84. telegrinder/bot/rules/__init__.py +166 -0
  85. telegrinder/bot/rules/abc.py +150 -0
  86. telegrinder/bot/rules/button.py +20 -0
  87. telegrinder/bot/rules/callback_data.py +109 -0
  88. telegrinder/bot/rules/chat_join.py +28 -0
  89. telegrinder/bot/rules/chat_member_updated.py +145 -0
  90. telegrinder/bot/rules/command.py +137 -0
  91. telegrinder/bot/rules/enum_text.py +29 -0
  92. telegrinder/bot/rules/func.py +21 -0
  93. telegrinder/bot/rules/fuzzy.py +21 -0
  94. telegrinder/bot/rules/inline.py +45 -0
  95. telegrinder/bot/rules/integer.py +19 -0
  96. telegrinder/bot/rules/is_from.py +213 -0
  97. telegrinder/bot/rules/logic.py +22 -0
  98. telegrinder/bot/rules/magic.py +60 -0
  99. telegrinder/bot/rules/markup.py +51 -0
  100. telegrinder/bot/rules/media.py +13 -0
  101. telegrinder/bot/rules/mention.py +15 -0
  102. telegrinder/bot/rules/message_entities.py +37 -0
  103. telegrinder/bot/rules/node.py +43 -0
  104. telegrinder/bot/rules/payload.py +89 -0
  105. telegrinder/bot/rules/payment_invoice.py +14 -0
  106. telegrinder/bot/rules/regex.py +34 -0
  107. telegrinder/bot/rules/rule_enum.py +71 -0
  108. telegrinder/bot/rules/start.py +73 -0
  109. telegrinder/bot/rules/state.py +35 -0
  110. telegrinder/bot/rules/text.py +27 -0
  111. telegrinder/bot/rules/update.py +14 -0
  112. telegrinder/bot/scenario/__init__.py +5 -0
  113. telegrinder/bot/scenario/abc.py +16 -0
  114. telegrinder/bot/scenario/checkbox.py +183 -0
  115. telegrinder/bot/scenario/choice.py +44 -0
  116. telegrinder/client/__init__.py +11 -0
  117. telegrinder/client/abc.py +136 -0
  118. telegrinder/client/form_data.py +34 -0
  119. telegrinder/client/rnet.py +198 -0
  120. telegrinder/model.py +133 -0
  121. telegrinder/model.pyi +57 -0
  122. telegrinder/modules.py +1081 -0
  123. telegrinder/msgspec_utils/__init__.py +42 -0
  124. telegrinder/msgspec_utils/abc.py +16 -0
  125. telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
  126. telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
  127. telegrinder/msgspec_utils/custom_types/enum_meta.py +61 -0
  128. telegrinder/msgspec_utils/custom_types/literal.py +25 -0
  129. telegrinder/msgspec_utils/custom_types/option.py +17 -0
  130. telegrinder/msgspec_utils/decoder.py +388 -0
  131. telegrinder/msgspec_utils/encoder.py +204 -0
  132. telegrinder/msgspec_utils/json.py +15 -0
  133. telegrinder/msgspec_utils/tools.py +80 -0
  134. telegrinder/node/__init__.py +80 -0
  135. telegrinder/node/compose.py +193 -0
  136. telegrinder/node/nodes/__init__.py +96 -0
  137. telegrinder/node/nodes/attachment.py +169 -0
  138. telegrinder/node/nodes/callback_query.py +25 -0
  139. telegrinder/node/nodes/channel.py +97 -0
  140. telegrinder/node/nodes/command.py +33 -0
  141. telegrinder/node/nodes/error.py +43 -0
  142. telegrinder/node/nodes/event.py +70 -0
  143. telegrinder/node/nodes/file.py +39 -0
  144. telegrinder/node/nodes/global_node.py +66 -0
  145. telegrinder/node/nodes/i18n.py +110 -0
  146. telegrinder/node/nodes/me.py +26 -0
  147. telegrinder/node/nodes/message_entities.py +15 -0
  148. telegrinder/node/nodes/payload.py +84 -0
  149. telegrinder/node/nodes/reply_message.py +14 -0
  150. telegrinder/node/nodes/source.py +172 -0
  151. telegrinder/node/nodes/state_mutator.py +71 -0
  152. telegrinder/node/nodes/text.py +62 -0
  153. telegrinder/node/scope.py +88 -0
  154. telegrinder/node/utils.py +38 -0
  155. telegrinder/py.typed +0 -0
  156. telegrinder/rules.py +1 -0
  157. telegrinder/tools/__init__.py +183 -0
  158. telegrinder/tools/aio.py +147 -0
  159. telegrinder/tools/final.py +21 -0
  160. telegrinder/tools/formatting/__init__.py +85 -0
  161. telegrinder/tools/formatting/deep_links/__init__.py +39 -0
  162. telegrinder/tools/formatting/deep_links/links.py +468 -0
  163. telegrinder/tools/formatting/deep_links/parsing.py +88 -0
  164. telegrinder/tools/formatting/deep_links/validators.py +8 -0
  165. telegrinder/tools/formatting/html.py +241 -0
  166. telegrinder/tools/fullname.py +82 -0
  167. telegrinder/tools/global_context/__init__.py +13 -0
  168. telegrinder/tools/global_context/abc.py +63 -0
  169. telegrinder/tools/global_context/builtin_context.py +45 -0
  170. telegrinder/tools/global_context/global_context.py +614 -0
  171. telegrinder/tools/input_file_directory.py +30 -0
  172. telegrinder/tools/keyboard/__init__.py +6 -0
  173. telegrinder/tools/keyboard/abc.py +84 -0
  174. telegrinder/tools/keyboard/base.py +108 -0
  175. telegrinder/tools/keyboard/button.py +181 -0
  176. telegrinder/tools/keyboard/data.py +31 -0
  177. telegrinder/tools/keyboard/keyboard.py +160 -0
  178. telegrinder/tools/keyboard/utils.py +95 -0
  179. telegrinder/tools/lifespan.py +188 -0
  180. telegrinder/tools/limited_dict.py +35 -0
  181. telegrinder/tools/loop_wrapper.py +271 -0
  182. telegrinder/tools/magic/__init__.py +29 -0
  183. telegrinder/tools/magic/annotations.py +172 -0
  184. telegrinder/tools/magic/descriptors.py +57 -0
  185. telegrinder/tools/magic/function.py +254 -0
  186. telegrinder/tools/magic/inspect.py +16 -0
  187. telegrinder/tools/magic/shortcut.py +107 -0
  188. telegrinder/tools/member_descriptor_proxy.py +95 -0
  189. telegrinder/tools/parse_mode.py +12 -0
  190. telegrinder/tools/serialization/__init__.py +5 -0
  191. telegrinder/tools/serialization/abc.py +34 -0
  192. telegrinder/tools/serialization/json_ser.py +60 -0
  193. telegrinder/tools/serialization/msgpack_ser.py +197 -0
  194. telegrinder/tools/serialization/utils.py +18 -0
  195. telegrinder/tools/singleton/__init__.py +4 -0
  196. telegrinder/tools/singleton/abc.py +14 -0
  197. telegrinder/tools/singleton/singleton.py +18 -0
  198. telegrinder/tools/state_mutator/__init__.py +4 -0
  199. telegrinder/tools/state_mutator/mutation.py +85 -0
  200. telegrinder/tools/state_storage/__init__.py +4 -0
  201. telegrinder/tools/state_storage/abc.py +38 -0
  202. telegrinder/tools/state_storage/memory.py +27 -0
  203. telegrinder/tools/strings.py +22 -0
  204. telegrinder/types/__init__.py +323 -0
  205. telegrinder/types/enums.py +754 -0
  206. telegrinder/types/input_file.py +51 -0
  207. telegrinder/types/methods.py +6143 -0
  208. telegrinder/types/methods_utils.py +66 -0
  209. telegrinder/types/objects.py +8184 -0
  210. telegrinder/types/webapp.py +129 -0
  211. telegrinder/verification_utils.py +35 -0
  212. telegrinder-1.0.0rc1.dist-info/METADATA +166 -0
  213. telegrinder-1.0.0rc1.dist-info/RECORD +215 -0
  214. telegrinder-1.0.0rc1.dist-info/WHEEL +4 -0
  215. telegrinder-1.0.0rc1.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,144 @@
1
+ from telegrinder.bot.bot import Telegrinder
2
+ from telegrinder.bot.cute_types import (
3
+ BaseCute,
4
+ BusinessConnectionCute,
5
+ BusinessMessagesDeletedCute,
6
+ CallbackQueryCute,
7
+ ChatBoostRemovedCute,
8
+ ChatBoostUpdatedCute,
9
+ ChatJoinRequestCute,
10
+ ChatMemberUpdatedCute,
11
+ ChosenInlineResultCute,
12
+ InlineQueryCute,
13
+ MessageCute,
14
+ MessageReactionCountUpdatedCute,
15
+ MessageReactionUpdatedCute,
16
+ PaidMediaPurchasedCute,
17
+ PollAnswerCute,
18
+ PollCute,
19
+ PreCheckoutQueryCute,
20
+ ShippingQueryCute,
21
+ UpdateCute,
22
+ )
23
+ from telegrinder.bot.dispatch import (
24
+ CALLBACK_QUERY_FOR_MESSAGE,
25
+ CALLBACK_QUERY_FROM_CHAT,
26
+ CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE,
27
+ MESSAGE_FROM_USER,
28
+ MESSAGE_FROM_USER_IN_CHAT,
29
+ MESSAGE_IN_CHAT,
30
+ ABCDispatch,
31
+ ABCHandler,
32
+ ABCMiddleware,
33
+ ABCReturnManager,
34
+ ABCRouter,
35
+ ABCView,
36
+ AudioReplyHandler,
37
+ BaseReturnManager,
38
+ CallbackQueryReturnManager,
39
+ Context,
40
+ Dispatch,
41
+ DocumentReplyHandler,
42
+ ErrorView,
43
+ EventModelView,
44
+ EventView,
45
+ FilterMiddleware,
46
+ FuncHandler,
47
+ Hasher,
48
+ InlineQueryReturnManager,
49
+ Manager,
50
+ MediaGroupMiddleware,
51
+ MediaGroupReplyHandler,
52
+ MediaGroupView,
53
+ MessageReplyHandler,
54
+ MessageReturnManager,
55
+ MiddlewareBox,
56
+ PhotoReplyHandler,
57
+ PreCheckoutQueryReturnManager,
58
+ RawEventView,
59
+ Router,
60
+ ShortState,
61
+ StickerReplyHandler,
62
+ VideoReplyHandler,
63
+ View,
64
+ ViewBox,
65
+ WaiterMachine,
66
+ action,
67
+ register_manager,
68
+ )
69
+ from telegrinder.bot.polling import ABCPolling, Polling
70
+ from telegrinder.bot.rules import ABCRule
71
+ from telegrinder.bot.scenario import ABCScenario, Checkbox, Choice
72
+
73
+ __all__ = (
74
+ "CALLBACK_QUERY_FOR_MESSAGE",
75
+ "CALLBACK_QUERY_FROM_CHAT",
76
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
77
+ "MESSAGE_FROM_USER",
78
+ "MESSAGE_FROM_USER_IN_CHAT",
79
+ "MESSAGE_IN_CHAT",
80
+ "ABCDispatch",
81
+ "ABCHandler",
82
+ "ABCMiddleware",
83
+ "ABCPolling",
84
+ "ABCReturnManager",
85
+ "ABCRouter",
86
+ "ABCRule",
87
+ "ABCScenario",
88
+ "ABCView",
89
+ "AudioReplyHandler",
90
+ "BaseCute",
91
+ "BaseReturnManager",
92
+ "BusinessConnectionCute",
93
+ "BusinessMessagesDeletedCute",
94
+ "CallbackQueryCute",
95
+ "CallbackQueryReturnManager",
96
+ "ChatBoostRemovedCute",
97
+ "ChatBoostUpdatedCute",
98
+ "ChatJoinRequestCute",
99
+ "ChatMemberUpdatedCute",
100
+ "Checkbox",
101
+ "Choice",
102
+ "ChosenInlineResultCute",
103
+ "Context",
104
+ "Dispatch",
105
+ "DocumentReplyHandler",
106
+ "ErrorView",
107
+ "EventModelView",
108
+ "EventView",
109
+ "FilterMiddleware",
110
+ "FuncHandler",
111
+ "Hasher",
112
+ "InlineQueryCute",
113
+ "InlineQueryReturnManager",
114
+ "Manager",
115
+ "MediaGroupMiddleware",
116
+ "MediaGroupReplyHandler",
117
+ "MediaGroupView",
118
+ "MessageCute",
119
+ "MessageReactionCountUpdatedCute",
120
+ "MessageReactionUpdatedCute",
121
+ "MessageReplyHandler",
122
+ "MessageReturnManager",
123
+ "MiddlewareBox",
124
+ "PaidMediaPurchasedCute",
125
+ "PhotoReplyHandler",
126
+ "PollAnswerCute",
127
+ "PollCute",
128
+ "Polling",
129
+ "PreCheckoutQueryCute",
130
+ "PreCheckoutQueryReturnManager",
131
+ "RawEventView",
132
+ "Router",
133
+ "ShippingQueryCute",
134
+ "ShortState",
135
+ "StickerReplyHandler",
136
+ "Telegrinder",
137
+ "UpdateCute",
138
+ "VideoReplyHandler",
139
+ "View",
140
+ "ViewBox",
141
+ "WaiterMachine",
142
+ "action",
143
+ "register_manager",
144
+ )
telegrinder/bot/bot.py ADDED
@@ -0,0 +1,70 @@
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.dispatch import dispatch as dp
5
+ from telegrinder.bot.dispatch.abc import ABCDispatch
6
+ from telegrinder.bot.polling import polling as pg
7
+ from telegrinder.bot.polling.abc import ABCPolling
8
+ from telegrinder.modules import logger
9
+ from telegrinder.tools.global_context.builtin_context import TelegrinderContext
10
+ from telegrinder.tools.loop_wrapper import LoopWrapper
11
+
12
+ TELEGRINDER_CONTEXT: typing.Final = TelegrinderContext()
13
+
14
+
15
+ class Telegrinder[Dispatch: ABCDispatch = dp.Dispatch, Polling: ABCPolling = pg.Polling]:
16
+ def __init__(
17
+ self,
18
+ api: API,
19
+ *,
20
+ dispatch: Dispatch | None = None,
21
+ polling: Polling | None = None,
22
+ loop_wrapper: LoopWrapper | None = None,
23
+ ) -> None:
24
+ self.api = api
25
+ self.dispatch = typing.cast("Dispatch", dispatch or dp.Dispatch())
26
+ self.polling = typing.cast("Polling", polling or pg.Polling(api))
27
+ self.loop_wrapper = loop_wrapper or TELEGRINDER_CONTEXT.loop_wrapper
28
+
29
+ def __repr__(self) -> str:
30
+ return "<{}: api={!r}, dispatch={!r}, polling={!r}, loop_wrapper={!r}>".format(
31
+ type(self).__name__,
32
+ self.api,
33
+ self.dispatch,
34
+ self.polling,
35
+ self.loop_wrapper,
36
+ )
37
+
38
+ @property
39
+ def on(self) -> Dispatch:
40
+ return self.dispatch
41
+
42
+ async def drop_pending_updates(self) -> None:
43
+ await logger.adebug("Dropping pending updates")
44
+ await self.api.delete_webhook(drop_pending_updates=True)
45
+
46
+ async def run_polling(
47
+ self,
48
+ *,
49
+ offset: int = 0,
50
+ skip_updates: bool = False,
51
+ ) -> None:
52
+ self.polling.offset = offset
53
+
54
+ async def listen_polling() -> None:
55
+ if skip_updates:
56
+ await self.drop_pending_updates()
57
+
58
+ async for updates in self.polling.listen():
59
+ for update in updates:
60
+ self.loop_wrapper.add_task(self.dispatch.feed(self.api, update))
61
+
62
+ self.loop_wrapper.add_task(listen_polling())
63
+
64
+ def run_forever(self, *, offset: int = 0, skip_updates: bool = False) -> typing.NoReturn:
65
+ logger.info("Running blocking polling (id={})", self.api.id)
66
+ self.loop_wrapper.add_task(self.run_polling(offset=offset, skip_updates=skip_updates))
67
+ self.loop_wrapper.run()
68
+
69
+
70
+ __all__ = ("Telegrinder",)
@@ -0,0 +1,41 @@
1
+ from telegrinder.bot.cute_types.base import BaseCute
2
+ from telegrinder.bot.cute_types.business_connection import BusinessConnectionCute
3
+ from telegrinder.bot.cute_types.business_messages_deleted import BusinessMessagesDeletedCute
4
+ from telegrinder.bot.cute_types.callback_query import CallbackQueryCute
5
+ from telegrinder.bot.cute_types.chat_boost_removed import ChatBoostRemovedCute
6
+ from telegrinder.bot.cute_types.chat_boost_updated import ChatBoostUpdatedCute
7
+ from telegrinder.bot.cute_types.chat_join_request import ChatJoinRequestCute
8
+ from telegrinder.bot.cute_types.chat_member_updated import ChatMemberUpdatedCute
9
+ from telegrinder.bot.cute_types.chosen_inline_result import ChosenInlineResultCute
10
+ from telegrinder.bot.cute_types.inline_query import InlineQueryCute
11
+ from telegrinder.bot.cute_types.message import MessageCute
12
+ from telegrinder.bot.cute_types.message_reaction_count_updated import MessageReactionCountUpdatedCute
13
+ from telegrinder.bot.cute_types.message_reaction_updated import MessageReactionUpdatedCute
14
+ from telegrinder.bot.cute_types.paid_media_purchased import PaidMediaPurchasedCute
15
+ from telegrinder.bot.cute_types.poll import PollCute
16
+ from telegrinder.bot.cute_types.poll_answer import PollAnswerCute
17
+ from telegrinder.bot.cute_types.pre_checkout_query import PreCheckoutQueryCute
18
+ from telegrinder.bot.cute_types.shipping_query import ShippingQueryCute
19
+ from telegrinder.bot.cute_types.update import UpdateCute
20
+
21
+ __all__ = (
22
+ "BaseCute",
23
+ "BusinessConnectionCute",
24
+ "BusinessMessagesDeletedCute",
25
+ "CallbackQueryCute",
26
+ "ChatBoostRemovedCute",
27
+ "ChatBoostUpdatedCute",
28
+ "ChatJoinRequestCute",
29
+ "ChatMemberUpdatedCute",
30
+ "ChosenInlineResultCute",
31
+ "InlineQueryCute",
32
+ "MessageCute",
33
+ "MessageReactionCountUpdatedCute",
34
+ "MessageReactionUpdatedCute",
35
+ "PaidMediaPurchasedCute",
36
+ "PollAnswerCute",
37
+ "PollCute",
38
+ "PreCheckoutQueryCute",
39
+ "ShippingQueryCute",
40
+ "UpdateCute",
41
+ )
@@ -0,0 +1,228 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+ from functools import cached_property
5
+
6
+ from kungfu.library import Some, Sum
7
+ from kungfu.library.misc import from_optional
8
+ from nodnod.error import NodeError
9
+
10
+ from telegrinder.api.api import API
11
+ from telegrinder.bot.dispatch.context import Context
12
+ from telegrinder.model import Model
13
+ from telegrinder.msgspec_utils import Option, encoder, get_class_annotations, struct_asdict
14
+ from telegrinder.tools.fullname import fullname
15
+ from telegrinder.tools.magic.shortcut import shortcut
16
+ from telegrinder.types.objects import Update
17
+
18
+ BOUND_API_KEY: typing.Final = "bound_api"
19
+ RAW_UPDATE_BIND_KEY: typing.Final = "raw_update_bind"
20
+
21
+
22
+ def compose_method_params[Cute: BaseCute](
23
+ params: dict[str, typing.Any],
24
+ update: Cute,
25
+ *,
26
+ default_params: set[str | tuple[str, str]] | None = None,
27
+ validators: dict[str, typing.Callable[[Cute], bool]] | None = None,
28
+ ) -> dict[str, typing.Any]:
29
+ default_params = default_params or set()
30
+ validators = validators or {}
31
+
32
+ for param in default_params:
33
+ param_name = param if isinstance(param, str) else param[0]
34
+ if param_name not in params:
35
+ if param_name in validators and not validators[param_name](update):
36
+ continue
37
+ params[param_name] = getattr(update, param if isinstance(param, str) else param[1], None)
38
+
39
+ return params
40
+
41
+
42
+ def get_cute_from_generic(generic_args: tuple[typing.Any, ...], /) -> typing.Any:
43
+ for arg in generic_args:
44
+ orig_arg = typing.get_origin(arg) or arg
45
+
46
+ if not isinstance(orig_arg, type):
47
+ continue
48
+
49
+ if orig_arg in (Sum, Some, Option):
50
+ return get_cute_from_generic(typing.get_args(arg))
51
+
52
+ if issubclass(orig_arg, BaseCute):
53
+ return arg
54
+
55
+ return None
56
+
57
+
58
+ def get_cute_annotations(annotations: dict[str, typing.Any], /) -> dict[str, type[BaseCute]]:
59
+ cute_annotations = {}
60
+
61
+ for key, hint in annotations.items():
62
+ if not isinstance(hint, type) and (cute := get_cute_from_generic(typing.get_args(hint))) is not None:
63
+ cute_annotations[key] = cute
64
+
65
+ elif isinstance(hint, type) and issubclass(hint, BaseCute):
66
+ cute_annotations[key] = hint
67
+
68
+ return cute_annotations
69
+
70
+
71
+ def maybe_wrapped(value: typing.Any, /) -> typing.Any:
72
+ wrapped_value = None
73
+
74
+ while isinstance(value, Sum | Some):
75
+ wrapped_value = value
76
+
77
+ if isinstance(value, Sum):
78
+ value = value.v
79
+
80
+ if isinstance(value, Some):
81
+ value = value.value
82
+
83
+ return wrapped_value if wrapped_value is not None else value
84
+
85
+
86
+ def wrap_value(value: typing.Any, type_: typing.Any, /) -> typing.Any:
87
+ args = [type_]
88
+ types = []
89
+
90
+ while args:
91
+ arg = args.pop(0)
92
+ origin_arg = typing.get_origin(arg) or arg
93
+
94
+ if issubclass(origin_arg, Sum | Option):
95
+ args.extend(typing.get_args(arg))
96
+ types.append(Some if issubclass(origin_arg, Option) else arg) # type: ignore
97
+
98
+ result = value
99
+
100
+ for t in types[::-1]:
101
+ result = t(result)
102
+
103
+ return result
104
+
105
+
106
+ def to_cute(
107
+ cute_cls: type[BaseCute],
108
+ field: str,
109
+ value: typing.Any,
110
+ bound_api: API,
111
+ ) -> typing.Any:
112
+ maybe_wrapped_value = maybe_wrapped(value)
113
+ is_wrapped_value = isinstance(maybe_wrapped_value, Sum | Some)
114
+ cute_annotations = cute_cls.__cute_annotations__
115
+
116
+ if cute_annotations is None:
117
+ cute_annotations = get_cute_annotations(get_class_annotations(cute_cls))
118
+
119
+ cute = cute_annotations[field].from_update(
120
+ maybe_wrapped_value._value if is_wrapped_value else maybe_wrapped_value,
121
+ bound_api=bound_api,
122
+ )
123
+ return wrap_value(cute, cute_cls.__annotations__[field]) if is_wrapped_value else cute
124
+
125
+
126
+ class BaseCute[T: Model = typing.Any](Model):
127
+ def __init_subclass__(cls, *args: typing.Any, **kwargs: typing.Any) -> None:
128
+ cls.__is_resolved_annotations__ = False
129
+ cls.__cute_annotations__ = None
130
+ super().__init_subclass__(*args, **kwargs)
131
+
132
+ @classmethod
133
+ def __compose__(cls, update: Update, context: Context) -> typing.Any:
134
+ update_cute = context.update_cute
135
+
136
+ if type(update_cute) is cls:
137
+ return update_cute
138
+
139
+ if type(update_cute.incoming_update) is cls:
140
+ return update_cute.incoming_update
141
+
142
+ raise NodeError(f"Incoming update is not `{fullname(cls)}`.")
143
+
144
+ @classmethod
145
+ def from_update(cls, update: Update, bound_api: API) -> typing.Self:
146
+ if not cls.__is_resolved_annotations__:
147
+ cls.__is_resolved_annotations__ = True
148
+ cls.__annotations__ = get_class_annotations(cls)
149
+
150
+ if cls.__cute_annotations__ is None:
151
+ cls.__cute_annotations__ = get_cute_annotations(cls.__annotations__)
152
+
153
+ cute = cls(
154
+ **{
155
+ field: to_cute(cls, field, value, bound_api) if field in cls.__cute_annotations__ else value
156
+ for field, value in update.to_dict().items()
157
+ },
158
+ )._set_bound_api(api=bound_api)
159
+ return cute.bind_raw_update(update) if isinstance(update, Update) else cute
160
+
161
+ @cached_property
162
+ def bound_api(self) -> API: ...
163
+
164
+ @property
165
+ def api(self) -> API:
166
+ return self.bound_api
167
+
168
+ @property
169
+ def ctx_api(self) -> API:
170
+ return self.bound_api
171
+
172
+ @property
173
+ def raw_update(self) -> Update:
174
+ return self.get_raw_update().expect(
175
+ AttributeError(f"Cute model `{type(self).__name__}` has no bound `Update` object."),
176
+ )
177
+
178
+ def get_raw_update(self) -> Option[Update]:
179
+ return from_optional(self.__dict__.get(RAW_UPDATE_BIND_KEY))
180
+
181
+ def bind_raw_update(self, raw_update: Update, /) -> typing.Self:
182
+ cuties = [self]
183
+
184
+ if isinstance(self, Update) and isinstance(cute := self.incoming_update, BaseCute):
185
+ cuties.append(cute)
186
+
187
+ for cute in cuties:
188
+ cute.__dict__[RAW_UPDATE_BIND_KEY] = raw_update # type: ignore
189
+
190
+ return self
191
+
192
+ def to_dict(
193
+ self,
194
+ *,
195
+ exclude_fields: set[str] | None = None,
196
+ full: bool = False,
197
+ ) -> dict[str, typing.Any]:
198
+ return self._to_dict(
199
+ "model_as_dict" if not full else "model_as_full_dict",
200
+ exclude_fields=exclude_fields or set(),
201
+ full=full,
202
+ )
203
+
204
+ def to_full_dict(self, *, exclude_fields: set[str] | None = None) -> dict[str, typing.Any]:
205
+ return self.to_dict(exclude_fields=exclude_fields, full=True)
206
+
207
+ def _set_bound_api(self, api: API) -> typing.Self:
208
+ self.__dict__[BOUND_API_KEY] = api # type: ignore
209
+ return self
210
+
211
+ def _to_dict(self, dct_name: str, exclude_fields: set[str], full: bool) -> dict[str, typing.Any]:
212
+ if dct_name not in self.__dict__:
213
+ dct = struct_asdict(self)
214
+ self.__dict__[dct_name] = dct if not full else encoder.to_builtins(dct, order="deterministic") # type: ignore
215
+
216
+ if not exclude_fields:
217
+ return self.__dict__[dct_name]
218
+
219
+ return {key: value for key, value in self.__dict__[dct_name].items() if key not in exclude_fields}
220
+
221
+
222
+ class BaseShortcuts[T: BaseCute = typing.Any]:
223
+ @cached_property
224
+ def cute(self) -> typing.Self:
225
+ return self
226
+
227
+
228
+ __all__ = ("BaseCute", "BaseShortcuts", "compose_method_params", "shortcut")
@@ -0,0 +1,49 @@
1
+ import typing
2
+
3
+ from kungfu.library.monad.option import Option
4
+
5
+ from telegrinder.api.api import API
6
+ from telegrinder.bot.dispatch.context import Context
7
+ from telegrinder.model import Model
8
+ from telegrinder.tools.magic.shortcut import shortcut
9
+ from telegrinder.types.objects import Update
10
+
11
+ def compose_method_params[Cute: BaseCute](
12
+ params: dict[str, typing.Any],
13
+ update: Cute,
14
+ *,
15
+ default_params: set[str | tuple[str, str]] | None = None,
16
+ validators: dict[str, typing.Callable[[Cute], bool]] | None = None,
17
+ ) -> dict[str, typing.Any]: ...
18
+
19
+ class BaseShortcuts[Cute: BaseCute[typing.Any] = typing.Any]:
20
+ cute: typing.Final[Cute]
21
+
22
+ class BaseCute[T: Model = typing.Any](Model):
23
+ api: typing.Final[API]
24
+ """`API` bound to the cute model."""
25
+
26
+ ctx_api: typing.Final[API]
27
+ """Alias for `api`."""
28
+
29
+ raw_update: typing.Final[Update]
30
+ """`Update` bound to the cute model."""
31
+
32
+ @classmethod
33
+ def __compose__(cls, update: Update, context: Context) -> typing.Self: ...
34
+ @classmethod
35
+ def from_update(cls, update: T, bound_api: API) -> typing.Self: ...
36
+ def bind_raw_update(self, raw_update: Update, /) -> typing.Self: ...
37
+ def get_raw_update(self) -> Option[Update]: ...
38
+ def to_dict(
39
+ self,
40
+ *,
41
+ exclude_fields: set[str] | None = None,
42
+ ) -> dict[str, typing.Any]: ...
43
+ def to_full_dict(
44
+ self,
45
+ *,
46
+ exclude_fields: set[str] | None = None,
47
+ ) -> dict[str, typing.Any]: ...
48
+
49
+ __all__ = ("BaseCute", "BaseShortcuts", "compose_method_params", "shortcut")
@@ -0,0 +1,9 @@
1
+ from telegrinder.bot.cute_types.base import BaseCute
2
+ from telegrinder.types.objects import BusinessConnection
3
+
4
+
5
+ class BusinessConnectionCute(BaseCute[BusinessConnection], BusinessConnection, kw_only=True):
6
+ pass
7
+
8
+
9
+ __all__ = ("BusinessConnectionCute",)
@@ -0,0 +1,9 @@
1
+ from telegrinder.bot.cute_types.base import BaseCute
2
+ from telegrinder.types.objects import BusinessMessagesDeleted
3
+
4
+
5
+ class BusinessMessagesDeletedCute(BaseCute[BusinessMessagesDeleted], BusinessMessagesDeleted, kw_only=True):
6
+ pass
7
+
8
+
9
+ __all__ = ("BusinessMessagesDeletedCute",)