storage: make constructors more idiomatic

This commit is contained in:
seth 2024-03-03 18:06:40 -05:00
parent 46fd02605f
commit 651f14d724
No known key found for this signature in database
GPG key ID: D31BD0D494BBEE86
5 changed files with 44 additions and 85 deletions

View file

@ -1,7 +1,7 @@
use crate::{api, Data}; use crate::{api, Data};
use eyre::{Report, Result}; use eyre::{Report, Result};
use log::{debug, info}; use log::{debug, info, trace};
use poise::serenity_prelude::{ActivityData, Context, FullEvent, OnlineStatus}; use poise::serenity_prelude::{ActivityData, Context, FullEvent, OnlineStatus};
use poise::FrameworkContext; use poise::FrameworkContext;
@ -15,7 +15,7 @@ mod support_onboard;
pub async fn handle( pub async fn handle(
ctx: &Context, ctx: &Context,
event: &FullEvent, event: &FullEvent,
_framework: FrameworkContext<'_, Data, Report>, _: FrameworkContext<'_, Data, Report>,
data: &Data, data: &Data,
) -> Result<()> { ) -> Result<()> {
match event { match event {
@ -33,7 +33,7 @@ pub async fn handle(
// ignore new messages from bots // ignore new messages from bots
// NOTE: the webhook_id check allows us to still respond to PK users // NOTE: the webhook_id check allows us to still respond to PK users
if new_message.author.bot && new_message.webhook_id.is_none() { if new_message.author.bot && new_message.webhook_id.is_none() {
debug!("Ignoring message {} from bot", new_message.id); trace!("Ignoring message {} from bot", new_message.id);
return Ok(()); return Ok(());
} }

View file

@ -2,7 +2,7 @@ use crate::{api, Data};
use std::time::Duration; use std::time::Duration;
use eyre::Result; use eyre::Result;
use log::debug; use log::{debug, trace};
use poise::serenity_prelude::{Context, Message}; use poise::serenity_prelude::{Context, Message};
use tokio::time::sleep; use tokio::time::sleep;
@ -20,14 +20,14 @@ pub async fn is_message_proxied(message: &Message) -> Result<bool> {
Ok(proxied) Ok(proxied)
} }
pub async fn handle(_ctx: &Context, msg: &Message, data: &Data) -> Result<()> { pub async fn handle(_: &Context, msg: &Message, data: &Data) -> Result<()> {
if msg.webhook_id.is_some() { if msg.webhook_id.is_some() {
debug!( debug!(
"Message {} has a webhook ID. Checking if it was sent through PluralKit", "Message {} has a webhook ID. Checking if it was sent through PluralKit",
msg.id msg.id
); );
debug!( trace!(
"Waiting on PluralKit API for {} seconds", "Waiting on PluralKit API for {} seconds",
PK_DELAY_SEC.as_secs() PK_DELAY_SEC.as_secs()
); );

View file

@ -8,6 +8,7 @@ use std::time::Duration;
use eyre::{eyre, Context as _, Report, Result}; use eyre::{eyre, Context as _, Report, Result};
use log::{info, warn}; use log::{info, warn};
use octocrab::Octocrab;
use poise::{ use poise::{
serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions, serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
}; };
@ -43,11 +44,7 @@ pub struct Data {
} }
impl Data { impl Data {
pub fn new() -> Result<Self> { pub fn new(config: Config, storage: Storage, octocrab: Arc<Octocrab>) -> Result<Self> {
let config = Config::new_from_env();
let storage = Storage::new(&config.redis_url)?;
let octocrab = octocrab::instance();
Ok(Self { Ok(Self {
config, config,
storage, storage,
@ -58,13 +55,16 @@ impl Data {
async fn setup( async fn setup(
ctx: &serenity::Context, ctx: &serenity::Context,
_ready: &serenity::Ready, _: &serenity::Ready,
framework: &Framework<Data, Report>, framework: &Framework<Data, Report>,
) -> Result<Data> { ) -> Result<Data> {
let data = Data::new()?; let config = Config::new_from_env();
let storage = Storage::from_url(&config.redis_url)?;
let octocrab = octocrab::instance();
let data = Data::new(config, storage, octocrab)?;
// test redis connection // test redis connection
let mut client = data.storage.client.clone(); let mut client = data.storage.client().clone();
if !client.check_connection() { if !client.check_connection() {
return Err(eyre!( return Err(eyre!(

View file

@ -1,110 +1,69 @@
use std::fmt::Debug; use std::fmt::Debug;
use eyre::Result; use eyre::Result;
use log::{debug, info}; use log::debug;
use poise::serenity_prelude::UserId; use poise::serenity_prelude::UserId;
use redis::{AsyncCommands as _, Client, FromRedisValue, ToRedisArgs}; use redis::{AsyncCommands, Client};
const PK_KEY: &str = "pluralkit-v1"; const PK_KEY: &str = "pluralkit-v1";
const LAUNCHER_VERSION_KEY: &str = "launcher-version-v1"; const LAUNCHER_VERSION_KEY: &str = "launcher-version-v1";
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Storage { pub struct Storage {
pub client: Client, client: Client,
} }
impl Storage { impl Storage {
pub fn new(redis_url: &str) -> Result<Self> { pub fn new(client: Client) -> Self {
Self { client }
}
pub fn from_url(redis_url: &str) -> Result<Self> {
let client = Client::open(redis_url)?; let client = Client::open(redis_url)?;
Ok(Self { client }) Ok(Self::new(client))
} }
/* pub fn client(&self) -> &Client {
these are mainly light abstractions to avoid the `let mut con` &self.client
boilerplate, as well as not require the caller to format the
strings for keys
*/
async fn get_key<T>(&self, key: &str) -> Result<T>
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<bool> {
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)
}
/* we'll probably use this again
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: i64) -> 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<()> { pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> {
info!("Marking {sender} as a PluralKit user"); debug!("Marking {sender} as a PluralKit user");
let key = format!("{PK_KEY}:{sender}"); let key = format!("{PK_KEY}:{sender}");
let mut con = self.client.get_async_connection().await?;
// Just store some value. We only care about the presence of this key // Just store some value. We only care about the presence of this key
self.set_key(&key, 0).await?; con.set_ex(key, 0, 7 * 24 * 60 * 60).await?; // 1 week
self.expire_key(&key, 7 * 24 * 60 * 60).await?; // weekly
Ok(()) Ok(())
} }
pub async fn is_user_plural(&self, user_id: UserId) -> Result<bool> { pub async fn is_user_plural(&self, user_id: UserId) -> Result<bool> {
debug!("Checking if user {user_id} is plural");
let key = format!("{PK_KEY}:{user_id}"); let key = format!("{PK_KEY}:{user_id}");
self.key_exists(&key).await
let mut con = self.client.get_async_connection().await?;
let exists = con.exists(key).await?;
Ok(exists)
} }
pub async fn cache_launcher_version(&self, version: &str) -> Result<()> { pub async fn cache_launcher_version(&self, version: &str) -> Result<()> {
self.set_key(LAUNCHER_VERSION_KEY, version).await?; debug!("Caching launcher version as {version}");
self.expire_key(LAUNCHER_VERSION_KEY, 24 * 60 * 60).await?; // 1 day
let mut con = self.client.get_async_connection().await?;
con.set_ex(LAUNCHER_VERSION_KEY, version, 24 * 60 * 60)
.await?; // 1 day
Ok(()) Ok(())
} }
pub async fn get_launcher_version(&self) -> Result<String> { pub async fn get_launcher_version(&self) -> Result<String> {
let res = self.get_key(LAUNCHER_VERSION_KEY).await?; debug!("Fetching launcher version");
let mut con = self.client.get_async_connection().await?;
let res = con.get(LAUNCHER_VERSION_KEY).await?;
Ok(res) Ok(res)
} }

View file

@ -13,7 +13,7 @@ static MESSAGE_PATTERN: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"/(https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<serverId>\d+)\/(?<channelId>\d+)\/(?<messageId>\d+)/g;").unwrap() Regex::new(r"/(https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<serverId>\d+)\/(?<channelId>\d+)\/(?<messageId>\d+)/g;").unwrap()
}); });
pub fn find_first_image(msg: &Message) -> Option<String> { fn find_first_image(msg: &Message) -> Option<String> {
msg.attachments msg.attachments
.iter() .iter()
.find(|a| { .find(|a| {