telegrinder 0.3.1__py3-none-any.whl → 0.3.3__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 (164) hide show
  1. telegrinder/__init__.py +144 -144
  2. telegrinder/api/__init__.py +8 -8
  3. telegrinder/api/api.py +93 -93
  4. telegrinder/api/error.py +16 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +66 -66
  8. telegrinder/bot/bot.py +76 -76
  9. telegrinder/bot/cute_types/__init__.py +17 -17
  10. telegrinder/bot/cute_types/base.py +258 -234
  11. telegrinder/bot/cute_types/callback_query.py +385 -382
  12. telegrinder/bot/cute_types/chat_join_request.py +61 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +160 -160
  14. telegrinder/bot/cute_types/inline_query.py +43 -53
  15. telegrinder/bot/cute_types/message.py +2637 -2631
  16. telegrinder/bot/cute_types/update.py +109 -75
  17. telegrinder/bot/cute_types/utils.py +95 -95
  18. telegrinder/bot/dispatch/__init__.py +55 -55
  19. telegrinder/bot/dispatch/abc.py +77 -77
  20. telegrinder/bot/dispatch/context.py +98 -92
  21. telegrinder/bot/dispatch/dispatch.py +202 -201
  22. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  23. telegrinder/bot/dispatch/handler/abc.py +24 -24
  24. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  25. telegrinder/bot/dispatch/handler/base.py +57 -57
  26. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  27. telegrinder/bot/dispatch/handler/func.py +135 -123
  28. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -43
  29. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  30. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  31. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  32. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  33. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  34. telegrinder/bot/dispatch/middleware/abc.py +16 -16
  35. telegrinder/bot/dispatch/process.py +132 -132
  36. telegrinder/bot/dispatch/return_manager/__init__.py +13 -13
  37. telegrinder/bot/dispatch/return_manager/abc.py +108 -108
  38. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  39. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  40. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  41. telegrinder/bot/dispatch/view/__init__.py +13 -13
  42. telegrinder/bot/dispatch/view/abc.py +41 -41
  43. telegrinder/bot/dispatch/view/base.py +200 -211
  44. telegrinder/bot/dispatch/view/box.py +129 -129
  45. telegrinder/bot/dispatch/view/callback_query.py +17 -17
  46. telegrinder/bot/dispatch/view/chat_join_request.py +16 -16
  47. telegrinder/bot/dispatch/view/chat_member.py +39 -39
  48. telegrinder/bot/dispatch/view/inline_query.py +17 -17
  49. telegrinder/bot/dispatch/view/message.py +44 -44
  50. telegrinder/bot/dispatch/view/raw.py +114 -118
  51. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  52. telegrinder/bot/dispatch/waiter_machine/actions.py +13 -13
  53. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  54. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
  55. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
  56. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
  57. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
  58. telegrinder/bot/dispatch/waiter_machine/machine.py +167 -170
  59. telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
  60. telegrinder/bot/dispatch/waiter_machine/short_state.py +68 -65
  61. telegrinder/bot/polling/__init__.py +4 -4
  62. telegrinder/bot/polling/abc.py +25 -25
  63. telegrinder/bot/polling/polling.py +131 -131
  64. telegrinder/bot/rules/__init__.py +62 -62
  65. telegrinder/bot/rules/abc.py +213 -238
  66. telegrinder/bot/rules/adapter/__init__.py +9 -9
  67. telegrinder/bot/rules/adapter/abc.py +29 -29
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +67 -76
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  72. telegrinder/bot/rules/callback_data.py +170 -171
  73. telegrinder/bot/rules/chat_join.py +46 -48
  74. telegrinder/bot/rules/command.py +126 -126
  75. telegrinder/bot/rules/enum_text.py +36 -36
  76. telegrinder/bot/rules/func.py +26 -26
  77. telegrinder/bot/rules/fuzzy.py +24 -24
  78. telegrinder/bot/rules/inline.py +60 -60
  79. telegrinder/bot/rules/integer.py +20 -20
  80. telegrinder/bot/rules/is_from.py +127 -127
  81. telegrinder/bot/rules/markup.py +43 -43
  82. telegrinder/bot/rules/mention.py +14 -14
  83. telegrinder/bot/rules/message.py +17 -17
  84. telegrinder/bot/rules/message_entities.py +35 -35
  85. telegrinder/bot/rules/node.py +27 -27
  86. telegrinder/bot/rules/regex.py +37 -37
  87. telegrinder/bot/rules/rule_enum.py +72 -72
  88. telegrinder/bot/rules/start.py +42 -42
  89. telegrinder/bot/rules/state.py +37 -37
  90. telegrinder/bot/rules/text.py +33 -33
  91. telegrinder/bot/rules/update.py +15 -15
  92. telegrinder/bot/scenario/__init__.py +5 -5
  93. telegrinder/bot/scenario/abc.py +19 -19
  94. telegrinder/bot/scenario/checkbox.py +167 -147
  95. telegrinder/bot/scenario/choice.py +46 -44
  96. telegrinder/client/__init__.py +4 -4
  97. telegrinder/client/abc.py +75 -75
  98. telegrinder/client/aiohttp.py +130 -130
  99. telegrinder/model.py +295 -244
  100. telegrinder/modules.py +237 -237
  101. telegrinder/msgspec_json.py +14 -14
  102. telegrinder/msgspec_utils.py +410 -410
  103. telegrinder/node/__init__.py +7 -3
  104. telegrinder/node/attachment.py +87 -87
  105. telegrinder/node/base.py +166 -144
  106. telegrinder/node/callback_query.py +53 -14
  107. telegrinder/node/command.py +33 -33
  108. telegrinder/node/composer.py +198 -184
  109. telegrinder/node/container.py +27 -27
  110. telegrinder/node/event.py +65 -73
  111. telegrinder/node/me.py +16 -16
  112. telegrinder/node/message.py +14 -14
  113. telegrinder/node/polymorphic.py +48 -52
  114. telegrinder/node/rule.py +76 -76
  115. telegrinder/node/scope.py +38 -38
  116. telegrinder/node/source.py +71 -71
  117. telegrinder/node/text.py +41 -21
  118. telegrinder/node/tools/__init__.py +3 -3
  119. telegrinder/node/tools/generator.py +40 -40
  120. telegrinder/node/update.py +15 -15
  121. telegrinder/rules.py +0 -0
  122. telegrinder/tools/__init__.py +74 -74
  123. telegrinder/tools/buttons.py +79 -79
  124. telegrinder/tools/error_handler/__init__.py +7 -7
  125. telegrinder/tools/error_handler/abc.py +33 -33
  126. telegrinder/tools/error_handler/error.py +9 -9
  127. telegrinder/tools/error_handler/error_handler.py +193 -193
  128. telegrinder/tools/formatting/__init__.py +46 -46
  129. telegrinder/tools/formatting/html.py +283 -283
  130. telegrinder/tools/formatting/links.py +33 -33
  131. telegrinder/tools/formatting/spec_html_formats.py +111 -111
  132. telegrinder/tools/functional.py +12 -12
  133. telegrinder/tools/global_context/__init__.py +7 -7
  134. telegrinder/tools/global_context/abc.py +63 -63
  135. telegrinder/tools/global_context/global_context.py +412 -412
  136. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  137. telegrinder/tools/i18n/__init__.py +7 -7
  138. telegrinder/tools/i18n/abc.py +30 -30
  139. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  140. telegrinder/tools/i18n/middleware/abc.py +25 -25
  141. telegrinder/tools/i18n/simple.py +43 -43
  142. telegrinder/tools/kb_set/__init__.py +4 -4
  143. telegrinder/tools/kb_set/base.py +15 -15
  144. telegrinder/tools/kb_set/yaml.py +63 -63
  145. telegrinder/tools/keyboard.py +132 -132
  146. telegrinder/tools/limited_dict.py +37 -37
  147. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  148. telegrinder/tools/loop_wrapper/abc.py +15 -15
  149. telegrinder/tools/loop_wrapper/loop_wrapper.py +224 -216
  150. telegrinder/tools/magic.py +157 -157
  151. telegrinder/tools/parse_mode.py +6 -6
  152. telegrinder/tools/state_storage/__init__.py +4 -4
  153. telegrinder/tools/state_storage/abc.py +35 -35
  154. telegrinder/tools/state_storage/memory.py +25 -25
  155. telegrinder/types/__init__.py +260 -260
  156. telegrinder/types/enums.py +701 -701
  157. telegrinder/types/methods.py +4633 -4633
  158. telegrinder/types/objects.py +8561 -6541
  159. telegrinder/verification_utils.py +32 -32
  160. {telegrinder-0.3.1.dist-info → telegrinder-0.3.3.dist-info}/LICENSE +22 -22
  161. {telegrinder-0.3.1.dist-info → telegrinder-0.3.3.dist-info}/METADATA +1 -1
  162. telegrinder-0.3.3.dist-info/RECORD +164 -0
  163. telegrinder-0.3.1.dist-info/RECORD +0 -164
  164. {telegrinder-0.3.1.dist-info → telegrinder-0.3.3.dist-info}/WHEEL +0 -0
