diff --git a/Cargo.toml b/Cargo.toml index 5be0d50..49e4fbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,6 @@ assets = [ ["reminder-dashboard/dist/index.html", "lib/reminder-rs/static/index.html", "644"], ["conf/default.env", "etc/reminder-rs/config.env", "600"], ["conf/Rocket.toml", "etc/reminder-rs/Rocket.toml", "600"], - ["bin/reminder-rs-clean-old", "usr/bin/reminder-rs-clean-old", "755"], # ["nginx/reminder-rs", "etc/nginx/sites-available/reminder-rs", "755"] ] conf-files = [ diff --git a/src/main.rs b/src/main.rs index 1513f9a..8f9cae4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,6 @@ use std::{ }; use chrono_tz::Tz; -use clap::Subcommand; use log::warn; use poise::serenity_prelude::{ model::{ @@ -121,174 +120,224 @@ async fn _main(tx: Sender<()>) -> Result<(), Box> { let _ = dotenv::dotenv(); } - let discord_token = env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment"); - - let options = poise::FrameworkOptions { - commands: vec![ - help::command(), - info::command(), - clock::command(), - donate::command(), - clock_context_menu(), - dashboard::command(), - timezone::command(), - poise::Command { - subcommands: vec![allowed_dm::set::set(), allowed_dm::unset::unset()], - ..allowed_dm::allowed_dm() - }, - poise::Command { - subcommands: vec![poise::Command { - subcommands: vec![ - settings::ephemeral_confirmations::set::set(), - settings::ephemeral_confirmations::unset::unset(), - ], - ..settings::ephemeral_confirmations::ephemeral_confirmations() - }], - ..settings::settings() - }, - webhook::command(), - poise::Command { - subcommands: vec![ - command_macro::delete_macro::delete_macro(), - command_macro::finish_macro::finish_macro(), - command_macro::list_macro::list_macro(), - command_macro::record_macro::record_macro(), - command_macro::run_macro::run_macro(), - ], - ..command_macro::command_macro() - }, - pause::command(), - offset::command(), - nudge::command(), - look::command(), - delete::command(), - poise::Command { - subcommands: vec![ - timer::list::list(), - timer::start::start(), - timer::delete::delete(), - ], - ..timer::timer() - }, - multiline::command(), - remind::command(), - poise::Command { - subcommands: vec![ - poise::Command { - subcommands: vec![todo::guild::add::add(), todo::guild::view::view()], - ..todo::guild::guild() - }, - poise::Command { - subcommands: vec![todo::channel::add::add(), todo::channel::view::view()], - ..todo::channel::channel() - }, - poise::Command { - subcommands: vec![todo::user::add::add(), todo::user::view::view()], - ..todo::user::user() - }, - ], - ..todo::todo() - }, - ], - allowed_mentions: None, - command_check: Some(|ctx| Box::pin(all_checks(ctx))), - event_handler: |ctx, event, _framework, data| Box::pin(listener(ctx, event, data)), - on_error: |error| { - Box::pin(async move { - match error { - poise::FrameworkError::CommandCheckFailed { .. } => { - // suppress error - } - error => { - if let Err(e) = poise::builtins::on_error(error).await { - log::error!("Error while handling error: {}", e); - } - } - } - }) - }, - ..Default::default() - }; - - // Start metrics - init_metrics(); + let args = env::args().collect::>(); + let cmd_word = args.last().map(|w| w.as_str()); let database = Pool::connect(&env::var("DATABASE_URL").expect("No database URL provided")).await.unwrap(); - sqlx::migrate!().run(&database).await?; + match cmd_word { + Some("clean") => { + let sent_clean_age = env::var("SENT_CLEAN_AGE")?; + if sent_clean_age.is_empty() { + panic!("No SENT_CLEAN_AGE") + } + sqlx::query!( + " + DELETE FROM reminders + WHERE `utc_time` < NOW() - INTERVAL ? DAY + AND status != 'pending' + ORDER BY `utc_time` + LIMIT 1000 + ", + sent_clean_age + ) + .execute(&database) + .await?; - let popular_timezones = sqlx::query!( - " - SELECT IFNULL(timezone, 'UTC') AS timezone - FROM users - WHERE timezone IS NOT NULL - GROUP BY timezone - ORDER BY COUNT(timezone) DESC - LIMIT 21 - " - ) - .fetch_all(&database) - .await - .unwrap() - .iter() - .map(|t| t.timezone.parse::().unwrap()) - .collect::>(); + let total_clean_age = env::var("TOTAL_CLEAN_AGE"); + if let Ok(total_clean_age) = total_clean_age { + sqlx::query!( + " + DELETE FROM reminders + WHERE `utc_time` < NOW() - INTERVAL ? DAY + ORDER BY `utc_time` + LIMIT 1000 + ", + total_clean_age + ) + .execute(&database) + .await?; + } - let framework = poise::Framework::builder() - .setup(move |ctx, _bot, framework| { - Box::pin(async move { - poise::builtins::register_globally(ctx, &framework.options().commands).await?; + Ok(()) + } + _ => { + let discord_token = + env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment"); - let kill_tx = tx.clone(); - let kill_recv = tx.subscribe(); - - let ctx1 = ctx.clone(); - let ctx2 = ctx.clone(); - - let pool1 = database.clone(); - let pool2 = database.clone(); - - let run_settings = env::var("DONTRUN").unwrap_or_else(|_| "".to_string()); - - if !run_settings.contains("postman") { - tokio::spawn(async move { - match postman::initialize(kill_recv, ctx1, &pool1).await { - Ok(_) => {} - Err(e) => { - panic!("postman exiting: {}", e); + let options = poise::FrameworkOptions { + commands: vec![ + help::command(), + info::command(), + clock::command(), + donate::command(), + clock_context_menu(), + dashboard::command(), + timezone::command(), + poise::Command { + subcommands: vec![allowed_dm::set::set(), allowed_dm::unset::unset()], + ..allowed_dm::allowed_dm() + }, + poise::Command { + subcommands: vec![poise::Command { + subcommands: vec![ + settings::ephemeral_confirmations::set::set(), + settings::ephemeral_confirmations::unset::unset(), + ], + ..settings::ephemeral_confirmations::ephemeral_confirmations() + }], + ..settings::settings() + }, + webhook::command(), + poise::Command { + subcommands: vec![ + command_macro::delete_macro::delete_macro(), + command_macro::finish_macro::finish_macro(), + command_macro::list_macro::list_macro(), + command_macro::record_macro::record_macro(), + command_macro::run_macro::run_macro(), + ], + ..command_macro::command_macro() + }, + pause::command(), + offset::command(), + nudge::command(), + look::command(), + delete::command(), + poise::Command { + subcommands: vec![ + timer::list::list(), + timer::start::start(), + timer::delete::delete(), + ], + ..timer::timer() + }, + multiline::command(), + remind::command(), + poise::Command { + subcommands: vec![ + poise::Command { + subcommands: vec![ + todo::guild::add::add(), + todo::guild::view::view(), + ], + ..todo::guild::guild() + }, + poise::Command { + subcommands: vec![ + todo::channel::add::add(), + todo::channel::view::view(), + ], + ..todo::channel::channel() + }, + poise::Command { + subcommands: vec![todo::user::add::add(), todo::user::view::view()], + ..todo::user::user() + }, + ], + ..todo::todo() + }, + ], + allowed_mentions: None, + command_check: Some(|ctx| Box::pin(all_checks(ctx))), + event_handler: |ctx, event, _framework, data| Box::pin(listener(ctx, event, data)), + on_error: |error| { + Box::pin(async move { + match error { + poise::FrameworkError::CommandCheckFailed { .. } => { + // suppress error } - }; - }); - } else { - warn!("Not running postman"); - } + error => { + if let Err(e) = poise::builtins::on_error(error).await { + log::error!("Error while handling error: {}", e); + } + } + } + }) + }, + ..Default::default() + }; - if !run_settings.contains("web") { - tokio::spawn(async move { - web::initialize(kill_tx, ctx2, pool2).await.unwrap(); - }); - } else { - warn!("Not running web"); - } + // Start metrics + init_metrics(); - Ok(Data { - database, - popular_timezones, - recording_macros: Default::default(), - _broadcast: tx, + sqlx::migrate!().run(&database).await?; + + let popular_timezones = sqlx::query!( + " + SELECT IFNULL(timezone, 'UTC') AS timezone + FROM users + WHERE timezone IS NOT NULL + GROUP BY timezone + ORDER BY COUNT(timezone) DESC + LIMIT 21 + " + ) + .fetch_all(&database) + .await + .unwrap() + .iter() + .map(|t| t.timezone.parse::().unwrap()) + .collect::>(); + + let framework = poise::Framework::builder() + .setup(move |ctx, _bot, framework| { + Box::pin(async move { + poise::builtins::register_globally(ctx, &framework.options().commands) + .await?; + + let kill_tx = tx.clone(); + let kill_recv = tx.subscribe(); + + let ctx1 = ctx.clone(); + let ctx2 = ctx.clone(); + + let pool1 = database.clone(); + let pool2 = database.clone(); + + let run_settings = env::var("DONTRUN").unwrap_or_else(|_| "".to_string()); + + if !run_settings.contains("postman") { + tokio::spawn(async move { + match postman::initialize(kill_recv, ctx1, &pool1).await { + Ok(_) => {} + Err(e) => { + panic!("postman exiting: {}", e); + } + }; + }); + } else { + warn!("Not running postman"); + } + + if !run_settings.contains("web") { + tokio::spawn(async move { + web::initialize(kill_tx, ctx2, pool2).await.unwrap(); + }); + } else { + warn!("Not running web"); + } + + Ok(Data { + database, + popular_timezones, + recording_macros: Default::default(), + _broadcast: tx, + }) + }) }) - }) - }) - .options(options) - .build(); + .options(options) + .build(); - let mut client = ClientBuilder::new(&discord_token, GatewayIntents::GUILDS) - .framework(framework) - .activity(ActivityData::watching("for /remind")) - .await?; + let mut client = ClientBuilder::new(&discord_token, GatewayIntents::GUILDS) + .framework(framework) + .activity(ActivityData::watching("for /remind")) + .await?; - client.start_autosharded().await?; + client.start_autosharded().await?; - Ok(()) + Ok(()) + } + } } diff --git a/systemd/reminder-rs-clean-old.service b/systemd/reminder-rs-clean-old.service new file mode 100644 index 0000000..b7adad6 --- /dev/null +++ b/systemd/reminder-rs-clean-old.service @@ -0,0 +1,11 @@ +[Unit] +Description=Clean old data from Reminder Bot + +[Service] +User=reminder +Type=simple +ExecStart=/usr/bin/reminder-rs clean +WorkingDirectory=/etc/reminder-rs + +[Install] +WantedBy=multi-user.target diff --git a/systemd/reminder-rs-clean-old.timer b/systemd/reminder-rs-clean-old.timer new file mode 100644 index 0000000..b8baf09 --- /dev/null +++ b/systemd/reminder-rs-clean-old.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Clean reminder data twice daily + +[Timer] +OnCalendar=*-*-* 0/8 +Unit=reminder-rs-clean-old.service + +[Install] +WantedBy=timers.target