telegrinder 0.4.1__py3-none-any.whl → 0.5.0__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.
- telegrinder/__init__.py +38 -56
- telegrinder/__meta__.py +1 -0
- telegrinder/api/__init__.py +6 -4
- telegrinder/api/api.py +100 -26
- telegrinder/api/error.py +42 -8
- telegrinder/api/response.py +4 -1
- telegrinder/api/token.py +2 -2
- telegrinder/bot/__init__.py +9 -25
- telegrinder/bot/bot.py +31 -25
- telegrinder/bot/cute_types/__init__.py +0 -0
- telegrinder/bot/cute_types/base.py +103 -61
- telegrinder/bot/cute_types/callback_query.py +447 -400
- telegrinder/bot/cute_types/chat_join_request.py +59 -62
- telegrinder/bot/cute_types/chat_member_updated.py +154 -157
- telegrinder/bot/cute_types/inline_query.py +41 -44
- telegrinder/bot/cute_types/message.py +2621 -2590
- telegrinder/bot/cute_types/pre_checkout_query.py +38 -42
- telegrinder/bot/cute_types/update.py +1 -8
- telegrinder/bot/cute_types/utils.py +1 -1
- telegrinder/bot/dispatch/__init__.py +10 -15
- telegrinder/bot/dispatch/abc.py +12 -11
- telegrinder/bot/dispatch/action.py +104 -0
- telegrinder/bot/dispatch/context.py +32 -26
- telegrinder/bot/dispatch/dispatch.py +61 -134
- telegrinder/bot/dispatch/handler/__init__.py +2 -0
- telegrinder/bot/dispatch/handler/abc.py +10 -8
- telegrinder/bot/dispatch/handler/audio_reply.py +2 -3
- telegrinder/bot/dispatch/handler/base.py +10 -33
- telegrinder/bot/dispatch/handler/document_reply.py +2 -3
- telegrinder/bot/dispatch/handler/func.py +55 -87
- telegrinder/bot/dispatch/handler/media_group_reply.py +2 -3
- telegrinder/bot/dispatch/handler/message_reply.py +2 -3
- telegrinder/bot/dispatch/handler/photo_reply.py +2 -3
- telegrinder/bot/dispatch/handler/sticker_reply.py +2 -3
- telegrinder/bot/dispatch/handler/video_reply.py +2 -3
- telegrinder/bot/dispatch/middleware/__init__.py +0 -0
- telegrinder/bot/dispatch/middleware/abc.py +79 -55
- telegrinder/bot/dispatch/middleware/global_middleware.py +18 -33
- telegrinder/bot/dispatch/process.py +84 -105
- telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
- telegrinder/bot/dispatch/return_manager/abc.py +102 -65
- telegrinder/bot/dispatch/return_manager/callback_query.py +4 -5
- telegrinder/bot/dispatch/return_manager/inline_query.py +3 -4
- telegrinder/bot/dispatch/return_manager/message.py +8 -10
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +4 -5
- telegrinder/bot/dispatch/view/__init__.py +4 -4
- telegrinder/bot/dispatch/view/abc.py +6 -16
- telegrinder/bot/dispatch/view/base.py +54 -178
- telegrinder/bot/dispatch/view/box.py +19 -18
- telegrinder/bot/dispatch/view/callback_query.py +4 -8
- telegrinder/bot/dispatch/view/chat_join_request.py +5 -6
- telegrinder/bot/dispatch/view/chat_member.py +5 -25
- telegrinder/bot/dispatch/view/error.py +9 -0
- telegrinder/bot/dispatch/view/inline_query.py +4 -8
- telegrinder/bot/dispatch/view/message.py +5 -25
- telegrinder/bot/dispatch/view/pre_checkout_query.py +4 -8
- telegrinder/bot/dispatch/view/raw.py +3 -109
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -5
- telegrinder/bot/dispatch/waiter_machine/actions.py +6 -4
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +1 -3
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +11 -7
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +43 -60
- telegrinder/bot/dispatch/waiter_machine/middleware.py +19 -23
- telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -5
- telegrinder/bot/polling/__init__.py +0 -0
- telegrinder/bot/polling/abc.py +0 -0
- telegrinder/bot/polling/polling.py +209 -88
- telegrinder/bot/rules/__init__.py +3 -16
- telegrinder/bot/rules/abc.py +42 -122
- telegrinder/bot/rules/callback_data.py +29 -49
- telegrinder/bot/rules/chat_join.py +5 -23
- telegrinder/bot/rules/command.py +8 -4
- telegrinder/bot/rules/enum_text.py +3 -4
- telegrinder/bot/rules/func.py +7 -14
- telegrinder/bot/rules/fuzzy.py +3 -4
- telegrinder/bot/rules/inline.py +8 -20
- telegrinder/bot/rules/integer.py +2 -3
- telegrinder/bot/rules/is_from.py +12 -11
- telegrinder/bot/rules/logic.py +11 -5
- telegrinder/bot/rules/markup.py +22 -14
- telegrinder/bot/rules/mention.py +8 -7
- telegrinder/bot/rules/message_entities.py +8 -4
- telegrinder/bot/rules/node.py +23 -12
- telegrinder/bot/rules/payload.py +5 -4
- telegrinder/bot/rules/payment_invoice.py +6 -21
- telegrinder/bot/rules/regex.py +2 -4
- telegrinder/bot/rules/rule_enum.py +8 -7
- telegrinder/bot/rules/start.py +5 -6
- telegrinder/bot/rules/state.py +1 -1
- telegrinder/bot/rules/text.py +4 -15
- telegrinder/bot/rules/update.py +3 -4
- telegrinder/bot/scenario/__init__.py +0 -0
- telegrinder/bot/scenario/abc.py +6 -5
- telegrinder/bot/scenario/checkbox.py +1 -1
- telegrinder/bot/scenario/choice.py +30 -39
- telegrinder/client/__init__.py +3 -5
- telegrinder/client/abc.py +11 -6
- telegrinder/client/aiohttp.py +141 -27
- telegrinder/client/form_data.py +1 -1
- telegrinder/model.py +61 -89
- telegrinder/modules.py +325 -102
- telegrinder/msgspec_utils/__init__.py +40 -0
- telegrinder/msgspec_utils/abc.py +18 -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 +43 -0
- telegrinder/msgspec_utils/custom_types/literal.py +25 -0
- telegrinder/msgspec_utils/custom_types/option.py +17 -0
- telegrinder/msgspec_utils/decoder.py +389 -0
- telegrinder/msgspec_utils/encoder.py +206 -0
- telegrinder/{msgspec_json.py → msgspec_utils/json.py} +6 -5
- telegrinder/msgspec_utils/tools.py +75 -0
- telegrinder/node/__init__.py +24 -7
- telegrinder/node/attachment.py +1 -0
- telegrinder/node/base.py +154 -72
- telegrinder/node/callback_query.py +5 -5
- telegrinder/node/collection.py +39 -0
- telegrinder/node/command.py +1 -2
- telegrinder/node/composer.py +121 -72
- telegrinder/node/container.py +11 -8
- telegrinder/node/context.py +48 -0
- telegrinder/node/either.py +27 -40
- telegrinder/node/error.py +41 -0
- telegrinder/node/event.py +37 -11
- telegrinder/node/exceptions.py +7 -0
- telegrinder/node/file.py +0 -0
- telegrinder/node/i18n.py +108 -0
- telegrinder/node/me.py +3 -2
- telegrinder/node/payload.py +1 -1
- telegrinder/node/polymorphic.py +63 -28
- telegrinder/node/reply_message.py +12 -0
- telegrinder/node/rule.py +6 -13
- telegrinder/node/scope.py +14 -5
- telegrinder/node/session.py +53 -0
- telegrinder/node/source.py +41 -9
- telegrinder/node/text.py +1 -2
- telegrinder/node/tools/__init__.py +0 -0
- telegrinder/node/tools/generator.py +3 -5
- telegrinder/node/utility.py +16 -0
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +0 -0
- telegrinder/tools/__init__.py +48 -88
- telegrinder/tools/aio.py +103 -0
- telegrinder/tools/callback_data_serialization/__init__.py +5 -0
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/abc.py +0 -0
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/json_ser.py +2 -3
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/msgpack_ser.py +45 -27
- telegrinder/tools/final.py +21 -0
- telegrinder/tools/formatting/__init__.py +2 -18
- telegrinder/tools/formatting/deep_links/__init__.py +39 -0
- telegrinder/tools/formatting/{deep_links.py → deep_links/links.py} +12 -85
- telegrinder/tools/formatting/deep_links/parsing.py +90 -0
- telegrinder/tools/formatting/deep_links/validators.py +8 -0
- telegrinder/tools/formatting/html_formatter.py +18 -45
- telegrinder/tools/fullname.py +83 -0
- telegrinder/tools/global_context/__init__.py +4 -3
- telegrinder/tools/global_context/abc.py +17 -14
- telegrinder/tools/global_context/builtin_context.py +39 -0
- telegrinder/tools/global_context/global_context.py +138 -39
- telegrinder/tools/input_file_directory.py +0 -0
- telegrinder/tools/keyboard/__init__.py +39 -0
- telegrinder/tools/keyboard/abc.py +159 -0
- telegrinder/tools/keyboard/base.py +77 -0
- telegrinder/tools/keyboard/buttons/__init__.py +14 -0
- telegrinder/tools/keyboard/buttons/base.py +18 -0
- telegrinder/tools/{buttons.py → keyboard/buttons/buttons.py} +71 -23
- telegrinder/tools/keyboard/buttons/static_buttons.py +56 -0
- telegrinder/tools/keyboard/buttons/tools.py +18 -0
- telegrinder/tools/keyboard/data.py +20 -0
- telegrinder/tools/keyboard/keyboard.py +131 -0
- telegrinder/tools/keyboard/static_keyboard.py +83 -0
- telegrinder/tools/lifespan.py +87 -51
- telegrinder/tools/limited_dict.py +4 -1
- telegrinder/tools/loop_wrapper.py +332 -0
- telegrinder/tools/magic/__init__.py +32 -0
- telegrinder/tools/magic/annotations.py +165 -0
- telegrinder/tools/magic/dictionary.py +20 -0
- telegrinder/tools/magic/function.py +246 -0
- telegrinder/tools/magic/shortcut.py +111 -0
- telegrinder/tools/parse_mode.py +9 -3
- telegrinder/tools/singleton/__init__.py +4 -0
- telegrinder/tools/singleton/abc.py +14 -0
- telegrinder/tools/singleton/singleton.py +18 -0
- telegrinder/tools/state_storage/__init__.py +0 -0
- telegrinder/tools/state_storage/abc.py +6 -1
- telegrinder/tools/state_storage/memory.py +1 -1
- telegrinder/tools/strings.py +0 -0
- telegrinder/types/__init__.py +307 -268
- telegrinder/types/enums.py +64 -37
- telegrinder/types/input_file.py +3 -3
- telegrinder/types/methods.py +5699 -5055
- telegrinder/types/methods_utils.py +62 -0
- telegrinder/types/objects.py +7846 -7058
- telegrinder/verification_utils.py +3 -1
- telegrinder-0.5.0.dist-info/METADATA +162 -0
- telegrinder-0.5.0.dist-info/RECORD +200 -0
- {telegrinder-0.4.1.dist-info → telegrinder-0.5.0.dist-info}/WHEEL +1 -1
- {telegrinder-0.4.1.dist-info → telegrinder-0.5.0.dist-info/licenses}/LICENSE +2 -2
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +0 -20
- telegrinder/bot/rules/id.py +0 -24
- telegrinder/bot/rules/message.py +0 -15
- telegrinder/client/sonic.py +0 -212
- telegrinder/msgspec_utils.py +0 -478
- telegrinder/tools/adapter/__init__.py +0 -19
- telegrinder/tools/adapter/abc.py +0 -49
- telegrinder/tools/adapter/dataclass.py +0 -56
- telegrinder/tools/adapter/errors.py +0 -5
- telegrinder/tools/adapter/event.py +0 -63
- telegrinder/tools/adapter/node.py +0 -46
- telegrinder/tools/adapter/raw_event.py +0 -27
- telegrinder/tools/adapter/raw_update.py +0 -30
- telegrinder/tools/callback_data_serilization/__init__.py +0 -5
- telegrinder/tools/error_handler/__init__.py +0 -10
- telegrinder/tools/error_handler/abc.py +0 -30
- telegrinder/tools/error_handler/error.py +0 -9
- telegrinder/tools/error_handler/error_handler.py +0 -179
- telegrinder/tools/formatting/spec_html_formats.py +0 -75
- telegrinder/tools/functional.py +0 -8
- telegrinder/tools/global_context/telegrinder_ctx.py +0 -27
- telegrinder/tools/i18n/__init__.py +0 -12
- telegrinder/tools/i18n/abc.py +0 -32
- telegrinder/tools/i18n/middleware/__init__.py +0 -3
- telegrinder/tools/i18n/middleware/abc.py +0 -22
- telegrinder/tools/i18n/simple.py +0 -43
- telegrinder/tools/keyboard.py +0 -132
- telegrinder/tools/loop_wrapper/__init__.py +0 -4
- telegrinder/tools/loop_wrapper/abc.py +0 -20
- telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -169
- telegrinder/tools/magic.py +0 -344
- telegrinder-0.4.1.dist-info/METADATA +0 -143
- telegrinder-0.4.1.dist-info/RECORD +0 -182
telegrinder/__init__.py
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Modern visionary telegram bot framework.
|
|
1
|
+
"""Modern visionary telegram bot framework.
|
|
4
2
|
|
|
5
3
|
* Type hinted
|
|
6
4
|
* Customizable and extensible
|
|
7
5
|
* Ready to use scenarios and rules
|
|
8
6
|
* Fast models built on [msgspec](https://github.com/jcrist/msgspec)
|
|
9
7
|
* Both low-level and high-level API
|
|
10
|
-
* Support [optional dependecies](https://github.com/timoniq/telegrinder/blob/dev/docs/guide/optional_dependencies.md)
|
|
11
8
|
|
|
12
9
|
Basic example:
|
|
13
10
|
|
|
@@ -16,13 +13,13 @@ from telegrinder import API, Message, Telegrinder, Token
|
|
|
16
13
|
from telegrinder.modules import logger
|
|
17
14
|
from telegrinder.rules import Text
|
|
18
15
|
|
|
16
|
+
logger.set_level("INFO")
|
|
19
17
|
api = API(token=Token("123:token"))
|
|
20
18
|
bot = Telegrinder(api)
|
|
21
|
-
logger.set_level("INFO")
|
|
22
19
|
|
|
23
20
|
|
|
24
21
|
@bot.on.message(Text("/start"))
|
|
25
|
-
async def start(message: Message):
|
|
22
|
+
async def start(message: Message) -> None:
|
|
26
23
|
me = (await api.get_me()).unwrap()
|
|
27
24
|
await message.answer(f"Hello, {message.from_user.full_name}! I'm {me.full_name}.")
|
|
28
25
|
|
|
@@ -48,19 +45,15 @@ from .bot import (
|
|
|
48
45
|
ABCReturnManager,
|
|
49
46
|
ABCRule,
|
|
50
47
|
ABCScenario,
|
|
51
|
-
ABCStateView,
|
|
52
48
|
ABCView,
|
|
53
49
|
AudioReplyHandler,
|
|
54
50
|
BaseCute,
|
|
55
51
|
BaseReturnManager,
|
|
56
|
-
BaseStateView,
|
|
57
52
|
BaseView,
|
|
58
53
|
CallbackQueryCute,
|
|
59
54
|
CallbackQueryReturnManager,
|
|
60
|
-
CallbackQueryRule,
|
|
61
55
|
CallbackQueryView,
|
|
62
56
|
ChatJoinRequestCute,
|
|
63
|
-
ChatJoinRequestRule,
|
|
64
57
|
ChatJoinRequestView,
|
|
65
58
|
ChatMemberUpdatedCute,
|
|
66
59
|
ChatMemberView,
|
|
@@ -73,12 +66,10 @@ from .bot import (
|
|
|
73
66
|
Hasher,
|
|
74
67
|
InlineQueryCute,
|
|
75
68
|
InlineQueryReturnManager,
|
|
76
|
-
InlineQueryRule,
|
|
77
69
|
MediaGroupReplyHandler,
|
|
78
70
|
MessageCute,
|
|
79
71
|
MessageReplyHandler,
|
|
80
72
|
MessageReturnManager,
|
|
81
|
-
MessageRule,
|
|
82
73
|
MessageView,
|
|
83
74
|
PhotoReplyHandler,
|
|
84
75
|
Polling,
|
|
@@ -87,40 +78,40 @@ from .bot import (
|
|
|
87
78
|
PreCheckoutQueryView,
|
|
88
79
|
RawEventView,
|
|
89
80
|
ShortState,
|
|
90
|
-
StateViewHasher,
|
|
91
81
|
StickerReplyHandler,
|
|
92
82
|
Telegrinder,
|
|
93
83
|
UpdateCute,
|
|
94
84
|
VideoReplyHandler,
|
|
95
85
|
ViewBox,
|
|
96
86
|
WaiterMachine,
|
|
87
|
+
action,
|
|
97
88
|
register_manager,
|
|
98
89
|
)
|
|
99
|
-
from .client import ABCClient, AiohttpClient
|
|
100
|
-
from .model import Model
|
|
90
|
+
from .client import ABCClient, AiohttpClient
|
|
91
|
+
from .model import Model, field
|
|
101
92
|
from .modules import logger
|
|
102
|
-
from .tools.error_handler import ABCErrorHandler, ErrorHandler
|
|
103
93
|
from .tools.formatting import HTMLFormatter
|
|
104
|
-
from .tools.global_context import ABCGlobalContext,
|
|
105
|
-
from .tools.i18n import (
|
|
106
|
-
ABCTranslator,
|
|
107
|
-
ABCTranslatorMiddleware,
|
|
108
|
-
I18nEnum,
|
|
109
|
-
SimpleI18n,
|
|
110
|
-
SimpleTranslator,
|
|
111
|
-
)
|
|
94
|
+
from .tools.global_context import ABCGlobalContext, GlobalContext, TelegrinderContext
|
|
112
95
|
from .tools.input_file_directory import InputFileDirectory
|
|
113
96
|
from .tools.keyboard import (
|
|
114
|
-
|
|
97
|
+
ABCKeyboard,
|
|
98
|
+
ABCStaticKeyboard,
|
|
99
|
+
BaseButton,
|
|
100
|
+
BaseKeyboard,
|
|
101
|
+
BaseStaticButton,
|
|
102
|
+
BaseStaticKeyboard,
|
|
115
103
|
Button,
|
|
116
104
|
InlineButton,
|
|
117
105
|
InlineKeyboard,
|
|
118
106
|
Keyboard,
|
|
119
107
|
RowButtons,
|
|
108
|
+
StaticButton,
|
|
109
|
+
StaticInlineButton,
|
|
110
|
+
StaticInlineKeyboard,
|
|
111
|
+
StaticKeyboard,
|
|
120
112
|
)
|
|
121
113
|
from .tools.lifespan import Lifespan
|
|
122
|
-
from .tools.loop_wrapper import
|
|
123
|
-
from .tools.magic import cache_translation, get_cached_translation, magic_bundle
|
|
114
|
+
from .tools.loop_wrapper import DelayedTask, LoopWrapper
|
|
124
115
|
from .tools.parse_mode import ParseMode
|
|
125
116
|
from .tools.state_storage import ABCStateStorage, MemoryStateStorage, StateData
|
|
126
117
|
|
|
@@ -135,12 +126,18 @@ Bot: typing.TypeAlias = Telegrinder
|
|
|
135
126
|
|
|
136
127
|
|
|
137
128
|
__all__ = (
|
|
129
|
+
"API",
|
|
130
|
+
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
131
|
+
"CALLBACK_QUERY_FROM_CHAT",
|
|
132
|
+
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
133
|
+
"MESSAGE_FROM_USER",
|
|
134
|
+
"MESSAGE_FROM_USER_IN_CHAT",
|
|
135
|
+
"MESSAGE_IN_CHAT",
|
|
138
136
|
"ABCClient",
|
|
139
137
|
"ABCDispatch",
|
|
140
|
-
"ABCErrorHandler",
|
|
141
138
|
"ABCGlobalContext",
|
|
142
139
|
"ABCHandler",
|
|
143
|
-
"
|
|
140
|
+
"ABCKeyboard",
|
|
144
141
|
"ABCMiddleware",
|
|
145
142
|
"ABCPolling",
|
|
146
143
|
"ABCReturnManager",
|
|
@@ -148,35 +145,28 @@ __all__ = (
|
|
|
148
145
|
"ABCScenario",
|
|
149
146
|
"ABCStateStorage",
|
|
150
147
|
"ABCStateStorage",
|
|
151
|
-
"
|
|
152
|
-
"ABCTranslator",
|
|
153
|
-
"ABCTranslatorMiddleware",
|
|
148
|
+
"ABCStaticKeyboard",
|
|
154
149
|
"ABCView",
|
|
155
|
-
"API",
|
|
156
150
|
"APIError",
|
|
157
151
|
"APIResponse",
|
|
158
152
|
"APIServerError",
|
|
159
153
|
"AiohttpClient",
|
|
160
|
-
"AiosonicClient",
|
|
161
|
-
"AnyMarkup",
|
|
162
154
|
"AudioReplyHandler",
|
|
155
|
+
"BaseButton",
|
|
163
156
|
"BaseCute",
|
|
157
|
+
"BaseKeyboard",
|
|
164
158
|
"BaseReturnManager",
|
|
165
|
-
"
|
|
159
|
+
"BaseStaticButton",
|
|
160
|
+
"BaseStaticKeyboard",
|
|
166
161
|
"BaseView",
|
|
167
162
|
"Bot",
|
|
168
163
|
"Button",
|
|
169
|
-
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
170
|
-
"CALLBACK_QUERY_FROM_CHAT",
|
|
171
|
-
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
172
164
|
"CallbackQuery",
|
|
173
165
|
"CallbackQueryCute",
|
|
174
166
|
"CallbackQueryReturnManager",
|
|
175
|
-
"CallbackQueryRule",
|
|
176
167
|
"CallbackQueryView",
|
|
177
168
|
"ChatJoinRequest",
|
|
178
169
|
"ChatJoinRequestCute",
|
|
179
|
-
"ChatJoinRequestRule",
|
|
180
170
|
"ChatJoinRequestView",
|
|
181
171
|
"ChatMemberUpdated",
|
|
182
172
|
"ChatMemberUpdatedCute",
|
|
@@ -184,29 +174,22 @@ __all__ = (
|
|
|
184
174
|
"Checkbox",
|
|
185
175
|
"Choice",
|
|
186
176
|
"Context",
|
|
187
|
-
"CtxVar",
|
|
188
177
|
"DelayedTask",
|
|
189
178
|
"Dispatch",
|
|
190
179
|
"DocumentReplyHandler",
|
|
191
|
-
"ErrorHandler",
|
|
192
180
|
"FuncHandler",
|
|
193
181
|
"GlobalContext",
|
|
194
182
|
"HTMLFormatter",
|
|
195
183
|
"Hasher",
|
|
196
|
-
"I18nEnum",
|
|
197
184
|
"InlineButton",
|
|
198
185
|
"InlineKeyboard",
|
|
199
186
|
"InlineQuery",
|
|
200
187
|
"InlineQueryCute",
|
|
201
188
|
"InlineQueryReturnManager",
|
|
202
|
-
"InlineQueryRule",
|
|
203
189
|
"InputFileDirectory",
|
|
204
190
|
"Keyboard",
|
|
205
191
|
"Lifespan",
|
|
206
192
|
"LoopWrapper",
|
|
207
|
-
"MESSAGE_FROM_USER",
|
|
208
|
-
"MESSAGE_FROM_USER_IN_CHAT",
|
|
209
|
-
"MESSAGE_IN_CHAT",
|
|
210
193
|
"MediaGroupReplyHandler",
|
|
211
194
|
"MemoryStateStorage",
|
|
212
195
|
"MemoryStateStorage",
|
|
@@ -215,7 +198,6 @@ __all__ = (
|
|
|
215
198
|
"MessageReplyHandler",
|
|
216
199
|
"MessageReplyHandler",
|
|
217
200
|
"MessageReturnManager",
|
|
218
|
-
"MessageRule",
|
|
219
201
|
"MessageView",
|
|
220
202
|
"Model",
|
|
221
203
|
"ParseMode",
|
|
@@ -228,23 +210,23 @@ __all__ = (
|
|
|
228
210
|
"RawEventView",
|
|
229
211
|
"RowButtons",
|
|
230
212
|
"ShortState",
|
|
231
|
-
"SimpleI18n",
|
|
232
|
-
"SimpleTranslator",
|
|
233
213
|
"StateData",
|
|
234
214
|
"StateData",
|
|
235
|
-
"
|
|
215
|
+
"StaticButton",
|
|
216
|
+
"StaticInlineButton",
|
|
217
|
+
"StaticInlineKeyboard",
|
|
218
|
+
"StaticKeyboard",
|
|
236
219
|
"StickerReplyHandler",
|
|
237
220
|
"Telegrinder",
|
|
221
|
+
"TelegrinderContext",
|
|
238
222
|
"Token",
|
|
239
223
|
"Update",
|
|
240
224
|
"UpdateCute",
|
|
241
225
|
"VideoReplyHandler",
|
|
242
226
|
"ViewBox",
|
|
243
227
|
"WaiterMachine",
|
|
244
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"get_cached_translation",
|
|
228
|
+
"action",
|
|
229
|
+
"field",
|
|
247
230
|
"logger",
|
|
248
|
-
"magic_bundle",
|
|
249
231
|
"register_manager",
|
|
250
232
|
)
|
telegrinder/__meta__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.0"
|
telegrinder/api/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from .api import API
|
|
2
|
-
from .error import APIError, APIServerError, InvalidTokenError
|
|
3
|
-
from .response import APIResponse
|
|
4
|
-
from .token import Token
|
|
1
|
+
from telegrinder.api.api import API, compose_data, retryer
|
|
2
|
+
from telegrinder.api.error import APIError, APIServerError, InvalidTokenError
|
|
3
|
+
from telegrinder.api.response import APIResponse
|
|
4
|
+
from telegrinder.api.token import Token
|
|
5
5
|
|
|
6
6
|
__all__ = (
|
|
7
7
|
"API",
|
|
@@ -10,4 +10,6 @@ __all__ = (
|
|
|
10
10
|
"APIServerError",
|
|
11
11
|
"InvalidTokenError",
|
|
12
12
|
"Token",
|
|
13
|
+
"compose_data",
|
|
14
|
+
"retryer",
|
|
13
15
|
)
|
telegrinder/api/api.py
CHANGED
|
@@ -1,50 +1,102 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import pathlib
|
|
5
|
+
from functools import cached_property, wraps
|
|
6
|
+
from http import HTTPStatus
|
|
2
7
|
|
|
3
8
|
import msgspec
|
|
4
9
|
import typing_extensions as typing
|
|
10
|
+
from fntypes.misc import is_ok
|
|
5
11
|
from fntypes.result import Error, Ok, Result
|
|
6
12
|
|
|
7
13
|
from telegrinder.api.error import APIError
|
|
8
14
|
from telegrinder.api.response import APIResponse
|
|
9
15
|
from telegrinder.api.token import Token
|
|
10
16
|
from telegrinder.client import ABCClient, AiohttpClient, MultipartFormProto
|
|
17
|
+
from telegrinder.client.aiohttp import DEFAULT_TIMEOUT
|
|
11
18
|
from telegrinder.model import decoder
|
|
12
19
|
from telegrinder.types.methods import APIMethods
|
|
13
20
|
|
|
14
|
-
HTTPClient = typing.TypeVar("HTTPClient", bound=ABCClient, default=AiohttpClient)
|
|
15
|
-
|
|
16
21
|
type Json = str | int | float | bool | list[Json] | dict[str, Json] | None
|
|
22
|
+
type Data = dict[str, typing.Any]
|
|
23
|
+
type Files = dict[str, tuple[str, bytes]]
|
|
24
|
+
type APIRequestMethod[T: API, **P, R] = typing.Callable[
|
|
25
|
+
typing.Concatenate[T, P],
|
|
26
|
+
typing.Coroutine[typing.Any, typing.Any, Result[R, APIError]],
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
DEFAULT_MAX_RETRIES: typing.Final[int] = 5
|
|
17
30
|
|
|
18
31
|
|
|
19
|
-
def compose_data
|
|
20
|
-
client: ABCClient
|
|
21
|
-
data:
|
|
22
|
-
files:
|
|
23
|
-
) ->
|
|
32
|
+
def compose_data(
|
|
33
|
+
client: ABCClient,
|
|
34
|
+
data: Data,
|
|
35
|
+
files: Files,
|
|
36
|
+
) -> MultipartFormProto:
|
|
24
37
|
if not data and not files:
|
|
25
38
|
return client.multipart_form_factory()
|
|
26
39
|
return client.get_form(data=data, files=files)
|
|
27
40
|
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
def retryer[T: API, **P, R](func: APIRequestMethod[T, P, R], /) -> APIRequestMethod[T, P, R]:
|
|
43
|
+
@wraps(func)
|
|
44
|
+
async def wrapper(
|
|
45
|
+
self: T,
|
|
46
|
+
*args: P.args,
|
|
47
|
+
**kwargs: P.kwargs,
|
|
48
|
+
) -> Result[typing.Any, APIError]:
|
|
49
|
+
retries_counter = 0
|
|
50
|
+
|
|
51
|
+
while True:
|
|
52
|
+
result = await func(self, *args, **kwargs)
|
|
53
|
+
|
|
54
|
+
if is_ok(result) or not self.retryer_enabled or retries_counter >= self.max_retries:
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
if result.error.status_code == HTTPStatus.TOO_MANY_REQUESTS:
|
|
58
|
+
await asyncio.sleep(result.error.retry_after.unwrap_or(5.0))
|
|
59
|
+
|
|
60
|
+
elif result.error.status_code == HTTPStatus.INTERNAL_SERVER_ERROR and "restart" in result.error.error:
|
|
61
|
+
await asyncio.sleep(10.0)
|
|
62
|
+
|
|
63
|
+
elif result.error.migrate_to_chat_id:
|
|
64
|
+
kwargs["chat_id"] = result.error.migrate_to_chat_id.value
|
|
65
|
+
|
|
66
|
+
retries_counter += 1
|
|
67
|
+
|
|
68
|
+
return wrapper # type: ignore
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class API(APIMethods):
|
|
30
72
|
"""Bot API with available API methods and http client."""
|
|
31
73
|
|
|
74
|
+
http: ABCClient
|
|
75
|
+
|
|
32
76
|
API_URL = "https://api.telegram.org/"
|
|
33
77
|
API_FILE_URL = "https://api.telegram.org/file/"
|
|
34
78
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
token: Token,
|
|
82
|
+
*,
|
|
83
|
+
http: ABCClient | None = None,
|
|
84
|
+
enable_retryer: bool = True,
|
|
85
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
86
|
+
) -> None:
|
|
39
87
|
self.token = token
|
|
40
|
-
self.http = http or AiohttpClient()
|
|
88
|
+
self.http = http or AiohttpClient()
|
|
89
|
+
self._retryer_enabled = enable_retryer
|
|
90
|
+
self._max_retries = max_retries
|
|
41
91
|
super().__init__(api=self)
|
|
42
92
|
|
|
43
93
|
def __repr__(self) -> str:
|
|
44
|
-
return "<{}:
|
|
45
|
-
self.
|
|
46
|
-
self.
|
|
94
|
+
return "<{}: id={}, http={!r}, retryer_enabled={}, max_retries={}>".format(
|
|
95
|
+
type(self).__name__,
|
|
96
|
+
self.id,
|
|
47
97
|
self.http,
|
|
98
|
+
self._retryer_enabled,
|
|
99
|
+
self._max_retries,
|
|
48
100
|
)
|
|
49
101
|
|
|
50
102
|
@cached_property
|
|
@@ -59,20 +111,38 @@ class API(APIMethods[HTTPClient], typing.Generic[HTTPClient]):
|
|
|
59
111
|
def request_file_url(self) -> str:
|
|
60
112
|
return self.API_FILE_URL + f"bot{self.token}/"
|
|
61
113
|
|
|
62
|
-
|
|
63
|
-
|
|
114
|
+
@property
|
|
115
|
+
def retryer_enabled(self) -> bool:
|
|
116
|
+
return self._retryer_enabled
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def max_retries(self) -> int:
|
|
120
|
+
return self._max_retries
|
|
121
|
+
|
|
122
|
+
async def download_file(
|
|
123
|
+
self,
|
|
124
|
+
file_path: str | pathlib.Path,
|
|
125
|
+
timeout: int | float = DEFAULT_TIMEOUT,
|
|
126
|
+
) -> bytes:
|
|
127
|
+
return await self.http.request_content(
|
|
128
|
+
url=f"{self.request_file_url}/{file_path}",
|
|
129
|
+
timeout=timeout,
|
|
130
|
+
)
|
|
64
131
|
|
|
132
|
+
@retryer
|
|
65
133
|
async def request(
|
|
66
134
|
self,
|
|
67
135
|
method: str,
|
|
68
|
-
data:
|
|
69
|
-
files:
|
|
136
|
+
data: Data | None = None,
|
|
137
|
+
files: Files | None = None,
|
|
138
|
+
**kwargs: typing.Any,
|
|
70
139
|
) -> Result[Json, APIError]:
|
|
71
|
-
"""Request a `JSON` response
|
|
140
|
+
"""Request a `JSON` response using http method `POST` and passing data, files as `multipart/form-data`."""
|
|
72
141
|
response = await self.http.request_json(
|
|
73
142
|
url=self.request_url + method,
|
|
74
143
|
method="POST",
|
|
75
144
|
data=compose_data(self.http, data or {}, files or {}),
|
|
145
|
+
**kwargs,
|
|
76
146
|
)
|
|
77
147
|
if response.get("ok", False) is True:
|
|
78
148
|
return Ok(response["result"])
|
|
@@ -80,22 +150,26 @@ class API(APIMethods[HTTPClient], typing.Generic[HTTPClient]):
|
|
|
80
150
|
APIError(
|
|
81
151
|
code=response.get("error_code", 400),
|
|
82
152
|
error=response.get("description", "Something went wrong"),
|
|
153
|
+
data=response.get("parameters", {}),
|
|
83
154
|
),
|
|
84
155
|
)
|
|
85
156
|
|
|
157
|
+
@retryer
|
|
86
158
|
async def request_raw(
|
|
87
159
|
self,
|
|
88
160
|
method: str,
|
|
89
|
-
data:
|
|
90
|
-
files:
|
|
161
|
+
data: Data | None = None,
|
|
162
|
+
files: Files | None = None,
|
|
163
|
+
**kwargs: typing.Any,
|
|
91
164
|
) -> Result[msgspec.Raw, APIError]:
|
|
92
|
-
"""Request a `raw` response
|
|
165
|
+
"""Request a `raw` response using http method `POST` and passing data, files as `multipart/form-data`."""
|
|
93
166
|
response_bytes = await self.http.request_bytes(
|
|
94
167
|
url=self.request_url + method,
|
|
95
168
|
method="POST",
|
|
96
169
|
data=compose_data(self.http, data or {}, files or {}),
|
|
170
|
+
**kwargs,
|
|
97
171
|
)
|
|
98
172
|
return decoder.decode(response_bytes, type=APIResponse).to_result()
|
|
99
173
|
|
|
100
174
|
|
|
101
|
-
__all__ = ("API",)
|
|
175
|
+
__all__ = ("API", "compose_data", "retryer")
|
telegrinder/api/error.py
CHANGED
|
@@ -1,16 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import typing
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
from http import HTTPStatus
|
|
4
|
+
|
|
5
|
+
from fntypes.misc import from_optional
|
|
6
|
+
from fntypes.option import Option
|
|
4
7
|
|
|
5
|
-
def __str__(self) -> str:
|
|
6
|
-
return f"[{self.code}] {self.error}"
|
|
7
8
|
|
|
9
|
+
class ReprErrorMixin:
|
|
8
10
|
def __repr__(self) -> str:
|
|
9
|
-
return f"
|
|
11
|
+
return f"{type(self).__name__}: {self}"
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
class
|
|
13
|
-
|
|
14
|
+
class APIError(ReprErrorMixin, Exception):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
code: int,
|
|
18
|
+
error: str,
|
|
19
|
+
data: dict[str, typing.Any],
|
|
20
|
+
) -> None:
|
|
21
|
+
self.code, self.error, self.parameters = code, error, data
|
|
22
|
+
super().__init__(code, error, data)
|
|
23
|
+
|
|
24
|
+
@cached_property
|
|
25
|
+
def status_code(self) -> HTTPStatus:
|
|
26
|
+
return HTTPStatus(self.code)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def retry_after(self) -> Option[int]:
|
|
30
|
+
return from_optional(self.parameters.get("retry_after"))
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def migrate_to_chat_id(self) -> Option[int]:
|
|
34
|
+
return from_optional(self.parameters.get("migrate_to_chat_id"))
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
return f"[{self.code}] ({self.status_code.name}) {self.error}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class APIServerError(ReprErrorMixin, Exception):
|
|
41
|
+
def __init__(self, message: str, retry_after: int) -> None:
|
|
42
|
+
self.message = message
|
|
43
|
+
self.retry_after = retry_after
|
|
44
|
+
super().__init__(message, retry_after)
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return self.message
|
|
14
48
|
|
|
15
49
|
|
|
16
50
|
class InvalidTokenError(BaseException):
|
telegrinder/api/response.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
1
3
|
import msgspec
|
|
2
4
|
from fntypes.result import Error, Ok, Result
|
|
3
5
|
|
|
@@ -10,11 +12,12 @@ class APIResponse(Model):
|
|
|
10
12
|
result: msgspec.Raw = msgspec.Raw(b"")
|
|
11
13
|
error_code: int = 400
|
|
12
14
|
description: str = "Something went wrong"
|
|
15
|
+
parameters: dict[str, typing.Any] = msgspec.field(default_factory=dict[str, typing.Any])
|
|
13
16
|
|
|
14
17
|
def to_result(self) -> Result[msgspec.Raw, APIError]:
|
|
15
18
|
if self.ok:
|
|
16
19
|
return Ok(self.result)
|
|
17
|
-
return Error(APIError(self.error_code, self.description))
|
|
20
|
+
return Error(APIError(code=self.error_code, error=self.description, data=self.parameters))
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
__all__ = ("APIResponse",)
|
telegrinder/api/token.py
CHANGED
|
@@ -4,7 +4,7 @@ from functools import cached_property
|
|
|
4
4
|
|
|
5
5
|
from envparse import env
|
|
6
6
|
|
|
7
|
-
from .error import InvalidTokenError
|
|
7
|
+
from telegrinder.api.error import InvalidTokenError
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class Token(str):
|
|
@@ -14,7 +14,7 @@ class Token(str):
|
|
|
14
14
|
return super().__new__(cls, token)
|
|
15
15
|
|
|
16
16
|
def __repr__(self) -> str:
|
|
17
|
-
return f"<Token: {self.bot_id}:{
|
|
17
|
+
return f"<Token: {self.bot_id}:{self.split(':')[1][:6]}...>"
|
|
18
18
|
|
|
19
19
|
@classmethod
|
|
20
20
|
def from_env(
|
telegrinder/bot/__init__.py
CHANGED
|
@@ -20,11 +20,9 @@ from telegrinder.bot.dispatch import (
|
|
|
20
20
|
ABCHandler,
|
|
21
21
|
ABCMiddleware,
|
|
22
22
|
ABCReturnManager,
|
|
23
|
-
ABCStateView,
|
|
24
23
|
ABCView,
|
|
25
24
|
AudioReplyHandler,
|
|
26
25
|
BaseReturnManager,
|
|
27
|
-
BaseStateView,
|
|
28
26
|
BaseView,
|
|
29
27
|
CallbackQueryReturnManager,
|
|
30
28
|
CallbackQueryView,
|
|
@@ -46,25 +44,24 @@ from telegrinder.bot.dispatch import (
|
|
|
46
44
|
PreCheckoutQueryView,
|
|
47
45
|
RawEventView,
|
|
48
46
|
ShortState,
|
|
49
|
-
StateViewHasher,
|
|
50
47
|
StickerReplyHandler,
|
|
51
48
|
VideoReplyHandler,
|
|
52
49
|
ViewBox,
|
|
53
50
|
WaiterMachine,
|
|
54
|
-
|
|
51
|
+
action,
|
|
55
52
|
register_manager,
|
|
56
53
|
)
|
|
57
54
|
from telegrinder.bot.polling import ABCPolling, Polling
|
|
58
|
-
from telegrinder.bot.rules import
|
|
59
|
-
ABCRule,
|
|
60
|
-
CallbackQueryRule,
|
|
61
|
-
ChatJoinRequestRule,
|
|
62
|
-
InlineQueryRule,
|
|
63
|
-
MessageRule,
|
|
64
|
-
)
|
|
55
|
+
from telegrinder.bot.rules import ABCRule
|
|
65
56
|
from telegrinder.bot.scenario import ABCScenario, Checkbox, Choice
|
|
66
57
|
|
|
67
58
|
__all__ = (
|
|
59
|
+
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
60
|
+
"CALLBACK_QUERY_FROM_CHAT",
|
|
61
|
+
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
62
|
+
"MESSAGE_FROM_USER",
|
|
63
|
+
"MESSAGE_FROM_USER_IN_CHAT",
|
|
64
|
+
"MESSAGE_IN_CHAT",
|
|
68
65
|
"ABCDispatch",
|
|
69
66
|
"ABCHandler",
|
|
70
67
|
"ABCMiddleware",
|
|
@@ -72,22 +69,15 @@ __all__ = (
|
|
|
72
69
|
"ABCReturnManager",
|
|
73
70
|
"ABCRule",
|
|
74
71
|
"ABCScenario",
|
|
75
|
-
"ABCStateView",
|
|
76
72
|
"ABCView",
|
|
77
73
|
"AudioReplyHandler",
|
|
78
74
|
"BaseCute",
|
|
79
75
|
"BaseReturnManager",
|
|
80
|
-
"BaseStateView",
|
|
81
76
|
"BaseView",
|
|
82
|
-
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
83
|
-
"CALLBACK_QUERY_FROM_CHAT",
|
|
84
|
-
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
85
77
|
"CallbackQueryCute",
|
|
86
78
|
"CallbackQueryReturnManager",
|
|
87
|
-
"CallbackQueryRule",
|
|
88
79
|
"CallbackQueryView",
|
|
89
80
|
"ChatJoinRequestCute",
|
|
90
|
-
"ChatJoinRequestRule",
|
|
91
81
|
"ChatJoinRequestView",
|
|
92
82
|
"ChatMemberUpdatedCute",
|
|
93
83
|
"ChatMemberView",
|
|
@@ -100,16 +90,11 @@ __all__ = (
|
|
|
100
90
|
"Hasher",
|
|
101
91
|
"InlineQueryCute",
|
|
102
92
|
"InlineQueryReturnManager",
|
|
103
|
-
"InlineQueryRule",
|
|
104
|
-
"MESSAGE_FROM_USER",
|
|
105
|
-
"MESSAGE_FROM_USER_IN_CHAT",
|
|
106
|
-
"MESSAGE_IN_CHAT",
|
|
107
93
|
"Manager",
|
|
108
94
|
"MediaGroupReplyHandler",
|
|
109
95
|
"MessageCute",
|
|
110
96
|
"MessageReplyHandler",
|
|
111
97
|
"MessageReturnManager",
|
|
112
|
-
"MessageRule",
|
|
113
98
|
"MessageView",
|
|
114
99
|
"PhotoReplyHandler",
|
|
115
100
|
"Polling",
|
|
@@ -118,13 +103,12 @@ __all__ = (
|
|
|
118
103
|
"PreCheckoutQueryView",
|
|
119
104
|
"RawEventView",
|
|
120
105
|
"ShortState",
|
|
121
|
-
"StateViewHasher",
|
|
122
106
|
"StickerReplyHandler",
|
|
123
107
|
"Telegrinder",
|
|
124
108
|
"UpdateCute",
|
|
125
109
|
"VideoReplyHandler",
|
|
126
110
|
"ViewBox",
|
|
127
111
|
"WaiterMachine",
|
|
128
|
-
"
|
|
112
|
+
"action",
|
|
129
113
|
"register_manager",
|
|
130
114
|
)
|