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
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:ABC'.")
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[str, typing.Any] | None = None,
46
+ ) -> Result[list[typing.Any] | dict[str, typing.Any] | 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[str, typing.Any] | 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,32 +1,46 @@
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 DataConverter, decoder
7
9
  from telegrinder.types.methods import APIMethods
8
- from telegrinder.model import convert
9
- from telegrinder.api.response import APIResponse
10
+
11
+ from .abc import ABCAPI, APIError, Token
10
12
 
11
13
 
12
- def compose_data(client: ABCClient, data: dict) -> typing.Any:
13
- data = {k: convert(v) for k, v in data.items()}
14
- if any(isinstance(v, tuple) for v in data.values()):
15
- data = client.get_form(data)
16
- return data
14
+ def compose_data(
15
+ client: ABCClient,
16
+ data: dict[str, typing.Any],
17
+ files: dict[str, tuple[str, bytes]],
18
+ ) -> typing.Any:
19
+ converter = DataConverter(files=files)
20
+ return client.get_form(
21
+ data={k: converter.convert(v) for k, v in data.items()},
22
+ files=converter.files,
23
+ )
17
24
 
18
25
 
19
26
  class API(ABCAPI, APIMethods):
20
- API_URL = "https://api.telegram.org/"
27
+ API_URL: typing.ClassVar[str] = "https://api.telegram.org/"
21
28
 
22
- def __init__(self, token: Token, http: typing.Optional[ABCClient] = None):
29
+ def __init__(self, token: Token, *, http: ABCClient | None = None) -> None:
23
30
  self.token = token
24
31
  self.http = http or AiohttpClient()
25
32
  super().__init__(self)
33
+
34
+ def __repr__(self) -> str:
35
+ return "<{}: id={}, http={!r}>".format(
36
+ self.__class__.__name__,
37
+ self.id,
38
+ self.http,
39
+ )
26
40
 
27
41
  @property
28
42
  def id(self) -> int:
29
- return int(self.token.split(":")[0])
43
+ return self.token.bot_id
30
44
 
31
45
  @property
32
46
  def request_url(self) -> str:
@@ -35,27 +49,32 @@ class API(ABCAPI, APIMethods):
35
49
  async def request(
36
50
  self,
37
51
  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)
52
+ data: dict[str, typing.Any] | None = None,
53
+ files: dict[str, tuple[str, bytes]] | None = None,
54
+ ) -> Result[dict[str, typing.Any] | list[typing.Any] | bool, APIError]:
55
+ response = await self.http.request_json(
56
+ url=self.request_url + method,
57
+ data=compose_data(self.http, data or {}, files or {})
58
+ )
42
59
  if response.get("ok"):
43
60
  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))
61
+ return Ok(response["result"])
62
+ return Error(APIError(
63
+ code=response.get("error_code", 0),
64
+ error=response.get("description"),
65
+ ))
48
66
 
49
67
  async def request_raw(
50
68
  self,
51
69
  method: str,
52
- data: typing.Optional[dict] = None,
70
+ data: dict[str, typing.Any] | None = None,
71
+ files: dict[str, tuple[str, bytes]] | None = None,
53
72
  ) -> Result[msgspec.Raw, APIError]:
54
- data = compose_data(self.http, data)
55
73
  response_bytes = await self.http.request_bytes(
56
- self.request_url + method, data=data
74
+ url=self.request_url + method,
75
+ data=compose_data(self.http, data or {}, files or {}),
57
76
  )
58
- response_skeleton: APIResponse = msgspec.json.decode(
59
- response_bytes, type=APIResponse
60
- )
61
- return response_skeleton.to_result()
77
+ return decoder.decode(response_bytes, type=APIResponse).to_result()
78
+
79
+
80
+ __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
+ )