added forum post
This commit is contained in:
parent
79eb9d7492
commit
b52d3b9477
7 changed files with 245 additions and 16 deletions
|
|
@ -1,2 +1,3 @@
|
|||
pub mod faq;
|
||||
pub mod forum;
|
||||
pub mod info;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use serenity::all::CreateEmbed;
|
|||
|
||||
// Define your FAQ struct
|
||||
struct Faq {
|
||||
id: i64,
|
||||
guild_id: String,
|
||||
// id: i64,
|
||||
// guild_id: String,
|
||||
title: String,
|
||||
description: String,
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ fn format_embed_description<S: Into<String>>(description: S) -> Result<String, E
|
|||
fn faq_exists(conn: &Connection, faq: &str, guild_id: &u64) -> Result<bool, Error> {
|
||||
let guild_id = guild_id.to_string();
|
||||
let exists: bool = conn.query_row(
|
||||
"SELECT EXISTS(SELECT 1 FROM faqs WHERE title = ?1 AND description = ?2)",
|
||||
"SELECT EXISTS(SELECT 1 FROM faqs WHERE title = ?1 AND guild_id = ?2)",
|
||||
[faq, &guild_id],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
|
|
@ -47,8 +47,8 @@ async fn autocomplete_faq_titles<'a>(
|
|||
let faqs: Vec<Faq> = stmt
|
||||
.query_map([], |row| {
|
||||
Ok(Faq {
|
||||
id: row.get(0)?,
|
||||
guild_id: row.get(1)?,
|
||||
// id: row.get(0)?,
|
||||
// guild_id: row.get(1)?,
|
||||
title: row.get(2)?,
|
||||
description: row.get(3)?,
|
||||
})
|
||||
|
|
@ -99,8 +99,8 @@ pub async fn view(
|
|||
let faqs: Vec<Faq> = stmt
|
||||
.query_map([], |row| {
|
||||
Ok(Faq {
|
||||
id: row.get(0)?,
|
||||
guild_id: row.get(1)?,
|
||||
// id: row.get(0)?,
|
||||
// guild_id: row.get(1)?,
|
||||
title: row.get(2)?,
|
||||
description: row.get(3)?,
|
||||
})
|
||||
|
|
@ -158,9 +158,8 @@ pub async fn add(
|
|||
})
|
||||
.await?;
|
||||
|
||||
let embed_description = format_embed_description(description).expect("Format error");
|
||||
|
||||
if result == 1 {
|
||||
let embed_description = format_embed_description(description).expect("Format error");
|
||||
let embed = CreateEmbed::new()
|
||||
.title(format!("Added FAQ: {}", &title))
|
||||
.description(embed_description);
|
||||
|
|
|
|||
123
src/commands/forum.rs
Normal file
123
src/commands/forum.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use crate::{Context, Error};
|
||||
use poise::CreateReply;
|
||||
use rusqlite::Connection;
|
||||
use serenity::all::{CreateEmbed, GuildChannel};
|
||||
|
||||
// struct Forum {
|
||||
// id: i64,
|
||||
// guild_id: String,
|
||||
// forum_id: String,
|
||||
// }
|
||||
|
||||
#[poise::command(slash_command, subcommands("add", "delete"), guild_only)]
|
||||
pub async fn forum(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn forum_exists<S: Into<String>>(
|
||||
conn: &Connection,
|
||||
forum_id: S,
|
||||
guild_id: S,
|
||||
) -> Result<bool, Error> {
|
||||
let exists: bool = conn.query_row(
|
||||
"SELECT EXISTS(SELECT 1 FROM forums WHERE forum_id = ?1 AND guild_id = ?2)",
|
||||
[forum_id.into(), guild_id.into()],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
/// Add forum
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
required_permissions = "MANAGE_MESSAGES"
|
||||
)]
|
||||
pub async fn add(
|
||||
ctx: Context<'_>,
|
||||
#[description = "FAQ Title"]
|
||||
#[channel_types("Forum")]
|
||||
forum_channel: GuildChannel,
|
||||
) -> Result<(), Error> {
|
||||
let data = ctx.data();
|
||||
|
||||
let result = data
|
||||
.execute_db({
|
||||
let guild_id = ctx.guild_id().expect("Guild ID not found").to_string();
|
||||
move |conn| {
|
||||
let exists = forum_exists(conn, forum_channel.id.to_string(), guild_id.clone())
|
||||
.unwrap_or(false);
|
||||
|
||||
// add
|
||||
if !exists {
|
||||
conn.execute(
|
||||
"INSERT INTO forums (guild_id, forum_id) VALUES (?1, ?2)",
|
||||
[&guild_id, &forum_channel.id.to_string()],
|
||||
)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
if result == 1 {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("Added new forum channel to monitor")
|
||||
.description(format!("<#{}>", forum_channel.id));
|
||||
ctx.send(CreateReply::default().embed(embed)).await?;
|
||||
} else {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("Error adding forum channel")
|
||||
.description("Forum channel already exists!");
|
||||
ctx.send(CreateReply::default().embed(embed)).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete forum
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
required_permissions = "MANAGE_MESSAGES"
|
||||
)]
|
||||
pub async fn delete(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Forum channel"]
|
||||
#[channel_types("Forum")]
|
||||
forum_channel: GuildChannel,
|
||||
) -> Result<(), Error> {
|
||||
let data = ctx.data();
|
||||
let server_guild_id = ctx.guild_id().expect("Guild ID not found");
|
||||
let guild_id = server_guild_id.to_string();
|
||||
|
||||
let result = data
|
||||
.execute_db({
|
||||
let forum_id = forum_channel.id.to_string();
|
||||
move |conn| {
|
||||
conn.execute(
|
||||
"DELETE FROM faqs WHERE forum_id = ?1 AND guild_id = ?2",
|
||||
[forum_id, guild_id],
|
||||
)
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
if result == 1 {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("Deleted Forum")
|
||||
.description(format!(
|
||||
"Forum <#{}> has been removed from the database",
|
||||
forum_channel.id
|
||||
));
|
||||
ctx.send(CreateReply::default().embed(embed)).await?;
|
||||
} else {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("Error deleting forum")
|
||||
.description(format!("{} rows were affected", result));
|
||||
ctx.send(CreateReply::default().embed(embed)).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -32,9 +32,6 @@ fn table_exists(conn: &Connection, table_name: &str) -> Result<bool> {
|
|||
|
||||
pub fn init_tables(conn: &Connection) -> Result<()> {
|
||||
// FAQ table
|
||||
// ID: Primary Key
|
||||
// FAQ title: string
|
||||
// FAQ description: string
|
||||
let faq_table_exists = table_exists(conn, "faqs").expect("FAQ table exists error");
|
||||
|
||||
if !faq_table_exists {
|
||||
|
|
@ -50,6 +47,21 @@ pub fn init_tables(conn: &Connection) -> Result<()> {
|
|||
.expect("FAQ Table Creation error");
|
||||
}
|
||||
|
||||
let forums_table_exists =
|
||||
table_exists(conn, "forums").expect("Forum channels table exists error");
|
||||
|
||||
if !forums_table_exists {
|
||||
conn.execute(
|
||||
"CREATE TABLE forums (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
forum_id TEXT NOT NULL
|
||||
)",
|
||||
(),
|
||||
)
|
||||
.expect("Forum Channels Table Creation error");
|
||||
}
|
||||
|
||||
// if we need to create more tables we'll use this function
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
pub mod ready;
|
||||
pub mod thread_create;
|
||||
|
|
|
|||
81
src/events/thread_create.rs
Normal file
81
src/events/thread_create.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::{Data, Error};
|
||||
use ::serenity::all::CreateMessage;
|
||||
use poise::serenity_prelude as serenity;
|
||||
use rusqlite::Connection;
|
||||
|
||||
fn forum_exists(conn: &Connection, forum_id: &str, guild_id: &str) -> Result<bool, Error> {
|
||||
let guild_id = guild_id.to_string();
|
||||
let exists: bool = conn.query_row(
|
||||
"SELECT EXISTS(SELECT 1 FROM forums WHERE forum_id = ?1 AND guild_id = ?2)",
|
||||
[forum_id, &guild_id],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
pub async fn on_thread_create(
|
||||
ctx: &serenity::Context,
|
||||
thread: &serenity::GuildChannel,
|
||||
data: &Data,
|
||||
) -> Result<(), Error> {
|
||||
const COOLDOWN_DURATION: Duration = Duration::from_secs(2);
|
||||
{
|
||||
let mut cooldowns = data.thread_cooldowns.lock().await;
|
||||
if let Some(last_call) = cooldowns.get(&thread.id) {
|
||||
if last_call.elapsed() < COOLDOWN_DURATION {
|
||||
println!("Thread {} is on cooldown, skipping", thread.id);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
cooldowns.insert(thread.id, Instant::now());
|
||||
}
|
||||
|
||||
// check if forum channel ID is in database
|
||||
let forum_exists = data
|
||||
.execute_db({
|
||||
let forum_channel_id = thread
|
||||
.parent_id
|
||||
.expect("Thread Parent ID not found")
|
||||
.to_string();
|
||||
|
||||
let guild_id = thread.guild_id.to_string();
|
||||
|
||||
move |conn| {
|
||||
let exists = forum_exists(conn, &forum_channel_id, &guild_id).unwrap_or(false);
|
||||
|
||||
Ok(exists)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("Checking forum error");
|
||||
|
||||
if !forum_exists {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// check if there's more than one tag
|
||||
if thread.applied_tags.len() <= 1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// go into the channel and ping the user to only use one tag
|
||||
let thread_post = thread.id;
|
||||
let thread_owner = thread
|
||||
.owner_id
|
||||
.expect("Failed to get thread owner ID")
|
||||
.to_string();
|
||||
|
||||
let message_builder = CreateMessage::new().content(format!(
|
||||
"<@{}> Please select only ONE (1) tag (personal-only or career-oriented)",
|
||||
thread_owner
|
||||
));
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
|
||||
thread_post.send_message(ctx, message_builder).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
20
src/main.rs
20
src/main.rs
|
|
@ -1,7 +1,7 @@
|
|||
use dotenv;
|
||||
use poise::serenity_prelude as serenity;
|
||||
use rusqlite::Connection;
|
||||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, sync::Arc, time::Instant};
|
||||
use tokio::sync::Mutex;
|
||||
mod commands;
|
||||
mod database;
|
||||
|
|
@ -12,8 +12,10 @@ use crate::database::database::{init_db, init_tables};
|
|||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
type Context<'a> = poise::Context<'a, Data, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Data {
|
||||
conn: Arc<Mutex<Connection>>,
|
||||
thread_cooldowns: Arc<Mutex<HashMap<serenity::ChannelId, Instant>>>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
|
|
@ -37,12 +39,15 @@ async fn event_handler(
|
|||
ctx: &serenity::Context,
|
||||
event: &serenity::FullEvent,
|
||||
_framework: poise::FrameworkContext<'_, Data, Error>,
|
||||
_data: &Data,
|
||||
data: &Data,
|
||||
) -> Result<(), Error> {
|
||||
match event {
|
||||
serenity::FullEvent::Ready { data_about_bot, .. } => {
|
||||
events::ready::on_ready(ctx, data_about_bot).await?;
|
||||
}
|
||||
serenity::FullEvent::ThreadCreate { thread } => {
|
||||
events::thread_create::on_thread_create(ctx, thread, data).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -80,7 +85,11 @@ async fn main() {
|
|||
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
commands: vec![commands::info::help(), commands::faq::faq()],
|
||||
commands: vec![
|
||||
commands::info::help(),
|
||||
commands::faq::faq(),
|
||||
commands::forum::forum(),
|
||||
],
|
||||
event_handler: |ctx, event, framework, data| {
|
||||
Box::pin(event_handler(ctx, event, framework, data))
|
||||
},
|
||||
|
|
@ -90,7 +99,10 @@ async fn main() {
|
|||
.setup(|ctx, _ready, framework| {
|
||||
Box::pin(async move {
|
||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||
Ok(Data { conn })
|
||||
Ok(Data {
|
||||
conn,
|
||||
thread_cooldowns: Arc::new(Mutex::new(HashMap::new())),
|
||||
})
|
||||
})
|
||||
})
|
||||
.build();
|
||||
|
|
|
|||
Loading…
Reference in a new issue