Инлайн-клавиатуры и обработка колбэков

CactusLib предоставляет элегантный способ создания инлайн-клавиатур и обработки нажатий на кнопки. Вся логика находится в пространстве имен CactusUtils.Inline.

1. Создание клавиатуры

Клавиатура состоит из рядов, а ряды — из кнопок.

CactusUtils.Inline.Button

Создает одну кнопку.

CactusUtils.Inline.Button(
    text: str,
    # Один из следующих аргументов обязателен:
    url: Optional[str] = None,
    callback_data: Optional[str] = None,
    query: Optional[str] = None,
    copy: Optional[str] = None,
    # ... другие
)
  • text: Текст на кнопке.

  • url: URL-адрес, который откроется при нажатии.

  • callback_data: Строка с данными, которая будет отправлена обратно вашему плагину при нажатии. Это основной способ обработки нажатий.

  • query: Строка, которая будет ставится в поле сообщения при нажатии.

  • copy: Текст, который будет скопирован в буфер обмена при нажатии.

Иконки и Premium-эмодзи в тексте кнопки

Вы можете использовать иконки и Premium-эмодзи в тексте кнопки. Синтаксис: <emoji id=5427317234403930129/> и <icon id=msg_search/>. Например:

button = CactusUtils.Inline.Button(
    # ID премиум эмодзи
    text="<emoji id=5427317234403930129/> Нажми меня",
    query="привет exteraGram", # Это будет выставлено в поле сообщения
)
Кнопка с премиум эмодзи
button = CactusUtils.Inline.Button(
    # ID Drawable иконки
    text="<icon id=msg_search/> Нажми меня",
    query="привет AyuGram", # Это будет выставлено в поле сообщения
)
Кнопка с Drawable иконкой

Примечание

Drawable иконки (R.Drawable.name) можно найти в плагине DevSettingsIcons

CactusUtils.Inline.CallbackData

Создает данные для колбэка для кнопки.

CactusUtils.Inline.CallbackData(
    plugin_id: str,
    method: str,
    # ... другие
    **kwargs
)
  • plugin_id: ID плагина. (Обычно это self.id)

  • method: Имя метода плагина, который будет вызван при нажатии.

  • **kwargs: Дополнительные аргументы, которые будут переданы в метод плагина.

# Создаем кнопку с колбэком
button = CactusUtils.Inline.Button(
    text="Нажми меня",
    callback_data=CactusUtils.Inline.CallbackData(
        plugin_id=self.id,
        method="on_button_click",
        arg1="value1",
        arg2="value2",
        # ...
    )
)

CactusUtils.Inline.Markup

Собирает кнопки в полноценную клавиатуру.

def __init__(self, is_global: bool = False, on_sent: Optional[Callable] = None, *args, **kwargs)
  • is_global: Если True, то сообщение будет отправлено в чат с метаданными внутри текста сообщения. Это позволит увидеть всем пользователям с CactusLib данную клавиатуру.

  • on_sent: Функция, которая будет вызвана после отправки сообщения с клавиатурой и полной инициализации.

  • args и kwargs: Опциональные аргументы, которые будут переданы в функцию on_sent.

Примечание

Если вы используете is_global=True, то on_sent будет проигнорирован.

# Создаем экземпляр разметки
markup = CactusUtils.Inline.Markup()
# Добавляем ряд с одной или несколькими кнопками
markup.add_row(button1, button2)
# Добавляем следующий ряд
markup.add_row(button3)

Или

# Создаем экземпляр разметки
markup = CactusUtils.Inline.Markup().add_row(button1, button2).add_row(button3)

2. Отправка сообщения с клавиатурой

Просто передайте созданный объект Markup в метод answer или send_message.

def send_menu(self, cmd: CactusUtils.Command):
    # Создаем данные для колбэка.
    # Формат: "cactus://{plugin_id}/{method}?{key}={value}"
    cb_data = CactusUtils.Inline.CallbackData(self.id, "menu_press", item="A")

    markup = CactusUtils.Inline.Markup().add_row(
        CactusUtils.Inline.Button("Открыть Google", url="https://google.com/"),
        CactusUtils.Inline.Button("Нажми меня!", callback_data=cb_data)
    )
    cmd.answer("Выберите опцию:", markup=markup)

    return HookResult(strategy=HookStrategy.CANCEL)

3. Обработка нажатий (колбэков)

