pub mod context;
pub(crate) mod routing;
use std::collections::HashMap;
use std::sync::Arc;
use twilight_gateway::Event;
use twilight_model::application::command::{Command as TwilightCommand, CommandType};
pub use twilight_model::channel::Message;
use twilight_model::id::Id;
use crate::commands::errors::{ArgumentError, CommandError};
use crate::commands::message::context::MessageContext;
use crate::commands::permissions::{PermissionChecker, PermissionContext};
use crate::commands::{CommandGroupIntoCommandNode, CommandNode, CommandResult};
use crate::errors::{ErrorHandler, ErrorHandlerWithoutType, ErrorHandlerWrapper};
use crate::state::StateBound;
use crate::utils::DynFuture;
#[derive(Clone)]
pub struct MessageCommand<State>
where
State: StateBound,
{
name: String,
name_i18n: HashMap<String, String>,
handler: Arc<dyn MessageCommandHandler<State>>,
on_errors: Vec<Arc<dyn ErrorHandlerWithoutType<State>>>,
checks: Vec<Arc<dyn PermissionChecker<State>>>,
}
impl<State> MessageCommand<State>
where
State: StateBound,
{
pub(crate) async fn run(&self, ctx: MessageContext<State>) -> CommandResult {
let permission_ctx = PermissionContext {
event: Event::InteractionCreate(Box::new(ctx.event.clone())),
handle: ctx.handle.clone(),
state: ctx.state.clone(),
};
for checker in &self.checks {
checker
.check(permission_ctx.clone())
.await
.map_err(CommandError::Permissions)?;
}
self.handler.run(ctx).await
}
}
impl<State> From<MessageCommand<State>> for TwilightCommand
where
State: StateBound,
{
fn from(value: MessageCommand<State>) -> Self {
#[allow(deprecated)]
TwilightCommand {
application_id: None,
contexts: None,
default_member_permissions: None,
description: String::new(),
description_localizations: None,
guild_id: None,
id: None,
integration_types: None,
kind: CommandType::Message,
name: value.name,
name_localizations: Some(value.name_i18n),
nsfw: None,
options: vec![],
version: Id::new(1),
dm_permission: None,
}
}
}
pub struct MessageCommandBuilder<State>
where
State: StateBound,
{
name: String,
name_i18n: HashMap<String, String>,
handler: Arc<dyn MessageCommandHandler<State>>,
on_errors: Vec<Arc<dyn ErrorHandlerWithoutType<State>>>,
checks: Vec<Arc<dyn PermissionChecker<State>>>,
}
impl<State> MessageCommandBuilder<State>
where
State: StateBound,
{
pub(crate) fn new(name: String, handler: impl MessageCommandHandler<State> + 'static) -> Self {
MessageCommandBuilder {
name,
name_i18n: HashMap::new(),
handler: Arc::new(handler),
on_errors: vec![],
checks: vec![],
}
}
pub fn name_i18n(mut self, lang: impl Into<String>, name: impl Into<String>) -> Self {
self.name_i18n.insert(lang.into(), name.into());
self
}
pub fn on_error<Error>(mut self, handler: impl ErrorHandler<State, Error> + 'static) -> Self
where
Error: Send + Sync + 'static,
{
self.on_errors
.push(Arc::new(ErrorHandlerWrapper::new(handler)));
self
}
pub fn check(mut self, checker: impl PermissionChecker<State> + 'static) -> Self {
self.checks.push(Arc::new(checker));
self
}
pub(crate) fn build(self) -> MessageCommand<State> {
MessageCommand {
name: self.name,
name_i18n: self.name_i18n,
handler: self.handler,
on_errors: self.on_errors,
checks: self.checks,
}
}
}
pub trait MessageCommandHandler<State>: Send + Sync
where
State: StateBound,
{
fn run(&self, ctx: MessageContext<State>) -> DynFuture<'_, CommandResult>;
}
impl<State, Func, Fut, Res> MessageCommandHandler<State> for Func
where
State: StateBound,
Func: Fn(MessageContext<State>, Message) -> Fut + Send + Sync,
Fut: Future<Output = Res> + Send,
{
fn run(&self, ctx: MessageContext<State>) -> DynFuture<'_, CommandResult> {
Box::pin(async move {
let message_id = ctx
.event_data
.target_id
.ok_or(ArgumentError::MissingResolved)?;
let resolved = ctx
.event_data
.resolved
.as_ref()
.ok_or(ArgumentError::MissingResolved)?;
let message = resolved
.messages
.get(&message_id.cast())
.cloned()
.ok_or(ArgumentError::MissingResolved)?;
self(ctx, message).await;
Ok(())
})
}
}
#[derive(Clone)]
pub struct MessageCommandGroup<State>
where
State: StateBound,
{
pub name: String,
pub children: Vec<CommandNode<State>>,
pub on_errors: Vec<Arc<dyn ErrorHandlerWithoutType<State>>>,
}
impl<State> MessageCommandGroup<State>
where
State: StateBound,
{
pub fn build(name: impl Into<String>) -> MessageCommandGroupBuilder<State> {
MessageCommandGroupBuilder::new(name)
}
}
#[derive(Clone)]
pub struct MessageCommandGroupBuilder<State>
where
State: StateBound,
{
name: String,
children: Vec<CommandNode<State>>,
on_errors: Vec<Arc<dyn ErrorHandlerWithoutType<State>>>,
}
impl<State> MessageCommandGroupBuilder<State>
where
State: StateBound,
{
pub(crate) fn new(name: impl Into<String>) -> Self {
MessageCommandGroupBuilder {
name: name.into(),
children: vec![],
on_errors: vec![],
}
}
pub fn command(mut self, command: impl Into<MessageCommand<State>>) -> Self {
self.children
.push(CommandNode::MessageCommand(command.into()));
self
}
pub fn nest(mut self, group: impl Into<MessageCommandGroup<State>>) -> Self {
self.children
.push(CommandNode::MessageCommandGroup(group.into()));
self
}
pub fn on_error<Error>(mut self, handler: impl ErrorHandler<State, Error> + 'static) -> Self
where
Error: Send + Sync + 'static,
{
self.on_errors
.push(Arc::new(ErrorHandlerWrapper::new(handler)));
self
}
pub(crate) fn build(self) -> MessageCommandGroup<State> {
MessageCommandGroup {
name: self.name,
children: self.children,
on_errors: self.on_errors,
}
}
}
impl<State> CommandGroupIntoCommandNode<State> for MessageCommandGroup<State>
where
State: StateBound,
{
fn into_command_node(self) -> CommandNode<State> {
CommandNode::MessageCommandGroup(self)
}
}
impl<State> CommandGroupIntoCommandNode<State> for MessageCommandGroupBuilder<State>
where
State: StateBound,
{
fn into_command_node(self) -> CommandNode<State> {
CommandNode::MessageCommandGroup(self.build())
}
}