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: + *

    + * + * @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 + * + * + * @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)