diff --git a/packages/discord.js/src/errors/ErrorCodes.js b/packages/discord.js/src/errors/ErrorCodes.js index 5640b1165..244d78d99 100644 --- a/packages/discord.js/src/errors/ErrorCodes.js +++ b/packages/discord.js/src/errors/ErrorCodes.js @@ -176,6 +176,8 @@ * @property {'SweepFilterReturn'} SweepFilterReturn * @property {'EntitlementCreateInvalidOwner'} EntitlementCreateInvalidOwner + + * @property {'BulkBanUsersOptionEmpty'} BulkBanUsersOptionEmpty */ const keys = [ @@ -329,6 +331,8 @@ const keys = [ 'GuildForumMessageRequired', 'EntitlementCreateInvalidOwner', + + 'BulkBanUsersOptionEmpty', ]; // JSDoc for IntelliSense purposes diff --git a/packages/discord.js/src/errors/Messages.js b/packages/discord.js/src/errors/Messages.js index c7927328c..4f0bfa641 100644 --- a/packages/discord.js/src/errors/Messages.js +++ b/packages/discord.js/src/errors/Messages.js @@ -169,6 +169,8 @@ const Messages = { [DjsErrorCodes.EntitlementCreateInvalidOwner]: 'You must provide either a guild or a user to create an entitlement, but not both', + + [DjsErrorCodes.BulkBanUsersOptionEmpty]: 'Option "users" array or collection is empty', }; module.exports = Messages; diff --git a/packages/discord.js/src/managers/GuildBanManager.js b/packages/discord.js/src/managers/GuildBanManager.js index d3c8a0033..5bcbd368f 100644 --- a/packages/discord.js/src/managers/GuildBanManager.js +++ b/packages/discord.js/src/managers/GuildBanManager.js @@ -199,6 +199,51 @@ class GuildBanManager extends CachedManager { await this.client.rest.delete(Routes.guildBan(this.guild.id, id), { reason }); return this.client.users.resolve(user); } + + /** + * Options used for bulk banning users from a guild. + * @typedef {Object} BulkBanOptions + * @property {number} [deleteMessageSeconds] Number of seconds of messages to delete, + * must be between 0 and 604800 (7 days), inclusive + * @property {string} [reason] The reason for the bans + */ + + /** + * Result of bulk banning users from a guild. + * @typedef {Object} BulkBanResult + * @property {Snowflake[]} bannedUsers IDs of the banned users + * @property {Snowflake[]} failedUsers IDs of the users that could not be banned or were already banned + */ + + /** + * Bulk ban users from a guild, and optionally delete previous messages sent by them. + * @param {Collection|UserResolvable[]} users The users to ban + * @param {BulkBanOptions} [options] The options for bulk banning users + * @returns {Promise} Returns an object with `bannedUsers` key containing the IDs of the banned users + * and the key `failedUsers` with the IDs that could not be banned or were already banned. + * @example + * // Bulk ban users by ids (or with user/guild member objects) and delete all their messages from the past 7 days + * guild.bans.bulkCreate(['84484653687267328'], { deleteMessageSeconds: 7 * 24 * 60 * 60 }) + * .then(result => { + * console.log(`Banned ${result.bannedUsers.length} users, failed to ban ${result.failedUsers.length} users.`) + * }) + * .catch(console.error); + */ + async bulkCreate(users, options = {}) { + if (!users || !(Array.isArray(users) || users instanceof Collection)) { + throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'users', 'Array or Collection of UserResolvable', true); + } + if (typeof options !== 'object') throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'options', 'object', true); + + const userIds = users.map(user => this.client.users.resolveId(user)); + if (userIds.length === 0) throw new DiscordjsError(ErrorCodes.BulkBanUsersOptionEmpty); + + const result = await this.client.rest.post(Routes.guildBulkBan(this.guild.id), { + body: { delete_message_seconds: options.deleteMessageSeconds, user_ids: userIds }, + reason: options.reason, + }); + return { bannedUsers: result.banned_users, failedUsers: result.failed_users }; + } } module.exports = GuildBanManager; diff --git a/packages/discord.js/src/managers/GuildMemberManager.js b/packages/discord.js/src/managers/GuildMemberManager.js index dab62e6c1..76443f134 100644 --- a/packages/discord.js/src/managers/GuildMemberManager.js +++ b/packages/discord.js/src/managers/GuildMemberManager.js @@ -500,6 +500,25 @@ class GuildMemberManager extends CachedManager { return this.guild.bans.remove(user, reason); } + /** + * Bulk ban users from a guild, and optionally delete previous messages sent by them. + * @param {Collection|UserResolvable[]} users The users to ban + * @param {BulkBanOptions} [options] The options for bulk banning users + * @returns {Promise} Returns an object with `bannedUsers` key containing the IDs of the banned users + * and the key `failedUsers` with the IDs that could not be banned or were already banned. + * Internally calls the GuildBanManager#bulkCreate method. + * @example + * // Bulk ban users by ids (or with user/guild member objects) and delete all their messages from the past 7 days + * guild.members.bulkBan(['84484653687267328'], { deleteMessageSeconds: 7 * 24 * 60 * 60 }) + * .then(result => { + * console.log(`Banned ${result.bannedUsers.length} users, failed to ban ${result.failedUsers.length} users.`) + * }) + * .catch(console.error); + */ + bulkBan(users, options = {}) { + return this.guild.bans.bulkCreate(users, options); + } + /** * Options used for adding or removing a role from a member. * @typedef {Object} AddOrRemoveGuildMemberRoleOptions diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 6d1051691..310a8bd09 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -3926,6 +3926,8 @@ export enum DiscordjsErrorCodes { GuildForumMessageRequired = 'GuildForumMessageRequired', EntitlementCreateInvalidOwner = 'EntitlementCreateInvalidOwner', + + BulkBanUsersOptionEmpty = 'BulkBanUsersOptionEmpty', } export class DiscordjsError extends Error { @@ -4249,6 +4251,10 @@ export class GuildMemberManager extends CachedManager; public add(user: UserResolvable, options: AddGuildMemberOptions): Promise; public ban(user: UserResolvable, options?: BanOptions): Promise; + public bulkBan( + users: ReadonlyCollection | readonly UserResolvable[], + options?: BulkBanOptions, + ): Promise; public edit(user: UserResolvable, options: GuildMemberEditOptions): Promise; public fetch( options: UserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: UserResolvable }), @@ -4272,6 +4278,10 @@ export class GuildBanManager extends CachedManager; public fetch(options?: FetchBansOptions): Promise>; public remove(user: UserResolvable, reason?: string): Promise; + public bulkCreate( + users: ReadonlyCollection | readonly UserResolvable[], + options?: BulkBanOptions, + ): Promise; } export class GuildInviteManager extends DataManager { @@ -4959,6 +4969,13 @@ export interface BanOptions { reason?: string; } +export interface BulkBanOptions extends Omit {} + +export interface BulkBanResult { + bannedUsers: readonly Snowflake[]; + failedUsers: readonly Snowflake[]; +} + export type Base64Resolvable = Buffer | Base64String; export type Base64String = string;