telegrinder 0.1.dev163__py3-none-any.whl → 0.1.dev165__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 +36 -0
- telegrinder/bot/bot.py +9 -0
- telegrinder/bot/cute_types/message.py +1 -1
- telegrinder/bot/dispatch/abc.py +1 -1
- telegrinder/bot/dispatch/composition.py +15 -1
- telegrinder/bot/dispatch/context.py +8 -0
- telegrinder/bot/dispatch/dispatch.py +65 -28
- telegrinder/bot/dispatch/handler/abc.py +2 -4
- telegrinder/bot/dispatch/handler/func.py +31 -25
- telegrinder/bot/dispatch/handler/message_reply.py +21 -9
- telegrinder/bot/dispatch/middleware/abc.py +1 -1
- telegrinder/bot/dispatch/process.py +5 -3
- telegrinder/bot/dispatch/return_manager/abc.py +6 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +3 -1
- telegrinder/bot/dispatch/return_manager/inline_query.py +3 -1
- telegrinder/bot/dispatch/return_manager/message.py +9 -1
- telegrinder/bot/dispatch/view/abc.py +31 -5
- telegrinder/bot/dispatch/view/box.py +2 -1
- telegrinder/bot/dispatch/view/inline_query.py +1 -1
- telegrinder/bot/dispatch/view/message.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/machine.py +14 -8
- telegrinder/bot/dispatch/waiter_machine/middleware.py +9 -7
- telegrinder/bot/dispatch/waiter_machine/short_state.py +17 -18
- telegrinder/bot/polling/polling.py +14 -0
- telegrinder/bot/rules/markup.py +1 -1
- telegrinder/bot/rules/text.py +1 -1
- telegrinder/bot/scenario/checkbox.py +16 -0
- telegrinder/model.py +10 -0
- telegrinder/msgspec_utils.py +13 -2
- telegrinder/node/base.py +1 -1
- telegrinder/node/composer.py +1 -1
- telegrinder/tools/__init__.py +2 -1
- telegrinder/tools/error_handler/abc.py +3 -3
- telegrinder/tools/error_handler/error_handler.py +21 -21
- telegrinder/tools/formatting/html.py +6 -12
- telegrinder/tools/formatting/links.py +12 -6
- telegrinder/tools/formatting/spec_html_formats.py +4 -5
- telegrinder/tools/global_context/global_context.py +2 -2
- telegrinder/tools/loop_wrapper/__init__.py +2 -2
- telegrinder/tools/loop_wrapper/abc.py +1 -4
- telegrinder/tools/loop_wrapper/loop_wrapper.py +79 -33
- telegrinder/verification_utils.py +31 -0
- {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/METADATA +1 -1
- {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/RECORD +46 -45
- {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/WHEEL +1 -1
- {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/LICENSE +0 -0
|
@@ -19,12 +19,18 @@ if typing.TYPE_CHECKING:
|
|
|
19
19
|
class WaiterMachine:
|
|
20
20
|
def __init__(self) -> None:
|
|
21
21
|
self.storage: Storage = {}
|
|
22
|
+
|
|
23
|
+
def __repr__(self) -> str:
|
|
24
|
+
return "<{}: storage={!r}>".format(
|
|
25
|
+
self.__class__.__name__,
|
|
26
|
+
self.storage,
|
|
27
|
+
)
|
|
22
28
|
|
|
23
29
|
async def drop(
|
|
24
30
|
self,
|
|
25
31
|
state_view: "ABCStateView[EventModel]",
|
|
26
32
|
id: Identificator,
|
|
27
|
-
**context,
|
|
33
|
+
**context: typing.Any,
|
|
28
34
|
) -> None:
|
|
29
35
|
view_name = state_view.__class__.__name__
|
|
30
36
|
if view_name not in self.storage:
|
|
@@ -60,9 +66,9 @@ class WaiterMachine:
|
|
|
60
66
|
*rules: ABCRule[EventModel],
|
|
61
67
|
default: Behaviour = None,
|
|
62
68
|
on_drop: Behaviour = None,
|
|
63
|
-
expiration: datetime.timedelta | int | None = None,
|
|
69
|
+
expiration: datetime.timedelta | int | float | None = None,
|
|
64
70
|
) -> tuple[EventModel, Context]:
|
|
65
|
-
if isinstance(expiration, int):
|
|
71
|
+
if isinstance(expiration, int | float):
|
|
66
72
|
expiration = datetime.timedelta(seconds=expiration)
|
|
67
73
|
|
|
68
74
|
api: ABCAPI
|
|
@@ -77,9 +83,9 @@ class WaiterMachine:
|
|
|
77
83
|
|
|
78
84
|
short_state = ShortState(
|
|
79
85
|
key,
|
|
80
|
-
|
|
81
|
-
event
|
|
82
|
-
rules
|
|
86
|
+
api,
|
|
87
|
+
event,
|
|
88
|
+
rules,
|
|
83
89
|
expiration=expiration,
|
|
84
90
|
default_behaviour=default,
|
|
85
91
|
on_drop_behaviour=on_drop,
|
|
@@ -104,13 +110,13 @@ class WaiterMachine:
|
|
|
104
110
|
view: "ABCStateView[EventModel]",
|
|
105
111
|
behaviour: Behaviour,
|
|
106
112
|
event: asyncio.Event | EventModel,
|
|
107
|
-
**context,
|
|
113
|
+
**context: typing.Any,
|
|
108
114
|
) -> None:
|
|
109
115
|
if behaviour is None:
|
|
110
116
|
return
|
|
111
117
|
# TODO: add behaviour check
|
|
112
118
|
# TODO: support view as a behaviour
|
|
113
|
-
await behaviour.run(event)
|
|
119
|
+
await behaviour.run(event, context) # type: ignore
|
|
114
120
|
|
|
115
121
|
|
|
116
122
|
__all__ = ("WaiterMachine",)
|
|
@@ -38,32 +38,34 @@ class WaiterMiddleware(ABCMiddleware[EventType]):
|
|
|
38
38
|
if key is None:
|
|
39
39
|
raise RuntimeError("Unable to get state key.")
|
|
40
40
|
|
|
41
|
-
short_state: typing.Optional["ShortState"] = self.machine.storage[view_name].get(key)
|
|
41
|
+
short_state: typing.Optional["ShortState[EventType]"] = self.machine.storage[view_name].get(key)
|
|
42
42
|
if not short_state:
|
|
43
43
|
return True
|
|
44
44
|
|
|
45
45
|
if (
|
|
46
|
-
short_state.
|
|
47
|
-
and datetime.datetime.now() >= short_state.
|
|
46
|
+
short_state.expiration_date is not None
|
|
47
|
+
and datetime.datetime.now() >= short_state.expiration_date
|
|
48
48
|
):
|
|
49
49
|
await self.machine.drop(self.view, short_state.key)
|
|
50
50
|
return True
|
|
51
51
|
|
|
52
52
|
handler = FuncHandler(
|
|
53
|
-
self.pass_runtime,
|
|
53
|
+
self.pass_runtime,
|
|
54
|
+
list(short_state.rules),
|
|
55
|
+
dataclass=None,
|
|
54
56
|
)
|
|
55
|
-
handler.
|
|
57
|
+
handler.preset_context.set("short_state", short_state)
|
|
56
58
|
result = await handler.check(event.ctx_api, ctx.raw_update, ctx)
|
|
57
59
|
|
|
58
60
|
if result is True:
|
|
59
|
-
await handler.run(event)
|
|
61
|
+
await handler.run(event, ctx)
|
|
60
62
|
|
|
61
63
|
elif short_state.default_behaviour is not None:
|
|
62
64
|
await self.machine.call_behaviour(
|
|
63
65
|
self.view,
|
|
64
66
|
short_state.default_behaviour,
|
|
65
67
|
event,
|
|
66
|
-
**handler.
|
|
68
|
+
**handler.preset_context,
|
|
67
69
|
)
|
|
68
70
|
|
|
69
71
|
return False
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import dataclasses
|
|
2
3
|
import datetime
|
|
3
4
|
import typing
|
|
4
5
|
|
|
5
|
-
from telegrinder.api
|
|
6
|
+
from telegrinder.api import ABCAPI
|
|
6
7
|
from telegrinder.bot.cute_types import BaseCute
|
|
7
8
|
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
8
9
|
from telegrinder.bot.rules.abc import ABCRule
|
|
@@ -14,24 +15,22 @@ EventModel = typing.TypeVar("EventModel", bound=BaseCute)
|
|
|
14
15
|
Behaviour: typing.TypeAlias = ABCHandler | None
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
@dataclasses.dataclass
|
|
17
19
|
class ShortState(typing.Generic[EventModel]):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
self.default_behaviour = default_behaviour
|
|
33
|
-
self.expiration = (datetime.datetime.now() + expiration) if expiration else None
|
|
34
|
-
self.on_drop_behaviour = on_drop_behaviour
|
|
20
|
+
key: "Identificator"
|
|
21
|
+
ctx_api: ABCAPI
|
|
22
|
+
event: asyncio.Event
|
|
23
|
+
rules: tuple[ABCRule[EventModel], ...]
|
|
24
|
+
_: dataclasses.KW_ONLY
|
|
25
|
+
expiration: dataclasses.InitVar[datetime.timedelta | None] = dataclasses.field(default=None)
|
|
26
|
+
default_behaviour: Behaviour | None = dataclasses.field(default=None)
|
|
27
|
+
on_drop_behaviour: Behaviour | None = dataclasses.field(default=None)
|
|
28
|
+
expiration_date: datetime.datetime | None = dataclasses.field(init=False)
|
|
29
|
+
|
|
30
|
+
def __post_init__(self, expiration: datetime.timedelta | None = None) -> None:
|
|
31
|
+
self.expiration_date = (
|
|
32
|
+
datetime.datetime.now() - expiration
|
|
33
|
+
) if expiration is not None else None
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
__all__ = ("ShortState",)
|
|
@@ -33,6 +33,20 @@ class Polling(ABCPolling):
|
|
|
33
33
|
self.max_reconnetions = 10 if max_reconnetions < 0 else max_reconnetions
|
|
34
34
|
self.offset = offset
|
|
35
35
|
self._stop = False
|
|
36
|
+
|
|
37
|
+
def __repr__(self) -> str:
|
|
38
|
+
return (
|
|
39
|
+
"<{}: with api={!r}, stopped={}, offset={}, allowed_updates={!r}, "
|
|
40
|
+
"max_reconnetions={}, reconnection_timeout={}>"
|
|
41
|
+
).format(
|
|
42
|
+
self.__class__.__name__,
|
|
43
|
+
self.api,
|
|
44
|
+
self._stop,
|
|
45
|
+
self.offset,
|
|
46
|
+
self.allowed_updates,
|
|
47
|
+
self.max_reconnetions,
|
|
48
|
+
self.reconnection_timeout,
|
|
49
|
+
)
|
|
36
50
|
|
|
37
51
|
def get_allowed_updates(
|
|
38
52
|
self,
|
telegrinder/bot/rules/markup.py
CHANGED
|
@@ -24,7 +24,7 @@ def check_string(patterns: list[vbml.Pattern], s: str, ctx: Context) -> bool:
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class Markup(TextMessageRule):
|
|
27
|
-
def __init__(self, patterns: PatternLike | list[PatternLike]):
|
|
27
|
+
def __init__(self, patterns: PatternLike | list[PatternLike], /):
|
|
28
28
|
if not isinstance(patterns, list):
|
|
29
29
|
patterns = [patterns]
|
|
30
30
|
self.patterns = [
|
telegrinder/bot/rules/text.py
CHANGED
|
@@ -14,7 +14,7 @@ class TextMessageRule(MessageRule, ABC, requires=[HasText()]):
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Text(TextMessageRule):
|
|
17
|
-
def __init__(self, texts: str | list[str], ignore_case: bool = False):
|
|
17
|
+
def __init__(self, texts: str | list[str], *, ignore_case: bool = False) -> None:
|
|
18
18
|
if not isinstance(texts, list):
|
|
19
19
|
texts = [texts]
|
|
20
20
|
self.texts = texts if not ignore_case else list(map(str.lower, texts))
|
|
@@ -45,6 +45,21 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
|
|
|
45
45
|
self.max_in_row = max_in_row
|
|
46
46
|
self.random_code = secrets.token_hex(8)
|
|
47
47
|
self.waiter_machine = waiter_machine
|
|
48
|
+
|
|
49
|
+
def __repr__(self) -> str:
|
|
50
|
+
return (
|
|
51
|
+
"<{}@{!r}: (choices={!r}, max_in_row={}) with waiter_machine={!r}, ready_text={!r} "
|
|
52
|
+
"for chat_id={} with message={!r}>"
|
|
53
|
+
).format(
|
|
54
|
+
self.__class__.__name__,
|
|
55
|
+
self.random_code,
|
|
56
|
+
self.choices,
|
|
57
|
+
self.max_in_row,
|
|
58
|
+
self.waiter_machine,
|
|
59
|
+
self.ready,
|
|
60
|
+
self.chat_id,
|
|
61
|
+
self.msg,
|
|
62
|
+
)
|
|
48
63
|
|
|
49
64
|
def get_markup(self) -> InlineKeyboardMarkup:
|
|
50
65
|
kb = InlineKeyboard()
|
|
@@ -70,6 +85,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
|
|
|
70
85
|
name: str,
|
|
71
86
|
default_text: str,
|
|
72
87
|
picked_text: str,
|
|
88
|
+
*,
|
|
73
89
|
is_picked: bool = False,
|
|
74
90
|
) -> typing.Self:
|
|
75
91
|
self.choices.append(
|
telegrinder/model.py
CHANGED
|
@@ -57,6 +57,10 @@ def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
class Model(msgspec.Struct, **MODEL_CONFIG):
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_bytes(cls, data: bytes) -> typing.Self:
|
|
62
|
+
return decoder.decode(data, type=cls)
|
|
63
|
+
|
|
60
64
|
def to_dict(
|
|
61
65
|
self,
|
|
62
66
|
*,
|
|
@@ -76,6 +80,12 @@ class Model(msgspec.Struct, **MODEL_CONFIG):
|
|
|
76
80
|
class DataConverter:
|
|
77
81
|
files: dict[str, tuple[str, bytes]] = dataclasses.field(default_factory=lambda: {})
|
|
78
82
|
|
|
83
|
+
def __repr__(self) -> str:
|
|
84
|
+
return "<{}: {}>".format(
|
|
85
|
+
self.__class__.__name__,
|
|
86
|
+
", ".join(f"{k}={v!r}" for k, v in self.converters),
|
|
87
|
+
)
|
|
88
|
+
|
|
79
89
|
@property
|
|
80
90
|
def converters(self) -> dict[type[typing.Any], typing.Callable[..., typing.Any]]:
|
|
81
91
|
return {
|
telegrinder/msgspec_utils.py
CHANGED
|
@@ -51,8 +51,7 @@ def msgspec_convert(obj: typing.Any, t: type[T]) -> Result[T, msgspec.Validation
|
|
|
51
51
|
def option_dec_hook(tp: type[Option[typing.Any]], obj: typing.Any) -> Option[typing.Any]:
|
|
52
52
|
if obj is None:
|
|
53
53
|
return Nothing
|
|
54
|
-
|
|
55
|
-
value_type: typing.Any | type[typing.Any] = typing.Any if not generic_args else generic_args[0]
|
|
54
|
+
value_type, = typing.get_args(tp) or (typing.Any,)
|
|
56
55
|
return msgspec_convert({"value": obj}, fntypes.option.Some[value_type]).unwrap()
|
|
57
56
|
|
|
58
57
|
|
|
@@ -123,6 +122,12 @@ class Decoder:
|
|
|
123
122
|
Variative: variative_dec_hook,
|
|
124
123
|
datetime: lambda t, obj: t.fromtimestamp(obj),
|
|
125
124
|
}
|
|
125
|
+
|
|
126
|
+
def __repr__(self) -> str:
|
|
127
|
+
return "<{}: dec_hooks={!r}>".format(
|
|
128
|
+
self.__class__.__name__,
|
|
129
|
+
self.dec_hooks,
|
|
130
|
+
)
|
|
126
131
|
|
|
127
132
|
def add_dec_hook(self, t: T): # type: ignore
|
|
128
133
|
def decorator(func: DecHook[T]) -> DecHook[T]:
|
|
@@ -216,6 +221,12 @@ class Encoder:
|
|
|
216
221
|
datetime: lambda date: int(date.timestamp()),
|
|
217
222
|
}
|
|
218
223
|
|
|
224
|
+
def __repr__(self) -> str:
|
|
225
|
+
return "<{}: enc_hooks={!r}>".format(
|
|
226
|
+
self.__class__.__name__,
|
|
227
|
+
self.enc_hooks,
|
|
228
|
+
)
|
|
229
|
+
|
|
219
230
|
def add_dec_hook(self, t: type[T]):
|
|
220
231
|
def decorator(func: EncHook[T]) -> EncHook[T]:
|
|
221
232
|
encode_hook = self.enc_hooks.setdefault(get_origin(t), func)
|
telegrinder/node/base.py
CHANGED
telegrinder/node/composer.py
CHANGED
|
@@ -27,7 +27,7 @@ class NodeSession:
|
|
|
27
27
|
self.generator = None
|
|
28
28
|
|
|
29
29
|
def __repr__(self) -> str:
|
|
30
|
-
return f"<
|
|
30
|
+
return f"<{self.__class__.__name__}: {self.value}" + ("ACTIVE>" if self.generator else ">")
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class NodeCollection:
|
telegrinder/tools/__init__.py
CHANGED
|
@@ -66,7 +66,7 @@ from .keyboard import (
|
|
|
66
66
|
Keyboard,
|
|
67
67
|
RowButtons,
|
|
68
68
|
)
|
|
69
|
-
from .loop_wrapper import ABCLoopWrapper, DelayedTask, LoopWrapper
|
|
69
|
+
from .loop_wrapper import ABCLoopWrapper, DelayedTask, Lifespan, LoopWrapper
|
|
70
70
|
from .magic import magic_bundle, resolve_arg_names
|
|
71
71
|
from .parse_mode import ParseMode
|
|
72
72
|
|
|
@@ -99,6 +99,7 @@ __all__ = (
|
|
|
99
99
|
"KeyboardSetBase",
|
|
100
100
|
"KeyboardSetYAML",
|
|
101
101
|
"Link",
|
|
102
|
+
"Lifespan",
|
|
102
103
|
"LoopWrapper",
|
|
103
104
|
"Mention",
|
|
104
105
|
"ParseMode",
|
|
@@ -13,12 +13,12 @@ Handler = typing.Callable[typing.Concatenate[EventT, ...], typing.Awaitable[typi
|
|
|
13
13
|
|
|
14
14
|
class ABCErrorHandler(ABC, typing.Generic[EventT]):
|
|
15
15
|
@abstractmethod
|
|
16
|
-
def
|
|
16
|
+
def __call__(
|
|
17
17
|
self,
|
|
18
18
|
*args: typing.Any,
|
|
19
19
|
**kwargs: typing.Any,
|
|
20
20
|
) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Callable[..., typing.Any]]:
|
|
21
|
-
|
|
21
|
+
"""Decorator for registering callback as an error handler."""
|
|
22
22
|
|
|
23
23
|
@abstractmethod
|
|
24
24
|
async def run(
|
|
@@ -28,7 +28,7 @@ class ABCErrorHandler(ABC, typing.Generic[EventT]):
|
|
|
28
28
|
api: ABCAPI,
|
|
29
29
|
ctx: Context,
|
|
30
30
|
) -> Result[typing.Any, typing.Any]:
|
|
31
|
-
|
|
31
|
+
"""Run error handler."""
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
__all__ = ("ABCErrorHandler",)
|
|
@@ -97,27 +97,8 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
97
97
|
if self.catcher is not None
|
|
98
98
|
else "<ErrorHandler: No catcher>"
|
|
99
99
|
)
|
|
100
|
-
|
|
101
|
-
async def __call__(
|
|
102
|
-
self,
|
|
103
|
-
handler: Handler[EventT],
|
|
104
|
-
event: EventT,
|
|
105
|
-
api: ABCAPI,
|
|
106
|
-
ctx: Context,
|
|
107
|
-
) -> Result[typing.Any, BaseException]:
|
|
108
|
-
assert self.catcher is not None
|
|
109
100
|
|
|
110
|
-
|
|
111
|
-
return await self.catcher(handler, event, api, ctx)
|
|
112
|
-
except BaseException as exc:
|
|
113
|
-
return Error(CatcherError(
|
|
114
|
-
exc,
|
|
115
|
-
"Exception {!r} was occurred during the running catcher {!r}.".format(
|
|
116
|
-
repr(exc), self.catcher.func.__name__
|
|
117
|
-
)
|
|
118
|
-
))
|
|
119
|
-
|
|
120
|
-
def register_catcher(
|
|
101
|
+
def __call__(
|
|
121
102
|
self,
|
|
122
103
|
*exceptions: type[BaseException] | BaseException,
|
|
123
104
|
logging: bool = False,
|
|
@@ -142,6 +123,25 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
142
123
|
return func
|
|
143
124
|
return decorator
|
|
144
125
|
|
|
126
|
+
async def process(
|
|
127
|
+
self,
|
|
128
|
+
handler: Handler[EventT],
|
|
129
|
+
event: EventT,
|
|
130
|
+
api: ABCAPI,
|
|
131
|
+
ctx: Context,
|
|
132
|
+
) -> Result[typing.Any, BaseException]:
|
|
133
|
+
assert self.catcher is not None
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
return await self.catcher(handler, event, api, ctx)
|
|
137
|
+
except BaseException as exc:
|
|
138
|
+
return Error(CatcherError(
|
|
139
|
+
exc,
|
|
140
|
+
"Exception {!r} was occurred during the running catcher {!r}.".format(
|
|
141
|
+
repr(exc), self.catcher.func.__name__
|
|
142
|
+
)
|
|
143
|
+
))
|
|
144
|
+
|
|
145
145
|
def process_catcher_error(self, error: CatcherError) -> Result[None, str]:
|
|
146
146
|
assert self.catcher is not None
|
|
147
147
|
|
|
@@ -164,7 +164,7 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
164
164
|
if not self.catcher:
|
|
165
165
|
return Ok(await handler(event, **magic_bundle(handler, ctx))) # type: ignore
|
|
166
166
|
|
|
167
|
-
match await self(handler, event, api, ctx):
|
|
167
|
+
match await self.process(handler, event, api, ctx):
|
|
168
168
|
case Ok(_) as ok:
|
|
169
169
|
return ok
|
|
170
170
|
case Error(exc) as err:
|
|
@@ -200,11 +200,8 @@ def bold(string: str) -> TagFormat:
|
|
|
200
200
|
return TagFormat(string, tag="b")
|
|
201
201
|
|
|
202
202
|
|
|
203
|
-
def channel_boost_link(
|
|
204
|
-
return link(
|
|
205
|
-
get_channel_boost_link(channel_username),
|
|
206
|
-
string or f"t.me/{channel_username}?boost",
|
|
207
|
-
)
|
|
203
|
+
def channel_boost_link(channel_id: str | int, string: str | None = None):
|
|
204
|
+
return link(get_channel_boost_link(channel_id), string)
|
|
208
205
|
|
|
209
206
|
|
|
210
207
|
def code_inline(string: str) -> TagFormat:
|
|
@@ -234,15 +231,12 @@ def spoiler(string: str) -> TagFormat:
|
|
|
234
231
|
return TagFormat(string, tag="tg-spoiler")
|
|
235
232
|
|
|
236
233
|
|
|
237
|
-
def start_bot_link(
|
|
238
|
-
return link(
|
|
239
|
-
get_start_bot_link(bot_username, data),
|
|
240
|
-
string or f"t.me/{bot_username}?start={data}"
|
|
241
|
-
)
|
|
234
|
+
def start_bot_link(bot_id: str | int, data: str, string: str | None = None) -> TagFormat:
|
|
235
|
+
return link(get_start_bot_link(bot_id, data), string)
|
|
242
236
|
|
|
243
237
|
|
|
244
|
-
def start_group_link(
|
|
245
|
-
return link(get_start_group_link(
|
|
238
|
+
def start_group_link(bot_id: str | int, data: str, string: str | None = None) -> TagFormat:
|
|
239
|
+
return link(get_start_group_link(bot_id, data), string)
|
|
246
240
|
|
|
247
241
|
|
|
248
242
|
def strike(string: str) -> TagFormat:
|
|
@@ -6,16 +6,22 @@ def get_resolve_domain_link(username: str) -> str:
|
|
|
6
6
|
return f"tg://resolve?domain={username}"
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def get_start_bot_link(
|
|
10
|
-
|
|
9
|
+
def get_start_bot_link(bot_id: str | int, data: str) -> str:
|
|
10
|
+
if isinstance(bot_id, int):
|
|
11
|
+
return get_mention_link(bot_id) + f"&start={data}"
|
|
12
|
+
return get_resolve_domain_link(bot_id) + f"&start={data}"
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
def get_start_group_link(
|
|
14
|
-
|
|
15
|
+
def get_start_group_link(bot_id: str | int, data: str) -> str:
|
|
16
|
+
if isinstance(bot_id, int):
|
|
17
|
+
return get_mention_link(bot_id) + f"&startgroup={data}"
|
|
18
|
+
return get_resolve_domain_link(bot_id) + f"&startgroup={data}"
|
|
15
19
|
|
|
16
20
|
|
|
17
|
-
def get_channel_boost_link(
|
|
18
|
-
|
|
21
|
+
def get_channel_boost_link(channel_id: str | int) -> str:
|
|
22
|
+
if isinstance(channel_id, int):
|
|
23
|
+
return get_mention_link(channel_id) + "&boost"
|
|
24
|
+
return get_resolve_domain_link(channel_id) + "&boost"
|
|
19
25
|
|
|
20
26
|
|
|
21
27
|
def get_invite_chat_link(invite_link: str) -> str:
|
|
@@ -3,14 +3,13 @@ import typing
|
|
|
3
3
|
|
|
4
4
|
from telegrinder.types.enums import ProgrammingLanguage
|
|
5
5
|
|
|
6
|
-
SpecialFormat = typing.Union[
|
|
6
|
+
SpecialFormat: typing.TypeAlias = typing.Union[
|
|
7
7
|
"ChannelBoostLink",
|
|
8
8
|
"InviteChatLink",
|
|
9
9
|
"Link",
|
|
10
10
|
"Mention",
|
|
11
11
|
"PreCode",
|
|
12
12
|
"ResolveDomain",
|
|
13
|
-
"SpecialFormat",
|
|
14
13
|
"StartBotLink",
|
|
15
14
|
"StartGroupLink",
|
|
16
15
|
"TgEmoji",
|
|
@@ -38,7 +37,7 @@ class BaseSpecFormat:
|
|
|
38
37
|
class ChannelBoostLink(BaseSpecFormat):
|
|
39
38
|
__formatter_name__ = "channel_boost_link"
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
channel_id: str | int
|
|
42
41
|
string: str | None = None
|
|
43
42
|
|
|
44
43
|
|
|
@@ -86,7 +85,7 @@ class TgEmoji(BaseSpecFormat):
|
|
|
86
85
|
class StartBotLink(BaseSpecFormat):
|
|
87
86
|
__formatter_name__ = "start_bot_link"
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
bot_id: str | int
|
|
90
89
|
data: str
|
|
91
90
|
string: str | None
|
|
92
91
|
|
|
@@ -95,7 +94,7 @@ class StartBotLink(BaseSpecFormat):
|
|
|
95
94
|
class StartGroupLink(BaseSpecFormat):
|
|
96
95
|
__formatter_name__ = "start_group_link"
|
|
97
96
|
|
|
98
|
-
|
|
97
|
+
bot_id: str | int
|
|
99
98
|
data: str
|
|
100
99
|
string: str | None = None
|
|
101
100
|
|
|
@@ -199,8 +199,8 @@ class GlobalContext(ABCGlobalContext, typing.Generic[CtxValueT], dict[str, Globa
|
|
|
199
199
|
self.set_context_variables(variables)
|
|
200
200
|
|
|
201
201
|
def __repr__(self) -> str:
|
|
202
|
-
return "<{
|
|
203
|
-
f"{self.__class__.__name__}@{self.ctx_name}",
|
|
202
|
+
return "<{} -> ({})>".format(
|
|
203
|
+
f"{self.__class__.__name__}@{self.ctx_name!r}",
|
|
204
204
|
", ".join(repr(var) for var in self),
|
|
205
205
|
)
|
|
206
206
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
from .abc import ABCLoopWrapper
|
|
2
|
-
from .loop_wrapper import DelayedTask, LoopWrapper
|
|
2
|
+
from .loop_wrapper import DelayedTask, Lifespan, LoopWrapper
|
|
3
3
|
|
|
4
|
-
__all__ = ("ABCLoopWrapper", "DelayedTask", "LoopWrapper")
|
|
4
|
+
__all__ = ("ABCLoopWrapper", "DelayedTask", "Lifespan", "LoopWrapper")
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
|
|
4
|
-
CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, typing.Any]
|
|
5
|
-
CoroutineFunc: typing.TypeAlias = typing.Callable[..., CoroutineTask]
|
|
6
|
-
|
|
7
4
|
|
|
8
5
|
class ABCLoopWrapper(ABC):
|
|
9
6
|
@abstractmethod
|
|
10
|
-
def add_task(self, task:
|
|
7
|
+
def add_task(self, task: typing.Any) -> None:
|
|
11
8
|
pass
|
|
12
9
|
|
|
13
10
|
@abstractmethod
|