summaryrefslogtreecommitdiff
path: root/src/handlers/event/reactboard.rs
diff options
context:
space:
mode:
authorseth <[email protected]>2023-11-30 23:30:00 -0500
committerseth <[email protected]>2023-12-01 07:12:49 -0500
commit85402f9103b55dff65c6a6079c6902080e5a30a6 (patch)
treeeba04a9f9938ac1d7ac14cb5d7bc5e0fb8f4db16 /src/handlers/event/reactboard.rs
parent41dfa94258215769b9d844875e79097d4a498770 (diff)
feat: add redis for reactboard
Diffstat (limited to 'src/handlers/event/reactboard.rs')
-rw-r--r--src/handlers/event/reactboard.rs143
1 files changed, 123 insertions, 20 deletions
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(())