Проблема с aiogram-calendar и FSM
День добрый! Народ, стою на асфальте в лыжах... Понимаю что проблема не в лыжах и не в асфальте, поэтому прошу помощи. Значит так! Telegram бот c применением aiogram.
Проблема в чём? Добавил aiogram-calendar + FSM. Всё вроде бы работает пока не до ходит до календаря. При выборе даты или месяца, повторно запускается календарь ( set_date) и на следующий handler не хочет прыгать. Функция process_dialog_calendar отвечает за выбор даты или месяца. Блок if выполняется только если дата выбрана.
class FSMClient(StatesGroup):
state_master = State()
state_calendar = State()
#
#
#
async def set_masters(call: types.CallbackQuery):
await call.message.delete_reply_markup()
await call.message.delete()
await call.message.answer(str.GET_MASTERS, reply_markup=keyboards_client.btn_add_masters)
await FSMClient.state_master.set()
async def set_date(call: types.CallbackQuery):
await call.message.delete_reply_markup()
await call.message.delete()
await call.message.answer(str.SET_DATE, reply_markup=await SimpleCalendar().start_calendar())
await FSMClient.next()
# Здесь функция set_date вызывается повторно снова и снова после каждого нажатия выбора даты. Следующий handler не вызывается.
@dp.callback_query_handler(simple_cal_callback.filter())
async def process_dialog_calendar(call: types.CallbackQuery, callback_data: dict):
selected, date = await SimpleCalendar().process_selection(call, callback_data)
if selected:
str.USER_DATE_SELECT = str.USER_DATE + f' {date.strftime("%d.%m.%Y")} \n'
await call.message.answer(str.USER_DATE_SELECT)
async def set_time(call: types.CallbackQuery):
await call.message.delete()
inline_timepicker.init(
datetime.time(12),
datetime.time(1),
datetime.time(23),
)
await call.message.answer(text=str.YOUR_TIME,
reply_markup=inline_timepicker.get_keyboard())
#
#
#
def register_handlers_client(dp: Dispatcher):
dp.register_callback_query_handler(set_date, state=FSMClient.state_master)
dp.register_callback_query_handler(set_time, state=FSMClient.state_calendar)
Если финишировать state в set_date вот так:
async def set_date(call: types.CallbackQuery, state: FSMContext):
# После финиша state handler выбора даты выполняется.
await state.finish()
await call.message.delete_reply_markup()
await call.message.delete()
await call.message.answer(str.SET_DATE, reply_markup=await SimpleCalendar().start_calendar())
следующий handler(process_dialog_calendar) срабатывает, выбираю дату и всё set_time запускаться не хочет.
И казалось бы в process_dialog_calendar в блоке if отличное место для установки state(Ну! я так думал).
@dp.callback_query_handler(simple_cal_callback.filter())
async def process_dialog_calendar(call: types.CallbackQuery, callback_data: dict):
selected, date = await SimpleCalendar().process_selection(call, callback_data)
if selected:
str.USER_DATE_SELECT = str.USER_DATE + f' {date.strftime("%d.%m.%Y")} \n'
await call.message.answer(str.USER_DATE_SELECT)
# Здесь магия не произошла set_time не сработал.
await FSMClient.state_calendar.set()
Но нет. У aiogram вместе FSM и aiogram-calendar были другие планы и рога мне запилили быстро. Следующий handler set_time не сработал.
Короче ребята, спасайте! Иначе заказчица разорвёт мою тощую задницу на кусочки, а так как это моя жена, мне вдвойне страшно. Надеюсь доступно описал проблему. Извините если что не так. Заранее спасибо! Пример календаря https://github.com/noXplode/aiogram_calendar.
Единственное что приходит в голову выполнить функцию process_dialog_calendarгде-то на стороне и вернуть результат в set_date. Только как это реализовать не могу сообразить. Спасибо!
Ребятушки, разобрался! Полезная функция у FSM имеется reset_state(with_data=False), параметр with_data отвечает за сохранение данных при сбросе состояния. Ну и так, ещё немного на говнокодил и вроде бы всё поехало. Всем спасибо!
По просьбе Vlad Mikliaiev добавляю код:
async def set_master(call: types.CallbackQuery, state: FSMContext):
await call.message.answer(strings_client.CHECK_MASTER, reply_markup=keyboards_client.btn_add_masters)
await FSMClient.state_master.set() # Установка состояния.
async def set_date(call: types.CallbackQuery, state: FSMContext):
await state.update_data(master=call.data) # Запись данных в "state".
await state.reset_state(with_data=False) # Здесь выполняется сброс "state". Календарь начинает работать.
await call.message.answer(strings_client.CHECK_DATE, reply_markup=await start_calendar()) # Запуск календаря.
@dp_client.callback_query_handler(calendar_callback.filter()) # "handler" выбора даты либо обновления календаря.
async def select_date(call: types.CallbackQuery, callback_data: dict, state: FSMContext):
selected, date = await process_selection(call, callback_data)
if selected:
await FSMClient.state_calendar.set() # Снова запускаю "state".
await state.update_data(date=f'{date.strftime("%d.%m.%Y")}') # Запись даты в "state".
Ответы (2 шт):
Расскажите, пожалуйста, подробнее, как получилось разобраться с этим aiogram-calendar и FSM?
вообще всё фигово)
две хендлеры зарегистрированы внизу. один хендлер - декоратор.
а то что хендлер обрабатывается бесконечно: в классе две State, ты в последнем, и ты пытаешься перейти на следующий НЕ существующий State
вот код который я как то попытался пофиксить
class FSMClient(StatesGroup):
state_date = State()
state_process = State()
state_time = State()
async def set_masters(call: types.CallbackQuery):
await call.message.delete_reply_markup()
await call.message.delete()
await call.message.answer(str.GET_MASTERS, reply_markup=keyboards_client.btn_add_masters)
await FSMClient.state_date.set()
async def set_date(call: types.CallbackQuery):
await call.message.delete_reply_markup()
await call.message.delete()
await call.message.answer(str.SET_DATE, reply_markup=await SimpleCalendar().start_calendar())
await FSMClient.next()
async def process_dialog_calendar(call: types.CallbackQuery, callback_data: dict):
selected, date = await SimpleCalendar().process_selection(call, callback_data)
if selected:
str.USER_DATE_SELECT = str.USER_DATE + f' {date.strftime("%d.%m.%Y")} \n'
await call.message.answer(str.USER_DATE_SELECT)
await FSMClient.next()
async def set_time(call: types.CallbackQuery):
await call.message.delete()
inline_timepicker.init(
datetime.time(12),
datetime.time(1),
datetime.time(23),
)
await call.message.answer(text=str.YOUR_TIME,
reply_markup=inline_timepicker.get_keyboard())
def register_handlers_client(dp: Dispatcher):
dp.register_callback_query_handler(set_date, state=FSMClient.state_date)
dp.register_callback_query_handler(process_dialog_calendar, simple_cal_callback.filter(), state=FSMClient.state_process)
dp.register_callback_query_handler(set_time, state=FSMClient.state_time)