telegrinder 0.4.2__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of telegrinder might be problematic. Click here for more details.

Files changed (233) hide show
  1. telegrinder/__init__.py +37 -55
  2. telegrinder/__meta__.py +1 -0
  3. telegrinder/api/__init__.py +6 -4
  4. telegrinder/api/api.py +100 -26
  5. telegrinder/api/error.py +42 -8
  6. telegrinder/api/response.py +4 -1
  7. telegrinder/api/token.py +2 -2
  8. telegrinder/bot/__init__.py +9 -25
  9. telegrinder/bot/bot.py +31 -25
  10. telegrinder/bot/cute_types/__init__.py +0 -0
  11. telegrinder/bot/cute_types/base.py +103 -61
  12. telegrinder/bot/cute_types/callback_query.py +447 -400
  13. telegrinder/bot/cute_types/chat_join_request.py +59 -62
  14. telegrinder/bot/cute_types/chat_member_updated.py +154 -157
  15. telegrinder/bot/cute_types/inline_query.py +41 -44
  16. telegrinder/bot/cute_types/message.py +2621 -2590
  17. telegrinder/bot/cute_types/pre_checkout_query.py +38 -42
  18. telegrinder/bot/cute_types/update.py +1 -8
  19. telegrinder/bot/cute_types/utils.py +1 -1
  20. telegrinder/bot/dispatch/__init__.py +10 -15
  21. telegrinder/bot/dispatch/abc.py +12 -11
  22. telegrinder/bot/dispatch/action.py +104 -0
  23. telegrinder/bot/dispatch/context.py +32 -26
  24. telegrinder/bot/dispatch/dispatch.py +61 -134
  25. telegrinder/bot/dispatch/handler/__init__.py +2 -0
  26. telegrinder/bot/dispatch/handler/abc.py +10 -8
  27. telegrinder/bot/dispatch/handler/audio_reply.py +2 -3
  28. telegrinder/bot/dispatch/handler/base.py +10 -33
  29. telegrinder/bot/dispatch/handler/document_reply.py +2 -3
  30. telegrinder/bot/dispatch/handler/func.py +55 -87
  31. telegrinder/bot/dispatch/handler/media_group_reply.py +2 -3
  32. telegrinder/bot/dispatch/handler/message_reply.py +2 -3
  33. telegrinder/bot/dispatch/handler/photo_reply.py +2 -3
  34. telegrinder/bot/dispatch/handler/sticker_reply.py +2 -3
  35. telegrinder/bot/dispatch/handler/video_reply.py +2 -3
  36. telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  37. telegrinder/bot/dispatch/middleware/abc.py +79 -55
  38. telegrinder/bot/dispatch/middleware/global_middleware.py +18 -33
  39. telegrinder/bot/dispatch/process.py +84 -105
  40. telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  41. telegrinder/bot/dispatch/return_manager/abc.py +102 -65
  42. telegrinder/bot/dispatch/return_manager/callback_query.py +4 -5
  43. telegrinder/bot/dispatch/return_manager/inline_query.py +3 -4
  44. telegrinder/bot/dispatch/return_manager/message.py +8 -10
  45. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +4 -5
  46. telegrinder/bot/dispatch/view/__init__.py +4 -4
  47. telegrinder/bot/dispatch/view/abc.py +6 -16
  48. telegrinder/bot/dispatch/view/base.py +54 -178
  49. telegrinder/bot/dispatch/view/box.py +19 -18
  50. telegrinder/bot/dispatch/view/callback_query.py +4 -8
  51. telegrinder/bot/dispatch/view/chat_join_request.py +5 -6
  52. telegrinder/bot/dispatch/view/chat_member.py +5 -25
  53. telegrinder/bot/dispatch/view/error.py +9 -0
  54. telegrinder/bot/dispatch/view/inline_query.py +4 -8
  55. telegrinder/bot/dispatch/view/message.py +5 -25
  56. telegrinder/bot/dispatch/view/pre_checkout_query.py +4 -8
  57. telegrinder/bot/dispatch/view/raw.py +3 -109
  58. telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -5
  59. telegrinder/bot/dispatch/waiter_machine/actions.py +6 -4
  60. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +1 -3
  61. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +1 -1
  62. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +11 -7
  63. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
  64. telegrinder/bot/dispatch/waiter_machine/machine.py +43 -60
  65. telegrinder/bot/dispatch/waiter_machine/middleware.py +19 -23
  66. telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -5
  67. telegrinder/bot/polling/__init__.py +0 -0
  68. telegrinder/bot/polling/abc.py +0 -0
  69. telegrinder/bot/polling/polling.py +209 -88
  70. telegrinder/bot/rules/__init__.py +3 -16
  71. telegrinder/bot/rules/abc.py +42 -122
  72. telegrinder/bot/rules/callback_data.py +29 -49
  73. telegrinder/bot/rules/chat_join.py +5 -23
  74. telegrinder/bot/rules/command.py +8 -4
  75. telegrinder/bot/rules/enum_text.py +3 -4
  76. telegrinder/bot/rules/func.py +7 -14
  77. telegrinder/bot/rules/fuzzy.py +3 -4
  78. telegrinder/bot/rules/inline.py +8 -20
  79. telegrinder/bot/rules/integer.py +2 -3
  80. telegrinder/bot/rules/is_from.py +12 -11
  81. telegrinder/bot/rules/logic.py +11 -5
  82. telegrinder/bot/rules/markup.py +22 -14
  83. telegrinder/bot/rules/mention.py +8 -7
  84. telegrinder/bot/rules/message_entities.py +8 -4
  85. telegrinder/bot/rules/node.py +23 -12
  86. telegrinder/bot/rules/payload.py +5 -4
  87. telegrinder/bot/rules/payment_invoice.py +6 -21
  88. telegrinder/bot/rules/regex.py +2 -4
  89. telegrinder/bot/rules/rule_enum.py +8 -7
  90. telegrinder/bot/rules/start.py +5 -6
  91. telegrinder/bot/rules/state.py +1 -1
  92. telegrinder/bot/rules/text.py +4 -15
  93. telegrinder/bot/rules/update.py +3 -4
  94. telegrinder/bot/scenario/__init__.py +0 -0
  95. telegrinder/bot/scenario/abc.py +6 -5
  96. telegrinder/bot/scenario/checkbox.py +1 -1
  97. telegrinder/bot/scenario/choice.py +30 -39
  98. telegrinder/client/__init__.py +3 -5
  99. telegrinder/client/abc.py +11 -6
  100. telegrinder/client/aiohttp.py +141 -27
  101. telegrinder/client/form_data.py +1 -1
  102. telegrinder/model.py +61 -89
  103. telegrinder/modules.py +325 -102
  104. telegrinder/msgspec_utils/__init__.py +40 -0
  105. telegrinder/msgspec_utils/abc.py +18 -0
  106. telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
  107. telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
  108. telegrinder/msgspec_utils/custom_types/enum_meta.py +43 -0
  109. telegrinder/msgspec_utils/custom_types/literal.py +25 -0
  110. telegrinder/msgspec_utils/custom_types/option.py +17 -0
  111. telegrinder/msgspec_utils/decoder.py +389 -0
  112. telegrinder/msgspec_utils/encoder.py +206 -0
  113. telegrinder/{msgspec_json.py → msgspec_utils/json.py} +6 -5
  114. telegrinder/msgspec_utils/tools.py +75 -0
  115. telegrinder/node/__init__.py +24 -7
  116. telegrinder/node/attachment.py +1 -0
  117. telegrinder/node/base.py +154 -72
  118. telegrinder/node/callback_query.py +5 -5
  119. telegrinder/node/collection.py +39 -0
  120. telegrinder/node/command.py +1 -2
  121. telegrinder/node/composer.py +121 -72
  122. telegrinder/node/container.py +11 -8
  123. telegrinder/node/context.py +48 -0
  124. telegrinder/node/either.py +27 -40
  125. telegrinder/node/error.py +41 -0
  126. telegrinder/node/event.py +37 -11
  127. telegrinder/node/exceptions.py +7 -0
  128. telegrinder/node/file.py +0 -0
  129. telegrinder/node/i18n.py +108 -0
  130. telegrinder/node/me.py +3 -2
  131. telegrinder/node/payload.py +1 -1
  132. telegrinder/node/polymorphic.py +63 -28
  133. telegrinder/node/reply_message.py +12 -0
  134. telegrinder/node/rule.py +6 -13
  135. telegrinder/node/scope.py +14 -5
  136. telegrinder/node/session.py +53 -0
  137. telegrinder/node/source.py +41 -9
  138. telegrinder/node/text.py +1 -2
  139. telegrinder/node/tools/__init__.py +0 -0
  140. telegrinder/node/tools/generator.py +3 -5
  141. telegrinder/node/utility.py +16 -0
  142. telegrinder/py.typed +0 -0
  143. telegrinder/rules.py +0 -0
  144. telegrinder/tools/__init__.py +48 -88
  145. telegrinder/tools/aio.py +103 -0
  146. telegrinder/tools/callback_data_serialization/__init__.py +5 -0
  147. telegrinder/tools/{callback_data_serilization → callback_data_serialization}/abc.py +0 -0
  148. telegrinder/tools/{callback_data_serilization → callback_data_serialization}/json_ser.py +2 -3
  149. telegrinder/tools/{callback_data_serilization → callback_data_serialization}/msgpack_ser.py +45 -27
  150. telegrinder/tools/final.py +21 -0
  151. telegrinder/tools/formatting/__init__.py +2 -18
  152. telegrinder/tools/formatting/deep_links/__init__.py +39 -0
  153. telegrinder/tools/formatting/{deep_links.py → deep_links/links.py} +12 -85
  154. telegrinder/tools/formatting/deep_links/parsing.py +90 -0
  155. telegrinder/tools/formatting/deep_links/validators.py +8 -0
  156. telegrinder/tools/formatting/html_formatter.py +18 -45
  157. telegrinder/tools/fullname.py +83 -0
  158. telegrinder/tools/global_context/__init__.py +4 -3
  159. telegrinder/tools/global_context/abc.py +17 -14
  160. telegrinder/tools/global_context/builtin_context.py +39 -0
  161. telegrinder/tools/global_context/global_context.py +138 -39
  162. telegrinder/tools/input_file_directory.py +0 -0
  163. telegrinder/tools/keyboard/__init__.py +39 -0
  164. telegrinder/tools/keyboard/abc.py +159 -0
  165. telegrinder/tools/keyboard/base.py +77 -0
  166. telegrinder/tools/keyboard/buttons/__init__.py +14 -0
  167. telegrinder/tools/keyboard/buttons/base.py +18 -0
  168. telegrinder/tools/{buttons.py → keyboard/buttons/buttons.py} +71 -23
  169. telegrinder/tools/keyboard/buttons/static_buttons.py +56 -0
  170. telegrinder/tools/keyboard/buttons/tools.py +18 -0
  171. telegrinder/tools/keyboard/data.py +20 -0
  172. telegrinder/tools/keyboard/keyboard.py +131 -0
  173. telegrinder/tools/keyboard/static_keyboard.py +83 -0
  174. telegrinder/tools/lifespan.py +87 -51
  175. telegrinder/tools/limited_dict.py +4 -1
  176. telegrinder/tools/loop_wrapper.py +332 -0
  177. telegrinder/tools/magic/__init__.py +32 -0
  178. telegrinder/tools/magic/annotations.py +165 -0
  179. telegrinder/tools/magic/dictionary.py +20 -0
  180. telegrinder/tools/magic/function.py +246 -0
  181. telegrinder/tools/magic/shortcut.py +111 -0
  182. telegrinder/tools/parse_mode.py +9 -3
  183. telegrinder/tools/singleton/__init__.py +4 -0
  184. telegrinder/tools/singleton/abc.py +14 -0
  185. telegrinder/tools/singleton/singleton.py +18 -0
  186. telegrinder/tools/state_storage/__init__.py +0 -0
  187. telegrinder/tools/state_storage/abc.py +6 -1
  188. telegrinder/tools/state_storage/memory.py +1 -1
  189. telegrinder/tools/strings.py +0 -0
  190. telegrinder/types/__init__.py +307 -268
  191. telegrinder/types/enums.py +64 -37
  192. telegrinder/types/input_file.py +3 -3
  193. telegrinder/types/methods.py +5699 -5055
  194. telegrinder/types/methods_utils.py +62 -0
  195. telegrinder/types/objects.py +7846 -7058
  196. telegrinder/verification_utils.py +3 -1
  197. telegrinder-0.5.0.dist-info/METADATA +162 -0
  198. telegrinder-0.5.0.dist-info/RECORD +200 -0
  199. {telegrinder-0.4.2.dist-info → telegrinder-0.5.0.dist-info}/licenses/LICENSE +2 -2
  200. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +0 -20
  201. telegrinder/bot/rules/id.py +0 -24
  202. telegrinder/bot/rules/message.py +0 -15
  203. telegrinder/client/sonic.py +0 -212
  204. telegrinder/msgspec_utils.py +0 -478
  205. telegrinder/tools/adapter/__init__.py +0 -19
  206. telegrinder/tools/adapter/abc.py +0 -49
  207. telegrinder/tools/adapter/dataclass.py +0 -56
  208. telegrinder/tools/adapter/errors.py +0 -5
  209. telegrinder/tools/adapter/event.py +0 -61
  210. telegrinder/tools/adapter/node.py +0 -46
  211. telegrinder/tools/adapter/raw_event.py +0 -27
  212. telegrinder/tools/adapter/raw_update.py +0 -30
  213. telegrinder/tools/callback_data_serilization/__init__.py +0 -5
  214. telegrinder/tools/error_handler/__init__.py +0 -10
  215. telegrinder/tools/error_handler/abc.py +0 -30
  216. telegrinder/tools/error_handler/error.py +0 -9
  217. telegrinder/tools/error_handler/error_handler.py +0 -179
  218. telegrinder/tools/formatting/spec_html_formats.py +0 -75
  219. telegrinder/tools/functional.py +0 -8
  220. telegrinder/tools/global_context/telegrinder_ctx.py +0 -27
  221. telegrinder/tools/i18n/__init__.py +0 -12
  222. telegrinder/tools/i18n/abc.py +0 -32
  223. telegrinder/tools/i18n/middleware/__init__.py +0 -3
  224. telegrinder/tools/i18n/middleware/abc.py +0 -22
  225. telegrinder/tools/i18n/simple.py +0 -43
  226. telegrinder/tools/keyboard.py +0 -132
  227. telegrinder/tools/loop_wrapper/__init__.py +0 -4
  228. telegrinder/tools/loop_wrapper/abc.py +0 -20
  229. telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -169
  230. telegrinder/tools/magic.py +0 -344
  231. telegrinder-0.4.2.dist-info/METADATA +0 -151
  232. telegrinder-0.4.2.dist-info/RECORD +0 -182
  233. {telegrinder-0.4.2.dist-info → telegrinder-0.5.0.dist-info}/WHEEL +0 -0
