Skip to content

Commit 21e92fb

Browse files
Merge pull request #2321 from coder2020official/statesv2
StatesV2: Support for topics, multibots, and business messages
2 parents 21fef81 + d44ebce commit 21e92fb

28 files changed

+2757
-985
lines changed
Lines changed: 132 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,154 @@
1-
from telebot import asyncio_filters
2-
from telebot.async_telebot import AsyncTeleBot
3-
4-
# list of storages, you can use any storage
1+
from telebot import async_telebot, asyncio_filters, types
52
from telebot.asyncio_storage import StateMemoryStorage
3+
from telebot.states import State, StatesGroup
4+
from telebot.states.asyncio.context import StateContext
65

7-
# new feature for states.
8-
from telebot.asyncio_handler_backends import State, StatesGroup
9-
10-
# default state storage is statememorystorage
11-
bot = AsyncTeleBot('TOKEN', state_storage=StateMemoryStorage())
6+
# Initialize the bot
7+
state_storage = StateMemoryStorage() # don't use this in production; switch to redis
8+
bot = async_telebot.AsyncTeleBot("TOKEN", state_storage=state_storage)
129

1310

14-
# Just create different statesgroup
11+
# Define states
1512
class MyStates(StatesGroup):
16-
name = State() # statesgroup should contain states
17-
surname = State()
13+
name = State()
1814
age = State()
15+
color = State()
16+
hobby = State()
1917

2018

19+
# Start command handler
20+
@bot.message_handler(commands=["start"])
21+
async def start_ex(message: types.Message, state: StateContext):
22+
await state.set(MyStates.name)
23+
await bot.send_message(
24+
message.chat.id,
25+
"Hello! What is your first name?",
26+
reply_to_message_id=message.message_id,
27+
)
2128

22-
# set_state -> sets a new state
23-
# delete_state -> delets state if exists
24-
# get_state -> returns state if exists
2529

30+
# Cancel command handler
31+
@bot.message_handler(state="*", commands=["cancel"])
32+
async def any_state(message: types.Message, state: StateContext):
33+
await state.delete()
34+
await bot.send_message(
35+
message.chat.id,
36+
"Your information has been cleared. Type /start to begin again.",
37+
reply_to_message_id=message.message_id,
38+
)
2639

27-
@bot.message_handler(commands=['start'])
28-
async def start_ex(message):
29-
"""
30-
Start command. Here we are starting state
31-
"""
32-
await bot.set_state(message.from_user.id, MyStates.name, message.chat.id)
33-
await bot.send_message(message.chat.id, 'Hi, write me a name')
34-
35-
36-
37-
@bot.message_handler(state="*", commands='cancel')
38-
async def any_state(message):
39-
"""
40-
Cancel state
41-
"""
42-
await bot.send_message(message.chat.id, "Your state was cancelled.")
43-
await bot.delete_state(message.from_user.id, message.chat.id)
4440

