const User = require('./User'); const Collection = require('../util/Collection'); const ClientUserSettings = require('./ClientUserSettings'); const Constants = require('../util/Constants'); /** * Represents the logged in client's Discord user. * @extends {User} */ class ClientUser extends User { setup(data) { super.setup(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.localPresence = {}; 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} */ if (data.user_settings) this.settings = new ClientUserSettings(this, data.user_settings); } edit(data) { return this.client.rest.methods.updateCurrentUser(data); } /** * 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.client.rest.methods.updateCurrentUser({ 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.client.rest.methods.updateCurrentUser({ 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 {string} oldPassword Current password * @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, oldPassword) { return this.client.rest.methods.updateCurrentUser({ password: newPassword }, oldPassword); } /** * 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); */ setAvatar(avatar) { if (typeof avatar === 'string' && avatar.startsWith('data:')) { return this.client.rest.methods.updateCurrentUser({ avatar }); } else { return this.client.resolver.resolveBuffer(avatar).then(data => this.client.rest.methods.updateCurrentUser({ avatar: data }) ); } } /** * 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} [game] Game the user is playing * @property {string} [game.name] Name of the game * @property {string} [game.url] Twitch stream URL */ /** * Sets the full presence of the client user. * @param {PresenceData} data Data for the presence * @returns {Promise} */ setPresence(data) { // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}} return new Promise(resolve => { let status = this.localPresence.status || this.presence.status; let game = this.localPresence.game; let afk = this.localPresence.afk || this.presence.afk; if (!game && this.presence.game) { game = { name: this.presence.game.name, type: this.presence.game.type, url: this.presence.game.url, }; } if (data.status) { if (typeof data.status !== 'string') throw new TypeError('Status must be a string'); if (this.bot) { status = data.status; } else { this.settings.update(Constants.UserSettingsMap.status, data.status); status = 'invisible'; } } if (data.game) { game = data.game; game.type = game.url ? 1 : 0; } else if (typeof data.game !== 'undefined') { game = null; } if (typeof data.afk !== 'undefined') afk = data.afk; afk = Boolean(afk); this.localPresence = { status, game, afk }; this.localPresence.since = 0; this.localPresence.game = this.localPresence.game || null; this.client.ws.send({ op: 3, d: this.localPresence, }); this.client._setPresence(this.id, this.localPresence); resolve(this); }); } /** * 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 game the client user is playing. * @param {?string} game Game being played * @param {string} [streamingURL] Twitch stream URL * @returns {Promise} */ setGame(game, streamingURL) { if (!game) return this.setPresence({ game: null }); return this.setPresence({ game: { name: game, url: streamingURL, }, }); } /** * 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 = {}) { return this.client.rest.methods.fetchMentions(options); } /** * Send a friend request. * This is only available when using a user account. * @param {UserResolvable} user The user to send the friend request to * @returns {Promise} The user the friend request was sent to */ addFriend(user) { user = this.client.resolver.resolveUser(user); return this.client.rest.methods.addFriend(user); } /** * Remove a friend. * This is only available when using a user account. * @param {UserResolvable} user The user to remove from your friends * @returns {Promise} The user that was removed */ removeFriend(user) { user = this.client.resolver.resolveUser(user); return this.client.rest.methods.removeFriend(user); } /** * Creates a guild. * This is only available when using a user account. * @param {string} name The name of the guild * @param {string} region The region for the server * @param {BufferResolvable|Base64Resolvable} [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.dataManager.newGuild(data)); }, 10000); return undefined; }, reject) ); } else { return this.client.resolver.resolveBuffer(icon) .then(data => this.createGuild(name, { region, icon: this.client.resolver.resolveBase64(data) || null })); } } /** * An object containing either a user or access token, and an optional nickname. * @typedef {Object} GroupDMRecipientOptions * @property {UserResolvable|Snowflake} [user] User to add to the Group DM * (only available if a user is creating the 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) */ /** * Creates a Group DM. * @param {GroupDMRecipientOptions[]} recipients The recipients * @returns {Promise} */ createGroupDM(recipients) { return this.client.rest.methods.createGroupDM({ recipients: recipients.map(u => this.client.resolver.resolveUserID(u.user)), accessTokens: recipients.map(u => u.accessToken), nicks: recipients.map(u => u.nick), }); } /** * Accepts an invite to join a guild. * This is only available when using a user account. * @param {Invite|string} invite Invite or code to accept * @returns {Promise} Joined guild */ acceptInvite(invite) { return this.client.rest.methods.acceptInvite(invite); } } module.exports = ClientUser;