telekit 2.1.0__tar.gz → 2.2.0__tar.gz
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.
- {telekit-2.1.0/telekit.egg-info → telekit-2.2.0}/PKG-INFO +50 -15
- {telekit-2.1.0 → telekit-2.2.0}/README.md +3 -3
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_chain.py +1 -4
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_chain_base.py +6 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_chain_entry_logic.py +12 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_chain_inline_keyboards_logic.py +41 -20
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_handler.py +2 -2
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_inline_buttons.py +238 -3
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_logger.py +29 -28
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/mixin.py +2 -2
- telekit-2.2.0/telekit/_user.py +199 -0
- telekit-2.2.0/telekit/_version.py +6 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/entry.py +1 -3
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/pages.py +12 -5
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/spells.py +3 -3
- {telekit-2.1.0 → telekit-2.2.0}/telekit/inline_buttons.py +8 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/senders.py +402 -100
- {telekit-2.1.0 → telekit-2.2.0}/telekit/types.py +45 -28
- {telekit-2.1.0 → telekit-2.2.0}/telekit/utils.py +25 -0
- {telekit-2.1.0 → telekit-2.2.0/telekit.egg-info}/PKG-INFO +50 -15
- telekit-2.1.0/telekit/_user.py +0 -138
- telekit-2.1.0/telekit/_version.py +0 -1
- {telekit-2.1.0 → telekit-2.2.0}/LICENSE +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/setup.cfg +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/setup.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_buildtext/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_buildtext/formatter.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_buildtext/styles.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_callback_query_handler.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_chapters/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_chapters/chapters.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_init.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_input_handler.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_on.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_snapvault/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_snapvault/snapcode.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_snapvault/snapvault.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_state.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/builder.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/canvas_parser.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/lexer.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/nodes.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/parser.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/parser/token.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/telekit_dsl.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_telekit_dsl/telekit_orm.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/_timeout.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/dices.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/__init__.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/complete_hotel.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/counter.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/dsl.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/faq.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/hotel.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/on_text.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/pyapi.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/qr.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/quiz.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/start.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_handlers/text_document.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/example/example_server.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/parameters.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/server.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit/styles.py +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit.egg-info/SOURCES.txt +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit.egg-info/dependency_links.txt +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit.egg-info/requires.txt +0 -0
- {telekit-2.1.0 → telekit-2.2.0}/telekit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telekit
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Declarative, developer-friendly library for building Telegram bots
|
|
5
5
|
Home-page: https://github.com/Romashkaa/telekit
|
|
6
6
|
Author: romashka
|
|
@@ -36,9 +36,9 @@ Dynamic: summary
|
|
|
36
36
|
|
|
37
37
|

