diff --git a/src/handlers/starboard.rs b/src/handlers/starboard.rs index b119f310a3bfd8dd046d476373cf26365f81d724..25bb92b4bc35ac237002181aa7593472cc241b52 100644 --- a/src/handlers/starboard.rs +++ b/src/handlers/starboard.rs @@ -7,35 +7,43 @@ use anyhow::Context as AnyhowContext; use bigdecimal::{BigDecimal, FromPrimitive, ToPrimitive}; use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl}; use serenity::async_trait; -use serenity::model::prelude::{ChannelId, EmojiId, GuildId, Message, Reaction, ReactionType}; +use serenity::builder::CreateEmbed; +use serenity::model::prelude::{ChannelId, Emoji, EmojiId, Message, Reaction, ReactionType}; use serenity::prelude::*; use std::sync::Arc; use tokio::sync::Mutex; -pub struct StarboardHandler { +#[derive(Clone)] +pub struct SingleMessageHandler { conn: Arc<Mutex<PgConnection>>, + server_settings: ServerSetting, + reaction_count: u64, + msg: Message, + emoji: Emoji, + name: String, + image: Option<String>, } -impl StarboardHandler { - pub fn new(conn: Arc<Mutex<PgConnection>>) -> Self { - Self { conn } - } - - async fn handle_reaction(&self, ctx: &Context, reaction: &Reaction) -> anyhow::Result<()> { +impl SingleMessageHandler { + pub async fn handle_reaction( + conn: Arc<Mutex<PgConnection>>, + ctx: &Context, + reaction: &Reaction, + ) -> anyhow::Result<()> { let guild = match reaction.channel_id.to_channel(ctx).await?.guild() { Some(x) => x, None => return Ok(()), }; // get corresponding guild settings - let gs: Vec<ServerSetting> = server_settings::dsl::server_settings + let mut gs: Vec<ServerSetting> = server_settings::dsl::server_settings .filter( schema::server_settings::columns::guild_id .eq(BigDecimal::from_u64(guild.guild_id.0).unwrap()), ) .limit(1) - .get_results(&*self.conn.lock().await)?; - let gs = match gs.first() { + .get_results(&*conn.lock().await)?; + let gs = match gs.pop() { Some(x) => x, None => return Ok(()), }; @@ -49,8 +57,38 @@ impl StarboardHandler { let msg = reaction.message(ctx).await?; for mr in &msg.reactions { if Self::emoji_match(&mr.reaction_type, emoji) { - self.handle_matching_reaction(ctx, gs, mr.count, &msg, &guild.guild_id) - .await?; + let guild_id = guild.guild_id; + + let emoji = guild_id + .emoji(ctx, emoji) + .await + .context("Could not get emoji from guild")?; + + let name = msg + .author + .nick_in(ctx, guild_id) + .await + .unwrap_or_else(|| msg.author.tag()); + + let image = msg + .attachments + .iter() + .filter(|a| a.width.is_some()) + .map(|a| &a.url) + .next() + .cloned(); + + let handler = Self { + conn, + server_settings: gs, + reaction_count: mr.count, + msg, + emoji, + name, + image, + }; + + handler.process_match(ctx).await?; break; } } @@ -59,16 +97,9 @@ impl StarboardHandler { Ok(()) } - async fn handle_matching_reaction( - &self, - ctx: &Context, - gs: &ServerSetting, - count: u64, - msg: &Message, - guild: &GuildId, - ) -> anyhow::Result<()> { - if count >= gs.starboard_threshold as u64 { - let original_id = BigDecimal::from(msg.id.0); + async fn process_match(&self, ctx: &Context) -> anyhow::Result<()> { + if self.reaction_count >= self.server_settings.starboard_threshold as u64 { + let original_id = BigDecimal::from(self.msg.id.0); diesel::insert_into(starboard_mappings::dsl::starboard_mappings) .values(starboard_mappings::columns::original_id.eq(&original_id)) .returning(starboard_mappings::columns::repost_id) @@ -85,47 +116,11 @@ impl StarboardHandler { repost_id.get(0).context("Insert of mapping failed")?; if repost_id.is_none() { - // post to repost channel - let name = msg - .author - .nick_in(&ctx, guild) - .await - .unwrap_or_else(|| msg.author.tag()); - - let image = msg - .attachments - .iter() - .filter(|a| a.width.is_some()) - .map(|a| &a.url) - .next(); - - let repost = ChannelId( - gs.starboard_channel - .to_u64() - .context("Could not convert starboard channel to a u64")?, - ) - .send_message(ctx, |m| { - m.embed(|e| { - let mut e = e - .description(format!( - "[Jump to source]({})\n{}", - msg.link(), - msg.content - )) - .author(|a| a.name(&name).icon_url(msg.author.face())) - .timestamp(&msg.timestamp); - - if let Some(url) = image { - e = e.image(url); - } - - e - }) - }) - .await?; + // post to repost + let repost = self.post_new_starboard_message(ctx).await?; // update the DB - let repost_id = BigDecimal::from_u64(repost.id.0); + let repost_id = BigDecimal::from_u64(repost); diesel::update( starboard_mappings::dsl::starboard_mappings .filter(starboard_mappings::columns::original_id.eq(original_id)), @@ -138,11 +133,56 @@ impl StarboardHandler { Ok(()) } + async fn post_new_starboard_message(&self, ctx: &Context) -> anyhow::Result<u64> { + let repost = ChannelId( + self.server_settings + .starboard_channel + .to_u64() + .context("Could not convert starboard channel to a u64")?, + ) + .send_message(ctx, |m| m.embed(|e| self.clone().starboard_message(e))) + .await?; + + Ok(repost.id.0) + } + + fn starboard_message(self, e: &mut CreateEmbed) -> &mut CreateEmbed { + let mut e = e + .description(format!( + "[Jump to source]({})\n{}", + self.msg.link(), + self.msg.content + )) + .title(format!("{} {}", self.reaction_count, self.emoji)) + .author(|a| a.name(&self.name).icon_url(self.msg.author.face())) + .timestamp(&self.msg.timestamp); + + if let Some(url) = self.image { + e = e.image(url); + } + + e + } + fn emoji_match(rt: &ReactionType, em: EmojiId) -> bool { matches!(rt, ReactionType::Custom { id, .. } if *id == em) } } +pub struct StarboardHandler { + conn: Arc<Mutex<PgConnection>>, +} + +impl StarboardHandler { + pub fn new(conn: Arc<Mutex<PgConnection>>) -> Self { + Self { conn } + } + + async fn handle_reaction(&self, ctx: &Context, reaction: &Reaction) -> anyhow::Result<()> { + SingleMessageHandler::handle_reaction(self.conn.clone(), ctx, reaction).await + } +} + #[async_trait] impl ReactionHandler for StarboardHandler { async fn reaction(&self, ctx: &Context, reaction: &Reaction) { diff --git a/src/models.rs b/src/models.rs index 10d32fea3696c52717ba4eb92d647342a6c39ac4..bae06f4d4f272ece41ec8dd572ff8443ca543da8 100644 --- a/src/models.rs +++ b/src/models.rs @@ -17,7 +17,7 @@ pub struct NewUserProgram<'a> { pub code: &'a str, } -#[derive(Queryable)] +#[derive(Clone, Queryable)] pub struct ServerSetting { pub id: i32, pub guild_id: BigDecimal, diff --git a/src/schema.rs b/src/schema.rs index 925378768a3e25685860c3e62d39c746a9d991c6..4a883c63425e0e2a5e64207792079a9764683d35 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,28 +1,32 @@ table! { - server_settings (id) { - id -> Int4, - guild_id -> Numeric, - starboard_threshold -> Int4, - starboard_emoji_id -> Numeric, - starboard_channel -> Numeric, - } + server_settings (id) { + id -> Int4, + guild_id -> Numeric, + starboard_threshold -> Int4, + starboard_emoji_id -> Numeric, + starboard_channel -> Numeric, + } } table! { - starboard_mappings (original_id) { - original_id -> Numeric, - repost_id -> Nullable<Numeric>, - } + starboard_mappings (original_id) { + original_id -> Numeric, + repost_id -> Nullable<Numeric>, + } } table! { - user_programs (id) { - id -> Int4, - discord_user_id -> Numeric, - name -> Varchar, - code -> Text, - published -> Int4, - } + user_programs (id) { + id -> Int4, + discord_user_id -> Numeric, + name -> Varchar, + code -> Text, + published -> Int4, + } } -allow_tables_to_appear_in_same_query!(server_settings, starboard_mappings, user_programs,); +allow_tables_to_appear_in_same_query!( + server_settings, + starboard_mappings, + user_programs, +);