Merge pull request 'Added configurable event reminders' (#10) from config_reminders into master

Reviewed-on: https://git.etztech.xyz/ZeroHD/HypeBot/pulls/10
master
Joey Hines 2020-08-09 17:06:04 +02:00
commit 47a1bc9a2a
4 changed files with 79 additions and 36 deletions

View File

@ -41,4 +41,19 @@ ping_roles = [0]
event_timezone = "America/New_York" event_timezone = "America/New_York"
# Path to place logs in # Path to place logs in
log_path = "log/" log_path = "log/"
# Configurable Reminders
[[reminders]]
# Message to send, "{EVENT_NAME}" gets replaced with the event's name
msg = "{EVENT_NAME} is starting now!"
# number of minutes before an event to send the reminder
reminder_time = 0
[[reminders]]
msg = "{EVENT_NAME} is starting in 1 minute!"
reminder_time = 1
[[reminders]]
msg = "{EVENT_NAME} is starting in 5 minutes!"
reminder_time = 5
``` ```

View File

@ -80,8 +80,10 @@ pub fn get_countdown_link(event_name: &String, utc: &DateTime<Utc>) -> String {
let msg = event_name.replace(" ", "+"); let msg = event_name.replace(" ", "+");
let time = utc.format("%G%m%dT%H%M").to_string(); let time = utc.format("%G%m%dT%H%M").to_string();
format!("https://www.timeanddate.com/countdown/generic?iso={}&p0=&msg={}&font=sanserif&csz=1", time, msg) format!(
"https://www.timeanddate.com/countdown/generic?iso={}&p0=&msg={}&font=sanserif&csz=1",
time, msg
)
} }
/// Sends the event message to the event channel /// Sends the event message to the event channel
@ -98,9 +100,13 @@ pub fn send_event_msg(
let native_time = utc_time.with_timezone(&config.event_timezone); let native_time = utc_time.with_timezone(&config.event_timezone);
let ping_roles = config.ping_roles.clone().into_iter().map(|role| { let ping_roles = config
format!("<@&{}>", role) .ping_roles
}).collect::<Vec<String>>().join(" "); .clone()
.into_iter()
.map(|role| format!("<@&{}>", role))
.collect::<Vec<String>>()
.join(" ");
// Send message // Send message
let msg = channel.id().send_message(&http, |m| { let msg = channel.id().send_message(&http, |m| {
@ -208,7 +214,7 @@ pub fn get_scheduler(
let mut context = data.write(); let mut context = data.write();
Ok(context Ok(context
.get_mut::<SchedulerKey>() .get_mut::<SchedulerKey>()
.ok_or(CommandError("Unable to scheduler".to_string()))? .ok_or(CommandError("Unable to get scheduler".to_string()))?
.clone()) .clone())
} }
@ -248,6 +254,7 @@ pub fn permission_check(ctx: &mut Context, msg: &Message, _command_name: &str) -
false false
} }
/// Schedule event reminders
pub fn schedule_event(http: &Arc<Http>, data: &Arc<RwLock<ShareMap>>, event: &Event) { pub fn schedule_event(http: &Arc<Http>, data: &Arc<RwLock<ShareMap>>, event: &Event) {
let scheduler = { let scheduler = {
let mut context = data.write(); let mut context = data.write();
@ -257,24 +264,48 @@ pub fn schedule_event(http: &Arc<Http>, data: &Arc<RwLock<ShareMap>>, event: &Ev
.clone() .clone()
}; };
if event.reminder_sent < 1 { let config = get_config(&data).unwrap();
if let Some(reminders) = config.reminders {
let event_time: DateTime<Utc> = DateTime::<Utc>::from_utc(event.event_time.clone(), Utc); let event_time: DateTime<Utc> = DateTime::<Utc>::from_utc(event.event_time.clone(), Utc);
let reminder_time = event_time - chrono::Duration::minutes(10); let delete_time = event_time + chrono::Duration::minutes(1);
let mut scheduler = scheduler.write(); let mut scheduler = scheduler.write();
for reminder in reminders {
let reminder_time =
event_time - chrono::Duration::minutes(reminder.reminder_time as i64);
if reminder_time > chrono::offset::Utc::now() {
let reminder_msg = reminder.msg.clone();
let http = http.clone();
let data = data.clone();
let event = event.clone();
scheduler.add_task_datetime(reminder_time, move |_| {
send_reminders(&http, &data, &event, &reminder_msg)
});
}
}
let http = http.clone(); let http = http.clone();
let data = data.clone(); let data = data.clone();
let event = event.clone(); let event = event.clone();
scheduler.add_task_datetime(delete_time, move |_| {
scheduler.add_task_datetime(reminder_time, move |_| send_reminders(&http, &data, &event)); delete_event(&http, &data, &event);
DateResult::Done
});
} }
} }
/// Send reminders /// Send reminders
pub fn send_reminders(http: &Arc<Http>, data: &Arc<RwLock<ShareMap>>, event: &Event) -> DateResult { pub fn send_reminders(
http: &Arc<Http>,
data: &Arc<RwLock<ShareMap>>,
event: &Event,
reminder_msg: &String,
) -> DateResult {
let config = get_config(&data).unwrap(); let config = get_config(&data).unwrap();
let event_channel_id = config.event_channel; let event_channel_id = config.event_channel;
let event_time: DateTime<Utc> = DateTime::<Utc>::from_utc(event.event_time.clone(), Utc);
let delete_time = event_time + chrono::Duration::minutes(60);
if let Ok(message_id) = event.message_id.parse::<u64>() { if let Ok(message_id) = event.message_id.parse::<u64>() {
// Get message id // Get message id
@ -284,27 +315,13 @@ pub fn send_reminders(http: &Arc<Http>, data: &Arc<RwLock<ShareMap>>, event: &Ev
.unwrap_or(Vec::<User>::new()); .unwrap_or(Vec::<User>::new());
// Build reminder message // Build reminder message
let msg: String = format!("Hello! **{}** is starting soon!", &event.event_name); let msg: String = reminder_msg.replace("{EVENT_NAME}", event.event_name.as_str());
// Send reminder to each reacted user // Send reminder to each reacted user
for user in reaction_users { for user in reaction_users {
send_dm_message(&http, user, &msg); send_dm_message(&http, user, &msg);
} }
} }
set_reminder(config.db_url.clone(), event.id, 1).ok();
let scheduler = get_scheduler(data).unwrap();
let mut scheduler = scheduler.write();
let http = http.clone();
let data = data.clone();
let event = event.clone();
scheduler.add_task_datetime(delete_time, move |_| {
delete_event(&http, &data, &event);
DateResult::Done
});
} }
DateResult::Done DateResult::Done

View File

@ -5,6 +5,12 @@ use serde::{Deserialize, Deserializer};
use serenity::prelude::TypeMapKey; use serenity::prelude::TypeMapKey;
use std::fmt; use std::fmt;
#[derive(Debug, Deserialize, Clone)]
pub struct EventReminder {
pub msg: String,
pub reminder_time: u32,
}
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct HypeBotConfig { pub struct HypeBotConfig {
pub db_url: String, pub db_url: String,
@ -17,6 +23,7 @@ pub struct HypeBotConfig {
#[serde(deserialize_with = "from_tz_string")] #[serde(deserialize_with = "from_tz_string")]
pub event_timezone: Tz, pub event_timezone: Tz,
pub log_path: String, pub log_path: String,
pub reminders: Option<Vec<EventReminder>>,
} }
struct ConfigValueVisitor; struct ConfigValueVisitor;

View File

@ -10,13 +10,13 @@ extern crate log4rs;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use clap::{App, Arg}; use clap::{App, Arg};
use log::LevelFilter; use log::LevelFilter;
use log4rs::append::rolling_file::{RollingFileAppender};
use log4rs::config::{Appender, Config, Root};
use log4rs::encode::pattern::PatternEncoder;
use log4rs::append::console::ConsoleAppender; use log4rs::append::console::ConsoleAppender;
use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller; use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger; use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
use log4rs::append::rolling_file::policy::compound::CompoundPolicy; use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
use log4rs::append::rolling_file::RollingFileAppender;
use log4rs::config::{Appender, Config, Root};
use log4rs::encode::pattern::PatternEncoder;
use log4rs::filter::threshold::ThresholdFilter; use log4rs::filter::threshold::ThresholdFilter;
use log4rs::init_config; use log4rs::init_config;
use serenity::client::Client; use serenity::client::Client;
@ -28,10 +28,10 @@ use serenity::model::id::UserId;
use serenity::model::prelude::Ready; use serenity::model::prelude::Ready;
use serenity::prelude::{Context, EventHandler, RwLock}; use serenity::prelude::{Context, EventHandler, RwLock};
use std::collections::HashSet; use std::collections::HashSet;
use std::path::Path;
use std::process::exit; use std::process::exit;
use std::sync::Arc; use std::sync::Arc;
use white_rabbit::{DateResult, Scheduler}; use white_rabbit::{DateResult, Scheduler};
use std::path::Path;
mod database; mod database;
mod discord; mod discord;
@ -41,8 +41,8 @@ use database::models::NewEvent;
use database::*; use database::*;
use discord::events::{CANCEL_COMMAND, CONFIRM_COMMAND, CREATE_COMMAND}; use discord::events::{CANCEL_COMMAND, CONFIRM_COMMAND, CREATE_COMMAND};
use discord::{ use discord::{
delete_event, get_config, get_scheduler, log_error, permission_check, delete_event, get_config, get_scheduler, log_error, permission_check, schedule_event,
send_message_to_reaction_users, schedule_event, DraftEvent, SchedulerKey, send_message_to_reaction_users, DraftEvent, SchedulerKey,
}; };
use hypebot_config::HypeBotConfig; use hypebot_config::HypeBotConfig;
@ -71,7 +71,9 @@ impl EventHandler for Handler {
return; return;
} }
}; };
if reaction.channel_id.0 == config.event_channel && reaction.emoji.as_data() == INTERESTED_EMOJI { if reaction.channel_id.0 == config.event_channel
&& reaction.emoji.as_data() == INTERESTED_EMOJI
{
send_message_to_reaction_users( send_message_to_reaction_users(
&ctx, &ctx,
&reaction, &reaction,
@ -89,7 +91,9 @@ impl EventHandler for Handler {
return; return;
} }
}; };
if reaction.channel_id.0 == config.event_channel && reaction.emoji.as_data() == INTERESTED_EMOJI { if reaction.channel_id.0 == config.event_channel
&& reaction.emoji.as_data() == INTERESTED_EMOJI
{
send_message_to_reaction_users( send_message_to_reaction_users(
&ctx, &ctx,
&reaction, &reaction,