mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
167 lines
5.3 KiB
Python
167 lines
5.3 KiB
Python
import logging
|
|
from typing import Union
|
|
|
|
import aiogram.utils.markdown as md
|
|
from aiogram import Bot, Dispatcher, types
|
|
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
|
from aiogram.dispatcher import FSMContext
|
|
from aiogram.dispatcher.filters import Text
|
|
from aiogram.dispatcher.filters.state import State, StatesGroup
|
|
from aiogram.types import ParseMode
|
|
from aiogram.utils import executor
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
API_TOKEN = 'BOT_TOKEN_HERE'
|
|
|
|
|
|
bot = Bot(token=API_TOKEN)
|
|
|
|
# For example use simple MemoryStorage for Dispatcher.
|
|
storage = MemoryStorage()
|
|
dp = Dispatcher(bot, storage=storage)
|
|
|
|
|
|
# States
|
|
class Form(StatesGroup):
|
|
name = State() # Will be represented in storage as 'Form:name'
|
|
age = State() # Will be represented in storage as 'Form:age'
|
|
gender = State() # Will be represented in storage as 'Form:gender'
|
|
|
|
|
|
# You can use pre_set decorator to call decorated functions before state will be setted
|
|
@Form.name.pre_set
|
|
async def name_pre_set(context: FSMContext, old_state: str, new_state: str):
|
|
logging.info('Calling before Form:name will be set')
|
|
await bot.send_message(
|
|
chat_id = context.chat,
|
|
text = "What's your name?"
|
|
)
|
|
|
|
|
|
# You can use post_set decorator to call decorated functions after state will be setted
|
|
@Form.age.post_set
|
|
async def age_post_set(context: FSMContext, old_state: str, new_state: str):
|
|
logging.info('Calling after Form:age will be set')
|
|
markup = types.InlineKeyboardMarkup(row_width=1)
|
|
markup.row(types.InlineKeyboardButton('Back', callback_data='back'))
|
|
|
|
await bot.send_message(
|
|
chat_id = context.chat,
|
|
text = "How old are you?",
|
|
reply_markup = markup
|
|
)
|
|
|
|
|
|
@Form.gender.pre_set
|
|
async def gender_pre_set(context: FSMContext, old_state: str, new_state: str):
|
|
logging.info('Calling before gender:age will be set')
|
|
|
|
markup = types.ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
|
|
markup.add("Male", "Female")
|
|
markup.add("Other", "Back")
|
|
|
|
await bot.send_message(
|
|
chat_id = context.chat,
|
|
text = "What is your gender?",
|
|
reply_markup = markup
|
|
)
|
|
|
|
|
|
# You can use pre_finish decorator to call decorated functions before state group will be finished
|
|
@Form.pre_finish
|
|
async def form_pre_finish(context: FSMContext, old_state: str):
|
|
async with context.proxy() as data:
|
|
markup = types.ReplyKeyboardRemove()
|
|
|
|
await bot.send_message(
|
|
context.chat,
|
|
md.text(
|
|
md.text('Hi! Nice to meet you,', md.bold(data['name'])),
|
|
md.text('Age:', md.code(data['age'])),
|
|
md.text('Gender:', data['gender']),
|
|
sep='\n',
|
|
),
|
|
reply_markup=markup,
|
|
parse_mode=ParseMode.MARKDOWN,
|
|
)
|
|
|
|
|
|
# You can use state Form if you need to handle all states from Form group
|
|
@dp.message_handler(commands='back', state=Form)
|
|
@dp.message_handler(Text(equals='back', ignore_case=True), state=Form)
|
|
@dp.callback_query_handler(text='back', state=Form)
|
|
async def process_gender_invalid(message: Union[types.Message, types.CallbackQuery]):
|
|
await Form.previous()
|
|
|
|
|
|
# You can use state '*' if you need to handle all states
|
|
@dp.message_handler(state='*', commands='cancel')
|
|
@dp.message_handler(Text(equals='cancel', ignore_case=True), state='*')
|
|
async def cancel_handler(message: types.Message, state: FSMContext):
|
|
"""
|
|
Allow user to cancel any action
|
|
"""
|
|
current_state = await state.get_state()
|
|
if current_state is None:
|
|
return
|
|
|
|
logging.info('Cancelling state %r', current_state)
|
|
# Cancel state and inform user about it
|
|
# You can set trigger to False to prevent pre_set and post_set signals
|
|
await state.finish(trigger=False)
|
|
# And remove keyboard (just in case)
|
|
await message.reply('Cancelled.', reply_markup=types.ReplyKeyboardRemove())
|
|
|
|
|
|
@dp.message_handler(commands='start')
|
|
async def cmd_start(message: types.Message):
|
|
"""
|
|
Conversation's entry point
|
|
"""
|
|
await message.reply("Hi there!")
|
|
await Form.name.set()
|
|
|
|
|
|
@dp.message_handler(state=Form.name)
|
|
async def process_name(message: types.Message, state: FSMContext):
|
|
"""
|
|
Process user name and go to age
|
|
"""
|
|
await state.update_data(name=message.text)
|
|
await Form.next()
|
|
|
|
|
|
@dp.message_handler(lambda message: not message.text.isdigit(), state=Form.age)
|
|
async def process_age_invalid(message: types.Message):
|
|
"""
|
|
If age is not digit
|
|
"""
|
|
return await message.reply("Age gotta be a number.\nHow old are you? (digits only)")
|
|
|
|
|
|
@dp.message_handler(lambda message: message.text.isdigit(), state=Form.age)
|
|
async def process_age(message: types.Message, state: FSMContext):
|
|
"""
|
|
Process user age and go to gender
|
|
"""
|
|
await state.update_data(age=int(message.text))
|
|
await Form.next()
|
|
|
|
|
|
@dp.message_handler(lambda message: message.text not in ["Male", "Female", "Other"], state=Form.gender)
|
|
async def process_gender_invalid(message: types.Message):
|
|
"""
|
|
In this example gender has to be one of: Male, Female, Other.
|
|
"""
|
|
return await message.reply("Bad gender name. Choose your gender from the keyboard.")
|
|
|
|
|
|
@dp.message_handler(state=Form.gender)
|
|
async def process_gender(message: types.Message, state: FSMContext):
|
|
await state.update_data(gender=message.text)
|
|
await state.finish()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
executor.start_polling(dp, skip_updates=True)
|