telegrinder 0.3.4__py3-none-any.whl → 0.4.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 +148 -149
- telegrinder/api/__init__.py +9 -8
- telegrinder/api/api.py +101 -93
- telegrinder/api/error.py +20 -16
- telegrinder/api/response.py +20 -20
- telegrinder/api/token.py +36 -36
- telegrinder/bot/__init__.py +72 -66
- telegrinder/bot/bot.py +83 -76
- telegrinder/bot/cute_types/__init__.py +19 -17
- telegrinder/bot/cute_types/base.py +184 -258
- telegrinder/bot/cute_types/callback_query.py +400 -385
- telegrinder/bot/cute_types/chat_join_request.py +62 -61
- telegrinder/bot/cute_types/chat_member_updated.py +157 -160
- telegrinder/bot/cute_types/inline_query.py +44 -43
- telegrinder/bot/cute_types/message.py +2590 -2637
- telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
- telegrinder/bot/cute_types/update.py +112 -104
- telegrinder/bot/cute_types/utils.py +62 -95
- telegrinder/bot/dispatch/__init__.py +59 -55
- telegrinder/bot/dispatch/abc.py +76 -77
- telegrinder/bot/dispatch/context.py +96 -98
- telegrinder/bot/dispatch/dispatch.py +254 -202
- telegrinder/bot/dispatch/handler/__init__.py +13 -13
- telegrinder/bot/dispatch/handler/abc.py +23 -24
- telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
- telegrinder/bot/dispatch/handler/base.py +57 -57
- telegrinder/bot/dispatch/handler/document_reply.py +44 -44
- telegrinder/bot/dispatch/handler/func.py +129 -135
- telegrinder/bot/dispatch/handler/media_group_reply.py +44 -43
- telegrinder/bot/dispatch/handler/message_reply.py +36 -36
- telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
- telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
- telegrinder/bot/dispatch/handler/video_reply.py +44 -44
- telegrinder/bot/dispatch/middleware/__init__.py +3 -3
- telegrinder/bot/dispatch/middleware/abc.py +97 -22
- telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
- telegrinder/bot/dispatch/process.py +151 -157
- telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
- telegrinder/bot/dispatch/return_manager/abc.py +104 -108
- telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
- telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
- telegrinder/bot/dispatch/return_manager/message.py +36 -36
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -13
- telegrinder/bot/dispatch/view/abc.py +45 -41
- telegrinder/bot/dispatch/view/base.py +231 -200
- telegrinder/bot/dispatch/view/box.py +140 -129
- telegrinder/bot/dispatch/view/callback_query.py +16 -17
- telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
- telegrinder/bot/dispatch/view/chat_member.py +37 -39
- telegrinder/bot/dispatch/view/inline_query.py +16 -17
- telegrinder/bot/dispatch/view/message.py +43 -44
- telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
- telegrinder/bot/dispatch/view/raw.py +116 -114
- telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
- telegrinder/bot/dispatch/waiter_machine/actions.py +14 -13
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +59 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
- telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
- telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
- telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
- telegrinder/bot/polling/__init__.py +4 -4
- telegrinder/bot/polling/abc.py +25 -25
- telegrinder/bot/polling/polling.py +139 -131
- telegrinder/bot/rules/__init__.py +85 -62
- telegrinder/bot/rules/abc.py +213 -206
- telegrinder/bot/rules/callback_data.py +122 -163
- telegrinder/bot/rules/chat_join.py +45 -43
- telegrinder/bot/rules/command.py +126 -126
- telegrinder/bot/rules/enum_text.py +33 -36
- telegrinder/bot/rules/func.py +28 -26
- telegrinder/bot/rules/fuzzy.py +24 -24
- telegrinder/bot/rules/id.py +24 -0
- telegrinder/bot/rules/inline.py +58 -56
- telegrinder/bot/rules/integer.py +21 -20
- telegrinder/bot/rules/is_from.py +127 -127
- telegrinder/bot/rules/logic.py +18 -0
- telegrinder/bot/rules/markup.py +42 -43
- telegrinder/bot/rules/mention.py +14 -14
- telegrinder/bot/rules/message.py +15 -17
- telegrinder/bot/rules/message_entities.py +33 -35
- telegrinder/bot/rules/node.py +33 -27
- telegrinder/bot/rules/payload.py +81 -0
- telegrinder/bot/rules/payment_invoice.py +29 -0
- telegrinder/bot/rules/regex.py +36 -37
- telegrinder/bot/rules/rule_enum.py +72 -72
- telegrinder/bot/rules/start.py +42 -42
- telegrinder/bot/rules/state.py +35 -37
- telegrinder/bot/rules/text.py +38 -33
- telegrinder/bot/rules/update.py +15 -15
- telegrinder/bot/scenario/__init__.py +5 -5
- telegrinder/bot/scenario/abc.py +17 -19
- telegrinder/bot/scenario/checkbox.py +174 -176
- telegrinder/bot/scenario/choice.py +48 -51
- telegrinder/client/__init__.py +12 -4
- telegrinder/client/abc.py +100 -75
- telegrinder/client/aiohttp.py +134 -130
- telegrinder/client/form_data.py +31 -0
- telegrinder/client/sonic.py +212 -0
- telegrinder/model.py +208 -315
- telegrinder/modules.py +239 -237
- telegrinder/msgspec_json.py +14 -14
- telegrinder/msgspec_utils.py +478 -410
- telegrinder/node/__init__.py +86 -25
- telegrinder/node/attachment.py +163 -87
- telegrinder/node/base.py +288 -160
- telegrinder/node/callback_query.py +54 -53
- telegrinder/node/command.py +34 -33
- telegrinder/node/composer.py +163 -198
- telegrinder/node/container.py +33 -27
- telegrinder/node/either.py +82 -0
- telegrinder/node/event.py +54 -65
- telegrinder/node/file.py +51 -0
- telegrinder/node/me.py +15 -16
- telegrinder/node/payload.py +78 -0
- telegrinder/node/polymorphic.py +67 -48
- telegrinder/node/rule.py +72 -76
- telegrinder/node/scope.py +36 -38
- telegrinder/node/source.py +87 -71
- telegrinder/node/text.py +53 -41
- telegrinder/node/tools/__init__.py +3 -3
- telegrinder/node/tools/generator.py +36 -40
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -62
- telegrinder/tools/__init__.py +152 -93
- telegrinder/tools/adapter/__init__.py +19 -0
- telegrinder/tools/adapter/abc.py +49 -0
- telegrinder/tools/adapter/dataclass.py +56 -0
- telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
- telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
- telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
- telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
- telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
- telegrinder/tools/buttons.py +106 -80
- telegrinder/tools/callback_data_serilization/__init__.py +5 -0
- telegrinder/tools/callback_data_serilization/abc.py +51 -0
- telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
- telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
- telegrinder/tools/error_handler/__init__.py +7 -7
- telegrinder/tools/error_handler/abc.py +30 -33
- telegrinder/tools/error_handler/error.py +9 -9
- telegrinder/tools/error_handler/error_handler.py +179 -193
- telegrinder/tools/formatting/__init__.py +83 -63
- telegrinder/tools/formatting/deep_links.py +541 -0
- telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
- telegrinder/tools/formatting/spec_html_formats.py +71 -117
- telegrinder/tools/functional.py +8 -12
- telegrinder/tools/global_context/__init__.py +7 -7
- telegrinder/tools/global_context/abc.py +63 -63
- telegrinder/tools/global_context/global_context.py +387 -412
- telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
- telegrinder/tools/i18n/__init__.py +7 -7
- telegrinder/tools/i18n/abc.py +30 -30
- telegrinder/tools/i18n/middleware/__init__.py +3 -3
- telegrinder/tools/i18n/middleware/abc.py +22 -25
- telegrinder/tools/i18n/simple.py +43 -43
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard.py +128 -128
- telegrinder/tools/lifespan.py +105 -0
- telegrinder/tools/limited_dict.py +32 -37
- telegrinder/tools/loop_wrapper/__init__.py +4 -4
- telegrinder/tools/loop_wrapper/abc.py +20 -15
- telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
- telegrinder/tools/magic.py +307 -157
- telegrinder/tools/parse_mode.py +6 -6
- telegrinder/tools/state_storage/__init__.py +4 -4
- telegrinder/tools/state_storage/abc.py +31 -35
- telegrinder/tools/state_storage/memory.py +25 -25
- telegrinder/tools/strings.py +13 -0
- telegrinder/types/__init__.py +268 -260
- telegrinder/types/enums.py +711 -701
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +5055 -4633
- telegrinder/types/objects.py +7058 -6950
- telegrinder/verification_utils.py +30 -32
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
- telegrinder-0.4.0.dist-info/METADATA +144 -0
- telegrinder-0.4.0.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
- telegrinder/bot/rules/adapter/__init__.py +0 -17
- telegrinder/bot/rules/adapter/abc.py +0 -31
- telegrinder/node/message.py +0 -14
- telegrinder/node/update.py +0 -15
- telegrinder/tools/formatting/links.py +0 -38
- telegrinder/tools/kb_set/__init__.py +0 -4
- telegrinder/tools/kb_set/base.py +0 -15
- telegrinder/tools/kb_set/yaml.py +0 -63
- telegrinder-0.3.4.dist-info/METADATA +0 -110
- telegrinder-0.3.4.dist-info/RECORD +0 -165
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import re
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
__ctx_name__ = "telegrinder"
|
|
22
|
-
|
|
23
|
-
vbml_pattern_flags: re.RegexFlag | None = None
|
|
24
|
-
vbml_patcher:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
__all__ = ("TelegrinderContext",)
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import vbml
|
|
4
|
+
|
|
5
|
+
from telegrinder.tools.global_context import GlobalContext, ctx_var
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TelegrinderContext(GlobalContext):
|
|
9
|
+
"""Basic type-hinted telegrinder context with context name `"telegrinder"`.
|
|
10
|
+
|
|
11
|
+
You can use this class or GlobalContext:
|
|
12
|
+
```
|
|
13
|
+
from telegrinder.tools.global_context import GlobalContext, TelegrinderContext
|
|
14
|
+
|
|
15
|
+
ctx1 = TelegrinderContext()
|
|
16
|
+
ctx2 = GlobalContext("telegrinder") # same, but without the type-hints
|
|
17
|
+
assert ctx1 == ctx2 # ok
|
|
18
|
+
```
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
__ctx_name__ = "telegrinder"
|
|
22
|
+
|
|
23
|
+
vbml_pattern_flags: re.RegexFlag | None = None
|
|
24
|
+
vbml_patcher: vbml.Patcher = ctx_var(default=vbml.Patcher(), frozen=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = ("TelegrinderContext",)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from .abc import ABCI18n, ABCTranslator, I18nEnum
|
|
2
|
-
from .middleware import ABCTranslatorMiddleware
|
|
3
|
-
from .simple import SimpleI18n, SimpleTranslator
|
|
4
|
-
|
|
5
|
-
__all__ = (
|
|
1
|
+
from .abc import ABCI18n, ABCTranslator, I18nEnum
|
|
2
|
+
from .middleware import ABCTranslatorMiddleware
|
|
3
|
+
from .simple import SimpleI18n, SimpleTranslator
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
6
|
"ABCI18n",
|
|
7
7
|
"ABCTranslator",
|
|
8
8
|
"ABCTranslatorMiddleware",
|
|
9
9
|
"I18nEnum",
|
|
10
10
|
"SimpleI18n",
|
|
11
|
-
"SimpleTranslator",
|
|
12
|
-
)
|
|
11
|
+
"SimpleTranslator",
|
|
12
|
+
)
|
telegrinder/tools/i18n/abc.py
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
import typing
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ABCI18n(ABC):
|
|
7
|
-
@abstractmethod
|
|
8
|
-
def get_translator_by_locale(self, locale: str) -> "ABCTranslator":
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ABCTranslator(ABC):
|
|
13
|
-
def __init__(self, locale: str, **kwargs: typing.Any) -> None:
|
|
14
|
-
self.locale = locale
|
|
15
|
-
|
|
16
|
-
@abstractmethod
|
|
17
|
-
def get(self, __key: str, *args: typing.Any, **kwargs: typing.Any) -> str:
|
|
18
|
-
"""This translates a key to actual human-readable string"""
|
|
19
|
-
|
|
20
|
-
def __call__(self, __key: str, *args: typing.Any, **kwargs: typing.Any) -> str:
|
|
21
|
-
return self.get(__key, *args, **kwargs)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class I18nEnum(enum.Enum):
|
|
25
|
-
I18N = "_"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
__all__ = (
|
|
1
|
+
import enum
|
|
2
|
+
import typing
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ABCI18n(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def get_translator_by_locale(self, locale: str) -> "ABCTranslator":
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ABCTranslator(ABC):
|
|
13
|
+
def __init__(self, locale: str, **kwargs: typing.Any) -> None:
|
|
14
|
+
self.locale = locale
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def get(self, __key: str, *args: typing.Any, **kwargs: typing.Any) -> str:
|
|
18
|
+
"""This translates a key to actual human-readable string"""
|
|
19
|
+
|
|
20
|
+
def __call__(self, __key: str, *args: typing.Any, **kwargs: typing.Any) -> str:
|
|
21
|
+
return self.get(__key, *args, **kwargs)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class I18nEnum(enum.Enum):
|
|
25
|
+
I18N = "_"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = (
|
|
29
29
|
"ABCI18n",
|
|
30
30
|
"ABCTranslator",
|
|
31
|
-
"I18nEnum",
|
|
32
|
-
)
|
|
31
|
+
"I18nEnum",
|
|
32
|
+
)
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from .abc import ABCTranslatorMiddleware
|
|
2
|
-
|
|
3
|
-
__all__ = ("ABCTranslatorMiddleware",)
|
|
1
|
+
from .abc import ABCTranslatorMiddleware
|
|
2
|
+
|
|
3
|
+
__all__ = ("ABCTranslatorMiddleware",)
|
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from telegrinder.bot.
|
|
5
|
-
from telegrinder.bot.dispatch.
|
|
6
|
-
from telegrinder.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
async def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
__all__ = ("ABCTranslatorMiddleware",)
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.bot.dispatch.middleware import ABCMiddleware
|
|
6
|
+
from telegrinder.tools.i18n import ABCI18n, I18nEnum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ABCTranslatorMiddleware[Event: BaseCute](ABCMiddleware[Event]):
|
|
10
|
+
def __init__(self, i18n: ABCI18n) -> None:
|
|
11
|
+
self.i18n = i18n
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
async def get_locale(self, event: Event) -> str:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
async def pre(self, event: Event, ctx: Context) -> bool:
|
|
18
|
+
ctx[I18nEnum.I18N] = self.i18n.get_translator_by_locale(await self.get_locale(event))
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ("ABCTranslatorMiddleware",)
|
telegrinder/tools/i18n/simple.py
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
"""This is an implementation of GNU gettext (pyBabel)."""
|
|
2
|
-
|
|
3
|
-
import gettext
|
|
4
|
-
import os
|
|
5
|
-
|
|
6
|
-
from telegrinder.tools.i18n.abc import ABCI18n, ABCTranslator
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SimpleTranslator(ABCTranslator):
|
|
10
|
-
def __init__(self, locale: str, g: gettext.GNUTranslations) -> None:
|
|
11
|
-
self.g = g
|
|
12
|
-
super().__init__(locale)
|
|
13
|
-
|
|
14
|
-
def get(self, __key: str, *args: object, **kwargs: object) -> str:
|
|
15
|
-
return self.g.gettext(__key).format(*args, **kwargs)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class SimpleI18n(ABCI18n):
|
|
19
|
-
def __init__(self, folder: str, domain: str, default_locale: str) -> None:
|
|
20
|
-
self.folder = folder
|
|
21
|
-
self.domain = domain
|
|
22
|
-
self.default_locale = default_locale
|
|
23
|
-
self.translators = self._load_translators()
|
|
24
|
-
|
|
25
|
-
def _load_translators(self) -> dict[str, gettext.GNUTranslations]:
|
|
26
|
-
result = {}
|
|
27
|
-
for name in os.listdir(self.folder):
|
|
28
|
-
if not os.path.isdir(os.path.join(self.folder, name)):
|
|
29
|
-
continue
|
|
30
|
-
|
|
31
|
-
mo_path = os.path.join(self.folder, name, "LC_MESSAGES", f"{self.domain}.mo")
|
|
32
|
-
if os.path.exists(mo_path):
|
|
33
|
-
with open(mo_path, "rb") as f:
|
|
34
|
-
result[name] = gettext.GNUTranslations(f)
|
|
35
|
-
elif os.path.exists(mo_path[:-2] + "po"):
|
|
36
|
-
raise FileNotFoundError(".po files should be compiled first")
|
|
37
|
-
return result
|
|
38
|
-
|
|
39
|
-
def get_translator_by_locale(self, locale: str) -> "SimpleTranslator":
|
|
40
|
-
return SimpleTranslator(locale, self.translators.get(locale, self.translators[self.default_locale]))
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
__all__ = ("SimpleI18n", "SimpleTranslator")
|
|
1
|
+
"""This is an implementation of GNU gettext (pyBabel)."""
|
|
2
|
+
|
|
3
|
+
import gettext
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from telegrinder.tools.i18n.abc import ABCI18n, ABCTranslator
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SimpleTranslator(ABCTranslator):
|
|
10
|
+
def __init__(self, locale: str, g: gettext.GNUTranslations) -> None:
|
|
11
|
+
self.g = g
|
|
12
|
+
super().__init__(locale)
|
|
13
|
+
|
|
14
|
+
def get(self, __key: str, *args: object, **kwargs: object) -> str:
|
|
15
|
+
return self.g.gettext(__key).format(*args, **kwargs)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SimpleI18n(ABCI18n):
|
|
19
|
+
def __init__(self, folder: str, domain: str, default_locale: str) -> None:
|
|
20
|
+
self.folder = folder
|
|
21
|
+
self.domain = domain
|
|
22
|
+
self.default_locale = default_locale
|
|
23
|
+
self.translators = self._load_translators()
|
|
24
|
+
|
|
25
|
+
def _load_translators(self) -> dict[str, gettext.GNUTranslations]:
|
|
26
|
+
result = {}
|
|
27
|
+
for name in os.listdir(self.folder):
|
|
28
|
+
if not os.path.isdir(os.path.join(self.folder, name)):
|
|
29
|
+
continue
|
|
30
|
+
|
|
31
|
+
mo_path = os.path.join(self.folder, name, "LC_MESSAGES", f"{self.domain}.mo")
|
|
32
|
+
if os.path.exists(mo_path):
|
|
33
|
+
with open(mo_path, "rb") as f:
|
|
34
|
+
result[name] = gettext.GNUTranslations(f)
|
|
35
|
+
elif os.path.exists(mo_path[:-2] + "po"):
|
|
36
|
+
raise FileNotFoundError(".po files should be compiled first")
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
def get_translator_by_locale(self, locale: str) -> "SimpleTranslator":
|
|
40
|
+
return SimpleTranslator(locale, self.translators.get(locale, self.translators[self.default_locale]))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = ("SimpleI18n", "SimpleTranslator")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import pathlib
|
|
3
|
+
|
|
4
|
+
from telegrinder.types.objects import InputFile
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class InputFileDirectory:
|
|
9
|
+
directory: pathlib.Path
|
|
10
|
+
storage: dict[str, InputFile] = dataclasses.field(init=False, repr=False)
|
|
11
|
+
|
|
12
|
+
def __post_init__(self) -> None:
|
|
13
|
+
self.storage = self._load_files()
|
|
14
|
+
|
|
15
|
+
def _load_files(self) -> dict[str, InputFile]:
|
|
16
|
+
files = {}
|
|
17
|
+
|
|
18
|
+
for path in self.directory.rglob("*"):
|
|
19
|
+
if path.is_file():
|
|
20
|
+
relative_path = path.relative_to(self.directory)
|
|
21
|
+
files[str(relative_path)] = InputFile(path.name, path.read_bytes())
|
|
22
|
+
|
|
23
|
+
return files
|
|
24
|
+
|
|
25
|
+
def get(self, filename: str, /) -> InputFile:
|
|
26
|
+
assert filename in self.storage, f"File {filename!r} not found."
|
|
27
|
+
return self.storage[filename]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ("InputFileDirectory",)
|
telegrinder/tools/keyboard.py
CHANGED
|
@@ -1,132 +1,132 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import typing
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from telegrinder.types.objects import (
|
|
9
|
-
InlineKeyboardMarkup,
|
|
10
|
-
ReplyKeyboardMarkup,
|
|
11
|
-
ReplyKeyboardRemove,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
from .buttons import Button, InlineButton,
|
|
15
|
-
|
|
16
|
-
DictStrAny
|
|
17
|
-
AnyMarkup
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def copy_keyboard(keyboard: list[list[DictStrAny]]) -> list[list[DictStrAny]]:
|
|
21
|
-
return [row.copy() for row in keyboard]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
25
|
-
class KeyboardModel:
|
|
26
|
-
resize_keyboard: bool
|
|
27
|
-
one_time_keyboard: bool
|
|
28
|
-
selective: bool
|
|
29
|
-
is_persistent: bool
|
|
30
|
-
keyboard: list[list[DictStrAny]]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class ABCMarkup
|
|
34
|
-
BUTTON: type[KeyboardButton]
|
|
35
|
-
keyboard: list[list[DictStrAny]]
|
|
36
|
-
|
|
37
|
-
@abstractmethod
|
|
38
|
-
def dict(self) -> DictStrAny:
|
|
39
|
-
pass
|
|
40
|
-
|
|
41
|
-
@abstractmethod
|
|
42
|
-
def get_markup(self) -> AnyMarkup:
|
|
43
|
-
pass
|
|
44
|
-
|
|
45
|
-
@classmethod
|
|
46
|
-
def get_empty_markup(cls) -> AnyMarkup:
|
|
47
|
-
return cls().get_markup()
|
|
48
|
-
|
|
49
|
-
def add(self, row_or_button: RowButtons[KeyboardButton] | KeyboardButton) -> typing.Self:
|
|
50
|
-
if not
|
|
51
|
-
self.row()
|
|
52
|
-
|
|
53
|
-
if isinstance(row_or_button, RowButtons):
|
|
54
|
-
self.keyboard[-1].extend(row_or_button.get_data())
|
|
55
|
-
if row_or_button.auto_row:
|
|
56
|
-
self.row()
|
|
57
|
-
return self
|
|
58
|
-
|
|
59
|
-
self.keyboard[-1].append(row_or_button.get_data())
|
|
60
|
-
return self
|
|
61
|
-
|
|
62
|
-
def row(self) -> typing.Self:
|
|
63
|
-
if len(self.keyboard) and not len(self.keyboard[-1]):
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
self.keyboard.append([])
|
|
67
|
-
return self
|
|
68
|
-
|
|
69
|
-
def format(self, **format_data: str) -> typing.Self:
|
|
70
|
-
copy_keyboard = self.__class__()
|
|
71
|
-
for row in self.keyboard:
|
|
72
|
-
for button in row:
|
|
73
|
-
copy_button = button.copy()
|
|
74
|
-
copy_button["text"] = copy_button["text"].format(**format_data)
|
|
75
|
-
copy_keyboard.add(self.BUTTON(**copy_button))
|
|
76
|
-
copy_keyboard.row()
|
|
77
|
-
return copy_keyboard
|
|
78
|
-
|
|
79
|
-
def merge(self, other: typing.Self) -> typing.Self:
|
|
80
|
-
self.keyboard.extend(copy_keyboard(other.keyboard))
|
|
81
|
-
return self
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
85
|
-
class Keyboard(ABCMarkup[Button], KeyboardModel):
|
|
86
|
-
BUTTON = Button
|
|
87
|
-
|
|
88
|
-
keyboard: list[list[DictStrAny]] = dataclasses.field(
|
|
89
|
-
default_factory=lambda: [[]],
|
|
90
|
-
init=False,
|
|
91
|
-
)
|
|
92
|
-
resize_keyboard: bool = dataclasses.field(default=True)
|
|
93
|
-
one_time_keyboard: bool = dataclasses.field(default=False)
|
|
94
|
-
selective: bool = dataclasses.field(default=False)
|
|
95
|
-
is_persistent: bool = dataclasses.field(default=False)
|
|
96
|
-
|
|
97
|
-
def dict(self) -> DictStrAny:
|
|
98
|
-
self.keyboard = [row for row in self.keyboard if row]
|
|
99
|
-
return {
|
|
100
|
-
k: v.unwrap() if v and isinstance(v, Some) else v
|
|
101
|
-
for k, v in dataclasses.asdict(self).items()
|
|
102
|
-
if
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
def get_markup(self) -> ReplyKeyboardMarkup:
|
|
106
|
-
return ReplyKeyboardMarkup(**self.dict())
|
|
107
|
-
|
|
108
|
-
def keyboard_remove(self, *, selective: bool = False) -> ReplyKeyboardRemove:
|
|
109
|
-
return ReplyKeyboardRemove(remove_keyboard=True, selective=selective)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class InlineKeyboard(ABCMarkup[InlineButton]):
|
|
113
|
-
BUTTON = InlineButton
|
|
114
|
-
|
|
115
|
-
def __init__(self) -> None:
|
|
116
|
-
self.keyboard = [[]]
|
|
117
|
-
|
|
118
|
-
def dict(self) -> DictStrAny:
|
|
119
|
-
self.keyboard = [row for row in self.keyboard if row]
|
|
120
|
-
return dict(inline_keyboard=self.keyboard)
|
|
121
|
-
|
|
122
|
-
def get_markup(self) -> InlineKeyboardMarkup:
|
|
123
|
-
return InlineKeyboardMarkup(**self.dict())
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
__all__ = (
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
from fntypes.option import Some
|
|
6
|
+
|
|
7
|
+
from telegrinder.model import is_none
|
|
8
|
+
from telegrinder.types.objects import (
|
|
9
|
+
InlineKeyboardMarkup,
|
|
10
|
+
ReplyKeyboardMarkup,
|
|
11
|
+
ReplyKeyboardRemove,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .buttons import BaseButton, Button, InlineButton, RowButtons
|
|
15
|
+
|
|
16
|
+
type DictStrAny = dict[str, typing.Any]
|
|
17
|
+
type AnyMarkup = InlineKeyboardMarkup | ReplyKeyboardMarkup
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def copy_keyboard(keyboard: list[list[DictStrAny]]) -> list[list[DictStrAny]]:
|
|
21
|
+
return [row.copy() for row in keyboard]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
25
|
+
class KeyboardModel:
|
|
26
|
+
resize_keyboard: bool
|
|
27
|
+
one_time_keyboard: bool
|
|
28
|
+
selective: bool
|
|
29
|
+
is_persistent: bool
|
|
30
|
+
keyboard: list[list[DictStrAny]]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ABCMarkup[KeyboardButton: BaseButton](ABC):
|
|
34
|
+
BUTTON: type[KeyboardButton]
|
|
35
|
+
keyboard: list[list[DictStrAny]]
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def dict(self) -> DictStrAny:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def get_markup(self) -> AnyMarkup:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def get_empty_markup(cls) -> AnyMarkup:
|
|
47
|
+
return cls().get_markup()
|
|
48
|
+
|
|
49
|
+
def add(self, row_or_button: RowButtons[KeyboardButton] | KeyboardButton, /) -> typing.Self:
|
|
50
|
+
if not self.keyboard:
|
|
51
|
+
self.row()
|
|
52
|
+
|
|
53
|
+
if isinstance(row_or_button, RowButtons):
|
|
54
|
+
self.keyboard[-1].extend(row_or_button.get_data())
|
|
55
|
+
if row_or_button.auto_row:
|
|
56
|
+
self.row()
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
self.keyboard[-1].append(row_or_button.get_data())
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
def row(self) -> typing.Self:
|
|
63
|
+
if len(self.keyboard) and not len(self.keyboard[-1]):
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
self.keyboard.append([])
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def format(self, **format_data: str) -> typing.Self:
|
|
70
|
+
copy_keyboard = self.__class__()
|
|
71
|
+
for row in self.keyboard:
|
|
72
|
+
for button in row:
|
|
73
|
+
copy_button = button.copy()
|
|
74
|
+
copy_button["text"] = copy_button["text"].format(**format_data)
|
|
75
|
+
copy_keyboard.add(self.BUTTON(**copy_button))
|
|
76
|
+
copy_keyboard.row()
|
|
77
|
+
return copy_keyboard
|
|
78
|
+
|
|
79
|
+
def merge(self, other: typing.Self) -> typing.Self:
|
|
80
|
+
self.keyboard.extend(copy_keyboard(other.keyboard))
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
85
|
+
class Keyboard(ABCMarkup[Button], KeyboardModel):
|
|
86
|
+
BUTTON = Button
|
|
87
|
+
|
|
88
|
+
keyboard: list[list[DictStrAny]] = dataclasses.field(
|
|
89
|
+
default_factory=lambda: [[]],
|
|
90
|
+
init=False,
|
|
91
|
+
)
|
|
92
|
+
resize_keyboard: bool = dataclasses.field(default=True)
|
|
93
|
+
one_time_keyboard: bool = dataclasses.field(default=False)
|
|
94
|
+
selective: bool = dataclasses.field(default=False)
|
|
95
|
+
is_persistent: bool = dataclasses.field(default=False)
|
|
96
|
+
|
|
97
|
+
def dict(self) -> DictStrAny:
|
|
98
|
+
self.keyboard = [row for row in self.keyboard if row]
|
|
99
|
+
return {
|
|
100
|
+
k: v.unwrap() if v and isinstance(v, Some) else v
|
|
101
|
+
for k, v in dataclasses.asdict(self).items()
|
|
102
|
+
if not is_none(v)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
def get_markup(self) -> ReplyKeyboardMarkup:
|
|
106
|
+
return ReplyKeyboardMarkup(**self.dict())
|
|
107
|
+
|
|
108
|
+
def keyboard_remove(self, *, selective: bool = False) -> ReplyKeyboardRemove:
|
|
109
|
+
return ReplyKeyboardRemove(remove_keyboard=True, selective=selective)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class InlineKeyboard(ABCMarkup[InlineButton]):
|
|
113
|
+
BUTTON = InlineButton
|
|
114
|
+
|
|
115
|
+
def __init__(self) -> None:
|
|
116
|
+
self.keyboard = [[]]
|
|
117
|
+
|
|
118
|
+
def dict(self) -> DictStrAny:
|
|
119
|
+
self.keyboard = [row for row in self.keyboard if row]
|
|
120
|
+
return dict(inline_keyboard=self.keyboard)
|
|
121
|
+
|
|
122
|
+
def get_markup(self) -> InlineKeyboardMarkup:
|
|
123
|
+
return InlineKeyboardMarkup(**self.dict())
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
__all__ = (
|
|
127
127
|
"ABCMarkup",
|
|
128
128
|
"InlineKeyboard",
|
|
129
129
|
"Keyboard",
|
|
130
130
|
"KeyboardModel",
|
|
131
|
-
"copy_keyboard",
|
|
132
|
-
)
|
|
131
|
+
"copy_keyboard",
|
|
132
|
+
)
|