main.rs 6.04 KB
Newer Older
tec's avatar
tec committed
1
2
3
#[macro_use]
extern crate lazy_static;

tec's avatar
tec committed
4
5
6
7
8
9
#[macro_use]
extern crate log;
extern crate simplelog;
use simplelog::*;
use std::fs::File;

tec's avatar
tec committed
10
11
12
13
14
15
use serenity::{
    model::{channel, channel::Message, gateway::Ready, guild::Member},
    prelude::*,
    utils::MessageBuilder,
};

tec's avatar
tec committed
16
17
18
mod config;
mod user_management;
mod voting;
tec's avatar
tec committed
19

Tom Almeida's avatar
Tom Almeida committed
20
21
22
23
macro_rules! e {
    ($error: literal, $x:expr) => {
        match $x {
            Ok(_) => (),
tec's avatar
tec committed
24
            Err(why) => error!($error, why),
Tom Almeida's avatar
Tom Almeida committed
25
        }
26
    };
Tom Almeida's avatar
Tom Almeida committed
27
28
29
30
}

struct Handler;

tec's avatar
tec committed
31
32
33
34
35
36
37
impl EventHandler for Handler {
    // Set a handler for the `message` event - so that whenever a new message
    // is received - the closure (or function) passed will be called.
    //
    // Event handlers are dispatched through a threadpool, and so multiple
    // events can be dispatched simultaneously.
    fn message(&self, ctx: Context, msg: Message) {
tec's avatar
tec committed
38
        if msg.content.starts_with(config::COMMAND_PREFIX) {
tec's avatar
tec committed
39
40
41
42
            let message_content: Vec<_> = msg.content[1..].splitn(2, ' ').collect();
            match message_content[0] {
                "register" => {
                    user_management::Commands::register(ctx, msg.clone(), message_content[1])
tec's avatar
tec committed
43
                }
tec's avatar
tec committed
44
45
                "join" => {
                    user_management::Commands::join(ctx, msg.clone(), message_content[1]);
tec's avatar
tec committed
46
                }
tec's avatar
tec committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
                "move" => {
                    voting::Commands::move_something(ctx, msg.clone(), message_content[1]);
                }
                "motion" => {
                    voting::Commands::motion(ctx, msg.clone(), message_content[1]);
                }
                "poll" => {
                    voting::Commands::poll(ctx, msg.clone(), message_content[1]);
                }
                "cowsay" => {
                    voting::Commands::cowsay(ctx, msg.clone(), message_content[1]);
                }
                "help" => {
                    let mut message = MessageBuilder::new();
tec's avatar
tec committed
61
62
63
64
65
66
67
68
                    message.push_line(format!(
                        "Use {}move <action> to make a circular motion",
                        config::COMMAND_PREFIX
                    ));
                    message.push_line(format!(
                        "Use {}poll <proposal> to see what people think about something",
                        config::COMMAND_PREFIX
                    ));
69
                    e!(
tec's avatar
tec committed
70
71
                        "Error sending message: {:?}",
                        msg.channel_id.say(&ctx.http, message.build())
Tom Almeida's avatar
Tom Almeida committed
72
                    );
tec's avatar
tec committed
73
74
                }
                _ => {
75
76
                    e!(
                        "Error sending message: {:?}",
tec's avatar
tec committed
77
78
79
80
                        msg.channel_id.say(
                            &ctx.http,
                            format!("Unrecognised command. Try {}help", config::COMMAND_PREFIX)
                        )
81
                    );
tec's avatar
tec committed
82
                }
83
            }
tec's avatar
tec committed
84
85
86
        }
    }

tec's avatar
tec committed
87
    fn reaction_add(&self, ctx: Context, add_reaction: channel::Reaction) {
tec's avatar
tec committed
88
89
90
91
92
93
94
95
96
97
98
99
100
        match add_reaction.message(&ctx.http) {
            Ok(message) => {
                if message.author.id.0 == config::BOT_ID {
                    match message_type(&message) {
                        "motion" => {
                            voting::reaction_add(ctx, add_reaction);
                        }
                        _ => {}
                    }
                }
            }
            Err(why) => error!("Failed to get react message {:?}", why),
        }
tec's avatar
tec committed
101
102
    }

tec's avatar
tec committed
103
    fn reaction_remove(&self, ctx: Context, removed_reaction: channel::Reaction) {
tec's avatar
tec committed
104
105
106
107
108
109
110
111
112
113
114
115
116
        match removed_reaction.message(&ctx.http) {
            Ok(message) => {
                if message.author.id.0 == config::BOT_ID {
                    match message_type(&message) {
                        "motion" => {
                            voting::reaction_remove(ctx, removed_reaction);
                        }
                        _ => {}
                    }
                }
            }
            Err(why) => error!("Failed to get react message {:?}", why),
        }
tec's avatar
tec committed
117
118
    }

tec's avatar
tec committed
119
120
121
122
    fn guild_member_addition(
        &self,
        ctx: Context,
        _guild_id: serenity::model::id::GuildId,
tec's avatar
tec committed
123
        the_new_member: Member,
tec's avatar
tec committed
124
    ) {
tec's avatar
tec committed
125
        user_management::new_member(&ctx, the_new_member);
tec's avatar
tec committed
126
127
128
129
130
131
132
133
134
    }

    // Set a handler to be called on the `ready` event. This is called when a
    // shard is booted, and a READY payload is sent by Discord. This payload
    // contains data like the current user's guild Ids, current user data,
    // private channels, and more.
    //
    // In this case, just print what the current user's username is.
    fn ready(&self, _: Context, ready: Ready) {
tec's avatar
tec committed
135
        info!("{} is connected!", ready.user.name);
tec's avatar
tec committed
136
137
138
139
    }
}

fn main() {
tec's avatar
tec committed
140
141
142
143
144
145
146
147
148
    CombinedLogger::init(vec![
        TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed).unwrap(),
        WriteLogger::new(
            LevelFilter::Info,
            Config::default(),
            File::create("ucc-bot.log").unwrap(),
        ),
    ])
    .unwrap();
tec's avatar
tec committed
149
    // Configure the client with your Discord bot token in the environment.
tec's avatar
tec committed
150
    let token = config::DISCORD_TOKEN;
tec's avatar
tec committed
151
152
153
154
155
156
157
158
159
160
161

    // Create a new instance of the Client, logging in as a bot. This will
    // automatically prepend your bot token with "Bot ", which is a requirement
    // by Discord for bot users.
    let mut client = Client::new(&token, Handler).expect("Err creating client");

    // Finally, start a single shard, and start listening to events.
    //
    // Shards will automatically attempt to reconnect, and will perform
    // exponential backoff until it reconnects.
    if let Err(why) = client.start() {
tec's avatar
tec committed
162
        error!("Client error: {:?}", why);
tec's avatar
tec committed
163
164
    }
}
tec's avatar
tec committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

fn message_type(message: &Message) -> &'static str {
    if message.embeds.len() > 0 {
        let title: String = message.embeds[0].title.clone().unwrap();
        let words_of_title: Vec<_> = title.splitn(2, ' ').collect();
        let first_word_of_title = words_of_title[0];
        return match first_word_of_title {
            "Motion" => "motion",
            "Poll" => "poll",
            _ => "misc",
        };
    } else {
        return "misc";
    }
}