telegrinder 0.3.4.post1__py3-none-any.whl → 0.4.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.
- telegrinder/__init__.py +30 -31
- telegrinder/api/__init__.py +2 -1
- telegrinder/api/api.py +28 -20
- telegrinder/api/error.py +8 -4
- telegrinder/api/response.py +2 -2
- telegrinder/api/token.py +2 -2
- telegrinder/bot/__init__.py +6 -0
- telegrinder/bot/bot.py +38 -31
- telegrinder/bot/cute_types/__init__.py +2 -0
- telegrinder/bot/cute_types/base.py +54 -128
- telegrinder/bot/cute_types/callback_query.py +76 -61
- telegrinder/bot/cute_types/chat_join_request.py +4 -3
- telegrinder/bot/cute_types/chat_member_updated.py +28 -31
- telegrinder/bot/cute_types/inline_query.py +5 -4
- telegrinder/bot/cute_types/message.py +555 -602
- telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
- telegrinder/bot/cute_types/update.py +20 -12
- telegrinder/bot/cute_types/utils.py +3 -36
- telegrinder/bot/dispatch/__init__.py +4 -0
- telegrinder/bot/dispatch/abc.py +8 -9
- telegrinder/bot/dispatch/context.py +5 -7
- telegrinder/bot/dispatch/dispatch.py +85 -33
- telegrinder/bot/dispatch/handler/abc.py +5 -6
- telegrinder/bot/dispatch/handler/audio_reply.py +2 -2
- telegrinder/bot/dispatch/handler/base.py +3 -3
- telegrinder/bot/dispatch/handler/document_reply.py +2 -2
- telegrinder/bot/dispatch/handler/func.py +36 -42
- telegrinder/bot/dispatch/handler/media_group_reply.py +5 -4
- telegrinder/bot/dispatch/handler/message_reply.py +2 -2
- telegrinder/bot/dispatch/handler/photo_reply.py +2 -2
- telegrinder/bot/dispatch/handler/sticker_reply.py +2 -2
- telegrinder/bot/dispatch/handler/video_reply.py +2 -2
- telegrinder/bot/dispatch/middleware/abc.py +83 -8
- telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
- telegrinder/bot/dispatch/process.py +44 -50
- telegrinder/bot/dispatch/return_manager/__init__.py +2 -0
- telegrinder/bot/dispatch/return_manager/abc.py +6 -10
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
- telegrinder/bot/dispatch/view/__init__.py +2 -0
- telegrinder/bot/dispatch/view/abc.py +10 -6
- telegrinder/bot/dispatch/view/base.py +81 -50
- telegrinder/bot/dispatch/view/box.py +20 -9
- telegrinder/bot/dispatch/view/callback_query.py +3 -4
- telegrinder/bot/dispatch/view/chat_join_request.py +2 -7
- telegrinder/bot/dispatch/view/chat_member.py +3 -5
- telegrinder/bot/dispatch/view/inline_query.py +3 -4
- telegrinder/bot/dispatch/view/message.py +3 -4
- telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
- telegrinder/bot/dispatch/view/raw.py +42 -40
- telegrinder/bot/dispatch/waiter_machine/actions.py +5 -4
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +9 -7
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +3 -2
- telegrinder/bot/dispatch/waiter_machine/machine.py +113 -34
- telegrinder/bot/dispatch/waiter_machine/middleware.py +15 -10
- telegrinder/bot/dispatch/waiter_machine/short_state.py +7 -18
- telegrinder/bot/polling/polling.py +62 -54
- telegrinder/bot/rules/__init__.py +24 -1
- telegrinder/bot/rules/abc.py +17 -10
- telegrinder/bot/rules/callback_data.py +20 -61
- telegrinder/bot/rules/chat_join.py +6 -4
- telegrinder/bot/rules/command.py +4 -4
- telegrinder/bot/rules/enum_text.py +1 -4
- telegrinder/bot/rules/func.py +5 -3
- telegrinder/bot/rules/fuzzy.py +1 -1
- telegrinder/bot/rules/id.py +24 -0
- telegrinder/bot/rules/inline.py +6 -4
- telegrinder/bot/rules/integer.py +2 -1
- telegrinder/bot/rules/logic.py +18 -0
- telegrinder/bot/rules/markup.py +5 -6
- telegrinder/bot/rules/message.py +2 -4
- telegrinder/bot/rules/message_entities.py +1 -3
- telegrinder/bot/rules/node.py +15 -9
- telegrinder/bot/rules/payload.py +81 -0
- telegrinder/bot/rules/payment_invoice.py +29 -0
- telegrinder/bot/rules/regex.py +5 -6
- telegrinder/bot/rules/state.py +1 -3
- telegrinder/bot/rules/text.py +10 -5
- telegrinder/bot/rules/update.py +0 -0
- telegrinder/bot/scenario/abc.py +2 -4
- telegrinder/bot/scenario/checkbox.py +12 -14
- telegrinder/bot/scenario/choice.py +6 -9
- telegrinder/client/__init__.py +9 -1
- telegrinder/client/abc.py +35 -10
- telegrinder/client/aiohttp.py +28 -24
- telegrinder/client/form_data.py +31 -0
- telegrinder/client/sonic.py +212 -0
- telegrinder/model.py +38 -145
- telegrinder/modules.py +3 -1
- telegrinder/msgspec_utils.py +136 -68
- telegrinder/node/__init__.py +74 -13
- telegrinder/node/attachment.py +92 -16
- telegrinder/node/base.py +196 -68
- telegrinder/node/callback_query.py +17 -16
- telegrinder/node/command.py +3 -2
- telegrinder/node/composer.py +40 -75
- telegrinder/node/container.py +13 -7
- telegrinder/node/either.py +82 -0
- telegrinder/node/event.py +20 -31
- telegrinder/node/file.py +51 -0
- telegrinder/node/me.py +4 -5
- telegrinder/node/payload.py +78 -0
- telegrinder/node/polymorphic.py +27 -8
- telegrinder/node/rule.py +2 -6
- telegrinder/node/scope.py +4 -6
- telegrinder/node/source.py +37 -21
- telegrinder/node/text.py +20 -8
- telegrinder/node/tools/generator.py +7 -11
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +0 -61
- telegrinder/tools/__init__.py +97 -38
- telegrinder/tools/adapter/__init__.py +19 -0
- telegrinder/tools/adapter/abc.py +49 -0
- telegrinder/tools/adapter/dataclass.py +56 -0
- telegrinder/{bot/rules → tools}/adapter/event.py +8 -10
- telegrinder/{bot/rules → tools}/adapter/node.py +8 -10
- telegrinder/{bot/rules → tools}/adapter/raw_event.py +2 -2
- telegrinder/{bot/rules → tools}/adapter/raw_update.py +2 -2
- telegrinder/tools/buttons.py +52 -26
- telegrinder/tools/callback_data_serilization/__init__.py +5 -0
- telegrinder/tools/callback_data_serilization/abc.py +51 -0
- telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
- telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
- telegrinder/tools/error_handler/abc.py +4 -7
- telegrinder/tools/error_handler/error.py +0 -0
- telegrinder/tools/error_handler/error_handler.py +34 -48
- telegrinder/tools/formatting/__init__.py +57 -37
- telegrinder/tools/formatting/deep_links.py +541 -0
- telegrinder/tools/formatting/{html.py → html_formatter.py} +51 -79
- telegrinder/tools/formatting/spec_html_formats.py +14 -60
- telegrinder/tools/functional.py +1 -5
- telegrinder/tools/global_context/global_context.py +26 -51
- telegrinder/tools/global_context/telegrinder_ctx.py +3 -3
- telegrinder/tools/i18n/abc.py +0 -0
- telegrinder/tools/i18n/middleware/abc.py +3 -6
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard.py +9 -9
- telegrinder/tools/lifespan.py +105 -0
- telegrinder/tools/limited_dict.py +5 -10
- telegrinder/tools/loop_wrapper/abc.py +7 -2
- telegrinder/tools/loop_wrapper/loop_wrapper.py +40 -95
- telegrinder/tools/magic.py +184 -34
- telegrinder/tools/state_storage/__init__.py +0 -0
- telegrinder/tools/state_storage/abc.py +5 -9
- telegrinder/tools/state_storage/memory.py +1 -1
- telegrinder/tools/strings.py +13 -0
- telegrinder/types/__init__.py +8 -0
- telegrinder/types/enums.py +31 -21
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +531 -109
- telegrinder/types/objects.py +934 -826
- telegrinder/verification_utils.py +0 -2
- {telegrinder-0.3.4.post1.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +2 -2
- telegrinder-0.4.0.dist-info/METADATA +144 -0
- telegrinder-0.4.0.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.post1.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
- telegrinder/bot/rules/adapter/__init__.py +0 -17
- telegrinder/bot/rules/adapter/abc.py +0 -31
- telegrinder/node/message.py +0 -14
- telegrinder/node/update.py +0 -15
- telegrinder/tools/formatting/links.py +0 -38
- telegrinder/tools/kb_set/__init__.py +0 -4
- telegrinder/tools/kb_set/base.py +0 -15
- telegrinder/tools/kb_set/yaml.py +0 -63
- telegrinder-0.3.4.post1.dist-info/METADATA +0 -110
- telegrinder-0.3.4.post1.dist-info/RECORD +0 -165
- /telegrinder/{bot/rules → tools}/adapter/errors.py +0 -0
|
@@ -1,46 +1,54 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import inspect
|
|
3
1
|
import typing
|
|
4
|
-
from functools import wraps
|
|
5
2
|
|
|
6
3
|
import msgspec
|
|
7
|
-
import typing_extensions
|
|
8
|
-
from fntypes.result import Result
|
|
9
4
|
|
|
10
5
|
from telegrinder.api.api import API
|
|
11
|
-
from telegrinder.model import Model,
|
|
6
|
+
from telegrinder.model import Model, is_none
|
|
12
7
|
|
|
13
|
-
F = typing.TypeVar("F", bound=typing.Callable[..., typing.Any])
|
|
14
|
-
Cute = typing.TypeVar("Cute", bound="BaseCute")
|
|
15
|
-
Update = typing_extensions.TypeVar("Update", bound=Model)
|
|
16
|
-
CtxAPI = typing_extensions.TypeVar("CtxAPI", bound=API, default=API)
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
def compose_method_params[Cute: BaseCute](
|
|
10
|
+
params: dict[str, typing.Any],
|
|
11
|
+
update: Cute,
|
|
12
|
+
*,
|
|
13
|
+
default_params: set[str | tuple[str, str]] | None = None,
|
|
14
|
+
validators: dict[str, typing.Callable[[Cute], bool]] | None = None,
|
|
15
|
+
) -> dict[str, typing.Any]:
|
|
16
|
+
default_params = default_params or set()
|
|
17
|
+
validators = validators or {}
|
|
18
|
+
|
|
19
|
+
for param in default_params:
|
|
20
|
+
param_name = param if isinstance(param, str) else param[0]
|
|
21
|
+
if param_name not in params:
|
|
22
|
+
if param_name in validators and not validators[param_name](update):
|
|
23
|
+
continue
|
|
24
|
+
params[param_name] = getattr(update, param if isinstance(param, str) else param[1])
|
|
25
|
+
|
|
26
|
+
return params
|
|
27
|
+
|
|
22
28
|
|
|
23
29
|
if typing.TYPE_CHECKING:
|
|
30
|
+
from telegrinder.node.base import Node
|
|
24
31
|
|
|
25
|
-
class BaseCute
|
|
32
|
+
class BaseCute[Update: Model](Model):
|
|
26
33
|
api: API
|
|
27
34
|
|
|
35
|
+
@classmethod
|
|
36
|
+
def as_node(cls) -> type[Node]: ...
|
|
37
|
+
|
|
28
38
|
@classmethod
|
|
29
39
|
def from_update(cls, update: Update, bound_api: API) -> typing.Self: ...
|
|
30
40
|
|
|
31
41
|
@property
|
|
32
|
-
def ctx_api(self) ->
|
|
42
|
+
def ctx_api(self) -> API: ...
|
|
33
43
|
|
|
34
44
|
def to_dict(
|
|
35
45
|
self,
|
|
36
46
|
*,
|
|
37
47
|
exclude_fields: set[str] | None = None,
|
|
38
48
|
) -> dict[str, typing.Any]:
|
|
39
|
-
"""
|
|
40
|
-
:param exclude_fields: Cute model field names to exclude from the dictionary representation of this cute model.
|
|
49
|
+
""":param exclude_fields: Cute model field names to exclude from the dictionary representation of this cute model.
|
|
41
50
|
:return: A dictionary representation of this cute model.
|
|
42
51
|
"""
|
|
43
|
-
|
|
44
52
|
...
|
|
45
53
|
|
|
46
54
|
def to_full_dict(
|
|
@@ -48,21 +56,19 @@ if typing.TYPE_CHECKING:
|
|
|
48
56
|
*,
|
|
49
57
|
exclude_fields: set[str] | None = None,
|
|
50
58
|
) -> dict[str, typing.Any]:
|
|
51
|
-
"""
|
|
52
|
-
:param exclude_fields: Cute model field names to exclude from the dictionary representation of this cute model.
|
|
59
|
+
""":param exclude_fields: Cute model field names to exclude from the dictionary representation of this cute model.
|
|
53
60
|
:return: A dictionary representation of this model including all models, structs, custom types.
|
|
54
61
|
"""
|
|
55
|
-
|
|
56
62
|
...
|
|
57
63
|
|
|
58
64
|
else:
|
|
59
65
|
import msgspec
|
|
60
66
|
from fntypes.co import Nothing, Some, Variative
|
|
61
67
|
|
|
62
|
-
from telegrinder.msgspec_utils import Option, decoder, encoder
|
|
68
|
+
from telegrinder.msgspec_utils import Option, decoder, encoder, struct_as_dict
|
|
63
69
|
from telegrinder.msgspec_utils import get_class_annotations as _get_class_annotations
|
|
64
70
|
|
|
65
|
-
def _get_cute_from_generic(generic_args):
|
|
71
|
+
def _get_cute_from_generic(generic_args, /):
|
|
66
72
|
for arg in generic_args:
|
|
67
73
|
orig_arg = typing.get_origin(arg) or arg
|
|
68
74
|
|
|
@@ -75,7 +81,7 @@ else:
|
|
|
75
81
|
|
|
76
82
|
return None
|
|
77
83
|
|
|
78
|
-
def _get_cute_annotations(annotations):
|
|
84
|
+
def _get_cute_annotations(annotations, /):
|
|
79
85
|
cute_annotations = {}
|
|
80
86
|
|
|
81
87
|
for key, hint in annotations.items():
|
|
@@ -88,7 +94,7 @@ else:
|
|
|
88
94
|
|
|
89
95
|
return cute_annotations
|
|
90
96
|
|
|
91
|
-
def
|
|
97
|
+
def _validate_value(value, /):
|
|
92
98
|
while isinstance(value, Variative | Some):
|
|
93
99
|
if isinstance(value, Variative):
|
|
94
100
|
value = value.v
|
|
@@ -96,7 +102,9 @@ else:
|
|
|
96
102
|
value = value.value
|
|
97
103
|
return value
|
|
98
104
|
|
|
99
|
-
class BaseCute
|
|
105
|
+
class BaseCute[Update]:
|
|
106
|
+
api: API
|
|
107
|
+
|
|
100
108
|
def __init_subclass__(cls, *args, **kwargs):
|
|
101
109
|
super().__init_subclass__(*args, **kwargs)
|
|
102
110
|
|
|
@@ -105,6 +113,15 @@ else:
|
|
|
105
113
|
|
|
106
114
|
cls.__is_solved_annotations__ = False
|
|
107
115
|
cls.__cute_annotations__ = None
|
|
116
|
+
cls.__event_node__ = None
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def as_node(cls):
|
|
120
|
+
if cls.__event_node__ is None:
|
|
121
|
+
from telegrinder.node.event import _EventNode
|
|
122
|
+
|
|
123
|
+
cls.__event_node__ = _EventNode[cls]
|
|
124
|
+
return cls.__event_node__
|
|
108
125
|
|
|
109
126
|
@classmethod
|
|
110
127
|
def from_update(cls, update, bound_api):
|
|
@@ -118,10 +135,10 @@ else:
|
|
|
118
135
|
return cls(
|
|
119
136
|
**{
|
|
120
137
|
field: decoder.convert(
|
|
121
|
-
cls.__cute_annotations__[field].from_update(
|
|
138
|
+
cls.__cute_annotations__[field].from_update(_validate_value(value), bound_api=bound_api),
|
|
122
139
|
type=cls.__annotations__[field],
|
|
123
140
|
)
|
|
124
|
-
if field in cls.__cute_annotations__ and not isinstance(value, Nothing)
|
|
141
|
+
if field in cls.__cute_annotations__ and not isinstance(value, Nothing | msgspec.UnsetType)
|
|
125
142
|
else value
|
|
126
143
|
for field, value in update.to_dict().items()
|
|
127
144
|
},
|
|
@@ -135,15 +152,17 @@ else:
|
|
|
135
152
|
def _to_dict(self, dct_name, exclude_fields, full):
|
|
136
153
|
if dct_name not in self.__dict__:
|
|
137
154
|
self.__dict__[dct_name] = (
|
|
138
|
-
|
|
155
|
+
struct_as_dict(self)
|
|
139
156
|
if not full
|
|
140
157
|
else encoder.to_builtins(
|
|
141
158
|
{
|
|
142
159
|
k: field.to_dict(exclude_fields=exclude_fields)
|
|
143
|
-
if isinstance(field
|
|
160
|
+
if isinstance(field, BaseCute)
|
|
144
161
|
else field
|
|
145
162
|
for k in self.__struct_fields__
|
|
146
163
|
if k not in exclude_fields
|
|
164
|
+
and not isinstance(field := _validate_value(getattr(self, k)), msgspec.UnsetType)
|
|
165
|
+
and not is_none(field)
|
|
147
166
|
},
|
|
148
167
|
order="deterministic",
|
|
149
168
|
)
|
|
@@ -154,105 +173,12 @@ else:
|
|
|
154
173
|
|
|
155
174
|
return {key: value for key, value in self.__dict__[dct_name].items() if key not in exclude_fields}
|
|
156
175
|
|
|
157
|
-
def to_dict(self, *, exclude_fields=None):
|
|
176
|
+
def to_dict(self, *, exclude_fields=None, full=False):
|
|
158
177
|
exclude_fields = exclude_fields or set()
|
|
159
|
-
return self._to_dict("model_as_dict", exclude_fields={"api"} | exclude_fields, full=
|
|
178
|
+
return self._to_dict("model_as_dict", exclude_fields={"api"} | exclude_fields, full=full)
|
|
160
179
|
|
|
161
180
|
def to_full_dict(self, *, exclude_fields=None):
|
|
162
|
-
exclude_fields
|
|
163
|
-
return self._to_dict("model_as_full_dict", exclude_fields={"api"} | exclude_fields, full=True)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def compose_method_params(
|
|
167
|
-
params: dict[str, typing.Any],
|
|
168
|
-
update: Cute,
|
|
169
|
-
*,
|
|
170
|
-
default_params: set[str | tuple[str, str]] | None = None,
|
|
171
|
-
validators: dict[str, typing.Callable[[Cute], bool]] | None = None,
|
|
172
|
-
) -> dict[str, typing.Any]:
|
|
173
|
-
"""Compose method `params` from `update` by `default_params` and `validators`.
|
|
174
|
-
|
|
175
|
-
:param params: Method params.
|
|
176
|
-
:param update: Update object.
|
|
177
|
-
:param default_params: Default params. \
|
|
178
|
-
(`str`) - Attribute name to be get from `update` if param is undefined. \
|
|
179
|
-
(`tuple[str, str]`): tuple[0] - Parameter name to be set in `params`, \
|
|
180
|
-
tuple[1] - attribute name to be get from `update`.
|
|
181
|
-
:param validators: Validators mapping (`str, Callable`), key - `Parameter name` \
|
|
182
|
-
for which the validator will be applied, value - `Validator`, if returned `True` \
|
|
183
|
-
parameter will be set, otherwise will not.
|
|
184
|
-
:return: Composed params.
|
|
185
|
-
"""
|
|
186
|
-
|
|
187
|
-
default_params = default_params or set()
|
|
188
|
-
validators = validators or {}
|
|
189
|
-
|
|
190
|
-
for param in default_params:
|
|
191
|
-
param_name = param if isinstance(param, str) else param[0]
|
|
192
|
-
if param_name not in params:
|
|
193
|
-
if param_name in validators and not validators[param_name](update):
|
|
194
|
-
continue
|
|
195
|
-
params[param_name] = getattr(update, param if isinstance(param, str) else param[1])
|
|
196
|
-
|
|
197
|
-
return params
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def shortcut(
|
|
201
|
-
method_name: str,
|
|
202
|
-
*,
|
|
203
|
-
executor: Executor[Cute] | None = None,
|
|
204
|
-
custom_params: set[str] | None = None,
|
|
205
|
-
):
|
|
206
|
-
def wrapper(func: F) -> F:
|
|
207
|
-
@wraps(func)
|
|
208
|
-
async def inner(
|
|
209
|
-
self: Cute,
|
|
210
|
-
*args: typing.Any,
|
|
211
|
-
**kwargs: typing.Any,
|
|
212
|
-
) -> typing.Any:
|
|
213
|
-
if executor is None:
|
|
214
|
-
return await func(self, *args, **kwargs)
|
|
215
|
-
|
|
216
|
-
if not hasattr(func, "_signature_params"):
|
|
217
|
-
setattr(
|
|
218
|
-
func,
|
|
219
|
-
"_signature_params",
|
|
220
|
-
{k: p for k, p in inspect.signature(func).parameters.items() if k != "self"},
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
signature_params: dict[str, inspect.Parameter] = getattr(func, "_signature_params")
|
|
224
|
-
params: dict[str, typing.Any] = {}
|
|
225
|
-
index = 0
|
|
226
|
-
|
|
227
|
-
for k, p in signature_params.items():
|
|
228
|
-
if p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY) and len(args) > index:
|
|
229
|
-
params[k] = args[index]
|
|
230
|
-
index += 1
|
|
231
|
-
continue
|
|
232
|
-
|
|
233
|
-
if p.kind in (p.VAR_KEYWORD, p.VAR_POSITIONAL):
|
|
234
|
-
params[k] = kwargs.copy() if p.kind is p.VAR_KEYWORD else args[index:]
|
|
235
|
-
continue
|
|
236
|
-
|
|
237
|
-
params[k] = kwargs.pop(k, p.default) if p.default is not p.empty else kwargs.pop(k)
|
|
238
|
-
|
|
239
|
-
return await executor(self, method_name, get_params(params))
|
|
240
|
-
|
|
241
|
-
inner.__shortcut__ = Shortcut( # type: ignore
|
|
242
|
-
method_name=method_name,
|
|
243
|
-
executor=executor,
|
|
244
|
-
custom_params=custom_params or set(),
|
|
245
|
-
)
|
|
246
|
-
return inner # type: ignore
|
|
247
|
-
|
|
248
|
-
return wrapper
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
@dataclasses.dataclass(slots=True, frozen=True)
|
|
252
|
-
class Shortcut:
|
|
253
|
-
method_name: str
|
|
254
|
-
executor: Executor | None = dataclasses.field(default=None, kw_only=True)
|
|
255
|
-
custom_params: set[str] = dataclasses.field(default_factory=lambda: set(), kw_only=True)
|
|
181
|
+
return self.to_dict(exclude_fields=exclude_fields, full=True)
|
|
256
182
|
|
|
257
183
|
|
|
258
|
-
__all__ = ("BaseCute", "
|
|
184
|
+
__all__ = ("BaseCute", "compose_method_params")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
import typing
|
|
2
3
|
from contextlib import suppress
|
|
3
4
|
|
|
@@ -5,10 +6,11 @@ import msgspec
|
|
|
5
6
|
from fntypes.co import Nothing, Result, Some, Variative, unwrapping
|
|
6
7
|
|
|
7
8
|
from telegrinder.api import API, APIError
|
|
8
|
-
from telegrinder.bot.cute_types.base import BaseCute, compose_method_params
|
|
9
|
+
from telegrinder.bot.cute_types.base import BaseCute, compose_method_params
|
|
9
10
|
from telegrinder.bot.cute_types.message import MediaType, MessageCute, ReplyMarkup, execute_method_edit
|
|
10
|
-
from telegrinder.model import From, field, get_params
|
|
11
|
+
from telegrinder.model import UNSET, From, field, get_params
|
|
11
12
|
from telegrinder.msgspec_utils import Option, decoder
|
|
13
|
+
from telegrinder.tools.magic import shortcut
|
|
12
14
|
from telegrinder.types.objects import *
|
|
13
15
|
|
|
14
16
|
|
|
@@ -16,7 +18,7 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
16
18
|
api: API
|
|
17
19
|
|
|
18
20
|
message: Option[Variative[MessageCute, InaccessibleMessage]] = field(
|
|
19
|
-
default=
|
|
21
|
+
default=UNSET,
|
|
20
22
|
converter=From[MessageCute | InaccessibleMessage | None],
|
|
21
23
|
)
|
|
22
24
|
"""Optional. Message sent by the bot with the callback button that originated
|
|
@@ -29,16 +31,16 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
29
31
|
@property
|
|
30
32
|
def chat_id(self) -> Option[int]:
|
|
31
33
|
"""Optional. Message from chat ID. This will be present if the message is sent
|
|
32
|
-
by the bot with the callback button that originated the query.
|
|
33
|
-
|
|
34
|
+
by the bot with the callback button that originated the query.
|
|
35
|
+
"""
|
|
34
36
|
return self.message.map(lambda m: m.v.chat.id)
|
|
35
37
|
|
|
36
38
|
@property
|
|
37
39
|
def is_topic_message(self) -> Option[bool]:
|
|
38
40
|
"""Optional. True, if the message is a topic message with a name,
|
|
39
41
|
color and icon. This will be present if the message is sent
|
|
40
|
-
by the bot with the callback button that originated the query.
|
|
41
|
-
|
|
42
|
+
by the bot with the callback button that originated the query.
|
|
43
|
+
"""
|
|
42
44
|
return self.message.map(
|
|
43
45
|
lambda m: m.only().map(lambda m: m.is_topic_message.unwrap_or(False)).unwrap_or(False),
|
|
44
46
|
)
|
|
@@ -48,8 +50,8 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
48
50
|
def message_thread_id(self) -> Option[int]:
|
|
49
51
|
"""Optional. Unique identifier of the target message thread (for forum supergroups only).
|
|
50
52
|
This will be present if the message is sent
|
|
51
|
-
by the bot with the callback button that originated the query.
|
|
52
|
-
|
|
53
|
+
by the bot with the callback button that originated the query.
|
|
54
|
+
"""
|
|
53
55
|
return self.message.unwrap().only().map(lambda m: m.message_thread_id.unwrap()).cast(Some, Nothing)
|
|
54
56
|
|
|
55
57
|
@property
|
|
@@ -57,7 +59,6 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
57
59
|
"""Optional. Unique message identifier inside this chat. This will be present
|
|
58
60
|
if the message is sent by the bot with the callback button that originated the query.
|
|
59
61
|
"""
|
|
60
|
-
|
|
61
62
|
return self.message.map(lambda m: m.v.message_id)
|
|
62
63
|
|
|
63
64
|
@property
|
|
@@ -65,26 +66,43 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
65
66
|
"""Optional. Chat the callback query originated from. This will be present
|
|
66
67
|
if the message is sent by the bot with the callback button that originated the query.
|
|
67
68
|
"""
|
|
68
|
-
|
|
69
69
|
return self.message.map(lambda m: m.v.chat)
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
@typing.overload
|
|
72
|
+
def decode_data(self) -> Option[dict[str, typing.Any]]: ...
|
|
73
|
+
|
|
74
|
+
@typing.overload
|
|
75
|
+
def decode_data[T](self, *, to: type[T]) -> Option[T]: ...
|
|
76
|
+
|
|
77
|
+
def decode_data[T](self, *, to: type[T] = dict[str, typing.Any]) -> Option[T]:
|
|
78
|
+
if not self.data:
|
|
79
|
+
return Nothing()
|
|
80
|
+
|
|
72
81
|
if "cached_callback_data" in self.__dict__:
|
|
73
82
|
return self.__dict__["cached_callback_data"]
|
|
83
|
+
|
|
74
84
|
data = Nothing()
|
|
75
85
|
with suppress(msgspec.ValidationError, msgspec.DecodeError):
|
|
76
|
-
data =
|
|
86
|
+
data = (
|
|
87
|
+
Some(decoder.decode(self.data.unwrap(), type=to))
|
|
88
|
+
if not issubclass(to, str | bytes)
|
|
89
|
+
else self.data
|
|
90
|
+
if issubclass(to, str)
|
|
91
|
+
else Some(base64.urlsafe_b64decode(self.data.unwrap()))
|
|
92
|
+
)
|
|
93
|
+
|
|
77
94
|
self.__dict__["cached_callback_data"] = data
|
|
78
|
-
return data
|
|
95
|
+
return data # type: ignore
|
|
79
96
|
|
|
80
97
|
@shortcut("answer_callback_query", custom_params={"callback_query_id"})
|
|
81
98
|
async def answer(
|
|
82
99
|
self,
|
|
83
100
|
text: str | None = None,
|
|
101
|
+
*,
|
|
102
|
+
cache_time: int | None = None,
|
|
84
103
|
callback_query_id: str | None = None,
|
|
85
104
|
show_alert: bool | None = None,
|
|
86
105
|
url: str | None = None,
|
|
87
|
-
cache_time: int | None = None,
|
|
88
106
|
**other: typing.Any,
|
|
89
107
|
) -> Result[bool, APIError]:
|
|
90
108
|
"""Shortcut `API.answer_callback_query()`, see the [documentation](https://core.telegram.org/bots/api#answercallbackquery)
|
|
@@ -92,16 +110,12 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
92
110
|
Use this method to send answers to callback queries sent from inline keyboards.
|
|
93
111
|
The answer will be displayed to the user as a notification at the top of the
|
|
94
112
|
chat screen or as an alert. On success, True is returned."""
|
|
95
|
-
|
|
96
|
-
params = compose_method_params(
|
|
97
|
-
get_params(locals()), self, default_params={("callback_query_id", "id")}
|
|
98
|
-
)
|
|
113
|
+
params = compose_method_params(get_params(locals()), self, default_params={("callback_query_id", "id")})
|
|
99
114
|
return await self.ctx_api.answer_callback_query(**params)
|
|
100
115
|
|
|
101
116
|
@shortcut(
|
|
102
117
|
"copy_message",
|
|
103
118
|
custom_params={
|
|
104
|
-
"reply_parameters",
|
|
105
119
|
"message_thread_id",
|
|
106
120
|
"chat_id",
|
|
107
121
|
"message_id",
|
|
@@ -112,17 +126,20 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
112
126
|
async def copy(
|
|
113
127
|
self,
|
|
114
128
|
chat_id: int | str | None = None,
|
|
129
|
+
*,
|
|
130
|
+
allow_paid_broadcast: bool | None = None,
|
|
131
|
+
caption: str | None = None,
|
|
132
|
+
caption_entities: list[MessageEntity] | None = None,
|
|
133
|
+
disable_notification: bool | None = None,
|
|
115
134
|
from_chat_id: int | str | None = None,
|
|
116
135
|
message_id: int | None = None,
|
|
117
136
|
message_thread_id: int | None = None,
|
|
118
|
-
caption: str | None = None,
|
|
119
137
|
parse_mode: str | None = None,
|
|
120
|
-
caption_entities: list[MessageEntity] | None = None,
|
|
121
|
-
disable_notification: bool | None = None,
|
|
122
138
|
protect_content: bool | None = None,
|
|
123
|
-
reply_parameters: ReplyParameters | dict[str, typing.Any] | None = None,
|
|
124
139
|
reply_markup: ReplyMarkup | None = None,
|
|
140
|
+
reply_parameters: ReplyParameters | None = None,
|
|
125
141
|
show_caption_above_media: bool | None = None,
|
|
142
|
+
video_start_timestamp: int | None = None,
|
|
126
143
|
**other: typing.Any,
|
|
127
144
|
) -> Result[MessageId, APIError]:
|
|
128
145
|
"""Shortcut `API.copy_message()`, see the [documentation](https://core.telegram.org/bots/api#copymessage)
|
|
@@ -133,12 +150,12 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
133
150
|
field correct_option_id is known to the bot. The method is analogous to
|
|
134
151
|
the method forwardMessage, but the copied message doesn't have a link to
|
|
135
152
|
the original message. Returns the MessageId of the sent message on success."""
|
|
136
|
-
|
|
137
153
|
return await MessageCute.copy(self, **get_params(locals())) # type: ignore
|
|
138
154
|
|
|
139
155
|
@shortcut("delete_message", custom_params={"message_thread_id", "chat_id", "message_id"})
|
|
140
156
|
async def delete(
|
|
141
157
|
self,
|
|
158
|
+
*,
|
|
142
159
|
chat_id: int | None = None,
|
|
143
160
|
message_id: int | None = None,
|
|
144
161
|
message_thread_id: int | None = None,
|
|
@@ -157,26 +174,26 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
157
174
|
of a group, it can delete any message there. - If the bot has can_delete_messages
|
|
158
175
|
permission in a supergroup or a channel, it can delete any message there.
|
|
159
176
|
Returns True on success."""
|
|
160
|
-
|
|
161
177
|
return await MessageCute.delete(self, **get_params(locals())) # type: ignore
|
|
162
178
|
|
|
163
179
|
@shortcut(
|
|
164
180
|
"edit_message_text",
|
|
165
181
|
executor=execute_method_edit,
|
|
166
|
-
custom_params={"message_thread_id"
|
|
182
|
+
custom_params={"message_thread_id"},
|
|
167
183
|
)
|
|
168
184
|
async def edit_text(
|
|
169
185
|
self,
|
|
170
186
|
text: str,
|
|
171
|
-
|
|
187
|
+
*,
|
|
188
|
+
business_connection_id: str | None = None,
|
|
172
189
|
chat_id: int | str | None = None,
|
|
190
|
+
entities: list[MessageEntity] | None = None,
|
|
191
|
+
inline_message_id: str | None = None,
|
|
192
|
+
link_preview_options: LinkPreviewOptions | None = None,
|
|
173
193
|
message_id: int | None = None,
|
|
174
194
|
message_thread_id: int | None = None,
|
|
175
195
|
parse_mode: str | None = None,
|
|
176
|
-
entities: list[MessageEntity] | None = None,
|
|
177
|
-
link_preview_options: LinkPreviewOptions | dict[str, typing.Any] | None = None,
|
|
178
196
|
reply_markup: InlineKeyboardMarkup | None = None,
|
|
179
|
-
business_connection_id: str | None = None,
|
|
180
197
|
**other: typing.Any,
|
|
181
198
|
) -> Result[Variative[MessageCute, bool], APIError]:
|
|
182
199
|
"""Shortcut `API.edit_message_text()`, see the [documentation](https://core.telegram.org/bots/api#editmessagetext)
|
|
@@ -202,7 +219,6 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
202
219
|
:param link_preview_options: Link preview generation options for the message.
|
|
203
220
|
|
|
204
221
|
:param reply_markup: A JSON-serialized object for an inline keyboard."""
|
|
205
|
-
|
|
206
222
|
...
|
|
207
223
|
|
|
208
224
|
@shortcut(
|
|
@@ -212,18 +228,18 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
212
228
|
)
|
|
213
229
|
async def edit_live_location(
|
|
214
230
|
self,
|
|
215
|
-
|
|
231
|
+
*,
|
|
216
232
|
longitude: float,
|
|
217
|
-
|
|
218
|
-
message_thread_id: int | None = None,
|
|
233
|
+
business_connection_id: str | None = None,
|
|
219
234
|
chat_id: int | str | None = None,
|
|
220
|
-
message_id: int | None = None,
|
|
221
|
-
horizontal_accuracy: float | None = None,
|
|
222
235
|
heading: int | None = None,
|
|
236
|
+
horizontal_accuracy: float | None = None,
|
|
237
|
+
inline_message_id: str | None = None,
|
|
238
|
+
live_period: int | None = None,
|
|
239
|
+
message_id: int | None = None,
|
|
240
|
+
message_thread_id: int | None = None,
|
|
223
241
|
proximity_alert_radius: int | None = None,
|
|
224
242
|
reply_markup: InlineKeyboardMarkup | None = None,
|
|
225
|
-
business_connection_id: str | None = None,
|
|
226
|
-
live_period: int | None = None,
|
|
227
243
|
**other: typing.Any,
|
|
228
244
|
) -> Result[Variative[MessageCute, bool], APIError]:
|
|
229
245
|
"""Shortcut `API.edit_message_live_location()`, see the [documentation](https://core.telegram.org/bots/api#editmessagelivelocation)
|
|
@@ -251,7 +267,6 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
251
267
|
:param proximity_alert_radius: The maximum distance for proximity alerts about approaching another chatmember, in meters. Must be between 1 and 100000 if specified.
|
|
252
268
|
|
|
253
269
|
:param reply_markup: A JSON-serialized object for a new inline keyboard."""
|
|
254
|
-
|
|
255
270
|
...
|
|
256
271
|
|
|
257
272
|
@shortcut(
|
|
@@ -262,14 +277,15 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
262
277
|
async def edit_caption(
|
|
263
278
|
self,
|
|
264
279
|
caption: str | None = None,
|
|
280
|
+
*,
|
|
281
|
+
business_connection_id: str | None = None,
|
|
282
|
+
caption_entities: list[MessageEntity] | None = None,
|
|
265
283
|
chat_id: int | str | None = None,
|
|
284
|
+
inline_message_id: str | None = None,
|
|
266
285
|
message_id: int | None = None,
|
|
267
286
|
message_thread_id: int | None = None,
|
|
268
|
-
inline_message_id: str | None = None,
|
|
269
287
|
parse_mode: str | None = None,
|
|
270
|
-
caption_entities: list[MessageEntity] | None = None,
|
|
271
288
|
reply_markup: InlineKeyboardMarkup | None = None,
|
|
272
|
-
business_connection_id: str | None = None,
|
|
273
289
|
show_caption_above_media: bool | None = None,
|
|
274
290
|
**other: typing.Any,
|
|
275
291
|
) -> Result[Variative[MessageCute, bool], APIError]:
|
|
@@ -295,7 +311,6 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
295
311
|
:param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supportedonly for animation, photo and video messages.
|
|
296
312
|
|
|
297
313
|
:param reply_markup: A JSON-serialized object for an inline keyboard."""
|
|
298
|
-
|
|
299
314
|
...
|
|
300
315
|
|
|
301
316
|
@shortcut(
|
|
@@ -312,29 +327,30 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
312
327
|
async def edit_media(
|
|
313
328
|
self,
|
|
314
329
|
media: str | InputFile | InputMedia,
|
|
315
|
-
|
|
330
|
+
*,
|
|
331
|
+
business_connection_id: str | None = None,
|
|
316
332
|
caption: str | None = None,
|
|
317
|
-
parse_mode: str | None = None,
|
|
318
333
|
caption_entities: list[MessageEntity] | None = None,
|
|
319
|
-
inline_message_id: str | None = None,
|
|
320
334
|
chat_id: int | str | None = None,
|
|
335
|
+
inline_message_id: str | None = None,
|
|
321
336
|
message_id: int | None = None,
|
|
322
337
|
message_thread_id: int | None = None,
|
|
338
|
+
parse_mode: str | None = None,
|
|
323
339
|
reply_markup: InlineKeyboardMarkup | None = None,
|
|
324
|
-
|
|
340
|
+
type: MediaType | None = None,
|
|
325
341
|
**other: typing.Any,
|
|
326
342
|
) -> Result[Variative[MessageCute, bool], APIError]:
|
|
327
343
|
"""Shortcut `API.edit_message_media()`, see the [documentation](https://core.telegram.org/bots/api#editmessagemedia)
|
|
328
344
|
|
|
329
|
-
Use this method to edit animation, audio, document, photo, or video messages
|
|
330
|
-
If a message is part of a message album, then
|
|
331
|
-
|
|
332
|
-
a video otherwise. When an inline message is edited,
|
|
333
|
-
use a previously uploaded file via its file_id
|
|
334
|
-
if the edited message is not an inline message,
|
|
335
|
-
otherwise True is returned. Note that business
|
|
336
|
-
by the bot and do not contain an inline keyboard
|
|
337
|
-
48 hours from the time they were sent.
|
|
345
|
+
Use this method to edit animation, audio, document, photo, or video messages,
|
|
346
|
+
or to add media to text messages. If a message is part of a message album, then
|
|
347
|
+
it can be edited only to an audio for audio albums, only to a document for document
|
|
348
|
+
albums and to a photo or a video otherwise. When an inline message is edited,
|
|
349
|
+
a new file can't be uploaded; use a previously uploaded file via its file_id
|
|
350
|
+
or specify a URL. On success, if the edited message is not an inline message,
|
|
351
|
+
the edited Message is returned, otherwise True is returned. Note that business
|
|
352
|
+
messages that were not sent by the bot and do not contain an inline keyboard
|
|
353
|
+
can only be edited within 48 hours from the time they were sent.
|
|
338
354
|
:param business_connection_id: Unique identifier of the business connection on behalf of which the messageto be edited was sent.
|
|
339
355
|
|
|
340
356
|
:param chat_id: Required if inline_message_id is not specified. Unique identifier forthe target chat or username of the target channel (in the format @channelusername).
|
|
@@ -345,7 +361,6 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
345
361
|
:param media: A JSON-serialized object for a new media content of the message.
|
|
346
362
|
|
|
347
363
|
:param reply_markup: A JSON-serialized object for a new inline keyboard."""
|
|
348
|
-
|
|
349
364
|
return await MessageCute.edit_media(self, **get_params(locals())) # type: ignore
|
|
350
365
|
|
|
351
366
|
@shortcut(
|
|
@@ -355,12 +370,13 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
355
370
|
)
|
|
356
371
|
async def edit_reply_markup(
|
|
357
372
|
self,
|
|
373
|
+
*,
|
|
374
|
+
business_connection_id: str | None = None,
|
|
375
|
+
chat_id: int | str | None = None,
|
|
358
376
|
inline_message_id: str | None = None,
|
|
359
377
|
message_id: int | None = None,
|
|
360
378
|
message_thread_id: int | None = None,
|
|
361
|
-
chat_id: int | str | None = None,
|
|
362
379
|
reply_markup: InlineKeyboardMarkup | None = None,
|
|
363
|
-
business_connection_id: str | None = None,
|
|
364
380
|
**other: typing.Any,
|
|
365
381
|
) -> Result[Variative[MessageCute, bool], APIError]:
|
|
366
382
|
"""Shortcut `API.edit_message_reply_markup()`, see the [documentation](https://core.telegram.org/bots/api#editmessagereplymarkup)
|
|
@@ -378,7 +394,6 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True):
|
|
|
378
394
|
:param inline_message_id: Required if chat_id and message_id are not specified. Identifier of theinline message.
|
|
379
395
|
|
|
380
396
|
:param reply_markup: A JSON-serialized object for an inline keyboard."""
|
|
381
|
-
|
|
382
397
|
...
|
|
383
398
|
|
|
384
399
|
|
|
@@ -3,8 +3,9 @@ import typing
|
|
|
3
3
|
from fntypes.result import Result
|
|
4
4
|
|
|
5
5
|
from telegrinder.api.api import API, APIError
|
|
6
|
-
from telegrinder.bot.cute_types.base import BaseCute
|
|
6
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
7
7
|
from telegrinder.bot.cute_types.chat_member_updated import ChatMemberShortcuts, chat_member_interaction
|
|
8
|
+
from telegrinder.tools.magic import shortcut
|
|
8
9
|
from telegrinder.types.objects import *
|
|
9
10
|
|
|
10
11
|
|
|
@@ -26,6 +27,7 @@ class ChatJoinRequestCute(BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMember
|
|
|
26
27
|
)
|
|
27
28
|
async def approve(
|
|
28
29
|
self,
|
|
30
|
+
*,
|
|
29
31
|
chat_id: int | str | None = None,
|
|
30
32
|
user_id: int | None = None,
|
|
31
33
|
**other: typing.Any,
|
|
@@ -35,7 +37,6 @@ class ChatJoinRequestCute(BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMember
|
|
|
35
37
|
Use this method to approve a chat join request. The bot must be an administrator
|
|
36
38
|
in the chat for this to work and must have the can_invite_users administrator
|
|
37
39
|
right. Returns True on success."""
|
|
38
|
-
|
|
39
40
|
...
|
|
40
41
|
|
|
41
42
|
@shortcut(
|
|
@@ -45,6 +46,7 @@ class ChatJoinRequestCute(BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMember
|
|
|
45
46
|
)
|
|
46
47
|
async def decline(
|
|
47
48
|
self,
|
|
49
|
+
*,
|
|
48
50
|
chat_id: int | str | None = None,
|
|
49
51
|
user_id: int | None = None,
|
|
50
52
|
**other: typing.Any,
|
|
@@ -54,7 +56,6 @@ class ChatJoinRequestCute(BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMember
|
|
|
54
56
|
Use this method to decline a chat join request. The bot must be an administrator
|
|
55
57
|
in the chat for this to work and must have the can_invite_users administrator
|
|
56
58
|
right. Returns True on success."""
|
|
57
|
-
|
|
58
59
|
...
|
|
59
60
|
|
|
60
61
|
|