Handle deleted channels in sender

This commit is contained in:
jude 2023-09-17 14:09:50 +01:00
parent 82dab53744
commit 5ee9094bac
8 changed files with 192 additions and 122 deletions

View File

@ -237,11 +237,11 @@ impl Into<CreateEmbed> for Embed {
pub struct Reminder { pub struct Reminder {
id: u32, id: u32,
channel_id: u64, channel_id: Option<u64>,
webhook_id: Option<u64>, webhook_id: Option<u64>,
webhook_token: Option<String>, webhook_token: Option<String>,
channel_paused: bool, channel_paused: Option<bool>,
channel_paused_until: Option<NaiveDateTime>, channel_paused_until: Option<NaiveDateTime>,
enabled: bool, enabled: bool,
@ -297,7 +297,7 @@ SELECT
reminders.`username` AS username reminders.`username` AS username
FROM FROM
reminders reminders
INNER JOIN LEFT JOIN
channels channels
ON ON
reminders.channel_id = channels.id reminders.channel_id = channels.id
@ -310,7 +310,6 @@ WHERE
reminders reminders
WHERE WHERE
reminders.`utc_time` <= NOW() AND reminders.`utc_time` <= NOW() AND
reminders.`channel_id` IS NOT NULL AND
`status` = 'pending' AND `status` = 'pending' AND
( (
reminders.`interval_seconds` IS NOT NULL reminders.`interval_seconds` IS NOT NULL
@ -344,7 +343,10 @@ WHERE
async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) { async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
let _ = sqlx::query!( 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 self.channel_id
) )
.execute(pool) .execute(pool)
@ -416,7 +418,9 @@ WHERE
self.set_sent(pool).await; self.set_sent(pool).await;
} else { } else {
sqlx::query!( sqlx::query!(
"UPDATE reminders SET `utc_time` = ? WHERE `id` = ?", "
UPDATE reminders SET `utc_time` = ? WHERE `id` = ?
",
updated_reminder_time.with_timezone(&Utc), updated_reminder_time.with_timezone(&Utc),
self.id self.id
) )
@ -449,7 +453,10 @@ WHERE
if *LOG_TO_DATABASE { if *LOG_TO_DATABASE {
sqlx::query!( sqlx::query!(
"INSERT INTO stat (type, reminder_id, message) VALUES ('reminder_failed', ?, ?)", "
INSERT INTO stat (type, reminder_id, message)
VALUES ('reminder_failed', ?, ?)
",
self.id, self.id,
message, message,
) )
@ -462,7 +469,10 @@ WHERE
async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) { async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) {
if *LOG_TO_DATABASE { if *LOG_TO_DATABASE {
sqlx::query!( sqlx::query!(
"INSERT INTO stat (type, reminder_id) VALUES ('reminder_sent', ?)", "
INSERT INTO stat (type, reminder_id)
VALUES ('reminder_sent', ?)
",
self.id, self.id,
) )
.execute(pool) .execute(pool)
@ -491,7 +501,11 @@ WHERE
message: &'static str, message: &'static str,
) { ) {
sqlx::query!( 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, message,
self.id self.id
) )
@ -501,7 +515,9 @@ WHERE
} }
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) { 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; 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( pub async fn send(
@ -511,10 +527,11 @@ WHERE
) { ) {
async fn send_to_channel( async fn send_to_channel(
cache_http: impl CacheHttp, cache_http: impl CacheHttp,
channel_id: u64,
reminder: &Reminder, reminder: &Reminder,
embed: Option<CreateEmbed>, embed: Option<CreateEmbed>,
) -> Result<()> { ) -> Result<()> {
let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await; let channel = ChannelId(channel_id).to_channel(&cache_http).await;
match channel { match channel {
Ok(Channel::Guild(channel)) => { Ok(Channel::Guild(channel)) => {
@ -546,6 +563,7 @@ WHERE
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
Ok(Channel::Private(channel)) => { Ok(Channel::Private(channel)) => {
match channel match channel
.send_message(&cache_http.http(), |m| { .send_message(&cache_http.http(), |m| {
@ -575,7 +593,9 @@ WHERE
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
Err(e) => Err(e), Err(e) => Err(e),
_ => Err(Error::Other("Channel not of valid type")), _ => Err(Error::Other("Channel not of valid type")),
} }
} }
@ -630,124 +650,151 @@ WHERE
} }
} }
if self.enabled match self.channel_id {
&& !(self.channel_paused Some(channel_id) => {
&& self if self.enabled
.channel_paused_until && !(self.channel_paused.unwrap_or(false)
.map_or(true, |inner| inner >= Utc::now().naive_local())) && self
{ .channel_paused_until
let _ = sqlx::query!( .map_or(true, |inner| inner >= Utc::now().naive_local()))
"UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?", {
self.channel_id let _ = sqlx::query!(
) "
.execute(pool) UPDATE `channels`
.await; 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)) = let result = if let (Some(webhook_id), Some(webhook_token)) =
(self.webhook_id, &self.webhook_token) (self.webhook_id, &self.webhook_token)
{ {
let webhook_res = let webhook_res = cache_http
cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await; .http()
.get_webhook_with_token(webhook_id, webhook_token)
.await;
if let Ok(webhook) = webhook_res { if let Ok(webhook) = webhook_res {
send_to_webhook(cache_http, &self, webhook, embed).await send_to_webhook(cache_http, &self, webhook, embed).await
} else { } else {
warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res); warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res);
self.reset_webhook(pool).await; self.reset_webhook(pool).await;
send_to_channel(cache_http, &self, embed).await send_to_channel(cache_http, channel_id, &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;
}
} }
} else { } 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; self.refresh(pool).await;
} }
} else { } else {
self.log_error(pool, "Non-HTTP error", Some(e)).await; info!("Reminder {} is paused", self.id);
self.refresh(pool).await; 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::<u8>::None).await;
self.set_failed(pool, "Could not be sent as channel was deleted").await;
}
} }
} }
} }

View File

@ -75,7 +75,8 @@ impl ChannelData {
UPDATE channels UPDATE channels
SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?, SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?,
paused = ?, paused_until = ? paused = ?, paused_until = ?
WHERE id = ?", WHERE id = ?
",
self.name, self.name,
self.nudge, self.nudge,
self.blacklisted, self.blacklisted,

View File

@ -173,6 +173,7 @@ pub async fn import_reminders(
utc_time: record.utc_time, utc_time: record.utc_time,
status: "pending".to_string(), status: "pending".to_string(),
status_change_time: None, status_change_time: None,
status_message: None,
}; };
create_reminder( create_reminder(

View File

@ -361,7 +361,8 @@ pub async fn get_reminders(
reminders.username, reminders.username,
reminders.utc_time, reminders.utc_time,
reminders.status, reminders.status,
reminders.status_change_time reminders.status_change_time,
reminders.status_message
FROM reminders FROM reminders
LEFT JOIN channels ON channels.id = reminders.channel_id LEFT JOIN channels ON channels.id = reminders.channel_id
WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)", 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!( match sqlx::query_as_unchecked!(
Reminder, Reminder,
"SELECT reminders.attachment, "
SELECT reminders.attachment,
reminders.attachment_name, reminders.attachment_name,
reminders.avatar, reminders.avatar,
channels.channel, channels.channel,
@ -576,7 +578,8 @@ pub async fn edit_reminder(
reminders.username, reminders.username,
reminders.utc_time, reminders.utc_time,
reminders.status, reminders.status,
reminders.status_change_time reminders.status_change_time,
reminders.status_message
FROM reminders FROM reminders
LEFT JOIN channels ON channels.id = reminders.channel_id LEFT JOIN channels ON channels.id = reminders.channel_id
WHERE uid = ?", WHERE uid = ?",

View File

@ -151,6 +151,7 @@ pub struct Reminder {
username: Option<String>, username: Option<String>,
utc_time: NaiveDateTime, utc_time: NaiveDateTime,
status: String, status: String,
status_message: Option<String>,
status_change_time: Option<NaiveDateTime>, status_change_time: Option<NaiveDateTime>,
} }
@ -504,7 +505,8 @@ pub async fn create_reminder(
// write to db // write to db
match sqlx::query!( match sqlx::query!(
"INSERT INTO reminders ( "
INSERT INTO reminders (
uid, uid,
attachment, attachment,
attachment_name, attachment_name,
@ -594,7 +596,8 @@ pub async fn create_reminder(
reminders.username, reminders.username,
reminders.utc_time, reminders.utc_time,
reminders.status, reminders.status,
reminders.status_change_time reminders.status_change_time,
reminders.status_message
FROM reminders FROM reminders
LEFT JOIN channels ON channels.id = reminders.channel_id LEFT JOIN channels ON channels.id = reminders.channel_id
WHERE uid = ?", WHERE uid = ?",

View File

@ -758,6 +758,16 @@ div.reminderError .errorHead .reminderTime {
border-style: solid; 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 */ /* other stuff */
.half-rem { .half-rem {

View File

@ -30,6 +30,8 @@ document.addEventListener("paneLoad", (ev) => {
{ zone: "UTC" } { zone: "UTC" }
); );
newRow.querySelector(".reminderName").textContent = reminder.name; newRow.querySelector(".reminderName").textContent = reminder.name;
newRow.querySelector(".reminderMessage").textContent =
reminder.status_message;
newRow.querySelector(".reminderTime").textContent = statusTime newRow.querySelector(".reminderTime").textContent = statusTime
.toLocal() .toLocal()
.toLocaleString(luxon.DateTime.DATETIME_MED); .toLocaleString(luxon.DateTime.DATETIME_MED);

View File

@ -9,6 +9,9 @@
</div> </div>
<div class="reminderName"> <div class="reminderName">
Reminder Reminder
</div>
<div class="reminderMessage">
</div> </div>
<div class="reminderTime"> <div class="reminderTime">