2020-09-11 16:41:15 +00:00
|
|
|
use custom_error::custom_error;
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
use regex_command_attr::command;
|
|
|
|
|
|
|
|
use serenity::{
|
|
|
|
client::Context,
|
|
|
|
model::{
|
2020-09-11 16:41:15 +00:00
|
|
|
id::{
|
|
|
|
UserId,
|
|
|
|
ChannelId,
|
|
|
|
GuildId,
|
|
|
|
},
|
2020-09-01 14:34:50 +00:00
|
|
|
channel::{
|
|
|
|
Message,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
framework::standard::CommandResult,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
models::{
|
|
|
|
ChannelData,
|
2020-09-01 17:18:45 +00:00
|
|
|
GuildData,
|
2020-09-01 14:34:50 +00:00
|
|
|
UserData,
|
2020-09-05 20:17:45 +00:00
|
|
|
Reminder,
|
2020-09-08 22:08:02 +00:00
|
|
|
Timer,
|
2020-09-01 14:34:50 +00:00
|
|
|
},
|
|
|
|
SQLPool,
|
|
|
|
time_parser::TimeParser,
|
|
|
|
};
|
|
|
|
|
|
|
|
use chrono::NaiveDateTime;
|
|
|
|
|
2020-09-08 22:08:02 +00:00
|
|
|
use num_integer::Integer;
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
default::Default,
|
|
|
|
time::{
|
|
|
|
SystemTime,
|
|
|
|
UNIX_EPOCH,
|
|
|
|
}
|
|
|
|
};
|
2020-09-05 20:17:45 +00:00
|
|
|
|
2020-09-08 22:08:02 +00:00
|
|
|
use regex::Regex;
|
2020-09-05 20:17:45 +00:00
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
use serde_json::json;
|
|
|
|
|
2020-09-05 20:17:45 +00:00
|
|
|
lazy_static! {
|
|
|
|
static ref REGEX_CHANNEL: Regex = Regex::new(r#"^\s*<#(\d+)>\s*$"#).unwrap();
|
2020-09-11 16:41:15 +00:00
|
|
|
|
|
|
|
static ref REGEX_CHANNEL_USER: Regex = Regex::new(r#"^\s*<(#|@)(?:!)?(\d+)>\s*$"#).unwrap();
|
2020-09-05 20:17:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
#[command]
|
2020-09-01 17:48:40 +00:00
|
|
|
#[supports_dm(false)]
|
|
|
|
#[permission_level(Restricted)]
|
2020-09-01 14:34:50 +00:00
|
|
|
async fn pause(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
2020-09-01 16:07:51 +00:00
|
|
|
let mut channel = ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), &pool).await.unwrap();
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
if args.len() == 0 {
|
|
|
|
channel.paused = !channel.paused;
|
|
|
|
channel.paused_until = None;
|
|
|
|
|
2020-09-01 16:07:51 +00:00
|
|
|
channel.commit_changes(&pool).await;
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
if channel.paused {
|
2020-09-01 17:37:43 +00:00
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "pause/paused_indefinite").await).await;
|
2020-09-01 14:34:50 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-09-01 17:37:43 +00:00
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "pause/unpaused").await).await;
|
2020-09-01 14:34:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let parser = TimeParser::new(args, user_data.timezone.parse().unwrap());
|
|
|
|
let pause_until = parser.timestamp();
|
|
|
|
|
|
|
|
match pause_until {
|
|
|
|
Ok(timestamp) => {
|
|
|
|
channel.paused = true;
|
|
|
|
channel.paused_until = Some(NaiveDateTime::from_timestamp(timestamp, 0));
|
|
|
|
|
2020-09-01 16:07:51 +00:00
|
|
|
channel.commit_changes(&pool).await;
|
2020-09-01 14:34:50 +00:00
|
|
|
|
2020-09-01 17:37:43 +00:00
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "pause/paused_until").await).await;
|
2020-09-01 14:34:50 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
Err(_) => {
|
2020-09-01 17:37:43 +00:00
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "pause/invalid_time").await).await;
|
2020-09-01 14:34:50 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-01 17:18:45 +00:00
|
|
|
|
|
|
|
#[command]
|
2020-09-01 17:48:40 +00:00
|
|
|
#[permission_level(Restricted)]
|
2020-09-01 17:18:45 +00:00
|
|
|
async fn offset(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
2020-09-01 17:18:45 +00:00
|
|
|
|
|
|
|
if args.len() == 0 {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "offset/help").await).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let parser = TimeParser::new(args, user_data.timezone());
|
|
|
|
|
|
|
|
if let Ok(displacement) = parser.displacement() {
|
|
|
|
if let Some(guild) = msg.guild(&ctx).await {
|
|
|
|
let guild_data = GuildData::from_guild(guild, &pool).await.unwrap();
|
|
|
|
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
UPDATE reminders
|
|
|
|
INNER JOIN `channels`
|
|
|
|
ON `channels`.id = reminders.channel_id
|
|
|
|
SET
|
|
|
|
reminders.`time` = reminders.`time` + ?
|
|
|
|
WHERE channels.guild_id = ?
|
|
|
|
", displacement, guild_data.id)
|
|
|
|
.execute(&pool)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
} else {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
UPDATE reminders SET `time` = `time` + ? WHERE reminders.channel_id = ?
|
|
|
|
", displacement, user_data.dm_channel)
|
|
|
|
.execute(&pool)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let response = user_data.response(&pool, "offset/success").await.replacen("{}", &displacement.to_string(), 1);
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, response).await;
|
|
|
|
} else {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "offset/invalid_time").await).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-01 17:37:43 +00:00
|
|
|
|
|
|
|
#[command]
|
2020-09-01 17:48:40 +00:00
|
|
|
#[permission_level(Restricted)]
|
2020-09-01 17:37:43 +00:00
|
|
|
async fn nudge(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
2020-09-01 17:37:43 +00:00
|
|
|
let mut channel = ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), &pool).await.unwrap();
|
|
|
|
|
|
|
|
if args.len() == 0 {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "nudge/invalid_time").await).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let parser = TimeParser::new(args, user_data.timezone.parse().unwrap());
|
|
|
|
let nudge_time = parser.displacement();
|
|
|
|
|
|
|
|
match nudge_time {
|
|
|
|
Ok(displacement) => {
|
|
|
|
if displacement < i16::MIN as i64 || displacement > i16::MAX as i64 {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "nudge/invalid_time").await).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
channel.nudge = displacement as i16;
|
|
|
|
|
|
|
|
channel.commit_changes(&pool).await;
|
|
|
|
|
|
|
|
let response = user_data.response(&pool, "nudge/success").await.replacen("{}", &displacement.to_string(), 1);
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, response).await;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
Err(_) => {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "nudge/invalid_time").await).await;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-05 20:17:45 +00:00
|
|
|
|
|
|
|
enum TimeDisplayType {
|
|
|
|
Absolute,
|
|
|
|
Relative,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct LookFlags {
|
|
|
|
pub limit: u16,
|
|
|
|
pub show_disabled: bool,
|
|
|
|
pub channel_id: Option<u64>,
|
|
|
|
time_display: TimeDisplayType,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for LookFlags {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
limit: u16::MAX,
|
|
|
|
show_disabled: true,
|
|
|
|
channel_id: None,
|
|
|
|
time_display: TimeDisplayType::Relative
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LookFlags {
|
|
|
|
fn from_string(args: &str) -> Self {
|
|
|
|
let mut new_flags: Self = Default::default();
|
|
|
|
|
|
|
|
for arg in args.split(" ") {
|
|
|
|
match arg {
|
|
|
|
"enabled" => {
|
|
|
|
new_flags.show_disabled = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
"time" => {
|
|
|
|
new_flags.time_display = TimeDisplayType::Absolute;
|
|
|
|
},
|
|
|
|
|
|
|
|
param => {
|
|
|
|
if let Ok(val) = param.parse::<u16>() {
|
|
|
|
new_flags.limit = val;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
new_flags.channel_id = REGEX_CHANNEL.captures(&args)
|
|
|
|
.map(|cap| cap.get(1))
|
|
|
|
.flatten()
|
|
|
|
.map(|c| c.as_str().parse::<u64>().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
new_flags
|
|
|
|
}
|
|
|
|
|
|
|
|
fn display_time(&self, timestamp: u64) -> String {
|
|
|
|
|
|
|
|
String::from("")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[command]
|
2020-09-07 23:12:17 +00:00
|
|
|
#[permission_level(Managed)]
|
2020-09-05 20:17:45 +00:00
|
|
|
async fn look(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
2020-09-05 20:17:45 +00:00
|
|
|
|
|
|
|
let flags = LookFlags::from_string(&args);
|
|
|
|
|
2020-09-08 22:08:02 +00:00
|
|
|
let enabled = if flags.show_disabled { "0,1" } else { "1" };
|
2020-09-05 20:17:45 +00:00
|
|
|
|
|
|
|
let reminders = if let Some(guild_id) = msg.guild_id.map(|f| f.as_u64().to_owned()) {
|
|
|
|
let channel_id = flags.channel_id.unwrap_or(msg.channel_id.as_u64().to_owned());
|
|
|
|
|
|
|
|
sqlx::query_as!(Reminder,
|
|
|
|
"
|
|
|
|
SELECT
|
2020-09-07 23:12:17 +00:00
|
|
|
reminders.id, reminders.time, reminders.name, reminders.channel_id
|
2020-09-05 20:17:45 +00:00
|
|
|
FROM
|
|
|
|
reminders
|
|
|
|
INNER JOIN
|
|
|
|
channels
|
|
|
|
ON
|
|
|
|
reminders.channel_id = channels.id
|
|
|
|
WHERE
|
|
|
|
channels.guild_id = (SELECT id FROM guilds WHERE guild = ?) AND
|
|
|
|
channels.channel = ? AND
|
2020-09-08 22:08:02 +00:00
|
|
|
FIND_IN_SET(reminders.enabled, ?)
|
2020-09-05 20:17:45 +00:00
|
|
|
LIMIT
|
|
|
|
?
|
|
|
|
", guild_id, channel_id, enabled, flags.limit)
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sqlx::query_as!(Reminder,
|
|
|
|
"
|
|
|
|
SELECT
|
2020-09-07 23:12:17 +00:00
|
|
|
id, time, name, channel_id
|
2020-09-05 20:17:45 +00:00
|
|
|
FROM
|
|
|
|
reminders
|
|
|
|
WHERE
|
2020-09-07 23:12:17 +00:00
|
|
|
reminders.channel_id = (SELECT id FROM channels WHERE channel = ?) AND
|
2020-09-08 22:08:02 +00:00
|
|
|
FIND_IN_SET(reminders.enabled, ?)
|
2020-09-05 20:17:45 +00:00
|
|
|
LIMIT
|
|
|
|
?
|
|
|
|
", msg.channel_id.as_u64(), enabled, flags.limit)
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await
|
2020-09-07 23:12:17 +00:00
|
|
|
}.unwrap();
|
2020-09-05 20:17:45 +00:00
|
|
|
|
|
|
|
if reminders.len() == 0 {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "look/no_reminders").await).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let inter = user_data.response(&pool, "look/inter").await;
|
|
|
|
|
|
|
|
let display = reminders
|
|
|
|
.iter()
|
|
|
|
.map(|reminder| format!("'{}' *{}* **{}**", reminder.name, &inter, reminder.time))
|
|
|
|
.collect::<Vec<String>>().join("\n");
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, display).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-07 23:12:17 +00:00
|
|
|
|
|
|
|
#[command]
|
|
|
|
#[permission_level(Managed)]
|
2020-09-08 22:08:02 +00:00
|
|
|
async fn delete(ctx: &Context, msg: &Message, _args: String) -> CommandResult {
|
2020-09-07 23:12:17 +00:00
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
2020-09-07 23:12:17 +00:00
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "del/listing").await).await;
|
|
|
|
|
|
|
|
let reminders = if let Some(guild_id) = msg.guild_id.map(|f| f.as_u64().to_owned()) {
|
|
|
|
sqlx::query_as!(Reminder,
|
|
|
|
"
|
|
|
|
SELECT
|
|
|
|
reminders.id, reminders.time, reminders.name, reminders.channel_id
|
|
|
|
FROM
|
|
|
|
reminders
|
|
|
|
INNER JOIN
|
|
|
|
channels
|
|
|
|
ON
|
|
|
|
reminders.channel_id = channels.id
|
|
|
|
WHERE
|
|
|
|
channels.guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
|
|
|
", guild_id)
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sqlx::query_as!(Reminder,
|
|
|
|
"
|
|
|
|
SELECT
|
|
|
|
id, time, name, channel_id
|
|
|
|
FROM
|
|
|
|
reminders
|
|
|
|
WHERE
|
|
|
|
channel_id = (SELECT id FROM channels WHERE channel = ?)
|
|
|
|
", msg.channel_id.as_u64())
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await
|
|
|
|
}.unwrap();
|
|
|
|
|
|
|
|
let mut reminder_ids: Vec<u32> = vec![];
|
|
|
|
|
|
|
|
let enumerated_reminders = reminders
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(count, reminder)| {
|
|
|
|
reminder_ids.push(reminder.id);
|
|
|
|
format!("**{}**: '{}' *{}* at {}", count + 1, reminder.name, reminder.channel_id, reminder.time)
|
|
|
|
})
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join("\n");
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, enumerated_reminders).await;
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "del/listed").await).await;
|
|
|
|
|
|
|
|
let reply = msg.channel_id.await_reply(&ctx)
|
|
|
|
.filter_limit(1)
|
|
|
|
.author_id(msg.author.id)
|
|
|
|
.channel_id(msg.channel_id).await;
|
|
|
|
|
|
|
|
if let Some(content) = reply.map(|m| m.content.replace(",", " ")) {
|
|
|
|
let parts = content.split(" ").filter(|i| i.len() > 0).collect::<Vec<&str>>();
|
|
|
|
|
|
|
|
let valid_parts = parts
|
|
|
|
.iter()
|
|
|
|
.filter_map(
|
|
|
|
|i|
|
|
|
|
i.parse::<usize>()
|
|
|
|
.ok()
|
|
|
|
.map(
|
|
|
|
|val|
|
2020-09-08 22:08:02 +00:00
|
|
|
reminder_ids.get(val - 1)
|
2020-09-07 23:12:17 +00:00
|
|
|
)
|
|
|
|
.flatten()
|
|
|
|
)
|
|
|
|
.map(|item| item.to_string())
|
|
|
|
.collect::<Vec<String>>();
|
|
|
|
|
|
|
|
if parts.len() == valid_parts.len() {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
2020-09-08 22:08:02 +00:00
|
|
|
DELETE FROM reminders WHERE FIND_IN_SET(id, ?)
|
2020-09-07 23:12:17 +00:00
|
|
|
", valid_parts.join(","))
|
|
|
|
.execute(&pool)
|
2020-09-08 22:08:02 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2020-09-07 23:12:17 +00:00
|
|
|
|
|
|
|
// TODO add deletion events to event list
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "del/count").await).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-08 22:08:02 +00:00
|
|
|
|
|
|
|
#[command]
|
|
|
|
#[permission_level(Managed)]
|
|
|
|
async fn timer(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
fn time_difference(start_time: NaiveDateTime) -> String {
|
|
|
|
let unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
|
|
|
|
let now = NaiveDateTime::from_timestamp(unix_time, 0);
|
|
|
|
|
|
|
|
let delta = (now - start_time).num_seconds();
|
|
|
|
|
|
|
|
let (minutes, seconds) = delta.div_rem(&60);
|
|
|
|
let (hours, minutes) = minutes.div_rem(&60);
|
|
|
|
|
|
|
|
format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
|
|
|
|
}
|
|
|
|
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
2020-09-11 16:41:15 +00:00
|
|
|
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
2020-09-08 22:08:02 +00:00
|
|
|
|
|
|
|
let mut args_iter = args.splitn(2, " ");
|
|
|
|
|
|
|
|
let owner = msg.guild_id.map(|g| g.as_u64().to_owned()).unwrap_or(msg.author.id.as_u64().to_owned());
|
|
|
|
|
|
|
|
match args_iter.next() {
|
|
|
|
Some("list") => {
|
|
|
|
let timers = Timer::from_owner(owner, &pool).await;
|
|
|
|
|
|
|
|
let _ = msg.channel_id.send_message(&ctx, |m| m
|
|
|
|
.embed(|e| {
|
|
|
|
e.fields(timers.iter().map(|timer| (&timer.name, time_difference(timer.start_time), false)))
|
|
|
|
})
|
|
|
|
).await;
|
|
|
|
},
|
|
|
|
|
|
|
|
Some("start") => {
|
|
|
|
let count = Timer::count_from_owner(owner, &pool).await;
|
|
|
|
|
|
|
|
if count >= 25 {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "timer/limit").await).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let name = args_iter.next().map(|s| s.to_string()).unwrap_or(format!("New timer #{}", count + 1));
|
|
|
|
|
|
|
|
Timer::create(&name, owner, &pool).await;
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "timer/success").await).await;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
Some("delete") => {
|
|
|
|
if let Some(name) = args_iter.next() {
|
|
|
|
let exists = sqlx::query!(
|
|
|
|
"
|
|
|
|
SELECT 1 as _r FROM timers WHERE owner = ? AND name = ?
|
|
|
|
", owner, name)
|
|
|
|
.fetch_one(&pool)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
if exists.is_ok() {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
DELETE FROM timers WHERE owner = ? AND name = ?
|
|
|
|
", owner, name)
|
|
|
|
.execute(&pool)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "timer/deleted").await).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "timer/not_found").await).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "timer/help").await).await;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
let _ = msg.channel_id.say(&ctx, user_data.response(&pool, "timer/help").await).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-09-11 16:41:15 +00:00
|
|
|
|
|
|
|
enum RemindCommand {
|
|
|
|
Remind,
|
|
|
|
Interval,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ReminderScope {
|
|
|
|
User(u64),
|
|
|
|
Channel(u64),
|
|
|
|
}
|
|
|
|
|
|
|
|
custom_error!{ReminderError
|
|
|
|
LongTime = "Time too long",
|
|
|
|
LongInterval = "Interval too long",
|
|
|
|
PastTime = "Time has already passed",
|
|
|
|
ShortInterval = "Interval too short",
|
|
|
|
InvalidTag = "Invalid reminder scope",
|
|
|
|
NotEnoughArgs = "Not enough args",
|
|
|
|
InvalidTime = "Invalid time provided",
|
|
|
|
DiscordError = "Bad response received from Discord"
|
|
|
|
}
|
|
|
|
|
|
|
|
#[command]
|
|
|
|
#[permission_level(Managed)]
|
|
|
|
async fn remind(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
remind_command(ctx, msg, args, RemindCommand::Remind).await;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[command]
|
|
|
|
#[permission_level(Managed)]
|
|
|
|
async fn interval(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
|
|
|
remind_command(ctx, msg, args, RemindCommand::Interval).await;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn remind_command(ctx: &Context, msg: &Message, args: String, command: RemindCommand) {
|
|
|
|
let user_data;
|
|
|
|
|
|
|
|
{
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
|
|
|
user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut args_iter = args.split(' ').filter(|s| s.len() > 0);
|
|
|
|
|
|
|
|
if let Some(first_arg) = args_iter.next().map(|s| s.to_string()) {
|
|
|
|
|
|
|
|
let scope_id;
|
|
|
|
let time_parser;
|
|
|
|
let content;
|
|
|
|
|
|
|
|
let guild_id = msg.guild_id;
|
|
|
|
|
|
|
|
if let Some((Some(scope_match), Some(id_match))) = REGEX_CHANNEL_USER
|
|
|
|
.captures(&first_arg)
|
|
|
|
.map(|cap| (cap.get(1), cap.get(2))) {
|
|
|
|
|
|
|
|
if scope_match.as_str() == "@" {
|
|
|
|
scope_id = ReminderScope::User(id_match.as_str().parse::<u64>().unwrap());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
scope_id = ReminderScope::Channel(id_match.as_str().parse::<u64>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(next) = args_iter.next().map(|inner| inner.to_string()) {
|
|
|
|
time_parser = TimeParser::new(next, user_data.timezone.parse().unwrap());
|
|
|
|
|
|
|
|
content = args_iter.collect::<Vec<&str>>().join(" ");
|
|
|
|
|
|
|
|
// TODO replace unwrap with converting response into discord response
|
|
|
|
create_reminder(ctx, guild_id, scope_id, time_parser, content).await.unwrap();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
scope_id = ReminderScope::Channel(msg.channel_id.as_u64().to_owned());
|
|
|
|
|
|
|
|
time_parser = TimeParser::new(first_arg, user_data.timezone.parse().unwrap());
|
|
|
|
|
|
|
|
content = args_iter.collect::<Vec<&str>>().join(" ");
|
|
|
|
|
|
|
|
// TODO replace unwrap with converting response into discord response
|
|
|
|
create_reminder(ctx, guild_id, scope_id, time_parser, content).await.unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn create_reminder(ctx: &Context, guild_id: Option<GuildId>, scope_id: ReminderScope, time_parser: TimeParser, content: String)
|
|
|
|
-> Result<(), ReminderError> {
|
|
|
|
|
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
|
|
|
|
|
|
|
let db_channel_id = match scope_id {
|
|
|
|
ReminderScope::User(user_id) => {
|
|
|
|
let user = UserId(user_id).to_user(&ctx).await.unwrap();
|
|
|
|
|
|
|
|
let user_data = UserData::from_user(&user, &ctx, &pool).await.unwrap();
|
|
|
|
|
|
|
|
user_data.dm_channel
|
|
|
|
},
|
|
|
|
|
|
|
|
ReminderScope::Channel(channel_id) => {
|
|
|
|
let channel = ChannelId(channel_id).to_channel(&ctx).await.unwrap();
|
|
|
|
|
|
|
|
if channel.clone().guild().map(|gc| gc.guild_id) != guild_id {
|
|
|
|
return Err(ReminderError::InvalidTag)
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut channel_data = ChannelData::from_channel(channel.clone(), &pool).await.unwrap();
|
|
|
|
|
|
|
|
if let Some(guild_channel) = channel.guild() {
|
|
|
|
if channel_data.webhook_token.is_none() || channel_data.webhook_id.is_none() {
|
|
|
|
if let Ok(webhook) = ctx.http.create_webhook(guild_channel.id.as_u64().to_owned(), &json!({"name": "Reminder"})).await {
|
|
|
|
channel_data.webhook_id = Some(webhook.id.as_u64().to_owned());
|
|
|
|
channel_data.webhook_token = Some(webhook.token);
|
|
|
|
|
|
|
|
channel_data.commit_changes(&pool).await;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Err(ReminderError::DiscordError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
channel_data.id
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// validate time, channel, content
|
|
|
|
if content.len() == 0 {
|
|
|
|
Err(ReminderError::NotEnoughArgs)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
match time_parser.timestamp() {
|
|
|
|
Ok(time) => {
|
|
|
|
let unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
|
|
|
|
|
|
|
|
if time > unix_time {
|
|
|
|
if time > unix_time + 60*60*24*365*50 {
|
|
|
|
Err(ReminderError::LongTime)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(ReminderError::PastTime)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
Err(_) => {
|
|
|
|
Err(ReminderError::InvalidTime)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|