telegrinder 0.1.dev161__py3-none-any.whl → 0.1.dev162__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.

@@ -37,7 +37,7 @@ class Composition:
37
37
  return None
38
38
  return NodeCollection(nodes)
39
39
 
40
- async def __call__(self, **kwargs) -> typing.Any:
40
+ async def __call__(self, **kwargs: typing.Any) -> typing.Any:
41
41
  return await self.func(**magic_bundle(self.func, kwargs, start_idx=0, bundle_ctx=False)) # type: ignore
42
42
 
43
43
 
@@ -87,7 +87,7 @@ class Dispatch(
87
87
  break
88
88
  return found
89
89
 
90
- def load(self, external: typing.Self):
90
+ def load(self, external: typing.Self) -> None:
91
91
  view_external = external.get_views()
92
92
  for name, view in self.get_views().items():
93
93
  assert (
@@ -11,7 +11,7 @@ if typing.TYPE_CHECKING:
11
11
  from .machine import Identificator
12
12
 
13
13
  EventModel = typing.TypeVar("EventModel", bound=BaseCute)
14
- Behaviour = ABCHandler | None
14
+ Behaviour: typing.TypeAlias = ABCHandler | None
15
15
 
16
16
 
17
17
  class ShortState(typing.Generic[EventModel]):
@@ -43,7 +43,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
43
43
  self.choices: list[Choice] = []
44
44
  self.ready = ready_text
45
45
  self.max_in_row = max_in_row
46
- self.random_code = secrets.token_hex(16)
46
+ self.random_code = secrets.token_hex(8)
47
47
  self.waiter_machine = waiter_machine
48
48
 
49
49
  def get_markup(self) -> InlineKeyboardMarkup:
@@ -73,7 +73,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
73
73
  is_picked: bool = False,
74
74
  ) -> typing.Self:
75
75
  self.choices.append(
76
- Choice(name, is_picked, default_text, picked_text, secrets.token_hex(16)),
76
+ Choice(name, is_picked, default_text, picked_text, secrets.token_hex(8)),
77
77
  )
78
78
  return self
79
79
 
@@ -103,7 +103,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
103
103
  assert len(self.choices) > 1
104
104
  message = (
105
105
  await api.send_message(
106
- self.chat_id,
106
+ chat_id=self.chat_id,
107
107
  text=self.msg,
108
108
  parse_mode=self.PARSE_MODE,
109
109
  reply_markup=self.get_markup(),
@@ -111,7 +111,6 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
111
111
  ).unwrap()
112
112
 
113
113
  while True:
114
- q: CallbackQueryCute
115
114
  q, _ = await self.waiter_machine.wait(view, (api, message.message_id))
116
115
  should_continue = await self.handle(q)
117
116
  await q.answer(self.CALLBACK_ANSWER)
telegrinder/modules.py CHANGED
@@ -4,14 +4,16 @@ import typing
4
4
  from choicelib import choice_in_order
5
5
 
6
6
 
7
+ @typing.runtime_checkable
7
8
  class JSONModule(typing.Protocol):
8
- def loads(self, s: str | bytes) -> dict[str, typing.Any] | list[typing.Any]:
9
+ def loads(self, s: str | bytes) -> typing.Any:
9
10
  ...
10
11
 
11
- def dumps(self, o: dict[str, typing.Any] | list[typing.Any]) -> str:
12
+ def dumps(self, o: typing.Any) -> str:
12
13
  ...
13
14
 
14
15
 
16
+ @typing.runtime_checkable
15
17
  class LoggerModule(typing.Protocol):
16
18
  def debug(self, __msg: object, *args: object, **kwargs: object) -> None:
17
19
  ...
@@ -47,7 +49,7 @@ class LoggerModule(typing.Protocol):
47
49
 
48
50
  logger: LoggerModule
49
51
  json: JSONModule = choice_in_order(
50
- ["ujson", "hyperjson", "orjson"], do_import=True, default="telegrinder.msgspec_json"
52
+ ["orjson", "ujson", "hyperjson"], do_import=True, default="telegrinder.msgspec_json"
51
53
  )
52
54
  logging_module = choice_in_order(["loguru"], default="logging")
53
55
  logging_level = os.getenv("LOGGER_LEVEL", default="DEBUG").upper()
@@ -3,11 +3,11 @@ import typing
3
3
  from .msgspec_utils import decoder, encoder
4
4
 
5
5
 
6
- def loads(s: str | bytes) -> dict[str, typing.Any] | list[typing.Any]:
7
- return decoder.decode(s, type=dict[str, typing.Any] | list[typing.Any]) # type: ignore
6
+ def loads(s: str | bytes) -> typing.Any:
7
+ return decoder.decode(s)
8
8
 
9
9
 
10
- def dumps(o: dict[str, typing.Any] | list[typing.Any]) -> str:
10
+ def dumps(o: typing.Any) -> str:
11
11
  return encoder.encode(o)
12
12
 
13
13
 
@@ -8,7 +8,6 @@ T = typing.TypeVar("T")
8
8
  Ts = typing.TypeVarTuple("Ts")
9
9
 
10
10
  if typing.TYPE_CHECKING:
11
- import types
12
11
  from datetime import datetime
13
12
 
14
13
  from fntypes.option import Option
@@ -48,10 +47,6 @@ def msgspec_convert(obj: typing.Any, t: type[T]) -> Result[T, msgspec.Validation
48
47
  return Error(exc)
49
48
 
50
49
 
51
- def datetime_dec_hook(tp: type[datetime], obj: int | float) -> datetime:
52
- return tp.fromtimestamp(obj)
53
-
54
-
55
50
  def option_dec_hook(tp: type[Option[typing.Any]], obj: typing.Any) -> Option[typing.Any]:
56
51
  if obj is None:
57
52
  return Nothing
@@ -88,37 +83,23 @@ def variative_dec_hook(tp: type[Variative], obj: typing.Any) -> Variative:
88
83
 
89
84
  raise TypeError(
90
85
  "Object of type `{}` does not belong to types `{}`".format(
91
- repr_type(type(obj)),
86
+ repr_type(obj.__class__),
92
87
  " | ".join(map(repr_type, union_types)),
93
88
  )
94
89
  )
95
90
 
96
91
 
97
- def datetime_enc_hook(obj: datetime) -> int:
98
- return int(obj.timestamp())
99
-
100
-
101
- def option_enc_hook(obj: Option[typing.Any]) -> typing.Any | None:
102
- return obj.value if isinstance(obj, fntypes.option.Some) else None
103
-
104
-
105
- def variative_enc_hook(obj: Variative[typing.Any]) -> typing.Any:
106
- return obj.v
107
-
108
-
109
92
  class Decoder:
110
93
  def __init__(self) -> None:
111
- self.dec_hooks: dict[
112
- typing.Union[type[typing.Any], "types.UnionType"], DecHook[typing.Any]
113
- ] = {
94
+ self.dec_hooks: dict[typing.Any, DecHook[typing.Any]] = {
114
95
  Option: option_dec_hook,
115
96
  Variative: variative_dec_hook,
116
- datetime: datetime_dec_hook,
97
+ datetime: lambda t, obj: t.fromtimestamp(obj),
117
98
  }
118
99
 
119
- def add_dec_hook(self, tp: type[T]):
100
+ def add_dec_hook(self, t: T): # type: ignore
120
101
  def decorator(func: DecHook[T]) -> DecHook[T]:
121
- return self.dec_hooks.setdefault(get_origin(tp), func)
102
+ return self.dec_hooks.setdefault(get_origin(t), func) # type: ignore
122
103
 
123
104
  return decorator
124
105
 
@@ -150,12 +131,30 @@ class Decoder:
150
131
  builtin_types=builtin_types,
151
132
  str_keys=str_keys,
152
133
  )
134
+
135
+ @typing.overload
136
+ def decode(self, buf: str | bytes) -> typing.Any:
137
+ ...
138
+
139
+ @typing.overload
140
+ def decode(self, buf: str | bytes, *, strict: bool = True) -> typing.Any:
141
+ ...
153
142
 
143
+ @typing.overload
154
144
  def decode(
155
145
  self,
156
146
  buf: str | bytes,
157
147
  *,
158
- type: type[T] = dict,
148
+ type: type[T],
149
+ strict: bool = True,
150
+ ) -> T:
151
+ ...
152
+
153
+ def decode(
154
+ self,
155
+ buf: str | bytes,
156
+ *,
157
+ type: type[T] = typing.Any, # type: ignore
159
158
  strict: bool = True,
160
159
  ) -> T:
161
160
  return msgspec.json.decode(
@@ -168,11 +167,11 @@ class Decoder:
168
167
 
169
168
  class Encoder:
170
169
  def __init__(self) -> None:
171
- self.enc_hooks: dict[type, EncHook] = {
172
- fntypes.option.Some: option_enc_hook,
173
- fntypes.option.Nothing: option_enc_hook,
174
- Variative: variative_enc_hook,
175
- datetime: datetime_enc_hook,
170
+ self.enc_hooks: dict[typing.Any, EncHook[typing.Any]] = {
171
+ fntypes.option.Some: lambda opt: opt.value,
172
+ fntypes.option.Nothing: lambda _: None,
173
+ Variative: lambda variative: variative.v,
174
+ datetime: lambda date: int(date.timestamp()),
176
175
  }
177
176
 
178
177
  def add_dec_hook(self, tp: type[T]):
@@ -182,13 +181,17 @@ class Encoder:
182
181
  return decorator
183
182
 
184
183
  def enc_hook(self, obj: object) -> object:
185
- origin_type = get_origin(type(obj))
184
+ origin_type = get_origin(obj.__class__)
186
185
  if origin_type not in self.enc_hooks:
187
186
  raise NotImplementedError(
188
187
  "Not implemented encode hook for "
189
188
  f"object of type `{repr_type(origin_type)}`."
190
189
  )
191
190
  return self.enc_hooks[origin_type](obj)
191
+
192
+ @typing.overload
193
+ def encode(self, obj: typing.Any) -> str:
194
+ ...
192
195
 
193
196
  @typing.overload
194
197
  def encode(self, obj: typing.Any, *, as_str: typing.Literal[True] = True) -> str:
@@ -215,12 +218,8 @@ __all__ = (
215
218
  "get_origin",
216
219
  "repr_type",
217
220
  "msgspec_convert",
218
- "datetime_dec_hook",
219
- "datetime_enc_hook",
220
221
  "option_dec_hook",
221
- "option_enc_hook",
222
222
  "variative_dec_hook",
223
- "variative_enc_hook",
224
223
  "datetime",
225
224
  "decoder",
226
225
  "encoder",
@@ -14,6 +14,7 @@ from .formatting import (
14
14
  StartBotLink,
15
15
  StartGroupLink,
16
16
  TgEmoji,
17
+ UserOpenMessage,
17
18
  block_quote,
18
19
  bold,
19
20
  channel_boost_link,
@@ -37,6 +38,8 @@ from .formatting import (
37
38
  strike,
38
39
  tg_emoji,
39
40
  underline,
41
+ user_open_message,
42
+ user_open_message_link,
40
43
  )
41
44
  from .global_context import (
42
45
  ABCGlobalContext,
@@ -109,6 +112,7 @@ __all__ = (
109
112
  "StartGroupLink",
110
113
  "TelegrinderCtx",
111
114
  "TgEmoji",
115
+ "UserOpenMessage",
112
116
  "block_quote",
113
117
  "bold",
114
118
  "channel_boost_link",
@@ -135,4 +139,6 @@ __all__ = (
135
139
  "strike",
136
140
  "tg_emoji",
137
141
  "underline",
142
+ "user_open_message",
143
+ "user_open_message_link",
138
144
  )
@@ -26,13 +26,11 @@ class DataclassInstance(typing.Protocol):
26
26
  class BaseButton:
27
27
  def get_data(self) -> dict[str, typing.Any]:
28
28
  return {
29
- k: v
30
- if k != "callback_data" or isinstance(v, str)
31
- else encoder.encode(v)
29
+ k: v if k != "callback_data" or isinstance(v, str) else encoder.encode(v)
32
30
  for k, v in dataclasses.asdict(self).items()
33
31
  if v is not None
34
32
  }
35
-
33
+
36
34
 
37
35
  class RowButtons(typing.Generic[ButtonT]):
38
36
  buttons: list[ButtonT]
@@ -65,18 +63,15 @@ class InlineButton(BaseButton):
65
63
  url: str | None = None
66
64
  login_url: dict[str, typing.Any] | LoginUrl | None = None
67
65
  pay: bool | None = None
68
- callback_data: typing.Union[
69
- str,
70
- dict[str, typing.Any],
71
- DataclassInstance,
72
- msgspec.Struct,
73
- ] | None = None
66
+ callback_data: (
67
+ str | dict[str, typing.Any] | DataclassInstance | msgspec.Struct | None
68
+ ) = None
74
69
  callback_game: dict[str, typing.Any] | CallbackGame | None = None
75
70
  switch_inline_query: str | None = None
76
71
  switch_inline_query_current_chat: str | None = None
77
- switch_inline_query_chosen_chat: dict[
78
- str, typing.Any
79
- ] | SwitchInlineQueryChosenChat | None = None
72
+ switch_inline_query_chosen_chat: (
73
+ dict[str, typing.Any] | SwitchInlineQueryChosenChat | None
74
+ ) = None
80
75
  web_app: dict[str, typing.Any] | WebAppInfo | None = None
81
76
 
82
77
 
@@ -18,6 +18,7 @@ from .html import (
18
18
  strike,
19
19
  tg_emoji,
20
20
  underline,
21
+ user_open_message,
21
22
  )
22
23
  from .links import (
23
24
  get_channel_boost_link,
@@ -26,6 +27,7 @@ from .links import (
26
27
  get_resolve_domain_link,
27
28
  get_start_bot_link,
28
29
  get_start_group_link,
30
+ user_open_message_link,
29
31
  )
30
32
  from .spec_html_formats import (
31
33
  BaseSpecFormat,
@@ -39,6 +41,7 @@ from .spec_html_formats import (
39
41
  StartBotLink,
40
42
  StartGroupLink,
41
43
  TgEmoji,
44
+ UserOpenMessage,
42
45
  )
43
46
 
44
47
  __all__ = (
@@ -55,6 +58,7 @@ __all__ = (
55
58
  "StartBotLink",
56
59
  "StartGroupLink",
57
60
  "TgEmoji",
61
+ "UserOpenMessage",
58
62
  "block_quote",
59
63
  "bold",
60
64
  "channel_boost_link",
@@ -78,4 +82,6 @@ __all__ = (
78
82
  "strike",
79
83
  "tg_emoji",
80
84
  "underline",
85
+ "user_open_message",
86
+ "user_open_message_link",
81
87
  )
@@ -13,6 +13,7 @@ from .links import (
13
13
  get_resolve_domain_link,
14
14
  get_start_bot_link,
15
15
  get_start_group_link,
16
+ user_open_message_link,
16
17
  )
17
18
  from .spec_html_formats import SpecialFormat, is_spec_format
18
19
 
@@ -274,6 +275,14 @@ def underline(string: str) -> TagFormat:
274
275
  return TagFormat(string, tag="u")
275
276
 
276
277
 
278
+ def user_open_message(
279
+ user_id: int,
280
+ message: str | None = None,
281
+ string: str | None = None,
282
+ ) -> TagFormat:
283
+ return link(user_open_message_link(user_id, message), string)
284
+
285
+
277
286
  __all__ = (
278
287
  "FormatString",
279
288
  "HTMLFormatter",
@@ -301,4 +310,5 @@ __all__ = (
301
310
  "strike",
302
311
  "tg_emoji",
303
312
  "underline",
313
+ "user_open_message",
304
314
  )
@@ -22,6 +22,12 @@ def get_invite_chat_link(invite_link: str) -> str:
22
22
  return f"tg://join?invite={invite_link}"
23
23
 
24
24
 
25
+ def user_open_message_link(user_id: int, message: str | None = None) -> str:
26
+ return f"tg://openmessage?user_id={user_id}" + (
27
+ "" if not message else f"&msg?text={message}"
28
+ )
29
+
30
+
25
31
  __all__ = (
26
32
  "get_channel_boost_link",
27
33
  "get_invite_chat_link",
@@ -29,4 +35,5 @@ __all__ = (
29
35
  "get_resolve_domain_link",
30
36
  "get_start_bot_link",
31
37
  "get_start_group_link",
38
+ "user_open_message_link",
32
39
  )
@@ -5,14 +5,16 @@ from telegrinder.types.enums import ProgrammingLanguage
5
5
 
6
6
  SpecialFormat = typing.Union[
7
7
  "ChannelBoostLink",
8
- "Mention",
8
+ "InviteChatLink",
9
9
  "Link",
10
+ "Mention",
10
11
  "PreCode",
11
- "TgEmoji",
12
+ "ResolveDomain",
13
+ "SpecialFormat",
12
14
  "StartBotLink",
13
15
  "StartGroupLink",
14
- "ResolveDomain",
15
- "InviteChatLink",
16
+ "TgEmoji",
17
+ "UserOpenMessage",
16
18
  ]
17
19
 
18
20
 
@@ -26,13 +28,13 @@ def is_spec_format(obj: typing.Any) -> typing.TypeGuard[SpecialFormat]:
26
28
 
27
29
  @dataclasses.dataclass(repr=False)
28
30
  class BaseSpecFormat:
29
- __formatter_name__: typing.ClassVar[str]
31
+ __formatter_name__: typing.ClassVar[str] = dataclasses.field(init=False, repr=False)
30
32
 
31
33
  def __repr__(self) -> str:
32
- return f"<{self.__class__.__name__!r}: {self.__formatter_name__!r}>"
34
+ return f"<Special formatter {self.__class__.__name__!r} -> {self.__formatter_name__!r}>"
33
35
 
34
36
 
35
- @dataclasses.dataclass
37
+ @dataclasses.dataclass(repr=False)
36
38
  class ChannelBoostLink(BaseSpecFormat):
37
39
  __formatter_name__ = "channel_boost_link"
38
40
 
@@ -40,7 +42,7 @@ class ChannelBoostLink(BaseSpecFormat):
40
42
  string: str | None = None
41
43
 
42
44
 
43
- @dataclasses.dataclass
45
+ @dataclasses.dataclass(repr=False)
44
46
  class InviteChatLink(BaseSpecFormat):
45
47
  __formatter_name__ = "invite_chat_link"
46
48
 
@@ -48,7 +50,7 @@ class InviteChatLink(BaseSpecFormat):
48
50
  string: str | None = None
49
51
 
50
52
 
51
- @dataclasses.dataclass
53
+ @dataclasses.dataclass(repr=False)
52
54
  class Mention(BaseSpecFormat):
53
55
  __formatter_name__ = "mention"
54
56
 
@@ -56,7 +58,7 @@ class Mention(BaseSpecFormat):
56
58
  user_id: int
57
59
 
58
60
 
59
- @dataclasses.dataclass
61
+ @dataclasses.dataclass(repr=False)
60
62
  class Link(BaseSpecFormat):
61
63
  __formatter_name__ = "link"
62
64
 
@@ -64,7 +66,7 @@ class Link(BaseSpecFormat):
64
66
  string: str | None = None
65
67
 
66
68
 
67
- @dataclasses.dataclass
69
+ @dataclasses.dataclass(repr=False)
68
70
  class PreCode(BaseSpecFormat):
69
71
  __formatter_name__ = "pre_code"
70
72
 
@@ -72,7 +74,7 @@ class PreCode(BaseSpecFormat):
72
74
  lang: str | ProgrammingLanguage | None = None
73
75
 
74
76
 
75
- @dataclasses.dataclass
77
+ @dataclasses.dataclass(repr=False)
76
78
  class TgEmoji(BaseSpecFormat):
77
79
  __formatter_name__ = "tg_emoji"
78
80
 
@@ -80,7 +82,7 @@ class TgEmoji(BaseSpecFormat):
80
82
  emoji_id: int
81
83
 
82
84
 
83
- @dataclasses.dataclass
85
+ @dataclasses.dataclass(repr=False)
84
86
  class StartBotLink(BaseSpecFormat):
85
87
  __formatter_name__ = "start_bot_link"
86
88
 
@@ -89,7 +91,7 @@ class StartBotLink(BaseSpecFormat):
89
91
  string: str | None
90
92
 
91
93
 
92
- @dataclasses.dataclass
94
+ @dataclasses.dataclass(repr=False)
93
95
  class StartGroupLink(BaseSpecFormat):
94
96
  __formatter_name__ = "start_group_link"
95
97
 
@@ -98,7 +100,7 @@ class StartGroupLink(BaseSpecFormat):
98
100
  string: str | None = None
99
101
 
100
102
 
101
- @dataclasses.dataclass
103
+ @dataclasses.dataclass(repr=False)
102
104
  class ResolveDomain(BaseSpecFormat):
103
105
  __formatter_name__ = "resolve_domain"
104
106
 
@@ -106,6 +108,15 @@ class ResolveDomain(BaseSpecFormat):
106
108
  string: str | None = None
107
109
 
108
110
 
111
+ @dataclasses.dataclass(repr=False)
112
+ class UserOpenMessage(BaseSpecFormat):
113
+ __formatter_name__ = "user_open_message"
114
+
115
+ user_id: int
116
+ message: str | None = None
117
+ string: str | None = None
118
+
119
+
109
120
  __all__ = (
110
121
  "BaseSpecFormat",
111
122
  "ChannelBoostLink",
@@ -118,4 +129,5 @@ __all__ = (
118
129
  "StartBotLink",
119
130
  "StartGroupLink",
120
131
  "TgEmoji",
132
+ "UserOpenMessage",
121
133
  )
@@ -91,9 +91,6 @@ class Keyboard(ABCMarkup[Button], KeyboardModel):
91
91
  selective: bool = dataclasses.field(default=False)
92
92
  is_persistent: bool = dataclasses.field(default=False)
93
93
 
94
- def get_empty_markup(self, *, selective: bool = False) -> ReplyKeyboardRemove:
95
- return ReplyKeyboardRemove(remove_keyboard=True, selective=selective) # type: ignore
96
-
97
94
  def dict(self) -> DictStrAny:
98
95
  self.keyboard = [row for row in self.keyboard if row]
99
96
  return {
@@ -105,6 +102,9 @@ class Keyboard(ABCMarkup[Button], KeyboardModel):
105
102
  def get_markup(self) -> ReplyKeyboardMarkup:
106
103
  return ReplyKeyboardMarkup(**self.dict())
107
104
 
105
+ def get_empty_markup(self, *, selective: bool = False) -> ReplyKeyboardRemove:
106
+ return ReplyKeyboardRemove(remove_keyboard=True, selective=Some(selective))
107
+
108
108
 
109
109
  class InlineKeyboard(ABCMarkup[InlineButton]):
110
110
  BUTTON = InlineButton
@@ -1,18 +1,18 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
3
 
4
- CoroutineTask = typing.Coroutine[typing.Any, typing.Any, typing.Any]
5
- CoroutineFunc = typing.Callable[..., CoroutineTask]
4
+ CoroutineTask: typing.TypeAlias = typing.Coroutine[typing.Any, typing.Any, typing.Any]
5
+ CoroutineFunc: typing.TypeAlias = typing.Callable[..., CoroutineTask]
6
6
 
7
7
 
8
8
  class ABCLoopWrapper(ABC):
9
9
  @abstractmethod
10
10
  def add_task(self, task: CoroutineFunc | CoroutineTask) -> None:
11
- ...
11
+ pass
12
12
 
13
13
  @abstractmethod
14
14
  def run_event_loop(self) -> None:
15
- ...
15
+ pass
16
16
 
17
17
 
18
18
  __all__ = ("ABCLoopWrapper",)
@@ -415,6 +415,10 @@ class UpdateType(str, enum.Enum):
415
415
  CHAT_JOIN_REQUEST = "chat_join_request"
416
416
  CHAT_BOOST = "chat_boost"
417
417
  REMOVED_CHAT_BOOST = "removed_chat_boost"
418
+ BUSINESS_CONNECTION = "business_connection"
419
+ BUSINESS_MESSAGE = "business_message"
420
+ EDITED_BUSINESS_MESSAGE = "edited_business_message"
421
+ DELETE_BUSINESS_MESSAGE = "delete_business_messages"
418
422
 
419
423
 
420
424
  class BotCommandScopeType(str, enum.Enum):