aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
blob: a6f97aed9e4d5186f5a293f8d53db90b1c7b9abb (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
152
153
154
#![feature(rustc_private)]

#[macro_use] extern crate serenity;
#[macro_use] extern crate log;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate lazy_static;

extern crate dotenv;
extern crate simple_logger;
extern crate typemap;
extern crate url;
extern crate parking_lot;

mod commands;
mod util;

use std::env;
use std::thread;
use std::time::{Duration, Instant};

use serenity::prelude::*;
use serenity::framework::StandardFramework;
use serenity::framework::standard::help_commands;
use serenity::model::gateway::Ready;
use serenity::model::id::{UserId, GuildId};

use dotenv::dotenv;

use commands::register_commands;

mod errors {
    error_chain! {
        foreign_links {
            Serenity(::serenity::Error);
            MissingVar(::std::env::VarError);
        }
    }
}

use errors::*;
pub use util::*;

lazy_static! {
    static ref TARGET_GUILD: u64 = must_env_lookup::<u64>("TARGET_GUILD");
    static ref TARGET_GUILD_ID: GuildId = GuildId(*TARGET_GUILD);
}

struct Handler;
impl EventHandler for Handler {
    fn ready(&self, _: Context, r: Ready) {
        let guild = r.guilds.iter()
            .find(|g| g.id().0 == *TARGET_GUILD);

        if guild.is_none() {
            info!("bot isn't in configured guild. join here: {:?}", OAUTH_URL.as_str());
        }
    }
}

fn run() -> Result<()> {
    let token = &env::var("THULANI_TOKEN")?;
    let mut client = Client::new(token, Handler)?;

    commands::VoiceManager::register(&mut client);
    commands::PlayQueue::register(&mut client);

    let owner_id = must_env_lookup::<u64>("OWNER_ID");
    let mut framework = StandardFramework::new()
        .configure(|c| c
            .allow_dm(false)
            .allow_whitespace(true)
            .prefixes(vec!["!thulani ", "!thulan ", "!thulando madando ", "!thulando "])
            .ignore_bots(true)
            .on_mention(false)
            .owners(vec![UserId(owner_id)].into_iter().collect())
            .case_insensitivity(true)
        )
        .before(|_ctx, message, cmd| {
            debug!("got command {} from user '{}' ({})", cmd, message.author.name, message.author.id);
            
            message.guild_id().map_or(false, |x| x.0 == *TARGET_GUILD)
        })
        .after(|_ctx, _msg, _cmd, err| {
            match err {
                Ok(()) => {},
                Err(_e) => {


                }
            }
        })
        .bucket("Standard", 1, 10, 3)
        .customised_help(help_commands::with_embeds, |c| {
            c
        });

    framework = register_commands(framework);

    client.with_framework(framework);
    client.start()?;

    Ok(())
}

fn main() {
    const BACKOFF_FACTOR: f64 = 2.0;
    const MAX_BACKOFFS: usize = 3;
    const BACKOFF_INIT: f64 = 100.0;

    const MIN_RUN_DURATION: Duration = Duration::from_secs(120);

    dotenv().ok();
    simple_logger::init_with_level(log::Level::Debug).unwrap();

    let mut backoff_count: usize = 0;

    loop {
        let start = Instant::now();

        info!("starting bot");
        match run() {
            Err(e) => {
                error!("error encountered running client: {}", e);
                e.iter().skip(1).for_each(|e| {
                    error!("caused by: {}", e);
                });

                if let Some(bt) = e.backtrace() {
                    error!("backtrace: {:?}", bt);
                }

                ::std::process::exit(1);
            },
            _ => {
                warn!("somehow `run` completed without an error. should probably take a look at this.");
            }
        }

        if Instant::now() - start >= MIN_RUN_DURATION {
            backoff_count = 0;
            continue;
        }

        backoff_count += 1;
        if backoff_count >= MAX_BACKOFFS {
            panic!("restarted bot too many times");
        }

        let backoff_millis = (BACKOFF_INIT*BACKOFF_FACTOR.powi(backoff_count as i32)) as u64;
        info!("bot died too quickly. backing off, retrying in {}ms.", backoff_millis);

        thread::sleep(Duration::from_millis(backoff_millis));
    }
}