From 8376c45c2d0ab8966dc8ef54974f8feb02f3f27c Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 7 Dec 2023 22:18:21 -0500 Subject: [PATCH] feat: flesh out storage object --- src/commands/mod.rs | 2 +- src/commands/moderation/mod.rs | 2 +- src/storage.rs | 40 ---------- src/storage/message_logger.rs | 11 +++ src/storage/mod.rs | 137 +++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 42 deletions(-) delete mode 100644 src/storage.rs create mode 100644 src/storage/message_logger.rs create mode 100644 src/storage/mod.rs diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 10f96815..99e4919e 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -4,7 +4,7 @@ use color_eyre::eyre::Report; use poise::Command; mod general; -mod moderation; +pub mod moderation; pub fn to_global_commands() -> Vec> { vec![ diff --git a/src/commands/moderation/mod.rs b/src/commands/moderation/mod.rs index 6f331850..c3b862e3 100644 --- a/src/commands/moderation/mod.rs +++ b/src/commands/moderation/mod.rs @@ -4,7 +4,7 @@ use std::error::Error; use color_eyre::eyre::{eyre, Result}; use poise::serenity_prelude::{ArgumentConvert, ChannelId, GuildId, Member}; -mod actions; +pub mod actions; use actions::{Ban, Kick, ModAction}; async fn split_argument( diff --git a/src/storage.rs b/src/storage.rs deleted file mode 100644 index 6cec18f7..00000000 --- a/src/storage.rs +++ /dev/null @@ -1,40 +0,0 @@ -use color_eyre::eyre::Result; -use log::*; -use poise::serenity_prelude::UserId; -use redis::{AsyncCommands as _, Client}; - -pub const USER_KEY: &str = "users-v1"; - -#[derive(Clone, Debug)] -pub struct Storage { - client: Client, -} - -impl Storage { - pub fn new(redis_url: &str) -> Result { - let client = Client::open(redis_url)?; - - Ok(Self { client }) - } - - pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> { - let mut con = self.client.get_async_connection().await?; - - info!("Marking {sender} as a PluralKit user"); - let key = format!("{USER_KEY}:{sender}:pk"); - - // Just store some value. We only care about the presence of this key - con.set(&key, 0).await?; - con.expire(key, 7 * 24 * 60 * 60).await?; - - Ok(()) - } - - pub async fn is_user_plural(&self, user_id: UserId) -> Result { - let key = format!("{USER_KEY}:{user_id}:pk"); - let mut con = self.client.get_async_connection().await?; - - let exists: bool = con.exists(key).await?; - Ok(exists) - } -} diff --git a/src/storage/message_logger.rs b/src/storage/message_logger.rs new file mode 100644 index 00000000..0ee24079 --- /dev/null +++ b/src/storage/message_logger.rs @@ -0,0 +1,11 @@ +use poise::serenity_prelude::UserId; +use redis_macros::{FromRedisValue, ToRedisArgs}; +use serde::{Deserialize, Serialize}; + +pub const MSG_LOG_KEY: &str = "message-log-v1"; + +#[derive(Clone, Debug, Deserialize, Serialize, FromRedisValue, ToRedisArgs)] +pub struct MessageLog { + pub author: UserId, + pub content: String, +} diff --git a/src/storage/mod.rs b/src/storage/mod.rs new file mode 100644 index 00000000..88f9544d --- /dev/null +++ b/src/storage/mod.rs @@ -0,0 +1,137 @@ +use std::fmt::Debug; + +use color_eyre::eyre::Result; +use log::*; +use poise::serenity_prelude::{ChannelId, MessageId, UserId}; +use redis::{AsyncCommands as _, Client, FromRedisValue, ToRedisArgs}; + +pub mod message_logger; +use message_logger::*; + +pub const PK_KEY: &str = "pluralkit-v1"; + +#[derive(Clone, Debug)] +pub struct Storage { + client: Client, +} + +impl Storage { + pub fn new(redis_url: &str) -> Result { + let client = Client::open(redis_url)?; + + 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 + */ + + async fn get_key(&self, key: &str) -> Result + where + T: FromRedisValue, + { + debug!("Getting key {key}"); + + let mut con = self.client.get_async_connection().await?; + let res: T = con.get(key).await?; + + Ok(res) + } + + async fn set_key<'a>( + &self, + key: &str, + value: impl ToRedisArgs + Debug + Send + Sync + 'a, + ) -> Result<()> { + debug!("Creating key {key}:\n{value:#?}"); + + let mut con = self.client.get_async_connection().await?; + con.set(key, value).await?; + + Ok(()) + } + + async fn key_exists(&self, key: &str) -> Result { + debug!("Checking if key {key} exists"); + + let mut con = self.client.get_async_connection().await?; + let exists: u64 = con.exists(key).await?; + + Ok(exists > 0) + } + + async fn delete_key(&self, key: &str) -> Result<()> { + debug!("Deleting key {key}"); + + let mut con = self.client.get_async_connection().await?; + con.del(key).await?; + + Ok(()) + } + + 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 store_user_plurality(&self, sender: UserId) -> Result<()> { + info!("Marking {sender} as a PluralKit user"); + let key = format!("{PK_KEY}:{sender}"); + + // Just store some value. We only care about the presence of this key + self.set_key(&key, 0).await?; + self.expire_key(&key, 7 * 24 * 60 * 60).await?; + + Ok(()) + } + + pub async fn is_user_plural(&self, user_id: UserId) -> Result { + let key = format!("{PK_KEY}:{user_id}"); + self.key_exists(&key).await + } + + pub async fn store_message( + &self, + channel_id: &ChannelId, + message_id: &MessageId, + content: String, + author: UserId, + ) -> Result<()> { + let key = format!("{MSG_LOG_KEY}:{channel_id}:{message_id}"); + + let val = MessageLog { author, content }; + + self.set_key(&key, val).await?; + self.expire_key(&key, 30 * 24 * 60 * 60).await?; // only store for 30 days + + Ok(()) + } + + pub async fn get_message( + &self, + channel_id: &ChannelId, + message_id: &MessageId, + ) -> Result { + let key = format!("{MSG_LOG_KEY}:{channel_id}:{message_id}"); + let res = self.get_key(&key).await?; + + Ok(res) + } + + pub async fn delete_message( + &self, + channel_id: &ChannelId, + message_id: &MessageId, + ) -> Result<()> { + let key = format!("{MSG_LOG_KEY}:{channel_id}:{message_id}"); + self.delete_key(&key).await?; + + Ok(()) + } +}