telegrinder 0.1.dev165__py3-none-any.whl → 0.1.dev167__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 (101) hide show
  1. telegrinder/__init__.py +20 -0
  2. telegrinder/api/abc.py +1 -1
  3. telegrinder/api/api.py +8 -6
  4. telegrinder/api/error.py +2 -3
  5. telegrinder/bot/__init__.py +12 -0
  6. telegrinder/bot/bot.py +2 -2
  7. telegrinder/bot/cute_types/__init__.py +4 -0
  8. telegrinder/bot/cute_types/base.py +10 -10
  9. telegrinder/bot/cute_types/callback_query.py +1 -3
  10. telegrinder/bot/cute_types/chat_join_request.py +65 -0
  11. telegrinder/bot/cute_types/chat_member_updated.py +246 -0
  12. telegrinder/bot/cute_types/message.py +44 -38
  13. telegrinder/bot/cute_types/update.py +5 -4
  14. telegrinder/bot/cute_types/utils.py +40 -20
  15. telegrinder/bot/dispatch/__init__.py +8 -1
  16. telegrinder/bot/dispatch/composition.py +7 -7
  17. telegrinder/bot/dispatch/context.py +9 -10
  18. telegrinder/bot/dispatch/dispatch.py +30 -22
  19. telegrinder/bot/dispatch/handler/abc.py +1 -0
  20. telegrinder/bot/dispatch/handler/func.py +21 -5
  21. telegrinder/bot/dispatch/handler/message_reply.py +2 -3
  22. telegrinder/bot/dispatch/middleware/abc.py +2 -4
  23. telegrinder/bot/dispatch/process.py +4 -3
  24. telegrinder/bot/dispatch/return_manager/__init__.py +1 -1
  25. telegrinder/bot/dispatch/return_manager/abc.py +28 -20
  26. telegrinder/bot/dispatch/return_manager/callback_query.py +4 -2
  27. telegrinder/bot/dispatch/return_manager/inline_query.py +4 -2
  28. telegrinder/bot/dispatch/return_manager/message.py +8 -4
  29. telegrinder/bot/dispatch/view/__init__.py +8 -2
  30. telegrinder/bot/dispatch/view/abc.py +27 -23
  31. telegrinder/bot/dispatch/view/box.py +74 -11
  32. telegrinder/bot/dispatch/view/callback_query.py +1 -3
  33. telegrinder/bot/dispatch/view/chat_join_request.py +17 -0
  34. telegrinder/bot/dispatch/view/chat_member.py +26 -0
  35. telegrinder/bot/dispatch/view/message.py +23 -1
  36. telegrinder/bot/dispatch/view/raw.py +112 -0
  37. telegrinder/bot/dispatch/waiter_machine/machine.py +41 -26
  38. telegrinder/bot/dispatch/waiter_machine/middleware.py +14 -7
  39. telegrinder/bot/dispatch/waiter_machine/short_state.py +10 -7
  40. telegrinder/bot/polling/polling.py +2 -4
  41. telegrinder/bot/rules/__init__.py +20 -12
  42. telegrinder/bot/rules/abc.py +0 -9
  43. telegrinder/bot/rules/adapter/event.py +29 -22
  44. telegrinder/bot/rules/callback_data.py +15 -18
  45. telegrinder/bot/rules/chat_join.py +47 -0
  46. telegrinder/bot/rules/enum_text.py +7 -2
  47. telegrinder/bot/rules/fuzzy.py +1 -2
  48. telegrinder/bot/rules/inline.py +3 -3
  49. telegrinder/bot/rules/is_from.py +39 -51
  50. telegrinder/bot/rules/markup.py +1 -2
  51. telegrinder/bot/rules/mention.py +1 -4
  52. telegrinder/bot/rules/message.py +17 -0
  53. telegrinder/bot/rules/message_entities.py +1 -1
  54. telegrinder/bot/rules/regex.py +1 -2
  55. telegrinder/bot/rules/rule_enum.py +1 -3
  56. telegrinder/bot/rules/start.py +7 -7
  57. telegrinder/bot/rules/text.py +2 -1
  58. telegrinder/bot/rules/update.py +16 -0
  59. telegrinder/bot/scenario/checkbox.py +5 -7
  60. telegrinder/client/aiohttp.py +5 -7
  61. telegrinder/model.py +37 -22
  62. telegrinder/modules.py +15 -33
  63. telegrinder/msgspec_utils.py +34 -35
  64. telegrinder/node/__init__.py +12 -12
  65. telegrinder/node/attachment.py +21 -7
  66. telegrinder/node/base.py +14 -13
  67. telegrinder/node/composer.py +5 -5
  68. telegrinder/node/container.py +1 -1
  69. telegrinder/node/message.py +3 -1
  70. telegrinder/node/rule.py +4 -4
  71. telegrinder/node/source.py +6 -2
  72. telegrinder/node/text.py +3 -1
  73. telegrinder/node/tools/generator.py +1 -1
  74. telegrinder/tools/__init__.py +3 -1
  75. telegrinder/tools/buttons.py +4 -6
  76. telegrinder/tools/error_handler/abc.py +1 -2
  77. telegrinder/tools/error_handler/error.py +3 -6
  78. telegrinder/tools/error_handler/error_handler.py +34 -24
  79. telegrinder/tools/formatting/html.py +9 -5
  80. telegrinder/tools/formatting/links.py +1 -3
  81. telegrinder/tools/formatting/spec_html_formats.py +1 -1
  82. telegrinder/tools/global_context/abc.py +3 -1
  83. telegrinder/tools/global_context/global_context.py +13 -31
  84. telegrinder/tools/global_context/telegrinder_ctx.py +1 -1
  85. telegrinder/tools/i18n/base.py +4 -3
  86. telegrinder/tools/i18n/middleware/base.py +1 -3
  87. telegrinder/tools/i18n/simple.py +1 -3
  88. telegrinder/tools/keyboard.py +1 -1
  89. telegrinder/tools/limited_dict.py +27 -0
  90. telegrinder/tools/loop_wrapper/loop_wrapper.py +18 -14
  91. telegrinder/tools/magic.py +1 -1
  92. telegrinder/types/__init__.py +236 -0
  93. telegrinder/types/enums.py +34 -0
  94. telegrinder/types/methods.py +52 -47
  95. telegrinder/types/objects.py +533 -90
  96. telegrinder/verification_utils.py +4 -1
  97. {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev167.dist-info}/METADATA +1 -1
  98. telegrinder-0.1.dev167.dist-info/RECORD +137 -0
  99. telegrinder-0.1.dev165.dist-info/RECORD +0 -128
  100. {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev167.dist-info}/LICENSE +0 -0
  101. {telegrinder-0.1.dev165.dist-info → telegrinder-0.1.dev167.dist-info}/WHEEL +0 -0
