2021-12-20 13:48:18 +00:00
|
|
|
use chrono::Duration;
|
2021-12-21 13:46:10 +00:00
|
|
|
use chrono_tz::Tz;
|
2022-02-06 15:47:59 +00:00
|
|
|
use lazy_static::lazy_static;
|
2021-12-20 13:48:18 +00:00
|
|
|
use log::{error, info, warn};
|
2021-12-21 13:46:10 +00:00
|
|
|
use num_integer::Integer;
|
|
|
|
use regex::{Captures, Regex};
|
2022-04-07 16:13:02 +00:00
|
|
|
use serde::Deserialize;
|
2021-12-20 13:48:18 +00:00
|
|
|
use serenity::{
|
|
|
|
builder::CreateEmbed,
|
2022-05-14 09:56:03 +00:00
|
|
|
http::{CacheHttp, Http, HttpError, StatusCode},
|
2021-12-21 13:46:10 +00:00
|
|
|
model::{
|
|
|
|
channel::{Channel, Embed as SerenityEmbed},
|
|
|
|
id::ChannelId,
|
|
|
|
webhook::Webhook,
|
|
|
|
},
|
2021-12-20 13:48:18 +00:00
|
|
|
Error, Result,
|
|
|
|
};
|
|
|
|
use sqlx::{
|
2022-04-07 16:13:02 +00:00
|
|
|
types::{
|
|
|
|
chrono::{NaiveDateTime, Utc},
|
|
|
|
Json,
|
|
|
|
},
|
2022-02-06 15:47:59 +00:00
|
|
|
Executor,
|
2021-12-20 13:48:18 +00:00
|
|
|
};
|
|
|
|
|
2022-02-19 22:11:21 +00:00
|
|
|
use crate::Database;
|
|
|
|
|
2021-12-20 13:48:18 +00:00
|
|
|
lazy_static! {
|
|
|
|
pub static ref TIMEFROM_REGEX: Regex =
|
|
|
|
Regex::new(r#"<<timefrom:(?P<time>\d+):(?P<format>.+)?>>"#).unwrap();
|
|
|
|
pub static ref TIMENOW_REGEX: Regex =
|
|
|
|
Regex::new(r#"<<timenow:(?P<timezone>(?:\w|/|_)+):(?P<format>.+)?>>"#).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fmt_displacement(format: &str, seconds: u64) -> String {
|
|
|
|
let mut seconds = seconds;
|
|
|
|
let mut days: u64 = 0;
|
|
|
|
let mut hours: u64 = 0;
|
|
|
|
let mut minutes: u64 = 0;
|
|
|
|
|
2021-12-21 13:46:10 +00:00
|
|
|
for (rep, time_type, div) in
|
|
|
|
[("%d", &mut days, 86400), ("%h", &mut hours, 3600), ("%m", &mut minutes, 60)].iter_mut()
|
2021-12-20 13:48:18 +00:00
|
|
|
{
|
|
|
|
if format.contains(*rep) {
|
|
|
|
let (divided, new_seconds) = seconds.div_rem(&div);
|
|
|
|
|
|
|
|
**time_type = divided;
|
|
|
|
seconds = new_seconds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
format
|
|
|
|
.replace("%s", &seconds.to_string())
|
|
|
|
.replace("%m", &minutes.to_string())
|
|
|
|
.replace("%h", &hours.to_string())
|
|
|
|
.replace("%d", &days.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn substitute(string: &str) -> String {
|
|
|
|
let new = TIMEFROM_REGEX.replace(string, |caps: &Captures| {
|
2022-05-13 22:08:52 +00:00
|
|
|
let final_time = caps.name("time").map(|m| m.as_str().parse::<i64>().ok()).flatten();
|
|
|
|
let format = caps.name("format").map(|m| m.as_str());
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
if let (Some(final_time), Some(format)) = (final_time, format) {
|
2021-12-20 13:48:18 +00:00
|
|
|
let dt = NaiveDateTime::from_timestamp(final_time, 0);
|
|
|
|
let now = Utc::now().naive_utc();
|
|
|
|
|
|
|
|
let difference = {
|
|
|
|
if now < dt {
|
|
|
|
dt - Utc::now().naive_utc()
|
|
|
|
} else {
|
|
|
|
Utc::now().naive_utc() - dt
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
fmt_displacement(format, difference.num_seconds() as u64)
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
TIMENOW_REGEX
|
|
|
|
.replace(&new, |caps: &Captures| {
|
2022-05-13 22:08:52 +00:00
|
|
|
let timezone = caps.name("timezone").map(|m| m.as_str().parse::<Tz>().ok()).flatten();
|
|
|
|
let format = caps.name("format").map(|m| m.as_str());
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
if let (Some(timezone), Some(format)) = (timezone, format) {
|
|
|
|
let now = Utc::now().with_timezone(&timezone);
|
2021-12-20 13:48:18 +00:00
|
|
|
|
|
|
|
now.format(format).to_string()
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.to_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Embed {
|
|
|
|
title: String,
|
|
|
|
description: String,
|
|
|
|
image_url: Option<String>,
|
|
|
|
thumbnail_url: Option<String>,
|
|
|
|
footer: String,
|
|
|
|
footer_url: Option<String>,
|
|
|
|
author: String,
|
|
|
|
author_url: Option<String>,
|
|
|
|
color: u32,
|
2022-04-07 16:13:02 +00:00
|
|
|
fields: Json<Vec<EmbedField>>,
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
#[derive(Deserialize)]
|
2021-12-20 13:48:18 +00:00
|
|
|
struct EmbedField {
|
|
|
|
title: String,
|
|
|
|
value: String,
|
|
|
|
inline: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Embed {
|
2022-02-06 15:47:59 +00:00
|
|
|
pub async fn from_id(
|
|
|
|
pool: impl Executor<'_, Database = Database> + Copy,
|
|
|
|
id: u32,
|
|
|
|
) -> Option<Self> {
|
2022-05-13 22:08:52 +00:00
|
|
|
match sqlx::query_as!(
|
2022-04-07 16:13:02 +00:00
|
|
|
Self,
|
|
|
|
r#"
|
|
|
|
SELECT
|
|
|
|
`embed_title` AS title,
|
|
|
|
`embed_description` AS description,
|
|
|
|
`embed_image_url` AS image_url,
|
|
|
|
`embed_thumbnail_url` AS thumbnail_url,
|
|
|
|
`embed_footer` AS footer,
|
|
|
|
`embed_footer_url` AS footer_url,
|
|
|
|
`embed_author` AS author,
|
|
|
|
`embed_author_url` AS author_url,
|
|
|
|
`embed_color` AS color,
|
|
|
|
IFNULL(`embed_fields`, '[]') AS "fields:_"
|
|
|
|
FROM reminders
|
|
|
|
WHERE `id` = ?"#,
|
2021-12-20 13:48:18 +00:00
|
|
|
id
|
|
|
|
)
|
2022-02-06 15:47:59 +00:00
|
|
|
.fetch_one(pool)
|
2021-12-20 13:48:18 +00:00
|
|
|
.await
|
2022-05-13 22:08:52 +00:00
|
|
|
{
|
|
|
|
Ok(mut embed) => {
|
|
|
|
embed.title = substitute(&embed.title);
|
|
|
|
embed.description = substitute(&embed.description);
|
|
|
|
embed.footer = substitute(&embed.footer);
|
|
|
|
|
|
|
|
embed.fields.iter_mut().for_each(|mut field| {
|
|
|
|
field.title = substitute(&field.title);
|
|
|
|
field.value = substitute(&field.value);
|
|
|
|
});
|
|
|
|
|
|
|
|
if embed.has_content() {
|
|
|
|
Some(embed)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
Err(e) => {
|
|
|
|
warn!("Error loading embed from reminder: {:?}", e);
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
None
|
|
|
|
}
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_content(&self) -> bool {
|
2022-04-07 16:13:02 +00:00
|
|
|
if self.title.is_empty()
|
|
|
|
&& self.description.is_empty()
|
|
|
|
&& self.image_url.is_none()
|
|
|
|
&& self.thumbnail_url.is_none()
|
|
|
|
&& self.footer.is_empty()
|
|
|
|
&& self.footer_url.is_none()
|
|
|
|
&& self.author.is_empty()
|
|
|
|
&& self.author_url.is_none()
|
|
|
|
&& self.fields.0.is_empty()
|
2021-12-20 13:48:18 +00:00
|
|
|
{
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Into<CreateEmbed> for Embed {
|
|
|
|
fn into(self) -> CreateEmbed {
|
|
|
|
let mut c = CreateEmbed::default();
|
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
c.title(&self.title)
|
|
|
|
.description(&self.description)
|
|
|
|
.color(self.color)
|
2021-12-20 13:48:18 +00:00
|
|
|
.author(|a| {
|
2022-04-07 16:13:02 +00:00
|
|
|
a.name(&self.author);
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
if let Some(author_icon) = &self.author_url {
|
2021-12-20 13:48:18 +00:00
|
|
|
a.icon_url(author_icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
a
|
|
|
|
})
|
|
|
|
.footer(|f| {
|
2022-04-07 16:13:02 +00:00
|
|
|
f.text(&self.footer);
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
if let Some(footer_icon) = &self.footer_url {
|
2021-12-20 13:48:18 +00:00
|
|
|
f.icon_url(footer_icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
f
|
|
|
|
});
|
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
for field in &self.fields.0 {
|
2021-12-20 13:48:18 +00:00
|
|
|
c.field(&field.title, &field.value, field.inline);
|
|
|
|
}
|
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
if let Some(image_url) = &self.image_url {
|
2021-12-20 13:48:18 +00:00
|
|
|
c.image(image_url);
|
|
|
|
}
|
|
|
|
|
2022-04-07 16:13:02 +00:00
|
|
|
if let Some(thumbnail_url) = &self.thumbnail_url {
|
2021-12-20 13:48:18 +00:00
|
|
|
c.thumbnail(thumbnail_url);
|
|
|
|
}
|
|
|
|
|
|
|
|
c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Reminder {
|
|
|
|
id: u32,
|
|
|
|
|
|
|
|
channel_id: u64,
|
|
|
|
webhook_id: Option<u64>,
|
|
|
|
webhook_token: Option<String>,
|
|
|
|
|
|
|
|
channel_paused: bool,
|
|
|
|
channel_paused_until: Option<NaiveDateTime>,
|
|
|
|
enabled: bool,
|
|
|
|
|
|
|
|
tts: bool,
|
|
|
|
pin: bool,
|
|
|
|
content: String,
|
|
|
|
attachment: Option<Vec<u8>>,
|
|
|
|
attachment_name: Option<String>,
|
|
|
|
|
|
|
|
utc_time: NaiveDateTime,
|
|
|
|
timezone: String,
|
|
|
|
restartable: bool,
|
|
|
|
expires: Option<NaiveDateTime>,
|
2022-02-01 23:41:28 +00:00
|
|
|
interval_seconds: Option<u32>,
|
|
|
|
interval_months: Option<u32>,
|
2021-12-20 13:48:18 +00:00
|
|
|
|
|
|
|
avatar: Option<String>,
|
|
|
|
username: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Reminder {
|
2022-02-06 15:47:59 +00:00
|
|
|
pub async fn fetch_reminders(pool: impl Executor<'_, Database = Database> + Copy) -> Vec<Self> {
|
2022-05-14 07:12:50 +00:00
|
|
|
match sqlx::query_as_unchecked!(
|
2021-12-20 13:48:18 +00:00
|
|
|
Reminder,
|
2022-05-13 22:08:52 +00:00
|
|
|
r#"
|
2021-12-20 13:48:18 +00:00
|
|
|
SELECT
|
|
|
|
reminders.`id` AS id,
|
|
|
|
|
|
|
|
channels.`channel` AS channel_id,
|
|
|
|
channels.`webhook_id` AS webhook_id,
|
|
|
|
channels.`webhook_token` AS webhook_token,
|
|
|
|
|
2022-05-14 07:12:50 +00:00
|
|
|
channels.`paused` AS 'channel_paused',
|
|
|
|
channels.`paused_until` AS 'channel_paused_until',
|
|
|
|
reminders.`enabled` AS 'enabled',
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-05-14 07:12:50 +00:00
|
|
|
reminders.`tts` AS tts,
|
|
|
|
reminders.`pin` AS pin,
|
2021-12-20 13:48:18 +00:00
|
|
|
reminders.`content` AS content,
|
|
|
|
reminders.`attachment` AS attachment,
|
|
|
|
reminders.`attachment_name` AS attachment_name,
|
|
|
|
|
2022-05-14 07:12:50 +00:00
|
|
|
reminders.`utc_time` AS 'utc_time',
|
2021-12-20 13:48:18 +00:00
|
|
|
reminders.`timezone` AS timezone,
|
2022-05-14 07:12:50 +00:00
|
|
|
reminders.`restartable` AS restartable,
|
|
|
|
reminders.`expires` AS 'expires',
|
2022-02-01 23:41:28 +00:00
|
|
|
reminders.`interval_seconds` AS 'interval_seconds',
|
|
|
|
reminders.`interval_months` AS 'interval_months',
|
2021-12-20 13:48:18 +00:00
|
|
|
|
|
|
|
reminders.`avatar` AS avatar,
|
|
|
|
reminders.`username` AS username
|
|
|
|
FROM
|
|
|
|
reminders
|
|
|
|
INNER JOIN
|
|
|
|
channels
|
|
|
|
ON
|
|
|
|
reminders.channel_id = channels.id
|
|
|
|
WHERE
|
2022-07-27 20:42:09 +00:00
|
|
|
reminders.`utc_time` < NOW()"#,
|
2021-12-20 13:48:18 +00:00
|
|
|
)
|
|
|
|
.fetch_all(pool)
|
|
|
|
.await
|
2022-05-13 22:08:52 +00:00
|
|
|
{
|
|
|
|
Ok(reminders) => reminders
|
|
|
|
.into_iter()
|
|
|
|
.map(|mut rem| {
|
|
|
|
rem.content = substitute(&rem.content);
|
2021-12-20 13:48:18 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
rem
|
|
|
|
})
|
|
|
|
.collect::<Vec<Self>>(),
|
|
|
|
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Could not fetch reminders: {:?}", e);
|
|
|
|
|
|
|
|
vec![]
|
|
|
|
}
|
|
|
|
}
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
2021-12-20 13:48:18 +00:00
|
|
|
let _ = sqlx::query!(
|
|
|
|
"
|
|
|
|
UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ?
|
|
|
|
",
|
|
|
|
self.channel_id
|
|
|
|
)
|
|
|
|
.execute(pool)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
async fn refresh(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
2022-02-01 23:41:28 +00:00
|
|
|
if self.interval_seconds.is_some() || self.interval_months.is_some() {
|
2021-12-20 13:48:18 +00:00
|
|
|
let now = Utc::now().naive_local();
|
|
|
|
let mut updated_reminder_time = self.utc_time;
|
|
|
|
|
2022-02-01 23:41:28 +00:00
|
|
|
if let Some(interval) = self.interval_months {
|
2022-05-13 22:08:52 +00:00
|
|
|
match sqlx::query!(
|
2022-02-06 15:47:59 +00:00
|
|
|
// use the second date_add to force return value to datetime
|
|
|
|
"SELECT DATE_ADD(DATE_ADD(?, INTERVAL ? MONTH), INTERVAL 0 SECOND) AS new_time",
|
2022-02-01 23:41:28 +00:00
|
|
|
updated_reminder_time,
|
|
|
|
interval
|
|
|
|
)
|
|
|
|
.fetch_one(pool)
|
|
|
|
.await
|
2022-05-13 22:08:52 +00:00
|
|
|
{
|
|
|
|
Ok(row) => match row.new_time {
|
|
|
|
Some(datetime) => {
|
|
|
|
updated_reminder_time = datetime;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
warn!("Could not update interval by months: got NULL");
|
|
|
|
|
|
|
|
updated_reminder_time += Duration::days(30);
|
|
|
|
}
|
|
|
|
},
|
2022-02-01 23:41:28 +00:00
|
|
|
|
2022-05-13 22:08:52 +00:00
|
|
|
Err(e) => {
|
|
|
|
warn!("Could not update interval by months: {:?}", e);
|
|
|
|
|
|
|
|
// naively fallback to adding 30 days
|
|
|
|
updated_reminder_time += Duration::days(30);
|
|
|
|
}
|
|
|
|
}
|
2022-02-01 23:41:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(interval) = self.interval_seconds {
|
|
|
|
while updated_reminder_time < now {
|
|
|
|
updated_reminder_time += Duration::seconds(interval as i64);
|
|
|
|
}
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.expires.map_or(false, |expires| {
|
|
|
|
NaiveDateTime::from_timestamp(updated_reminder_time.timestamp(), 0) > expires
|
|
|
|
}) {
|
|
|
|
self.force_delete(pool).await;
|
|
|
|
} else {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
UPDATE reminders SET `utc_time` = ? WHERE `id` = ?
|
|
|
|
",
|
|
|
|
updated_reminder_time,
|
|
|
|
self.id
|
|
|
|
)
|
|
|
|
.execute(pool)
|
|
|
|
.await
|
|
|
|
.expect(&format!("Could not update time on Reminder {}", self.id));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.force_delete(pool).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
async fn force_delete(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
2021-12-20 13:48:18 +00:00
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
DELETE FROM reminders WHERE `id` = ?
|
|
|
|
",
|
|
|
|
self.id
|
|
|
|
)
|
|
|
|
.execute(pool)
|
|
|
|
.await
|
|
|
|
.expect(&format!("Could not delete Reminder {}", self.id));
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) {
|
|
|
|
let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await;
|
|
|
|
}
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
pub async fn send(
|
|
|
|
&self,
|
|
|
|
pool: impl Executor<'_, Database = Database> + Copy,
|
|
|
|
cache_http: impl CacheHttp,
|
|
|
|
) {
|
2021-12-20 13:48:18 +00:00
|
|
|
async fn send_to_channel(
|
|
|
|
cache_http: impl CacheHttp,
|
|
|
|
reminder: &Reminder,
|
|
|
|
embed: Option<CreateEmbed>,
|
|
|
|
) -> Result<()> {
|
|
|
|
let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await;
|
|
|
|
|
|
|
|
match channel {
|
|
|
|
Ok(Channel::Guild(channel)) => {
|
|
|
|
match channel
|
|
|
|
.send_message(&cache_http, |m| {
|
|
|
|
m.content(&reminder.content).tts(reminder.tts);
|
|
|
|
|
|
|
|
if let (Some(attachment), Some(name)) =
|
|
|
|
(&reminder.attachment, &reminder.attachment_name)
|
|
|
|
{
|
|
|
|
m.add_file((attachment as &[u8], name.as_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(embed) = embed {
|
|
|
|
m.set_embed(embed);
|
|
|
|
}
|
|
|
|
|
|
|
|
m
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(m) => {
|
|
|
|
if reminder.pin {
|
|
|
|
reminder.pin_message(m.id, cache_http.http()).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(e) => Err(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Channel::Private(channel)) => {
|
|
|
|
match channel
|
|
|
|
.send_message(&cache_http.http(), |m| {
|
|
|
|
m.content(&reminder.content).tts(reminder.tts);
|
|
|
|
|
|
|
|
if let (Some(attachment), Some(name)) =
|
|
|
|
(&reminder.attachment, &reminder.attachment_name)
|
|
|
|
{
|
|
|
|
m.add_file((attachment as &[u8], name.as_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(embed) = embed {
|
|
|
|
m.set_embed(embed);
|
|
|
|
}
|
|
|
|
|
|
|
|
m
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(m) => {
|
|
|
|
if reminder.pin {
|
|
|
|
reminder.pin_message(m.id, cache_http.http()).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(e) => Err(e),
|
|
|
|
}
|
|
|
|
}
|
2021-12-21 13:46:10 +00:00
|
|
|
Err(e) => Err(e),
|
|
|
|
_ => Err(Error::Other("Channel not of valid type")),
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn send_to_webhook(
|
|
|
|
cache_http: impl CacheHttp,
|
|
|
|
reminder: &Reminder,
|
|
|
|
webhook: Webhook,
|
|
|
|
embed: Option<CreateEmbed>,
|
|
|
|
) -> Result<()> {
|
|
|
|
match webhook
|
|
|
|
.execute(&cache_http.http(), reminder.pin || reminder.restartable, |w| {
|
|
|
|
w.content(&reminder.content).tts(reminder.tts);
|
|
|
|
|
|
|
|
if let Some(username) = &reminder.username {
|
|
|
|
w.username(username);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(avatar) = &reminder.avatar {
|
|
|
|
w.avatar_url(avatar);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let (Some(attachment), Some(name)) =
|
|
|
|
(&reminder.attachment, &reminder.attachment_name)
|
|
|
|
{
|
|
|
|
w.add_file((attachment as &[u8], name.as_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(embed) = embed {
|
|
|
|
w.embeds(vec![SerenityEmbed::fake(|c| {
|
|
|
|
*c = embed;
|
|
|
|
c
|
|
|
|
})]);
|
|
|
|
}
|
|
|
|
|
|
|
|
w
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(m) => {
|
|
|
|
if reminder.pin {
|
|
|
|
if let Some(message) = m {
|
|
|
|
reminder.pin_message(message.id, cache_http.http()).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(e) => Err(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.enabled
|
|
|
|
&& !(self.channel_paused
|
|
|
|
&& self
|
|
|
|
.channel_paused_until
|
|
|
|
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
|
|
|
{
|
|
|
|
let _ = sqlx::query!(
|
|
|
|
"
|
|
|
|
UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?
|
|
|
|
",
|
|
|
|
self.channel_id
|
|
|
|
)
|
2022-02-06 15:47:59 +00:00
|
|
|
.execute(pool)
|
2021-12-20 13:48:18 +00:00
|
|
|
.await;
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
let embed = Embed::from_id(pool, self.id).await.map(|e| e.into());
|
2021-12-20 13:48:18 +00:00
|
|
|
|
|
|
|
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
|
|
|
(self.webhook_id, &self.webhook_token)
|
|
|
|
{
|
2021-12-21 13:46:10 +00:00
|
|
|
let webhook_res =
|
|
|
|
cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await;
|
2021-12-20 13:48:18 +00:00
|
|
|
|
|
|
|
if let Ok(webhook) = webhook_res {
|
|
|
|
send_to_webhook(cache_http, &self, webhook, embed).await
|
|
|
|
} else {
|
|
|
|
warn!("Webhook vanished: {:?}", webhook_res);
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
self.reset_webhook(pool).await;
|
2021-12-20 13:48:18 +00:00
|
|
|
send_to_channel(cache_http, &self, embed).await
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
send_to_channel(cache_http, &self, embed).await
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(e) = result {
|
2022-07-22 22:30:45 +00:00
|
|
|
error!("Error sending reminder {}: {:?}", self.id, e);
|
2021-12-20 13:48:18 +00:00
|
|
|
|
|
|
|
if let Error::Http(error) = e {
|
2022-05-13 22:08:52 +00:00
|
|
|
if error.status_code() == Some(StatusCode::NOT_FOUND) {
|
2022-05-14 09:56:03 +00:00
|
|
|
warn!("Seeing channel is deleted. Removing reminder");
|
2022-02-06 15:47:59 +00:00
|
|
|
self.force_delete(pool).await;
|
2022-05-14 09:56:03 +00:00
|
|
|
} else if let HttpError::UnsuccessfulRequest(error) = *error {
|
|
|
|
if error.error.code == 50007 {
|
|
|
|
warn!("User cannot receive DMs");
|
|
|
|
self.force_delete(pool).await;
|
|
|
|
} else {
|
|
|
|
self.refresh(pool).await;
|
|
|
|
}
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-02-06 15:47:59 +00:00
|
|
|
self.refresh(pool).await;
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-02-06 15:47:59 +00:00
|
|
|
self.refresh(pool).await;
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
info!("Reminder {} is paused", self.id);
|
|
|
|
|
2022-02-06 15:47:59 +00:00
|
|
|
self.refresh(pool).await;
|
2021-12-20 13:48:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|