telegrinder 0.1.dev19__py3-none-any.whl → 0.1.dev158__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 (136) 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 +29 -24
  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 +47 -0
  11. telegrinder/bot/cute_types/callback_query.py +64 -14
  12. telegrinder/bot/cute_types/inline_query.py +22 -16
  13. telegrinder/bot/cute_types/message.py +163 -43
  14. telegrinder/bot/cute_types/update.py +23 -0
  15. telegrinder/bot/dispatch/__init__.py +56 -3
  16. telegrinder/bot/dispatch/abc.py +9 -7
  17. telegrinder/bot/dispatch/composition.py +74 -0
  18. telegrinder/bot/dispatch/context.py +71 -0
  19. telegrinder/bot/dispatch/dispatch.py +86 -49
  20. telegrinder/bot/dispatch/handler/__init__.py +3 -0
  21. telegrinder/bot/dispatch/handler/abc.py +11 -5
  22. telegrinder/bot/dispatch/handler/func.py +41 -32
  23. telegrinder/bot/dispatch/handler/message_reply.py +46 -0
  24. telegrinder/bot/dispatch/middleware/__init__.py +2 -0
  25. telegrinder/bot/dispatch/middleware/abc.py +10 -4
  26. telegrinder/bot/dispatch/process.py +53 -49
  27. telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
  28. telegrinder/bot/dispatch/return_manager/abc.py +95 -0
  29. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  30. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  31. telegrinder/bot/dispatch/return_manager/message.py +25 -0
  32. telegrinder/bot/dispatch/view/__init__.py +14 -2
  33. telegrinder/bot/dispatch/view/abc.py +121 -2
  34. telegrinder/bot/dispatch/view/box.py +38 -0
  35. telegrinder/bot/dispatch/view/callback_query.py +13 -38
  36. telegrinder/bot/dispatch/view/inline_query.py +11 -38
  37. telegrinder/bot/dispatch/view/message.py +11 -46
  38. telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
  39. telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
  40. telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
  41. telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
  42. telegrinder/bot/polling/__init__.py +2 -0
  43. telegrinder/bot/polling/abc.py +11 -4
  44. telegrinder/bot/polling/polling.py +89 -40
  45. telegrinder/bot/rules/__init__.py +92 -5
  46. telegrinder/bot/rules/abc.py +81 -63
  47. telegrinder/bot/rules/adapter/__init__.py +11 -0
  48. telegrinder/bot/rules/adapter/abc.py +21 -0
  49. telegrinder/bot/rules/adapter/errors.py +5 -0
  50. telegrinder/bot/rules/adapter/event.py +43 -0
  51. telegrinder/bot/rules/adapter/raw_update.py +24 -0
  52. telegrinder/bot/rules/callback_data.py +159 -38
  53. telegrinder/bot/rules/command.py +116 -0
  54. telegrinder/bot/rules/enum_text.py +28 -0
  55. telegrinder/bot/rules/func.py +17 -17
  56. telegrinder/bot/rules/fuzzy.py +13 -10
  57. telegrinder/bot/rules/inline.py +61 -0
  58. telegrinder/bot/rules/integer.py +12 -7
  59. telegrinder/bot/rules/is_from.py +148 -7
  60. telegrinder/bot/rules/markup.py +21 -18
  61. telegrinder/bot/rules/mention.py +17 -0
  62. telegrinder/bot/rules/message_entities.py +33 -0
  63. telegrinder/bot/rules/regex.py +27 -19
  64. telegrinder/bot/rules/rule_enum.py +74 -0
  65. telegrinder/bot/rules/start.py +42 -0
  66. telegrinder/bot/rules/text.py +23 -14
  67. telegrinder/bot/scenario/__init__.py +2 -0
  68. telegrinder/bot/scenario/abc.py +12 -5
  69. telegrinder/bot/scenario/checkbox.py +48 -30
  70. telegrinder/bot/scenario/choice.py +16 -10
  71. telegrinder/client/__init__.py +2 -0
  72. telegrinder/client/abc.py +8 -21
  73. telegrinder/client/aiohttp.py +30 -21
  74. telegrinder/model.py +68 -37
  75. telegrinder/modules.py +189 -21
  76. telegrinder/msgspec_json.py +14 -0
  77. telegrinder/msgspec_utils.py +207 -0
  78. telegrinder/node/__init__.py +31 -0
  79. telegrinder/node/attachment.py +71 -0
  80. telegrinder/node/base.py +93 -0
  81. telegrinder/node/composer.py +71 -0
  82. telegrinder/node/container.py +22 -0
  83. telegrinder/node/message.py +18 -0
  84. telegrinder/node/rule.py +56 -0
  85. telegrinder/node/source.py +31 -0
  86. telegrinder/node/text.py +13 -0
  87. telegrinder/node/tools/__init__.py +3 -0
  88. telegrinder/node/tools/generator.py +40 -0
  89. telegrinder/node/update.py +12 -0
  90. telegrinder/rules.py +1 -1
  91. telegrinder/tools/__init__.py +165 -4
  92. telegrinder/tools/buttons.py +75 -51
  93. telegrinder/tools/error_handler/__init__.py +8 -0
  94. telegrinder/tools/error_handler/abc.py +30 -0
  95. telegrinder/tools/error_handler/error_handler.py +156 -0
  96. telegrinder/tools/formatting/__init__.py +81 -3
  97. telegrinder/tools/formatting/html.py +283 -37
  98. telegrinder/tools/formatting/links.py +32 -0
  99. telegrinder/tools/formatting/spec_html_formats.py +121 -0
  100. telegrinder/tools/global_context/__init__.py +12 -0
  101. telegrinder/tools/global_context/abc.py +66 -0
  102. telegrinder/tools/global_context/global_context.py +451 -0
  103. telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
  104. telegrinder/tools/i18n/__init__.py +12 -0
  105. telegrinder/tools/i18n/base.py +31 -0
  106. telegrinder/tools/i18n/middleware/__init__.py +3 -0
  107. telegrinder/tools/i18n/middleware/base.py +26 -0
  108. telegrinder/tools/i18n/simple.py +48 -0
  109. telegrinder/tools/inline_query.py +684 -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 +651 -0
  121. telegrinder/types/methods.py +3933 -1128
  122. telegrinder/types/objects.py +4755 -1633
  123. {telegrinder-0.1.dev19.dist-info → telegrinder-0.1.dev158.dist-info}/LICENSE +2 -1
  124. telegrinder-0.1.dev158.dist-info/METADATA +108 -0
  125. telegrinder-0.1.dev158.dist-info/RECORD +126 -0
  126. {telegrinder-0.1.dev19.dist-info → telegrinder-0.1.dev158.dist-info}/WHEEL +1 -1
  127. telegrinder/bot/dispatch/waiter.py +0 -37
  128. telegrinder/result.py +0 -38
  129. telegrinder/tools/formatting/abc.py +0 -52
  130. telegrinder/tools/formatting/markdown.py +0 -57
  131. telegrinder/typegen/__init__.py +0 -1
  132. telegrinder/typegen/__main__.py +0 -3
  133. telegrinder/typegen/nicification.py +0 -20
  134. telegrinder/typegen/schema_generator.py +0 -259
  135. telegrinder-0.1.dev19.dist-info/METADATA +0 -22
  136. telegrinder-0.1.dev19.dist-info/RECORD +0 -74
