From ceff0598115ffe2e8e9e9094ab055cad2e99b398 Mon Sep 17 00:00:00 2001 From: CircuitSacul Date: Mon, 23 Jan 2023 21:43:09 -0500 Subject: [PATCH] handle message forbidden properly (#177) --- src/cache/cache_struct.rs | 44 +++++++++++++++++++++--- src/cache/mod.rs | 2 +- src/core/autostar.rs | 4 +-- src/core/embedder/builder.rs | 6 ++-- src/core/embedder/handle.rs | 40 +++++++++++++++------ src/core/starboard/handle.rs | 32 +++++++++++------ src/core/starboard/message.rs | 2 +- src/core/starboard/vote_status.rs | 1 + src/errors.rs | 11 ++++++ src/interactions/commands/chat/random.rs | 5 +-- src/owner/handle.rs | 2 +- 11 files changed, 114 insertions(+), 35 deletions(-) diff --git a/src/cache/cache_struct.rs b/src/cache/cache_struct.rs index e7be6fc4..516f4de5 100644 --- a/src/cache/cache_struct.rs +++ b/src/cache/cache_struct.rs @@ -39,6 +39,42 @@ macro_rules! update_cache_events { }; } +#[derive(Clone)] +pub enum MessageResult { + Ok(Arc), + Missing, + Forbidden, +} + +impl MessageResult { + pub fn into_option(self) -> Option> { + match self { + Self::Ok(msg) => Some(msg), + _ => None, + } + } + + pub fn as_option(&self) -> Option<&Arc> { + match &self { + Self::Ok(msg) => Some(msg), + _ => None, + } + } + + pub fn is_missing(&self) -> bool { + matches!(self, Self::Missing) + } +} + +impl From>> for MessageResult { + fn from(value: Option>) -> Self { + match value { + None => Self::Missing, + Some(msg) => Self::Ok(msg), + } + } +} + pub struct Cache { // discord side pub guilds: AsyncDashMap, CachedGuild>, @@ -277,9 +313,9 @@ impl Cache { bot: &StarboardBot, channel_id: Id, message_id: Id, - ) -> StarboardResult>> { + ) -> StarboardResult { if let Some(cached) = self.messages.get(&message_id) { - return Ok(cached.value().clone()); + return Ok(cached.value().clone().into()); } let msg = bot.http.message(channel_id, message_id).await; @@ -289,7 +325,7 @@ impl Cache { if status == Some(404) { None } else if status == Some(403) { - return Ok(None); + return Ok(MessageResult::Forbidden); } else { return Err(why.into()); } @@ -300,7 +336,7 @@ impl Cache { let ret = msg.clone(); self.messages.insert(message_id, msg, 1).await; - Ok(ret) + Ok(ret.into()) } async fn fetch_channel_or_thread_parent( diff --git a/src/cache/mod.rs b/src/cache/mod.rs index e59b044a..cdbacb4c 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -3,4 +3,4 @@ mod events; pub mod models; mod update; -pub use cache_struct::Cache; +pub use cache_struct::{Cache, MessageResult}; diff --git a/src/core/autostar.rs b/src/core/autostar.rs index a0efc245..356910a0 100644 --- a/src/core/autostar.rs +++ b/src/core/autostar.rs @@ -56,7 +56,7 @@ pub async fn handle( let message = match message { Some(msg) => msg, None => { - let Some(msg) = bot.cache.fog_message(bot, channel_id, message_id).await? else { + let Some(msg) = bot.cache.fog_message(bot, channel_id, message_id).await?.into_option() else { return Ok(()); }; message_owner = msg; @@ -136,7 +136,7 @@ async fn get_status( let updated_msg = bot.cache.fog_message(bot, channel_id, message_id).await?; let mut still_invalid = true; - let msg = match updated_msg { + let msg = match updated_msg.into_option() { None => return Ok(Status::InvalidStay), Some(msg) => msg, }; diff --git a/src/core/embedder/builder.rs b/src/core/embedder/builder.rs index 1ed76e0f..c4c67ce3 100644 --- a/src/core/embedder/builder.rs +++ b/src/core/embedder/builder.rs @@ -13,7 +13,7 @@ use twilight_util::{ }; use crate::{ - cache::models::message::CachedMessage, + cache::{models::message::CachedMessage, MessageResult}, constants, core::emoji::{EmojiCommon, SimpleEmoji}, utils::{id_as_i64::GetI64, into_id::IntoId, message_link::fmt_message_link}, @@ -38,7 +38,7 @@ pub enum BuiltStarboardEmbed { impl BuiltStarboardEmbed { pub fn build(handle: &Embedder, force_partial: bool, watermark: bool) -> Self { - if let Some(orig) = &handle.orig_message { + if let MessageResult::Ok(orig) = &handle.orig_message { if !force_partial { let parsed = ParsedMessage::parse(orig); @@ -151,7 +151,7 @@ impl BuiltStarboardEmbed { let mid = match is_reply { true => handle .orig_message - .as_ref() + .as_option() .unwrap() .referenced_message .unwrap() diff --git a/src/core/embedder/handle.rs b/src/core/embedder/handle.rs index 9c5f3a89..24440d21 100644 --- a/src/core/embedder/handle.rs +++ b/src/core/embedder/handle.rs @@ -3,7 +3,10 @@ use std::sync::Arc; use twilight_model::id::{marker::MessageMarker, Id}; use crate::{ - cache::models::{message::CachedMessage, user::CachedUser}, + cache::{ + models::{message::CachedMessage, user::CachedUser}, + MessageResult, + }, client::bot::StarboardBot, core::{ premium::is_premium::is_guild_premium, @@ -20,7 +23,7 @@ pub struct Embedder<'config, 'bot> { pub bot: &'bot StarboardBot, pub points: i32, pub config: &'config StarboardConfig, - pub orig_message: Option>, + pub orig_message: MessageResult, pub orig_message_author: Option>, pub referenced_message: Option>, pub referenced_message_author: Option>, @@ -58,7 +61,10 @@ impl Embedder<'_, '_> { let forum_post_name = if bot.cache.is_channel_forum(guild_id, sb_channel_id) { let name = &built.embeds[0].author.as_ref().unwrap().name; - let mut content = &*self.orig_message.as_ref().unwrap().content; + let mut content = match &self.orig_message { + MessageResult::Ok(msg) => &*msg.content, + _ => unreachable!("Tried to send a message when the original was unfetchable."), + }; if content.is_empty() { content = "Click to see attachments"; } @@ -162,8 +168,14 @@ impl Embedder<'_, '_> { sb_channel_id }; - let Some(msg) = bot.cache.fog_message(bot, real_channel_id, message_id).await? else { - return Ok(true); + let ret = bot + .cache + .fog_message(bot, real_channel_id, message_id) + .await?; + let msg = match ret { + MessageResult::Ok(msg) => msg, + MessageResult::Forbidden => return Ok(false), + MessageResult::Missing => return Ok(true), }; let (wh, is_thread) = if msg.author_id.get() != bot.config.bot_id { @@ -236,7 +248,7 @@ impl Embedder<'_, '_> { &self, bot: &StarboardBot, message_id: Id, - ) -> StarboardResult<()> { + ) -> StarboardResult { let sb_channel_id = self.config.starboard.channel_id.into_id(); let is_forum = bot @@ -248,8 +260,14 @@ impl Embedder<'_, '_> { sb_channel_id }; - let Some(msg) = bot.cache.fog_message(bot, real_channel_id, message_id).await? else { - return Ok(()); + let ret = bot + .cache + .fog_message(bot, real_channel_id, message_id) + .await?; + let msg = match ret { + MessageResult::Ok(msg) => msg, + MessageResult::Forbidden => return Ok(false), + MessageResult::Missing => return Ok(true), }; if let Some(wh_id) = self.config.starboard.webhook_id { @@ -277,14 +295,14 @@ impl Embedder<'_, '_> { let ret = ud.await; if ret.is_ok() { - return Ok(()); + return Ok(true); } } } } - let _ = bot.http.delete_message(real_channel_id, message_id).await; + let ret = bot.http.delete_message(real_channel_id, message_id).await; - Ok(()) + Ok(ret.is_ok()) } } diff --git a/src/core/starboard/handle.rs b/src/core/starboard/handle.rs index 8851083a..cec68114 100644 --- a/src/core/starboard/handle.rs +++ b/src/core/starboard/handle.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use twilight_model::id::{marker::MessageMarker, Id}; use crate::{ - cache::models::message::CachedMessage, + cache::MessageResult, client::bot::StarboardBot, constants, core::{ @@ -60,7 +60,7 @@ pub struct RefreshMessage { /// The id of the inputted message. May or may not be the original. message_id: Id, sql_message: Option>, - orig_message: Option>>, + orig_message: Option, configs: Option>>>, } @@ -160,11 +160,11 @@ impl RefreshMessage { Ok(self.sql_message.as_ref().unwrap().clone()) } - pub fn set_orig_message(&mut self, message: Option>) { + pub fn set_orig_message(&mut self, message: MessageResult) { self.orig_message.replace(message); } - async fn get_orig_message(&mut self) -> StarboardResult>> { + async fn get_orig_message(&mut self) -> StarboardResult { if self.orig_message.is_none() { let sql_message = self.get_sql_message().await?; let orig_message = self @@ -239,14 +239,15 @@ impl RefreshStarboard { .cache .fog_user(&self.refresh.bot, sql_message.author_id.into_id()) .await?; - let (ref_msg, ref_msg_author) = if let Some(msg) = &orig_message { + let (ref_msg, ref_msg_author) = if let MessageResult::Ok(msg) = &orig_message { if let Some(id) = msg.referenced_message { let ref_msg = self .refresh .bot .cache .fog_message(&self.refresh.bot, sql_message.channel_id.into_id(), id) - .await?; + .await? + .into_option(); let ref_msg_author = match &ref_msg { None => None, @@ -283,7 +284,7 @@ impl RefreshStarboard { &self.refresh.bot, &self.config, &orig, - embedder.orig_message.is_none(), + embedder.orig_message.is_missing(), points, violates_exclusive_group, ) @@ -312,8 +313,8 @@ impl RefreshStarboard { .auto_deleted_posts .insert_with_ttl(sb_message_id, (), 0, constants::AUTO_DELETES_TTL) .await; - embedder.delete(&self.refresh.bot, sb_message_id).await?; - (false, true) + let deleted = embedder.delete(&self.refresh.bot, sb_message_id).await?; + (false, deleted) } MessageStatus::Send(full_update) | MessageStatus::Update(full_update) => { if self @@ -349,7 +350,18 @@ impl RefreshStarboard { return Ok((false, false)); } - let msg = embedder.send(&self.refresh.bot).await?; + let msg = embedder.send(&self.refresh.bot).await; + let msg = match msg { + Ok(msg) => msg, + Err(why) => { + if why.http_status() == Some(403) { + return Ok((false, false)); + } else { + return Err(why); + } + } + }; + StarboardMessage::create( &self.refresh.bot.pool, orig.message_id, diff --git a/src/core/starboard/message.rs b/src/core/starboard/message.rs index 6a9500ca..fed2158a 100644 --- a/src/core/starboard/message.rs +++ b/src/core/starboard/message.rs @@ -27,7 +27,7 @@ pub async fn get_or_create_original( // author data let (author_is_bot, author_id) = { let orig_msg_obj = bot.cache.fog_message(bot, channel_id, message_id).await?; - let orig_msg_obj = match orig_msg_obj { + let orig_msg_obj = match orig_msg_obj.into_option() { None => return Ok((None, None)), Some(obj) => obj, }; diff --git a/src/core/starboard/vote_status.rs b/src/core/starboard/vote_status.rs index 1f128421..230d986b 100644 --- a/src/core/starboard/vote_status.rs +++ b/src/core/starboard/vote_status.rs @@ -50,6 +50,7 @@ impl<'a> VoteStatus<'a> { .cache .fog_message(bot, vote.channel_id, vote.message_id) .await? + .into_option() .as_ref() .as_ref() .map(|msg| has_image(&msg.embeds, &msg.attachments)), diff --git a/src/errors.rs b/src/errors.rs index 4393d10f..b2d521d6 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,7 @@ use snafu::Backtrace; +use crate::utils::get_status::get_status; + pub type StarboardResult = Result; #[derive(Debug, snafu::Snafu)] @@ -60,3 +62,12 @@ pub enum StarboardError { backtrace: Backtrace, }, } + +impl StarboardError { + pub fn http_status(&self) -> Option { + match &self { + Self::TwilightHttp { source, .. } => get_status(&source), + _ => None, + } + } +} diff --git a/src/interactions/commands/chat/random.rs b/src/interactions/commands/chat/random.rs index 55dd3db5..f085d397 100644 --- a/src/interactions/commands/chat/random.rs +++ b/src/interactions/commands/chat/random.rs @@ -88,7 +88,7 @@ pub async fn get_embedder<'config, 'bot>( orig_sql_msg.message_id.into_id(), ) .await?; - let Some(orig_msg) = orig_msg else { + let Some(orig_msg) = orig_msg.into_option() else { return Ok(None); }; let orig_author = bot @@ -100,6 +100,7 @@ pub async fn get_embedder<'config, 'bot>( bot.cache .fog_message(bot, orig_sql_msg.channel_id.into_id(), ref_msg_id) .await? + .into_option() } else { None }; @@ -114,7 +115,7 @@ pub async fn get_embedder<'config, 'bot>( bot, points: msg.last_known_point_count as i32, config, - orig_message: Some(orig_msg), + orig_message: Some(orig_msg).into(), orig_message_author: orig_author, referenced_message: ref_msg, referenced_message_author: ref_msg_author, diff --git a/src/owner/handle.rs b/src/owner/handle.rs index 199350d7..de2147a2 100644 --- a/src/owner/handle.rs +++ b/src/owner/handle.rs @@ -29,7 +29,7 @@ pub async fn handle_message( let message = match message { Some(msg) => msg, None => { - let Some(msg) = bot.cache.fog_message(bot, channel_id, message_id).await? else { + let Some(msg) = bot.cache.fog_message(bot, channel_id, message_id).await?.into_option() else { return Ok(()); }; message_owner = msg;