added FAQ

This commit is contained in:
liyunze 2026-01-03 19:54:11 +08:00
parent 1673087e07
commit 17fa355ecb
7 changed files with 128 additions and 14 deletions

3
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target /target
.env .env
*.sqlite

13
Cargo.lock generated
View file

@ -453,6 +453,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor",
"futures-io", "futures-io",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
@ -475,6 +476,17 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.31" version = "0.3.31"
@ -1185,6 +1197,7 @@ name = "portfolio-roast-bot"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dotenv", "dotenv",
"futures",
"poise", "poise",
"reqwest", "reqwest",
"rusqlite", "rusqlite",

View file

@ -11,4 +11,5 @@ serde_json = "1.0.145"
serenity = "0.12.5" serenity = "0.12.5"
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] }
serde = "1.0.228" serde = "1.0.228"
rusqlite = { version = "0.37.0", features = ["bundled"] } rusqlite = { version = "0.37.0", features = ["bundled"] }
futures = "0.3.31"

View file

@ -6,4 +6,9 @@ To do:
- [ ] Database - [ ] Database
- [ ] FAQs - [ ] FAQs
- [ ] Slash commands - [ ] Slash commands
- [ ] Portfolio Roast Forum: One tag only - [ ] Portfolio Roast Forum: One tag only
FAQ table
- ID: Primary Key
- title: string
- description: string

Binary file not shown.

View file

@ -1,24 +1,118 @@
use crate::{Context, Error}; use crate::{Context, Error};
use futures::{Stream, StreamExt};
use poise::CreateReply;
use serenity::all::{CreateEmbed};
#[poise::command(slash_command, subcommands("add", "child2"))] // Define your FAQ struct
struct Faq {
// id: i64,
title: String,
description: String,
}
async fn autocomplete_faq_titles<'a>(
ctx: Context<'_>,
partial: &'a str,
) -> impl Stream<Item = String> + 'a {
let data = ctx.data();
let fetch_faqs = data
.execute_db(|conn| {
let mut stmt = conn.prepare("SELECT ID, title, description FROM faqs")?;
let faqs: Vec<Faq> = stmt
.query_map([], |row| {
Ok(Faq {
title: row.get(1)?,
description: row.get(2)?,
})
})?
.collect::<Result<Vec<_>, _>>()?;
Ok(faqs)
})
.await;
let faqs: Vec<Faq> = fetch_faqs.unwrap_or(vec![]);
// Now you can use the faqs Vec
// ctx.say(format!("Found {} FAQs", faqs.len())).await?;
futures::stream::iter(faqs)
.filter(move |faq| futures::future::ready(faq.title.starts_with(partial)))
.map(|faq| faq.title)
}
#[poise::command(slash_command, subcommands("view", "add"))]
pub async fn faq(_ctx: Context<'_>) -> Result<(), Error> { pub async fn faq(_ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// View messages
#[poise::command(prefix_command, slash_command)]
pub async fn view(
ctx: Context<'_>,
#[description = "FAQ Title"]
#[autocomplete = "autocomplete_faq_titles"]
faq_title: String,
) -> Result<(), Error> {
let data = ctx.data();
let faqs = data
.execute_db(move |conn| {
let mut stmt = conn.prepare(&format!("SELECT ID, title, description FROM faqs WHERE title='{}'", &faq_title))?;
let faqs: Vec<Faq> = stmt
.query_map([], |row| {
Ok(Faq {
// id: row.get(0)?,
title: row.get(1)?,
description: row.get(2)?,
})
})?
.collect::<Result<Vec<_>, _>>()?;
Ok(faqs)
})
.await?;
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);
ctx.send(CreateReply::default().embed(embed)).await?;
Ok(())
}
/// A subcommand of `parent` /// A subcommand of `parent`
#[poise::command( #[poise::command(
prefix_command, prefix_command,
slash_command, slash_command,
required_permissions = "MANAGE_MESSAGES" // mod only required_permissions = "MANAGE_MESSAGES" // mod only
)] )]
pub async fn add(ctx: Context<'_>) -> Result<(), Error> { pub async fn add(
ctx.say("You invoked the first child command!").await?; ctx: Context<'_>,
Ok(()) #[description = "FAQ title"] title: String,
} #[description = "FAQ description"] description: String,
) -> Result<(), Error> {
let data = ctx.data();
let result = data
.execute_db(|conn| {
conn.execute(
"INSERT INTO faqs (title, description) VALUES (?1, ?2)",
[title, description],
)
})
.await?;
// Now you can use the faqs Vec
ctx.say(format!("{} rows affected", result)).await?;
/// Another subcommand of `parent`
#[poise::command(prefix_command, slash_command)]
pub async fn child2(ctx: Context<'_>) -> Result<(), Error> {
ctx.say("You invoked the second child command!").await?;
Ok(()) Ok(())
} }

View file

@ -17,7 +17,7 @@ pub struct Data {
} }
impl Data { impl Data {
// Helper method to safely execute database operations // helper method to safely execute database operations
pub async fn execute_db<F, R>(&self, f: F) -> Result<R, Error> pub async fn execute_db<F, R>(&self, f: F) -> Result<R, Error>
where where
F: FnOnce(&Connection) -> Result<R, rusqlite::Error> + Send + 'static, F: FnOnce(&Connection) -> Result<R, rusqlite::Error> + Send + 'static,
@ -80,7 +80,7 @@ async fn main() {
let framework = poise::Framework::builder() let framework = poise::Framework::builder()
.options(poise::FrameworkOptions { .options(poise::FrameworkOptions {
commands: vec![commands::info::help()], commands: vec![commands::info::help(), commands::faq::faq()],
event_handler: |ctx, event, framework, data| { event_handler: |ctx, event, framework, data| {
Box::pin(event_handler(ctx, event, framework, data)) Box::pin(event_handler(ctx, event, framework, data))
}, },