feat: GuildBanManager (#5276)

Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com>
Co-authored-by: Jan <66554238+vaporox@users.noreply.github.com>
Co-authored-by: izexi <43889168+izexi@users.noreply.github.com>
Co-authored-by: Shubham Parihar <shubhamparihar391@gmail.com>
This commit is contained in:
MBR-0001
2021-05-10 12:35:25 +02:00
committed by GitHub
parent 4a06dd1295
commit 6d09160f5b
11 changed files with 320 additions and 88 deletions

View File

@@ -32,6 +32,7 @@ export const {
BaseGuildEmojiManager, BaseGuildEmojiManager,
ChannelManager, ChannelManager,
GuildApplicationCommandManager, GuildApplicationCommandManager,
GuildBanManager,
GuildChannelManager, GuildChannelManager,
GuildEmojiManager, GuildEmojiManager,
GuildEmojiRoleManager, GuildEmojiRoleManager,
@@ -67,6 +68,7 @@ export const {
Emoji, Emoji,
Guild, Guild,
GuildAuditLogs, GuildAuditLogs,
GuildBan,
GuildChannel, GuildChannel,
GuildEmoji, GuildEmoji,
GuildMember, GuildMember,

View File

@@ -21,6 +21,7 @@ class ActionsManager {
this.register(require('./InviteDelete')); this.register(require('./InviteDelete'));
this.register(require('./GuildMemberRemove')); this.register(require('./GuildMemberRemove'));
this.register(require('./GuildMemberUpdate')); this.register(require('./GuildMemberUpdate'));
this.register(require('./GuildBanAdd'));
this.register(require('./GuildBanRemove')); this.register(require('./GuildBanRemove'));
this.register(require('./GuildRoleCreate')); this.register(require('./GuildRoleCreate'));
this.register(require('./GuildRoleDelete')); this.register(require('./GuildRoleDelete'));

View File

@@ -0,0 +1,20 @@
'use strict';
const Action = require('./Action');
const { Events } = require('../../util/Constants');
class GuildBanAdd extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.cache.get(data.guild_id);
/**
* Emitted whenever a member is banned from a guild.
* @event Client#guildBanAdd
* @param {GuildBan} ban The ban that occurred
*/
if (guild) client.emit(Events.GUILD_BAN_ADD, guild.bans.add(data));
}
}
module.exports = GuildBanAdd;

View File

@@ -1,20 +1,24 @@
'use strict'; 'use strict';
const Action = require('./Action'); const Action = require('./Action');
const GuildBan = require('../../structures/GuildBan');
const { Events } = require('../../util/Constants'); const { Events } = require('../../util/Constants');
class GuildBanRemove extends Action { class GuildBanRemove extends Action {
handle(data) { handle(data) {
const client = this.client; const client = this.client;
const guild = client.guilds.cache.get(data.guild_id); const guild = client.guilds.cache.get(data.guild_id);
const user = client.users.add(data.user);
/** /**
* Emitted whenever a member is unbanned from a guild. * Emitted whenever a member is unbanned from a guild.
* @event Client#guildBanRemove * @event Client#guildBanRemove
* @param {Guild} guild The guild that the unban occurred in * @param {GuildBan} ban The ban that was removed
* @param {User} user The user that was unbanned
*/ */
if (guild && user) client.emit(Events.GUILD_BAN_REMOVE, guild, user); if (guild) {
const ban = guild.bans.cache.get(data.user.id) ?? new GuildBan(client, data, guild);
guild.bans.cache.delete(ban.user.id);
client.emit(Events.GUILD_BAN_REMOVE, ban);
}
} }
} }

View File

@@ -1,16 +1,5 @@
'use strict'; 'use strict';
const { Events } = require('../../../util/Constants'); module.exports = (client, packet) => {
client.actions.GuildBanAdd.handle(packet.d);
module.exports = (client, { d: data }) => {
const guild = client.guilds.cache.get(data.guild_id);
const user = client.users.add(data.user);
/**
* Emitted whenever a member is banned from a guild.
* @event Client#guildBanAdd
* @param {Guild} guild The guild that the ban occurred in
* @param {User} user The user that was banned
*/
if (guild && user) client.emit(Events.GUILD_BAN_ADD, guild, user);
}; };

View File

@@ -37,6 +37,7 @@ module.exports = {
BaseGuildEmojiManager: require('./managers/BaseGuildEmojiManager'), BaseGuildEmojiManager: require('./managers/BaseGuildEmojiManager'),
ChannelManager: require('./managers/ChannelManager'), ChannelManager: require('./managers/ChannelManager'),
GuildApplicationCommandManager: require('./managers/GuildApplicationCommandManager'), GuildApplicationCommandManager: require('./managers/GuildApplicationCommandManager'),
GuildBanManager: require('./managers/GuildBanManager'),
GuildChannelManager: require('./managers/GuildChannelManager'), GuildChannelManager: require('./managers/GuildChannelManager'),
GuildEmojiManager: require('./managers/GuildEmojiManager'), GuildEmojiManager: require('./managers/GuildEmojiManager'),
GuildEmojiRoleManager: require('./managers/GuildEmojiRoleManager'), GuildEmojiRoleManager: require('./managers/GuildEmojiRoleManager'),
@@ -79,6 +80,7 @@ module.exports = {
Emoji: require('./structures/Emoji'), Emoji: require('./structures/Emoji'),
Guild: require('./structures/Guild'), Guild: require('./structures/Guild'),
GuildAuditLogs: require('./structures/GuildAuditLogs'), GuildAuditLogs: require('./structures/GuildAuditLogs'),
GuildBan: require('./structures/GuildBan'),
GuildChannel: require('./structures/GuildChannel'), GuildChannel: require('./structures/GuildChannel'),
GuildEmoji: require('./structures/GuildEmoji'), GuildEmoji: require('./structures/GuildEmoji'),
GuildMember: require('./structures/GuildMember'), GuildMember: require('./structures/GuildMember'),

View File

@@ -0,0 +1,177 @@
'use strict';
const BaseManager = require('./BaseManager');
const GuildBan = require('../structures/GuildBan');
const GuildMember = require('../structures/GuildMember');
const Collection = require('../util/Collection');
/**
* Manages API methods for GuildBans and stores their cache.
* @extends {BaseManager}
*/
class GuildBanManager extends BaseManager {
constructor(guild, iterable) {
super(guild.client, iterable, GuildBan);
/**
* The guild this Manager belongs to
* @type {Guild}
*/
this.guild = guild;
}
/**
* The cache of this Manager
* @type {Collection<Snowflake, GuildBan>}
* @name GuildBanManager#cache
*/
add(data, cache) {
return super.add(data, cache, { id: data.user.id, extras: [this.guild] });
}
/**
* Data that resolves to give a GuildBan object. This can be:
* * A GuildBan object
* * A User resolvable
* @typedef {GuildBan|UserResolvable} GuildBanResolvable
*/
/**
* Resolves a GuildBanResolvable to a GuildBan object.
* @param {GuildBanResolvable} ban The ban that is in the guild
* @returns {?GuildBan}
*/
resolve(ban) {
return super.resolve(ban) ?? super.resolve(this.client.users.resolveID(ban));
}
/**
* Options used to fetch a single ban from a guild.
* @typedef {Object} FetchBanOptions
* @property {UserResolvable} user The ban to fetch
* @property {boolean} [cache=true] Whether or not to cache the fetched ban
* @property {boolean} [force=false] Whether to skip the cache check and request the API
*/
/**
* Options used to fetch all bans from a guild.
* @typedef {Object} FetchBansOptions
* @property {boolean} cache Whether or not to cache the fetched bans
*/
/**
* Fetches ban(s) from Discord.
* @param {UserResolvable|FetchBanOptions|FetchBansOptions} [options] Options for fetching guild ban(s)
* @returns {Promise<GuildBan>|Promise<Collection<Snowflake, GuildBan>>}
* @example
* // Fetch all bans from a guild
* guild.bans.fetch()
* .then(console.log)
* .catch(console.error);
* @example
* // Fetch all bans from a guild without caching
* guild.bans.fetch({ cache: false })
* .then(console.log)
* .catch(console.error);
* @example
* // Fetch a single ban
* guild.bans.fetch('351871113346809860')
* .then(console.log)
* .catch(console.error);
* @example
* // Fetch a single ban without checking cache
* guild.bans.fetch({ user, force: true })
* .then(console.log)
* .catch(console.error)
* @example
* // Fetch a single ban without caching
* guild.bans.fetch({ user, cache: false })
* .then(console.log)
* .catch(console.error);
*/
fetch(options) {
if (!options) return this._fetchMany();
const user = this.client.users.resolveID(options);
if (user) return this._fetchSingle({ user, cache: true });
if (options.user) {
options.user = this.client.users.resolveID(options.user);
}
if (!options.user) {
if ('cache' in options) return this._fetchMany(options.cache);
return Promise.reject(new Error('FETCH_BAN_RESOLVE_ID'));
}
return this._fetchSingle(options);
}
async _fetchSingle({ user, cache, force = false }) {
if (!force) {
const existing = this.cache.get(user);
if (existing && !existing.partial) return existing;
}
const data = await this.client.api.guilds(this.guild.id).bans(user).get();
return this.add(data, cache);
}
async _fetchMany(cache) {
const data = await this.client.api.guilds(this.guild.id).bans.get();
return data.reduce((col, ban) => col.set(ban.user.id, this.add(ban, cache)), new Collection());
}
/**
* Bans a user from the guild.
* @param {UserResolvable} user The user to ban
* @param {Object} [options] Options for the ban
* @param {number} [options.days=0] Number of days of messages to delete, must be between 0 and 7, inclusive
* @param {string} [options.reason] Reason for banning
* @returns {Promise<GuildMember|User|Snowflake>} Result object will be resolved as specifically as possible.
* If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot
* be resolved, the user ID will be the result.
* @example
* // Ban a user by ID (or with a user/guild member object)
* guild.bans.create('84484653687267328')
* .then(user => console.log(`Banned ${user.username ?? user.id ?? user} from ${guild.name}`))
* .catch(console.error);
*/
async create(user, options = { days: 0 }) {
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
const id = this.client.users.resolveID(user);
if (!id) throw new Error('BAN_RESOLVE_ID', true);
await this.client.api
.guilds(this.guild.id)
.bans(id)
.put({
data: {
reason: options.reason,
delete_message_days: options.days,
},
});
if (user instanceof GuildMember) return user;
const _user = this.client.users.resolve(id);
if (_user) {
return this.guild.members.resolve(_user) ?? _user;
}
return id;
}
/**
* Unbans a user from the guild.
* @param {UserResolvable} user The user to unban
* @param {string} [reason] Reason for unbanning user
* @returns {Promise<User>}
* @example
* // Unban a user by ID (or with a user/guild member object)
* guild.bans.remove('84484653687267328')
* .then(user => console.log(`Unbanned ${user.username} from ${guild.name}`))
* .catch(console.error);
*/
async remove(user, reason) {
const id = this.client.users.resolveID(user);
if (!id) throw new Error('BAN_RESOLVE_ID');
await this.client.api.guilds(this.guild.id).bans(id).delete({ reason });
return this.client.users.resolve(user);
}
}
module.exports = GuildBanManager;

View File

@@ -216,29 +216,15 @@ class GuildMemberManager extends BaseManager {
* @returns {Promise<GuildMember|User|Snowflake>} Result object will be resolved as specifically as possible. * @returns {Promise<GuildMember|User|Snowflake>} Result object will be resolved as specifically as possible.
* If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot
* be resolved, the user ID will be the result. * be resolved, the user ID will be the result.
* Internally calls the GuildBanManager#create method.
* @example * @example
* // Ban a user by ID (or with a user/guild member object) * // Ban a user by ID (or with a user/guild member object)
* guild.members.ban('84484653687267328') * guild.members.ban('84484653687267328')
* .then(user => console.log(`Banned ${user.username || user.id || user} from ${guild.name}`)) * .then(user => console.log(`Banned ${user.username ?? user.id ?? user} from ${guild.name}`))
* .catch(console.error); * .catch(console.error);
*/ */
ban(user, options = { days: 0 }) { ban(user, options = { days: 0 }) {
if (typeof options !== 'object') return Promise.reject(new TypeError('INVALID_TYPE', 'options', 'object', true)); return this.guild.bans.create(user, options);
if (options.days) options.delete_message_days = options.days;
const id = this.client.users.resolveID(user);
if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID', true));
return this.client.api
.guilds(this.guild.id)
.bans[id].put({ data: options })
.then(() => {
if (user instanceof GuildMember) return user;
const _user = this.client.users.resolve(id);
if (_user) {
const member = this.resolve(_user);
return member || _user;
}
return id;
});
} }
/** /**
@@ -246,6 +232,7 @@ class GuildMemberManager extends BaseManager {
* @param {UserResolvable} user The user to unban * @param {UserResolvable} user The user to unban
* @param {string} [reason] Reason for unbanning user * @param {string} [reason] Reason for unbanning user
* @returns {Promise<User>} * @returns {Promise<User>}
* Internally calls the GuildBanManager#remove method.
* @example * @example
* // Unban a user by ID (or with a user/guild member object) * // Unban a user by ID (or with a user/guild member object)
* guild.members.unban('84484653687267328') * guild.members.unban('84484653687267328')
@@ -253,12 +240,7 @@ class GuildMemberManager extends BaseManager {
* .catch(console.error); * .catch(console.error);
*/ */
unban(user, reason) { unban(user, reason) {
const id = this.client.users.resolveID(user); return this.guild.bans.remove(user, reason);
if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID'));
return this.client.api
.guilds(this.guild.id)
.bans[id].delete({ reason })
.then(() => this.client.users.resolve(user));
} }
_fetchSingle({ user, cache, force = false }) { _fetchSingle({ user, cache, force = false }) {

View File

@@ -10,6 +10,7 @@ const VoiceRegion = require('./VoiceRegion');
const Webhook = require('./Webhook'); const Webhook = require('./Webhook');
const { Error, TypeError } = require('../errors'); const { Error, TypeError } = require('../errors');
const GuildApplicationCommandManager = require('../managers/GuildApplicationCommandManager'); const GuildApplicationCommandManager = require('../managers/GuildApplicationCommandManager');
const GuildBanManager = require('../managers/GuildBanManager');
const GuildChannelManager = require('../managers/GuildChannelManager'); const GuildChannelManager = require('../managers/GuildChannelManager');
const GuildEmojiManager = require('../managers/GuildEmojiManager'); const GuildEmojiManager = require('../managers/GuildEmojiManager');
const GuildMemberManager = require('../managers/GuildMemberManager'); const GuildMemberManager = require('../managers/GuildMemberManager');
@@ -61,6 +62,12 @@ class Guild extends Base {
*/ */
this.channels = new GuildChannelManager(this); this.channels = new GuildChannelManager(this);
/**
* A manager of the bans belonging to this guild
* @type {GuildBanManager}
*/
this.bans = new GuildBanManager(this);
/** /**
* A manager of the roles belonging to this guild * A manager of the roles belonging to this guild
* @type {RoleManager} * @type {RoleManager}
@@ -632,50 +639,6 @@ class Guild extends Base {
}); });
} }
/**
* An object containing information about a guild member's ban.
* @typedef {Object} BanInfo
* @property {User} user User that was banned
* @property {?string} reason Reason the user was banned
*/
/**
* Fetches information on a banned user from this guild.
* @param {UserResolvable} user The User to fetch the ban info of
* @returns {Promise<BanInfo>}
*/
fetchBan(user) {
const id = this.client.users.resolveID(user);
if (!id) throw new Error('FETCH_BAN_RESOLVE_ID');
return this.client.api
.guilds(this.id)
.bans(id)
.get()
.then(ban => ({
reason: ban.reason,
user: this.client.users.add(ban.user),
}));
}
/**
* Fetches a collection of banned users in this guild.
* @returns {Promise<Collection<Snowflake, BanInfo>>}
*/
fetchBans() {
return this.client.api
.guilds(this.id)
.bans.get()
.then(bans =>
bans.reduce((collection, ban) => {
collection.set(ban.user.id, {
reason: ban.reason,
user: this.client.users.add(ban.user),
});
return collection;
}, new Collection()),
);
}
/** /**
* Fetches a collection of integrations to this guild. * Fetches a collection of integrations to this guild.
* Resolves with a collection mapping integrations by their ids. * Resolves with a collection mapping integrations by their ids.

View File

@@ -0,0 +1,63 @@
'use strict';
const Base = require('./Base');
/**
* Represents a ban in a guild on Discord.
* @extends {Base}
*/
class GuildBan extends Base {
/**
* @param {Client} client The instantiating client
* @param {Object} data The data for the ban
* @param {Guild} guild The guild in which the ban is
*/
constructor(client, data, guild) {
super(client);
/**
* The guild in which the ban is
* @type {Guild}
*/
this.guild = guild;
this._patch(data);
}
_patch(data) {
/**
* The user this ban applies to
* @type {User}
*/
this.user = this.client.users.add(data.user, true);
if ('reason' in data) {
/**
* The reason for the ban
* @type {?string}
*/
this.reason = data.reason;
}
}
/**
* Whether this GuildBan is a partial
* If the reason is not provided the value is null
* @type {boolean}
* @readonly
*/
get partial() {
return !('reason' in this);
}
/**
* Fetches this GuildBan.
* @param {boolean} [force=false] Whether to skip the cache check and request the API
* @returns {Promise<GuildBan>}
*/
fetch(force = false) {
return this.guild.bans.fetch({ user: this.user, cache: true, force });
}
}
module.exports = GuildBan;

37
typings/index.d.ts vendored
View File

@@ -696,6 +696,7 @@ declare module 'discord.js' {
public approximatePresenceCount: number | null; public approximatePresenceCount: number | null;
public available: boolean; public available: boolean;
public banner: string | null; public banner: string | null;
public bans: GuildBanManager;
public channels: GuildChannelManager; public channels: GuildChannelManager;
public commands: GuildApplicationCommandManager; public commands: GuildApplicationCommandManager;
public readonly createdAt: Date; public readonly createdAt: Date;
@@ -757,8 +758,6 @@ declare module 'discord.js' {
public equals(guild: Guild): boolean; public equals(guild: Guild): boolean;
public fetch(): Promise<Guild>; public fetch(): Promise<Guild>;
public fetchAuditLogs(options?: GuildAuditLogsFetchOptions): Promise<GuildAuditLogs>; public fetchAuditLogs(options?: GuildAuditLogsFetchOptions): Promise<GuildAuditLogs>;
public fetchBan(user: UserResolvable): Promise<{ user: User; reason: string }>;
public fetchBans(): Promise<Collection<Snowflake, { user: User; reason: string }>>;
public fetchIntegrations(): Promise<Collection<string, Integration>>; public fetchIntegrations(): Promise<Collection<string, Integration>>;
public fetchInvites(): Promise<Collection<string, Invite>>; public fetchInvites(): Promise<Collection<string, Invite>>;
public fetchOwner(options?: FetchOwnerOptions): Promise<GuildMember>; public fetchOwner(options?: FetchOwnerOptions): Promise<GuildMember>;
@@ -844,6 +843,15 @@ declare module 'discord.js' {
public toJSON(): object; public toJSON(): object;
} }
export class GuildBan extends Base {
constructor(client: Client, data: object, guild: Guild);
public guild: Guild;
public user: User;
public readonly partial: boolean;
public reason?: string | null;
public fetch(force?: boolean): Promise<GuildBan>;
}
export class GuildChannel extends Channel { export class GuildChannel extends Channel {
constructor(guild: Guild, data?: object); constructor(guild: Guild, data?: object);
private memberPermissions(member: GuildMember): Readonly<Permissions>; private memberPermissions(member: GuildMember): Readonly<Permissions>;
@@ -2158,6 +2166,15 @@ declare module 'discord.js' {
public unban(user: UserResolvable, reason?: string): Promise<User>; public unban(user: UserResolvable, reason?: string): Promise<User>;
} }
export class GuildBanManager extends BaseManager<Snowflake, GuildBan, GuildBanResolvable> {
constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild;
public create(user: UserResolvable, options?: BanOptions): Promise<GuildMember | User | Snowflake>;
public fetch(options: UserResolvable | FetchBanOptions): Promise<GuildBan>;
public fetch(options?: FetchBansOptions): Promise<Collection<Snowflake, GuildBan>>;
public remove(user: UserResolvable, reason?: string): Promise<User>;
}
export class GuildMemberRoleManager { export class GuildMemberRoleManager {
constructor(member: GuildMember); constructor(member: GuildMember);
public readonly cache: Collection<Snowflake, Role>; public readonly cache: Collection<Snowflake, Role>;
@@ -2560,8 +2577,8 @@ declare module 'discord.js' {
emojiDelete: [emoji: GuildEmoji]; emojiDelete: [emoji: GuildEmoji];
emojiUpdate: [oldEmoji: GuildEmoji, newEmoji: GuildEmoji]; emojiUpdate: [oldEmoji: GuildEmoji, newEmoji: GuildEmoji];
error: [error: Error]; error: [error: Error];
guildBanAdd: [guild: Guild, user: User]; guildBanAdd: [ban: GuildBan];
guildBanRemove: [guild: Guild, user: User]; guildBanRemove: [ban: GuildBan];
guildCreate: [guild: Guild]; guildCreate: [guild: Guild];
guildDelete: [guild: Guild]; guildDelete: [guild: Guild];
guildUnavailable: [guild: Guild]; guildUnavailable: [guild: Guild];
@@ -2776,6 +2793,16 @@ declare module 'discord.js' {
CommandInteraction: typeof CommandInteraction; CommandInteraction: typeof CommandInteraction;
} }
interface FetchBanOptions {
user: UserResolvable;
cache?: boolean;
force?: boolean;
}
interface FetchBansOptions {
cache: boolean;
}
interface FetchMemberOptions { interface FetchMemberOptions {
user: UserResolvable; user: UserResolvable;
cache?: boolean; cache?: boolean;
@@ -2870,6 +2897,8 @@ declare module 'discord.js' {
UNKNOWN?: string; UNKNOWN?: string;
} }
type GuildBanResolvable = GuildBan | UserResolvable;
type GuildChannelResolvable = Snowflake | GuildChannel; type GuildChannelResolvable = Snowflake | GuildChannel;
interface GuildCreateChannelOptions { interface GuildCreateChannelOptions {