|
|
38
38
|
|
|
39
|
-
[](https://pypi.org/project/telekit/)
|
|
40
40
|
[](https://pypi.org/project/telekit/)
|
|
41
|
-
[](https://pepy.tech/project/telekit)
|
|
42
42
|
|
|
43
43
|
# Telekit
|
|
44
44
|
|
|
@@ -89,7 +89,7 @@ Even in its beta stage, Telekit accelerates bot development, offering typed comm
|
|
|
89
89
|
- Fast to develop and easy-to-extend code
|
|
90
90
|
|
|
91
91
|
[GitHub](https://github.com/Romashkaa/telekit)
|
|
92
|
-
[
|
|
92
|
+
[PyPI](https://pypi.org/project/telekit/)
|
|
93
93
|
[Telegram](https://t.me/NotRomashka)
|
|
94
94
|
[Community](https://t.me/+wu-dFrOBFIwyNzc0)
|
|
95
95
|
|
|
@@ -378,20 +378,55 @@ It tries to make Telegram bot development easier.
|
|
|
378
378
|
|
|
379
379
|
---
|
|
380
380
|
|
|
381
|
-
# Changes in version 2.
|
|
381
|
+
# Changes in version 2.2.0
|
|
382
382
|
|
|
383
|
-
##
|
|
383
|
+
## New Button Types
|
|
384
384
|
|
|
385
|
-
| **Name**
|
|
386
|
-
|
|
387
|
-
| `
|
|
385
|
+
| **Name** | **Description** |
|
|
386
|
+
|----------------------|----------------------------------------------------------|
|
|
387
|
+
| `AlertButton` | A callback button that shows a popup alert when pressed. |
|
|
388
|
+
| `NotificationButton` | A callback button that shows a notification. |
|
|
389
|
+
| `InvokeButton` | A callback button that calls the object method. |
|
|
390
|
+
|
|
391
|
+
## User Improvements
|
|
392
|
+
|
|
393
|
+
| **Name** | **Description** |
|
|
394
|
+
| ---------------------- | --------------------------------------------------------- |
|
|
395
|
+
| `bio` | Bio of the user or description of the chat. |
|
|
396
|
+
| `birthdate` | Birthdate of the user, if set and visible. |
|
|
397
|
+
| `description` | Description of the group or channel. |
|
|
398
|
+
| `mention` | `tg://user?id=` deep link, works even without a username. |
|
|
399
|
+
| `is_private` | Whether the message was sent in a private chat. |
|
|
400
|
+
| `is_group` | Whether the message was sent in a group. |
|
|
401
|
+
| `is_supergroup` | Whether the message was sent in a supergroup. |
|
|
402
|
+
| `is_channel` | Whether the message was sent in a channel. |
|
|
403
|
+
| `avatar` | File ID of the user's most recent profile photo. |
|
|
404
|
+
| `profile_photos_count` | Total number of profile photos the user has set. |
|
|
405
|
+
|
|
406
|
+
- Refactor: `User` now accepts a `Message` object instead of `chat_id` + `from_user`. All properties are derived from `_sender` (`from_user` or `chat`) and migrated to `cached_property`. Fixed broken `get_id` and `get_full_name` references.
|
|
407
|
+
- Added `__repr__`.
|
|
408
|
+
|
|
409
|
+
## Sender Improvements
|
|
410
|
+
|
|
411
|
+
| **Name** | **Description** |
|
|
412
|
+
| -------------------------- | ------------------------------------------------------ |
|
|
413
|
+
| `sent_message` | The last message sent by this sender instance. |
|
|
414
|
+
| `disable_notification` | Disables notification sound when the message is sent. |
|
|
415
|
+
| `protect_content` | Protects the message contents from forwarding and saving. |
|
|
416
|
+
| `reply_parameters` | Reply parameters for the message to be sent. |
|
|
417
|
+
| `link_preview_options` | Link preview options for the message to be sent. |
|
|
418
|
+
| `show_caption_above_media` | Shows the caption above the media instead of below. |
|
|
419
|
+
|
|
420
|
+
- Refactored sending system.
|
|
388
421
|
|
|
389
|
-
|
|
422
|
+
## Chain Improvements
|
|
390
423
|
|
|
391
|
-
|
|
424
|
+
| **Name** | **Description** |
|
|
425
|
+
| ----------- | ------------------------------------------------------------ |
|
|
426
|
+
| `received` | The message received from the user during entry processing. |
|
|
392
427
|
|
|
428
|
+
## Bug Fixes
|
|
393
429
|
| **Name** | **Description** |
|
|
394
|
-
|
|
395
|
-
|
|
|
396
|
-
|
|
|
397
|
-
| `make_qrcode` | Generates a [QR code](./docs/examples/qr_gen.md) URL from any text or link. |
|
|
430
|
+
| -------- | --------------- |
|
|
431
|
+
| Media edit fallback | Fixed a bug where editing a text message with a new attachment (photo, audio, document, etc.) would only update the text, ignoring the attachment entirely. |
|
|
432
|
+
| Callback answer order | Fixed a bug where `answer_callback_query` was not always displayed. |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/telekit/)
|
|
4
4
|
[](https://pypi.org/project/telekit/)
|
|
5
|
-
[](https://pepy.tech/project/telekit)
|
|
6
6
|
|
|
7
7
|
# Telekit
|
|
8
8
|
|
|
@@ -53,7 +53,7 @@ Even in its beta stage, Telekit accelerates bot development, offering typed comm
|
|
|
53
53
|
- Fast to develop and easy-to-extend code
|
|
54
54
|
|
|
55
55
|
[GitHub](https://github.com/Romashkaa/telekit)
|
|
56
|
-
[
|
|
56
|
+
[PyPI](https://pypi.org/project/telekit/)
|
|
57
57
|
[Telegram](https://t.me/NotRomashka)
|
|
58
58
|
[Community](https://t.me/+wu-dFrOBFIwyNzc0)
|
|
59
59
|
|
|
@@ -28,11 +28,8 @@ from telebot.types import Message
|
|
|
28
28
|
from . import senders
|
|
29
29
|
from .styles import TextEntity
|
|
30
30
|
|
|
31
|
-
# Logging
|
|
32
|
-
from ._logger import logger
|
|
33
|
-
library = logger.library
|
|
34
|
-
|
|
35
31
|
# Chain modules
|
|
32
|
+
from ._chain_base import library
|
|
36
33
|
from ._chain_inline_keyboards_logic import ChainInlineKeyboardLogic
|
|
37
34
|
from ._chain_entry_logic import ChainEntryLogic, TextDocument
|
|
38
35
|
|
|
@@ -30,6 +30,10 @@ from . import senders
|
|
|
30
30
|
from . import _input_handler
|
|
31
31
|
from . import _timeout
|
|
32
32
|
|
|
33
|
+
# Logging
|
|
34
|
+
from ._logger import logger
|
|
35
|
+
library = logger.library
|
|
36
|
+
|
|
33
37
|
class ChainBase:
|
|
34
38
|
|
|
35
39
|
bot: telebot.TeleBot
|
|
@@ -49,6 +53,8 @@ class ChainBase:
|
|
|
49
53
|
def __init__(self, chat_id: int, *, previous_message: Message | None = None):
|
|
50
54
|
self.chat_id = chat_id
|
|
51
55
|
self.sender = senders.Sender(chat_id)
|
|
56
|
+
self.received = None
|
|
57
|
+
|
|
52
58
|
self._handler = _input_handler.InputHandler(chat_id)
|
|
53
59
|
self._previous_message = previous_message
|
|
54
60
|
self._timeout_handler = _timeout.TimeoutHandler()
|
|
@@ -68,6 +68,8 @@ class ChainEntryLogic(ChainBase):
|
|
|
68
68
|
"""
|
|
69
69
|
|
|
70
70
|
def callback(message: Message) -> bool:
|
|
71
|
+
self.received = message
|
|
72
|
+
|
|
71
73
|
if delete_user_response:
|
|
72
74
|
self.sender.delete_message(message, True)
|
|
73
75
|
|
|
@@ -154,6 +156,8 @@ class ChainEntryLogic(ChainBase):
|
|
|
154
156
|
"""
|
|
155
157
|
|
|
156
158
|
def callback(message: Message) -> bool:
|
|
159
|
+
self.received = message
|
|
160
|
+
|
|
157
161
|
if delete_user_response:
|
|
158
162
|
self.sender.delete_message(message, True)
|
|
159
163
|
|
|
@@ -246,6 +250,8 @@ class ChainEntryLogic(ChainBase):
|
|
|
246
250
|
"""
|
|
247
251
|
|
|
248
252
|
def callback(message: Message) -> bool:
|
|
253
|
+
self.received = message
|
|
254
|
+
|
|
249
255
|
if delete_user_response:
|
|
250
256
|
self.sender.delete_message(message, True)
|
|
251
257
|
|
|
@@ -340,6 +346,8 @@ class ChainEntryLogic(ChainBase):
|
|
|
340
346
|
"""
|
|
341
347
|
|
|
342
348
|
def callback(message: Message) -> bool:
|
|
349
|
+
self.received = message
|
|
350
|
+
|
|
343
351
|
if delete_user_response:
|
|
344
352
|
self.sender.delete_message(message, True)
|
|
345
353
|
|
|
@@ -453,6 +461,8 @@ class ChainEntryLogic(ChainBase):
|
|
|
453
461
|
"""
|
|
454
462
|
|
|
455
463
|
def callback(message: Message) -> bool:
|
|
464
|
+
self.received = message
|
|
465
|
+
|
|
456
466
|
if delete_user_response:
|
|
457
467
|
self.sender.delete_message(message, True)
|
|
458
468
|
|
|
@@ -592,6 +602,8 @@ class ChainEntryLogic(ChainBase):
|
|
|
592
602
|
"""
|
|
593
603
|
|
|
594
604
|
def callback(message: Message) -> bool:
|
|
605
|
+
self.received = message
|
|
606
|
+
|
|
595
607
|
if delete_user_response:
|
|
596
608
|
self.sender.delete_message(message, True)
|
|
597
609
|
|
|
@@ -32,7 +32,7 @@ from telebot.types import (
|
|
|
32
32
|
|
|
33
33
|
from ._callback_query_handler import CallbackQueryHandler
|
|
34
34
|
from ._inline_buttons import InlineButton, CallbackButton
|
|
35
|
-
from ._chain_base import ChainBase
|
|
35
|
+
from ._chain_base import ChainBase, library
|
|
36
36
|
|
|
37
37
|
if typing.TYPE_CHECKING:
|
|
38
38
|
from ._chain import Chain # only for type hints
|
|
@@ -151,15 +151,24 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
151
151
|
buttons: list[InlineKeyboardButton] = []
|
|
152
152
|
|
|
153
153
|
for index, (caption, value) in enumerate(keyboard.items()):
|
|
154
|
-
if enable_special_buttons and isinstance(value, InlineButton):
|
|
154
|
+
if enable_special_buttons and isinstance(value, InlineButton) and not isinstance(value, CallbackButton):
|
|
155
155
|
buttons.append(value._compile(caption))
|
|
156
156
|
else:
|
|
157
|
+
if isinstance(value, CallbackButton):
|
|
158
|
+
invoker = value.build_invoker(self._cancel_timeout_and_handlers)
|
|
159
|
+
else:
|
|
160
|
+
invoker = self._get_invoker_with_argument(func, value)
|
|
161
|
+
|
|
157
162
|
callback_data = CallbackQueryHandler.inline_button(f"{index}:{random.randint(1000, 9999)}")
|
|
158
|
-
callback_functions[callback_data] =
|
|
163
|
+
callback_functions[callback_data] = invoker
|
|
164
|
+
|
|
165
|
+
kwargs: dict[str, Any] = getattr(invoker, "_kwargs", None) or {}
|
|
166
|
+
|
|
159
167
|
buttons.append(
|
|
160
168
|
InlineKeyboardButton(
|
|
161
169
|
text=caption,
|
|
162
|
-
callback_data=callback_data
|
|
170
|
+
callback_data=callback_data,
|
|
171
|
+
**kwargs
|
|
163
172
|
)
|
|
164
173
|
)
|
|
165
174
|
|
|
@@ -242,18 +251,34 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
242
251
|
buttons: list[InlineKeyboardButton] = []
|
|
243
252
|
|
|
244
253
|
if not isinstance(choices, dict):
|
|
254
|
+
for c in choices:
|
|
255
|
+
if type(c).__str__ is object.__str__:
|
|
256
|
+
library.warning(
|
|
257
|
+
f"{type(c).__name__} does not implement __str__. "
|
|
258
|
+
f"Consider passing a dict with explicit labels.",
|
|
259
|
+
stacklevel=3
|
|
260
|
+
)
|
|
245
261
|
choices = {str(c): c for c in choices}
|
|
246
262
|
|
|
247
263
|
for index, (caption, value) in enumerate(choices.items()):
|
|
248
|
-
if enable_special_buttons and isinstance(value, InlineButton):
|
|
264
|
+
if enable_special_buttons and isinstance(value, InlineButton) and not isinstance(value, CallbackButton):
|
|
249
265
|
buttons.append(value._compile(caption))
|
|
250
266
|
else:
|
|
267
|
+
if isinstance(value, CallbackButton):
|
|
268
|
+
invoker = value.build_invoker(self._cancel_timeout_and_handlers)
|
|
269
|
+
else:
|
|
270
|
+
invoker = self._get_invoker_with_argument(func, value)
|
|
271
|
+
|
|
251
272
|
callback_data = CallbackQueryHandler.inline_button(f"{index}:{random.randint(1000, 9999)}")
|
|
252
|
-
callback_functions[callback_data] =
|
|
273
|
+
callback_functions[callback_data] = invoker
|
|
274
|
+
|
|
275
|
+
kwargs: dict[str, Any] = getattr(invoker, "_kwargs", None) or {}
|
|
276
|
+
|
|
253
277
|
buttons.append(
|
|
254
278
|
InlineKeyboardButton(
|
|
255
279
|
text=caption,
|
|
256
|
-
callback_data=callback_data
|
|
280
|
+
callback_data=callback_data,
|
|
281
|
+
**kwargs
|
|
257
282
|
)
|
|
258
283
|
)
|
|
259
284
|
|
|
@@ -314,21 +339,21 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
314
339
|
|
|
315
340
|
self.sender.set_reply_markup(markup)
|
|
316
341
|
|
|
317
|
-
def
|
|
318
|
-
def
|
|
342
|
+
def _get_invoker_with_argument(self, callback: Callable, argument: Any, query_answer: tuple[str, bool] | None = None) -> Callable[[CallbackQuery], None]:
|
|
343
|
+
def invoker(call: CallbackQuery) -> None:
|
|
319
344
|
self._cancel_timeout_and_handlers()
|
|
320
|
-
|
|
345
|
+
callback(argument)
|
|
321
346
|
self._answer_callback_query(call, query_answer)
|
|
322
347
|
|
|
323
|
-
return
|
|
348
|
+
return invoker
|
|
324
349
|
|
|
325
|
-
def
|
|
326
|
-
def
|
|
350
|
+
def _get_invoker(self, callback: Callable[..., None], query_answer: tuple[str, bool] | None = None) -> Callable[[CallbackQuery], None]:
|
|
351
|
+
def invoker(call: CallbackQuery):
|
|
327
352
|
self._cancel_timeout_and_handlers()
|
|
328
|
-
|
|
353
|
+
callback()
|
|
329
354
|
self._answer_callback_query(call, query_answer)
|
|
330
355
|
|
|
331
|
-
return
|
|
356
|
+
return invoker
|
|
332
357
|
|
|
333
358
|
def _answer_callback_query(self, call: CallbackQuery, query_answer: tuple[str, bool] | None = None):
|
|
334
359
|
if query_answer is None:
|
|
@@ -369,8 +394,4 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
369
394
|
rows.append(buttons[index:index + last_width])
|
|
370
395
|
index += last_width
|
|
371
396
|
|
|
372
|
-
return rows
|
|
373
|
-
|
|
374
|
-
def _is_valid_telegram_callback(self, data: str) -> bool:
|
|
375
|
-
byte_size = len(data.encode('utf-8'))
|
|
376
|
-
return 1 <= byte_size <= 64
|
|
397
|
+
return rows
|
|
@@ -98,7 +98,7 @@ class Handler:
|
|
|
98
98
|
|
|
99
99
|
def __init__(self, message: Message):
|
|
100
100
|
self.message: Message = message
|
|
101
|
-
self.user = User(self.message
|
|
101
|
+
self.user = User(self.message)
|
|
102
102
|
self.new_chain()
|
|
103
103
|
|
|
104
104
|
def handle(self) -> Any:
|
|
@@ -112,7 +112,7 @@ class Handler:
|
|
|
112
112
|
avoids the need for multiple custom entry methods
|
|
113
113
|
like `start` or `handle_start`.
|
|
114
114
|
"""
|
|
115
|
-
library.warning("
|
|
115
|
+
library.warning(f"`{type(self).__qualname__}().handle()` was called but not overridden; no logic executed.")
|
|
116
116
|
return
|
|
117
117
|
|
|
118
118
|
# -----------------------------------------------------
|
|
@@ -34,6 +34,9 @@ __all__ = [
|
|
|
34
34
|
"SuggestButton",
|
|
35
35
|
"CopyTextButton",
|
|
36
36
|
"CallbackButton",
|
|
37
|
+
"AlertButton",
|
|
38
|
+
"NotificationButton",
|
|
39
|
+
"InvokeButton",
|
|
37
40
|
|
|
38
41
|
"ButtonStyle"
|
|
39
42
|
]
|
|
@@ -54,6 +57,9 @@ class InlineButton:
|
|
|
54
57
|
- `SuggestButton`
|
|
55
58
|
- `CopyTextButton`
|
|
56
59
|
- `CallbackButton`
|
|
60
|
+
- `AlertButton`
|
|
61
|
+
- `NotificationButton`
|
|
62
|
+
- `InvokeButton`
|
|
57
63
|
"""
|
|
58
64
|
|
|
59
65
|
_bot: TeleBot
|
|
@@ -70,6 +76,9 @@ class InlineButton:
|
|
|
70
76
|
Suggest: type["SuggestButton"]
|
|
71
77
|
CopyText: type["CopyTextButton"]
|
|
72
78
|
Callback: type["CallbackButton"]
|
|
79
|
+
Alert: type["AlertButton"]
|
|
80
|
+
Notification: type["NotificationButton"]
|
|
81
|
+
Invoke: type["InvokeButton"]
|
|
73
82
|
|
|
74
83
|
Styles: type[ButtonStyle] = ButtonStyle
|
|
75
84
|
|
|
@@ -330,11 +339,11 @@ class CallbackButton(InlineButton):
|
|
|
330
339
|
|
|
331
340
|
def __call__(self, call: CallbackQuery):
|
|
332
341
|
self._invoke_chain_callback()
|
|
333
|
-
self._invoke_callback()
|
|
334
342
|
self._answer_callback_query(call)
|
|
343
|
+
self._invoke_callback()
|
|
335
344
|
|
|
336
345
|
def __init__(
|
|
337
|
-
self,
|
|
346
|
+
self,
|
|
338
347
|
callback: Callable[..., Any] | None,
|
|
339
348
|
|
|
340
349
|
pass_args: tuple | list | None = None,
|
|
@@ -378,8 +387,234 @@ class CallbackButton(InlineButton):
|
|
|
378
387
|
callback=callback,
|
|
379
388
|
)
|
|
380
389
|
|
|
390
|
+
class AlertButton(CallbackButton):
|
|
391
|
+
"""
|
|
392
|
+
An inline keyboard button that shows a popup alert when pressed and terminates the chain.
|
|
393
|
+
|
|
394
|
+
When the user presses this button, the specified text is displayed as a modal alert dialog.
|
|
395
|
+
The chain is finalized: all other buttons in the message become inactive and no further
|
|
396
|
+
interaction is expected. Typically used as a session-ending buttons.
|
|
397
|
+
|
|
398
|
+
:param text: Text to display in the alert popup.
|
|
399
|
+
:type text: `str | None`
|
|
400
|
+
|
|
401
|
+
:param style: Style of the button. Must be one of `ButtonStyle.DANGER` (red),
|
|
402
|
+
`*.SUCCESS` (green) or `*.PRIMARY` (blue).
|
|
403
|
+
You can also pass these as string values: "danger", "success", "primary".
|
|
404
|
+
If omitted, an app-specific default style is used.
|
|
405
|
+
:type style: `str | ButtonStyle | None`
|
|
406
|
+
|
|
407
|
+
:param kwargs: Additional keyword arguments passed directly to `InlineKeyboardButton`.
|
|
408
|
+
:type kwargs: `Any`
|
|
409
|
+
|
|
410
|
+
Example::
|
|
411
|
+
|
|
412
|
+
def entry_word(self) -> None:
|
|
413
|
+
self.chain.sender.set_title("✏️ Add a new word")
|
|
414
|
+
self.chain.sender.set_message("Type the word you want to save to your dictionary:")
|
|
415
|
+
self.chain.set_inline_keyboard({
|
|
416
|
+
"✕ Cancel": AlertButton(text="Cancelled")
|
|
417
|
+
})
|
|
418
|
+
self.chain.set_entry_text(
|
|
419
|
+
self.entry_translation,
|
|
420
|
+
delete_user_response=True,
|
|
421
|
+
)
|
|
422
|
+
self.chain.edit()
|
|
423
|
+
"""
|
|
424
|
+
def __init__(
|
|
425
|
+
self,
|
|
426
|
+
text: str | None = None,
|
|
427
|
+
*,
|
|
428
|
+
style: str | None | ButtonStyle = None,
|
|
429
|
+
**kwargs
|
|
430
|
+
):
|
|
431
|
+
super().__init__(
|
|
432
|
+
callback=None,
|
|
433
|
+
answer_text=text,
|
|
434
|
+
answer_as_alert=True,
|
|
435
|
+
style=style,
|
|
436
|
+
**kwargs
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
class NotificationButton(CallbackButton):
|
|
440
|
+
"""
|
|
441
|
+
An inline keyboard button that shows a brief notification at the top of the chat screen
|
|
442
|
+
when pressed and terminates the chain.
|
|
443
|
+
|
|
444
|
+
When the user presses this button, the specified text appears as a short non-blocking
|
|
445
|
+
notification. The chain is finalized: all other buttons in the message become inactive
|
|
446
|
+
and no further interaction is expected. Typically used as a session-ending buttons.
|
|
447
|
+
|
|
448
|
+
:param text: Text to display in the notification.
|
|
449
|
+
:type text: `str | None`
|
|
450
|
+
|
|
451
|
+
:param style: Style of the button. Must be one of `ButtonStyle.DANGER` (red),
|
|
452
|
+
`*.SUCCESS` (green) or `*.PRIMARY` (blue).
|
|
453
|
+
You can also pass these as string values: "danger", "success", "primary".
|
|
454
|
+
If omitted, an app-specific default style is used.
|
|
455
|
+
:type style: `str | ButtonStyle | None`
|
|
456
|
+
|
|
457
|
+
:param kwargs: Additional keyword arguments passed directly to `InlineKeyboardButton`.
|
|
458
|
+
:type kwargs: `Any`
|
|
459
|
+
|
|
460
|
+
Example::
|
|
461
|
+
|
|
462
|
+
def entry_word(self) -> None:
|
|
463
|
+
self.chain.sender.set_title("✏️ Add a new word")
|
|
464
|
+
self.chain.sender.set_message("Type the word you want to save to your dictionary:")
|
|
465
|
+
self.chain.set_inline_keyboard({
|
|
466
|
+
"✕ Cancel": NotificationButton(text="Cancelled")
|
|
467
|
+
})
|
|
468
|
+
self.chain.set_entry_text(
|
|
469
|
+
self.entry_translation,
|
|
470
|
+
delete_user_response=True,
|
|
471
|
+
)
|
|
472
|
+
self.chain.edit()
|
|
473
|
+
"""
|
|
474
|
+
def __init__(
|
|
475
|
+
self,
|
|
476
|
+
text: str | None = None,
|
|
477
|
+
*,
|
|
478
|
+
style: str | None | ButtonStyle = None,
|
|
479
|
+
**kwargs
|
|
480
|
+
):
|
|
481
|
+
super().__init__(
|
|
482
|
+
callback=None,
|
|
483
|
+
answer_text=text,
|
|
484
|
+
answer_as_alert=False,
|
|
485
|
+
style=style,
|
|
486
|
+
**kwargs
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
class InvokeButton(CallbackButton):
|
|
490
|
+
"""
|
|
491
|
+
An inline keyboard button that calls a named method on a given object when pressed.
|
|
492
|
+
|
|
493
|
+
Unlike `CallbackButton`, which takes a callable directly, `InvokeButton` resolves
|
|
494
|
+
the method at invocation time via `getattr(obj, invoke)`.
|
|
495
|
+
|
|
496
|
+
:param obj: The object on which the method will be called.
|
|
497
|
+
:type obj: `Any`
|
|
498
|
+
|
|
499
|
+
:param invoke: Name of the method to call on `obj`.
|
|
500
|
+
:type invoke: `str`
|
|
501
|
+
|
|
502
|
+
:param pass_args: Positional arguments to pass into the method.
|
|
503
|
+
:type pass_args: `tuple | list | None`
|
|
504
|
+
|
|
505
|
+
:param pass_kwargs: Keyword arguments to pass into the method.
|
|
506
|
+
:type pass_kwargs: `dict[str, Any] | None`
|
|
507
|
+
|
|
508
|
+
:param answer_text: Optional text to send as an answer to the callback query.
|
|
509
|
+
:type answer_text: `str | None`
|
|
510
|
+
|
|
511
|
+
:param answer_as_alert: If `True`, the answer is shown as a popup alert.
|
|
512
|
+
If `False`, it appears as a notification at the top of the chat.
|
|
513
|
+
:type answer_as_alert: `bool`
|
|
514
|
+
|
|
515
|
+
:param style: Style of the button. Must be one of `ButtonStyle.DANGER` (red),
|
|
516
|
+
`*.SUCCESS` (green) or `*.PRIMARY` (blue).
|
|
517
|
+
You can also pass these as string values: "danger", "success", "primary".
|
|
518
|
+
If omitted, an app-specific default style is used.
|
|
519
|
+
:type style: `str | ButtonStyle | None`
|
|
520
|
+
|
|
521
|
+
:param kwargs: Additional keyword arguments passed directly to `InlineKeyboardButton`.
|
|
522
|
+
:type kwargs: `Any`
|
|
523
|
+
|
|
524
|
+
Example::
|
|
525
|
+
|
|
526
|
+
self.chain.set_inline_keyboard({
|
|
527
|
+
"📖 My Deck": InvokeButton(self.handoff(DeckHandler), "handle"),
|
|
528
|
+
})
|
|
529
|
+
"""
|
|
530
|
+
class _CallbackInvoker(CallbackButton._CallbackInvoker):
|
|
531
|
+
def __init__(
|
|
532
|
+
self,
|
|
533
|
+
chain_callback: Callable[[], None],
|
|
534
|
+
|
|
535
|
+
obj: Any,
|
|
536
|
+
invoke: str,
|
|
537
|
+
|
|
538
|
+
pass_args: tuple | list | None = None,
|
|
539
|
+
pass_kwargs: dict[str, Any] | None = None,
|
|
540
|
+
|
|
541
|
+
answer_text: str | None = None,
|
|
542
|
+
answer_as_alert: bool = True,
|
|
543
|
+
|
|
544
|
+
style: str | None | ButtonStyle = None,
|
|
545
|
+
|
|
546
|
+
kwargs: dict[str, Any] = {}
|
|
547
|
+
):
|
|
548
|
+
self._obj = obj
|
|
549
|
+
self._invoke = invoke
|
|
550
|
+
|
|
551
|
+
self._pass_args = pass_args
|
|
552
|
+
self._pass_kwargs = pass_kwargs
|
|
553
|
+
|
|
554
|
+
self._answer_text = answer_text
|
|
555
|
+
self._answer_as_alert = answer_as_alert
|
|
556
|
+
|
|
557
|
+
self._style = style
|
|
558
|
+
|
|
559
|
+
self._kwargs = {"style": style} | kwargs
|
|
560
|
+
|
|
561
|
+
self._chain_callback: Callable[[], None] = chain_callback
|
|
562
|
+
|
|
563
|
+
def _invoke_callback(self):
|
|
564
|
+
obj: Any = self.__dict__["_obj"]
|
|
565
|
+
callback: Any = getattr(obj, self._invoke)
|
|
566
|
+
|
|
567
|
+
args = self._pass_args or ()
|
|
568
|
+
kwargs = self._pass_kwargs or {}
|
|
569
|
+
|
|
570
|
+
callback(*args, **kwargs)
|
|
571
|
+
|
|
572
|
+
def __init__(
|
|
573
|
+
self,
|
|
574
|
+
obj: Any,
|
|
575
|
+
invoke: str,
|
|
576
|
+
|
|
577
|
+
pass_args: tuple | list | None = None,
|
|
578
|
+
pass_kwargs: dict[str, Any] | None = None,
|
|
579
|
+
*,
|
|
580
|
+
answer_text: str | None = None,
|
|
581
|
+
answer_as_alert: bool = True,
|
|
582
|
+
|
|
583
|
+
style: str | None | ButtonStyle = None,
|
|
584
|
+
|
|
585
|
+
**kwargs
|
|
586
|
+
):
|
|
587
|
+
self._obj = obj
|
|
588
|
+
self._invoke = invoke
|
|
589
|
+
|
|
590
|
+
self._pass_args = pass_args
|
|
591
|
+
self._pass_kwargs = pass_kwargs
|
|
592
|
+
|
|
593
|
+
self._answer_text = answer_text
|
|
594
|
+
self._answer_as_alert = answer_as_alert
|
|
595
|
+
|
|
596
|
+
self._style = self._normalize_style(style)
|
|
597
|
+
|
|
598
|
+
self._kwargs = kwargs
|
|
599
|
+
|
|
600
|
+
def build_invoker(self, chain_callback: Callable[[], None]) -> _CallbackInvoker:
|
|
601
|
+
return self._CallbackInvoker(
|
|
602
|
+
chain_callback=chain_callback,
|
|
603
|
+
obj=self._obj,
|
|
604
|
+
invoke=self._invoke,
|
|
605
|
+
pass_args=self._pass_args,
|
|
606
|
+
pass_kwargs=self._pass_kwargs,
|
|
607
|
+
answer_text=self._answer_text,
|
|
608
|
+
answer_as_alert=self._answer_as_alert,
|
|
609
|
+
style=self._style,
|
|
610
|
+
kwargs=self._kwargs
|
|
611
|
+
)
|
|
612
|
+
|
|
381
613
|
InlineButton.Link = LinkButton
|
|
382
614
|
InlineButton.WebApp = WebAppButton
|
|
383
615
|
InlineButton.Suggest = SuggestButton
|
|
384
616
|
InlineButton.CopyText = CopyTextButton
|
|
385
|
-
InlineButton.Callback = CallbackButton
|
|
617
|
+
InlineButton.Callback = CallbackButton
|
|
618
|
+
InlineButton.Alert = AlertButton
|
|
619
|
+
InlineButton.Notification = NotificationButton
|
|
620
|
+
InlineButton.Invoke = InvokeButton
|