From 904259063831738d357a8092bee7c0e30988b0f6 Mon Sep 17 00:00:00 2001 From: seth Date: Wed, 6 Dec 2023 03:57:33 -0500 Subject: refactor: use reactboard-v2 yay! we don't need to fetch every single reaction from every guild whenever a new one is triggered --- src/storage/mod.rs | 126 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 31 deletions(-) (limited to 'src/storage/mod.rs') diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 64a3856..2ab54f6 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1,15 +1,18 @@ -use std::fmt::{Debug, Display}; +use std::fmt::Debug; use color_eyre::eyre::Result; use log::*; -use poise::serenity_prelude::GuildId; +use poise::serenity_prelude::{GuildId, MessageId}; use redis::{AsyncCommands, Client, FromRedisValue, ToRedisArgs}; -pub mod reactboard; -pub mod settings; +mod reactboard; +mod settings; +// these are purposefully private. see the comment below +use reactboard::REACTBOARD_KEY; +use settings::SETTINGS_KEY; -pub use reactboard::*; -pub use settings::*; +pub use reactboard::ReactBoardEntry; +pub use settings::{Settings, SettingsProperties}; #[derive(Clone, Debug)] pub struct Storage { @@ -23,6 +26,12 @@ impl Storage { Ok(Self { client }) } + /* + these are mainly light abstractions to avoid the `let mut con` + boilerplate, as well as not require the caller to format the + strings for keys + */ + pub async fn get_key(&self, key: &str) -> Result where T: FromRedisValue, @@ -66,6 +75,15 @@ impl Storage { Ok(()) } + pub async fn expire_key(&self, key: &str, expire_seconds: usize) -> Result<()> { + debug!("Expiring key {key} in {expire_seconds}"); + + let mut con = self.client.get_async_connection().await?; + con.expire(key, expire_seconds).await?; + + Ok(()) + } + pub async fn add_to_index<'a>( &self, key: &str, @@ -80,19 +98,60 @@ impl Storage { Ok(()) } - pub fn format_settings_key(subkey: impl Display) -> String { - format!("{}:{subkey}", SETTINGS_KEY) + pub async fn get_from_index(&self, key: &str) -> Result> + where + T: FromRedisValue, + { + let index = format!("{key}:index"); + debug!("Fetching index {index}"); + + let mut con = self.client.get_async_connection().await?; + let mems = con.smembers(key).await?; + + Ok(mems) } - pub async fn create_settings_key(&self, settings: Settings) -> Result<()> { - let key = Self::format_settings_key(settings.guild_id); + // guild settings + + pub async fn create_guild_settings(&self, settings: Settings) -> Result<()> { + let key = format!("{SETTINGS_KEY}:{}", settings.guild_id); self.set_key(&key, &settings).await?; + // adding to index since we need to look all of these up sometimes self.add_to_index(SETTINGS_KEY, settings).await?; Ok(()) } + pub async fn get_guild_settings(&self, guild_id: &GuildId) -> Result { + debug!("Fetching guild settings for {guild_id}"); + + let key = format!("{SETTINGS_KEY}:{guild_id}"); + let settings: Settings = self.get_key(&key).await?; + + Ok(settings) + } + + pub async fn delete_guild_settings(&self, guild_id: &GuildId) -> Result<()> { + let key = format!("{SETTINGS_KEY}:{guild_id}"); + self.delete_key(&key).await?; + + Ok(()) + } + + pub async fn guild_settings_exist(&self, guild_id: &GuildId) -> Result { + let key = format!("{SETTINGS_KEY}:{guild_id}"); + self.key_exists(&key).await + } + + pub async fn get_all_guild_settings(&self) -> Result> { + debug!("Fetching all guild settings"); + + let guilds: Vec = self.get_from_index(SETTINGS_KEY).await?; + + Ok(guilds) + } + /// get guilds that have enabled optional commands pub async fn get_opted_guilds(&self) -> Result> { debug!("Fetching opted-in guilds"); @@ -112,35 +171,40 @@ impl Storage { Ok(opted) } - pub async fn get_all_guild_settings(&self) -> Result> { - debug!("Fetching all guild settings"); + // reactboard - let mut con = self.client.get_async_connection().await?; - let key = Self::format_settings_key("index"); + pub async fn create_reactboard_entry( + &self, + guild_id: &GuildId, + entry: ReactBoardEntry, + ) -> Result<()> { + let key = format!("{REACTBOARD_KEY}:{guild_id}:{}", entry.original_message_id); - let guilds: Vec = con.smembers(key).await?; + self.set_key(&key, &entry).await?; + self.expire_key(&key, 30 * 24 * 60 * 60).await?; // 30 days - Ok(guilds) + Ok(()) } - pub async fn get_guild_settings(&self, guild_id: &GuildId) -> Result { - debug!("Fetching guild settings for {guild_id}"); - - let key = Self::format_settings_key(guild_id); - let settings: Settings = self.get_key(&key).await?; + pub async fn get_reactboard_entry( + &self, + guild_id: &GuildId, + message_id: &MessageId, + ) -> Result { + debug!("Fetching reactboard entry in {guild_id}"); - Ok(settings) - } + let key = format!("{REACTBOARD_KEY}:{guild_id}:{message_id}"); + let entry: ReactBoardEntry = self.get_key(&key).await?; - pub async fn create_reactboard_info_key(&self, reactboard: ReactBoardInfo) -> Result<()> { - self.set_key(REACT_BOARD_KEY, reactboard).await?; - Ok(()) + Ok(entry) } - pub async fn get_reactboard_info(&self) -> Result { - debug!("Fetching reactboard info"); - let reactboard: ReactBoardInfo = self.get_key(REACT_BOARD_KEY).await?; - - Ok(reactboard) + pub async fn reactboard_entry_exists( + &self, + guild_id: &GuildId, + message_id: &MessageId, + ) -> Result { + let key = format!("{REACTBOARD_KEY}:{guild_id}:{message_id}"); + self.key_exists(&key).await } } -- cgit v1.2.3