summaryrefslogtreecommitdiff
path: root/src/events/reactboard.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/events/reactboard.rs')
-rw-r--r--src/events/reactboard.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/events/reactboard.rs b/src/events/reactboard.rs
new file mode 100644
index 0000000..c27bd80
--- /dev/null
+++ b/src/events/reactboard.rs
@@ -0,0 +1,143 @@
+use crate::{client::Data, storage, utils};
+use storage::reactboard::ReactBoardEntry;
+
+use eyre::{eyre, Context as _, Result};
+use log::{debug, warn};
+use poise::serenity_prelude::{
+ Context, CreateMessage, EditMessage, GuildId, Message, MessageReaction, Reaction,
+};
+
+pub async fn handle(ctx: &Context, reaction: &Reaction, data: &Data) -> Result<()> {
+ // TODO @getchoo: don't do anything if this message is old
+ let msg = reaction
+ .message(&ctx.http)
+ .await
+ .wrap_err("Couldn't get reaction from message!")?;
+
+ let matched = msg
+ .clone()
+ .reactions
+ .into_iter()
+ .find(|r| r.reaction_type == reaction.emoji)
+ .ok_or_else(|| {
+ eyre!(
+ "Couldn't find any matching reactions for {} in message {}!",
+ reaction.emoji.as_data(),
+ msg.id
+ )
+ })?;
+
+ send_to_reactboard(
+ ctx,
+ &matched,
+ &msg,
+ &reaction.guild_id.unwrap_or_default(),
+ data,
+ )
+ .await?;
+
+ Ok(())
+}
+
+async fn send_to_reactboard(
+ ctx: &Context,
+ reaction: &MessageReaction,
+ msg: &Message,
+ guild_id: &GuildId,
+ data: &Data,
+) -> Result<()> {
+ let Some(storage) = &data.storage else {
+ warn!("Can't make ReactBoard entry; no storage backend found!");
+ return Ok(());
+ };
+
+ let settings = storage.get_guild_settings(guild_id).await?;
+
+ // make sure everything is in order...
+ if !settings.reactboard_enabled {
+ debug!("ReactBoard is disabled in {guild_id}, ignoring");
+ return Ok(());
+ }
+
+ let Some(target) = settings.reactboard_channel else {
+ debug!("ReactBoard is disabled in {guild_id}, ignoring");
+ return Ok(());
+ };
+
+ if !settings.can_use_reaction(&reaction.reaction_type) {
+ debug!("Reaction {} can't be used!", reaction.reaction_type);
+ return Ok(());
+ }
+
+ let count = if msg
+ .reaction_users(ctx, reaction.reaction_type.clone(), None, None)
+ .await?
+ .contains(&msg.author)
+ {
+ reaction.count - 1
+ } else {
+ reaction.count
+ };
+
+ if count < settings.reactboard_requirement.unwrap_or(5) {
+ debug!(
+ "Ignoring message {} on ReactBoard, not enough reactions",
+ msg.id
+ );
+ return Ok(());
+ }
+
+ let content = format!("{} **#{}**", reaction.reaction_type, count);
+
+ let entry = if storage.reactboard_entry_exists(guild_id, &msg.id).await? {
+ // bump reaction count if previous entry exists
+ let mut entry = storage.get_reactboard_entry(guild_id, &msg.id).await?;
+
+ // bail if we don't need to edit anything
+ if entry.reaction_count >= count {
+ debug!("Message {} doesn't need updating", msg.id);
+ return Ok(());
+ }
+
+ debug!(
+ "Bumping {} reaction count from {} to {}",
+ msg.id, entry.reaction_count, count
+ );
+
+ let edited = EditMessage::new().content(content);
+
+ ctx.http
+ .get_message(entry.posted_channel_id, entry.posted_message_id)
+ .await
+ .wrap_err_with(|| {
+ format!(
+ "Couldn't get previous message from ReactBoardEntry {} in Redis DB!",
+ entry.original_message_id
+ )
+ })?
+ .edit(ctx, edited)
+ .await?;
+
+ // update reaction count in redis
+ entry.reaction_count = count;
+ entry
+ } else {
+ // make new message and add entry to redis otherwise
+ let embed = utils::resolve_message_to_embed(ctx, msg).await;
+ let message = CreateMessage::default().content(content).embed(embed);
+
+ let resp = target.send_message(ctx, message).await?;
+
+ ReactBoardEntry {
+ original_message_id: msg.id,
+ reaction_count: count,
+ posted_channel_id: resp.channel_id,
+ posted_message_id: resp.id,
+ }
+ };
+
+ debug!("Creating new ReactBoard entry:\n{entry:#?}");
+ storage.create_reactboard_entry(guild_id, entry).await?;
+
+ Ok(())
+}