Перейти к содержанию

FSM

Этот модуль имплементирует Finite-state machine для ботов.

FSM разделяет пользовательские состояния, состояния в чатах и состояния пользователя в чате, для выбора одного из них вы должны указать for_what аргумент в методах fsm.

Состояние для одного пользователя в личных сообщениях - ForWhat.FOR_USER

Для всех пользователей в чате - ForWhat.FOR_CHAT

Для одного пользователя в чате - ForWhat.FOR_USER_IN_CHAT

Чтобы хендлеры без StateFilter могли быть выбраны корректно, нужно установить следующий стандартный фильтр.

router.registrar.add_default_filter(StateFilter(fsm, ..., ..., always_false=True))

Пример опроса с именем и возрастом.

from vkwave.bots import EventTypeFilter, BotEvent
from vkwave.types.bot_events import BotEventType
from vkwave.bots.fsm import FiniteStateMachine, StateFilter, ForWhat, State, ANY_STATE

fsm = FiniteStateMachine()
router.registrar.add_default_filter(StateFilter(fsm, ..., ..., always_false=True))
router.registrar.add_default_filter(EventTypeFilter(BotEventType.MESSAGE_NEW.value))  # we don't want to write it in all handlers.


class MyState:
    name = State("name")
    age = State("age")


# начинаем интервью
@router.registrar.with_decorator(
    lambda event: event.object.object.message.text == "start",
)
async def simple_handler(event: BotEvent):
    await fsm.set_state(event=event, state=MyState.name, for_what=ForWhat.FOR_USER)
    return "Input your name"


#  выход из опроса (срабатывает на любой стейт `state=ANY_STATE`)
@router.registrar.with_decorator(
    lambda event: event.object.object.message.text == "exit",
    StateFilter(fsm=fsm, state=ANY_STATE, for_what=ForWhat.FOR_USER)
)
async def simple_handler(event: BotEvent):
    # Проверяем есть ли юзер в базе
    if await fsm.get_data(event, for_what=ForWhat.FOR_USER) is not None:
        await fsm.finish(event=event, for_what=ForWhat.FOR_USER)
    return "You are quited!"


@router.registrar.with_decorator(
    StateFilter(fsm=fsm, state=MyState.name, for_what=ForWhat.FOR_USER),
)
async def simple_handler(event: BotEvent):
    await fsm.set_state(
        event=event,
        state=MyState.age,
        for_what=ForWhat.FOR_USER,
        extra_state_data={"name": event.object.object.message.text},
    )
    # extra_state_data работает так же как fsm.add_data(..., state_data={"name": event.object.object.message.text})

    return "Input your age"


@router.registrar.with_decorator(
    StateFilter(fsm=fsm, state=MyState.age, for_what=ForWhat.FOR_USER),
)
async def simple_handler(event: BotEvent):
    if not event.object.object.message.text.isdigit():
        return f"Please, send only positive numbers!"
    await fsm.add_data(
        event=event,
        for_what=ForWhat.FOR_USER,
        state_data={"age": event.object.object.message.text},
    )
    user_data = await fsm.get_data(event=event, for_what=ForWhat.FOR_USER)

    # завершаем опрос и удаляем юзера
    # `fsm.finish` делает это
    await fsm.finish(event=event, for_what=ForWhat.FOR_USER)
    return f"Your data - {user_data}"

В конце опроса вы получите нечто похожее:

Your data - {'__vkwave_fsm_state__': '<vkwave.bots_fsm.fsm.State object at 0x0000021C19D61A90>', 'name': 'Nick', 'age': '46'}