mod help;
pub use help::*;
mod pretty_help;
pub use pretty_help::*;
mod register;
pub use register::*;
#[cfg(feature = "chrono")]
mod paginate;
#[cfg(feature = "chrono")]
pub use paginate::*;
use crate::{serenity::CreateAllowedMentions, serenity_prelude as serenity, CreateReply};
pub async fn on_error<U, E: std::fmt::Display + std::fmt::Debug>(
error: crate::FrameworkError<'_, U, E>,
) -> Result<(), serenity::Error> {
match error {
crate::FrameworkError::Setup { error, .. } => {
eprintln!("Error in user data setup: {}", error);
}
crate::FrameworkError::EventHandler { error, event, .. } => tracing::error!(
"User event event handler encountered an error on {} event: {}",
event.snake_case_name(),
error
),
crate::FrameworkError::Command { ctx, error } => {
let error = error.to_string();
eprintln!("An error occured in a command: {}", error);
let mentions = CreateAllowedMentions::new()
.everyone(false)
.all_roles(false)
.all_users(false);
ctx.send(
CreateReply::default()
.content(error)
.allowed_mentions(mentions)
.ephemeral(true),
)
.await?;
}
crate::FrameworkError::SubcommandRequired { ctx } => {
let subcommands = ctx
.command()
.subcommands
.iter()
.map(|s| &*s.name)
.collect::<Vec<_>>();
let response = format!(
"You must specify one of the following subcommands: {}",
subcommands.join(", ")
);
ctx.send(CreateReply::default().content(response).ephemeral(true))
.await?;
}
crate::FrameworkError::CommandPanic { ctx, payload: _ } => {
let embed = serenity::CreateEmbed::default()
.title("Internal error")
.color((255, 0, 0))
.description("An unexpected internal error has occurred.");
ctx.send(CreateReply::default().embed(embed).ephemeral(true))
.await?;
}
crate::FrameworkError::ArgumentParse { ctx, input, error } => {
let usage = match &ctx.command().help_text {
Some(help_text) => &**help_text,
None => "Please check the help menu for usage information",
};
let response = if let Some(input) = input {
format!(
"**Cannot parse `{}` as argument: {}**\n{}",
input, error, usage
)
} else {
format!("**{}**\n{}", error, usage)
};
let mentions = CreateAllowedMentions::new()
.everyone(false)
.all_roles(false)
.all_users(false);
ctx.send(
CreateReply::default()
.content(response)
.allowed_mentions(mentions)
.ephemeral(true),
)
.await?;
}
crate::FrameworkError::CommandStructureMismatch { ctx, description } => {
tracing::error!(
"Error: failed to deserialize interaction arguments for `/{}`: {}",
ctx.command.name,
description,
);
}
crate::FrameworkError::CommandCheckFailed { ctx, error } => {
tracing::error!(
"A command check failed in command {} for user {}: {:?}",
ctx.command().name,
ctx.author().name,
error,
);
}
crate::FrameworkError::CooldownHit {
remaining_cooldown,
ctx,
} => {
let msg = format!(
"You're too fast. Please wait {} seconds before retrying",
remaining_cooldown.as_secs()
);
ctx.send(CreateReply::default().content(msg).ephemeral(true))
.await?;
}
crate::FrameworkError::MissingBotPermissions {
missing_permissions,
ctx,
} => {
let msg = format!(
"Command cannot be executed because the bot is lacking permissions: {}",
missing_permissions,
);
ctx.send(CreateReply::default().content(msg).ephemeral(true))
.await?;
}
crate::FrameworkError::MissingUserPermissions {
missing_permissions,
ctx,
} => {
let response = if let Some(missing_permissions) = missing_permissions {
format!(
"You're lacking permissions for `{}{}`: {}",
ctx.prefix(),
ctx.command().name,
missing_permissions,
)
} else {
format!(
"You may be lacking permissions for `{}{}`. Not executing for safety",
ctx.prefix(),
ctx.command().name,
)
};
ctx.send(CreateReply::default().content(response).ephemeral(true))
.await?;
}
crate::FrameworkError::NotAnOwner { ctx } => {
let response = "Only bot owners can call this command";
ctx.send(CreateReply::default().content(response).ephemeral(true))
.await?;
}
crate::FrameworkError::GuildOnly { ctx } => {
let response = "You cannot run this command in DMs.";
ctx.send(CreateReply::default().content(response).ephemeral(true))
.await?;
}
crate::FrameworkError::DmOnly { ctx } => {
let response = "You cannot run this command outside DMs.";
ctx.send(CreateReply::default().content(response).ephemeral(true))
.await?;
}
crate::FrameworkError::NsfwOnly { ctx } => {
let response = "You cannot run this command outside NSFW channels.";
ctx.send(CreateReply::default().content(response).ephemeral(true))
.await?;
}
crate::FrameworkError::DynamicPrefix { error, msg, .. } => {
tracing::error!(
"Dynamic prefix failed for message {:?}: {}",
msg.content,
error
);
}
crate::FrameworkError::UnknownCommand {
msg_content,
prefix,
..
} => {
tracing::warn!(
"Recognized prefix `{}`, but didn't recognize command name in `{}`",
prefix,
msg_content,
);
}
crate::FrameworkError::UnknownInteraction { interaction, .. } => {
tracing::warn!("received unknown interaction \"{}\"", interaction.data.name);
}
crate::FrameworkError::NonCommandMessage { error, .. } => {
tracing::warn!("error in non-command message handler: {}", error);
}
crate::FrameworkError::__NonExhaustive(unreachable) => match unreachable {},
}
Ok(())
}
#[allow(clippy::unused_async)] pub async fn autocomplete_command<'a, U, E>(
ctx: crate::Context<'a, U, E>,
partial: &'a str,
) -> impl Iterator<Item = String> + 'a {
ctx.framework()
.options()
.commands
.iter()
.filter(move |cmd| cmd.name.starts_with(partial))
.map(|cmd| cmd.name.to_string())
}
#[cfg(feature = "cache")]
pub async fn servers<U, E>(ctx: crate::Context<'_, U, E>) -> Result<(), serenity::Error> {
use std::fmt::Write as _;
let show_private_guilds = ctx.framework().options().owners.contains(&ctx.author().id);
let mut hidden_guilds = 0;
let mut hidden_guilds_members = 0;
let mut shown_guilds = Vec::<(String, u64)>::new();
for guild_id in ctx.cache().guilds() {
match ctx.cache().guild(guild_id) {
Some(guild) => {
let is_public = guild.features.iter().any(|x| x == "DISCOVERABLE");
if !is_public && !show_private_guilds {
hidden_guilds += 1; } else {
shown_guilds.push((guild.name.clone(), guild.member_count))
}
}
None => hidden_guilds += 1, }
}
shown_guilds.sort_by_key(|(_, member)| u64::MAX - member);
let mut response = format!(
"I am currently in {} servers!\n",
shown_guilds.len() + hidden_guilds
);
if show_private_guilds {
response.insert_str(0, "_Showing private guilds because you are a bot owner_\n");
}
let mut guilds = shown_guilds.into_iter().peekable();
while let Some((name, member_count)) = guilds.peek() {
let line = format!("- **{}** ({} members)\n", name, member_count);
if response.len() + line.len() > 1940 {
for (_remaining_guild_name, members) in guilds {
hidden_guilds += 1;
hidden_guilds_members += members;
}
break;
}
response += &line;
guilds.next(); }
if hidden_guilds > 0 {
let _ = writeln!(
response,
"- {} remaining servers with {} members total",
hidden_guilds, hidden_guilds_members
);
}
if response.len() > 2000 {
let mut truncate_at = 2000;
while !response.is_char_boundary(truncate_at) {
truncate_at -= 1;
}
response.truncate(truncate_at);
}
let reply = CreateReply::default()
.content(response)
.ephemeral(show_private_guilds);
ctx.send(reply).await?;
Ok(())
}