@@ -1,24 +1,34 @@
1
1
  import typing
2
2
 
3
+ from fntypes.co import Nothing, Some
4
+
3
5
  from telegrinder.bot.cute_types.base import BaseCute
6
+ from telegrinder.bot.cute_types.update import UpdateCute
4
7
  from telegrinder.bot.dispatch.context import Context
5
8
  from telegrinder.msgspec_utils import Option
6
9
  from telegrinder.types.enums import ChatType, DiceEmoji
7
10
  from telegrinder.types.objects import User
8
11
 
9
- from .abc import ABCRule, Message, MessageRule
12
+ from .abc import ABCRule, Message
13
+ from .message import MessageRule
10
14
 
11
15
  T = typing.TypeVar("T", bound=BaseCute)
12
16
 
13
17
 
18
+ def get_from_user(obj: typing.Any) -> User:
19
+ assert isinstance(obj, FromUserProto)
20
+ return obj.from_.unwrap() if isinstance(obj.from_, Some | Nothing) else obj.from_
21
+
22
+
14
23
  @typing.runtime_checkable
15
24
  class FromUserProto(typing.Protocol):
16
25
  from_: User | Option[User]
17
26
 
18
27
 
19
28
  class HasFrom(ABCRule[T]):
20
- async def check(self, event: T, ctx: Context) -> bool:
21
- return isinstance(event, FromUserProto) and bool(event.from_)
29
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
30
+ event_model = event.incoming_update.unwrap()
31
+ return isinstance(event_model, FromUserProto) and bool(event_model.from_)
22
32
 
23
33
 
24
34
  class HasDice(MessageRule):
@@ -32,7 +42,9 @@ class IsForward(MessageRule):
32
42
 
