From 5ee9094bac0d6f6cdf26f2d168b92311fd209fd8 Mon Sep 17 00:00:00 2001 From: jude Date: Sun, 17 Sep 2023 14:09:50 +0100 Subject: [PATCH] Handle deleted channels in sender --- postman/src/sender.rs | 279 ++++++++++-------- src/models/channel_data.rs | 3 +- web/src/routes/dashboard/export.rs | 1 + web/src/routes/dashboard/guild.rs | 9 +- web/src/routes/dashboard/mod.rs | 7 +- web/static/css/style.css | 10 + web/static/js/reminder_errors.js | 2 + .../templates/reminder_error.html.tera | 3 + 8 files changed, 192 insertions(+), 122 deletions(-) diff --git a/postman/src/sender.rs b/postman/src/sender.rs index 9b325fe..5a8fdfb 100644 --- a/postman/src/sender.rs +++ b/postman/src/sender.rs @@ -237,11 +237,11 @@ impl Into for Embed { pub struct Reminder { id: u32, - channel_id: u64, + channel_id: Option, webhook_id: Option, webhook_token: Option, - channel_paused: bool, + channel_paused: Option, channel_paused_until: Option, enabled: bool, @@ -297,7 +297,7 @@ SELECT reminders.`username` AS username FROM reminders -INNER JOIN +LEFT JOIN channels ON reminders.channel_id = channels.id @@ -310,7 +310,6 @@ WHERE reminders WHERE reminders.`utc_time` <= NOW() AND - reminders.`channel_id` IS NOT NULL AND `status` = 'pending' AND ( reminders.`interval_seconds` IS NOT NULL @@ -344,7 +343,10 @@ WHERE async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) { let _ = sqlx::query!( - "UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ?", + " + UPDATE channels SET webhook_id = NULL, webhook_token = NULL + WHERE channel = ? + ", self.channel_id ) .execute(pool) @@ -416,7 +418,9 @@ WHERE self.set_sent(pool).await; } else { sqlx::query!( - "UPDATE reminders SET `utc_time` = ? WHERE `id` = ?", + " + UPDATE reminders SET `utc_time` = ? WHERE `id` = ? + ", updated_reminder_time.with_timezone(&Utc), self.id ) @@ -449,7 +453,10 @@ WHERE if *LOG_TO_DATABASE { sqlx::query!( - "INSERT INTO stat (type, reminder_id, message) VALUES ('reminder_failed', ?, ?)", + " + INSERT INTO stat (type, reminder_id, message) + VALUES ('reminder_failed', ?, ?) + ", self.id, message, ) @@ -462,7 +469,10 @@ WHERE async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) { if *LOG_TO_DATABASE { sqlx::query!( - "INSERT INTO stat (type, reminder_id) VALUES ('reminder_sent', ?)", + " + INSERT INTO stat (type, reminder_id) + VALUES ('reminder_sent', ?) + ", self.id, ) .execute(pool) @@ -491,7 +501,11 @@ WHERE message: &'static str, ) { sqlx::query!( - "UPDATE reminders SET `status` = 'failed', `status_message` = ? WHERE `id` = ?", + " + UPDATE reminders + SET `status` = 'failed', `status_message` = ?, `status_change_time` = NOW() + WHERE `id` = ? + ", message, self.id ) @@ -501,7 +515,9 @@ WHERE } async fn pin_message>(&self, message_id: M, http: impl AsRef) { - let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await; + if let Some(channel_id) = self.channel_id { + let _ = http.as_ref().pin_message(channel_id, message_id.into(), None).await; + } } pub async fn send( @@ -511,10 +527,11 @@ WHERE ) { async fn send_to_channel( cache_http: impl CacheHttp, + channel_id: u64, reminder: &Reminder, embed: Option, ) -> Result<()> { - let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await; + let channel = ChannelId(channel_id).to_channel(&cache_http).await; match channel { Ok(Channel::Guild(channel)) => { @@ -546,6 +563,7 @@ WHERE Err(e) => Err(e), } } + Ok(Channel::Private(channel)) => { match channel .send_message(&cache_http.http(), |m| { @@ -575,7 +593,9 @@ WHERE Err(e) => Err(e), } } + Err(e) => Err(e), + _ => Err(Error::Other("Channel not of valid type")), } } @@ -630,124 +650,151 @@ WHERE } } - 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 - ) - .execute(pool) - .await; + match self.channel_id { + Some(channel_id) => { + if self.enabled + && !(self.channel_paused.unwrap_or(false) + && 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 + ) + .execute(pool) + .await; - let embed = Embed::from_id(pool, self.id).await.map(|e| e.into()); + let embed = Embed::from_id(pool, self.id).await.map(|e| e.into()); - let result = if let (Some(webhook_id), Some(webhook_token)) = - (self.webhook_id, &self.webhook_token) - { - let webhook_res = - cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await; + let result = if let (Some(webhook_id), Some(webhook_token)) = + (self.webhook_id, &self.webhook_token) + { + let webhook_res = cache_http + .http() + .get_webhook_with_token(webhook_id, webhook_token) + .await; - if let Ok(webhook) = webhook_res { - send_to_webhook(cache_http, &self, webhook, embed).await - } else { - warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res); + if let Ok(webhook) = webhook_res { + send_to_webhook(cache_http, &self, webhook, embed).await + } else { + warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res); - self.reset_webhook(pool).await; - send_to_channel(cache_http, &self, embed).await - } - } else { - send_to_channel(cache_http, &self, embed).await - }; - - if let Err(e) = result { - if let Error::Http(error) = e { - if let HttpError::UnsuccessfulRequest(http_error) = *error { - match http_error.error.code { - 10003 => { - self.log_error( - pool, - "Could not be sent as channel does not exist", - None::<&'static str>, - ) - .await; - self.set_failed( - pool, - "Could not be sent as channel does not exist", - ) - .await; - } - 10004 => { - self.log_error( - pool, - "Could not be sent as guild does not exist", - None::<&'static str>, - ) - .await; - self.set_failed(pool, "Could not be sent as guild does not exist") - .await; - } - 50001 => { - self.log_error( - pool, - "Could not be sent as missing access", - None::<&'static str>, - ) - .await; - self.set_failed(pool, "Could not be sent as missing access").await; - } - 50007 => { - self.log_error( - pool, - "Could not be sent as user has DMs disabled", - None::<&'static str>, - ) - .await; - self.set_failed(pool, "Could not be sent as user has DMs disabled") - .await; - } - 50013 => { - self.log_error( - pool, - "Could not be sent as permissions are invalid", - None::<&'static str>, - ) - .await; - self.set_failed( - pool, - "Could not be sent as permissions are invalid", - ) - .await; - } - _ => { - self.log_error( - pool, - "HTTP error sending reminder", - Some(http_error), - ) - .await; - self.refresh(pool).await; - } + self.reset_webhook(pool).await; + send_to_channel(cache_http, channel_id, &self, embed).await } } else { - self.log_error(pool, "(Likely) a parsing error", Some(error)).await; + send_to_channel(cache_http, channel_id, &self, embed).await + }; + + if let Err(e) = result { + if let Error::Http(error) = e { + if let HttpError::UnsuccessfulRequest(http_error) = *error { + match http_error.error.code { + 10003 => { + self.log_error( + pool, + "Could not be sent as channel does not exist", + None::<&'static str>, + ) + .await; + self.set_failed( + pool, + "Could not be sent as channel does not exist", + ) + .await; + } + 10004 => { + self.log_error( + pool, + "Could not be sent as guild does not exist", + None::<&'static str>, + ) + .await; + self.set_failed( + pool, + "Could not be sent as guild does not exist", + ) + .await; + } + 50001 => { + self.log_error( + pool, + "Could not be sent as missing access", + None::<&'static str>, + ) + .await; + self.set_failed( + pool, + "Could not be sent as missing access", + ) + .await; + } + 50007 => { + self.log_error( + pool, + "Could not be sent as user has DMs disabled", + None::<&'static str>, + ) + .await; + self.set_failed( + pool, + "Could not be sent as user has DMs disabled", + ) + .await; + } + 50013 => { + self.log_error( + pool, + "Could not be sent as permissions are invalid", + None::<&'static str>, + ) + .await; + self.set_failed( + pool, + "Could not be sent as permissions are invalid", + ) + .await; + } + _ => { + self.log_error( + pool, + "HTTP error sending reminder", + Some(http_error), + ) + .await; + self.refresh(pool).await; + } + } + } else { + self.log_error(pool, "(Likely) a parsing error", Some(error)).await; + self.refresh(pool).await; + } + } else { + self.log_error(pool, "Non-HTTP error", Some(e)).await; + self.refresh(pool).await; + } + } else { + self.log_success(pool).await; self.refresh(pool).await; } } else { - self.log_error(pool, "Non-HTTP error", Some(e)).await; + info!("Reminder {} is paused", self.id); + self.refresh(pool).await; } - } else { - self.log_success(pool).await; - self.refresh(pool).await; } - } else { - info!("Reminder {} is paused", self.id); - self.refresh(pool).await; + None => { + info!("Reminder {} is orphaned", self.id); + + self.log_error(pool, "Orphaned", Option::::None).await; + self.set_failed(pool, "Could not be sent as channel was deleted").await; + } } } } diff --git a/src/models/channel_data.rs b/src/models/channel_data.rs index 1b29675..f8c0dc0 100644 --- a/src/models/channel_data.rs +++ b/src/models/channel_data.rs @@ -75,7 +75,8 @@ impl ChannelData { UPDATE channels SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?, paused = ?, paused_until = ? - WHERE id = ?", + WHERE id = ? + ", self.name, self.nudge, self.blacklisted, diff --git a/web/src/routes/dashboard/export.rs b/web/src/routes/dashboard/export.rs index ba02886..f26a4aa 100644 --- a/web/src/routes/dashboard/export.rs +++ b/web/src/routes/dashboard/export.rs @@ -173,6 +173,7 @@ pub async fn import_reminders( utc_time: record.utc_time, status: "pending".to_string(), status_change_time: None, + status_message: None, }; create_reminder( diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index 1adf4a8..023903a 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -361,7 +361,8 @@ pub async fn get_reminders( reminders.username, reminders.utc_time, reminders.status, - reminders.status_change_time + reminders.status_change_time, + reminders.status_message FROM reminders LEFT JOIN channels ON channels.id = reminders.channel_id WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)", @@ -549,7 +550,8 @@ pub async fn edit_reminder( match sqlx::query_as_unchecked!( Reminder, - "SELECT reminders.attachment, + " + SELECT reminders.attachment, reminders.attachment_name, reminders.avatar, channels.channel, @@ -576,7 +578,8 @@ pub async fn edit_reminder( reminders.username, reminders.utc_time, reminders.status, - reminders.status_change_time + reminders.status_change_time, + reminders.status_message FROM reminders LEFT JOIN channels ON channels.id = reminders.channel_id WHERE uid = ?", diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index 7a4d784..d72b52f 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -151,6 +151,7 @@ pub struct Reminder { username: Option, utc_time: NaiveDateTime, status: String, + status_message: Option, status_change_time: Option, } @@ -504,7 +505,8 @@ pub async fn create_reminder( // write to db match sqlx::query!( - "INSERT INTO reminders ( + " + INSERT INTO reminders ( uid, attachment, attachment_name, @@ -594,7 +596,8 @@ pub async fn create_reminder( reminders.username, reminders.utc_time, reminders.status, - reminders.status_change_time + reminders.status_change_time, + reminders.status_message FROM reminders LEFT JOIN channels ON channels.id = reminders.channel_id WHERE uid = ?", diff --git a/web/static/css/style.css b/web/static/css/style.css index 0bb3d1b..55fa8ee 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -758,6 +758,16 @@ div.reminderError .errorHead .reminderTime { border-style: solid; } +div.reminderError .reminderMessage { + font-size: 1rem; + display: flex; + flex-direction: column; + justify-content: center; + color: rgb(54, 54, 54); + flex-grow: 1; + font-style: italic; +} + /* other stuff */ .half-rem { diff --git a/web/static/js/reminder_errors.js b/web/static/js/reminder_errors.js index 181cf83..95d6466 100644 --- a/web/static/js/reminder_errors.js +++ b/web/static/js/reminder_errors.js @@ -30,6 +30,8 @@ document.addEventListener("paneLoad", (ev) => { { zone: "UTC" } ); newRow.querySelector(".reminderName").textContent = reminder.name; + newRow.querySelector(".reminderMessage").textContent = + reminder.status_message; newRow.querySelector(".reminderTime").textContent = statusTime .toLocal() .toLocaleString(luxon.DateTime.DATETIME_MED); diff --git a/web/templates/reminder_dashboard/templates/reminder_error.html.tera b/web/templates/reminder_dashboard/templates/reminder_error.html.tera index 8f57a86..201bc49 100644 --- a/web/templates/reminder_dashboard/templates/reminder_error.html.tera +++ b/web/templates/reminder_dashboard/templates/reminder_error.html.tera @@ -9,6 +9,9 @@
Reminder +
+
+