use crate::{
client::Client,
error::Error as HttpError,
request::{self, AuditLogReason, Request, TryIntoRequest},
response::ResponseFuture,
routing::Route,
};
use serde::Serialize;
use twilight_model::{
guild::auto_moderation::{
AutoModerationActionType, AutoModerationEventType, AutoModerationKeywordPresetType,
AutoModerationRule, AutoModerationTriggerType,
},
id::{
marker::{ChannelMarker, GuildMarker, RoleMarker},
Id,
},
};
use twilight_validate::request::{
audit_reason as validate_audit_reason,
auto_moderation_metadata_mention_total_limit as validate_auto_moderation_metadata_mention_total_limit,
ValidationError,
};
#[derive(Serialize)]
struct CreateAutoModerationRuleFieldsAction {
pub kind: AutoModerationActionType,
pub metadata: CreateAutoModerationRuleFieldsActionMetadata,
}
#[derive(Default, Serialize)]
struct CreateAutoModerationRuleFieldsActionMetadata {
pub channel_id: Option<Id<ChannelMarker>>,
pub duration_seconds: Option<u32>,
}
#[derive(Serialize)]
struct CreateAutoModerationRuleFieldsTriggerMetadata<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
allow_list: Option<&'a [&'a str]>,
#[serde(skip_serializing_if = "Option::is_none")]
keyword_filter: Option<&'a [&'a str]>,
#[serde(skip_serializing_if = "Option::is_none")]
presets: Option<&'a [AutoModerationKeywordPresetType]>,
#[serde(skip_serializing_if = "Option::is_none")]
mention_total_limit: Option<u8>,
}
#[derive(Serialize)]
struct CreateAutoModerationRuleFields<'a> {
actions: Option<Vec<CreateAutoModerationRuleFieldsAction>>,
enabled: Option<bool>,
event_type: AutoModerationEventType,
exempt_channels: Option<&'a [Id<ChannelMarker>]>,
exempt_roles: Option<&'a [Id<RoleMarker>]>,
name: &'a str,
trigger_metadata: Option<CreateAutoModerationRuleFieldsTriggerMetadata<'a>>,
trigger_type: Option<AutoModerationTriggerType>,
}
#[must_use = "requests must be configured and executed"]
pub struct CreateAutoModerationRule<'a> {
fields: CreateAutoModerationRuleFields<'a>,
guild_id: Id<GuildMarker>,
http: &'a Client,
reason: Option<&'a str>,
}
impl<'a> CreateAutoModerationRule<'a> {
pub(crate) const fn new(
http: &'a Client,
guild_id: Id<GuildMarker>,
name: &'a str,
event_type: AutoModerationEventType,
) -> Self {
Self {
fields: CreateAutoModerationRuleFields {
actions: None,
enabled: None,
event_type,
exempt_channels: None,
exempt_roles: None,
name,
trigger_metadata: None,
trigger_type: None,
},
guild_id,
http,
reason: None,
}
}
pub fn action_block_message(mut self) -> Self {
self.fields.actions.get_or_insert_with(Vec::new).push(
CreateAutoModerationRuleFieldsAction {
kind: AutoModerationActionType::BlockMessage,
metadata: CreateAutoModerationRuleFieldsActionMetadata::default(),
},
);
self
}
pub fn action_send_alert_message(mut self, channel_id: Id<ChannelMarker>) -> Self {
self.fields.actions.get_or_insert_with(Vec::new).push(
CreateAutoModerationRuleFieldsAction {
kind: AutoModerationActionType::SendAlertMessage,
metadata: CreateAutoModerationRuleFieldsActionMetadata {
channel_id: Some(channel_id),
..Default::default()
},
},
);
self
}
pub fn action_timeout(mut self, duration_seconds: u32) -> Self {
self.fields.actions.get_or_insert_with(Vec::new).push(
CreateAutoModerationRuleFieldsAction {
kind: AutoModerationActionType::Timeout,
metadata: CreateAutoModerationRuleFieldsActionMetadata {
duration_seconds: Some(duration_seconds),
..Default::default()
},
},
);
self
}
pub const fn enabled(mut self, enabled: bool) -> Self {
self.fields.enabled = Some(enabled);
self
}
pub const fn exempt_channels(mut self, exempt_channels: &'a [Id<ChannelMarker>]) -> Self {
self.fields.exempt_channels = Some(exempt_channels);
self
}
pub const fn exempt_roles(mut self, exempt_roles: &'a [Id<RoleMarker>]) -> Self {
self.fields.exempt_roles = Some(exempt_roles);
self
}
pub fn with_keyword(
mut self,
keyword_filter: &'a [&'a str],
) -> ResponseFuture<AutoModerationRule> {
self.fields.trigger_metadata = Some(CreateAutoModerationRuleFieldsTriggerMetadata {
allow_list: None,
keyword_filter: Some(keyword_filter),
presets: None,
mention_total_limit: None,
});
self.fields.trigger_type = Some(AutoModerationTriggerType::Keyword);
self.exec()
}
pub fn with_spam(mut self) -> ResponseFuture<AutoModerationRule> {
self.fields.trigger_type = Some(AutoModerationTriggerType::Spam);
self.exec()
}
pub fn with_keyword_preset(
mut self,
presets: &'a [AutoModerationKeywordPresetType],
allow_list: &'a [&'a str],
) -> ResponseFuture<AutoModerationRule> {
self.fields.trigger_metadata = Some(CreateAutoModerationRuleFieldsTriggerMetadata {
allow_list: Some(allow_list),
keyword_filter: None,
presets: Some(presets),
mention_total_limit: None,
});
self.fields.trigger_type = Some(AutoModerationTriggerType::KeywordPreset);
self.exec()
}
pub fn with_mention_spam(
mut self,
mention_total_limit: u8,
) -> Result<ResponseFuture<AutoModerationRule>, ValidationError> {
#[allow(clippy::question_mark)]
if let Err(source) =
validate_auto_moderation_metadata_mention_total_limit(mention_total_limit)
{
return Err(source);
}
self.fields.trigger_metadata = Some(CreateAutoModerationRuleFieldsTriggerMetadata {
allow_list: None,
keyword_filter: None,
presets: None,
mention_total_limit: Some(mention_total_limit),
});
self.fields.trigger_type = Some(AutoModerationTriggerType::MentionSpam);
Ok(self.exec())
}
fn exec(self) -> ResponseFuture<AutoModerationRule> {
let http = self.http;
match self.try_into_request() {
Ok(request) => http.request(request),
Err(source) => ResponseFuture::error(source),
}
}
}
impl<'a> AuditLogReason<'a> for CreateAutoModerationRule<'a> {
fn reason(mut self, reason: &'a str) -> Result<Self, ValidationError> {
validate_audit_reason(reason)?;
self.reason.replace(reason);
Ok(self)
}
}
impl TryIntoRequest for CreateAutoModerationRule<'_> {
fn try_into_request(self) -> Result<Request, HttpError> {
let mut request = Request::builder(&Route::CreateAutoModerationRule {
guild_id: self.guild_id.get(),
})
.json(&self.fields)?;
if let Some(reason) = self.reason {
let header = request::audit_header(reason)?;
request = request.headers(header);
}
Ok(request.build())
}
}