33
43
 
34
44
  class IsForwardType(MessageRule, requires=[IsForward()]):
35
- def __init__(self, fwd_type: typing.Literal["user", "hidden_user", "chat", "channel"], /) -> None:
45
+ def __init__(
46
+ self, fwd_type: typing.Literal["user", "hidden_user", "chat", "channel"], /
47
+ ) -> None:
36
48
  self.fwd_type = fwd_type
37
49
 
38
50
  async def check(self, message: Message, ctx: Context) -> bool:
@@ -49,29 +61,30 @@ class IsSticker(MessageRule):
49
61
  return bool(message.sticker)
50
62
 
51
63
 
52
- class IsBot(MessageRule, requires=[HasFrom()]):
53
- async def check(self, message: Message, ctx: Context) -> bool:
54
- return message.from_user.is_bot
64
+ class IsBot(ABCRule[T], requires=[HasFrom()]):
65
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
66
+ return get_from_user(event.incoming_update.unwrap()).is_bot
55
67
 
56
68
 
57
- class IsUser(MessageRule, requires=[HasFrom()]):
58
- async def check(self, message: Message, ctx: Context) -> bool:
59
- return not message.from_user.is_bot
69
+ class IsUser(ABCRule[T], requires=[HasFrom()]):
70
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
71
+ return not get_from_user(event.incoming_update.unwrap()).is_bot
60
72
 
61
73
 
62
- class IsPremium(MessageRule, requires=[HasFrom()]):
63
- async def check(self, message: Message, ctx: Context) -> bool:
64
- return message.from_user.is_premium.unwrap_or(False)
74
+ class IsPremium(ABCRule[T], requires=[HasFrom()]):
75
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
76
+ return get_from_user(event.incoming_update.unwrap()).is_premium.unwrap_or(False)
65
77
 
66
78
 
67
- class IsLanguageCode(MessageRule, requires=[HasFrom()]):
79
+ class IsLanguageCode(ABCRule[T], requires=[HasFrom()]):
68
80
  def __init__(self, lang_codes: str | list[str], /) -> None:
69
81
  self.lang_codes = [lang_codes] if isinstance(lang_codes, str) else lang_codes
70
82
 
71
- async def check(self, message: Message, ctx: Context) -> bool:
72
- if not message.from_user.language_code:
73
- return False
74
- return message.from_user.language_code.unwrap() in self.lang_codes
83
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
84
+ return (
85
+ get_from_user(event.incoming_update.unwrap()).language_code.unwrap_or_none()
86
+ in self.lang_codes
87
+ )
75
88
 
76
89
 
77
90
  class IsForum(MessageRule):
@@ -79,12 +92,12 @@ class IsForum(MessageRule):
79
92
  return message.chat.is_forum.unwrap_or(False)
80
93
 
81
94
 
82
- class IsUserId(MessageRule, requires=[HasFrom()]):
95
+ class IsUserId(ABCRule[T], requires=[HasFrom()]):
83
96
  def __init__(self, user_ids: int | list[int], /) -> None:
84
97
  self.user_ids = [user_ids] if isinstance(user_ids, int) else user_ids
85
98
 
86
- async def check(self, message: Message, ctx: Context) -> bool:
87
- return message.from_user.id in self.user_ids
99
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
100
+ return get_from_user(event.incoming_update.unwrap()).id in self.user_ids
88
101
 
89
102
 
90
103
  class IsChatId(MessageRule):
@@ -115,44 +128,19 @@ class IsChat(MessageRule):
115
128
  return message.chat.type in (ChatType.GROUP, ChatType.SUPERGROUP)
116
129
 
117
130
 
