telegrinder 0.3.4__py3-none-any.whl → 0.4.0__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 (192) hide show
  1. telegrinder/__init__.py +148 -149
  2. telegrinder/api/__init__.py +9 -8
  3. telegrinder/api/api.py +101 -93
  4. telegrinder/api/error.py +20 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +72 -66
  8. telegrinder/bot/bot.py +83 -76
  9. telegrinder/bot/cute_types/__init__.py +19 -17
  10. telegrinder/bot/cute_types/base.py +184 -258
  11. telegrinder/bot/cute_types/callback_query.py +400 -385
  12. telegrinder/bot/cute_types/chat_join_request.py +62 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +157 -160
  14. telegrinder/bot/cute_types/inline_query.py +44 -43
  15. telegrinder/bot/cute_types/message.py +2590 -2637
  16. telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
  17. telegrinder/bot/cute_types/update.py +112 -104
  18. telegrinder/bot/cute_types/utils.py +62 -95
  19. telegrinder/bot/dispatch/__init__.py +59 -55
  20. telegrinder/bot/dispatch/abc.py +76 -77
  21. telegrinder/bot/dispatch/context.py +96 -98
  22. telegrinder/bot/dispatch/dispatch.py +254 -202
  23. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  24. telegrinder/bot/dispatch/handler/abc.py +23 -24
  25. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  26. telegrinder/bot/dispatch/handler/base.py +57 -57
  27. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  28. telegrinder/bot/dispatch/handler/func.py +129 -135
  29. telegrinder/bot/dispatch/handler/media_group_reply.py +44 -43
  30. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  31. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  32. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  33. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  34. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  35. telegrinder/bot/dispatch/middleware/abc.py +97 -22
  36. telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
  37. telegrinder/bot/dispatch/process.py +151 -157
  38. telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
  39. telegrinder/bot/dispatch/return_manager/abc.py +104 -108
  40. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  41. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  42. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  43. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
  44. telegrinder/bot/dispatch/view/__init__.py +15 -13
  45. telegrinder/bot/dispatch/view/abc.py +45 -41
  46. telegrinder/bot/dispatch/view/base.py +231 -200
  47. telegrinder/bot/dispatch/view/box.py +140 -129
  48. telegrinder/bot/dispatch/view/callback_query.py +16 -17
  49. telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
  50. telegrinder/bot/dispatch/view/chat_member.py +37 -39
  51. telegrinder/bot/dispatch/view/inline_query.py +16 -17
  52. telegrinder/bot/dispatch/view/message.py +43 -44
  53. telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
  54. telegrinder/bot/dispatch/view/raw.py +116 -114
  55. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  56. telegrinder/bot/dispatch/waiter_machine/actions.py +14 -13
  57. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  58. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
  59. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +59 -57
  60. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
  61. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
  62. telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
  63. telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
  64. telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
  65. telegrinder/bot/polling/__init__.py +4 -4
  66. telegrinder/bot/polling/abc.py +25 -25
  67. telegrinder/bot/polling/polling.py +139 -131
  68. telegrinder/bot/rules/__init__.py +85 -62
  69. telegrinder/bot/rules/abc.py +213 -206
  70. telegrinder/bot/rules/callback_data.py +122 -163
  71. telegrinder/bot/rules/chat_join.py +45 -43
  72. telegrinder/bot/rules/command.py +126 -126
  73. telegrinder/bot/rules/enum_text.py +33 -36
  74. telegrinder/bot/rules/func.py +28 -26
  75. telegrinder/bot/rules/fuzzy.py +24 -24
  76. telegrinder/bot/rules/id.py +24 -0
  77. telegrinder/bot/rules/inline.py +58 -56
  78. telegrinder/bot/rules/integer.py +21 -20
  79. telegrinder/bot/rules/is_from.py +127 -127
  80. telegrinder/bot/rules/logic.py +18 -0
  81. telegrinder/bot/rules/markup.py +42 -43
  82. telegrinder/bot/rules/mention.py +14 -14
  83. telegrinder/bot/rules/message.py +15 -17
  84. telegrinder/bot/rules/message_entities.py +33 -35
  85. telegrinder/bot/rules/node.py +33 -27
  86. telegrinder/bot/rules/payload.py +81 -0
  87. telegrinder/bot/rules/payment_invoice.py +29 -0
  88. telegrinder/bot/rules/regex.py +36 -37
  89. telegrinder/bot/rules/rule_enum.py +72 -72
  90. telegrinder/bot/rules/start.py +42 -42
  91. telegrinder/bot/rules/state.py +35 -37
  92. telegrinder/bot/rules/text.py +38 -33
  93. telegrinder/bot/rules/update.py +15 -15
  94. telegrinder/bot/scenario/__init__.py +5 -5
  95. telegrinder/bot/scenario/abc.py +17 -19
  96. telegrinder/bot/scenario/checkbox.py +174 -176
  97. telegrinder/bot/scenario/choice.py +48 -51
  98. telegrinder/client/__init__.py +12 -4
  99. telegrinder/client/abc.py +100 -75
  100. telegrinder/client/aiohttp.py +134 -130
  101. telegrinder/client/form_data.py +31 -0
  102. telegrinder/client/sonic.py +212 -0
  103. telegrinder/model.py +208 -315
  104. telegrinder/modules.py +239 -237
  105. telegrinder/msgspec_json.py +14 -14
  106. telegrinder/msgspec_utils.py +478 -410
  107. telegrinder/node/__init__.py +86 -25
  108. telegrinder/node/attachment.py +163 -87
  109. telegrinder/node/base.py +288 -160
  110. telegrinder/node/callback_query.py +54 -53
  111. telegrinder/node/command.py +34 -33
  112. telegrinder/node/composer.py +163 -198
  113. telegrinder/node/container.py +33 -27
  114. telegrinder/node/either.py +82 -0
  115. telegrinder/node/event.py +54 -65
  116. telegrinder/node/file.py +51 -0
  117. telegrinder/node/me.py +15 -16
  118. telegrinder/node/payload.py +78 -0
  119. telegrinder/node/polymorphic.py +67 -48
  120. telegrinder/node/rule.py +72 -76
  121. telegrinder/node/scope.py +36 -38
  122. telegrinder/node/source.py +87 -71
  123. telegrinder/node/text.py +53 -41
  124. telegrinder/node/tools/__init__.py +3 -3
  125. telegrinder/node/tools/generator.py +36 -40
  126. telegrinder/py.typed +0 -0
  127. telegrinder/rules.py +1 -62
  128. telegrinder/tools/__init__.py +152 -93
  129. telegrinder/tools/adapter/__init__.py +19 -0
  130. telegrinder/tools/adapter/abc.py +49 -0
  131. telegrinder/tools/adapter/dataclass.py +56 -0
  132. telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
  133. telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
  134. telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
  135. telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
  136. telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
  137. telegrinder/tools/buttons.py +106 -80
  138. telegrinder/tools/callback_data_serilization/__init__.py +5 -0
  139. telegrinder/tools/callback_data_serilization/abc.py +51 -0
  140. telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
  141. telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
  142. telegrinder/tools/error_handler/__init__.py +7 -7
  143. telegrinder/tools/error_handler/abc.py +30 -33
  144. telegrinder/tools/error_handler/error.py +9 -9
  145. telegrinder/tools/error_handler/error_handler.py +179 -193
  146. telegrinder/tools/formatting/__init__.py +83 -63
  147. telegrinder/tools/formatting/deep_links.py +541 -0
  148. telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
  149. telegrinder/tools/formatting/spec_html_formats.py +71 -117
  150. telegrinder/tools/functional.py +8 -12
  151. telegrinder/tools/global_context/__init__.py +7 -7
  152. telegrinder/tools/global_context/abc.py +63 -63
  153. telegrinder/tools/global_context/global_context.py +387 -412
  154. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  155. telegrinder/tools/i18n/__init__.py +7 -7
  156. telegrinder/tools/i18n/abc.py +30 -30
  157. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  158. telegrinder/tools/i18n/middleware/abc.py +22 -25
  159. telegrinder/tools/i18n/simple.py +43 -43
  160. telegrinder/tools/input_file_directory.py +30 -0
  161. telegrinder/tools/keyboard.py +128 -128
  162. telegrinder/tools/lifespan.py +105 -0
  163. telegrinder/tools/limited_dict.py +32 -37
  164. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  165. telegrinder/tools/loop_wrapper/abc.py +20 -15
  166. telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
  167. telegrinder/tools/magic.py +307 -157
  168. telegrinder/tools/parse_mode.py +6 -6
  169. telegrinder/tools/state_storage/__init__.py +4 -4
  170. telegrinder/tools/state_storage/abc.py +31 -35
  171. telegrinder/tools/state_storage/memory.py +25 -25
  172. telegrinder/tools/strings.py +13 -0
  173. telegrinder/types/__init__.py +268 -260
  174. telegrinder/types/enums.py +711 -701
  175. telegrinder/types/input_file.py +51 -0
  176. telegrinder/types/methods.py +5055 -4633
  177. telegrinder/types/objects.py +7058 -6950
  178. telegrinder/verification_utils.py +30 -32
  179. {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
  180. telegrinder-0.4.0.dist-info/METADATA +144 -0
  181. telegrinder-0.4.0.dist-info/RECORD +182 -0
  182. {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
  183. telegrinder/bot/rules/adapter/__init__.py +0 -17
  184. telegrinder/bot/rules/adapter/abc.py +0 -31
  185. telegrinder/node/message.py +0 -14
  186. telegrinder/node/update.py +0 -15
  187. telegrinder/tools/formatting/links.py +0 -38
  188. telegrinder/tools/kb_set/__init__.py +0 -4
  189. telegrinder/tools/kb_set/base.py +0 -15
  190. telegrinder/tools/kb_set/yaml.py +0 -63
  191. telegrinder-0.3.4.dist-info/METADATA +0 -110
  192. telegrinder-0.3.4.dist-info/RECORD +0 -165
@@ -1,68 +1,57 @@
1
- import asyncio
2
- import dataclasses
3
- import datetime
4
- import typing
5
- from contextlib import suppress
6
-
7
- from telegrinder.bot.cute_types import BaseCute
8
- from telegrinder.bot.dispatch.context import Context
9
- from telegrinder.bot.dispatch.handler.abc import ABCHandler
10
- from telegrinder.bot.rules.abc import ABCRule
11
- from telegrinder.model import Model
12
-
13
- if typing.TYPE_CHECKING:
14
- from .actions import WaiterActions
15
-
16
-
17
- T = typing.TypeVar("T", bound=Model)
18
- EventModel = typing.TypeVar("EventModel", bound=BaseCute)
19
-
20
- Behaviour: typing.TypeAlias = ABCHandler[T] | None
21
-
22
-
23
- class ShortStateContext(typing.Generic[EventModel], typing.NamedTuple):
24
- event: EventModel
25
- context: Context
26
-
27
-
28
- @dataclasses.dataclass(slots=True)
29
- class ShortState(typing.Generic[EventModel]):
30
- event: asyncio.Event
31
- actions: "WaiterActions[EventModel]"
32
-
33
- release: ABCRule | None = dataclasses.field(
34
- default=None,
35
- kw_only=True,
36
- )
37
- filter: ABCRule | None = dataclasses.field(
38
- default=None,
39
- kw_only=True,
40
- )
41
-
42
- lifetime: dataclasses.InitVar[datetime.timedelta | None] = dataclasses.field(
43
- default=None,
44
- kw_only=True,
45
- )
46
-
47
- expiration_date: datetime.datetime | None = dataclasses.field(init=False, kw_only=True)
48
- creation_date: datetime.datetime = dataclasses.field(init=False)
49
- context: ShortStateContext[EventModel] | None = dataclasses.field(default=None, init=False, kw_only=True)
50
-
51
- def __post_init__(self, expiration: datetime.timedelta | None = None) -> None:
52
- self.creation_date = datetime.datetime.now()
53
- self.expiration_date = (self.creation_date + expiration) if expiration is not None else None
54
-
55
- async def cancel(self) -> None:
56
- """Cancel schedule waiters."""
57
-
58
- waiters = typing.cast(
59
- typing.Iterable[asyncio.Future[typing.Any]],
60
- self.event._waiters, # type: ignore
61
- )
62
- for future in waiters:
63
- future.cancel()
64
- with suppress(asyncio.CancelledError):
65
- await future
66
-
67
-
68
- __all__ = ("ShortState", "ShortStateContext")
1
+ import asyncio
2
+ import dataclasses
3
+ import datetime
4
+ import typing
5
+
6
+ from telegrinder.bot.cute_types import BaseCute
7
+ from telegrinder.bot.dispatch.context import Context
8
+ from telegrinder.bot.rules.abc import ABCRule
9
+ from telegrinder.tools.magic import cancel_future
10
+
11
+ if typing.TYPE_CHECKING:
12
+ from .actions import WaiterActions
13
+
14
+
15
+ class ShortStateContext[Event: BaseCute](typing.NamedTuple):
16
+ event: Event
17
+ context: Context
18
+
19
+
20
+ @dataclasses.dataclass(slots=True)
21
+ class ShortState[Event: BaseCute]:
22
+ event: asyncio.Event
23
+ actions: "WaiterActions[Event]"
24
+
25
+ release: ABCRule | None = dataclasses.field(
26
+ default=None,
27
+ kw_only=True,
28
+ )
29
+ filter: ABCRule | None = dataclasses.field(
30
+ default=None,
31
+ kw_only=True,
32
+ )
33
+
34
+ lifetime: dataclasses.InitVar[datetime.timedelta | None] = dataclasses.field(
35
+ default=None,
36
+ kw_only=True,
37
+ )
38
+
39
+ expiration_date: datetime.datetime | None = dataclasses.field(init=False, kw_only=True)
40
+ creation_date: datetime.datetime = dataclasses.field(init=False)
41
+ context: ShortStateContext[Event] | None = dataclasses.field(default=None, init=False, kw_only=True)
42
+
43
+ def __post_init__(self, expiration: datetime.timedelta | None = None) -> None:
44
+ self.creation_date = datetime.datetime.now()
45
+ self.expiration_date = (self.creation_date + expiration) if expiration is not None else None
46
+
47
+ async def cancel(self) -> None:
48
+ """Cancel schedule waiters."""
49
+ waiters = typing.cast(
50
+ typing.Iterable[asyncio.Future[typing.Any]],
51
+ self.event._waiters, # type: ignore
52
+ )
53
+ for future in waiters:
54
+ await cancel_future(future)
55
+
56
+
57
+ __all__ = ("ShortState", "ShortStateContext")
@@ -1,4 +1,4 @@
1
- from .abc import ABCPolling
2
- from .polling import Polling
3
-
4
- __all__ = ("ABCPolling", "Polling")
1
+ from .abc import ABCPolling
2
+ from .polling import Polling
3
+
4
+ __all__ = ("ABCPolling", "Polling")
@@ -1,25 +1,25 @@
1
- import typing
2
- from abc import ABC, abstractmethod
3
-
4
- import msgspec
5
-
6
- from telegrinder.types.objects import Update
7
-
8
-
9
- class ABCPolling(ABC):
10
- offset: int
11
-
12
- @abstractmethod
13
- async def get_updates(self) -> list[msgspec.Raw]:
14
- pass
15
-
16
- @abstractmethod
17
- async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
18
- yield []
19
-
20
- @abstractmethod
21
- def stop(self) -> None:
22
- pass
23
-
24
-
25
- __all__ = ("ABCPolling",)
1
+ import typing
2
+ from abc import ABC, abstractmethod
3
+
4
+ import msgspec
5
+
6
+ from telegrinder.types.objects import Update
7
+
8
+
9
+ class ABCPolling(ABC):
10
+ offset: int
11
+
12
+ @abstractmethod
13
+ async def get_updates(self) -> list[msgspec.Raw]:
14
+ pass
15
+
16
+ @abstractmethod
17
+ async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
18
+ yield []
19
+
20
+ @abstractmethod
21
+ def stop(self) -> None:
22
+ pass
23
+
24
+
25
+ __all__ = ("ABCPolling",)
@@ -1,131 +1,139 @@
1
- import asyncio
2
- import typing
3
-
4
- import aiohttp
5
- import msgspec
6
- from fntypes.result import Error, Ok
7
-
8
- from telegrinder.api.api import API
9
- from telegrinder.api.error import InvalidTokenError
10
- from telegrinder.bot.polling.abc import ABCPolling
11
- from telegrinder.modules import logger
12
- from telegrinder.msgspec_utils import decoder
13
- from telegrinder.types.objects import Update, UpdateType
14
-
15
-
16
- class Polling(ABCPolling):
17
- def __init__(
18
- self,
19
- api: API,
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,
26
- ) -> None:
27
- self.api = api
28
- self.allowed_updates = self.get_allowed_updates(
29
- include_updates=include_updates,
30
- exclude_updates=exclude_updates,
31
- )
32
- self.reconnection_timeout = 5.0 if reconnection_timeout < 0 else reconnection_timeout
33
- self.max_reconnetions = 10 if max_reconnetions < 0 else max_reconnetions
34
- self.offset = offset
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
- )
50
-
51
- @staticmethod
52
- def get_allowed_updates(
53
- *,
54
- include_updates: set[str | UpdateType] | None = None,
55
- exclude_updates: set[str | UpdateType] | None = None,
56
- ) -> list[str]:
57
- allowed_updates: list[str] = list(x.value for x in UpdateType)
58
- if not include_updates and not exclude_updates:
59
- return allowed_updates
60
-
61
- if include_updates and exclude_updates:
62
- allowed_updates = [
63
- x for x in allowed_updates if x in include_updates and x not in exclude_updates
64
- ]
65
- elif exclude_updates:
66
- allowed_updates = [x for x in allowed_updates if x not in exclude_updates]
67
- elif include_updates:
68
- allowed_updates = [x for x in allowed_updates if x in include_updates]
69
-
70
- return [x.value if isinstance(x, UpdateType) else x for x in allowed_updates]
71
-
72
- async def get_updates(self) -> msgspec.Raw | None:
73
- raw_updates = await self.api.request_raw(
74
- "getUpdates",
75
- {
76
- "offset": self.offset,
77
- "allowed_updates": self.allowed_updates,
78
- },
79
- )
80
- match raw_updates:
81
- case Ok(value):
82
- return value
83
- case Error(err) if err.code in (401, 404):
84
- raise InvalidTokenError("Token seems to be invalid")
85
-
86
- async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
87
- logger.debug("Listening polling")
88
- reconn_counter = 0
89
-
90
- while not self._stop:
91
- try:
92
- updates = await self.get_updates()
93
- reconn_counter = 0
94
- if not updates:
95
- continue
96
- updates_list: list[Update] = decoder.decode(updates, type=list[Update])
97
- if updates_list:
98
- yield updates_list
99
- self.offset = updates_list[-1].update_id + 1
100
- except InvalidTokenError as e:
101
- logger.error(e)
102
- self.stop()
103
- exit(3)
104
- except asyncio.CancelledError:
105
- logger.info("Caught cancel, polling stopping...")
106
- self.stop()
107
- except (aiohttp.client.ServerConnectionError, TimeoutError):
108
- if reconn_counter > self.max_reconnetions:
109
- logger.error(
110
- "Failed to reconnect to the server after {} attempts, polling stopping.",
111
- self.max_reconnetions,
112
- )
113
- self.stop()
114
- exit(6)
115
- else:
116
- logger.warning(
117
- "Server disconnected, waiting 5 seconds to reconnect...",
118
- )
119
- reconn_counter += 1
120
- await asyncio.sleep(self.reconnection_timeout)
121
- except (aiohttp.ClientConnectorError, aiohttp.ClientOSError):
122
- logger.error("Client connection failed, attempted to reconnect...")
123
- await asyncio.sleep(self.reconnection_timeout)
124
- except BaseException as e:
125
- logger.exception(e)
126
-
127
- def stop(self) -> None:
128
- self._stop = True
129
-
130
-
131
- __all__ = ("Polling",)
1
+ import asyncio
2
+ import sys
3
+ import typing
4
+ from http import HTTPStatus
5
+
6
+ import msgspec
7
+ from fntypes.result import Error, Ok
8
+
9
+ from telegrinder.api.api import API, HTTPClient
10
+ from telegrinder.api.error import APIServerError, InvalidTokenError
11
+ from telegrinder.bot.polling.abc import ABCPolling
12
+ from telegrinder.modules import logger
13
+ from telegrinder.msgspec_utils import decoder
14
+ from telegrinder.types.objects import Update, UpdateType
15
+
16
+
17
+ class Polling(ABCPolling, typing.Generic[HTTPClient]):
18
+ def __init__(
19
+ self,
20
+ api: API[HTTPClient],
21
+ *,
22
+ offset: int = 0,
23
+ reconnection_timeout: float = 5.0,
24
+ max_reconnetions: int = 15,
25
+ include_updates: set[str | UpdateType] | None = None,
26
+ exclude_updates: set[str | UpdateType] | None = None,
27
+ ) -> None:
28
+ self.api = api
29
+ self.allowed_updates = self.get_allowed_updates(
30
+ include_updates=include_updates,
31
+ exclude_updates=exclude_updates,
32
+ )
33
+ self.reconnection_timeout = 5.0 if reconnection_timeout < 0 else reconnection_timeout
34
+ self.max_reconnetions = 15 if max_reconnetions < 0 else max_reconnetions
35
+ self.offset = offset
36
+ self._stop = True
37
+
38
+ def __repr__(self) -> str:
39
+ return (
40
+ "<{}: with api={!r}, stopped={}, offset={}, allowed_updates={!r}, "
41
+ "max_reconnetions={}, reconnection_timeout={}>"
42
+ ).format(
43
+ self.__class__.__name__,
44
+ self.api,
45
+ self._stop,
46
+ self.offset,
47
+ self.allowed_updates,
48
+ self.max_reconnetions,
49
+ self.reconnection_timeout,
50
+ )
51
+
52
+ @staticmethod
53
+ def get_allowed_updates(
54
+ *,
55
+ include_updates: set[str | UpdateType] | None = None,
56
+ exclude_updates: set[str | UpdateType] | None = None,
57
+ ) -> list[str]:
58
+ allowed_updates: list[str] = list(x.value for x in UpdateType)
59
+ if not include_updates and not exclude_updates:
60
+ return allowed_updates
61
+
62
+ if include_updates and exclude_updates:
63
+ allowed_updates = [x for x in allowed_updates if x in include_updates and x not in exclude_updates]
64
+ elif exclude_updates:
65
+ allowed_updates = [x for x in allowed_updates if x not in exclude_updates]
66
+ elif include_updates:
67
+ allowed_updates = [x for x in allowed_updates if x in include_updates]
68
+
69
+ return [x.value if isinstance(x, UpdateType) else x for x in allowed_updates]
70
+
71
+ async def get_updates(self) -> msgspec.Raw:
72
+ raw_updates = await self.api.request_raw(
73
+ method="getUpdates",
74
+ data=dict(
75
+ offset=self.offset,
76
+ allowed_updates=self.allowed_updates,
77
+ ),
78
+ )
79
+
80
+ match raw_updates:
81
+ case Ok(value):
82
+ return value
83
+ case Error(err):
84
+ if err.code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.NOT_FOUND):
85
+ raise InvalidTokenError("Token seems to be invalid")
86
+ if err.code in (HTTPStatus.BAD_GATEWAY, HTTPStatus.GATEWAY_TIMEOUT):
87
+ raise APIServerError("Unavilability of the API Telegram server")
88
+ raise err from None
89
+
90
+ async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
91
+ logger.debug("Listening polling")
92
+ reconn_counter = 0
93
+ self._stop = False
94
+
95
+ with decoder(list[Update]) as dec: # For improve performance
96
+ while not self._stop:
97
+ try:
98
+ updates = await self.get_updates()
99
+ reconn_counter = 0
100
+ updates_list = dec.decode(updates)
101
+ if updates_list:
102
+ yield updates_list
103
+ self.offset = updates_list[-1].update_id + 1
104
+ except InvalidTokenError as e:
105
+ logger.error(e)
106
+ self.stop()
107
+ sys.exit(3)
108
+ except APIServerError as e:
109
+ logger.error(f"{e}, waiting {self.reconnection_timeout} seconds to the next request...")
110
+ await asyncio.sleep(self.reconnection_timeout)
111
+ except asyncio.CancelledError:
112
+ logger.info("Caught cancel, polling stopping...")
113
+ self.stop()
114
+ except self.api.http.CONNECTION_TIMEOUT_ERRORS:
115
+ if reconn_counter > self.max_reconnetions:
116
+ logger.error(
117
+ "Failed to reconnect to the server after {} attempts, polling stopping.",
118
+ self.max_reconnetions,
119
+ )
120
+ self.stop()
121
+ sys.exit(6)
122
+ else:
123
+ logger.warning(
124
+ "Server disconnected, waiting {} seconds to reconnect...",
125
+ self.reconnection_timeout,
126
+ )
127
+ reconn_counter += 1
128
+ await asyncio.sleep(self.reconnection_timeout)
129
+ except self.api.http.CLIENT_CONNECTION_ERRORS:
130
+ logger.error("Client connection failed, attempted to reconnect...")
131
+ await asyncio.sleep(self.reconnection_timeout)
132
+ except BaseException as e:
133
+ logger.exception("Traceback message below:")
134
+
135
+ def stop(self) -> None:
136
+ self._stop = True
137
+
138
+
139
+ __all__ = ("Polling",)
@@ -1,63 +1,76 @@
1
- from telegrinder.bot.rules.abc import ABCRule, AndRule, NotRule, OrRule
2
- from telegrinder.bot.rules.callback_data import (
3
- CallbackDataEq,
4
- CallbackDataJsonEq,
5
- CallbackDataJsonModel,
6
- CallbackDataMap,
7
- CallbackDataMarkup,
8
- CallbackQueryDataRule,
9
- CallbackQueryRule,
10
- HasData,
11
- )
12
- from telegrinder.bot.rules.chat_join import (
13
- ChatJoinRequestRule,
14
- HasInviteLink,
15
- InviteLinkByCreator,
16
- InviteLinkName,
17
- )
18
- from telegrinder.bot.rules.command import Argument, Command
19
- from telegrinder.bot.rules.enum_text import EnumTextRule
20
- from telegrinder.bot.rules.func import FuncRule
21
- from telegrinder.bot.rules.fuzzy import FuzzyText
22
- from telegrinder.bot.rules.inline import (
23
- HasLocation,
24
- InlineQueryChatType,
25
- InlineQueryMarkup,
26
- InlineQueryRule,
27
- InlineQueryText,
28
- )
29
- from telegrinder.bot.rules.integer import IntegerInRange, IsInteger
30
- from telegrinder.bot.rules.is_from import (
31
- IsBot,
32
- IsChat,
33
- IsChatId,
34
- IsDice,
35
- IsDiceEmoji,
36
- IsForum,
37
- IsForward,
38
- IsForwardType,
39
- IsGroup,
40
- IsLanguageCode,
41
- IsPremium,
42
- IsPrivate,
43
- IsReply,
44
- IsSuperGroup,
45
- IsUser,
46
- IsUserId,
47
- )
48
- from telegrinder.bot.rules.markup import Markup
49
- from telegrinder.bot.rules.mention import HasMention
50
- from telegrinder.bot.rules.message import MessageRule
51
- from telegrinder.bot.rules.message_entities import HasEntities, MessageEntities
52
- from telegrinder.bot.rules.node import NodeRule
53
- from telegrinder.bot.rules.regex import Regex
54
- from telegrinder.bot.rules.rule_enum import RuleEnum
55
- from telegrinder.bot.rules.start import StartCommand
56
- from telegrinder.bot.rules.state import State, StateMeta
57
- from telegrinder.bot.rules.text import HasText, Text
58
- from telegrinder.bot.rules.update import IsUpdateType
59
-
60
- __all__ = (
1
+ from telegrinder.bot.rules.abc import ABCRule, AndRule, NotRule, OrRule
2
+ from telegrinder.bot.rules.callback_data import (
3
+ CallbackDataEq,
4
+ CallbackDataJsonEq,
5
+ CallbackDataJsonModel,
6
+ CallbackDataMap,
7
+ CallbackDataMarkup,
8
+ CallbackQueryDataRule,
9
+ CallbackQueryRule,
10
+ HasData,
11
+ )
12
+ from telegrinder.bot.rules.chat_join import (
13
+ ChatJoinRequestRule,
14
+ HasInviteLink,
15
+ InviteLinkByCreator,
16
+ InviteLinkName,
17
+ )
18
+ from telegrinder.bot.rules.command import Argument, Command
19
+ from telegrinder.bot.rules.enum_text import EnumTextRule
20
+ from telegrinder.bot.rules.func import FuncRule
21
+ from telegrinder.bot.rules.fuzzy import FuzzyText
22
+ from telegrinder.bot.rules.id import IdRule
23
+ from telegrinder.bot.rules.inline import (
24
+ HasLocation,
25
+ InlineQueryChatType,
26
+ InlineQueryMarkup,
27
+ InlineQueryRule,
28
+ InlineQueryText,
29
+ )
30
+ from telegrinder.bot.rules.integer import IntegerInRange, IsInteger
31
+ from telegrinder.bot.rules.is_from import (
32
+ IsBot,
33
+ IsChat,
34
+ IsChatId,
35
+ IsDice,
36
+ IsDiceEmoji,
37
+ IsForum,
38
+ IsForward,
39
+ IsForwardType,
40
+ IsGroup,
41
+ IsLanguageCode,
42
+ IsPremium,
43
+ IsPrivate,
44
+ IsReply,
45
+ IsSuperGroup,
46
+ IsUser,
47
+ IsUserId,
48
+ )
49
+ from telegrinder.bot.rules.logic import If
50
+ from telegrinder.bot.rules.markup import Markup
51
+ from telegrinder.bot.rules.mention import HasMention
52
+ from telegrinder.bot.rules.message import MessageRule
53
+ from telegrinder.bot.rules.message_entities import HasEntities, MessageEntities
54
+ from telegrinder.bot.rules.node import NodeRule
55
+ from telegrinder.bot.rules.payload import (
56
+ PayloadEqRule,
57
+ PayloadJsonEqRule,
58
+ PayloadMarkupRule,
59
+ PayloadModelRule,
60
+ PayloadRule,
61
+ )
62
+ from telegrinder.bot.rules.payment_invoice import (
63
+ PaymentInvoiceCurrency,
64
+ PaymentInvoiceRule,
65
+ )
66
+ from telegrinder.bot.rules.regex import Regex
67
+ from telegrinder.bot.rules.rule_enum import RuleEnum
68
+ from telegrinder.bot.rules.start import StartCommand
69
+ from telegrinder.bot.rules.state import State, StateMeta
70
+ from telegrinder.bot.rules.text import HasCaption, HasText, Text
71
+ from telegrinder.bot.rules.update import IsUpdateType
72
+
73
+ __all__ = (
61
74
  "ABCRule",
62
75
  "AndRule",
63
76
  "Argument",
@@ -73,12 +86,15 @@ __all__ = (
73
86
  "EnumTextRule",
74
87
  "FuncRule",
75
88
  "FuzzyText",
89
+ "HasCaption",
76
90
  "HasData",
77
91
  "HasEntities",
78
92
  "HasInviteLink",
79
93
  "HasLocation",
80
94
  "HasMention",
81
95
  "HasText",
96
+ "IdRule",
97
+ "If",
82
98
  "InlineQueryChatType",
83
99
  "InlineQueryMarkup",
84
100
  "InlineQueryRule",
@@ -110,10 +126,17 @@ __all__ = (
110
126
  "NodeRule",
111
127
  "NotRule",
112
128
  "OrRule",
129
+ "PayloadEqRule",
130
+ "PayloadJsonEqRule",
131
+ "PayloadMarkupRule",
132
+ "PayloadModelRule",
133
+ "PayloadRule",
134
+ "PaymentInvoiceCurrency",
135
+ "PaymentInvoiceRule",
113
136
  "Regex",
114
137
  "RuleEnum",
115
138
  "StartCommand",
116
139
  "State",
117
140
  "StateMeta",
118
- "Text",
119
- )
141
+ "Text",
142
+ )