aiogram/examples/finite_state_machine_example.py

127 lines
4 KiB
Python
Raw Normal View History

2017-08-02 07:31:30 +03:00
import asyncio
from aiogram import Bot, types
2017-08-04 16:01:10 +03:00
from aiogram.contrib.fsm_storage.memory import MemoryStorage
2017-08-02 07:31:30 +03:00
from aiogram.dispatcher import Dispatcher
from aiogram.types import ParseMode
2017-11-15 18:46:19 +02:00
from aiogram.utils import executor
2017-08-02 07:31:30 +03:00
from aiogram.utils.markdown import text, bold
API_TOKEN = 'BOT TOKEN HERE'
loop = asyncio.get_event_loop()
2017-08-02 07:39:26 +03:00
2017-08-02 07:31:30 +03:00
bot = Bot(token=API_TOKEN, loop=loop)
2017-08-02 07:39:26 +03:00
# For example use simple MemoryStorage for Dispatcher.
2017-08-02 07:31:30 +03:00
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
# States
AGE = 'process_age'
NAME = 'process_name'
GENDER = 'process_gender'
@dp.message_handler(commands=['start'])
async def cmd_start(message: types.Message):
"""
2018-02-21 19:09:44 +03:00
Conversation's entry point
2017-08-02 07:31:30 +03:00
"""
# Get current state
state = dp.current_state(chat=message.chat.id, user=message.from_user.id)
2018-02-21 19:09:44 +03:00
# Update user's state
2017-08-02 07:31:30 +03:00
await state.set_state(NAME)
await message.reply("Hi there! What's your name?")
2018-02-21 19:09:44 +03:00
# You can use state '*' if you need to handle all states
2017-08-02 07:31:30 +03:00
@dp.message_handler(state='*', commands=['cancel'])
2017-08-02 07:43:04 +03:00
@dp.message_handler(state='*', func=lambda message: message.text.lower() == 'cancel')
2017-08-02 07:31:30 +03:00
async def cancel_handler(message: types.Message):
"""
2018-02-21 19:09:44 +03:00
Allow user to cancel any action
2017-08-02 07:31:30 +03:00
"""
with dp.current_state(chat=message.chat.id, user=message.from_user.id) as state:
2017-08-02 22:08:58 +03:00
# Ignore command if user is not in any (defined) state
2017-08-02 07:31:30 +03:00
if await state.get_state() is None:
return
2017-08-02 07:39:26 +03:00
2018-02-21 19:09:44 +03:00
# Otherwise cancel state and inform user about it
# And remove keyboard (just in case)
2017-08-02 07:31:30 +03:00
await state.reset_state(with_data=True)
await message.reply('Canceled.', reply_markup=types.ReplyKeyboardRemove())
@dp.message_handler(state=NAME)
async def process_name(message: types.Message):
"""
Process user name
"""
# Save name to storage and go to next step
# You can use context manager
with dp.current_state(chat=message.chat.id, user=message.from_user.id) as state:
await state.update_data(name=message.text)
await state.set_state(AGE)
await message.reply("How old are you?")
2018-02-21 19:09:44 +03:00
# Check age. Age gotta be digit
2017-08-02 07:31:30 +03:00
@dp.message_handler(state=AGE, func=lambda message: not message.text.isdigit())
async def failed_process_age(message: types.Message):
"""
2018-02-21 19:09:44 +03:00
If age is invalid
2017-08-02 07:31:30 +03:00
"""
2018-02-21 19:09:44 +03:00
return await message.reply("Age gotta be a number.\nHow old are you? (digits only)")
2017-08-02 07:31:30 +03:00
@dp.message_handler(state=AGE, func=lambda message: message.text.isdigit())
async def process_age(message: types.Message):
# Update state and data
with dp.current_state(chat=message.chat.id, user=message.from_user.id) as state:
await state.set_state(GENDER)
await state.update_data(age=int(message.text))
# Configure ReplyKeyboardMarkup
2017-08-04 16:01:10 +03:00
markup = types.ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
2017-08-02 07:31:30 +03:00
markup.add("Male", "Female")
markup.add("Other")
await message.reply("What is your gender?", reply_markup=markup)
@dp.message_handler(state=GENDER, func=lambda message: message.text not in ["Male", "Female", "Other"])
2018-02-21 19:09:44 +03:00
async def failed_process_gender(message: types.Message):
2017-08-02 07:31:30 +03:00
"""
2018-02-21 19:09:44 +03:00
In this example gender has to be one of: Male, Female, Other.
2017-08-02 07:31:30 +03:00
"""
return await message.reply("Bad gender name. Choose you gender from keyboard.")
@dp.message_handler(state=GENDER)
2018-02-21 19:09:44 +03:00
async def process_gender(message: types.Message):
2017-08-02 07:31:30 +03:00
state = dp.current_state(chat=message.chat.id, user=message.from_user.id)
data = await state.get_data()
2018-02-21 19:09:44 +03:00
data['gender'] = message.text
2017-08-02 07:31:30 +03:00
# Remove keyboard
markup = types.ReplyKeyboardRemove()
2017-08-02 07:43:04 +03:00
# And send message
2017-08-02 07:31:30 +03:00
await bot.send_message(message.chat.id, text(
text('Hi! Nice to meet you,', bold(data['name'])),
text('Age:', data['age']),
2018-02-21 19:09:44 +03:00
text('Gender:', data['gender']),
2017-08-02 07:31:30 +03:00
sep='\n'), reply_markup=markup, parse_mode=ParseMode.MARKDOWN)
# Finish conversation
2017-11-15 18:46:19 +02:00
# WARNING! This method will destroy all data in storage for current user!
2017-08-02 07:31:30 +03:00
await state.finish()
if __name__ == '__main__':
2018-06-27 01:13:53 +03:00
executor.start_polling(dp, loop=loop, skip_updates=True)