telegrinder/__init__.py CHANGED
@@ -1,13 +1,10 @@
1
- """Telegrinder
2
-
3
- Modern visionary telegram bot framework.
1
+ """Modern visionary telegram bot framework.
4
2
 
5
3
  * Type hinted
6
4
  * Customizable and extensible
7
5
  * Ready to use scenarios and rules
8
6
  * Fast models built on [msgspec](https://github.com/jcrist/msgspec)
9
7
  * Both low-level and high-level API
10
- * Support [optional dependecies](https://github.com/timoniq/telegrinder/blob/dev/docs/guide/optional_dependencies.md)
11
8
 
12
9
  Basic example:
13
10
 
@@ -16,9 +13,9 @@ from telegrinder import API, Message, Telegrinder, Token
16
13
  from telegrinder.modules import logger
17
14
  from telegrinder.rules import Text
18
15
 
16
+ logger.set_level("INFO")
19
17
  api = API(token=Token("123:token"))
20
18
  bot = Telegrinder(api)
21
- logger.set_level("INFO")
22
19
 
23
20
 
24
21
  @bot.on.message(Text("/start"))
@@ -48,19 +45,15 @@ from .bot import (
48
45
  ABCReturnManager,
49
46
  ABCRule,
50
47
  ABCScenario,
51
- ABCStateView,
52
48
  ABCView,
53
49
  AudioReplyHandler,
54
50
  BaseCute,
55
51
  BaseReturnManager,
56
- BaseStateView,
57
52
  BaseView,
58
53
  CallbackQueryCute,
59
54
  CallbackQueryReturnManager,
60
- CallbackQueryRule,
61
55
  CallbackQueryView,
62
56
  ChatJoinRequestCute,
63
- ChatJoinRequestRule,
64
57
  ChatJoinRequestView,
65
58
  ChatMemberUpdatedCute,
66
59
  ChatMemberView,
@@ -73,12 +66,10 @@ from .bot import (
73
66
  Hasher,
74
67
  InlineQueryCute,
75
68
  InlineQueryReturnManager,
76
- InlineQueryRule,
77
69
  MediaGroupReplyHandler,
78
70
  MessageCute,
79
71
  MessageReplyHandler,
80
72
  MessageReturnManager,
81
- MessageRule,
82
73
  MessageView,
83
74
  PhotoReplyHandler,
84
75
  Polling,
@@ -87,40 +78,40 @@ from .bot import (
87
78
  PreCheckoutQueryView,
88
79
  RawEventView,
89
80
  ShortState,
90
- StateViewHasher,
91
81
  StickerReplyHandler,
92
82
  Telegrinder,
93
83
  UpdateCute,
94
84
  VideoReplyHandler,
95
85
  ViewBox,
96
86
  WaiterMachine,
87
+ action,
97
88
  register_manager,
98
89
  )
99
- from .client import ABCClient, AiohttpClient, AiosonicClient
100
- from .model import Model
90
+ from .client import ABCClient, AiohttpClient
91
+ from .model import Model, field
101
92
  from .modules import logger
102
- from .tools.error_handler import ABCErrorHandler, ErrorHandler
103
93
  from .tools.formatting import HTMLFormatter
104
- from .tools.global_context import ABCGlobalContext, CtxVar, GlobalContext, ctx_var
105
- from .tools.i18n import (
106
- ABCTranslator,
107
- ABCTranslatorMiddleware,
108
- I18nEnum,
109
- SimpleI18n,
110
- SimpleTranslator,
111
- )
94
+ from .tools.global_context import ABCGlobalContext, GlobalContext, TelegrinderContext
112
95
  from .tools.input_file_directory import InputFileDirectory
113
96
  from .tools.keyboard import (
114
- AnyMarkup,
97
+ ABCKeyboard,
98
+ ABCStaticKeyboard,
99
+ BaseButton,
100
+ BaseKeyboard,
101
+ BaseStaticButton,
102
+ BaseStaticKeyboard,
115
103
  Button,
116
104
  InlineButton,
117
105
  InlineKeyboard,
118
106
  Keyboard,
119
107
  RowButtons,
108
+ StaticButton,
109
+ StaticInlineButton,
110
+ StaticInlineKeyboard,
111
+ StaticKeyboard,
120
112
  )
121
113
  from .tools.lifespan import Lifespan
122
- from .tools.loop_wrapper import ABCLoopWrapper, DelayedTask, LoopWrapper
123
- from .tools.magic import cache_translation, get_cached_translation, magic_bundle
114
+ from .tools.loop_wrapper import DelayedTask, LoopWrapper
124
115
  from .tools.parse_mode import ParseMode
125
116
  from .tools.state_storage import ABCStateStorage, MemoryStateStorage, StateData
126
117
 
@@ -135,12 +126,18 @@ Bot: typing.TypeAlias = Telegrinder
135
126
 
136
127
 
137
128
  __all__ = (
129
+ "API",
130
+ "CALLBACK_QUERY_FOR_MESSAGE",
131
+ "CALLBACK_QUERY_FROM_CHAT",
132
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
133
+ "MESSAGE_FROM_USER",
134
+ "MESSAGE_FROM_USER_IN_CHAT",
135
+ "MESSAGE_IN_CHAT",
138
136
  "ABCClient",
139
137
  "ABCDispatch",
140
- "ABCErrorHandler",
141
138
  "ABCGlobalContext",
142
139
  "ABCHandler",
143
- "ABCLoopWrapper",
140
+ "ABCKeyboard",
144
141
  "ABCMiddleware",
145
142
  "ABCPolling",
146
143
  "ABCReturnManager",
@@ -148,35 +145,28 @@ __all__ = (
148
145
  "ABCScenario",
149
146
  "ABCStateStorage",
150
147
  "ABCStateStorage",
151
- "ABCStateView",
152
- "ABCTranslator",
153
- "ABCTranslatorMiddleware",
148
+ "ABCStaticKeyboard",
154
149
  "ABCView",
155
- "API",
156
150
  "APIError",
157
151
  "APIResponse",
158
152
  "APIServerError",
159
153
  "AiohttpClient",
160
- "AiosonicClient",
161
- "AnyMarkup",
162
154
  "AudioReplyHandler",
155
+ "BaseButton",
163
156
  "BaseCute",
157
+ "BaseKeyboard",
164
158
  "BaseReturnManager",
165
- "BaseStateView",
159
+ "BaseStaticButton",
160
+ "BaseStaticKeyboard",
166
161
  "BaseView",
167
162
  "Bot",
168
163
  "Button",
169
- "CALLBACK_QUERY_FOR_MESSAGE",
170
- "CALLBACK_QUERY_FROM_CHAT",
171
- "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
172
164
  "CallbackQuery",
173
165
  "CallbackQueryCute",
174
166
  "CallbackQueryReturnManager",
175
- "CallbackQueryRule",
176
167
  "CallbackQueryView",
177
168
  "ChatJoinRequest",
178
169
  "ChatJoinRequestCute",
179
- "ChatJoinRequestRule",
180
170
  "ChatJoinRequestView",
181
171
  "ChatMemberUpdated",
182
172
  "ChatMemberUpdatedCute",
@@ -184,29 +174,22 @@ __all__ = (
184
174
  "Checkbox",
185
175
  "Choice",
186
176
  "Context",
187
- "CtxVar",
188
177
  "DelayedTask",
189
178
  "Dispatch",
190
179
  "DocumentReplyHandler",
191
- "ErrorHandler",
192
180
  "FuncHandler",
193
181
  "GlobalContext",
194
182
  "HTMLFormatter",
195
183
  "Hasher",
196
- "I18nEnum",
197
184
  "InlineButton",
198
185
  "InlineKeyboard",
199
186
  "InlineQuery",
200
187
  "InlineQueryCute",
201
188
  "InlineQueryReturnManager",
202
- "InlineQueryRule",
203
189
  "InputFileDirectory",
204
190
  "Keyboard",
205
191
  "Lifespan",
206
192
  "LoopWrapper",
207
- "MESSAGE_FROM_USER",
208
- "MESSAGE_FROM_USER_IN_CHAT",
209
- "MESSAGE_IN_CHAT",
210
193
  "MediaGroupReplyHandler",
211
194
  "MemoryStateStorage",
212
195
  "MemoryStateStorage",
@@ -215,7 +198,6 @@ __all__ = (
215
198
  "MessageReplyHandler",
216
199
  "MessageReplyHandler",
217
200
  "MessageReturnManager",
218
- "MessageRule",
219
201
  "MessageView",
220
202
  "Model",
221
203
  "ParseMode",
@@ -228,23 +210,23 @@ __all__ = (
228
210
  "RawEventView",
229
211
  "RowButtons",
230
212
  "ShortState",
231
- "SimpleI18n",
232
- "SimpleTranslator",
233
213
  "StateData",
234
214
  "StateData",
235
- "StateViewHasher",
215
+ "StaticButton",
216
+ "StaticInlineButton",
217
+ "StaticInlineKeyboard",
218
+ "StaticKeyboard",
236
219
  "StickerReplyHandler",
237
220
  "Telegrinder",
221
+ "TelegrinderContext",
238
222
  "Token",
239
223
  "Update",
240
224
  "UpdateCute",
241
225
  "VideoReplyHandler",
242
226
  "ViewBox",
243
227
  "WaiterMachine",
244
- "cache_translation",
245
- "ctx_var",
246
- "get_cached_translation",
228
+ "action",
229
+ "field",
247
230
  "logger",
248
- "magic_bundle",
249
231
  "register_manager",
250
232
  )
@@ -0,0 +1 @@
1
+ __version__ = "0.5.0"
@@ -1,7 +1,7 @@
1
- from .api import API
2
- from .error import APIError, APIServerError, InvalidTokenError
3
- from .response import APIResponse
4
- from .token import Token
1
+ from telegrinder.api.api import API, compose_data, retryer
2
+ from telegrinder.api.error import APIError, APIServerError, InvalidTokenError
3
+ from telegrinder.api.response import APIResponse
4
+ from telegrinder.api.token import Token
5
5
 
6
6
  __all__ = (
7
7
  "API",
@@ -10,4 +10,6 @@ __all__ = (
10
10
  "APIServerError",
11
11
  "InvalidTokenError",
12
12
  "Token",
13
+ "compose_data",
14
+ "retryer",
13
15
  )
telegrinder/api/api.py CHANGED
@@ -1,50 +1,102 @@
1
- from functools import cached_property
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import pathlib
5
+ from functools import cached_property, wraps
6
+ from http import HTTPStatus
2
7
 
3
8
  import msgspec
4
9
  import typing_extensions as typing
10
+ from fntypes.misc import is_ok
5
11
  from fntypes.result import Error, Ok, Result
6
12
 
7
13
  from telegrinder.api.error import APIError
8
14
  from telegrinder.api.response import APIResponse
9
15
  from telegrinder.api.token import Token
10
16
  from telegrinder.client import ABCClient, AiohttpClient, MultipartFormProto
17
+ from telegrinder.client.aiohttp import DEFAULT_TIMEOUT
11
18
  from telegrinder.model import decoder
12
19
  from telegrinder.types.methods import APIMethods
13
20
 
14
- HTTPClient = typing.TypeVar("HTTPClient", bound=ABCClient, default=AiohttpClient)
15
-
16
21
  type Json = str | int | float | bool | list[Json] | dict[str, Json] | None
22
+ type Data = dict[str, typing.Any]
23
+ type Files = dict[str, tuple[str, bytes]]
24
+ type APIRequestMethod[T: API, **P, R] = typing.Callable[
25
+ typing.Concatenate[T, P],
26
+ typing.Coroutine[typing.Any, typing.Any, Result[R, APIError]],
27
+ ]
28
+
29
+ DEFAULT_MAX_RETRIES: typing.Final[int] = 5
17
30
 
18
31
 
19
- def compose_data[MultipartForm: MultipartFormProto](
20
- client: ABCClient[MultipartForm],
21
- data: dict[str, typing.Any],
22
- files: dict[str, tuple[str, bytes]],
23
- ) -> MultipartForm:
32
+ def compose_data(
33
+ client: ABCClient,
34
+ data: Data,
35
+ files: Files,
36
+ ) -> MultipartFormProto:
24
37
  if not data and not files:
25
38
  return client.multipart_form_factory()
26
39
  return client.get_form(data=data, files=files)
27
40
 
28
41
 
29
- class API(APIMethods[HTTPClient], typing.Generic[HTTPClient]):
42
+ def retryer[T: API, **P, R](func: APIRequestMethod[T, P, R], /) -> APIRequestMethod[T, P, R]:
43
+ @wraps(func)
44
+ async def wrapper(
45
+ self: T,
46
+ *args: P.args,
47
+ **kwargs: P.kwargs,
48
+ ) -> Result[typing.Any, APIError]:
49
+ retries_counter = 0
50
+
51
+ while True:
52
+ result = await func(self, *args, **kwargs)
53
+
54
+ if is_ok(result) or not self.retryer_enabled or retries_counter >= self.max_retries:
55
+ return result
56
+
57
+ if result.error.status_code == HTTPStatus.TOO_MANY_REQUESTS:
58
+ await asyncio.sleep(result.error.retry_after.unwrap_or(5.0))
59
+
60
+ elif result.error.status_code == HTTPStatus.INTERNAL_SERVER_ERROR and "restart" in result.error.error:
61
+ await asyncio.sleep(10.0)
62
+
63
+ elif result.error.migrate_to_chat_id:
64
+ kwargs["chat_id"] = result.error.migrate_to_chat_id.value
65
+
66
+ retries_counter += 1
67
+
68
+ return wrapper # type: ignore
69
+
70
+
71
+ class API(APIMethods):
30
72
  """Bot API with available API methods and http client."""
31
73
 
74
+ http: ABCClient
75
+
32
76
  API_URL = "https://api.telegram.org/"
33
77
  API_FILE_URL = "https://api.telegram.org/file/"
34
78
 
35
- token: Token
36
- http: HTTPClient
37
-
38
- def __init__(self, token: Token, *, http: HTTPClient | None = None) -> None:
79
+ def __init__(
80
+ self,
81
+ token: Token,
82
+ *,
83
+ http: ABCClient | None = None,
84
+ enable_retryer: bool = True,
85
+ max_retries: int = DEFAULT_MAX_RETRIES,
86
+ ) -> None:
39
87
  self.token = token
40
- self.http = http or AiohttpClient() # type: ignore
88
+ self.http = http or AiohttpClient()
89
+ self._retryer_enabled = enable_retryer
90
+ self._max_retries = max_retries
41
91
  super().__init__(api=self)
42
92
 
43
93
  def __repr__(self) -> str:
44
- return "<{}: token={!r}, http={!r}>".format(
45
- self.__class__.__name__,
46
- self.token,
94
+ return "<{}: id={}, http={!r}, retryer_enabled={}, max_retries={}>".format(
95
+ type(self).__name__,
96
+ self.id,
47
97
  self.http,
98
+ self._retryer_enabled,
99
+ self._max_retries,
48
100
  )
49
101
 
50
102
  @cached_property
@@ -59,20 +111,38 @@ class API(APIMethods[HTTPClient], typing.Generic[HTTPClient]):
59
111
  def request_file_url(self) -> str:
60
112
  return self.API_FILE_URL + f"bot{self.token}/"
61
113
 
62
- async def download_file(self, file_path: str) -> bytes:
63
- return await self.http.request_content(f"{self.request_file_url}/{file_path}")
114
+ @property
115
+ def retryer_enabled(self) -> bool:
116
+ return self._retryer_enabled
117
+
118
+ @property
119
+ def max_retries(self) -> int:
120
+ return self._max_retries
121
+
122
+ async def download_file(
123
+ self,
124
+ file_path: str | pathlib.Path,
125
+ timeout: int | float = DEFAULT_TIMEOUT,
126
+ ) -> bytes:
127
+ return await self.http.request_content(
128
+ url=f"{self.request_file_url}/{file_path}",
129
+ timeout=timeout,
130
+ )
64
131
 
132
+ @retryer
65
133
  async def request(
66
134
  self,
67
135
  method: str,
68
- data: dict[str, typing.Any] | None = None,
69
- files: dict[str, tuple[str, bytes]] | None = None,
136
+ data: Data | None = None,
137
+ files: Files | None = None,
138
+ **kwargs: typing.Any,
70
139
  ) -> Result[Json, APIError]:
71
- """Request a `JSON` response with the `POST` HTTP method and passing data, files as `multipart/form-data`."""
140
+ """Request a `JSON` response using http method `POST` and passing data, files as `multipart/form-data`."""
72
141
  response = await self.http.request_json(
73
142
  url=self.request_url + method,
74
143
  method="POST",
75
144
  data=compose_data(self.http, data or {}, files or {}),
145
+ **kwargs,
76
146
  )
77
147
  if response.get("ok", False) is True:
78
148
  return Ok(response["result"])
@@ -80,22 +150,26 @@ class API(APIMethods[HTTPClient], typing.Generic[HTTPClient]):
80
150
  APIError(
81
151
  code=response.get("error_code", 400),
82
152
  error=response.get("description", "Something went wrong"),
153
+ data=response.get("parameters", {}),
83
154
  ),
84
155
  )
85
156
 
157
+ @retryer
86
158
  async def request_raw(
87
159
  self,
88
160
  method: str,
89
- data: dict[str, typing.Any] | None = None,
90
- files: dict[str, tuple[str, bytes]] | None = None,
161
+ data: Data | None = None,
162
+ files: Files | None = None,
163
+ **kwargs: typing.Any,
91
164
  ) -> Result[msgspec.Raw, APIError]:
92
- """Request a `raw` response with the `POST` HTTP method and passing data, files as `multipart/form-data`."""
165
+ """Request a `raw` response using http method `POST` and passing data, files as `multipart/form-data`."""
93
166
  response_bytes = await self.http.request_bytes(
94
167
  url=self.request_url + method,
95
168
  method="POST",
96
169
  data=compose_data(self.http, data or {}, files or {}),
170
+ **kwargs,
97
171
  )
98
172
  return decoder.decode(response_bytes, type=APIResponse).to_result()
99
173
 
100
174
 
101
- __all__ = ("API",)
175
+ __all__ = ("API", "compose_data", "retryer")
telegrinder/api/error.py CHANGED
@@ -1,16 +1,50 @@
1
- class APIError(Exception):
2
- def __init__(self, code: int, error: str) -> None:
3
- self.code, self.error = code, error
1
+ import typing
2
+ from functools import cached_property
3
+ from http import HTTPStatus
4
+
5
+ from fntypes.misc import from_optional
6
+ from fntypes.option import Option
4
7
 
5
- def __str__(self) -> str:
6
- return f"[{self.code}] {self.error}"
7
8
 
9
+ class ReprErrorMixin:
8
10
  def __repr__(self) -> str:
9
- return f"<APIError: {self.__str__()}>"
11
+ return f"{type(self).__name__}: {self}"
10
12
 
11
13
 
12
- class APIServerError(Exception):
13
- pass
14
+ class APIError(ReprErrorMixin, Exception):
15
+ def __init__(
16
+ self,
17
+ code: int,
18
+ error: str,
19
+ data: dict[str, typing.Any],
20
+ ) -> None:
21
+ self.code, self.error, self.parameters = code, error, data
22
+ super().__init__(code, error, data)
23
+
24
+ @cached_property
25
+ def status_code(self) -> HTTPStatus:
26
+ return HTTPStatus(self.code)
27
+
28
+ @property
29
+ def retry_after(self) -> Option[int]:
30
+ return from_optional(self.parameters.get("retry_after"))
31
+
32
+ @property
33
+ def migrate_to_chat_id(self) -> Option[int]:
34
+ return from_optional(self.parameters.get("migrate_to_chat_id"))
35
+
36
+ def __str__(self) -> str:
37
+ return f"[{self.code}] ({self.status_code.name}) {self.error}"
38
+
39
+
40
+ class APIServerError(ReprErrorMixin, Exception):
41
+ def __init__(self, message: str, retry_after: int) -> None:
42
+ self.message = message
43
+ self.retry_after = retry_after
44
+ super().__init__(message, retry_after)
45
+
46
+ def __str__(self) -> str:
47
+ return self.message
14
48
 
15
49
 
16
50
  class InvalidTokenError(BaseException):
@@ -1,3 +1,5 @@
1
+ import typing
2
+
1
3
  import msgspec
2
4
  from fntypes.result import Error, Ok, Result
3
5
 
@@ -10,11 +12,12 @@ class APIResponse(Model):
10
12
  result: msgspec.Raw = msgspec.Raw(b"")
11
13
  error_code: int = 400
12
14
  description: str = "Something went wrong"
15
+ parameters: dict[str, typing.Any] = msgspec.field(default_factory=dict[str, typing.Any])
13
16
 
14
17
  def to_result(self) -> Result[msgspec.Raw, APIError]:
15
18
  if self.ok:
16
19
  return Ok(self.result)
17
- return Error(APIError(self.error_code, self.description))
20
+ return Error(APIError(code=self.error_code, error=self.description, data=self.parameters))
18
21
 
19
22
 
20
23
  __all__ = ("APIResponse",)
telegrinder/api/token.py CHANGED
@@ -4,7 +4,7 @@ from functools import cached_property
4
4
 
5
5
  from envparse import env
6
6
 
7
- from .error import InvalidTokenError
7
+ from telegrinder.api.error import InvalidTokenError
8
8
 
9
9
 
10
10
  class Token(str):
@@ -14,7 +14,7 @@ class Token(str):
14
14
  return super().__new__(cls, token)
15
15
 
16
16
  def __repr__(self) -> str:
17
- return f"<Token: {self.bot_id}:{''.join(self.split(':')[-1])[:6]}...>"
17
+ return f"<Token: {self.bot_id}:{self.split(':')[1][:6]}...>"
18
18
 
19
19
  @classmethod
20
20
  def from_env(
@@ -20,11 +20,9 @@ from telegrinder.bot.dispatch import (
20
20
  ABCHandler,
21
21
  ABCMiddleware,
22
22
  ABCReturnManager,
23
- ABCStateView,
24
23
  ABCView,
25
24
  AudioReplyHandler,
26
25
  BaseReturnManager,
27
- BaseStateView,
28
26
  BaseView,
29
27
  CallbackQueryReturnManager,
30
28
  CallbackQueryView,
@@ -46,25 +44,24 @@ from telegrinder.bot.dispatch import (
46
44
  PreCheckoutQueryView,
47
45
  RawEventView,
48
46
  ShortState,
49
- StateViewHasher,
50
47
  StickerReplyHandler,
51
48
  VideoReplyHandler,
52
49
  ViewBox,
53
50
  WaiterMachine,
54
- clear_wm_storage_worker,
51
+ action,
55
52
  register_manager,
56
53
  )
57
54
  from telegrinder.bot.polling import ABCPolling, Polling
58
- from telegrinder.bot.rules import (
59
- ABCRule,
60
- CallbackQueryRule,
61
- ChatJoinRequestRule,
62
- InlineQueryRule,
63
- MessageRule,
64
- )
55
+ from telegrinder.bot.rules import ABCRule
65
56
  from telegrinder.bot.scenario import ABCScenario, Checkbox, Choice
66
57
 
67
58
  __all__ = (
59
+ "CALLBACK_QUERY_FOR_MESSAGE",
60
+ "CALLBACK_QUERY_FROM_CHAT",
61
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
62
+ "MESSAGE_FROM_USER",
63
+ "MESSAGE_FROM_USER_IN_CHAT",
64
+ "MESSAGE_IN_CHAT",
68
65
  "ABCDispatch",
69
66
  "ABCHandler",
70
67
  "ABCMiddleware",
@@ -72,22 +69,15 @@ __all__ = (
72
69
  "ABCReturnManager",
73
70
  "ABCRule",
74
71
  "ABCScenario",
75
- "ABCStateView",
76
72
  "ABCView",
77
73
  "AudioReplyHandler",
78
74
  "BaseCute",
79
75
  "BaseReturnManager",
80
- "BaseStateView",
81
76
  "BaseView",
82
- "CALLBACK_QUERY_FOR_MESSAGE",
83
- "CALLBACK_QUERY_FROM_CHAT",
84
- "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
85
77
  "CallbackQueryCute",
86
78
  "CallbackQueryReturnManager",
87
- "CallbackQueryRule",
88
79
  "CallbackQueryView",
89
80
  "ChatJoinRequestCute",
90
- "ChatJoinRequestRule",
91
81
  "ChatJoinRequestView",
92
82
  "ChatMemberUpdatedCute",
93
83
  "ChatMemberView",
@@ -100,16 +90,11 @@ __all__ = (
100
90
  "Hasher",
101
91
  "InlineQueryCute",
102
92
  "InlineQueryReturnManager",
103
- "InlineQueryRule",
104
- "MESSAGE_FROM_USER",
105
- "MESSAGE_FROM_USER_IN_CHAT",
106
- "MESSAGE_IN_CHAT",
107
93
  "Manager",
108
94
  "MediaGroupReplyHandler",
109
95
  "MessageCute",
110
96
  "MessageReplyHandler",
111
97
  "MessageReturnManager",
112
- "MessageRule",
113
98
  "MessageView",
114
99
  "PhotoReplyHandler",
115
100
  "Polling",
@@ -118,13 +103,12 @@ __all__ = (
118
103
  "PreCheckoutQueryView",
119
104
  "RawEventView",
120
105
  "ShortState",
121
- "StateViewHasher",
122
106
  "StickerReplyHandler",
123
107
  "Telegrinder",
124
108
  "UpdateCute",
125
109
  "VideoReplyHandler",
126
110
  "ViewBox",
127
111
  "WaiterMachine",
128
- "clear_wm_storage_worker",
112
+ "action",
129
113
  "register_manager",
130
114
  )