118
- class IsDice(MessageRule, requires=[HasDice()]):
119
- async def check(self, message: Message, ctx: Context) -> bool:
120
- return message.dice.unwrap().emoji == DiceEmoji.DICE
121
-
122
-
123
- class IsDartDice(MessageRule, requires=[HasDice()]):
124
- async def check(self, message: Message, ctx: Context) -> bool:
125
- return message.dice.unwrap().emoji == DiceEmoji.DART
126
-
127
-
128
- class IsBasketballDice(MessageRule, requires=[HasDice()]):
129
- async def check(self, message: Message, ctx: Context) -> bool:
130
- return message.dice.unwrap().emoji == DiceEmoji.BASKETBALL
131
-
132
-
133
- class IsFootballDice(MessageRule, requires=[HasDice()]):
134
- async def check(self, message: Message, ctx: Context) -> bool:
135
- return message.dice.unwrap().emoji == DiceEmoji.FOOTBALL
136
-
137
-
138
- class IsSlotMachineDice(MessageRule, requires=[HasDice()]):
139
- async def check(self, message: Message, ctx: Context) -> bool:
140
- return message.dice.unwrap().emoji == DiceEmoji.SLOT_MACHINE
141
-
131
+ class IsDiceEmoji(MessageRule, requires=[HasDice()]):
132
+ def __init__(self, dice_emoji: DiceEmoji, /) -> None:
133
+ self.dice_emoji = dice_emoji
142
134
 
143
- class IsBowlingDice(MessageRule, requires=[HasDice()]):
144
135
  async def check(self, message: Message, ctx: Context) -> bool:
145
- return message.dice.unwrap().emoji == DiceEmoji.BOWLING
136
+ return message.dice.unwrap().emoji == self.dice_emoji
146
137
 
147
138
 
