telegrinder 0.4.2__py3-none-any.whl → 0.5.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 (233) hide show
  1. telegrinder/__init__.py +37 -55
  2. telegrinder/__meta__.py +1 -0
  3. telegrinder/api/__init__.py +6 -4
  4. telegrinder/api/api.py +100 -26
  5. telegrinder/api/error.py +42 -8
  6. telegrinder/api/response.py +4 -1
  7. telegrinder/api/token.py +2 -2
  8. telegrinder/bot/__init__.py +9 -25
  9. telegrinder/bot/bot.py +31 -25
  10. telegrinder/bot/cute_types/__init__.py +0 -0
  11. telegrinder/bot/cute_types/base.py +103 -61
  12. telegrinder/bot/cute_types/callback_query.py +447 -400
  13. telegrinder/bot/cute_types/chat_join_request.py +59 -62
  14. telegrinder/bot/cute_types/chat_member_updated.py +154 -157
  15. telegrinder/bot/cute_types/inline_query.py +41 -44
  16. telegrinder/bot/cute_types/message.py +2621 -2590
  17. telegrinder/bot/cute_types/pre_checkout_query.py +38 -42
  18. telegrinder/bot/cute_types/update.py +1 -8
  19. telegrinder/bot/cute_types/utils.py +1 -1
  20. telegrinder/bot/dispatch/__init__.py +10 -15
  21. telegrinder/bot/dispatch/abc.py +12 -11
  22. telegrinder/bot/dispatch/action.py +104 -0
  23. telegrinder/bot/dispatch/context.py +32 -26
  24. telegrinder/bot/dispatch/dispatch.py +61 -134
  25. telegrinder/bot/dispatch/handler/__init__.py +2 -0
  26. telegrinder/bot/dispatch/handler/abc.py +10 -8
  27. telegrinder/bot/dispatch/handler/audio_reply.py +2 -3
  28. telegrinder/bot/dispatch/handler/base.py +10 -33
  29. telegrinder/bot/dispatch/handler/document_reply.py +2 -3
  30. telegrinder/bot/dispatch/handler/func.py +55 -87
  31. telegrinder/bot/dispatch/handler/media_group_reply.py +2 -3
  32. telegrinder/bot/dispatch/handler/message_reply.py +2 -3
  33. telegrinder/bot/dispatch/handler/photo_reply.py +2 -3
  34. telegrinder/bot/dispatch/handler/sticker_reply.py +2 -3
  35. telegrinder/bot/dispatch/handler/video_reply.py +2 -3
  36. telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  37. telegrinder/bot/dispatch/middleware/abc.py +79 -55
  38. telegrinder/bot/dispatch/middleware/global_middleware.py +18 -33
  39. telegrinder/bot/dispatch/process.py +84 -105
  40. telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  41. telegrinder/bot/dispatch/return_manager/abc.py +102 -65
  42. telegrinder/bot/dispatch/return_manager/callback_query.py +4 -5
  43. telegrinder/bot/dispatch/return_manager/inline_query.py +3 -4
  44. telegrinder/bot/dispatch/return_manager/message.py +8 -10
  45. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +4 -5
  46. telegrinder/bot/dispatch/view/__init__.py +4 -4
  47. telegrinder/bot/dispatch/view/abc.py +6 -16
  48. telegrinder/bot/dispatch/view/base.py +54 -178
  49. telegrinder/bot/dispatch/view/box.py +19 -18
  50. telegrinder/bot/dispatch/view/callback_query.py +4 -8
  51. telegrinder/bot/dispatch/view/chat_join_request.py +5 -6
  52. telegrinder/bot/dispatch/view/chat_member.py +5 -25
  53. telegrinder/bot/dispatch/view/error.py +9 -0
  54. telegrinder/bot/dispatch/view/inline_query.py +4 -8
  55. telegrinder/bot/dispatch/view/message.py +5 -25
  56. telegrinder/bot/dispatch/view/pre_checkout_query.py +4 -8
  57. telegrinder/bot/dispatch/view/raw.py +3 -109
  58. telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -5
  59. telegrinder/bot/dispatch/waiter_machine/actions.py +6 -4
  60. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +1 -3
  61. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +1 -1
  62. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +11 -7
  63. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
  64. telegrinder/bot/dispatch/waiter_machine/machine.py +43 -60
  65. telegrinder/bot/dispatch/waiter_machine/middleware.py +19 -23
  66. telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -5
  67. telegrinder/bot/polling/__init__.py +0 -0
  68. telegrinder/bot/polling/abc.py +0 -0
  69. telegrinder/bot/polling/polling.py +209 -88
  70. telegrinder/bot/rules/__init__.py +3 -16
  71. telegrinder/bot/rules/abc.py +42 -122
  72. telegrinder/bot/rules/callback_data.py +29 -49
  73. telegrinder/bot/rules/chat_join.py +5 -23
  74. telegrinder/bot/rules/command.py +8 -4
  75. telegrinder/bot/rules/enum_text.py +3 -4
  76. telegrinder/bot/rules/func.py +7 -14
  77. telegrinder/bot/rules/fuzzy.py +3 -4
  78. telegrinder/bot/rules/inline.py +8 -20
  79. telegrinder/bot/rules/integer.py +2 -3
  80. telegrinder/bot/rules/is_from.py +12 -11
  81. telegrinder/bot/rules/logic.py +11 -5
  82. telegrinder/bot/rules/markup.py +22 -14
  83. telegrinder/bot/rules/mention.py +8 -7
  84. telegrinder/bot/rules/message_entities.py +8 -4
  85. telegrinder/bot/rules/node.py +23 -12
  86. telegrinder/bot/rules/payload.py +5 -4
  87. telegrinder/bot/rules/payment_invoice.py +6 -21
  88. telegrinder/bot/rules/regex.py +2 -4
  89. telegrinder/bot/rules/rule_enum.py +8 -7
  90. telegrinder/bot/rules/start.py +5 -6
  91. telegrinder/bot/rules/state.py +1 -1
  92. telegrinder/bot/rules/text.py +4 -15
  93. telegrinder/bot/rules/update.py +3 -4
  94. telegrinder/bot/scenario/__init__.py +0 -0
  95. telegrinder/bot/scenario/abc.py +6 -5
  96. telegrinder/bot/scenario/checkbox.py +1 -1
  97. telegrinder/bot/scenario/choice.py +30 -39
  98. telegrinder/client/__init__.py +3 -5
  99. telegrinder/client/abc.py +11 -6
  100. telegrinder/client/aiohttp.py +141 -27
  101. telegrinder/client/form_data.py +1 -1
  102. telegrinder/model.py +61 -89
  103. telegrinder/modules.py +325 -102
  104. telegrinder/msgspec_utils/__init__.py +40 -0
  105. telegrinder/msgspec_utils/abc.py +18 -0
  106. telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
  107. telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
  108. telegrinder/msgspec_utils/custom_types/enum_meta.py +43 -0
  109. telegrinder/msgspec_utils/custom_types/literal.py +25 -0
  110. telegrinder/msgspec_utils/custom_types/option.py +17 -0
  111. telegrinder/msgspec_utils/decoder.py +389 -0
  112. telegrinder/msgspec_utils/encoder.py +206 -0
  113. telegrinder/{msgspec_json.py → msgspec_utils/json.py} +6 -5
  114. telegrinder/msgspec_utils/tools.py +75 -0
  115. telegrinder/node/__init__.py +24 -7
  116. telegrinder/node/attachment.py +1 -0
  117. telegrinder/node/base.py +154 -72
  118. telegrinder/node/callback_query.py +5 -5
  119. telegrinder/node/collection.py +39 -0
  120. telegrinder/node/command.py +1 -2
  121. telegrinder/node/composer.py +121 -72
  122. telegrinder/node/container.py +11 -8
  123. telegrinder/node/context.py +48 -0
  124. telegrinder/node/either.py +27 -40
  125. telegrinder/node/error.py +41 -0
  126. telegrinder/node/event.py +37 -11
  127. telegrinder/node/exceptions.py +7 -0
  128. telegrinder/node/file.py +0 -0
  129. telegrinder/node/i18n.py +108 -0
  130. telegrinder/node/me.py +3 -2
  131. telegrinder/node/payload.py +1 -1
  132. telegrinder/node/polymorphic.py +63 -28
  133. telegrinder/node/reply_message.py +12 -0
  134. telegrinder/node/rule.py +6 -13
  135. telegrinder/node/scope.py +14 -5
  136. telegrinder/node/session.py +53 -0
  137. telegrinder/node/source.py +41 -9
  138. telegrinder/node/text.py +1 -2
  139. telegrinder/node/tools/__init__.py +0 -0
  140. telegrinder/node/tools/generator.py +3 -5
  141. telegrinder/node/utility.py +16 -0
  142. telegrinder/py.typed +0 -0
  143. telegrinder/rules.py +0 -0
  144. telegrinder/tools/__init__.py +48 -88
  145. telegrinder/tools/aio.py +103 -0
  146. telegrinder/tools/callback_data_serialization/__init__.py +5 -0
  147. telegrinder/tools/{callback_data_serilization → callback_data_serialization}/abc.py +0 -0
  148. telegrinder/tools/{callback_data_serilization → callback_data_serialization}/json_ser.py +2 -3
  149. telegrinder/tools/{callback_data_serilization → callback_data_serialization}/msgpack_ser.py +45 -27
  150. telegrinder/tools/final.py +21 -0
  151. telegrinder/tools/formatting/__init__.py +2 -18
  152. telegrinder/tools/formatting/deep_links/__init__.py +39 -0
  153. telegrinder/tools/formatting/{deep_links.py → deep_links/links.py} +12 -85
  154. telegrinder/tools/formatting/deep_links/parsing.py +90 -0
  155. telegrinder/tools/formatting/deep_links/validators.py +8 -0
  156. telegrinder/tools/formatting/html_formatter.py +18 -45
  157. telegrinder/tools/fullname.py +83 -0
  158. telegrinder/tools/global_context/__init__.py +4 -3
  159. telegrinder/tools/global_context/abc.py +17 -14
  160. telegrinder/tools/global_context/builtin_context.py +39 -0
  161. telegrinder/tools/global_context/global_context.py +138 -39
  162. telegrinder/tools/input_file_directory.py +0 -0
  163. telegrinder/tools/keyboard/__init__.py +39 -0
  164. telegrinder/tools/keyboard/abc.py +159 -0
  165. telegrinder/tools/keyboard/base.py +77 -0
  166. telegrinder/tools/keyboard/buttons/__init__.py +14 -0
  167. telegrinder/tools/keyboard/buttons/base.py +18 -0
  168. telegrinder/tools/{buttons.py → keyboard/buttons/buttons.py} +71 -23
  169. telegrinder/tools/keyboard/buttons/static_buttons.py +56 -0
  170. telegrinder/tools/keyboard/buttons/tools.py +18 -0
  171. telegrinder/tools/keyboard/data.py +20 -0
  172. telegrinder/tools/keyboard/keyboard.py +131 -0
  173. telegrinder/tools/keyboard/static_keyboard.py +83 -0
  174. telegrinder/tools/lifespan.py +87 -51
  175. telegrinder/tools/limited_dict.py +4 -1
  176. telegrinder/tools/loop_wrapper.py +332 -0
  177. telegrinder/tools/magic/__init__.py +32 -0
  178. telegrinder/tools/magic/annotations.py +165 -0
  179. telegrinder/tools/magic/dictionary.py +20 -0
  180. telegrinder/tools/magic/function.py +246 -0
  181. telegrinder/tools/magic/shortcut.py +111 -0
  182. telegrinder/tools/parse_mode.py +9 -3
  183. telegrinder/tools/singleton/__init__.py +4 -0
  184. telegrinder/tools/singleton/abc.py +14 -0
  185. telegrinder/tools/singleton/singleton.py +18 -0
  186. telegrinder/tools/state_storage/__init__.py +0 -0
  187. telegrinder/tools/state_storage/abc.py +6 -1
  188. telegrinder/tools/state_storage/memory.py +1 -1
  189. telegrinder/tools/strings.py +0 -0
  190. telegrinder/types/__init__.py +307 -268
  191. telegrinder/types/enums.py +64 -37
  192. telegrinder/types/input_file.py +3 -3
  193. telegrinder/types/methods.py +5699 -5055
  194. telegrinder/types/methods_utils.py +62 -0
  195. telegrinder/types/objects.py +7846 -7058
  196. telegrinder/verification_utils.py +3 -1
  197. telegrinder-0.5.0.dist-info/METADATA +162 -0
  198. telegrinder-0.5.0.dist-info/RECORD +200 -0
  199. {telegrinder-0.4.2.dist-info → telegrinder-0.5.0.dist-info}/licenses/LICENSE +2 -2
  200. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +0 -20
  201. telegrinder/bot/rules/id.py +0 -24
  202. telegrinder/bot/rules/message.py +0 -15
  203. telegrinder/client/sonic.py +0 -212
  204. telegrinder/msgspec_utils.py +0 -478
  205. telegrinder/tools/adapter/__init__.py +0 -19
  206. telegrinder/tools/adapter/abc.py +0 -49
  207. telegrinder/tools/adapter/dataclass.py +0 -56
  208. telegrinder/tools/adapter/errors.py +0 -5
  209. telegrinder/tools/adapter/event.py +0 -61
  210. telegrinder/tools/adapter/node.py +0 -46
  211. telegrinder/tools/adapter/raw_event.py +0 -27
  212. telegrinder/tools/adapter/raw_update.py +0 -30
  213. telegrinder/tools/callback_data_serilization/__init__.py +0 -5
  214. telegrinder/tools/error_handler/__init__.py +0 -10
  215. telegrinder/tools/error_handler/abc.py +0 -30
  216. telegrinder/tools/error_handler/error.py +0 -9
  217. telegrinder/tools/error_handler/error_handler.py +0 -179
  218. telegrinder/tools/formatting/spec_html_formats.py +0 -75
  219. telegrinder/tools/functional.py +0 -8
  220. telegrinder/tools/global_context/telegrinder_ctx.py +0 -27
  221. telegrinder/tools/i18n/__init__.py +0 -12
  222. telegrinder/tools/i18n/abc.py +0 -32
  223. telegrinder/tools/i18n/middleware/__init__.py +0 -3
  224. telegrinder/tools/i18n/middleware/abc.py +0 -22
  225. telegrinder/tools/i18n/simple.py +0 -43
  226. telegrinder/tools/keyboard.py +0 -132
  227. telegrinder/tools/loop_wrapper/__init__.py +0 -4
  228. telegrinder/tools/loop_wrapper/abc.py +0 -20
  229. telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -169
  230. telegrinder/tools/magic.py +0 -344
  231. telegrinder-0.4.2.dist-info/METADATA +0 -151
  232. telegrinder-0.4.2.dist-info/RECORD +0 -182
  233. {telegrinder-0.4.2.dist-info → telegrinder-0.5.0.dist-info}/WHEEL +0 -0
