summaryrefslogtreecommitdiff
path: root/src/handlers/event
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
parent41dfa94258215769b9d844875e79097d4a498770 (diff)
feat: add redis for reactboard
Diffstat (limited to 'src/handlers/event')
-rw-r--r--src/handlers/event/message.rs16
-rw-r--r--src/handlers/event/mod.rs10
-rw-r--r--src/handlers/event/pinboard.rs31
-rw-r--r--src/handlers/event/reactboard.rs143
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(())