telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev159__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 +129 -22
- telegrinder/api/__init__.py +11 -2
- telegrinder/api/abc.py +25 -9
- telegrinder/api/api.py +47 -28
- telegrinder/api/error.py +14 -4
- telegrinder/api/response.py +11 -7
- telegrinder/bot/__init__.py +68 -7
- telegrinder/bot/bot.py +30 -24
- telegrinder/bot/cute_types/__init__.py +11 -1
- telegrinder/bot/cute_types/base.py +138 -0
- telegrinder/bot/cute_types/callback_query.py +458 -15
- telegrinder/bot/cute_types/inline_query.py +30 -24
- telegrinder/bot/cute_types/message.py +2982 -78
- telegrinder/bot/cute_types/update.py +30 -0
- telegrinder/bot/cute_types/utils.py +794 -0
- telegrinder/bot/dispatch/__init__.py +56 -3
- telegrinder/bot/dispatch/abc.py +9 -7
- telegrinder/bot/dispatch/composition.py +74 -0
- telegrinder/bot/dispatch/context.py +71 -0
- telegrinder/bot/dispatch/dispatch.py +86 -49
- telegrinder/bot/dispatch/handler/__init__.py +3 -0
- telegrinder/bot/dispatch/handler/abc.py +11 -5
- telegrinder/bot/dispatch/handler/func.py +41 -32
- telegrinder/bot/dispatch/handler/message_reply.py +46 -0
- telegrinder/bot/dispatch/middleware/__init__.py +2 -0
- telegrinder/bot/dispatch/middleware/abc.py +10 -4
- telegrinder/bot/dispatch/process.py +53 -49
- telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
- telegrinder/bot/dispatch/return_manager/abc.py +95 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +25 -0
- telegrinder/bot/dispatch/view/__init__.py +14 -2
- telegrinder/bot/dispatch/view/abc.py +128 -2
- telegrinder/bot/dispatch/view/box.py +38 -0
- telegrinder/bot/dispatch/view/callback_query.py +13 -39
- telegrinder/bot/dispatch/view/inline_query.py +11 -39
- telegrinder/bot/dispatch/view/message.py +11 -47
- telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
- telegrinder/bot/polling/__init__.py +2 -0
- telegrinder/bot/polling/abc.py +11 -4
- telegrinder/bot/polling/polling.py +89 -40
- telegrinder/bot/rules/__init__.py +91 -5
- telegrinder/bot/rules/abc.py +81 -63
- telegrinder/bot/rules/adapter/__init__.py +11 -0
- telegrinder/bot/rules/adapter/abc.py +21 -0
- telegrinder/bot/rules/adapter/errors.py +5 -0
- telegrinder/bot/rules/adapter/event.py +49 -0
- telegrinder/bot/rules/adapter/raw_update.py +24 -0
- telegrinder/bot/rules/callback_data.py +159 -38
- telegrinder/bot/rules/command.py +116 -0
- telegrinder/bot/rules/enum_text.py +28 -0
- telegrinder/bot/rules/func.py +17 -17
- telegrinder/bot/rules/fuzzy.py +13 -10
- telegrinder/bot/rules/inline.py +61 -0
- telegrinder/bot/rules/integer.py +12 -7
- telegrinder/bot/rules/is_from.py +148 -7
- telegrinder/bot/rules/markup.py +21 -18
- telegrinder/bot/rules/mention.py +17 -0
- telegrinder/bot/rules/message_entities.py +33 -0
- telegrinder/bot/rules/regex.py +27 -19
- telegrinder/bot/rules/rule_enum.py +74 -0
- telegrinder/bot/rules/start.py +25 -13
- telegrinder/bot/rules/text.py +23 -14
- telegrinder/bot/scenario/__init__.py +2 -0
- telegrinder/bot/scenario/abc.py +12 -5
- telegrinder/bot/scenario/checkbox.py +48 -30
- telegrinder/bot/scenario/choice.py +16 -10
- telegrinder/client/__init__.py +3 -1
- telegrinder/client/abc.py +26 -16
- telegrinder/client/aiohttp.py +54 -32
- telegrinder/model.py +119 -40
- telegrinder/modules.py +189 -21
- telegrinder/msgspec_json.py +14 -0
- telegrinder/msgspec_utils.py +227 -0
- telegrinder/node/__init__.py +31 -0
- telegrinder/node/attachment.py +71 -0
- telegrinder/node/base.py +93 -0
- telegrinder/node/composer.py +71 -0
- telegrinder/node/container.py +22 -0
- telegrinder/node/message.py +18 -0
- telegrinder/node/rule.py +56 -0
- telegrinder/node/source.py +31 -0
- telegrinder/node/text.py +13 -0
- telegrinder/node/tools/__init__.py +3 -0
- telegrinder/node/tools/generator.py +40 -0
- telegrinder/node/update.py +12 -0
- telegrinder/rules.py +1 -1
- telegrinder/tools/__init__.py +138 -4
- telegrinder/tools/buttons.py +89 -51
- telegrinder/tools/error_handler/__init__.py +8 -0
- telegrinder/tools/error_handler/abc.py +30 -0
- telegrinder/tools/error_handler/error_handler.py +156 -0
- telegrinder/tools/formatting/__init__.py +81 -3
- telegrinder/tools/formatting/html.py +283 -37
- telegrinder/tools/formatting/links.py +32 -0
- telegrinder/tools/formatting/spec_html_formats.py +121 -0
- telegrinder/tools/global_context/__init__.py +12 -0
- telegrinder/tools/global_context/abc.py +66 -0
- telegrinder/tools/global_context/global_context.py +451 -0
- telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
- telegrinder/tools/i18n/__init__.py +12 -0
- telegrinder/tools/i18n/base.py +31 -0
- telegrinder/tools/i18n/middleware/__init__.py +3 -0
- telegrinder/tools/i18n/middleware/base.py +26 -0
- telegrinder/tools/i18n/simple.py +48 -0
- telegrinder/tools/kb_set/__init__.py +2 -0
- telegrinder/tools/kb_set/base.py +3 -0
- telegrinder/tools/kb_set/yaml.py +28 -17
- telegrinder/tools/keyboard.py +84 -62
- telegrinder/tools/loop_wrapper/__init__.py +4 -0
- telegrinder/tools/loop_wrapper/abc.py +18 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
- telegrinder/tools/magic.py +48 -23
- telegrinder/tools/parse_mode.py +1 -2
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +653 -0
- telegrinder/types/methods.py +4107 -1279
- telegrinder/types/objects.py +4771 -1745
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
- telegrinder-0.1.dev159.dist-info/METADATA +109 -0
- telegrinder-0.1.dev159.dist-info/RECORD +126 -0
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
- telegrinder/bot/dispatch/waiter.py +0 -38
- telegrinder/result.py +0 -38
- telegrinder/tools/formatting/abc.py +0 -52
- telegrinder/tools/formatting/markdown.py +0 -57
- telegrinder-0.1.dev20.dist-info/METADATA +0 -22
- telegrinder-0.1.dev20.dist-info/RECORD +0 -71
|
@@ -1,54 +1,18 @@
|
|
|
1
|
-
from .abc import ABCView
|
|
2
|
-
from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler
|
|
3
|
-
from telegrinder.bot.dispatch.waiter import Waiter
|
|
4
|
-
from telegrinder.bot.dispatch.middleware import ABCMiddleware
|
|
5
|
-
from telegrinder.bot.rules import ABCRule
|
|
6
1
|
from telegrinder.bot.cute_types import MessageCute
|
|
7
|
-
from telegrinder.
|
|
8
|
-
from telegrinder.bot.dispatch.waiter import WithWaiter, DefaultWaiterHandler
|
|
9
|
-
from telegrinder.bot.dispatch.process import process_waiters, process_inner
|
|
10
|
-
from telegrinder.types import Update
|
|
11
|
-
import typing
|
|
2
|
+
from telegrinder.bot.dispatch.return_manager import MessageReturnManager
|
|
12
3
|
|
|
4
|
+
from .abc import BaseStateView
|
|
13
5
|
|
|
14
|
-
class MessageView(ABCView, WithWaiter[int, MessageCute]):
|
|
15
|
-
def __init__(self):
|
|
16
|
-
self.auto_rules: list[ABCRule] = []
|
|
17
|
-
self.handlers: typing.List[ABCHandler[MessageCute]] = []
|
|
18
|
-
self.middlewares: typing.List[ABCMiddleware[MessageCute]] = []
|
|
19
|
-
self.short_waiters: typing.Dict[int, Waiter] = {}
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
7
|
+
class MessageView(BaseStateView[MessageCute]):
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self.auto_rules = []
|
|
10
|
+
self.handlers = []
|
|
11
|
+
self.middlewares = []
|
|
12
|
+
self.return_manager = MessageReturnManager()
|
|
27
13
|
|
|
28
|
-
|
|
14
|
+
def get_state_key(self, event: MessageCute) -> int | None:
|
|
15
|
+
return event.chat.id
|
|
29
16
|
|
|
30
|
-
def load(self, external: "MessageView"):
|
|
31
|
-
self.handlers.extend(external.handlers)
|
|
32
|
-
self.middlewares.extend(external.middlewares)
|
|
33
|
-
external.short_waiters = self.short_waiters
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
return bool(event.message)
|
|
37
|
-
|
|
38
|
-
async def process(self, event: Update, api: ABCAPI):
|
|
39
|
-
msg = MessageCute(**event.message.to_dict(), api=api)
|
|
40
|
-
|
|
41
|
-
if await process_waiters(
|
|
42
|
-
self.short_waiters, msg.chat.id, msg, event, msg.answer
|
|
43
|
-
):
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
return await process_inner(msg, event, self.middlewares, self.handlers)
|
|
47
|
-
|
|
48
|
-
async def wait_for_message(
|
|
49
|
-
self,
|
|
50
|
-
chat_id: int,
|
|
51
|
-
*rules: ABCRule,
|
|
52
|
-
default: typing.Optional[typing.Union[DefaultWaiterHandler, str]] = None
|
|
53
|
-
) -> typing.Tuple["MessageCute", dict]:
|
|
54
|
-
return await self.wait_for_answer(chat_id, *self.auto_rules, *rules, default=default)
|
|
18
|
+
__all__ = ("MessageView",)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import datetime
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.abc import ABCAPI
|
|
6
|
+
from telegrinder.bot.dispatch.context import Context
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
|
|
9
|
+
from .middleware import WaiterMiddleware
|
|
10
|
+
from .short_state import Behaviour, EventModel, ShortState
|
|
11
|
+
|
|
12
|
+
Identificator: typing.TypeAlias = str | int
|
|
13
|
+
Storage: typing.TypeAlias = dict[str, dict[Identificator, "ShortState"]]
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from telegrinder.bot.dispatch.view.abc import ABCStateView, BaseStateView
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WaiterMachine:
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self.storage: Storage = {}
|
|
22
|
+
|
|
23
|
+
async def drop(
|
|
24
|
+
self,
|
|
25
|
+
state_view: "ABCStateView[EventModel]",
|
|
26
|
+
id: Identificator,
|
|
27
|
+
**context,
|
|
28
|
+
) -> None:
|
|
29
|
+
view_name = state_view.__class__.__name__
|
|
30
|
+
if view_name not in self.storage:
|
|
31
|
+
raise LookupError("No record of view {!r} found".format(view_name))
|
|
32
|
+
|
|
33
|
+
short_state = self.storage[view_name].pop(id, None)
|
|
34
|
+
if not short_state:
|
|
35
|
+
raise LookupError(
|
|
36
|
+
"Waiter with identificator {} is not found for view {!r}".format(
|
|
37
|
+
id,
|
|
38
|
+
view_name,
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
waiters = typing.cast(
|
|
43
|
+
typing.Iterable[asyncio.Future[typing.Any]],
|
|
44
|
+
short_state.event._waiters # type: ignore
|
|
45
|
+
)
|
|
46
|
+
for future in waiters:
|
|
47
|
+
future.cancel()
|
|
48
|
+
|
|
49
|
+
await self.call_behaviour(
|
|
50
|
+
state_view,
|
|
51
|
+
short_state.on_drop_behaviour,
|
|
52
|
+
short_state.event,
|
|
53
|
+
**context,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def wait(
|
|
57
|
+
self,
|
|
58
|
+
state_view: "BaseStateView[EventModel]",
|
|
59
|
+
linked: EventModel | tuple[ABCAPI, Identificator],
|
|
60
|
+
*rules: ABCRule[EventModel],
|
|
61
|
+
default: Behaviour = None,
|
|
62
|
+
on_drop: Behaviour = None,
|
|
63
|
+
expiration: datetime.timedelta | int | None = None,
|
|
64
|
+
) -> tuple[EventModel, Context]:
|
|
65
|
+
if isinstance(expiration, int):
|
|
66
|
+
expiration = datetime.timedelta(seconds=expiration)
|
|
67
|
+
|
|
68
|
+
api: ABCAPI
|
|
69
|
+
key: Identificator
|
|
70
|
+
event = asyncio.Event()
|
|
71
|
+
if isinstance(linked, tuple):
|
|
72
|
+
api, key = linked
|
|
73
|
+
else:
|
|
74
|
+
api, key = linked.ctx_api, state_view.get_state_key(linked) # type: ignore
|
|
75
|
+
if not key:
|
|
76
|
+
raise RuntimeError("Unable to get state key.")
|
|
77
|
+
|
|
78
|
+
short_state = ShortState(
|
|
79
|
+
key,
|
|
80
|
+
ctx_api=api,
|
|
81
|
+
event=event,
|
|
82
|
+
rules=rules,
|
|
83
|
+
expiration=expiration,
|
|
84
|
+
default_behaviour=default,
|
|
85
|
+
on_drop_behaviour=on_drop,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
view_name = state_view.__class__.__name__
|
|
89
|
+
if view_name not in self.storage:
|
|
90
|
+
state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
|
|
91
|
+
self.storage[view_name] = {}
|
|
92
|
+
|
|
93
|
+
self.storage[view_name][key] = short_state
|
|
94
|
+
|
|
95
|
+
await event.wait()
|
|
96
|
+
|
|
97
|
+
e, ctx = getattr(event, "context")
|
|
98
|
+
self.storage[view_name].pop(key)
|
|
99
|
+
|
|
100
|
+
return e, ctx
|
|
101
|
+
|
|
102
|
+
async def call_behaviour(
|
|
103
|
+
self,
|
|
104
|
+
view: "ABCStateView[EventModel]",
|
|
105
|
+
behaviour: Behaviour,
|
|
106
|
+
event: asyncio.Event | EventModel,
|
|
107
|
+
**context,
|
|
108
|
+
) -> None:
|
|
109
|
+
if behaviour is None:
|
|
110
|
+
return
|
|
111
|
+
# TODO: add behaviour check
|
|
112
|
+
# TODO: support view as a behaviour
|
|
113
|
+
await behaviour.run(event)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
__all__ = ("WaiterMachine",)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.func import FuncHandler
|
|
7
|
+
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
|
|
8
|
+
from telegrinder.bot.dispatch.view.abc import ABCStateView
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from .machine import WaiterMachine
|
|
12
|
+
from .short_state import ShortState
|
|
13
|
+
|
|
14
|
+
EventType = typing.TypeVar("EventType", bound=BaseCute)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WaiterMiddleware(ABCMiddleware[EventType]):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
machine: "WaiterMachine",
|
|
21
|
+
view: ABCStateView[EventType],
|
|
22
|
+
) -> None:
|
|
23
|
+
self.machine = machine
|
|
24
|
+
self.view = view
|
|
25
|
+
|
|
26
|
+
async def pre(self, event: EventType, ctx: Context) -> bool:
|
|
27
|
+
if not self.view or not hasattr(self.view, "get_state_key"):
|
|
28
|
+
raise RuntimeError(
|
|
29
|
+
"WaiterMiddleware cannot be used inside a view which doesn't "
|
|
30
|
+
"provide get_state_key (ABCStateView Protocol)."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
view_name = self.view.__class__.__name__
|
|
34
|
+
if view_name not in self.machine.storage:
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
key = self.view.get_state_key(event)
|
|
38
|
+
if key is None:
|
|
39
|
+
raise RuntimeError("Unable to get state key.")
|
|
40
|
+
|
|
41
|
+
short_state: typing.Optional["ShortState"] = self.machine.storage[view_name].get(key)
|
|
42
|
+
if not short_state:
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
short_state.expiration is not None
|
|
47
|
+
and datetime.datetime.now() >= short_state.expiration
|
|
48
|
+
):
|
|
49
|
+
await self.machine.drop(self.view, short_state.key)
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
handler = FuncHandler(
|
|
53
|
+
self.pass_runtime, list(short_state.rules), dataclass=None
|
|
54
|
+
)
|
|
55
|
+
handler.ctx.set("short_state", short_state)
|
|
56
|
+
result = await handler.check(event.ctx_api, ctx.raw_update, ctx)
|
|
57
|
+
|
|
58
|
+
if result is True:
|
|
59
|
+
await handler.run(event)
|
|
60
|
+
|
|
61
|
+
elif short_state.default_behaviour is not None:
|
|
62
|
+
await self.machine.call_behaviour(
|
|
63
|
+
self.view,
|
|
64
|
+
short_state.default_behaviour,
|
|
65
|
+
event,
|
|
66
|
+
**handler.ctx,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
async def pass_runtime(self, event: EventType, short_state: "ShortState[EventType]", ctx: Context) -> None:
|
|
72
|
+
setattr(short_state.event, "context", (event, ctx))
|
|
73
|
+
short_state.event.set()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
__all__ = ("WaiterMiddleware",)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import datetime
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.abc import ABCAPI
|
|
6
|
+
from telegrinder.bot.cute_types import BaseCute
|
|
7
|
+
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
8
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from .machine import Identificator
|
|
12
|
+
|
|
13
|
+
EventModel = typing.TypeVar("EventModel", bound=BaseCute)
|
|
14
|
+
Behaviour = ABCHandler | None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
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
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ("ShortState",)
|
telegrinder/bot/polling/abc.py
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
import msgspec
|
|
5
|
+
|
|
3
6
|
from telegrinder.types import Update
|
|
4
|
-
from telegrinder.model import Raw
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class ABCPolling(ABC):
|
|
10
|
+
offset: int
|
|
11
|
+
|
|
8
12
|
@abstractmethod
|
|
9
|
-
async def get_updates(self) ->
|
|
13
|
+
async def get_updates(self) -> list[msgspec.Raw]:
|
|
10
14
|
pass
|
|
11
15
|
|
|
12
16
|
@abstractmethod
|
|
13
|
-
async def listen(self) -> typing.
|
|
14
|
-
|
|
17
|
+
async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
|
|
18
|
+
yield []
|
|
15
19
|
|
|
16
20
|
@abstractmethod
|
|
17
21
|
def stop(self) -> None:
|
|
18
22
|
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ("ABCPolling",)
|
|
@@ -1,71 +1,120 @@
|
|
|
1
|
-
import traceback
|
|
2
1
|
import asyncio
|
|
2
|
+
import typing
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import aiohttp
|
|
5
|
+
import msgspec
|
|
6
|
+
from fntypes.result import Error, Ok
|
|
5
7
|
|
|
6
|
-
from .abc import ABCPolling
|
|
7
8
|
from telegrinder.api.abc import ABCAPI
|
|
8
|
-
import
|
|
9
|
+
from telegrinder.api.error import InvalidTokenError
|
|
10
|
+
from telegrinder.bot.polling.abc import ABCPolling
|
|
9
11
|
from telegrinder.modules import logger
|
|
10
|
-
from telegrinder.
|
|
11
|
-
from telegrinder.types import Update
|
|
12
|
-
|
|
13
|
-
ALLOWED_UPDATES = [
|
|
14
|
-
"update_id",
|
|
15
|
-
"message",
|
|
16
|
-
"edited_message",
|
|
17
|
-
"channel_post",
|
|
18
|
-
"edited_channel_post",
|
|
19
|
-
"inline_query",
|
|
20
|
-
"chosen_inline_result",
|
|
21
|
-
"callback_query",
|
|
22
|
-
"shipping_query",
|
|
23
|
-
"pre_checkout_query",
|
|
24
|
-
"poll",
|
|
25
|
-
"poll_answer",
|
|
26
|
-
"my_chat_member",
|
|
27
|
-
"chat_member",
|
|
28
|
-
]
|
|
12
|
+
from telegrinder.msgspec_utils import decoder
|
|
13
|
+
from telegrinder.types import Update, UpdateType
|
|
29
14
|
|
|
30
15
|
|
|
31
16
|
class Polling(ABCPolling):
|
|
32
17
|
def __init__(
|
|
33
18
|
self,
|
|
34
|
-
api:
|
|
35
|
-
|
|
19
|
+
api: ABCAPI,
|
|
20
|
+
*,
|
|
21
|
+
offset: int = 0,
|
|
22
|
+
reconnection_timeout: float = 5,
|
|
23
|
+
max_reconnetions: int = 10,
|
|
24
|
+
include_updates: set[str | UpdateType] | None = None,
|
|
25
|
+
exclude_updates: set[str | UpdateType] | None = None,
|
|
36
26
|
):
|
|
37
27
|
self.api = api
|
|
38
|
-
self.
|
|
28
|
+
self.allowed_updates = self.get_allowed_updates(
|
|
29
|
+
include_updates=include_updates,
|
|
30
|
+
exclude_updates=exclude_updates,
|
|
31
|
+
)
|
|
32
|
+
self.reconnection_timeout = 5 if reconnection_timeout < 0 else reconnection_timeout
|
|
33
|
+
self.max_reconnetions = 10 if max_reconnetions < 0 else max_reconnetions
|
|
34
|
+
self.offset = offset
|
|
39
35
|
self._stop = False
|
|
40
|
-
self.allowed_updates = ALLOWED_UPDATES
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
def get_allowed_updates(
|
|
38
|
+
self,
|
|
39
|
+
*,
|
|
40
|
+
include_updates: set[str | UpdateType] | None = None,
|
|
41
|
+
exclude_updates: set[str | UpdateType] | None = None,
|
|
42
|
+
) -> list[str]:
|
|
43
|
+
allowed_updates: list[str] = list(x.value for x in UpdateType)
|
|
44
|
+
if not include_updates and not exclude_updates:
|
|
45
|
+
return allowed_updates
|
|
46
|
+
|
|
47
|
+
if include_updates and exclude_updates:
|
|
48
|
+
allowed_updates = [
|
|
49
|
+
x
|
|
50
|
+
for x in allowed_updates
|
|
51
|
+
if x in include_updates and x not in exclude_updates
|
|
52
|
+
]
|
|
53
|
+
elif exclude_updates:
|
|
54
|
+
allowed_updates = [x for x in allowed_updates if x not in exclude_updates]
|
|
55
|
+
elif include_updates:
|
|
56
|
+
allowed_updates = [x for x in allowed_updates if x in include_updates]
|
|
57
|
+
|
|
58
|
+
return [x.value if isinstance(x, UpdateType) else x for x in allowed_updates]
|
|
59
|
+
|
|
60
|
+
async def get_updates(self) -> msgspec.Raw | None:
|
|
43
61
|
raw_updates = await self.api.request_raw(
|
|
44
62
|
"getUpdates",
|
|
45
63
|
{"offset": self.offset, "allowed_updates": self.allowed_updates},
|
|
46
64
|
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
match raw_updates:
|
|
66
|
+
case Ok(value):
|
|
67
|
+
return value
|
|
68
|
+
case Error(err) if err.code in (401, 404):
|
|
69
|
+
raise InvalidTokenError("Token seems to be invalid")
|
|
51
70
|
|
|
52
|
-
async def listen(self) -> typing.
|
|
53
|
-
logger.debug("
|
|
71
|
+
async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
|
|
72
|
+
logger.debug("Listening polling")
|
|
73
|
+
reconn_counter = 0
|
|
74
|
+
|
|
54
75
|
while not self._stop:
|
|
55
76
|
try:
|
|
56
77
|
updates = await self.get_updates()
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
reconn_counter = 0
|
|
79
|
+
if not updates:
|
|
80
|
+
continue
|
|
81
|
+
updates_list: list[Update] = decoder.decode(
|
|
82
|
+
updates, type=list[Update]
|
|
59
83
|
)
|
|
60
84
|
if updates_list:
|
|
61
85
|
yield updates_list
|
|
62
86
|
self.offset = updates_list[-1].update_id + 1
|
|
87
|
+
except InvalidTokenError as e:
|
|
88
|
+
logger.error(e)
|
|
89
|
+
self.stop()
|
|
90
|
+
exit(6)
|
|
63
91
|
except asyncio.CancelledError:
|
|
64
|
-
logger.info("
|
|
65
|
-
self.
|
|
92
|
+
logger.info("Caught cancel, polling stopping...")
|
|
93
|
+
self.stop()
|
|
94
|
+
except (aiohttp.client.ServerConnectionError, TimeoutError):
|
|
95
|
+
if reconn_counter > self.max_reconnetions:
|
|
96
|
+
logger.error(
|
|
97
|
+
"Failed to reconnect to the server after {} attempts, polling stopping.",
|
|
98
|
+
self.max_reconnetions,
|
|
99
|
+
)
|
|
100
|
+
self.stop()
|
|
101
|
+
exit(9)
|
|
102
|
+
else:
|
|
103
|
+
logger.warning("Server disconnected, waiting 5 seconds to reconnetion...")
|
|
104
|
+
reconn_counter += 1
|
|
105
|
+
await asyncio.sleep(self.reconnection_timeout)
|
|
106
|
+
except aiohttp.ClientConnectorError:
|
|
107
|
+
logger.error(
|
|
108
|
+
"Client connection failed, polling stopping! "
|
|
109
|
+
"Please, check your internet connection."
|
|
110
|
+
)
|
|
111
|
+
self.stop()
|
|
112
|
+
exit(3)
|
|
66
113
|
except BaseException as e:
|
|
67
|
-
|
|
68
|
-
logger.error(e)
|
|
114
|
+
logger.exception(e)
|
|
69
115
|
|
|
70
116
|
def stop(self) -> None:
|
|
71
117
|
self._stop = True
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
__all__ = ("Polling",)
|
|
@@ -1,15 +1,101 @@
|
|
|
1
|
-
from .abc import ABCRule,
|
|
1
|
+
from .abc import ABCRule, AndRule, MessageRule, OrRule
|
|
2
2
|
from .callback_data import (
|
|
3
3
|
CallbackDataEq,
|
|
4
4
|
CallbackDataJsonEq,
|
|
5
5
|
CallbackDataJsonModel,
|
|
6
|
+
CallbackDataMap,
|
|
6
7
|
CallbackDataMarkup,
|
|
8
|
+
CallbackQueryDataRule,
|
|
9
|
+
CallbackQueryRule,
|
|
10
|
+
HasData,
|
|
7
11
|
)
|
|
12
|
+
from .command import Argument, Command
|
|
13
|
+
from .enum_text import EnumTextRule
|
|
8
14
|
from .func import FuncRule
|
|
9
|
-
from .is_from import IsPrivate, IsChat
|
|
10
|
-
from .markup import Markup
|
|
11
|
-
from .regex import Regex
|
|
12
|
-
from .text import Text, HasText, ABCTextMessageRule
|
|
13
15
|
from .fuzzy import FuzzyText
|
|
16
|
+
from .inline import (
|
|
17
|
+
HasLocation,
|
|
18
|
+
InlineQueryChatType,
|
|
19
|
+
InlineQueryMarkup,
|
|
20
|
+
InlineQueryRule,
|
|
21
|
+
InlineQueryText,
|
|
22
|
+
)
|
|
14
23
|
from .integer import Integer, IntegerInRange
|
|
24
|
+
from .is_from import (
|
|
25
|
+
IsBasketballDice,
|
|
26
|
+
IsBot,
|
|
27
|
+
IsBowlingDice,
|
|
28
|
+
IsChat,
|
|
29
|
+
IsChatId,
|
|
30
|
+
IsDartDice,
|
|
31
|
+
IsDice,
|
|
32
|
+
IsForum,
|
|
33
|
+
IsGroup,
|
|
34
|
+
IsLanguageCode,
|
|
35
|
+
IsPremium,
|
|
36
|
+
IsPrivate,
|
|
37
|
+
IsReply,
|
|
38
|
+
IsSuperGroup,
|
|
39
|
+
IsUser,
|
|
40
|
+
IsUserId,
|
|
41
|
+
)
|
|
42
|
+
from .markup import Markup
|
|
43
|
+
from .mention import HasMention
|
|
44
|
+
from .message_entities import HasEntities, MessageEntities
|
|
45
|
+
from .regex import Regex
|
|
46
|
+
from .rule_enum import RuleEnum
|
|
15
47
|
from .start import StartCommand
|
|
48
|
+
from .text import HasText, Text, TextMessageRule
|
|
49
|
+
|
|
50
|
+
__all__ = (
|
|
51
|
+
"ABCRule",
|
|
52
|
+
"AndRule",
|
|
53
|
+
"Argument",
|
|
54
|
+
"CallbackDataEq",
|
|
55
|
+
"CallbackDataJsonEq",
|
|
56
|
+
"CallbackDataJsonModel",
|
|
57
|
+
"CallbackDataMap",
|
|
58
|
+
"CallbackDataMarkup",
|
|
59
|
+
"CallbackQueryDataRule",
|
|
60
|
+
"CallbackQueryRule",
|
|
61
|
+
"Command",
|
|
62
|
+
"EnumTextRule",
|
|
63
|
+
"FuncRule",
|
|
64
|
+
"FuzzyText",
|
|
65
|
+
"HasData",
|
|
66
|
+
"HasEntities",
|
|
67
|
+
"HasMention",
|
|
68
|
+
"HasText",
|
|
69
|
+
"InlineQueryRule",
|
|
70
|
+
"InlineQueryText",
|
|
71
|
+
"Integer",
|
|
72
|
+
"IntegerInRange",
|
|
73
|
+
"IsBasketballDice",
|
|
74
|
+
"IsBot",
|
|
75
|
+
"IsBowlingDice",
|
|
76
|
+
"IsChat",
|
|
77
|
+
"IsChatId",
|
|
78
|
+
"IsDartDice",
|
|
79
|
+
"IsDice",
|
|
80
|
+
"IsForum",
|
|
81
|
+
"IsGroup",
|
|
82
|
+
"IsLanguageCode",
|
|
83
|
+
"IsPremium",
|
|
84
|
+
"IsPrivate",
|
|
85
|
+
"IsReply",
|
|
86
|
+
"IsSuperGroup",
|
|
87
|
+
"IsUser",
|
|
88
|
+
"IsUserId",
|
|
89
|
+
"HasLocation",
|
|
90
|
+
"InlineQueryChatType",
|
|
91
|
+
"InlineQueryMarkup",
|
|
92
|
+
"Markup",
|
|
93
|
+
"MessageEntities",
|
|
94
|
+
"MessageRule",
|
|
95
|
+
"OrRule",
|
|
96
|
+
"Regex",
|
|
97
|
+
"RuleEnum",
|
|
98
|
+
"StartCommand",
|
|
99
|
+
"Text",
|
|
100
|
+
"TextMessageRule",
|
|
101
|
+
)
|