use crate::{Context, Error, models::{FilterAction, MessageFilter, ConfigChannel}}; use poise::{serenity_prelude as serenity, AutocompleteChoice}; use regex::Regex; use std::str::FromStr; use futures::{Stream, StreamExt}; /// Base command for all filter actions /// /// Provides a CRUD interface for managing automatic message filters #[poise::command(slash_command, prefix_command)] pub async fn filter(ctx: Context<'_>) -> Result<(), Error> { Ok(()) } /// Lists message filters /// /// Usage: /// filter list #[poise::command(slash_command, ephemeral, prefix_command)] pub async fn list(ctx: Context<'_>) -> Result<(), Error> { let pool = ctx.data().pg.lock().unwrap().clone(); let filters = sqlx::query_as::<_, MessageFilter>("SELECT * FROM message_filter WHERE guild_id=$1").bind(ctx.guild().unwrap().id.0.to_string()) .fetch_all(&pool).await?; let mut list = String::from(""); if filters.len() == 0 { list = "No filters set, try adding one with `/filter add`!".to_string(); } else { for (i, f) in filters.iter().enumerate() { list.push_str(&format!("**{}.** {}\n", (i + 1), f.to_string())); } } ctx.send(|m| { m.embed(|e| { e.title("Message Filter List"); e.description(list); e }) }).await?; Ok(()) } /// Creates a new message filter /// /// Usage: /// filter add /// /// Where is one of "review" or "delete" #[poise::command(slash_command, ephemeral, prefix_command, check = "c_modify_filters")] pub async fn add(ctx: Context<'_>, #[description = "The regular expression to match against"] regex: String, #[description = "The action to take when the expression is matched"] #[autocomplete = "ac_action"] action: String, ) -> Result<(), Error> { let pool = ctx.data().pg.lock().unwrap().clone(); let action = FilterAction::from_str(&action)?; let regex = Regex::new(®ex)?; sqlx::query("INSERT INTO message_filter (pattern, action, guild_id) VALUES ($1, $2, $3)") .bind(regex.to_string()) .bind(action as FilterAction) .bind(ctx.guild().unwrap().id.0.to_string()) .execute(&pool) .await?; ctx.say("Sounds good! I've written that down and will keep a close eye out").await?; Ok(()) } /// Deletes a message filter /// /// Usage: /// filter del /// /// You can get the filter ID from `filter list` #[poise::command(slash_command, ephemeral, prefix_command, check = "c_modify_filters")] pub async fn del(ctx: Context<'_>, #[description = "The ID to delete"] id: i32, ) -> Result<(), Error> { let pool = ctx.data().pg.lock().unwrap().clone(); sqlx::query("DELETE FROM message_filter WHERE id=$1") .bind(id) .execute(&pool) .await?; ctx.say("Got it! I've removed that from my list.").await?; Ok(()) } /// Sets the review channel /// /// Usage: /// filter channel #[poise::command(slash_command, ephemeral, prefix_command, check = "c_modify_filters")] pub async fn channel(ctx: Context<'_>, #[description = "The channel to send flagged messages"] channel_id: serenity::ChannelId, ) -> Result<(), Error> { let pool = ctx.data().pg.lock().unwrap().clone(); if let Ok(m) = sqlx::query_as!(ConfigChannel, "SELECT * FROM channels WHERE guild_id=$1 AND purpose='review'", ctx.guild().unwrap().id.0.to_string()).fetch_one(&pool).await { sqlx::query!("UPDATE channels SET channel_id=$1 WHERE id=$2", channel_id.0.to_string(), m.id).execute(&pool).await?; } else { sqlx::query!("INSERT INTO channels (channel_id, purpose, guild_id) VALUES ($1, 'review', $2)", channel_id.0.to_string(), ctx.guild().unwrap().id.0.to_string()) .execute(&pool).await?; } ctx.say(format!("All set! I'll send flagged messages to <#{}> from now on", channel_id.0)).await?; Ok(()) } async fn ac_action(_ctx: Context<'_>, partial: String) -> impl Stream { futures::stream::iter(&["review", "delete"]) .filter(move |name| futures::future::ready(name.starts_with(&partial))) .map(|name| name.to_string()) } #[cfg(debug_assertions)] async fn c_modify_filters(ctx: Context<'_>) -> Result { if ctx.author().id.0 == 118455061222260736u64 { Ok(true) } else { Ok(false) } } #[cfg(not(debug_assertions))] async fn c_modify_filters(ctx: Context<'_>) -> Result { if let Some(guild) = ctx.guild() { if guild.owner_id == ctx.author().id { Ok(true) } else { let member = guild.member(ctx.discord(), ctx.author().id).await?; let member_permissions = member.permissions(ctx.discord())?; Ok(member_permissions.manage_messages()) } } else { Ok(false) } }