telegrinder 0.1.dev158__tar.gz → 0.1.dev159__tar.gz
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-0.1.dev158 → telegrinder-0.1.dev159}/PKG-INFO +3 -2
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/pyproject.toml +2 -2
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/api/abc.py +4 -4
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/api/api.py +25 -11
- telegrinder-0.1.dev159/telegrinder/bot/cute_types/base.py +138 -0
- telegrinder-0.1.dev159/telegrinder/bot/cute_types/callback_query.py +471 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/cute_types/inline_query.py +13 -13
- telegrinder-0.1.dev159/telegrinder/bot/cute_types/message.py +3005 -0
- telegrinder-0.1.dev159/telegrinder/bot/cute_types/update.py +30 -0
- telegrinder-0.1.dev159/telegrinder/bot/cute_types/utils.py +794 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/view/abc.py +22 -15
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/adapter/event.py +12 -6
- telegrinder-0.1.dev159/telegrinder/client/__init__.py +4 -0
- telegrinder-0.1.dev159/telegrinder/client/abc.py +75 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/client/aiohttp.py +35 -22
- telegrinder-0.1.dev159/telegrinder/model.py +144 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/modules.py +2 -2
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/msgspec_utils.py +28 -8
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/__init__.py +0 -27
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/buttons.py +19 -5
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/types/enums.py +11 -9
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/types/methods.py +311 -152
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/types/objects.py +109 -67
- telegrinder-0.1.dev158/telegrinder/bot/cute_types/base.py +0 -47
- telegrinder-0.1.dev158/telegrinder/bot/cute_types/callback_query.py +0 -78
- telegrinder-0.1.dev158/telegrinder/bot/cute_types/message.py +0 -193
- telegrinder-0.1.dev158/telegrinder/bot/cute_types/update.py +0 -23
- telegrinder-0.1.dev158/telegrinder/client/__init__.py +0 -4
- telegrinder-0.1.dev158/telegrinder/client/abc.py +0 -52
- telegrinder-0.1.dev158/telegrinder/model.py +0 -96
- telegrinder-0.1.dev158/telegrinder/tools/inline_query.py +0 -684
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/LICENSE +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/readme.md +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/api/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/api/error.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/api/response.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/bot.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/cute_types/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/composition.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/context.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/dispatch.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/handler/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/handler/func.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/handler/message_reply.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/middleware/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/process.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/return_manager/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/return_manager/message.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/view/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/view/box.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/view/inline_query.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/view/message.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/waiter_machine/machine.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/polling/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/polling/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/polling/polling.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/adapter/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/adapter/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/adapter/errors.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/adapter/raw_update.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/callback_data.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/command.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/enum_text.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/func.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/fuzzy.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/inline.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/integer.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/is_from.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/markup.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/mention.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/message_entities.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/regex.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/rule_enum.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/start.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/rules/text.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/scenario/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/scenario/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/scenario/checkbox.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/bot/scenario/choice.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/msgspec_json.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/attachment.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/base.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/composer.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/container.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/message.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/rule.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/source.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/text.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/tools/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/tools/generator.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/node/update.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/rules.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/error_handler/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/error_handler/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/error_handler/error_handler.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/formatting/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/formatting/html.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/formatting/links.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/formatting/spec_html_formats.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/global_context/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/global_context/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/global_context/global_context.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/i18n/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/i18n/base.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/i18n/middleware/base.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/i18n/simple.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/kb_set/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/kb_set/base.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/kb_set/yaml.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/keyboard.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/loop_wrapper/__init__.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/loop_wrapper/abc.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/magic.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/tools/parse_mode.py +0 -0
- {telegrinder-0.1.dev158 → telegrinder-0.1.dev159}/telegrinder/types/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: telegrinder
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.dev159
|
|
4
4
|
Summary: Framework for effective and reliable async telegram bot building.
|
|
5
5
|
Home-page: https://github.com/timoniq/telegrinder
|
|
6
6
|
License: MIT
|
|
@@ -14,12 +14,13 @@ Classifier: Intended Audience :: Developers
|
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
19
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
20
|
Classifier: Typing :: Typed
|
|
20
21
|
Requires-Dist: PyYAML (>=6.0,<7.0)
|
|
21
22
|
Requires-Dist: aiohttp (>=3.8.1,<4.0.0)
|
|
22
|
-
Requires-Dist: certifi (>=
|
|
23
|
+
Requires-Dist: certifi (>=2023.7.22,<2024.0.0)
|
|
23
24
|
Requires-Dist: choicelib (>=0.1.5,<0.2.0)
|
|
24
25
|
Requires-Dist: colorama (>=0.4.0,<0.5.0)
|
|
25
26
|
Requires-Dist: envparse (>=0.2.0,<0.3.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "telegrinder"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.dev159"
|
|
4
4
|
description = "Framework for effective and reliable async telegram bot building."
|
|
5
5
|
authors = ["timoniq <tesseradecades@mail.ru>"]
|
|
6
6
|
maintainers = ["luwqz1"]
|
|
@@ -36,7 +36,7 @@ vbml = "^1.1.post1"
|
|
|
36
36
|
choicelib = "^0.1.5"
|
|
37
37
|
envparse = "^0.2.0"
|
|
38
38
|
PyYAML = "^6.0"
|
|
39
|
-
certifi = "^
|
|
39
|
+
certifi = "^2023.7.22"
|
|
40
40
|
msgspec = "^0.18.4"
|
|
41
41
|
requests = "^2.28.1"
|
|
42
42
|
typing-extensions = "^4.8.0"
|
|
@@ -15,7 +15,7 @@ from .error import InvalidTokenError
|
|
|
15
15
|
class Token(str):
|
|
16
16
|
def __new__(cls, token: str) -> typing.Self:
|
|
17
17
|
if token.count(":") != 1 or not token.split(":")[0].isdigit():
|
|
18
|
-
raise InvalidTokenError("Invalid token, it should look like this '123:
|
|
18
|
+
raise InvalidTokenError("Invalid token, it should look like this '123:ABC'.")
|
|
19
19
|
return super().__new__(cls, token)
|
|
20
20
|
|
|
21
21
|
@classmethod
|
|
@@ -42,15 +42,15 @@ class ABCAPI(ABC):
|
|
|
42
42
|
async def request(
|
|
43
43
|
self,
|
|
44
44
|
method: str,
|
|
45
|
-
data: dict | None = None,
|
|
46
|
-
) -> Result[list | dict | bool, APIError]:
|
|
45
|
+
data: dict[str, typing.Any] | None = None,
|
|
46
|
+
) -> Result[list[typing.Any] | dict[str, typing.Any] | bool, APIError]:
|
|
47
47
|
pass
|
|
48
48
|
|
|
49
49
|
@abstractmethod
|
|
50
50
|
async def request_raw(
|
|
51
51
|
self,
|
|
52
52
|
method: str,
|
|
53
|
-
data: dict | None = None,
|
|
53
|
+
data: dict[str, typing.Any] | None = None,
|
|
54
54
|
) -> Result[msgspec.Raw, APIError]:
|
|
55
55
|
pass
|
|
56
56
|
|
|
@@ -5,26 +5,38 @@ from fntypes.result import Error, Ok, Result
|
|
|
5
5
|
|
|
6
6
|
from telegrinder.api.response import APIResponse
|
|
7
7
|
from telegrinder.client import ABCClient, AiohttpClient
|
|
8
|
-
from telegrinder.model import
|
|
8
|
+
from telegrinder.model import DataConverter, decoder
|
|
9
9
|
from telegrinder.types.methods import APIMethods
|
|
10
10
|
|
|
11
11
|
from .abc import ABCAPI, APIError, Token
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def compose_data(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
def compose_data(
|
|
15
|
+
client: ABCClient,
|
|
16
|
+
data: dict[str, typing.Any],
|
|
17
|
+
files: dict[str, tuple[str, bytes]],
|
|
18
|
+
) -> typing.Any:
|
|
19
|
+
converter = DataConverter(files=files)
|
|
20
|
+
return client.get_form(
|
|
21
|
+
data={k: converter.convert(v) for k, v in data.items()},
|
|
22
|
+
files=converter.files,
|
|
23
|
+
)
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
class API(ABCAPI, APIMethods):
|
|
22
27
|
API_URL: typing.ClassVar[str] = "https://api.telegram.org/"
|
|
23
28
|
|
|
24
|
-
def __init__(self, token: Token, *, http: ABCClient | None = None):
|
|
29
|
+
def __init__(self, token: Token, *, http: ABCClient | None = None) -> None:
|
|
25
30
|
self.token = token
|
|
26
31
|
self.http = http or AiohttpClient()
|
|
27
32
|
super().__init__(self)
|
|
33
|
+
|
|
34
|
+
def __repr__(self) -> str:
|
|
35
|
+
return "<{}: id={}, http={!r}>".format(
|
|
36
|
+
self.__class__.__name__,
|
|
37
|
+
self.id,
|
|
38
|
+
self.http,
|
|
39
|
+
)
|
|
28
40
|
|
|
29
41
|
@property
|
|
30
42
|
def id(self) -> int:
|
|
@@ -37,11 +49,12 @@ class API(ABCAPI, APIMethods):
|
|
|
37
49
|
async def request(
|
|
38
50
|
self,
|
|
39
51
|
method: str,
|
|
40
|
-
data: dict | None = None,
|
|
41
|
-
|
|
52
|
+
data: dict[str, typing.Any] | None = None,
|
|
53
|
+
files: dict[str, tuple[str, bytes]] | None = None,
|
|
54
|
+
) -> Result[dict[str, typing.Any] | list[typing.Any] | bool, APIError]:
|
|
42
55
|
response = await self.http.request_json(
|
|
43
56
|
url=self.request_url + method,
|
|
44
|
-
data=compose_data(self.http, data or {})
|
|
57
|
+
data=compose_data(self.http, data or {}, files or {})
|
|
45
58
|
)
|
|
46
59
|
if response.get("ok"):
|
|
47
60
|
assert "result" in response
|
|
@@ -55,10 +68,11 @@ class API(ABCAPI, APIMethods):
|
|
|
55
68
|
self,
|
|
56
69
|
method: str,
|
|
57
70
|
data: dict[str, typing.Any] | None = None,
|
|
71
|
+
files: dict[str, tuple[str, bytes]] | None = None,
|
|
58
72
|
) -> Result[msgspec.Raw, APIError]:
|
|
59
73
|
response_bytes = await self.http.request_bytes(
|
|
60
74
|
url=self.request_url + method,
|
|
61
|
-
data=compose_data(self.http, data or {}),
|
|
75
|
+
data=compose_data(self.http, data or {}, files or {}),
|
|
62
76
|
)
|
|
63
77
|
return decoder.decode(response_bytes, type=APIResponse).to_result()
|
|
64
78
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import inspect
|
|
3
|
+
import typing
|
|
4
|
+
from functools import wraps
|
|
5
|
+
|
|
6
|
+
from fntypes.result import Result
|
|
7
|
+
|
|
8
|
+
from telegrinder.api import ABCAPI, API
|
|
9
|
+
from telegrinder.model import Model, get_params
|
|
10
|
+
|
|
11
|
+
F = typing.TypeVar("F", bound=typing.Callable[..., typing.Any])
|
|
12
|
+
CuteT = typing.TypeVar("CuteT", bound="BaseCute")
|
|
13
|
+
UpdateT = typing.TypeVar("UpdateT", bound=Model)
|
|
14
|
+
|
|
15
|
+
Executor: typing.TypeAlias = typing.Callable[
|
|
16
|
+
[CuteT, str, dict[str, typing.Any]],
|
|
17
|
+
typing.Awaitable[Result[typing.Any, typing.Any]],
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
if typing.TYPE_CHECKING:
|
|
21
|
+
|
|
22
|
+
class BaseCute(Model, typing.Generic[UpdateT]):
|
|
23
|
+
api: ABCAPI
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_update(cls, update: UpdateT, bound_api: ABCAPI) -> typing.Self:
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def ctx_api(self) -> API:
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
|
|
35
|
+
class BaseCute(typing.Generic[UpdateT]):
|
|
36
|
+
api: ABCAPI
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_update(cls, update, bound_api):
|
|
40
|
+
return cls(**update.to_dict(), api=bound_api)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def ctx_api(self):
|
|
44
|
+
assert isinstance(self.api, API)
|
|
45
|
+
return self.api
|
|
46
|
+
|
|
47
|
+
def to_dict(self, *, exclude_fields=None):
|
|
48
|
+
exclude_fields = exclude_fields or set()
|
|
49
|
+
return super().to_dict(exclude_fields={"api"} | exclude_fields)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def compose_method_params(
|
|
53
|
+
params: dict[str, typing.Any],
|
|
54
|
+
update: CuteT,
|
|
55
|
+
*,
|
|
56
|
+
default_params: set[str | tuple[str, str]] | None = None,
|
|
57
|
+
validators: dict[str, typing.Callable[[CuteT], bool]] | None = None,
|
|
58
|
+
) -> dict[str, typing.Any]:
|
|
59
|
+
"""Compose method `params` from `update` by `default_params` and `validators`.
|
|
60
|
+
|
|
61
|
+
:param params: Method params.
|
|
62
|
+
:param update: Update object.
|
|
63
|
+
:param default_params: Default params. \
|
|
64
|
+
type (`str`) - Attribute name to be taken from `update` if param undefined. \
|
|
65
|
+
type (`tuple[str, str]`) - tuple[0] Parameter name to be set in `params`, \
|
|
66
|
+
tuple[1] attribute name to be taken from `update`.
|
|
67
|
+
:param validators: Validators mapping (`str, Callable`), key - `Parameter name` \
|
|
68
|
+
for which the validator will be applied, value - `Validator`, if returned `True` \
|
|
69
|
+
parameter will be set, otherwise will not be set.
|
|
70
|
+
:return: Composed params.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
default_params = default_params or set()
|
|
74
|
+
validators = validators or {}
|
|
75
|
+
|
|
76
|
+
for param in default_params:
|
|
77
|
+
param_name = param if isinstance(param, str) else param[0]
|
|
78
|
+
if param_name not in params:
|
|
79
|
+
if param_name in validators and not validators[param_name](update):
|
|
80
|
+
continue
|
|
81
|
+
params[param_name] = getattr(update, param if isinstance(param, str) else param[1])
|
|
82
|
+
|
|
83
|
+
return params
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# NOTE: implement parser on ast for methods decorated this decorator
|
|
87
|
+
# to support updates to the schema Bot API.
|
|
88
|
+
def shortcut(
|
|
89
|
+
method_name: str,
|
|
90
|
+
*,
|
|
91
|
+
executor: Executor[CuteT] | None = None,
|
|
92
|
+
custom_params: set[str] | None = None,
|
|
93
|
+
):
|
|
94
|
+
def wrapper(func: F) -> F:
|
|
95
|
+
@wraps(func)
|
|
96
|
+
async def inner(self: CuteT, *args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
|
97
|
+
if executor is None:
|
|
98
|
+
return await func(self, *args, **kwargs)
|
|
99
|
+
signature_params = {
|
|
100
|
+
k: p
|
|
101
|
+
for k, p in inspect.signature(func).parameters.items()
|
|
102
|
+
if k != "self"
|
|
103
|
+
}
|
|
104
|
+
params: dict[str, typing.Any] = {}
|
|
105
|
+
index = 0
|
|
106
|
+
|
|
107
|
+
for k, p in signature_params.items():
|
|
108
|
+
if p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY) and len(args) > index:
|
|
109
|
+
params[k] = args[index]
|
|
110
|
+
index += 1
|
|
111
|
+
continue
|
|
112
|
+
if p.kind in (p.VAR_KEYWORD, p.VAR_POSITIONAL):
|
|
113
|
+
params[k] = kwargs.copy() if p.kind is p.VAR_KEYWORD else args[index:]
|
|
114
|
+
continue
|
|
115
|
+
params[k] = kwargs.pop(k, p.default) if p.default is not p.empty else kwargs.pop(k)
|
|
116
|
+
|
|
117
|
+
return await executor(self, method_name, get_params(params))
|
|
118
|
+
|
|
119
|
+
func.__repr__ = lambda _: f"<Shortcut {method_name!r}@{func!r}>"
|
|
120
|
+
inner.__shortcut__ = Shortcut( # type: ignore
|
|
121
|
+
method_name=method_name,
|
|
122
|
+
executor=executor,
|
|
123
|
+
custom_params=custom_params or set(),
|
|
124
|
+
)
|
|
125
|
+
return inner # type: ignore
|
|
126
|
+
|
|
127
|
+
return wrapper
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclasses.dataclass
|
|
131
|
+
class Shortcut:
|
|
132
|
+
method_name: str
|
|
133
|
+
_: dataclasses.KW_ONLY
|
|
134
|
+
executor: Executor | None = dataclasses.field(default=None)
|
|
135
|
+
custom_params: set[str] = dataclasses.field(default_factory=lambda: set())
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
__all__ = ("BaseCute", "Shortcut", "compose_method_params", "shortcut")
|