telegrinder 0.1.dev164__tar.gz → 0.1.dev165__tar.gz
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-0.1.dev164 → telegrinder-0.1.dev165}/PKG-INFO +1 -1
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/pyproject.toml +1 -1
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/__init__.py +36 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/bot.py +9 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/message.py +1 -1
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/abc.py +6 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/polling/polling.py +14 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/markup.py +1 -1
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/model.py +10 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/msgspec_utils.py +13 -2
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/composer.py +1 -1
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/__init__.py +2 -1
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/html.py +6 -12
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/links.py +12 -6
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/spec_html_formats.py +4 -5
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/global_context.py +2 -2
- telegrinder-0.1.dev165/telegrinder/tools/loop_wrapper/__init__.py +4 -0
- telegrinder-0.1.dev165/telegrinder/tools/loop_wrapper/abc.py +15 -0
- telegrinder-0.1.dev165/telegrinder/tools/loop_wrapper/loop_wrapper.py +178 -0
- telegrinder-0.1.dev165/telegrinder/verification_utils.py +31 -0
- telegrinder-0.1.dev164/telegrinder/tools/loop_wrapper/__init__.py +0 -4
- telegrinder-0.1.dev164/telegrinder/tools/loop_wrapper/abc.py +0 -18
- telegrinder-0.1.dev164/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -132
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/LICENSE +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/readme.md +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/api/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/api/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/api/api.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/api/error.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/api/response.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/base.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/callback_query.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/inline_query.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/update.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/utils.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/composition.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/context.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/dispatch.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/func.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/message_reply.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/middleware/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/process.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/message.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/box.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/inline_query.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/message.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/machine.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/polling/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/polling/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/errors.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/event.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/raw_update.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/callback_data.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/command.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/enum_text.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/func.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/fuzzy.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/inline.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/integer.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/is_from.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/mention.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/message_entities.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/regex.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/rule_enum.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/start.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/text.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/checkbox.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/choice.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/client/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/client/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/client/aiohttp.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/modules.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/msgspec_json.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/attachment.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/base.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/container.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/message.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/rule.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/source.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/text.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/tools/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/tools/generator.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/node/update.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/rules.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/buttons.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/error.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/error_handler.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/abc.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/base.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/middleware/base.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/simple.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/kb_set/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/kb_set/base.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/kb_set/yaml.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/keyboard.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/magic.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/parse_mode.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/types/__init__.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/types/enums.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/types/methods.py +0 -0
- {telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/types/objects.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "telegrinder"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.dev165"
|
|
4
4
|
description = "Framework for effective and reliable async telegram bot building."
|
|
5
5
|
authors = ["timoniq <tesseradecades@mail.ru>"]
|
|
6
6
|
maintainers = ["luwqz1 <howluwqz1@gmail.com>"]
|
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
"""Telegrinder
|
|
2
|
+
|
|
3
|
+
Framework for effective and reliable telegram bot building.
|
|
4
|
+
|
|
5
|
+
* Type hinted
|
|
6
|
+
* Customizable and extensible
|
|
7
|
+
* Ready to use scenarios and rules
|
|
8
|
+
* Fast models built on msgspec
|
|
9
|
+
* Both low-level and high-level API
|
|
10
|
+
|
|
11
|
+
Basic example:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from telegrinder import API, Message, Telegrinder, Token
|
|
15
|
+
from telegrinder.modules import logger
|
|
16
|
+
from telegrinder.rules import Text
|
|
17
|
+
|
|
18
|
+
api = API(token=Token("123:token"))
|
|
19
|
+
bot = Telegrinder(api)
|
|
20
|
+
logger.set_level("INFO")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@bot.on.message(Text("/start"))
|
|
24
|
+
async def start(message: Message):
|
|
25
|
+
me = (await api.get_me()).unwrap()
|
|
26
|
+
await message.answer(
|
|
27
|
+
f"Hello, {message.from_user.full_name}! I'm {me.full_name}."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
bot.run_forever()
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
1
35
|
import typing
|
|
2
36
|
|
|
3
37
|
from .api import ABCAPI, API, APIError, APIResponse, Token
|
|
@@ -58,6 +92,7 @@ from .tools import (
|
|
|
58
92
|
Keyboard,
|
|
59
93
|
KeyboardSetBase,
|
|
60
94
|
KeyboardSetYAML,
|
|
95
|
+
Lifespan,
|
|
61
96
|
LoopWrapper,
|
|
62
97
|
ParseMode,
|
|
63
98
|
RowButtons,
|
|
@@ -123,6 +158,7 @@ __all__ = (
|
|
|
123
158
|
"Keyboard",
|
|
124
159
|
"KeyboardSetBase",
|
|
125
160
|
"KeyboardSetYAML",
|
|
161
|
+
"Lifespan",
|
|
126
162
|
"LoopWrapper",
|
|
127
163
|
"Message",
|
|
128
164
|
"MessageCute",
|
|
@@ -24,6 +24,15 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
|
|
|
24
24
|
self.dispatch = typing.cast(DispatchT, dispatch or Dispatch())
|
|
25
25
|
self.polling = typing.cast(PollingT, polling or Polling(api))
|
|
26
26
|
self.loop_wrapper = typing.cast(LoopWrapperT, loop_wrapper or LoopWrapper())
|
|
27
|
+
|
|
28
|
+
def __repr__(self) -> str:
|
|
29
|
+
return "<{}: api={!r}, dispatch={!r}, polling={!r}, loop_wrapper={!r}>".format(
|
|
30
|
+
self.__class__.__name__,
|
|
31
|
+
self.api,
|
|
32
|
+
self.dispatch,
|
|
33
|
+
self.polling,
|
|
34
|
+
self.loop_wrapper,
|
|
35
|
+
)
|
|
27
36
|
|
|
28
37
|
@property
|
|
29
38
|
def on(self) -> DispatchT:
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/abc.py
RENAMED
|
@@ -43,6 +43,12 @@ class ABCReturnManager(ABC, typing.Generic[EventT]):
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class BaseReturnManager(ABCReturnManager[EventT]):
|
|
46
|
+
def __repr__(self) -> str:
|
|
47
|
+
return "<{}: {}>".format(
|
|
48
|
+
self.__class__.__name__,
|
|
49
|
+
", ".join(x.callback.__name__ + "=" + repr(x) for x in self.managers),
|
|
50
|
+
)
|
|
51
|
+
|
|
46
52
|
@property
|
|
47
53
|
def managers(self) -> list[Manager]:
|
|
48
54
|
return [
|
|
@@ -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 = [
|
|
@@ -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
|
-
|
|
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)
|
|
@@ -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:
|
|
@@ -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",
|
|
@@ -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:
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/spec_html_formats.py
RENAMED
|
@@ -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
|
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/global_context.py
RENAMED
|
@@ -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
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ABCLoopWrapper(ABC):
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def add_task(self, task: typing.Any) -> None:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def run_event_loop(self) -> None:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
__all__ = ("ABCLoopWrapper",)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import contextlib
|
|
3
|
+
import dataclasses
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from telegrinder.modules import logger
|
|
7
|
+
|
|
8
|
+
from .abc import ABCLoopWrapper
|
|
9
|
+
|
|
10
|
+
T = typing.TypeVar("T")
|
|
11
|
+
P = typing.ParamSpec("P")
|
|
12
|
+
CoroFunc = typing.TypeVar("CoroFunc", bound="CoroutineFunc")
|
|
13
|
+
|
|
14
|
+
CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, T]
|
|
15
|
+
CoroutineFunc: typing.TypeAlias = typing.Callable[P, CoroutineTask[T]]
|
|
16
|
+
Task: typing.TypeAlias = typing.Union[CoroutineFunc, CoroutineTask, "DelayedTask"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_tasks(tasks: list[CoroutineTask[typing.Any]], loop: asyncio.AbstractEventLoop) -> None:
|
|
20
|
+
while len(tasks) != 0:
|
|
21
|
+
loop.run_until_complete(tasks.pop(0))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def to_coroutine_task(task: Task) -> CoroutineTask:
|
|
25
|
+
if asyncio.iscoroutinefunction(task) or isinstance(task, DelayedTask):
|
|
26
|
+
task = task()
|
|
27
|
+
elif not asyncio.iscoroutine(task):
|
|
28
|
+
raise TypeError("Task should be coroutine or coroutine function.")
|
|
29
|
+
return task
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclasses.dataclass
|
|
33
|
+
class DelayedTask(typing.Generic[CoroFunc]):
|
|
34
|
+
handler: CoroFunc
|
|
35
|
+
seconds: float
|
|
36
|
+
repeat: bool = dataclasses.field(default=False, kw_only=True)
|
|
37
|
+
_cancelled: bool = dataclasses.field(default=False, init=False, repr=False)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def is_cancelled(self) -> bool:
|
|
41
|
+
return self._cancelled
|
|
42
|
+
|
|
43
|
+
async def __call__(self, *args, **kwargs) -> None:
|
|
44
|
+
while not self.is_cancelled:
|
|
45
|
+
await asyncio.sleep(self.seconds)
|
|
46
|
+
if self.is_cancelled:
|
|
47
|
+
break
|
|
48
|
+
await self.handler(*args, **kwargs)
|
|
49
|
+
if not self.repeat:
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
def cancel(self) -> None:
|
|
53
|
+
if not self._cancelled:
|
|
54
|
+
self._cancelled = True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclasses.dataclass(kw_only=True)
|
|
58
|
+
class Lifespan:
|
|
59
|
+
startup_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
|
|
60
|
+
shutdown_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
|
|
61
|
+
|
|
62
|
+
def on_startup(self, task_or_func: Task) -> Task:
|
|
63
|
+
task_or_func = to_coroutine_task(task_or_func)
|
|
64
|
+
self.startup_tasks.append(task_or_func)
|
|
65
|
+
return task_or_func
|
|
66
|
+
|
|
67
|
+
def on_shutdown(self, task_or_func: Task) -> Task:
|
|
68
|
+
task_or_func = to_coroutine_task(task_or_func)
|
|
69
|
+
self.shutdown_tasks.append(task_or_func)
|
|
70
|
+
return task_or_func
|
|
71
|
+
|
|
72
|
+
def start(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
73
|
+
run_tasks(self.startup_tasks, loop)
|
|
74
|
+
|
|
75
|
+
def shutdown(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
76
|
+
run_tasks(self.shutdown_tasks, loop)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class LoopWrapper(ABCLoopWrapper):
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
*,
|
|
83
|
+
tasks: list[CoroutineTask[typing.Any]] | None = None,
|
|
84
|
+
lifespan: Lifespan | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
self.tasks: list[CoroutineTask[typing.Any]] = tasks or []
|
|
87
|
+
self.lifespan = lifespan or Lifespan()
|
|
88
|
+
self._loop = asyncio.new_event_loop()
|
|
89
|
+
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
return "<{}: loop={!r} with tasks={!r}, lifespan={!r}>".format(
|
|
92
|
+
self.__class__.__name__,
|
|
93
|
+
self._loop,
|
|
94
|
+
self.tasks,
|
|
95
|
+
self.lifespan,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def run_event_loop(self) -> None:
|
|
99
|
+
if not self.tasks:
|
|
100
|
+
logger.warning("You run loop with 0 tasks!")
|
|
101
|
+
|
|
102
|
+
self.lifespan.start(self._loop)
|
|
103
|
+
run_tasks(self.tasks, self._loop)
|
|
104
|
+
tasks = asyncio.all_tasks(self._loop)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
while tasks:
|
|
108
|
+
tasks_results, _ = self._loop.run_until_complete(
|
|
109
|
+
asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION),
|
|
110
|
+
)
|
|
111
|
+
for task_result in tasks_results:
|
|
112
|
+
try:
|
|
113
|
+
task_result.result()
|
|
114
|
+
except BaseException as ex:
|
|
115
|
+
logger.exception(ex)
|
|
116
|
+
tasks = asyncio.all_tasks(self._loop)
|
|
117
|
+
except KeyboardInterrupt:
|
|
118
|
+
print() # blank print for ^C
|
|
119
|
+
logger.info("Caught KeyboardInterrupt, cancellation...")
|
|
120
|
+
self.complete_tasks(tasks)
|
|
121
|
+
finally:
|
|
122
|
+
self.lifespan.shutdown(self._loop)
|
|
123
|
+
if self._loop.is_running():
|
|
124
|
+
self._loop.close()
|
|
125
|
+
|
|
126
|
+
def add_task(self, task: Task) -> None:
|
|
127
|
+
task = to_coroutine_task(task)
|
|
128
|
+
|
|
129
|
+
if self._loop and self._loop.is_running():
|
|
130
|
+
self._loop.create_task(task)
|
|
131
|
+
else:
|
|
132
|
+
self.tasks.append(task)
|
|
133
|
+
|
|
134
|
+
def complete_tasks(self, tasks: set[asyncio.Task[typing.Any]]) -> None:
|
|
135
|
+
tasks = tasks | asyncio.all_tasks(self._loop)
|
|
136
|
+
task_to_cancel = asyncio.gather(*tasks, return_exceptions=True)
|
|
137
|
+
task_to_cancel.cancel()
|
|
138
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
139
|
+
self._loop.run_until_complete(task_to_cancel)
|
|
140
|
+
|
|
141
|
+
def timer(
|
|
142
|
+
self,
|
|
143
|
+
*,
|
|
144
|
+
days: int = 0,
|
|
145
|
+
hours: int = 0,
|
|
146
|
+
minutes: int = 0,
|
|
147
|
+
seconds: float = 0,
|
|
148
|
+
):
|
|
149
|
+
seconds += minutes * 60
|
|
150
|
+
seconds += hours * 60 * 60
|
|
151
|
+
seconds += days * 24 * 60 * 60
|
|
152
|
+
|
|
153
|
+
def decorator(func: CoroFunc) -> CoroFunc:
|
|
154
|
+
self.add_task(DelayedTask(func, seconds, repeat=False))
|
|
155
|
+
return func
|
|
156
|
+
|
|
157
|
+
return decorator
|
|
158
|
+
|
|
159
|
+
def interval(
|
|
160
|
+
self,
|
|
161
|
+
*,
|
|
162
|
+
days: int = 0,
|
|
163
|
+
hours: int = 0,
|
|
164
|
+
minutes: int = 0,
|
|
165
|
+
seconds: float = 0,
|
|
166
|
+
):
|
|
167
|
+
seconds += minutes * 60
|
|
168
|
+
seconds += hours * 60 * 60
|
|
169
|
+
seconds += days * 24 * 60 * 60
|
|
170
|
+
|
|
171
|
+
def decorator(func: CoroFunc) -> CoroFunc:
|
|
172
|
+
self.add_task(DelayedTask(func, seconds, repeat=True))
|
|
173
|
+
return func
|
|
174
|
+
|
|
175
|
+
return decorator
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
__all__ = ("DelayedTask", "Lifespan", "LoopWrapper", "to_coroutine_task")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import hmac
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.abc import Token
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def verify_webapp_request(secret_token: str, request_headers: typing.Mapping[str, typing.Any]) -> bool:
|
|
9
|
+
"""Verifies update request is from telegram."""
|
|
10
|
+
|
|
11
|
+
return request_headers.get("X-Telegram-Bot-Api-Secret-Token") == secret_token
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def webapp_validate_request(
|
|
15
|
+
bot_token: Token,
|
|
16
|
+
request_query_params: typing.Mapping[str, typing.Any],
|
|
17
|
+
) -> bool:
|
|
18
|
+
"""Verifies authentity of webapp request by counting hash of its parameters."""
|
|
19
|
+
|
|
20
|
+
items = sorted(request_query_params.items(), key=lambda kv: kv[0])
|
|
21
|
+
data_check_string = "\n".join(f"{k}={param}" for k, param in items if k != "hash")
|
|
22
|
+
secret = hmac.new(
|
|
23
|
+
"WebAppData".encode(),
|
|
24
|
+
bot_token.encode(),
|
|
25
|
+
hashlib.sha256,
|
|
26
|
+
).digest()
|
|
27
|
+
data_chk = hmac.new(secret, data_check_string.encode(), hashlib.sha256)
|
|
28
|
+
return data_chk.hexdigest() == request_query_params.get("hash")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = ("verify_webapp_request", "webapp_validate_request")
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
|
|
4
|
-
CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, typing.Any]
|
|
5
|
-
CoroutineFunc: typing.TypeAlias = typing.Callable[..., CoroutineTask]
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ABCLoopWrapper(ABC):
|
|
9
|
-
@abstractmethod
|
|
10
|
-
def add_task(self, task: CoroutineFunc | CoroutineTask) -> None:
|
|
11
|
-
pass
|
|
12
|
-
|
|
13
|
-
@abstractmethod
|
|
14
|
-
def run_event_loop(self) -> None:
|
|
15
|
-
pass
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
__all__ = ("ABCLoopWrapper",)
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import contextlib
|
|
3
|
-
import dataclasses
|
|
4
|
-
import typing
|
|
5
|
-
|
|
6
|
-
from telegrinder.modules import logger
|
|
7
|
-
|
|
8
|
-
from .abc import ABCLoopWrapper, CoroutineFunc, CoroutineTask
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@dataclasses.dataclass
|
|
12
|
-
class DelayedTask:
|
|
13
|
-
handler: CoroutineFunc
|
|
14
|
-
seconds: float
|
|
15
|
-
repeat: bool = dataclasses.field(default=False, kw_only=True)
|
|
16
|
-
_cancelled: bool = dataclasses.field(default=False, init=False, repr=False)
|
|
17
|
-
|
|
18
|
-
@property
|
|
19
|
-
def is_cancelled(self) -> bool:
|
|
20
|
-
return self._cancelled
|
|
21
|
-
|
|
22
|
-
async def __call__(self, *args, **kwargs) -> None:
|
|
23
|
-
while not self.is_cancelled:
|
|
24
|
-
await asyncio.sleep(self.seconds)
|
|
25
|
-
if self.is_cancelled:
|
|
26
|
-
break
|
|
27
|
-
await self.handler(*args, **kwargs)
|
|
28
|
-
if not self.repeat:
|
|
29
|
-
break
|
|
30
|
-
|
|
31
|
-
def cancel(self) -> None:
|
|
32
|
-
if not self._cancelled:
|
|
33
|
-
self._cancelled = True
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class LoopWrapper(ABCLoopWrapper):
|
|
37
|
-
def __init__(self, tasks: list[CoroutineTask] | None = None):
|
|
38
|
-
self.on_startup: list[CoroutineTask] = []
|
|
39
|
-
self.on_shutdown: list[CoroutineTask] = []
|
|
40
|
-
self.tasks = tasks or []
|
|
41
|
-
self._loop = asyncio.new_event_loop()
|
|
42
|
-
|
|
43
|
-
def run_event_loop(self) -> None:
|
|
44
|
-
if not self.tasks:
|
|
45
|
-
logger.warning("You run loop with 0 tasks!")
|
|
46
|
-
|
|
47
|
-
for startup_task in self.on_startup:
|
|
48
|
-
self._loop.run_until_complete(startup_task)
|
|
49
|
-
for task in self.tasks:
|
|
50
|
-
self._loop.create_task(task)
|
|
51
|
-
|
|
52
|
-
self.tasks.clear()
|
|
53
|
-
tasks = asyncio.all_tasks(self._loop)
|
|
54
|
-
try:
|
|
55
|
-
while tasks:
|
|
56
|
-
tasks_results, _ = self._loop.run_until_complete(
|
|
57
|
-
asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
|
58
|
-
)
|
|
59
|
-
for task_result in tasks_results:
|
|
60
|
-
try:
|
|
61
|
-
task_result.result()
|
|
62
|
-
except BaseException as ex:
|
|
63
|
-
logger.exception(ex)
|
|
64
|
-
tasks = asyncio.all_tasks(self._loop)
|
|
65
|
-
except KeyboardInterrupt:
|
|
66
|
-
print() # blank print for ^C
|
|
67
|
-
logger.info("KeyboardInterrupt")
|
|
68
|
-
self.complete_tasks(tasks)
|
|
69
|
-
finally:
|
|
70
|
-
for shutdown_task in self.on_shutdown:
|
|
71
|
-
self._loop.run_until_complete(shutdown_task)
|
|
72
|
-
if self._loop.is_running():
|
|
73
|
-
self._loop.close()
|
|
74
|
-
|
|
75
|
-
def add_task(self, task: CoroutineFunc | CoroutineTask | DelayedTask):
|
|
76
|
-
if asyncio.iscoroutinefunction(task) or isinstance(task, DelayedTask):
|
|
77
|
-
task = task()
|
|
78
|
-
elif not asyncio.iscoroutine(task):
|
|
79
|
-
raise TypeError("Task should be coroutine or coroutine function.")
|
|
80
|
-
|
|
81
|
-
if self._loop and self._loop.is_running():
|
|
82
|
-
self._loop.create_task(task)
|
|
83
|
-
else:
|
|
84
|
-
self.tasks.append(task)
|
|
85
|
-
|
|
86
|
-
def complete_tasks(self, tasks: set[asyncio.Task[typing.Any]]) -> None:
|
|
87
|
-
tasks = tasks | asyncio.all_tasks(self._loop)
|
|
88
|
-
task_to_cancel = asyncio.gather(*tasks, return_exceptions=True)
|
|
89
|
-
task_to_cancel.cancel()
|
|
90
|
-
with contextlib.suppress(asyncio.CancelledError):
|
|
91
|
-
self._loop.run_until_complete(task_to_cancel)
|
|
92
|
-
|
|
93
|
-
def timer(
|
|
94
|
-
self,
|
|
95
|
-
*,
|
|
96
|
-
days: int = 0,
|
|
97
|
-
hours: int = 0,
|
|
98
|
-
minutes: int = 0,
|
|
99
|
-
seconds: float = 0,
|
|
100
|
-
) -> typing.Callable[[typing.Callable], DelayedTask]:
|
|
101
|
-
seconds += minutes * 60
|
|
102
|
-
seconds += hours * 60 * 60
|
|
103
|
-
seconds += days * 24 * 60 * 60
|
|
104
|
-
|
|
105
|
-
def decorator(func: typing.Callable) -> DelayedTask:
|
|
106
|
-
delayed_task = DelayedTask(func, seconds, repeat=False)
|
|
107
|
-
self.add_task(delayed_task)
|
|
108
|
-
return delayed_task
|
|
109
|
-
|
|
110
|
-
return decorator
|
|
111
|
-
|
|
112
|
-
def interval(
|
|
113
|
-
self,
|
|
114
|
-
*,
|
|
115
|
-
days: int = 0,
|
|
116
|
-
hours: int = 0,
|
|
117
|
-
minutes: int = 0,
|
|
118
|
-
seconds: float = 0,
|
|
119
|
-
) -> typing.Callable[[typing.Callable], DelayedTask]:
|
|
120
|
-
seconds += minutes * 60
|
|
121
|
-
seconds += hours * 60 * 60
|
|
122
|
-
seconds += days * 24 * 60 * 60
|
|
123
|
-
|
|
124
|
-
def decorator(func: typing.Callable) -> DelayedTask:
|
|
125
|
-
delayed_task = DelayedTask(func, seconds, repeat=True)
|
|
126
|
-
self.add_task(delayed_task)
|
|
127
|
-
return delayed_task
|
|
128
|
-
|
|
129
|
-
return decorator
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
__all__ = ("DelayedTask", "LoopWrapper")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/callback_query.py
RENAMED
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/inline_query.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/message_reply.py
RENAMED
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/middleware/__init__.py
RENAMED
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/middleware/abc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/message.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/callback_query.py
RENAMED
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/inline_query.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/machine.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/raw_update.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/error_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegrinder-0.1.dev164 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/middleware/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|