148
139
  __all__ = (
149
- "IsBasketballDice",
150
140
  "IsBot",
151
- "IsBowlingDice",
152
141
  "IsChat",
153
142
  "IsChatId",
154
- "IsDartDice",
155
- "IsDice",
143
+ "IsDiceEmoji",
156
144
  "IsForum",
157
145
  "IsForward",
158
146
  "IsForwardType",
@@ -28,8 +28,7 @@ class Markup(TextMessageRule):
28
28
  if not isinstance(patterns, list):
29
29
  patterns = [patterns]
30
30
  self.patterns = [
31
- vbml.Pattern(pattern) if isinstance(pattern, str) else pattern
32
- for pattern in patterns
31
+ vbml.Pattern(pattern) if isinstance(pattern, str) else pattern for pattern in patterns
33
32
  ]
34
33
 
35
34
  async def check(self, message: Message, ctx: Context) -> bool:
@@ -8,10 +8,7 @@ class HasMention(TextMessageRule):
8
8
  async def check(self, message: Message, ctx: Context) -> bool:
9
9
  if not message.entities.unwrap_or_none():
10
10
  return False
11
- return any(
12
- entity.type == MessageEntityType.MENTION
13
- for entity in message.entities.unwrap()
14
- )
11
+ return any(entity.type == MessageEntityType.MENTION for entity in message.entities.unwrap())
15
12
 
16
13
 
17
14
  __all__ = ("HasMention",)
@@ -0,0 +1,17 @@
1
+ import abc
2
+
3
+ from telegrinder.bot.dispatch.context import Context
4
+ from telegrinder.types.objects import Message as MessageEvent
5
+
6
+ from .abc import ABCRule, Message
7
+ from .adapter import EventAdapter
8
+
9
+
10
+ class MessageRule(ABCRule[Message], abc.ABC):
11
+ adapter = EventAdapter(MessageEvent, Message)
12
+
13
+ @abc.abstractmethod
14
+ async def check(self, message: Message, ctx: Context) -> bool: ...
15
+
16
+
17
+ __all__ = ("MessageRule",)
@@ -4,7 +4,7 @@ from telegrinder.bot.dispatch.context import Context
4
4
  from telegrinder.types.enums import MessageEntityType
5
5
  from telegrinder.types.objects import MessageEntity
6
6
 
7
- from .abc import Message, MessageRule
7
+ from .message import Message, MessageRule
8
8
 
9
9
  Entity: typing.TypeAlias = str | MessageEntityType
10
10
 
@@ -19,8 +19,7 @@ class Regex(TextMessageRule):
19
19
  self.regexp.append(re.compile(regex))
20
20
  case _:
21
21
  self.regexp.extend(
22
- re.compile(regexp) if isinstance(regexp, str) else regexp
23
- for regexp in regexp
22
+ re.compile(regexp) if isinstance(regexp, str) else regexp for regexp in regexp
24
23
  )
25
24
 
26
25
  async def check(self, message: Message, ctx: Context) -> bool:
@@ -21,9 +21,7 @@ class RuleEnum(ABCRule[T]):
21
21
  __enum__: list[RuleEnumState]
22
22
 
23
23
  def __init_subclass__(cls, *args, **kwargs):
24
- new_attributes = (
25
- set(cls.__dict__) - set(RuleEnum.__dict__) - {"__enum__", "__init__"}
26
- )
24
+ new_attributes = set(cls.__dict__) - set(RuleEnum.__dict__) - {"__enum__", "__init__"}
27
25
  enum_lst: list[RuleEnumState] = []
28
26
 
29
27
  self = cls.__new__(cls)
@@ -3,17 +3,19 @@ import typing
3
3
  from telegrinder.bot.dispatch.context import Context
4
4
  from telegrinder.types.enums import MessageEntityType
5
5
 
6
- from .abc import MessageRule
7
6
  from .is_from import IsPrivate
8
7
  from .markup import Markup, Message
8
+ from .message import MessageRule
9
9
  from .message_entities import MessageEntities
10
10
 
11
11
 
12
12
  class StartCommand(
13
- MessageRule, requires=[
14
- IsPrivate() & MessageEntities(MessageEntityType.BOT_COMMAND)
13
+ MessageRule,
14
+ requires=[
15
+ IsPrivate()
16
+ & MessageEntities(MessageEntityType.BOT_COMMAND)
15
17
  & Markup(["/start <param>", "/start"]),
16
- ]
18
+ ],
17
19
  ):
18
20
  def __init__(
19
21
  self,
@@ -28,9 +30,7 @@ class StartCommand(
28
30
 
29
31
  async def check(self, _: Message, ctx: Context) -> bool:
30
32
  param: str | None = ctx.pop("param", None)
31
- validated_param = (
32
- self.validator(param) if self.validator and param is not None else param
33
- )
33
+ validated_param = self.validator(param) if self.validator and param is not None else param
34
34
 
35
35
  if self.param_required and validated_param is None:
36
36
  return False
@@ -1,7 +1,8 @@
1
1
  from telegrinder.bot.dispatch.context import Context
2
2
  from telegrinder.tools.i18n.base import ABCTranslator
3
3
 
4
- from .abc import ABC, Message, MessageRule, with_caching_translations
4
+ from .abc import ABC, Message, with_caching_translations
5
+ from .message import MessageRule
5
6
 
6
7
 
7
8
  class HasText(MessageRule):
@@ -0,0 +1,16 @@
1
+ from telegrinder.bot.cute_types.update import UpdateCute
2
+ from telegrinder.bot.dispatch.context import Context
3
+ from telegrinder.types.enums import UpdateType
4
+
5
+ from .abc import ABCRule, T
6
+
7
+
8
+ class IsUpdate(ABCRule[T]):
9
+ def __init__(self, update_type: UpdateType, /) -> None:
10
+ self.update_type = update_type
11
+
12
+ async def check(self, event: UpdateCute, ctx: Context) -> bool:
13
+ return event.update_type.unwrap_or_none() == self.update_type
14
+
15
+
16
+ __all__ = ("IsUpdate",)
@@ -45,7 +45,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
45
45
  self.max_in_row = max_in_row
46
46
  self.random_code = secrets.token_hex(8)
47
47
  self.waiter_machine = waiter_machine
48
-
48
+
49
49
  def __repr__(self) -> str:
50
50
  return (
51
51
  "<{}@{!r}: (choices={!r}, max_in_row={}) with waiter_machine={!r}, ready_text={!r} "
@@ -69,14 +69,12 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
69
69
  choice = choices.pop(0)
70
70
  kb.add(
71
71
  InlineButton(
72
- text=choice.default_text
73
- if not choice.is_picked
74
- else choice.picked_text,
72
+ text=(choice.default_text if not choice.is_picked else choice.picked_text),
75
73
  callback_data=self.random_code + "/" + choice.code,
76
74
  )
77
75
  )
78
76
  kb.row()
79
-
77
+
80
78
  kb.add(InlineButton(self.ready, callback_data=self.random_code + "/ready"))
81
79
  return kb.get_markup()
82
80
 
@@ -125,14 +123,14 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
125
123
  reply_markup=self.get_markup(),
126
124
  )
127
125
  ).unwrap()
128
-
126
+
129
127
  while True:
130
128
  q, _ = await self.waiter_machine.wait(view, (api, message.message_id))
131
129
  should_continue = await self.handle(q)
132
130
  await q.answer(self.CALLBACK_ANSWER)
133
131
  if not should_continue:
134
132
  break
135
-
133
+
136
134
  return (
137
135
  {choice.name: choice.is_picked for choice in self.choices},
138
136
  message.message_id,
@@ -24,7 +24,7 @@ class AiohttpClient(ABCClient):
24
24
  self.json_processing_module = json_processing_module or json
25
25
  self.session_params = session_params
26
26
  self.timeout = timeout or aiohttp.ClientTimeout(total=0)
27
-
27
+
28
28
  def __repr__(self) -> str:
29
29
  return "<{}: session={!r}, timeout={}, closed={}>".format(
30
30
  self.__class__.__name__,
@@ -32,7 +32,7 @@ class AiohttpClient(ABCClient):
32
32
  self.timeout,
33
33
  True if self.session is None else self.session.closed,
34
34
  )
35
-
35
+
36
36
  async def request_raw(
37
37
  self,
38
38
  url: str,
@@ -42,9 +42,7 @@ class AiohttpClient(ABCClient):
42
42
  ) -> "ClientResponse":
43
43
  if not self.session:
44
44
  self.session = ClientSession(
45
- connector=TCPConnector(
46
- ssl=ssl.create_default_context(cafile=certifi.where())
47
- ),
45
+ connector=TCPConnector(ssl=ssl.create_default_context(cafile=certifi.where())),
48
46
  json_serialize=self.json_processing_module.dumps,
49
47
  **self.session_params,
50
48
  )
@@ -118,10 +116,10 @@ class AiohttpClient(ABCClient):
118
116
  form = aiohttp.formdata.FormData(quote_fields=False)
119
117
  for k, v in data.items():
120
118
  form.add_field(k, str(v))
121
-
119
+
122
120
  for n, f in files.items():
123
121
  form.add_field(n, f[1], filename=f[0])
124
-
122
+
125
123
  return form
126
124
 
127
125
  def __del__(self) -> None:
telegrinder/model.py CHANGED
@@ -10,11 +10,11 @@ from fntypes.co import Nothing, Result, Some
10
10
 
11
11
  from .msgspec_utils import decoder, encoder, get_origin
12
12
 
13
- T = typing.TypeVar("T")
14
-
15
13
  if typing.TYPE_CHECKING:
16
14
  from telegrinder.api.error import APIError
17
-
15
+
16
+ T = typing.TypeVar("T")
17
+
18
18
 
19
19
  MODEL_CONFIG: typing.Final[dict[str, typing.Any]] = {
20
20
  "omit_defaults": True,
@@ -25,17 +25,16 @@ MODEL_CONFIG: typing.Final[dict[str, typing.Any]] = {
25
25
 
26
26
  @typing.overload
27
27
  def full_result(
28
- result: Result[msgspec.Raw, "APIError"], full_t: type[T]
29
- ) -> Result[T, "APIError"]:
30
- ...
28
+ result: Result[msgspec.Raw, "APIError"],
29
+ full_t: type[T],
30
+ ) -> Result[T, "APIError"]: ...
31
31
 
32
32
 
33
33
  @typing.overload
34
34
  def full_result(
35
35
  result: Result[msgspec.Raw, "APIError"],
36
36
  full_t: tuple[type[T], ...],
37
- ) -> Result[T, "APIError"]:
38
- ...
37
+ ) -> Result[T, "APIError"]: ...
39
38
 
40
39
 
41
40
  def full_result(
@@ -65,7 +64,7 @@ class Model(msgspec.Struct, **MODEL_CONFIG):
65
64
  self,
66
65
  *,
67
66
  exclude_fields: set[str] | None = None,
68
- ) -> dict[str, typing.Any]:
67
+ ) -> dict[str, typing.Any]:
69
68
  exclude_fields = exclude_fields or set()
70
69
  if "model_as_dict" not in self.__dict__:
71
70
  self.__dict__["model_as_dict"] = msgspec.structs.asdict(self)
@@ -91,13 +90,13 @@ class DataConverter:
91
90
  return {
92
91
  get_origin(value.__annotations__["data"]): value
93
92
  for key, value in vars(self.__class__).items()
94
- if key.startswith("convert_") and callable(value)
93
+ if key.startswith("convert_") and callable(value)
95
94
  }
96
95
 
97
96
  @staticmethod
98
97
  def convert_enum(data: enum.Enum, _: bool = True) -> typing.Any:
99
98
  return data.value
100
-
99
+
101
100
  @staticmethod
102
101
  def convert_datetime(data: datetime, _: bool = True) -> int:
103
102
  return int(data.timestamp())
@@ -109,29 +108,45 @@ class DataConverter:
109
108
  return converter(data, serialize)
110
109
  return converter(self, data, serialize)
111
110
  return data
112
-
111
+
113
112
  def get_converter(self, t: type[typing.Any]):
114
113
  for type, converter in self.converters.items():
115
114
  if issubclass(t, type):
116
115
  return converter
117
116
  return None
118
-
119
- def convert_model(self, data: Model, serialize: bool = True) -> str | dict[str, typing.Any]:
117
+
118
+ def convert_model(
119
+ self,
120
+ data: Model,
121
+ serialize: bool = True,
122
+ ) -> str | dict[str, typing.Any]:
120
123
  converted_dct = self(data.to_dict(), serialize=False)
121
124
  return encoder.encode(converted_dct) if serialize is True else converted_dct
122
-
123
- def convert_dct(self, data: dict[str, typing.Any], serialize: bool = True) -> dict[str, typing.Any]:
125
+
126
+ def convert_dct(
127
+ self,
128
+ data: dict[str, typing.Any],
129
+ serialize: bool = True,
130
+ ) -> dict[str, typing.Any]:
124
131
  return {
125
132
  k: self(v, serialize=serialize)
126
133
  for k, v in data.items()
127
134
  if type(v) not in (NoneType, Nothing)
128
135
  }
129
-
130
- def convert_lst(self, data: list[typing.Any], serialize: bool = True) -> str | list[typing.Any]:
136
+
137
+ def convert_lst(
138
+ self,
139
+ data: list[typing.Any],
140
+ serialize: bool = True,
141
+ ) -> str | list[typing.Any]:
131
142
  converted_lst = [self(x, serialize=False) for x in data]
132
143
  return encoder.encode(converted_lst) if serialize is True else converted_lst
133
-
134
- def convert_tpl(self, data: tuple[typing.Any, ...], _: bool = True) -> str | tuple[typing.Any, ...]:
144
+
145
+ def convert_tpl(
146
+ self,
147
+ data: tuple[typing.Any, ...],
148
+ _: bool = True,
149
+ ) -> str | tuple[typing.Any, ...]:
135
150
  if (
136
151
  isinstance(data, tuple)
137
152
  and len(data) == 2
@@ -142,12 +157,12 @@ class DataConverter:
142
157
  self.files[attach_name] = data
143
158
  return "attach://{}".format(attach_name)
144
159
  return data
145
-
160
+
146
161
 
147
162
  __all__ = (
148
163
  "DataConverter",
164
+ "MODEL_CONFIG",
149
165
  "Model",
150
166
  "full_result",
151
167
  "get_params",
152
- "MODEL_CONFIG",
153
168
  )
telegrinder/modules.py CHANGED
@@ -6,32 +6,24 @@ from choicelib import choice_in_order
6
6
 
7
7
  @typing.runtime_checkable
8
8
  class JSONModule(typing.Protocol):
9
- def loads(self, s: str | bytes) -> typing.Any:
10
- ...
9
+ def loads(self, s: str | bytes) -> typing.Any: ...
11
10
 
12
- def dumps(self, o: typing.Any) -> str:
13
- ...
11
+ def dumps(self, o: typing.Any) -> str: ...
14
12
 
15
13
 
16
14
  @typing.runtime_checkable
17
15
  class LoggerModule(typing.Protocol):
18
- def debug(self, __msg: object, *args: object, **kwargs: object) -> None:
19
- ...
16
+ def debug(self, __msg: object, *args: object, **kwargs: object) -> None: ...
20
17
 
21
- def info(self, __msg: object, *args: object, **kwargs: object) -> None:
22
- ...
18
+ def info(self, __msg: object, *args: object, **kwargs: object) -> None: ...
23
19
 
24
- def warning(self, __msg: object, *args: object, **kwargs: object) -> None:
25
- ...
20
+ def warning(self, __msg: object, *args: object, **kwargs: object) -> None: ...
26
21
 
27
- def error(self, __msg: object, *args: object, **kwargs: object) -> None:
28
- ...
22
+ def error(self, __msg: object, *args: object, **kwargs: object) -> None: ...
29
23
 
30
- def critical(self, __msg: object, *args: object, **kwargs: object) -> None:
31
- ...
24
+ def critical(self, __msg: object, *args: object, **kwargs: object) -> None: ...
32
25
 
33
- def exception(self, __msg: object, *args: object, **kwargs: object) -> None:
34
- ...
26
+ def exception(self, __msg: object, *args: object, **kwargs: object) -> None: ...
35
27
 
36
28
  def set_level(
37
29
  self,
@@ -43,8 +35,7 @@ class LoggerModule(typing.Protocol):
43
35
  "CRITICAL",
44
36
  "EXCEPTION",
45
37
  ],
46
- ) -> None:
47
- ...
38
+ ) -> None: ...
48
39
 
49
40
 
50
41
  logger: LoggerModule
@@ -147,8 +138,7 @@ elif logging_module == "logging":
147
138
  },
