Конечные автоматы (FSM)#
Если вашему боту необходима система диалогов, то в дело вступают конечные автоматы. Это некая математическая модель, которая представляет собой набор состояний, которые переключаются в определённых условиях. В библиотеке это реализовано с помощью классов
from vk_maria.dispatcher.fsm import StatesGroup, State, MemoryStorage, FSMContext
class Form(StatesGroup):
waiting_for_name: State
waiting_for_age: State
waiting_for_gender: State
Необходимо описать в классе переменные состояний.
Чтобы имелась возможность запоминать текущие состояния для диалогов всех пользователей, в класс Dispatcher
необходимо передать экземпляр хранилища состояний.
dp = Dispatcher(vk, MemoryStorage())
В библиотеке на данный момент реализованы следующие хранилища состояний: MemoryStorage
, JSONStorage
, PickleStorage
.
Пример реализации#
from vk_maria import Vk, types
from vk_maria.dispatcher import Dispatcher
from vk_maria.dispatcher.fsm import StatesGroup, State, MemoryStorage, FSMContext
from vk_maria.types import KeyboardMarkup, Button, Color, RemoveReplyMarkup
vk = Vk(access_token='token')
dp = Dispatcher(vk, MemoryStorage())
GENDERS = ('Male', 'Female', 'Other')
class Form(StatesGroup):
waiting_for_name: State
waiting_for_age: State
waiting_for_gender: State
@dp.message_handler(commands=['/start'])
def cmd_start(event: types.Message):
event.reply("Hi there! What's your name?")
Form.waiting_for_name.set()
@dp.message_handler(state=Form.waiting_for_name)
def process_name(event: types.Message, state: FSMContext):
state.update_data(name=event.message.text)
event.reply("How old are you?")
Form.next()
@dp.message_handler(state=Form.waiting_for_age)
def process_age(event: types.Message, state: FSMContext):
state.update_data(age=event.message.text)
markup = KeyboardMarkup(one_time=False)
for gender in GENDERS:
markup.add_button(Button.Text(Color.PRIMARY, gender))
event.reply('What is your gender?', keyboard=markup)
Form.next()
@dp.message_handler(state=Form.waiting_for_gender)
def process_gender(event: types.Message, state: FSMContext):
if event.message.text not in GENDERS:
return event.reply('Bad gender name. Choose your gender from the keyboard.')
state.update_data(gender=event.message.text)
user_data = state.get_data()
event.answer(f'Hi! Nice to meet you, {user_data["name"]}\n'
f'Age: {user_data["age"]}\n'
f'Gender: {user_data["gender"]}', keyboard=RemoveReplyMarkup)
Form.finish()
if __name__ == '__main__':
dp.start_polling(debug=True)
Примечание
Чтобы каждый раз не писать FSMContext.get_current(), можно передать аргумент state: FSMContext в функцию-обработчик.
Чтобы отлавливать состояние, передаётся аргумент state
для message_handler