From 2e2464bf07c2b2e08d396b093126f887d19aec57 Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 12 May 2021 06:23:42 +1000 Subject: [PATCH] feat(GuildMemberManager): extend API coverage (#4872) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antonio Román Co-authored-by: SpaceEEC --- src/managers/GuildMemberManager.js | 65 ++++++++++++++++++++++++++++++ src/structures/GuildMember.js | 37 ++--------------- typings/index.d.ts | 2 + 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/managers/GuildMemberManager.js b/src/managers/GuildMemberManager.js index f4ac65ac9..2c80c99f3 100644 --- a/src/managers/GuildMemberManager.js +++ b/src/managers/GuildMemberManager.js @@ -3,6 +3,7 @@ const BaseManager = require('./BaseManager'); const { Error, TypeError, RangeError } = require('../errors'); const GuildMember = require('../structures/GuildMember'); +const Role = require('../structures/Role'); const Collection = require('../util/Collection'); const { Events, OPCodes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); @@ -149,6 +150,47 @@ class GuildMemberManager extends BaseManager { return data.reduce((col, member) => col.set(member.user.id, this.add(member, cache)), new Collection()); } + /** + * Edits a member of the guild. + * The user must be a member of the guild + * @param {UserResolvable} user The member to edit + * @param {GuildMemberEditData} data The data to edit the member with + * @param {string} [reason] Reason for editing this user + * @returns {Promise} + */ + async edit(user, data, reason) { + const id = this.client.users.resolveID(user); + if (!id) throw new TypeError('INVALID_TYPE', 'user', 'UserResolvable'); + + // Clone the data object for immutability + const _data = { ...data }; + if (_data.channel) { + _data.channel = this.guild.channels.resolve(_data.channel); + if (!_data.channel || _data.channel.type !== 'voice') { + throw new Error('GUILD_VOICE_CHANNEL_RESOLVE'); + } + _data.channel_id = _data.channel.id; + _data.channel = undefined; + } else if (_data.channel === null) { + _data.channel_id = null; + _data.channel = undefined; + } + if (_data.roles) _data.roles = _data.roles.map(role => (role instanceof Role ? role.id : role)); + let endpoint = this.client.api.guilds(this.guild.id); + if (id === this.client.user.id) { + const keys = Object.keys(_data); + if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members('@me').nick; + else endpoint = endpoint.members(id); + } else { + endpoint = endpoint.members(id); + } + const d = await endpoint.patch({ data: _data, reason }); + + const clone = this.cache.get(id)?._clone(); + clone?.patch(d); + return clone ?? this.add(d, false); + } + /** * Prunes members from the guild based on how long they have been inactive. * It's recommended to set options.count to `false` for large guilds. @@ -207,6 +249,29 @@ class GuildMemberManager extends BaseManager { .then(data => data.pruned); } + /** + * Kicks a user from the guild. + * The user must be a member of the guild + * @param {UserResolvable} user The member to kick + * @param {string} [reason] Reason for kicking + * @returns {Promise} 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 + * // Kick a user by ID (or with a user/guild member object) + * guild.members.kick('84484653687267328') + * .then(user => console.log(`Kicked ${user.username || user.id || user} from ${guild.name}`)) + * .catch(console.error); + */ + async kick(user, reason) { + const id = this.client.users.resolveID(user); + if (!id) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable')); + + await this.client.api.guilds(this.guild.id).members(id).delete({ reason }); + + return this.resolve(user) ?? this.client.users.resolve(user) ?? id; + } + /** * Bans a user from the guild. * @param {UserResolvable} user The user to ban diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index f00af7f01..53c33e387 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -1,7 +1,6 @@ 'use strict'; const Base = require('./Base'); -const Role = require('./Role'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); const { Error } = require('../errors'); const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); @@ -283,34 +282,8 @@ class GuildMember extends Base { * @param {string} [reason] Reason for editing this user * @returns {Promise} */ - async edit(data, reason) { - if (data.channel) { - const voiceChannelID = this.guild.channels.resolveID(data.channel); - const voiceChannel = this.guild.channels.cache.get(voiceChannelID); - if (!voiceChannelID || (voiceChannel && voiceChannel?.type !== 'voice')) { - throw new Error('GUILD_VOICE_CHANNEL_RESOLVE'); - } - data.channel_id = voiceChannelID; - data.channel = undefined; - } else if (data.channel === null) { - data.channel_id = null; - data.channel = undefined; - } - if (data.roles) data.roles = data.roles.map(role => (role instanceof Role ? role.id : role)); - let endpoint = this.client.api.guilds(this.guild.id); - if (this.user.id === this.client.user.id) { - const keys = Object.keys(data); - if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members('@me').nick; - else endpoint = endpoint.members(this.id); - } else { - endpoint = endpoint.members(this.id); - } - await endpoint.patch({ data, reason }); - - const clone = this._clone(); - data.user = this.user; - clone._patch(data); - return clone; + edit(data, reason) { + return this.guild.members.edit(this, data, reason); } /** @@ -345,11 +318,7 @@ class GuildMember extends Base { * @returns {Promise} */ kick(reason) { - return this.client.api - .guilds(this.guild.id) - .members(this.user.id) - .delete({ reason }) - .then(() => this); + return this.guild.members.kick(this, reason); } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 3f56237bc..619e39d69 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2145,10 +2145,12 @@ declare module 'discord.js' { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; public ban(user: UserResolvable, options?: BanOptions): Promise; + public edit(user: UserResolvable, data: GuildMemberEditData, reason?: string): Promise; public fetch( options: UserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: UserResolvable }), ): Promise; public fetch(options?: FetchMembersOptions): Promise>; + public kick(user: UserResolvable, reason?: string): Promise; public prune(options: GuildPruneMembersOptions & { dry?: false; count: false }): Promise; public prune(options?: GuildPruneMembersOptions): Promise; public search(options: GuildSearchMembersOptions): Promise>;