telegrinder 0.3.0.post2__py3-none-any.whl → 0.3.2__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 (164) hide show
  1. telegrinder/__init__.py +144 -144
  2. telegrinder/api/__init__.py +8 -8
  3. telegrinder/api/api.py +93 -93
  4. telegrinder/api/error.py +16 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +66 -66
  8. telegrinder/bot/bot.py +76 -76
  9. telegrinder/bot/cute_types/__init__.py +11 -11
  10. telegrinder/bot/cute_types/base.py +258 -234
  11. telegrinder/bot/cute_types/callback_query.py +382 -382
  12. telegrinder/bot/cute_types/chat_join_request.py +61 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +160 -160
  14. telegrinder/bot/cute_types/inline_query.py +53 -53
  15. telegrinder/bot/cute_types/message.py +2631 -2631
  16. telegrinder/bot/cute_types/update.py +75 -75
  17. telegrinder/bot/cute_types/utils.py +92 -92
  18. telegrinder/bot/dispatch/__init__.py +55 -55
  19. telegrinder/bot/dispatch/abc.py +77 -77
  20. telegrinder/bot/dispatch/context.py +92 -92
  21. telegrinder/bot/dispatch/dispatch.py +202 -201
  22. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  23. telegrinder/bot/dispatch/handler/abc.py +24 -24
  24. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  25. telegrinder/bot/dispatch/handler/base.py +57 -57
  26. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  27. telegrinder/bot/dispatch/handler/func.py +128 -123
  28. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -43
  29. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  30. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  31. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  32. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  33. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  34. telegrinder/bot/dispatch/middleware/abc.py +16 -16
  35. telegrinder/bot/dispatch/process.py +132 -132
  36. telegrinder/bot/dispatch/return_manager/__init__.py +13 -13
  37. telegrinder/bot/dispatch/return_manager/abc.py +108 -108
  38. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  39. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  40. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  41. telegrinder/bot/dispatch/view/__init__.py +13 -13
  42. telegrinder/bot/dispatch/view/abc.py +41 -41
  43. telegrinder/bot/dispatch/view/base.py +200 -211
  44. telegrinder/bot/dispatch/view/box.py +129 -129
  45. telegrinder/bot/dispatch/view/callback_query.py +17 -17
  46. telegrinder/bot/dispatch/view/chat_join_request.py +16 -16
  47. telegrinder/bot/dispatch/view/chat_member.py +39 -39
  48. telegrinder/bot/dispatch/view/inline_query.py +17 -17
  49. telegrinder/bot/dispatch/view/message.py +44 -44
  50. telegrinder/bot/dispatch/view/raw.py +114 -118
  51. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  52. telegrinder/bot/dispatch/waiter_machine/actions.py +13 -13
  53. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  54. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +57 -57
  55. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
  56. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +53 -53
  57. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
  58. telegrinder/bot/dispatch/waiter_machine/machine.py +168 -170
  59. telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
  60. telegrinder/bot/dispatch/waiter_machine/short_state.py +65 -65
  61. telegrinder/bot/polling/__init__.py +4 -4
  62. telegrinder/bot/polling/abc.py +25 -25
  63. telegrinder/bot/polling/polling.py +131 -131
  64. telegrinder/bot/rules/__init__.py +62 -62
  65. telegrinder/bot/rules/abc.py +238 -233
  66. telegrinder/bot/rules/adapter/__init__.py +9 -9
  67. telegrinder/bot/rules/adapter/abc.py +29 -29
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +76 -76
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  72. telegrinder/bot/rules/callback_data.py +171 -171
  73. telegrinder/bot/rules/chat_join.py +48 -48
  74. telegrinder/bot/rules/command.py +126 -126
  75. telegrinder/bot/rules/enum_text.py +36 -33
  76. telegrinder/bot/rules/func.py +26 -26
  77. telegrinder/bot/rules/fuzzy.py +24 -24
  78. telegrinder/bot/rules/inline.py +60 -60
  79. telegrinder/bot/rules/integer.py +20 -20
  80. telegrinder/bot/rules/is_from.py +146 -146
  81. telegrinder/bot/rules/markup.py +43 -43
  82. telegrinder/bot/rules/mention.py +14 -14
  83. telegrinder/bot/rules/message.py +17 -17
  84. telegrinder/bot/rules/message_entities.py +35 -35
  85. telegrinder/bot/rules/node.py +27 -27
  86. telegrinder/bot/rules/regex.py +37 -37
  87. telegrinder/bot/rules/rule_enum.py +72 -72
  88. telegrinder/bot/rules/start.py +42 -42
  89. telegrinder/bot/rules/state.py +37 -37
  90. telegrinder/bot/rules/text.py +33 -33
  91. telegrinder/bot/rules/update.py +15 -15
  92. telegrinder/bot/scenario/__init__.py +5 -5
  93. telegrinder/bot/scenario/abc.py +19 -19
  94. telegrinder/bot/scenario/checkbox.py +167 -139
  95. telegrinder/bot/scenario/choice.py +46 -44
  96. telegrinder/client/__init__.py +4 -4
  97. telegrinder/client/abc.py +75 -75
  98. telegrinder/client/aiohttp.py +130 -130
  99. telegrinder/model.py +244 -244
  100. telegrinder/modules.py +237 -237
  101. telegrinder/msgspec_json.py +14 -14
  102. telegrinder/msgspec_utils.py +410 -410
  103. telegrinder/node/__init__.py +20 -20
  104. telegrinder/node/attachment.py +92 -92
  105. telegrinder/node/base.py +143 -144
  106. telegrinder/node/callback_query.py +14 -14
  107. telegrinder/node/command.py +33 -33
  108. telegrinder/node/composer.py +196 -184
  109. telegrinder/node/container.py +27 -27
  110. telegrinder/node/event.py +71 -73
  111. telegrinder/node/me.py +16 -16
  112. telegrinder/node/message.py +14 -14
  113. telegrinder/node/polymorphic.py +48 -52
  114. telegrinder/node/rule.py +76 -76
  115. telegrinder/node/scope.py +38 -38
  116. telegrinder/node/source.py +71 -71
  117. telegrinder/node/text.py +21 -21
  118. telegrinder/node/tools/__init__.py +3 -3
  119. telegrinder/node/tools/generator.py +40 -40
  120. telegrinder/node/update.py +15 -15
  121. telegrinder/rules.py +0 -0
  122. telegrinder/tools/__init__.py +74 -74
  123. telegrinder/tools/buttons.py +79 -79
  124. telegrinder/tools/error_handler/__init__.py +7 -7
  125. telegrinder/tools/error_handler/abc.py +33 -33
  126. telegrinder/tools/error_handler/error.py +9 -9
  127. telegrinder/tools/error_handler/error_handler.py +193 -193
  128. telegrinder/tools/formatting/__init__.py +46 -46
  129. telegrinder/tools/formatting/html.py +308 -308
  130. telegrinder/tools/formatting/links.py +33 -33
  131. telegrinder/tools/formatting/spec_html_formats.py +111 -111
  132. telegrinder/tools/functional.py +12 -12
  133. telegrinder/tools/global_context/__init__.py +7 -7
  134. telegrinder/tools/global_context/abc.py +63 -63
  135. telegrinder/tools/global_context/global_context.py +412 -412
  136. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  137. telegrinder/tools/i18n/__init__.py +12 -12
  138. telegrinder/tools/i18n/abc.py +32 -32
  139. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  140. telegrinder/tools/i18n/middleware/abc.py +25 -25
  141. telegrinder/tools/i18n/simple.py +43 -43
  142. telegrinder/tools/kb_set/__init__.py +4 -4
  143. telegrinder/tools/kb_set/base.py +15 -15
  144. telegrinder/tools/kb_set/yaml.py +63 -63
  145. telegrinder/tools/keyboard.py +128 -128
  146. telegrinder/tools/limited_dict.py +37 -37
  147. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  148. telegrinder/tools/loop_wrapper/abc.py +15 -15
  149. telegrinder/tools/loop_wrapper/loop_wrapper.py +216 -216
  150. telegrinder/tools/magic.py +168 -164
  151. telegrinder/tools/parse_mode.py +6 -6
  152. telegrinder/tools/state_storage/__init__.py +4 -4
  153. telegrinder/tools/state_storage/abc.py +35 -35
  154. telegrinder/tools/state_storage/memory.py +25 -25
  155. telegrinder/types/__init__.py +6 -6
  156. telegrinder/types/enums.py +672 -672
  157. telegrinder/types/methods.py +4633 -4633
  158. telegrinder/types/objects.py +6317 -6317
  159. telegrinder/verification_utils.py +32 -32
  160. {telegrinder-0.3.0.post2.dist-info → telegrinder-0.3.2.dist-info}/LICENSE +22 -22
  161. {telegrinder-0.3.0.post2.dist-info → telegrinder-0.3.2.dist-info}/METADATA +1 -1
  162. telegrinder-0.3.2.dist-info/RECORD +164 -0
  163. telegrinder-0.3.0.post2.dist-info/RECORD +0 -164
  164. {telegrinder-0.3.0.post2.dist-info → telegrinder-0.3.2.dist-info}/WHEEL +0 -0
