Add basic moderator commands
This commit is contained in:
@@ -64,7 +64,7 @@ where
|
||||
fn build(&self, id: Option<String>) -> TwilightComponent {
|
||||
TwilightComponent::Button(TwilightButton {
|
||||
custom_id: id,
|
||||
disabled: false,
|
||||
disabled: self.disabled(),
|
||||
emoji: None,
|
||||
label: self.label().map(|i| i.to_string()),
|
||||
style: if self.url().is_none() {
|
||||
@@ -95,16 +95,23 @@ where
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> ButtonStyle {
|
||||
ButtonStyle::Secondary
|
||||
}
|
||||
fn label(&self) -> Option<&'static str> {
|
||||
|
||||
fn label(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
fn url(&self) -> Option<&'static str> {
|
||||
|
||||
fn url(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn disabled(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn on_press(&self, ctx: InteractionContext<S, E>) -> Result<(), InteractionError> {
|
||||
ctx.defer().await
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use twilight_http::{Error, Response, response::marker::EmptyBody};
|
||||
use twilight_model::{
|
||||
gateway::payload::incoming::InteractionCreate,
|
||||
http::interaction::{InteractionResponse, InteractionResponseType},
|
||||
id::{Id, marker::InteractionMarker},
|
||||
};
|
||||
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||
|
||||
use crate::framework::{
|
||||
commands::messages::OriginalMessageUpdater, interactions::InteractionError, runtime::Runtime,
|
||||
interactions::{InteractionError, components::ActionRow},
|
||||
messages::MessageEditBuilder,
|
||||
runtime::Runtime,
|
||||
};
|
||||
|
||||
pub struct InteractionContext<T, E> {
|
||||
@@ -17,7 +20,11 @@ pub struct InteractionContext<T, E> {
|
||||
pub extras: E,
|
||||
}
|
||||
|
||||
impl<T, E> InteractionContext<T, E> {
|
||||
impl<T, E> InteractionContext<T, E>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
E: Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
event: InteractionCreate,
|
||||
rt: Arc<Runtime<T, E>>,
|
||||
@@ -44,37 +51,85 @@ impl<T, E> InteractionContext<T, E> {
|
||||
kind: InteractionResponseType::DeferredUpdateMessage,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|_| InteractionError::DeferringFailed)?;
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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(),
|
||||
rt: self.rt.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OriginalMessage<T, E> {
|
||||
interaction_id: Id<InteractionMarker>,
|
||||
interaction_token: String,
|
||||
rt: Arc<Runtime<T, E>>,
|
||||
}
|
||||
|
||||
impl<T, E> OriginalMessage<T, E>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
E: Send + Sync + 'static,
|
||||
{
|
||||
pub fn edit(&self) -> OriginalMessageUpdater<T, E> {
|
||||
OriginalMessageUpdater::new(
|
||||
self.interaction_id,
|
||||
self.interaction_token.clone(),
|
||||
self.rt.clone(),
|
||||
)
|
||||
pub async fn edit_original_message(
|
||||
&self,
|
||||
builder: MessageEditBuilder<T, E>,
|
||||
) -> Result<Response<EmptyBody>, Error> {
|
||||
let mut sender = InteractionResponseDataBuilder::new();
|
||||
|
||||
if let Some(content) = &builder.content {
|
||||
if let Some(content) = content {
|
||||
sender = sender.content(content);
|
||||
} else {
|
||||
sender = sender.content("");
|
||||
}
|
||||
}
|
||||
|
||||
let mut current_row = ActionRow::new(&[]);
|
||||
let mut current_index = 0;
|
||||
|
||||
let mut custom_ids: Vec<String> = vec![];
|
||||
|
||||
if let Some(components) = builder.components {
|
||||
let mut rows = vec![];
|
||||
|
||||
for (component, index) in components {
|
||||
if index != current_index {
|
||||
if !current_row.is_empty() {
|
||||
rows.push(current_row.build());
|
||||
current_row = ActionRow::new(&[]);
|
||||
}
|
||||
|
||||
current_index += 1;
|
||||
}
|
||||
|
||||
let custom_id = component.custom_id();
|
||||
|
||||
if let Some(custom_id) = &custom_id {
|
||||
custom_ids.push(custom_id.clone());
|
||||
|
||||
self.rt.registry.register(custom_id, component.clone());
|
||||
}
|
||||
|
||||
current_row.add(custom_id.clone(), component.clone());
|
||||
}
|
||||
|
||||
if !current_row.is_empty() {
|
||||
rows.push(current_row.build());
|
||||
}
|
||||
|
||||
sender = sender.components(rows);
|
||||
}
|
||||
|
||||
if let Some(embeds) = builder.embeds {
|
||||
sender = sender.embeds(embeds);
|
||||
}
|
||||
|
||||
let result = self
|
||||
.rt
|
||||
.http
|
||||
.interaction(self.rt.app.id)
|
||||
.create_response(
|
||||
self.event.id,
|
||||
&self.event.token,
|
||||
&InteractionResponse {
|
||||
data: Some(sender.build()),
|
||||
kind: InteractionResponseType::UpdateMessage,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
if result.is_err() {
|
||||
for custom_id in custom_ids {
|
||||
self.rt.registry.unregister(custom_id);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::database::models::DatabaseError;
|
||||
|
||||
pub mod components;
|
||||
pub mod context;
|
||||
pub mod registry;
|
||||
@@ -12,9 +14,6 @@ pub enum InteractionError {
|
||||
)]
|
||||
InteractionNotFound,
|
||||
|
||||
#[error("The interaction was attempted to be deferred, but it failed to be done")]
|
||||
DeferringFailed,
|
||||
|
||||
#[error("Discord responded with an interaction type Payphone does not use")]
|
||||
InteractionTypeUnsent,
|
||||
|
||||
@@ -26,4 +25,10 @@ pub enum InteractionError {
|
||||
|
||||
#[error("The extras getter failed to retrieve extras")]
|
||||
ExtrasGetterFailed,
|
||||
|
||||
#[error(transparent)]
|
||||
DatabaseError(#[from] DatabaseError),
|
||||
|
||||
#[error(transparent)]
|
||||
DiscordAPIError(#[from] twilight_http::Error),
|
||||
}
|
||||
|
||||
@@ -47,7 +47,10 @@ where
|
||||
if let Some(data) = &event.data {
|
||||
match data {
|
||||
InteractionData::MessageComponent(data) => {
|
||||
if let Some(component) = self.inner.get_async(&data.custom_id).await {
|
||||
if let Some(guarded_component) = self.inner.get(&data.custom_id) {
|
||||
let component = guarded_component.clone();
|
||||
drop(guarded_component);
|
||||
|
||||
let prefixes = rt
|
||||
.get_prefixes(
|
||||
event.guild_id.map(|id| id.get()),
|
||||
@@ -60,7 +63,7 @@ where
|
||||
|
||||
let extras = extras
|
||||
.get_extra(
|
||||
&event.author().expect("Component interaction had no author"),
|
||||
event.author().expect("Component interaction had no author"),
|
||||
&rt,
|
||||
)
|
||||
.await
|
||||
@@ -77,6 +80,10 @@ where
|
||||
extras,
|
||||
))
|
||||
.await
|
||||
.inspect(|_| log::debug!("Executed component interaction"))
|
||||
.inspect_err(|e| {
|
||||
log::error!("Interaction execution failed: {e:?}")
|
||||
})
|
||||
} else {
|
||||
Err(InteractionError::InteractionNotFound)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user