const User = require('./User'); const Collection = require('../util/Collection'); const ClientUserSettings = require('./ClientUserSettings'); const ClientUserGuildSettings = require('./ClientUserGuildSettings'); const Constants = require('../util/Constants'); const Util = require('../util/Util'); const Guild = require('./Guild'); const Message = require('./Message'); const GroupDMChannel = require('./GroupDMChannel'); /** * Represents the logged in client's Discord user. * @extends {User} */ class ClientUser extends User { _patch(data) { super._patch(data); /** * Whether or not this account has been verified * @type {boolean} */ this.verified = data.verified; /** * The email of this account * @type {string} */ this.email = data.email; this._typing = new Map(); /** * A Collection of friends for the logged in user * This is only filled when using a user account. * @type {Collection} */ this.friends = new Collection(); /** * A Collection of blocked users for the logged in user * This is only filled when using a user account. * @type {Collection} */ this.blocked = new Collection(); /** * A Collection of notes for the logged in user * This is only filled when using a user account. * @type {Collection} */ this.notes = new Collection(); /** * If the user has Discord premium (nitro) * This is only filled when using a user account. * @type {?boolean} */ this.premium = typeof data.premium === 'boolean' ? data.premium : null; /** * If the user has MFA enabled on their account * This is only filled when using a user account. * @type {?boolean} */ this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null; /** * If the user has ever used a mobile device on Discord * This is only filled when using a user account. * @type {?boolean} */ this.mobile = typeof data.mobile === 'boolean' ? data.mobile : null; /** * Various settings for this user * This is only filled when using a user account. * @type {?ClientUserSettings} */ this.settings = data.user_settings ? new ClientUserSettings(this, data.user_settings) : null; /** * All of the user's guild settings * This is only filled when using a user account. * @type {Collection} */ this.guildSettings = new Collection(); if (data.user_guild_settings) { for (const settings of data.user_guild_settings) { this.guildSettings.set(settings.guild_id, new ClientUserGuildSettings(settings, this.client)); } } } /** * ClientUser's presence * @readonly * @type {Presence} */ get presence() { return this.client.presences.clientPresence; } edit(data, passcode) { if (!this.bot) { if (typeof passcode !== 'object') { data.password = passcode; } else { data.code = passcode.mfaCode; data.password = passcode.password; } } return this.client.api.users('@me').patch({ data }) .then(newData => { this.client.token = newData.token; return this.client.actions.UserUpdate.handle(newData).updated; }); } /** * Set the username of the logged in client. * Changing usernames in Discord is heavily rate limited, with only 2 requests * every hour. Use this sparingly! * @param {string} username The new username * @param {string} [password] Current password (only for user accounts) * @returns {Promise} * @example * // Set username * client.user.setUsername('discordjs') * .then(user => console.log(`My new username is ${user.username}`)) * .catch(console.error); */ setUsername(username, password) { return this.edit({ username }, password); } /** * Changes the email for the client user's account. * This is only available when using a user account. * @param {string} email New email to change to * @param {string} password Current password * @returns {Promise} * @example * // Set email * client.user.setEmail('bob@gmail.com', 'some amazing password 123') * .then(user => console.log(`My new email is ${user.email}`)) * .catch(console.error); */ setEmail(email, password) { return this.edit({ email }, password); } /** * Changes the password for the client user's account. * This is only available when using a user account. * @param {string} newPassword New password to change to * @param {Object|string} options Object containing an MFA code, password or both. * Can be just a string for the password. * @param {string} [options.oldPassword] Current password * @param {string} [options.mfaCode] Timed MFA Code * @returns {Promise} * @example * // Set password * client.user.setPassword('some new amazing password 456', 'some amazing password 123') * .then(user => console.log('New password set!')) * .catch(console.error); */ setPassword(newPassword, options) { return this.edit({ new_password: newPassword }, { password: options.oldPassword, mfaCode: options.mfaCode }); } /** * Set the avatar of the logged in client. * @param {BufferResolvable|Base64Resolvable} avatar The new avatar * @returns {Promise} * @example * // Set avatar * client.user.setAvatar('./avatar.png') * .then(user => console.log(`New avatar set!`)) * .catch(console.error); */ async setAvatar(avatar) { return this.edit({ avatar: await this.client.resolver.resolveImage(avatar) }); } /** * Data resembling a raw Discord presence. * @typedef {Object} PresenceData * @property {PresenceStatus} [status] Status of the user * @property {boolean} [afk] Whether the user is AFK * @property {Object} [activity] activity the user is playing * @property {string} [activity.name] Name of the activity * @property {ActivityType|number} [activity.type] Type of the activity * @property {string} [activity.url] Stream url */ /** * Sets the full presence of the client user. * @param {PresenceData} data Data for the presence * @returns {Promise} */ setPresence(data) { return this.client.presences.setClientPresence(data); } /** * A user's status. Must be one of: * * `online` * * `idle` * * `invisible` * * `dnd` (do not disturb) * @typedef {string} PresenceStatus */ /** * Sets the status of the client user. * @param {PresenceStatus} status Status to change to * @returns {Promise} */ setStatus(status) { return this.setPresence({ status }); } /** * Sets the activity the client user is playing. * @param {?string} name Activity being played * @param {Object} [options] Options for setting the activity * @param {string} [options.url] Twitch stream URL * @param {ActivityType|number} [options.type] Type of the activity * @returns {Promise} */ setActivity(name, { url, type } = {}) { if (!name) return this.setPresence({ activity: null }); return this.setPresence({ activity: { name, type, url }, }); } /** * Sets/removes the AFK flag for the client user. * @param {boolean} afk Whether or not the user is AFK * @returns {Promise} */ setAFK(afk) { return this.setPresence({ afk }); } /** * Fetches messages that mentioned the client's user. * @param {Object} [options] Options for the fetch * @param {number} [options.limit=25] Maximum number of mentions to retrieve * @param {boolean} [options.roles=true] Whether to include role mentions * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions * @param {Guild|Snowflake} [options.guild] Limit the search to a specific guild * @returns {Promise} */ fetchMentions(options = {}) { if (options.guild instanceof Guild) options.guild = options.guild.id; Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options); return this.client.api.users('@me').mentions.get({ query: options }) .then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))); } /** * Creates a guild. * This is only available when using a user account. * @param {string} name The name of the guild * @param {Object} [options] Options for the creating * @param {string} [options.region] The region for the server, defaults to the closest one available * @param {BufferResolvable|Base64Resolvable} [options.icon=null] The icon for the guild * @returns {Promise} The guild that was created */ createGuild(name, { region, icon = null } = {}) { if (!icon || (typeof icon === 'string' && icon.startsWith('data:'))) { return new Promise((resolve, reject) => this.client.api.guilds.post({ data: { name, region, icon } }) .then(data => { if (this.client.guilds.has(data.id)) return resolve(this.client.guilds.get(data.id)); const handleGuild = guild => { if (guild.id === data.id) { this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild); this.client.clearTimeout(timeout); resolve(guild); } }; this.client.on(Constants.Events.GUILD_CREATE, handleGuild); const timeout = this.client.setTimeout(() => { this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild); resolve(this.client.guilds.create(data)); }, 10000); return undefined; }, reject) ); } return this.client.resolver.resolveImage(icon) .then(data => this.createGuild(name, { region, icon: data || null })); } /** * An object containing either a user or access token, and an optional nickname. * @typedef {Object} GroupDMRecipientOptions * @property {UserResolvable} [user] User to add to the Group DM * @property {string} [accessToken] Access token to use to add a user to the Group DM * (only available if a bot is creating the DM) * @property {string} [nick] Permanent nickname (only available if a bot is creating the DM) * @property {string} [id] If no user resolvable is provided and you want to assign nicknames * you must provide user ids instead */ /** * Creates a Group DM. * @param {GroupDMRecipientOptions[]} recipients The recipients * @returns {Promise} */ createGroupDM(recipients) { const data = this.bot ? { access_tokens: recipients.map(u => u.accessToken), nicks: recipients.reduce((o, r) => { if (r.nick) o[r.user ? r.user.id : r.id] = r.nick; return o; }, {}), } : { recipients: recipients.map(u => this.client.resolver.resolveUserID(u.user || u.id)) }; return this.client.api.users('@me').channels.post({ data }) .then(res => new GroupDMChannel(this.client, res)); } } module.exports = ClientUser;