diff --git a/src/main/java/net/dv8tion/jda/api/audit/ActionType.java b/src/main/java/net/dv8tion/jda/api/audit/ActionType.java index c77764b6ca..32efb8933e 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/ActionType.java +++ b/src/main/java/net/dv8tion/jda/api/audit/ActionType.java @@ -586,6 +586,55 @@ public enum ActionType */ APPLICATION_COMMAND_PRIVILEGES_UPDATE(121, TargetType.INTEGRATION), + /** + * A moderator created a new {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule} + */ + AUTO_MODERATION_RULE_CREATE(140, TargetType.AUTO_MODERATION_RULE), + + /** + * A moderator updated an existing {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule} + */ + AUTO_MODERATION_RULE_UPDATE(141, TargetType.AUTO_MODERATION_RULE), + + /** + * A moderator deleted an existing {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule} + */ + AUTO_MODERATION_RULE_DELETE(142, TargetType.AUTO_MODERATION_RULE), + + /** + * An automod rule blocked a message from being sent + * + *

Possible Keys
+ *

+ */ + AUTO_MODERATION_RULE_BLOCK_MESSAGE(143, TargetType.MEMBER), + + /** + * An automod rule sent an alert to a channel + * + *

Possible Keys
+ *

+ */ + AUTO_MODERATION_FLAG_TO_CHANNEL( 144, TargetType.MEMBER), + + /** + * An automod rule put a user in {@link Member#isTimedOut() timeout} + * + *

Possible Keys
+ *

+ */ + AUTO_MODERATION_MEMBER_TIMEOUT( 145, TargetType.MEMBER), + UNKNOWN(-1, TargetType.UNKNOWN); private final int key; diff --git a/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java b/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java index 3c2b45da6e..848a42eb88 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java +++ b/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java @@ -20,6 +20,8 @@ import net.dv8tion.jda.annotations.ForRemoval; import net.dv8tion.jda.annotations.ReplaceWith; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.attribute.ICategorizableChannel; @@ -312,6 +314,13 @@ public enum AuditLogKey */ CHANNEL_AVAILABLE_TAGS("available_tags"), + /** + * The relevant channel for the audit log entry. + * + *

Expected type: String + */ + CHANNEL_ID("channel_id"), + // /** // * The {@link ForumChannel#getDefaultSortOrder()} value. // *
Only for {@link ChannelType#FORUM}. @@ -657,7 +666,25 @@ public enum AuditLogKey * *

Expected type: int */ - INVITE_MAX_USES("max_uses"); + INVITE_MAX_USES("max_uses"), + + // AUTO MODERATION + /** + * Change of the {@link AutoModRule#getName()} for the target {@link AutoModRule} + * + *

Expected type: String + */ + AUTO_MODERATION_RULE_NAME("auto_moderation_rule_name"), + + /** + * The {@link AutoModRule#getTriggerType()} for an {@link AutoModRule} trigger + * + *

Use with {@link AutoModTriggerType#fromKey(int)} + * + *

Expected type: int + */ + AUTO_MODERATION_RULE_TRIGGER_TYPE("auto_moderation_rule_trigger_type"), + ; private final String key; diff --git a/src/main/java/net/dv8tion/jda/api/audit/TargetType.java b/src/main/java/net/dv8tion/jda/api/audit/TargetType.java index 98c5918365..7ef8b38967 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/TargetType.java +++ b/src/main/java/net/dv8tion/jda/api/audit/TargetType.java @@ -42,6 +42,7 @@ public enum TargetType STAGE_INSTANCE, STICKER, THREAD, - SCHEDULED_EVENT, + SCHEDULED_EVENT, + AUTO_MODERATION_RULE, UNKNOWN } diff --git a/src/main/java/net/dv8tion/jda/api/entities/Guild.java b/src/main/java/net/dv8tion/jda/api/entities/Guild.java index a08d1984b4..1775542b6e 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -22,6 +22,10 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Region; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.entities.automod.build.AutoModRuleData; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.attribute.ICopyableChannel; import net.dv8tion.jda.api.entities.channel.attribute.IGuildChannelContainer; @@ -42,10 +46,7 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.privileges.IntegrationPrivilege; -import net.dv8tion.jda.api.managers.AudioManager; -import net.dv8tion.jda.api.managers.GuildManager; -import net.dv8tion.jda.api.managers.GuildStickerManager; -import net.dv8tion.jda.api.managers.GuildWelcomeScreenManager; +import net.dv8tion.jda.api.managers.*; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.*; @@ -406,6 +407,142 @@ default RestAction> retrieveRegions() @CheckReturnValue RestAction> retrieveRegions(boolean includeDeprecated); + /** + * Retrieves all current {@link AutoModRule AutoModRules} for this guild. + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission + * + * @return {@link RestAction} - Type: {@link List} of {@link AutoModRule} + */ + @Nonnull + @CheckReturnValue + RestAction> retrieveAutoModRules(); + + /** + * Retrieves the {@link AutoModRule} for the provided id. + * + * @param id + * The id of the rule + * + * @throws IllegalArgumentException + * If the provided id is not a valid snowflake + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission + * + * @return {@link RestAction} - Type: {@link AutoModRule} + */ + @Nonnull + @CheckReturnValue + RestAction retrieveAutoModRuleById(@Nonnull String id); + + /** + * Retrieves the {@link AutoModRule} for the provided id. + * + * @param id + * The id of the rule + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission + * + * @return {@link RestAction} - Type: {@link AutoModRule} + */ + @Nonnull + @CheckReturnValue + default RestAction retrieveAutoModRuleById(long id) + { + return retrieveAutoModRuleById(Long.toUnsignedString(id)); + } + + /** + * Creates a new {@link AutoModRule} for this guild. + * + *

You can only create a certain number of rules for each {@link AutoModTriggerType AutoModTriggerType}. + * The maximum is provided by {@link AutoModTriggerType#getMaxPerGuild()}. + * + * @param data + * The data for the new rule + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link AutoModRuleData#getRequiredPermissions() required permissions} + * @throws IllegalStateException + *

+ * + * @return {@link AuditableRestAction} - Type: {@link AutoModRule} + */ + @Nonnull + @CheckReturnValue + AuditableRestAction createAutoModRule(@Nonnull AutoModRuleData data); + + /** + * Returns an {@link AutoModRuleManager}, which can be used to modify the rule for the provided id. + *

The manager allows modifying multiple fields in a single request. + *
You modify multiple fields in one request by chaining setters before calling {@link net.dv8tion.jda.api.requests.RestAction#queue() RestAction.queue()}. + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission. + * + * @return The manager instance + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager modifyAutoModRuleById(@Nonnull String id); + + /** + * Returns an {@link AutoModRuleManager}, which can be used to modify the rule for the provided id. + *

The manager allows modifying multiple fields in a single request. + *
You modify multiple fields in one request by chaining setters before calling {@link net.dv8tion.jda.api.requests.RestAction#queue() RestAction.queue()}. + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission. + * + * @return The manager instance + */ + @Nonnull + @CheckReturnValue + default AutoModRuleManager modifyAutoModRuleById(long id) + { + return modifyAutoModRuleById(Long.toUnsignedString(id)); + } + + /** + * Deletes the {@link AutoModRule} for the provided id. + * + * @param id + * The id of the rule + * + * @throws IllegalArgumentException + * If the provided id is not a valid snowflake + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission + * + * @return {@link AuditableRestAction} - Type: {@link Void} + */ + @Nonnull + @CheckReturnValue + AuditableRestAction deleteAutoModRuleById(@Nonnull String id); + + /** + * Deletes the {@link AutoModRule} for the provided id. + * + * @param id + * The id of the rule + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission + * + * @return {@link AuditableRestAction} - Type: {@link Void} + */ + @Nonnull + @CheckReturnValue + default AuditableRestAction deleteAutoModRuleById(long id) + { + return deleteAutoModRuleById(Long.toUnsignedString(id)); + } + /** * Adds the user to this guild as a member. *
This requires an OAuth2 Access Token with the scope {@code guilds.join}. diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index 29dc086a9a..334c071747 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -1132,6 +1132,18 @@ default MessageEditAction editMessageAttachments(@Nonnull AttachedFile... attach * *

For further info, see {@link GuildMessageChannel#sendStickers(Collection)} and {@link MessageCreateAction#setMessageReference(Message)}. * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

+ * * @param stickers * The 1-3 stickers to send * @@ -1172,6 +1184,18 @@ default MessageCreateAction replyStickers(@Nonnull CollectionFor further info, see {@link GuildMessageChannel#sendStickers(Collection)} and {@link MessageCreateAction#setMessageReference(Message)}. * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

+ * * @param stickers * The 1-3 stickers to send * @@ -1204,7 +1228,19 @@ default MessageCreateAction replyStickers(@Nonnull StickerSnowflake... stickers) } /** - * Shortcut for {@code getChannel().sendMessage(content).setMessageReference(this)}- + * Shortcut for {@code getChannel().sendMessage(content).setMessageReference(this)}. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

* * @param content * The reply content @@ -1224,7 +1260,19 @@ default MessageCreateAction reply(@Nonnull CharSequence content) } /** - * Shortcut for {@code getChannel().sendMessage(data).setMessageReference(this)}- + * Shortcut for {@code getChannel().sendMessage(data).setMessageReference(this)}. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

* * @param msg * The {@link MessageCreateData} to send @@ -1244,7 +1292,19 @@ default MessageCreateAction reply(@Nonnull MessageCreateData msg) } /** - * Shortcut for {@code getChannel().sendMessageEmbeds(embed, other).setMessageReference(this)}- + * Shortcut for {@code getChannel().sendMessageEmbeds(embed, other).setMessageReference(this)}. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

* * @param embed * The {@link MessageEmbed} to send @@ -1271,7 +1331,19 @@ default MessageCreateAction replyEmbeds(@Nonnull MessageEmbed embed, @Nonnull Me } /** - * Shortcut for {@code getChannel().sendMessageEmbeds(embeds).setMessageReference(this)}- + * Shortcut for {@code getChannel().sendMessageEmbeds(embeds).setMessageReference(this)}. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

* * @param embeds * The {@link MessageEmbed MessageEmbeds} to send @@ -1293,6 +1365,18 @@ default MessageCreateAction replyEmbeds(@Nonnull CollectionPossible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + * + * * @param component * The {@link LayoutComponent} to send * @param other @@ -1320,6 +1404,18 @@ default MessageCreateAction replyComponents(@Nonnull LayoutComponent component, /** * Shortcut for {@code getChannel().sendMessageComponents(components).setMessageReference(this)}. * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

+ * * @param components * The {@link LayoutComponent LayoutComponents} to send * @@ -1338,7 +1434,19 @@ default MessageCreateAction replyComponents(@Nonnull CollectionPossible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + * * * @param format * The format string @@ -1360,7 +1468,19 @@ default MessageCreateAction replyFormat(@Nonnull String format, @Nonnull Object. } /** - * Shortcut for {@code getChannel().sendFiles(files).setMessageReference(this)}- + * Shortcut for {@code getChannel().sendFiles(files).setMessageReference(this)}. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

* * @param files * The {@link FileUpload FileUploads} to send @@ -1380,7 +1500,19 @@ default MessageCreateAction replyFiles(@Nonnull FileUpload... files) } /** - * Shortcut for {@code getChannel().sendFiles(files).setMessageReference(this)}- + * Shortcut for {@code getChannel().sendFiles(files).setMessageReference(this)}. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

* * @param files * The {@link FileUpload FileUploads} to send diff --git a/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java b/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java index 860f32d9b8..d0fe01f6d7 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java +++ b/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java @@ -53,6 +53,12 @@ public interface WebhookClient * * * @param content @@ -76,6 +82,12 @@ public interface WebhookClient *
    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • *
* * @param message @@ -101,6 +113,12 @@ public interface WebhookClient *
    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • *
* * @param format @@ -130,6 +148,12 @@ default WebhookMessageCreateAction sendMessageFormat(@Nonnull String format, *
    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • *
* *

Example: Attachment Images @@ -171,6 +195,12 @@ default WebhookMessageCreateAction sendMessageFormat(@Nonnull String format, *

    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • *
* *

Example: Attachment Images @@ -222,6 +252,12 @@ default WebhookMessageCreateAction sendMessageEmbeds(@Nonnull MessageEmbed em *

    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • *
* * @param components @@ -245,6 +281,12 @@ default WebhookMessageCreateAction sendMessageEmbeds(@Nonnull MessageEmbed em *
    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • *
* * @param component @@ -297,6 +339,21 @@ default WebhookMessageCreateAction sendMessageComponents(@Nonnull LayoutCompo * .queue(); * } * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} + *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} + *
    If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
  • + *
+ * * @param files * The {@link FileUpload FileUploads} to attach to the message * @@ -339,6 +396,21 @@ default WebhookMessageCreateAction sendMessageComponents(@Nonnull LayoutCompo * .queue(); * } * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK} + *
    The webhook is no longer available, either it was deleted or in case of interactions it expired.
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} + *
    If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
  • + *
+ * * @param files * The {@link FileUpload FileUploads} to attach to the message * diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModEventType.java b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModEventType.java new file mode 100644 index 0000000000..4b7f6967c8 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModEventType.java @@ -0,0 +1,86 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod; + +import net.dv8tion.jda.annotations.Incubating; +import net.dv8tion.jda.api.entities.automod.build.AutoModRuleData; +import net.dv8tion.jda.api.entities.automod.build.TriggerConfig; + +import javax.annotation.Nonnull; + +/** + * The type of event an {@link AutoModRule} is triggered by. + * + * @see AutoModRule#getEventType() + * @see AutoModRuleData#onMessage(String, TriggerConfig) + */ +public enum AutoModEventType +{ + /** + * The rule is triggered by a message being sent in a guild channel. + */ + MESSAGE_SEND(1), + + /** + * The rule is triggered when a member updates their profile. + * + * @incubating This has not been officially released yet + */ + @Incubating + MEMBER_UPDATE(2), + + /** + * Placeholder for unknown types which haven't been added yet. + */ + UNKNOWN(-1); + + private final int key; + + AutoModEventType(int key) + { + this.key = key; + } + + /** + * The raw value used by Discord to represent this type. + * + * @return The raw value + */ + public int getKey() + { + return key; + } + + /** + * The {@link AutoModEventType} represented by the provided key. + * + * @param key + * The raw key + * + * @return The {@link AutoModEventType} or {@link #UNKNOWN} + */ + @Nonnull + public static AutoModEventType fromKey(int key) + { + for (AutoModEventType type : values()) + { + if (type.key == key) + return type; + } + return UNKNOWN; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModExecution.java b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModExecution.java new file mode 100644 index 0000000000..50a4ac1807 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModExecution.java @@ -0,0 +1,168 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.unions.GuildMessageChannelUnion; +import net.dv8tion.jda.api.requests.GatewayIntent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Event triggered by an {@link AutoModRule} activation. + */ +public interface AutoModExecution +{ + /** + * The {@link Guild} that this execution occurred in. + * + * @return The {@link Guild} + */ + @Nonnull + Guild getGuild(); + + /** + * The {@link GuildMessageChannelUnion} that this execution occurred in. + * + *

This might be {@code null} if the execution occurred by future event types. + * + * @return The {@link GuildMessageChannelUnion} + */ + @Nullable + GuildMessageChannelUnion getChannel(); + + /** + * The {@link AutoModResponse} that has been triggered by this execution. + * + * @return The {@link AutoModResponse} + */ + @Nonnull + AutoModResponse getResponse(); + + /** + * The {@link AutoModTriggerType} for the execution. + * + * @return The {@link AutoModTriggerType} + */ + @Nonnull + AutoModTriggerType getTriggerType(); + + /** + * The id of the user that triggered this execution. + * + * @return The id of the user + */ + long getUserIdLong(); + + /** + * The id of the user that triggered this execution. + * + * @return The id of the user + */ + @Nonnull + default String getUserId() + { + return Long.toUnsignedString(getUserIdLong()); + } + + /** + * The id of the {@link AutoModRule} which has been triggered. + * + * @return The id of the rule + */ + long getRuleIdLong(); + + /** + * The id of the {@link AutoModRule} which has been triggered. + * + * @return The id of the rule + */ + @Nonnull + default String getRuleId() + { + return Long.toUnsignedString(getRuleIdLong()); + } + + /** + * The id of the {@link net.dv8tion.jda.api.entities.Message Message} which triggered the rule. + * + * @return The id of the message, or 0 if the message has been blocked + */ + long getMessageIdLong(); + + /** + * The id of the {@link net.dv8tion.jda.api.entities.Message Message} which triggered the rule. + * + * @return The id of the message, or {@code null} if the message has been blocked + */ + @Nullable + default String getMessageId() + { + long id = getMessageIdLong(); + return id == 0L ? null : Long.toUnsignedString(getMessageIdLong()); + } + + /** + * The id of the alert {@link net.dv8tion.jda.api.entities.Message Message} sent to the alert channel. + * + * @return The id of the alert message, or 0 if {@link AutoModResponse#getType()} is not {@link AutoModResponse.Type#SEND_ALERT_MESSAGE} + */ + long getAlertMessageIdLong(); + + /** + * The id of the alert {@link net.dv8tion.jda.api.entities.Message Message} sent to the alert channel. + * + * @return The id of the alert message, or {@code null} if {@link AutoModResponse#getType()} is not {@link AutoModResponse.Type#SEND_ALERT_MESSAGE} + */ + @Nullable + default String getAlertMessageId() + { + long id = getAlertMessageIdLong(); + return id == 0L ? null : Long.toUnsignedString(getAlertMessageIdLong()); + } + + /** + * The user content that triggered this rule. + * + *

This is empty if {@link GatewayIntent#MESSAGE_CONTENT} is not enabled. + * However, you can still use {@link #getMatchedKeyword()} regardless. + * + * @return The user content + */ + @Nonnull + String getContent(); + + /** + * The substring match of the user content that triggered this rule. + * + *

This is empty if {@link GatewayIntent#MESSAGE_CONTENT} is not enabled. + * However, you can still use {@link #getMatchedKeyword()} regardless. + * + * @return The user content substring + */ + @Nullable + String getMatchedContent(); + + /** + * The keyword that was found in the {@link #getContent()}. + * + * @return The keyword that was found in the content + */ + @Nullable + String getMatchedKeyword(); +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModResponse.java b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModResponse.java new file mode 100644 index 0000000000..b0cfe0a57a --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModResponse.java @@ -0,0 +1,260 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod; + +import net.dv8tion.jda.annotations.Incubating; +import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.utils.data.SerializableData; +import net.dv8tion.jda.internal.entities.automod.AutoModResponseImpl; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.Duration; +import java.util.EnumSet; + +/** + * An automated response to an {@link AutoModRule}. + */ +public interface AutoModResponse extends SerializableData +{ + /** + * The maximum length of a custom message. ({@value}) + */ + int MAX_CUSTOM_MESSAGE_LENGTH = 150; + + /** + * The type of response. + * + * @return The type of response + */ + @Nonnull + Type getType(); + + /** + * The channel to send the alert message to. + * + * @return The channel to send the alert message to, or null if this is not a {@link Type#SEND_ALERT_MESSAGE} response + */ + @Nullable + GuildMessageChannel getChannel(); + + /** + * The custom message to send to the user. + * + * @return The custom message to send to the user, or null if this is not a {@link Type#BLOCK_MESSAGE} response + */ + @Nullable + String getCustomMessage(); + + /** + * The duration to timeout the user for. + * + * @return The duration to timeout the user for, or null if this is not a {@link Type#TIMEOUT} response + */ + @Nullable + Duration getTimeoutDuration(); + + /** + * Create a response that will block the message. + *

You can optionally pass a custom message to send to the user. + * + * @return The response instance + * + * @see #blockMessage(String) + */ + @Nonnull + static AutoModResponse blockMessage() + { + return blockMessage(null); + } + + /** + * Create a response that will block the message. + * + * @param customMessage + * The custom message to send to the user, or null to send the default message + * + * @throws IllegalArgumentException + * If the provided custom message is longer than {@value #MAX_CUSTOM_MESSAGE_LENGTH} characters + * + * @return The response instance + */ + @Nonnull + static AutoModResponse blockMessage(@Nullable String customMessage) + { + return new AutoModResponseImpl(Type.BLOCK_MESSAGE, customMessage); + } + + /** + * Create a response that will send an alert message to the specified channel. + * + * @param channel + * The channel to send the alert message to + * + * @throws IllegalArgumentException + * If the provided channel is {@code null} + * + * @return The response instance + */ + @Nonnull + static AutoModResponse sendAlert(@Nonnull GuildMessageChannel channel) + { + Checks.notNull(channel, "Channel"); + return new AutoModResponseImpl(Type.SEND_ALERT_MESSAGE, channel); + } + + /** + * Create a response that will timeout the user for the specified duration. + * + *

To create a rule with this response, the creator must also have the {@link net.dv8tion.jda.api.Permission#MODERATE_MEMBERS MODERATE_MEMBERS} permission. + * + * @param duration + * The duration to timeout the user for + * + * @throws IllegalArgumentException + * If the provided duration is not positive or longer than {@value net.dv8tion.jda.api.entities.Member#MAX_TIME_OUT_LENGTH} days + * + * @return The response instance + */ + @Nonnull + static AutoModResponse timeoutMember(@Nonnull Duration duration) + { + Checks.notNull(duration, "Duration"); + Checks.check(!duration.isNegative() && !duration.isZero(), "Duration must be positive"); + return new AutoModResponseImpl(Type.TIMEOUT, duration); + } + + /** + * Create a response that will prevent the member from interacting with anything in the guild until the offending content is removed. + * + * @return The response instance + * + * @incubating This has not been officially released yet + */ + @Nonnull + @Incubating + static AutoModResponse blockMemberInteraction() + { + return new AutoModResponseImpl(Type.BLOCK_MEMBER_INTERACTION); + } + + /** + * The type of response. + */ + enum Type + { + /** + * Blocks the message from being sent. + */ + BLOCK_MESSAGE(1, EnumSet.of(AutoModTriggerType.KEYWORD, AutoModTriggerType.KEYWORD_PRESET, AutoModTriggerType.SPAM, AutoModTriggerType.MENTION_SPAM)), + /** + * Sends an alert message to the specified channel. + */ + SEND_ALERT_MESSAGE(2, EnumSet.of(AutoModTriggerType.KEYWORD, AutoModTriggerType.KEYWORD_PRESET, AutoModTriggerType.SPAM, AutoModTriggerType.MENTION_SPAM)), + /** + * Times out the user for the specified duration. + * + *

To create a rule with this response, the creator must also have the {@link net.dv8tion.jda.api.Permission#MODERATE_MEMBERS MODERATE_MEMBERS} permission. + */ + TIMEOUT(3, EnumSet.of(AutoModTriggerType.KEYWORD, AutoModTriggerType.MENTION_SPAM)), + /** + * Blocks the member from interacting with the guild until they update the offending content. + * + * @incubating This has not been officially released yet + */ + @Incubating + BLOCK_MEMBER_INTERACTION(4, EnumSet.of(AutoModTriggerType.MEMBER_PROFILE_KEYWORD)), + /** + * Placeholder for unknown types. + */ + UNKNOWN(-1, EnumSet.noneOf(AutoModTriggerType.class)) + ; + + private final int key; + private final EnumSet supportedTypes; + + Type(int key) + { + this.key = key; + this.supportedTypes = EnumSet.complementOf(EnumSet.of(AutoModTriggerType.UNKNOWN)); + } + + Type(int key, EnumSet supportedTypes) + { + this.key = key; + this.supportedTypes = supportedTypes; + } + + /** + * The raw value used by Discord to represent this type. + * + * @return The raw value + */ + public int getKey() + { + return key; + } + + /** + * The {@link AutoModTriggerType AutoModTriggerTypes} that this response supports. + * + * @return The supported trigger types + */ + @Nonnull + public EnumSet getSupportedTypes() + { + return EnumSet.copyOf(supportedTypes); + } + + /** + * Whether this response supports the provided trigger type. + * + * @param type + * The trigger type + * + * @throws IllegalArgumentException + * If the provided trigger type is {@code null} + * + * @return True, if this response supports the provided trigger type + */ + public boolean isSupportedTrigger(@Nonnull AutoModTriggerType type) + { + Checks.notNull(type, "AutoModTriggerType"); + return supportedTypes.contains(type); + } + + /** + * The {@link Type} represented by the provided key. + * + * @param key + * The raw key + * + * @return The {@link Type} or {@link #UNKNOWN} + */ + @Nonnull + public static Type fromKey(int key) + { + for (Type type : values()) + { + if (type.key == key) + return type; + } + return UNKNOWN; + } + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModRule.java b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModRule.java new file mode 100644 index 0000000000..952ee53142 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModRule.java @@ -0,0 +1,305 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.ISnowflake; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.automod.build.AutoModRuleData; +import net.dv8tion.jda.api.entities.automod.build.TriggerConfig; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; +import net.dv8tion.jda.api.managers.AutoModRuleManager; +import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; + +import javax.annotation.Nonnull; +import java.util.EnumSet; +import java.util.List; + +/** + * Rule used for auto-moderation in a {@link Guild}. + * + * @see Guild#retrieveAutoModRules() + * @see Guild#createAutoModRule(AutoModRuleData) + */ +public interface AutoModRule extends ISnowflake +{ + /** + * The maximum length of a rule name. ({@value}) + */ + int MAX_RULE_NAME_LENGTH = 100; + /** + * The maximum length of a keyword in {@link TriggerConfig#keywordFilter(String...)}. ({@value}) + */ + int MAX_KEYWORD_LENGTH = 60; + /** + * The maximum amount of keywords in {@link TriggerConfig#keywordFilter(String...)}. ({@value}) + */ + int MAX_KEYWORD_AMOUNT = 1000; + /** + * The maximum amount of whitelisted keywords in {@link TriggerConfig#keywordFilter(String...)}. ({@value}) + */ + int MAX_ALLOWLIST_CUSTOM_AMOUNT = 100; + /** + * The maximum amount of whitelisted keywords in {@link TriggerConfig#presetKeywordFilter(KeywordPreset...)}. ({@value}) + */ + int MAX_ALLOWLIST_PRESET_AMOUNT = 1000; + /** + * The maximum length of a regex pattern in {@link TriggerConfig#patternFilter(String...)}. ({@value}) + */ + int MAX_PATTERN_LENGTH = 260; + /** + * The maximum amount of regex patterns in {@link TriggerConfig#patternFilter(String...)}. ({@value}) + */ + int MAX_PATTERN_AMOUNT = 10; + /** + * The maximum limit of mentions in {@link TriggerConfig#mentionSpam(int)}. ({@value}) + */ + int MAX_MENTION_LIMIT = 50; + /** + * The maximum amount of roles that can be added to {@link AutoModRule#getExemptRoles()}. ({@value}) + */ + int MAX_EXEMPT_ROLES = 20; + /** + * The maximum amount of channels that can be added to {@link AutoModRule#getExemptChannels()}. ({@value}) + */ + int MAX_EXEMPT_CHANNELS = 50; + + /** + * The {@link Guild} this rule belongs to. + * + * @return The guild + */ + @Nonnull + Guild getGuild(); + + /** + * The user id of the creator of this rule. + * + * @return The owner id + */ + long getCreatorIdLong(); + + /** + * The user id of the creator of this rule. + * + * @return The owner id + */ + @Nonnull + default String getCreatorId() + { + return Long.toUnsignedString(getCreatorIdLong()); + } + + /** + * The name of this rule. + * + * @return The name + */ + @Nonnull + String getName(); + + /** + * The type of event that triggers this rule. + * + * @return The event type + */ + @Nonnull + AutoModEventType getEventType(); + + /** + * The type of trigger that this rule uses. + * + * @return The trigger type + */ + @Nonnull + AutoModTriggerType getTriggerType(); + + /** + * Whether this rule is enabled. + * + * @return True, if enabled + */ + boolean isEnabled(); + + /** + * The roles which are exempt from the rule. + *

All members of the exempt roles will bypass the rule. + * + * @return The exempt roles + */ + @Nonnull + List getExemptRoles(); + + /** + * The channels which are exempt from the rule. + *

All messages in the listed channels will bypass the rule. + * + * @return The exempt channels + */ + @Nonnull + List getExemptChannels(); + + /** + * The automated {@link AutoModResponse AutoModResponses} that will be activated when the rule is triggered. + * + * @return The {@link AutoModResponse AutoModResponses} + */ + @Nonnull + List getActions(); + + /** + * The keywords that are blocked by this rule. + *

Only applies to {@link AutoModTriggerType#KEYWORD}. + * + * @return The blocked keywords + */ + @Nonnull + List getFilteredKeywords(); + + /** + * The regex patterns that are blocked by this rule. + *

Only applies to {@link AutoModTriggerType#KEYWORD}. + * + * @return The blocked regex patterns + */ + @Nonnull + List getFilteredRegex(); + + /** + * The keyword presets that are blocked by this rule. + *

Only applies to {@link AutoModTriggerType#KEYWORD_PRESET}. + * + * @return The blocked keyword presets + */ + @Nonnull + EnumSet getFilteredPresets(); + + /** + * The whitelisted keywords that are allowed by this rule. + *

Only applies to {@link AutoModTriggerType#KEYWORD} and {@link AutoModTriggerType#KEYWORD_PRESET}. + * + * @return The whitelisted keywords + */ + @Nonnull + List getAllowlist(); + + /** + * The maximum amount of mentions that are allowed in a message. + *

Only applies to {@link AutoModTriggerType#MENTION_SPAM}. + * + * @return The mention limit, or 0 if this is not using {@link AutoModTriggerType#MENTION_SPAM} + */ + int getMentionLimit(); + + /** + * Whether this rule is using the raid protection feature. + *

Only applies to {@link AutoModTriggerType#MENTION_SPAM}. + * + * @return True, if mention raid protection is enabled + */ + boolean isMentionRaidProtectionEnabled(); + + /** + * Returns an {@link AutoModRuleManager}, which can be used to modify this rule. + *

The manager allows modifying multiple fields in a single request. + *
You modify multiple fields in one request by chaining setters before calling {@link net.dv8tion.jda.api.requests.RestAction#queue() RestAction.queue()}. + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission. + * + * @return The manager instance + */ + @Nonnull + default AutoModRuleManager getManager() + { + return getGuild().modifyAutoModRuleById(getId()); + } + + /** + * Deletes this rule. + * + * @throws InsufficientPermissionException + * If the currently logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_SERVER MANAGE_SERVER} permission. + * + * @return {@link net.dv8tion.jda.api.requests.RestAction RestAction} - Type: {@link Void} + */ + @Nonnull + default AuditableRestAction delete() + { + return getGuild().deleteAutoModRuleById(getId()); + } + + /** + * Keyword presets that can be used in {@link AutoModRule#getFilteredPresets()}. + */ + enum KeywordPreset + { + /** + * Words that can be considered as swearing or cursing. + */ + PROFANITY(1), + /** + * Words that can be considered as sexual in nature. + */ + SEXUAL_CONTENT(2), + /** + * Words that can be considered as slurs or insults. + */ + SLURS(3), + /** + * Placeholder for unknown values. + */ + UNKNOWN(-1); + + private final int key; + + KeywordPreset(int key) + { + this.key = key; + } + + /** + * The raw value used by Discord to represent this preset. + * + * @return The raw value + */ + public int getKey() + { + return key; + } + + /** + * The {@link KeywordPreset} represented by the provided key. + * + * @param key + * The raw key + * + * @return The {@link KeywordPreset} or {@link #UNKNOWN} + */ + @Nonnull + public static KeywordPreset fromKey(int key) + { + for (KeywordPreset preset : values()) + { + if (preset.key == key) + return preset; + } + return UNKNOWN; + } + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModTriggerType.java b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModTriggerType.java new file mode 100644 index 0000000000..c7f3ca8986 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/AutoModTriggerType.java @@ -0,0 +1,135 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod; + +import net.dv8tion.jda.annotations.Incubating; +import net.dv8tion.jda.internal.utils.Helpers; + +import javax.annotation.Nonnull; +import java.util.EnumSet; + +/** + * The type which defines what triggers an {@link AutoModRule}. + */ +public enum AutoModTriggerType +{ + /** + * The rule is triggered by user message content containing specific keywords or phrases. + */ + KEYWORD(1, 6, AutoModEventType.MESSAGE_SEND, AutoModEventType.MEMBER_UPDATE), + /** + * The rule is triggered by user message content containing classified spam content. + */ + SPAM(3, 1, AutoModEventType.MESSAGE_SEND), + /** + * The rule is triggered by user message content containing keywords from a predefined list (such as {@link AutoModRule.KeywordPreset#SLURS slurs}). + */ + KEYWORD_PRESET(4, 1, AutoModEventType.MESSAGE_SEND), + /** + * The rule is triggered by user message content containing more than the allowed number of mentions. + */ + MENTION_SPAM(5, 1, AutoModEventType.MESSAGE_SEND), + /** + * The rule is triggered by a member profile containing specific keywords or phrases. + * + * @incubating This has not been officially released yet + */ + @Incubating + MEMBER_PROFILE_KEYWORD(6, 1, AutoModEventType.MEMBER_UPDATE), + /** + * Placeholder for unknown trigger types that haven't been added yet. + */ + UNKNOWN(-1, 0), + ; + + private final int key; + private final int maxPerGuild; + private final EnumSet eventTypes; + + AutoModTriggerType(int key, int maxPerGuild, AutoModEventType... supportedEvents) + { + this.key = key; + this.maxPerGuild = maxPerGuild; + if (supportedEvents.length > 0) + this.eventTypes = EnumSet.of(supportedEvents[0], supportedEvents); + else + this.eventTypes = EnumSet.noneOf(AutoModEventType.class); + } + + /** + * The raw API key used to indicate this type. + * + * @return The int key + */ + public int getKey() + { + return key; + } + + /** + * The maximum number of rules that can use this trigger type in a guild. + * + * @return The maximum number of rules + */ + public int getMaxPerGuild() + { + return maxPerGuild; + } + + /** + * The {@link AutoModEventType AutoModEventTypes} that support this trigger type. + * + * @return The supported event types + */ + @Nonnull + public EnumSet getSupportedEventTypes() + { + return Helpers.copyEnumSet(AutoModEventType.class, eventTypes); + } + + /** + * Whether the provided {@link AutoModEventType} is supported by this trigger type. + * + * @param type + * The event type to check + * + * @return True, if the event type is supported + */ + public boolean isEventTypeSupported(@Nonnull AutoModEventType type) + { + return type != null && eventTypes.contains(type); + } + + /** + * The {@link AutoModTriggerType} that matches the provided key. + * + * @param key + * The key to match + * + * @return The matching {@link AutoModTriggerType} or {@link #UNKNOWN} + */ + @Nonnull + public static AutoModTriggerType fromKey(int key) + { + for (AutoModTriggerType trigger : values()) + { + if (trigger.key == key) + return trigger; + } + return UNKNOWN; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/AbstractKeywordTriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AbstractKeywordTriggerConfig.java new file mode 100644 index 0000000000..ae7047d4e1 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AbstractKeywordTriggerConfig.java @@ -0,0 +1,151 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nonnull; +import java.util.*; + +/** + * Abstract for all keyword trigger types. + * + * @param + * The builder type + */ +@SuppressWarnings("unchecked") +public abstract class AbstractKeywordTriggerConfig> extends AbstractTriggerConfig +{ + protected final List allowList = new ArrayList<>(); + + protected AbstractKeywordTriggerConfig(AutoModTriggerType triggerType) + { + super(triggerType); + } + + /** + * Add keywords to the allow list. + *

Keywords added to the allow list will not be considered as a match and won't trigger the rule execution. + * + *

Keywords follow the same rules as {@link CustomKeywordTriggerConfig#addKeywords(String...)}. + * + * @param keywords + * The keywords to allow + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords is empty, blank, or null
  • + *
  • If more than the allowed number of keywords are added to the list + * ({@value AutoModRule#MAX_ALLOWLIST_CUSTOM_AMOUNT} for custom keyword lists, + * {@value AutoModRule#MAX_ALLOWLIST_PRESET_AMOUNT} for preset keyword lists)
  • + *
  • If any keyword is longer than {@link AutoModRule#MAX_KEYWORD_LENGTH}
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public B addAllowList(@Nonnull String... keywords) + { + Checks.noneNull(keywords, "Keywords"); + Checks.check(this.allowList.size() + keywords.length <= maxAllowListAmount(), "Cannot add more than %d keywords!", maxAllowListAmount()); + Arrays.stream(keywords).forEach(AbstractKeywordTriggerConfig::checkKeyword); + Collections.addAll(allowList, keywords); + return (B) this; + } + + /** + * Add keywords to the allow list. + *

Keywords added to the allow list will not be considered as a match and won't trigger the rule execution. + * + *

Keywords follow the same rules as {@link CustomKeywordTriggerConfig#addKeywords(String...)}. + * + * @param keywords + * The keywords to allow + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords is empty, blank, or null
  • + *
  • If more than the allowed number of keywords are added to the list + * ({@value AutoModRule#MAX_ALLOWLIST_CUSTOM_AMOUNT} for custom keyword lists, + * {@value AutoModRule#MAX_ALLOWLIST_PRESET_AMOUNT} for preset keyword lists)
  • + *
  • If any keyword is longer than {@link AutoModRule#MAX_KEYWORD_LENGTH}
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public B addAllowList(@Nonnull Collection keywords) + { + Checks.noneNull(keywords, "Keywords"); + Checks.check(this.allowList.size() + keywords.size() <= maxAllowListAmount(), "Cannot add more than %d keywords!", maxAllowListAmount()); + keywords.forEach(AbstractKeywordTriggerConfig::checkKeyword); + allowList.addAll(keywords); + return (B) this; + } + + /** + * Change the allow list to the provided keywords. + *

Keywords added to the allow list will not be considered as a match and won't trigger the rule execution. + * + *

Keywords follow the same rules as {@link CustomKeywordTriggerConfig#addKeywords(String...)}. + * + * @param keywords + * The keywords to allow + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords is empty, blank, or null
  • + *
  • If more than the allowed number of keywords are added to the list + * ({@value AutoModRule#MAX_ALLOWLIST_CUSTOM_AMOUNT} for custom keyword lists, + * {@value AutoModRule#MAX_ALLOWLIST_PRESET_AMOUNT} for preset keyword lists)
  • + *
  • If any keyword is longer than {@link AutoModRule#MAX_KEYWORD_LENGTH}
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public B setAllowList(@Nonnull Collection keywords) + { + Checks.noneNull(keywords, "Keywords"); + Checks.check(keywords.size() <= maxAllowListAmount(), "Cannot add more than %d keywords!", maxAllowListAmount()); + keywords.forEach(AbstractKeywordTriggerConfig::checkKeyword); + allowList.clear(); + allowList.addAll(keywords); + return (B) this; + } + + protected abstract int maxAllowListAmount(); + + protected static void checkKeyword(String keyword) + { + Checks.notEmpty(keyword, "Keyword"); + Checks.notLonger(keyword, AutoModRule.MAX_KEYWORD_LENGTH, "Keyword"); + } + + @Nonnull + @Override + public DataObject toData() + { + DataObject data = super.toData(); + data.put("allow_list", DataArray.fromCollection(allowList)); + return data; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/AbstractTriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AbstractTriggerConfig.java new file mode 100644 index 0000000000..fc524a9186 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AbstractTriggerConfig.java @@ -0,0 +1,57 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.utils.data.DataObject; + +import javax.annotation.Nonnull; + +/** + * Abstract base class for all trigger configurations. + * + * @param + * The builder type + */ +public class AbstractTriggerConfig> implements TriggerConfig +{ + protected final AutoModTriggerType type; + + protected AbstractTriggerConfig(AutoModTriggerType type) + { + this.type = type; + } + + /** + * The type of trigger this config applies to. + * + * @return {@link AutoModTriggerType} + */ + @Nonnull + @Override + public AutoModTriggerType getType() + { + return type; + } + + @Nonnull + @Override + public DataObject toData() + { + return DataObject.empty(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/AntiSpamTriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AntiSpamTriggerConfig.java new file mode 100644 index 0000000000..c730f55d08 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AntiSpamTriggerConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; + +/** + * Configuration for the {@link net.dv8tion.jda.api.entities.automod.AutoModTriggerType#SPAM SPAM} trigger. + */ +public class AntiSpamTriggerConfig extends AbstractTriggerConfig +{ + protected AntiSpamTriggerConfig() + { + super(AutoModTriggerType.SPAM); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/AutoModRuleData.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AutoModRuleData.java new file mode 100644 index 0000000000..3fe3b48acb --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/AutoModRuleData.java @@ -0,0 +1,454 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.automod.AutoModEventType; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.data.SerializableData; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumMap; +import java.util.EnumSet; + +/** + * Data class used to create new {@link AutoModRule AutoModRules}. + * + *

Every rule must configure at least one {@link #putResponses(AutoModResponse...) response}. + * + *

Example
+ * + *

{@code
+ * TriggerConfig config = TriggerConfig.keywordFilter("discord.gg/*").addAllowList("gateway.discord.gg/*");
+ * AutoModRuleData data = AutoModRuleData.onMessage("Invite Block", config);
+ * data.addExemptRoles(guild.getRolesByName("Moderator", true));
+ * data.putResponse(AutoModResponse.blockMessage());
+ * }
+ * + *
    + *
  1. The {@link TriggerConfig} defines under what conditions the rule should be triggered and execute a response. + * It should trigger on all invite links, but not trigger on the gateway subdomain.
  2. + *
  3. The rule is then created with this trigger config and we name it {@code "Invite Block"}.
  4. + *
  5. Using {@link #addExemptRoles(Role...)}, the moderator role has been excluded to allow moderators to post links.
  6. + *
  7. With {@link #putResponses(AutoModResponse...)}, an automatic action is enabled to block the message, whenever it triggers the rule.
  8. + *
+ * + * @see net.dv8tion.jda.api.entities.Guild#createAutoModRule(AutoModRuleData) + */ +public class AutoModRuleData implements SerializableData +{ + protected final AutoModEventType eventType; + protected String name; + protected boolean enabled = true; + protected TriggerConfig triggerMetadata; + + protected final EnumMap actions = new EnumMap<>(AutoModResponse.Type.class); + protected final Collection exemptChannels = new ArrayList<>(); + protected final Collection exemptRoles = new ArrayList<>(); + + protected AutoModRuleData(AutoModEventType eventType, String name, TriggerConfig triggerMetadata) + { + this.eventType = eventType; + this.setName(name); + this.setTriggerConfig(triggerMetadata); + } + + /** + * Create a new {@link AutoModRule} which triggers on a message being sent in a channel. + * + * @param name + * The name of the rule (1-{@value AutoModRule#MAX_RULE_NAME_LENGTH} characters) + * @param triggerConfig + * The trigger configuration for this rule + * + * @throws IllegalArgumentException + * If null is provided or the name is not between 1 and {@value AutoModRule#MAX_RULE_NAME_LENGTH} characters + * + * @return The new {@link AutoModRuleData} instance + */ + @Nonnull + public static AutoModRuleData onMessage(@Nonnull String name, @Nonnull TriggerConfig triggerConfig) + { + return new AutoModRuleData(AutoModEventType.MESSAGE_SEND, name, triggerConfig); + } + +// /** +// * Create a new {@link AutoModRule} which triggers on a member profile being updated. +// * +// * @param name +// * The name of the rule (1-{@value AutoModRule#MAX_RULE_NAME_LENGTH} characters) +// * @param triggerConfig +// * The trigger configuration for this rule +// * +// * @throws IllegalArgumentException +// * If null is provided or the name is not between 1 and {@value AutoModRule#MAX_RULE_NAME_LENGTH} characters +// * +// * @return The new {@link AutoModRuleData} instance +// */ +// @Nonnull +// public static AutoModRuleData onMemberProfile(@Nonnull String name, @Nonnull TriggerConfig triggerConfig) +// { +// return new AutoModRuleData(AutoModEventType.MEMBER_UPDATE, name, triggerConfig) +// .putResponses(AutoModResponse.blockMemberInteraction()); +// } + + + /** + * Change the name of the rule. + * + * @param name + * The new name (1-{@value AutoModRule#MAX_RULE_NAME_LENGTH} characters) + * + * @throws IllegalArgumentException + * If the name is not between 1 and {@value AutoModRule#MAX_RULE_NAME_LENGTH} characters + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData setName(@Nonnull String name) + { + Checks.notEmpty(name, "Name"); + Checks.notLonger(name, AutoModRule.MAX_RULE_NAME_LENGTH, "Name"); + this.name = name; + return this; + } + + /** + * Enable or disable the rule. + * + * @param enabled + * True, if the rule should be enabled + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData setEnabled(boolean enabled) + { + this.enabled = enabled; + return this; + } + + /** + * Configure what the rule should do upon triggering. + *
This is accumulative and adds ontop of the currently configured responses. + * + *

Note that each response type can only be used once. + * If multiple responses of the same type are provided, the last one is used. + * + * @param responses + * The responses to configure + * + * @throws IllegalArgumentException + * If null is provided or any of the responses has an {@link AutoModResponse.Type#UNKNOWN unknown type} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData putResponses(@Nonnull AutoModResponse... responses) + { + Checks.noneNull(responses, "Responses"); + for (AutoModResponse response : responses) + { + AutoModResponse.Type type = response.getType(); + Checks.check(type != AutoModResponse.Type.UNKNOWN, "Cannot create response with unknown response type"); + actions.put(type, response); + } + return this; + } + + /** + * Configure what the rule should do upon triggering. + *
This is accumulative and adds ontop of the currently configured responses. + * + *

Note that each response type can only be used once. + * If multiple responses of the same type are provided, the last one is used. + * + * @param responses + * The responses to configure + * + * @throws IllegalArgumentException + * If null is provided or any of the responses has an {@link AutoModResponse.Type#UNKNOWN unknown type} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData putResponses(@Nonnull Collection responses) + { + Checks.noneNull(responses, "Responses"); + for (AutoModResponse response : responses) + { + AutoModResponse.Type type = response.getType(); + Checks.check(type != AutoModResponse.Type.UNKNOWN, "Cannot create response with unknown response type"); + actions.put(type, response); + } + return this; + } + + /** + * Configure what the rule should do upon triggering. + *
This replaces the currently configured responses, removing all previously configured responses. + * + *

Note that each response type can only be used once. + * If multiple responses of the same type are provided, the last one is used. + * + * @param responses + * The responses to configure + * + * @throws IllegalArgumentException + * If null is provided or any of the responses has an {@link AutoModResponse.Type#UNKNOWN unknown type} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData setResponses(@Nonnull Collection responses) + { + Checks.noneNull(responses, "Responses"); + actions.clear(); + if (eventType == AutoModEventType.MEMBER_UPDATE) + actions.put(AutoModResponse.Type.BLOCK_MEMBER_INTERACTION, AutoModResponse.blockMemberInteraction()); + for (AutoModResponse response : responses) + { + AutoModResponse.Type type = response.getType(); + Checks.check(type != AutoModResponse.Type.UNKNOWN, "Cannot create response with unknown response type"); + actions.put(type, response); + } + return this; + } + + /** + * Add roles which can bypass this rule. + * + *

Roles added to the exemptions will allow all of its members to bypass this rule. + * + * @param roles + * The roles to add (up to {@value AutoModRule#MAX_EXEMPT_ROLES} roles) + * + * @throws IllegalArgumentException + * If null is provided or the number of roles exceeds {@value AutoModRule#MAX_EXEMPT_ROLES} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData addExemptRoles(@Nonnull Role... roles) + { + Checks.noneNull(roles, "Roles"); + Checks.check(roles.length + exemptRoles.size() <= AutoModRule.MAX_EXEMPT_ROLES, "Cannot add more than %d roles", AutoModRule.MAX_EXEMPT_ROLES); + for (Role role : roles) + exemptRoles.add(role.getId()); + return this; + } + + /** + * Add roles which can bypass this rule. + * + *

Roles added to the exemptions will allow all of its members to bypass this rule. + * + * @param roles + * The roles to add (up to {@value AutoModRule#MAX_EXEMPT_ROLES} roles) + * + * @throws IllegalArgumentException + * If null is provided or the number of roles exceeds {@value AutoModRule#MAX_EXEMPT_ROLES} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData addExemptRoles(@Nonnull Collection roles) + { + Checks.noneNull(roles, "Roles"); + Checks.check(roles.size() + exemptRoles.size() <= AutoModRule.MAX_EXEMPT_ROLES, "Cannot add more than %d roles", AutoModRule.MAX_EXEMPT_ROLES); + for (Role role : roles) + exemptRoles.add(role.getId()); + return this; + } + + /** + * Set which roles can bypass this rule. + * + *

Roles added to the exemptions will allow all of its members to bypass this rule. + * + * @param roles + * The roles to exempt (up to {@value AutoModRule#MAX_EXEMPT_ROLES} roles) + * + * @throws IllegalArgumentException + * If null is provided or the number of roles exceeds {@value AutoModRule#MAX_EXEMPT_ROLES} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData setExemptRoles(@Nonnull Collection roles) + { + Checks.noneNull(roles, "Roles"); + Checks.check(roles.size() <= AutoModRule.MAX_EXEMPT_ROLES, "Cannot add more than %d roles", AutoModRule.MAX_EXEMPT_ROLES); + exemptRoles.clear(); + for (Role role : roles) + exemptRoles.add(role.getId()); + return this; + } + + /** + * Add channels which can bypass this rule. + * + *

No messages sent in this channel will trigger the rule. + * + * @param channels + * The channels to add (up to {@value AutoModRule#MAX_EXEMPT_CHANNELS} channels) + * + * @throws IllegalArgumentException + * If null is provided or the number of channels exceeds {@value AutoModRule#MAX_EXEMPT_CHANNELS} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData addExemptChannels(@Nonnull GuildChannel... channels) + { + Checks.noneNull(channels, "Channels"); + Checks.check(channels.length + exemptChannels.size() <= AutoModRule.MAX_EXEMPT_CHANNELS, "Cannot add more than %d channels", AutoModRule.MAX_EXEMPT_CHANNELS); + for (GuildChannel channel : channels) + exemptChannels.add(channel.getId()); + return this; + } + + /** + * Add channels which can bypass this rule. + * + *

No messages sent in this channel will trigger the rule. + * + * @param channels + * The channels to add (up to {@value AutoModRule#MAX_EXEMPT_CHANNELS} channels) + * + * @throws IllegalArgumentException + * If null is provided or the number of channels exceeds {@value AutoModRule#MAX_EXEMPT_CHANNELS} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData addExemptChannels(@Nonnull Collection channels) + { + Checks.noneNull(channels, "Channels"); + Checks.check(channels.size() + exemptChannels.size() <= AutoModRule.MAX_EXEMPT_CHANNELS, "Cannot add more than %d channels", AutoModRule.MAX_EXEMPT_CHANNELS); + for (GuildChannel channel : channels) + exemptChannels.add(channel.getId()); + return this; + } + + /** + * Set which channels can bypass this rule. + * + *

No messages sent in this channel will trigger the rule. + * + * @param channels + * The channels to add (up to {@value AutoModRule#MAX_EXEMPT_CHANNELS} channels) + * + * @throws IllegalArgumentException + * If null is provided or the number of channels exceeds {@value AutoModRule#MAX_EXEMPT_CHANNELS} + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData setExemptChannels(@Nonnull Collection channels) + { + Checks.noneNull(channels, "Channels"); + Checks.check(channels.size() <= AutoModRule.MAX_EXEMPT_CHANNELS, "Cannot add more than %d channels", AutoModRule.MAX_EXEMPT_CHANNELS); + exemptChannels.clear(); + for (GuildChannel channel : channels) + exemptChannels.add(channel.getId()); + return this; + } + + /** + * Change the {@link TriggerConfig} for this rule. + * + * @param config + * The new config + * + * @throws IllegalArgumentException + * If null is provided + * + * @return The same {@link AutoModRuleData} instance + */ + @Nonnull + public AutoModRuleData setTriggerConfig(@Nonnull TriggerConfig config) + { + Checks.notNull(config, "TriggerConfig"); + Checks.check(config.getType().isEventTypeSupported(eventType), "Cannot use trigger type %s with event type %s", config.getType(), eventType); + this.triggerMetadata = config; + return this; + } + + /** + * Returns the {@link Permission Permissions} required to create this rule. + *
Certain {@link AutoModResponse.Type Types} require additional permissions, such as {@link AutoModResponse.Type#TIMEOUT}. + * All rules require {@link Permission#MANAGE_SERVER} to be created. + * + * @return The required permissions to create this rule + */ + @Nonnull + public EnumSet getRequiredPermissions() + { + if (actions.containsKey(AutoModResponse.Type.TIMEOUT)) + return EnumSet.of(Permission.MANAGE_SERVER, Permission.MODERATE_MEMBERS); + else + return EnumSet.of(Permission.MANAGE_SERVER); + } + + @Nonnull + @Override + public DataObject toData() + { + AutoModTriggerType triggerType = triggerMetadata.getType(); + if (eventType == AutoModEventType.MEMBER_UPDATE) + { + if (triggerType == AutoModTriggerType.KEYWORD) + triggerType = AutoModTriggerType.MEMBER_PROFILE_KEYWORD; + else + throw new IllegalStateException("Cannot create rule of trigger type " + triggerType + " with event type " + eventType); + } + + for (AutoModResponse response : actions.values()) + { + if (!response.getType().isSupportedTrigger(triggerType)) + throw new IllegalStateException("Cannot create a rule of trigger type " + triggerType + " with response type " + response.getType()); + } + + if (actions.isEmpty()) + throw new IllegalStateException("Cannot create a rule with no responses. Add at least one response with putResponses(...)"); + + DataObject data = DataObject.empty() + .put("name", name) + .put("enabled", enabled) + .put("event_type", eventType.getKey()); + + data.put("actions", DataArray.fromCollection(actions.values())); + + data.put("exempt_roles", DataArray.fromCollection(exemptRoles)); + data.put("exempt_channels", DataArray.fromCollection(exemptChannels)); + + data.put("trigger_type", triggerType.getKey()); + data.put("trigger_metadata", triggerMetadata.toData()); + + return data; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/CustomKeywordTriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/CustomKeywordTriggerConfig.java new file mode 100644 index 0000000000..33031cfc93 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/CustomKeywordTriggerConfig.java @@ -0,0 +1,273 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Configuration for a {@link net.dv8tion.jda.api.entities.automod.AutoModTriggerType#KEYWORD KEYWORD} trigger. + */ +public class CustomKeywordTriggerConfig extends AbstractKeywordTriggerConfig +{ + protected final Set keywords = new HashSet<>(); + protected final Set patterns = new HashSet<>(); + + protected CustomKeywordTriggerConfig() + { + super(AutoModTriggerType.KEYWORD); + } + + /** + * Add more keywords match against. + *
Keywords are matched case-insensitively, and may also contain whitespace. + * + *

You can use wildcards at the keyword boundaries to extend the matches: + *
{@code "foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "foo-bar"}, etc. + *
{@code "*foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "barfoo"}, etc. + *
{@code "*foo"} can match {@code "foo"}, {@code "barfoo"}, {@code "bar-foo"}, etc. + * + *

You can also use regex patterns using {@link #patternFilter(String...)}. + * + * @param keywords + * The keywords to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_KEYWORD_AMOUNT} keywords are added
  • + *
  • If any of the keywords is longer than {@value AutoModRule#MAX_KEYWORD_LENGTH} characters
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public CustomKeywordTriggerConfig addKeywords(@Nonnull String... keywords) + { + Checks.noneNull(keywords, "Keywords"); + Checks.check(this.keywords.size() + keywords.length <= AutoModRule.MAX_KEYWORD_AMOUNT, "Cannot add more than %d keywords!", AutoModRule.MAX_KEYWORD_AMOUNT); + for (String keyword : keywords) + checkKeyword(keyword); + + Collections.addAll(this.keywords, keywords); + return this; + } + + /** + * Add more keywords match against. + *
Keywords are matched case-insensitively, and may also contain whitespace. + * + *

You can use wildcards at the keyword boundaries to extend the matches: + *
{@code "foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "foo-bar"}, etc. + *
{@code "*foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "barfoo"}, etc. + *
{@code "*foo"} can match {@code "foo"}, {@code "barfoo"}, {@code "bar-foo"}, etc. + * + *

You can also use regex patterns using {@link #patternFilter(Collection)}. + * + * @param keywords + * The keywords to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_KEYWORD_AMOUNT} keywords are added
  • + *
  • If any of the keywords is longer than {@value AutoModRule#MAX_KEYWORD_LENGTH} characters
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public CustomKeywordTriggerConfig addKeywords(@Nonnull Collection keywords) + { + Checks.noneNull(keywords, "Keywords"); + Checks.check(this.keywords.size() + keywords.size() <= AutoModRule.MAX_KEYWORD_AMOUNT, "Cannot add more than %d keywords!", AutoModRule.MAX_KEYWORD_AMOUNT); + for (String keyword : keywords) + checkKeyword(keyword); + + this.keywords.addAll(keywords); + return this; + } + + /** + * Changes the keywords to match against to the new list. + *
Keywords are matched case-insensitively, and may also contain whitespace. + * + *

You can use wildcards at the keyword boundaries to extend the matches: + *
{@code "foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "foo-bar"}, etc. + *
{@code "*foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "barfoo"}, etc. + *
{@code "*foo"} can match {@code "foo"}, {@code "barfoo"}, {@code "bar-foo"}, etc. + * + *

You can also use regex patterns using {@link #patternFilter(Collection)}. + * + * @param keywords + * The keywords to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_KEYWORD_AMOUNT} keywords are added
  • + *
  • If any of the keywords is longer than {@value AutoModRule#MAX_KEYWORD_LENGTH} characters
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public CustomKeywordTriggerConfig setKeywords(@Nonnull Collection keywords) + { + Checks.noneNull(keywords, "Keywords"); + Checks.check(keywords.size() <= AutoModRule.MAX_KEYWORD_AMOUNT, "Cannot add more than %d keywords!", AutoModRule.MAX_KEYWORD_AMOUNT); + for (String keyword : keywords) + checkKeyword(keyword); + + this.keywords.clear(); + this.keywords.addAll(keywords); + return this; + } + + + /** + * Add keywords regex patterns to match against. + *
Keyword patterns are matched case-insensitively, and may also contain whitespace. + * + *

Patterns may use anything supported by the rust regex crate. + * You can use a validator such as Rustexp to validate your pattern. + * + *

You can also use simple substring keywords using {@link #keywordFilter(String...)}. + * + * @param patterns + * The keyword patterns to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the patterns are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_PATTERN_AMOUNT} patterns are added
  • + *
  • If any of the patterns is longer than {@value AutoModRule#MAX_PATTERN_LENGTH} characters
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public CustomKeywordTriggerConfig addPatterns(@Nonnull String... patterns) + { + Checks.noneNull(patterns, "Patterns"); + Checks.check(this.patterns.size() + patterns.length <= AutoModRule.MAX_PATTERN_AMOUNT, "Cannot add more than %d patterns!", AutoModRule.MAX_PATTERN_AMOUNT); + for (String pattern : patterns) + checkPattern(pattern); + + Collections.addAll(this.patterns, patterns); + return this; + } + + /** + * Add keywords regex patterns to match against. + *
Keyword patterns are matched case-insensitively, and may also contain whitespace. + * + *

Patterns may use anything supported by the rust regex crate. + * You can use a validator such as Rustexp to validate your pattern. + * + *

You can also use simple substring keywords using {@link #keywordFilter(String...)}. + * + * @param patterns + * The keyword patterns to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the patterns are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_PATTERN_AMOUNT} patterns are added
  • + *
  • If any of the patterns is longer than {@value AutoModRule#MAX_PATTERN_LENGTH} characters
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public CustomKeywordTriggerConfig addPatterns(@Nonnull Collection patterns) + { + Checks.noneNull(patterns, "Patterns"); + Checks.check(this.patterns.size() + patterns.size() <= AutoModRule.MAX_PATTERN_AMOUNT, "Cannot add more than %d patterns!", AutoModRule.MAX_PATTERN_AMOUNT); + for (String pattern : patterns) + checkPattern(pattern); + + this.patterns.addAll(patterns); + return this; + } + + /** + * Change the list of keywords regex patterns to match against. + *
Keyword patterns are matched case-insensitively, and may also contain whitespace. + * + *

Patterns may use anything supported by the rust regex crate. + * You can use a validator such as Rustexp to validate your pattern. + * + *

You can also use simple substring keywords using {@link #keywordFilter(String...)}. + * + * @param patterns + * The keyword patterns to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the patterns are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_PATTERN_AMOUNT} patterns are added
  • + *
  • If any of the patterns is longer than {@value AutoModRule#MAX_PATTERN_LENGTH} characters
  • + *
+ * + * @return The current config for chaining convenience + */ + @Nonnull + public CustomKeywordTriggerConfig setPatterns(@Nonnull Collection patterns) + { + Checks.noneNull(patterns, "Patterns"); + Checks.check(patterns.size() <= AutoModRule.MAX_PATTERN_AMOUNT, "Cannot add more than %d patterns!", AutoModRule.MAX_PATTERN_AMOUNT); + for (String pattern : patterns) + checkPattern(pattern); + + this.patterns.clear(); + this.patterns.addAll(patterns); + return this; + } + + protected static void checkPattern(String pattern) + { + Checks.notBlank(pattern, "Pattern"); + Checks.notLonger(pattern, AutoModRule.MAX_PATTERN_LENGTH, "Pattern"); + } + + @Override + protected int maxAllowListAmount() + { + return AutoModRule.MAX_ALLOWLIST_CUSTOM_AMOUNT; + } + + @Nonnull + @Override + public DataObject toData() + { + Checks.check(!keywords.isEmpty() || !patterns.isEmpty(), "Must have at least one keyword or pattern!"); + DataObject data = super.toData(); + data.put("keyword_filter", DataArray.fromCollection(keywords)); + data.put("regex_patterns", DataArray.fromCollection(patterns)); + return data; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/MentionSpamTriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/MentionSpamTriggerConfig.java new file mode 100644 index 0000000000..ff91534d1c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/MentionSpamTriggerConfig.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.Nonnull; + +/** + * Configuration for {@link AutoModTriggerType#MENTION_SPAM MENTION_SPAM} trigger. + */ +public class MentionSpamTriggerConfig extends AbstractTriggerConfig implements TriggerConfig +{ + private int mentionLimit; + private boolean isMentionRaidProtectionEnabled; + + public MentionSpamTriggerConfig(int mentionLimit) + { + super(AutoModTriggerType.MENTION_SPAM); + this.mentionLimit = mentionLimit; + } + + /** + * Configure the maximum number of unique mentions allowed in a message. + * + * @param mentionLimit + * The maximum number of unique mentions allowed in a message (1-{@value AutoModRule#MAX_MENTION_LIMIT}) + * + * @throws IllegalArgumentException + * If the provided mention limit is not between 1 and {@value AutoModRule#MAX_MENTION_LIMIT} + * + * @return The current config for chaining convenience + */ + @Nonnull + public MentionSpamTriggerConfig setMentionLimit(int mentionLimit) + { + Checks.positive(mentionLimit, "Mention Limit"); + Checks.check(mentionLimit <= AutoModRule.MAX_MENTION_LIMIT, "Mention Limit cannot be higher than %d. Provided: %d", AutoModRule.MAX_MENTION_LIMIT, mentionLimit); + this.mentionLimit = mentionLimit; + return this; + } + + /** + * Whether to enable mention raid protection. + * + * @param enabled + * True, if mention raid protection should be enabled + * + * @return The current config for chaining convenience + */ + @Nonnull + public MentionSpamTriggerConfig setMentionRaidProtectionEnabled(boolean enabled) + { + this.isMentionRaidProtectionEnabled = enabled; + return this; + } + + @Nonnull + @Override + public DataObject toData() + { + DataObject data = super.toData(); + data.put("mention_total_limit", mentionLimit); + data.put("mention_raid_protection_enabled", isMentionRaidProtectionEnabled); + return data; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/PresetKeywordTriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/PresetKeywordTriggerConfig.java new file mode 100644 index 0000000000..828b6b25c3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/PresetKeywordTriggerConfig.java @@ -0,0 +1,142 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.Helpers; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; + +/** + * Configuration for a {@link AutoModTriggerType#KEYWORD_PRESET KEYWORD_PRESET} trigger. + */ +public class PresetKeywordTriggerConfig extends AbstractKeywordTriggerConfig +{ + private final EnumSet presets = EnumSet.noneOf(AutoModRule.KeywordPreset.class); + + protected PresetKeywordTriggerConfig() + { + super(AutoModTriggerType.KEYWORD_PRESET); + } + + /** + * Enable the provided keyword preset lists. + * + * @param presets + * The keyword presets to enable + * + * @throws IllegalArgumentException + * If any of the provided presets is null or {@link net.dv8tion.jda.api.entities.automod.AutoModRule.KeywordPreset#UNKNOWN UNKNOWN} + * + * @return The current config for chaining convenience + */ + @Nonnull + public PresetKeywordTriggerConfig enablePresets(@Nonnull AutoModRule.KeywordPreset... presets) + { + Checks.notNull(presets, "Presets"); + for (AutoModRule.KeywordPreset preset : presets) + checkKnown(preset); + Collections.addAll(this.presets, presets); + return this; + } + + /** + * Enable the provided keyword preset lists. + * + * @param presets + * The keyword presets to enable + * + * @throws IllegalArgumentException + * If any of the provided presets is null or {@link net.dv8tion.jda.api.entities.automod.AutoModRule.KeywordPreset#UNKNOWN UNKNOWN} + * + * @return The current config for chaining convenience + */ + @Nonnull + public PresetKeywordTriggerConfig enablePresets(@Nonnull Collection presets) + { + Checks.notNull(presets, "Presets"); + presets.forEach(PresetKeywordTriggerConfig::checkKnown); + this.presets.addAll(presets); + return this; + } + + /** + * Disable the provided keyword preset lists. + * + * @param presets + * The keyword presets to disable + * + * @throws IllegalArgumentException + * If any of the provided presets is null + * + * @return The current config for chaining convenience + */ + @Nonnull + public PresetKeywordTriggerConfig disablePresets(@Nonnull AutoModRule.KeywordPreset... presets) + { + Checks.noneNull(presets, "Presets"); + for (AutoModRule.KeywordPreset preset : presets) + this.presets.remove(preset); + return this; + } + + /** + * Disable the provided keyword preset lists. + * + * @param presets + * The keyword presets to disable + * + * @throws IllegalArgumentException + * If any of the provided presets is null + * + * @return The current config for chaining convenience + */ + @Nonnull + public PresetKeywordTriggerConfig disablePresets(@Nonnull Collection presets) + { + Checks.noneNull(presets, "Presets"); + this.presets.removeAll(presets); + return this; + } + + @Override + protected int maxAllowListAmount() + { + return AutoModRule.MAX_ALLOWLIST_PRESET_AMOUNT; + } + + private static void checkKnown(AutoModRule.KeywordPreset preset) + { + Checks.notNull(preset, "Presets"); + Checks.check(preset != AutoModRule.KeywordPreset.UNKNOWN, "Cannot use unknown preset"); + } + + @Nonnull + @Override + public DataObject toData() + { + DataObject data = super.toData(); + data.put("presets", presets.stream().map(AutoModRule.KeywordPreset::getKey).collect(Helpers.toDataArray())); + return data; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/automod/build/TriggerConfig.java b/src/main/java/net/dv8tion/jda/api/entities/automod/build/TriggerConfig.java new file mode 100644 index 0000000000..f2155f863c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/automod/build/TriggerConfig.java @@ -0,0 +1,233 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.automod.build; + +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModRule.KeywordPreset; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.utils.data.SerializableData; + +import javax.annotation.Nonnull; +import java.util.Collection; + +/** + * Configuration for {@link AutoModRule}, which defines under what conditions the rule should be triggered. + * + *

Each rule is limited to a single trigger type. You can use the various factory methods on this interface to create a config. + * + *

Supported factories: + *

    + *
  • {@link #mentionSpam(int)} - Trigger on mention thresholds in messages
  • + *
  • {@link #antiSpam()} - Trigger on spam content in messages (classified by Discord magic)
  • + *
  • {@link #keywordFilter(Collection)}/{@link #patternFilter(Collection)} - Trigger on messages containing certain keywords or regex patterns
  • + *
  • {@link #presetKeywordFilter(AutoModRule.KeywordPreset...)} - Trigger on messages containing words from predefined lists
  • + *
+ * + *

Example
+ *

{@code
+ * AutoModRuleData rule = AutoModRule.onMessage("Invite Links",
+ *   TriggerConfig.keywordFilter("discord.gg/*") // trigger on all invite links
+ *     .setAllowList("discord.gg/discord-api")   // except certain whitelisted ones
+ * );
+ * }
+ * + * @see AutoModRule + */ +public interface TriggerConfig extends SerializableData +{ + /** + * The type of trigger for this config. + * + * @return {@link AutoModTriggerType} + */ + @Nonnull + AutoModTriggerType getType(); + + /** + * Trigger on mention thresholds in messages. + * + * @param mentionLimit + * The maximum number of unique mentions allowed in a message (1-{@value AutoModRule#MAX_MENTION_LIMIT}) + * + * @throws IllegalArgumentException + * If the provided mention limit is not between 1 and {@value AutoModRule#MAX_MENTION_LIMIT} + * + * @return {@link MentionSpamTriggerConfig} + */ + @Nonnull + static MentionSpamTriggerConfig mentionSpam(int mentionLimit) + { + return new MentionSpamTriggerConfig(mentionLimit); + } + + /** + * Trigger on spam content in messages (classified by Discord magic). + * + * @return {@link AntiSpamTriggerConfig} + */ + @Nonnull + static AntiSpamTriggerConfig antiSpam() + { + return new AntiSpamTriggerConfig(); + } + + /** + * Trigger on messages containing certain keywords or regex patterns. + *
Keywords are matched case-insensitively, and may also contain whitespace. + * + *

You can use wildcards at the keyword boundaries to extend the matches: + *
{@code "foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "foo-bar"}, etc. + *
{@code "*foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "barfoo"}, etc. + *
{@code "*foo"} can match {@code "foo"}, {@code "barfoo"}, {@code "bar-foo"}, etc. + * + *

You can also use regex patterns using {@link #patternFilter(Collection)} or {@link CustomKeywordTriggerConfig#addPatterns(Collection)}. + * + * @param keywords + * The keywords to match (case-insensitive) + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_KEYWORD_AMOUNT} keywords are added
  • + *
  • If any of the keywords is longer than {@value AutoModRule#MAX_KEYWORD_LENGTH} characters
  • + *
+ * + * @return {@link CustomKeywordTriggerConfig} + */ + @Nonnull + static CustomKeywordTriggerConfig keywordFilter(@Nonnull Collection keywords) + { + return new CustomKeywordTriggerConfig().addKeywords(keywords); + } + + /** + * Trigger on messages containing certain keywords or regex patterns. + *
Keywords are matched case-insensitively, and may also contain whitespace. + * + *

You can use wildcards at the keyword boundaries to extend the matches: + *
{@code "foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "foo-bar"}, etc. + *
{@code "*foo*"} can match {@code "foo"}, {@code "foobar"}, {@code "barfoo"}, etc. + *
{@code "*foo"} can match {@code "foo"}, {@code "barfoo"}, {@code "bar-foo"}, etc. + * + *

You can also use regex patterns using {@link #patternFilter(String...)} or {@link CustomKeywordTriggerConfig#addPatterns(String...)}. + * + * @param keywords + * The keywords to match (case-insensitive) + * + * @throws IllegalArgumentException + *

    + *
  • If any of the keywords are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_KEYWORD_AMOUNT} keywords are added
  • + *
  • If any of the keywords is longer than {@value AutoModRule#MAX_KEYWORD_LENGTH} characters
  • + *
+ * + * @return {@link CustomKeywordTriggerConfig} + */ + @Nonnull + static CustomKeywordTriggerConfig keywordFilter(@Nonnull String... keywords) + { + return new CustomKeywordTriggerConfig().addKeywords(keywords); + } + + /** + * Trigger on messages containing certain keywords regex patterns. + *
Keyword patterns are matched case-insensitively, and may also contain whitespace. + * + *

Patterns may use anything supported by the rust regex crate. + * You can use a validator such as Rustexp to validate your pattern. + * + *

You can also use simple substring keywords using {@link #keywordFilter(String...)} or {@link CustomKeywordTriggerConfig#addKeywords(String...)}. + * + * @param patterns + * The keyword patterns to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the patterns are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_PATTERN_AMOUNT} patterns are added
  • + *
  • If any of the patterns is longer than {@value AutoModRule#MAX_PATTERN_LENGTH} characters
  • + *
+ * + * @return {@link CustomKeywordTriggerConfig} + */ + @Nonnull + static CustomKeywordTriggerConfig patternFilter(@Nonnull Collection patterns) + { + return new CustomKeywordTriggerConfig().addPatterns(patterns); + } + + /** + * Trigger on messages containing certain keywords regex patterns. + *
Keyword patterns are matched case-insensitively, and may also contain whitespace. + * + *

Patterns may use anything supported by the rust regex crate. + * You can use a validator such as Rustexp to validate your pattern. + * + *

You can also use simple substring keywords using {@link #keywordFilter(String...)} or {@link CustomKeywordTriggerConfig#addKeywords(String...)}. + * + * @param patterns + * The keyword patterns to match + * + * @throws IllegalArgumentException + *

    + *
  • If any of the patterns are empty, blank, or null
  • + *
  • If more than {@value AutoModRule#MAX_PATTERN_AMOUNT} patterns are added
  • + *
  • If any of the patterns is longer than {@value AutoModRule#MAX_PATTERN_LENGTH} characters
  • + *
+ * + * @return {@link CustomKeywordTriggerConfig} + */ + @Nonnull + static CustomKeywordTriggerConfig patternFilter(@Nonnull String... patterns) + { + return new CustomKeywordTriggerConfig().addPatterns(patterns); + } + + /** + * Trigger on keywords from predefined lists. + * + * @param presets + * The presets to enable + * + * @throws IllegalArgumentException + * If null or {@link KeywordPreset#UNKNOWN} is provided + * + * @return {@link PresetKeywordTriggerConfig} + */ + @Nonnull + static PresetKeywordTriggerConfig presetKeywordFilter(@Nonnull Collection presets) + { + return new PresetKeywordTriggerConfig().enablePresets(presets); + } + + /** + * Trigger on keywords from predefined lists. + * + * @param presets + * The presets to enable + * + * @throws IllegalArgumentException + * If null or {@link KeywordPreset#UNKNOWN} is provided + * + * @return {@link PresetKeywordTriggerConfig} + */ + @Nonnull + static PresetKeywordTriggerConfig presetKeywordFilter(@Nonnull AutoModRule.KeywordPreset... presets) + { + return new PresetKeywordTriggerConfig().enablePresets(presets); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java index 9c849ba16f..3d151591e4 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java @@ -227,8 +227,18 @@ default boolean isTagRequired() *
    *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL} *
    If the forum channel was deleted
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • + * *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} *
    If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#TITLE_BLOCKED_BY_AUTOMOD TITLE_BLOCKED_BY_AUTOMOD} + *
    If the forum post name was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • *
* * @param name diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java b/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java index 92caba6fed..c325f09390 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java @@ -300,6 +300,12 @@ default List> purgeMessagesById(@Nonnull long... message *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * * @param text @@ -334,6 +340,12 @@ default MessageCreateAction sendMessage(@Nonnull CharSequence text) *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * * @param msg @@ -370,6 +382,12 @@ default MessageCreateAction sendMessage(@Nonnull MessageCreateData msg) *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * * @param format @@ -413,6 +431,12 @@ default MessageCreateAction sendMessageFormat(@Nonnull String format, @Nonnull O *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * *

    Example: Attachment Images @@ -471,6 +495,12 @@ default MessageCreateAction sendMessageEmbeds(@Nonnull MessageEmbed embed, @Nonn *

  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * *

    Example: Attachment Images @@ -522,6 +552,12 @@ default MessageCreateAction sendMessageEmbeds(@Nonnull Collection{@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User + * + *

  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * * @param component @@ -562,6 +598,12 @@ default MessageCreateAction sendMessageComponents(@Nonnull LayoutComponent compo *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • * * *

    Example: Attachment Images @@ -618,6 +660,15 @@ default MessageCreateAction sendMessageComponents(@Nonnull Collection{@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User + * + *

  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} + *
    If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
  • * * *

    Example: Attachment Images @@ -678,6 +729,15 @@ default MessageCreateAction sendFiles(@Nonnull Collection *

  • {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER} *
    If this is a {@link PrivateChannel PrivateChannel} and the currently logged in account * does not share any Guilds with the recipient User
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
    If this message was blocked by the harmful link filter
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} + *
    If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
  • * * *

    Example: Attachment Images diff --git a/src/main/java/net/dv8tion/jda/api/events/automod/AutoModExecutionEvent.java b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModExecutionEvent.java new file mode 100644 index 0000000000..fbb160d7b3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModExecutionEvent.java @@ -0,0 +1,121 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.automod; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.automod.AutoModExecution; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.entities.channel.unions.GuildMessageChannelUnion; +import net.dv8tion.jda.api.events.Event; +import net.dv8tion.jda.api.requests.GatewayIntent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Indicates that an automated {@link AutoModResponse} has been triggered through an {@link AutoModRule}. + * + *

    Requirements
    + * This event requires the {@link GatewayIntent#AUTO_MODERATION_EXECUTION AUTO_MODERATION_EXECUTION} intent to be enabled. + * Additionally, access to {@link #getContent()} and {@link #getMatchedContent()} requires the {@link GatewayIntent#MESSAGE_CONTENT MESSAGE_CONTENT} intent to be enabled. + */ +public class AutoModExecutionEvent extends Event implements AutoModExecution +{ + private final AutoModExecution execution; + + public AutoModExecutionEvent(@Nonnull JDA api, long responseNumber, @Nonnull AutoModExecution execution) + { + super(api, responseNumber); + this.execution = execution; + } + + @Nonnull + @Override + public Guild getGuild() + { + return execution.getGuild(); + } + + @Nullable + @Override + public GuildMessageChannelUnion getChannel() + { + return execution.getChannel(); + } + + @Nonnull + @Override + public AutoModResponse getResponse() + { + return execution.getResponse(); + } + + @Nonnull + @Override + public AutoModTriggerType getTriggerType() + { + return execution.getTriggerType(); + } + + @Override + public long getUserIdLong() + { + return execution.getUserIdLong(); + } + + @Override + public long getRuleIdLong() + { + return execution.getRuleIdLong(); + } + + @Override + public long getMessageIdLong() + { + return execution.getMessageIdLong(); + } + + @Override + public long getAlertMessageIdLong() + { + return execution.getAlertMessageIdLong(); + } + + @Nonnull + @Override + public String getContent() + { + return execution.getContent(); + } + + @Nullable + @Override + public String getMatchedContent() + { + return execution.getMatchedContent(); + } + + @Nullable + @Override + public String getMatchedKeyword() + { + return execution.getMatchedKeyword(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleCreateEvent.java new file mode 100644 index 0000000000..40e19d0372 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleCreateEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.automod; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.automod.AutoModRule; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link AutoModRule} was created. + * + *

    Requirements
    + * + *

    These events require the {@link net.dv8tion.jda.api.requests.GatewayIntent#AUTO_MODERATION_CONFIGURATION AUTO_MODERATION_CONFIGURATION} intent to be enabled. + */ +public class AutoModRuleCreateEvent extends GenericAutoModRuleEvent +{ + public AutoModRuleCreateEvent(@Nonnull JDA api, long responseNumber, @Nonnull AutoModRule rule) + { + super(api, responseNumber, rule); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleDeleteEvent.java b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleDeleteEvent.java new file mode 100644 index 0000000000..67b65ab229 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleDeleteEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.automod; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.automod.AutoModRule; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link AutoModRule} was deleted. + * + *

    Requirements
    + * + *

    These events require the {@link net.dv8tion.jda.api.requests.GatewayIntent#AUTO_MODERATION_CONFIGURATION AUTO_MODERATION_CONFIGURATION} intent to be enabled. + */ +public class AutoModRuleDeleteEvent extends GenericAutoModRuleEvent +{ + public AutoModRuleDeleteEvent(@Nonnull JDA api, long responseNumber, @Nonnull AutoModRule rule) + { + super(api, responseNumber, rule); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleUpdateEvent.java new file mode 100644 index 0000000000..3714dc8f6e --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/automod/AutoModRuleUpdateEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.automod; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.automod.AutoModRule; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link AutoModRule} was updated. + * + *

    Requirements
    + * + *

    These events require the {@link net.dv8tion.jda.api.requests.GatewayIntent#AUTO_MODERATION_CONFIGURATION AUTO_MODERATION_CONFIGURATION} intent to be enabled. + */ +public class AutoModRuleUpdateEvent extends GenericAutoModRuleEvent +{ + public AutoModRuleUpdateEvent(@Nonnull JDA api, long responseNumber, @Nonnull AutoModRule rule) + { + super(api, responseNumber, rule); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/automod/GenericAutoModRuleEvent.java b/src/main/java/net/dv8tion/jda/api/events/automod/GenericAutoModRuleEvent.java new file mode 100644 index 0000000000..4126257e6f --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/automod/GenericAutoModRuleEvent.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.automod; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.events.Event; + +import javax.annotation.Nonnull; + +/** + * Indicates that na {@link AutoModRule} was created/removed/updated. + * + *

    Requirements
    + * + *

    These events require the {@link net.dv8tion.jda.api.requests.GatewayIntent#AUTO_MODERATION_CONFIGURATION AUTO_MODERATION_CONFIGURATION} intent to be enabled. + */ +public class GenericAutoModRuleEvent extends Event +{ + private final AutoModRule rule; + + public GenericAutoModRuleEvent(@Nonnull JDA api, long responseNumber, @Nonnull AutoModRule rule) + { + super(api, responseNumber); + this.rule = rule; + } + + /** + * The {@link AutoModRule} that was created/removed/updated. + * + * @return The {@link AutoModRule} + */ + @Nonnull + public AutoModRule getRule() + { + return rule; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java index ff85f8ec7f..2362720ba2 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java @@ -17,6 +17,10 @@ import net.dv8tion.jda.annotations.ForRemoval; import net.dv8tion.jda.api.events.*; +import net.dv8tion.jda.api.events.automod.AutoModExecutionEvent; +import net.dv8tion.jda.api.events.automod.AutoModRuleCreateEvent; +import net.dv8tion.jda.api.events.automod.AutoModRuleDeleteEvent; +import net.dv8tion.jda.api.events.automod.AutoModRuleUpdateEvent; import net.dv8tion.jda.api.events.channel.ChannelCreateEvent; import net.dv8tion.jda.api.events.channel.ChannelDeleteEvent; import net.dv8tion.jda.api.events.channel.GenericChannelEvent; @@ -320,6 +324,12 @@ public void onGuildVoiceStream(@Nonnull GuildVoiceStreamEvent event) {} public void onGuildVoiceVideo(@Nonnull GuildVoiceVideoEvent event) {} public void onGuildVoiceRequestToSpeak(@Nonnull GuildVoiceRequestToSpeakEvent event) {} + //Guild AutoMod Events + public void onAutoModExecution(@Nonnull AutoModExecutionEvent event) {} + public void onAutoModRuleCreate(@Nonnull AutoModRuleCreateEvent event) {} + public void onAutoModRuleUpdate(@Nonnull AutoModRuleUpdateEvent event) {} + public void onAutoModRuleDelete(@Nonnull AutoModRuleDeleteEvent event) {} + //Role events public void onRoleCreate(@Nonnull RoleCreateEvent event) {} public void onRoleDelete(@Nonnull RoleDeleteEvent event) {} diff --git a/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java b/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java index 8ea5e9ae31..2988de9137 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java @@ -16,6 +16,7 @@ package net.dv8tion.jda.api.interactions.callbacks; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.interactions.InteractionHook; @@ -114,6 +115,18 @@ default ReplyCallbackAction deferReply(boolean ephemeral) *
    When the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param message * The {@link MessageCreateData} to send * @@ -143,6 +156,18 @@ default ReplyCallbackAction reply(@Nonnull MessageCreateData message) *
    When the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param content * The message content to send * @@ -169,6 +194,18 @@ default ReplyCallbackAction reply(@Nonnull String content) *
    When the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param embeds * The {@link MessageEmbed MessageEmbeds} to send * @@ -194,6 +231,18 @@ default ReplyCallbackAction replyEmbeds(@Nonnull CollectionWhen the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param embed * The message embed to send * @param embeds @@ -223,6 +272,18 @@ default ReplyCallbackAction replyEmbeds(@Nonnull MessageEmbed embed, @Nonnull Me *
    When the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param components * The {@link LayoutComponent LayoutComponents} to send, such as {@link ActionRow} * @@ -248,6 +309,18 @@ default ReplyCallbackAction replyComponents(@Nonnull CollectionWhen the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param component * The {@link LayoutComponent} to send * @param other @@ -280,6 +353,18 @@ default ReplyCallbackAction replyComponents(@Nonnull LayoutComponent component, *
    When the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}. *

    If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + *
    + * * @param format * Format string for the message content * @param args @@ -313,6 +398,21 @@ default ReplyCallbackAction replyFormat(@Nonnull String format, @Nonnull Object. * For instance, if an exception occurs after using {@link FileUpload#fromData(File)}, before calling {@link RestAction#queue()}. * You can safely use a try-with-resources to handle this, since {@link FileUpload#close()} becomes ineffective once the request is handed off. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} + *
      If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
    • + *
    + * * @param files * The {@link FileUpload FileUploads} to attach to the message * @@ -346,6 +446,21 @@ default ReplyCallbackAction replyFiles(@Nonnull Collection * For instance, if an exception occurs after using {@link FileUpload#fromData(File)}, before calling {@link RestAction#queue()}. * You can safely use a try-with-resources to handle this, since {@link FileUpload#close()} becomes ineffective once the request is handed off. * + *

    Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

      + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION} + *
      If the interaction has already been acknowledged or timed out
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} + *
      If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} + *
      If this message was blocked by the harmful link filter
    • + * + *
    • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} + *
      If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
    • + *
    + * * @param files * The {@link FileUpload FileUploads} to attach to the message * diff --git a/src/main/java/net/dv8tion/jda/api/managers/AutoModRuleManager.java b/src/main/java/net/dv8tion/jda/api/managers/AutoModRuleManager.java new file mode 100644 index 0000000000..72a6b36e3b --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/AutoModRuleManager.java @@ -0,0 +1,284 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.managers; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.build.TriggerConfig; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.util.Arrays; +import java.util.Collection; + +/** + * Manager providing functionality to update one or more fields for an {@link AutoModRule}. + * + *

    Example + *

    {@code
    + * manager.setName("Discord Invites")
    + *        .setEnables(false)
    + *        .queue();
    + * manager.reset(AutoModRuleManager.NAME | AutoModRuleManager.ENABLED)
    + *        .setName("Invites")
    + *        .setEnabled(true)
    + *        .queue();
    + * }
    + * + * @see Guild#modifyAutoModRuleById(long) + * @see Guild#modifyAutoModRuleById(String) + * @see AutoModRule#getManager() + */ +public interface AutoModRuleManager extends Manager +{ + /** Used to reset the name field. */ + long NAME = 1; + /** Used to reset the enabled field. */ + long ENABLED = 1 << 1; + /** Used to reset the response field. */ + long RESPONSE = 1 << 2; + /** Used to reset the exempt roles field. */ + long EXEMPT_ROLES = 1 << 3; + /** Used to reset the exempt channels field. */ + long EXEMPT_CHANNELS = 1 << 4; + /** Used to reset the trigger metadata field. */ + long TRIGGER_METADATA = 1 << 5; + + /** + * Resets the fields specified by the provided bit-flag pattern. + * You can specify a combination by using a bitwise OR concat of the flag constants. + *
    Example: {@code manager.reset(AutoModRuleManager.NAME | AutoModRuleManager.RESPONSE);} + * + *

    Flag Constants: + *

      + *
    • {@link #NAME}
    • + *
    • {@link #ENABLED}
    • + *
    • {@link #RESPONSE}
    • + *
    • {@link #EXEMPT_ROLES}
    • + *
    • {@link #EXEMPT_CHANNELS}
    • + *
    • {@link #TRIGGER_METADATA}
    • + *
    + * + * @param fields + * Integer value containing the flags to reset. + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @Override + AutoModRuleManager reset(long fields); + + /** + * Resets the fields specified by the provided bit-flag pattern. + * You can specify a combination by using a bitwise OR concat of the flag constants. + *
    Example: {@code manager.reset(AutoModRuleManager.NAME, AutoModRuleManager.RESPONSE);} + * + *

    Flag Constants: + *

      + *
    • {@link #NAME}
    • + *
    • {@link #ENABLED}
    • + *
    • {@link #RESPONSE}
    • + *
    • {@link #EXEMPT_ROLES}
    • + *
    • {@link #EXEMPT_CHANNELS}
    • + *
    • {@link #TRIGGER_METADATA}
    • + *
    + * + * @param fields + * Integer value containing the flags to reset. + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @Override + AutoModRuleManager reset(long... fields); + + /** + * Sets the name of the selected {@link AutoModRule}. + * + *

    A rule name must be between 1-{@value AutoModRule#MAX_RULE_NAME_LENGTH} characters long! + * + * @param name + * The new name for the selected {@link AutoModRule} + * + * @throws IllegalArgumentException + * If the provided name is {@code null} or not between 1-{@value AutoModRule#MAX_RULE_NAME_LENGTH} characters long + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager setName(@Nonnull String name); + + /** + * Sets the enabled state of the selected {@link AutoModRule}. + * + *

    When a rule is disabled, it will not be applied to any messages. + * + * @param enabled + * True, if the selected {@link AutoModRule} should be enabled + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager setEnabled(boolean enabled); + +// @Nonnull +// @CheckReturnValue +// AutoModRuleManager setEventType(@Nonnull AutoModEventType type); + + /** + * Sets what the rule should do upon triggering. + * + *

    Note that each response type can only be used once. + * If multiple responses of the same type are provided, the last one is used. + * + * @param responses + * The responses to configure + * + * @throws IllegalArgumentException + *

      + *
    • If {@code null} or {@link AutoModResponse.Type#UNKNOWN} is provided
    • + *
    • If the collection is empty
    • + *
    + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager setResponses(@Nonnull Collection responses); + + /** + * Sets what the rule should do upon triggering. + * + *

    Note that each response type can only be used once. + * If multiple responses of the same type are provided, the last one is used. + * + * @param responses + * The responses to configure + * + * @throws IllegalArgumentException + *

      + *
    • If {@code null} or {@link AutoModResponse.Type#UNKNOWN} is provided
    • + *
    • If the collection is empty
    • + *
    + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + default AutoModRuleManager setResponses(@Nonnull AutoModResponse... responses) + { + Checks.noneNull(responses, "Responses"); + return setResponses(Arrays.asList(responses)); + } + + /** + * Set which roles can bypass this rule. + * + *

    Roles added to the exemptions will allow all of its members to bypass this rule. + * + * @param roles + * The roles to exempt (up to {@value AutoModRule#MAX_EXEMPT_ROLES} roles) + * + * @throws IllegalArgumentException + * If null is provided or the number of roles exceeds {@value AutoModRule#MAX_EXEMPT_ROLES} + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager setExemptRoles(@Nonnull Collection roles); + + /** + * Set which roles can bypass this rule. + * + *

    Roles added to the exemptions will allow all of its members to bypass this rule. + * + * @param roles + * The roles to exempt (up to {@value AutoModRule#MAX_EXEMPT_ROLES} roles) + * + * @throws IllegalArgumentException + * If null is provided or the number of roles exceeds {@value AutoModRule#MAX_EXEMPT_ROLES} + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + default AutoModRuleManager setExemptRoles(@Nonnull Role... roles) + { + Checks.noneNull(roles, "Roles"); + return setExemptRoles(Arrays.asList(roles)); + } + + /** + * Set which channels can bypass this rule. + * + *

    No messages sent in this channel will trigger the rule. + * + * @param channels + * The channels to add (up to {@value AutoModRule#MAX_EXEMPT_CHANNELS} channels) + * + * @throws IllegalArgumentException + * If null is provided or the number of channels exceeds {@value AutoModRule#MAX_EXEMPT_CHANNELS} + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager setExemptChannels(@Nonnull Collection channels); + + /** + * Set which channels can bypass this rule. + * + *

    No messages sent in this channel will trigger the rule. + * + * @param channels + * The channels to add (up to {@value AutoModRule#MAX_EXEMPT_CHANNELS} channels) + * + * @throws IllegalArgumentException + * If null is provided or the number of channels exceeds {@value AutoModRule#MAX_EXEMPT_CHANNELS} + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + default AutoModRuleManager setExemptChannels(@Nonnull GuildChannel... channels) + { + Checks.noneNull(channels, "Channels"); + return setExemptChannels(Arrays.asList(channels)); + } + + /** + * Change the {@link TriggerConfig} for this rule. + * + * @param config + * The new config + * + * @throws IllegalArgumentException + * If null is provided + * + * @return AutoModRuleManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + AutoModRuleManager setTriggerConfig(@Nonnull TriggerConfig config); +} diff --git a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java index e39190b742..f59c3a5c65 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java +++ b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java @@ -181,6 +181,9 @@ public enum ErrorResponse MAX_LOTTIE_ANIMATION_DIMENSION( 170005, "Lottie animation maximum dimensions exceeded"), STICKER_FPS_TOO_SMALL_OR_TOO_LARGE( 170006, "Sticker frame rate is either too small or too large"), MAX_STICKER_ANIMATION_DURATION( 170007, "Sticker animation duration exceeds maximum of 5 seconds"), + MESSAGE_BLOCKED_BY_AUTOMOD( 200000, "Message was blocked by automatic moderation"), + TITLE_BLOCKED_BY_AUTOMOD( 200001, "Title was blocked by automatic moderation"), + MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER( 240000, "Message blocked by harmful links filter"), SERVER_ERROR( 0, "Discord encountered an internal server error! Not good!"); diff --git a/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java b/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java index 51db8d7722..c7eb6f6a9f 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java +++ b/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java @@ -22,6 +22,8 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.events.GenericEvent; +import net.dv8tion.jda.api.events.automod.AutoModExecutionEvent; +import net.dv8tion.jda.api.events.automod.GenericAutoModRuleEvent; import net.dv8tion.jda.api.events.emoji.GenericEmojiEvent; import net.dv8tion.jda.api.events.guild.GuildAuditLogEntryCreateEvent; import net.dv8tion.jda.api.events.guild.GuildBanEvent; @@ -67,6 +69,10 @@ *

  • DIRECT_MESSAGES - This is used to receive incoming messages in private channels (DMs). You can still send private messages without this intent.
  • *
  • DIRECT_MESSAGE_REACTIONS - This is used to track reactions on messages in private channels (DMs).
  • *
  • DIRECT_MESSAGE_TYPING - This is used to track when a user starts typing in private channels (DMs). Almost no bot will have a use for this.
  • + *
  • MESSAGE_CONTENT - This is a privileged gateway intent this is only used to enable access to the user content in messages (also including embeds/attachments/components).
  • + *
  • SCHEDULED_EVENTS - This is used to keep track of scheduled events in guilds.
  • + *
  • AUTO_MODERATION_CONFIGURATION - This is used to keep track of auto-mod rule changes in guilds.
  • + *
  • AUTO_MODERATION_EXECUTION - This is used to receive events related to auto-mod response actions.
  • * * * If an intent is not specifically mentioned to be privileged, it is not required to be on the whitelist to use it (and its related events). @@ -177,7 +183,17 @@ public enum GatewayIntent * Scheduled Events events. */ SCHEDULED_EVENTS(16), - + + /** + * Events related to {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule} changes. + */ + AUTO_MODERATION_CONFIGURATION(20), + + /** + * Events related to {@link net.dv8tion.jda.api.entities.automod.AutoModResponse AutoModResponse} triggers. + */ + AUTO_MODERATION_EXECUTION(21), + ; /** @@ -411,6 +427,11 @@ else if (GenericMessageEvent.class.isAssignableFrom(event)) else if (UserTypingEvent.class.isAssignableFrom(event)) Collections.addAll(intents, GUILD_MESSAGE_TYPING, DIRECT_MESSAGE_TYPING); + + else if (AutoModExecutionEvent.class.isAssignableFrom(event)) + intents.add(AUTO_MODERATION_EXECUTION); + else if (GenericAutoModRuleEvent.class.isAssignableFrom(event)) + intents.add(AUTO_MODERATION_CONFIGURATION); } return intents; } diff --git a/src/main/java/net/dv8tion/jda/api/requests/Route.java b/src/main/java/net/dv8tion/jda/api/requests/Route.java index 3c64d22762..f5e04a79ab 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java @@ -229,6 +229,15 @@ public static class StageInstances public static final Route CREATE_INSTANCE = new Route(POST, "stage-instances"); } + public static class AutoModeration + { + public static final Route LIST_RULES = new Route(GET, "guilds/{guild_id}/auto-moderation/rules"); + public static final Route GET_RULE = new Route(GET, "guilds/{guild_id}/auto-moderation/rules/{rule_id}"); + public static final Route CREATE_RULE = new Route(POST, "guilds/{guild_id}/auto-moderation/rules"); + public static final Route UPDATE_RULE = new Route(PATCH, "guilds/{guild_id}/auto-moderation/rules/{rule_id}"); + public static final Route DELETE_RULE = new Route(DELETE, "guilds/{guild_id}/auto-moderation/rules/{rule_id}"); + } + public static class Messages { public static final Route EDIT_MESSAGE = new Route(PATCH, "channels/{channel_id}/messages/{message_id}"); // requires special handling, same bucket but different endpoints diff --git a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java index 68ff32091b..d8e386a4a1 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -23,6 +23,8 @@ import net.dv8tion.jda.api.Region; import net.dv8tion.jda.api.audio.hooks.ConnectionStatus; import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.build.AutoModRuleData; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.*; @@ -43,10 +45,7 @@ import net.dv8tion.jda.api.interactions.commands.PrivilegeConfig; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.privileges.IntegrationPrivilege; -import net.dv8tion.jda.api.managers.AudioManager; -import net.dv8tion.jda.api.managers.GuildManager; -import net.dv8tion.jda.api.managers.GuildStickerManager; -import net.dv8tion.jda.api.managers.GuildWelcomeScreenManager; +import net.dv8tion.jda.api.managers.*; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.Route; @@ -61,13 +60,11 @@ import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.automod.AutoModRuleImpl; import net.dv8tion.jda.internal.handle.EventCache; import net.dv8tion.jda.internal.interactions.CommandDataImpl; import net.dv8tion.jda.internal.interactions.command.CommandImpl; -import net.dv8tion.jda.internal.managers.AudioManagerImpl; -import net.dv8tion.jda.internal.managers.GuildManagerImpl; -import net.dv8tion.jda.internal.managers.GuildStickerManagerImpl; -import net.dv8tion.jda.internal.managers.GuildWelcomeScreenManagerImpl; +import net.dv8tion.jda.internal.managers.*; import net.dv8tion.jda.internal.requests.*; import net.dv8tion.jda.internal.requests.restaction.*; import net.dv8tion.jda.internal.requests.restaction.order.CategoryOrderActionImpl; @@ -358,6 +355,71 @@ public RestAction> retrieveRegions(boolean includeDeprecated) }); } + @Nonnull + @Override + public RestAction> retrieveAutoModRules() + { + checkPermission(Permission.MANAGE_SERVER); + Route.CompiledRoute route = Route.AutoModeration.LIST_RULES.compile(getId()); + return new RestActionImpl<>(api, route, (response, request) -> + { + DataArray array = response.getArray(); + List rules = new ArrayList<>(array.length()); + for (int i = 0; i < array.length(); i++) + { + try + { + DataObject obj = array.getObject(i); + rules.add(AutoModRuleImpl.fromData(this, obj)); + } + catch (ParsingException exception) + { + EntityBuilder.LOG.error("Failed to parse AutoModRule", exception); + } + } + return Collections.unmodifiableList(rules); + }); + } + + @Nonnull + @Override + public RestAction retrieveAutoModRuleById(@Nonnull String id) + { + Checks.isSnowflake(id); + checkPermission(Permission.MANAGE_SERVER); + Route.CompiledRoute route = Route.AutoModeration.GET_RULE.compile(getId(), id); + return new RestActionImpl<>(api, route, (response, request) -> AutoModRuleImpl.fromData(this, response.getObject())); + } + + @Nonnull + @Override + public AuditableRestAction createAutoModRule(@Nonnull AutoModRuleData rule) + { + Checks.notNull(rule, "AutoMod Rule"); + rule.getRequiredPermissions().forEach(this::checkPermission); + Route.CompiledRoute route = Route.AutoModeration.CREATE_RULE.compile(getId()); + return new AuditableRestActionImpl<>(api, route, rule.toData(), (response, request) -> AutoModRuleImpl.fromData(this, response.getObject())); + } + + @Nonnull + @Override + public AutoModRuleManager modifyAutoModRuleById(@Nonnull String id) + { + Checks.isSnowflake(id); + checkPermission(Permission.MANAGE_SERVER); + return new AutoModRuleManagerImpl(this, id); + } + + @Nonnull + @Override + public AuditableRestAction deleteAutoModRuleById(@Nonnull String id) + { + Checks.isSnowflake(id); + checkPermission(Permission.MANAGE_SERVER); + Route.CompiledRoute route = Route.AutoModeration.DELETE_RULE.compile(getId(), id); + return new AuditableRestActionImpl<>(api, route); + } + @Nonnull @Override public MemberAction addMember(@Nonnull String accessToken, @Nonnull UserSnowflake user) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModExecutionImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModExecutionImpl.java new file mode 100644 index 0000000000..8eb6e97a87 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModExecutionImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities.automod; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.automod.AutoModExecution; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.entities.channel.unions.GuildMessageChannelUnion; +import net.dv8tion.jda.api.utils.data.DataObject; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AutoModExecutionImpl implements AutoModExecution +{ + private final Guild guild; + private final GuildMessageChannel channel; + private final AutoModResponse response; + private final AutoModTriggerType type; + private final long userId, ruleId, messageId, alertMessageId; + private final String content, matchedContent, matchedKeyword; + + public AutoModExecutionImpl(Guild guild, DataObject json) + { + this.guild = guild; + this.channel = guild.getChannelById(GuildMessageChannel.class, json.getUnsignedLong("channel_id")); + this.response = new AutoModResponseImpl(guild, json.getObject("action")); + this.type = AutoModTriggerType.fromKey(json.getInt("rule_trigger_type", -1)); + this.userId = json.getUnsignedLong("user_id"); + this.ruleId = json.getUnsignedLong("rule_id"); + this.messageId = json.getUnsignedLong("message_id", 0L); + this.alertMessageId = json.getUnsignedLong("alert_system_message_id", 0L); + this.content = json.getString("content", ""); + this.matchedContent = json.getString("matched_content", null); + this.matchedKeyword = json.getString("matched_keyword", null); + } + + @Nonnull + @Override + public Guild getGuild() + { + return guild; + } + + @Nullable + @Override + public GuildMessageChannelUnion getChannel() + { + return (GuildMessageChannelUnion) channel; + } + + @Nonnull + @Override + public AutoModResponse getResponse() + { + return response; + } + + @Nonnull + @Override + public AutoModTriggerType getTriggerType() + { + return type; + } + + @Override + public long getUserIdLong() + { + return userId; + } + + @Override + public long getRuleIdLong() + { + return ruleId; + } + + @Override + public long getMessageIdLong() + { + return messageId; + } + + @Override + public long getAlertMessageIdLong() + { + return alertMessageId; + } + + @Nonnull + @Override + public String getContent() + { + return content; + } + + @Nullable + @Override + public String getMatchedContent() + { + return matchedContent; + } + + @Nullable + @Override + public String getMatchedKeyword() + { + return matchedKeyword; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModResponseImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModResponseImpl.java new file mode 100644 index 0000000000..f15fd97c4d --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModResponseImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities.automod; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.utils.data.DataObject; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.Duration; + +public class AutoModResponseImpl implements AutoModResponse +{ + private final Type type; + private final GuildMessageChannel channel; + private final String customMessage; + private final long timeoutDuration; + + public AutoModResponseImpl(Type type) + { + this.type = type; + this.channel = null; + this.customMessage = null; + this.timeoutDuration = 0; + } + + public AutoModResponseImpl(Type type, GuildMessageChannel channel) + { + this.type = type; + this.channel = channel; + this.customMessage = null; + this.timeoutDuration = 0; + } + + public AutoModResponseImpl(Type type, String customMessage) + { + this.type = type; + this.customMessage = customMessage; + this.channel = null; + this.timeoutDuration = 0; + } + + public AutoModResponseImpl(Type type, Duration duration) + { + this.type = type; + this.timeoutDuration = duration.getSeconds(); + this.customMessage = null; + this.channel = null; + } + + public AutoModResponseImpl(Guild guild, DataObject json) + { + this.type = AutoModResponse.Type.fromKey(json.getInt("type", -1)); + this.channel = guild.getChannelById(GuildMessageChannel.class, json.getUnsignedLong("channel_id", 0L)); + this.customMessage = json.getString("custom_message", null); + this.timeoutDuration = json.getUnsignedLong("duration_seconds", 0L); + } + + @Nonnull + @Override + public Type getType() + { + return type; + } + + @Nullable + @Override + public GuildMessageChannel getChannel() + { + return channel; + } + + @Nullable + @Override + public String getCustomMessage() + { + return customMessage; + } + + @Nullable + @Override + public Duration getTimeoutDuration() + { + return timeoutDuration == 0 ? null : Duration.ofSeconds(timeoutDuration); + } + + @Nonnull + @Override + public DataObject toData() + { + DataObject action = DataObject.empty(); + action.put("type", type.getKey()); + if (type == Type.BLOCK_MESSAGE && customMessage == null) + return action; + + DataObject metadata = DataObject.empty(); + if (customMessage != null) + metadata.put("custom_message", customMessage); + if (channel != null) + metadata.put("channel_id", channel.getId()); + if (timeoutDuration > 0) + metadata.put("duration_seconds", timeoutDuration); + action.put("metadata", metadata); + return action; + } + + @Override + public int hashCode() + { + return type.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!(obj instanceof AutoModResponseImpl)) + return false; + AutoModResponseImpl o = (AutoModResponseImpl) obj; + return type == o.type; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java new file mode 100644 index 0000000000..9bfbd75cad --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java @@ -0,0 +1,358 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities.automod; + +import gnu.trove.list.TLongList; +import gnu.trove.list.array.TLongArrayList; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.automod.AutoModEventType; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.EntityString; +import net.dv8tion.jda.internal.utils.Helpers; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +public class AutoModRuleImpl implements AutoModRule +{ + private final long id; + private Guild guild; + private long ownerId; + private String name = ""; + private AutoModEventType eventType = AutoModEventType.UNKNOWN; + private AutoModTriggerType triggerType = AutoModTriggerType.UNKNOWN; + private boolean enabled = false; + private TLongList exemptRoles = new TLongArrayList(); + private TLongList exemptChannels = new TLongArrayList(); + private List actions = Collections.emptyList(); + private List filteredKeywords = Collections.emptyList(); + private List filteredRegex = Collections.emptyList(); + private EnumSet filteredPresets = EnumSet.noneOf(KeywordPreset.class); + private List allowlist = Collections.emptyList(); + private int mentionLimit = -1; + private boolean isMentionRaidProtectionEnabled = false; + + public AutoModRuleImpl(Guild guild, long id) + { + this.id = id; + this.guild = guild; + } + + @Override + public long getIdLong() + { + return id; + } + + @NotNull + @Override + public Guild getGuild() + { + Guild realGuild = guild.getJDA().getGuildById(guild.getIdLong()); + if (realGuild != null) + guild = realGuild; + return guild; + } + + @Override + public long getCreatorIdLong() + { + return ownerId; + } + + @NotNull + @Override + public String getName() + { + return name; + } + + @NotNull + @Override + public AutoModEventType getEventType() + { + return eventType; + } + + @NotNull + @Override + public AutoModTriggerType getTriggerType() + { + return triggerType; + } + + @Override + public boolean isEnabled() + { + return enabled; + } + + @NotNull + @Override + public List getExemptRoles() + { + List roles = new ArrayList<>(exemptRoles.size()); + for (int i = 0; i < exemptRoles.size(); i++) + { + long roleId = exemptRoles.get(i); + Role role = guild.getRoleById(roleId); + if (role != null) + roles.add(role); + } + return Collections.unmodifiableList(roles); + } + + @NotNull + @Override + public List getExemptChannels() + { + List channels = new ArrayList<>(exemptChannels.size()); + for (int i = 0; i < exemptChannels.size(); i++) + { + long channelId = exemptChannels.get(i); + GuildChannel channel = guild.getGuildChannelById(channelId); + if (channel != null) + channels.add(channel); + } + return Collections.unmodifiableList(channels); + } + + @NotNull + @Override + public List getActions() + { + return actions; + } + + @NotNull + @Override + public List getFilteredKeywords() + { + return filteredKeywords; + } + + @NotNull + @Override + public List getFilteredRegex() + { + return filteredRegex; + } + + @NotNull + @Override + public EnumSet getFilteredPresets() + { + return Helpers.copyEnumSet(KeywordPreset.class, filteredPresets); + } + + @NotNull + @Override + public List getAllowlist() + { + return allowlist; + } + + @Override + public int getMentionLimit() + { + return mentionLimit; + } + + @Override + public boolean isMentionRaidProtectionEnabled() + { + return isMentionRaidProtectionEnabled; + } + + public AutoModRuleImpl setName(String name) + { + this.name = name; + return this; + } + + public AutoModRuleImpl setEnabled(boolean enabled) + { + this.enabled = enabled; + return this; + } + + public AutoModRuleImpl setOwnerId(long ownerId) + { + this.ownerId = ownerId; + return this; + } + + public AutoModRuleImpl setEventType(AutoModEventType eventType) + { + this.eventType = eventType; + return this; + } + + public AutoModRuleImpl setTriggerType(AutoModTriggerType triggerType) + { + this.triggerType = triggerType; + return this; + } + + public AutoModRuleImpl setExemptRoles(TLongList exemptRoles) + { + this.exemptRoles = exemptRoles; + return this; + } + + public AutoModRuleImpl setExemptChannels(TLongList exemptChannels) + { + this.exemptChannels = exemptChannels; + return this; + } + + public AutoModRuleImpl setActions(List actions) + { + this.actions = actions; + return this; + } + + public AutoModRuleImpl setFilteredKeywords(List filteredKeywords) + { + this.filteredKeywords = filteredKeywords; + return this; + } + + public AutoModRuleImpl setFilteredRegex(List filteredRegex) + { + this.filteredRegex = filteredRegex; + return this; + } + + public AutoModRuleImpl setFilteredPresets(EnumSet filteredPresets) + { + this.filteredPresets = filteredPresets; + return this; + } + + public AutoModRuleImpl setAllowlist(List allowlist) + { + this.allowlist = allowlist; + return this; + } + + public AutoModRuleImpl setMentionLimit(int mentionLimit) + { + this.mentionLimit = mentionLimit; + return this; + } + + public AutoModRuleImpl setMentionRaidProtectionEnabled(boolean mentionRaidProtectionEnabled) + { + isMentionRaidProtectionEnabled = mentionRaidProtectionEnabled; + return this; + } + + @Override + public int hashCode() + { + return Long.hashCode(id); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof AutoModRuleImpl)) + return false; + AutoModRuleImpl oRule = (AutoModRuleImpl) obj; + return this.id == oRule.id; + } + + @Override + public String toString() + { + return new EntityString(this) + .setType(triggerType) + .setName(name) + .addMetadata("id", getId()) + .toString(); + } + + public static AutoModRuleImpl fromData(Guild guild, DataObject data) + { + long id = data.getUnsignedLong("id"); + AutoModRuleImpl rule = new AutoModRuleImpl(guild, id); + + rule.setName(data.getString("name")) + .setEnabled(data.getBoolean("enabled", true)) + .setOwnerId(data.getUnsignedLong("creator_id", 0L)) + .setEventType(AutoModEventType.fromKey(data.getInt("event_type", -1))) + .setTriggerType(AutoModTriggerType.fromKey(data.getInt("trigger_type", -1))); + + data.optArray("exempt_roles").ifPresent(array -> rule.setExemptRoles(parseList(array))); + data.optArray("exempt_channels").ifPresent(array -> rule.setExemptChannels(parseList(array))); + + data.optArray("actions").ifPresent(array -> + rule.setActions(array.stream(DataArray::getObject) + .map(obj -> new AutoModResponseImpl(guild, obj)) + .collect(Helpers.toUnmodifiableList())) + ); + + data.optObject("trigger_metadata").ifPresent(metadata -> { + // Only for KEYWORD type + metadata.optArray("keyword_filter").ifPresent(array -> + rule.setFilteredKeywords(array.stream(DataArray::getString) + .collect(Helpers.toUnmodifiableList())) + ); + metadata.optArray("regex_patterns").ifPresent(array -> + rule.setFilteredRegex(array.stream(DataArray::getString) + .collect(Helpers.toUnmodifiableList())) + ); + // Both KEYWORD and KEYWORD_PRESET + metadata.optArray("allow_list").ifPresent(array -> + rule.setAllowlist(array.stream(DataArray::getString) + .collect(Helpers.toUnmodifiableList())) + ); + // Only KEYWORD_PRESET + metadata.optArray("presets").ifPresent(array -> + rule.setFilteredPresets(array.stream(DataArray::getInt) + .map(KeywordPreset::fromKey) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(KeywordPreset.class)))) + ); + // Only for MENTION type + rule.setMentionLimit(metadata.getInt("mention_total_limit", 0)); + rule.setMentionRaidProtectionEnabled(metadata.getBoolean("mention_raid_protection_enabled")); + }); + + return rule; + } + + private static TLongList parseList(DataArray array) + { + TLongList list = new TLongArrayList(array.length()); + for (int i = 0; i < array.length(); i++) + list.add(array.getUnsignedLong(i)); + return list; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/AutoModExecutionHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/AutoModExecutionHandler.java new file mode 100644 index 0000000000..4edd95f1dd --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/AutoModExecutionHandler.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.events.automod.AutoModExecutionEvent; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.automod.AutoModExecutionImpl; + +public class AutoModExecutionHandler extends SocketHandler +{ + public AutoModExecutionHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + long guildId = content.getUnsignedLong("guild_id"); + if (api.getGuildSetupController().isLocked(guildId)) + return guildId; + Guild guild = api.getGuildById(guildId); + if (guild == null) + { + api.getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + EventCache.LOG.debug("Received a AUTO_MODERATION_ACTION_EXECUTION for a guild that is not yet cached. JSON: {}", content); + return null; + } + + AutoModExecutionImpl execution = new AutoModExecutionImpl(guild, content); + api.handleEvent( + new AutoModExecutionEvent( + api, responseNumber, + execution)); + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/AutoModRuleHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/AutoModRuleHandler.java new file mode 100644 index 0000000000..53f9e78ccf --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/AutoModRuleHandler.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.events.automod.AutoModRuleCreateEvent; +import net.dv8tion.jda.api.events.automod.AutoModRuleDeleteEvent; +import net.dv8tion.jda.api.events.automod.AutoModRuleUpdateEvent; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.automod.AutoModRuleImpl; + +public class AutoModRuleHandler extends SocketHandler +{ + private final String type; + + public AutoModRuleHandler(JDAImpl api, String type) + { + super(api); + this.type = type; + } + + @Override + protected Long handleInternally(DataObject content) + { + long guildId = content.getUnsignedLong("guild_id"); + if (api.getGuildSetupController().isLocked(guildId)) + return guildId; + Guild guild = api.getGuildById(guildId); + if (guild == null) + { + api.getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + EventCache.LOG.debug("Received a AUTO_MODERATION_RULE_{} for a guild that is not yet cached. JSON: {}", type, content); + return null; + } + + AutoModRule rule = AutoModRuleImpl.fromData(guild, content); + switch (type) + { + case "CREATE": + api.handleEvent( + new AutoModRuleCreateEvent( + api, responseNumber, + rule)); + break; + case "UPDATE": + api.handleEvent( + new AutoModRuleUpdateEvent( + api, responseNumber, + rule)); + break; + case "DELETE": + api.handleEvent( + new AutoModRuleDeleteEvent( + api, responseNumber, + rule)); + break; + } + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/managers/AutoModRuleManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/AutoModRuleManagerImpl.java new file mode 100644 index 0000000000..ce6d34c85b --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/managers/AutoModRuleManagerImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.managers; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.automod.AutoModResponse; +import net.dv8tion.jda.api.entities.automod.AutoModRule; +import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; +import net.dv8tion.jda.api.entities.automod.build.TriggerConfig; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.managers.AutoModRuleManager; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; +import okhttp3.RequestBody; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumMap; +import java.util.List; +import java.util.stream.Collectors; + +public class AutoModRuleManagerImpl extends ManagerBase implements AutoModRuleManager +{ + protected final Guild guild; + protected String name; + protected boolean enabled; + protected EnumMap responses; + protected List exemptRoles; + protected List exemptChannels; + protected TriggerConfig triggerConfig; + + public AutoModRuleManagerImpl(Guild guild, String ruleId) + { + super(guild.getJDA(), Route.AutoModeration.UPDATE_RULE.compile(guild.getId(), ruleId)); + this.guild = guild; + } + + @Nonnull + @Override + public AutoModRuleManager setName(@Nonnull String name) + { + Checks.notEmpty(name, "Name"); + Checks.notLonger(name, AutoModRule.MAX_RULE_NAME_LENGTH, "Name"); + this.name = name; + set |= NAME; + return this; + } + + @Nonnull + @Override + public AutoModRuleManager setEnabled(boolean enabled) + { + this.enabled = enabled; + set |= ENABLED; + return this; + } + + @Nonnull + @Override + public AutoModRuleManager setResponses(@Nonnull Collection responses) + { + Checks.noneNull(responses, "Responses"); + Checks.notEmpty(responses, "Responses"); + this.responses = new EnumMap<>(AutoModResponse.Type.class); + for (AutoModResponse response : responses) + { + AutoModResponse.Type type = response.getType(); + Checks.check(type != AutoModResponse.Type.UNKNOWN, "Cannot add response with unknown response type!"); + this.responses.put(type, response); + } + set |= RESPONSE; + return this; + } + + @Nonnull + @Override + public AutoModRuleManager setExemptRoles(@Nonnull Collection roles) + { + Checks.noneNull(roles, "Roles"); + Checks.check(roles.size() <= AutoModRule.MAX_EXEMPT_ROLES, "Cannot have more than %d exempt roles!", AutoModRule.MAX_EXEMPT_ROLES); + for (Role role : roles) + Checks.check(role.getGuild().equals(guild), "Role %s is not from the same guild as this rule!", role); + this.exemptRoles = new ArrayList<>(roles); + set |= EXEMPT_ROLES; + return this; + } + + @Nonnull + @Override + public AutoModRuleManager setExemptChannels(@Nonnull Collection channels) + { + Checks.noneNull(channels, "Channels"); + Checks.check(channels.size() <= AutoModRule.MAX_EXEMPT_CHANNELS, "Cannot have more than %d exempt channels!", AutoModRule.MAX_EXEMPT_CHANNELS); + for (GuildChannel channel : channels) + Checks.check(channel.getGuild().equals(guild), "Channel %s is not from the same guild as this rule!", channel); + this.exemptChannels = new ArrayList<>(channels); + set |= EXEMPT_CHANNELS; + return this; + } + + @Nonnull + @Override + public AutoModRuleManager setTriggerConfig(@Nonnull TriggerConfig config) + { + Checks.notNull(config, "TriggerConfig"); + Checks.check(config.getType() != AutoModTriggerType.UNKNOWN, "Unknown trigger type!"); + this.triggerConfig = config; + set |= TRIGGER_METADATA; + return this; + } + + @Override + protected RequestBody finalizeData() + { + DataObject body = DataObject.empty(); + + if (shouldUpdate(NAME)) + body.put("name", name); + if (shouldUpdate(ENABLED)) + body.put("enabled", enabled); + if (shouldUpdate(RESPONSE)) + body.put("actions", DataArray.fromCollection(responses.values())); + if (shouldUpdate(EXEMPT_ROLES)) + body.put("exempt_roles", DataArray.fromCollection(exemptRoles.stream().map(Role::getId).collect(Collectors.toList()))); + if (shouldUpdate(EXEMPT_CHANNELS)) + body.put("exempt_channels", DataArray.fromCollection(exemptChannels.stream().map(GuildChannel::getId).collect(Collectors.toList()))); + if (shouldUpdate(TRIGGER_METADATA)) + { + body.put("trigger_type", triggerConfig.getType().getKey()); + body.put("trigger_metadata", triggerConfig.toData()); + } + + reset(); + return getRequestBody(body); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java index 6135f4b489..1467d81cba 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java @@ -1362,6 +1362,10 @@ protected void setupHandlers() { final SocketHandler.NOPHandler nopHandler = new SocketHandler.NOPHandler(api); handlers.put("APPLICATION_COMMAND_PERMISSIONS_UPDATE", new ApplicationCommandPermissionsUpdateHandler(api)); + handlers.put("AUTO_MODERATION_RULE_CREATE", new AutoModRuleHandler(api, "CREATE")); + handlers.put("AUTO_MODERATION_RULE_UPDATE", new AutoModRuleHandler(api, "UPDATE")); + handlers.put("AUTO_MODERATION_RULE_DELETE", new AutoModRuleHandler(api, "DELETE")); + handlers.put("AUTO_MODERATION_ACTION_EXECUTION", new AutoModExecutionHandler(api)); handlers.put("CHANNEL_CREATE", new ChannelCreateHandler(api)); handlers.put("CHANNEL_DELETE", new ChannelDeleteHandler(api)); handlers.put("CHANNEL_UPDATE", new ChannelUpdateHandler(api)); diff --git a/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java b/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java index 05b7f0f5b3..84fa4ab311 100644 --- a/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java +++ b/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java @@ -316,4 +316,9 @@ public static boolean hasCause(Throwable throwable, Class c { return Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList); } + + public static Collector toDataArray() + { + return Collector.of(DataArray::empty, DataArray::add, DataArray::addAll); + } }