telegrinder/__init__.py CHANGED
@@ -1,39 +1,146 @@
1
- from .client import ABCClient, AiohttpClient
2
- from .api import ABCAPI, Token, API, APIError, APIResponse
1
+ import typing
2
+
3
+ from .api import ABCAPI, API, APIError, APIResponse, Token
3
4
  from .bot import (
4
- ABCPolling,
5
- Polling,
6
5
  ABCDispatch,
6
+ ABCHandler,
7
+ ABCMiddleware,
8
+ ABCPolling,
9
+ ABCReturnManager,
7
10
  ABCRule,
8
- ABCMessageRule,
9
- Dispatch,
10
- Telegrinder,
11
+ ABCScenario,
12
+ ABCStateView,
11
13
  ABCView,
12
- ABCHandler,
13
- MessageView,
14
+ BaseCute,
15
+ BaseReturnManager,
16
+ BaseStateView,
17
+ BaseView,
18
+ CallbackQueryCute,
19
+ CallbackQueryReturnManager,
14
20
  CallbackQueryView,
21
+ Checkbox,
22
+ Dispatch,
15
23
  FuncHandler,
16
- MessageCute,
17
- CallbackQueryCute,
18
24
  InlineQueryCute,
19
- ABCMiddleware,
20
- ABCScenario,
21
- Checkbox,
25
+ InlineQueryReturnManager,
26
+ MessageCute,
27
+ MessageReplyHandler,
28
+ MessageReturnManager,
29
+ MessageRule,
30
+ MessageView,
31
+ Polling,
22
32
  SingleChoice,
33
+ Telegrinder,
34
+ ViewBox,
35
+ WaiterMachine,
36
+ register_manager,
23
37
  )
38
+ from .client import ABCClient, AiohttpClient
39
+ from .model import Model
40
+ from .modules import logger
24
41
  from .tools import (
25
- Keyboard,
42
+ ABCGlobalContext,
43
+ ABCLoopWrapper,
44
+ ABCTranslator,
45
+ ABCTranslatorMiddleware,
46
+ AnyMarkup,
26
47
  Button,
48
+ CtxVar,
49
+ DelayedTask,
50
+ FormatString,
51
+ GlobalContext,
52
+ HTMLFormatter,
53
+ I18nEnum,
27
54
  InlineButton,
28
55
  InlineKeyboard,
29
- VarUnset,
30
- magic_bundle,
56
+ Keyboard,
31
57
  KeyboardSetBase,
32
58
  KeyboardSetYAML,
33
- AnyMarkup,
59
+ LoopWrapper,
60
+ ParseMode,
61
+ RowButtons,
62
+ SimpleI18n,
63
+ SimpleTranslator,
64
+ ctx_var,
65
+ keyboard_remove,
66
+ magic_bundle,
34
67
  )
35
- from .result import Result
36
68
 
37
- Message = MessageCute
38
- CallbackQuery = CallbackQueryCute
39
- InlineQuery = InlineQueryCute
69
+ Message: typing.TypeAlias = MessageCute
70
+ CallbackQuery: typing.TypeAlias = CallbackQueryCute
71
+ InlineQuery: typing.TypeAlias = InlineQueryCute
72
+ Bot: typing.TypeAlias = Telegrinder
73
+
74
+
75
+ __all__ = (
76
+ "ABCAPI",
77
+ "ABCClient",
78
+ "ABCDispatch",
79
+ "ABCGlobalContext",
80
+ "ABCHandler",
81
+ "ABCLoopWrapper",
82
+ "ABCMiddleware",
83
+ "ABCPolling",
84
+ "ABCReturnManager",
85
+ "ABCRule",
86
+ "ABCScenario",
87
+ "ABCStateView",
88
+ "ABCTranslator",
89
+ "ABCTranslatorMiddleware",
90
+ "ABCView",
91
+ "API",
92
+ "APIError",
93
+ "APIResponse",
94
+ "AiohttpClient",
95
+ "AnyMarkup",
96
+ "BaseCute",
97
+ "BaseReturnManager",
98
+ "BaseStateView",
99
+ "BaseView",
100
+ "Bot",
101
+ "Button",
102
+ "CallbackQuery",
103
+ "CallbackQueryCute",
104
+ "CallbackQueryReturnManager",
105
+ "CallbackQueryView",
106
+ "Checkbox",
107
+ "CtxVar",
108
+ "DelayedTask",
109
+ "Dispatch",
110
+ "FormatString",
111
+ "FuncHandler",
112
+ "GlobalContext",
113
+ "HTMLFormatter",
114
+ "I18nEnum",
115
+ "InlineButton",
116
+ "InlineKeyboard",
117
+ "InlineQuery",
118
+ "InlineQueryCute",
119
+ "InlineQueryReturnManager",
120
+ "Keyboard",
121
+ "KeyboardSetBase",
122
+ "KeyboardSetYAML",
123
+ "LoopWrapper",
124
+ "Message",
125
+ "MessageCute",
126
+ "MessageReplyHandler",
127
+ "MessageReturnManager",
128
+ "MessageRule",
129
+ "MessageView",
130
+ "Model",
131
+ "ParseMode",
132
+ "Polling",
133
+ "RowButtons",
134
+ "SimpleI18n",
135
+ "SimpleTranslator",
136
+ "SingleChoice",
137
+ "Telegrinder",
138
+ "Token",
139
+ "ViewBox",
140
+ "WaiterMachine",
141
+ "ctx_var",
142
+ "keyboard_remove",
143
+ "logger",
144
+ "magic_bundle",
145
+ "register_manager",
146
+ )
@@ -1,4 +1,13 @@
1
- from .abc import ABCAPI, APIError, Token
1
+ from .abc import ABCAPI, Token
2
2
  from .api import API
