1import logging
2
3import aiogram.utils.markdown as md
4from aiogram import Bot, Dispatcher, types
5from aiogram.contrib.fsm_storage.memory import MemoryStorage
6from aiogram.dispatcher import FSMContext
7from aiogram.dispatcher.filters import Text
8from aiogram.dispatcher.filters.state import State, StatesGroup
9from aiogram.types import ParseMode
10from aiogram.utils import executor
11
12logging.basicConfig(level=logging.INFO)
13
14API_TOKEN = 'BOT TOKEN HERE'
15
16
17bot = Bot(token=API_TOKEN)
18
19# For example use simple MemoryStorage for Dispatcher.
20storage = MemoryStorage()
21dp = Dispatcher(bot, storage=storage)
22
23
24# States
25class Form(StatesGroup):
26 name = State() # Will be represented in storage as 'Form:name'
27 age = State() # Will be represented in storage as 'Form:age'
28 gender = State() # Will be represented in storage as 'Form:gender'
29
30
31@dp.message_handler(commands='start')
32async def cmd_start(message: types.Message):
33 """
34 Conversation's entry point
35 """
36 # Set state
37 await Form.name.set()
38
39 await message.reply("Hi there! What's your name?")
40
41
42# You can use state '*' if you need to handle all states
43@dp.message_handler(state='*', commands='cancel')
44@dp.message_handler(Text(equals='cancel', ignore_case=True), state='*')
45async def cancel_handler(message: types.Message, state: FSMContext):
46 """
47 Allow user to cancel any action
48 """
49 current_state = await state.get_state()
50 if current_state is None:
51 return
52
53 logging.info('Cancelling state %r', current_state)
54 # Cancel state and inform user about it
55 await state.finish()
56 # And remove keyboard (just in case)
57 await message.reply('Cancelled.', reply_markup=types.ReplyKeyboardRemove())
58
59
60@dp.message_handler(state=Form.name)
61async def process_name(message: types.Message, state: FSMContext):
62 """
63 Process user name
64 """
65 async with state.proxy() as data:
66 data['name'] = message.text
67
68 await Form.next()
69 await message.reply("How old are you?")
70
71
72# Check age. Age gotta be digit
73@dp.message_handler(lambda message: not message.text.isdigit(), state=Form.age)
74async def process_age_invalid(message: types.Message):
75 """
76 If age is invalid
77 """
78 return await message.reply("Age gotta be a number.\nHow old are you? (digits only)")
79
80
81@dp.message_handler(lambda message: message.text.isdigit(), state=Form.age)
82async def process_age(message: types.Message, state: FSMContext):
83 # Update state and data
84 await Form.next()
85 await state.update_data(age=int(message.text))
86
87 # Configure ReplyKeyboardMarkup
88 markup = types.ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
89 markup.add("Male", "Female")
90 markup.add("Other")
91
92 await message.reply("What is your gender?", reply_markup=markup)
93
94
95@dp.message_handler(lambda message: message.text not in ["Male", "Female", "Other"], state=Form.gender)
96async def process_gender_invalid(message: types.Message):
97 """
98 In this example gender has to be one of: Male, Female, Other.
99 """
100 return await message.reply("Bad gender name. Choose your gender from the keyboard.")
101
102
103@dp.message_handler(state=Form.gender)
104async def process_gender(message: types.Message, state: FSMContext):
105 async with state.proxy() as data:
106 data['gender'] = message.text
107
108 # Remove keyboard
109 markup = types.ReplyKeyboardRemove()
110
111 # And send message
112 await bot.send_message(
113 message.chat.id,
114 md.text(
115 md.text('Hi! Nice to meet you,', md.bold(data['name'])),
116 md.text('Age:', md.code(data['age'])),
117 md.text('Gender:', data['gender']),
118 sep='\n',
119 ),
120 reply_markup=markup,
121 parse_mode=ParseMode.MARKDOWN,
122 )
123
124 # Finish conversation
125 await state.finish()
126
127
128if __name__ == '__main__':
129 executor.start_polling(dp, skip_updates=True)