This commit is contained in:
seth 2024-03-31 17:26:16 -04:00
parent 9dfc3b21ff
commit 0b0779f8b7
No known key found for this signature in database
GPG key ID: D31BD0D494BBEE86
32 changed files with 137 additions and 181 deletions

12
Cargo.lock generated
View file

@ -263,7 +263,7 @@ dependencies = [
"eyre",
"indenter",
"once_cell",
"owo-colors 3.5.0",
"owo-colors",
"tracing-error",
]
@ -274,7 +274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
dependencies = [
"once_cell",
"owo-colors 3.5.0",
"owo-colors",
"tracing-core",
"tracing-error",
]
@ -1235,12 +1235,6 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "owo-colors"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1459,7 +1453,6 @@ dependencies = [
"gray_matter",
"log",
"octocrab",
"owo-colors 4.0.0",
"poise",
"redis",
"regex",
@ -1467,7 +1460,6 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"url",
]
[[package]]

View file

@ -23,7 +23,6 @@ eyre = "0.6.11"
log = "0.4.20"
poise = "0.6.1"
octocrab = "0.37.0"
owo-colors = "4.0.0"
redis = { version = "0.25.2", features = ["tokio-comp", "tokio-rustls-comp"] }
regex = "1.10.3"
reqwest = { version = "0.12.2", default-features = false, features = [
@ -37,7 +36,17 @@ tokio = { version = "1.35.1", features = [
"rt-multi-thread",
"signal",
] }
url = { version = "2.5.0", features = ["serde"] }
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
complexity = "warn"
correctness = "deny"
pedantic = "warn"
perf = "warn"
style = "warn"
suspicious = "deny"
[profile.release]
codegen-units = 1

View file

@ -6,7 +6,7 @@ use gray_matter::{engine, Matter};
include!("src/tags.rs");
/// generate the ChoiceParameter enum and tag data we will use in the `tag` command
/// generate the `ChoiceParameter` enum and tag data we will use in the `tag` command
#[allow(dead_code)]
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();

View file

@ -1,12 +1,12 @@
use std::sync::{Arc, OnceLock};
use std::sync::OnceLock;
use eyre::{Context, OptionExt, Result};
use log::debug;
use octocrab::Octocrab;
fn octocrab() -> &'static Arc<Octocrab> {
static OCTOCRAB: OnceLock<Arc<Octocrab>> = OnceLock::new();
OCTOCRAB.get_or_init(octocrab::instance)
fn octocrab() -> &'static Octocrab {
static OCTOCRAB: OnceLock<Octocrab> = OnceLock::new();
OCTOCRAB.get_or_init(Octocrab::default)
}
pub async fn get_latest_prism_version() -> Result<String> {

View file

@ -3,6 +3,7 @@ use std::sync::OnceLock;
use eyre::Result;
use log::debug;
use reqwest::{Client, Response};
use serde::de::DeserializeOwned;
pub mod dadjoke;
pub mod github;
@ -44,3 +45,10 @@ pub async fn bytes_from_url(url: &str) -> Result<Vec<u8>> {
let bytes = resp.bytes().await?;
Ok(bytes.to_vec())
}
pub async fn json_from_url<T: DeserializeOwned>(url: &str) -> Result<T> {
let resp = get_url(url).await?;
let json = resp.json().await?;
Ok(json)
}

View file

@ -6,7 +6,7 @@ const PASTE_GG: &str = "https://api.paste.gg/v1";
const PASTES: &str = "/pastes";
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
enum Status {
pub enum Status {
#[serde(rename = "success")]
Success,
#[serde(rename = "error")]
@ -15,10 +15,10 @@ enum Status {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Response<T> {
status: Status,
pub status: Status,
pub result: Option<Vec<T>>,
error: Option<String>,
message: Option<String>,
pub error: Option<String>,
pub message: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -27,12 +27,11 @@ pub struct Files {
pub name: Option<String>,
}
pub async fn get_files(id: &str) -> Result<Response<Files>> {
pub async fn files_from(id: &str) -> Result<Response<Files>> {
let url = format!("{PASTE_GG}{PASTES}/{id}/files");
debug!("Making request to {url}");
let resp = super::client().get(url).send().await?;
resp.error_for_status_ref()?;
let resp: Response<Files> = resp.json().await?;
let resp: Response<Files> = super::json_from_url(&url).await?;
if resp.status == Status::Error {
let message = resp

View file

@ -11,17 +11,15 @@ pub struct Message {
const PLURAL_KIT: &str = "https://api.pluralkit.me/v2";
const MESSAGES: &str = "/messages";
pub async fn get_sender(message_id: MessageId) -> Result<UserId> {
pub async fn sender_from(message_id: MessageId) -> Result<UserId> {
let url = format!("{PLURAL_KIT}{MESSAGES}/{message_id}");
debug!("Making request to {url}");
let resp = super::client().get(url).send().await?;
resp.error_for_status_ref()?;
let data: Message = resp.json().await?;
let resp: Message = super::json_from_url(&url).await?;
let id: u64 =
data.sender.parse().wrap_err_with(|| {
format!("Couldn't parse response from PluralKit as a UserId! Here's the response:\n{data:#?}")
resp.sender.parse().wrap_err_with(|| {
format!("Couldn't parse response from PluralKit as a UserId! Here's the response:\n{resp:#?}")
})?;
let sender = UserId::from(id);

View file

@ -14,7 +14,7 @@ pub struct MinecraftPackageJson {
const META: &str = "https://meta.prismlauncher.org/v1";
const MINECRAFT_PACKAGEJSON: &str = "/net.minecraft/package.json";
pub async fn get_latest_minecraft_version() -> Result<String> {
pub async fn latest_minecraft_version() -> Result<String> {
let url = format!("{META}{MINECRAFT_PACKAGEJSON}");
debug!("Making request to {url}");

View file

@ -1,6 +1,5 @@
use crate::Context;
use crate::{Context, Error};
use eyre::Result;
use log::trace;
use poise::samples::HelpConfiguration;
@ -9,7 +8,7 @@ use poise::samples::HelpConfiguration;
pub async fn help(
ctx: Context<'_>,
#[description = "Provide information about a specific command"] command: Option<String>,
) -> Result<()> {
) -> Result<(), Error> {
trace!("Running help command");
let configuration = HelpConfiguration {

View file

@ -1,12 +1,11 @@
use crate::api::dadjoke;
use crate::Context;
use crate::{api::dadjoke, Context, Error};
use eyre::Result;
use log::trace;
/// It's a joke
#[poise::command(slash_command, prefix_command, track_edits = true)]
pub async fn joke(ctx: Context<'_>) -> Result<()> {
pub async fn joke(ctx: Context<'_>) -> Result<(), Error> {
trace!("Running joke command");
ctx.defer().await?;

View file

@ -1,13 +1,13 @@
use crate::{consts::Colors, Context};
use crate::{consts::Colors, Context, Error};
use eyre::{eyre, Context as _, OptionExt, Result};
use eyre::{eyre, Context as _, OptionExt};
use log::trace;
use poise::serenity_prelude::CreateEmbed;
use poise::CreateReply;
/// Returns the number of members in the server
#[poise::command(slash_command, prefix_command, guild_only = true, track_edits = true)]
pub async fn members(ctx: Context<'_>) -> Result<()> {
pub async fn members(ctx: Context<'_>) -> Result<(), Error> {
trace!("Running members command");
ctx.defer().await?;
@ -29,7 +29,7 @@ pub async fn members(ctx: Context<'_>) -> Result<()> {
let embed = CreateEmbed::new()
.title(format!("{member_count} total members!",))
.description(format!("{online_count} online members",))
.color(Colors::BLUE);
.color(Colors::Blue);
let reply = CreateReply::default().embed(embed);
ctx.send(reply).await?;

View file

@ -1,11 +1,10 @@
use crate::Context;
use crate::{Context, Error};
use eyre::Result;
use log::trace;
/// Replies with pong!
#[poise::command(slash_command, prefix_command, track_edits = true, ephemeral)]
pub async fn ping(ctx: Context<'_>) -> Result<()> {
pub async fn ping(ctx: Context<'_>) -> Result<(), Error> {
trace!("Running ping command!");
ctx.say("Pong!").await?;
Ok(())

View file

@ -1,7 +1,5 @@
use crate::api::rory;
use crate::Context;
use crate::{api::rory, Context, Error};
use eyre::Result;
use log::trace;
use poise::serenity_prelude::{CreateEmbed, CreateEmbedFooter};
use poise::CreateReply;
@ -11,7 +9,7 @@ use poise::CreateReply;
pub async fn rory(
ctx: Context<'_>,
#[description = "specify a Rory ID"] id: Option<u64>,
) -> Result<()> {
) -> Result<(), Error> {
trace!("Running rory command");
ctx.defer().await?;

View file

@ -1,6 +1,5 @@
use crate::{utils, Context};
use crate::{utils, Context, Error};
use eyre::Result;
use log::trace;
use poise::serenity_prelude::{CreateEmbed, CreateMessage};
@ -15,18 +14,12 @@ use poise::serenity_prelude::{CreateEmbed, CreateMessage};
pub async fn say(
ctx: Context<'_>,
#[description = "the message content"] content: String,
) -> Result<()> {
) -> Result<(), Error> {
let channel = ctx.channel_id();
channel.say(ctx, &content).await?;
ctx.say("I said what you said!").await?;
if let Some(channel_id) = ctx
.data()
.config
.discord_config()
.channels()
.log_channel_id()
{
if let Some(channel_id) = ctx.data().config.discord.channels.log_channel_id {
let author = utils::embed_author_from_user(ctx.author());
let embed = CreateEmbed::default()

View file

@ -1,13 +1,12 @@
use crate::{api, consts::Colors, Context};
use crate::{api, consts::Colors, Context, Error};
use eyre::Result;
use log::trace;
use poise::serenity_prelude::CreateEmbed;
use poise::CreateReply;
/// Returns GitHub stargazer count
#[poise::command(slash_command, prefix_command, track_edits = true)]
pub async fn stars(ctx: Context<'_>) -> Result<()> {
pub async fn stars(ctx: Context<'_>) -> Result<(), Error> {
trace!("Running stars command");
ctx.defer().await?;
@ -27,7 +26,7 @@ pub async fn stars(ctx: Context<'_>) -> Result<()> {
let embed = CreateEmbed::new()
.title(format!("{count} total stars!"))
.color(Colors::YELLOW);
.color(Colors::Yellow);
let reply = CreateReply::default().embed(embed);
ctx.send(reply).await?;

View file

@ -1,10 +1,10 @@
#![allow(non_camel_case_types, clippy::upper_case_acronyms)]
use crate::{consts::Colors, tags::Tag, Context};
use crate::{consts::Colors, tags::Tag, Context, Error};
use std::env;
use std::str::FromStr;
use std::sync::OnceLock;
use eyre::{eyre, Result};
use eyre::eyre;
use log::trace;
use poise::serenity_prelude::{Color, CreateEmbed, User};
use poise::CreateReply;
@ -26,7 +26,7 @@ pub async fn tag(
ctx: Context<'_>,
#[description = "the tag to send"] name: Choice,
#[description = "a user to mention"] user: Option<User>,
) -> Result<()> {
) -> Result<(), Error> {
trace!("Running tag command");
let tag_id = name.as_str();

View file

@ -1,6 +1,4 @@
use crate::Data;
use eyre::Report;
use crate::{Data, Error};
mod general;
mod moderation;
@ -32,7 +30,7 @@ macro_rules! module_macro {
module_macro!(general);
module_macro!(moderation);
pub type Command = poise::Command<Data, Report>;
pub type Command = poise::Command<Data, Error>;
pub fn get() -> Vec<Command> {
vec![

View file

@ -1,24 +1,23 @@
use std::{fmt::Write, str::FromStr};
use crate::{api, utils, Context};
use crate::{api, utils, Context, Error};
use eyre::{bail, Result};
use eyre::Result;
use log::trace;
use poise::serenity_prelude::{
futures::TryStreamExt, Attachment, CreateActionRow, CreateButton, CreateEmbed, CreateMessage,
Mentionable, Message, ReactionType,
};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct WelcomeEmbed {
title: String,
description: Option<String>,
url: Option<Url>,
url: Option<String>,
hex_color: Option<String>,
image: Option<Url>,
image: Option<String>,
}
impl From<WelcomeEmbed> for CreateMessage {
@ -112,11 +111,11 @@ pub async fn set_welcome(
ctx: Context<'_>,
#[description = "A file to use"] file: Option<Attachment>,
#[description = "A URL for a file to use"] url: Option<String>,
) -> Result<()> {
) -> Result<(), Error> {
trace!("Running set_welcome command!");
let configured_channels = ctx.data().config.discord_config().channels();
let Some(channel_id) = configured_channels.welcome_channel_id() else {
let configured_channels = ctx.data().config.discord.channels;
let Some(channel_id) = configured_channels.welcome_channel_id else {
ctx.say("You don't have a welcome channel ID set, so I can't do anything :(")
.await?;
return Ok(());
@ -127,7 +126,7 @@ pub async fn set_welcome(
// download attachment from discord or URL
let file = if let Some(attachment) = file {
let Some(content_type) = &attachment.content_type else {
bail!("Welcome channel attachment was sent without a content type!");
return Err("Welcome channel attachment was sent without a content type!".into());
};
if !content_type.starts_with("application/json;") {
@ -139,11 +138,6 @@ pub async fn set_welcome(
let downloaded = attachment.download().await?;
String::from_utf8(downloaded)?
} else if let Some(url) = url {
if Url::parse(&url).is_err() {
ctx.say("Invalid url!").await?;
return Ok(());
}
api::text_from_url(&url).await?
} else {
ctx.say("A text file or URL must be provided!").await?;
@ -180,7 +174,7 @@ pub async fn set_welcome(
channel_id.say(ctx, message).await?;
}
if let Some(log_channel) = configured_channels.log_channel_id() {
if let Some(log_channel) = configured_channels.log_channel_id {
let author = utils::embed_author_from_user(ctx.author());
let embed = CreateEmbed::new()
.title("set_welcome command used!")

View file

@ -2,7 +2,7 @@ use log::{info, warn};
#[derive(Clone, Debug, Default)]
pub struct Config {
redis_url: Option<String>,
pub redis_url: Option<String>,
}
impl Config {
@ -21,8 +21,4 @@ impl Config {
Self::new(redis_url)
}
pub fn redis_url(&self) -> Option<&str> {
self.redis_url.as_deref()
}
}

View file

@ -5,13 +5,13 @@ use poise::serenity_prelude::ChannelId;
#[derive(Clone, Copy, Debug, Default)]
pub struct RefractionChannels {
log_channel_id: Option<ChannelId>,
welcome_channel_id: Option<ChannelId>,
pub log_channel_id: Option<ChannelId>,
pub welcome_channel_id: Option<ChannelId>,
}
#[derive(Clone, Debug, Default)]
pub struct Config {
channels: RefractionChannels,
pub channels: RefractionChannels,
}
impl RefractionChannels {
@ -45,14 +45,6 @@ impl RefractionChannels {
.ok()
.and_then(|env_var| ChannelId::from_str(&env_var).ok())
}
pub fn log_channel_id(&self) -> Option<&ChannelId> {
self.log_channel_id.as_ref()
}
pub fn welcome_channel_id(&self) -> Option<&ChannelId> {
self.welcome_channel_id.as_ref()
}
}
impl Config {
@ -65,8 +57,4 @@ impl Config {
Self::new(channels)
}
pub fn channels(&self) -> &RefractionChannels {
&self.channels
}
}

View file

@ -3,8 +3,8 @@ mod discord;
#[derive(Debug, Clone, Default)]
pub struct Config {
bot: bot::Config,
discord: discord::Config,
pub bot: bot::Config,
pub discord: discord::Config,
}
impl Config {
@ -21,12 +21,4 @@ impl Config {
Self::new(bot, discord)
}
pub fn bot_config(&self) -> &bot::Config {
&self.bot
}
pub fn discord_config(&self) -> &discord::Config {
&self.discord
}
}

View file

@ -3,18 +3,31 @@ use std::str::FromStr;
use poise::serenity_prelude::Colour;
const BLUE: u32 = 0x60A5FA;
const GREEN: u32 = 0x22C55E;
const ORANGE: u32 = 0xFB923C;
const RED: u32 = 0xEF4444;
const YELLOW: u32 = 0xFDE047;
#[derive(Clone, Copy, Debug, Default)]
pub struct Colors(i32);
pub enum Colors {
Blue,
#[default]
Green,
Orange,
Red,
Yellow,
}
impl Colors {
pub const RED: i32 = 0xEF4444;
pub const GREEN: i32 = 0x22C55E;
pub const BLUE: i32 = 0x60A5FA;
pub const YELLOW: i32 = 0xFDE047;
pub const ORANGE: i32 = 0xFB923C;
pub fn as_i32(self) -> i32 {
self.0
impl From<Colors> for Colour {
fn from(value: Colors) -> Self {
Self::from(match &value {
Colors::Blue => BLUE,
Colors::Green => GREEN,
Colors::Orange => ORANGE,
Colors::Red => RED,
Colors::Yellow => YELLOW,
})
}
}
@ -22,18 +35,12 @@ impl FromStr for Colors {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"red" => Ok(Colors(Self::RED)),
"green" => Ok(Colors(Self::GREEN)),
"blue" => Ok(Colors(Self::BLUE)),
"yellow" => Ok(Colors(Self::YELLOW)),
"orange" => Ok(Colors(Self::ORANGE)),
"blue" => Ok(Self::Blue),
"green" => Ok(Self::Green),
"orange" => Ok(Self::Orange),
"red" => Ok(Self::Red),
"yellow" => Ok(Self::Yellow),
_ => Err(()),
}
}
}
impl From<Colors> for Colour {
fn from(value: Colors) -> Self {
Self::from(value.as_i32())
}
}

View file

@ -1,8 +1,7 @@
use crate::{consts::Colors, Data};
use crate::{consts::Colors, Data, Error};
use std::fmt::Write;
use eyre::Report;
use log::error;
use poise::serenity_prelude::{CreateEmbed, Timestamp};
use poise::{CreateReply, FrameworkError};
@ -16,7 +15,7 @@ macro_rules! writelne {
}
}
pub async fn handle(error: FrameworkError<'_, Data, Report>) {
pub async fn handle(error: FrameworkError<'_, Data, Error>) {
match error {
FrameworkError::Setup {
error, framework, ..
@ -34,7 +33,7 @@ pub async fn handle(error: FrameworkError<'_, Data, Report>) {
.title("Something went wrong!")
.description("oopsie")
.timestamp(Timestamp::now())
.color(Colors::RED);
.color(Colors::Red);
let reply = CreateReply::default().embed(embed);

View file

@ -198,7 +198,9 @@ async fn outdated_launcher(log: &str, data: &Data) -> Result<Issue> {
if let Ok(version) = storage.launcher_version().await {
version
} else {
api::github::get_latest_prism_version().await?
let version = api::github::get_latest_prism_version().await?;
storage.cache_launcher_version(&version).await?;
version
}
} else {
trace!("Not caching launcher version, as we're running without a storage backend");

View file

@ -48,10 +48,10 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
if issues.is_empty() {
e = e
.color(Colors::GREEN)
.color(Colors::Green)
.description("No issues found automatically");
} else {
e = e.color(Colors::RED);
e = e.color(Colors::Red);
for (title, description) in issues {
e = e.field(title, description, false);

View file

@ -19,7 +19,7 @@ impl super::LogProvider for PasteGG {
}
async fn fetch(&self, content: &str) -> Result<String> {
let files = paste_gg::get_files(content).await?;
let files = paste_gg::files_from(content).await?;
let result = files
.result
.ok_or_eyre("Got an empty result from paste.gg!")?;

View file

@ -4,7 +4,7 @@ use poise::serenity_prelude::{Context, CreateAllowedMentions, CreateMessage, Mes
use crate::utils;
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
let embeds = utils::resolve_message::from_message(ctx, message).await?;
let embeds = utils::messages::from_message(ctx, message).await?;
if !embeds.is_empty() {
let allowed_mentions = CreateAllowedMentions::new().replied_user(false);

View file

@ -1,6 +1,5 @@
use crate::{api, Data};
use crate::{api, Data, Error};
use eyre::{Report, Result};
use log::{debug, info, trace};
use poise::serenity_prelude::{ActivityData, Context, FullEvent, OnlineStatus};
use poise::FrameworkContext;
@ -10,20 +9,20 @@ mod delete_on_reaction;
mod eta;
mod expand_link;
mod give_role;
pub mod pluralkit;
mod pluralkit;
mod support_onboard;
pub async fn handle(
ctx: &Context,
event: &FullEvent,
_: FrameworkContext<'_, Data, Report>,
_: FrameworkContext<'_, Data, Error>,
data: &Data,
) -> Result<()> {
) -> Result<(), Error> {
match event {
FullEvent::Ready { data_about_bot } => {
info!("Logged in as {}!", data_about_bot.user.name);
let latest_minecraft_version = api::prism_meta::get_latest_minecraft_version().await?;
let latest_minecraft_version = api::prism_meta::latest_minecraft_version().await?;
let activity = ActivityData::playing(format!("Minecraft {latest_minecraft_version}"));
info!("Setting presence to activity {activity:#?}");

View file

@ -15,7 +15,7 @@ pub async fn is_message_proxied(message: &Message) -> Result<bool> {
);
sleep(PK_DELAY).await;
let proxied = api::pluralkit::get_sender(message.id).await.is_ok();
let proxied = api::pluralkit::sender_from(message.id).await.is_ok();
Ok(proxied)
}
@ -36,7 +36,7 @@ pub async fn handle(_: &Context, msg: &Message, storage: &Storage) -> Result<()>
);
sleep(PK_DELAY).await;
if let Ok(sender) = api::pluralkit::get_sender(msg.id).await {
if let Ok(sender) = api::pluralkit::sender_from(msg.id).await {
storage.store_user_plurality(sender).await?;
}

View file

@ -1,18 +1,10 @@
#![warn(clippy::all, clippy::pedantic, clippy::perf)]
#![allow(clippy::missing_errors_doc)]
#![forbid(unsafe_code)]
use std::{sync::Arc, time::Duration};
use eyre::{bail, Context as _, Report, Result};
use eyre::Context as _;
use log::{info, trace, warn};
use poise::{
serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
};
use owo_colors::OwoColorize;
use tokio::signal::ctrl_c;
#[cfg(target_family = "unix")]
use tokio::signal::unix::{signal, SignalKind};
@ -31,7 +23,8 @@ mod utils;
use config::Config;
use storage::Storage;
type Context<'a> = poise::Context<'a, Data, Report>;
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;
#[derive(Clone, Debug, Default)]
pub struct Data {
@ -39,21 +32,14 @@ pub struct Data {
storage: Option<Storage>,
}
impl Data {
#[must_use]
pub fn new(config: Config, storage: Option<Storage>) -> Self {
Self { config, storage }
}
}
async fn setup(
ctx: &serenity::Context,
_: &serenity::Ready,
framework: &Framework<Data, Report>,
) -> Result<Data> {
framework: &Framework<Data, Error>,
) -> Result<Data, Error> {
let config = Config::new_from_env();
let storage = if let Some(url) = config.bot_config().redis_url() {
let storage = if let Some(url) = &config.bot.redis_url {
Some(Storage::from_url(url)?)
} else {
None
@ -61,13 +47,15 @@ async fn setup(
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?")
return Err(
"You specified a storage backend but there's no connection! Is it running?".into(),
);
}
trace!("Redis connection looks good!");
}
let data = Data::new(config, storage);
let data = Data { config, storage };
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
info!("Registered global commands!");
@ -78,11 +66,11 @@ async fn setup(
async fn handle_shutdown(shard_manager: Arc<serenity::ShardManager>, reason: &str) {
warn!("{reason}! Shutting down bot...");
shard_manager.shutdown_all().await;
println!("{}", "Everything is shutdown. Goodbye!".green());
println!("Everything is shutdown. Goodbye!");
}
#[tokio::main]
async fn main() -> Result<()> {
async fn main() -> eyre::Result<()> {
dotenvy::dotenv().ok();
color_eyre::install()?;
env_logger::init();
@ -134,7 +122,7 @@ async fn main() -> Result<()> {
let mut sigterm = ctrl_close()?;
tokio::select! {
result = client.start() => result.map_err(Report::from),
result = client.start() => result.map_err(eyre::Report::from),
_ = sigterm.recv() => {
handle_shutdown(shard_manager, "Received SIGTERM").await;
std::process::exit(0);

View file

@ -24,7 +24,7 @@ fn find_first_image(message: &Message) -> Option<String> {
}
async fn find_real_author_id(message: &Message) -> UserId {
if let Ok(sender) = pluralkit::get_sender(message.id).await {
if let Ok(sender) = pluralkit::sender_from(message.id).await {
sender
} else {
message.author.id

View file

@ -1,6 +1,6 @@
use poise::serenity_prelude::{CreateEmbedAuthor, User};
pub mod resolve_message;
pub mod messages;
pub fn embed_author_from_user(user: &User) -> CreateEmbedAuthor {
CreateEmbedAuthor::new(user.tag()).icon_url(