diff --git a/src/main/java/net/dv8tion/jda/api/entities/BulkBanResponse.java b/src/main/java/net/dv8tion/jda/api/entities/BulkBanResponse.java
new file mode 100644
index 0000000000..d165bace9b
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/entities/BulkBanResponse.java
@@ -0,0 +1,62 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Response to {@link Guild#ban(java.util.Collection, Duration)}
+ *
+ *
This response includes a list of successfully banned users and users which could not be banned.
+ * Discord might fail to ban a user due to permission issues or an internal server error.
+ */
+public class BulkBanResponse
+{
+ private final List bannedUsers;
+ private final List failedUsers;
+
+ public BulkBanResponse(@Nonnull List bannedUsers, @Nonnull List failedUsers)
+ {
+ this.bannedUsers = Collections.unmodifiableList(bannedUsers);
+ this.failedUsers = Collections.unmodifiableList(failedUsers);
+ }
+
+ /**
+ * List of successfully banned users.
+ *
+ * @return {@link List} of {@link UserSnowflake}
+ */
+ @Nonnull
+ public List getBannedUsers()
+ {
+ return bannedUsers;
+ }
+
+ /**
+ * List of users which could not be banned.
+ *
+ * @return {@link List} of {@link UserSnowflake}
+ */
+ @Nonnull
+ public List getFailedUsers()
+ {
+ return failedUsers;
+ }
+}
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 c409d0ab6e..2881b50295 100644
--- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java
+++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java
@@ -3629,8 +3629,8 @@ default AuditableRestAction kick(@Nonnull UserSnowflake user, @Nullable St
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
*
The target Member cannot be banned due to a permission discrepancy
*
- * {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MEMBER UNKNOWN_MEMBER}
- *
The specified Member was removed from the Guild before finishing the task
+ * {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_USER UNKNOWN_USER}
+ *
The user does not exist
*
*
* @param user
@@ -3661,6 +3661,90 @@ default AuditableRestAction kick(@Nonnull UserSnowflake user, @Nullable St
@CheckReturnValue
AuditableRestAction ban(@Nonnull UserSnowflake user, int deletionTimeframe, @Nonnull TimeUnit unit);
+ /**
+ * Bans up to 200 of the provided users.
+ *
To set a ban reason, use {@link AuditableRestAction#reason(String)}.
+ *
+ * The {@link BulkBanResponse} includes a list of {@link BulkBanResponse#getFailedUsers() failed users},
+ * which is populated with users that could not be banned, for instance due to some internal server error or permission issues.
+ * This list of failed users also includes all users that were already banned.
+ *
+ *
Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by
+ * the returned {@link RestAction RestAction} include the following:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
+ *
The target Member cannot be banned due to a permission discrepancy
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#FAILED_TO_BAN_USERS FAILED_TO_BAN_USERS}
+ *
None of the users could be banned
+ *
+ *
+ * @param users
+ * The users to ban
+ * @param deletionTime
+ * Delete recent messages of the given timeframe (for instance the last hour with {@code Duration.ofHours(1)})
+ *
+ * @throws net.dv8tion.jda.api.exceptions.HierarchyException
+ * If any of the provided users is the guild owner or has a higher or equal role position
+ * @throws InsufficientPermissionException
+ * If the bot does not have {@link Permission#BAN_MEMBERS} or {@link Permission#MANAGE_SERVER}
+ * @throws IllegalArgumentException
+ *
+ * - If the users collection is null or contains null
+ * - If the deletionTime is negative
+ *
+ *
+ * @return {@link AuditableRestAction} - Type: {@link BulkBanResponse}
+ */
+ @Nonnull
+ @CheckReturnValue
+ AuditableRestAction ban(@Nonnull Collection users, @Nullable Duration deletionTime);
+
+ /**
+ * Bans up to 200 of the provided users.
+ *
To set a ban reason, use {@link AuditableRestAction#reason(String)}.
+ *
+ * The {@link BulkBanResponse} includes a list of {@link BulkBanResponse#getFailedUsers() failed users},
+ * which is populated with users that could not be banned, for instance due to some internal server error or permission issues.
+ * This list of failed users also includes all users that were already banned.
+ *
+ *
Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by
+ * the returned {@link RestAction RestAction} include the following:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
+ *
The target Member cannot be banned due to a permission discrepancy
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#FAILED_TO_BAN_USERS FAILED_TO_BAN_USERS}
+ *
None of the users could be banned
+ *
+ *
+ * @param users
+ * The users to ban
+ * @param deletionTimeframe
+ * The timeframe for the history of messages that will be deleted. (seconds precision)
+ * @param unit
+ * Timeframe unit as a {@link TimeUnit} (for example {@code ban(user, 7, TimeUnit.DAYS)}).
+ *
+ * @throws net.dv8tion.jda.api.exceptions.HierarchyException
+ * If any of the provided users is the guild owner or has a higher or equal role position
+ * @throws InsufficientPermissionException
+ * If the bot does not have {@link Permission#BAN_MEMBERS} or {@link Permission#MANAGE_SERVER}
+ * @throws IllegalArgumentException
+ *
+ * - If null is provided
+ * - If the deletionTimeframe is negative
+ *
+ *
+ * @return {@link AuditableRestAction} - Type: {@link BulkBanResponse}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default AuditableRestAction ban(@Nonnull Collection users, int deletionTimeframe, @Nonnull TimeUnit unit)
+ {
+ Checks.notNull(unit, "TimeUnit");
+ return ban(users, Duration.ofSeconds(unit.toSeconds(deletionTimeframe)));
+ }
+
/**
* Unbans the specified {@link UserSnowflake} from this Guild.
*
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 f59c3a5c65..450f4f9844 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java
@@ -184,6 +184,7 @@ public enum ErrorResponse
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"),
+ FAILED_TO_BAN_USERS( 500000, "Failed to ban users"),
SERVER_ERROR( 0, "Discord encountered an internal server error! Not good!");
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 f15462924c..d454255048 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/Route.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java
@@ -103,6 +103,7 @@ public static class Guilds
public static final Route GET_BAN = new Route(GET, "guilds/{guild_id}/bans/{user_id}");
public static final Route UNBAN = new Route(DELETE, "guilds/{guild_id}/bans/{user_id}");
public static final Route BAN = new Route(PUT, "guilds/{guild_id}/bans/{user_id}");
+ public static final Route BULK_BAN = new Route(POST, "guilds/{guild_id}/bulk-ban");
public static final Route KICK_MEMBER = new Route(DELETE, "guilds/{guild_id}/members/{user_id}");
public static final Route MODIFY_MEMBER = new Route(PATCH, "guilds/{guild_id}/members/{user_id}");
public static final Route ADD_MEMBER = new Route(PUT, "guilds/{guild_id}/members/{user_id}");
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 c13416bf14..e0dedbf78b 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
@@ -82,10 +82,12 @@
import net.dv8tion.jda.internal.utils.concurrent.task.GatewayTask;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
+import org.jetbrains.annotations.NotNull;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.*;
@@ -1545,6 +1547,44 @@ public AuditableRestAction ban(@Nonnull UserSnowflake user, int duration,
return new AuditableRestActionImpl<>(getJDA(), route, params);
}
+ @Nonnull
+ @Override
+ public AuditableRestAction ban(@Nonnull Collection users, @Nullable Duration deletionTime)
+ {
+ deletionTime = deletionTime == null ? Duration.ZERO : deletionTime;
+ Checks.noneNull(users, "Users");
+ Checks.check(!deletionTime.isNegative(), "Deletion time cannot be negative");
+ Checks.check(deletionTime.getSeconds() <= TimeUnit.DAYS.toSeconds(7), "Deletion timeframe must not be larger than 7 days. Provided: %d seconds", deletionTime.getSeconds());
+ Checks.check(users.size() <= 200, "Cannot ban more than 200 users at once");
+ checkPermission(Permission.BAN_MEMBERS);
+ checkPermission(Permission.MANAGE_SERVER);
+
+ for (UserSnowflake user : users)
+ {
+ checkOwner(user.getIdLong(), "ban");
+ checkPosition(user);
+ }
+
+ Set userIds = users.stream().map(UserSnowflake::getIdLong).collect(Collectors.toSet());
+ DataObject body = DataObject.empty()
+ .put("user_ids", userIds)
+ .put("delete_message_seconds", deletionTime.getSeconds());
+ Route.CompiledRoute route = Route.Guilds.BULK_BAN.compile(getId());
+
+ return new AuditableRestActionImpl<>(getJDA(), route, body, (res, req) -> {
+ DataObject responseBody = res.getObject();
+ List bannedUsers = responseBody.getArray("banned_users")
+ .stream(DataArray::getLong)
+ .map(UserSnowflake::fromId)
+ .collect(Collectors.toList());
+ List failedUsers = responseBody.getArray("failed_users")
+ .stream(DataArray::getLong)
+ .map(UserSnowflake::fromId)
+ .collect(Collectors.toList());
+ return new BulkBanResponse(bannedUsers, failedUsers);
+ });
+ }
+
@Nonnull
@Override
public AuditableRestAction unban(@Nonnull UserSnowflake user)