telegrinder 0.1.dev158__py3-none-any.whl → 0.1.dev160__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 +4 -2
- telegrinder/api/__init__.py +0 -0
- telegrinder/api/abc.py +6 -4
- telegrinder/api/api.py +25 -11
- telegrinder/api/error.py +0 -0
- telegrinder/api/response.py +0 -0
- telegrinder/bot/__init__.py +0 -0
- telegrinder/bot/bot.py +0 -0
- telegrinder/bot/cute_types/__init__.py +0 -0
- telegrinder/bot/cute_types/base.py +100 -9
- telegrinder/bot/cute_types/callback_query.py +426 -37
- telegrinder/bot/cute_types/inline_query.py +44 -17
- telegrinder/bot/cute_types/message.py +2885 -117
- telegrinder/bot/cute_types/update.py +14 -7
- telegrinder/bot/cute_types/utils.py +542 -0
- telegrinder/bot/dispatch/__init__.py +0 -0
- telegrinder/bot/dispatch/abc.py +0 -0
- telegrinder/bot/dispatch/composition.py +0 -0
- telegrinder/bot/dispatch/context.py +0 -0
- telegrinder/bot/dispatch/dispatch.py +0 -0
- telegrinder/bot/dispatch/handler/__init__.py +0 -0
- telegrinder/bot/dispatch/handler/abc.py +0 -0
- telegrinder/bot/dispatch/handler/func.py +1 -1
- telegrinder/bot/dispatch/handler/message_reply.py +0 -0
- telegrinder/bot/dispatch/middleware/__init__.py +0 -0
- telegrinder/bot/dispatch/middleware/abc.py +0 -0
- telegrinder/bot/dispatch/process.py +0 -0
- telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
- telegrinder/bot/dispatch/return_manager/abc.py +0 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
- telegrinder/bot/dispatch/return_manager/message.py +0 -0
- telegrinder/bot/dispatch/view/__init__.py +0 -0
- telegrinder/bot/dispatch/view/abc.py +40 -29
- telegrinder/bot/dispatch/view/box.py +0 -0
- telegrinder/bot/dispatch/view/callback_query.py +0 -0
- telegrinder/bot/dispatch/view/inline_query.py +0 -0
- telegrinder/bot/dispatch/view/message.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -0
- telegrinder/bot/polling/__init__.py +0 -0
- telegrinder/bot/polling/abc.py +0 -0
- telegrinder/bot/polling/polling.py +7 -11
- telegrinder/bot/rules/__init__.py +0 -0
- telegrinder/bot/rules/abc.py +1 -1
- telegrinder/bot/rules/adapter/__init__.py +0 -0
- telegrinder/bot/rules/adapter/abc.py +0 -0
- telegrinder/bot/rules/adapter/errors.py +0 -0
- telegrinder/bot/rules/adapter/event.py +12 -6
- telegrinder/bot/rules/adapter/raw_update.py +0 -0
- telegrinder/bot/rules/callback_data.py +3 -11
- telegrinder/bot/rules/command.py +0 -0
- telegrinder/bot/rules/enum_text.py +0 -0
- telegrinder/bot/rules/func.py +0 -0
- telegrinder/bot/rules/fuzzy.py +0 -0
- telegrinder/bot/rules/inline.py +2 -1
- telegrinder/bot/rules/integer.py +0 -0
- telegrinder/bot/rules/is_from.py +0 -0
- telegrinder/bot/rules/markup.py +3 -1
- telegrinder/bot/rules/mention.py +0 -0
- telegrinder/bot/rules/message_entities.py +3 -1
- telegrinder/bot/rules/regex.py +1 -1
- telegrinder/bot/rules/rule_enum.py +0 -0
- telegrinder/bot/rules/start.py +0 -0
- telegrinder/bot/rules/text.py +0 -0
- telegrinder/bot/scenario/__init__.py +0 -0
- telegrinder/bot/scenario/abc.py +0 -0
- telegrinder/bot/scenario/checkbox.py +0 -0
- telegrinder/bot/scenario/choice.py +0 -0
- telegrinder/client/__init__.py +2 -2
- telegrinder/client/abc.py +35 -12
- telegrinder/client/aiohttp.py +35 -22
- telegrinder/model.py +70 -23
- telegrinder/modules.py +22 -12
- telegrinder/msgspec_json.py +0 -0
- telegrinder/msgspec_utils.py +30 -10
- telegrinder/node/__init__.py +0 -0
- telegrinder/node/attachment.py +0 -0
- telegrinder/node/base.py +0 -0
- telegrinder/node/composer.py +0 -0
- telegrinder/node/container.py +0 -0
- telegrinder/node/message.py +0 -0
- telegrinder/node/rule.py +0 -0
- telegrinder/node/source.py +0 -0
- telegrinder/node/text.py +0 -0
- telegrinder/node/tools/__init__.py +0 -0
- telegrinder/node/tools/generator.py +0 -0
- telegrinder/node/update.py +0 -0
- telegrinder/rules.py +0 -0
- telegrinder/tools/__init__.py +2 -30
- telegrinder/tools/buttons.py +19 -5
- telegrinder/tools/error_handler/__init__.py +2 -0
- telegrinder/tools/error_handler/abc.py +5 -1
- telegrinder/tools/error_handler/error.py +10 -0
- telegrinder/tools/error_handler/error_handler.py +100 -81
- telegrinder/tools/formatting/__init__.py +0 -0
- telegrinder/tools/formatting/html.py +0 -0
- telegrinder/tools/formatting/links.py +0 -0
- telegrinder/tools/formatting/spec_html_formats.py +0 -0
- telegrinder/tools/global_context/__init__.py +0 -0
- telegrinder/tools/global_context/abc.py +0 -0
- telegrinder/tools/global_context/global_context.py +65 -67
- telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
- telegrinder/tools/i18n/__init__.py +0 -0
- telegrinder/tools/i18n/base.py +0 -0
- telegrinder/tools/i18n/middleware/__init__.py +0 -0
- telegrinder/tools/i18n/middleware/base.py +0 -0
- telegrinder/tools/i18n/simple.py +0 -0
- telegrinder/tools/kb_set/__init__.py +0 -0
- telegrinder/tools/kb_set/base.py +0 -0
- telegrinder/tools/kb_set/yaml.py +3 -3
- telegrinder/tools/keyboard.py +17 -26
- telegrinder/tools/loop_wrapper/__init__.py +0 -0
- telegrinder/tools/loop_wrapper/abc.py +0 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
- telegrinder/tools/magic.py +1 -1
- telegrinder/tools/parse_mode.py +0 -0
- telegrinder/types/__init__.py +0 -0
- telegrinder/types/enums.py +13 -9
- telegrinder/types/methods.py +783 -673
- telegrinder/types/objects.py +317 -163
- {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/METADATA +9 -7
- {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/RECORD +42 -41
- {telegrinder-0.1.dev158.dist-info → telegrinder-0.1.dev160.dist-info}/WHEEL +1 -1
- telegrinder/tools/inline_query.py +0 -684
telegrinder/__init__.py
CHANGED
|
@@ -39,6 +39,7 @@ from .client import ABCClient, AiohttpClient
|
|
|
39
39
|
from .model import Model
|
|
40
40
|
from .modules import logger
|
|
41
41
|
from .tools import (
|
|
42
|
+
ABCErrorHandler,
|
|
42
43
|
ABCGlobalContext,
|
|
43
44
|
ABCLoopWrapper,
|
|
44
45
|
ABCTranslator,
|
|
@@ -47,6 +48,7 @@ from .tools import (
|
|
|
47
48
|
Button,
|
|
48
49
|
CtxVar,
|
|
49
50
|
DelayedTask,
|
|
51
|
+
ErrorHandler,
|
|
50
52
|
FormatString,
|
|
51
53
|
GlobalContext,
|
|
52
54
|
HTMLFormatter,
|
|
@@ -62,7 +64,6 @@ from .tools import (
|
|
|
62
64
|
SimpleI18n,
|
|
63
65
|
SimpleTranslator,
|
|
64
66
|
ctx_var,
|
|
65
|
-
keyboard_remove,
|
|
66
67
|
magic_bundle,
|
|
67
68
|
)
|
|
68
69
|
|
|
@@ -76,6 +77,7 @@ __all__ = (
|
|
|
76
77
|
"ABCAPI",
|
|
77
78
|
"ABCClient",
|
|
78
79
|
"ABCDispatch",
|
|
80
|
+
"ABCErrorHandler",
|
|
79
81
|
"ABCGlobalContext",
|
|
80
82
|
"ABCHandler",
|
|
81
83
|
"ABCLoopWrapper",
|
|
@@ -107,6 +109,7 @@ __all__ = (
|
|
|
107
109
|
"CtxVar",
|
|
108
110
|
"DelayedTask",
|
|
109
111
|
"Dispatch",
|
|
112
|
+
"ErrorHandler",
|
|
110
113
|
"FormatString",
|
|
111
114
|
"FuncHandler",
|
|
112
115
|
"GlobalContext",
|
|
@@ -139,7 +142,6 @@ __all__ = (
|
|
|
139
142
|
"ViewBox",
|
|
140
143
|
"WaiterMachine",
|
|
141
144
|
"ctx_var",
|
|
142
|
-
"keyboard_remove",
|
|
143
145
|
"logger",
|
|
144
146
|
"magic_bundle",
|
|
145
147
|
"register_manager",
|
telegrinder/api/__init__.py
CHANGED
|
File without changes
|
telegrinder/api/abc.py
CHANGED
|
@@ -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,17 @@ class ABCAPI(ABC):
|
|
|
42
42
|
async def request(
|
|
43
43
|
self,
|
|
44
44
|
method: str,
|
|
45
|
-
data: dict | None = None,
|
|
46
|
-
|
|
45
|
+
data: dict[str, typing.Any] | None = None,
|
|
46
|
+
files: dict[str, tuple[str, bytes]] | None = None,
|
|
47
|
+
) -> Result[list[typing.Any] | dict[str, typing.Any] | bool, APIError]:
|
|
47
48
|
pass
|
|
48
49
|
|
|
49
50
|
@abstractmethod
|
|
50
51
|
async def request_raw(
|
|
51
52
|
self,
|
|
52
53
|
method: str,
|
|
53
|
-
data: dict | None = None,
|
|
54
|
+
data: dict[str, typing.Any] | None = None,
|
|
55
|
+
files: dict[str, tuple[str, bytes]] | None = None,
|
|
54
56
|
) -> Result[msgspec.Raw, APIError]:
|
|
55
57
|
pass
|
|
56
58
|
|
telegrinder/api/api.py
CHANGED
|
@@ -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(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
|
|
telegrinder/api/error.py
CHANGED
|
File without changes
|
telegrinder/api/response.py
CHANGED
|
File without changes
|
telegrinder/bot/__init__.py
CHANGED
|
File without changes
|
telegrinder/bot/bot.py
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import inspect
|
|
1
3
|
import typing
|
|
4
|
+
from functools import wraps
|
|
5
|
+
|
|
6
|
+
from fntypes.result import Result
|
|
2
7
|
|
|
3
8
|
from telegrinder.api import ABCAPI, API
|
|
4
|
-
from telegrinder.model import Model
|
|
9
|
+
from telegrinder.model import Model, get_params
|
|
5
10
|
|
|
11
|
+
F = typing.TypeVar("F", bound=typing.Callable[..., typing.Any])
|
|
12
|
+
CuteT = typing.TypeVar("CuteT", bound="BaseCute")
|
|
6
13
|
UpdateT = typing.TypeVar("UpdateT", bound=Model)
|
|
7
14
|
|
|
15
|
+
Executor: typing.TypeAlias = typing.Callable[
|
|
16
|
+
[CuteT, str, dict[str, typing.Any]],
|
|
17
|
+
typing.Awaitable[Result[typing.Any, typing.Any]],
|
|
18
|
+
]
|
|
19
|
+
|
|
8
20
|
if typing.TYPE_CHECKING:
|
|
9
21
|
|
|
10
22
|
class BaseCute(Model, typing.Generic[UpdateT]):
|
|
@@ -18,13 +30,6 @@ if typing.TYPE_CHECKING:
|
|
|
18
30
|
def ctx_api(self) -> API:
|
|
19
31
|
...
|
|
20
32
|
|
|
21
|
-
def to_dict(
|
|
22
|
-
self,
|
|
23
|
-
*,
|
|
24
|
-
exclude_fields: set[str] | None = None,
|
|
25
|
-
) -> dict[str, typing.Any]:
|
|
26
|
-
...
|
|
27
|
-
|
|
28
33
|
else:
|
|
29
34
|
|
|
30
35
|
class BaseCute(typing.Generic[UpdateT]):
|
|
@@ -44,4 +49,90 @@ else:
|
|
|
44
49
|
return super().to_dict(exclude_fields={"api"} | exclude_fields)
|
|
45
50
|
|
|
46
51
|
|
|
47
|
-
|
|
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")
|