148
139
  }
149
140
  FORMAT = (
150
- FORMAT
151
- .replace("<white>", COLORS["white"])
141
+ FORMAT.replace("<white>", COLORS["white"])
152
142
  .replace("</white>", COLORS["reset"])
153
143
  .replace("<green>", COLORS["green"])
154
144
  .replace("</green>", COLORS["reset"])
@@ -157,24 +147,18 @@ elif logging_module == "logging":
157
147
  for level, settings in LEVEL_SETTINGS.items():
158
148
  fmt = FORMAT
159
149
  for name, color in settings.items():
160
- fmt = (
161
- fmt
162
- .replace(f"<{name}>", COLORS[color])
163
- .replace(f"</{name}>", COLORS["reset"])
164
- )
150
+ fmt = fmt.replace(f"<{name}>", COLORS[color]).replace(f"</{name}>", COLORS["reset"])
165
151
  LEVEL_FORMATS[level] = fmt
166
152
 
167
-
168
153
  class TelegrinderLoggingFormatter(logging.Formatter):
169
- def format(self, record: logging.LogRecord) -> str:
154
+ def format(self, record: logging.LogRecord) -> str:
170
155
  if not record.funcName or record.funcName == "<module>":
171
156
  record.funcName = "\b"
172
157
  frame = next(
173
158
  (
174
159
  frame
175
160
  for frame in inspect.stack()
176
- if frame.filename == record.pathname
177
- and frame.lineno == record.lineno
161
+ if frame.filename == record.pathname and frame.lineno == record.lineno
178
162
  ),
179
163
  None,
180
164
  )
@@ -187,7 +171,6 @@ elif logging_module == "logging":
187
171
  style="{",
188
172
  ).format(record)
