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.
- telegrinder/__init__.py +258 -0
- telegrinder/__meta__.py +1 -0
- telegrinder/api/__init__.py +15 -0
- telegrinder/api/api.py +175 -0
- telegrinder/api/error.py +50 -0
- telegrinder/api/response.py +23 -0
- telegrinder/api/token.py +30 -0
- telegrinder/api/validators.py +30 -0
- telegrinder/bot/__init__.py +144 -0
- telegrinder/bot/bot.py +70 -0
- telegrinder/bot/cute_types/__init__.py +41 -0
- telegrinder/bot/cute_types/base.py +228 -0
- telegrinder/bot/cute_types/base.pyi +49 -0
- telegrinder/bot/cute_types/business_connection.py +9 -0
- telegrinder/bot/cute_types/business_messages_deleted.py +9 -0
- telegrinder/bot/cute_types/callback_query.py +248 -0
- telegrinder/bot/cute_types/chat_boost_removed.py +9 -0
- telegrinder/bot/cute_types/chat_boost_updated.py +9 -0
- telegrinder/bot/cute_types/chat_join_request.py +59 -0
- telegrinder/bot/cute_types/chat_member_updated.py +158 -0
- telegrinder/bot/cute_types/chosen_inline_result.py +11 -0
- telegrinder/bot/cute_types/inline_query.py +41 -0
- telegrinder/bot/cute_types/message.py +2809 -0
- telegrinder/bot/cute_types/message_reaction_count_updated.py +9 -0
- telegrinder/bot/cute_types/message_reaction_updated.py +9 -0
- telegrinder/bot/cute_types/paid_media_purchased.py +11 -0
- telegrinder/bot/cute_types/poll.py +9 -0
- telegrinder/bot/cute_types/poll_answer.py +9 -0
- telegrinder/bot/cute_types/pre_checkout_query.py +36 -0
- telegrinder/bot/cute_types/shipping_query.py +11 -0
- telegrinder/bot/cute_types/update.py +209 -0
- telegrinder/bot/cute_types/utils.py +141 -0
- telegrinder/bot/dispatch/__init__.py +99 -0
- telegrinder/bot/dispatch/abc.py +74 -0
- telegrinder/bot/dispatch/action.py +99 -0
- telegrinder/bot/dispatch/context.py +162 -0
- telegrinder/bot/dispatch/dispatch.py +362 -0
- telegrinder/bot/dispatch/handler/__init__.py +23 -0
- telegrinder/bot/dispatch/handler/abc.py +25 -0
- telegrinder/bot/dispatch/handler/audio_reply.py +43 -0
- telegrinder/bot/dispatch/handler/base.py +34 -0
- telegrinder/bot/dispatch/handler/document_reply.py +43 -0
- telegrinder/bot/dispatch/handler/func.py +73 -0
- telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
- telegrinder/bot/dispatch/handler/message_reply.py +35 -0
- telegrinder/bot/dispatch/handler/photo_reply.py +43 -0
- telegrinder/bot/dispatch/handler/sticker_reply.py +36 -0
- telegrinder/bot/dispatch/handler/video_reply.py +43 -0
- telegrinder/bot/dispatch/middleware/__init__.py +13 -0
- telegrinder/bot/dispatch/middleware/abc.py +112 -0
- telegrinder/bot/dispatch/middleware/box.py +32 -0
- telegrinder/bot/dispatch/middleware/filter.py +88 -0
- telegrinder/bot/dispatch/middleware/media_group.py +69 -0
- telegrinder/bot/dispatch/process.py +93 -0
- telegrinder/bot/dispatch/return_manager/__init__.py +21 -0
- telegrinder/bot/dispatch/return_manager/abc.py +107 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +34 -0
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/utils.py +20 -0
- telegrinder/bot/dispatch/router/__init__.py +4 -0
- telegrinder/bot/dispatch/router/abc.py +15 -0
- telegrinder/bot/dispatch/router/base.py +154 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -0
- telegrinder/bot/dispatch/view/abc.py +15 -0
- telegrinder/bot/dispatch/view/base.py +226 -0
- telegrinder/bot/dispatch/view/box.py +207 -0
- telegrinder/bot/dispatch/view/media_group.py +25 -0
- telegrinder/bot/dispatch/waiter_machine/__init__.py +25 -0
- telegrinder/bot/dispatch/waiter_machine/actions.py +16 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +13 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +53 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +61 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +49 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +264 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +77 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +105 -0
- telegrinder/bot/polling/__init__.py +4 -0
- telegrinder/bot/polling/abc.py +25 -0
- telegrinder/bot/polling/error_handler.py +93 -0
- telegrinder/bot/polling/polling.py +167 -0
- telegrinder/bot/polling/utils.py +12 -0
- telegrinder/bot/rules/__init__.py +166 -0
- telegrinder/bot/rules/abc.py +150 -0
- telegrinder/bot/rules/button.py +20 -0
- telegrinder/bot/rules/callback_data.py +109 -0
- telegrinder/bot/rules/chat_join.py +28 -0
- telegrinder/bot/rules/chat_member_updated.py +145 -0
- telegrinder/bot/rules/command.py +137 -0
- telegrinder/bot/rules/enum_text.py +29 -0
- telegrinder/bot/rules/func.py +21 -0
- telegrinder/bot/rules/fuzzy.py +21 -0
- telegrinder/bot/rules/inline.py +45 -0
- telegrinder/bot/rules/integer.py +19 -0
- telegrinder/bot/rules/is_from.py +213 -0
- telegrinder/bot/rules/logic.py +22 -0
- telegrinder/bot/rules/magic.py +60 -0
- telegrinder/bot/rules/markup.py +51 -0
- telegrinder/bot/rules/media.py +13 -0
- telegrinder/bot/rules/mention.py +15 -0
- telegrinder/bot/rules/message_entities.py +37 -0
- telegrinder/bot/rules/node.py +43 -0
- telegrinder/bot/rules/payload.py +89 -0
- telegrinder/bot/rules/payment_invoice.py +14 -0
- telegrinder/bot/rules/regex.py +34 -0
- telegrinder/bot/rules/rule_enum.py +71 -0
- telegrinder/bot/rules/start.py +73 -0
- telegrinder/bot/rules/state.py +35 -0
- telegrinder/bot/rules/text.py +27 -0
- telegrinder/bot/rules/update.py +14 -0
- telegrinder/bot/scenario/__init__.py +5 -0
- telegrinder/bot/scenario/abc.py +16 -0
- telegrinder/bot/scenario/checkbox.py +183 -0
- telegrinder/bot/scenario/choice.py +44 -0
- telegrinder/client/__init__.py +11 -0
- telegrinder/client/abc.py +136 -0
- telegrinder/client/form_data.py +34 -0
- telegrinder/client/rnet.py +198 -0
- telegrinder/model.py +133 -0
- telegrinder/model.pyi +57 -0
- telegrinder/modules.py +1081 -0
- telegrinder/msgspec_utils/__init__.py +42 -0
- telegrinder/msgspec_utils/abc.py +16 -0
- telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
- telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
- telegrinder/msgspec_utils/custom_types/enum_meta.py +61 -0
- telegrinder/msgspec_utils/custom_types/literal.py +25 -0
- telegrinder/msgspec_utils/custom_types/option.py +17 -0
- telegrinder/msgspec_utils/decoder.py +388 -0
- telegrinder/msgspec_utils/encoder.py +204 -0
- telegrinder/msgspec_utils/json.py +15 -0
- telegrinder/msgspec_utils/tools.py +80 -0
- telegrinder/node/__init__.py +80 -0
- telegrinder/node/compose.py +193 -0
- telegrinder/node/nodes/__init__.py +96 -0
- telegrinder/node/nodes/attachment.py +169 -0
- telegrinder/node/nodes/callback_query.py +25 -0
- telegrinder/node/nodes/channel.py +97 -0
- telegrinder/node/nodes/command.py +33 -0
- telegrinder/node/nodes/error.py +43 -0
- telegrinder/node/nodes/event.py +70 -0
- telegrinder/node/nodes/file.py +39 -0
- telegrinder/node/nodes/global_node.py +66 -0
- telegrinder/node/nodes/i18n.py +110 -0
- telegrinder/node/nodes/me.py +26 -0
- telegrinder/node/nodes/message_entities.py +15 -0
- telegrinder/node/nodes/payload.py +84 -0
- telegrinder/node/nodes/reply_message.py +14 -0
- telegrinder/node/nodes/source.py +172 -0
- telegrinder/node/nodes/state_mutator.py +71 -0
- telegrinder/node/nodes/text.py +62 -0
- telegrinder/node/scope.py +88 -0
- telegrinder/node/utils.py +38 -0
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -0
- telegrinder/tools/__init__.py +183 -0
- telegrinder/tools/aio.py +147 -0
- telegrinder/tools/final.py +21 -0
- telegrinder/tools/formatting/__init__.py +85 -0
- telegrinder/tools/formatting/deep_links/__init__.py +39 -0
- telegrinder/tools/formatting/deep_links/links.py +468 -0
- telegrinder/tools/formatting/deep_links/parsing.py +88 -0
- telegrinder/tools/formatting/deep_links/validators.py +8 -0
- telegrinder/tools/formatting/html.py +241 -0
- telegrinder/tools/fullname.py +82 -0
- telegrinder/tools/global_context/__init__.py +13 -0
- telegrinder/tools/global_context/abc.py +63 -0
- telegrinder/tools/global_context/builtin_context.py +45 -0
- telegrinder/tools/global_context/global_context.py +614 -0
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard/__init__.py +6 -0
- telegrinder/tools/keyboard/abc.py +84 -0
- telegrinder/tools/keyboard/base.py +108 -0
- telegrinder/tools/keyboard/button.py +181 -0
- telegrinder/tools/keyboard/data.py +31 -0
- telegrinder/tools/keyboard/keyboard.py +160 -0
- telegrinder/tools/keyboard/utils.py +95 -0
- telegrinder/tools/lifespan.py +188 -0
- telegrinder/tools/limited_dict.py +35 -0
- telegrinder/tools/loop_wrapper.py +271 -0
- telegrinder/tools/magic/__init__.py +29 -0
- telegrinder/tools/magic/annotations.py +172 -0
- telegrinder/tools/magic/descriptors.py +57 -0
- telegrinder/tools/magic/function.py +254 -0
- telegrinder/tools/magic/inspect.py +16 -0
- telegrinder/tools/magic/shortcut.py +107 -0
- telegrinder/tools/member_descriptor_proxy.py +95 -0
- telegrinder/tools/parse_mode.py +12 -0
- telegrinder/tools/serialization/__init__.py +5 -0
- telegrinder/tools/serialization/abc.py +34 -0
- telegrinder/tools/serialization/json_ser.py +60 -0
- telegrinder/tools/serialization/msgpack_ser.py +197 -0
- telegrinder/tools/serialization/utils.py +18 -0
- telegrinder/tools/singleton/__init__.py +4 -0
- telegrinder/tools/singleton/abc.py +14 -0
- telegrinder/tools/singleton/singleton.py +18 -0
- telegrinder/tools/state_mutator/__init__.py +4 -0
- telegrinder/tools/state_mutator/mutation.py +85 -0
- telegrinder/tools/state_storage/__init__.py +4 -0
- telegrinder/tools/state_storage/abc.py +38 -0
- telegrinder/tools/state_storage/memory.py +27 -0
- telegrinder/tools/strings.py +22 -0
- telegrinder/types/__init__.py +323 -0
- telegrinder/types/enums.py +754 -0
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +6143 -0
- telegrinder/types/methods_utils.py +66 -0
- telegrinder/types/objects.py +8184 -0
- telegrinder/types/webapp.py +129 -0
- telegrinder/verification_utils.py +35 -0
- telegrinder-1.0.0rc1.dist-info/METADATA +166 -0
- telegrinder-1.0.0rc1.dist-info/RECORD +215 -0
- telegrinder-1.0.0rc1.dist-info/WHEEL +4 -0
- 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 BusinessMessagesDeleted
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BusinessMessagesDeletedCute(BaseCute[BusinessMessagesDeleted], BusinessMessagesDeleted, kw_only=True):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = ("BusinessMessagesDeletedCute",)
|