3
+ from .error import APIError, InvalidTokenError
3
4
  from .response import APIResponse
4
- from .error import APIError
5
+
6
+ __all__ = (
7
+ "ABCAPI",
8
+ "API",
9
+ "APIError",
10
+ "APIResponse",
11
+ "InvalidTokenError",
12
+ "Token",
13
+ )
telegrinder/api/abc.py CHANGED
@@ -1,20 +1,33 @@
1
- from abc import ABC, abstractmethod
1
+ import pathlib
2
2
  import typing
3
+ from abc import ABC, abstractmethod
3
4
 
4
5
  import msgspec
6
+ from envparse import env
7
+ from fntypes.result import Result
5
8
 
6
- from telegrinder.client import ABCClient
7
- from telegrinder.result import Result
8
9
  from telegrinder.api.error import APIError
10
+ from telegrinder.client import ABCClient
9
11
 
10
- from envparse import env
12
+ from .error import InvalidTokenError
11
13
 
12
14
 
13
15
  class Token(str):
16
+ def __new__(cls, token: str) -> typing.Self:
17
+ if token.count(":") != 1 or not token.split(":")[0].isdigit():
18
+ raise InvalidTokenError("Invalid token, it should look like this '123:token'.")
19
+ return super().__new__(cls, token)
20
+
14
21
  @classmethod
15
- def from_env(cls, var_name: str = "BOT_TOKEN", is_read: bool = False) -> "Token":
22
+ def from_env(
23
+ cls,
24
+ var_name: str = "BOT_TOKEN",
25
+ *,
26
+ is_read: bool = False,
27
+ path_to_envfile: str | pathlib.Path | None = None,
28
+ ) -> typing.Self:
16
29
  if not is_read:
17
- env.read_envfile()
30
+ env.read_envfile(path_to_envfile)
18
31
  return cls(env.str(var_name))
19
32
 
20
33
  @property
@@ -29,15 +42,15 @@ class ABCAPI(ABC):
29
42
  async def request(
30
43
  self,
31
44
  method: str,
32
- data: typing.Optional[dict] = None,
33
- ) -> Result[typing.Union[list, dict, bool], APIError]:
45
+ data: dict | None = None,
46
+ ) -> Result[list | dict | bool, APIError]:
34
47
  pass
35
48
 
36
49
  @abstractmethod
37
50
  async def request_raw(
38
51
  self,
39
52
  method: str,
40
- data: typing.Optional[dict] = None,
53
+ data: dict | None = None,
41
54
  ) -> Result[msgspec.Raw, APIError]:
42
55
  pass
43
56
 
@@ -50,3 +63,6 @@ class ABCAPI(ABC):
50
63
  @abstractmethod
51
64
  def id(self) -> int:
52
65
  pass
66
+
67
+
68
+ __all__ = ("ABCAPI", "Token")
telegrinder/api/api.py CHANGED
@@ -1,15 +1,17 @@
1
+ import typing
2
+
1
3
  import msgspec
4
+ from fntypes.result import Error, Ok, Result
2
5
 
3
- from .abc import ABCAPI, APIError, Token
4
- import typing
5
- from telegrinder.result import Result
6
+ from telegrinder.api.response import APIResponse
6
7
  from telegrinder.client import ABCClient, AiohttpClient
8
+ from telegrinder.model import convert, decoder
7
9
  from telegrinder.types.methods import APIMethods
8
- from telegrinder.model import convert
9
- from telegrinder.api.response import APIResponse
10
10
 
11
+ from .abc import ABCAPI, APIError, Token
11
12
 
12
- def compose_data(client: ABCClient, data: dict) -> typing.Any:
13
+
14
+ def compose_data(client: ABCClient, data: dict[str, typing.Any]) -> typing.Any:
13
15
  data = {k: convert(v) for k, v in data.items()}
14
16
  if any(isinstance(v, tuple) for v in data.values()):
15
17
  data = client.get_form(data)
@@ -17,16 +19,16 @@ def compose_data(client: ABCClient, data: dict) -> typing.Any:
17
19
 
18
20
 
19
21
  class API(ABCAPI, APIMethods):
20
- API_URL = "https://api.telegram.org/"
22
+ API_URL: typing.ClassVar[str] = "https://api.telegram.org/"
21
23
 
