From f0550dd429b9fe8d25cc3b0117e27527471050f9 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 8 Jan 2024 14:56:37 -0500 Subject: [PATCH] style: use tabs over spaces --- .rustfmt.toml | 1 + build.rs | 66 ++--- src/api/dadjoke.rs | 18 +- src/api/mod.rs | 12 +- src/api/pluralkit.rs | 32 ++- src/api/prism_meta.rs | 46 +-- src/api/rory.rs | 50 ++-- src/commands/general/joke.rs | 6 +- src/commands/general/members.rs | 32 +-- src/commands/general/ping.rs | 4 +- src/commands/general/rory.rs | 34 +-- src/commands/general/say.rs | 76 ++--- src/commands/general/stars.rs | 40 +-- src/commands/general/tag.rs | 70 ++--- src/commands/mod.rs | 18 +- src/config/discord.rs | 108 +++---- src/config/github.rs | 88 +++--- src/config/mod.rs | 42 +-- src/consts.rs | 48 ++-- src/handlers/error.rs | 70 ++--- src/handlers/event/analyze_logs/issues.rs | 264 +++++++++--------- src/handlers/event/analyze_logs/mod.rs | 86 +++--- .../event/analyze_logs/providers/0x0.rs | 22 +- .../analyze_logs/providers/attachment.rs | 26 +- .../event/analyze_logs/providers/haste.rs | 26 +- .../event/analyze_logs/providers/mclogs.rs | 24 +- .../event/analyze_logs/providers/mod.rs | 32 +-- .../event/analyze_logs/providers/paste_gg.rs | 78 +++--- .../event/analyze_logs/providers/pastebin.rs | 26 +- src/handlers/event/delete_on_reaction.rs | 34 +-- src/handlers/event/eta.rs | 18 +- src/handlers/event/expand_link.rs | 36 +-- src/handlers/event/mod.rs | 76 ++--- src/handlers/event/pluralkit.rs | 44 +-- src/handlers/event/support_onboard.rs | 50 ++-- src/main.rs | 150 +++++----- src/storage/mod.rs | 142 +++++----- src/tags.rs | 14 +- src/utils/macros.rs | 6 +- src/utils/mod.rs | 10 +- src/utils/resolve_message.rs | 196 ++++++------- 41 files changed, 1112 insertions(+), 1109 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/build.rs b/build.rs index 1426363..5cd6329 100644 --- a/build.rs +++ b/build.rs @@ -8,15 +8,15 @@ include!("src/tags.rs"); /// 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(); - let generated = Path::new(&out_dir).join("generated.rs"); + let out_dir = env::var_os("OUT_DIR").unwrap(); + let generated = Path::new(&out_dir).join("generated.rs"); - let tag_files: Vec = fs::read_dir(TAG_DIR) - .unwrap() - .map(|f| f.unwrap().file_name().to_string_lossy().to_string()) - .collect(); + let tag_files: Vec = fs::read_dir(TAG_DIR) + .unwrap() + .map(|f| f.unwrap().file_name().to_string_lossy().to_string()) + .collect(); - let tags: Vec = tag_files + let tags: Vec = tag_files .clone() .into_iter() .map(|name| { @@ -45,23 +45,23 @@ fn main() { }) .collect(); - let formatted_names: Vec = tags - .iter() - .map(|t| t.file_name.replace(".md", "").replace('-', "_")) - .collect(); + let formatted_names: Vec = tags + .iter() + .map(|t| t.file_name.replace(".md", "").replace('-', "_")) + .collect(); - let tag_choice = format!( - r#" + let tag_choice = format!( + r#" #[allow(non_camel_case_types, clippy::upper_case_acronyms)] #[derive(Clone, Debug, poise::ChoiceParameter)] pub enum TagChoice {{ {} }}"#, - formatted_names.join(",\n") - ); + formatted_names.join(",\n") + ); - let to_str = format!( - r#" + let to_str = format!( + r#" impl TagChoice {{ fn as_str(&self) -> &str {{ match &self {{ @@ -70,22 +70,22 @@ fn main() { }} }} "#, - formatted_names - .iter() - .map(|n| { - let file_name = n.replace('_', "-") + ".md"; - format!("Self::{n} => \"{file_name}\",") - }) - .collect::>() - .join("\n") - ); + formatted_names + .iter() + .map(|n| { + let file_name = n.replace('_', "-") + ".md"; + format!("Self::{n} => \"{file_name}\",") + }) + .collect::>() + .join("\n") + ); - let contents = Vec::from([tag_choice, to_str]).join("\n\n"); + let contents = Vec::from([tag_choice, to_str]).join("\n\n"); - fs::write(generated, contents).unwrap(); - println!( - "cargo:rustc-env=TAGS={}", - // make sure we can deserialize with env! at runtime - serde_json::to_string(&tags).unwrap() - ); + fs::write(generated, contents).unwrap(); + println!( + "cargo:rustc-env=TAGS={}", + // make sure we can deserialize with env! at runtime + serde_json::to_string(&tags).unwrap() + ); } diff --git a/src/api/dadjoke.rs b/src/api/dadjoke.rs index 8daef2c..c0eed87 100644 --- a/src/api/dadjoke.rs +++ b/src/api/dadjoke.rs @@ -7,15 +7,15 @@ use reqwest::StatusCode; const DADJOKE: &str = "https://icanhazdadjoke.com"; pub async fn get_joke() -> Result { - let req = REQWEST_CLIENT.get(DADJOKE).build()?; + let req = REQWEST_CLIENT.get(DADJOKE).build()?; - debug!("Making request to {}", req.url()); - let resp = REQWEST_CLIENT.execute(req).await?; - let status = resp.status(); + debug!("Making request to {}", req.url()); + let resp = REQWEST_CLIENT.execute(req).await?; + let status = resp.status(); - if let StatusCode::OK = status { - Ok(resp.text().await?) - } else { - Err(eyre!("Couldn't get a joke!")) - } + if let StatusCode::OK = status { + Ok(resp.text().await?) + } else { + Err(eyre!("Couldn't get a joke!")) + } } diff --git a/src/api/mod.rs b/src/api/mod.rs index a76d5bc..5198953 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -6,14 +6,14 @@ pub mod prism_meta; pub mod rory; pub static USER_AGENT: Lazy = Lazy::new(|| { - let version = option_env!("CARGO_PKG_VERSION").unwrap_or("development"); + let version = option_env!("CARGO_PKG_VERSION").unwrap_or("development"); - format!("refraction/{version}") + format!("refraction/{version}") }); pub static REQWEST_CLIENT: Lazy = Lazy::new(|| { - reqwest::Client::builder() - .user_agent(USER_AGENT.to_string()) - .build() - .unwrap_or_default() + reqwest::Client::builder() + .user_agent(USER_AGENT.to_string()) + .build() + .unwrap_or_default() }); diff --git a/src/api/pluralkit.rs b/src/api/pluralkit.rs index 89cfb88..439670a 100644 --- a/src/api/pluralkit.rs +++ b/src/api/pluralkit.rs @@ -8,30 +8,32 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct PluralKitMessage { - pub sender: String, + pub sender: String, } const PLURAL_KIT: &str = "https://api.pluralkit.me/v2"; const MESSAGES_ENDPOINT: &str = "/messages"; pub async fn get_sender(message_id: MessageId) -> Result { - let req = REQWEST_CLIENT - .get(format!("{PLURAL_KIT}{MESSAGES_ENDPOINT}/{message_id}")) - .build()?; + let req = REQWEST_CLIENT + .get(format!("{PLURAL_KIT}{MESSAGES_ENDPOINT}/{message_id}")) + .build()?; - debug!("Making request to {}", req.url()); - let resp = REQWEST_CLIENT.execute(req).await?; - let status = resp.status(); + debug!("Making request to {}", req.url()); + let resp = REQWEST_CLIENT.execute(req).await?; + let status = resp.status(); - if let StatusCode::OK = status { - let data = resp.json::().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:#?}"))?; - let sender = UserId::from(id); + if let StatusCode::OK = status { + let data = resp.json::().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:#?}") + })?; + let sender = UserId::from(id); - Ok(sender) - } else { - Err(eyre!( + Ok(sender) + } else { + Err(eyre!( "Failed to get PluralKit message information from {PLURAL_KIT}{MESSAGES_ENDPOINT}/{message_id} with {status}", )) - } + } } diff --git a/src/api/prism_meta.rs b/src/api/prism_meta.rs index 3a99e40..7e24d08 100644 --- a/src/api/prism_meta.rs +++ b/src/api/prism_meta.rs @@ -8,39 +8,39 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MinecraftPackageJson { - pub format_version: u8, - pub name: String, - pub recommended: Vec, - pub uid: String, + pub format_version: u8, + pub name: String, + pub recommended: Vec, + pub uid: String, } const PRISM_META: &str = "https://meta.prismlauncher.org/v1"; const MINECRAFT_PACKAGEJSON_ENDPOINT: &str = "/net.minecraft/package.json"; pub async fn get_latest_minecraft_version() -> Result { - let req = REQWEST_CLIENT - .get(format!("{PRISM_META}{MINECRAFT_PACKAGEJSON_ENDPOINT}")) - .build()?; + let req = REQWEST_CLIENT + .get(format!("{PRISM_META}{MINECRAFT_PACKAGEJSON_ENDPOINT}")) + .build()?; - debug!("Making request to {}", req.url()); - let resp = REQWEST_CLIENT.execute(req).await?; - let status = resp.status(); + debug!("Making request to {}", req.url()); + let resp = REQWEST_CLIENT.execute(req).await?; + let status = resp.status(); - if let StatusCode::OK = status { - let data = resp - .json::() - .await - .wrap_err_with(|| "Couldn't parse Minecraft versions!")?; + if let StatusCode::OK = status { + let data = resp + .json::() + .await + .wrap_err_with(|| "Couldn't parse Minecraft versions!")?; - let version = data - .recommended - .first() - .ok_or_else(|| eyre!("Couldn't find latest version of Minecraft!"))?; + let version = data + .recommended + .first() + .ok_or_else(|| eyre!("Couldn't find latest version of Minecraft!"))?; - Ok(version.clone()) - } else { - Err(eyre!( + Ok(version.clone()) + } else { + Err(eyre!( "Failed to get latest Minecraft version from {PRISM_META}{MINECRAFT_PACKAGEJSON_ENDPOINT} with {status}", )) - } + } } diff --git a/src/api/rory.rs b/src/api/rory.rs index 399500c..e527bba 100644 --- a/src/api/rory.rs +++ b/src/api/rory.rs @@ -7,40 +7,40 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct RoryResponse { - pub id: u64, - pub url: String, - pub error: Option, + pub id: u64, + pub url: String, + pub error: Option, } const RORY: &str = "https://rory.cat"; const ENDPOINT: &str = "/purr"; pub async fn get_rory(id: Option) -> Result { - let target = id.map(|id| id.to_string()).unwrap_or_default(); + let target = id.map(|id| id.to_string()).unwrap_or_default(); - let req = REQWEST_CLIENT - .get(format!("{RORY}{ENDPOINT}/{target}")) - .build() - .wrap_err_with(|| "Couldn't build reqwest client!")?; + let req = REQWEST_CLIENT + .get(format!("{RORY}{ENDPOINT}/{target}")) + .build() + .wrap_err_with(|| "Couldn't build reqwest client!")?; - debug!("Making request to {}", req.url()); - let resp = REQWEST_CLIENT - .execute(req) - .await - .wrap_err_with(|| "Couldn't make request for rory!")?; + debug!("Making request to {}", req.url()); + let resp = REQWEST_CLIENT + .execute(req) + .await + .wrap_err_with(|| "Couldn't make request for rory!")?; - let status = resp.status(); + let status = resp.status(); - if let StatusCode::OK = status { - let data = resp - .json::() - .await - .wrap_err_with(|| "Couldn't parse the rory response!")?; + if let StatusCode::OK = status { + let data = resp + .json::() + .await + .wrap_err_with(|| "Couldn't parse the rory response!")?; - Ok(data) - } else { - Err(eyre!( - "Failed to get rory from {RORY}{ENDPOINT}/{target} with {status}", - )) - } + Ok(data) + } else { + Err(eyre!( + "Failed to get rory from {RORY}{ENDPOINT}/{target} with {status}", + )) + } } diff --git a/src/commands/general/joke.rs b/src/commands/general/joke.rs index 638db3e..f1790a0 100644 --- a/src/commands/general/joke.rs +++ b/src/commands/general/joke.rs @@ -6,8 +6,8 @@ use color_eyre::eyre::Result; /// It's a joke #[poise::command(slash_command, prefix_command)] pub async fn joke(ctx: Context<'_>) -> Result<()> { - let joke = dadjoke::get_joke().await?; + let joke = dadjoke::get_joke().await?; - ctx.reply(joke).await?; - Ok(()) + ctx.reply(joke).await?; + Ok(()) } diff --git a/src/commands/general/members.rs b/src/commands/general/members.rs index 5498dd0..a971e65 100644 --- a/src/commands/general/members.rs +++ b/src/commands/general/members.rs @@ -5,22 +5,22 @@ use color_eyre::eyre::{eyre, Result}; /// Returns the number of members in the server #[poise::command(slash_command, prefix_command)] pub async fn members(ctx: Context<'_>) -> Result<()> { - let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't fetch guild!"))?; + let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't fetch guild!"))?; - let count = guild.member_count; - let online = if let Some(count) = guild.approximate_presence_count { - count.to_string() - } else { - "Undefined".to_string() - }; + let count = guild.member_count; + let online = if let Some(count) = guild.approximate_presence_count { + count.to_string() + } else { + "Undefined".to_string() + }; - ctx.send(|m| { - m.embed(|e| { - e.title(format!("{count} total members!")) - .description(format!("{online} online members")) - .color(consts::COLORS["blue"]) - }) - }) - .await?; - Ok(()) + ctx.send(|m| { + m.embed(|e| { + e.title(format!("{count} total members!")) + .description(format!("{online} online members")) + .color(consts::COLORS["blue"]) + }) + }) + .await?; + Ok(()) } diff --git a/src/commands/general/ping.rs b/src/commands/general/ping.rs index 4563af3..41634dc 100644 --- a/src/commands/general/ping.rs +++ b/src/commands/general/ping.rs @@ -5,6 +5,6 @@ use color_eyre::eyre::Result; /// Replies with pong! #[poise::command(slash_command, prefix_command, ephemeral)] pub async fn ping(ctx: Context<'_>) -> Result<()> { - ctx.reply("Pong!").await?; - Ok(()) + ctx.reply("Pong!").await?; + Ok(()) } diff --git a/src/commands/general/rory.rs b/src/commands/general/rory.rs index 59d157e..f20700c 100644 --- a/src/commands/general/rory.rs +++ b/src/commands/general/rory.rs @@ -6,24 +6,24 @@ use color_eyre::eyre::Result; /// Gets a Rory photo! #[poise::command(slash_command, prefix_command)] pub async fn rory( - ctx: Context<'_>, - #[description = "specify a Rory ID"] id: Option, + ctx: Context<'_>, + #[description = "specify a Rory ID"] id: Option, ) -> Result<()> { - let rory = get_rory(id).await?; + let rory = get_rory(id).await?; - ctx.send(|m| { - m.embed(|e| { - if let Some(error) = rory.error { - e.title("Error!").description(error) - } else { - e.title("Rory :3") - .url(&rory.url) - .image(rory.url) - .footer(|f| f.text(format!("ID {}", rory.id))) - } - }) - }) - .await?; + ctx.send(|m| { + m.embed(|e| { + if let Some(error) = rory.error { + e.title("Error!").description(error) + } else { + e.title("Rory :3") + .url(&rory.url) + .image(rory.url) + .footer(|f| f.text(format!("ID {}", rory.id))) + } + }) + }) + .await?; - Ok(()) + Ok(()) } diff --git a/src/commands/general/say.rs b/src/commands/general/say.rs index 6439f4b..aad9836 100644 --- a/src/commands/general/say.rs +++ b/src/commands/general/say.rs @@ -4,48 +4,48 @@ use color_eyre::eyre::{eyre, Result}; /// Say something through the bot #[poise::command( - slash_command, - prefix_command, - ephemeral, - default_member_permissions = "MODERATE_MEMBERS", - required_permissions = "MODERATE_MEMBERS" + slash_command, + prefix_command, + ephemeral, + default_member_permissions = "MODERATE_MEMBERS", + required_permissions = "MODERATE_MEMBERS" )] pub async fn say(ctx: Context<'_>, #[description = "Just content?"] content: String) -> Result<()> { - let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't get guild!"))?; - let channel = ctx - .guild_channel() - .await - .ok_or_else(|| eyre!("Couldn't get channel!"))?; + let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't get guild!"))?; + let channel = ctx + .guild_channel() + .await + .ok_or_else(|| eyre!("Couldn't get channel!"))?; - ctx.defer_ephemeral().await?; - channel.say(ctx, &content).await?; - ctx.say("I said what you said!").await?; + ctx.defer_ephemeral().await?; + channel.say(ctx, &content).await?; + ctx.say("I said what you said!").await?; - if let Some(channel_id) = ctx.data().config.discord.channels.say_log_channel_id { - let log_channel = guild - .channels - .iter() - .find(|c| c.0 == &channel_id) - .ok_or_else(|| eyre!("Couldn't get log channel from guild!"))?; + if let Some(channel_id) = ctx.data().config.discord.channels.say_log_channel_id { + let log_channel = guild + .channels + .iter() + .find(|c| c.0 == &channel_id) + .ok_or_else(|| eyre!("Couldn't get log channel from guild!"))?; - log_channel - .1 - .clone() - .guild() - .ok_or_else(|| eyre!("Couldn't cast channel we found from guild as GuildChannel?????"))? - .send_message(ctx, |m| { - m.embed(|e| { - e.title("Say command used!") - .description(content) - .author(|a| { - a.name(ctx.author().tag()).icon_url( - ctx.author().avatar_url().unwrap_or("undefined".to_string()), - ) - }) - }) - }) - .await?; - } + log_channel + .1 + .clone() + .guild() + .ok_or_else(|| eyre!("Couldn't cast channel we found from guild as GuildChannel?????"))? + .send_message(ctx, |m| { + m.embed(|e| { + e.title("Say command used!") + .description(content) + .author(|a| { + a.name(ctx.author().tag()).icon_url( + ctx.author().avatar_url().unwrap_or("undefined".to_string()), + ) + }) + }) + }) + .await?; + } - Ok(()) + Ok(()) } diff --git a/src/commands/general/stars.rs b/src/commands/general/stars.rs index fef4fe8..470712f 100644 --- a/src/commands/general/stars.rs +++ b/src/commands/general/stars.rs @@ -5,27 +5,27 @@ use color_eyre::eyre::{Context as _, Result}; /// Returns GitHub stargazer count #[poise::command(slash_command, prefix_command)] pub async fn stars(ctx: Context<'_>) -> Result<()> { - let prismlauncher = ctx - .data() - .octocrab - .repos("PrismLauncher", "PrismLauncher") - .get() - .await - .wrap_err_with(|| "Couldn't get PrismLauncher/PrismLauncher from GitHub!")?; + let prismlauncher = ctx + .data() + .octocrab + .repos("PrismLauncher", "PrismLauncher") + .get() + .await + .wrap_err_with(|| "Couldn't get PrismLauncher/PrismLauncher from GitHub!")?; - let count = if let Some(count) = prismlauncher.stargazers_count { - count.to_string() - } else { - "undefined".to_string() - }; + let count = if let Some(count) = prismlauncher.stargazers_count { + count.to_string() + } else { + "undefined".to_string() + }; - ctx.send(|m| { - m.embed(|e| { - e.title(format!("⭐ {count} total stars!")) - .color(COLORS["yellow"]) - }) - }) - .await?; + ctx.send(|m| { + m.embed(|e| { + e.title(format!("⭐ {count} total stars!")) + .color(COLORS["yellow"]) + }) + }) + .await?; - Ok(()) + Ok(()) } diff --git a/src/commands/general/tag.rs b/src/commands/general/tag.rs index ff1138c..7d1c55f 100644 --- a/src/commands/general/tag.rs +++ b/src/commands/general/tag.rs @@ -13,48 +13,48 @@ static TAGS: Lazy> = Lazy::new(|| serde_json::from_str(env!("TAGS")).un /// Send a tag #[poise::command(slash_command, prefix_command)] pub async fn tag( - ctx: Context<'_>, - #[description = "the copypasta you want to send"] name: TagChoice, - user: Option, + ctx: Context<'_>, + #[description = "the copypasta you want to send"] name: TagChoice, + user: Option, ) -> Result<()> { - let tag_file = name.as_str(); - let tag = TAGS - .iter() - .find(|t| t.file_name == tag_file) - .ok_or_else(|| eyre!("Tried to get non-existent tag: {tag_file}"))?; + let tag_file = name.as_str(); + let tag = TAGS + .iter() + .find(|t| t.file_name == tag_file) + .ok_or_else(|| eyre!("Tried to get non-existent tag: {tag_file}"))?; - let frontmatter = &tag.frontmatter; + let frontmatter = &tag.frontmatter; - ctx.send(|m| { - if let Some(user) = user { - m.content(format!("<@{}>", user.id)); - } + ctx.send(|m| { + if let Some(user) = user { + m.content(format!("<@{}>", user.id)); + } - m.embed(|e| { - e.title(&frontmatter.title); - e.description(&tag.content); + m.embed(|e| { + e.title(&frontmatter.title); + e.description(&tag.content); - if let Some(color) = &frontmatter.color { - let color = *consts::COLORS - .get(color.as_str()) - .unwrap_or(&Color::default()); - e.color(color); - } + if let Some(color) = &frontmatter.color { + let color = *consts::COLORS + .get(color.as_str()) + .unwrap_or(&Color::default()); + e.color(color); + } - if let Some(image) = &frontmatter.image { - e.image(image); - } + if let Some(image) = &frontmatter.image { + e.image(image); + } - if let Some(fields) = &frontmatter.fields { - for field in fields { - e.field(&field.name, &field.value, field.inline); - } - } + if let Some(fields) = &frontmatter.fields { + for field in fields { + e.field(&field.name, &field.value, field.inline); + } + } - e - }) - }) - .await?; + e + }) + }) + .await?; - Ok(()) + Ok(()) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 45c23d6..0a2293f 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -6,13 +6,13 @@ use poise::Command; mod general; pub fn to_global_commands() -> Vec> { - vec![ - general::joke(), - general::members(), - general::ping(), - general::rory(), - general::say(), - general::stars(), - general::tag(), - ] + vec![ + general::joke(), + general::members(), + general::ping(), + general::rory(), + general::say(), + general::stars(), + general::tag(), + ] } diff --git a/src/config/discord.rs b/src/config/discord.rs index 77cc392..9897ef7 100644 --- a/src/config/discord.rs +++ b/src/config/discord.rs @@ -7,81 +7,81 @@ use url::Url; #[derive(Debug, Clone)] pub struct RefractionOAuth2 { - pub redirect_uri: Url, - pub scope: String, + pub redirect_uri: Url, + pub scope: String, } #[derive(Debug, Clone, Default)] pub struct RefractionChannels { - pub say_log_channel_id: Option, + pub say_log_channel_id: Option, } #[derive(Debug, Clone, Default)] pub struct DiscordConfig { - pub client_id: ApplicationId, - pub client_secret: String, - pub bot_token: String, - pub oauth2: RefractionOAuth2, - pub channels: RefractionChannels, + pub client_id: ApplicationId, + pub client_secret: String, + pub bot_token: String, + pub oauth2: RefractionOAuth2, + pub channels: RefractionChannels, } impl Default for RefractionOAuth2 { - fn default() -> Self { - Self { - scope: "identify connections role_connections.write".to_string(), - redirect_uri: Url::parse("https://google.com").unwrap(), - } - } + fn default() -> Self { + Self { + scope: "identify connections role_connections.write".to_string(), + redirect_uri: Url::parse("https://google.com").unwrap(), + } + } } impl RefractionOAuth2 { - pub fn new_from_env() -> Result { - let unparsed = format!("{}/oauth2/callback", required_var!("PUBLIC_URI")); - let redirect_uri = Url::parse(&unparsed)?; + pub fn new_from_env() -> Result { + let unparsed = format!("{}/oauth2/callback", required_var!("PUBLIC_URI")); + let redirect_uri = Url::parse(&unparsed)?; - debug!("OAuth2 Redirect URI is {redirect_uri}"); - Ok(Self { - redirect_uri, - ..Default::default() - }) - } + debug!("OAuth2 Redirect URI is {redirect_uri}"); + Ok(Self { + redirect_uri, + ..Default::default() + }) + } } impl RefractionChannels { - pub fn new_from_env() -> Result { - let unparsed = std::env::var("DISCORD_SAY_LOG_CHANNELID"); - if let Ok(unparsed) = unparsed { - let id = unparsed.parse::()?; - let channel_id = ChannelId::from(id); + pub fn new_from_env() -> Result { + let unparsed = std::env::var("DISCORD_SAY_LOG_CHANNELID"); + if let Ok(unparsed) = unparsed { + let id = unparsed.parse::()?; + let channel_id = ChannelId::from(id); - debug!("Log channel is {id}"); - Ok(Self { - say_log_channel_id: Some(channel_id), - }) - } else { - warn!("DISCORD_SAY_LOG_CHANNELID is empty; this will disable logging in your server."); - Ok(Self { - say_log_channel_id: None, - }) - } - } + debug!("Log channel is {id}"); + Ok(Self { + say_log_channel_id: Some(channel_id), + }) + } else { + warn!("DISCORD_SAY_LOG_CHANNELID is empty; this will disable logging in your server."); + Ok(Self { + say_log_channel_id: None, + }) + } + } } impl DiscordConfig { - pub fn new_from_env() -> Result { - let unparsed_client = required_var!("DISCORD_CLIENT_ID").parse::()?; - let client_id = ApplicationId::from(unparsed_client); - let client_secret = required_var!("DISCORD_CLIENT_SECRET"); - let bot_token = required_var!("DISCORD_BOT_TOKEN"); - let oauth2 = RefractionOAuth2::new_from_env()?; - let channels = RefractionChannels::new_from_env()?; + pub fn new_from_env() -> Result { + let unparsed_client = required_var!("DISCORD_CLIENT_ID").parse::()?; + let client_id = ApplicationId::from(unparsed_client); + let client_secret = required_var!("DISCORD_CLIENT_SECRET"); + let bot_token = required_var!("DISCORD_BOT_TOKEN"); + let oauth2 = RefractionOAuth2::new_from_env()?; + let channels = RefractionChannels::new_from_env()?; - Ok(Self { - client_id, - client_secret, - bot_token, - oauth2, - channels, - }) - } + Ok(Self { + client_id, + client_secret, + bot_token, + oauth2, + channels, + }) + } } diff --git a/src/config/github.rs b/src/config/github.rs index d9ca12e..d7b2c04 100644 --- a/src/config/github.rs +++ b/src/config/github.rs @@ -4,62 +4,62 @@ use crate::required_var; #[derive(Debug, Clone)] pub struct RefractionRepo { - pub owner: String, - pub repo: String, - pub key: String, - pub name: String, + pub owner: String, + pub repo: String, + pub key: String, + pub name: String, } #[derive(Debug, Clone)] pub struct GithubConfig { - pub token: String, - pub repos: Vec, - pub cache_sec: u16, - pub update_cron_job: String, + pub token: String, + pub repos: Vec, + pub cache_sec: u16, + pub update_cron_job: String, } impl Default for GithubConfig { - fn default() -> Self { - let owner = "PrismLauncher".to_string(); - let repos = Vec::::from([ - RefractionRepo { - owner: owner.clone(), - repo: "PrismLauncher".to_string(), - key: "launcher".to_string(), - name: "Launcher contributor".to_string(), - }, - RefractionRepo { - owner: owner.clone(), - repo: "prismlauncher.org".to_string(), + fn default() -> Self { + let owner = "PrismLauncher".to_string(); + let repos = Vec::::from([ + RefractionRepo { + owner: owner.clone(), + repo: "PrismLauncher".to_string(), + key: "launcher".to_string(), + name: "Launcher contributor".to_string(), + }, + RefractionRepo { + owner: owner.clone(), + repo: "prismlauncher.org".to_string(), - key: "website".to_string(), - name: "Web developer".to_string(), - }, - RefractionRepo { - owner: owner.clone(), - repo: "Translations".to_string(), + key: "website".to_string(), + name: "Web developer".to_string(), + }, + RefractionRepo { + owner: owner.clone(), + repo: "Translations".to_string(), - key: "translations".to_string(), - name: "Translator".to_string(), - }, - ]); + key: "translations".to_string(), + name: "Translator".to_string(), + }, + ]); - Self { - repos, - cache_sec: 3600, - update_cron_job: "0 */10 * * * *".to_string(), // every 10 minutes - token: String::default(), - } - } + Self { + repos, + cache_sec: 3600, + update_cron_job: "0 */10 * * * *".to_string(), // every 10 minutes + token: String::default(), + } + } } impl GithubConfig { - pub fn new_from_env() -> Result { - let token = required_var!("GITHUB_TOKEN"); + pub fn new_from_env() -> Result { + let token = required_var!("GITHUB_TOKEN"); - Ok(Self { - token, - ..Default::default() - }) - } + Ok(Self { + token, + ..Default::default() + }) + } } diff --git a/src/config/mod.rs b/src/config/mod.rs index a2bec63..b04938c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,32 +8,32 @@ pub use github::*; #[derive(Debug, Clone)] pub struct Config { - pub discord: DiscordConfig, - pub github: GithubConfig, - pub http_port: u16, - pub redis_url: String, + pub discord: DiscordConfig, + pub github: GithubConfig, + pub http_port: u16, + pub redis_url: String, } impl Default for Config { - fn default() -> Self { - Self { - discord: DiscordConfig::default(), - github: GithubConfig::default(), - http_port: 3000, - redis_url: "redis://localhost:6379".to_string(), - } - } + fn default() -> Self { + Self { + discord: DiscordConfig::default(), + github: GithubConfig::default(), + http_port: 3000, + redis_url: "redis://localhost:6379".to_string(), + } + } } impl Config { - pub fn new_from_env() -> Result { - let discord = DiscordConfig::new_from_env()?; - let github = GithubConfig::new_from_env()?; + pub fn new_from_env() -> Result { + let discord = DiscordConfig::new_from_env()?; + let github = GithubConfig::new_from_env()?; - Ok(Self { - discord, - github, - ..Default::default() - }) - } + Ok(Self { + discord, + github, + ..Default::default() + }) + } } diff --git a/src/consts.rs b/src/consts.rs index 35fdb61..3e9f541 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -4,31 +4,31 @@ use once_cell::sync::Lazy; use poise::serenity_prelude::Color; pub static COLORS: Lazy> = Lazy::new(|| { - HashMap::from([ - ("red", Color::from((239, 68, 68))), - ("green", Color::from((34, 197, 94))), - ("blue", Color::from((96, 165, 250))), - ("yellow", Color::from((253, 224, 71))), - ("orange", Color::from((251, 146, 60))), - // TODO purple & pink :D - ]) + HashMap::from([ + ("red", Color::from((239, 68, 68))), + ("green", Color::from((34, 197, 94))), + ("blue", Color::from((96, 165, 250))), + ("yellow", Color::from((253, 224, 71))), + ("orange", Color::from((251, 146, 60))), + // TODO purple & pink :D + ]) }); pub const ETA_MESSAGES: [&str; 16] = [ - "Sometime", - "Some day", - "Not far", - "The future", - "Never", - "Perhaps tomorrow?", - "There are no ETAs", - "No", - "Nah", - "Yes", - "Yas", - "Next month", - "Next year", - "Next week", - "In Prism Launcher 2.0.0", - "At the appropriate juncture, in due course, in the fullness of time", + "Sometime", + "Some day", + "Not far", + "The future", + "Never", + "Perhaps tomorrow?", + "There are no ETAs", + "No", + "Nah", + "Yes", + "Yas", + "Next month", + "Next year", + "Next week", + "In Prism Launcher 2.0.0", + "At the appropriate juncture, in due course, in the fullness of time", ]; diff --git a/src/handlers/error.rs b/src/handlers/error.rs index 66277e0..12e4980 100644 --- a/src/handlers/error.rs +++ b/src/handlers/error.rs @@ -7,43 +7,43 @@ use poise::serenity_prelude::Timestamp; use poise::FrameworkError; pub async fn handle(error: FrameworkError<'_, Data, Report>) { - match error { - FrameworkError::Setup { - error, framework, .. - } => { - error!("Error setting up client! Bailing out"); - framework.shard_manager().lock().await.shutdown_all().await; + match error { + FrameworkError::Setup { + error, framework, .. + } => { + error!("Error setting up client! Bailing out"); + framework.shard_manager().lock().await.shutdown_all().await; - panic!("{error}") - } + panic!("{error}") + } - FrameworkError::Command { error, ctx } => { - error!("Error in command {}:\n{error:?}", ctx.command().name); - ctx.send(|c| { - c.embed(|e| { - e.title("Something went wrong!") - .description("oopsie") - .timestamp(Timestamp::now()) - .color(COLORS["red"]) - }) - }) - .await - .ok(); - } + FrameworkError::Command { error, ctx } => { + error!("Error in command {}:\n{error:?}", ctx.command().name); + ctx.send(|c| { + c.embed(|e| { + e.title("Something went wrong!") + .description("oopsie") + .timestamp(Timestamp::now()) + .color(COLORS["red"]) + }) + }) + .await + .ok(); + } - FrameworkError::EventHandler { - error, - ctx: _, - event, - framework: _, - } => { - error!("Error while handling event {}:\n{error:?}", event.name()); - } + FrameworkError::EventHandler { + error, + ctx: _, + event, + framework: _, + } => { + error!("Error while handling event {}:\n{error:?}", event.name()); + } - error => { - if let Err(e) = poise::builtins::on_error(error).await { - error!("Unhandled error occured:\n{e:#?}"); - } - } - } + error => { + if let Err(e) = poise::builtins::on_error(error).await { + error!("Unhandled error occured:\n{e:#?}"); + } + } + } } diff --git a/src/handlers/event/analyze_logs/issues.rs b/src/handlers/event/analyze_logs/issues.rs index 77acb2b..0f411e9 100644 --- a/src/handlers/event/analyze_logs/issues.rs +++ b/src/handlers/event/analyze_logs/issues.rs @@ -7,164 +7,164 @@ use regex::Regex; pub type Issue = Option<(String, String)>; pub async fn find_issues(log: &str, data: &Data) -> Result> { - let issues = [ - fabric_internal, - flatpak_nvidia, - forge_java, - intel_hd, - java_option, - lwjgl_2_java_9, - macos_ns, - oom, - optinotfine, - pre_1_12_native_transport_java_9, - wrong_java, - ]; + let issues = [ + fabric_internal, + flatpak_nvidia, + forge_java, + intel_hd, + java_option, + lwjgl_2_java_9, + macos_ns, + oom, + optinotfine, + pre_1_12_native_transport_java_9, + wrong_java, + ]; - let mut res: Vec<(String, String)> = issues.iter().filter_map(|issue| issue(log)).collect(); + let mut res: Vec<(String, String)> = issues.iter().filter_map(|issue| issue(log)).collect(); - if let Some(issues) = outdated_launcher(log, data).await? { - res.push(issues) - } + if let Some(issues) = outdated_launcher(log, data).await? { + res.push(issues) + } - Ok(res) + Ok(res) } fn fabric_internal(log: &str) -> Issue { - const CLASS_NOT_FOUND: &str = "Caused by: java.lang.ClassNotFoundException: "; + const CLASS_NOT_FOUND: &str = "Caused by: java.lang.ClassNotFoundException: "; - let issue = ( - "Fabric Internal Access".to_string(), - "The mod you are using is using fabric internals that are not meant \ + let issue = ( + "Fabric Internal Access".to_string(), + "The mod you are using is using fabric internals that are not meant \ to be used by anything but the loader itself. Those mods break both on Quilt and with fabric updates. If you're using fabric, downgrade your fabric loader could work, \ on Quilt you can try updating to the latest beta version, \ but there's nothing much to do unless the mod author stops using them." - .to_string(), - ); + .to_string(), + ); - let errors = [ - &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.impl"), - &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.mixin"), - &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.impl"), - &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.mixin"), - "org.quiltmc.loader.impl.FormattedException: java.lang.NoSuchMethodError:", - ]; + let errors = [ + &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.impl"), + &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.mixin"), + &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.impl"), + &format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.mixin"), + "org.quiltmc.loader.impl.FormattedException: java.lang.NoSuchMethodError:", + ]; - let found = errors.iter().any(|e| log.contains(e)); - found.then_some(issue) + let found = errors.iter().any(|e| log.contains(e)); + found.then_some(issue) } fn flatpak_nvidia(log: &str) -> Issue { - let issue = ( - "Outdated Nvidia Flatpak Driver".to_string(), - "The Nvidia driver for flatpak is outdated. + let issue = ( + "Outdated Nvidia Flatpak Driver".to_string(), + "The Nvidia driver for flatpak is outdated. Please run `flatpak update` to fix this issue. \ If that does not solve it, \ please wait until the driver is added to Flathub and run it again." - .to_string(), - ); + .to_string(), + ); - let found = log.contains("org.lwjgl.LWJGLException: Could not choose GLX13 config") - || log.contains("GLFW error 65545: GLX: Failed to find a suitable GLXFBConfig"); + let found = log.contains("org.lwjgl.LWJGLException: Could not choose GLX13 config") + || log.contains("GLFW error 65545: GLX: Failed to find a suitable GLXFBConfig"); - found.then_some(issue) + found.then_some(issue) } fn forge_java(log: &str) -> Issue { - let issue = ( - "Forge Java Bug".to_string(), - "Old versions of Forge crash with Java 8u321+. + let issue = ( + "Forge Java Bug".to_string(), + "Old versions of Forge crash with Java 8u321+. To fix this, update forge to the latest version via the Versions tab (right click on Forge, click Change Version, and choose the latest one) Alternatively, you can download 8u312 or lower. \ See [archive](https://github.com/adoptium/temurin8-binaries/releases/tag/jdk8u312-b07)" - .to_string(), - ); + .to_string(), + ); - let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.(Ljava/util/jar/Manifest;)V"); - found.then_some(issue) + let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.(Ljava/util/jar/Manifest;)V"); + found.then_some(issue) } fn intel_hd(log: &str) -> Issue { - let issue = + let issue = ( "Intel HD Windows 10".to_string(), "Your drivers don't support windows 10 officially See https://prismlauncher.org/wiki/getting-started/installing-java/#a-note-about-intel-hd-20003000-on-windows-10 for more info".to_string() ); - let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.(Ljava/util/jar/Manifest;)V"); - found.then_some(issue) + let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.(Ljava/util/jar/Manifest;)V"); + found.then_some(issue) } fn java_option(log: &str) -> Issue { - static VM_OPTION_REGEX: Lazy = - Lazy::new(|| Regex::new(r"Unrecognized VM option '(.+)'[\r\n]").unwrap()); - static OPTION_REGEX: Lazy = - Lazy::new(|| Regex::new(r"Unrecognized option: (.+)[\r\n]").unwrap()); + static VM_OPTION_REGEX: Lazy = + Lazy::new(|| Regex::new(r"Unrecognized VM option '(.+)'[\r\n]").unwrap()); + static OPTION_REGEX: Lazy = + Lazy::new(|| Regex::new(r"Unrecognized option: (.+)[\r\n]").unwrap()); - if let Some(captures) = VM_OPTION_REGEX.captures(log) { - let title = if &captures[1] == "UseShenandoahGC" { - "Wrong Java Arguments" - } else { - "Java 8 and below don't support ShenandoahGC" - }; - return Some(( - title.to_string(), - format!("Remove `-XX:{}` from your Java arguments", &captures[1]), - )); - } + if let Some(captures) = VM_OPTION_REGEX.captures(log) { + let title = if &captures[1] == "UseShenandoahGC" { + "Wrong Java Arguments" + } else { + "Java 8 and below don't support ShenandoahGC" + }; + return Some(( + title.to_string(), + format!("Remove `-XX:{}` from your Java arguments", &captures[1]), + )); + } - if let Some(captures) = OPTION_REGEX.captures(log) { - return Some(( - "Wrong Java Arguments".to_string(), - format!("Remove `{}` from your Java arguments", &captures[1]), - )); - } + if let Some(captures) = OPTION_REGEX.captures(log) { + return Some(( + "Wrong Java Arguments".to_string(), + format!("Remove `{}` from your Java arguments", &captures[1]), + )); + } - None + None } fn lwjgl_2_java_9(log: &str) -> Issue { - let issue = ( - "Linux: crash with pre-1.13 and Java 9+".to_string(), - "Using pre-1.13 (which uses LWJGL 2) with Java 9 or later usually causes a crash. \ + let issue = ( + "Linux: crash with pre-1.13 and Java 9+".to_string(), + "Using pre-1.13 (which uses LWJGL 2) with Java 9 or later usually causes a crash. \ Switching to Java 8 or below will fix your issue. Alternatively, you can use [Temurin](https://adoptium.net/temurin/releases). \ However, multiplayer will not work in versions from 1.8 to 1.11. For more information, type `/tag java`." - .to_string(), - ); + .to_string(), + ); - let found = log.contains("check_match: Assertion `version->filename == NULL || ! _dl_name_match_p (version->filename, map)' failed!"); - found.then_some(issue) + let found = log.contains("check_match: Assertion `version->filename == NULL || ! _dl_name_match_p (version->filename, map)' failed!"); + found.then_some(issue) } fn macos_ns(log: &str) -> Issue { - let issue = ( + let issue = ( "MacOS NSInternalInconsistencyException".to_string(), "You need to downgrade your Java 8 version. See https://prismlauncher.org/wiki/getting-started/installing-java/#older-minecraft-on-macos".to_string() ); - let found = - log.contains("Terminating app due to uncaught exception 'NSInternalInconsistencyException"); - found.then_some(issue) + let found = + log.contains("Terminating app due to uncaught exception 'NSInternalInconsistencyException"); + found.then_some(issue) } fn oom(log: &str) -> Issue { - let issue = ( - "Out of Memory".to_string(), - "Allocating more RAM to your instance could help prevent this crash.".to_string(), - ); + let issue = ( + "Out of Memory".to_string(), + "Allocating more RAM to your instance could help prevent this crash.".to_string(), + ); - let found = log.contains("java.lang.OutOfMemoryError"); - found.then_some(issue) + let found = log.contains("java.lang.OutOfMemoryError"); + found.then_some(issue) } fn optinotfine(log: &str) -> Issue { - let issue = ( + let issue = ( "Potential OptiFine Incompatibilities".to_string(), "OptiFine is known to cause problems when paired with other mods. \ Try to disable OptiFine and see if the issue persists. @@ -172,50 +172,50 @@ fn optinotfine(log: &str) -> Issue { .to_string(), ); - let found = log.contains("[✔] OptiFine_") || log.contains("[✔] optifabric-"); - found.then_some(issue) + let found = log.contains("[✔] OptiFine_") || log.contains("[✔] optifabric-"); + found.then_some(issue) } async fn outdated_launcher(log: &str, data: &Data) -> Result { - static OUTDATED_LAUNCHER_REGEX: Lazy = - Lazy::new(|| Regex::new("Prism Launcher version: [0-9].[0-9].[0-9]").unwrap()); + static OUTDATED_LAUNCHER_REGEX: Lazy = + Lazy::new(|| Regex::new("Prism Launcher version: [0-9].[0-9].[0-9]").unwrap()); - let Some(captures) = OUTDATED_LAUNCHER_REGEX.captures(log) else { - return Ok(None); - }; + let Some(captures) = OUTDATED_LAUNCHER_REGEX.captures(log) else { + return Ok(None); + }; - 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 storage.launcher_version_is_cached().await? { - storage.get_launcher_version().await? - } else { - let version = data - .octocrab - .repos("PrismLauncher", "PrismLauncher") - .releases() - .get_latest() - .await? - .tag_name; + let storage = &data.storage; + let latest_version = if storage.launcher_version_is_cached().await? { + storage.get_launcher_version().await? + } else { + let version = data + .octocrab + .repos("PrismLauncher", "PrismLauncher") + .releases() + .get_latest() + .await? + .tag_name; - storage.cache_launcher_version(&version).await?; - version - }; + storage.cache_launcher_version(&version).await?; + version + }; - if version_from_log < latest_version { - let issue = ( + if version_from_log < latest_version { + let issue = ( "Outdated Prism Launcher".to_string(), format!("Your installed version is {version_from_log}, while the newest version is {latest_version}.\nPlease update, for more info see https://prismlauncher.org/download/") ); - Ok(Some(issue)) - } else { - Ok(None) - } + Ok(Some(issue)) + } else { + Ok(None) + } } fn pre_1_12_native_transport_java_9(log: &str) -> Issue { - let issue = ( + let issue = ( "Linux: broken multiplayer with 1.8-1.11 and Java 9+".to_string(), "These versions of Minecraft use an outdated version of Netty which does not properly support Java 9. @@ -229,33 +229,33 @@ which is why the issue was not present." .to_string(), ); - let found = log.contains( + let found = log.contains( "java.lang.RuntimeException: Unable to access address of buffer\n\tat io.netty.channel.epoll" ); - found.then_some(issue) + found.then_some(issue) } fn wrong_java(log: &str) -> Issue { - static SWITCH_VERSION_REGEX: Lazy = Lazy::new(|| { - Regex::new( + static SWITCH_VERSION_REGEX: Lazy = Lazy::new(|| { + Regex::new( r"(?m)Please switch to one of the following Java versions for this instance:[\r\n]+(Java version [\d.]+)", ).unwrap() - }); + }); - if let Some(captures) = SWITCH_VERSION_REGEX.captures(log) { - let versions = captures[1].split('\n').collect::>().join(", "); - return Some(( + if let Some(captures) = SWITCH_VERSION_REGEX.captures(log) { + let versions = captures[1].split('\n').collect::>().join(", "); + return Some(( "Wrong Java Version".to_string(), format!("Please switch to one of the following: `{versions}`\nFor more information, type `/tag java`"), )); - } + } - let issue = ( + let issue = ( "Java compatibility check skipped".to_string(), "The Java major version may not work with your Minecraft instance. Please switch to a compatible version".to_string() ); - log.contains("Java major version is incompatible. Things might break.") - .then_some(issue) + log.contains("Java major version is incompatible. Things might break.") + .then_some(issue) } diff --git a/src/handlers/event/analyze_logs/mod.rs b/src/handlers/event/analyze_logs/mod.rs index 0e12127..80c660d 100644 --- a/src/handlers/event/analyze_logs/mod.rs +++ b/src/handlers/event/analyze_logs/mod.rs @@ -12,57 +12,57 @@ use issues::find_issues; use providers::find_log; pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()> { - let channel = message.channel_id; + let channel = message.channel_id; - let log = find_log(message).await; + let log = find_log(message).await; - if log.is_err() { - channel - .send_message(ctx, |m| { - m.reference_message(message) - .allowed_mentions(|am| am.replied_user(true)) - .embed(|e| { - e.title("Analyze failed!") - .description("Couldn't download log") - }) - }) - .await?; + if log.is_err() { + channel + .send_message(ctx, |m| { + m.reference_message(message) + .allowed_mentions(|am| am.replied_user(true)) + .embed(|e| { + e.title("Analyze failed!") + .description("Couldn't download log") + }) + }) + .await?; - return Ok(()); - } + return Ok(()); + } - let Some(log) = log? else { - debug!("No log found in message! Skipping analysis"); - return Ok(()); - }; + let Some(log) = log? else { + debug!("No log found in message! Skipping analysis"); + return Ok(()); + }; - let issues = find_issues(&log, data).await?; + let issues = find_issues(&log, data).await?; - channel - .send_message(ctx, |m| { - m.reference_message(message) - .allowed_mentions(|am| am.replied_user(true)) - .embed(|e| { - e.title("Log analysis"); + channel + .send_message(ctx, |m| { + m.reference_message(message) + .allowed_mentions(|am| am.replied_user(true)) + .embed(|e| { + e.title("Log analysis"); - if issues.is_empty() { - e.color(COLORS["green"]).field( - "Analyze failed!", - "No issues found automatically", - false, - ); - } else { - e.color(COLORS["red"]); + if issues.is_empty() { + e.color(COLORS["green"]).field( + "Analyze failed!", + "No issues found automatically", + false, + ); + } else { + e.color(COLORS["red"]); - for (title, description) in issues { - e.field(title, description, false); - } - } + for (title, description) in issues { + e.field(title, description, false); + } + } - e - }) - }) - .await?; + e + }) + }) + .await?; - Ok(()) + Ok(()) } diff --git a/src/handlers/event/analyze_logs/providers/0x0.rs b/src/handlers/event/analyze_logs/providers/0x0.rs index c958389..9a81771 100644 --- a/src/handlers/event/analyze_logs/providers/0x0.rs +++ b/src/handlers/event/analyze_logs/providers/0x0.rs @@ -8,17 +8,17 @@ use reqwest::StatusCode; static REGEX: Lazy = Lazy::new(|| Regex::new(r"https://0x0\.st/\w*\.\w*").unwrap()); pub async fn find(content: &str) -> Result> { - let Some(url) = REGEX.find(content).map(|m| &content[m.range()]) else { - return Ok(None); - }; + let Some(url) = REGEX.find(content).map(|m| &content[m.range()]) else { + return Ok(None); + }; - let request = REQWEST_CLIENT.get(url).build()?; - let response = REQWEST_CLIENT.execute(request).await?; - let status = response.status(); + let request = REQWEST_CLIENT.get(url).build()?; + let response = REQWEST_CLIENT.execute(request).await?; + let status = response.status(); - if let StatusCode::OK = status { - Ok(Some(response.text().await?)) - } else { - Err(eyre!("Failed to fetch paste from {url} with {status}",)) - } + if let StatusCode::OK = status { + Ok(Some(response.text().await?)) + } else { + Err(eyre!("Failed to fetch paste from {url} with {status}",)) + } } diff --git a/src/handlers/event/analyze_logs/providers/attachment.rs b/src/handlers/event/analyze_logs/providers/attachment.rs index 71d8d65..b69640b 100644 --- a/src/handlers/event/analyze_logs/providers/attachment.rs +++ b/src/handlers/event/analyze_logs/providers/attachment.rs @@ -2,17 +2,17 @@ use color_eyre::eyre::Result; use poise::serenity_prelude::Message; pub async fn find(message: &Message) -> Result> { - // find first uploaded text file - if let Some(attachment) = message.attachments.iter().find(|a| { - a.content_type - .as_ref() - .and_then(|ct| ct.starts_with("text/").then_some(true)) - .is_some() - }) { - let bytes = attachment.download().await?; - let res = String::from_utf8(bytes)?; - Ok(Some(res)) - } else { - Ok(None) - } + // find first uploaded text file + if let Some(attachment) = message.attachments.iter().find(|a| { + a.content_type + .as_ref() + .and_then(|ct| ct.starts_with("text/").then_some(true)) + .is_some() + }) { + let bytes = attachment.download().await?; + let res = String::from_utf8(bytes)?; + Ok(Some(res)) + } else { + Ok(None) + } } diff --git a/src/handlers/event/analyze_logs/providers/haste.rs b/src/handlers/event/analyze_logs/providers/haste.rs index 525da1b..885d70e 100644 --- a/src/handlers/event/analyze_logs/providers/haste.rs +++ b/src/handlers/event/analyze_logs/providers/haste.rs @@ -6,21 +6,21 @@ use regex::Regex; use reqwest::StatusCode; static REGEX: Lazy = - Lazy::new(|| Regex::new(r"https://hst\.sh(?:/raw)?/(\w+(?:\.\w*)?)").unwrap()); + Lazy::new(|| Regex::new(r"https://hst\.sh(?:/raw)?/(\w+(?:\.\w*)?)").unwrap()); pub async fn find(content: &str) -> Result> { - let Some(captures) = REGEX.captures(content) else { - return Ok(None); - }; + let Some(captures) = REGEX.captures(content) else { + return Ok(None); + }; - let url = format!("https://hst.sh/raw/{}", &captures[1]); - let request = REQWEST_CLIENT.get(&url).build()?; - let response = REQWEST_CLIENT.execute(request).await?; - let status = response.status(); + let url = format!("https://hst.sh/raw/{}", &captures[1]); + let request = REQWEST_CLIENT.get(&url).build()?; + let response = REQWEST_CLIENT.execute(request).await?; + let status = response.status(); - if let StatusCode::OK = status { - Ok(Some(response.text().await?)) - } else { - Err(eyre!("Failed to fetch paste from {url} with {status}")) - } + if let StatusCode::OK = status { + Ok(Some(response.text().await?)) + } else { + Err(eyre!("Failed to fetch paste from {url} with {status}")) + } } diff --git a/src/handlers/event/analyze_logs/providers/mclogs.rs b/src/handlers/event/analyze_logs/providers/mclogs.rs index c22f0c6..6663940 100644 --- a/src/handlers/event/analyze_logs/providers/mclogs.rs +++ b/src/handlers/event/analyze_logs/providers/mclogs.rs @@ -8,18 +8,18 @@ use reqwest::StatusCode; static REGEX: Lazy = Lazy::new(|| Regex::new(r"https://mclo\.gs/(\w+)").unwrap()); pub async fn find(content: &str) -> Result> { - let Some(captures) = REGEX.captures(content) else { - return Ok(None); - }; + let Some(captures) = REGEX.captures(content) else { + return Ok(None); + }; - let url = format!("https://api.mclo.gs/1/raw/{}", &captures[1]); - let request = REQWEST_CLIENT.get(&url).build()?; - let response = REQWEST_CLIENT.execute(request).await?; - let status = response.status(); + let url = format!("https://api.mclo.gs/1/raw/{}", &captures[1]); + let request = REQWEST_CLIENT.get(&url).build()?; + let response = REQWEST_CLIENT.execute(request).await?; + let status = response.status(); - if let StatusCode::OK = status { - Ok(Some(response.text().await?)) - } else { - Err(eyre!("Failed to fetch log from {url} with {status}")) - } + if let StatusCode::OK = status { + Ok(Some(response.text().await?)) + } else { + Err(eyre!("Failed to fetch log from {url} with {status}")) + } } diff --git a/src/handlers/event/analyze_logs/providers/mod.rs b/src/handlers/event/analyze_logs/providers/mod.rs index 3ebeb9a..8185056 100644 --- a/src/handlers/event/analyze_logs/providers/mod.rs +++ b/src/handlers/event/analyze_logs/providers/mod.rs @@ -12,22 +12,22 @@ mod pastebin; pub type LogProvider = Result>; pub async fn find_log(message: &Message) -> LogProvider { - macro_rules! provider_impl { - ($provider:ident) => { - if let Some(content) = $provider::find(&message.content).await? { - return Ok(Some(content)); - } - }; - } - provider_impl!(_0x0); - provider_impl!(mclogs); - provider_impl!(haste); - provider_impl!(paste_gg); - provider_impl!(pastebin); + macro_rules! provider_impl { + ($provider:ident) => { + if let Some(content) = $provider::find(&message.content).await? { + return Ok(Some(content)); + } + }; + } + provider_impl!(_0x0); + provider_impl!(mclogs); + provider_impl!(haste); + provider_impl!(paste_gg); + provider_impl!(pastebin); - if let Some(content) = attachment::find(message).await? { - return Ok(Some(content)); - } + if let Some(content) = attachment::find(message).await? { + return Ok(Some(content)); + } - Ok(None) + Ok(None) } diff --git a/src/handlers/event/analyze_logs/providers/paste_gg.rs b/src/handlers/event/analyze_logs/providers/paste_gg.rs index da52453..a5a53c9 100644 --- a/src/handlers/event/analyze_logs/providers/paste_gg.rs +++ b/src/handlers/event/analyze_logs/providers/paste_gg.rs @@ -12,59 +12,59 @@ static REGEX: Lazy = Lazy::new(|| Regex::new(r"https://paste.gg/p/\w+/(\w #[derive(Clone, Debug, Deserialize, Serialize)] struct PasteResponse { - status: String, - result: Option>, - error: Option, - message: Option, + status: String, + result: Option>, + error: Option, + message: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] struct PasteResult { - id: String, - name: Option, - description: Option, - visibility: Option, + id: String, + name: Option, + description: Option, + visibility: Option, } pub async fn find(content: &str) -> Result> { - let Some(captures) = REGEX.captures(content) else { - return Ok(None); - }; + let Some(captures) = REGEX.captures(content) else { + return Ok(None); + }; - let paste_id = &captures[1]; - let files_url = format!("{PASTE_GG}{PASTES_ENDPOINT}/{paste_id}/files"); + let paste_id = &captures[1]; + let files_url = format!("{PASTE_GG}{PASTES_ENDPOINT}/{paste_id}/files"); - let resp = REQWEST_CLIENT - .execute(REQWEST_CLIENT.get(&files_url).build()?) - .await?; - let status = resp.status(); + let resp = REQWEST_CLIENT + .execute(REQWEST_CLIENT.get(&files_url).build()?) + .await?; + let status = resp.status(); - if resp.status() != StatusCode::OK { - return Err(eyre!( - "Couldn't get paste {paste_id} from {PASTE_GG} with status {status}!" - )); - } + if resp.status() != StatusCode::OK { + return Err(eyre!( + "Couldn't get paste {paste_id} from {PASTE_GG} with status {status}!" + )); + } - let paste_files: PasteResponse = resp.json().await?; - let file_id = &paste_files - .result - .ok_or_else(|| eyre!("Couldn't find any files associated with paste {paste_id}!"))?[0] - .id; + let paste_files: PasteResponse = resp.json().await?; + let file_id = &paste_files + .result + .ok_or_else(|| eyre!("Couldn't find any files associated with paste {paste_id}!"))?[0] + .id; - let raw_url = format!("{PASTE_GG}{PASTES_ENDPOINT}/{paste_id}/files/{file_id}/raw"); + let raw_url = format!("{PASTE_GG}{PASTES_ENDPOINT}/{paste_id}/files/{file_id}/raw"); - let resp = REQWEST_CLIENT - .execute(REQWEST_CLIENT.get(&raw_url).build()?) - .await?; - let status = resp.status(); + let resp = REQWEST_CLIENT + .execute(REQWEST_CLIENT.get(&raw_url).build()?) + .await?; + let status = resp.status(); - if status != StatusCode::OK { - return Err(eyre!( - "Couldn't get file {file_id} from paste {paste_id} with status {status}!" - )); - } + if status != StatusCode::OK { + return Err(eyre!( + "Couldn't get file {file_id} from paste {paste_id} with status {status}!" + )); + } - let text = resp.text().await?; + let text = resp.text().await?; - Ok(Some(text)) + Ok(Some(text)) } diff --git a/src/handlers/event/analyze_logs/providers/pastebin.rs b/src/handlers/event/analyze_logs/providers/pastebin.rs index b8dc0bb..0d870ab 100644 --- a/src/handlers/event/analyze_logs/providers/pastebin.rs +++ b/src/handlers/event/analyze_logs/providers/pastebin.rs @@ -6,21 +6,21 @@ use regex::Regex; use reqwest::StatusCode; static REGEX: Lazy = - Lazy::new(|| Regex::new(r"https://pastebin\.com(?:/raw)?/(\w+)").unwrap()); + Lazy::new(|| Regex::new(r"https://pastebin\.com(?:/raw)?/(\w+)").unwrap()); pub async fn find(content: &str) -> Result> { - let Some(captures) = REGEX.captures(content) else { - return Ok(None); - }; + let Some(captures) = REGEX.captures(content) else { + return Ok(None); + }; - let url = format!("https://pastebin.com/raw/{}", &captures[1]); - let request = REQWEST_CLIENT.get(&url).build()?; - let response = REQWEST_CLIENT.execute(request).await?; - let status = response.status(); + let url = format!("https://pastebin.com/raw/{}", &captures[1]); + let request = REQWEST_CLIENT.get(&url).build()?; + let response = REQWEST_CLIENT.execute(request).await?; + let status = response.status(); - if let StatusCode::OK = status { - Ok(Some(response.text().await?)) - } else { - Err(eyre!("Failed to fetch paste from {url} with {status}")) - } + if let StatusCode::OK = status { + Ok(Some(response.text().await?)) + } else { + Err(eyre!("Failed to fetch paste from {url} with {status}")) + } } diff --git a/src/handlers/event/delete_on_reaction.rs b/src/handlers/event/delete_on_reaction.rs index dddcc9a..c17deb5 100644 --- a/src/handlers/event/delete_on_reaction.rs +++ b/src/handlers/event/delete_on_reaction.rs @@ -2,24 +2,24 @@ use color_eyre::eyre::{Context as _, Result}; use poise::serenity_prelude::{Context, InteractionType, Reaction}; pub async fn handle(ctx: &Context, reaction: &Reaction) -> Result<()> { - let user = reaction - .user(ctx) - .await - .wrap_err_with(|| "Couldn't fetch user from reaction!")?; + let user = reaction + .user(ctx) + .await + .wrap_err_with(|| "Couldn't fetch user from reaction!")?; - let message = reaction - .message(ctx) - .await - .wrap_err_with(|| "Couldn't fetch message from reaction!")?; + let message = reaction + .message(ctx) + .await + .wrap_err_with(|| "Couldn't fetch message from reaction!")?; - if let Some(interaction) = &message.interaction { - if interaction.kind == InteractionType::ApplicationCommand - && interaction.user == user - && reaction.emoji.unicode_eq("❌") - { - message.delete(ctx).await?; - } - } + if let Some(interaction) = &message.interaction { + if interaction.kind == InteractionType::ApplicationCommand + && interaction.user == user + && reaction.emoji.unicode_eq("❌") + { + message.delete(ctx).await?; + } + } - Ok(()) + Ok(()) } diff --git a/src/handlers/event/eta.rs b/src/handlers/event/eta.rs index e62d22a..8480189 100644 --- a/src/handlers/event/eta.rs +++ b/src/handlers/event/eta.rs @@ -8,15 +8,15 @@ use regex::Regex; static ETA_REGEX: Lazy = Lazy::new(|| Regex::new(r"\beta\b").unwrap()); pub async fn handle(ctx: &Context, message: &Message) -> Result<()> { - if !ETA_REGEX.is_match(&message.content) { - return Ok(()); - } + if !ETA_REGEX.is_match(&message.content) { + return Ok(()); + } - let response = format!( - "{} <:pofat:1031701005559144458>", - utils::random_choice(consts::ETA_MESSAGES)? - ); + let response = format!( + "{} <:pofat:1031701005559144458>", + utils::random_choice(consts::ETA_MESSAGES)? + ); - message.reply(ctx, response).await?; - Ok(()) + message.reply(ctx, response).await?; + Ok(()) } diff --git a/src/handlers/event/expand_link.rs b/src/handlers/event/expand_link.rs index 0863115..30504f1 100644 --- a/src/handlers/event/expand_link.rs +++ b/src/handlers/event/expand_link.rs @@ -4,25 +4,25 @@ use poise::serenity_prelude::{Context, Message}; use crate::utils; pub async fn handle(ctx: &Context, message: &Message) -> Result<()> { - let embeds = utils::resolve_message(ctx, message).await?; + let embeds = utils::resolve_message(ctx, message).await?; - // TOOD getchoo: actually reply to user - // ...not sure why Message doesn't give me a builder in reply() or equivalents - let our_channel = message - .channel(ctx) - .await - .wrap_err_with(|| "Couldn't get channel from message!")? - .guild() - .ok_or_else(|| eyre!("Couldn't convert to GuildChannel!"))?; + // TOOD getchoo: actually reply to user + // ...not sure why Message doesn't give me a builder in reply() or equivalents + let our_channel = message + .channel(ctx) + .await + .wrap_err_with(|| "Couldn't get channel from message!")? + .guild() + .ok_or_else(|| eyre!("Couldn't convert to GuildChannel!"))?; - if !embeds.is_empty() { - our_channel - .send_message(ctx, |m| { - m.set_embeds(embeds) - .allowed_mentions(|am| am.replied_user(false)) - }) - .await?; - } + if !embeds.is_empty() { + our_channel + .send_message(ctx, |m| { + m.set_embeds(embeds) + .allowed_mentions(|am| am.replied_user(false)) + }) + .await?; + } - Ok(()) + Ok(()) } diff --git a/src/handlers/event/mod.rs b/src/handlers/event/mod.rs index 57f614a..8d82984 100644 --- a/src/handlers/event/mod.rs +++ b/src/handlers/event/mod.rs @@ -13,53 +13,53 @@ pub mod pluralkit; mod support_onboard; pub async fn handle( - ctx: &Context, - event: &Event<'_>, - _framework: FrameworkContext<'_, Data, Report>, - data: &Data, + ctx: &Context, + event: &Event<'_>, + _framework: FrameworkContext<'_, Data, Report>, + data: &Data, ) -> Result<()> { - match event { - Event::Ready { data_about_bot } => { - info!("Logged in as {}!", data_about_bot.user.name); + match event { + Event::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 activity = Activity::playing(format!("Minecraft {}", latest_minecraft_version)); + let latest_minecraft_version = api::prism_meta::get_latest_minecraft_version().await?; + let activity = Activity::playing(format!("Minecraft {}", latest_minecraft_version)); - info!("Setting presence to activity {activity:#?}"); - ctx.set_presence(Some(activity), OnlineStatus::Online).await; - } + info!("Setting presence to activity {activity:#?}"); + ctx.set_presence(Some(activity), OnlineStatus::Online).await; + } - Event::Message { new_message } => { - // ignore new messages from bots - // NOTE: the webhook_id check allows us to still respond to PK users - if new_message.author.bot && new_message.webhook_id.is_none() { - debug!("Ignoring message {} from bot", new_message.id); - return Ok(()); - } + Event::Message { new_message } => { + // ignore new messages from bots + // NOTE: the webhook_id check allows us to still respond to PK users + if new_message.author.bot && new_message.webhook_id.is_none() { + debug!("Ignoring message {} from bot", new_message.id); + return Ok(()); + } - // detect PK users first to make sure we don't respond to unproxied messages - 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, data).await?; - if data.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(()); - } + if data.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?; - analyze_logs::handle(ctx, new_message, data).await?; - } + eta::handle(ctx, new_message).await?; + expand_link::handle(ctx, new_message).await?; + analyze_logs::handle(ctx, new_message, data).await?; + } - Event::ReactionAdd { add_reaction } => { - delete_on_reaction::handle(ctx, add_reaction).await? - } + Event::ReactionAdd { add_reaction } => { + delete_on_reaction::handle(ctx, add_reaction).await? + } - Event::ThreadCreate { thread } => support_onboard::handle(ctx, thread).await?, + Event::ThreadCreate { thread } => support_onboard::handle(ctx, thread).await?, - _ => {} - } + _ => {} + } - Ok(()) + Ok(()) } diff --git a/src/handlers/event/pluralkit.rs b/src/handlers/event/pluralkit.rs index b44aff0..0fdaf3c 100644 --- a/src/handlers/event/pluralkit.rs +++ b/src/handlers/event/pluralkit.rs @@ -9,34 +9,34 @@ use tokio::time::sleep; const PK_DELAY_SEC: Duration = Duration::from_secs(1000); pub async fn is_message_proxied(message: &Message) -> Result { - debug!( - "Waiting on PluralKit API for {} seconds", - PK_DELAY_SEC.as_secs() - ); - sleep(PK_DELAY_SEC).await; + debug!( + "Waiting on PluralKit API for {} seconds", + PK_DELAY_SEC.as_secs() + ); + sleep(PK_DELAY_SEC).await; - let proxied = api::pluralkit::get_sender(message.id).await.is_ok(); + let proxied = api::pluralkit::get_sender(message.id).await.is_ok(); - Ok(proxied) + Ok(proxied) } pub async fn handle(_ctx: &Context, msg: &Message, data: &Data) -> Result<()> { - if msg.webhook_id.is_some() { - debug!( - "Message {} has a webhook ID. Checking if it was sent through PluralKit", - msg.id - ); + if msg.webhook_id.is_some() { + debug!( + "Message {} has a webhook ID. Checking if it was sent through PluralKit", + msg.id + ); - debug!( - "Waiting on PluralKit API for {} seconds", - PK_DELAY_SEC.as_secs() - ); - sleep(PK_DELAY_SEC).await; + debug!( + "Waiting on PluralKit API for {} seconds", + PK_DELAY_SEC.as_secs() + ); + sleep(PK_DELAY_SEC).await; - if let Ok(sender) = api::pluralkit::get_sender(msg.id).await { - data.storage.store_user_plurality(sender).await?; - } - } + if let Ok(sender) = api::pluralkit::get_sender(msg.id).await { + data.storage.store_user_plurality(sender).await?; + } + } - Ok(()) + Ok(()) } diff --git a/src/handlers/event/support_onboard.rs b/src/handlers/event/support_onboard.rs index 9b6bc16..e83a239 100644 --- a/src/handlers/event/support_onboard.rs +++ b/src/handlers/event/support_onboard.rs @@ -3,41 +3,41 @@ use log::*; use poise::serenity_prelude::{ChannelType, Context, GuildChannel}; pub async fn handle(ctx: &Context, thread: &GuildChannel) -> Result<()> { - if thread.kind != ChannelType::PublicThread { - return Ok(()); - } + if thread.kind != ChannelType::PublicThread { + return Ok(()); + } - let parent_id = thread - .parent_id - .ok_or_else(|| eyre!("Couldn't get parent ID from thread {}!", thread.name))?; + let parent_id = thread + .parent_id + .ok_or_else(|| eyre!("Couldn't get parent ID from thread {}!", thread.name))?; - let parent_channel = ctx - .cache - .guild_channel(parent_id) - .ok_or_else(|| eyre!("Couldn't get GuildChannel {}!", parent_id))?; + let parent_channel = ctx + .cache + .guild_channel(parent_id) + .ok_or_else(|| eyre!("Couldn't get GuildChannel {}!", parent_id))?; - if parent_channel.name != "support" { - debug!("Not posting onboarding message to threads outside of support"); - return Ok(()); - } + if parent_channel.name != "support" { + debug!("Not posting onboarding message to threads outside of support"); + return Ok(()); + } - let owner = thread - .owner_id - .ok_or_else(|| eyre!("Couldn't get owner of thread!"))?; + let owner = thread + .owner_id + .ok_or_else(|| eyre!("Couldn't get owner of thread!"))?; - let msg = format!( + let msg = format!( "<@{}> We've received your support ticket! {} {}", owner, "Please upload your logs and post the link here if possible (run `tag log` to find out how).", "Please don't ping people for support questions, unless you have their permission." ); - thread - .send_message(ctx, |m| { - m.content(msg) - .allowed_mentions(|am| am.replied_user(true).users(Vec::from([owner]))) - }) - .await?; + thread + .send_message(ctx, |m| { + m.content(msg) + .allowed_mentions(|am| am.replied_user(true).users(Vec::from([owner]))) + }) + .await?; - Ok(()) + Ok(()) } diff --git a/src/main.rs b/src/main.rs index f3b47e8..4f83a70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use color_eyre::owo_colors::OwoColorize; use log::*; use poise::{ - serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions, + serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions, }; use serenity::ShardManager; @@ -34,108 +34,108 @@ type Context<'a> = poise::Context<'a, Data, Report>; #[derive(Clone)] pub struct Data { - config: Config, - storage: Storage, - octocrab: Arc, + config: Config, + storage: Storage, + octocrab: Arc, } impl Data { - pub fn new() -> Result { - let config = Config::new_from_env()?; - let storage = Storage::new(&config.redis_url)?; - let octocrab = octocrab::instance(); + pub fn new() -> Result { + let config = Config::new_from_env()?; + let storage = Storage::new(&config.redis_url)?; + let octocrab = octocrab::instance(); - Ok(Self { - config, - storage, - octocrab, - }) - } + Ok(Self { + config, + storage, + octocrab, + }) + } } async fn setup( - ctx: &serenity::Context, - _ready: &serenity::Ready, - framework: &Framework, + ctx: &serenity::Context, + _ready: &serenity::Ready, + framework: &Framework, ) -> Result { - let data = Data::new()?; + let data = Data::new()?; - // test redis connection - let mut client = data.storage.client.clone(); + // test redis connection + let mut client = data.storage.client.clone(); - if !client.check_connection() { - return Err(eyre!( - "Couldn't connect to storage! Is your daemon running?" - )); - } + if !client.check_connection() { + return Err(eyre!( + "Couldn't connect to storage! Is your daemon running?" + )); + } - poise::builtins::register_globally(ctx, &framework.options().commands).await?; - info!("Registered global commands!"); + poise::builtins::register_globally(ctx, &framework.options().commands).await?; + info!("Registered global commands!"); - Ok(data) + Ok(data) } async fn handle_shutdown(shard_manager: Arc>, reason: &str) { - warn!("{reason}! Shutting down bot..."); - shard_manager.lock().await.shutdown_all().await; - println!("{}", "Everything is shutdown. Goodbye!".green()) + warn!("{reason}! Shutting down bot..."); + shard_manager.lock().await.shutdown_all().await; + println!("{}", "Everything is shutdown. Goodbye!".green()) } #[tokio::main] async fn main() -> Result<()> { - dotenvy::dotenv().ok(); - color_eyre::install()?; - env_logger::init(); + dotenvy::dotenv().ok(); + color_eyre::install()?; + env_logger::init(); - let token = std::env::var("DISCORD_BOT_TOKEN") - .wrap_err_with(|| "Couldn't find bot token in environment!")?; + let token = std::env::var("DISCORD_BOT_TOKEN") + .wrap_err_with(|| "Couldn't find bot token in environment!")?; - let intents = - serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT; + let intents = + serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT; - let options = FrameworkOptions { - commands: commands::to_global_commands(), + let options = FrameworkOptions { + commands: commands::to_global_commands(), - on_error: |error| Box::pin(handlers::handle_error(error)), + on_error: |error| Box::pin(handlers::handle_error(error)), - command_check: Some(|ctx| { - Box::pin(async move { Ok(ctx.author().id != ctx.framework().bot_id) }) - }), + command_check: Some(|ctx| { + Box::pin(async move { Ok(ctx.author().id != ctx.framework().bot_id) }) + }), - event_handler: |ctx, event, framework, data| { - Box::pin(handlers::handle_event(ctx, event, framework, data)) - }, + event_handler: |ctx, event, framework, data| { + Box::pin(handlers::handle_event(ctx, event, framework, data)) + }, - prefix_options: PrefixFrameworkOptions { - prefix: Some("r".into()), - edit_tracker: Some(EditTracker::for_timespan(Duration::from_secs(3600))), - ..Default::default() - }, + prefix_options: PrefixFrameworkOptions { + prefix: Some("r".into()), + edit_tracker: Some(EditTracker::for_timespan(Duration::from_secs(3600))), + ..Default::default() + }, - ..Default::default() - }; + ..Default::default() + }; - let framework = Framework::builder() - .token(token) - .intents(intents) - .options(options) - .setup(|ctx, ready, framework| Box::pin(setup(ctx, ready, framework))) - .build() - .await - .wrap_err_with(|| "Failed to build framework!")?; + let framework = Framework::builder() + .token(token) + .intents(intents) + .options(options) + .setup(|ctx, ready, framework| Box::pin(setup(ctx, ready, framework))) + .build() + .await + .wrap_err_with(|| "Failed to build framework!")?; - let shard_manager = framework.shard_manager().clone(); - let mut sigterm = signal(SignalKind::terminate())?; + let shard_manager = framework.shard_manager().clone(); + let mut sigterm = signal(SignalKind::terminate())?; - tokio::select! { - result = framework.start() => result.map_err(Report::from), - _ = sigterm.recv() => { - handle_shutdown(shard_manager, "Recieved SIGTERM").await; - std::process::exit(0); - } - _ = ctrl_c() => { - handle_shutdown(shard_manager, "Interrupted").await; - std::process::exit(130); - } - } + tokio::select! { + result = framework.start() => result.map_err(Report::from), + _ = sigterm.recv() => { + handle_shutdown(shard_manager, "Recieved SIGTERM").await; + std::process::exit(0); + } + _ = ctrl_c() => { + handle_shutdown(shard_manager, "Interrupted").await; + std::process::exit(130); + } + } } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 2cfbe33..022a051 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -10,105 +10,105 @@ const LAUNCHER_VERSION_KEY: &str = "launcher-version-v1"; #[derive(Clone, Debug)] pub struct Storage { - pub client: Client, + pub client: Client, } impl Storage { - pub fn new(redis_url: &str) -> Result { - let client = Client::open(redis_url)?; + pub fn new(redis_url: &str) -> Result { + let client = Client::open(redis_url)?; - Ok(Self { client }) - } + Ok(Self { client }) + } - /* - these are mainly light abstractions to avoid the `let mut con` - boilerplate, as well as not require the caller to format the - strings for keys - */ + /* + these are mainly light abstractions to avoid the `let mut con` + boilerplate, as well as not require the caller to format the + strings for keys + */ - async fn get_key(&self, key: &str) -> Result - where - T: FromRedisValue, - { - debug!("Getting key {key}"); + async fn get_key(&self, key: &str) -> Result + where + T: FromRedisValue, + { + debug!("Getting key {key}"); - let mut con = self.client.get_async_connection().await?; - let res: T = con.get(key).await?; + let mut con = self.client.get_async_connection().await?; + let res: T = con.get(key).await?; - Ok(res) - } + Ok(res) + } - async fn set_key<'a>( - &self, - key: &str, - value: impl ToRedisArgs + Debug + Send + Sync + 'a, - ) -> Result<()> { - debug!("Creating key {key}:\n{value:#?}"); + 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?; + let mut con = self.client.get_async_connection().await?; + con.set(key, value).await?; - Ok(()) - } + Ok(()) + } - async fn key_exists(&self, key: &str) -> Result { - debug!("Checking if key {key} exists"); + async fn key_exists(&self, key: &str) -> Result { + debug!("Checking if key {key} exists"); - let mut con = self.client.get_async_connection().await?; - let exists: u64 = con.exists(key).await?; + let mut con = self.client.get_async_connection().await?; + let exists: u64 = con.exists(key).await?; - Ok(exists > 0) - } + Ok(exists > 0) + } - async fn delete_key(&self, key: &str) -> Result<()> { - debug!("Deleting key {key}"); + 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?; + let mut con = self.client.get_async_connection().await?; + con.del(key).await?; - Ok(()) - } + Ok(()) + } - async fn expire_key(&self, key: &str, expire_seconds: usize) -> Result<()> { - debug!("Expiring key {key} in {expire_seconds}"); + async fn expire_key(&self, key: &str, expire_seconds: usize) -> Result<()> { + debug!("Expiring key {key} in {expire_seconds}"); - let mut con = self.client.get_async_connection().await?; - con.expire(key, expire_seconds).await?; + let mut con = self.client.get_async_connection().await?; + con.expire(key, expire_seconds).await?; - Ok(()) - } + Ok(()) + } - pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> { - info!("Marking {sender} as a PluralKit user"); - let key = format!("{PK_KEY}:{sender}"); + pub async fn store_user_plurality(&self, sender: UserId) -> Result<()> { + info!("Marking {sender} as a PluralKit user"); + let key = format!("{PK_KEY}:{sender}"); - // Just store some value. We only care about the presence of this key - self.set_key(&key, 0).await?; - self.expire_key(&key, 7 * 24 * 60 * 60).await?; + // Just store some value. We only care about the presence of this key + self.set_key(&key, 0).await?; + self.expire_key(&key, 7 * 24 * 60 * 60).await?; - Ok(()) - } + Ok(()) + } - pub async fn is_user_plural(&self, user_id: UserId) -> Result { - let key = format!("{PK_KEY}:{user_id}"); - self.key_exists(&key).await - } + pub async fn is_user_plural(&self, user_id: UserId) -> Result { + let key = format!("{PK_KEY}:{user_id}"); + self.key_exists(&key).await + } - pub async fn cache_launcher_version(&self, version: &str) -> Result<()> { - self.set_key(LAUNCHER_VERSION_KEY, version).await?; + pub async fn cache_launcher_version(&self, version: &str) -> Result<()> { + self.set_key(LAUNCHER_VERSION_KEY, version).await?; - Ok(()) - } + Ok(()) + } - pub async fn get_launcher_version(&self) -> Result { - let res = self.get_key(LAUNCHER_VERSION_KEY).await?; + pub async fn get_launcher_version(&self) -> Result { + let res = self.get_key(LAUNCHER_VERSION_KEY).await?; - Ok(res) - } + Ok(res) + } - pub async fn launcher_version_is_cached(&self) -> Result { - let res = self.key_exists(LAUNCHER_VERSION_KEY).await?; + pub async fn launcher_version_is_cached(&self) -> Result { + let res = self.key_exists(LAUNCHER_VERSION_KEY).await?; - Ok(res) - } + Ok(res) + } } diff --git a/src/tags.rs b/src/tags.rs index dacdd01..ab77b1e 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -6,15 +6,15 @@ pub const TAG_DIR: &str = "tags"; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TagFrontmatter { - pub title: String, - pub color: Option, - pub image: Option, - pub fields: Option>, + pub title: String, + pub color: Option, + pub image: Option, + pub fields: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Tag { - pub content: String, - pub file_name: String, - pub frontmatter: TagFrontmatter, + pub content: String, + pub file_name: String, + pub frontmatter: TagFrontmatter, } diff --git a/src/utils/macros.rs b/src/utils/macros.rs index a473959..c167420 100644 --- a/src/utils/macros.rs +++ b/src/utils/macros.rs @@ -1,6 +1,6 @@ #[macro_export] macro_rules! required_var { - ($name: expr) => { - std::env::var($name).wrap_err_with(|| format!("Couldn't find {} in environment!", $name))? - }; + ($name: expr) => { + std::env::var($name).wrap_err_with(|| format!("Couldn't find {} in environment!", $name))? + }; } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 700ce3d..e8928e2 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -11,10 +11,10 @@ pub use resolve_message::resolve as resolve_message; * chooses a random element from an array */ pub fn random_choice(arr: [&str; N]) -> Result { - let mut rng = rand::thread_rng(); - let resp = arr - .choose(&mut rng) - .ok_or_else(|| eyre!("Couldn't choose random object from array:\n{arr:#?}!"))?; + let mut rng = rand::thread_rng(); + let resp = arr + .choose(&mut rng) + .ok_or_else(|| eyre!("Couldn't choose random object from array:\n{arr:#?}!"))?; - Ok((*resp).to_string()) + Ok((*resp).to_string()) } diff --git a/src/utils/resolve_message.rs b/src/utils/resolve_message.rs index cbf7a82..165e8c9 100644 --- a/src/utils/resolve_message.rs +++ b/src/utils/resolve_message.rs @@ -5,120 +5,120 @@ use poise::serenity_prelude::{ChannelType, Colour, Context, CreateEmbed, Message use regex::Regex; static MESSAGE_PATTERN: Lazy = Lazy::new(|| { - Regex::new(r"/(https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?\d+)\/(?\d+)\/(?\d+)/g;").unwrap() + Regex::new(r"/(https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?\d+)\/(?\d+)\/(?\d+)/g;").unwrap() }); pub fn find_first_image(msg: &Message) -> Option { - msg.attachments - .iter() - .find(|a| { - a.content_type - .as_ref() - .unwrap_or(&"".to_string()) - .starts_with("image/") - }) - .map(|res| res.url.clone()) + msg.attachments + .iter() + .find(|a| { + a.content_type + .as_ref() + .unwrap_or(&"".to_string()) + .starts_with("image/") + }) + .map(|res| res.url.clone()) } pub async fn resolve(ctx: &Context, msg: &Message) -> Result> { - let matches = MESSAGE_PATTERN.captures_iter(&msg.content); - let mut embeds: Vec = vec![]; + let matches = MESSAGE_PATTERN.captures_iter(&msg.content); + let mut embeds: Vec = vec![]; - for captured in matches.take(3) { - // don't leak messages from other servers - if let Some(server_id) = captured.get(0) { - let other_server: u64 = server_id.as_str().parse().unwrap_or_default(); - let current_id = msg.guild_id.unwrap_or_default(); + for captured in matches.take(3) { + // don't leak messages from other servers + if let Some(server_id) = captured.get(0) { + let other_server: u64 = server_id.as_str().parse().unwrap_or_default(); + let current_id = msg.guild_id.unwrap_or_default(); - if &other_server != current_id.as_u64() { - debug!("Not resolving message of other guild."); - continue; - } - } else { - warn!("Couldn't find server_id from Discord link! Not resolving message to be safe"); - continue; - } + if &other_server != current_id.as_u64() { + debug!("Not resolving message of other guild."); + continue; + } + } else { + warn!("Couldn't find server_id from Discord link! Not resolving message to be safe"); + continue; + } - if let Some(channel_id) = captured.get(1) { - let parsed: u64 = channel_id.as_str().parse().unwrap_or_default(); - let req_channel = ctx - .cache - .channel(parsed) - .ok_or_else(|| eyre!("Couldn't get channel_id from Discord regex!"))? - .guild() - .ok_or_else(|| { - eyre!("Couldn't convert to GuildChannel from channel_id {parsed}!") - })?; + if let Some(channel_id) = captured.get(1) { + let parsed: u64 = channel_id.as_str().parse().unwrap_or_default(); + let req_channel = ctx + .cache + .channel(parsed) + .ok_or_else(|| eyre!("Couldn't get channel_id from Discord regex!"))? + .guild() + .ok_or_else(|| { + eyre!("Couldn't convert to GuildChannel from channel_id {parsed}!") + })?; - if !req_channel.is_text_based() { - debug!("Not resolving message is non-text-based channel."); - continue; - } + if !req_channel.is_text_based() { + debug!("Not resolving message is non-text-based channel."); + continue; + } - if req_channel.kind == ChannelType::PrivateThread { - if let Some(id) = req_channel.parent_id { - let parent = ctx.cache.guild_channel(id).ok_or_else(|| { - eyre!("Couldn't get parent channel {id} for thread {req_channel}!") - })?; - let parent_members = parent.members(ctx).await.unwrap_or_default(); + if req_channel.kind == ChannelType::PrivateThread { + if let Some(id) = req_channel.parent_id { + let parent = ctx.cache.guild_channel(id).ok_or_else(|| { + eyre!("Couldn't get parent channel {id} for thread {req_channel}!") + })?; + let parent_members = parent.members(ctx).await.unwrap_or_default(); - if !parent_members.iter().any(|m| m.user.id == msg.author.id) { - debug!("Not resolving message for user not a part of a private thread."); - continue; - } - } - } else if req_channel - .members(ctx) - .await? - .iter() - .any(|m| m.user.id == msg.author.id) - { - debug!("Not resolving for message for user not a part of a channel"); - continue; - } + if !parent_members.iter().any(|m| m.user.id == msg.author.id) { + debug!("Not resolving message for user not a part of a private thread."); + continue; + } + } + } else if req_channel + .members(ctx) + .await? + .iter() + .any(|m| m.user.id == msg.author.id) + { + debug!("Not resolving for message for user not a part of a channel"); + continue; + } - let message_id: u64 = captured - .get(2) - .ok_or_else(|| eyre!("Couldn't get message_id from Discord regex!"))? - .as_str() - .parse() - .wrap_err_with(|| { - eyre!("Couldn't parse message_id from Discord regex as a MessageId!") - })?; + let message_id: u64 = captured + .get(2) + .ok_or_else(|| eyre!("Couldn't get message_id from Discord regex!"))? + .as_str() + .parse() + .wrap_err_with(|| { + eyre!("Couldn't parse message_id from Discord regex as a MessageId!") + })?; - let original_message = req_channel.message(ctx, message_id).await?; - let mut embed = CreateEmbed::default(); - embed - .author(|a| { - a.name(original_message.author.tag()) - .icon_url(original_message.author.default_avatar_url()) - }) - .color(Colour::BLITZ_BLUE) - .timestamp(original_message.timestamp) - .footer(|f| f.text(format!("#{}", req_channel.name))) - .description(format!( - "{}\n\n[Jump to original message]({})", - original_message.content, - original_message.link() - )); + let original_message = req_channel.message(ctx, message_id).await?; + let mut embed = CreateEmbed::default(); + embed + .author(|a| { + a.name(original_message.author.tag()) + .icon_url(original_message.author.default_avatar_url()) + }) + .color(Colour::BLITZ_BLUE) + .timestamp(original_message.timestamp) + .footer(|f| f.text(format!("#{}", req_channel.name))) + .description(format!( + "{}\n\n[Jump to original message]({})", + original_message.content, + original_message.link() + )); - if !original_message.attachments.is_empty() { - embed.fields(original_message.attachments.iter().map(|a| { - ( - "Attachments".to_string(), - format!("[{}]({})", a.filename, a.url), - false, - ) - })); + if !original_message.attachments.is_empty() { + embed.fields(original_message.attachments.iter().map(|a| { + ( + "Attachments".to_string(), + format!("[{}]({})", a.filename, a.url), + false, + ) + })); - if let Some(image) = find_first_image(msg) { - embed.image(image); - } - } + if let Some(image) = find_first_image(msg) { + embed.image(image); + } + } - embeds.push(embed); - } - } + embeds.push(embed); + } + } - Ok(embeds) + Ok(embeds) }