Merge pull request 'Added configurable event reminders' (#10) from config_reminders into master
Reviewed-on: https://git.etztech.xyz/ZeroHD/HypeBot/pulls/10master
commit
47a1bc9a2a
15
README.md
15
README.md
|
@ -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
|
||||||
```
|
```
|
|
@ -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 http = http.clone();
|
||||||
let data = data.clone();
|
let data = data.clone();
|
||||||
let event = event.clone();
|
let event = event.clone();
|
||||||
|
|
||||||
scheduler.add_task_datetime(reminder_time, move |_| send_reminders(&http, &data, &event));
|
scheduler.add_task_datetime(reminder_time, move |_| {
|
||||||
|
send_reminders(&http, &data, &event, &reminder_msg)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue