ephemeral & better errors
This commit is contained in:
parent
a18ea80d1a
commit
52b61d47ca
9 changed files with 69 additions and 48 deletions
|
@ -6,11 +6,11 @@ use serenity::{
|
||||||
json::JsonError,
|
json::JsonError,
|
||||||
};
|
};
|
||||||
use serenity::all::ButtonStyle;
|
use serenity::all::ButtonStyle;
|
||||||
use sqlx::{query_as, Pool, Sqlite};
|
use sqlx::{Pool, query_as, Sqlite};
|
||||||
|
|
||||||
use crate::commands::command_helper::cooldown;
|
use crate::commands::command_helper::cooldown;
|
||||||
use crate::error::Error;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Links {
|
struct Links {
|
||||||
|
@ -145,7 +145,7 @@ pub(crate) async fn add<'a>(
|
||||||
#[max_length = 16]
|
#[max_length = 16]
|
||||||
ign: String,
|
ign: String,
|
||||||
#[description = "Discord User"] user: Option<User>,
|
#[description = "Discord User"] user: Option<User>,
|
||||||
#[description = "Minecraft username"] force: Option<bool>,
|
#[description = "admin-only"] force: Option<bool>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
ctx.defer_ephemeral().await?;
|
ctx.defer_ephemeral().await?;
|
||||||
let user: User = user.unwrap_or(ctx.author().clone());
|
let user: User = user.unwrap_or(ctx.author().clone());
|
||||||
|
@ -187,20 +187,20 @@ pub(crate) async fn add<'a>(
|
||||||
mc_id.cast_signed(),
|
mc_id.cast_signed(),
|
||||||
dc_id.cast_signed()
|
dc_id.cast_signed()
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
)
|
)
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
.await?;
|
.await?;
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
format!(
|
format!(
|
||||||
"UPDATE discord_links SET link_id = {} WHERE link_id = {};",
|
"UPDATE discord_links SET link_id = {} WHERE link_id = {};",
|
||||||
mc_id.cast_signed(),
|
mc_id.cast_signed(),
|
||||||
dc_id.cast_signed()
|
dc_id.cast_signed()
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
)
|
)
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
.await?;
|
.await?;
|
||||||
"Both your Discord and Minecraft account had linked accounts. Merged all account links."
|
"Both your Discord and Minecraft account had linked accounts. Merged all account links."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -213,9 +213,15 @@ pub(crate) async fn add<'a>(
|
||||||
.content(s)
|
.content(s)
|
||||||
.allowed_mentions(CreateAllowedMentions::new().empty_roles().all_users(true))
|
.allowed_mentions(CreateAllowedMentions::new().empty_roles().all_users(true))
|
||||||
.components(vec![CreateActionRow::Buttons(vec![
|
.components(vec![CreateActionRow::Buttons(vec![
|
||||||
CreateButton::new("accept_verification").emoji(ReactionType::from('✅')).style(ButtonStyle::Primary),
|
CreateButton::new("accept_verification")
|
||||||
CreateButton::new("deny_verification").emoji(ReactionType::from('❌')).style(ButtonStyle::Primary),
|
.emoji(ReactionType::from('✅'))
|
||||||
CreateButton::new("list_accounts").emoji(ReactionType::from('📜')).style(ButtonStyle::Secondary),
|
.style(ButtonStyle::Primary),
|
||||||
|
CreateButton::new("deny_verification")
|
||||||
|
.emoji(ReactionType::from('❌'))
|
||||||
|
.style(ButtonStyle::Primary),
|
||||||
|
CreateButton::new("list_accounts")
|
||||||
|
.emoji(ReactionType::from('📜'))
|
||||||
|
.style(ButtonStyle::Secondary),
|
||||||
])]),
|
])]),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -242,7 +248,7 @@ pub(crate) async fn list(ctx: Context<'_>, user: Option<User>) -> Result<(), Err
|
||||||
r.content(s)
|
r.content(s)
|
||||||
.allowed_mentions(CreateAllowedMentions::new().empty_roles().empty_users()),
|
.allowed_mentions(CreateAllowedMentions::new().empty_roles().empty_users()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,12 +291,12 @@ async fn link_id_from_discord(pool: &Pool<Sqlite>, snowflake: u64) -> Option<u16
|
||||||
"SELECT link_id FROM discord_links WHERE discord_id = {} LIMIT 1;",
|
"SELECT link_id FROM discord_links WHERE discord_id = {} LIMIT 1;",
|
||||||
snowflake.cast_signed()
|
snowflake.cast_signed()
|
||||||
)
|
)
|
||||||
.as_str(),
|
.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)]
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::string::String;
|
||||||
|
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
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(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
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();
|
||||||
|
|
|
@ -2,8 +2,8 @@ use poise::CreateReply;
|
||||||
use serenity::all::CreateAllowedMentions;
|
use serenity::all::CreateAllowedMentions;
|
||||||
|
|
||||||
use crate::commands::command_helper;
|
use crate::commands::command_helper;
|
||||||
use crate::error::Error;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
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(
|
||||||
|
|
|
@ -7,8 +7,8 @@ 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;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
#[derive(Debug, poise::ChoiceParameter, PartialEq)]
|
#[derive(Debug, poise::ChoiceParameter, PartialEq)]
|
||||||
pub enum Map {
|
pub enum Map {
|
||||||
|
@ -96,7 +96,7 @@ pub(crate) async fn lfg(
|
||||||
AlienArcadium => Normal,
|
AlienArcadium => Normal,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut reply_content: String = format!("<@&{ping}> {current}/{desired} {map_name}",);
|
let mut reply_content: String = format!("<@&{ping}> {current}/{desired} {map_name}", );
|
||||||
match difficulty {
|
match difficulty {
|
||||||
Normal => {}
|
Normal => {}
|
||||||
Difficulty::Hard | Difficulty::Rip => {
|
Difficulty::Hard | Difficulty::Rip => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::error::Error;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\\
|
const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\\
|
||||||
n⣿⣿⣿⣧⣄⡀⠀⠀⠀⢀⣠⣼⣿⣿⣿⣿⣧⣄⡀⠀⠀⠀⣀⣤⣼⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿\\
|
n⣿⣿⣿⣧⣄⡀⠀⠀⠀⢀⣠⣼⣿⣿⣿⣿⣧⣄⡀⠀⠀⠀⣀⣤⣼⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿\\
|
||||||
|
|
34
src/error.rs
34
src/error.rs
|
@ -1,3 +1,15 @@
|
||||||
|
use poise::{CreateReply, FrameworkError};
|
||||||
|
|
||||||
|
use crate::Data;
|
||||||
|
|
||||||
|
macro_rules! reply_fail_handler {
|
||||||
|
($fut:expr) => {{
|
||||||
|
if let Err(e) = $fut.await {
|
||||||
|
tracing::error!("Fatal error while sending error message: {}", e);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
SqlxError(sqlx::Error),
|
SqlxError(sqlx::Error),
|
||||||
|
@ -14,7 +26,7 @@ impl std::fmt::Display for Error {
|
||||||
Error::SqlxError(e) => write!(f, "SQLx Error: {}", e),
|
Error::SqlxError(e) => write!(f, "SQLx Error: {}", e),
|
||||||
Error::HttpsError(e) => write!(f, "HTTPS Error (Hypixel / Mojang API):\n{}", e),
|
Error::HttpsError(e) => write!(f, "HTTPS Error (Hypixel / Mojang API):\n{}", e),
|
||||||
Error::ParsingError(e) => write!(f, "Parsing Error:\n {}", e),
|
Error::ParsingError(e) => write!(f, "Parsing Error:\n {}", e),
|
||||||
Error::SerenityError(e) => write!(f, "Serenity Error:\n {}", e),
|
Error::SerenityError(e) => write!(f, "Discord Error:\n {}", e),
|
||||||
Error::OnCooldown(d) => {
|
Error::OnCooldown(d) => {
|
||||||
write!(f, "This command is on cooldown. {}s remaining.", d.as_secs())
|
write!(f, "This command is on cooldown. {}s remaining.", d.as_secs())
|
||||||
}
|
}
|
||||||
|
@ -46,3 +58,23 @@ impl From<serenity::Error> for Error {
|
||||||
Error::SerenityError(error)
|
Error::SerenityError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn handle_error<'a>(error: FrameworkError<'a, Data, Error>) {
|
||||||
|
match error {
|
||||||
|
FrameworkError::Command { error, ctx, .. } => {
|
||||||
|
reply_fail_handler!(ctx.send(CreateReply::default().content(error.to_string()).ephemeral(true)))
|
||||||
|
},
|
||||||
|
FrameworkError::CommandStructureMismatch { description, ctx, .. } => {
|
||||||
|
reply_fail_handler!(ctx.send(
|
||||||
|
CreateReply::default()
|
||||||
|
.content(format!(
|
||||||
|
"# Command arguments did not match. The command probably has been updated recently. Try reloading Discord. \
|
||||||
|
Description:\n{}",
|
||||||
|
description
|
||||||
|
))
|
||||||
|
.ephemeral(true)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
other => reply_fail_handler!(poise::builtins::on_error(other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use serenity::all::{ButtonStyle, ComponentInteraction};
|
||||||
use serenity::all::ButtonStyle::Success;
|
use serenity::all::ButtonStyle::Success;
|
||||||
use serenity::all::ComponentInteractionDataKind;
|
use serenity::all::ComponentInteractionDataKind;
|
||||||
use serenity::all::Context;
|
use serenity::all::Context;
|
||||||
|
@ -11,10 +12,9 @@ use serenity::all::GuildId;
|
||||||
use serenity::all::Interaction;
|
use serenity::all::Interaction;
|
||||||
use serenity::all::ReactionType;
|
use serenity::all::ReactionType;
|
||||||
use serenity::all::RoleId;
|
use serenity::all::RoleId;
|
||||||
use serenity::all::{ButtonStyle, ComponentInteraction};
|
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
pub(crate) async fn component(ctx: &Context, interaction: &Interaction, data: &Data) -> Result<(), Error> {
|
pub(crate) async fn component(ctx: &Context, interaction: &Interaction, data: &Data) -> Result<(), Error> {
|
||||||
let component = interaction.clone().message_component().unwrap();
|
let component = interaction.clone().message_component().unwrap();
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -5,10 +5,10 @@ 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, CreateReply, FrameworkError};
|
use poise::serenity_prelude as serenity;
|
||||||
|
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 serenity::{model::id::UserId, FullEvent};
|
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
@ -56,25 +56,7 @@ async fn main() {
|
||||||
},
|
},
|
||||||
on_error: |error| {
|
on_error: |error| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
match error {
|
error::handle_error(error).await;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
owners: { HashSet::from([UserId::new(449579075531440128_u64), UserId::new(659112817508745216_u64)]) },
|
owners: { HashSet::from([UserId::new(449579075531440128_u64), UserId::new(659112817508745216_u64)]) },
|
||||||
|
@ -104,6 +86,7 @@ async fn main() {
|
||||||
.await;
|
.await;
|
||||||
client.unwrap().start_autosharded().await.unwrap()
|
client.unwrap().start_autosharded().await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn event_handler(
|
async fn event_handler(
|
||||||
ctx: &serenity::Context,
|
ctx: &serenity::Context,
|
||||||
event: &FullEvent,
|
event: &FullEvent,
|
||||||
|
|
Loading…
Add table
Reference in a new issue