Fix dialogue handler structure and enhance duration input
- Fix handler type mismatch error by properly ordering dialogue entry - Move .enter_dialogue() before handlers that need dialogue context - Remove duplicate command handler branches - Add duration callback handler for inline keyboard buttons - Add duration keyboard with 1, 3, 7, and 14 day options - Refactor duration processing into shared function - Simplify slots keyboard layout to single row - Improve code organization and error handling
This commit is contained in:
@@ -9,9 +9,6 @@ pub mod start;
|
||||
pub use help::handle_help;
|
||||
pub use my_bids::handle_my_bids;
|
||||
pub use my_listings::handle_my_listings;
|
||||
pub use new_listing::{
|
||||
handle_new_listing, new_listing_callback_handler, new_listing_dialogue_handler,
|
||||
};
|
||||
pub use settings::handle_settings;
|
||||
pub use start::handle_start;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::{
|
||||
},
|
||||
message_utils::{is_cancel, is_cancel_or_no, UserHandleAndId},
|
||||
sqlite_storage::SqliteStorage,
|
||||
HandlerResult,
|
||||
Command, HandlerResult,
|
||||
};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Default)]
|
||||
@@ -55,24 +55,33 @@ pub enum ListingWizardState {
|
||||
}
|
||||
|
||||
// Type alias for the dialogue
|
||||
pub type NewListingDialogue = Dialogue<ListingWizardState, SqliteStorage<Json>>;
|
||||
type NewListingDialogue = Dialogue<ListingWizardState, SqliteStorage<Json>>;
|
||||
|
||||
// Create the dialogue handler tree for new listing wizard
|
||||
pub fn new_listing_dialogue_handler() -> Handler<'static, HandlerResult, DpHandlerDescription> {
|
||||
pub fn new_listing_handler() -> Handler<'static, HandlerResult, DpHandlerDescription> {
|
||||
dptree::entry()
|
||||
.branch(
|
||||
Update::filter_message()
|
||||
.enter_dialogue::<Message, SqliteStorage<Json>, ListingWizardState>()
|
||||
.branch(dptree::entry().filter_command::<Command>().branch(
|
||||
dptree::case![Command::NewListing].endpoint(handle_new_listing_command),
|
||||
))
|
||||
.branch(dptree::case![ListingWizardState::Start].endpoint(start_new_listing))
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingTitle(state)].endpoint(handle_title_input),
|
||||
dptree::case![ListingWizardState::AwaitingTitle(state)]
|
||||
.endpoint(handle_title_input),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingDescription(state)]
|
||||
.endpoint(handle_description_input),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingPrice(state)].endpoint(handle_price_input),
|
||||
dptree::case![ListingWizardState::AwaitingPrice(state)]
|
||||
.endpoint(handle_price_input),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingSlots(state)].endpoint(handle_slots_input),
|
||||
dptree::case![ListingWizardState::AwaitingSlots(state)]
|
||||
.endpoint(handle_slots_input),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingStartTime(state)]
|
||||
@@ -83,19 +92,29 @@ pub fn new_listing_dialogue_handler() -> Handler<'static, HandlerResult, DpHandl
|
||||
.endpoint(handle_duration_input),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::ViewingDraft(state)].endpoint(handle_viewing_draft),
|
||||
dptree::case![ListingWizardState::ViewingDraft(state)]
|
||||
.endpoint(handle_viewing_draft),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingListing(state)]
|
||||
.endpoint(handle_editing_screen),
|
||||
)
|
||||
.branch(dptree::case![ListingWizardState::EditingTitle(state)].endpoint(handle_edit_title))
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingTitle(state)]
|
||||
.endpoint(handle_edit_title),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingDescription(state)]
|
||||
.endpoint(handle_edit_description),
|
||||
)
|
||||
.branch(dptree::case![ListingWizardState::EditingPrice(state)].endpoint(handle_edit_price))
|
||||
.branch(dptree::case![ListingWizardState::EditingSlots(state)].endpoint(handle_edit_slots))
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingPrice(state)]
|
||||
.endpoint(handle_edit_price),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingSlots(state)]
|
||||
.endpoint(handle_edit_slots),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingStartTime(state)]
|
||||
.endpoint(handle_edit_start_time),
|
||||
@@ -103,11 +122,64 @@ pub fn new_listing_dialogue_handler() -> Handler<'static, HandlerResult, DpHandl
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingDuration(state)]
|
||||
.endpoint(handle_edit_duration),
|
||||
),
|
||||
)
|
||||
.branch(
|
||||
Update::filter_callback_query()
|
||||
.enter_dialogue::<CallbackQuery, SqliteStorage<Json>, ListingWizardState>()
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingDescription(state)]
|
||||
.endpoint(handle_description_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingSlots(state)]
|
||||
.endpoint(handle_slots_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingStartTime(state)]
|
||||
.endpoint(handle_start_time_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingDuration(state)]
|
||||
.endpoint(handle_duration_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::ViewingDraft(state)]
|
||||
.endpoint(handle_viewing_draft_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingListing(state)]
|
||||
.endpoint(handle_editing_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingTitle(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingDescription(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingPrice(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingSlots(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingStartTime(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingDuration(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle the /newlisting command - starts the dialogue
|
||||
pub async fn handle_new_listing(
|
||||
// Handle the /newlisting command - starts the dialogue by setting it to Start state
|
||||
async fn handle_new_listing_command(
|
||||
bot: Bot,
|
||||
dialogue: NewListingDialogue,
|
||||
msg: Message,
|
||||
@@ -118,6 +190,28 @@ pub async fn handle_new_listing(
|
||||
msg.chat.id
|
||||
);
|
||||
|
||||
// Initialize the dialogue to Start state
|
||||
dialogue.update(ListingWizardState::Start).await?;
|
||||
|
||||
let response = "🛍️ <b>Creating New Fixed Price Listing</b>\n\n\
|
||||
Let's create your fixed price listing step by step!\n\n\
|
||||
<i>Step 1 of 6: Title</i>\n\
|
||||
Please enter a title for your listing (max 100 characters):";
|
||||
|
||||
bot.send_message(msg.chat.id, response)
|
||||
.parse_mode(ParseMode::Html)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Handle the /newlisting command - starts the dialogue (called from within dialogue context)
|
||||
async fn handle_new_listing(bot: Bot, dialogue: NewListingDialogue, msg: Message) -> HandlerResult {
|
||||
info!(
|
||||
"User {} ({}) started new fixed price listing wizard",
|
||||
msg.chat.username().unwrap_or("unknown"),
|
||||
msg.chat.id
|
||||
);
|
||||
|
||||
let response = "🛍️ <b>Creating New Fixed Price Listing</b>\n\n\
|
||||
Let's create your fixed price listing step by step!\n\n\
|
||||
<i>Step 1 of 6: Title</i>\n\
|
||||
@@ -275,54 +369,6 @@ pub async fn handle_description_callback(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Create callback query handler for skip button, slots buttons, and start time button
|
||||
pub fn new_listing_callback_handler() -> Handler<'static, HandlerResult, DpHandlerDescription> {
|
||||
dptree::entry()
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingDescription(state)]
|
||||
.endpoint(handle_description_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingSlots(state)].endpoint(handle_slots_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::AwaitingStartTime(state)]
|
||||
.endpoint(handle_start_time_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::ViewingDraft(state)]
|
||||
.endpoint(handle_viewing_draft_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingListing(state)]
|
||||
.endpoint(handle_editing_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingTitle(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingDescription(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingPrice(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingSlots(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingStartTime(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
.branch(
|
||||
dptree::case![ListingWizardState::EditingDuration(state)]
|
||||
.endpoint(handle_edit_field_callback),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn handle_slots_callback(
|
||||
bot: Bot,
|
||||
dialogue: NewListingDialogue,
|
||||
@@ -609,11 +655,21 @@ async fn process_start_time_and_respond(
|
||||
|
||||
bot.send_message(chat_id, response)
|
||||
.parse_mode(ParseMode::Html)
|
||||
.reply_markup(create_duration_keyboard())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_duration_keyboard() -> InlineKeyboardMarkup {
|
||||
InlineKeyboardMarkup::new([[
|
||||
InlineKeyboardButton::callback("1 day", "duration_1_day"),
|
||||
InlineKeyboardButton::callback("3 days", "duration_3_days"),
|
||||
InlineKeyboardButton::callback("7 days", "duration_7_days"),
|
||||
InlineKeyboardButton::callback("14 days", "duration_14_days"),
|
||||
]])
|
||||
}
|
||||
|
||||
pub async fn handle_price_input(
|
||||
bot: Bot,
|
||||
dialogue: NewListingDialogue,
|
||||
@@ -667,16 +723,12 @@ pub async fn handle_price_input(
|
||||
price
|
||||
);
|
||||
|
||||
let slots_buttons = InlineKeyboardMarkup::new([
|
||||
[
|
||||
let slots_buttons = InlineKeyboardMarkup::new([[
|
||||
InlineKeyboardButton::callback("1", "slots_1"),
|
||||
InlineKeyboardButton::callback("2", "slots_2"),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton::callback("5", "slots_5"),
|
||||
InlineKeyboardButton::callback("10", "slots_10"),
|
||||
],
|
||||
]);
|
||||
]]);
|
||||
|
||||
bot.send_message(chat_id, response)
|
||||
.parse_mode(ParseMode::Html)
|
||||
@@ -781,7 +833,7 @@ pub async fn handle_start_time_input(
|
||||
pub async fn handle_duration_input(
|
||||
bot: Bot,
|
||||
dialogue: NewListingDialogue,
|
||||
mut draft: ListingDraft,
|
||||
draft: ListingDraft,
|
||||
msg: Message,
|
||||
) -> HandlerResult {
|
||||
let chat_id = msg.chat.id;
|
||||
@@ -818,14 +870,56 @@ pub async fn handle_duration_input(
|
||||
}
|
||||
};
|
||||
|
||||
process_duration_and_respond(bot, dialogue, draft, chat_id, duration).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_duration_callback(
|
||||
bot: Bot,
|
||||
dialogue: NewListingDialogue,
|
||||
draft: ListingDraft,
|
||||
callback_query: CallbackQuery,
|
||||
) -> HandlerResult {
|
||||
let data = match callback_query.data.as_deref() {
|
||||
Some(data) => data,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let chat_id = match callback_query.message {
|
||||
Some(message) => message.chat().id,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
let days = match data {
|
||||
"duration_1_day" => 1,
|
||||
"duration_3_days" => 3,
|
||||
"duration_7_days" => 7,
|
||||
"duration_14_days" => 14,
|
||||
_ => {
|
||||
bot.send_message(
|
||||
chat_id,
|
||||
"❌ Invalid duration. Please enter number of days (1-14):",
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
process_duration_and_respond(bot, dialogue, draft, chat_id, days).await
|
||||
}
|
||||
|
||||
async fn process_duration_and_respond(
|
||||
bot: Bot,
|
||||
dialogue: NewListingDialogue,
|
||||
mut draft: ListingDraft,
|
||||
chat_id: ChatId,
|
||||
duration: i32,
|
||||
) -> HandlerResult {
|
||||
draft.duration_hours = duration;
|
||||
dialogue
|
||||
.update(ListingWizardState::ViewingDraft(draft.clone()))
|
||||
.await?;
|
||||
|
||||
show_confirmation(bot, chat_id, draft).await?;
|
||||
|
||||
Ok(())
|
||||
show_confirmation(bot, chat_id, draft).await
|
||||
}
|
||||
|
||||
async fn show_confirmation(bot: Bot, chat_id: ChatId, state: ListingDraft) -> HandlerResult {
|
||||
|
||||
20
src/main.rs
20
src/main.rs
@@ -8,13 +8,13 @@ mod wizard_utils;
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use teloxide::dispatching::dialogue::serializer::Json;
|
||||
use teloxide::{prelude::*, types::CallbackQuery, utils::command::BotCommands};
|
||||
use teloxide::{prelude::*, utils::command::BotCommands};
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
use commands::new_listing::ListingWizardState;
|
||||
use commands::*;
|
||||
use config::Config;
|
||||
|
||||
use crate::commands::new_listing::new_listing_handler;
|
||||
use crate::sqlite_storage::SqliteStorage;
|
||||
|
||||
pub type HandlerResult = anyhow::Result<()>;
|
||||
@@ -52,26 +52,16 @@ async fn main() -> Result<()> {
|
||||
// Create dispatcher with dialogue system
|
||||
Dispatcher::builder(
|
||||
bot,
|
||||
dptree::entry()
|
||||
.branch(
|
||||
Update::filter_message()
|
||||
.enter_dialogue::<Message, SqliteStorage<Json>, ListingWizardState>()
|
||||
.branch(
|
||||
dptree::entry().branch(new_listing_handler()).branch(
|
||||
Update::filter_message().branch(
|
||||
dptree::entry()
|
||||
.filter_command::<Command>()
|
||||
.branch(dptree::case![Command::Start].endpoint(handle_start))
|
||||
.branch(dptree::case![Command::Help].endpoint(handle_help))
|
||||
.branch(dptree::case![Command::NewListing].endpoint(handle_new_listing))
|
||||
.branch(dptree::case![Command::MyListings].endpoint(handle_my_listings))
|
||||
.branch(dptree::case![Command::MyBids].endpoint(handle_my_bids))
|
||||
.branch(dptree::case![Command::Settings].endpoint(handle_settings)),
|
||||
)
|
||||
.branch(new_listing_dialogue_handler()),
|
||||
)
|
||||
.branch(
|
||||
Update::filter_callback_query()
|
||||
.enter_dialogue::<CallbackQuery, SqliteStorage<Json>, ListingWizardState>()
|
||||
.branch(new_listing_callback_handler()),
|
||||
),
|
||||
),
|
||||
)
|
||||
.dependencies(dptree::deps![db_pool, dialog_storage])
|
||||
|
||||
Reference in New Issue
Block a user