diff --git a/src/managers/UserManager.js b/src/managers/UserManager.js index 816214d07..f5045795b 100644 --- a/src/managers/UserManager.js +++ b/src/managers/UserManager.js @@ -31,6 +31,89 @@ class UserManager extends CachedManager { * @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable */ + /** + * The DM between the client's user and a user + * @param {Snowflake} userId The user id + * @returns {?DMChannel} + * @private + */ + dmChannel(userId) { + return this.client.channels.cache.find(c => c.type === 'DM' && c.recipient.id === userId) ?? null; + } + + /** + * Creates a {@link DMChannel} between the client and a user. + * @param {UserResolvable} user The UserResolvable to identify + * @param {BaseFetchOptions} [options] Additional options for this fetch + * @returns {Promise} + */ + async createDM(user, { cache = true, force = false } = {}) { + const id = this.resolveId(user); + + if (!force) { + const dmChannel = this.dmChannel(id); + if (dmChannel && !dmChannel.partial) return dmChannel; + } + + const data = await this.client.api.users(this.client.user.id).channels.post({ + data: { + recipient_id: id, + }, + }); + return this.client.channels._add(data, null, { cache }); + } + + /** + * Deletes a {@link DMChannel} (if one exists) between the client and a user. Resolves with the channel if successful. + * @param {UserResolvable} user The UserResolvable to identify + * @returns {Promise} + */ + async deleteDM(user) { + const id = this.resolveId(user); + const dmChannel = this.dmChannel(id); + if (!dmChannel) throw new Error('USER_NO_DM_CHANNEL'); + await this.client.api.channels(dmChannel.id).delete(); + this.client.channels._remove(dmChannel.id); + return dmChannel; + } + + /** + * Obtains a user from Discord, or the user cache if it's already available. + * @param {UserResolvable} user The user to fetch + * @param {BaseFetchOptions} [options] Additional options for this fetch + * @returns {Promise} + */ + async fetch(user, { cache = true, force = false } = {}) { + const id = this.resolveId(user); + if (!force) { + const existing = this.cache.get(id); + if (existing && !existing.partial) return existing; + } + + const data = await this.client.api.users(id).get(); + return this._add(data, cache); + } + + /** + * Fetches a user's flags. + * @param {UserResolvable} user The UserResolvable to identify + * @param {BaseFetchOptions} [options] Additional options for this fetch + * @returns {Promise} + */ + async fetchFlags(user, options) { + return (await this.fetch(user, options)).flags; + } + + /** + * Sends a message to a user. + * @param {UserResolvable} user The UserResolvable to identify + * @param {string|MessagePayload|MessageOptions} options The options to provide + * @returns {Promise} + */ + async send(user, options) { + return (await this.createDM(user)).send(options); + } + /** * Resolves a {@link UserResolvable} to a {@link User} object. * @param {UserResolvable} user The UserResolvable to identify @@ -53,23 +136,6 @@ class UserManager extends CachedManager { if (user instanceof Message) return user.author.id; return super.resolveId(user); } - - /** - * Obtains a user from Discord, or the user cache if it's already available. - * @param {UserResolvable} user The user to fetch - * @param {BaseFetchOptions} [options] Additional options for this fetch - * @returns {Promise} - */ - async fetch(user, { cache = true, force = false } = {}) { - const id = this.resolveId(user); - if (!force) { - const existing = this.cache.get(id); - if (existing && !existing.partial) return existing; - } - - const data = await this.client.api.users(id).get(); - return this._add(data, cache); - } } module.exports = UserManager; diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index c52d319fb..b91110a11 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -318,10 +318,11 @@ class GuildMember extends Base { /** * Creates a DM channel between the client and this member. + * @param {boolean} [force=false] Whether to skip the cache check and request the API * @returns {Promise} */ - createDM() { - return this.user.createDM(); + createDM(force = false) { + return this.user.createDM(force); } /** diff --git a/src/structures/User.js b/src/structures/User.js index a0a4c08e8..588519053 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -207,7 +207,7 @@ class User extends Base { * @readonly */ get dmChannel() { - return this.client.channels.cache.find(c => c.type === 'DM' && c.recipient.id === this.id) ?? null; + return this.client.users.dmChannel(this.id); } /** @@ -215,30 +215,16 @@ class User extends Base { * @param {boolean} [force=false] Whether to skip the cache check and request the API * @returns {Promise} */ - async createDM(force = false) { - if (!force) { - const { dmChannel } = this; - if (dmChannel && !dmChannel.partial) return dmChannel; - } - - const data = await this.client.api.users(this.client.user.id).channels.post({ - data: { - recipient_id: this.id, - }, - }); - return this.client.channels._add(data); + createDM(force = false) { + return this.client.users.createDM(this.id, force); } /** * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful. * @returns {Promise} */ - async deleteDM() { - const { dmChannel } = this; - if (!dmChannel) throw new Error('USER_NO_DM_CHANNEL'); - await this.client.api.channels(dmChannel.id).delete(); - this.client.channels._remove(dmChannel.id); - return dmChannel; + deleteDM() { + return this.client.users.deleteDM(this.id); } /** @@ -285,11 +271,8 @@ class User extends Base { * @param {boolean} [force=false] Whether to skip the cache check and request the API * @returns {Promise} */ - async fetchFlags(force = false) { - if (this.flags && !force) return this.flags; - const data = await this.client.api.users(this.id).get(); - this._patch(data); - return this.flags; + fetchFlags(force = false) { + return this.client.users.fetchFlags(this.id, { force }); } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 824b6b422..41877c252 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2331,7 +2331,7 @@ export class User extends PartialTextBasedChannel(Base) { public username: string; public avatarURL(options?: ImageURLOptions): string | null; public bannerURL(options?: ImageURLOptions): string | null; - public createDM(): Promise; + public createDM(force?: boolean): Promise; public deleteDM(): Promise; public displayAvatarURL(options?: ImageURLOptions): string; public equals(user: User): boolean; @@ -3164,7 +3164,12 @@ export class ThreadMemberManager extends CachedManager { private constructor(client: Client, iterable?: Iterable); + private dmChannel(userId: Snowflake): DMChannel | null; + public createDM(user: UserResolvable, options?: BaseFetchOptions): Promise; + public deleteDM(user: UserResolvable): Promise; public fetch(user: UserResolvable, options?: BaseFetchOptions): Promise; + public fetchFlags(user: UserResolvable, options?: BaseFetchOptions): Promise; + public send(user: UserResolvable, options: string | MessagePayload | MessageOptions): Promise; } export class VoiceStateManager extends CachedManager {