114 lines
2.9 KiB
Rust
114 lines
2.9 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use async_trait::async_trait;
|
|
use deadpool_redis::{cluster::PoolError, redis::RedisError};
|
|
use thiserror::Error;
|
|
use twilight_http::response::DeserializeBodyError;
|
|
|
|
use crate::{database::models::{DatabaseError, DatabaseOrDiscordError}, framework::{commands::{
|
|
arguments::Argument, context::CommandContext, permissions::Permission,
|
|
}, runtime::Runtime}};
|
|
|
|
pub mod arguments;
|
|
pub mod context;
|
|
pub mod extras;
|
|
pub mod messages;
|
|
pub mod permissions;
|
|
pub mod runner;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum CommandError {
|
|
#[error("A database query failed")]
|
|
DatabaseFailed(#[from] DatabaseError),
|
|
|
|
#[error("A database operation failed due to an inconsistent state")]
|
|
DatabaseInconsistentState,
|
|
|
|
#[error("The command was used in DMs, but it can only be used in servers")]
|
|
ServerCommandRunInDMs,
|
|
|
|
#[error(transparent)]
|
|
DiscordAPIFailed(#[from] twilight_http::Error),
|
|
|
|
#[error(transparent)]
|
|
DiscordDeserializationFailed(#[from] DeserializeBodyError),
|
|
|
|
#[error(transparent)]
|
|
RedisPoolFailed(#[from] PoolError),
|
|
|
|
#[error(transparent)]
|
|
RedisFailed(#[from] RedisError),
|
|
|
|
#[error(transparent)]
|
|
DatabaseOrDiscordError(#[from] DatabaseOrDiscordError)
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait Command<T, E>: Send + Sync + Debug {
|
|
fn name(&self) -> &'static str;
|
|
|
|
fn aliases(&self) -> &'static [&'static str] {
|
|
&[]
|
|
}
|
|
|
|
fn description(&self) -> &'static str {
|
|
"A Payphone command."
|
|
}
|
|
|
|
fn arguments(&self) -> &'static [Argument] {
|
|
&[]
|
|
}
|
|
|
|
fn permissions(&self) -> Vec<Box<dyn Permission<T, E> + Send + Sync + 'static>> {
|
|
vec![]
|
|
}
|
|
|
|
fn show_in_help(&self, _rt: &Runtime<T, E>, _extras: &E) -> bool {
|
|
true
|
|
}
|
|
|
|
/// Returns the command name + all its aliases.
|
|
///
|
|
/// Returns:
|
|
/// [`Vec<&'static str>`] - A vector with the command's name and its aliases.
|
|
fn identifiers(&self) -> Vec<&'static str> {
|
|
let mut all = vec![self.name()];
|
|
all.extend_from_slice(self.aliases());
|
|
all
|
|
}
|
|
|
|
async fn execute(&self, ctx: CommandContext<'_, T, E>, args: &str) -> Result<(), CommandError>;
|
|
}
|
|
|
|
pub struct CommandParts<'a> {
|
|
pub name: &'a str,
|
|
pub args: &'a str,
|
|
}
|
|
|
|
impl<'a> CommandParts<'a> {
|
|
fn new(name: &'a str, args: &'a str) -> Self {
|
|
Self { name, args }
|
|
}
|
|
}
|
|
|
|
pub fn get_command_parts<'a>(prefix: &str, message: &'a str) -> Option<CommandParts<'a>> {
|
|
let message_trimmed_start = message.trim_ascii_start();
|
|
|
|
if message_trimmed_start
|
|
.to_ascii_lowercase()
|
|
.starts_with(&prefix.to_ascii_lowercase())
|
|
{
|
|
let message = message_trimmed_start
|
|
.split_at(prefix.len())
|
|
.1
|
|
.trim_ascii_start();
|
|
|
|
match message.split_once(" ") {
|
|
Some((name, args)) => Some(CommandParts::new(name, args)),
|
|
None => Some(CommandParts::new(message, "")),
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|