Сигналы

Сигналы — своего рода кастомные события. "А зачем это нужно?" — возникает очевидный вопрос. Представим, что вы имеете достаточно сложную архитектуру, и поскольку пишете в своего рода функциональном стиле, рефакторите свою логику на более мелкие части (тобишь другие функции), и, скорее всего, разделяя их в разные файлы. Чтобы не погрязнуть в туче импортов, вы можете пометить свою функцию как обработчик сигнала и из любого места в вашем коде вызвать его через имя и передать нужные аргументы

Помимо своих сигналов, вы можете обрабатывать и встроенные сигналы, например, startup, вызываемый во время запуска бота и shutdown во время выключения бота

Чтобы создать обработчик сигнала (своего или встроенного), воспользуйтесь следующей командой

$ bot signal startup
Added a hander on a signal startup into path src/startup.py

Вы заметите, что появился (в нашем случае) файл src/startup.py (и добавился импорт в __init__.py) вот с таким содержимым

import vkquick as vq


@vq.Signal("startup")
def startup():
    """
    Handler to signal `startup`
    """

Появился новый декоратор Signal, как раз-таки и говорящий о том, что эта функция — обработчик сигнала. Теперь, когда мы будем запускать нашего бота, перед его началом работы вызовется эта функция (сам обработчик может быть и sync, и async)


Теперь попробуем создать свой собственный сигнал

$ bot signal example
Added a hander on a signal example into path src/startup.py

Выглядит он почти точно также (кроме имени), как и startup, описанный выше. Немного поменяем содержимое, добавив принт

import vkquick as vq


@vq.Signal("example")
def example():
    """
    Handler to signal `example`
    """
    print("Called signal `example`")

Теперь можно создать команду, вызывающую этот сигнал

$ bot com call_example
Added a command call_example into path src/call_example

Для того, чтобы вызвать сигнал, надо вызвать корутинный метод у bot. Считается устаревшим с v0.2. Будет удален в v1

async def resolve(self, name: str, /, *args, **kwargs)

Либо воспользоваться корутинной функцией, добавленной в v0.2:

async def signal(self, name: str, /, *args, **kwargs)

Где name — имя сигнала (в нашем случае example), а *аргс и **kwargs — передаваемые параметры в обработчик. Поэтому сделаем команду async и добавим вызов сигнала:

import vkquick as vq

from . import config


@vq.Cmd(names=config.NAMES)
@vq.Reaction("message_new")
async def call_example(bot: vq.Bot):
    """
    Handler to command `call_example`
    """
    await bot.signals.resolve("example")
    # либо
    await vq.signal("example")

Запускаем бота ($ bot run --reload --debug), переходим в лс и пишем call_example, после чего смотрим в консоль и видим, что появился вывод Called signal `example`, что говорит нам о том, что обработчк сигнала вызвался успешно

Note

Вы также можете вернуть своим сигналом какое-либо значение и далее использовать его

Todo

В v0.3 появится встроенный сигнал, вызываемый после получения нового события

Todo

В планах есть своего рода репиторы (repeators), которые автоматически повторяют тот или иной сигнал (а может и не сигнал) через каждый n промежуток