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
use crate::{
    checks::{
        ADMIN_CHECK,
        ENABLED_CHECK,
    },
    ClientDataKey,
};
use serenity::{
    framework::standard::{
        macros::command,
        Args,
        CommandResult,
    },
    model::prelude::*,
    prelude::*,
};
use std::fmt::Write;
use tracing::error;

// Broken in help:
// #[required_permissions("ADMINISTRATOR")]

#[command]
#[description("Disable a command")]
#[usage("<enable/disable> <cmd>")]
#[example("disable ping")]
#[min_args(2)]
#[max_args(2)]
#[sub_commands(list)]
#[checks(Admin, Enabled)]
#[bucket("default")]
pub async fn cmd(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
    let guild_id = match msg.guild_id {
        Some(id) => id,
        None => {
            msg.channel_id
                .say(&ctx.http, "You cannot use this in a DM.")
                .await?;
            return Ok(());
        }
    };

    let data_lock = ctx.data.read().await;
    let client_data = data_lock.get::<ClientDataKey>().unwrap();
    let data = client_data.enabled_check_data.clone();
    let db = client_data.db.clone();
    drop(data_lock);

    let disable = match args.current().expect("invalid arg") {
        "enable" => false,
        "disable" => true,
        _ => {
            msg.channel_id
                .say(&ctx.http, "Invalid Arg. Choose \"enable\" or \"disable\".")
                .await?;
            return Ok(());
        }
    };

    args.advance();

    let cmd_name = args.current().expect("missing cmd name");

    let is_valid_command = {
        let names = data.get_command_names();
        names.iter().any(|name| name == cmd_name)
    };

    if !is_valid_command {
        msg.channel_id
            .say(
                &ctx.http,
                "Invalid Command. Use `cmd list` to list valid commands.",
            )
            .await?;
        return Ok(());
    }

    match db.set_disabled_command(guild_id, cmd_name, disable).await {
        Ok(_old_value) => {
            let status_str = status_to_str(disable);

            // TODO: Tell user if the command is already disabled/enabled
            msg.channel_id
                .say(&ctx.http, format!("Command '{}' {}.", cmd_name, status_str))
                .await?;
        }
        Err(e) => {
            error!("failed to disable command '{}': {:?}", cmd_name, e);
            msg.channel_id
                .say(
                    &ctx.http,
                    format!("Failed to disable command '{}' ", cmd_name),
                )
                .await?;
        }
    }

    Ok(())
}

#[command]
#[description("List commands that can be enabled/disabled")]
pub async fn list(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
    let guild_id = match msg.guild_id {
        Some(id) => id,
        None => {
            msg.channel_id
                .say(&ctx.http, "You cannot use this in a DM.")
                .await?;
            return Ok(());
        }
    };

    let data_lock = ctx.data.read().await;
    let client_data = data_lock.get::<ClientDataKey>().expect("client data");
    let data = client_data.enabled_check_data.clone();
    let db = client_data.db.clone();
    drop(data_lock);

    let res = {
        let mut res = "Commands:\n".to_string();

        let names = data.get_command_names().clone();

        for name in names.iter() {
            let state = if db.is_command_disabled(guild_id, name).await? {
                "DISABLED"
            } else {
                "ENABLED"
            };
            writeln!(res, "{}: **{}**", name, state)?;
        }

        res
    };

    msg.channel_id.say(&ctx.http, res).await?;
    Ok(())
}

fn status_to_str(status: bool) -> &'static str {
    if status {
        "disabled"
    } else {
        "enabled"
    }
}