Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by
+ * the returned {@link RestAction RestAction} include the following:
+ *
modifySecurityIncidents(@Nonnull SecurityIncidents incidents);
+
/**
* Kicks the {@link UserSnowflake} from the {@link net.dv8tion.jda.api.entities.Guild Guild}.
*
diff --git a/src/main/java/net/dv8tion/jda/api/entities/SecurityIncidents.java b/src/main/java/net/dv8tion/jda/api/entities/SecurityIncidents.java
new file mode 100644
index 0000000000..1a5bb76289
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/entities/SecurityIncidents.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.entities;
+
+import net.dv8tion.jda.internal.utils.Helpers;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.OffsetDateTime;
+import java.util.Objects;
+
+/**
+ * The active security incidents in a {@link Guild}.
+ *
+ * Security incidents are used to temporarily disable features for the purpose of moderation.
+ *
+ * @see #enabled(OffsetDateTime, OffsetDateTime)
+ * @see #disabled()
+ */
+public class SecurityIncidents
+{
+ private static final SecurityIncidents disabled = new SecurityIncidents(0, 0);
+
+ private final long invitesDisabledUntil;
+ private final long directMessagesDisabledUntil;
+
+ private SecurityIncidents(long invitesDisabledUntil, long directMessagesDisabledUntil)
+ {
+ this.invitesDisabledUntil = invitesDisabledUntil;
+ this.directMessagesDisabledUntil = directMessagesDisabledUntil;
+ }
+
+ /**
+ * The time until when invites are paused.
+ *
+ * @return The time until invites are paused, or null if unpaused
+ */
+ @Nullable
+ public OffsetDateTime getInvitesDisabledUntil()
+ {
+ return invitesDisabledUntil == 0 ? null : Helpers.toOffset(invitesDisabledUntil);
+ }
+
+ /**
+ * The time until when direct messages are paused.
+ *
+ * @return The time until direct messages are paused, or null if unpaused
+ */
+ @Nullable
+ public OffsetDateTime getDirectMessagesDisabledUntil()
+ {
+ return directMessagesDisabledUntil == 0 ? null : Helpers.toOffset(directMessagesDisabledUntil);
+ }
+
+ /**
+ * Incidents state, which disables all active security incidents.
+ *
The resulting object is used with {@link Guild#modifySecurityIncidents(SecurityIncidents)} to update the active incidents of the guild.
+ *
+ * @return The new security incidents
+ */
+ @Nonnull
+ public static SecurityIncidents disabled()
+ {
+ return disabled;
+ }
+
+ /**
+ * Incidents state, which enables security incidents based on the provided deadlines.
+ *
The resulting object is used with {@link Guild#modifySecurityIncidents(SecurityIncidents)} to update the active incidents of the guild.
+ *
+ * @param invitesDisabledUntil
+ * The time until invites are paused
+ * @param directMessagesDisabledUntil
+ * The time until direct messages are paused
+ *
+ * @return The new security incidents
+ */
+ @Nonnull
+ public static SecurityIncidents enabled(
+ @Nullable
+ OffsetDateTime invitesDisabledUntil,
+ @Nullable
+ OffsetDateTime directMessagesDisabledUntil
+ ) {
+ return new SecurityIncidents(
+ invitesDisabledUntil == null ? 0 : invitesDisabledUntil.toInstant().toEpochMilli(),
+ directMessagesDisabledUntil == null ? 0 : directMessagesDisabledUntil.toInstant().toEpochMilli()
+ );
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(invitesDisabledUntil, directMessagesDisabledUntil);
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof SecurityIncidents))
+ return false;
+ SecurityIncidents other = (SecurityIncidents) obj;
+ return this.invitesDisabledUntil == other.invitesDisabledUntil && this.directMessagesDisabledUntil == other.directMessagesDisabledUntil;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/guild/update/GuildUpdateSecurityIncidentsEvent.java b/src/main/java/net/dv8tion/jda/api/events/guild/update/GuildUpdateSecurityIncidentsEvent.java
new file mode 100644
index 0000000000..506ac28dc2
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/guild/update/GuildUpdateSecurityIncidentsEvent.java
@@ -0,0 +1,63 @@
+/*
+ * 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.guild.update;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.SecurityIncidents;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Indicates that the {@link SecurityIncidents} of a {@link Guild Guild} changed.
+ *
+ *
Can be used to detect when a guild pauses or unpauses invites.
+ *
+ *
Identifier: {@code security_incidents}
+ */
+public class GuildUpdateSecurityIncidentsEvent extends GenericGuildUpdateEvent
+{
+ public static final String IDENTIFIER = "security_incidents";
+
+ public GuildUpdateSecurityIncidentsEvent(@Nonnull JDA api, long responseNumber, @Nonnull Guild guild, @Nullable SecurityIncidents previous)
+ {
+ super(api, responseNumber, guild, previous, guild.getSecurityIncidents(), IDENTIFIER);
+ }
+
+ /**
+ * The old security incidents, or null if disabled.
+ *
+ * @return The old incidents
+ */
+ @Nullable
+ public SecurityIncidents getOldSecurityIncidents()
+ {
+ return getOldValue();
+ }
+
+ /**
+ * The new security incidents, or null if disabled.
+ *
+ * @return The new incidents
+ */
+ @Nullable
+ public SecurityIncidents getNewSecurityIncidents()
+ {
+ return getNewValue();
+ }
+}
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 aa26bce27c..8adf3d87e2 100644
--- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java
+++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java
@@ -256,6 +256,7 @@ public void onGuildUpdateSystemChannel(@Nonnull GuildUpdateSystemChannelEvent ev
public void onGuildUpdateRulesChannel(@Nonnull GuildUpdateRulesChannelEvent event) {}
public void onGuildUpdateCommunityUpdatesChannel(@Nonnull GuildUpdateCommunityUpdatesChannelEvent event) {}
public void onGuildUpdateAfkTimeout(@Nonnull GuildUpdateAfkTimeoutEvent event) {}
+ public void onGuildUpdateSecurityIncidents(@Nonnull GuildUpdateSecurityIncidentsEvent event) {}
public void onGuildUpdateExplicitContentLevel(@Nonnull GuildUpdateExplicitContentLevelEvent event) {}
public void onGuildUpdateIcon(@Nonnull GuildUpdateIconEvent event) {}
public void onGuildUpdateMFALevel(@Nonnull GuildUpdateMFALevelEvent event) {}
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 e28e1ca5e1..7535807239 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/Route.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java
@@ -136,8 +136,9 @@ public static class Guilds
public static final Route DELETE_SCHEDULED_EVENT = new Route(DELETE, "guilds/{guild_id}/scheduled-events/{scheduled_event_id}");
public static final Route GET_SCHEDULED_EVENT_USERS = new Route(GET, "guilds/{guild_id}/scheduled-events/{scheduled_event_id}/users");
- public static final Route GET_WELCOME_SCREEN = new Route(GET, "guilds/{guild_id}/welcome-screen");
- public static final Route MODIFY_WELCOME_SCREEN = new Route(PATCH, "guilds/{guild_id}/welcome-screen");
+ public static final Route GET_WELCOME_SCREEN = new Route(GET, "guilds/{guild_id}/welcome-screen");
+ public static final Route MODIFY_WELCOME_SCREEN = new Route(PATCH, "guilds/{guild_id}/welcome-screen");
+ public static final Route MODIFY_GUILD_INCIDENTS = new Route(PUT, "guilds/{guild_id}/incident-actions");
public static final Route CREATE_GUILD = new Route(POST, "guilds");
public static final Route DELETE_GUILD = new Route(POST, "guilds/{guild_id}/delete");
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
index 09b06f2ad8..9b7116df59 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
@@ -248,6 +248,14 @@ private void createGuildStickerPass(GuildImpl guildObj, DataArray array)
}
}
+ public SecurityIncidents createSecurityIncidents(DataObject data) {
+ SecurityIncidents enabled = SecurityIncidents.enabled(
+ data.getOffsetDateTime("invites_disabled_until", null),
+ data.getOffsetDateTime("dms_disabled_until", null)
+ );
+ return enabled.equals(SecurityIncidents.disabled()) ? null : enabled;
+ }
+
public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap members, int memberCount)
{
final GuildImpl guildObj = new GuildImpl(getJDA(), guildId);
@@ -258,6 +266,7 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap<
final String vanityCode = guildJson.getString("vanity_url_code", null);
final String bannerId = guildJson.getString("banner", null);
final String locale = guildJson.getString("preferred_locale", "en-US");
+ final SecurityIncidents securityIncidents = guildJson.optObject("incidents_data").map(this::createSecurityIncidents).orElse(null);
final DataArray roleArray = guildJson.getArray("roles");
final DataArray channelArray = guildJson.getArray("channels");
final DataArray threadArray = guildJson.getArray("threads");
@@ -294,6 +303,7 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap<
.setMaxPresences(maxPresences)
.setOwnerId(ownerId)
.setAfkTimeout(Guild.Timeout.fromKey(afkTimeout))
+ .setSecurityIncidents(securityIncidents)
.setVerificationLevel(VerificationLevel.fromKey(verificationLevel))
.setDefaultNotificationLevel(Guild.NotificationLevel.fromKey(notificationLevel))
.setExplicitContentLevel(Guild.ExplicitContentLevel.fromKey(explicitContentLevel))
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 11ecc5bc7f..f91a62b5fc 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
@@ -134,6 +134,7 @@ public class GuildImpl implements Guild
private TextChannel rulesChannel;
private TextChannel communityUpdatesChannel;
private Role publicRole;
+ private SecurityIncidents securityIncidents;
private VerificationLevel verificationLevel = VerificationLevel.UNKNOWN;
private NotificationLevel defaultNotificationLevel = NotificationLevel.UNKNOWN;
private MFALevel mfaLevel = MFALevel.UNKNOWN;
@@ -778,6 +779,13 @@ public Timeout getAfkTimeout()
return afkTimeout;
}
+ @Nonnull
+ @Override
+ public SecurityIncidents getSecurityIncidents()
+ {
+ return securityIncidents;
+ }
+
@Override
public boolean isMember(@Nonnull UserSnowflake user)
{
@@ -1583,6 +1591,20 @@ public AuditableRestAction prune(int days, boolean wait, @Nonnull Role.
return new AuditableRestActionImpl<>(getJDA(), route, body, (response, request) -> response.getObject().getInt("pruned", 0));
}
+ @Nonnull
+ @Override
+ public AuditableRestAction modifySecurityIncidents(@Nonnull SecurityIncidents incidents)
+ {
+ Checks.notNull(incidents, "SecurityIncidents");
+ checkPermission(Permission.MANAGE_SERVER);
+
+ Route.CompiledRoute route = Route.Guilds.MODIFY_GUILD_INCIDENTS.compile(getId());
+ DataObject body = DataObject.empty()
+ .put("invites_disabled_until", Objects.toString(incidents.getInvitesDisabledUntil(), null))
+ .put("dms_disabled_until", Objects.toString(incidents.getDirectMessagesDisabledUntil(), null));
+ return new AuditableRestActionImpl<>(api, route, body);
+ }
+
@Nonnull
@Override
public AuditableRestAction kick(@Nonnull UserSnowflake user)
@@ -2230,6 +2252,12 @@ public GuildImpl setPublicRole(Role publicRole)
return this;
}
+ public GuildImpl setSecurityIncidents(SecurityIncidents incidents)
+ {
+ this.securityIncidents = incidents;
+ return this;
+ }
+
public GuildImpl setVerificationLevel(VerificationLevel level)
{
this.verificationLevel = level;
diff --git a/src/main/java/net/dv8tion/jda/internal/handle/GuildUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/GuildUpdateHandler.java
index 15bedb0e28..a8c0ae383a 100644
--- a/src/main/java/net/dv8tion/jda/internal/handle/GuildUpdateHandler.java
+++ b/src/main/java/net/dv8tion/jda/internal/handle/GuildUpdateHandler.java
@@ -17,6 +17,7 @@
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.SecurityIncidents;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.events.guild.update.*;
@@ -74,6 +75,7 @@ protected Long handleInternally(DataObject content)
String name = content.getString("name");
String iconId = content.getString("icon", null);
String splashId = content.getString("splash", null);
+ SecurityIncidents securityIncidents = content.optObject("incidents_data").map(api.getEntityBuilder()::createSecurityIncidents).orElse(null);
Guild.VerificationLevel verificationLevel = Guild.VerificationLevel.fromKey(content.getInt("verification_level"));
Guild.NotificationLevel notificationLevel = Guild.NotificationLevel.fromKey(content.getInt("default_message_notifications"));
Guild.MFALevel mfaLevel = Guild.MFALevel.fromKey(content.getInt("mfa_level"));
@@ -304,6 +306,15 @@ protected Long handleInternally(DataObject content)
getJDA(), responseNumber,
guild, oldCommunityUpdatesChannel));
}
+ if (!Objects.equals(securityIncidents, guild.getSecurityIncidents()))
+ {
+ SecurityIncidents oldIncidents = guild.getSecurityIncidents();
+ guild.setSecurityIncidents(securityIncidents);
+ api.handleEvent(
+ new GuildUpdateSecurityIncidentsEvent(
+ getJDA(), responseNumber,
+ guild, oldIncidents));
+ }
if (content.hasKey("nsfw_level") && nsfwLevel != guild.getNSFWLevel())
{
Guild.NSFWLevel oldNSFWLevel = guild.getNSFWLevel();