Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/source/plugins/conversations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ Building multi-step dialogs in Telegram bots is often clunky.

Raito provides a lightweight way to wait for the **next user message** in a clean, linear style.

.. warning::

Conversations DO NOT work with ``Dispatcher.events_isolation``,
since ``SimpleEventIsolation`` operates with a ``asyncio.Lock`` on active handlers,
and ``wait_for`` will never receive an update.


--------

Example
Expand Down
3 changes: 2 additions & 1 deletion examples/12-conversations/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path

from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import DisabledEventIsolation

from raito import Raito

Expand All @@ -10,7 +11,7 @@
DEBUG = False

bot = Bot(token=TOKEN)
dispatcher = Dispatcher()
dispatcher = Dispatcher(events_isolation=DisabledEventIsolation())
raito = Raito(dispatcher, HANDLERS_DIR, developers=[], locales=["en"], production=not DEBUG)
raito.init_logging()

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "raito"
version = "1.3.6"
version = "1.3.7"
description = "REPL, hot-reload, keyboards, pagination, and internal dev tools — all in one. That's Raito."
authors = [{ name = "Aiden", email = "aidenthedev@gmail.com" }]
license = "MIT"
Expand Down
27 changes: 25 additions & 2 deletions raito/plugins/keyboards/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
P = ParamSpec("P")
R = TypeVar("R")

ButtonData: TypeAlias = str | tuple[str] | tuple[str, str]
ButtonData: TypeAlias = str | tuple[str] | tuple[str, str] | list[str]
LayoutRow: TypeAlias = Sequence[ButtonData]
LayoutReturn: TypeAlias = Sequence[ButtonData | LayoutRow]

Expand All @@ -31,6 +31,12 @@
BuilderFn: TypeAlias = Callable[P, KeyboardMarkupT]


@overload
def _get_button(data: ButtonData, *, inline: Literal[True]) -> InlineKeyboardButton: ...
@overload
def _get_button(data: ButtonData, *, inline: Literal[False]) -> KeyboardButton: ...
@overload
def _get_button(data: ButtonData, *, inline: bool) -> InlineKeyboardButton | KeyboardButton: ...
def _get_button(data: ButtonData, *, inline: bool) -> InlineKeyboardButton | KeyboardButton:
"""Convert button data to the appropriate button type.

Expand All @@ -44,7 +50,24 @@ def _get_button(data: ButtonData, *, inline: bool) -> InlineKeyboardButton | Key

if isinstance(data, str) or len(data) != 2:
raise ValueError("InlineKeyboardButton must be tuple of (text, callback_data)")
return InlineKeyboardButton(text=data[0], callback_data=data[1])

name, value = data

url: str | None = None
if value.startswith("t.me/"):
url = "https://" + value
elif value.startswith(
(
"http://",
"https://",
"tg://",
)
):
url = value

if url:
return InlineKeyboardButton(text=name, url=url)
return InlineKeyboardButton(text=name, callback_data=value)


def _is_button(row: LayoutRow) -> bool:
Expand Down