This commit is contained in:
jude 2024-10-13 19:01:37 +01:00
parent 6340f5a20f
commit eb5c851d97
4 changed files with 226 additions and 158 deletions

View File

@ -59,7 +59,6 @@ assets = [
["reminder-dashboard/dist/index.html", "lib/reminder-rs/static/index.html", "644"], ["reminder-dashboard/dist/index.html", "lib/reminder-rs/static/index.html", "644"],
["conf/default.env", "etc/reminder-rs/config.env", "600"], ["conf/default.env", "etc/reminder-rs/config.env", "600"],
["conf/Rocket.toml", "etc/reminder-rs/Rocket.toml", "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"] # ["nginx/reminder-rs", "etc/nginx/sites-available/reminder-rs", "755"]
] ]
conf-files = [ conf-files = [

View File

@ -29,7 +29,6 @@ use std::{
}; };
use chrono_tz::Tz; use chrono_tz::Tz;
use clap::Subcommand;
use log::warn; use log::warn;
use poise::serenity_prelude::{ use poise::serenity_prelude::{
model::{ model::{
@ -121,174 +120,224 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
let _ = dotenv::dotenv(); let _ = dotenv::dotenv();
} }
let discord_token = env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment"); let args = env::args().collect::<Vec<_>>();
let cmd_word = args.last().map(|w| w.as_str());
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 database = let database =
Pool::connect(&env::var("DATABASE_URL").expect("No database URL provided")).await.unwrap(); 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!( let total_clean_age = env::var("TOTAL_CLEAN_AGE");
" if let Ok(total_clean_age) = total_clean_age {
SELECT IFNULL(timezone, 'UTC') AS timezone sqlx::query!(
FROM users "
WHERE timezone IS NOT NULL DELETE FROM reminders
GROUP BY timezone WHERE `utc_time` < NOW() - INTERVAL ? DAY
ORDER BY COUNT(timezone) DESC ORDER BY `utc_time`
LIMIT 21 LIMIT 1000
" ",
) total_clean_age
.fetch_all(&database) )
.await .execute(&database)
.unwrap() .await?;
.iter() }
.map(|t| t.timezone.parse::<Tz>().unwrap())
.collect::<Vec<Tz>>();
let framework = poise::Framework::builder() Ok(())
.setup(move |ctx, _bot, framework| { }
Box::pin(async move { _ => {
poise::builtins::register_globally(ctx, &framework.options().commands).await?; let discord_token =
env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment");
let kill_tx = tx.clone(); let options = poise::FrameworkOptions {
let kill_recv = tx.subscribe(); commands: vec![
help::command(),
let ctx1 = ctx.clone(); info::command(),
let ctx2 = ctx.clone(); clock::command(),
donate::command(),
let pool1 = database.clone(); clock_context_menu(),
let pool2 = database.clone(); dashboard::command(),
timezone::command(),
let run_settings = env::var("DONTRUN").unwrap_or_else(|_| "".to_string()); poise::Command {
subcommands: vec![allowed_dm::set::set(), allowed_dm::unset::unset()],
if !run_settings.contains("postman") { ..allowed_dm::allowed_dm()
tokio::spawn(async move { },
match postman::initialize(kill_recv, ctx1, &pool1).await { poise::Command {
Ok(_) => {} subcommands: vec![poise::Command {
Err(e) => { subcommands: vec![
panic!("postman exiting: {}", e); 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 {
} else { log::error!("Error while handling error: {}", e);
warn!("Not running postman"); }
} }
}
})
},
..Default::default()
};
if !run_settings.contains("web") { // Start metrics
tokio::spawn(async move { init_metrics();
web::initialize(kill_tx, ctx2, pool2).await.unwrap();
});
} else {
warn!("Not running web");
}
Ok(Data { sqlx::migrate!().run(&database).await?;
database,
popular_timezones, let popular_timezones = sqlx::query!(
recording_macros: Default::default(), "
_broadcast: tx, 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::<Tz>().unwrap())
.collect::<Vec<Tz>>();
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) let mut client = ClientBuilder::new(&discord_token, GatewayIntents::GUILDS)
.framework(framework) .framework(framework)
.activity(ActivityData::watching("for /remind")) .activity(ActivityData::watching("for /remind"))
.await?; .await?;
client.start_autosharded().await?; client.start_autosharded().await?;
Ok(()) Ok(())
}
}
} }

View File

@ -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

View File

@ -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