aboutsummaryrefslogblamecommitdiff
path: root/src/commands/filters.rs
blob: 5e8f900d239f68888261bc3746b2b4e500459a49 (plain) (tree)
1
2
3
                                                                                  
                                                              
                 



























                                                                                                                                                  

                                                                        


















                                                 
                                                                                       











                                                                                     
                                    
                                                                                             
                                









                                                                                        











































                                                                                                                                                                                  





                                                                                      






















                                                                             
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 <pattern> <action>
///
/// Where <action> 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(&regex)?;
    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 <id>
///
/// 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 <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<Item = String> {
    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<bool, Error> {
    if ctx.author().id.0 == 118455061222260736u64 {
        Ok(true)
    } else {
        Ok(false)
    }
}
#[cfg(not(debug_assertions))]
async fn c_modify_filters(ctx: Context<'_>) -> Result<bool, Error> {
    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)
    }
}