22
- def __init__(self, token: Token, http: typing.Optional[ABCClient] = None):
24
+ def __init__(self, token: Token, *, http: ABCClient | None = None):
23
25
  self.token = token
24
26
  self.http = http or AiohttpClient()
25
27
  super().__init__(self)
26
28
 
27
29
  @property
28
30
  def id(self) -> int:
29
- return int(self.token.split(":")[0])
31
+ return self.token.bot_id
30
32
 
31
33
  @property
32
34
  def request_url(self) -> str:
@@ -35,27 +37,30 @@ class API(ABCAPI, APIMethods):
35
37
  async def request(
36
38
  self,
37
39
  method: str,
38
- data: typing.Optional[dict] = None,
39
- ) -> Result[typing.Union[dict, list, bool], APIError]:
40
- data = compose_data(self.http, data)
41
- response = await self.http.request_json(self.request_url + method, data=data)
40
+ data: dict | None = None,
41
+ ) -> Result[dict | list | bool, APIError]:
42
+ response = await self.http.request_json(
43
+ url=self.request_url + method,
44
+ data=compose_data(self.http, data or {})
45
+ )
42
46
  if response.get("ok"):
43
47
  assert "result" in response
44
- return Result(True, value=response["result"])
45
-
46
- code, msg = response.get("error_code"), response.get("description")
47
- return Result(False, error=APIError(code, msg))
48
+ return Ok(response["result"])
49
+ return Error(APIError(
50
+ code=response.get("error_code", 0),
51
+ error=response.get("description"),
52
+ ))
48
53
 
49
54
  async def request_raw(
50
55
  self,
51
56
  method: str,
52
- data: typing.Optional[dict] = None,
57
+ data: dict[str, typing.Any] | None = None,
53
58
  ) -> Result[msgspec.Raw, APIError]:
54
- data = compose_data(self.http, data)
55
59
  response_bytes = await self.http.request_bytes(
56
- self.request_url + method, data=data
60
+ url=self.request_url + method,
61
+ data=compose_data(self.http, data or {}),
57
62
  )
58
- response_skeleton: APIResponse = msgspec.json.decode(
59
- response_bytes, type=APIResponse
60
- )
61
- return response_skeleton.to_result()
63
+ return decoder.decode(response_bytes, type=APIResponse).to_result()
64
+
65
+
66
+ __all__ = ("API",)
telegrinder/api/error.py CHANGED
@@ -1,6 +1,16 @@
1
- import typing
2
-
3
-
4
1
  class APIError(BaseException):
5
- def __init__(self, code: int, error: typing.Optional[str] = None):
2
+ def __init__(self, code: int, error: str | None = None):
6
3
  self.code, self.error = code, error
4
+
5
+ def __str__(self) -> str:
6
+ return f"[{self.code}] {self.error}"
7
+
8
+ def __repr__(self) -> str:
9
+ return f"<APIError {self.__str__()}>"
10
+
11
+
12
+ class InvalidTokenError(BaseException):
13
+ ...
14
+
15
+
16
+ __all__ = ("APIError", "InvalidTokenError")
@@ -1,16 +1,20 @@
1
- from telegrinder.model import Model
2
- from telegrinder.result import Result
3
- from telegrinder.api.error import APIError
4
1
  import msgspec
2
+ from fntypes.result import Error, Ok, Result
3
+
4
+ from telegrinder.api.error import APIError
5
+ from telegrinder.model import Model
5
6
 
6
7
 
7
8
  class APIResponse(Model):
8
- ok: bool
9
- result: msgspec.Raw = b""
9
+ ok: bool = False
10
+ result: msgspec.Raw = msgspec.Raw(b"")
10
11
  error_code: int = 0
11
12
  description: str = ""
12
13
 
13
14
  def to_result(self) -> Result[msgspec.Raw, APIError]:
14
15
  if self.ok:
