telekit 2.0.1__tar.gz → 2.1.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.0.1/telekit.egg-info → telekit-2.1.0}/PKG-INFO +19 -67
- {telekit-2.0.1 → telekit-2.1.0}/README.md +1 -1
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_buildtext/styles.py +4 -22
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_on.py +84 -70
- telekit-2.1.0/telekit/_version.py +1 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/__init__.py +2 -1
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/on_text.py +2 -0
- telekit-2.1.0/telekit/example/example_handlers/qr.py +86 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/start.py +5 -1
- {telekit-2.0.1 → telekit-2.1.0}/telekit/utils.py +141 -1
- {telekit-2.0.1 → telekit-2.1.0/telekit.egg-info}/PKG-INFO +19 -67
- {telekit-2.0.1 → telekit-2.1.0}/telekit.egg-info/SOURCES.txt +1 -0
- telekit-2.0.1/telekit/_version.py +0 -1
- {telekit-2.0.1 → telekit-2.1.0}/LICENSE +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/setup.cfg +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/setup.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_buildtext/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_buildtext/formatter.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_callback_query_handler.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_chain.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_chain_base.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_chain_entry_logic.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_chain_inline_keyboards_logic.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_chapters/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_chapters/chapters.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_handler.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_init.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_inline_buttons.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_input_handler.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_logger.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_snapvault/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_snapvault/snapcode.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_snapvault/snapvault.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_state.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/mixin.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/builder.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/canvas_parser.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/lexer.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/nodes.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/parser.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/parser/token.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/telekit_dsl.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_telekit_dsl/telekit_orm.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_timeout.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/_user.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/dices.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/__init__.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/complete_hotel.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/counter.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/dsl.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/entry.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/faq.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/hotel.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/pages.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/pyapi.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/quiz.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/spells.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_handlers/text_document.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/example/example_server.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/inline_buttons.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/parameters.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/senders.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/server.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/styles.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit/types.py +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit.egg-info/dependency_links.txt +0 -0
- {telekit-2.0.1 → telekit-2.1.0}/telekit.egg-info/requires.txt +0 -0
- {telekit-2.0.1 → telekit-2.1.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.0
|
|
3
|
+
Version: 2.1.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
|
|
@@ -62,7 +62,7 @@ telekit.Server("BOT_TOKEN").polling()
|
|
|
62
62
|
|
|
63
63
|
> Send "Hello!" with a photo on `/start`
|
|
64
64
|
|
|
65
|
-
Telekit comes with a [built-in DSL](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial/11_telekit_dsl.md), allowing developers to create fully interactive bots with minimal code. It also integrates **Jinja
|
|
65
|
+
Telekit comes with a [built-in DSL](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial/11_telekit_dsl.md), allowing developers to create fully interactive bots with minimal code. It also integrates [**Jinja**](https://github.com/Romashkaa/telekit/blob/main/docs/examples/jinja_engine.md), giving you loops, conditionals, expressions, and filters to generate dynamic content.
|
|
66
66
|
|
|
67
67
|
```js
|
|
68
68
|
@ main {
|
|
@@ -378,68 +378,20 @@ It tries to make Telegram bot development easier.
|
|
|
378
378
|
|
|
379
379
|
---
|
|
380
380
|
|
|
381
|
-
# Changes in version 2.0
|
|
382
|
-
|
|
383
|
-
##
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
- Added `utils.telegramify_markdown()` — convenience pipeline: adapt + sanitize
|
|
399
|
-
- `StyleFormatter` renamed to `TextEntity`
|
|
400
|
-
|
|
401
|
-
## Inline Keyboards
|
|
402
|
-
|
|
403
|
-
Inline keyboards got a proper type system. Previously, any string value in the keyboard dict was treated as a link. Now every button type is explicit.
|
|
404
|
-
|
|
405
|
-
- Introduced `InlineButton` base class with subclasses:
|
|
406
|
-
`LinkButton`, `WebAppButton`, `SuggestButton`, `CopyTextButton`, `CallbackButton`
|
|
407
|
-
- `CallbackButton` — pass arguments to callbacks and set visual style
|
|
408
|
-
- Added `ButtonStyle` enum — style your inline buttons (Bot API 9.4)
|
|
409
|
-
- Added `inline_choice` / `set_inline_choice` methods
|
|
410
|
-
- `set_entry_suggestions` now warns when Telegram Bot API limits are exceeded
|
|
411
|
-
- It's no longer pass `Message` object to callbacks
|
|
412
|
-
- Reduced closure size and improved callback performance
|
|
413
|
-
|
|
414
|
-
## Chain Improvements
|
|
415
|
-
- Added `set_break_only_on_match()` — control whether chain interrupts only on valid handler match, not random input
|
|
416
|
-
- Added `set_break_on_commands()` — control whether commands take priority over entry handlers
|
|
417
|
-
- Improved file extension validation via **pathlib**
|
|
418
|
-
- `entry_*` handlers no longer pass `Message` by default — use `include_message=True` to restore
|
|
419
|
-
|
|
420
|
-
## CallbackQueryHandler
|
|
421
|
-
- Callbacks now invoked directly — no virtual message conversion, faster response times
|
|
422
|
-
- Native alert support for expired or inactive buttons
|
|
423
|
-
- Added `set_invalid_data_answer()` and `set_button_is_no_active_answer()` for global customization
|
|
424
|
-
|
|
425
|
-
## DSL Improvements
|
|
426
|
-
- `jinja_env` is now private — use `get_jinja_env()` instead
|
|
427
|
-
- `TelekitDSL` class:
|
|
428
|
-
- All `from_*` factory methods now use `type()` for class creation
|
|
429
|
-
|
|
430
|
-
## Fixes
|
|
431
|
-
- Fixed one-time trigger bug — triggers like `on.text` now fire reliably across multiple messages while a chain timeout is active
|
|
432
|
-
|
|
433
|
-
## Other
|
|
434
|
-
- Added `utils.read_token()` and `utils.read_canvas_path()` — clean helpers for reading credentials from files
|
|
435
|
-
- Added `utils.format_file_size()`
|
|
436
|
-
- Added `set_license_text` to senders
|
|
437
|
-
- Added `format_size` to `TextDocument`
|
|
438
|
-
- Added `TelekitState` class
|
|
439
|
-
- Bumped `pyTelegramBotAPI` to **v4.31.0**
|
|
440
|
-
- Added GPL v3 License
|
|
441
|
-
|
|
442
|
-
## 🚀 Upcoming
|
|
443
|
-
|
|
444
|
-
- New triggers: `document`, `text_document`
|
|
445
|
-
- Telekit DSL: shorter notation and button attributes with style, alert, ... support
|
|
381
|
+
# Changes in version 2.1.0
|
|
382
|
+
|
|
383
|
+
## Trigger Improvements
|
|
384
|
+
|
|
385
|
+
| **Name** | **Description** |
|
|
386
|
+
|----------|-----------------|
|
|
387
|
+
| `on.text` | *BugFix:* Commands are no longer matched as text triggers. |
|
|
388
|
+
|
|
389
|
+
- Internal refactor of the `On` class.
|
|
390
|
+
|
|
391
|
+
## New Utils
|
|
392
|
+
|
|
393
|
+
| **Name** | **Description** |
|
|
394
|
+
|----------|-----------------|
|
|
395
|
+
| `make_bot_link` | Builds a `t.me` URL to a bot with an optional deep-link payload. |
|
|
396
|
+
| `make_user_link` | Builds a `t.me` URL to a user with an optional pre-filled message. |
|
|
397
|
+
| `make_qrcode` | Generates a [QR code](./docs/examples/qr_gen.md) URL from any text or link. |
|
|
@@ -26,7 +26,7 @@ telekit.Server("BOT_TOKEN").polling()
|
|
|
26
26
|
|
|
27
27
|
> Send "Hello!" with a photo on `/start`
|
|
28
28
|
|
|
29
|
-
Telekit comes with a [built-in DSL](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial/11_telekit_dsl.md), allowing developers to create fully interactive bots with minimal code. It also integrates **Jinja
|
|
29
|
+
Telekit comes with a [built-in DSL](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial/11_telekit_dsl.md), allowing developers to create fully interactive bots with minimal code. It also integrates [**Jinja**](https://github.com/Romashkaa/telekit/blob/main/docs/examples/jinja_engine.md), giving you loops, conditionals, expressions, and filters to generate dynamic content.
|
|
30
30
|
|
|
31
31
|
```js
|
|
32
32
|
@ main {
|
|
@@ -21,6 +21,8 @@ from urllib.parse import quote
|
|
|
21
21
|
|
|
22
22
|
import telebot.formatting
|
|
23
23
|
|
|
24
|
+
import telekit.utils
|
|
25
|
+
|
|
24
26
|
from .formatter import TextEntity, EasyTextEntity, StaticTextEntity, EasyTextEntityWithPostRender, Group
|
|
25
27
|
|
|
26
28
|
|
|
@@ -516,20 +518,10 @@ class UserLink(Link):
|
|
|
516
518
|
`Documentation <https://github.com/Romashkaa/telekit/blob/main/docs/tutorial2/6_styles.md>`_ · on GitHub
|
|
517
519
|
"""
|
|
518
520
|
def __init__(self, *content, username: str, text: str | None = None, escape: bool = True, sep: Union["TextEntity", str] = ""):
|
|
519
|
-
url: str =
|
|
521
|
+
url: str = telekit.utils.make_user_link(username, text)
|
|
520
522
|
|
|
521
523
|
super().__init__(*content, url=url, escape=escape, sep=sep)
|
|
522
524
|
|
|
523
|
-
@staticmethod
|
|
524
|
-
def gen_link(username: str, text: str | None = None) -> str:
|
|
525
|
-
username = username.lstrip("@")
|
|
526
|
-
|
|
527
|
-
if text is None:
|
|
528
|
-
return f"https://t.me/{username}"
|
|
529
|
-
else:
|
|
530
|
-
encoded_text: str = quote(text, safe="")
|
|
531
|
-
return f"https://t.me/{username}?text={encoded_text}"
|
|
532
|
-
|
|
533
525
|
|
|
534
526
|
class BotLink(Link):
|
|
535
527
|
"""
|
|
@@ -559,20 +551,10 @@ class BotLink(Link):
|
|
|
559
551
|
"""
|
|
560
552
|
|
|
561
553
|
def __init__(self, *content, username: str, start: str | None = None, escape: bool = True, sep: Union["TextEntity", str] = ""):
|
|
562
|
-
url: str =
|
|
554
|
+
url: str = telekit.utils.make_bot_link(username, start)
|
|
563
555
|
|
|
564
556
|
super().__init__(*content, url=url, escape=escape, sep=sep)
|
|
565
557
|
|
|
566
|
-
@staticmethod
|
|
567
|
-
def gen_link(username: str, start: str | None = None) -> str:
|
|
568
|
-
username = username.lstrip("@")
|
|
569
|
-
|
|
570
|
-
if start is None:
|
|
571
|
-
return f"https://t.me/{username}"
|
|
572
|
-
else:
|
|
573
|
-
encoded_start = quote(start, safe="")
|
|
574
|
-
return f"https://t.me/{username}?start={encoded_start}"
|
|
575
|
-
|
|
576
558
|
class EncodeURL(StaticTextEntity):
|
|
577
559
|
"""
|
|
578
560
|
URL-encodes the provided content string.
|
|
@@ -23,6 +23,7 @@ import shlex
|
|
|
23
23
|
import re
|
|
24
24
|
|
|
25
25
|
import telebot
|
|
26
|
+
import telebot.types
|
|
26
27
|
|
|
27
28
|
from . import parameters
|
|
28
29
|
from ._logger import logger
|
|
@@ -32,7 +33,7 @@ if typing.TYPE_CHECKING:
|
|
|
32
33
|
from telekit._handler import Handler # only for type hints
|
|
33
34
|
|
|
34
35
|
# --------------------------------------------------------
|
|
35
|
-
#
|
|
36
|
+
# Invoker
|
|
36
37
|
# --------------------------------------------------------
|
|
37
38
|
|
|
38
39
|
class Invoker:
|
|
@@ -40,12 +41,11 @@ class Invoker:
|
|
|
40
41
|
• [See Documentation on GitHub](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial2/3_triggers.md)
|
|
41
42
|
"""
|
|
42
43
|
|
|
43
|
-
def __init__(self,
|
|
44
|
+
def __init__(self, registrar: Callable[[Callable], Callable | None], handler: type["Handler"]):
|
|
44
45
|
"""
|
|
45
|
-
Wraps
|
|
46
|
-
to add callback/trigger functionality.
|
|
46
|
+
Wraps a registrar to add callback/trigger functionality.
|
|
47
47
|
"""
|
|
48
|
-
self.
|
|
48
|
+
self._register: Callable = registrar
|
|
49
49
|
self._Handler = handler
|
|
50
50
|
|
|
51
51
|
def __call__(self, func: Callable):
|
|
@@ -58,7 +58,7 @@ class Invoker:
|
|
|
58
58
|
cls(message).handle(*args, **kwargs)
|
|
59
59
|
```
|
|
60
60
|
"""
|
|
61
|
-
return self.
|
|
61
|
+
return self._register(func)
|
|
62
62
|
|
|
63
63
|
def invoke(self, callback: Callable, pass_args: bool = True):
|
|
64
64
|
"""
|
|
@@ -74,10 +74,10 @@ class Invoker:
|
|
|
74
74
|
else:
|
|
75
75
|
callback(self._Handler(message))
|
|
76
76
|
|
|
77
|
-
self.
|
|
77
|
+
self._register(handler)
|
|
78
78
|
|
|
79
79
|
# --------------------------------------------------------
|
|
80
|
-
#
|
|
80
|
+
# Triggers
|
|
81
81
|
# --------------------------------------------------------
|
|
82
82
|
|
|
83
83
|
class On:
|
|
@@ -149,7 +149,7 @@ class On:
|
|
|
149
149
|
Returns:
|
|
150
150
|
Invoker: An invoker object allowing `.invoke()` or @decorator-style usage.
|
|
151
151
|
"""
|
|
152
|
-
|
|
152
|
+
register_trigger = self.bot.message_handler(
|
|
153
153
|
commands=commands,
|
|
154
154
|
regexp=regexp,
|
|
155
155
|
func=func,
|
|
@@ -159,18 +159,18 @@ class On:
|
|
|
159
159
|
)
|
|
160
160
|
|
|
161
161
|
def decorator(handler: Callable[..., typing.Any]):
|
|
162
|
-
def
|
|
162
|
+
def trigger(message):
|
|
163
163
|
if whitelist is not None and message.chat.id not in whitelist:
|
|
164
164
|
return
|
|
165
165
|
return handler(message)
|
|
166
166
|
|
|
167
|
-
return
|
|
167
|
+
return register_trigger(trigger)
|
|
168
168
|
|
|
169
169
|
return Invoker(decorator, self.handler)
|
|
170
170
|
|
|
171
171
|
def text(
|
|
172
172
|
self,
|
|
173
|
-
*patterns: str,
|
|
173
|
+
*patterns: str,
|
|
174
174
|
chat_types: list[str] | None = None,
|
|
175
175
|
whitelist: list[int] | None = None
|
|
176
176
|
):
|
|
@@ -225,11 +225,14 @@ class On:
|
|
|
225
225
|
re.IGNORECASE
|
|
226
226
|
))
|
|
227
227
|
|
|
228
|
-
def
|
|
229
|
-
if
|
|
228
|
+
def _filter(message: telebot.types.Message) -> bool:
|
|
229
|
+
if not message.text:
|
|
230
230
|
return False
|
|
231
231
|
|
|
232
|
-
if
|
|
232
|
+
if message.text.startswith("/"):
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
if whitelist is not None and message.chat.id not in whitelist:
|
|
233
236
|
return False
|
|
234
237
|
|
|
235
238
|
for cregex in compiled:
|
|
@@ -239,25 +242,37 @@ class On:
|
|
|
239
242
|
|
|
240
243
|
return False
|
|
241
244
|
|
|
242
|
-
def
|
|
243
|
-
@self.bot.message_handler(func=
|
|
244
|
-
def
|
|
245
|
+
def register(func: Callable): # pyright: ignore[reportRedeclaration]
|
|
246
|
+
@self.bot.message_handler(func=_filter, chat_types=chat_types)
|
|
247
|
+
def trigger(message):
|
|
245
248
|
for cregex in compiled:
|
|
246
249
|
match = cregex.match(message.text)
|
|
247
250
|
if match:
|
|
248
251
|
func(message, **match.groupdict())
|
|
249
252
|
return
|
|
250
|
-
|
|
251
|
-
return
|
|
253
|
+
|
|
254
|
+
return trigger
|
|
252
255
|
else:
|
|
253
|
-
def
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
def _filter(message: telebot.types.Message) -> bool:
|
|
257
|
+
if not message.text:
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
if message.text.startswith("/"):
|
|
261
|
+
return False
|
|
262
|
+
|
|
263
|
+
if whitelist is not None and message.chat.id not in whitelist:
|
|
264
|
+
return False
|
|
265
|
+
|
|
266
|
+
return True
|
|
267
|
+
|
|
268
|
+
def register(func: Callable):
|
|
269
|
+
return self.bot.message_handler(
|
|
270
|
+
content_types=["text"],
|
|
271
|
+
func=_filter,
|
|
272
|
+
chat_types=chat_types
|
|
273
|
+
)(func)
|
|
274
|
+
|
|
275
|
+
return Invoker(register, self.handler)
|
|
261
276
|
|
|
262
277
|
def command(
|
|
263
278
|
self,
|
|
@@ -305,14 +320,13 @@ class On:
|
|
|
305
320
|
Returns:
|
|
306
321
|
Invoker: An invoker object allowing `.invoke()` or decorator-style usage.
|
|
307
322
|
"""
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
def wrapped(message):
|
|
323
|
+
def register(handler: Callable[..., typing.Any]):
|
|
324
|
+
@self.bot.message_handler(
|
|
325
|
+
commands=[c.lstrip("/") for c in commands],
|
|
326
|
+
chat_types=chat_types,
|
|
327
|
+
**kwargs
|
|
328
|
+
)
|
|
329
|
+
def trigger(message):
|
|
316
330
|
if whitelist is not None and message.chat.id not in whitelist:
|
|
317
331
|
return
|
|
318
332
|
if params:
|
|
@@ -320,9 +334,9 @@ class On:
|
|
|
320
334
|
|
|
321
335
|
return handler(message)
|
|
322
336
|
|
|
323
|
-
return
|
|
337
|
+
return trigger
|
|
324
338
|
|
|
325
|
-
return Invoker(
|
|
339
|
+
return Invoker(register, self.handler)
|
|
326
340
|
|
|
327
341
|
def _analyze_params(self, text: str, types: list[parameters.Parameter]) -> list:
|
|
328
342
|
values: list[str] = shlex.split(text)[1:] # skip the command name
|
|
@@ -380,21 +394,20 @@ class On:
|
|
|
380
394
|
Returns:
|
|
381
395
|
Invoker: An invoker object allowing `.invoke()` or decorator-style usage.
|
|
382
396
|
"""
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
def wrapped(message):
|
|
397
|
+
def register(handler: Callable[..., typing.Any]):
|
|
398
|
+
@self.bot.message_handler(
|
|
399
|
+
regexp=regexp,
|
|
400
|
+
chat_types=chat_types,
|
|
401
|
+
**kwargs
|
|
402
|
+
)
|
|
403
|
+
def trigger(message):
|
|
391
404
|
if whitelist is not None and message.chat.id not in whitelist:
|
|
392
405
|
return
|
|
393
406
|
return handler(message)
|
|
394
407
|
|
|
395
|
-
return
|
|
408
|
+
return trigger
|
|
396
409
|
|
|
397
|
-
return Invoker(
|
|
410
|
+
return Invoker(register, self.handler)
|
|
398
411
|
|
|
399
412
|
def photo(
|
|
400
413
|
self,
|
|
@@ -422,8 +435,7 @@ class On:
|
|
|
422
435
|
```
|
|
423
436
|
---
|
|
424
437
|
|
|
425
|
-
Triggers
|
|
426
|
-
- By receiving a photo message (content_type="photo")
|
|
438
|
+
Triggers when receive a photo (content_type="photo")
|
|
427
439
|
|
|
428
440
|
Filters:
|
|
429
441
|
- By chat type(s) (via `chat_types` argument)
|
|
@@ -438,21 +450,20 @@ class On:
|
|
|
438
450
|
Returns:
|
|
439
451
|
Invoker: An invoker object allowing `.invoke()` or decorator-style usage.
|
|
440
452
|
"""
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
def wrapped(message):
|
|
453
|
+
def register(handler: Callable[..., typing.Any]):
|
|
454
|
+
@self.bot.message_handler(
|
|
455
|
+
content_types=["photo"],
|
|
456
|
+
chat_types=chat_types,
|
|
457
|
+
**kwargs
|
|
458
|
+
)
|
|
459
|
+
def trigger(message):
|
|
449
460
|
if whitelist is not None and message.chat.id not in whitelist:
|
|
450
461
|
return
|
|
451
462
|
return handler(message)
|
|
452
463
|
|
|
453
|
-
return
|
|
464
|
+
return trigger
|
|
454
465
|
|
|
455
|
-
return Invoker(
|
|
466
|
+
return Invoker(register, self.handler)
|
|
456
467
|
|
|
457
468
|
def func(
|
|
458
469
|
self,
|
|
@@ -508,17 +519,20 @@ class On:
|
|
|
508
519
|
return False
|
|
509
520
|
return bool(func(message))
|
|
510
521
|
|
|
511
|
-
original_decorator = self.bot.message_handler(
|
|
512
|
-
func=_filter,
|
|
513
|
-
chat_types=chat_types,
|
|
514
|
-
**kwargs
|
|
515
|
-
)
|
|
516
|
-
|
|
517
522
|
def decorator(handler: Callable[..., typing.Any]):
|
|
518
523
|
if not invoke_args and not invoke_kwargs:
|
|
519
|
-
return
|
|
520
|
-
|
|
521
|
-
|
|
524
|
+
return self.bot.message_handler(
|
|
525
|
+
func=_filter,
|
|
526
|
+
chat_types=chat_types,
|
|
527
|
+
**kwargs
|
|
528
|
+
)(handler)
|
|
529
|
+
|
|
530
|
+
@self.bot.message_handler(
|
|
531
|
+
func=_filter,
|
|
532
|
+
chat_types=chat_types,
|
|
533
|
+
**kwargs
|
|
534
|
+
)
|
|
535
|
+
def trigger(message, *args, **kwargs):
|
|
522
536
|
final_args = args
|
|
523
537
|
final_kwargs = kwargs
|
|
524
538
|
|
|
@@ -530,6 +544,6 @@ class On:
|
|
|
530
544
|
|
|
531
545
|
return handler(message, *final_args, **final_kwargs)
|
|
532
546
|
|
|
533
|
-
return
|
|
547
|
+
return trigger
|
|
534
548
|
|
|
535
549
|
return Invoker(decorator, self.handler)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.0"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import telekit
|
|
4
|
+
from telekit.styles import Quote
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class QRCode:
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self.text: str | None = None
|
|
10
|
+
self.caption: str | None = None
|
|
11
|
+
|
|
12
|
+
def get_url(self) -> str:
|
|
13
|
+
kwargs: dict[str, Any] = {"text": self.text or "Example"}
|
|
14
|
+
if self.caption is not None:
|
|
15
|
+
kwargs["caption"] = self.caption
|
|
16
|
+
return telekit.utils.make_qrcode(**kwargs)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class QRHandler(telekit.Handler):
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def init_handler(cls) -> None:
|
|
23
|
+
cls.on.command("start").invoke(cls.handle)
|
|
24
|
+
|
|
25
|
+
def handle(self) -> None:
|
|
26
|
+
self.qrcode = QRCode()
|
|
27
|
+
self.display_qrcode()
|
|
28
|
+
|
|
29
|
+
# ── entry points ──────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
def change_text(self) -> None:
|
|
32
|
+
self.chain.sender.set_title("⌨️ Enter text")
|
|
33
|
+
self.chain.sender.set_message("Send any text or URL to encode into a QR code")
|
|
34
|
+
self.chain.set_entry_text(self.set_text, delete_user_response=True)
|
|
35
|
+
|
|
36
|
+
keyboard: dict[str, Any] = {}
|
|
37
|
+
keyboard["« Back"] = self.display_qrcode
|
|
38
|
+
|
|
39
|
+
if self.qrcode.text:
|
|
40
|
+
keyboard["✕ Remove"] = self.set_text
|
|
41
|
+
|
|
42
|
+
self.chain.set_inline_keyboard(keyboard, row_width=2)
|
|
43
|
+
self.chain.edit()
|
|
44
|
+
|
|
45
|
+
def change_caption(self) -> None:
|
|
46
|
+
self.chain.sender.set_title("⌨️ Enter caption")
|
|
47
|
+
self.chain.sender.set_message("Send a caption to display below the QR code")
|
|
48
|
+
self.chain.set_entry_text(self.set_caption, delete_user_response=True)
|
|
49
|
+
|
|
50
|
+
keyboard: dict[str, Any] = {}
|
|
51
|
+
keyboard["« Back"] = self.display_qrcode
|
|
52
|
+
|
|
53
|
+
if self.qrcode.caption:
|
|
54
|
+
keyboard["✕ Remove"] = self.set_caption
|
|
55
|
+
|
|
56
|
+
self.chain.set_inline_keyboard(keyboard, row_width=2)
|
|
57
|
+
self.chain.edit()
|
|
58
|
+
|
|
59
|
+
# ── entry handlers ──────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
def set_text(self, text: str | None = None) -> None:
|
|
62
|
+
self.qrcode.text = text
|
|
63
|
+
self.display_qrcode()
|
|
64
|
+
|
|
65
|
+
def set_caption(self, caption: str | None = None) -> None:
|
|
66
|
+
self.qrcode.caption = caption
|
|
67
|
+
self.display_qrcode()
|
|
68
|
+
|
|
69
|
+
# ── display QR code ───────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
def display_qrcode(self) -> None:
|
|
72
|
+
url: str = self.qrcode.get_url()
|
|
73
|
+
|
|
74
|
+
self.chain.sender.set_photo(url)
|
|
75
|
+
self.chain.sender.set_text(
|
|
76
|
+
Quote(url, expandable=True),
|
|
77
|
+
"Use the buttons below to edit the QR code:"
|
|
78
|
+
)
|
|
79
|
+
self.chain.set_inline_keyboard(
|
|
80
|
+
{
|
|
81
|
+
"✏️ Edit Text": self.change_text,
|
|
82
|
+
"✏️ Edit Caption" if self.qrcode.caption else "➕ Add Caption": self.change_caption,
|
|
83
|
+
},
|
|
84
|
+
row_width=2
|
|
85
|
+
)
|
|
86
|
+
self.chain.edit()
|
|
@@ -22,11 +22,15 @@ class StartHandler(telekit.Handler):
|
|
|
22
22
|
"🧮 Counter": "CounterHandler",
|
|
23
23
|
"🧙♀️ Spells": "SpellsHandler",
|
|
24
24
|
"⌨️ Entry": "EntryHandler",
|
|
25
|
+
|
|
25
26
|
"📚 Telekit DSL Examples": "DSLExampleHandler",
|
|
27
|
+
|
|
26
28
|
"📖 Pages": "PagesHandler",
|
|
27
29
|
"🦻 On Text": "OnTextHandler",
|
|
28
30
|
"📄 File Info": "TextDocumentHandler",
|
|
29
|
-
|
|
31
|
+
|
|
32
|
+
"🖼️ QR Editor": "QRHandler",
|
|
33
|
+
}, row_width=[3, 1, 3, 1]
|
|
30
34
|
)
|
|
31
35
|
def handle_response(handler: str):
|
|
32
36
|
self.handoff(handler).handle()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import re
|
|
2
|
+
|
|
2
3
|
from pathlib import Path
|
|
4
|
+
from urllib.parse import urlencode, quote
|
|
5
|
+
from typing import Literal
|
|
3
6
|
|
|
4
7
|
ROOT_DIR = Path(__file__).resolve().parent # telekit/
|
|
5
8
|
|
|
@@ -100,7 +103,6 @@ def read_token(path: str = "token.txt") -> str:
|
|
|
100
103
|
token, *_ = first_line.split()
|
|
101
104
|
return token
|
|
102
105
|
|
|
103
|
-
|
|
104
106
|
def read_canvas_path(path: str = "canvas_path.txt") -> str:
|
|
105
107
|
"""
|
|
106
108
|
Read the ``.canvas`` file path from a file.
|
|
@@ -115,7 +117,145 @@ def read_canvas_path(path: str = "canvas_path.txt") -> str:
|
|
|
115
117
|
"""
|
|
116
118
|
with open(path) as f:
|
|
117
119
|
return f.readline().strip()
|
|
120
|
+
|
|
121
|
+
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
122
|
+
# Link Generators
|
|
123
|
+
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
124
|
+
|
|
125
|
+
def make_bot_link(botname: str, start: str | None = None) -> str:
|
|
126
|
+
"""Return a t.me link to a bot.
|
|
127
|
+
|
|
128
|
+
>>> make_bot_link("UserNameOfBot")
|
|
129
|
+
"https://t.me/UserNameOfBot"
|
|
130
|
+
|
|
131
|
+
>>> make_bot_link("UserNameOfBot", start="Hello!")
|
|
132
|
+
"https://t.me/UserNameOfBot?start=Hello%21"
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
botname: Bot username with or without leading '@'.
|
|
136
|
+
start: Optional deep-link payload appended as ?start=.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
A t.me URL string.
|
|
140
|
+
"""
|
|
141
|
+
botname = botname.lstrip("@")
|
|
142
|
+
if start is None:
|
|
143
|
+
return f"https://t.me/{botname}"
|
|
144
|
+
encoded_start = quote(start, safe="")
|
|
145
|
+
return f"https://t.me/{botname}?start={encoded_start}"
|
|
146
|
+
|
|
147
|
+
def make_user_link(username: str, text: str | None = None) -> str:
|
|
148
|
+
"""Return a t.me link to a user.
|
|
149
|
+
|
|
150
|
+
>>> make_user_link("UserName")
|
|
151
|
+
"https://t.me/UserName"
|
|
152
|
+
|
|
153
|
+
>>> make_user_link("UserName", text="Hello!")
|
|
154
|
+
"https://t.me/UserName?text=Hello%21"
|
|
118
155
|
|
|
156
|
+
Args:
|
|
157
|
+
username: Telegram username with or without leading '@'.
|
|
158
|
+
text: Optional pre-filled message text appended as ?text=.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
A t.me URL string.
|
|
162
|
+
"""
|
|
163
|
+
username = username.lstrip("@")
|
|
164
|
+
if text is None:
|
|
165
|
+
return f"https://t.me/{username}"
|
|
166
|
+
encoded_text = quote(text, safe="")
|
|
167
|
+
return f"https://t.me/{username}?text={encoded_text}"
|
|
168
|
+
|
|
169
|
+
def make_qrcode(
|
|
170
|
+
text: str,
|
|
171
|
+
*,
|
|
172
|
+
size: int | None = None,
|
|
173
|
+
margin: int | None = None,
|
|
174
|
+
dark: str | None = None,
|
|
175
|
+
light: str | None = None,
|
|
176
|
+
ec_level: Literal["L", "M", "Q", "H"] | None = None,
|
|
177
|
+
format: Literal["png", "svg", "base64"] | None = None,
|
|
178
|
+
center_image_url: str | None = None,
|
|
179
|
+
center_image_size_ratio: float | None = None,
|
|
180
|
+
center_image_width: int | None = None,
|
|
181
|
+
center_image_height: int | None = None,
|
|
182
|
+
caption: str | None = None,
|
|
183
|
+
caption_font_family: str | None = None,
|
|
184
|
+
caption_font_size: int | None = None,
|
|
185
|
+
caption_font_color: str | None = None,
|
|
186
|
+
) -> str:
|
|
187
|
+
"""Return a QuickChart URL that renders a QR code.
|
|
188
|
+
|
|
189
|
+
>>> make_qrcode("https://example.com")
|
|
190
|
+
'https://quickchart.io/qr?text=https%3A%2F%2Fexample.com'
|
|
191
|
+
|
|
192
|
+
>>> make_qrcode("Hello", dark="ff0000", size=300)
|
|
193
|
+
'https://quickchart.io/qr?text=Hello&size=300&dark=ff0000'
|
|
194
|
+
|
|
195
|
+
Use with sender:
|
|
196
|
+
>>> sender.set_photo(make_qrcode("https://example.com", caption="Scan Me"))
|
|
197
|
+
|
|
198
|
+
Check out the [QuickChart QR Code API](https://quickchart.io/documentation/qr-codes/)
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
text: Content to encode (URL or any string).
|
|
202
|
+
size: Width and height of the image in pixels.
|
|
203
|
+
margin: Whitespace around the QR image in modules.
|
|
204
|
+
dark: Hex color of dark cells, without '#'.
|
|
205
|
+
light: Hex color of light cells, without '#'.
|
|
206
|
+
Use "0000" for a transparent background.
|
|
207
|
+
ec_level: Error correction level — L, M, Q, or H.
|
|
208
|
+
format: Output format — png, svg, or base64.
|
|
209
|
+
center_image_url: URL of an image to display in the center.
|
|
210
|
+
Must be publicly accessible (PNG or JPG).
|
|
211
|
+
center_image_size_ratio: Float 0.0–1.0 — portion of QR area the
|
|
212
|
+
center image occupies. Keep below ~0.3.
|
|
213
|
+
center_image_width: Center image width in pixels.
|
|
214
|
+
center_image_height: Center image height in pixels.
|
|
215
|
+
caption: Caption text displayed below the QR code.
|
|
216
|
+
caption_font_family: Font family of the caption.
|
|
217
|
+
caption_font_size: Font size of the caption in pixels.
|
|
218
|
+
caption_font_color: Color of the caption — name or hex.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
A quickchart.io URL string.
|
|
222
|
+
"""
|
|
223
|
+
params: dict = {"text": text}
|
|
224
|
+
|
|
225
|
+
if size is not None:
|
|
226
|
+
params["size"] = size
|
|
227
|
+
if margin is not None:
|
|
228
|
+
params["margin"] = margin
|
|
229
|
+
if dark is not None:
|
|
230
|
+
params["dark"] = dark
|
|
231
|
+
if light is not None:
|
|
232
|
+
params["light"] = light
|
|
233
|
+
if ec_level is not None:
|
|
234
|
+
params["ecLevel"] = ec_level
|
|
235
|
+
if format is not None:
|
|
236
|
+
params["format"] = format
|
|
237
|
+
if center_image_url is not None:
|
|
238
|
+
params["centerImageUrl"] = center_image_url
|
|
239
|
+
if center_image_size_ratio is not None:
|
|
240
|
+
params["centerImageSizeRatio"] = center_image_size_ratio
|
|
241
|
+
if center_image_width is not None:
|
|
242
|
+
params["centerImageWidth"] = center_image_width
|
|
243
|
+
if center_image_height is not None:
|
|
244
|
+
params["centerImageHeight"] = center_image_height
|
|
245
|
+
if caption is not None:
|
|
246
|
+
params["caption"] = caption
|
|
247
|
+
if caption_font_family is not None:
|
|
248
|
+
params["captionFontFamily"] = caption_font_family
|
|
249
|
+
if caption_font_size is not None:
|
|
250
|
+
params["captionFontSize"] = caption_font_size
|
|
251
|
+
if caption_font_color is not None:
|
|
252
|
+
params["captionFontColor"] = caption_font_color
|
|
253
|
+
|
|
254
|
+
return "https://quickchart.io/qr?" + urlencode(params, quote_via=quote)
|
|
255
|
+
|
|
256
|
+
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
257
|
+
# Markdown Sanitizing
|
|
258
|
+
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
|
|
119
259
|
|
|
120
260
|
# All special chars that must be escaped in MarkdownV2
|
|
121
261
|
_SPECIAL = r'_*[]()~`>#+-=|{}.!'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telekit
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.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
|
|
@@ -62,7 +62,7 @@ telekit.Server("BOT_TOKEN").polling()
|
|
|
62
62
|
|
|
63
63
|
> Send "Hello!" with a photo on `/start`
|
|
64
64
|
|
|
65
|
-
Telekit comes with a [built-in DSL](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial/11_telekit_dsl.md), allowing developers to create fully interactive bots with minimal code. It also integrates **Jinja
|
|
65
|
+
Telekit comes with a [built-in DSL](https://github.com/Romashkaa/telekit/blob/main/docs/tutorial/11_telekit_dsl.md), allowing developers to create fully interactive bots with minimal code. It also integrates [**Jinja**](https://github.com/Romashkaa/telekit/blob/main/docs/examples/jinja_engine.md), giving you loops, conditionals, expressions, and filters to generate dynamic content.
|
|
66
66
|
|
|
67
67
|
```js
|
|
68
68
|
@ main {
|
|
@@ -378,68 +378,20 @@ It tries to make Telegram bot development easier.
|
|
|
378
378
|
|
|
379
379
|
---
|
|
380
380
|
|
|
381
|
-
# Changes in version 2.0
|
|
382
|
-
|
|
383
|
-
##
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
- Added `utils.telegramify_markdown()` — convenience pipeline: adapt + sanitize
|
|
399
|
-
- `StyleFormatter` renamed to `TextEntity`
|
|
400
|
-
|
|
401
|
-
## Inline Keyboards
|
|
402
|
-
|
|
403
|
-
Inline keyboards got a proper type system. Previously, any string value in the keyboard dict was treated as a link. Now every button type is explicit.
|
|
404
|
-
|
|
405
|
-
- Introduced `InlineButton` base class with subclasses:
|
|
406
|
-
`LinkButton`, `WebAppButton`, `SuggestButton`, `CopyTextButton`, `CallbackButton`
|
|
407
|
-
- `CallbackButton` — pass arguments to callbacks and set visual style
|
|
408
|
-
- Added `ButtonStyle` enum — style your inline buttons (Bot API 9.4)
|
|
409
|
-
- Added `inline_choice` / `set_inline_choice` methods
|
|
410
|
-
- `set_entry_suggestions` now warns when Telegram Bot API limits are exceeded
|
|
411
|
-
- It's no longer pass `Message` object to callbacks
|
|
412
|
-
- Reduced closure size and improved callback performance
|
|
413
|
-
|
|
414
|
-
## Chain Improvements
|
|
415
|
-
- Added `set_break_only_on_match()` — control whether chain interrupts only on valid handler match, not random input
|
|
416
|
-
- Added `set_break_on_commands()` — control whether commands take priority over entry handlers
|
|
417
|
-
- Improved file extension validation via **pathlib**
|
|
418
|
-
- `entry_*` handlers no longer pass `Message` by default — use `include_message=True` to restore
|
|
419
|
-
|
|
420
|
-
## CallbackQueryHandler
|
|
421
|
-
- Callbacks now invoked directly — no virtual message conversion, faster response times
|
|
422
|
-
- Native alert support for expired or inactive buttons
|
|
423
|
-
- Added `set_invalid_data_answer()` and `set_button_is_no_active_answer()` for global customization
|
|
424
|
-
|
|
425
|
-
## DSL Improvements
|
|
426
|
-
- `jinja_env` is now private — use `get_jinja_env()` instead
|
|
427
|
-
- `TelekitDSL` class:
|
|
428
|
-
- All `from_*` factory methods now use `type()` for class creation
|
|
429
|
-
|
|
430
|
-
## Fixes
|
|
431
|
-
- Fixed one-time trigger bug — triggers like `on.text` now fire reliably across multiple messages while a chain timeout is active
|
|
432
|
-
|
|
433
|
-
## Other
|
|
434
|
-
- Added `utils.read_token()` and `utils.read_canvas_path()` — clean helpers for reading credentials from files
|
|
435
|
-
- Added `utils.format_file_size()`
|
|
436
|
-
- Added `set_license_text` to senders
|
|
437
|
-
- Added `format_size` to `TextDocument`
|
|
438
|
-
- Added `TelekitState` class
|
|
439
|
-
- Bumped `pyTelegramBotAPI` to **v4.31.0**
|
|
440
|
-
- Added GPL v3 License
|
|
441
|
-
|
|
442
|
-
## 🚀 Upcoming
|
|
443
|
-
|
|
444
|
-
- New triggers: `document`, `text_document`
|
|
445
|
-
- Telekit DSL: shorter notation and button attributes with style, alert, ... support
|
|
381
|
+
# Changes in version 2.1.0
|
|
382
|
+
|
|
383
|
+
## Trigger Improvements
|
|
384
|
+
|
|
385
|
+
| **Name** | **Description** |
|
|
386
|
+
|----------|-----------------|
|
|
387
|
+
| `on.text` | *BugFix:* Commands are no longer matched as text triggers. |
|
|
388
|
+
|
|
389
|
+
- Internal refactor of the `On` class.
|
|
390
|
+
|
|
391
|
+
## New Utils
|
|
392
|
+
|
|
393
|
+
| **Name** | **Description** |
|
|
394
|
+
|----------|-----------------|
|
|
395
|
+
| `make_bot_link` | Builds a `t.me` URL to a bot with an optional deep-link payload. |
|
|
396
|
+
| `make_user_link` | Builds a `t.me` URL to a user with an optional pre-filled message. |
|
|
397
|
+
| `make_qrcode` | Generates a [QR code](./docs/examples/qr_gen.md) URL from any text or link. |
|
|
@@ -62,6 +62,7 @@ telekit/example/example_handlers/hotel.py
|
|
|
62
62
|
telekit/example/example_handlers/on_text.py
|
|
63
63
|
telekit/example/example_handlers/pages.py
|
|
64
64
|
telekit/example/example_handlers/pyapi.py
|
|
65
|
+
telekit/example/example_handlers/qr.py
|
|
65
66
|
telekit/example/example_handlers/quiz.py
|
|
66
67
|
telekit/example/example_handlers/spells.py
|
|
67
68
|
telekit/example/example_handlers/start.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.0.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|