Add support for extras, conditionally displaying commands in the help command, and start working on moderation commands

This commit is contained in:
2025-08-22 18:06:52 -03:00
parent 55ae1c8937
commit 8868b18be3
41 changed files with 1075 additions and 368 deletions
+19 -13
View File
@@ -8,20 +8,20 @@ use twilight_model::channel::message::{
use crate::framework::interactions::{InteractionError, context::InteractionContext};
type ComponentWithID<T> = (Option<String>, Arc<dyn Component<T>>);
type ComponentWithID<T, E> = (Option<String>, Arc<dyn Component<T, E>>);
pub struct ActionRow<T> {
components: Vec<ComponentWithID<T>>,
pub struct ActionRow<T, E> {
components: Vec<ComponentWithID<T, E>>,
}
impl<T> ActionRow<T> {
pub fn new(components: &[ComponentWithID<T>]) -> Self {
impl<T, E> ActionRow<T, E> {
pub fn new(components: &[ComponentWithID<T, E>]) -> Self {
Self {
components: components.to_vec(),
}
}
pub fn add(&mut self, id: Option<String>, component: Arc<dyn Component<T>>) {
pub fn add(&mut self, id: Option<String>, component: Arc<dyn Component<T, E>>) {
self.components.push((id, component));
}
@@ -41,19 +41,21 @@ impl<T> ActionRow<T> {
}
#[async_trait]
pub trait Component<T>: Send + Sync + Debug {
pub trait Component<T, E>: Send + Sync + Debug {
fn custom_id(&self) -> Option<String> {
Some(String::from("component"))
}
fn build(&self, id: Option<String>) -> TwilightComponent;
async fn execute(&self, ctx: InteractionContext<T>) -> Result<(), InteractionError>;
async fn execute(&self, ctx: InteractionContext<T, E>) -> Result<(), InteractionError>;
}
#[async_trait]
impl<T, S: Send + Sync + 'static> Component<S> for T
impl<T, S, E> Component<S, E> for T
where
T: Button<S>,
T: Button<S, E>,
S: Send + Sync + 'static,
E: Send + Sync + 'static,
{
fn custom_id(&self) -> Option<String> {
Button::id(self)
@@ -75,13 +77,17 @@ where
})
}
async fn execute(&self, ctx: InteractionContext<S>) -> Result<(), InteractionError> {
async fn execute(&self, ctx: InteractionContext<S, E>) -> Result<(), InteractionError> {
self.on_press(ctx).await
}
}
#[async_trait]
pub trait Button<S: Send + Sync + 'static>: Send + Sync + Debug {
pub trait Button<S, E>: Send + Sync + Debug
where
S: Send + Sync + 'static,
E: Send + Sync + 'static,
{
fn id(&self) -> Option<String> {
if self.url().is_none() {
Some(String::from("Button"))
@@ -99,7 +105,7 @@ pub trait Button<S: Send + Sync + 'static>: Send + Sync + Debug {
None
}
async fn on_press(&self, ctx: InteractionContext<S>) -> Result<(), InteractionError> {
async fn on_press(&self, ctx: InteractionContext<S, E>) -> Result<(), InteractionError> {
ctx.defer().await
}
}
+22 -10
View File
@@ -10,15 +10,26 @@ use crate::framework::{
commands::messages::OriginalMessageUpdater, interactions::InteractionError, runtime::Runtime,
};
pub struct InteractionContext<T> {
pub struct InteractionContext<T, E> {
pub event: InteractionCreate,
pub rt: Arc<Runtime<T>>,
pub rt: Arc<Runtime<T, E>>,
pub prefix: String,
pub extras: E,
}
impl<T> InteractionContext<T> {
pub fn new(event: InteractionCreate, rt: Arc<Runtime<T>>, prefix: String) -> Self {
Self { event, rt, prefix }
impl<T, E> InteractionContext<T, E> {
pub fn new(
event: InteractionCreate,
rt: Arc<Runtime<T, E>>,
prefix: String,
extras: E,
) -> Self {
Self {
event,
rt,
prefix,
extras,
}
}
pub async fn defer(&self) -> Result<(), InteractionError> {
@@ -39,7 +50,7 @@ impl<T> InteractionContext<T> {
Ok(())
}
pub fn original_message(&self) -> Option<OriginalMessage<T>> {
pub fn original_message(&self) -> Option<OriginalMessage<T, E>> {
self.event.message.as_ref().map(|_| OriginalMessage {
interaction_id: self.event.id,
interaction_token: self.event.token.clone(),
@@ -48,17 +59,18 @@ impl<T> InteractionContext<T> {
}
}
pub struct OriginalMessage<T> {
pub struct OriginalMessage<T, E> {
interaction_id: Id<InteractionMarker>,
interaction_token: String,
rt: Arc<Runtime<T>>,
rt: Arc<Runtime<T, E>>,
}
impl<T> OriginalMessage<T>
impl<T, E> OriginalMessage<T, E>
where
T: Send + Sync + 'static,
E: Send + Sync + 'static,
{
pub fn edit(&self) -> OriginalMessageUpdater<T> {
pub fn edit(&self) -> OriginalMessageUpdater<T, E> {
OriginalMessageUpdater::new(
self.interaction_id,
self.interaction_token.clone(),
+3
View File
@@ -23,4 +23,7 @@ pub enum InteractionError {
#[error("No prefixes were returned for the event")]
PrefixesVectorEmpty,
#[error("The extras getter failed to retrieve extras")]
ExtrasGetterFailed,
}
+20 -5
View File
@@ -7,6 +7,7 @@ use twilight_model::{
};
use crate::framework::{
commands::extras::ExtrasGetter,
interactions::{InteractionError, context::InteractionContext},
runtime::Runtime,
};
@@ -14,13 +15,14 @@ use crate::framework::{
use super::components::Component;
#[derive(Default)]
pub struct ComponentRegistry<T> {
inner: HashMap<String, Arc<dyn Component<T>>>,
pub struct ComponentRegistry<T, E> {
inner: HashMap<String, Arc<dyn Component<T, E>>>,
}
impl<T> ComponentRegistry<T>
impl<T, E> ComponentRegistry<T, E>
where
T: Send + Sync + 'static,
E: Send + Sync + 'static,
{
pub fn new() -> Self {
Self {
@@ -28,7 +30,7 @@ where
}
}
pub fn register(&self, id: impl ToString, component: Arc<dyn Component<T>>) {
pub fn register(&self, id: impl ToString, component: Arc<dyn Component<T, E>>) {
self.inner.upsert(id.to_string(), component);
}
@@ -39,7 +41,8 @@ where
pub async fn execute(
&self,
event: &InteractionCreate,
rt: Arc<Runtime<T>>,
rt: Arc<Runtime<T, E>>,
extras: Arc<dyn ExtrasGetter<T, Extras = E>>,
) -> Result<(), InteractionError> {
if let Some(data) = &event.data {
match data {
@@ -55,11 +58,23 @@ where
.first()
.ok_or(InteractionError::PrefixesVectorEmpty)?;
let extras = extras
.get_extra(
&event.author().expect("Component interaction had no author"),
&rt,
)
.await
.inspect_err(|e| {
log::error!("Failed to get extras for component interaction: {e:?}")
})
.map_err(|_| InteractionError::ExtrasGetterFailed)?;
component
.execute(InteractionContext::new(
event.clone(),
rt.clone(),
prefix.to_string(),
extras,
))
.await
} else {
+10 -3
View File
@@ -4,6 +4,7 @@ use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::InteractionCreate;
use crate::framework::{
commands::extras::ExtrasGetter,
events::traits::{EventResult, OnInteractionHandler},
runtime::Runtime,
};
@@ -12,11 +13,17 @@ use crate::framework::{
pub struct InteractionRunner;
#[async_trait]
impl<T> OnInteractionHandler<T> for InteractionRunner
impl<T, E> OnInteractionHandler<T, E> for InteractionRunner
where
T: Send + Sync + 'static,
E: Send + Sync + 'static,
{
async fn on_interaction(&self, event: &InteractionCreate, rt: Arc<Runtime<T>>) -> EventResult {
Ok(rt.registry.execute(event, rt.clone()).await?)
async fn on_interaction(
&self,
event: &InteractionCreate,
rt: Arc<Runtime<T, E>>,
extras: Arc<dyn ExtrasGetter<T, Extras = E>>,
) -> EventResult {
Ok(rt.registry.execute(event, rt.clone(), extras).await?)
}
}