telegrinder 0.3.4__py3-none-any.whl → 0.3.4.post1__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 (165) 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 -258
  11. telegrinder/bot/cute_types/callback_query.py +385 -385
  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 -43
  15. telegrinder/bot/cute_types/message.py +2637 -2637
  16. telegrinder/bot/cute_types/update.py +104 -104
  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 -98
  21. telegrinder/bot/dispatch/dispatch.py +202 -202
  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 -135
  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 +22 -22
  35. telegrinder/bot/dispatch/process.py +157 -157
  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 -200
  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 -114
  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 +172 -172
  59. telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
  60. telegrinder/bot/dispatch/waiter_machine/short_state.py +68 -68
  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 +206 -206
  66. telegrinder/bot/rules/adapter/__init__.py +17 -17
  67. telegrinder/bot/rules/adapter/abc.py +31 -31
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +65 -65
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_event.py +27 -27
  72. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  73. telegrinder/bot/rules/callback_data.py +163 -163
  74. telegrinder/bot/rules/chat_join.py +43 -43
  75. telegrinder/bot/rules/command.py +126 -126
  76. telegrinder/bot/rules/enum_text.py +36 -36
  77. telegrinder/bot/rules/func.py +26 -26
  78. telegrinder/bot/rules/fuzzy.py +24 -24
  79. telegrinder/bot/rules/inline.py +56 -56
  80. telegrinder/bot/rules/integer.py +20 -20
  81. telegrinder/bot/rules/is_from.py +127 -127
  82. telegrinder/bot/rules/markup.py +43 -43
  83. telegrinder/bot/rules/mention.py +14 -14
  84. telegrinder/bot/rules/message.py +17 -17
  85. telegrinder/bot/rules/message_entities.py +35 -35
  86. telegrinder/bot/rules/node.py +27 -27
  87. telegrinder/bot/rules/regex.py +37 -37
  88. telegrinder/bot/rules/rule_enum.py +72 -72
  89. telegrinder/bot/rules/start.py +42 -42
  90. telegrinder/bot/rules/state.py +37 -37
  91. telegrinder/bot/rules/text.py +33 -33
  92. telegrinder/bot/rules/update.py +15 -15
  93. telegrinder/bot/scenario/__init__.py +5 -5
  94. telegrinder/bot/scenario/abc.py +19 -19
  95. telegrinder/bot/scenario/checkbox.py +176 -176
  96. telegrinder/bot/scenario/choice.py +51 -51
  97. telegrinder/client/__init__.py +4 -4
  98. telegrinder/client/abc.py +75 -75
  99. telegrinder/client/aiohttp.py +130 -130
  100. telegrinder/model.py +313 -313
  101. telegrinder/modules.py +237 -237
  102. telegrinder/msgspec_json.py +14 -14
  103. telegrinder/msgspec_utils.py +410 -410
  104. telegrinder/node/__init__.py +20 -20
  105. telegrinder/node/attachment.py +87 -87
  106. telegrinder/node/base.py +157 -157
  107. telegrinder/node/callback_query.py +53 -53
  108. telegrinder/node/command.py +33 -33
  109. telegrinder/node/composer.py +198 -198
  110. telegrinder/node/container.py +27 -27
  111. telegrinder/node/event.py +65 -65
  112. telegrinder/node/me.py +16 -16
  113. telegrinder/node/message.py +14 -14
  114. telegrinder/node/polymorphic.py +48 -48
  115. telegrinder/node/rule.py +76 -76
  116. telegrinder/node/scope.py +38 -38
  117. telegrinder/node/source.py +71 -71
  118. telegrinder/node/text.py +41 -41
  119. telegrinder/node/tools/__init__.py +3 -3
  120. telegrinder/node/tools/generator.py +40 -40
  121. telegrinder/node/update.py +15 -15
  122. telegrinder/rules.py +5 -5
  123. telegrinder/tools/__init__.py +74 -74
  124. telegrinder/tools/buttons.py +79 -79
  125. telegrinder/tools/error_handler/__init__.py +7 -7
  126. telegrinder/tools/error_handler/abc.py +33 -33
  127. telegrinder/tools/error_handler/error.py +9 -9
  128. telegrinder/tools/error_handler/error_handler.py +193 -193
  129. telegrinder/tools/formatting/__init__.py +46 -46
  130. telegrinder/tools/formatting/html.py +283 -283
  131. telegrinder/tools/formatting/links.py +33 -33
  132. telegrinder/tools/formatting/spec_html_formats.py +111 -111
  133. telegrinder/tools/functional.py +12 -12
  134. telegrinder/tools/global_context/__init__.py +7 -7
  135. telegrinder/tools/global_context/abc.py +63 -63
  136. telegrinder/tools/global_context/global_context.py +412 -412
  137. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  138. telegrinder/tools/i18n/__init__.py +7 -7
  139. telegrinder/tools/i18n/abc.py +30 -30
  140. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  141. telegrinder/tools/i18n/middleware/abc.py +25 -25
  142. telegrinder/tools/i18n/simple.py +43 -43
  143. telegrinder/tools/kb_set/__init__.py +4 -4
  144. telegrinder/tools/kb_set/base.py +15 -15
  145. telegrinder/tools/kb_set/yaml.py +63 -63
  146. telegrinder/tools/keyboard.py +128 -128
  147. telegrinder/tools/limited_dict.py +37 -37
  148. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  149. telegrinder/tools/loop_wrapper/abc.py +15 -15
  150. telegrinder/tools/loop_wrapper/loop_wrapper.py +224 -224
  151. telegrinder/tools/magic.py +157 -157
  152. telegrinder/tools/parse_mode.py +6 -6
  153. telegrinder/tools/state_storage/__init__.py +4 -4
  154. telegrinder/tools/state_storage/abc.py +35 -35
  155. telegrinder/tools/state_storage/memory.py +25 -25
  156. telegrinder/types/__init__.py +260 -260
  157. telegrinder/types/enums.py +701 -701
  158. telegrinder/types/methods.py +4633 -4633
  159. telegrinder/types/objects.py +6950 -6950
  160. telegrinder/verification_utils.py +32 -32
  161. {telegrinder-0.3.4.dist-info → telegrinder-0.3.4.post1.dist-info}/LICENSE +22 -22
  162. {telegrinder-0.3.4.dist-info → telegrinder-0.3.4.post1.dist-info}/METADATA +1 -1
  163. telegrinder-0.3.4.post1.dist-info/RECORD +165 -0
  164. telegrinder-0.3.4.dist-info/RECORD +0 -165
  165. {telegrinder-0.3.4.dist-info → telegrinder-0.3.4.post1.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",)