twilight_model/guild/auto_moderation/
mod.rs

1//! Types for Auto Moderation.
2//!
3//! Auto Moderation is a feature which allows each guild to set up rules that
4//! trigger based on some criteria. For example, a rule can trigger whenever a
5//! message contains a specific keyword.
6//!
7//! Rules can be configured to automatically execute actions whenever they
8//! trigger. For example, if a user tries to send a message which contains a
9//! certain keyword, a rule can trigger and block the message before it is sent.
10
11#![warn(missing_docs)]
12
13mod action;
14mod event_type;
15mod preset_type;
16mod trigger_metadata;
17mod trigger_type;
18
19pub use self::{
20    action::{AutoModerationAction, AutoModerationActionMetadata, AutoModerationActionType},
21    event_type::AutoModerationEventType,
22    preset_type::AutoModerationKeywordPresetType,
23    trigger_metadata::AutoModerationTriggerMetadata,
24    trigger_type::AutoModerationTriggerType,
25};
26
27use crate::id::{
28    marker::{AutoModerationRuleMarker, ChannelMarker, GuildMarker, RoleMarker, UserMarker},
29    Id,
30};
31use serde::{Deserialize, Serialize};
32
33/// Configured auto moderation rule.
34#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
35pub struct AutoModerationRule {
36    /// Actions which will execute when the rule is triggered.
37    pub actions: Vec<AutoModerationAction>,
38    /// User which created the rule.
39    pub creator_id: Id<UserMarker>,
40    /// Whether the rule is enabled.
41    pub enabled: bool,
42    /// Rule event type.
43    pub event_type: AutoModerationEventType,
44    /// Channels that should not be affected by the rule.
45    ///
46    /// Maximum of 50.
47    pub exempt_channels: Vec<Id<ChannelMarker>>,
48    /// Roles that should not be affected by the rule.
49    ///
50    /// Maximum of 20.
51    pub exempt_roles: Vec<Id<RoleMarker>>,
52    /// ID of the guild the rule belongs to.
53    pub guild_id: Id<GuildMarker>,
54    /// ID of the rule.
55    pub id: Id<AutoModerationRuleMarker>,
56    /// Name of the rule.
57    pub name: String,
58    /// Rule trigger metadata.
59    pub trigger_metadata: AutoModerationTriggerMetadata,
60    /// Rule trigger type.
61    pub trigger_type: AutoModerationTriggerType,
62}
63
64#[cfg(test)]
65mod tests {
66    use super::{
67        AutoModerationAction, AutoModerationActionMetadata, AutoModerationActionType,
68        AutoModerationEventType, AutoModerationRule, AutoModerationTriggerMetadata,
69        AutoModerationTriggerType,
70    };
71    use crate::id::{
72        marker::{AutoModerationRuleMarker, ChannelMarker, GuildMarker, RoleMarker, UserMarker},
73        Id,
74    };
75    use serde::{Deserialize, Serialize};
76    use serde_test::Token;
77    use static_assertions::{assert_fields, assert_impl_all};
78    use std::{fmt::Debug, hash::Hash};
79
80    assert_fields!(
81        AutoModerationRule: actions,
82        creator_id,
83        enabled,
84        event_type,
85        exempt_channels,
86        exempt_roles,
87        guild_id,
88        id,
89        name,
90        trigger_metadata,
91        trigger_type
92    );
93    assert_impl_all!(
94        AutoModerationRule: Clone,
95        Debug,
96        Deserialize<'static>,
97        Eq,
98        Hash,
99        PartialEq,
100        Serialize,
101        Send,
102        Sync,
103    );
104
105    #[allow(clippy::too_many_lines)]
106    #[test]
107    fn rule() {
108        const ACTION_CHANNEL_ID: Id<ChannelMarker> = Id::new(1);
109        const AUTO_MODERATION_RULE_ID: Id<AutoModerationRuleMarker> = Id::new(2);
110        const CREATOR_ID: Id<UserMarker> = Id::new(3);
111        const EXEMPT_CHANNEL_ID: Id<ChannelMarker> = Id::new(4);
112        const EXEMPT_ROLE_ID: Id<RoleMarker> = Id::new(5);
113        const GUILD_ID: Id<GuildMarker> = Id::new(6);
114
115        let value = AutoModerationRule {
116            actions: Vec::from([
117                AutoModerationAction {
118                    kind: AutoModerationActionType::BlockMessage,
119                    metadata: None,
120                },
121                AutoModerationAction {
122                    kind: AutoModerationActionType::SendAlertMessage,
123                    metadata: Some(AutoModerationActionMetadata {
124                        channel_id: Some(ACTION_CHANNEL_ID),
125                        custom_message: None,
126                        duration_seconds: None,
127                    }),
128                },
129                AutoModerationAction {
130                    kind: AutoModerationActionType::Timeout,
131                    metadata: Some(AutoModerationActionMetadata {
132                        channel_id: None,
133                        custom_message: None,
134                        duration_seconds: Some(120),
135                    }),
136                },
137            ]),
138            creator_id: CREATOR_ID,
139            enabled: true,
140            event_type: AutoModerationEventType::MessageSend,
141            exempt_channels: Vec::from([EXEMPT_CHANNEL_ID]),
142            exempt_roles: Vec::from([EXEMPT_ROLE_ID]),
143            guild_id: GUILD_ID,
144            id: AUTO_MODERATION_RULE_ID,
145            name: "rule".into(),
146            trigger_metadata: AutoModerationTriggerMetadata {
147                allow_list: None,
148                keyword_filter: Some(Vec::from(["shoot".into(), "darn".into()])),
149                presets: None,
150                mention_raid_protection_enabled: Some(true),
151                mention_total_limit: None,
152                regex_patterns: Some(Vec::from(["[a-z]+".into(), "[0-9]+".into()])),
153            },
154            trigger_type: AutoModerationTriggerType::Keyword,
155        };
156
157        serde_test::assert_tokens(
158            &value,
159            &[
160                Token::Struct {
161                    name: "AutoModerationRule",
162                    len: 11,
163                },
164                Token::Str("actions"),
165                Token::Seq { len: Some(3) },
166                Token::Struct {
167                    name: "AutoModerationAction",
168                    len: 1,
169                },
170                Token::Str("type"),
171                Token::U8(u8::from(AutoModerationActionType::BlockMessage)),
172                Token::StructEnd,
173                Token::Struct {
174                    name: "AutoModerationAction",
175                    len: 2,
176                },
177                Token::Str("type"),
178                Token::U8(u8::from(AutoModerationActionType::SendAlertMessage)),
179                Token::Str("metadata"),
180                Token::Some,
181                Token::Struct {
182                    name: "AutoModerationActionMetadata",
183                    len: 1,
184                },
185                Token::Str("channel_id"),
186                Token::Some,
187                Token::NewtypeStruct { name: "Id" },
188                Token::Str("1"),
189                Token::StructEnd,
190                Token::StructEnd,
191                Token::Struct {
192                    name: "AutoModerationAction",
193                    len: 2,
194                },
195                Token::Str("type"),
196                Token::U8(u8::from(AutoModerationActionType::Timeout)),
197                Token::Str("metadata"),
198                Token::Some,
199                Token::Struct {
200                    name: "AutoModerationActionMetadata",
201                    len: 1,
202                },
203                Token::Str("duration_seconds"),
204                Token::Some,
205                Token::U32(120),
206                Token::StructEnd,
207                Token::StructEnd,
208                Token::SeqEnd,
209                Token::Str("creator_id"),
210                Token::NewtypeStruct { name: "Id" },
211                Token::Str("3"),
212                Token::Str("enabled"),
213                Token::Bool(true),
214                Token::Str("event_type"),
215                Token::U8(u8::from(AutoModerationEventType::MessageSend)),
216                Token::Str("exempt_channels"),
217                Token::Seq { len: Some(1) },
218                Token::NewtypeStruct { name: "Id" },
219                Token::Str("4"),
220                Token::SeqEnd,
221                Token::Str("exempt_roles"),
222                Token::Seq { len: Some(1) },
223                Token::NewtypeStruct { name: "Id" },
224                Token::Str("5"),
225                Token::SeqEnd,
226                Token::Str("guild_id"),
227                Token::NewtypeStruct { name: "Id" },
228                Token::Str("6"),
229                Token::Str("id"),
230                Token::NewtypeStruct { name: "Id" },
231                Token::Str("2"),
232                Token::Str("name"),
233                Token::Str("rule"),
234                Token::Str("trigger_metadata"),
235                Token::Struct {
236                    name: "AutoModerationTriggerMetadata",
237                    len: 3,
238                },
239                Token::Str("keyword_filter"),
240                Token::Some,
241                Token::Seq { len: Some(2) },
242                Token::Str("shoot"),
243                Token::Str("darn"),
244                Token::SeqEnd,
245                Token::Str("mention_raid_protection_enabled"),
246                Token::Some,
247                Token::Bool(true),
248                Token::Str("regex_patterns"),
249                Token::Some,
250                Token::Seq { len: Some(2) },
251                Token::Str("[a-z]+"),
252                Token::Str("[0-9]+"),
253                Token::SeqEnd,
254                Token::StructEnd,
255                Token::Str("trigger_type"),
256                Token::U8(u8::from(AutoModerationTriggerType::Keyword)),
257                Token::StructEnd,
258            ],
259        );
260    }
261}