reminder-bot/src/main.rs

210 lines
6.1 KiB
Rust
Raw Normal View History

#![feature(int_roundings)]
2020-08-26 17:26:28 +00:00
#[macro_use]
extern crate lazy_static;
2020-08-09 22:59:31 +00:00
mod commands;
mod component_models;
mod consts;
mod event_handlers;
mod hooks;
2022-02-01 23:04:31 +00:00
mod interval_parser;
mod models;
mod time_parser;
mod utils;
2020-08-06 14:22:13 +00:00
2022-03-21 23:11:52 +00:00
use std::{
collections::HashMap,
env,
error::Error as StdError,
fmt::{Debug, Display, Formatter},
sync::atomic::AtomicBool,
};
use chrono_tz::Tz;
use dotenv::dotenv;
2022-02-19 14:32:03 +00:00
use poise::serenity::model::{
gateway::GatewayIntents,
2022-02-19 14:32:03 +00:00
id::{GuildId, UserId},
2020-08-06 14:22:13 +00:00
};
2022-02-19 14:32:03 +00:00
use sqlx::{MySql, Pool};
2022-03-21 23:11:52 +00:00
use tokio::sync::{broadcast, broadcast::Sender, RwLock};
2020-08-06 14:22:13 +00:00
use crate::{
commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds},
consts::THEME_COLOR,
2022-02-19 14:32:03 +00:00
event_handlers::listener,
hooks::all_checks,
2021-10-26 20:10:14 +00:00
models::command_macro::CommandMacro,
2022-02-19 14:32:03 +00:00
utils::register_application_commands,
2020-08-09 22:59:31 +00:00
};
2022-02-19 14:32:03 +00:00
type Database = MySql;
2020-08-06 14:22:13 +00:00
2022-02-19 14:32:03 +00:00
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;
2020-08-06 14:22:13 +00:00
2022-02-19 14:32:03 +00:00
pub struct Data {
database: Pool<Database>,
http: reqwest::Client,
recording_macros: RwLock<HashMap<(GuildId, UserId), CommandMacro<Data, Error>>>,
popular_timezones: Vec<Tz>,
2021-12-20 13:48:18 +00:00
is_loop_running: AtomicBool,
2022-03-21 23:11:52 +00:00
broadcast: Sender<()>,
2021-12-20 13:48:18 +00:00
}
impl Debug for Data {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Data {{ .. }}")
}
}
2022-03-21 23:11:52 +00:00
struct Ended;
impl Debug for Ended {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Process ended.")
}
}
impl Display for Ended {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Process ended.")
}
}
impl StdError for Ended {}
2020-08-06 14:22:13 +00:00
#[tokio::main]
2022-03-21 23:11:52 +00:00
async fn main() -> Result<(), Box<dyn StdError + Send + Sync>> {
let (tx, mut rx) = broadcast::channel(16);
tokio::select! {
output = _main(tx) => output,
_ = rx.recv() => Err(Box::new(Ended) as Box<dyn StdError + Send + Sync>)
}
}
async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
2020-10-17 22:56:19 +00:00
env_logger::init();
2020-08-06 14:22:13 +00:00
dotenv()?;
2022-02-19 14:32:03 +00:00
let discord_token = env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment");
let options = poise::FrameworkOptions {
commands: vec![
info_cmds::help(),
info_cmds::info(),
info_cmds::donate(),
info_cmds::clock(),
2022-02-19 22:11:21 +00:00
info_cmds::clock_context_menu(),
2022-02-19 14:32:03 +00:00
info_cmds::dashboard(),
moderation_cmds::timezone(),
poise::Command {
subcommands: vec![
moderation_cmds::set_allowed_dm(),
moderation_cmds::unset_allowed_dm(),
],
..moderation_cmds::allowed_dm()
},
moderation_cmds::webhook(),
2022-02-19 14:32:03 +00:00
poise::Command {
subcommands: vec![
moderation_cmds::delete_macro(),
moderation_cmds::finish_macro(),
moderation_cmds::list_macro(),
moderation_cmds::record_macro(),
moderation_cmds::run_macro(),
],
..moderation_cmds::macro_base()
},
reminder_cmds::pause(),
reminder_cmds::offset(),
reminder_cmds::nudge(),
reminder_cmds::look(),
reminder_cmds::delete(),
poise::Command {
subcommands: vec![
reminder_cmds::list_timer(),
reminder_cmds::start_timer(),
reminder_cmds::delete_timer(),
],
..reminder_cmds::timer_base()
},
reminder_cmds::remind(),
poise::Command {
subcommands: vec![
poise::Command {
subcommands: vec![
todo_cmds::todo_guild_add(),
todo_cmds::todo_guild_view(),
],
..todo_cmds::todo_guild_base()
},
poise::Command {
subcommands: vec![
todo_cmds::todo_channel_add(),
todo_cmds::todo_channel_view(),
],
..todo_cmds::todo_channel_base()
},
poise::Command {
subcommands: vec![todo_cmds::todo_user_add(), todo_cmds::todo_user_view()],
..todo_cmds::todo_user_base()
},
],
..todo_cmds::todo_base()
},
2022-02-19 14:32:03 +00:00
],
allowed_mentions: None,
command_check: Some(|ctx| Box::pin(all_checks(ctx))),
listener: |ctx, event, _framework, data| Box::pin(listener(ctx, event, data)),
..Default::default()
2021-12-20 13:48:18 +00:00
};
2022-02-19 14:32:03 +00:00
let database =
Pool::connect(&env::var("DATABASE_URL").expect("No database URL provided")).await.unwrap();
let popular_timezones = sqlx::query!(
"SELECT timezone FROM users GROUP BY timezone ORDER BY COUNT(timezone) DESC LIMIT 21"
2022-02-19 14:32:03 +00:00
)
.fetch_all(&database)
.await
.unwrap()
.iter()
.map(|t| t.timezone.parse::<Tz>().unwrap())
.collect::<Vec<Tz>>();
poise::Framework::build()
.token(discord_token)
.user_data_setup(move |ctx, _bot, framework| {
Box::pin(async move {
register_application_commands(
ctx,
framework,
env::var("DEBUG_GUILD")
.map(|inner| GuildId(inner.parse().expect("DEBUG_GUILD not valid")))
.ok(),
)
.await
.unwrap();
Ok(Data {
http: reqwest::Client::new(),
database,
popular_timezones,
recording_macros: Default::default(),
is_loop_running: AtomicBool::new(false),
2022-03-21 23:11:52 +00:00
broadcast: tx,
2022-02-19 14:32:03 +00:00
})
})
})
.options(options)
2022-04-19 14:23:27 +00:00
.intents(GatewayIntents::GUILDS)
2022-02-19 14:32:03 +00:00
.run_autosharded()
.await?;
2020-08-06 14:22:13 +00:00
Ok(())
}