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
|
|
9
.envrc
9
.envrc
|
@ -1,10 +1,5 @@
|
||||||
# only use flake when `nix` is present
|
if has nix_direnv_version; then
|
||||||
if command -v nix &> /dev/null; then
|
use flake
|
||||||
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
|
|
||||||
|
|
||||||
use flake
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dotenv_if_exists
|
dotenv_if_exists
|
||||||
|
|
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;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
pub mod dadjoke;
|
pub mod dadjoke;
|
||||||
|
pub mod github;
|
||||||
pub mod paste_gg;
|
pub mod paste_gg;
|
||||||
pub mod pluralkit;
|
pub mod pluralkit;
|
||||||
pub mod prism_meta;
|
pub mod prism_meta;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
use eyre::{OptionExt, Result};
|
use eyre::{OptionExt, Result};
|
||||||
|
use log::trace;
|
||||||
use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateMessage};
|
use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateMessage};
|
||||||
|
|
||||||
/// Say something through the bot
|
/// Say something through the bot
|
||||||
|
@ -34,7 +35,14 @@ pub async fn say(
|
||||||
ctx.say("I said what you said!").await?;
|
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
|
let log_channel = guild
|
||||||
.channels
|
.channels
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -54,6 +62,8 @@ pub async fn say(
|
||||||
|
|
||||||
let message = CreateMessage::new().embed(embed);
|
let message = CreateMessage::new().embed(embed);
|
||||||
log_channel.1.send_message(ctx, message).await?;
|
log_channel.1.send_message(ctx, message).await?;
|
||||||
|
} else {
|
||||||
|
trace!("Not sending /say log as no channel is set");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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 log::trace;
|
||||||
use poise::serenity_prelude::CreateEmbed;
|
use poise::serenity_prelude::CreateEmbed;
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
|
@ -12,18 +12,17 @@ pub async fn stars(ctx: Context<'_>) -> Result<()> {
|
||||||
|
|
||||||
ctx.defer().await?;
|
ctx.defer().await?;
|
||||||
|
|
||||||
let prismlauncher = ctx
|
let count = if let Some(storage) = &ctx.data().storage {
|
||||||
.data()
|
if let Ok(count) = storage.get_launcher_stargazer_count().await {
|
||||||
.octocrab
|
count
|
||||||
.repos("PrismLauncher", "PrismLauncher")
|
} else {
|
||||||
.get()
|
let count = api::github::get_prism_stargazers_count().await?;
|
||||||
.await
|
storage.cache_launcher_stargazer_count(count).await?;
|
||||||
.wrap_err("Couldn't get PrismLauncher/PrismLauncher from GitHub!")?;
|
count
|
||||||
|
}
|
||||||
let count = if let Some(count) = prismlauncher.stargazers_count {
|
|
||||||
count.to_string()
|
|
||||||
} else {
|
} else {
|
||||||
"undefined".to_string()
|
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()
|
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>,
|
say_log_channel_id: Option<ChannelId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
channels: RefractionChannels,
|
channels: RefractionChannels,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
|
mod bot;
|
||||||
mod discord;
|
mod discord;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub discord: discord::Config,
|
bot: bot::Config,
|
||||||
pub redis_url: String,
|
discord: discord::Config,
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
discord: discord::Config::default(),
|
|
||||||
redis_url: "redis://localhost:6379".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new_from_env() -> Self {
|
pub fn new(bot_config: bot::Config, discord_config: discord::Config) -> Self {
|
||||||
let discord = discord::Config::new_from_env();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
discord,
|
bot: bot_config,
|
||||||
..Default::default()
|
discord: discord_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_env() -> Self {
|
||||||
|
let bot = bot::Config::new_from_env();
|
||||||
|
let discord = discord::Config::new_from_env();
|
||||||
|
|
||||||
|
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 eyre::Result;
|
||||||
use log::trace;
|
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 version_from_log = captures[0].replace("Prism Launcher version: ", "");
|
||||||
|
|
||||||
let storage = &data.storage;
|
let latest_version = if let Some(storage) = &data.storage {
|
||||||
let latest_version = if let Ok(version) = storage.get_launcher_version().await {
|
if let Ok(version) = storage.get_launcher_version().await {
|
||||||
version
|
version
|
||||||
|
} else {
|
||||||
|
api::github::get_latest_prism_version().await?
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let version = data
|
trace!("Not caching launcher version, as we're running without a storage backend");
|
||||||
.octocrab
|
api::github::get_latest_prism_version().await?
|
||||||
.repos("PrismLauncher", "PrismLauncher")
|
|
||||||
.releases()
|
|
||||||
.get_latest()
|
|
||||||
.await?
|
|
||||||
.tag_name;
|
|
||||||
|
|
||||||
storage.cache_launcher_version(&version).await?;
|
|
||||||
version
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if version_from_log < latest_version {
|
if version_from_log < latest_version {
|
||||||
|
|
|
@ -41,14 +41,16 @@ pub async fn handle(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect PK users first to make sure we don't respond to unproxied messages
|
if let Some(storage) = &data.storage {
|
||||||
pluralkit::handle(ctx, new_message, data).await?;
|
// detect PK users first to make sure we don't respond to unproxied messages
|
||||||
|
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?
|
&& pluralkit::is_message_proxied(new_message).await?
|
||||||
{
|
{
|
||||||
debug!("Not replying to unproxied PluralKit message");
|
debug!("Not replying to unproxied PluralKit message");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eta::handle(ctx, new_message).await?;
|
eta::handle(ctx, new_message).await?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{api, Data};
|
use crate::{api, storage::Storage};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
@ -20,7 +20,7 @@ pub async fn is_message_proxied(message: &Message) -> Result<bool> {
|
||||||
Ok(proxied)
|
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() {
|
if msg.webhook_id.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ pub async fn handle(_: &Context, msg: &Message, data: &Data) -> Result<()> {
|
||||||
sleep(PK_DELAY).await;
|
sleep(PK_DELAY).await;
|
||||||
|
|
||||||
if let Ok(sender) = api::pluralkit::get_sender(msg.id).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(())
|
Ok(())
|
||||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -4,16 +4,14 @@
|
||||||
|
|
||||||
use std::{sync::Arc, time::Duration};
|
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 log::{info, trace, warn};
|
||||||
|
|
||||||
use octocrab::Octocrab;
|
|
||||||
use poise::{
|
use poise::{
|
||||||
serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
|
serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use redis::ConnectionLike;
|
|
||||||
|
|
||||||
use tokio::signal::ctrl_c;
|
use tokio::signal::ctrl_c;
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
|
@ -38,17 +36,13 @@ type Context<'a> = poise::Context<'a, Data, Report>;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
config: Config,
|
config: Config,
|
||||||
storage: Storage,
|
storage: Option<Storage>,
|
||||||
octocrab: Arc<octocrab::Octocrab>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
pub fn new(config: Config, storage: Storage, octocrab: Arc<Octocrab>) -> Result<Self> {
|
#[must_use]
|
||||||
Ok(Self {
|
pub fn new(config: Config, storage: Option<Storage>) -> Self {
|
||||||
config,
|
Self { config, storage }
|
||||||
storage,
|
|
||||||
octocrab,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,19 +52,22 @@ async fn setup(
|
||||||
framework: &Framework<Data, Report>,
|
framework: &Framework<Data, Report>,
|
||||||
) -> Result<Data> {
|
) -> Result<Data> {
|
||||||
let config = Config::new_from_env();
|
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 storage = if let Some(url) = &config.clone().bot_config().redis_url() {
|
||||||
let mut client = data.storage.client().clone();
|
Some(Storage::from_url(url)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
if !client.check_connection() {
|
if let Some(storage) = storage.as_ref() {
|
||||||
return Err(eyre!(
|
if !storage.clone().has_connection() {
|
||||||
"Couldn't connect to storage! Is your daemon running?"
|
bail!("You specified a storage backend but there's no connection! Is it running?")
|
||||||
));
|
}
|
||||||
|
|
||||||
|
trace!("Redis connection looks good!");
|
||||||
}
|
}
|
||||||
trace!("Redis connection looks good!");
|
|
||||||
|
let data = Data::new(config, storage);
|
||||||
|
|
||||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||||
info!("Registered global commands!");
|
info!("Registered global commands!");
|
||||||
|
|
|
@ -3,10 +3,11 @@ use std::fmt::Debug;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use poise::serenity_prelude::UserId;
|
use poise::serenity_prelude::UserId;
|
||||||
use redis::{AsyncCommands, Client};
|
use redis::{AsyncCommands, Client, ConnectionLike};
|
||||||
|
|
||||||
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";
|
||||||
|
const LAUNCHER_STARGAZER_KEY: &str = "launcher-stargazer-v1";
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Storage {
|
pub struct Storage {
|
||||||
|
@ -24,8 +25,8 @@ impl Storage {
|
||||||
Ok(Self::new(client))
|
Ok(Self::new(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn client(&self) -> &Client {
|
pub fn has_connection(mut self) -> bool {
|
||||||
&self.client
|
self.client.check_connection()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> {
|
pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> {
|
||||||
|
@ -67,4 +68,23 @@ impl Storage {
|
||||||
|
|
||||||
Ok(res)
|
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