From 56f28d58911f1179447a433affdbd3adff46db46 Mon Sep 17 00:00:00 2001 From: liyunze <50455574+liyunze-coding@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:24:40 +0800 Subject: [PATCH] added edit faq command --- src/commands/faq.rs | 122 +++++++++++++++++++++++++++++++++------ src/database/database.rs | 7 +-- 2 files changed, 106 insertions(+), 23 deletions(-) diff --git a/src/commands/faq.rs b/src/commands/faq.rs index 7e2a0d7..0cc723b 100644 --- a/src/commands/faq.rs +++ b/src/commands/faq.rs @@ -1,7 +1,10 @@ +use std::string; + use crate::{Context, Error}; use futures::{Stream, StreamExt}; use poise::CreateReply; -use serenity::all::{CreateEmbed}; +use rusqlite::Connection; +use serenity::all::CreateEmbed; // Define your FAQ struct struct Faq { @@ -10,6 +13,21 @@ struct Faq { description: String, } +fn format_embed_description>(description: S) -> Result { + let string_val = description.into(); + Ok(str::replace(&string_val, "\\n", "\n")) +} + +fn faq_exists(conn: &Connection, faq: &str) -> Result { + let exists: bool = conn.query_row( + "SELECT EXISTS(SELECT 1 FROM faqs WHERE title = ?)", + [faq], + |row| row.get(0), + )?; + + Ok(exists) +} + async fn autocomplete_faq_titles<'a>( ctx: Context<'_>, partial: &'a str, @@ -43,12 +61,12 @@ async fn autocomplete_faq_titles<'a>( .map(|faq| faq.title) } -#[poise::command(slash_command, subcommands("view", "add"))] +#[poise::command(slash_command, subcommands("view", "add", "edit"))] pub async fn faq(_ctx: Context<'_>) -> Result<(), Error> { Ok(()) } -/// View messages +/// View FAQ #[poise::command(prefix_command, slash_command)] pub async fn view( ctx: Context<'_>, @@ -60,7 +78,10 @@ pub async fn view( let faqs = data .execute_db(move |conn| { - let mut stmt = conn.prepare(&format!("SELECT ID, title, description FROM faqs WHERE title='{}'", &faq_title))?; + let mut stmt = conn.prepare(&format!( + "SELECT ID, title, description FROM faqs WHERE title='{}'", + &faq_title + ))?; let faqs: Vec = stmt .query_map([], |row| { @@ -78,18 +99,17 @@ pub async fn view( let faq = faqs.first().unwrap(); - // Now you can use the faqs Vec - // ctx.say(format!("# {}\n{}", faqs[0].title, faqs[0].description )).await?; - - // #131416 19,20,22 - let embed = CreateEmbed::new().title(&faq.title).description(&faq.description); + let embed_description = format_embed_description(&faq.description).expect("Format error"); + let embed = CreateEmbed::new() + .title(&faq.title) + .description(embed_description); ctx.send(CreateReply::default().embed(embed)).await?; Ok(()) } -/// A subcommand of `parent` +/// Add FAQ #[poise::command( prefix_command, slash_command, @@ -100,19 +120,85 @@ pub async fn add( #[description = "FAQ title"] title: String, #[description = "FAQ description"] description: String, ) -> Result<(), Error> { - let data = ctx.data(); + let data: &crate::Data = ctx.data(); let result = data - .execute_db(|conn| { - conn.execute( - "INSERT INTO faqs (title, description) VALUES (?1, ?2)", - [title, description], - ) + .execute_db({ + let title = title.clone(); + let description = description.clone(); + |conn| { + let exists = faq_exists(conn, &title).unwrap_or(false); + + if !exists { + conn.execute( + "INSERT INTO faqs (title, description) VALUES (?1, ?2)", + [title, description], + ) + } else { + Ok(0) + } + } }) .await?; - // Now you can use the faqs Vec - ctx.say(format!("{} rows affected", result)).await?; + let embed_description = format_embed_description(description).expect("Format error"); + + if result == 1 { + let embed = CreateEmbed::new() + .title(format!("Added FAQ: {}", &title)) + .description(embed_description); + ctx.send(CreateReply::default().embed(embed)).await?; + } else { + let embed = CreateEmbed::new() + .title(format!("Error adding FAQ: {}", &title)) + .description("Title already exists!"); + ctx.send(CreateReply::default().embed(embed)).await?; + } + + Ok(()) +} + +/// Edit FAQ +#[poise::command( + prefix_command, + slash_command, + required_permissions = "MANAGE_MESSAGES" +)] +pub async fn edit( + ctx: Context<'_>, + #[description = "FAQ Title"] + #[autocomplete = "autocomplete_faq_titles"] + title: String, + #[description = "FAQ description"] description: String, +) -> Result<(), Error> { + let data = ctx.data(); + + let result = data + .execute_db({ + let title = title.clone(); + let description = description.clone(); + move |conn| { + conn.execute( + "UPDATE faqs SET description = ?2 WHERE title = ?1", + [title, description], + ) + } + }) + .await?; + + let embed_description = format_embed_description(description).expect("Format error"); + + if result == 1 { + let embed = CreateEmbed::new() + .title(format!("Edited FAQ: {}", &title)) + .description(&embed_description); + ctx.send(CreateReply::default().embed(embed)).await?; + } else { + let embed = CreateEmbed::new() + .title(format!("Error editing FAQ: {}", &title)) + .description(format!("{} rows were affected", result)); + ctx.send(CreateReply::default().embed(embed)).await?; + } Ok(()) } diff --git a/src/database/database.rs b/src/database/database.rs index fc8a784..8457402 100644 --- a/src/database/database.rs +++ b/src/database/database.rs @@ -15,11 +15,8 @@ pub fn init_db(filename: &str) -> Result { fn table_exists(conn: &Connection, table_name: &str) -> Result { let exists: bool = conn.query_row( - &format!( - "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='{}')", - table_name - ), - [], + "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name=?1)", + [table_name], |row| row.get(0), )?;