telekit 2.2.0a3__tar.gz → 2.3.0b2__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.2.0a3 → telekit-2.3.0b2}/PKG-INFO +138 -38
- {telekit-2.2.0a3 → telekit-2.3.0b2}/README.md +2 -1
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/__init__.py +2 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_callback_query_handler.py +18 -6
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_chain_inline_keyboards_logic.py +1 -5
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_handler.py +7 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_inline_buttons.py +103 -11
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_version.py +1 -1
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/entry.py +5 -1
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/spells.py +12 -13
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/text_document.py +6 -2
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/inline_buttons.py +8 -2
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/senders.py +130 -39
- telekit-2.3.0b2/telekit/traits/__init__.py +2 -0
- telekit-2.3.0b2/telekit/traits/paginated_choice.py +145 -0
- telekit-2.3.0b2/telekit/traits/track_handoff_origin.py +99 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/utils.py +190 -15
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit.egg-info/PKG-INFO +138 -38
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit.egg-info/SOURCES.txt +4 -1
- {telekit-2.2.0a3 → telekit-2.3.0b2}/LICENSE +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/setup.cfg +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/setup.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_buildtext/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_buildtext/formatter.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_buildtext/styles.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_chain.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_chain_base.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_chain_entry_logic.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_chapters/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_chapters/chapters.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_init.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_input_handler.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_logger.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_on.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_snapvault/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_snapvault/snapcode.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_snapvault/snapvault.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_state.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/mixin.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/builder.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/canvas_parser.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/lexer.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/nodes.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/parser.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/parser/token.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/telekit_dsl.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_telekit_dsl/telekit_orm.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_timeout.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/_user.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/dices.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/__init__.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/complete_hotel.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/counter.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/dsl.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/faq.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/hotel.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/on_text.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/pages.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/pyapi.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/qr.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/quiz.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_handlers/start.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/example/example_server.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/parameters.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/server.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/styles.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit/types.py +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit.egg-info/dependency_links.txt +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/telekit.egg-info/requires.txt +0 -0
- {telekit-2.2.0a3 → telekit-2.3.0b2}/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.3.0b2
|
|
4
4
|
Summary: Declarative, developer-friendly library for building Telegram bots
|
|
5
5
|
Home-page: https://github.com/Romashkaa/telekit
|
|
6
6
|
Author: romashka
|
|
@@ -101,7 +101,8 @@ Even in its beta stage, Telekit accelerates bot development, offering typed comm
|
|
|
101
101
|
- [Dialogue](https://github.com/Romashkaa/telekit/blob/main/docs/examples/dialogue.md)
|
|
102
102
|
- [Risk Game](https://github.com/Romashkaa/telekit/blob/main/docs/examples/risk_game.md)
|
|
103
103
|
- [Counter](https://github.com/Romashkaa/telekit/blob/main/docs/examples/counter.md)
|
|
104
|
-
- [Quiz (
|
|
104
|
+
- [Quiz (DSL)](https://github.com/Romashkaa/telekit/blob/main/docs/examples/quiz.md)
|
|
105
|
+
- [Hotel (DSL)](https://github.com/Romashkaa/telekit/blob/main/docs/examples/complete_hotel.md)
|
|
105
106
|
- [More...](https://github.com/Romashkaa/telekit/blob/main/docs/examples/examples.md)
|
|
106
107
|
|
|
107
108
|
## Overview
|
|
@@ -378,49 +379,148 @@ It tries to make Telegram bot development easier.
|
|
|
378
379
|
|
|
379
380
|
---
|
|
380
381
|
|
|
381
|
-
# Changes in version 2.
|
|
382
|
+
# Changes in version 2.3.0b2
|
|
382
383
|
|
|
383
|
-
##
|
|
384
|
+
## Inline Buttons
|
|
384
385
|
|
|
385
|
-
| **Name**
|
|
386
|
-
|
|
387
|
-
| `
|
|
388
|
-
| `
|
|
389
|
-
| `InvokeButton` | A callback button that calls the object method. |
|
|
386
|
+
| **Name** | **Description** |
|
|
387
|
+
| ---------------- | ------------------------------------------------------------------------------- |
|
|
388
|
+
| `StaticButton` | A non-interactive inline button that performs no action when pressed. |
|
|
389
|
+
| `AnswerButton` | A button that responds to a callback query with a notification or alert without executing custom logic. |
|
|
390
390
|
|
|
391
|
-
|
|
391
|
+
- `AnswerButton` and its subclasses (`AlertButton`, `NotificationButton`) now support the `persistent` parameter (Defauts to `True`):
|
|
392
|
+
- `True` — non-blocking hint (does not affect further interactions)
|
|
393
|
+
- `False` — terminates interaction after click
|
|
392
394
|
|
|
393
|
-
|
|
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. |
|
|
395
|
+
## Traits
|
|
405
396
|
|
|
406
|
-
|
|
407
|
-
|
|
397
|
+
| **Name** | **Description** |
|
|
398
|
+
| -------------------------- | --------------------------------------------------------------------- |
|
|
399
|
+
| `TrackHandoffOrigin` | Tracks which handler transferred control to this one via `handoff()`. |
|
|
400
|
+
| `PaginatedChoice` | Adds a paginated inline keyboard for choosing from a list of items. |
|
|
408
401
|
|
|
409
|
-
|
|
402
|
+
### TrackHandoffOrigin
|
|
410
403
|
|
|
411
|
-
|
|
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. |
|
|
404
|
+
`TrackHandoffOrigin` adds three members to any handler that inherits it:
|
|
419
405
|
|
|
420
|
-
|
|
406
|
+
| **Member** | **Description** |
|
|
407
|
+
| ------------------- | -------------------------------------------------------------------------------- |
|
|
408
|
+
| `handoff_origin` | The handler instance that handed off to this one, or `None` if invoked directly. |
|
|
409
|
+
| `is_handed_off` | `True` if this handler was reached via `handoff()`, `False` otherwise. |
|
|
410
|
+
| `handoff_back()` | Transfer control back to the origin handler via `self.handoff_origin.handle()` |
|
|
411
|
+
| `handoff_back_or(handler)` | Like `handoff_back()`, but falls back to `handler` on fail. |
|
|
421
412
|
|
|
422
|
-
|
|
413
|
+
Set `TRACK_HANDOFF_ORIGIN = False` on any subclass to opt out of tracking.
|
|
423
414
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
415
|
+
```python
|
|
416
|
+
class MyHandler(TrackHandoffOrigin, telekit.Handler):
|
|
417
|
+
|
|
418
|
+
def handle(self):
|
|
419
|
+
...
|
|
420
|
+
self.chain.set_inline_keyboard({
|
|
421
|
+
"« Back": self.handoff_back_or(StartHandler)
|
|
422
|
+
})
|
|
423
|
+
self.chain.edit()
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### PaginatedChoice
|
|
427
|
+
|
|
428
|
+
Renders a paginated inline keyboard from any dict or iterable.
|
|
429
|
+
Navigation buttons (`« Back`, `Next »`) are added automatically.
|
|
430
|
+
|
|
431
|
+
| **Member** | **Description** |
|
|
432
|
+
| -------------------- | ---------------------------------------------------------------------------- |
|
|
433
|
+
| `paginated_choice(choices, on_choice, ...)` | Display a paginated choice keyboard. |
|
|
434
|
+
| `PAGINATED_CHOICE_BACK_LABEL` | Label for the back navigation button. Defaults to `« Back`. |
|
|
435
|
+
| `PAGINATED_CHOICE_NEXT_LABEL` | Label for the next navigation button. Defaults to `Next »`. |
|
|
436
|
+
| `PAGINATED_CHOICE_PAGE_LABEL` | Label template for the page indicator button. Supports `{page}` and `{pages}` placeholders. Set to `None` to hide. Defaults to `{page} / {pages}`. |
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
self.chain.sender.set_title("🔤 What is your initial?")
|
|
441
|
+
self.chain.sender.set_message("Pick the first letter of your name")
|
|
442
|
+
self.chain.sender.set_remove_text(False)
|
|
443
|
+
|
|
444
|
+
self.paginated_choice(
|
|
445
|
+
choices="ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
446
|
+
on_choice=self.handle_letter,
|
|
447
|
+
row_width=5
|
|
448
|
+
)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Override labels per handler to localise or restyle navigation:
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
class MyHandler(PaginatedChoice, telekit.Handler):
|
|
455
|
+
PAGINATED_CHOICE_BACK_LABEL = "⬅️ Назад"
|
|
456
|
+
PAGINATED_CHOICE_NEXT_LABEL = "Далі ➡️"
|
|
457
|
+
PAGINATED_CHOICE_PAGE_LABEL = None # hide page indicator
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
<details>
|
|
461
|
+
<summary>(Click to see the result)</summary>
|
|
462
|
+
<table>
|
|
463
|
+
<tr>
|
|
464
|
+
<td><img src="https://github.com/Romashkaa/telekit/blob/main/docs/images/paginated_choice.png?raw=true" alt="Example" width="300"></td>
|
|
465
|
+
</tr>
|
|
466
|
+
</table>
|
|
467
|
+
</details>
|
|
468
|
+
|
|
469
|
+
`choices` accepts a `dict[str, T]`, or any `Iterable[T]`.
|
|
470
|
+
If only one item is present, `on_choice` is called immediately without rendering a keyboard.
|
|
471
|
+
|
|
472
|
+
## Utils
|
|
473
|
+
|
|
474
|
+
| **Name** | **Description** |
|
|
475
|
+
| ------------------ | ------------------------------------------------------------------ |
|
|
476
|
+
| `load_env` | Load all key-value pairs from a `.env` file into a dictionary. |
|
|
477
|
+
| `read_env_var` | Read a single variable by name from a `.env` file. |
|
|
478
|
+
| `compose_keyboard` | Merge multiple button groups into a single inline keyboard with computed `row_width`. |
|
|
479
|
+
|
|
480
|
+
### Environment Utils
|
|
481
|
+
|
|
482
|
+
- `read_token` and `read_canvas_path` now support reading from `.env` files.
|
|
483
|
+
Pass `".env"` to use the default key, or `".env:KEY"` to specify a custom one:
|
|
484
|
+
|
|
485
|
+
```python
|
|
486
|
+
read_token(".env") # reads TOKEN
|
|
487
|
+
read_token(".env:BOT_TOKEN") # reads BOT_TOKEN
|
|
488
|
+
|
|
489
|
+
read_canvas_path(".env") # reads CANVAS_PATH
|
|
490
|
+
read_canvas_path(".env:MY_CANVAS") # reads MY_CANVAS
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Inline Keyboard Utils
|
|
494
|
+
|
|
495
|
+
`compose_keyboard` combines multiple button groups into a single keyboard and automatically calculates `row_width` for each group.
|
|
496
|
+
|
|
497
|
+
Each group is laid out independently using its corresponding width from `widths`.
|
|
498
|
+
A width of `-1` means "all buttons in one row" (i.e. ``len(group)``).
|
|
499
|
+
|
|
500
|
+
| **Param** | **Type** | **Description** |
|
|
501
|
+
|-----------|------------------|---------------------------------------------|
|
|
502
|
+
| `groups` | `dict[str, Any]` | One or more button groups |
|
|
503
|
+
| `widths` | `Iterable[int]` | Row width per group or single value for all |
|
|
504
|
+
|
|
505
|
+
| **Returns** | **Type** |
|
|
506
|
+
|------------|----------|
|
|
507
|
+
| `keyboard` | `dict[str, Any]` |
|
|
508
|
+
| `row_width` | `tuple[int, ...]` |
|
|
509
|
+
|
|
510
|
+
```py
|
|
511
|
+
# row_width → (1, 3, 3, 3, 2)
|
|
512
|
+
# layout:
|
|
513
|
+
# | 🆕 Create |
|
|
514
|
+
# | 1 | 2 | 3 |
|
|
515
|
+
# | 4 | 5 | 6 |
|
|
516
|
+
# | 7 | 8 | 9 |
|
|
517
|
+
# | « Back | Next » |
|
|
518
|
+
|
|
519
|
+
keyboard, row_width = compose_keyboard(
|
|
520
|
+
{"🆕 Create": "create"},
|
|
521
|
+
{str(n): str(n) for n in range(1, 10)},
|
|
522
|
+
{"« Back": "back", "Next »": "next"},
|
|
523
|
+
widths=(-1, 3, -1), # or (1, 3, 2)
|
|
524
|
+
)
|
|
525
|
+
self.chain.set_inline_choice(keyboard, row_width)
|
|
526
|
+
```
|
|
@@ -65,7 +65,8 @@ Even in its beta stage, Telekit accelerates bot development, offering typed comm
|
|
|
65
65
|
- [Dialogue](https://github.com/Romashkaa/telekit/blob/main/docs/examples/dialogue.md)
|
|
66
66
|
- [Risk Game](https://github.com/Romashkaa/telekit/blob/main/docs/examples/risk_game.md)
|
|
67
67
|
- [Counter](https://github.com/Romashkaa/telekit/blob/main/docs/examples/counter.md)
|
|
68
|
-
- [Quiz (
|
|
68
|
+
- [Quiz (DSL)](https://github.com/Romashkaa/telekit/blob/main/docs/examples/quiz.md)
|
|
69
|
+
- [Hotel (DSL)](https://github.com/Romashkaa/telekit/blob/main/docs/examples/complete_hotel.md)
|
|
69
70
|
- [More...](https://github.com/Romashkaa/telekit/blob/main/docs/examples/examples.md)
|
|
70
71
|
|
|
71
72
|
## Overview
|
|
@@ -35,12 +35,14 @@ from . import parameters
|
|
|
35
35
|
from . import inline_buttons
|
|
36
36
|
from . import dices
|
|
37
37
|
from . import utils
|
|
38
|
+
from . import traits
|
|
38
39
|
|
|
39
40
|
Styles = styles.Styles
|
|
40
41
|
|
|
41
42
|
from ._version import __version__
|
|
42
43
|
|
|
43
44
|
__all__ = [
|
|
45
|
+
"traits",
|
|
44
46
|
"utils",
|
|
45
47
|
"senders",
|
|
46
48
|
"types",
|
|
@@ -40,15 +40,17 @@ class CallbackQueryHandler:
|
|
|
40
40
|
bot (TeleBot): The Telegram bot instance to be used for sending messages.
|
|
41
41
|
"""
|
|
42
42
|
cls.bot = bot
|
|
43
|
-
cls.user_button_callbacks: dict[int, dict[str, Callable[[
|
|
43
|
+
cls.user_button_callbacks: dict[int, dict[str, Callable[[CallbackQuery], None]]] = {}
|
|
44
44
|
|
|
45
45
|
@bot.callback_query_handler(func=lambda call: True)
|
|
46
|
-
def handle(call:
|
|
46
|
+
def handle(call: CallbackQuery) -> None:
|
|
47
47
|
if not call.data:
|
|
48
48
|
return
|
|
49
49
|
|
|
50
50
|
if call.data.startswith(cls.INLINE_BUTTON):
|
|
51
51
|
cls._handle_inline_button(call)
|
|
52
|
+
elif call.data.startswith(cls.STATIC_BUTTON):
|
|
53
|
+
cls._handle_static_button(call)
|
|
52
54
|
elif call.data.startswith(cls.SUGGEST):
|
|
53
55
|
cls._handle_suggestion(call)
|
|
54
56
|
else:
|
|
@@ -59,7 +61,7 @@ class CallbackQueryHandler:
|
|
|
59
61
|
# –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
60
62
|
|
|
61
63
|
@classmethod
|
|
62
|
-
def _handle_inline_button(cls, call:
|
|
64
|
+
def _handle_inline_button(cls, call: CallbackQuery):
|
|
63
65
|
if not call.data:
|
|
64
66
|
cls.bot.answer_callback_query(call.id, text=cls._invalid_data_answer[0], show_alert=cls._invalid_data_answer[1])
|
|
65
67
|
return
|
|
@@ -76,10 +78,19 @@ class CallbackQueryHandler:
|
|
|
76
78
|
cls.bot.answer_callback_query(call.id, text=cls._button_is_no_active_answer[0], show_alert=cls._button_is_no_active_answer[1])
|
|
77
79
|
return
|
|
78
80
|
|
|
79
|
-
|
|
81
|
+
if not getattr(callback, "_persistent", False):
|
|
82
|
+
cls.remove_user_button_callbacks(call.from_user.id)
|
|
80
83
|
|
|
81
84
|
callback(call)
|
|
82
85
|
|
|
86
|
+
# –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
87
|
+
# Static Buttons Handling
|
|
88
|
+
# –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def _handle_static_button(cls, call: CallbackQuery):
|
|
92
|
+
cls.bot.answer_callback_query(call.id, text="")
|
|
93
|
+
|
|
83
94
|
# –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
84
95
|
# Suggestion Handling
|
|
85
96
|
# –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
@@ -87,7 +98,7 @@ class CallbackQueryHandler:
|
|
|
87
98
|
@classmethod
|
|
88
99
|
def _handle_suggestion(cls, call: CallbackQuery):
|
|
89
100
|
text: str = str(call.data)[len(cls.SUGGEST):]
|
|
90
|
-
cls.simulate(call.message, text, from_user=call.from_user)
|
|
101
|
+
cls.simulate(call.message, text, from_user=call.from_user) # pyright: ignore[reportArgumentType]
|
|
91
102
|
cls.bot.answer_callback_query(call.id)
|
|
92
103
|
|
|
93
104
|
@classmethod
|
|
@@ -126,9 +137,10 @@ class CallbackQueryHandler:
|
|
|
126
137
|
# Query Types
|
|
127
138
|
# –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
128
139
|
|
|
140
|
+
STATIC_BUTTON: str = "static_button"
|
|
129
141
|
INLINE_BUTTON: str = "inline_button:"
|
|
130
142
|
SUGGEST: str = "suggest:"
|
|
131
|
-
|
|
143
|
+
|
|
132
144
|
@classmethod
|
|
133
145
|
def suggest(cls, suggestion: str):
|
|
134
146
|
return f"{cls.SUGGEST}{suggestion}"
|
|
@@ -40,8 +40,7 @@ if typing.TYPE_CHECKING:
|
|
|
40
40
|
class ChainInlineKeyboardLogic(ChainBase):
|
|
41
41
|
def set_inline_keyboard(
|
|
42
42
|
self,
|
|
43
|
-
keyboard: dict[str,
|
|
44
|
-
*,
|
|
43
|
+
keyboard: dict[str, Any],
|
|
45
44
|
row_width: int | Iterable[int] = 1
|
|
46
45
|
) -> None:
|
|
47
46
|
"""
|
|
@@ -118,7 +117,6 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
118
117
|
def inline_keyboard[Caption: str, Value](
|
|
119
118
|
self,
|
|
120
119
|
keyboard: dict[Caption, Value],
|
|
121
|
-
*,
|
|
122
120
|
row_width: int | Iterable[int] = 1,
|
|
123
121
|
enable_special_buttons: bool = True
|
|
124
122
|
) -> Callable[[Callable[[Value], None]], None]:
|
|
@@ -183,7 +181,6 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
183
181
|
def inline_choice(
|
|
184
182
|
self,
|
|
185
183
|
choices: list[Any] | tuple[Any, ...] | dict[str, Any | InlineButton],
|
|
186
|
-
*,
|
|
187
184
|
row_width: int | Iterable[int] = 1,
|
|
188
185
|
enable_special_buttons: bool = True
|
|
189
186
|
) -> Callable[[Callable[[Any], None]], None]:
|
|
@@ -219,7 +216,6 @@ class ChainInlineKeyboardLogic(ChainBase):
|
|
|
219
216
|
self,
|
|
220
217
|
func: Callable[[Any], None],
|
|
221
218
|
choices: list[Any] | tuple[Any, ...] | dict[str, Any | InlineButton],
|
|
222
|
-
*,
|
|
223
219
|
row_width: int | Iterable[int] = 1,
|
|
224
220
|
enable_special_buttons: bool = True
|
|
225
221
|
) -> None:
|
|
@@ -225,9 +225,16 @@ class Handler:
|
|
|
225
225
|
|
|
226
226
|
handler_instance = handler(self.message)
|
|
227
227
|
handler_instance.chain._set_previous_message(self.chain.get_previous_message())
|
|
228
|
+
handler_instance._on_handoff(self)
|
|
228
229
|
|
|
229
230
|
return handler_instance
|
|
230
231
|
|
|
232
|
+
|
|
233
|
+
def _on_handoff(self, origin: "Handler") -> None:
|
|
234
|
+
"""Called when this handler is reached via handoff(). Override to customize."""
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
|
|
231
238
|
def freeze(self, func, *args):
|
|
232
239
|
"""
|
|
233
240
|
Return a zero-argument callback that invokes the given function
|
|
@@ -27,13 +27,15 @@ from telebot import TeleBot
|
|
|
27
27
|
from ._callback_query_handler import CallbackQueryHandler
|
|
28
28
|
|
|
29
29
|
__all__ = [
|
|
30
|
-
"InlineButton",
|
|
30
|
+
"InlineButton",
|
|
31
31
|
|
|
32
|
+
"StaticButton",
|
|
32
33
|
"LinkButton",
|
|
33
34
|
"WebAppButton",
|
|
34
35
|
"SuggestButton",
|
|
35
36
|
"CopyTextButton",
|
|
36
37
|
"CallbackButton",
|
|
38
|
+
"AnswerButton",
|
|
37
39
|
"AlertButton",
|
|
38
40
|
"NotificationButton",
|
|
39
41
|
"InvokeButton",
|
|
@@ -57,9 +59,11 @@ class InlineButton:
|
|
|
57
59
|
- `SuggestButton`
|
|
58
60
|
- `CopyTextButton`
|
|
59
61
|
- `CallbackButton`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
- `InvokeButton`
|
|
63
|
+
- `AnswerButton`
|
|
64
|
+
- `AlertButton`
|
|
65
|
+
- `NotificationButton`
|
|
66
|
+
- `StaticButton`
|
|
63
67
|
"""
|
|
64
68
|
|
|
65
69
|
_bot: TeleBot
|
|
@@ -70,12 +74,14 @@ class InlineButton:
|
|
|
70
74
|
|
|
71
75
|
MAX_SIZE: int | None = None
|
|
72
76
|
MIN_SIZE: int | None = None
|
|
73
|
-
|
|
77
|
+
|
|
78
|
+
Static: type["StaticButton"]
|
|
74
79
|
Link: type["LinkButton"]
|
|
75
80
|
WebApp: type["WebAppButton"]
|
|
76
81
|
Suggest: type["SuggestButton"]
|
|
77
82
|
CopyText: type["CopyTextButton"]
|
|
78
83
|
Callback: type["CallbackButton"]
|
|
84
|
+
Answer: type["AnswerButton"]
|
|
79
85
|
Alert: type["AlertButton"]
|
|
80
86
|
Notification: type["NotificationButton"]
|
|
81
87
|
Invoke: type["InvokeButton"]
|
|
@@ -115,7 +121,20 @@ class InlineButton:
|
|
|
115
121
|
return normalized
|
|
116
122
|
raise ValueError(f"Unknown style: {style!r}. Must be one of {_BUTTON_STYLES_LIST}")
|
|
117
123
|
raise TypeError(f"Style must be str, ButtonStyle, or None, got {type(style)}")
|
|
124
|
+
|
|
125
|
+
class StaticButton(InlineButton):
|
|
118
126
|
|
|
127
|
+
def __init__(self, *, style: str | None | ButtonStyle = None, **kwargs):
|
|
128
|
+
self._style = self._normalize_style(style)
|
|
129
|
+
self._kwargs = kwargs
|
|
130
|
+
|
|
131
|
+
def _compile(self, caption: str) -> InlineKeyboardButton:
|
|
132
|
+
return InlineKeyboardButton(
|
|
133
|
+
text=caption,
|
|
134
|
+
callback_data=CallbackQueryHandler.STATIC_BUTTON,
|
|
135
|
+
style=self._style,
|
|
136
|
+
**self._kwargs,
|
|
137
|
+
)
|
|
119
138
|
|
|
120
139
|
class LinkButton(InlineButton):
|
|
121
140
|
"""
|
|
@@ -339,8 +358,8 @@ class CallbackButton(InlineButton):
|
|
|
339
358
|
|
|
340
359
|
def __call__(self, call: CallbackQuery):
|
|
341
360
|
self._invoke_chain_callback()
|
|
342
|
-
self._invoke_callback()
|
|
343
361
|
self._answer_callback_query(call)
|
|
362
|
+
self._invoke_callback()
|
|
344
363
|
|
|
345
364
|
def __init__(
|
|
346
365
|
self,
|
|
@@ -387,7 +406,77 @@ class CallbackButton(InlineButton):
|
|
|
387
406
|
callback=callback,
|
|
388
407
|
)
|
|
389
408
|
|
|
390
|
-
class
|
|
409
|
+
class AnswerButton(CallbackButton):
|
|
410
|
+
|
|
411
|
+
class _CallbackInvoker(CallbackButton._CallbackInvoker):
|
|
412
|
+
def __init__(
|
|
413
|
+
self,
|
|
414
|
+
chain_callback: Callable[[], None],
|
|
415
|
+
|
|
416
|
+
answer_text: str | None = None,
|
|
417
|
+
answer_as_alert: bool = True,
|
|
418
|
+
|
|
419
|
+
persistent: bool = True,
|
|
420
|
+
|
|
421
|
+
style: str | None | ButtonStyle = None,
|
|
422
|
+
|
|
423
|
+
kwargs: dict[str, Any] = {}
|
|
424
|
+
):
|
|
425
|
+
self._answer_text = answer_text
|
|
426
|
+
self._answer_as_alert = answer_as_alert
|
|
427
|
+
|
|
428
|
+
self._persistent = persistent
|
|
429
|
+
|
|
430
|
+
self._style = style
|
|
431
|
+
|
|
432
|
+
self._kwargs = {"style": style} | kwargs
|
|
433
|
+
|
|
434
|
+
self._chain_callback: Callable[[], None] = chain_callback
|
|
435
|
+
|
|
436
|
+
def _answer_callback_query(self, call: CallbackQuery):
|
|
437
|
+
if self._answer_text:
|
|
438
|
+
InlineButton._bot.answer_callback_query(
|
|
439
|
+
call.id,
|
|
440
|
+
self._answer_text,
|
|
441
|
+
self._answer_as_alert
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
def __call__(self, call: CallbackQuery):
|
|
445
|
+
if not self._persistent:
|
|
446
|
+
self._invoke_chain_callback()
|
|
447
|
+
self._answer_callback_query(call)
|
|
448
|
+
|
|
449
|
+
def __init__(
|
|
450
|
+
self,
|
|
451
|
+
answer_text: str | None = None,
|
|
452
|
+
answer_as_alert: bool = True,
|
|
453
|
+
|
|
454
|
+
persistent: bool = True,
|
|
455
|
+
|
|
456
|
+
style: str | None | ButtonStyle = None,
|
|
457
|
+
|
|
458
|
+
**kwargs
|
|
459
|
+
):
|
|
460
|
+
self._answer_text = answer_text
|
|
461
|
+
self._answer_as_alert = answer_as_alert
|
|
462
|
+
|
|
463
|
+
self._persistent = persistent
|
|
464
|
+
|
|
465
|
+
self._style = self._normalize_style(style)
|
|
466
|
+
|
|
467
|
+
self._kwargs = kwargs
|
|
468
|
+
|
|
469
|
+
def build_invoker(self, chain_callback: Callable[[], None]) -> _CallbackInvoker:
|
|
470
|
+
return self._CallbackInvoker(
|
|
471
|
+
chain_callback=chain_callback,
|
|
472
|
+
answer_text=self._answer_text,
|
|
473
|
+
answer_as_alert=self._answer_as_alert,
|
|
474
|
+
persistent=self._persistent,
|
|
475
|
+
style=self._style,
|
|
476
|
+
kwargs=self._kwargs
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
class AlertButton(AnswerButton):
|
|
391
480
|
"""
|
|
392
481
|
An inline keyboard button that shows a popup alert when pressed and terminates the chain.
|
|
393
482
|
|
|
@@ -424,19 +513,19 @@ class AlertButton(CallbackButton):
|
|
|
424
513
|
def __init__(
|
|
425
514
|
self,
|
|
426
515
|
text: str | None = None,
|
|
427
|
-
|
|
516
|
+
persistent: bool = True,
|
|
428
517
|
style: str | None | ButtonStyle = None,
|
|
429
518
|
**kwargs
|
|
430
519
|
):
|
|
431
520
|
super().__init__(
|
|
432
|
-
callback=None,
|
|
433
521
|
answer_text=text,
|
|
434
522
|
answer_as_alert=True,
|
|
523
|
+
persistent=persistent,
|
|
435
524
|
style=style,
|
|
436
525
|
**kwargs
|
|
437
526
|
)
|
|
438
527
|
|
|
439
|
-
class NotificationButton(
|
|
528
|
+
class NotificationButton(AnswerButton):
|
|
440
529
|
"""
|
|
441
530
|
An inline keyboard button that shows a brief notification at the top of the chat screen
|
|
442
531
|
when pressed and terminates the chain.
|
|
@@ -475,13 +564,14 @@ class NotificationButton(CallbackButton):
|
|
|
475
564
|
self,
|
|
476
565
|
text: str | None = None,
|
|
477
566
|
*,
|
|
567
|
+
persistent: bool = True,
|
|
478
568
|
style: str | None | ButtonStyle = None,
|
|
479
569
|
**kwargs
|
|
480
570
|
):
|
|
481
571
|
super().__init__(
|
|
482
|
-
callback=None,
|
|
483
572
|
answer_text=text,
|
|
484
573
|
answer_as_alert=False,
|
|
574
|
+
persistent=persistent,
|
|
485
575
|
style=style,
|
|
486
576
|
**kwargs
|
|
487
577
|
)
|
|
@@ -611,10 +701,12 @@ class InvokeButton(CallbackButton):
|
|
|
611
701
|
)
|
|
612
702
|
|
|
613
703
|
InlineButton.Link = LinkButton
|
|
704
|
+
InlineButton.Static = StaticButton
|
|
614
705
|
InlineButton.WebApp = WebAppButton
|
|
615
706
|
InlineButton.Suggest = SuggestButton
|
|
616
707
|
InlineButton.CopyText = CopyTextButton
|
|
617
708
|
InlineButton.Callback = CallbackButton
|
|
709
|
+
InlineButton.Answer = AnswerButton
|
|
618
710
|
InlineButton.Alert = AlertButton
|
|
619
711
|
InlineButton.Notification = NotificationButton
|
|
620
712
|
InlineButton.Invoke = InvokeButton
|
|
@@ -44,8 +44,12 @@ class EntryHandler(telekit.Handler):
|
|
|
44
44
|
# Handling Logic
|
|
45
45
|
# ------------------------------------------
|
|
46
46
|
|
|
47
|
+
def __init__(self, *args, **kwargs):
|
|
48
|
+
super().__init__(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
self._user_data = UserData(self.user.id)
|
|
51
|
+
|
|
47
52
|
def handle(self) -> None:
|
|
48
|
-
self._user_data = UserData(self.message.chat.id)
|
|
49
53
|
self.chain.disable_timeout_warnings()
|
|
50
54
|
self.entry_name()
|
|
51
55
|
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import telekit
|
|
2
2
|
|
|
3
|
-
from telekit.inline_buttons import
|
|
3
|
+
from telekit.inline_buttons import AlertButton
|
|
4
4
|
|
|
5
5
|
spells_text = """
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
The Disarming Charm. It causes whatever the victim is holding to fly out of their hand. It became Harry Potter's signature spell.
|
|
9
|
-
|
|
10
|
-
# Wingardium Leviosa
|
|
6
|
+
# 🦋 Wingardium Leviosa
|
|
11
7
|
|
|
12
8
|
The Levitation Charm. Used to make objects fly. As Hermione Granger famously noted, it's "Levi-o-sa, not Levio-sar."
|
|
13
9
|
|
|
14
|
-
# Expecto Patronum
|
|
10
|
+
# 🧌 Expecto Patronum
|
|
15
11
|
|
|
16
12
|
The Patronus Charm. A highly advanced spell that conjures a silver guardian to protect the caster against Dementors.
|
|
17
13
|
|
|
18
|
-
# Alohomora
|
|
14
|
+
# 🗝️ Alohomora
|
|
19
15
|
|
|
20
16
|
The Unlocking Charm. Used to open doors and windows that are not protected by magic.
|
|
21
17
|
|
|
22
|
-
# Lumos
|
|
18
|
+
# 🪄 Lumos
|
|
23
19
|
|
|
24
20
|
The Wand-Lighting Charm. Illuminates the tip of the caster's wand, allowing them to see in the dark.
|
|
25
21
|
"""
|
|
@@ -48,11 +44,14 @@ class SpellsHandler(telekit.Handler):
|
|
|
48
44
|
|
|
49
45
|
self.chain.set_inline_keyboard(
|
|
50
46
|
{
|
|
51
|
-
title:
|
|
47
|
+
title: AlertButton(content)
|
|
52
48
|
for title, content in spells.items()
|
|
53
|
-
}
|
|
49
|
+
} | {"« Back": self.handle_back},
|
|
50
|
+
row_width=2
|
|
54
51
|
)
|
|
55
|
-
self.chain.set_remove_inline_keyboard(False)
|
|
56
52
|
|
|
57
53
|
self.chain.disable_timeout_warnings()
|
|
58
|
-
self.chain.edit()
|
|
54
|
+
self.chain.edit()
|
|
55
|
+
|
|
56
|
+
def handle_back(self):
|
|
57
|
+
self.handoff("StartHandler").handle()
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
from telebot.types import Message
|
|
2
1
|
from telekit.types import (
|
|
3
2
|
TextDocument, CopyTextButton, ParseMode
|
|
4
3
|
)
|
|
5
4
|
from telekit.styles import Quote, Escape
|
|
6
5
|
import telekit
|
|
7
6
|
|
|
8
|
-
class TextDocumentHandler(telekit.Handler):
|
|
7
|
+
class TextDocumentHandler(telekit.traits.TrackHandoffOrigin, telekit.Handler):
|
|
9
8
|
|
|
10
9
|
@classmethod
|
|
11
10
|
def init_handler(cls) -> None:
|
|
@@ -26,6 +25,11 @@ class TextDocumentHandler(telekit.Handler):
|
|
|
26
25
|
self.handle_text_document,
|
|
27
26
|
allowed_extensions=(".txt", ".py", ".md", ".html")
|
|
28
27
|
)
|
|
28
|
+
self.chain.set_inline_keyboard(
|
|
29
|
+
{
|
|
30
|
+
"« Back": self.handoff_back_or("StartHandler")
|
|
31
|
+
}
|
|
32
|
+
)
|
|
29
33
|
|
|
30
34
|
self.chain.disable_timeout_warnings()
|
|
31
35
|
self.chain.edit()
|