rewrite errors

This commit is contained in:
Stachelbeere1248 2024-11-12 01:00:22 +01:00
parent c9ea6a393a
commit c1ab3424bd
Signed by: Stachelbeere1248
SSH key fingerprint: SHA256:IozEKdw2dB8TZxkpPdMxcWSoWTIMwoLaCcZJ1AJnY2o
12 changed files with 135 additions and 103 deletions

View file

@ -2,5 +2,10 @@
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="RsMainFunctionNotFound" enabled="false" level="ERROR" enabled_by_default="false" /> <inspection_tool class="RsMainFunctionNotFound" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile> </profile>
</component> </component>

View file

@ -15,7 +15,8 @@ use serenity::{
}; };
use sqlx::{Pool, query_as, Sqlite}; use sqlx::{Pool, query_as, Sqlite};
use crate::{Context, Error}; use crate::Context;
use crate::error::Error;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Links { struct Links {
@ -53,7 +54,12 @@ impl Uuid {
async fn fetch(ign: &str) -> Result<Self, Error> { async fn fetch(ign: &str) -> Result<Self, Error> {
let url: String = format!("https://api.mojang.com/users/profiles/minecraft/{ign}"); let url: String = format!("https://api.mojang.com/users/profiles/minecraft/{ign}");
let response: Response = reqwest::get(url).await?; let response: Response = reqwest::get(url).await?;
match response.error_for_status() { let response_text = response.error_for_status()?.text().await.unwrap();
let uuid = (serde_json::from_str(response_text.as_str())
as Result<MojangPlayer, JsonError>)
.map(|mojang_player: MojangPlayer| Uuid { uuid: mojang_player.id })?;
Ok(uuid)
/*match response.error_for_status() {
Ok(res) => { Ok(res) => {
let response_text = res.text().await.unwrap(); let response_text = res.text().await.unwrap();
let uuid = (serde_json::from_str(response_text.as_str()) let uuid = (serde_json::from_str(response_text.as_str())
@ -64,7 +70,7 @@ impl Uuid {
Err(why) => Err(Error::from(format!( Err(why) => Err(Error::from(format!(
"Mojang returned an error. Please make sure to enter a valid Minecraft username.\n\n\ "Mojang returned an error. Please make sure to enter a valid Minecraft username.\n\n\
Details: {}", why).as_str())), Details: {}", why).as_str())),
} }*/
} }
} }
#[derive(PartialEq)] #[derive(PartialEq)]
@ -75,19 +81,11 @@ impl DiscordId {
async fn matches_fetch(user: &User, uuid: &str, client: &Client) -> Result<bool, Error> { async fn matches_fetch(user: &User, uuid: &str, client: &Client) -> Result<bool, Error> {
let url: String = format!("https://api.hypixel.net/v2/player?uuid={}", uuid); let url: String = format!("https://api.hypixel.net/v2/player?uuid={}", uuid);
let response: Response = client.get(url).send().await?; let response: Response = client.get(url).send().await?;
match response.error_for_status() { let response_text = response.error_for_status()?.text().await.unwrap();
Ok(res) => { let matches = (serde_json::from_str(response_text.as_str())
let response_text = res.text().await.unwrap(); as Result<HypixelResponse, JsonError>)
let matches = (serde_json::from_str(response_text.as_str()) .map(|hypixel_response: HypixelResponse| user.name == hypixel_response.player.social_media.links.discord)?;
as Result<HypixelResponse, JsonError>) Ok(matches)
.map(|hypixel_response: HypixelResponse| user.name == hypixel_response.player.social_media.links.discord)?;
Ok(matches)
}
Err(why) => {
println!("Hypixel issue: {}", why);
Err(Error::from("Hypixel returned an error."))
}
}
} }
} }
impl<'a, R: sqlx::Row> sqlx::FromRow<'a, R> for DiscordId impl<'a, R: sqlx::Row> sqlx::FromRow<'a, R> for DiscordId
@ -116,17 +114,17 @@ impl Link {
minecraft_accounts: vec![], minecraft_accounts: vec![],
} }
} }
async fn minecraft(mut self, pool: &Pool<Sqlite>) -> Self { async fn minecraft(mut self, pool: &Pool<Sqlite>) -> Result<Self, Error> {
let link_id: i16 = self.link_id.cast_signed(); let link_id: i16 = self.link_id.cast_signed();
self.minecraft_accounts = query_as(format!("SELECT minecraft_uuid AS uuid FROM minecraft_links WHERE link_id = {link_id};").as_str()) self.minecraft_accounts = query_as(format!("SELECT minecraft_uuid AS uuid FROM minecraft_links WHERE link_id = {link_id};").as_str())
.fetch_all(pool).await.expect("Error getting Minecraft UUIDs."); .fetch_all(pool).await?;
self Ok(self)
} }
async fn discord(mut self, pool: &Pool<Sqlite>) -> Self { async fn discord(mut self, pool: &Pool<Sqlite>) -> Result<Self, Error> {
let link_id: i16 = self.link_id.cast_signed(); let link_id: i16 = self.link_id.cast_signed();
self.discord_ids = query_as(format!("SELECT discord_id FROM discord_links WHERE link_id = {link_id};").as_str()) self.discord_ids = query_as(format!("SELECT discord_id FROM discord_links WHERE link_id = {link_id};").as_str())
.fetch_all(pool).await.expect("Error getting Discord IDs."); .fetch_all(pool).await?;
self Ok(self)
} }
} }
#[poise::command(slash_command, subcommands("add", "list"))] #[poise::command(slash_command, subcommands("add", "list"))]
@ -148,26 +146,23 @@ pub(crate) async fn add<'a>(
ctx.defer_ephemeral().await?; ctx.defer_ephemeral().await?;
let user = user.unwrap_or(ctx.author().clone()); let user = user.unwrap_or(ctx.author().clone());
let uuid = Uuid::fetch(ign.as_str()).await?; let uuid = Uuid::fetch(ign.as_str()).await?;
let valid = DiscordId::matches_fetch(&user, uuid.get(), &ctx.data().hypixel_api_client).await match DiscordId::matches_fetch(&user, uuid.get(), &ctx.data().hypixel_api_client).await? {
.expect("This Minecraft account does not have a Discord account linked.");
match valid {
true => { true => {
let r = CreateReply::default().ephemeral(false);
let pool: Pool<Sqlite> = ctx.data().sqlite_pool.clone(); let pool: Pool<Sqlite> = ctx.data().sqlite_pool.clone();
let (status, link_id) = match link_id_from_minecraft(&pool, uuid.get()).await { let (status, link_id) = match link_id_from_minecraft(&pool, uuid.get()).await {
None => { None => {
match link_id_from_discord(&pool, user.id.get()).await { match link_id_from_discord(&pool, user.id.get()).await {
None => { None => {
let id = new_link_id(&pool).await; let id = new_link_id(&pool).await?;
sqlx::query(format!("INSERT INTO discord_links VALUES ({}, {});", id.cast_signed(), user.id.get()).as_str()) sqlx::query(format!("INSERT INTO discord_links VALUES ({}, {});", id.cast_signed(), user.id.get()).as_str())
.execute(&pool).await.expect("Database Error: inserting new minecraft value"); .execute(&pool).await?;
sqlx::query(format!("INSERT INTO minecraft_links VALUES ({}, \"{}\");", id.cast_signed(), uuid.get()).as_str()) sqlx::query(format!("INSERT INTO minecraft_links VALUES ({}, \"{}\");", id.cast_signed(), uuid.get()).as_str())
.execute(&pool).await.expect("Database Error: inserting new minecraft value"); .execute(&pool).await?;
("Linked your Discord and Minecraft account.", id) ("Linked your Discord and Minecraft account.", id)
} }
Some(dc_id) => { Some(dc_id) => {
sqlx::query(format!("INSERT INTO minecraft_links VALUES ({}, \"{}\");", dc_id.cast_signed(), uuid.get()).as_str()) sqlx::query(format!("INSERT INTO minecraft_links VALUES ({}, \"{}\");", dc_id.cast_signed(), uuid.get()).as_str())
.execute(&pool).await.expect("Database Error: inserting new minecraft value"); .execute(&pool).await?;
("Your Discord account has previously had an account linked. Added the new link.", dc_id) ("Your Discord account has previously had an account linked. Added the new link.", dc_id)
} }
} }
@ -176,22 +171,21 @@ pub(crate) async fn add<'a>(
match link_id_from_discord(&pool, user.id.get()).await { match link_id_from_discord(&pool, user.id.get()).await {
None => { None => {
sqlx::query(format!("INSERT INTO discord_links VALUES ({}, {});", mc_id.cast_signed(), user.id.get()).as_str()) sqlx::query(format!("INSERT INTO discord_links VALUES ({}, {});", mc_id.cast_signed(), user.id.get()).as_str())
.execute(&pool).await.expect("Database Error: inserting new minecraft value"); .execute(&pool).await?;
("Your Minecraft account has previously had an account linked. Added the new link.", mc_id) ("Your Minecraft account has previously had an account linked. Added the new link.", mc_id)
} }
Some(dc_id) => { Some(dc_id) => {
sqlx::query(format!("UPDATE minecraft_links SET link_id = {} WHERE link_id = {};", mc_id.cast_signed(), dc_id.cast_signed()).as_str()) sqlx::query(format!("UPDATE minecraft_links SET link_id = {} WHERE link_id = {};", mc_id.cast_signed(), dc_id.cast_signed()).as_str())
.execute(&pool).await.expect("Database Error: Merging Minecraft Accounts."); .execute(&pool).await?;
sqlx::query(format!("UPDATE discord_links SET link_id = {} WHERE link_id = {};", mc_id.cast_signed(), dc_id.cast_signed()).as_str()) sqlx::query(format!("UPDATE discord_links SET link_id = {} WHERE link_id = {};", mc_id.cast_signed(), dc_id.cast_signed()).as_str())
.execute(&pool).await.expect("Database Error: Merging Discord Accounts."); .execute(&pool).await?;
("Both your Discord and Minecraft account had linked accounts. Merged all account links.", mc_id) ("Both your Discord and Minecraft account had linked accounts. Merged all account links.", mc_id)
} }
} }
} }
}; };
ctx.send(r.content(status)).await?; let link = Link::new(link_id).minecraft(&pool).await?.discord(&pool).await?;
let link = Link::new(link_id).minecraft(&pool).await.discord(&pool).await; let s = list_string(link, user.id.get());
let s = list_string(link, user.id.get()).await;
ChannelId::new(1257776992497959075).send_message( ChannelId::new(1257776992497959075).send_message(
ctx, ctx,
CreateMessage::new() CreateMessage::new()
@ -202,15 +196,14 @@ pub(crate) async fn add<'a>(
CreateButton::new("deny_verification").emoji(ReactionType::from('❌')), CreateButton::new("deny_verification").emoji(ReactionType::from('❌')),
])]), ])]),
).await?; ).await?;
} ctx.send(CreateReply::default().ephemeral(false).content(status)).await?;
Ok(())
},
false => { false => {
let r = CreateReply::default().ephemeral(true) Err(Error::Other(format!("The Discord account linked on Hypixel does not match the specified discord account.\n\
.content(format!("The Discord account linked on Hypixel does not seem to match the specified account.\n\ Please set your linked Discord account on Hypixel to `{}`.", user.name)))
Expected account link: `{}`", user.name));
ctx.send(r).await?;
} }
} }
Ok(())
} }
#[poise::command(slash_command)] #[poise::command(slash_command)]
@ -223,18 +216,18 @@ pub(crate) async fn list(
let r = CreateReply::default().ephemeral(false); let r = CreateReply::default().ephemeral(false);
let pool: Pool<Sqlite> = ctx.data().sqlite_pool.clone(); let pool: Pool<Sqlite> = ctx.data().sqlite_pool.clone();
let link_id = link_id_from_discord(&pool, user.id.get()).await.expect("This user has no linked accounts"); let link_id = link_id_from_discord(&pool, user.id.get()).await.expect("This user has no linked accounts");
let link = Link::new(link_id).minecraft(&pool).await.discord(&pool).await; let link = Link::new(link_id).minecraft(&pool).await?.discord(&pool).await?;
let s = list_string(link, user.id.get()).await; let s = list_string(link, user.id.get());
ctx.send(r.content(s).allowed_mentions(CreateAllowedMentions::new().empty_roles().empty_users())).await?; ctx.send(r.content(s).allowed_mentions(CreateAllowedMentions::new().empty_roles().empty_users())).await?;
Ok(()) Ok(())
} }
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub(crate) async fn remove(_ctx: Context<'_>) -> Result<(), Error> { pub(crate) async fn remove(_ctx: Context<'_>) -> Result<(), Error> {
unreachable!() unimplemented!()
} }
async fn list_string(link: Link, user_id: u64) -> String { fn list_string(link: Link, user_id: u64) -> String {
let mut discord_list = String::from("### Discord:"); let mut discord_list = String::from("### Discord:");
for dc in link.discord_ids { for dc in link.discord_ids {
discord_list.push_str(format!("\n- <@{}>", dc.id).as_str()); discord_list.push_str(format!("\n- <@{}>", dc.id).as_str());
@ -247,26 +240,16 @@ async fn list_string(link: Link, user_id: u64) -> String {
} }
async fn link_id_from_minecraft(pool: &Pool<Sqlite>, minecraft_uuid: &str) -> Option<u16> { async fn link_id_from_minecraft(pool: &Pool<Sqlite>, minecraft_uuid: &str) -> Option<u16> {
return query_as( query_as(format!("SELECT link_id FROM minecraft_links WHERE minecraft_uuid = \"{minecraft_uuid}\" LIMIT 1;").as_str())
format!(
"SELECT link_id FROM minecraft_links WHERE minecraft_uuid = \"{minecraft_uuid}\" LIMIT 1;"
)
.as_str(),
)
.fetch_optional(pool) .fetch_optional(pool)
.await .await.expect("Database error: fetching link id by uuid")
.expect("Database error: fetching link id by uuid") .map(|link_id: LinkId| link_id.link_id.cast_unsigned())
.map(|link_id: LinkId| link_id.link_id.cast_unsigned());
} }
async fn link_id_from_discord(pool: &Pool<Sqlite>, snowflake: u64) -> Option<u16> { async fn link_id_from_discord(pool: &Pool<Sqlite>, snowflake: u64) -> Option<u16> {
let discord_id: i64 = snowflake.cast_signed(); query_as(format!("SELECT link_id FROM discord_links WHERE discord_id = {} LIMIT 1;", snowflake.cast_signed()).as_str())
return query_as(
format!("SELECT link_id FROM discord_links WHERE discord_id = {discord_id} LIMIT 1;").as_str(),
)
.fetch_optional(pool) .fetch_optional(pool)
.await .await.expect("Database error: fetching link id by discord")
.expect("Database error: fetching link id by discord") .map(|link_id: LinkId| link_id.link_id.cast_unsigned())
.map(|link_id: LinkId| link_id.link_id.cast_unsigned());
} }
#[derive(sqlx::FromRow)] #[derive(sqlx::FromRow)]
@ -274,11 +257,9 @@ struct LinkId {
link_id: i16, link_id: i16,
} }
async fn new_link_id(pool: &Pool<Sqlite>) -> u16 { async fn new_link_id(pool: &Pool<Sqlite>) -> Result<u16, Error> {
let result: Result<LinkId, sqlx::Error> = query_as("SELECT MAX(link_id) AS link_id FROM minecraft_links;") let result: LinkId = query_as("SELECT MAX(link_id) AS link_id FROM minecraft_links;")
.fetch_one(pool) .fetch_one(pool)
.await; .await?;
result Ok(result.link_id.cast_unsigned() + 1)
.expect("Database error: fetching new id")
.link_id.cast_unsigned() + 1
} }

View file

@ -1,7 +1,7 @@
use std::string::String; use std::string::String;
use poise::CreateReply;
use crate::{Context, Error}; use crate::Context;
use crate::commands::command_helper; use crate::error::Error;
#[poise::command(slash_command, guild_only, owners_only)] #[poise::command(slash_command, guild_only, owners_only)]
pub(crate) async fn bots( pub(crate) async fn bots(
@ -12,6 +12,7 @@ pub(crate) async fn bots(
) -> Result<(), Error> { ) -> Result<(), Error> {
ctx.defer_ephemeral().await?; ctx.defer_ephemeral().await?;
*ctx.data().bots.write().await = bots; *ctx.data().bots.write().await = bots;
let reply = format!("{} bots are now registered as available", bots).to_string(); let content = format!("{} bots are now registered as available", bots).to_string();
command_helper::send_simple(ctx, reply).await ctx.send(CreateReply::default().content(content).ephemeral(true)).await?;
Ok(())
} }

View file

@ -1,25 +1,7 @@
use std::time::Duration; use std::time::Duration;
use crate::{Context, Error}; use crate::Context;
use crate::error::Error;
pub(crate) async fn send_simple(ctx: Context<'_>, reply: String) -> Result<(), Error> {
if let Err(why) = ctx
.send(poise::CreateReply {
content: Some(reply),
embeds: vec![],
attachments: vec![],
ephemeral: Some(true),
components: None,
allowed_mentions: None,
reply: false,
__non_exhaustive: (),
})
.await
{
println!("Error sending message: {:?}", why)
}
Ok(())
}
pub(crate) fn cooldown(ctx: &Context, user: u64, guild: u64) -> Result<(), Error> { pub(crate) fn cooldown(ctx: &Context, user: u64, guild: u64) -> Result<(), Error> {
let mut cooldown_tracker = ctx.command().cooldowns.lock().unwrap(); let mut cooldown_tracker = ctx.command().cooldowns.lock().unwrap();
@ -35,7 +17,7 @@ pub(crate) fn cooldown(ctx: &Context, user: u64, guild: u64) -> Result<(), Error
Ok(()) Ok(())
} else { } else {
match cooldown_tracker.remaining_cooldown((*ctx).cooldown_context(), &cooldown_durations) { match cooldown_tracker.remaining_cooldown((*ctx).cooldown_context(), &cooldown_durations) {
Some(remaining) => Err(format!("Please wait {} seconds", remaining.as_secs()).into()), Some(remaining) => Err(Error::OnCooldown(remaining)),
None => Ok(cooldown_tracker.start_cooldown((*ctx).cooldown_context())), None => Ok(cooldown_tracker.start_cooldown((*ctx).cooldown_context())),
} }
} }

View file

@ -1,8 +1,9 @@
use poise::CreateReply; use poise::CreateReply;
use serenity::all::CreateAllowedMentions; use serenity::all::CreateAllowedMentions;
use crate::{Context, Error}; use crate::Context;
use crate::commands::command_helper; use crate::commands::command_helper;
use crate::error::Error;
#[poise::command(slash_command, guild_only)] #[poise::command(slash_command, guild_only)]
pub(crate) async fn helpstart( pub(crate) async fn helpstart(

View file

@ -1,13 +1,14 @@
use poise::{ChoiceParameter, CreateReply}; use poise::{ChoiceParameter, CreateReply};
use serenity::all::{CreateAllowedMentions, RoleId}; use serenity::all::{CreateAllowedMentions, RoleId};
use crate::{Context, Error}; use crate::Context;
//from main.rs //from main.rs
use crate::commands::command_helper::cooldown; use crate::commands::command_helper::cooldown;
// //
use crate::commands::lfg::Difficulty::Normal; use crate::commands::lfg::Difficulty::Normal;
use crate::commands::lfg::Map::*; use crate::commands::lfg::Map::*;
use crate::commands::lfg::Mode::*; use crate::commands::lfg::Mode::*;
use crate::error::Error;
#[derive(Debug, poise::ChoiceParameter, PartialEq)] #[derive(Debug, poise::ChoiceParameter, PartialEq)]
pub enum Map { pub enum Map {

View file

@ -1,4 +1,5 @@
use crate::{Context, Error}; use crate::Context;
use crate::error::Error;
const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣧⣄⡀⠀⠀⠀⢀⣠⣼⣿⣿⣿⣿⣧⣄⡀⠀⠀⠀⣀⣤⣼⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⠋⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⡿⠁⠀⠀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⢸⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡗⠀⠀⠀⠐⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⠀⠀⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⣠⡀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⠀⣰⣿⣷⡄⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⣾⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⢀⣼⣿⣿⣿⣿⣆⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⢀⣼⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⠋⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⠿⠛⠁⠀⠀⣠⣾⣿⣿⣿⣿⣿\n⣿⣿⣿⠿⠛⠁⠀⠀⠀⠙⠻⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠈⠛⠻⡿⠟⠛⠁⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣿⣿⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣶⣶⣷⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n"; const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣧⣄⡀⠀⠀⠀⢀⣠⣼⣿⣿⣿⣿⣧⣄⡀⠀⠀⠀⣀⣤⣼⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⠋⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⡿⠁⠀⠀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⢸⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡗⠀⠀⠀⠐⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⠀⠀⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⣠⡀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⠀⣰⣿⣷⡄⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⣾⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⢀⣼⣿⣿⣿⣿⣆⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⢀⣼⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⠋⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⠿⠛⠁⠀⠀⣠⣾⣿⣿⣿⣿⣿\n⣿⣿⣿⠿⠛⠁⠀⠀⠀⠙⠻⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠈⠛⠻⡿⠟⠛⠁⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣿⣿⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣶⣶⣷⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n";

46
src/error.rs Normal file
View file

@ -0,0 +1,46 @@
#[derive(Debug)]
pub enum Error {
SqlxError(sqlx::Error),
HttpsError(reqwest::Error),
ParsingError(serde_json::Error),
SerenityError(serenity::Error),
OnCooldown(std::time::Duration),
Other(String)
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::SqlxError(e) => write!(f, "SQLx Error: {}", e),
Error::HttpsError(e) => write!(f, "HTTPS Error (Hypixel / Mojang API):\n{}", e),
Error::ParsingError(e) => write!(f, "Parsing Error:\n {}", e),
Error::SerenityError(e) => write!(f, "Serenity Error:\n {}", e),
Error::OnCooldown(d) => write!(f, "This command is on cooldown. {}s remaining.", d.as_secs()),
Error::Other(s) => write!(f, "{}", s),
}
}
}
impl From<sqlx::Error> for Error {
fn from(error: sqlx::Error) -> Self {
Error::SqlxError(error)
}
}
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Error::HttpsError(error)
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Error::ParsingError(error)
}
}
impl From<serenity::Error> for Error {
fn from(error: serenity::Error) -> Self {
Error::SerenityError(error)
}
}

View file

@ -1,6 +1,6 @@
use serenity::all::{ComponentInteraction, ComponentInteractionDataKind, Context, CreateMessage, EditMessage, GuildId, Interaction, RoleId}; use serenity::all::{ComponentInteraction, ComponentInteractionDataKind, Context, CreateMessage, EditMessage, GuildId, Interaction, RoleId};
use crate::Error; use crate::error::Error;
pub(crate) async fn component(ctx: &Context, interaction: &Interaction) -> Result<(), Error> { pub(crate) async fn component(ctx: &Context, interaction: &Interaction) -> Result<(), Error> {
let component = interaction.clone().message_component().unwrap(); let component = interaction.clone().message_component().unwrap();

View file

@ -1,6 +1,6 @@
use serenity::all::{Context, Message}; use serenity::all::{Context, Message};
use crate::Error; use crate::error::Error;
pub(crate) async fn on_create(ctx: &Context, msg: &Message) -> Result<(), Error> { pub(crate) async fn on_create(ctx: &Context, msg: &Message) -> Result<(), Error> {
match msg.guild_id.map(|g| g.get()) { match msg.guild_id.map(|g| g.get()) {

View file

@ -1,7 +1,7 @@
use serenity::all::{Context, GuildChannel}; use serenity::all::{Context, GuildChannel};
use serenity::builder::EditThread; use serenity::builder::EditThread;
use crate::Error; use crate::error::Error;
pub(crate) async fn on_create(ctx: &Context, thread: &GuildChannel) -> Result<(), Error> { pub(crate) async fn on_create(ctx: &Context, thread: &GuildChannel) -> Result<(), Error> {
match thread.parent_id.map(|parent| parent.get()) { match thread.parent_id.map(|parent| parent.get()) {

View file

@ -5,22 +5,25 @@ use std::convert::Into;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use poise::serenity_prelude as serenity; use poise::{CreateReply, FrameworkError, serenity_prelude as serenity};
use serenity::{FullEvent, model::id::UserId}; use serenity::{FullEvent, model::id::UserId};
use serenity::all::{ActivityData, InteractionType, RoleId}; use serenity::all::{ActivityData, InteractionType, RoleId};
use serenity::prelude::GatewayIntents; use serenity::prelude::GatewayIntents;
use sqlx::Sqlite; use sqlx::Sqlite;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use error::Error;
mod commands; mod commands;
mod handlers; mod handlers;
mod error;
struct Data { struct Data {
bots: Arc<RwLock<u8>>, bots: Arc<RwLock<u8>>,
sqlite_pool: sqlx::Pool<Sqlite>, sqlite_pool: sqlx::Pool<Sqlite>,
hypixel_api_client: reqwest::Client, hypixel_api_client: reqwest::Client,
} // User data, which is stored and accessible in all command invocations } // User data, which is stored and accessible in all command invocations
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>; type Context<'a> = poise::Context<'a, Data, Error>;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -59,7 +62,18 @@ async fn main() {
on_error: |error| { on_error: |error| {
Box::pin(async move { Box::pin(async move {
match error { match error {
other => poise::builtins::on_error(other).await.unwrap(), FrameworkError::CommandStructureMismatch { description, ctx, .. } => {
if let Err(e) = ctx.send(CreateReply::default()
.content(format!("# Command arguments did not match. The command probably has been updated recently. Try reloading Discord. Description:\n{}", description)))
.await {
tracing::error!("Fatal error while sending error message: {}", e);
}
}
other => {
if let Err(e) = poise::builtins::on_error(other).await {
tracing::error!("Fatal error while sending error message: {}", e);
}
}
} }
}) })
}, },