treewide: allow for running w/o storage

This commit is contained in:
seth 2024-03-27 18:55:31 -04:00
parent 827b5a4bd7
commit a9a63f36ad
14 changed files with 174 additions and 90 deletions

View file

@ -1,3 +0,0 @@
DISCORD_TOKEN=
SAY_LOGS_CHANNEL=
RUST_LOG=refraction=info,warn,error

7
.envrc
View file

@ -1,9 +1,4 @@
# only use flake when `nix` is present
if command -v nix &> /dev/null; then
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
fi
if has nix_direnv_version; then
use flake
fi

35
src/api/github.rs Normal file
View file

@ -0,0 +1,35 @@
use std::sync::Arc;
use eyre::{Context, OptionExt, Result};
use log::debug;
use octocrab::Octocrab;
use once_cell::sync::Lazy;
static OCTOCRAB: Lazy<Arc<Octocrab>> = Lazy::new(octocrab::instance);
pub async fn get_latest_prism_version() -> Result<String> {
debug!("Fetching the latest version of Prism Launcher");
let version = OCTOCRAB
.repos("PrismLauncher", "PrismLauncher")
.releases()
.get_latest()
.await?
.tag_name;
Ok(version)
}
pub async fn get_prism_stargazers_count() -> Result<u32> {
debug!("Fetching Prism Launcher's stargazer count");
let stargazers_count = OCTOCRAB
.repos("PrismLauncher", "PrismLauncher")
.get()
.await
.wrap_err("Couldn't fetch PrismLauncher/PrismLauncher!")?
.stargazers_count
.ok_or_eyre("Couldn't retrieve stargazers_coutn from GitHub!")?;
Ok(stargazers_count)
}

View file

@ -1,6 +1,7 @@
use once_cell::sync::Lazy;
pub mod dadjoke;
pub mod github;
pub mod paste_gg;
pub mod pluralkit;
pub mod prism_meta;

View file