41+
# Handler for name input
4542
@bot.message_handler(state=MyStates.name)
46-
async def name_get(message):
47-
"""
48-
State 1. Will process when user's state is MyStates.name.
49-
"""
50-
await bot.send_message(message.chat.id, f'Now write me a surname')
51-
await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id)
52-
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
53-
data['name'] = message.text
54-
55-
56-
@bot.message_handler(state=MyStates.surname)
57-
async def ask_age(message):
58-
"""
59-
State 2. Will process when user's state is MyStates.surname.
60-
"""
61-
await bot.send_message(message.chat.id, "What is your age?")
62-
await bot.set_state(message.from_user.id, MyStates.age, message.chat.id)
63-
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
64-
data['surname'] = message.text
65-
66-
# result
43+
async def name_get(message: types.Message, state: StateContext):
44+
await state.set(MyStates.age)
45+
await bot.send_message(
46+
message.chat.id, "How old are you?", reply_to_message_id=message.message_id
47+
)
48+
await state.add_data(name=message.text)
49+
50+
51+
# Handler for age input
6752
@bot.message_handler(state=MyStates.age, is_digit=True)
68-
async def ready_for_answer(message):
69-
"""
70-
State 3. Will process when user's state is MyStates.age.
71-
"""
72-
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
73-
await bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html")
74-
await bot.delete_state(message.from_user.id, message.chat.id)
75-
76-
#incorrect number
53+
async def ask_color(message: types.Message, state: StateContext):
54+
await state.set(MyStates.color)
55+
await state.add_data(age=message.text)
56+
57+
# Define reply keyboard for color selection
58+
keyboard = types.ReplyKeyboardMarkup(row_width=2)
59+
colors = ["Red", "Green", "Blue", "Yellow", "Purple", "Orange", "Other"]
60+
buttons = [types.KeyboardButton(color) for color in colors]
61+
keyboard.add(*buttons)
62+
63+
await bot.send_message(
64+
message.chat.id,
65+
"What is your favorite color? Choose from the options below.",
66+
reply_markup=keyboard,
67+
reply_to_message_id=message.message_id,
68+
)
69+
70+
71+
# Handler for color input
72+
@bot.message_handler(state=MyStates.color)
73+
async def ask_hobby(message: types.Message, state: StateContext):
74+
await state.set(MyStates.hobby)
75+
await state.add_data(color=message.text)
76+
77+
# Define reply keyboard for hobby selection
78+
keyboard = types.ReplyKeyboardMarkup(row_width=2)
79+
hobbies = ["Reading", "Traveling", "Gaming", "Cooking"]
80+
buttons = [types.KeyboardButton(hobby) for hobby in hobbies]
81+
keyboard.add(*buttons)
82+
83+
await bot.send_message(
84+
message.chat.id,
85+
"What is one of your hobbies? Choose from the options below.",
86+
reply_markup=keyboard,
87+
reply_to_message_id=message.message_id,
88+
)
89+
90+
91+
# Handler for hobby input; use filters to ease validation
92+
@bot.message_handler(
93+
state=MyStates.hobby, text=["Reading", "Traveling", "Gaming", "Cooking"]
94+
)
95+
async def finish(message: types.Message, state: StateContext):
96+
async with state.data() as data:
97+
name = data.get("name")
98+
age = data.get("age")
99+
color = data.get("color")
100+
hobby = message.text # Get the hobby from the message text
101+
102+
# Provide a fun fact based on color
103+
color_facts = {
104+
"Red": "Red is often associated with excitement and passion.",
105+
"Green": "Green is the color of nature and tranquility.",
106+
"Blue": "Blue is known for its calming and serene effects.",
107+
"Yellow": "Yellow is a cheerful color often associated with happiness.",
108+
"Purple": "Purple signifies royalty and luxury.",
109+
"Orange": "Orange is a vibrant color that stimulates enthusiasm.",
110+
"Other": "Colors have various meanings depending on context.",
111+
}
112+
color_fact = color_facts.get(
113+
color, "Colors have diverse meanings, and yours is unique!"
114+
)
115+
116+
msg = (
117+
f"Thank you for sharing! Here is a summary of your information:\n"
118+
f"First Name: {name}\n"
119+
f"Age: {age}\n"
120+
f"Favorite Color: {color}\n"
121+
f"Fun Fact about your color: {color_fact}\n"
122+
f"Favorite Hobby: {hobby}"
123+
)
124+
125+
await bot.send_message(
126+
message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id
127+
)
128+
await state.delete()
129+
130+
131+
# Handler for incorrect age input
77132
@bot.message_handler(state=MyStates.age, is_digit=False)
78-
async def age_incorrect(message):
79-
"""
80-
Will process for wrong input when state is MyState.age
81-
"""
82-
await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
133+
async def age_incorrect(message: types.Message):
134+
await bot.send_message(
135+
message.chat.id,
136+
"Please enter a valid number for age.",
137+
reply_to_message_id=message.message_id,
138+
)
83139

84-
# register filters
85140

141+
# Add custom filters
86142
bot.add_custom_filter(asyncio_filters.StateFilter(bot))
87143
bot.add_custom_filter(asyncio_filters.IsDigitFilter())
144+
bot.add_custom_filter(asyncio_filters.TextMatchFilter())
145+
146+
# necessary for state parameter in handlers.
147+
from telebot.states.asyncio.middleware import StateMiddleware
88148

149+
bot.setup_middleware(StateMiddleware(bot))
89150

151+
# Start polling
90152
import asyncio
91-
asyncio.run(bot.polling())
153+
154+
asyncio.run(bot.polling())

0 commit comments

Comments
 (0)