15
- return Result(True, value=self.result)
16
- return Result(False, error=APIError(self.error_code, self.description))
16
+ return Ok(self.result)
17
+ return Error(APIError(self.error_code, self.description))
18
+
19
+
20
+ __all__ = ("APIResponse",)
@@ -1,15 +1,76 @@
1
- from .polling import ABCPolling, Polling
1
+ from .bot import Telegrinder
2
+ from .cute_types import (
3
+ BaseCute,
4
+ CallbackQueryCute,
5
+ InlineQueryCute,
6
+ MessageCute,
7
+ UpdateCute,
8
+ )
2
9
  from .dispatch import (
3
10
  ABCDispatch,
4
- Dispatch,
5
11
  ABCHandler,
12
+ ABCMiddleware,
13
+ ABCReturnManager,
14
+ ABCStateView,
6
15
  ABCView,
16
+ BaseReturnManager,
17
+ BaseStateView,
18
+ BaseView,
19
+ CallbackQueryReturnManager,
20
+ CallbackQueryView,
21
+ CompositionDispatch,
22
+ Context,
23
+ Dispatch,
7
24
  FuncHandler,
25
+ InlineQueryReturnManager,
26
+ Manager,
27
+ MessageReplyHandler,
28
+ MessageReturnManager,
8
29
  MessageView,
9
- CallbackQueryView,
10
- ABCMiddleware,
30
+ ViewBox,
31
+ WaiterMachine,
32
+ register_manager,
11
33
  )
12
- from .bot import Telegrinder
13
- from .cute_types import MessageCute, CallbackQueryCute, InlineQueryCute
14
- from .rules import ABCRule, ABCMessageRule
34
+ from .polling import ABCPolling, Polling
35
+ from .rules import ABCRule, CallbackQueryRule, MessageRule
15
36
  from .scenario import ABCScenario, Checkbox, SingleChoice
37
+
38
+ __all__ = (
39
+ "ABCDispatch",
40
+ "ABCHandler",
41
+ "ABCMiddleware",
42
+ "ABCPolling",
43
+ "ABCReturnManager",
44
+ "ABCRule",
45
+ "ABCScenario",
46
+ "ABCStateView",
47
+ "ABCView",
48
+ "BaseCute",
49
+ "BaseReturnManager",
50
+ "BaseStateView",
51
+ "BaseView",
52
+ "CallbackQueryCute",
53
+ "CallbackQueryReturnManager",
54
+ "CallbackQueryRule",
55
+ "CallbackQueryView",
56
+ "Checkbox",
57
+ "CompositionDispatch",
58
+ "Context",
59
+ "Dispatch",
60
+ "FuncHandler",
61
+ "InlineQueryCute",
62
+ "InlineQueryReturnManager",
63
+ "Manager",
64
+ "MessageCute",
65
+ "MessageReplyHandler",
66
+ "MessageReturnManager",
67
+ "MessageRule",
68
+ "MessageView",
69
+ "Polling",
70
+ "SingleChoice",
71
+ "Telegrinder",
72
+ "UpdateCute",
73
+ "ViewBox",
74
+ "WaiterMachine",
75
+ "register_manager",
76
+ )
telegrinder/bot/bot.py CHANGED
@@ -1,53 +1,59 @@
1
- import asyncio
1
+ import typing_extensions as typing
2
2
 
3
3
  from telegrinder.api import API
4
- from telegrinder.bot.polling import ABCPolling, Polling
5
4
  from telegrinder.bot.dispatch import ABCDispatch, Dispatch
5
+ from telegrinder.bot.polling import ABCPolling, Polling
6
6
  from telegrinder.modules import logger
7
- import typing
7
+ from telegrinder.tools.loop_wrapper import ABCLoopWrapper, LoopWrapper
8
+
9
+ DispatchT = typing.TypeVar("DispatchT", bound=ABCDispatch, default=Dispatch)
10
+ PollingT = typing.TypeVar("PollingT", bound=ABCPolling, default=Polling)
11
+ LoopWrapperT = typing.TypeVar("LoopWrapperT", bound=ABCLoopWrapper, default=LoopWrapper)
12
+
8
13
 
14
+ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
15
+ dispatch: DispatchT
16
+ polling: PollingT
17
+ loop_wrapper: LoopWrapperT
9
18
 
10
- class Telegrinder:
11
19
  def __init__(
12
20
  self,
13
21
  api: API,
14
- polling: typing.Optional[ABCPolling] = None,
15
- dispatch: typing.Optional[ABCDispatch] = None,
22
+ *,
23
+ polling: PollingT | None = None,
24
+ dispatch: DispatchT | None = None,
25
+ loop_wrapper: LoopWrapperT | None = None,
16
26
  ):
17
27
  self.api = api
