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.

Files changed (46) hide show
  1. telegrinder/__init__.py +36 -0
  2. telegrinder/bot/bot.py +9 -0
  3. telegrinder/bot/cute_types/message.py +1 -1
  4. telegrinder/bot/dispatch/abc.py +1 -1
  5. telegrinder/bot/dispatch/composition.py +15 -1
  6. telegrinder/bot/dispatch/context.py +8 -0
  7. telegrinder/bot/dispatch/dispatch.py +65 -28
  8. telegrinder/bot/dispatch/handler/abc.py +2 -4
  9. telegrinder/bot/dispatch/handler/func.py +31 -25
  10. telegrinder/bot/dispatch/handler/message_reply.py +21 -9
  11. telegrinder/bot/dispatch/middleware/abc.py +1 -1
  12. telegrinder/bot/dispatch/process.py +5 -3
  13. telegrinder/bot/dispatch/return_manager/abc.py +6 -0
  14. telegrinder/bot/dispatch/return_manager/callback_query.py +3 -1
  15. telegrinder/bot/dispatch/return_manager/inline_query.py +3 -1
  16. telegrinder/bot/dispatch/return_manager/message.py +9 -1
  17. telegrinder/bot/dispatch/view/abc.py +31 -5
  18. telegrinder/bot/dispatch/view/box.py +2 -1
  19. telegrinder/bot/dispatch/view/inline_query.py +1 -1
  20. telegrinder/bot/dispatch/view/message.py +1 -1
  21. telegrinder/bot/dispatch/waiter_machine/machine.py +14 -8
  22. telegrinder/bot/dispatch/waiter_machine/middleware.py +9 -7
  23. telegrinder/bot/dispatch/waiter_machine/short_state.py +17 -18
  24. telegrinder/bot/polling/polling.py +14 -0
  25. telegrinder/bot/rules/markup.py +1 -1
  26. telegrinder/bot/rules/text.py +1 -1
  27. telegrinder/bot/scenario/checkbox.py +16 -0
  28. telegrinder/model.py +10 -0
  29. telegrinder/msgspec_utils.py +13 -2
  30. telegrinder/node/base.py +1 -1
  31. telegrinder/node/composer.py +1 -1
  32. telegrinder/tools/__init__.py +2 -1
  33. telegrinder/tools/error_handler/abc.py +3 -3
  34. telegrinder/tools/error_handler/error_handler.py +21 -21
  35. telegrinder/tools/formatting/html.py +6 -12
  36. telegrinder/tools/formatting/links.py +12 -6
  37. telegrinder/tools/formatting/spec_html_formats.py +4 -5
  38. telegrinder/tools/global_context/global_context.py +2 -2
  39. telegrinder/tools/loop_wrapper/__init__.py +2 -2
  40. telegrinder/tools/loop_wrapper/abc.py +1 -4
  41. telegrinder/tools/loop_wrapper/loop_wrapper.py +79 -33
  42. telegrinder/verification_utils.py +31 -0
  43. {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/METADATA +1 -1
  44. {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/RECORD +46 -45
  45. {telegrinder-0.1.dev163.dist-info → telegrinder-0.1.dev165.dist-info}/WHEEL +1 -1
  46. {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
- ctx_api=api,
81
- event=event,
82
- rules=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.expiration is not None
47
- and datetime.datetime.now() >= short_state.expiration
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, list(short_state.rules), dataclass=None
53
+ self.pass_runtime,
54
+ list(short_state.rules),
55
+ dataclass=None,
54
56
  )
55
- handler.ctx.set("short_state", short_state)
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.ctx,
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.abc import ABCAPI
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
- def __init__(
19
- self,
20
- key: "Identificator",
21
- ctx_api: ABCAPI,
22
- event: asyncio.Event,
23
- rules: tuple[ABCRule[EventModel], ...],
24
- expiration: datetime.timedelta | None = None,
25
- default_behaviour: Behaviour | None = None,
26
- on_drop_behaviour: Behaviour | None = None,
27
- ) -> None:
28
- self.key = key
29
- self.ctx_api = ctx_api
30
- self.event = event
31
- self.rules = rules
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,
@@ -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 = [
@@ -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 {
@@ -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
- generic_args = typing.get_args(tp)
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
@@ -6,7 +6,7 @@ ComposeResult: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, typin
6
6
 
7
7
 
8
8
  class ComposeError(BaseException):
9
- pass
9
+ ...
10
10
 
11
11
 
12
12
  class Node(abc.ABC):
@@ -27,7 +27,7 @@ class NodeSession:
27
27
  self.generator = None
28
28
 
29
29
  def __repr__(self) -> str:
30
- return f"<NodeSession {self.value}" + ("ACTIVE>" if self.generator else ">")
30
+ return f"<{self.__class__.__name__}: {self.value}" + ("ACTIVE>" if self.generator else ">")
31
31
 
32
32
 
33
33
  class NodeCollection:
@@ -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 register_catcher(
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
- try:
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(channel_username: str, string: str | None = None):
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(bot_username: str, data: str, string: str | None = None) -> TagFormat:
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(bot_username: str, data: str, string: str | None = None) -> TagFormat:
245
- return link(get_start_group_link(bot_username, data), string)
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(bot_username: str, data: str) -> str:
10
- return get_resolve_domain_link(bot_username) + f"&start={data}"
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(bot_username: str, data: str) -> str:
14
- return get_resolve_domain_link(bot_username) + f"&startgroup={data}"
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(channel_username: str) -> str:
18
- return get_resolve_domain_link(channel_username) + "&boost"
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
- channel_username: str
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
- bot_username: str
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
- bot_username: str
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 "<{!r} -> ({})>".format(
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: CoroutineFunc | CoroutineTask) -> None:
7
+ def add_task(self, task: typing.Any) -> None:
11
8
  pass
12
9
 
13
10
  @abstractmethod