added edit faq command

This commit is contained in:
liyunze 2026-01-06 13:24:40 +08:00
parent 7c3e66f513
commit 56f28d5891
2 changed files with 106 additions and 23 deletions

View file

@ -1,7 +1,10 @@
use std::string;
use crate::{Context, Error}; use crate::{Context, Error};
use futures::{Stream, StreamExt}; use futures::{Stream, StreamExt};
use poise::CreateReply; use poise::CreateReply;
use serenity::all::{CreateEmbed}; use rusqlite::Connection;
use serenity::all::CreateEmbed;
// Define your FAQ struct // Define your FAQ struct
struct Faq { struct Faq {
@ -10,6 +13,21 @@ struct Faq {
description: String, description: String,
} }
fn format_embed_description<S: Into<String>>(description: S) -> Result<String, Error> {
let string_val = description.into();
Ok(str::replace(&string_val, "\\n", "\n"))
}
fn faq_exists(conn: &Connection, faq: &str) -> Result<bool, Error> {
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>( async fn autocomplete_faq_titles<'a>(
ctx: Context<'_>, ctx: Context<'_>,
partial: &'a str, partial: &'a str,
@ -43,12 +61,12 @@ async fn autocomplete_faq_titles<'a>(
.map(|faq| faq.title) .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> { pub async fn faq(_ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// View messages /// View FAQ
#[poise::command(prefix_command, slash_command)] #[poise::command(prefix_command, slash_command)]
pub async fn view( pub async fn view(
ctx: Context<'_>, ctx: Context<'_>,
@ -60,7 +78,10 @@ pub async fn view(
let faqs = data let faqs = data
.execute_db(move |conn| { .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<Faq> = stmt let faqs: Vec<Faq> = stmt
.query_map([], |row| { .query_map([], |row| {
@ -78,18 +99,17 @@ pub async fn view(
let faq = faqs.first().unwrap(); let faq = faqs.first().unwrap();
// Now you can use the faqs Vec let embed_description = format_embed_description(&faq.description).expect("Format error");
// ctx.say(format!("# {}\n{}", faqs[0].title, faqs[0].description )).await?; let embed = CreateEmbed::new()
.title(&faq.title)
// #131416 19,20,22 .description(embed_description);
let embed = CreateEmbed::new().title(&faq.title).description(&faq.description);
ctx.send(CreateReply::default().embed(embed)).await?; ctx.send(CreateReply::default().embed(embed)).await?;
Ok(()) Ok(())
} }
/// A subcommand of `parent` /// Add FAQ
#[poise::command( #[poise::command(
prefix_command, prefix_command,
slash_command, slash_command,
@ -100,19 +120,85 @@ pub async fn add(
#[description = "FAQ title"] title: String, #[description = "FAQ title"] title: String,
#[description = "FAQ description"] description: String, #[description = "FAQ description"] description: String,
) -> Result<(), Error> { ) -> Result<(), Error> {
let data = ctx.data(); let data: &crate::Data = ctx.data();
let result = data let result = data
.execute_db(|conn| { .execute_db({
conn.execute( let title = title.clone();
"INSERT INTO faqs (title, description) VALUES (?1, ?2)", let description = description.clone();
[title, description], |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?; .await?;
// Now you can use the faqs Vec let embed_description = format_embed_description(description).expect("Format error");
ctx.say(format!("{} rows affected", result)).await?;
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(()) Ok(())
} }

View file

@ -15,11 +15,8 @@ pub fn init_db(filename: &str) -> Result<Connection, rusqlite::Error> {
fn table_exists(conn: &Connection, table_name: &str) -> Result<bool> { fn table_exists(conn: &Connection, table_name: &str) -> Result<bool> {
let exists: bool = conn.query_row( let exists: bool = conn.query_row(
&format!( "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name=?1)",
"SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='{}')", [table_name],
table_name
),
[],
|row| row.get(0), |row| row.get(0),
)?; )?;