18
- self.polling = polling or Polling(api)
19
- self.dispatch = dispatch or Dispatch()
28
+ self.dispatch = dispatch or Dispatch() # type: ignore
29
+ self.polling = polling or Polling(api) # type: ignore
30
+ self.loop_wrapper = loop_wrapper or LoopWrapper() # type: ignore
20
31
 
21
32
  @property
22
- def on(self) -> Dispatch:
23
- return self.dispatch # type: ignore
33
+ def on(self) -> DispatchT:
34
+ return self.dispatch
24
35
 
25
36
  async def reset_webhook(self) -> None:
26
37
  if not (await self.api.get_webhook_info()).unwrap().url:
27
38
  return
28
-
29
39
  await self.api.delete_webhook()
30
40
 
31
41
  async def run_polling(self, offset: int = 0, skip_updates: bool = False) -> None:
32
42
  if skip_updates:
33
- logger.debug("dropping pending updates")
43
+ logger.debug("Dropping pending updates")
34
44
  await self.reset_webhook()
35
45
  await self.api.delete_webhook(drop_pending_updates=True)
36
46
  self.polling.offset = offset
37
47
 
38
- loop = asyncio.get_running_loop()
39
- assert loop, "No running loop"
40
-
41
48
  async for updates in self.polling.listen():
42
49
  for update in updates:
43
- logger.debug("received update (update_id={})", update.update_id)
44
- loop.create_task(self.dispatch.feed(update, self.api))
50
+ logger.debug("Received update (update_id={})", update.update_id)
51
+ self.loop_wrapper.add_task(self.dispatch.feed(update, self.api))
45
52
 
46
53
  def run_forever(self, offset: int = 0, skip_updates: bool = False) -> None:
47
- logger.debug("running blocking polling (id={})", self.api.id)
48
- loop = asyncio.new_event_loop()
49
- loop.create_task(self.run_polling(offset, skip_updates=skip_updates))
50
- try:
51
- loop.run_forever()
52
- except KeyboardInterrupt:
53
- logger.info("KeyboardInterrupt")
54
+ logger.debug("Running blocking polling (id={})", self.api.id)
55
+ self.loop_wrapper.add_task(self.run_polling(offset, skip_updates=skip_updates))
56
+ self.loop_wrapper.run_event_loop()
57
+
58
+
59
+ __all__ = ("Telegrinder",)
@@ -1,3 +1,13 @@
1
- from .message import MessageCute
1
+ from .base import BaseCute
2
2
  from .callback_query import CallbackQueryCute
3
3
  from .inline_query import InlineQueryCute
4
+ from .message import MessageCute
5
+ from .update import UpdateCute
6
+
7
+ __all__ = (
8
+ "BaseCute",
9
+ "CallbackQueryCute",
10
+ "InlineQueryCute",
11
+ "MessageCute",
12
+ "UpdateCute",
13
+ )
@@ -0,0 +1,47 @@
1
+ import typing
2
+
3
+ from telegrinder.api import ABCAPI, API
4
+ from telegrinder.model import Model
5
+
6
+ UpdateT = typing.TypeVar("UpdateT", bound=Model)
7
+
8
+ if typing.TYPE_CHECKING:
9
+
10
+ class BaseCute(Model, typing.Generic[UpdateT]):
11
+ api: ABCAPI
12
+
13
+ @classmethod
14
+ def from_update(cls, update: UpdateT, bound_api: ABCAPI) -> typing.Self:
15
+ ...
16
+
17
+ @property
18
+ def ctx_api(self) -> API:
19
+ ...
20
+
21
+ def to_dict(
22
+ self,
23
+ *,
24
+ exclude_fields: set[str] | None = None,
25
+ ) -> dict[str, typing.Any]:
26
+ ...
27
+
28
+ else:
29
+
30
+ class BaseCute(typing.Generic[UpdateT]):
31
+ api: ABCAPI
32
+
33
+ @classmethod
34
+ def from_update(cls, update, bound_api):
35
+ return cls(**update.to_dict(), api=bound_api)
36
+
37
+ @property
38
+ def ctx_api(self):
39
+ assert isinstance(self.api, API)
40
+ return self.api
41
+
42
+ def to_dict(self, *, exclude_fields=None):
43
+ exclude_fields = exclude_fields or set()
44
+ return super().to_dict(exclude_fields={"api"} | exclude_fields)
45
+
46
+
47
+ __all__ = ("BaseCute",)