diff options
Diffstat (limited to 'src/handlers/event')
| -rw-r--r-- | src/handlers/event/message.rs | 16 | ||||
| -rw-r--r-- | src/handlers/event/mod.rs | 10 | ||||
| -rw-r--r-- | src/handlers/event/pinboard.rs | 31 | ||||
| -rw-r--r-- | src/handlers/event/reactboard.rs | 143 |
4 files changed, 157 insertions, 43 deletions
diff --git a/src/handlers/event/message.rs b/src/handlers/event/message.rs index 88faf85..c90ae3a 100644 --- a/src/handlers/event/message.rs +++ b/src/handlers/event/message.rs @@ -1,8 +1,8 @@ -use crate::settings::Settings; +use crate::Settings; use crate::{consts, Data}; use color_eyre::eyre::{Report, Result}; -use log::*; +use log::info; use poise::serenity_prelude::{Context, Message}; use poise::FrameworkContext; @@ -10,9 +10,9 @@ pub async fn handle( ctx: &Context, framework: FrameworkContext<'_, Data, Report>, msg: &Message, - settings: &Settings, + data: &Data, ) -> Result<()> { - if should_echo(framework, msg, settings) { + if should_echo(framework, msg, &data.settings) { msg.reply(ctx, &msg.content).await?; } @@ -25,8 +25,12 @@ fn should_echo( settings: &Settings, ) -> bool { let gid = msg.guild_id.unwrap_or_default(); - if msg.author.id == framework.bot_id || !settings.is_guild_allowed(gid) { - info!("not running copypasta command in {gid}"); + if msg.author.id == framework.bot_id { + info!("I don't like repeating myself..."); + } + + if !settings.is_guild_allowed(gid) { + info!("Not echoing in guild {gid}"); return false; } diff --git a/src/handlers/event/mod.rs b/src/handlers/event/mod.rs index bbfc642..a587c77 100644 --- a/src/handlers/event/mod.rs +++ b/src/handlers/event/mod.rs @@ -16,18 +16,16 @@ pub async fn handle( ) -> Result<()> { match event { Event::Ready { data_about_bot } => { - log::info!("logged in as {}", data_about_bot.user.name) + log::info!("Logged in as {}!", data_about_bot.user.name) } Event::Message { new_message } => { - message::handle(ctx, framework, new_message, &data.settings).await? + message::handle(ctx, framework, new_message, data).await? } - Event::ChannelPinsUpdate { pin } => pinboard::handle(ctx, pin, &data.settings).await, + Event::ChannelPinsUpdate { pin } => pinboard::handle(ctx, pin, data).await?, - Event::ReactionAdd { add_reaction } => { - reactboard::handle(ctx, add_reaction, &data.settings).await? - } + Event::ReactionAdd { add_reaction } => reactboard::handle(ctx, add_reaction, data).await?, _ => {} } diff --git a/src/handlers/event/pinboard.rs b/src/handlers/event/pinboard.rs index 0c87a5b..33d2680 100644 --- a/src/handlers/event/pinboard.rs +++ b/src/handlers/event/pinboard.rs @@ -1,15 +1,15 @@ -use crate::settings::Settings; -use crate::utils; +use crate::{utils, Data}; +use color_eyre::eyre::{eyre, Context as _, Result}; use log::*; use poise::serenity_prelude::model::prelude::*; use poise::serenity_prelude::Context; -pub async fn handle(ctx: &Context, pin: &ChannelPinsUpdateEvent, settings: &Settings) { - if let Some(sources) = &settings.pinboard_sources { +pub async fn handle(ctx: &Context, pin: &ChannelPinsUpdateEvent, data: &Data) -> Result<()> { + if let Some(sources) = &data.settings.pinboard_sources { if !sources.contains(&pin.channel_id) { - warn!("can't access source of pin!"); - return; + warn!("Can't access source of pin!"); + return Ok(()); } } @@ -18,16 +18,23 @@ pub async fn handle(ctx: &Context, pin: &ChannelPinsUpdateEvent, settings: &Sett .channel_id .pins(&ctx.http) .await - .expect("couldn't get a list of pins!?"); + .expect("Couldn't get a list of pins!?"); for pin in pins { // We call `take` because it's supposed to be just for the latest message. - redirect(ctx, &pin, pinner.take(), settings.pinboard_target).await; - pin.unpin(&ctx).await.expect("couldn't unpin message"); + redirect(ctx, &pin, pinner.take(), data.settings.pinboard_target).await?; + pin.unpin(&ctx).await?; } + + Ok(()) } -async fn redirect(ctx: &Context, pin: &Message, pinner: Option<UserId>, target: ChannelId) { +async fn redirect( + ctx: &Context, + pin: &Message, + pinner: Option<UserId>, + target: ChannelId, +) -> Result<()> { let pinner = pinner.map_or("*someone*".to_owned(), |u| format!("<@{u}>")); let embed = utils::resolve_message_to_embed(ctx, pin).await; @@ -38,7 +45,9 @@ async fn redirect(ctx: &Context, pin: &Message, pinner: Option<UserId>, target: .set_embed(embed) }) .await - .expect("couldn't redirect message"); + .wrap_err_with(|| eyre!("couldn't redirect message"))?; + + Ok(()) } /// (Desperate, best-effort) attempt to get the user that pinned the last message diff --git a/src/handlers/event/reactboard.rs b/src/handlers/event/reactboard.rs index 3972931..2a417da 100644 --- a/src/handlers/event/reactboard.rs +++ b/src/handlers/event/reactboard.rs @@ -1,14 +1,32 @@ -use crate::{settings::Settings, utils}; +use crate::{utils, Data}; use color_eyre::eyre::{eyre, Context as _, Result}; use log::*; -use poise::serenity_prelude::{Context, Message, MessageReaction, Reaction}; +use poise::serenity_prelude::{ChannelId, Context, Message, MessageId, MessageReaction, Reaction}; +use redis::AsyncCommands as _; +use redis_macros::{FromRedisValue, ToRedisArgs}; +use serde::{Deserialize, Serialize}; -pub async fn handle(ctx: &Context, reaction: &Reaction, settings: &Settings) -> Result<()> { +#[derive(Clone, Debug, Serialize, Deserialize, FromRedisValue, ToRedisArgs)] +struct ReactBoardEntry { + original_id: MessageId, + reaction_count: u64, + channel_id: ChannelId, + message_id: MessageId, +} + +#[derive(Default, Serialize, Deserialize, FromRedisValue, ToRedisArgs)] +struct ReactBoardInfo { + reactions: Vec<ReactBoardEntry>, +} + +const REACT_BOARD_KEY: &str = "reactboard-v1"; + +pub async fn handle(ctx: &Context, reaction: &Reaction, data: &Data) -> Result<()> { let msg = reaction .message(&ctx.http) .await - .wrap_err("couldn't get reaction from message!")?; + .wrap_err_with(|| "Couldn't get reaction from message!")?; let matched = msg .clone() @@ -17,13 +35,13 @@ pub async fn handle(ctx: &Context, reaction: &Reaction, settings: &Settings) -> .find(|r| r.reaction_type == reaction.emoji) .ok_or_else(|| { eyre!( - "couldn't find any matching reactions for {} in message {}!", + "Couldn't find any matching reactions for {} in message {}!", reaction.emoji.as_data(), msg.id ) })?; - send_to_reactboard(ctx, &matched, &msg, settings).await?; + send_to_reactboard(ctx, &matched, &msg, data).await?; Ok(()) } @@ -32,32 +50,117 @@ async fn send_to_reactboard( ctx: &Context, reaction: &MessageReaction, msg: &Message, - settings: &Settings, + data: &Data, ) -> Result<()> { - if !settings.can_use_reaction(reaction) { - info!("reaction {} can't be used!", reaction.reaction_type); + // make sure everything is in order... + if !data.settings.can_use_reaction(reaction) { + info!("Reaction {} can't be used!", reaction.reaction_type); return Ok(()); } - if reaction.count == settings.reactboard_requirement.unwrap_or(5) { + if reaction.count < data.settings.reactboard_requirement.unwrap_or(5) { + info!( + "Ignoring message {} on reactboard, not enough reactions", + msg.id + ); + return Ok(()); + } + + let mut con = data.redis.get_async_connection().await?; + let req = con.get(REACT_BOARD_KEY).await; + + let mut reactboard: ReactBoardInfo = if let Err(why) = req { + // set the value to the default if the key is uninitialized + match why.kind() { + redis::ErrorKind::TypeError => { + warn!("Initializing {REACT_BOARD_KEY} key in Redis..."); + con.set(REACT_BOARD_KEY, ReactBoardInfo::default()).await?; + con.get(REACT_BOARD_KEY).await? + } + _ => return Err(why.into()), + } + } else { + req? + }; + + // try to find previous reactboard entry by the id of the original message + let old_index = reactboard + .reactions + .iter() + .position(|r| r.original_id == msg.id); + + let content = format!("{} **#{}**", reaction.reaction_type, reaction.count); + + // bump reaction count if previous entry exists + if let Some(old_index) = old_index { + let old_entry = reactboard.reactions[old_index].clone(); + + // bail if we don't need to edit anything + if old_entry.reaction_count >= reaction.count { + info!("Message {} doesn't need updating", msg.id); + return Ok(()); + } + + info!( + "Bumping {} reaction count from {} to {}", + msg.id, old_entry.reaction_count, reaction.count + ); + + ctx.http + .get_message( + *old_entry.channel_id.as_u64(), + *old_entry.message_id.as_u64(), + ) + .await + .wrap_err_with(|| { + format!( + "Couldn't get previous message from ReactBoardEntry {} in Redis DB!", + old_entry.original_id + ) + })? + .edit(ctx, |m| m.content(content)) + .await?; + + // update reaction count in redis + let mut new_entry = old_entry.clone(); + new_entry.reaction_count = reaction.count; + + reactboard.reactions.remove(old_index); + reactboard.reactions.push(new_entry.clone()); + + info!( + "Updating ReactBoard entry {} in {REACT_BOARD_KEY}\nOld:\n{old_entry:#?}\nNew:\n{new_entry:#?}", + msg.id + ); + con.set(REACT_BOARD_KEY, reactboard).await?; + // make new message and add entry to redis otherwise + } else { let embed = utils::resolve_message_to_embed(ctx, msg).await; - settings + let resp = data + .settings .reactboard_target - .send_message(&ctx.http, |m| { + .send_message(ctx, |m| { m.allowed_mentions(|am| am.empty_parse()) - .content(format!( - "{} **#{}**", - reaction.reaction_type, reaction.count - )) + .content(content) .set_embed(embed) }) .await?; - } else { + + let entry = ReactBoardEntry { + original_id: msg.id, + reaction_count: reaction.count, + channel_id: resp.channel_id, + message_id: resp.id, + }; + + reactboard.reactions.push(entry.clone()); + info!( - "not putting message {} on reactboard, not enough reactions", - msg.id - ) + "Creating new ReactBoard entry {} in {REACT_BOARD_KEY}:\n{:#?}", + msg.id, entry + ); + con.set(REACT_BOARD_KEY, reactboard).await?; } Ok(()) |
