Skip to content

Commit

Permalink
Merge pull request #2321 from coder2020official/statesv2
Browse files Browse the repository at this point in the history
StatesV2: Support for topics, multibots, and business messages
  • Loading branch information
coder2020official authored Aug 11, 2024
2 parents 21fef81 + d44ebce commit 21e92fb
Show file tree
Hide file tree
Showing 28 changed files with 2,757 additions and 985 deletions.
201 changes: 132 additions & 69 deletions examples/asynchronous_telebot/custom_states.py
Original file line number Diff line number Diff line change
@@ -1,91 +1,154 @@
from telebot import asyncio_filters
from telebot.async_telebot import AsyncTeleBot

# list of storages, you can use any storage
from telebot import async_telebot, asyncio_filters, types
from telebot.asyncio_storage import StateMemoryStorage
from telebot.states import State, StatesGroup
from telebot.states.asyncio.context import StateContext

# new feature for states.
from telebot.asyncio_handler_backends import State, StatesGroup

# default state storage is statememorystorage
bot = AsyncTeleBot('TOKEN', state_storage=StateMemoryStorage())
# Initialize the bot
state_storage = StateMemoryStorage() # don't use this in production; switch to redis
bot = async_telebot.AsyncTeleBot("TOKEN", state_storage=state_storage)


# Just create different statesgroup
# Define states
class MyStates(StatesGroup):
name = State() # statesgroup should contain states
surname = State()
name = State()
age = State()
color = State()
hobby = State()


# Start command handler
@bot.message_handler(commands=["start"])
async def start_ex(message: types.Message, state: StateContext):
await state.set(MyStates.name)
await bot.send_message(
message.chat.id,
"Hello! What is your first name?",
reply_to_message_id=message.message_id,
)

# set_state -> sets a new state
# delete_state -> delets state if exists
# get_state -> returns state if exists

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

@bot.message_handler(commands=['start'])
async def start_ex(message):
"""
Start command. Here we are starting state
"""
await bot.set_state(message.from_user.id, MyStates.name, message.chat.id)
await bot.send_message(message.chat.id, 'Hi, write me a name')



@bot.message_handler(state="*", commands='cancel')
async def any_state(message):
"""
Cancel state
"""
await bot.send_message(message.chat.id, "Your state was cancelled.")
await bot.delete_state(message.from_user.id, message.chat.id)

# Handler for name input
@bot.message_handler(state=MyStates.name)
async def name_get(message):
"""
State 1. Will process when user's state is MyStates.name.
"""
await bot.send_message(message.chat.id, f'Now write me a surname')
await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id)
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
data['name'] = message.text


@bot.message_handler(state=MyStates.surname)
async def ask_age(message):
"""
State 2. Will process when user's state is MyStates.surname.
"""
await bot.send_message(message.chat.id, "What is your age?")
await bot.set_state(message.from_user.id, MyStates.age, message.chat.id)
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
data['surname'] = message.text

# result
async def name_get(message: types.Message, state: StateContext):
await state.set(MyStates.age)
await bot.send_message(
message.chat.id, "How old are you?", reply_to_message_id=message.message_id
)
await state.add_data(name=message.text)


# Handler for age input
@bot.message_handler(state=MyStates.age, is_digit=True)
async def ready_for_answer(message):
"""
State 3. Will process when user's state is MyStates.age.
"""
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
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")
await bot.delete_state(message.from_user.id, message.chat.id)

#incorrect number
async def ask_color(message: types.Message, state: StateContext):
await state.set(MyStates.color)
await state.add_data(age=message.text)

# Define reply keyboard for color selection
keyboard = types.ReplyKeyboardMarkup(row_width=2)
colors = ["Red", "Green", "Blue", "Yellow", "Purple", "Orange", "Other"]
buttons = [types.KeyboardButton(color) for color in colors]
keyboard.add(*buttons)

await bot.send_message(
message.chat.id,
"What is your favorite color? Choose from the options below.",
reply_markup=keyboard,
reply_to_message_id=message.message_id,
)


# Handler for color input
@bot.message_handler(state=MyStates.color)
async def ask_hobby(message: types.Message, state: StateContext):
await state.set(MyStates.hobby)
await state.add_data(color=message.text)

# Define reply keyboard for hobby selection
keyboard = types.ReplyKeyboardMarkup(row_width=2)
hobbies = ["Reading", "Traveling", "Gaming", "Cooking"]
buttons = [types.KeyboardButton(hobby) for hobby in hobbies]
keyboard.add(*buttons)

await bot.send_message(
message.chat.id,
"What is one of your hobbies? Choose from the options below.",
reply_markup=keyboard,
reply_to_message_id=message.message_id,
)


# Handler for hobby input; use filters to ease validation
@bot.message_handler(
state=MyStates.hobby, text=["Reading", "Traveling", "Gaming", "Cooking"]
)
async def finish(message: types.Message, state: StateContext):
async with state.data() as data:
name = data.get("name")
age = data.get("age")
color = data.get("color")
hobby = message.text # Get the hobby from the message text

# Provide a fun fact based on color
color_facts = {
"Red": "Red is often associated with excitement and passion.",
"Green": "Green is the color of nature and tranquility.",
"Blue": "Blue is known for its calming and serene effects.",
"Yellow": "Yellow is a cheerful color often associated with happiness.",
"Purple": "Purple signifies royalty and luxury.",
"Orange": "Orange is a vibrant color that stimulates enthusiasm.",
"Other": "Colors have various meanings depending on context.",
}
color_fact = color_facts.get(
color, "Colors have diverse meanings, and yours is unique!"
)

msg = (
f"Thank you for sharing! Here is a summary of your information:\n"
f"First Name: {name}\n"
f"Age: {age}\n"
f"Favorite Color: {color}\n"
f"Fun Fact about your color: {color_fact}\n"
f"Favorite Hobby: {hobby}"
)

await bot.send_message(
message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id
)
await state.delete()


# Handler for incorrect age input
@bot.message_handler(state=MyStates.age, is_digit=False)
async def age_incorrect(message):
"""
Will process for wrong input when state is MyState.age
"""
await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
async def age_incorrect(message: types.Message):
await bot.send_message(
message.chat.id,
"Please enter a valid number for age.",
reply_to_message_id=message.message_id,
)

# register filters

# Add custom filters
bot.add_custom_filter(asyncio_filters.StateFilter(bot))
bot.add_custom_filter(asyncio_filters.IsDigitFilter())
bot.add_custom_filter(asyncio_filters.TextMatchFilter())

# necessary for state parameter in handlers.
from telebot.states.asyncio.middleware import StateMiddleware

bot.setup_middleware(StateMiddleware(bot))

# Start polling
import asyncio
asyncio.run(bot.polling())

asyncio.run(bot.polling())
Loading

0 comments on commit 21e92fb

Please sign in to comment.