Для обработки нажатий используется декоратор @CactusUtils.Inline.on_click.

  • @CactusUtils.Inline.on_click(method: str): Декорирует функцию, которая будет вызвана, когда пользователь нажмет на кнопку с callback_data, где method совпадает с методом в CallbackData.

В функцию-обработчик передается объект CactusUtils.Inline.CallbackParams.

  • params.message: MessageObject: Объект сообщения, к которому привязана клавиатура.

  • params.cell: ChatMessageCell: UI-элемент сообщения.

  • params.edit(text, **kwargs): Редактирует текст сообщения. Альтернатива CactusUtils.edit_message(params.message, text, fragment=get_last_fragment(), **kwargs).

  • params.edit_markup(new_markup): Редактирует клавиатуру сообщения.

  • params.delete(): Удаляет сообщение.

Полный пример

class MyPlugin(CactusUtils.Plugin):
    @command(doc="Показывает интерактивное меню")
    def menu(self, cmd: CactusUtils.Command):
        # Создаем клавиатуру с 1 рядом в 1 кнопку
        markup = CactusUtils.Inline.Markup().add_row(
            CactusUtils.Inline.Button(
                "<icon id=msg_add/> Увеличить счетчик",
                callback_data=CactusUtils.Inline.CallbackData(self.id, "counter_click")
            )
        )
        # Получаем текущий счетчик
        count = self.get("menu_counter", 0)
        cmd.answer(f"Счетчик: {count}", markup=markup)

        return HookResult(strategy=HookStrategy.CANCEL)

    @CactusUtils.Inline.on_click("counter_click")
    def _on_counter_click(self, params: CactusUtils.Inline.CallbackParams):
        # Увеличиваем счетчик
        count = self.get("menu_counter", 0) + 1
        self.set("menu_counter", count)

        # Создаем новую клавиатуру
        markup = CactusUtils.Inline.Markup().add_row(
            CactusUtils.Inline.Button(
                "<icon id=msg_add/> Увеличить счетчик",
                callback_data=CactusUtils.Inline.CallbackData(self.id, "counter_click")
            )
        )
        # Редактируем исходное сообщение, чтобы показать новый счетчик
        params.edit(f"Счетчик: {count}", markup=markup)
Анимированный пример

Как это работает:

  1. Пользователь пишет .menu.

  2. Плагин отправляет сообщение «Счетчик: 0» с кнопкой.

  3. Пользователь нажимает на кнопку.

  4. CactusLib перехватывает колбэк и видит, что метод — counter_click.

  5. Вызывается функция _on_counter_click.

  6. Функция обновляет значение в БД и редактирует исходное сообщение, заменяя его на «Счетчик: 1». Клавиатура остается на месте.

Отправка сообщения с клавиатурой в чат с метаданными внутри

Чтобы отправить сообщение с клавиатурой в чат с метаданными, нужно передать is_global=True в конструктор CactusUtils.Inline.Markup.

class MyPlugin(CactusUtils.Plugin):
    @command(doc="Показывает интерактивное меню всем пользователям")
    def items(self, cmd: CactusUtils.Command):
        # Создаем клавиатуру с 1 рядом в 1 кнопку
        markup = CactusUtils.Inline.Markup(is_global=True).add_row(
            CactusUtils.Inline.Button(
                "Нажми меня!",
                url="https://t.me/CactusPlugins"
            )
        )
        cmd.answer(f"Сообщение с Inline-кнопками для всех", markup=markup)

        return HookResult(strategy=HookStrategy.CANCEL)

    @command(doc="Показывает интерактивное меню всем пользователям альтернативным методом")
    def items2(self, cmd: CactusUtils.Command):
        # Создаем клавиатуру с 1 рядом в 1 кнопку
        markup = CactusUtils.Inline.Markup(is_global=True).add_row(
            CactusUtils.Inline.Button(
                "Нажми меня!",
                url="https://t.me/CactusPlugins"
            )
        )
        # Ставим ссылку с метаданными в пробел, чтобы не было заметно
        cmd.answer(f"Сообщение с<a href='{markup.to_url_with_data()}'> </a>Inline-кнопками для всех")

        return HookResult(strategy=HookStrategy.CANCEL)
../_images/items1.png

.items1 - Показывает интерактивное меню всем пользователям обычным способом


../_images/items2.png

.items2 - Показывает интерактивное меню всем пользователям альтернативным способом