telegrinder 0.4.2__py3-none-any.whl → 0.5.1__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 +37 -55
- 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 +98 -67
- 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 +68 -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 +1782 -994
- telegrinder/verification_utils.py +3 -1
- telegrinder-0.5.1.dist-info/METADATA +162 -0
- telegrinder-0.5.1.dist-info/RECORD +200 -0
- {telegrinder-0.4.2.dist-info → telegrinder-0.5.1.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 -61
- 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.2.dist-info/METADATA +0 -151
- telegrinder-0.4.2.dist-info/RECORD +0 -182
- {telegrinder-0.4.2.dist-info → telegrinder-0.5.1.dist-info}/WHEEL +0 -0
telegrinder/tools/__init__.py
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
1
|
-
from .
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
from telegrinder.tools.aio import (
|
|
2
|
+
cancel_future,
|
|
3
|
+
maybe_awaitable,
|
|
4
|
+
next_generator,
|
|
5
|
+
run_task,
|
|
6
|
+
send_generator_value,
|
|
7
|
+
stop_generator,
|
|
8
8
|
)
|
|
9
|
-
from .
|
|
10
|
-
from .callback_data_serilization import (
|
|
9
|
+
from telegrinder.tools.callback_data_serialization import (
|
|
11
10
|
ABCDataSerializer,
|
|
12
11
|
JSONSerializer,
|
|
13
12
|
MsgPackSerializer,
|
|
14
13
|
)
|
|
15
|
-
from .
|
|
16
|
-
from .formatting import (
|
|
17
|
-
Base,
|
|
18
|
-
BlockQuote,
|
|
14
|
+
from telegrinder.tools.formatting import (
|
|
19
15
|
FormatString,
|
|
20
16
|
HTMLFormatter,
|
|
21
|
-
Link,
|
|
22
|
-
Mention,
|
|
23
|
-
PreCode,
|
|
24
|
-
SpecialFormat,
|
|
25
|
-
TgEmoji,
|
|
26
17
|
block_quote,
|
|
27
18
|
bold,
|
|
28
19
|
code_inline,
|
|
@@ -61,119 +52,88 @@ from .formatting import (
|
|
|
61
52
|
tg_story_link,
|
|
62
53
|
underline,
|
|
63
54
|
)
|
|
64
|
-
from .
|
|
65
|
-
from .global_context import (
|
|
55
|
+
from telegrinder.tools.fullname import fullname
|
|
56
|
+
from telegrinder.tools.global_context import (
|
|
66
57
|
ABCGlobalContext,
|
|
67
58
|
CtxVar,
|
|
68
59
|
GlobalContext,
|
|
69
60
|
GlobalCtxVar,
|
|
70
61
|
TelegrinderContext,
|
|
71
62
|
ctx_var,
|
|
63
|
+
runtime_init,
|
|
72
64
|
)
|
|
73
|
-
from .
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
from .input_file_directory import InputFileDirectory
|
|
82
|
-
from .keyboard import (
|
|
83
|
-
AnyMarkup,
|
|
84
|
-
Button,
|
|
85
|
-
InlineButton,
|
|
86
|
-
InlineKeyboard,
|
|
87
|
-
Keyboard,
|
|
88
|
-
RowButtons,
|
|
89
|
-
)
|
|
90
|
-
from .limited_dict import LimitedDict
|
|
91
|
-
from .loop_wrapper import ABCLoopWrapper, DelayedTask, Lifespan, LoopWrapper
|
|
92
|
-
from .magic import (
|
|
93
|
-
cancel_future,
|
|
94
|
-
get_annotations,
|
|
95
|
-
get_cached_translation,
|
|
65
|
+
from telegrinder.tools.input_file_directory import InputFileDirectory
|
|
66
|
+
from telegrinder.tools.lifespan import Lifespan
|
|
67
|
+
from telegrinder.tools.limited_dict import LimitedDict
|
|
68
|
+
from telegrinder.tools.loop_wrapper import DelayedTask, LoopWrapper
|
|
69
|
+
from telegrinder.tools.magic import (
|
|
70
|
+
Annotations,
|
|
71
|
+
Bundle,
|
|
72
|
+
bundle,
|
|
96
73
|
get_default_args,
|
|
74
|
+
get_func_annotations,
|
|
97
75
|
get_func_parameters,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
magic_bundle,
|
|
76
|
+
get_generic_parameters,
|
|
77
|
+
join_dicts,
|
|
101
78
|
resolve_arg_names,
|
|
79
|
+
resolve_kwonly_arg_names,
|
|
80
|
+
resolve_posonly_arg_names,
|
|
81
|
+
shortcut,
|
|
102
82
|
)
|
|
103
|
-
from .parse_mode import ParseMode
|
|
104
|
-
from .
|
|
83
|
+
from telegrinder.tools.parse_mode import ParseMode
|
|
84
|
+
from telegrinder.tools.singleton import ABCSingleton, ABCSingletonMeta, Singleton, SingletonMeta
|
|
105
85
|
|
|
106
86
|
__all__ = (
|
|
107
|
-
"ABCAdapter",
|
|
108
87
|
"ABCDataSerializer",
|
|
109
|
-
"ABCErrorHandler",
|
|
110
88
|
"ABCGlobalContext",
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"ABCTranslatorMiddleware",
|
|
116
|
-
"AnyMarkup",
|
|
117
|
-
"Base",
|
|
118
|
-
"BaseButton",
|
|
119
|
-
"BlockQuote",
|
|
120
|
-
"Button",
|
|
121
|
-
"Catcher",
|
|
122
|
-
"CatcherError",
|
|
89
|
+
"ABCSingleton",
|
|
90
|
+
"ABCSingletonMeta",
|
|
91
|
+
"Annotations",
|
|
92
|
+
"Bundle",
|
|
123
93
|
"CtxVar",
|
|
124
|
-
"DataclassAdapter",
|
|
125
94
|
"DelayedTask",
|
|
126
|
-
"ErrorHandler",
|
|
127
|
-
"EventAdapter",
|
|
128
95
|
"FormatString",
|
|
129
96
|
"GlobalContext",
|
|
130
97
|
"GlobalCtxVar",
|
|
131
98
|
"HTMLFormatter",
|
|
132
|
-
"I18nEnum",
|
|
133
|
-
"InlineButton",
|
|
134
|
-
"InlineKeyboard",
|
|
135
99
|
"InputFileDirectory",
|
|
136
100
|
"JSONSerializer",
|
|
137
|
-
"Keyboard",
|
|
138
101
|
"Lifespan",
|
|
139
102
|
"LimitedDict",
|
|
140
|
-
"Link",
|
|
141
103
|
"LoopWrapper",
|
|
142
|
-
"MemoryStateStorage",
|
|
143
|
-
"Mention",
|
|
144
104
|
"MsgPackSerializer",
|
|
145
|
-
"NodeAdapter",
|
|
146
105
|
"ParseMode",
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"RawUpdateAdapter",
|
|
150
|
-
"RowButtons",
|
|
151
|
-
"SimpleI18n",
|
|
152
|
-
"SimpleTranslator",
|
|
153
|
-
"SpecialFormat",
|
|
154
|
-
"StateData",
|
|
106
|
+
"Singleton",
|
|
107
|
+
"SingletonMeta",
|
|
155
108
|
"TelegrinderContext",
|
|
156
|
-
"TgEmoji",
|
|
157
109
|
"block_quote",
|
|
158
110
|
"bold",
|
|
111
|
+
"bundle",
|
|
159
112
|
"cancel_future",
|
|
160
113
|
"code_inline",
|
|
161
114
|
"ctx_var",
|
|
162
115
|
"escape",
|
|
163
|
-
"
|
|
164
|
-
"get_annotations",
|
|
165
|
-
"get_cached_translation",
|
|
116
|
+
"fullname",
|
|
166
117
|
"get_default_args",
|
|
118
|
+
"get_func_annotations",
|
|
167
119
|
"get_func_parameters",
|
|
168
|
-
"
|
|
169
|
-
"impl",
|
|
120
|
+
"get_generic_parameters",
|
|
170
121
|
"italic",
|
|
122
|
+
"join_dicts",
|
|
171
123
|
"link",
|
|
172
|
-
"
|
|
124
|
+
"maybe_awaitable",
|
|
173
125
|
"mention",
|
|
126
|
+
"next_generator",
|
|
174
127
|
"pre_code",
|
|
175
128
|
"resolve_arg_names",
|
|
129
|
+
"resolve_kwonly_arg_names",
|
|
130
|
+
"resolve_posonly_arg_names",
|
|
131
|
+
"run_task",
|
|
132
|
+
"runtime_init",
|
|
133
|
+
"send_generator_value",
|
|
134
|
+
"shortcut",
|
|
176
135
|
"spoiler",
|
|
136
|
+
"stop_generator",
|
|
177
137
|
"strike",
|
|
178
138
|
"tg_bot_attach_open_any_chat",
|
|
179
139
|
"tg_bot_attach_open_current_chat",
|
telegrinder/tools/aio.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from inspect import isasyncgen, isawaitable
|
|
3
|
+
|
|
4
|
+
import typing_extensions as typing
|
|
5
|
+
|
|
6
|
+
type Generator[Yield, Send, Return] = typing.AsyncGenerator[Yield, Send] | typing.Generator[Yield, Send, Return]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run_task[T](
|
|
10
|
+
task: typing.Awaitable[T],
|
|
11
|
+
/,
|
|
12
|
+
*,
|
|
13
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
14
|
+
) -> T:
|
|
15
|
+
loop = loop or asyncio.get_event_loop()
|
|
16
|
+
return loop.run_until_complete(future=task)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def next_generator[T](generator: Generator[T, typing.Any, typing.Any], /) -> T:
|
|
20
|
+
return await send_generator_value(generator, None)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def send_generator_value[Yield, Send](
|
|
24
|
+
generator: Generator[Yield, Send, typing.Any],
|
|
25
|
+
value: Send | None,
|
|
26
|
+
/,
|
|
27
|
+
) -> Yield:
|
|
28
|
+
try:
|
|
29
|
+
return (
|
|
30
|
+
await generator.asend(value) if isasyncgen(generator) else generator.send(value) # type: ignore
|
|
31
|
+
)
|
|
32
|
+
except StopIteration as exc:
|
|
33
|
+
raise StopGenerator(exc.value, exc.args) from exc
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def stop_generator[Send, Return](
|
|
37
|
+
generator: Generator[typing.Any, Send, Return],
|
|
38
|
+
with_value: Send | None = None,
|
|
39
|
+
/,
|
|
40
|
+
) -> Return | None:
|
|
41
|
+
try:
|
|
42
|
+
await send_generator_value(generator, with_value)
|
|
43
|
+
except (StopGenerator, StopAsyncIteration) as exc:
|
|
44
|
+
return exc.value if isinstance(exc, StopGenerator) else None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def maybe_awaitable[T](obj: T | typing.Awaitable[T], /) -> T:
|
|
48
|
+
if isawaitable(obj):
|
|
49
|
+
return await obj
|
|
50
|
+
return obj
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Source code: https://github.com/facebookincubator/later/blob/main/later/task.py#L75
|
|
54
|
+
async def cancel_future(fut: asyncio.Future[typing.Any], /) -> None:
|
|
55
|
+
if fut.done():
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
fut.cancel()
|
|
59
|
+
exc: asyncio.CancelledError | None = None
|
|
60
|
+
|
|
61
|
+
while not fut.done():
|
|
62
|
+
shielded = asyncio.shield(fut)
|
|
63
|
+
try:
|
|
64
|
+
await asyncio.wait([shielded])
|
|
65
|
+
except asyncio.CancelledError as ex:
|
|
66
|
+
exc = ex
|
|
67
|
+
finally:
|
|
68
|
+
# Insure we handle the exception/value that may exist on the shielded task
|
|
69
|
+
# This will prevent errors logged to the asyncio logger
|
|
70
|
+
if shielded.done() and not shielded.cancelled() and not shielded.exception():
|
|
71
|
+
shielded.result()
|
|
72
|
+
|
|
73
|
+
if fut.cancelled():
|
|
74
|
+
if exc is None:
|
|
75
|
+
return
|
|
76
|
+
raise exc from None
|
|
77
|
+
|
|
78
|
+
ex = fut.exception()
|
|
79
|
+
if ex is not None:
|
|
80
|
+
raise ex from None
|
|
81
|
+
|
|
82
|
+
raise asyncio.InvalidStateError(
|
|
83
|
+
f"Task did not raise CancelledError on cancel: {fut!r} had result {fut.result()!r}",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class StopGenerator(Exception):
|
|
88
|
+
value: typing.Any
|
|
89
|
+
|
|
90
|
+
def __init__(self, value: typing.Any, *args: object) -> None:
|
|
91
|
+
super().__init__(*args)
|
|
92
|
+
self.value = value
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = (
|
|
96
|
+
"StopGenerator",
|
|
97
|
+
"cancel_future",
|
|
98
|
+
"maybe_awaitable",
|
|
99
|
+
"next_generator",
|
|
100
|
+
"run_task",
|
|
101
|
+
"send_generator_value",
|
|
102
|
+
"stop_generator",
|
|
103
|
+
)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from telegrinder.tools.callback_data_serialization.abc import ABCDataSerializer
|
|
2
|
+
from telegrinder.tools.callback_data_serialization.json_ser import JSONSerializer
|
|
3
|
+
from telegrinder.tools.callback_data_serialization.msgpack_ser import MsgPackSerializer
|
|
4
|
+
|
|
5
|
+
__all__ = ("ABCDataSerializer", "JSONSerializer", "MsgPackSerializer")
|
|
File without changes
|
|
@@ -5,8 +5,7 @@ from fntypes.result import Error, Ok, Result
|
|
|
5
5
|
|
|
6
6
|
from telegrinder.modules import json
|
|
7
7
|
from telegrinder.msgspec_utils import decoder
|
|
8
|
-
|
|
9
|
-
from .abc import ABCDataSerializer, ModelType
|
|
8
|
+
from telegrinder.tools.callback_data_serialization.abc import ABCDataSerializer, ModelType
|
|
10
9
|
|
|
11
10
|
type Json = dict[str, typing.Any] | ModelType
|
|
12
11
|
|
|
@@ -39,7 +38,7 @@ class JSONSerializer[JsonT: Json](ABCDataSerializer[JsonT]):
|
|
|
39
38
|
return self.key + json.dumps(data)
|
|
40
39
|
|
|
41
40
|
def deserialize(self, serialized_data: str) -> Result[JsonT, str]:
|
|
42
|
-
if
|
|
41
|
+
if not serialized_data.startswith(self.key):
|
|
43
42
|
return Error("Data is not corresponding to key.")
|
|
44
43
|
|
|
45
44
|
data = serialized_data.removeprefix(self.key)
|
|
@@ -10,8 +10,20 @@ import msgspec
|
|
|
10
10
|
from fntypes.result import Error, Ok, Result
|
|
11
11
|
|
|
12
12
|
from telegrinder.msgspec_utils import decoder, encoder, get_class_annotations
|
|
13
|
+
from telegrinder.tools.callback_data_serialization.abc import ABCDataSerializer, ModelType
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
DESERIALIZE_EXCEPTIONS: typing.Final[set[type[BaseException]]] = {
|
|
16
|
+
msgspec.DecodeError,
|
|
17
|
+
msgspec.ValidationError,
|
|
18
|
+
binascii.Error,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import brotli # type: ignore
|
|
23
|
+
|
|
24
|
+
DESERIALIZE_EXCEPTIONS.add(brotli.error) # type: ignore
|
|
25
|
+
except ImportError:
|
|
26
|
+
brotli = None
|
|
15
27
|
|
|
16
28
|
|
|
17
29
|
@dataclasses.dataclass(frozen=True, slots=True)
|
|
@@ -37,16 +49,18 @@ class ModelParser[Model: ModelType]:
|
|
|
37
49
|
return isinstance(inspected_type, msgspec.inspect.DataclassType | msgspec.inspect.StructType)
|
|
38
50
|
|
|
39
51
|
def _is_iter_of_model(
|
|
40
|
-
self,
|
|
52
|
+
self,
|
|
53
|
+
inspected_type: msgspec.inspect.Type,
|
|
54
|
+
/,
|
|
41
55
|
) -> typing.TypeGuard[msgspec.inspect.ListType]:
|
|
42
56
|
return isinstance(
|
|
43
57
|
inspected_type,
|
|
44
58
|
msgspec.inspect.ListType | msgspec.inspect.SetType | msgspec.inspect.FrozenSetType,
|
|
45
59
|
) and self._is_model_type(inspected_type.item_type)
|
|
46
60
|
|
|
47
|
-
def
|
|
61
|
+
def _inspect_annotation(self, annotation: typing.Any, /) -> tuple[type[ModelType], bool] | None:
|
|
48
62
|
is_iter_of_model = False
|
|
49
|
-
type_args: tuple[msgspec.inspect.Type, ...]
|
|
63
|
+
type_args: tuple[msgspec.inspect.Type, ...] = tuple()
|
|
50
64
|
inspected_type = msgspec.inspect.type_info(annotation)
|
|
51
65
|
|
|
52
66
|
if self._is_union(inspected_type):
|
|
@@ -57,14 +71,15 @@ class ModelParser[Model: ModelType]:
|
|
|
57
71
|
elif self._is_model_type(inspected_type):
|
|
58
72
|
type_args = (inspected_type,)
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
for arg in type_args:
|
|
75
|
+
if self._is_union(arg):
|
|
76
|
+
type_args += arg.types
|
|
77
|
+
|
|
78
|
+
if self._is_model_type(arg):
|
|
79
|
+
return (arg.cls, is_iter_of_model)
|
|
80
|
+
|
|
81
|
+
if self._is_iter_of_model(arg):
|
|
82
|
+
return (arg.item_type.cls, True) # type: ignore
|
|
68
83
|
|
|
69
84
|
return None
|
|
70
85
|
|
|
@@ -103,8 +118,8 @@ class ModelParser[Model: ModelType]:
|
|
|
103
118
|
for index, (field, annotation) in enumerate(get_class_annotations(current_model).items()):
|
|
104
119
|
obj, model_type, is_iter_of_model = current_data[index], None, False
|
|
105
120
|
|
|
106
|
-
if isinstance(obj, list) and (
|
|
107
|
-
model_type, is_iter_of_model =
|
|
121
|
+
if isinstance(obj, list) and (inspected := self._inspect_annotation(annotation)):
|
|
122
|
+
model_type, is_iter_of_model = inspected
|
|
108
123
|
|
|
109
124
|
if model_type is not None:
|
|
110
125
|
if is_iter_of_model:
|
|
@@ -145,19 +160,22 @@ class MsgPackSerializer[Model: ModelType](ABCDataSerializer[Model]):
|
|
|
145
160
|
|
|
146
161
|
@cached_property
|
|
147
162
|
def key(self) -> bytes:
|
|
148
|
-
if self.ident_key
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
return msgspec.msgpack.encode(super().key) if self.ident_key else b""
|
|
164
|
+
|
|
165
|
+
def serialize(self, data: Model, /) -> str:
|
|
166
|
+
encoded = self.key + msgspec.msgpack.encode(self._model_parser.parse(data), enc_hook=encoder.enc_hook)
|
|
167
|
+
if brotli is not None:
|
|
168
|
+
return base64.b85encode(brotli.compress(encoded, quality=11)).decode() # type: ignore
|
|
169
|
+
return base64.urlsafe_b64encode(encoded).decode()
|
|
170
|
+
|
|
171
|
+
def deserialize(self, serialized_data: str, /) -> Result[Model, str]:
|
|
172
|
+
with suppress(*DESERIALIZE_EXCEPTIONS):
|
|
173
|
+
if brotli is not None:
|
|
174
|
+
ser_data = typing.cast("bytes", brotli.decompress(base64.b85decode(serialized_data)))
|
|
175
|
+
else:
|
|
176
|
+
ser_data = base64.urlsafe_b64decode(serialized_data)
|
|
177
|
+
|
|
178
|
+
if not ser_data.startswith(self.key):
|
|
161
179
|
return Error("Data is not corresponding to key.")
|
|
162
180
|
|
|
163
181
|
data: list[typing.Any] = msgspec.msgpack.decode(
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.tools.fullname import fullname
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Final:
|
|
7
|
+
if not typing.TYPE_CHECKING:
|
|
8
|
+
|
|
9
|
+
def __new__(cls, *args, **kwargs):
|
|
10
|
+
if cls is Final:
|
|
11
|
+
raise TypeError("Class Final cannot be instantiate.")
|
|
12
|
+
return super().__new__(cls, *args, **kwargs)
|
|
13
|
+
|
|
14
|
+
@typing.final
|
|
15
|
+
def __init_subclass__(cls, **kwargs: typing.Any) -> None:
|
|
16
|
+
for base in cls.__bases__:
|
|
17
|
+
if base is not Final and issubclass(base, Final):
|
|
18
|
+
raise TypeError(f"Final class `{fullname(base)}` cannot be subclassed.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ("Final",)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .deep_links import (
|
|
1
|
+
from telegrinder.tools.formatting.deep_links import (
|
|
2
2
|
tg_bot_attach_open_any_chat,
|
|
3
3
|
tg_bot_attach_open_current_chat,
|
|
4
4
|
tg_bot_attach_open_specific_chat,
|
|
@@ -25,7 +25,7 @@ from .deep_links import (
|
|
|
25
25
|
tg_share_link,
|
|
26
26
|
tg_story_link,
|
|
27
27
|
)
|
|
28
|
-
from .html_formatter import (
|
|
28
|
+
from telegrinder.tools.formatting.html_formatter import (
|
|
29
29
|
FormatString,
|
|
30
30
|
HTMLFormatter,
|
|
31
31
|
block_quote,
|
|
@@ -41,26 +41,10 @@ from .html_formatter import (
|
|
|
41
41
|
tg_emoji,
|
|
42
42
|
underline,
|
|
43
43
|
)
|
|
44
|
-
from .spec_html_formats import (
|
|
45
|
-
Base,
|
|
46
|
-
BlockQuote,
|
|
47
|
-
Link,
|
|
48
|
-
Mention,
|
|
49
|
-
PreCode,
|
|
50
|
-
SpecialFormat,
|
|
51
|
-
TgEmoji,
|
|
52
|
-
)
|
|
53
44
|
|
|
54
45
|
__all__ = (
|
|
55
|
-
"Base",
|
|
56
|
-
"BlockQuote",
|
|
57
46
|
"FormatString",
|
|
58
47
|
"HTMLFormatter",
|
|
59
|
-
"Link",
|
|
60
|
-
"Mention",
|
|
61
|
-
"PreCode",
|
|
62
|
-
"SpecialFormat",
|
|
63
|
-
"TgEmoji",
|
|
64
48
|
"block_quote",
|
|
65
49
|
"bold",
|
|
66
50
|
"code_inline",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from telegrinder.tools.formatting.deep_links.links import *
|
|
2
|
+
from telegrinder.tools.formatting.deep_links.parsing import *
|
|
3
|
+
from telegrinder.tools.formatting.deep_links.validators import *
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
|
+
"NO_VALUE",
|
|
7
|
+
"NoValue",
|
|
8
|
+
"Parameter",
|
|
9
|
+
"get_parameter_metadata",
|
|
10
|
+
"get_query_params",
|
|
11
|
+
"parse_deep_link",
|
|
12
|
+
"parse_query_params",
|
|
13
|
+
"separate_by_plus_char",
|
|
14
|
+
"tg_bot_attach_open_any_chat",
|
|
15
|
+
"tg_bot_attach_open_current_chat",
|
|
16
|
+
"tg_bot_attach_open_specific_chat",
|
|
17
|
+
"tg_bot_start_link",
|
|
18
|
+
"tg_bot_startchannel_link",
|
|
19
|
+
"tg_bot_startgroup_link",
|
|
20
|
+
"tg_chat_folder_link",
|
|
21
|
+
"tg_chat_invite_link",
|
|
22
|
+
"tg_direct_mini_app_link",
|
|
23
|
+
"tg_emoji_link",
|
|
24
|
+
"tg_emoji_stickerset_link",
|
|
25
|
+
"tg_invoice_link",
|
|
26
|
+
"tg_language_pack_link",
|
|
27
|
+
"tg_main_mini_app_link",
|
|
28
|
+
"tg_mention_link",
|
|
29
|
+
"tg_open_message_link",
|
|
30
|
+
"tg_premium_multigift_link",
|
|
31
|
+
"tg_premium_offer_link",
|
|
32
|
+
"tg_private_channel_boost_link",
|
|
33
|
+
"tg_private_message_link",
|
|
34
|
+
"tg_public_channel_boost_link",
|
|
35
|
+
"tg_public_message_link",
|
|
36
|
+
"tg_public_username_link",
|
|
37
|
+
"tg_share_link",
|
|
38
|
+
"tg_story_link",
|
|
39
|
+
)
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import types
|
|
2
1
|
import typing
|
|
3
|
-
from collections import OrderedDict
|
|
4
2
|
from datetime import timedelta
|
|
5
3
|
from functools import wraps
|
|
6
|
-
from urllib.parse import urlencode
|
|
7
4
|
|
|
8
|
-
from telegrinder.tools.
|
|
5
|
+
from telegrinder.tools.formatting.deep_links.parsing import (
|
|
6
|
+
NO_VALUE,
|
|
7
|
+
DeepLinkFunction,
|
|
8
|
+
NoValue,
|
|
9
|
+
Parameter,
|
|
10
|
+
get_query_params,
|
|
11
|
+
parse_deep_link,
|
|
12
|
+
)
|
|
13
|
+
from telegrinder.tools.formatting.deep_links.validators import separate_by_plus_char
|
|
9
14
|
|
|
10
|
-
type DeepLinkFunction[**P] = typing.Callable[P, str]
|
|
11
|
-
type NoValue = types.EllipsisType
|
|
12
15
|
type Permission = typing.Literal[
|
|
13
16
|
"change_info",
|
|
14
17
|
"post_messages",
|
|
@@ -32,9 +35,6 @@ type Peer = typing.Literal[
|
|
|
32
35
|
"groups",
|
|
33
36
|
"channels",
|
|
34
37
|
]
|
|
35
|
-
Parameter = typing.Annotated
|
|
36
|
-
|
|
37
|
-
NO_VALUE: typing.Final[NoValue] = typing.cast(NoValue, ...)
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def deep_link[**P](
|
|
@@ -61,79 +61,6 @@ def deep_link[**P](
|
|
|
61
61
|
return inner
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def get_query_params(
|
|
65
|
-
func: DeepLinkFunction[...],
|
|
66
|
-
kwargs: dict[str, typing.Any],
|
|
67
|
-
order_params: set[str] | None = None,
|
|
68
|
-
) -> dict[str, typing.Any]:
|
|
69
|
-
annotations = get_annotations(func)
|
|
70
|
-
params = OrderedDict()
|
|
71
|
-
param_names = (
|
|
72
|
-
[*order_params, *(p for p in annotations if p not in order_params)] if order_params else annotations
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
for param_name in param_names:
|
|
76
|
-
annotation = annotations[param_name]
|
|
77
|
-
if param_name in kwargs:
|
|
78
|
-
value = kwargs[param_name]
|
|
79
|
-
if typing.get_origin(annotation) is Parameter:
|
|
80
|
-
param_name, validator = get_parameter_metadata(annotation)
|
|
81
|
-
value = validator(value) if validator is not None else value
|
|
82
|
-
|
|
83
|
-
params[param_name] = value
|
|
84
|
-
|
|
85
|
-
return params
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def parse_query_params(
|
|
89
|
-
params: dict[str, typing.Any],
|
|
90
|
-
no_value_params: set[str] | None = None,
|
|
91
|
-
/,
|
|
92
|
-
) -> tuple[set[str], dict[str, typing.Any]]:
|
|
93
|
-
no_value_params = no_value_params or set()
|
|
94
|
-
params_: dict[str, typing.Any] = {}
|
|
95
|
-
|
|
96
|
-
for key, value in params.items():
|
|
97
|
-
if value in (False, None):
|
|
98
|
-
continue
|
|
99
|
-
|
|
100
|
-
if value in (True, NO_VALUE):
|
|
101
|
-
no_value_params.add(key)
|
|
102
|
-
continue
|
|
103
|
-
if isinstance(value, timedelta):
|
|
104
|
-
value = int(value.total_seconds())
|
|
105
|
-
|
|
106
|
-
params_[key] = value
|
|
107
|
-
|
|
108
|
-
return (no_value_params, params_)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def get_parameter_metadata(
|
|
112
|
-
parameter: typing.Any,
|
|
113
|
-
) -> tuple[str, typing.Callable[[typing.Any], typing.Any] | None]:
|
|
114
|
-
meta: tuple[typing.Any, ...] = getattr(parameter, "__metadata__")
|
|
115
|
-
return meta if len(meta) == 2 else (meta[0], None)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def parse_deep_link(
|
|
119
|
-
*,
|
|
120
|
-
link: str,
|
|
121
|
-
params: dict[str, typing.Any],
|
|
122
|
-
no_value_params: set[str] | None = None,
|
|
123
|
-
) -> str:
|
|
124
|
-
no_value_params, params = parse_query_params(params, no_value_params)
|
|
125
|
-
query = urlencode(params, encoding="UTF-8") + ("&" if no_value_params else "") + "&".join(no_value_params)
|
|
126
|
-
return f"{link}?{query}"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def validate_permissions(perms: list[Permission] | None, /) -> str | None:
|
|
130
|
-
return None if not perms else "+".join(perms)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def validate_peer(peer: list[Peer] | None, /) -> str | None:
|
|
134
|
-
return None if not peer else "+".join(peer)
|
|
135
|
-
|
|
136
|
-
|
|
137
64
|
@deep_link("tg://resolve")
|
|
138
65
|
def tg_public_username_link(
|
|
139
66
|
*,
|
|
@@ -364,7 +291,7 @@ def tg_bot_startgroup_link(
|
|
|
364
291
|
*,
|
|
365
292
|
bot_username: Parameter[str, "domain"],
|
|
366
293
|
parameter: Parameter[str | NoValue, "startgroup"] = NO_VALUE,
|
|
367
|
-
permissions: Parameter[list[Permission] | None, "admin",
|
|
294
|
+
permissions: Parameter[list[Permission] | None, "admin", separate_by_plus_char] = None,
|
|
368
295
|
) -> str:
|
|
369
296
|
"""Used to add bots to groups.
|
|
370
297
|
First of all, check that the `<bot_username>` indeed links to a bot.
|
|
@@ -394,7 +321,7 @@ def tg_bot_startgroup_link(
|
|
|
394
321
|
def tg_bot_startchannel_link(
|
|
395
322
|
*,
|
|
396
323
|
bot_username: Parameter[str, "domain"],
|
|
397
|
-
permissions: Parameter[list[Permission], "admin",
|
|
324
|
+
permissions: Parameter[list[Permission], "admin", separate_by_plus_char],
|
|
398
325
|
) -> str:
|
|
399
326
|
"""Used to add bots to channels.
|
|
400
327
|
First of all, check that the `<bot_username>` indeed links to a bot.
|
|
@@ -499,7 +426,7 @@ def tg_bot_attach_open_any_chat(
|
|
|
499
426
|
*,
|
|
500
427
|
bot_username: Parameter[str, "domain"],
|
|
501
428
|
parameter: Parameter[str | NoValue, "startattach"] = NO_VALUE,
|
|
502
|
-
peer: Parameter[list[Peer] | None, "choose",
|
|
429
|
+
peer: Parameter[list[Peer] | None, "choose", separate_by_plus_char] = None,
|
|
503
430
|
) -> str:
|
|
504
431
|
"""After installing the `attachment/side` menu entry globally, opens a dialog selection form that will open the attachment menu
|
|
505
432
|
mini app using `messages.requestWebView` in a specific chat (pass it to the peer parameter of `messages.requestWebView`).
|