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.

Files changed (132) hide show
  1. telegrinder/__init__.py +129 -22
  2. telegrinder/api/__init__.py +11 -2
  3. telegrinder/api/abc.py +25 -9
  4. telegrinder/api/api.py +47 -28
  5. telegrinder/api/error.py +14 -4
  6. telegrinder/api/response.py +11 -7
  7. telegrinder/bot/__init__.py +68 -7
  8. telegrinder/bot/bot.py +30 -24
  9. telegrinder/bot/cute_types/__init__.py +11 -1
  10. telegrinder/bot/cute_types/base.py +138 -0
  11. telegrinder/bot/cute_types/callback_query.py +458 -15
  12. telegrinder/bot/cute_types/inline_query.py +30 -24
  13. telegrinder/bot/cute_types/message.py +2982 -78
  14. telegrinder/bot/cute_types/update.py +30 -0
  15. telegrinder/bot/cute_types/utils.py +794 -0
  16. telegrinder/bot/dispatch/__init__.py +56 -3
  17. telegrinder/bot/dispatch/abc.py +9 -7
  18. telegrinder/bot/dispatch/composition.py +74 -0
  19. telegrinder/bot/dispatch/context.py +71 -0
  20. telegrinder/bot/dispatch/dispatch.py +86 -49
  21. telegrinder/bot/dispatch/handler/__init__.py +3 -0
  22. telegrinder/bot/dispatch/handler/abc.py +11 -5
  23. telegrinder/bot/dispatch/handler/func.py +41 -32
  24. telegrinder/bot/dispatch/handler/message_reply.py +46 -0
  25. telegrinder/bot/dispatch/middleware/__init__.py +2 -0
  26. telegrinder/bot/dispatch/middleware/abc.py +10 -4
  27. telegrinder/bot/dispatch/process.py +53 -49
  28. telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
  29. telegrinder/bot/dispatch/return_manager/abc.py +95 -0
  30. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  31. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  32. telegrinder/bot/dispatch/return_manager/message.py +25 -0
  33. telegrinder/bot/dispatch/view/__init__.py +14 -2
  34. telegrinder/bot/dispatch/view/abc.py +128 -2
  35. telegrinder/bot/dispatch/view/box.py +38 -0
  36. telegrinder/bot/dispatch/view/callback_query.py +13 -39
  37. telegrinder/bot/dispatch/view/inline_query.py +11 -39
  38. telegrinder/bot/dispatch/view/message.py +11 -47
  39. telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
  40. telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
  41. telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
  42. telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
  43. telegrinder/bot/polling/__init__.py +2 -0
  44. telegrinder/bot/polling/abc.py +11 -4
  45. telegrinder/bot/polling/polling.py +89 -40
  46. telegrinder/bot/rules/__init__.py +91 -5
  47. telegrinder/bot/rules/abc.py +81 -63
  48. telegrinder/bot/rules/adapter/__init__.py +11 -0
  49. telegrinder/bot/rules/adapter/abc.py +21 -0
  50. telegrinder/bot/rules/adapter/errors.py +5 -0
  51. telegrinder/bot/rules/adapter/event.py +49 -0
  52. telegrinder/bot/rules/adapter/raw_update.py +24 -0
  53. telegrinder/bot/rules/callback_data.py +159 -38
  54. telegrinder/bot/rules/command.py +116 -0
  55. telegrinder/bot/rules/enum_text.py +28 -0
  56. telegrinder/bot/rules/func.py +17 -17
  57. telegrinder/bot/rules/fuzzy.py +13 -10
  58. telegrinder/bot/rules/inline.py +61 -0
  59. telegrinder/bot/rules/integer.py +12 -7
  60. telegrinder/bot/rules/is_from.py +148 -7
  61. telegrinder/bot/rules/markup.py +21 -18
  62. telegrinder/bot/rules/mention.py +17 -0
  63. telegrinder/bot/rules/message_entities.py +33 -0
  64. telegrinder/bot/rules/regex.py +27 -19
  65. telegrinder/bot/rules/rule_enum.py +74 -0
  66. telegrinder/bot/rules/start.py +25 -13
  67. telegrinder/bot/rules/text.py +23 -14
  68. telegrinder/bot/scenario/__init__.py +2 -0
  69. telegrinder/bot/scenario/abc.py +12 -5
  70. telegrinder/bot/scenario/checkbox.py +48 -30
  71. telegrinder/bot/scenario/choice.py +16 -10
  72. telegrinder/client/__init__.py +3 -1
  73. telegrinder/client/abc.py +26 -16
  74. telegrinder/client/aiohttp.py +54 -32
  75. telegrinder/model.py +119 -40
  76. telegrinder/modules.py +189 -21
  77. telegrinder/msgspec_json.py +14 -0
  78. telegrinder/msgspec_utils.py +227 -0
  79. telegrinder/node/__init__.py +31 -0
  80. telegrinder/node/attachment.py +71 -0
  81. telegrinder/node/base.py +93 -0
  82. telegrinder/node/composer.py +71 -0
  83. telegrinder/node/container.py +22 -0
  84. telegrinder/node/message.py +18 -0
  85. telegrinder/node/rule.py +56 -0
  86. telegrinder/node/source.py +31 -0
  87. telegrinder/node/text.py +13 -0
  88. telegrinder/node/tools/__init__.py +3 -0
  89. telegrinder/node/tools/generator.py +40 -0
  90. telegrinder/node/update.py +12 -0
  91. telegrinder/rules.py +1 -1
  92. telegrinder/tools/__init__.py +138 -4
  93. telegrinder/tools/buttons.py +89 -51
  94. telegrinder/tools/error_handler/__init__.py +8 -0
  95. telegrinder/tools/error_handler/abc.py +30 -0
  96. telegrinder/tools/error_handler/error_handler.py +156 -0
  97. telegrinder/tools/formatting/__init__.py +81 -3
  98. telegrinder/tools/formatting/html.py +283 -37
  99. telegrinder/tools/formatting/links.py +32 -0
  100. telegrinder/tools/formatting/spec_html_formats.py +121 -0
  101. telegrinder/tools/global_context/__init__.py +12 -0
  102. telegrinder/tools/global_context/abc.py +66 -0
  103. telegrinder/tools/global_context/global_context.py +451 -0
  104. telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
  105. telegrinder/tools/i18n/__init__.py +12 -0
  106. telegrinder/tools/i18n/base.py +31 -0
  107. telegrinder/tools/i18n/middleware/__init__.py +3 -0
  108. telegrinder/tools/i18n/middleware/base.py +26 -0
  109. telegrinder/tools/i18n/simple.py +48 -0
  110. telegrinder/tools/kb_set/__init__.py +2 -0
  111. telegrinder/tools/kb_set/base.py +3 -0
  112. telegrinder/tools/kb_set/yaml.py +28 -17
  113. telegrinder/tools/keyboard.py +84 -62
  114. telegrinder/tools/loop_wrapper/__init__.py +4 -0
  115. telegrinder/tools/loop_wrapper/abc.py +18 -0
  116. telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
  117. telegrinder/tools/magic.py +48 -23
  118. telegrinder/tools/parse_mode.py +1 -2
  119. telegrinder/types/__init__.py +1 -0
  120. telegrinder/types/enums.py +653 -0
  121. telegrinder/types/methods.py +4107 -1279
  122. telegrinder/types/objects.py +4771 -1745
  123. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
  124. telegrinder-0.1.dev159.dist-info/METADATA +109 -0
  125. telegrinder-0.1.dev159.dist-info/RECORD +126 -0
  126. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
  127. telegrinder/bot/dispatch/waiter.py +0 -38
  128. telegrinder/result.py +0 -38
  129. telegrinder/tools/formatting/abc.py +0 -52
  130. telegrinder/tools/formatting/markdown.py +0 -57
  131. telegrinder-0.1.dev20.dist-info/METADATA +0 -22
  132. 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.api.abc import ABCAPI
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
- def __call__(self, *rules: ABCRule, is_blocking: bool = True):
22
- def wrapper(func: typing.Callable[..., typing.Coroutine]):
23
- self.handlers.append(
24
- FuncHandler(func, [*self.auto_rules, *rules], is_blocking, dataclass=None)
25
- )
26
- return func
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
- return wrapper
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
- async def check(self, event: Update) -> bool:
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,9 @@
1
+ from .machine import WaiterMachine
2
+ from .middleware import WaiterMiddleware
3
+ from .short_state import ShortState
4
+
5
+ __all__ = (
6
+ "ShortState",
7
+ "WaiterMachine",
8
+ "WaiterMiddleware",
9
+ )
@@ -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",)
@@ -1,2 +1,4 @@
1
1
  from .abc import ABCPolling
2
2
  from .polling import Polling
3
+
4
+ __all__ = ("ABCPolling", "Polling")
@@ -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) -> typing.List[Raw]:
13
+ async def get_updates(self) -> list[msgspec.Raw]:
10
14
  pass
11
15
 
12
16
  @abstractmethod
13
- async def listen(self) -> typing.AsyncIterator[typing.List[Update]]:
14
- pass
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 msgspec.json
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 typing
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.model import Raw
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: typing.Optional[ABCAPI] = None,
35
- offset: typing.Optional[int] = None,
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.offset = offset or 0
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
- async def get_updates(self) -> typing.Optional[Raw]:
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
- if not raw_updates.is_ok and raw_updates.error.code == 404:
48
- logger.fatal("Token seems to be invalid")
49
- exit(6)
50
- return raw_updates.unwrap()
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.AsyncIterator[typing.List[Update]]:
53
- logger.debug("listening polling")
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
- updates_list: typing.List[Update] = msgspec.json.decode(
58
- updates, type=typing.List[Update]
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("caught cancel, stopping")
65
- self._stop = True
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
- traceback.print_exc()
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, ABCMessageRule, AndRule, OrRule
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
+ )