Skip to content

Commit

Permalink
feat: GuildBanManager (#5276)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
5 people committed May 10, 2021
1 parent 4a06dd1 commit 6d09160
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 88 deletions.
2 changes: 2 additions & 0 deletions esm/discord.mjs
Expand Up @@ -32,6 +32,7 @@ export const {
BaseGuildEmojiManager,
ChannelManager,
GuildApplicationCommandManager,
GuildBanManager,
GuildChannelManager,
GuildEmojiManager,
GuildEmojiRoleManager,
Expand Down Expand Up @@ -67,6 +68,7 @@ export const {
Emoji,
Guild,
GuildAuditLogs,
GuildBan,
GuildChannel,
GuildEmoji,
GuildMember,
Expand Down
1 change: 1 addition & 0 deletions src/client/actions/ActionsManager.js
Expand Up @@ -21,6 +21,7 @@ class ActionsManager {
this.register(require('./InviteDelete'));
this.register(require('./GuildMemberRemove'));
this.register(require('./GuildMemberUpdate'));
this.register(require('./GuildBanAdd'));
this.register(require('./GuildBanRemove'));
this.register(require('./GuildRoleCreate'));
this.register(require('./GuildRoleDelete'));
Expand Down
20 changes: 20 additions & 0 deletions src/client/actions/GuildBanAdd.js
@@ -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;
12 changes: 8 additions & 4 deletions src/client/actions/GuildBanRemove.js
@@ -1,20 +1,24 @@
'use strict';

const Action = require('./Action');
const GuildBan = require('../../structures/GuildBan');
const { Events } = require('../../util/Constants');

class GuildBanRemove extends Action {
handle(data) {
const client = this.client;
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.
* @event Client#guildBanRemove
* @param {Guild} guild The guild that the unban occurred in
* @param {User} user The user that was unbanned
* @param {GuildBan} ban The ban that was removed
*/
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);
}
}
}

Expand Down
15 changes: 2 additions & 13 deletions src/client/websocket/handlers/GUILD_BAN_ADD.js
@@ -1,16 +1,5 @@
'use strict';

const { Events } = require('../../../util/Constants');

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);
module.exports = (client, packet) => {
client.actions.GuildBanAdd.handle(packet.d);
};
2 changes: 2 additions & 0 deletions src/index.js
Expand Up @@ -37,6 +37,7 @@ module.exports = {
BaseGuildEmojiManager: require('./managers/BaseGuildEmojiManager'),
ChannelManager: require('./managers/ChannelManager'),
GuildApplicationCommandManager: require('./managers/GuildApplicationCommandManager'),
GuildBanManager: require('./managers/GuildBanManager'),
GuildChannelManager: require('./managers/GuildChannelManager'),
GuildEmojiManager: require('./managers/GuildEmojiManager'),
GuildEmojiRoleManager: require('./managers/GuildEmojiRoleManager'),
Expand Down Expand Up @@ -79,6 +80,7 @@ module.exports = {
Emoji: require('./structures/Emoji'),
Guild: require('./structures/Guild'),
GuildAuditLogs: require('./structures/GuildAuditLogs'),
GuildBan: require('./structures/GuildBan'),
GuildChannel: require('./structures/GuildChannel'),
GuildEmoji: require('./structures/GuildEmoji'),
GuildMember: require('./structures/GuildMember'),
Expand Down
177 changes: 177 additions & 0 deletions src/managers/GuildBanManager.js
@@ -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;
28 changes: 5 additions & 23 deletions src/managers/GuildMemberManager.js
Expand Up @@ -216,49 +216,31 @@ class GuildMemberManager extends BaseManager {
* @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.
* Internally calls the GuildBanManager#create method.
* @example
* // Ban a user by ID (or with a user/guild member object)
* 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);
*/
ban(user, options = { days: 0 }) {
if (typeof options !== 'object') return Promise.reject(new TypeError('INVALID_TYPE', 'options', 'object', true));
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;
});
return this.guild.bans.create(user, options);
}

/**
* Unbans a user from the guild.
* @param {UserResolvable} user The user to unban
* @param {string} [reason] Reason for unbanning user
* @returns {Promise<User>}
* Internally calls the GuildBanManager#remove method.
* @example
* // Unban a user by ID (or with a user/guild member object)
* guild.members.unban('84484653687267328')
* .then(user => console.log(`Unbanned ${user.username} from ${guild.name}`))
* .catch(console.error);
*/
unban(user, reason) {
const id = this.client.users.resolveID(user);
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));
return this.guild.bans.remove(user, reason);
}

_fetchSingle({ user, cache, force = false }) {
Expand Down

0 comments on commit 6d09160

Please sign in to comment.