telegrinder 0.1.dev170__py3-none-any.whl → 0.2.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 +2 -2
- telegrinder/api/__init__.py +1 -2
- telegrinder/api/api.py +15 -6
- telegrinder/api/error.py +2 -1
- telegrinder/api/token.py +36 -0
- telegrinder/bot/__init__.py +12 -6
- telegrinder/bot/bot.py +18 -6
- telegrinder/bot/cute_types/__init__.py +7 -7
- telegrinder/bot/cute_types/base.py +122 -20
- telegrinder/bot/cute_types/callback_query.py +10 -6
- telegrinder/bot/cute_types/chat_join_request.py +4 -5
- telegrinder/bot/cute_types/chat_member_updated.py +4 -6
- telegrinder/bot/cute_types/inline_query.py +3 -4
- telegrinder/bot/cute_types/message.py +32 -21
- telegrinder/bot/cute_types/update.py +51 -4
- telegrinder/bot/cute_types/utils.py +3 -466
- telegrinder/bot/dispatch/__init__.py +10 -11
- telegrinder/bot/dispatch/abc.py +8 -5
- telegrinder/bot/dispatch/context.py +17 -8
- telegrinder/bot/dispatch/dispatch.py +71 -48
- telegrinder/bot/dispatch/handler/__init__.py +3 -3
- telegrinder/bot/dispatch/handler/abc.py +4 -4
- telegrinder/bot/dispatch/handler/func.py +46 -22
- telegrinder/bot/dispatch/handler/message_reply.py +6 -7
- telegrinder/bot/dispatch/middleware/__init__.py +1 -1
- telegrinder/bot/dispatch/middleware/abc.py +2 -2
- telegrinder/bot/dispatch/process.py +38 -19
- telegrinder/bot/dispatch/return_manager/__init__.py +4 -4
- telegrinder/bot/dispatch/return_manager/abc.py +3 -3
- telegrinder/bot/dispatch/return_manager/callback_query.py +1 -2
- telegrinder/bot/dispatch/return_manager/inline_query.py +1 -2
- telegrinder/bot/dispatch/return_manager/message.py +1 -2
- telegrinder/bot/dispatch/view/__init__.py +8 -8
- telegrinder/bot/dispatch/view/abc.py +18 -16
- telegrinder/bot/dispatch/view/box.py +75 -64
- telegrinder/bot/dispatch/view/callback_query.py +1 -2
- telegrinder/bot/dispatch/view/chat_join_request.py +1 -2
- telegrinder/bot/dispatch/view/chat_member.py +16 -2
- telegrinder/bot/dispatch/view/inline_query.py +1 -2
- telegrinder/bot/dispatch/view/message.py +12 -5
- telegrinder/bot/dispatch/view/raw.py +9 -8
- telegrinder/bot/dispatch/waiter_machine/__init__.py +3 -3
- telegrinder/bot/dispatch/waiter_machine/machine.py +12 -8
- telegrinder/bot/dispatch/waiter_machine/middleware.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/short_state.py +4 -3
- telegrinder/bot/polling/abc.py +1 -1
- telegrinder/bot/polling/polling.py +6 -6
- telegrinder/bot/rules/__init__.py +20 -20
- telegrinder/bot/rules/abc.py +57 -43
- telegrinder/bot/rules/adapter/__init__.py +5 -5
- telegrinder/bot/rules/adapter/abc.py +6 -3
- telegrinder/bot/rules/adapter/errors.py +2 -1
- telegrinder/bot/rules/adapter/event.py +28 -13
- telegrinder/bot/rules/adapter/node.py +28 -22
- telegrinder/bot/rules/adapter/raw_update.py +13 -5
- telegrinder/bot/rules/callback_data.py +4 -4
- telegrinder/bot/rules/chat_join.py +4 -4
- telegrinder/bot/rules/command.py +5 -7
- telegrinder/bot/rules/func.py +2 -2
- telegrinder/bot/rules/fuzzy.py +1 -1
- telegrinder/bot/rules/inline.py +3 -3
- telegrinder/bot/rules/integer.py +1 -2
- telegrinder/bot/rules/markup.py +5 -3
- telegrinder/bot/rules/message_entities.py +2 -2
- telegrinder/bot/rules/node.py +2 -2
- telegrinder/bot/rules/regex.py +1 -1
- telegrinder/bot/rules/rule_enum.py +1 -1
- telegrinder/bot/rules/text.py +1 -2
- telegrinder/bot/rules/update.py +1 -2
- telegrinder/bot/scenario/abc.py +2 -2
- telegrinder/bot/scenario/checkbox.py +3 -4
- telegrinder/bot/scenario/choice.py +1 -2
- telegrinder/model.py +89 -45
- telegrinder/modules.py +3 -3
- telegrinder/msgspec_utils.py +85 -57
- telegrinder/node/__init__.py +17 -10
- telegrinder/node/attachment.py +19 -16
- telegrinder/node/base.py +46 -22
- telegrinder/node/callback_query.py +5 -9
- telegrinder/node/command.py +6 -2
- telegrinder/node/composer.py +102 -77
- telegrinder/node/container.py +3 -3
- telegrinder/node/event.py +68 -0
- telegrinder/node/me.py +3 -0
- telegrinder/node/message.py +6 -10
- telegrinder/node/polymorphic.py +15 -10
- telegrinder/node/rule.py +20 -6
- telegrinder/node/scope.py +9 -1
- telegrinder/node/source.py +21 -11
- telegrinder/node/text.py +4 -4
- telegrinder/node/update.py +7 -4
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +59 -0
- telegrinder/tools/__init__.py +2 -2
- telegrinder/tools/buttons.py +5 -10
- telegrinder/tools/error_handler/abc.py +2 -2
- telegrinder/tools/error_handler/error.py +2 -0
- telegrinder/tools/error_handler/error_handler.py +6 -6
- telegrinder/tools/formatting/spec_html_formats.py +10 -10
- telegrinder/tools/global_context/__init__.py +2 -2
- telegrinder/tools/global_context/global_context.py +3 -3
- telegrinder/tools/global_context/telegrinder_ctx.py +4 -4
- telegrinder/tools/keyboard.py +3 -3
- telegrinder/tools/loop_wrapper/loop_wrapper.py +47 -13
- telegrinder/tools/magic.py +96 -18
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +2 -0
- telegrinder/types/methods.py +91 -15
- telegrinder/types/objects.py +49 -24
- telegrinder/verification_utils.py +1 -3
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/METADATA +2 -2
- telegrinder-0.2.0.dist-info/RECORD +145 -0
- telegrinder/api/abc.py +0 -73
- telegrinder-0.1.dev170.dist-info/RECORD +0 -143
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/WHEEL +0 -0
telegrinder/bot/rules/command.py
CHANGED
|
@@ -2,24 +2,22 @@ import dataclasses
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
-
from telegrinder.node import Source
|
|
6
5
|
from telegrinder.node.command import CommandInfo, single_split
|
|
7
6
|
from telegrinder.node.me import Me
|
|
7
|
+
from telegrinder.node.source import Source
|
|
8
|
+
from telegrinder.types.enums import ChatType
|
|
8
9
|
|
|
9
|
-
from ...types import ChatType
|
|
10
10
|
from .abc import ABCRule
|
|
11
11
|
|
|
12
|
-
Validator = typing.Callable[[str], typing.Any | None]
|
|
12
|
+
Validator: typing.TypeAlias = typing.Callable[[str], typing.Any | None]
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
@dataclasses.dataclass(frozen=True)
|
|
15
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
16
16
|
class Argument:
|
|
17
17
|
name: str
|
|
18
18
|
validators: list[Validator] = dataclasses.field(default_factory=lambda: [])
|
|
19
19
|
optional: bool = dataclasses.field(default=False, kw_only=True)
|
|
20
20
|
|
|
21
|
-
# NOTE: add optional param `description`
|
|
22
|
-
|
|
23
21
|
def check(self, data: str) -> typing.Any | None:
|
|
24
22
|
for validator in self.validators:
|
|
25
23
|
data = validator(data) # type: ignore
|
|
@@ -79,7 +77,7 @@ class Command(ABCRule):
|
|
|
79
77
|
|
|
80
78
|
return self.parse_arguments(arguments[1:], s)
|
|
81
79
|
|
|
82
|
-
def parse_arguments(self, arguments: list[Argument], s: str) -> dict | None:
|
|
80
|
+
def parse_arguments(self, arguments: list[Argument], s: str) -> dict[str, typing.Any] | None:
|
|
83
81
|
if not arguments:
|
|
84
82
|
return {} if not s else None
|
|
85
83
|
|
telegrinder/bot/rules/func.py
CHANGED
|
@@ -2,7 +2,7 @@ import inspect
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
-
from telegrinder.types import Update
|
|
5
|
+
from telegrinder.types.objects import Update
|
|
6
6
|
|
|
7
7
|
from .abc import ABCAdapter, ABCRule, AdaptTo, RawUpdateAdapter
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ class FuncRule(ABCRule, typing.Generic[AdaptTo]):
|
|
|
12
12
|
self,
|
|
13
13
|
func: typing.Callable[[AdaptTo, Context], typing.Awaitable[bool] | bool],
|
|
14
14
|
adapter: ABCAdapter[Update, AdaptTo] | None = None,
|
|
15
|
-
):
|
|
15
|
+
) -> None:
|
|
16
16
|
self.func = func
|
|
17
17
|
self.adapter = adapter or RawUpdateAdapter() # type: ignore
|
|
18
18
|
|
telegrinder/bot/rules/fuzzy.py
CHANGED
|
@@ -7,7 +7,7 @@ from .abc import ABCRule
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class FuzzyText(ABCRule):
|
|
10
|
-
def __init__(self, texts: str | list[str], min_ratio: float = 0.7):
|
|
10
|
+
def __init__(self, texts: str | list[str], min_ratio: float = 0.7) -> None:
|
|
11
11
|
if isinstance(texts, str):
|
|
12
12
|
texts = [texts]
|
|
13
13
|
self.texts = texts
|
telegrinder/bot/rules/inline.py
CHANGED
|
@@ -21,7 +21,7 @@ class InlineQueryRule(ABCRule[InlineQuery], abc.ABC):
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class HasLocation(InlineQueryRule):
|
|
24
|
-
async def check(self, query: InlineQuery
|
|
24
|
+
async def check(self, query: InlineQuery) -> bool:
|
|
25
25
|
return bool(query.location)
|
|
26
26
|
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ class InlineQueryChatType(InlineQueryRule):
|
|
|
29
29
|
def __init__(self, chat_type: ChatType, /) -> None:
|
|
30
30
|
self.chat_type = chat_type
|
|
31
31
|
|
|
32
|
-
async def check(self, query: InlineQuery
|
|
32
|
+
async def check(self, query: InlineQuery) -> bool:
|
|
33
33
|
return query.chat_type.map(lambda x: x == self.chat_type).unwrap_or(False)
|
|
34
34
|
|
|
35
35
|
|
|
@@ -40,7 +40,7 @@ class InlineQueryText(InlineQueryRule):
|
|
|
40
40
|
]
|
|
41
41
|
self.lower_case = lower_case
|
|
42
42
|
|
|
43
|
-
async def check(self, query: InlineQuery
|
|
43
|
+
async def check(self, query: InlineQuery) -> bool:
|
|
44
44
|
return (query.query.lower() if self.lower_case else query.query) in self.texts
|
|
45
45
|
|
|
46
46
|
|
telegrinder/bot/rules/integer.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from telegrinder.bot.dispatch.context import Context
|
|
2
1
|
from telegrinder.node.text import TextInteger
|
|
3
2
|
|
|
4
3
|
from .abc import ABCRule
|
|
@@ -11,7 +10,7 @@ class IsInteger(NodeRule):
|
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class IntegerInRange(ABCRule):
|
|
14
|
-
def __init__(self, rng: range):
|
|
13
|
+
def __init__(self, rng: range) -> None:
|
|
15
14
|
self.rng = rng
|
|
16
15
|
|
|
17
16
|
async def check(self, integer: TextInteger) -> bool:
|
telegrinder/bot/rules/markup.py
CHANGED
|
@@ -4,12 +4,12 @@ import vbml
|
|
|
4
4
|
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
6
|
from telegrinder.node.text import Text
|
|
7
|
-
from telegrinder.tools.global_context import
|
|
7
|
+
from telegrinder.tools.global_context.telegrinder_ctx import TelegrinderContext
|
|
8
8
|
|
|
9
9
|
from .abc import ABCRule
|
|
10
10
|
|
|
11
11
|
PatternLike: typing.TypeAlias = str | vbml.Pattern
|
|
12
|
-
global_ctx =
|
|
12
|
+
global_ctx: typing.Final[TelegrinderContext] = TelegrinderContext()
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def check_string(patterns: list[vbml.Pattern], s: str, ctx: Context) -> bool:
|
|
@@ -24,7 +24,9 @@ def check_string(patterns: list[vbml.Pattern], s: str, ctx: Context) -> bool:
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class Markup(ABCRule):
|
|
27
|
-
|
|
27
|
+
"""Markup Language. See [VBML docs](https://github.com/tesseradecade/vbml/blob/master/docs/index.md)"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
|
|
28
30
|
if not isinstance(patterns, list):
|
|
29
31
|
patterns = [patterns]
|
|
30
32
|
self.patterns = [
|
|
@@ -10,12 +10,12 @@ Entity: typing.TypeAlias = str | MessageEntityType
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class HasEntities(MessageRule):
|
|
13
|
-
async def check(self, message: Message
|
|
13
|
+
async def check(self, message: Message) -> bool:
|
|
14
14
|
return bool(message.entities)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class MessageEntities(MessageRule, requires=[HasEntities()]):
|
|
18
|
-
def __init__(self, entities: Entity | list[Entity]):
|
|
18
|
+
def __init__(self, entities: Entity | list[Entity], /) -> None:
|
|
19
19
|
self.entities = [entities] if not isinstance(entities, list) else entities
|
|
20
20
|
|
|
21
21
|
async def check(self, message: Message, ctx: Context) -> bool:
|
telegrinder/bot/rules/node.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
3
|
from telegrinder.bot.dispatch.context import Context
|
|
4
|
-
from telegrinder.node import Node
|
|
4
|
+
from telegrinder.node.base import Node
|
|
5
5
|
|
|
6
6
|
from .abc import ABCRule
|
|
7
7
|
from .adapter.node import NodeAdapter
|
|
@@ -15,7 +15,7 @@ class NodeRule(ABCRule[tuple[Node, ...]]):
|
|
|
15
15
|
|
|
16
16
|
@property
|
|
17
17
|
def adapter(self) -> NodeAdapter:
|
|
18
|
-
return NodeAdapter(*self.nodes)
|
|
18
|
+
return NodeAdapter(*self.nodes) # type: ignore
|
|
19
19
|
|
|
20
20
|
async def check(self, resolved_nodes: tuple[Node, ...], ctx: Context) -> typing.Literal[True]:
|
|
21
21
|
for i, node in enumerate(resolved_nodes):
|
telegrinder/bot/rules/regex.py
CHANGED
|
@@ -10,7 +10,7 @@ PatternLike: typing.TypeAlias = str | typing.Pattern[str]
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Regex(ABCRule):
|
|
13
|
-
def __init__(self, regexp: PatternLike | list[PatternLike]):
|
|
13
|
+
def __init__(self, regexp: PatternLike | list[PatternLike]) -> None:
|
|
14
14
|
self.regexp: list[re.Pattern[str]] = []
|
|
15
15
|
match regexp:
|
|
16
16
|
case re.Pattern() as pattern:
|
telegrinder/bot/rules/text.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from telegrinder import node
|
|
2
|
-
from telegrinder.bot.dispatch.context import Context
|
|
3
2
|
from telegrinder.tools.i18n.base import ABCTranslator
|
|
4
3
|
|
|
5
4
|
from .abc import ABCRule, with_caching_translations
|
|
@@ -18,7 +17,7 @@ class Text(ABCRule):
|
|
|
18
17
|
self.texts = texts if not ignore_case else list(map(str.lower, texts))
|
|
19
18
|
self.ignore_case = ignore_case
|
|
20
19
|
|
|
21
|
-
async def check(self, text: node.text.Text
|
|
20
|
+
async def check(self, text: node.text.Text) -> bool:
|
|
22
21
|
return (text if not self.ignore_case else text.lower()) in self.texts
|
|
23
22
|
|
|
24
23
|
@with_caching_translations
|
telegrinder/bot/rules/update.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from telegrinder.bot.cute_types.update import UpdateCute
|
|
2
|
-
from telegrinder.bot.dispatch.context import Context
|
|
3
2
|
from telegrinder.types.enums import UpdateType
|
|
4
3
|
|
|
5
4
|
from .abc import ABCRule
|
|
@@ -9,7 +8,7 @@ class IsUpdateType(ABCRule):
|
|
|
9
8
|
def __init__(self, update_type: UpdateType, /) -> None:
|
|
10
9
|
self.update_type = update_type
|
|
11
10
|
|
|
12
|
-
async def check(self, event: UpdateCute
|
|
11
|
+
async def check(self, event: UpdateCute) -> bool:
|
|
13
12
|
return event.update_type == self.update_type
|
|
14
13
|
|
|
15
14
|
|
telegrinder/bot/scenario/abc.py
CHANGED
|
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
5
|
|
|
6
6
|
if typing.TYPE_CHECKING:
|
|
7
|
-
from telegrinder.api import
|
|
7
|
+
from telegrinder.api import API
|
|
8
8
|
from telegrinder.bot.dispatch.view.abc import ABCStateView
|
|
9
9
|
|
|
10
10
|
EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
@@ -12,7 +12,7 @@ EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
|
12
12
|
|
|
13
13
|
class ABCScenario(ABC, typing.Generic[EventT]):
|
|
14
14
|
@abstractmethod
|
|
15
|
-
def wait(self, api: "
|
|
15
|
+
def wait(self, api: "API", view: "ABCStateView[EventT]") -> typing.Any:
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
18
|
|
|
@@ -4,18 +4,17 @@ import typing
|
|
|
4
4
|
|
|
5
5
|
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
6
6
|
from telegrinder.bot.dispatch.waiter_machine import WaiterMachine
|
|
7
|
+
from telegrinder.bot.scenario.abc import ABCScenario
|
|
7
8
|
from telegrinder.tools import InlineButton, InlineKeyboard
|
|
8
9
|
from telegrinder.tools.parse_mode import ParseMode
|
|
9
10
|
from telegrinder.types.objects import InlineKeyboardMarkup
|
|
10
11
|
|
|
11
|
-
from .abc import ABCScenario
|
|
12
|
-
|
|
13
12
|
if typing.TYPE_CHECKING:
|
|
14
13
|
from telegrinder.api import API
|
|
15
14
|
from telegrinder.bot.dispatch.view.abc import BaseStateView
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
@dataclasses.dataclass
|
|
17
|
+
@dataclasses.dataclass(slots=True)
|
|
19
18
|
class Choice:
|
|
20
19
|
name: str
|
|
21
20
|
is_picked: bool
|
|
@@ -114,7 +113,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
|
|
|
114
113
|
api: "API",
|
|
115
114
|
view: "BaseStateView[CallbackQueryCute]",
|
|
116
115
|
) -> tuple[dict[str, bool], int]:
|
|
117
|
-
assert len(self.choices) >
|
|
116
|
+
assert len(self.choices) > 0
|
|
118
117
|
message = (
|
|
119
118
|
await api.send_message(
|
|
120
119
|
chat_id=self.chat_id,
|
telegrinder/model.py
CHANGED
|
@@ -9,14 +9,13 @@ from types import NoneType
|
|
|
9
9
|
import msgspec
|
|
10
10
|
from fntypes.co import Nothing, Result, Some
|
|
11
11
|
|
|
12
|
-
from .msgspec_utils import decoder, encoder, get_origin
|
|
12
|
+
from telegrinder.msgspec_utils import decoder, encoder, get_origin
|
|
13
13
|
|
|
14
14
|
if typing.TYPE_CHECKING:
|
|
15
15
|
from telegrinder.api.error import APIError
|
|
16
16
|
|
|
17
17
|
T = typing.TypeVar("T")
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
MODEL_CONFIG: typing.Final[dict[str, typing.Any]] = {
|
|
21
20
|
"omit_defaults": True,
|
|
22
21
|
"dict": True,
|
|
@@ -59,49 +58,85 @@ def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
|
|
|
59
58
|
return validated_params
|
|
60
59
|
|
|
61
60
|
|
|
61
|
+
def encode_name(name: str) -> str:
|
|
62
|
+
# TODO
|
|
63
|
+
return MODEL_CONFIG.get("rename", {}).get(name, name)
|
|
64
|
+
|
|
65
|
+
|
|
62
66
|
class Model(msgspec.Struct, **MODEL_CONFIG):
|
|
67
|
+
@classmethod
|
|
68
|
+
def from_data(cls, data: dict[str, typing.Any]) -> typing.Self:
|
|
69
|
+
return decoder.convert(data, type=cls)
|
|
70
|
+
|
|
63
71
|
@classmethod
|
|
64
72
|
def from_bytes(cls, data: bytes) -> typing.Self:
|
|
65
73
|
return decoder.decode(data, type=cls)
|
|
66
74
|
|
|
75
|
+
def _to_dict(
|
|
76
|
+
self,
|
|
77
|
+
dct_name: str,
|
|
78
|
+
exclude_fields: set[str],
|
|
79
|
+
full: bool,
|
|
80
|
+
) -> dict[str, typing.Any]:
|
|
81
|
+
if dct_name not in self.__dict__:
|
|
82
|
+
self.__dict__[dct_name] = (
|
|
83
|
+
msgspec.structs.asdict(self)
|
|
84
|
+
if not full
|
|
85
|
+
else encoder.to_builtins(self.to_dict(exclude_fields=exclude_fields), order="deterministic")
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not exclude_fields:
|
|
89
|
+
return self.__dict__[dct_name]
|
|
90
|
+
|
|
91
|
+
return {key: value for key, value in self.__dict__[dct_name].items() if key not in exclude_fields}
|
|
92
|
+
|
|
67
93
|
def to_dict(
|
|
68
94
|
self,
|
|
69
95
|
*,
|
|
70
96
|
exclude_fields: set[str] | None = None,
|
|
71
97
|
) -> dict[str, typing.Any]:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
key: value for key, value in self.__dict__["model_as_dict"].items() if key not in exclude_fields
|
|
77
|
-
}
|
|
98
|
+
"""
|
|
99
|
+
:param exclude_fields: Model field names to exclude from the dictionary representation of this model.
|
|
100
|
+
:return: A dictionary representation of this model.
|
|
101
|
+
"""
|
|
78
102
|
|
|
103
|
+
return self._to_dict("model_as_dict", exclude_fields or set(), full=False)
|
|
79
104
|
|
|
80
|
-
|
|
105
|
+
def to_full_dict(
|
|
106
|
+
self,
|
|
107
|
+
*,
|
|
108
|
+
exclude_fields: set[str] | None = None,
|
|
109
|
+
) -> dict[str, typing.Any]:
|
|
110
|
+
"""
|
|
111
|
+
:param exclude_fields: Model field names to exclude from the dictionary representation of this model.
|
|
112
|
+
:return: A dictionary representation of this model including all models, structs, custom types.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
return self._to_dict("model_as_full_dict", exclude_fields or set(), full=True)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclasses.dataclass(kw_only=True, frozen=True, slots=True, repr=False)
|
|
81
119
|
class DataConverter:
|
|
82
|
-
|
|
120
|
+
_converters: dict[type[typing.Any], typing.Callable[..., typing.Any]] = dataclasses.field(
|
|
121
|
+
init=False,
|
|
122
|
+
default_factory=lambda: {},
|
|
123
|
+
)
|
|
124
|
+
_files: dict[str, tuple[str, bytes]] = dataclasses.field(default_factory=lambda: {})
|
|
83
125
|
|
|
84
126
|
def __repr__(self) -> str:
|
|
85
127
|
return "<{}: {}>".format(
|
|
86
128
|
self.__class__.__name__,
|
|
87
|
-
", ".join(f"{k}={v.__name__!r}" for k, v in self.
|
|
129
|
+
", ".join(f"{k}={v.__name__!r}" for k, v in self._converters.items()),
|
|
88
130
|
)
|
|
89
131
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@staticmethod
|
|
99
|
-
def convert_enum(data: enum.Enum, _: bool = True) -> typing.Any:
|
|
100
|
-
return data.value
|
|
101
|
-
|
|
102
|
-
@staticmethod
|
|
103
|
-
def convert_datetime(data: datetime, _: bool = True) -> int:
|
|
104
|
-
return int(data.timestamp())
|
|
132
|
+
def __post_init__(self) -> None:
|
|
133
|
+
self._converters.update(
|
|
134
|
+
{
|
|
135
|
+
get_origin(value.__annotations__["data"]): value
|
|
136
|
+
for key, value in vars(self.__class__).items()
|
|
137
|
+
if key.startswith("convert_") and callable(value)
|
|
138
|
+
}
|
|
139
|
+
)
|
|
105
140
|
|
|
106
141
|
def __call__(self, data: typing.Any, *, serialize: bool = True) -> typing.Any:
|
|
107
142
|
converter = self.get_converter(get_origin(type(data)))
|
|
@@ -111,9 +146,25 @@ class DataConverter:
|
|
|
111
146
|
return converter(self, data, serialize)
|
|
112
147
|
return data
|
|
113
148
|
|
|
149
|
+
@property
|
|
150
|
+
def converters(self) -> dict[type[typing.Any], typing.Callable[..., typing.Any]]:
|
|
151
|
+
return self._converters.copy()
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def files(self) -> dict[str, tuple[str, bytes]]:
|
|
155
|
+
return self._files.copy()
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def convert_enum(data: enum.Enum, _: bool = False) -> typing.Any:
|
|
159
|
+
return data.value
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def convert_datetime(data: datetime, _: bool = False) -> int:
|
|
163
|
+
return int(data.timestamp())
|
|
164
|
+
|
|
114
165
|
def get_converter(self, t: type[typing.Any]):
|
|
115
|
-
for
|
|
116
|
-
if issubclass(t,
|
|
166
|
+
for type_, converter in self._converters.items():
|
|
167
|
+
if issubclass(t, type_):
|
|
117
168
|
return converter
|
|
118
169
|
return None
|
|
119
170
|
|
|
@@ -142,25 +193,18 @@ class DataConverter:
|
|
|
142
193
|
converted_lst = [self(x, serialize=False) for x in data]
|
|
143
194
|
return encoder.encode(converted_lst) if serialize is True else converted_lst
|
|
144
195
|
|
|
145
|
-
def convert_tpl(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
and len(data) == 2
|
|
153
|
-
and isinstance(data[0], str)
|
|
154
|
-
and isinstance(data[1], bytes)
|
|
155
|
-
):
|
|
156
|
-
attach_name = secrets.token_urlsafe(16)
|
|
157
|
-
self.files[attach_name] = data
|
|
158
|
-
return "attach://{}".format(attach_name)
|
|
196
|
+
def convert_tpl(self, data: tuple[typing.Any, ...], _: bool = False) -> str | tuple[typing.Any, ...]:
|
|
197
|
+
match data:
|
|
198
|
+
case (str(filename), bytes(content)):
|
|
199
|
+
attach_name = secrets.token_urlsafe(16)
|
|
200
|
+
self._files[attach_name] = (filename, content)
|
|
201
|
+
return "attach://{}".format(attach_name)
|
|
202
|
+
|
|
159
203
|
return data
|
|
160
204
|
|
|
161
205
|
|
|
162
206
|
class Proxy:
|
|
163
|
-
def __init__(self, cfg: "_ProxiedDict", key: str):
|
|
207
|
+
def __init__(self, cfg: "_ProxiedDict", key: str) -> None:
|
|
164
208
|
self.key = key
|
|
165
209
|
self.cfg = cfg
|
|
166
210
|
|
|
@@ -193,11 +237,11 @@ else:
|
|
|
193
237
|
|
|
194
238
|
|
|
195
239
|
__all__ = (
|
|
196
|
-
"Proxy",
|
|
197
240
|
"DataConverter",
|
|
198
|
-
"ProxiedDict",
|
|
199
241
|
"MODEL_CONFIG",
|
|
200
242
|
"Model",
|
|
243
|
+
"ProxiedDict",
|
|
244
|
+
"Proxy",
|
|
201
245
|
"full_result",
|
|
202
246
|
"get_params",
|
|
203
247
|
)
|
telegrinder/modules.py
CHANGED
|
@@ -108,8 +108,8 @@ elif logging_module == "logging":
|
|
|
108
108
|
"level": "green",
|
|
109
109
|
"level_module": "blue",
|
|
110
110
|
"level_func": "cyan",
|
|
111
|
-
"level_lineno": "
|
|
112
|
-
"level_message": "
|
|
111
|
+
"level_lineno": "white",
|
|
112
|
+
"level_message": "green",
|
|
113
113
|
},
|
|
114
114
|
"DEBUG": {
|
|
115
115
|
"level": "blue",
|
|
@@ -232,7 +232,7 @@ def _set_logger_level(level):
|
|
|
232
232
|
if logging_module == "logging":
|
|
233
233
|
import logging
|
|
234
234
|
|
|
235
|
-
logging.getLogger("telegrinder").setLevel(
|
|
235
|
+
logging.getLogger("telegrinder").setLevel(level)
|
|
236
236
|
elif logging_module == "loguru":
|
|
237
237
|
import loguru # type: ignore
|
|
238
238
|
|