@ -1,6 +1,7 @@
use crate::Context;
use eyre::{OptionExt, Result};
use log::trace;
use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateMessage};
/// Say something through the bot
@ -34,7 +35,14 @@ pub async fn say(
ctx.say("I said what you said!").await?;
}
if let Some(channel_id) = ctx.data().config.discord.channels().say_log_channel_id() {
if let Some(channel_id) = ctx
.data()
.config
.clone()
.discord_config()
.channels()
.say_log_channel_id()
{
let log_channel = guild
.channels
.iter()
@ -54,6 +62,8 @@ pub async fn say(
let message = CreateMessage::new().embed(embed);
log_channel.1.send_message(ctx, message).await?;
} else {
trace!("Not sending /say log as no channel is set");
}
Ok(())

View file

@ -1,6 +1,6 @@
use crate::{consts, Context};
use crate::{api, consts, Context};
use eyre::{Context as _, Result};
use eyre::Result;
use log::trace;
use poise::serenity_prelude::CreateEmbed;
use poise::CreateReply;
@ -12,18 +12,17 @@ pub async fn stars(ctx: Context<'_>) -> Result<()> {
ctx.defer().await?;
let prismlauncher = ctx
.data()
.octocrab
.repos("PrismLauncher", "PrismLauncher")
.get()
.await
.wrap_err("Couldn't get PrismLauncher/PrismLauncher from GitHub!")?;
let count = if let Some(count) = prismlauncher.stargazers_count {
count.to_string()
let count = if let Some(storage) = &ctx.data().storage {
if let Ok(count) = storage.get_launcher_stargazer_count().await {
count
} else {
"undefined".to_string()
let count = api::github::get_prism_stargazers_count().await?;
storage.cache_launcher_stargazer_count(count).await?;
count
}
} else {
trace!("Not caching launcher stargazer count, as we're running without a storage backend");
api::github::get_prism_stargazers_count().await?
};
let embed = CreateEmbed::new()

28
src/config/bot.rs Normal file
View file

@ -0,0 +1,28 @@
use log::{info, warn};
#[derive(Clone, Debug, Default)]
pub struct Config {
redis_url: Option<String>,
}
impl Config {
pub fn new(redis_url: Option<String>) -> Self {
Self { redis_url }
}
pub fn new_from_env() -> Self {
let redis_url = std::env::var("BOT_REDIS_URL").ok();
if let Some(url) = &redis_url {
info!("Redis URL is {url}");
} else {
warn!("BOT_REDIS_URL is empty; features requiring storage will be disabled.");
}
Self::new(redis_url)
}
pub fn redis_url(self) -> Option<String> {
self.redis_url
}
}

View file

@ -8,7 +8,7 @@ pub struct RefractionChannels {
say_log_channel_id: Option<ChannelId>,
}
#[derive(Clone, Copy, Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct Config {
channels: RefractionChannels,
}

View file

@ -1,27 +1,32 @@
mod bot;
mod discord;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct Config {
pub discord: discord::Config,
pub redis_url: String,
}
impl Default for Config {
fn default() -> Self {
Self {
discord: discord::Config::default(),
redis_url: "redis://localhost:6379".to_string(),
}
}
bot: bot::Config,
discord: discord::Config,
}
impl Config {
pub fn new(bot_config: bot::Config, discord_config: discord::Config) -> Self {
Self {
bot: bot_config,
discord: discord_config,
}
}
pub fn new_from_env() -> Self {
let bot = bot::Config::new_from_env();
let discord = discord::Config::new_from_env();
Self {
discord,
..Default::default()
Self::new(bot, discord)
}
pub fn bot_config(self) -> bot::Config {
self.bot
}
pub fn discord_config(self) -> discord::Config {
self.discord
}
}

View file

@ -1,4 +1,4 @@
use crate::Data;
use crate::{api, Data};
use eyre::Result;
use log::trace;
@ -189,20 +189,15 @@ async fn outdated_launcher(log: &str, data: &Data) -> Result<Issue> {
let version_from_log = captures[0].replace("Prism Launcher version: ", "");
let storage = &data.storage;
let latest_version = if let Ok(version) = storage.get_launcher_version().await {
let latest_version = if let Some(storage) = &data.storage {
if let Ok(version) = storage.get_launcher_version().await {
version
} else {
let version = data
.octocrab
.repos("PrismLauncher", "PrismLauncher")
.releases()
.get_latest()
.await?
.tag_name;
storage.cache_launcher_version(&version).await?;
version
api::github::get_latest_prism_version().await?
}
} else {
trace!("Not caching launcher version, as we're running without a storage backend");
api::github::get_latest_prism_version().await?
};
if version_from_log < latest_version {

View file

@ -41,15 +41,17 @@ pub async fn handle(
return Ok(());
}
if let Some(storage) = &data.storage {
// detect PK users first to make sure we don't respond to unproxied messages
pluralkit::handle(ctx, new_message, data).await?;
pluralkit::handle(ctx, new_message, storage).await?;
if data.storage.is_user_plural(new_message.author.id).await?
if storage.is_user_plural(new_message.author.id).await?
&& pluralkit::is_message_proxied(new_message).await?
{
debug!("Not replying to unproxied PluralKit message");
return Ok(());
}
}
eta::handle(ctx, new_message).await?;
expand_link::handle(ctx, new_message).await?;

View file

@ -1,4 +1,4 @@
use crate::{api, Data};
use crate::{api, storage::Storage};
use std::time::Duration;
use eyre::Result;
@ -20,7 +20,7 @@ pub async fn is_message_proxied(message: &Message) -> Result<bool> {
Ok(proxied)
}
pub async fn handle(_: &Context, msg: &Message, data: &Data) -> Result<()> {
pub async fn handle(_: &Context, msg: &Message, storage: &Storage) -> Result<()> {
if msg.webhook_id.is_none() {
return Ok(());
}
@ -37,7 +37,7 @@ pub async fn handle(_: &Context, msg: &Message, data: &Data) -> Result<()> {
sleep(PK_DELAY).await;
if let Ok(sender) = api::pluralkit::get_sender(msg.id).await {
data.storage.store_user_plurality(sender).await?;
storage.store_user_plurality(sender).await?;
}
Ok(())

View file

@ -4,16 +4,14 @@
use std::{sync::Arc, time::Duration};
use eyre::{eyre, Context as _, Report, Result};
use eyre::{bail, Context as _, Report, Result};
use log::{info, trace, warn};
use octocrab::Octocrab;
use poise::{
serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
};
use owo_colors::OwoColorize;
use redis::ConnectionLike;
use tokio::signal::ctrl_c;
#[cfg(target_family = "unix")]
@ -38,17 +36,13 @@ type Context<'a> = poise::Context<'a, Data, Report>;
#[derive(Clone)]
pub struct Data {
config: Config,
storage: Storage,
octocrab: Arc<octocrab::Octocrab>,
storage: Option<Storage>,
}
impl Data {
pub fn new(config: Config, storage: Storage, octocrab: Arc<Octocrab>) -> Result<Self> {
Ok(Self {
config,
storage,
octocrab,
})
#[must_use]
pub fn new(config: Config, storage: Option<Storage>) -> Self {
Self { config, storage }
}
}
@ -58,19 +52,22 @@ async fn setup(
framework: &Framework<Data, Report>,
) -> Result<Data> {
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
let mut client = data.storage.client().clone();
let storage = if let Some(url) = &config.clone().bot_config().redis_url() {
Some(Storage::from_url(url)?)
} else {
None
};
if !client.check_connection() {
return Err(eyre!(
"Couldn't connect to storage! Is your daemon running?"
));
if let Some(storage) = storage.as_ref() {
if !storage.clone().has_connection() {
bail!("You specified a storage backend but there's no connection! Is it running?")
}
trace!("Redis connection looks good!");
}
let data = Data::new(config, storage);
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
info!("Registered global commands!");

View file

@ -3,10 +3,11 @@ use std::fmt::Debug;
use eyre::Result;
use log::debug;
use poise::serenity_prelude::UserId;
use redis::{AsyncCommands, Client};
use redis::{AsyncCommands, Client, ConnectionLike};
const PK_KEY: &str = "pluralkit-v1";
const LAUNCHER_VERSION_KEY: &str = "launcher-version-v1";
const LAUNCHER_STARGAZER_KEY: &str = "launcher-stargazer-v1";
#[derive(Clone, Debug)]
pub struct Storage {
@ -24,8 +25,8 @@ impl Storage {
Ok(Self::new(client))
}
pub fn client(&self) -> &Client {
&self.client
pub fn has_connection(mut self) -> bool {
self.client.check_connection()
}
pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> {
@ -67,4 +68,23 @@ impl Storage {
Ok(res)
}
pub async fn cache_launcher_stargazer_count(&self, stargazers: u32) -> Result<()> {
debug!("Caching stargazer count as {stargazers}");
let mut con = self.client.get_async_connection().await?;
con.set_ex(LAUNCHER_STARGAZER_KEY, stargazers, 60 * 60)
.await?;
Ok(())
}
pub async fn get_launcher_stargazer_count(&self) -> Result<u32> {
debug!("Fetching launcher stargazer count");
let mut con = self.client.get_async_connection().await?;
let res: u32 = con.get(LAUNCHER_STARGAZER_KEY).await?;
Ok(res)
}
}