@@ -1,216 +1,216 @@
1
- import asyncio
2
- import contextlib
3
- import dataclasses
4
- import datetime
5
- import typing
6
-
7
- from telegrinder.modules import logger
8
-
9
- from .abc import ABCLoopWrapper
10
-
11
- T = typing.TypeVar("T")
12
- P = typing.ParamSpec("P")
13
- CoroFunc = typing.TypeVar("CoroFunc", bound="CoroutineFunc")
14
-
15
- CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, T]
16
- CoroutineFunc: typing.TypeAlias = typing.Callable[P, CoroutineTask[T]]
17
- Task: typing.TypeAlias = typing.Union[CoroutineFunc, CoroutineTask, "DelayedTask"]
18
-
19
-
20
- def run_tasks(
21
- tasks: list[CoroutineTask[typing.Any]],
22
- loop: asyncio.AbstractEventLoop,
23
- ) -> None:
24
- while tasks:
25
- loop.run_until_complete(tasks.pop(0))
26
-
27
-
28
- def to_coroutine_task(task: Task) -> CoroutineTask[typing.Any]:
29
- if asyncio.iscoroutinefunction(task) or isinstance(task, DelayedTask):
30
- task = task()
31
- elif not asyncio.iscoroutine(task):
32
- raise TypeError("Task should be coroutine or coroutine function.")
33
- return task
34
-
35
-
36
- @dataclasses.dataclass(slots=True)
37
- class DelayedTask(typing.Generic[CoroFunc]):
38
- handler: CoroFunc
39
- seconds: float
40
- repeat: bool = dataclasses.field(default=False, kw_only=True)
41
- _cancelled: bool = dataclasses.field(default=False, init=False, repr=False)
42
-
43
- @property
44
- def is_cancelled(self) -> bool:
45
- return self._cancelled
46
-
47
- async def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
48
- while not self.is_cancelled:
49
- await asyncio.sleep(self.seconds)
50
- if self.is_cancelled:
51
- break
52
- try:
53
- await self.handler(*args, **kwargs)
54
- finally:
55
- if not self.repeat:
56
- break
57
-
58
- def cancel(self) -> None:
59
- if not self._cancelled:
60
- self._cancelled = True
61
-
62
-
63
- @dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
64
- class Lifespan:
65
- startup_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
66
- shutdown_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
67
-
68
- def on_startup(self, task: Task, /) -> Task:
69
- self.startup_tasks.append(to_coroutine_task(task))
70
- return task
71
-
72
- def on_shutdown(self, task: Task, /) -> Task:
73
- self.shutdown_tasks.append(to_coroutine_task(task))
74
- return task
75
-
76
- def start(self, loop: asyncio.AbstractEventLoop, /) -> None:
77
- run_tasks(self.startup_tasks, loop)
78
-
79
- def shutdown(self, loop: asyncio.AbstractEventLoop, /) -> None:
80
- run_tasks(self.shutdown_tasks, loop)
81
-
82
-
83
- class LoopWrapper(ABCLoopWrapper):
84
- def __init__(
85
- self,
86
- *,
87
- tasks: list[CoroutineTask[typing.Any]] | None = None,
88
- lifespan: Lifespan | None = None,
89
- event_loop: asyncio.AbstractEventLoop | None = None,
90
- ) -> None:
91
- self.tasks: list[CoroutineTask[typing.Any]] = tasks or []
92
- self.lifespan = lifespan or Lifespan()
93
- self._loop = event_loop or asyncio.new_event_loop()
94
-
95
- def __repr__(self) -> str:
96
- return "<{}: loop={!r} with tasks={!r}, lifespan={!r}>".format(
97
- self.__class__.__name__,
98
- self._loop,
99
- self.tasks,
100
- self.lifespan,
101
- )
102
-
103
- def run_event_loop(self) -> None:
104
- if not self.tasks:
105
- logger.warning("You run loop with 0 tasks!")
106
-
107
- self.lifespan.start(self._loop)
108
- while self.tasks:
109
- self._loop.create_task(self.tasks.pop(0))
110
- tasks = asyncio.all_tasks(self._loop)
111
-
112
- try:
113
- while tasks:
114
- tasks_results, _ = self._loop.run_until_complete(
115
- asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION),
116
- )
117
- for task_result in tasks_results:
118
- try:
119
- task_result.result()
120
- except BaseException as ex:
121
- logger.exception(ex)
122
- tasks = asyncio.all_tasks(self._loop)
123
- except KeyboardInterrupt:
124
- print() # blank print for ^C
125
- logger.info("Caught KeyboardInterrupt, cancellation...")
126
- self.complete_tasks(tasks)
127
- finally:
128
- self.lifespan.shutdown(self._loop)
129
- if self._loop.is_running():
130
- self._loop.close()
131
-
132
- def add_task(self, task: Task) -> None:
133
- task = to_coroutine_task(task)
134
-
135
- if self._loop and self._loop.is_running():
136
- self._loop.create_task(task)
137
- else:
138
- self.tasks.append(task)
139
-
140
- def complete_tasks(self, tasks: set[asyncio.Task[typing.Any]]) -> None:
141
- tasks = tasks | asyncio.all_tasks(self._loop)
142
- task_to_cancel = asyncio.gather(*tasks, return_exceptions=True)
143
- task_to_cancel.cancel()
144
- with contextlib.suppress(asyncio.CancelledError):
145
- self._loop.run_until_complete(task_to_cancel)
146
-
147
- @typing.overload
148
- def timer(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
149
-
150
- @typing.overload
151
- def timer(
152
- self,
153
- *,
154
- days: int = 0,
155
- hours: int = 0,
156
- minutes: int = 0,
157
- seconds: float = 0,
158
- ) -> typing.Callable[..., typing.Any]: ...
159
-
160
- def timer(
161
- self,
162
- *,
163
- days: int = 0,
164
- hours: int = 0,
165
- minutes: int = 0,
166
- seconds: float | datetime.timedelta = 0,
167
- ) -> typing.Callable[..., typing.Any]:
168
- if isinstance(seconds, datetime.timedelta):
169
- seconds = seconds.total_seconds()
170
-
171
- seconds += minutes * 60
172
- seconds += hours * 60 * 60
173
- seconds += days * 24 * 60 * 60
174
-
175
- def decorator(func: CoroFunc) -> CoroFunc:
176
- self.add_task(DelayedTask(func, seconds, repeat=False))
177
- return func
178
-
179
- return decorator
180
-
181
- @typing.overload
182
- def interval(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
183
-
184
- @typing.overload
185
- def interval(
186
- self,
187
- *,
188
- days: int = 0,
189
- hours: int = 0,
190
- minutes: int = 0,
191
- seconds: float = 0,
192
- ) -> typing.Callable[..., typing.Any]: ...
193
-
194
- def interval(
195
- self,
196
- *,
197
- days: int = 0,
198
- hours: int = 0,
199
- minutes: int = 0,
200
- seconds: float | datetime.timedelta = 0,
201
- ) -> typing.Callable[..., typing.Any]:
202
- if isinstance(seconds, datetime.timedelta):
203
- seconds = seconds.total_seconds()
204
-
205
- seconds += minutes * 60
206
- seconds += hours * 60 * 60
207
- seconds += days * 24 * 60 * 60
208
-
209
- def decorator(func: CoroFunc) -> CoroFunc:
210
- self.add_task(DelayedTask(func, seconds, repeat=True))
211
- return func
212
-
213
- return decorator
214
-
215
-
216
- __all__ = ("DelayedTask", "Lifespan", "LoopWrapper", "to_coroutine_task")
1
+ import asyncio
2
+ import contextlib
3
+ import dataclasses
4
+ import datetime
5
+ import typing
6
+
7
+ from telegrinder.modules import logger
8
+
9
+ from .abc import ABCLoopWrapper
10
+
11
+ T = typing.TypeVar("T")
12
+ P = typing.ParamSpec("P")
13
+ CoroFunc = typing.TypeVar("CoroFunc", bound="CoroutineFunc")
14
+
15
+ CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, T]
16
+ CoroutineFunc: typing.TypeAlias = typing.Callable[P, CoroutineTask[T]]
17
+ Task: typing.TypeAlias = typing.Union[CoroutineFunc, CoroutineTask, "DelayedTask"]
18
+
19
+
20
+ def run_tasks(
21
+ tasks: list[CoroutineTask[typing.Any]],
22
+ loop: asyncio.AbstractEventLoop,
23
+ ) -> None:
24
+ while tasks:
25
+ loop.run_until_complete(tasks.pop(0))
26
+
27
+
28
+ def to_coroutine_task(task: Task) -> CoroutineTask[typing.Any]:
29
+ if asyncio.iscoroutinefunction(task) or isinstance(task, DelayedTask):
30
+ task = task()
31
+ elif not asyncio.iscoroutine(task):
32
+ raise TypeError("Task should be coroutine or coroutine function.")
33
+ return task
34
+
35
+
36
+ @dataclasses.dataclass(slots=True)
37
+ class DelayedTask(typing.Generic[CoroFunc]):
38
+ handler: CoroFunc
39
+ seconds: float
40
+ repeat: bool = dataclasses.field(default=False, kw_only=True)
41
+ _cancelled: bool = dataclasses.field(default=False, init=False, repr=False)
42
+
43
+ @property
44
+ def is_cancelled(self) -> bool:
45
+ return self._cancelled
46
+
47
+ async def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
48
+ while not self.is_cancelled:
49
+ await asyncio.sleep(self.seconds)
50
+ if self.is_cancelled:
51
+ break
52
+ try:
53
+ await self.handler(*args, **kwargs)
54
+ finally:
55
+ if not self.repeat:
56
+ break
57
+
58
+ def cancel(self) -> None:
59
+ if not self._cancelled:
60
+ self._cancelled = True
61
+
62
+
63
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
64
+ class Lifespan:
65
+ startup_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
66
+ shutdown_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
67
+
68
+ def on_startup(self, task: Task, /) -> Task:
69
+ self.startup_tasks.append(to_coroutine_task(task))
70
+ return task
71
+
72
+ def on_shutdown(self, task: Task, /) -> Task:
73
+ self.shutdown_tasks.append(to_coroutine_task(task))
74
+ return task
75
+
76
+ def start(self, loop: asyncio.AbstractEventLoop, /) -> None:
77
+ run_tasks(self.startup_tasks, loop)
78
+
79
+ def shutdown(self, loop: asyncio.AbstractEventLoop, /) -> None:
80
+ run_tasks(self.shutdown_tasks, loop)
81
+
82
+
83
+ class LoopWrapper(ABCLoopWrapper):
84
+ def __init__(
85
+ self,
86
+ *,
87
+ tasks: list[CoroutineTask[typing.Any]] | None = None,
88
+ lifespan: Lifespan | None = None,
89
+ event_loop: asyncio.AbstractEventLoop | None = None,
90
+ ) -> None:
91
+ self.tasks: list[CoroutineTask[typing.Any]] = tasks or []
92
+ self.lifespan = lifespan or Lifespan()
93
+ self._loop = event_loop or asyncio.new_event_loop()
94
+
95
+ def __repr__(self) -> str:
96
+ return "<{}: loop={!r} with tasks={!r}, lifespan={!r}>".format(
97
+ self.__class__.__name__,
98
+ self._loop,
99
+ self.tasks,
100
+ self.lifespan,
101
+ )
102
+
103
+ def run_event_loop(self) -> None:
104
+ if not self.tasks:
105
+ logger.warning("You run loop with 0 tasks!")
106
+
107
+ self.lifespan.start(self._loop)
108
+ while self.tasks:
109
+ self._loop.create_task(self.tasks.pop(0))
110
+ tasks = asyncio.all_tasks(self._loop)
111
+
112
+ try:
113
+ while tasks:
114
+ tasks_results, _ = self._loop.run_until_complete(
115
+ asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION),
116
+ )
117
+ for task_result in tasks_results:
118
+ try:
119
+ task_result.result()
120
+ except BaseException as ex:
121
+ logger.exception(ex)
122
+ tasks = asyncio.all_tasks(self._loop)
123
+ except KeyboardInterrupt:
124
+ print() # blank print for ^C
125
+ logger.info("Caught KeyboardInterrupt, cancellation...")
126
+ self.complete_tasks(tasks)
127
+ finally:
128
+ self.lifespan.shutdown(self._loop)
129
+ if self._loop.is_running():
130
+ self._loop.close()
131
+
132
+ def add_task(self, task: Task) -> None:
133
+ task = to_coroutine_task(task)
134
+
135
+ if self._loop and self._loop.is_running():
136
+ self._loop.create_task(task)
137
+ else:
138
+ self.tasks.append(task)
139
+
140
+ def complete_tasks(self, tasks: set[asyncio.Task[typing.Any]]) -> None:
141
+ tasks = tasks | asyncio.all_tasks(self._loop)
142
+ task_to_cancel = asyncio.gather(*tasks, return_exceptions=True)
143
+ task_to_cancel.cancel()
144
+ with contextlib.suppress(asyncio.CancelledError):
145
+ self._loop.run_until_complete(task_to_cancel)
146
+
147
+ @typing.overload
148
+ def timer(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
149
+
150
+ @typing.overload
151
+ def timer(
152
+ self,
153
+ *,
154
+ days: int = 0,
155
+ hours: int = 0,
156
+ minutes: int = 0,
157
+ seconds: float = 0,
158
+ ) -> typing.Callable[..., typing.Any]: ...
159
+
160
+ def timer(
161
+ self,
162
+ *,
163
+ days: int = 0,
164
+ hours: int = 0,
165
+ minutes: int = 0,
166
+ seconds: float | datetime.timedelta = 0,
167
+ ) -> typing.Callable[..., typing.Any]:
168
+ if isinstance(seconds, datetime.timedelta):
169
+ seconds = seconds.total_seconds()
170
+
171
+ seconds += minutes * 60
172
+ seconds += hours * 60 * 60
173
+ seconds += days * 24 * 60 * 60
174
+
175
+ def decorator(func: CoroFunc) -> CoroFunc:
176
+ self.add_task(DelayedTask(func, seconds, repeat=False))
177
+ return func
178
+
179
+ return decorator
180
+
181
+ @typing.overload
182
+ def interval(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
183
+
184
+ @typing.overload
185
+ def interval(
186
+ self,
187
+ *,
188
+ days: int = 0,
189
+ hours: int = 0,
190
+ minutes: int = 0,
191
+ seconds: float = 0,
192
+ ) -> typing.Callable[..., typing.Any]: ...
193
+
194
+ def interval(
195
+ self,
196
+ *,
197
+ days: int = 0,
198
+ hours: int = 0,
199
+ minutes: int = 0,
200
+ seconds: float | datetime.timedelta = 0,
201
+ ) -> typing.Callable[..., typing.Any]:
202
+ if isinstance(seconds, datetime.timedelta):
203
+ seconds = seconds.total_seconds()
204
+
205
+ seconds += minutes * 60
206
+ seconds += hours * 60 * 60
207
+ seconds += days * 24 * 60 * 60
208
+
209
+ def decorator(func: CoroFunc) -> CoroFunc:
210
+ self.add_task(DelayedTask(func, seconds, repeat=True))
211
+ return func
212
+
213
+ return decorator
214
+
215
+
216
+ __all__ = ("DelayedTask", "Lifespan", "LoopWrapper", "to_coroutine_task")