aboutsummaryrefslogtreecommitdiff
path: root/src/commands/filters.rs
blob: 5e8f900d239f68888261bc3746b2b4e500459a49 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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)
    }
}