@@ -1,49 +0,0 @@
1
- import abc
2
- import dataclasses
3
- import inspect
4
- import typing
5
-
6
- from fntypes import Error, Nothing, Ok, Option, Some
7
- from fntypes.result import Result
8
-
9
- from telegrinder.modules import logger
10
- from telegrinder.tools.adapter.errors import AdapterError
11
-
12
- type AdaptResult[To] = Result[To, AdapterError] | typing.Awaitable[Result[To, AdapterError]]
13
-
14
-
15
- if typing.TYPE_CHECKING:
16
- from telegrinder.api.api import API
17
- from telegrinder.bot.dispatch.context import Context
18
- from telegrinder.model import Model
19
-
20
-
21
- class ABCAdapter[From: "Model", To](abc.ABC):
22
- ADAPTED_VALUE_KEY: str | None = None
23
-
24
- @abc.abstractmethod
25
- def adapt(self, api: "API", update: From, context: "Context") -> AdaptResult[To]:
26
- pass
27
-
28
-
29
- @dataclasses.dataclass(slots=True)
30
- class Event[To]:
31
- obj: To
32
-
33
-
34
- async def run_adapter[T, U: "Model"](
35
- adapter: "ABCAdapter[U, T]",
36
- api: "API",
37
- update: U,
38
- context: "Context",
39
- ) -> Option[T]:
40
- adapt_result = adapter.adapt(api, update, context)
41
- match await adapt_result if inspect.isawaitable(adapt_result) else adapt_result:
42
- case Ok(value):
43
- return Some(value)
44
- case Error(err):
45
- logger.debug("Adapter {!r} failed with error message: {!r}", adapter, str(err))
46
- return Nothing()
47
-
48
-
49
- __all__ = ("ABCAdapter", "AdaptResult", "Event", "run_adapter")
@@ -1,56 +0,0 @@
1
- from fntypes.option import Nothing, Some
2
- from fntypes.result import Error, Ok, Result
3
-
4
- from telegrinder.api.api import API
5
- from telegrinder.bot.cute_types.base import BaseCute
6
- from telegrinder.bot.cute_types.update import UpdateCute
7
- from telegrinder.bot.dispatch.context import Context
8
- from telegrinder.tools.adapter.abc import ABCAdapter
9
- from telegrinder.tools.adapter.errors import AdapterError
10
- from telegrinder.types.enums import UpdateType
11
- from telegrinder.types.objects import Update
12
-
13
-
14
- class DataclassAdapter[Dataclass](ABCAdapter[Update, Dataclass]):
15
- ADAPTED_VALUE_KEY: str
16
-
17
- def __init__(
18
- self,
19
- dataclass: type[Dataclass],
20
- update_type: UpdateType | None = None,
21
- ) -> None:
22
- self.ADAPTED_VALUE_KEY = f"_adapted_dataclass_{dataclass.__name__}"
23
- self.dataclass = dataclass
24
- self.update_type = update_type
25
-
26
- def __repr__(self) -> str:
27
- return f"<Update -> {self.dataclass.__name__}>"
28
-
29
- def adapt(self, api: API, update: Update, context: Context) -> Result[Dataclass, AdapterError]:
30
- if self.ADAPTED_VALUE_KEY in context:
31
- return Ok(context[self.ADAPTED_VALUE_KEY])
32
-
33
- update_type = (self.update_type or update.update_type).value
34
- try:
35
- if self.dataclass is Update:
36
- return Ok(update) # type: ignore
37
- elif issubclass(self.dataclass, UpdateCute):
38
- dataclass = self.dataclass.from_update(update, bound_api=api)
39
- else:
40
- match getattr(update, update_type):
41
- case Some(val):
42
- dataclass = (
43
- self.dataclass.from_update(val, bound_api=api)
44
- if issubclass(self.dataclass, BaseCute)
45
- else self.dataclass(**val.to_dict())
46
- )
47
- case Nothing():
48
- return Error(AdapterError(f"Update has no event {update_type!r}."))
49
- except Exception as e:
50
- return Error(AdapterError(f"Cannot adapt Update to {self.dataclass!r}, error: {e!r}"))
51
-
52
- context[self.ADAPTED_VALUE_KEY] = dataclass
53
- return Ok(dataclass) # type: ignore
54
-
55
-
56
- __all__ = ("DataclassAdapter",)
@@ -1,5 +0,0 @@
1
- class AdapterError(RuntimeError):
2
- pass
3
-
4
-
5
- __all__ = ("AdapterError",)
@@ -1,61 +0,0 @@
1
- import typing
2
-
3
- from fntypes.result import Error, Ok, Result
4
-
5
- from telegrinder.api.api import API
6
- from telegrinder.bot.cute_types.base import BaseCute
7
- from telegrinder.bot.cute_types.update import UpdateCute
8
- from telegrinder.bot.dispatch.context import Context
9
- from telegrinder.tools.adapter.abc import ABCAdapter
10
- from telegrinder.tools.adapter.errors import AdapterError
11
- from telegrinder.tools.adapter.raw_update import RawUpdateAdapter
12
- from telegrinder.types.enums import UpdateType
13
- from telegrinder.types.objects import Model, Update
14
-
15
-
16
- class EventAdapter[ToEvent: BaseCute](ABCAdapter[Update, ToEvent]):
17
- ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
18
-
19
- def __init__(self, event: UpdateType | type[Model], cute_model: type[ToEvent]) -> None:
20
- self.event = event
21
- self.cute_model = cute_model
22
-
23
- def __repr__(self) -> str:
24
- raw_update_type = (
25
- f"Update -> {self.event.__name__}" if isinstance(self.event, type) else f"Update.{self.event.value}" # type: ignore
26
- )
27
- return "<{}: adapt {} -> {}>".format(
28
- self.__class__.__name__,
29
- raw_update_type,
30
- self.cute_model.__name__,
31
- )
32
-
33
- def get_event(self, update: UpdateCute) -> Model | None:
34
- if isinstance(self.event, UpdateType) and self.event == update.update_type:
35
- return update.incoming_update
36
-
37
- if not isinstance(self.event, UpdateType) and (event := update.get_event(self.event)):
38
- return event.unwrap()
39
-
40
- return None
41
-
42
- def adapt(self, api: API, update: Update, context: Context) -> Result[ToEvent, AdapterError]:
43
- match RawUpdateAdapter().adapt(api, update, context):
44
- case Ok(update_cute) if event := self.get_event(update_cute):
45
- if self.ADAPTED_VALUE_KEY in context:
46
- return Ok(context[self.ADAPTED_VALUE_KEY])
47
-
48
- adapted = (
49
- typing.cast(ToEvent, event)
50
- if isinstance(event, BaseCute)
51
- else self.cute_model.from_update(event, bound_api=api)
52
- )
53
- context[self.ADAPTED_VALUE_KEY] = adapted
54
- return Ok(adapted)
55
- case Error(_) as err:
56
- return err
57
- case _:
58
- return Error(AdapterError(f"Update is not an {self.event!r}."))
59
-
60
-
61
- __all__ = ("EventAdapter",)
@@ -1,46 +0,0 @@
1
- import typing_extensions as typing
2
- from fntypes.result import Error, Ok, Result
3
-
4
- from telegrinder.api.api import API
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.msgspec_utils import repr_type
7
- from telegrinder.node.composer import NodeSession, compose_nodes
8
- from telegrinder.tools.adapter.abc import ABCAdapter, Event
9
- from telegrinder.tools.adapter.errors import AdapterError
10
- from telegrinder.types.objects import Update
11
-
12
- if typing.TYPE_CHECKING:
13
- from telegrinder.node.base import IsNode
14
-
15
-
16
- class NodeAdapter(ABCAdapter[Update, Event[tuple["IsNode", ...]]]):
17
- def __init__(self, *nodes: "IsNode") -> None:
18
- self.nodes = nodes
19
-
20
- def __repr__(self) -> str:
21
- return "<{}: adapt Update -> ({})>".format(
22
- self.__class__.__name__,
23
- ", ".join(repr_type(node) for node in self.nodes),
24
- )
25
-
26
- async def adapt(
27
- self,
28
- api: API,
29
- update: Update,
30
- context: Context,
31
- ) -> Result[Event[tuple[NodeSession, ...]], AdapterError]:
32
- result = await compose_nodes(
33
- nodes={f"node_{i}": typing.cast("IsNode", node) for i, node in enumerate(self.nodes)},
34
- ctx=context,
35
- data={Update: update, API: api},
36
- )
37
-
38
- match result:
39
- case Ok(collection):
40
- sessions: list[NodeSession] = list(collection.sessions.values())
41
- return Ok(Event(tuple(sessions)))
42
- case Error(err):
43
- return Error(AdapterError(f"Couldn't compose nodes, error: {err}."))
44
-
45
-
46
- __all__ = ("NodeAdapter",)
@@ -1,27 +0,0 @@
1
- from fntypes.result import Error, Ok, Result
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.dispatch.context import Context
5
- from telegrinder.model import Model
6
- from telegrinder.tools.adapter.abc import ABCAdapter
7
- from telegrinder.tools.adapter.errors import AdapterError
8
- from telegrinder.types.objects import Update
9
-
10
-
11
- class RawEventAdapter(ABCAdapter[Update, Model]):
12
- def __init__(self, event_model: type[Model], /) -> None:
13
- self.event_model = event_model
14
-
15
- def __repr__(self) -> str:
16
- return "<{}: adapt Update -> {}>".format(
17
- self.__class__.__name__,
18
- self.event_model.__name__,
19
- )
20
-
21
- def adapt(self, api: API, update: Update, context: Context) -> Result[Model, AdapterError]:
22
- if isinstance(update.incoming_update, self.event_model):
23
- return Ok(update.incoming_update)
24
- return Error(AdapterError(f"Update is not an {self.event_model.__name__!r}."))
25
-
26
-
27
- __all__ = ("RawEventAdapter",)
@@ -1,30 +0,0 @@
1
- from fntypes.result import Ok, Result
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.update import UpdateCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.tools.adapter.abc import ABCAdapter
7
- from telegrinder.tools.adapter.errors import AdapterError
8
- from telegrinder.types.objects import Update
9
-
10
-
11
- class RawUpdateAdapter(ABCAdapter[Update, UpdateCute]):
12
- ADAPTED_VALUE_KEY: str = "_adapted_update_cute"
13
-
14
- def __repr__(self) -> str:
15
- return f"<{self.__class__.__name__}: adapt Update -> UpdateCute>"
16
-
17
- def adapt(
18
- self,
19
- api: API,
20
- update: Update,
21
- context: Context,
22
- ) -> Result[UpdateCute, AdapterError]:
23
- if self.ADAPTED_VALUE_KEY not in context:
24
- context[self.ADAPTED_VALUE_KEY] = (
25
- UpdateCute.from_update(update, api) if not isinstance(update, UpdateCute) else update
26
- )
27
- return Ok(context[self.ADAPTED_VALUE_KEY])
28
-
29
-
30
- __all__ = ("RawUpdateAdapter",)
@@ -1,5 +0,0 @@
1
- from .abc import ABCDataSerializer
2
- from .json_ser import JSONSerializer
3
- from .msgpack_ser import MsgPackSerializer
4
-
5
- __all__ = ("ABCDataSerializer", "JSONSerializer", "MsgPackSerializer")
@@ -1,10 +0,0 @@
1
- from .abc import ABCErrorHandler
2
- from .error import CatcherError
3
- from .error_handler import Catcher, ErrorHandler
4
-
5
- __all__ = (
6
- "ABCErrorHandler",
7
- "Catcher",
8
- "CatcherError",
9
- "ErrorHandler",
10
- )
@@ -1,30 +0,0 @@
1
- import typing
2
- from abc import ABC, abstractmethod
3
-
4
- from telegrinder.api import API
5
- from telegrinder.bot.dispatch.context import Context
6
-
7
- type Handler = typing.Callable[..., typing.Awaitable[typing.Any]]
8
-
9
-
10
- class ABCErrorHandler[Event](ABC):
11
- @abstractmethod
12
- def __call__(
13
- self,
14
- *args: typing.Any,
15
- **kwargs: typing.Any,
16
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Callable[..., typing.Any]]:
17
- """Decorator for registering callback as a catcher for the error handler."""
18
-
19
- @abstractmethod
20
- async def run(
21
- self,
22
- exception: BaseException,
23
- event: Event,
24
- api: API,
25
- ctx: Context,
26
- ) -> typing.Any:
27
- """Run the error handler."""
28
-
29
-
30
- __all__ = ("ABCErrorHandler",)
@@ -1,9 +0,0 @@
1
- class CatcherError(BaseException):
2
- __slots__ = ("exc", "message")
3
-
4
- def __init__(self, exc: BaseException, message: str) -> None:
5
- self.exc = exc
6
- self.message = message
7
-
8
-
9
- __all__ = ("CatcherError",)
@@ -1,179 +0,0 @@
1
- import dataclasses
2
- import typing
3
-
4
- from fntypes.result import Error, Ok, Result
5
-
6
- from telegrinder.api.api import API
7
- from telegrinder.bot.dispatch.context import Context
8
- from telegrinder.modules import logger
9
- from telegrinder.tools.error_handler.abc import ABCErrorHandler
10
- from telegrinder.tools.error_handler.error import CatcherError
11
- from telegrinder.tools.magic import magic_bundle
12
-
13
- type FuncCatcher[Exc: BaseException] = typing.Callable[
14
- typing.Concatenate[Exc, ...],
15
- typing.Awaitable[typing.Any],
16
- ]
17
-
18
-
19
- @dataclasses.dataclass(frozen=True, repr=False, slots=True)
20
- class Catcher[Event]:
21
- func: FuncCatcher[BaseException]
22
- exceptions: list[type[BaseException] | BaseException] = dataclasses.field(
23
- default_factory=lambda: [],
24
- kw_only=True,
25
- )
26
- logging: bool = dataclasses.field(default=False, kw_only=True)
27
- raise_exception: bool = dataclasses.field(default=False, kw_only=True)
28
- ignore_errors: bool = dataclasses.field(default=False, kw_only=True)
29
-
30
- def __repr__(self) -> str:
31
- return "<Catcher: function={!r}, logging={}, raise_exception={}, ignore_errors={}>".format(
32
- self.func.__name__,
33
- self.logging,
34
- self.raise_exception,
35
- self.ignore_errors,
36
- )
37
-
38
- async def __call__(
39
- self,
40
- exception: BaseException,
41
- event: Event,
42
- api: API,
43
- ctx: Context,
44
- ) -> Result[typing.Any, BaseException]:
45
- return await self.run(api, event, ctx, exception)
46
-
47
- def match_exception(self, exception: BaseException) -> bool:
48
- for exc in self.exceptions:
49
- if isinstance(exc, type) and type(exception) is exc:
50
- return True
51
- if isinstance(exc, object) and type(exception) is type(exc):
52
- return True if not exc.args else exc.args == exception.args
53
-
54
- return False
55
-
56
- async def run(
57
- self,
58
- api: API,
59
- event: Event,
60
- ctx: Context,
61
- exception: BaseException,
62
- ) -> Result[typing.Any, BaseException]:
63
- if self.match_exception(exception):
64
- logger.debug(
65
- "Error handler caught an exception {!r}, running catcher {!r}...".format(
66
- exception,
67
- self.func.__name__,
68
- )
69
- )
70
- return Ok(await self.func(exception, **magic_bundle(self.func, {"event": event, "api": api} | ctx)))
71
-
72
- logger.debug("Failed to match exception {!r}.", exception.__class__.__name__)
73
- return Error(exception)
74
-
75
-
76
- class ErrorHandler[Event](ABCErrorHandler[Event]):
77
- def __init__(self, catcher: Catcher[Event] | None = None, /) -> None:
78
- self.catcher = catcher
79
-
80
- def __repr__(self) -> str:
81
- return (
82
- "<{}: exceptions=[{}], catcher={!r}>".format(
83
- self.__class__.__name__,
84
- ", ".join(e.__name__ if isinstance(e, type) else repr(e) for e in self.catcher.exceptions),
85
- self.catcher,
86
- )
87
- if self.catcher is not None
88
- else "<{}()>".format(self.__class__.__name__)
89
- )
90
-
91
- def __call__(
92
- self,
93
- *exceptions: type[BaseException] | BaseException,
94
- logging: bool = False,
95
- raise_exception: bool = False,
96
- ignore_errors: bool = False,
97
- ):
98
- """Register the catcher.
99
-
100
- :param logging: Logging the result of the catcher at the level `DEBUG`.
101
- :param raise_exception: Raise an exception if the catcher has not started.
102
- :param ignore_errors: Ignore errors that may occur.
103
- """
104
-
105
- def decorator[Func: FuncCatcher](catcher: Func, /) -> Func:
106
- if not self.catcher:
107
- self.catcher = Catcher(
108
- catcher,
109
- exceptions=list(exceptions),
110
- logging=logging,
111
- raise_exception=raise_exception,
112
- ignore_errors=ignore_errors,
113
- )
114
- return catcher
115
-
116
- return decorator
117
-
118
- def _process_catcher_error(self, error: CatcherError) -> Result[None, BaseException]:
119
- assert self.catcher is not None
120
-
121
- if self.catcher.raise_exception:
122
- raise error.exc from None
123
- if self.catcher.logging:
124
- logger.error(error.message)
125
- if not self.catcher.ignore_errors:
126
- return Error(error.exc)
127
-
128
- return Ok(None)
129
-
130
- async def suppress(
131
- self,
132
- exception: BaseException,
133
- event: Event,
134
- api: API,
135
- ctx: Context,
136
- ) -> Result[typing.Any, BaseException]:
137
- assert self.catcher is not None
138
-
139
- try:
140
- return await self.catcher(exception, event, api, ctx)
141
- except BaseException as exc:
142
- return Error(
143
- CatcherError(
144
- exc,
145
- "{!r} was occurred during the running catcher {!r}.".format(
146
- exc,
147
- self.catcher.func.__name__,
148
- ),
149
- )
150
- )
151
-
152
- async def run(
153
- self,
154
- exception: BaseException,
155
- event: Event,
156
- api: API,
157
- ctx: Context,
158
- ) -> typing.Any:
159
- if not self.catcher:
160
- raise exception from None
161
-
162
- match await self.suppress(exception, event, api, ctx):
163
- case Ok(value):
164
- if self.catcher.logging:
165
- logger.debug(
166
- "Catcher {!r} returned: {!r}",
167
- self.catcher.func.__name__,
168
- value,
169
- )
170
- return value
171
- case Error(exc):
172
- if isinstance(exc, CatcherError):
173
- return self._process_catcher_error(exc).unwrap()
174
- if self.catcher.ignore_errors:
175
- return None
176
- raise exc from None
177
-
178
-
179
- __all__ = ("Catcher", "ErrorHandler")
@@ -1,75 +0,0 @@
1
- import dataclasses
2
- import typing
3
-
4
- from telegrinder.types.enums import ProgrammingLanguage
5
-
6
- SpecialFormat: typing.TypeAlias = typing.Union[
7
- "BlockQuote",
8
- "Link",
9
- "Mention",
10
- "PreCode",
11
- "TgEmoji",
12
- ]
13
-
14
-
15
- def is_spec_format(obj: typing.Any) -> typing.TypeGuard[SpecialFormat]:
16
- return dataclasses.is_dataclass(obj) and hasattr(obj, "__formatter_name__") and isinstance(obj, Base)
17
-
18
-
19
- @dataclasses.dataclass(repr=False)
20
- class Base:
21
- __formatter_name__: typing.ClassVar[str] = dataclasses.field(init=False, repr=False)
22
-
23
- def __repr__(self) -> str:
24
- return f"<Special formatter {self.__class__.__name__!r} -> {self.__formatter_name__!r}>"
25
-
26
-
27
- @dataclasses.dataclass(repr=False, slots=True)
28
- class Mention(Base):
29
- __formatter_name__ = "mention"
30
-
31
- string: str
32
- user_id: int
33
-
34
-
35
- @dataclasses.dataclass(repr=False, slots=True)
36
- class Link(Base):
37
- __formatter_name__ = "link"
38
-
39
- href: str
40
- string: str | None = None
41
-
42
-
43
- @dataclasses.dataclass(repr=False, slots=True)
44
- class PreCode(Base):
45
- __formatter_name__ = "pre_code"
46
-
47
- string: str
48
- lang: str | ProgrammingLanguage | None = None
49
-
50
-
51
- @dataclasses.dataclass(repr=False, slots=True)
52
- class TgEmoji(Base):
53
- __formatter_name__ = "tg_emoji"
54
-
55
- string: str
56
- emoji_id: int
57
-
58
-
59
- @dataclasses.dataclass(repr=False, slots=True)
60
- class BlockQuote(Base):
61
- __formatter_name__ = "block_quote"
62
-
63
- string: str
64
- expandable: bool = False
65
-
66
-
67
- __all__ = (
68
- "Base",
69
- "BlockQuote",
70
- "Link",
71
- "Mention",
72
- "PreCode",
73
- "SpecialFormat",
74
- "TgEmoji",
75
- )
@@ -1,8 +0,0 @@
1
- from fntypes import Nothing, Option, Some
2
-
3
-
4
- def from_optional[Value](value: Value | None, /) -> Option[Value]:
5
- return Some(value) if value is not None else Nothing()
6
-
7
-
8
- __all__ = ("from_optional",)
@@ -1,27 +0,0 @@
1
- import re
2
-
3
- import vbml
4
-
5
- from telegrinder.tools.global_context import GlobalContext, ctx_var
6
-
7
-
8
- class TelegrinderContext(GlobalContext):
9
- """Basic type-hinted telegrinder context with context name `"telegrinder"`.
10
-
11
- You can use this class or GlobalContext:
12
- ```
13
- from telegrinder.tools.global_context import GlobalContext, TelegrinderContext
14
-
15
- ctx1 = TelegrinderContext()
16
- ctx2 = GlobalContext("telegrinder") # same, but without the type-hints
17
- assert ctx1 == ctx2 # ok
18
- ```
19
- """
20
-
21
- __ctx_name__ = "telegrinder"
22
-
23
- vbml_pattern_flags: re.RegexFlag | None = None
24
- vbml_patcher: vbml.Patcher = ctx_var(default=vbml.Patcher(), frozen=True)
25
-
26
-
27
- __all__ = ("TelegrinderContext",)
@@ -1,12 +0,0 @@
1
- from .abc import ABCI18n, ABCTranslator, I18nEnum
2
- from .middleware import ABCTranslatorMiddleware
3
- from .simple import SimpleI18n, SimpleTranslator
4
-
5
- __all__ = (
6
- "ABCI18n",
7
- "ABCTranslator",
8
- "ABCTranslatorMiddleware",
9
- "I18nEnum",
10
- "SimpleI18n",
11
- "SimpleTranslator",
12
- )
@@ -1,32 +0,0 @@
1
- import enum
2
- import typing
3
- from abc import ABC, abstractmethod
4
-
5
-
6
- class ABCI18n(ABC):
7
- @abstractmethod
8
- def get_translator_by_locale(self, locale: str) -> "ABCTranslator":
9
- pass
10
-
11
-
12
- class ABCTranslator(ABC):
13
- def __init__(self, locale: str, **kwargs: typing.Any) -> None:
14
- self.locale = locale
15
-
16
- @abstractmethod
17
- def get(self, __key: str, *args: typing.Any, **kwargs: typing.Any) -> str:
18
- """This translates a key to actual human-readable string"""
19
-
20
- def __call__(self, __key: str, *args: typing.Any, **kwargs: typing.Any) -> str:
21
- return self.get(__key, *args, **kwargs)
22
-
23
-
24
- class I18nEnum(enum.Enum):
25
- I18N = "_"
26
-
27
-
28
- __all__ = (
29
- "ABCI18n",
30
- "ABCTranslator",
31
- "I18nEnum",
32
- )
@@ -1,3 +0,0 @@
1
- from .abc import ABCTranslatorMiddleware
2
-
3
- __all__ = ("ABCTranslatorMiddleware",)