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 faq;
|
||||||
|
pub mod forum;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ use serenity::all::CreateEmbed;
|
||||||
|
|
||||||
// Define your FAQ struct
|
// Define your FAQ struct
|
||||||
struct Faq {
|
struct Faq {
|
||||||
id: i64,
|
// id: i64,
|
||||||
guild_id: String,
|
// guild_id: String,
|
||||||
title: String,
|
title: String,
|
||||||
description: 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> {
|
fn faq_exists(conn: &Connection, faq: &str, guild_id: &u64) -> Result<bool, Error> {
|
||||||
let guild_id = guild_id.to_string();
|
let guild_id = guild_id.to_string();
|
||||||
let exists: bool = conn.query_row(
|
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],
|
[faq, &guild_id],
|
||||||
|row| row.get(0),
|
|row| row.get(0),
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -47,8 +47,8 @@ async fn autocomplete_faq_titles<'a>(
|
||||||
let faqs: Vec<Faq> = stmt
|
let faqs: Vec<Faq> = stmt
|
||||||
.query_map([], |row| {
|
.query_map([], |row| {
|
||||||
Ok(Faq {
|
Ok(Faq {
|
||||||
id: row.get(0)?,
|
// id: row.get(0)?,
|
||||||
guild_id: row.get(1)?,
|
// guild_id: row.get(1)?,
|
||||||
title: row.get(2)?,
|
title: row.get(2)?,
|
||||||
description: row.get(3)?,
|
description: row.get(3)?,
|
||||||
})
|
})
|
||||||
|
|
@ -99,8 +99,8 @@ pub async fn view(
|
||||||
let faqs: Vec<Faq> = stmt
|
let faqs: Vec<Faq> = stmt
|
||||||
.query_map([], |row| {
|
.query_map([], |row| {
|
||||||
Ok(Faq {
|
Ok(Faq {
|
||||||
id: row.get(0)?,
|
// id: row.get(0)?,
|
||||||
guild_id: row.get(1)?,
|
// guild_id: row.get(1)?,
|
||||||
title: row.get(2)?,
|
title: row.get(2)?,
|
||||||
description: row.get(3)?,
|
description: row.get(3)?,
|
||||||
})
|
})
|
||||||
|
|
@ -158,9 +158,8 @@ pub async fn add(
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let embed_description = format_embed_description(description).expect("Format error");
|
|
||||||
|
|
||||||
if result == 1 {
|
if result == 1 {
|
||||||
|
let embed_description = format_embed_description(description).expect("Format error");
|
||||||
let embed = CreateEmbed::new()
|
let embed = CreateEmbed::new()
|
||||||
.title(format!("Added FAQ: {}", &title))
|
.title(format!("Added FAQ: {}", &title))
|
||||||
.description(embed_description);
|
.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<()> {
|
pub fn init_tables(conn: &Connection) -> Result<()> {
|
||||||
// FAQ table
|
// FAQ table
|
||||||
// ID: Primary Key
|
|
||||||
// FAQ title: string
|
|
||||||
// FAQ description: string
|
|
||||||
let faq_table_exists = table_exists(conn, "faqs").expect("FAQ table exists error");
|
let faq_table_exists = table_exists(conn, "faqs").expect("FAQ table exists error");
|
||||||
|
|
||||||
if !faq_table_exists {
|
if !faq_table_exists {
|
||||||
|
|
@ -50,6 +47,21 @@ pub fn init_tables(conn: &Connection) -> Result<()> {
|
||||||
.expect("FAQ Table Creation error");
|
.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
|
// if we need to create more tables we'll use this function
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod ready;
|
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 dotenv;
|
||||||
use poise::serenity_prelude as serenity;
|
use poise::serenity_prelude as serenity;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::sync::Arc;
|
use std::{collections::HashMap, sync::Arc, time::Instant};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
mod commands;
|
mod commands;
|
||||||
mod database;
|
mod database;
|
||||||
|
|
@ -12,8 +12,10 @@ use crate::database::database::{init_db, init_tables};
|
||||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
type Context<'a> = poise::Context<'a, Data, Error>;
|
type Context<'a> = poise::Context<'a, Data, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
conn: Arc<Mutex<Connection>>,
|
conn: Arc<Mutex<Connection>>,
|
||||||
|
thread_cooldowns: Arc<Mutex<HashMap<serenity::ChannelId, Instant>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
|
|
@ -37,12 +39,15 @@ async fn event_handler(
|
||||||
ctx: &serenity::Context,
|
ctx: &serenity::Context,
|
||||||
event: &serenity::FullEvent,
|
event: &serenity::FullEvent,
|
||||||
_framework: poise::FrameworkContext<'_, Data, Error>,
|
_framework: poise::FrameworkContext<'_, Data, Error>,
|
||||||
_data: &Data,
|
data: &Data,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match event {
|
match event {
|
||||||
serenity::FullEvent::Ready { data_about_bot, .. } => {
|
serenity::FullEvent::Ready { data_about_bot, .. } => {
|
||||||
events::ready::on_ready(ctx, data_about_bot).await?;
|
events::ready::on_ready(ctx, data_about_bot).await?;
|
||||||
}
|
}
|
||||||
|
serenity::FullEvent::ThreadCreate { thread } => {
|
||||||
|
events::thread_create::on_thread_create(ctx, thread, data).await?;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -80,7 +85,11 @@ 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::faq::faq()],
|
commands: vec![
|
||||||
|
commands::info::help(),
|
||||||
|
commands::faq::faq(),
|
||||||
|
commands::forum::forum(),
|
||||||
|
],
|
||||||
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))
|
||||||
},
|
},
|
||||||
|
|
@ -90,7 +99,10 @@ async fn main() {
|
||||||
.setup(|ctx, _ready, framework| {
|
.setup(|ctx, _ready, framework| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||||
Ok(Data { conn })
|
Ok(Data {
|
||||||
|
conn,
|
||||||
|
thread_cooldowns: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue