2022-02-19 13:28:24 +00:00
|
|
|
#![feature(int_roundings)]
|
2022-05-13 22:08:52 +00:00
|
|
|
|
2020-08-26 17:26:28 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
|
|
|
|
2020-08-09 22:59:31 +00:00
|
|
|
mod commands;
|
2022-02-19 18:21:11 +00:00
|
|
|
mod component_models;
|
2020-09-28 12:42:20 +00:00
|
|
|
mod consts;
|
2022-02-19 13:28:24 +00:00
|
|
|
mod event_handlers;
|
2021-09-22 20:12:29 +00:00
|
|
|
mod hooks;
|
2022-02-01 23:04:31 +00:00
|
|
|
mod interval_parser;
|
2020-10-12 20:01:27 +00:00
|
|
|
mod models;
|
|
|
|
mod time_parser;
|
2022-02-19 13:28:24 +00:00
|
|
|
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,
|
|
|
|
};
|
2021-09-06 12:46:16 +00:00
|
|
|
|
|
|
|
use chrono_tz::Tz;
|
|
|
|
use dotenv::dotenv;
|
2022-02-19 14:32:03 +00:00
|
|
|
use poise::serenity::model::{
|
2022-05-13 22:08:52 +00:00
|
|
|
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
|
|
|
|
2020-09-28 12:42:20 +00:00
|
|
|
use crate::{
|
2022-02-19 18:21:11 +00:00
|
|
|
commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds},
|
2022-02-19 13:28:24 +00:00
|
|
|
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
|
|
|
};
|
2020-10-11 16:41:26 +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
|
|
|
}
|
2020-10-18 16:26:07 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
impl Debug for Data {
|
2022-02-20 12:19:39 +00:00
|
|
|
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(),
|
2022-06-17 16:15:48 +00:00
|
|
|
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()
|
|
|
|
},
|
2022-02-19 18:21:11 +00:00
|
|
|
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
|
|
|
};
|
2020-10-12 17:37:14 +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!(
|
2022-02-20 12:19:39 +00:00
|
|
|
"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(())
|
|
|
|
}
|