189
173
 
190
-
191
174
  class LogMessage:
192
175
  def __init__(self, fmt: typing.Any, args: typing.Any, kwargs: typing.Any) -> None:
193
176
  self.fmt = fmt
@@ -197,7 +180,6 @@ elif logging_module == "logging":
197
180
  def __str__(self) -> str:
198
181
  return self.fmt.format(*self.args, **self.kwargs)
199
182
 
200
-
201
183
  class TelegrinderLoggingStyleAdapter(logging.LoggerAdapter):
202
184
  def __init__(
203
185
  self,
@@ -223,7 +205,7 @@ elif logging_module == "logging":
223
205
  for key in inspect.getfullargspec(self.logger._log).args[1:]
224
206
  if key in kwargs
225
207
  }
226
-
208
+
227
209
  if isinstance(msg, str):
228
210
  msg = LogMessage(msg, args, kwargs)
229
211
  args = tuple()
@@ -245,7 +227,7 @@ def _set_logger_level(level):
245
227
  logging.getLogger("telegrinder").setLevel(logging.getLevelName(level))
246
228
  elif logging_module == "loguru":
247
229
  import loguru # type: ignore
248
-
230
+
249
231
  if handler_id in loguru.logger._core.handlers: # type: ignore
250
232
  loguru.logger._core.handlers[handler_id]._levelno = loguru.logger.level(level).no # type: ignore
251
233