telegrinder/__init__.py CHANGED
@@ -1,145 +1,145 @@
1
- """Telegrinder
2
-
3
- Modern visionary telegram bot framework.
4
-
5
- * Type hinted
6
- * Customizable and extensible
7
- * Ready to use scenarios and rules
8
- * Fast models built on [msgspec](https://github.com/jcrist/msgspec)
9
- * Both low-level and high-level API
10
- * Support [optional dependecies](https://github.com/timoniq/telegrinder/blob/dev/docs/guide/optional_dependencies.md)
11
-
12
- Basic example:
13
-
14
- ```python
15
- from telegrinder import API, Message, Telegrinder, Token
16
- from telegrinder.modules import logger
17
- from telegrinder.rules import Text
18
-
19
- api = API(token=Token("123:token"))
20
- bot = Telegrinder(api)
21
- logger.set_level("INFO")
22
-
23
-
24
- @bot.on.message(Text("/start"))
25
- async def start(message: Message):
26
- me = (await api.get_me()).unwrap()
27
- await message.answer(f"Hello, {message.from_user.full_name}! I'm {me.full_name}.")
28
-
29
-
30
- bot.run_forever()
31
- ```
32
- """
33
-
34
- import typing
35
-
36
- from .api import API, APIError, APIResponse, Token
37
- from .bot import (
38
- CALLBACK_QUERY_FOR_MESSAGE,
39
- CALLBACK_QUERY_FROM_CHAT,
40
- CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE,
41
- MESSAGE_FROM_USER,
42
- MESSAGE_FROM_USER_IN_CHAT,
43
- MESSAGE_IN_CHAT,
44
- ABCDispatch,
45
- ABCHandler,
46
- ABCMiddleware,
47
- ABCPolling,
48
- ABCReturnManager,
49
- ABCRule,
50
- ABCScenario,
51
- ABCStateView,
52
- ABCView,
53
- AudioReplyHandler,
54
- BaseCute,
55
- BaseReturnManager,
56
- BaseStateView,
57
- BaseView,
58
- CallbackQueryCute,
59
- CallbackQueryReturnManager,
60
- CallbackQueryRule,
61
- CallbackQueryView,
62
- ChatJoinRequestCute,
63
- ChatJoinRequestRule,
64
- ChatJoinRequestView,
65
- ChatMemberUpdatedCute,
66
- ChatMemberView,
67
- Checkbox,
68
- Choice,
69
- Context,
70
- Dispatch,
71
- DocumentReplyHandler,
72
- FuncHandler,
73
- Hasher,
74
- InlineQueryCute,
75
- InlineQueryReturnManager,
76
- InlineQueryRule,
77
- MediaGroupReplyHandler,
78
- MessageCute,
79
- MessageReplyHandler,
80
- MessageReturnManager,
81
- MessageRule,
82
- MessageView,
83
- PhotoReplyHandler,
84
- Polling,
85
- RawEventView,
86
- ShortState,
87
- StateViewHasher,
88
- StickerReplyHandler,
89
- Telegrinder,
90
- UpdateCute,
91
- VideoReplyHandler,
92
- ViewBox,
93
- WaiterMachine,
94
- register_manager,
95
- )
96
- from .bot.rules import StateMeta
97
- from .client import ABCClient, AiohttpClient
98
- from .model import Model
99
- from .modules import logger
100
- from .tools import (
101
- ABCErrorHandler,
102
- ABCGlobalContext,
103
- ABCLoopWrapper,
104
- ABCStateStorage,
105
- ABCTranslator,
106
- ABCTranslatorMiddleware,
107
- AnyMarkup,
108
- Button,
109
- CtxVar,
110
- DelayedTask,
111
- ErrorHandler,
112
- FormatString,
113
- GlobalContext,
114
- HTMLFormatter,
115
- I18nEnum,
116
- InlineButton,
117
- InlineKeyboard,
118
- Keyboard,
119
- KeyboardSetBase,
120
- KeyboardSetYAML,
121
- Lifespan,
122
- LoopWrapper,
123
- MemoryStateStorage,
124
- ParseMode,
125
- RowButtons,
126
- SimpleI18n,
127
- SimpleTranslator,
128
- StateData,
129
- ctx_var,
130
- magic_bundle,
131
- )
132
-
133
- Update: typing.TypeAlias = UpdateCute
134
- Message: typing.TypeAlias = MessageCute
135
- ChatJoinRequest: typing.TypeAlias = ChatJoinRequestCute
136
- ChatMemberUpdated: typing.TypeAlias = ChatMemberUpdatedCute
137
- CallbackQuery: typing.TypeAlias = CallbackQueryCute
138
- InlineQuery: typing.TypeAlias = InlineQueryCute
139
- Bot: typing.TypeAlias = Telegrinder
140
-
141
-
142
- __all__ = (
1
+ """Telegrinder
2
+
3
+ Modern visionary telegram bot framework.
4
+
5
+ * Type hinted
6
+ * Customizable and extensible
7
+ * Ready to use scenarios and rules
8
+ * Fast models built on [msgspec](https://github.com/jcrist/msgspec)
9
+ * Both low-level and high-level API
10
+ * Support [optional dependecies](https://github.com/timoniq/telegrinder/blob/dev/docs/guide/optional_dependencies.md)
11
+
12
+ Basic example:
13
+
14
+ ```python
15
+ from telegrinder import API, Message, Telegrinder, Token
16
+ from telegrinder.modules import logger
17
+ from telegrinder.rules import Text
18
+
19
+ api = API(token=Token("123:token"))
20
+ bot = Telegrinder(api)
21
+ logger.set_level("INFO")
22
+
23
+
24
+ @bot.on.message(Text("/start"))
25
+ async def start(message: Message):
26
+ me = (await api.get_me()).unwrap()
27
+ await message.answer(f"Hello, {message.from_user.full_name}! I'm {me.full_name}.")
28
+
29
+
30
+ bot.run_forever()
31
+ ```
32
+ """
33
+
34
+ import typing
35
+
36
+ from .api import API, APIError, APIResponse, Token
37
+ from .bot import (
38
+ CALLBACK_QUERY_FOR_MESSAGE,
39
+ CALLBACK_QUERY_FROM_CHAT,
40
+ CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE,
41
+ MESSAGE_FROM_USER,
42
+ MESSAGE_FROM_USER_IN_CHAT,
43
+ MESSAGE_IN_CHAT,
44
+ ABCDispatch,
45
+ ABCHandler,
46
+ ABCMiddleware,
47
+ ABCPolling,
48
+ ABCReturnManager,
49
+ ABCRule,
50
+ ABCScenario,
51
+ ABCStateView,
52
+ ABCView,
53
+ AudioReplyHandler,
54
+ BaseCute,
55
+ BaseReturnManager,
56
+ BaseStateView,
57
+ BaseView,
58
+ CallbackQueryCute,
59
+ CallbackQueryReturnManager,
60
+ CallbackQueryRule,
61
+ CallbackQueryView,
62
+ ChatJoinRequestCute,
63
+ ChatJoinRequestRule,
64
+ ChatJoinRequestView,
65
+ ChatMemberUpdatedCute,
66
+ ChatMemberView,
67
+ Checkbox,
68
+ Choice,
69
+ Context,
70
+ Dispatch,
71
+ DocumentReplyHandler,
72
+ FuncHandler,
73
+ Hasher,
74
+ InlineQueryCute,
75
+ InlineQueryReturnManager,
76
+ InlineQueryRule,
77
+ MediaGroupReplyHandler,
78
+ MessageCute,
79
+ MessageReplyHandler,
80
+ MessageReturnManager,
81
+ MessageRule,
82
+ MessageView,
83
+ PhotoReplyHandler,
84
+ Polling,
85
+ RawEventView,
86
+ ShortState,
87
+ StateViewHasher,
88
+ StickerReplyHandler,
89
+ Telegrinder,
90
+ UpdateCute,
91
+ VideoReplyHandler,
92
+ ViewBox,
93
+ WaiterMachine,
94
+ register_manager,
95
+ )
96
+ from .bot.rules import StateMeta
97
+ from .client import ABCClient, AiohttpClient
98
+ from .model import Model
99
+ from .modules import logger
100
+ from .tools import (
101
+ ABCErrorHandler,
102
+ ABCGlobalContext,
103
+ ABCLoopWrapper,
104
+ ABCStateStorage,
105
+ ABCTranslator,
106
+ ABCTranslatorMiddleware,
107
+ AnyMarkup,
108
+ Button,
109
+ CtxVar,
110
+ DelayedTask,
111
+ ErrorHandler,
112
+ FormatString,
113
+ GlobalContext,
114
+ HTMLFormatter,
115
+ I18nEnum,
116
+ InlineButton,
117
+ InlineKeyboard,
118
+ Keyboard,
119
+ KeyboardSetBase,
120
+ KeyboardSetYAML,
121
+ Lifespan,
122
+ LoopWrapper,
123
+ MemoryStateStorage,
124
+ ParseMode,
125
+ RowButtons,
126
+ SimpleI18n,
127
+ SimpleTranslator,
128
+ StateData,
129
+ ctx_var,
130
+ magic_bundle,
131
+ )
132
+
133
+ Update: typing.TypeAlias = UpdateCute
134
+ Message: typing.TypeAlias = MessageCute
135
+ ChatJoinRequest: typing.TypeAlias = ChatJoinRequestCute
136
+ ChatMemberUpdated: typing.TypeAlias = ChatMemberUpdatedCute
137
+ CallbackQuery: typing.TypeAlias = CallbackQueryCute
138
+ InlineQuery: typing.TypeAlias = InlineQueryCute
139
+ Bot: typing.TypeAlias = Telegrinder
140
+
141
+
142
+ __all__ = (
143
143
  "ABCClient",
144
144
  "ABCDispatch",
145
145
  "ABCErrorHandler",
@@ -247,5 +247,5 @@ __all__ = (
247
247
  "ctx_var",
248
248
  "logger",
249
249
  "magic_bundle",
250
- "register_manager",
251
- )
250
+ "register_manager",
251
+ )
@@ -1,12 +1,12 @@
1
- from .api import API
2
- from .error import APIError, InvalidTokenError
3
- from .response import APIResponse
4
- from .token import Token
5
-
6
- __all__ = (
1
+ from .api import API
2
+ from .error import APIError, InvalidTokenError
3
+ from .response import APIResponse
4
+ from .token import Token
5
+
6
+ __all__ = (
7
7
  "API",
8
8
  "APIError",
9
9
  "APIResponse",
10
10
  "InvalidTokenError",
11
- "Token",
12
- )
11
+ "Token",
12
+ )
telegrinder/api/api.py CHANGED
@@ -1,93 +1,93 @@
1
- import typing
2
- from functools import cached_property
3
-
4
- import msgspec
5
- from fntypes.result import Error, Ok, Result
6
-
7
- from telegrinder.api.error import APIError
8
- from telegrinder.api.response import APIResponse
9
- from telegrinder.api.token import Token
10
- from telegrinder.client import ABCClient, AiohttpClient
11
- from telegrinder.model import DataConverter, decoder
12
- from telegrinder.types.methods import APIMethods
13
-
14
-
15
- def compose_data(
16
- client: ABCClient,
17
- data: dict[str, typing.Any],
18
- files: dict[str, tuple[str, bytes]],
19
- ) -> typing.Any:
20
- converter = DataConverter(_files=files.copy())
21
- return client.get_form(
22
- data={k: converter(v) for k, v in data.items()},
23
- files=converter.files,
24
- )
25
-
26
-
27
- class API(APIMethods):
28
- """Bot API with available API methods and http client."""
29
-
30
- API_URL = "https://api.telegram.org/"
31
- API_FILE_URL = "https://api.telegram.org/file/"
32
-
33
- def __init__(self, token: Token, *, http: ABCClient | None = None) -> None:
34
- self.token = token
35
- self.http = http or AiohttpClient()
36
- super().__init__(self)
37
-
38
- def __repr__(self) -> str:
39
- return "<{}: token={!r}, http={!r}>".format(
40
- self.__class__.__name__,
41
- self.token,
42
- self.http,
43
- )
44
-
45
- @cached_property
46
- def id(self) -> int:
47
- return self.token.bot_id
48
-
49
- @property
50
- def request_url(self) -> str:
51
- return self.API_URL + f"bot{self.token}/"
52
-
53
- @property
54
- def request_file_url(self) -> str:
55
- return self.API_FILE_URL + f"bot{self.token}/"
56
-
57
- async def download_file(self, file_path: str) -> bytes:
58
- return await self.http.request_content(f"{self.request_file_url}/{file_path}")
59
-
60
- async def request(
61
- self,
62
- method: str,
63
- data: dict[str, typing.Any] | None = None,
64
- files: dict[str, tuple[str, bytes]] | None = None,
65
- ) -> Result[dict[str, typing.Any] | list[typing.Any] | bool, APIError]:
66
- response = await self.http.request_json(
67
- url=self.request_url + method,
68
- data=compose_data(self.http, data or {}, files or {}),
69
- )
70
- if response.get("ok"):
71
- assert "result" in response
72
- return Ok(response["result"])
73
- return Error(
74
- APIError(
75
- code=response.get("error_code", 400),
76
- error=response.get("description"),
77
- )
78
- )
79
-
80
- async def request_raw(
81
- self,
82
- method: str,
83
- data: dict[str, typing.Any] | None = None,
84
- files: dict[str, tuple[str, bytes]] | None = None,
85
- ) -> Result[msgspec.Raw, APIError]:
86
- response_bytes = await self.http.request_bytes(
87
- url=self.request_url + method,
88
- data=compose_data(self.http, data or {}, files or {}),
89
- )
90
- return decoder.decode(response_bytes, type=APIResponse).to_result()
91
-
92
-
93
- __all__ = ("API",)
1
+ import typing
2
+ from functools import cached_property
3
+
4
+ import msgspec
5
+ from fntypes.result import Error, Ok, Result
6
+
7
+ from telegrinder.api.error import APIError
8
+ from telegrinder.api.response import APIResponse
9
+ from telegrinder.api.token import Token
10
+ from telegrinder.client import ABCClient, AiohttpClient
11
+ from telegrinder.model import DataConverter, decoder
12
+ from telegrinder.types.methods import APIMethods
13
+
14
+
15
+ def compose_data(
16
+ client: ABCClient,
17
+ data: dict[str, typing.Any],
18
+ files: dict[str, tuple[str, bytes]],
19
+ ) -> typing.Any:
20
+ converter = DataConverter(_files=files.copy())
21
+ return client.get_form(
22
+ data={k: converter(v) for k, v in data.items()},
23
+ files=converter.files,
24
+ )
25
+
26
+
27
+ class API(APIMethods):
28
+ """Bot API with available API methods and http client."""
29
+
30
+ API_URL = "https://api.telegram.org/"
31
+ API_FILE_URL = "https://api.telegram.org/file/"
32
+
33
+ def __init__(self, token: Token, *, http: ABCClient | None = None) -> None:
34
+ self.token = token
35
+ self.http = http or AiohttpClient()
36
+ super().__init__(self)
37
+
38
+ def __repr__(self) -> str:
39
+ return "<{}: token={!r}, http={!r}>".format(
40
+ self.__class__.__name__,
41
+ self.token,
42
+ self.http,
43
+ )
44
+
45
+ @cached_property
46
+ def id(self) -> int:
47
+ return self.token.bot_id
48
+
49
+ @property
50
+ def request_url(self) -> str:
51
+ return self.API_URL + f"bot{self.token}/"
52
+
53
+ @property
54
+ def request_file_url(self) -> str:
55
+ return self.API_FILE_URL + f"bot{self.token}/"
56
+
57
+ async def download_file(self, file_path: str) -> bytes:
58
+ return await self.http.request_content(f"{self.request_file_url}/{file_path}")
59
+
60
+ async def request(
61
+ self,
62
+ method: str,
63
+ data: dict[str, typing.Any] | None = None,
64
+ files: dict[str, tuple[str, bytes]] | None = None,
65
+ ) -> Result[dict[str, typing.Any] | list[typing.Any] | bool, APIError]:
66
+ response = await self.http.request_json(
67
+ url=self.request_url + method,
68
+ data=compose_data(self.http, data or {}, files or {}),
69
+ )
70
+ if response.get("ok"):
71
+ assert "result" in response
72
+ return Ok(response["result"])
73
+ return Error(
74
+ APIError(
75
+ code=response.get("error_code", 400),
76
+ error=response.get("description"),
77
+ )
78
+ )
79
+
80
+ async def request_raw(
81
+ self,
82
+ method: str,
83
+ data: dict[str, typing.Any] | None = None,
84
+ files: dict[str, tuple[str, bytes]] | None = None,
85
+ ) -> Result[msgspec.Raw, APIError]:
86
+ response_bytes = await self.http.request_bytes(
87
+ url=self.request_url + method,
88
+ data=compose_data(self.http, data or {}, files or {}),
89
+ )
90
+ return decoder.decode(response_bytes, type=APIResponse).to_result()
91
+
92
+
93
+ __all__ = ("API",)
telegrinder/api/error.py CHANGED
@@ -1,16 +1,16 @@
1
- class APIError(BaseException):
2
- def __init__(self, code: int, error: str | None = None) -> None:
3
- self.code, self.error = code, error
4
-
5
- def __str__(self) -> str:
6
- return f"[{self.code}] {self.error or 'Something went wrong'}"
7
-
8
- def __repr__(self) -> str:
9
- return f"<APIError: {self.__str__()}>"
10
-
11
-
12
- class InvalidTokenError(BaseException):
13
- pass
14
-
15
-
16
- __all__ = ("APIError", "InvalidTokenError")
1
+ class APIError(BaseException):
2
+ def __init__(self, code: int, error: str | None = None) -> None:
3
+ self.code, self.error = code, error
4
+
5
+ def __str__(self) -> str:
6
+ return f"[{self.code}] {self.error or 'Something went wrong'}"
7
+
8
+ def __repr__(self) -> str:
9
+ return f"<APIError: {self.__str__()}>"
10
+
11
+
12
+ class InvalidTokenError(BaseException):
13
+ pass
14
+
15
+
16
+ __all__ = ("APIError", "InvalidTokenError")
@@ -1,20 +1,20 @@
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
6
-
7
-
8
- class APIResponse(Model):
9
- ok: bool = False
10
- result: msgspec.Raw = msgspec.Raw(b"")
11
- error_code: int = 0
12
- description: str = ""
13
-
14
- def to_result(self) -> Result[msgspec.Raw, APIError]:
15
- if self.ok:
16
- return Ok(self.result)
17
- return Error(APIError(self.error_code, self.description))
18
-
19
-
20
- __all__ = ("APIResponse",)
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
6
+
7
+
8
+ class APIResponse(Model):
9
+ ok: bool = False
10
+ result: msgspec.Raw = msgspec.Raw(b"")
11
+ error_code: int = 0
12
+ description: str = ""
13
+
14
+ def to_result(self) -> Result[msgspec.Raw, APIError]:
15
+ if self.ok:
16
+ return Ok(self.result)
17
+ return Error(APIError(self.error_code, self.description))
18
+
19
+
20
+ __all__ = ("APIResponse",)
telegrinder/api/token.py CHANGED
@@ -1,36 +1,36 @@
1
- import pathlib
2
- import typing
3
- from functools import cached_property
4
-
5
- from envparse import env
6
-
7
- from .error import InvalidTokenError
8
-
9
-
10
- class Token(str):
11
- def __new__(cls, token: str) -> typing.Self:
12
- if token.count(":") != 1 or not token.split(":")[0].isdigit():
13
- raise InvalidTokenError("Invalid token, it should look like this '123:ABC'.")
14
- return super().__new__(cls, token)
15
-
16
- def __repr__(self) -> str:
17
- return f"<Token: {self.bot_id}:{''.join(self.split(':')[-1])[:6]}...>"
18
-
19
- @classmethod
20
- def from_env(
21
- cls,
22
- var_name: str = "BOT_TOKEN",
23
- *,
24
- is_read: bool = False,
25
- path_to_envfile: str | pathlib.Path | None = None,
26
- ) -> typing.Self:
27
- if not is_read:
28
- env.read_envfile(path_to_envfile)
29
- return cls(env.str(var_name))
30
-
31
- @cached_property
32
- def bot_id(self) -> int:
33
- return int(self.split(":")[0])
34
-
35
-
36
- __all__ = ("Token",)
1
+ import pathlib
2
+ import typing
3
+ from functools import cached_property
4
+
5
+ from envparse import env
6
+
7
+ from .error import InvalidTokenError
8
+
9
+
10
+ class Token(str):
11
+ def __new__(cls, token: str) -> typing.Self:
12
+ if token.count(":") != 1 or not token.split(":")[0].isdigit():
13
+ raise InvalidTokenError("Invalid token, it should look like this '123:ABC'.")
14
+ return super().__new__(cls, token)
15
+
16
+ def __repr__(self) -> str:
17
+ return f"<Token: {self.bot_id}:{''.join(self.split(':')[-1])[:6]}...>"
18
+
19
+ @classmethod
20
+ def from_env(
21
+ cls,
22
+ var_name: str = "BOT_TOKEN",
23
+ *,
24
+ is_read: bool = False,
25
+ path_to_envfile: str | pathlib.Path | None = None,
26
+ ) -> typing.Self:
27
+ if not is_read:
28
+ env.read_envfile(path_to_envfile)
29
+ return cls(env.str(var_name))
30
+
31
+ @cached_property
32
+ def bot_id(self) -> int:
33
+ return int(self.split(":")[0])
34
+
35
+
36
+ __all__ = ("Token",)