treewide: allow for running w/o storage
This commit is contained in:
parent
827b5a4bd7
commit
a9a63f36ad
14 changed files with 174 additions and 90 deletions
|
@ -1,3 +0,0 @@
|
|||
DISCORD_TOKEN=
|
||||
SAY_LOGS_CHANNEL=
|
||||
RUST_LOG=refraction=info,warn,error
|
7
.envrc
7
.envrc
|
@ -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
35
src/api/github.rs
Normal 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)
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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
28
src/config/bot.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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(())
|
||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -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!");
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue