diff --git a/src/structures/ClientPresence.js b/src/structures/ClientPresence.js index 2761668..3b98218 100644 --- a/src/structures/ClientPresence.js +++ b/src/structures/ClientPresence.js @@ -2,7 +2,7 @@ const { Presence } = require('./Presence'); const { TypeError } = require('../errors'); -const { Opcodes, ActivityTypes } = require('../util/Constants'); +const { ActivityTypes, Opcodes } = require('../util/Constants'); const CustomStatusActivityTypes = [ActivityTypes.CUSTOM, ActivityTypes[ActivityTypes.CUSTOM]]; @@ -22,7 +22,6 @@ class ClientPresence extends Presence { */ set(presence) { const packet = this._parse(presence); - // Parse with custom class this._patch(packet, true); if (typeof presence.shardId === 'undefined') { this.client.ws.broadcast({ op: Opcodes.STATUS_UPDATE, d: packet }); @@ -33,8 +32,6 @@ class ClientPresence extends Presence { } else { this.client.ws.shards.get(presence.shardId).send({ op: Opcodes.STATUS_UPDATE, d: packet }); } - // Parse with default class - // this._patch(packet, false); return this; } @@ -48,14 +45,13 @@ class ClientPresence extends Presence { const data = { activities: [], afk: typeof afk === 'boolean' ? afk : false, - since: 0, + since: typeof since === 'number' && !Number.isNaN(since) ? since : null, status: status ?? this.status, }; if (activities?.length) { for (const [i, activity] of activities.entries()) { - if (![ActivityTypes.CUSTOM, 'CUSTOM'].includes(activity.type) && typeof activity.name !== 'string') { - throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string'); - } + if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string'); + activity.type ??= ActivityTypes.PLAYING; if (CustomStatusActivityTypes.includes(activity.type) && !activity.state) { diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index be2239f..e579fe2 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -3,19 +3,24 @@ const { setInterval } = require('node:timers'); const { Collection } = require('@discordjs/collection'); const Invite = require('./Invite'); -const { Message } = require('./Message'); const User = require('./User'); -const { Util } = require('..'); -const { Error: Error_ } = require('../errors'); -const { Opcodes, NitroType, HypeSquadType } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); const PremiumUsageFlags = require('../util/PremiumUsageFlags'); const PurchasedFlags = require('../util/PurchasedFlags'); +const Util = require('../util/Util'); + /** * Represents the logged in client's Discord user. * @extends {User} */ class ClientUser extends User { + #packageName = null; + #intervalSamsungPresence = setInterval(() => { + this.client.emit('debug', `[UPDATE] Samsung Presence: ${this.#packageName}`); + if (!this.#packageName) return; + this.setSamsungActivity(this.#packageName, 'UPDATE'); + }, 1000 * 60 * 10).unref(); + _patch(data) { super._patch(data); @@ -29,7 +34,7 @@ class ClientUser extends User { if ('mfa_enabled' in data) { /** - * If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account + * If the bot's {@link Application#owner Owner} has MFA enabled on their account * @type {?boolean} */ this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null; @@ -39,16 +44,6 @@ class ClientUser extends User { if ('token' in data) this.client.token = data.token; - // Todo: Add (Selfbot) - if ('premium_type' in data) { - const nitro = NitroType[data.premium_type ?? 0]; - /** - * Nitro type of the client user. - * @type {NitroType} - */ - this.nitroType = nitro ?? `UNKNOWN_TYPE_${data.premium_type}`; - } - if ('purchased_flags' in data) { /** * Purchased state of the client user. @@ -70,7 +65,7 @@ class ClientUser extends User { * Phone number of the client user. * @type {?string} */ - this.phoneNumber = data.phone; + this.phone = data.phone; } if ('nsfw_allowed' in data) { @@ -86,48 +81,35 @@ class ClientUser extends User { * Email address of the client user. * @type {?string} */ - this.emailAddress = data.email; + this.email = data.email; } if ('bio' in data) { + /** + * About me (User) + * The user must be force fetched for this property to be present or be updated + * @type {?string} + */ this.bio = data.bio; } if ('pronouns' in data) { + /** + * Pronouns (User) + * The user must be force fetched for this property to be present or be updated + * @type {?string} + */ this.pronouns = data.pronouns; } - if (!this.friendNicknames?.size) { + if ('premium_type' in data) { /** - * The friend nicknames cache of the client user. - * @type {Collection} - * @private + * Premium types denote the level of premium a user has. + * @type {number} + * @see {@link https://discord-userdoccers.vercel.app/resources/user#premium-type} */ - this.friendNicknames = new Collection(); + this.premiumType = data.premium_type; } - - if (!this._intervalSamsungPresence) { - this._intervalSamsungPresence = setInterval(() => { - this.client.emit('debug', `Samsung Presence: ${this._packageName}`); - if (!this._packageName) return; - this.setSamsungActivity(this._packageName, 'UPDATE'); - }, 1000 * 60 * 10).unref(); - // 20 minutes max - } - } - - /** - * Patch note - * @param {Object} data Note data - * @private - */ - _patchNote(data) { - /** - * The notes cache of the client user. - * @type {Collection} - * @private - */ - this.notes = data ? new Collection(Object.entries(data)) : new Collection(); } /** @@ -165,7 +147,6 @@ class ClientUser extends User { * 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 The password of the account * @returns {Promise} * @example * // Set username @@ -173,14 +154,8 @@ class ClientUser extends User { * .then(user => console.log(`My new username is ${user.username}`)) * .catch(console.error); */ - setUsername(username, password) { - if (!password && !this.client.password) { - throw new Error('A password is required to change a username.'); - } - return this.edit({ - username, - password: this.client.password ? this.client.password : password, - }); + setUsername(username) { + return this.edit({ username }); } /** @@ -197,186 +172,6 @@ class ClientUser extends User { avatar = avatar && (await DataResolver.resolveImage(avatar)); return this.edit({ avatar }); } - /** - * Sets the banner of the logged in client. - * @param {?(BufferResolvable|Base64Resolvable)} banner The new banner - * @returns {Promise} - * @example - * // Set banner - * client.user.setBanner('./banner.png') - * .then(user => console.log(`New banner set!`)) - * .catch(console.error); - */ - async setBanner(banner) { - if (this.nitroType !== 'NITRO_BOOST') { - throw new Error('You must be a Nitro Boosted User to change your banner.'); - } - banner = banner && (await DataResolver.resolveImage(banner)); - return this.edit({ banner }); - } - - /** - * Set HyperSquad House - * @param {HypeSquadType} type - * * `LEAVE`: 0 - * * `HOUSE_BRAVERY`: 1 - * * `HOUSE_BRILLIANCE`: 2 - * * `HOUSE_BALANCE`: 3 - * @returns {Promise} - * @example - * // Set HyperSquad HOUSE_BRAVERY - * client.user.setHypeSquad(1); || client.user.setHypeSquad('HOUSE_BRAVERY'); - * // Leave - * client.user.setHypeSquad(0); - */ - async setHypeSquad(type) { - const id = typeof type === 'string' ? HypeSquadType[type] : type; - if (!id && id !== 0) throw new Error('Invalid HypeSquad type.'); - if (id !== 0) { - const data = await this.client.api.hypesquad.online.post({ - data: { house_id: id }, - }); - return data; - } else { - const data = await this.client.api.hypesquad.online.delete(); - return data; - } - } - - /** - * Set Accent color - * @param {ColorResolvable} color Color to set - * @returns {Promise} - */ - setAccentColor(color = null) { - return this.edit({ accent_color: color ? Util.resolveColor(color) : null }); - } - - /** - * Set discriminator - * @param {User.discriminator} discriminator It is #1234 - * @param {string} password The password of the account - * @returns {Promise} - */ - setDiscriminator(discriminator, password) { - if (this.nitroType == 'NONE') throw new Error('You must be a Nitro User to change your discriminator.'); - if (!password && !this.client.password) { - throw new Error('A password is required to change a discriminator.'); - } - return this.edit({ - discriminator, - username: this.username, - password: this.client.password ? this.client.password : password, - }); - } - - /** - * Set About me - * @param {string | null} bio Bio to set - * @returns {Promise} - */ - setAboutMe(bio = null) { - return this.edit({ - bio, - }); - } - - /** - * Change the email - * @param {Email} email Email to change - * @param {string} password Password of the account - * @returns {Promise} - */ - setEmail(email, password) { - throw new Error('This method is not available yet. Please use the official Discord client to change your email.'); - // eslint-disable-next-line no-unreachable - if (!password && !this.client.password) { - throw new Error('A password is required to change a email.'); - } - return this.edit({ - email, - password: this.client.password ? this.client.password : password, - }); - } - - /** - * Set new password - * @param {string} oldPassword Old password - * @param {string} newPassword New password to set - * @returns {Promise} - */ - setPassword(oldPassword, newPassword) { - if (!oldPassword && !this.client.password) { - throw new Error('A password is required to change a password.'); - } - if (!newPassword) throw new Error('New password is required.'); - return this.edit({ - password: this.client.password ? this.client.password : oldPassword, - new_password: newPassword, - }); - } - - /** - * Disable account - * @param {string} password Password of the account - * @returns {Promise} - */ - async disableAccount(password) { - if (!password && !this.client.password) { - throw new Error('A password is required to disable an account.'); - } - const data = await this.client.api.users['@me'].disable.post({ - data: { - password: this.client.password ? this.client.password : password, - }, - }); - return data; - } - - /** - * Set selfdeaf (Global) - * @param {boolean} status Whether or not the ClientUser is deafened - * @returns {boolean} - */ - setDeaf(status) { - if (typeof status !== 'boolean') throw new Error('Deaf status must be a boolean.'); - this.client.ws.broadcast({ - op: Opcodes.VOICE_STATE_UPDATE, - d: { self_deaf: status }, - }); - return status; - } - - /** - * Set selfmute (Global) - * @param {boolean} status Whether or not the ClientUser is muted - * @returns {boolean} - */ - setMute(status) { - if (typeof status !== 'boolean') throw new Error('Mute status must be a boolean.'); - this.client.ws.broadcast({ - op: Opcodes.VOICE_STATE_UPDATE, - d: { self_mute: status }, - }); - return status; - } - - /** - * Delete account. Warning: Cannot be changed once used! - * @param {string} password Password of the account - * @returns {Promise} - */ - async deleteAccount(password) { - if (!password && !this.client.password) { - throw new Error('A password is required to delete an account.'); - } - const data = await this.client.api.users['@me/delete'].post({ - data: { - password: this.client.password ? this.client.password : password, - }, - }); - return data; - } /** * Options for setting activities @@ -392,7 +187,7 @@ class ClientUser extends User { * @typedef {Object} PresenceData * @property {PresenceStatusData} [status] Status of the user * @property {boolean} [afk] Whether the user is AFK - * @property {ActivitiesOptions[]|CustomStatus[]|RichPresence[]|SpotifyRPC[]} [activities] Activity the user is playing + * @property {(ActivitiesOptions|CustomStatus|RichPresence|SpotifyRPC)[]} [activities] Activity the user is playing * @property {number|number[]} [shardId] Shard id(s) to have the activity set on */ @@ -451,15 +246,10 @@ class ClientUser extends User { * @see {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/RichPresence.md} */ setActivity(name, options = {}) { - if (!name) { - return this.setPresence({ activities: [], shardId: options.shardId }); - } + if (!name) return this.setPresence({ activities: [], shardId: options.shardId }); const activity = Object.assign({}, options, typeof name === 'object' ? name : { name }); - return this.setPresence({ - activities: [activity], - shardId: activity.shardId, - }); + return this.setPresence({ activities: [activity], shardId: activity.shardId }); } /** @@ -472,6 +262,81 @@ class ClientUser extends User { return this.setPresence({ afk, shardId }); } + /** + * Sets the banner of the logged in client. + * @param {?(BufferResolvable|Base64Resolvable)} banner The new banner + * @returns {Promise} + * @example + * // Set banner + * client.user.setBanner('./banner.png') + * .then(user => console.log(`New banner set!`)) + * .catch(console.error); + */ + async setBanner(banner) { + banner = banner && (await DataResolver.resolveImage(banner)); + return this.edit({ banner }); + } + + /** + * Set HyperSquad House + * @param {string|number} type + * * `LEAVE`: 0 + * * `HOUSE_BRAVERY`: 1 + * * `HOUSE_BRILLIANCE`: 2 + * * `HOUSE_BALANCE`: 3 + * @returns {Promise} + * @example + * // Set HyperSquad HOUSE_BRAVERY + * client.user.setHypeSquad(1); || client.user.setHypeSquad('HOUSE_BRAVERY'); + * // Leave + * client.user.setHypeSquad(0); + */ + setHypeSquad(type) { + switch (type) { + case 'LEAVE': { + type = 0; + break; + } + case 'HOUSE_BRAVERY': { + type = 1; + break; + } + case 'HOUSE_BRILLIANCE': { + type = 2; + break; + } + case 'HOUSE_BALANCE': { + type = 3; + break; + } + } + if (type == 0) { + return this.client.api.hypesquad.online.delete(); + } else { + return this.client.api.hypesquad.online.post({ + data: { house_id: type }, + }); + } + } + + /** + * Set Accent color + * @param {ColorResolvable} color Color to set + * @returns {Promise} + */ + setAccentColor(color = null) { + return this.edit({ accent_color: color ? Util.resolveColor(color) : null }); + } + + /** + * Set About me + * @param {string} [bio=null] Bio to set + * @returns {Promise} + */ + setAboutMe(bio = null) { + return this.edit({ bio }); + } + /** * Create an invite [Friend Invites] * maxAge: 604800 | maxUses: 1 @@ -505,63 +370,10 @@ class ClientUser extends User { /** * Revoke all friend invites - * @returns {Promise>} + * @returns {Promise} */ - async revokeAllFriendInvites() { - const data = await this.client.api.users['@me'].invites.delete(); - const collection = new Collection(); - for (const invite of data) { - collection.set(invite.code, new Invite(this.client, invite)); - } - return collection; - } - - /** - * Get a collection of messages mentioning clientUser - * @param {number} [limit=25] Maximum number of messages to get - * @param {boolean} [mentionRoles=true] Whether or not to mention roles - * @param {boolean} [mentionEveryone=true] Whether or not to mention `@everyone` - * @returns {Promise>} - */ - async getMentions(limit = 25, mentionRoles = true, mentionEveryone = true) { - // https://canary.discord.com/api/v9/users/@me/mentions?limit=25&roles=true&everyone=true - const data = await this.client.api.users['@me'].mentions.get({ - query: { - limit, - roles: mentionRoles, - everyone: mentionEveryone, - }, - }); - const collection = new Collection(); - for (const msg of data) { - collection.set(msg.id, new Message(this.client, msg)); - } - return collection; - } - - /** - * Change Theme color - * @param {ColorResolvable} primary The primary color of the user's profile - * @param {ColorResolvable} accent The accent color of the user's profile - * @returns {Promise} - */ - async setThemeColors(primary, accent) { - if (!primary || !accent) throw new Error('PRIMARY_COLOR or ACCENT_COLOR are required.'); - // Check nitro - if (this.nitroType !== 'NITRO_BOOST') { - throw new Error_('NITRO_BOOST_REQUIRED', 'themeColors'); - } - primary = Util.resolveColor(primary) || this.themeColors[0]; - accent = Util.resolveColor(accent) || this.themeColors[1]; - const data_ = await this.client.api.users['@me'].profile.patch({ - data: { - theme_colors: [primary, accent], - }, - }); - this._ProfilePatch({ - user_profile: data_, - }); - return this; + revokeAllFriendInvites() { + return this.client.api.users['@me'].invites.delete(); } /** @@ -587,8 +399,8 @@ class ClientUser extends User { update: type, }, }); - if (type !== 'STOP') this._packageName = packageName; - else this._packageName = null; + if (type !== 'STOP') this.#packageName = packageName; + else this.#packageName = null; return this; } @@ -598,9 +410,7 @@ class ClientUser extends User { * @returns {Promise} */ stopRinging(channel) { - const id = this.client.channels.resolveId(channel); - if (!channel) return false; - return this.client.api.channels(id).call['stop-ringing'].post({ + return this.client.api.channels(this.client.channels.resolveId(channel)).call['stop-ringing'].post({ data: {}, }); } diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 6756362..7574a78 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -8,7 +8,6 @@ const { Error } = require('../errors'); const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); const GuildMemberFlags = require('../util/GuildMemberFlags'); const Permissions = require('../util/Permissions'); -const Util = require('../util/Util'); /** * @type {WeakSet} @@ -102,7 +101,6 @@ class GuildMember extends Base { this.communicationDisabledUntilTimestamp = data.communication_disabled_until && Date.parse(data.communication_disabled_until); } - if ('flags' in data) { /** * The flags of this member @@ -114,51 +112,6 @@ class GuildMember extends Base { } } - _ProfilePatch(data) { - if ('accent_color' in data) { - /** - * The member's accent color - * The user must be force fetched for this property to be present or be updated - * @type {?number} - */ - this.accentColor = data.accent_color; - } - if ('banner' in data) { - /** - * The member's banner hash - * The user must be force fetched for this property to be present or be updated - * @type {?string} - */ - this.banner = data.banner; - } - if ('bio' in data) { - /** - * The member's biography (About me) - * The user must be force fetched for this property to be present or be updated - * @type {?string} - */ - this.bio = data.bio; - } - if ('theme_colors' in data) { - /** - * The member's theme colors (Profile theme) [Primary, Accent] - * The user must be force fetched for this property to be present or be updated - * @type {?Array} - */ - this.themeColors = data.theme_colors; - } - } - - /** - * The hexadecimal version of the user theme color, with a leading hash [Primary, Accent] - * The user must be force fetched for this property to be present or be updated - * @type {?Array} - * @readonly - */ - get hexThemeColor() { - return this.themeColors?.map(c => `#${c.toString(16).padStart(6, '0')}`) || null; - } - _clone() { const clone = super._clone(); clone._roles = this._roles.slice(); @@ -232,21 +185,6 @@ class GuildMember extends Base { return this.client.rest.cdn.GuildMemberAvatar(this.guild.id, this.id, this.avatar, format, size, dynamic); } - /** - * A link to the user's banner. - * This method will throw an error if called before the user is force fetched Profile. - * See {@link GuildMember#banner} for more info - * @param {ImageURLOptions} [options={}] Options for the Image URL - * @returns {?string} - */ - bannerURL({ format, size, dynamic } = {}) { - if (typeof this.banner === 'undefined') { - throw new Error('USER_BANNER_NOT_FETCHED'); - } - if (!this.banner) return null; - return this.client.rest.cdn.GuildMemberBanner(this.guild.id, this.id, this.banner, format, size, dynamic); - } - /** * A link to the member's guild avatar if they have one. * Otherwise, a link to their {@link User#displayAvatarURL} will be returned. @@ -444,79 +382,6 @@ class GuildMember extends Base { return this.edit({ flags, reason }); } - /** - * Sets the guild avatar of the logged in client. - * @param {?(BufferResolvable|Base64Resolvable)} avatar The new avatar - * @returns {Promise} - */ - setAvatar(avatar) { - if (this.user.id !== this.client.user.id) { - throw new Error('ONLY_ME'); - } - if (this.client.user.nitroType !== 'NITRO_BOOST') { - throw new Error('NITRO_BOOST_REQUIRED', 'avatar'); - } - return this.edit({ avatar }); - } - - /** - * Sets the guild banner of the logged in client. - * @param {?(BufferResolvable|Base64Resolvable)} banner The new banner - * @returns {Promise} - */ - setBanner(banner) { - if (this.user.id !== this.client.user.id) { - throw new Error('ONLY_ME'); - } - if (this.client.user.nitroType !== 'NITRO_BOOST') { - throw new Error('NITRO_BOOST_REQUIRED', 'banner'); - } - return this.edit({ banner }); - } - - /** - * Set Guild About me - * @param {string | null} bio Bio to set - * @returns {Promise} - */ - setAboutMe(bio = null) { - if (this.user.id !== this.client.user.id) { - throw new Error('ONLY_ME'); - } - if (this.client.user.nitroType !== 'NITRO_BOOST') { - throw new Error('NITRO_BOOST_REQUIRED', 'bio'); - } - return this.edit({ bio }); - } - - /** - * Change Theme color - * @param {ColorResolvable} primary The primary color of the user's profile - * @param {ColorResolvable} accent The accent color of the user's profile - * @returns {Promise} - */ - async setThemeColors(primary, accent) { - if (this.user.id !== this.client.user.id) { - throw new Error('ONLY_ME'); - } - if (!primary || !accent) throw new Error('PRIMARY_COLOR or ACCENT_COLOR are required.'); - // Check nitro - if (this.nitroType !== 'NITRO_BOOST') { - throw new Error('NITRO_BOOST_REQUIRED', 'themeColors'); - } - primary = Util.resolveColor(primary) || this.themeColors ? this.themeColors[0] : 0; - accent = Util.resolveColor(accent) || this.themeColors ? this.themeColors[1] : 0; - const data_ = await this.client.api.guilds[this.guild.id].profile['@me'].patch({ - data: { - theme_colors: [primary, accent], - }, - }); - this._ProfilePatch({ - guild_member_profile: data_, - }); - return this; - } - /** * Creates a DM channel between the client and this member. * @param {boolean} [force=false] Whether to skip the cache check and request the API @@ -619,8 +484,6 @@ class GuildMember extends Base { this.joinedTimestamp === member.joinedTimestamp && this.nickname === member.nickname && this.avatar === member.avatar && - this.accentColor === member.accentColor && - this.bio === member.bio && this.pending === member.pending && this.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp && this.flags.equals(member.flags) && @@ -629,14 +492,6 @@ class GuildMember extends Base { ); } - /** - * Get profile guildMember - * @returns {Promise} - */ - getProfile() { - return this.user.getProfile(this.guild.id); - } - /** * When concatenated with a string, this automatically returns the user's mention instead of the GuildMember object. * @returns {string} @@ -659,6 +514,33 @@ class GuildMember extends Base { json.displayAvatarURL = this.displayAvatarURL(); return json; } + + /** + * Sets the guild avatar of the logged in client. + * @param {?(BufferResolvable|Base64Resolvable)} avatar The new avatar + * @returns {Promise} + */ + setAvatar(avatar) { + return this.edit({ avatar }); + } + + /** + * Sets the guild banner of the logged in client. + * @param {?(BufferResolvable|Base64Resolvable)} banner The new banner + * @returns {Promise} + */ + setBanner(banner) { + return this.edit({ banner }); + } + + /** + * Set Guild About me + * @param {string | null} bio Bio to set + * @returns {Promise} + */ + setAboutMe(bio = null) { + return this.edit({ bio }); + } } /** diff --git a/src/structures/Invite.js b/src/structures/Invite.js index e26960d..6e5b9c5 100644 --- a/src/structures/Invite.js +++ b/src/structures/Invite.js @@ -1,12 +1,11 @@ 'use strict'; -const Buffer = require('node:buffer').Buffer; const Base = require('./Base'); const { GuildScheduledEvent } = require('./GuildScheduledEvent'); const IntegrationApplication = require('./IntegrationApplication'); const InviteStageInstance = require('./InviteStageInstance'); const { Error } = require('../errors'); -const { ChannelTypes, Endpoints } = require('../util/Constants'); +const { Endpoints } = require('../util/Constants'); const Permissions = require('../util/Permissions'); // TODO: Convert `inviter` and `channel` in this class to a getter. @@ -179,7 +178,7 @@ class Invite extends Base { this.channel = this.client.channels.cache.get(data.channel_id); } - if ('channel' in data && data.channel) { + if ('channel' in data) { /** * The channel this invite is for * @type {Channel} @@ -317,53 +316,6 @@ class Invite extends Base { valueOf() { return this.code; } - - /** - * Join this Guild using this invite. - * @param {boolean} [autoVerify] Whether to automatically verify member - * @returns {Promise} - * @example - * await client.fetchInvite('code').then(async invite => { - * await invite.acceptInvite(); - * }); - */ - async acceptInvite(autoVerify = false) { - if (!this.guild) throw new Error('INVITE_NO_GUILD'); - if (this.client.guilds.cache.get(this.guild.id)) return this.guild; - const dataHeader = { - location: 'Join Guild', - location_guild_id: this.guild?.id, - location_channel_id: this.channelId, - location_channel_type: ChannelTypes[this.channel?.type] ?? 0, - }; - await this.client.api.invites(this.code).post({ - data: { - session_id: this.client.sessionId, - }, - headers: { - 'X-Context-Properties': Buffer.from(JSON.stringify(dataHeader), 'utf8').toString('base64'), - }, - }); - const guild = this.client.guilds.cache.get(this.guild.id); - /* - // - if (autoVerify) { - console.warn('Feature is under maintenance - Invite#acceptInvite(true)'); - } - */ - if (autoVerify) { - const getForm = await this.client.api - .guilds(this.guild.id) - ['member-verification'].get({ query: { with_guild: false, invite_code: this.code } }) - .catch(() => {}); - if (!getForm) return guild; - const form = Object.assign(getForm.form_fields[0], { response: true }); - // Respond to the form - // https://discord.com/api/v9/guilds/:id/requests/@me - await this.client.api.guilds(this.guild.id).requests['@me'].put({ data: { form_fields: [form] } }); - } - return guild; - } } /** diff --git a/src/structures/Presence.js b/src/structures/Presence.js index 9f50945..f7352e0 100644 --- a/src/structures/Presence.js +++ b/src/structures/Presence.js @@ -91,7 +91,7 @@ class Presence extends Base { if ('activities' in data) { /** * The activities of this presence - * @type {Activity[]} + * @type {(Activity|CustomStatus|SpotifyRPC|RichPresence)[]} */ this.activities = data.activities.map(activity => { if (fromClient === true) { diff --git a/src/structures/RichPresence.js b/src/structures/RichPresence.js index 159d661..b649211 100644 --- a/src/structures/RichPresence.js +++ b/src/structures/RichPresence.js @@ -1,11 +1,8 @@ 'use strict'; +const { randomUUID } = require('node:crypto'); const { ActivityTypes } = require('../util/Constants'); const { resolvePartialEmoji } = require('../util/Util'); -// eslint-disable-next-line -const getUUID = () => - ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, a => (a ^ ((Math.random() * 16) >> (a / 4))).toString(16)); -// Function check url valid (ok copilot) // eslint-disable-next-line const checkUrl = url => { try { @@ -351,7 +348,7 @@ https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Documents/RichP if (!party.max || typeof party.max != 'number') throw new Error('Party must have max number'); if (!party.current || typeof party.current != 'number') throw new Error('Party must have current'); if (party.current > party.max) throw new Error('Party current must be less than max number'); - if (!party.id || typeof party.id != 'string') party.id = getUUID(); + if (!party.id || typeof party.id != 'string') party.id = randomUUID(); this.party = { size: [party.current, party.max], id: party.id, @@ -488,16 +485,6 @@ https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Documents/RichP } } - /** - * Get random UUID string (Util) - * @returns {string} - */ - static getUUID() { - return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, a => - (a ^ ((Math.random() * 16) >> (a / 4))).toString(16), - ); - } - /** * Get Assets from a RichPresence (Util) * @param {Client} client Discord Client @@ -718,5 +705,4 @@ module.exports = { CustomStatus, RichPresence, SpotifyRPC, - getUUID, }; diff --git a/src/structures/User.js b/src/structures/User.js index 4164bb1..a797220 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -1,12 +1,10 @@ 'use strict'; -const { Collection } = require('@discordjs/collection'); const Base = require('./Base'); -const ClientApplication = require('./ClientApplication'); const VoiceState = require('./VoiceState'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); const { Error } = require('../errors'); -const { RelationshipTypes, NitroType } = require('../util/Constants'); +const { RelationshipTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); const UserFlags = require('../util/UserFlags'); const Util = require('../util/Util'); @@ -17,8 +15,9 @@ const Util = require('../util/Util'); * @extends {Base} */ class User extends Base { - constructor(client, data, application) { + constructor(client, data) { super(client); + /** * The user's id * @type {Snowflake} @@ -31,52 +30,6 @@ class User extends Base { this.flags = null; - /** - * An array of object (connected accounts), containing the following properties: - * @property {string} type The account type (twitch, youtube, etc) - * @property {string} name The account name - * @property {string} id The account id - * @property {boolean} verified Whether the account is verified - * @see {@link https://discord.com/developers/docs/resources/user#connection-object} - * @typedef {Object} ConnectionAccount - */ - - /** - * Accounts connected to this user - * The user must be force fetched for this property to be present or be updated - * @type {?ConnectionAccount[]} - */ - this.connectedAccounts = []; - /** - * Time that User has nitro (Unix Timestamp) - * The user must be force fetched for this property to be present or be updated - * @type {?number} - */ - this.premiumSince = null; - /** - * Time that User has nitro and boost server (Unix Timestamp) - * @type {?number} - */ - this.premiumGuildSince = null; - /** - * About me (User) - * The user must be force fetched for this property to be present or be updated - * @type {?string} - */ - this.bio = null; - /** - * Pronouns (User) - * The user must be force fetched for this property to be present or be updated - * @type {?string} - */ - this.pronouns = null; - this._mutualGuilds = []; - /** - * [Bot] Application - * @type {?ClientApplication} - */ - this.application = application ? new ClientApplication(this.client, application, this) : null; - this._partial = true; this._patch(data); } @@ -107,10 +60,6 @@ class User extends Base { * @type {?boolean} */ this.bot = Boolean(data.bot); - if (this.bot === true && !this.application) { - this.application = new ClientApplication(this.client, { id: this.id }, this); - this.botInGuildsCount = null; - } } else if (!this.partial && typeof this.bot !== 'boolean') { this.bot = false; } @@ -147,6 +96,17 @@ class User extends Base { this.banner ??= undefined; } + if ('banner_color' in data) { + /** + * The user banner's hex + * The user must be force fetched for this property to be present or be updated + * @type {?string} + */ + this.bannerColor = data.banner_color; + } else if (this.bannerColor !== null) { + this.bannerColor ??= undefined; + } + if ('accent_color' in data) { /** * The base 10 accent color of the user's banner @@ -176,252 +136,23 @@ class User extends Base { this.flags = new UserFlags(data.public_flags); } - if ('approximate_guild_count' in data) { - /** - * Check how many guilds the bot is in (Probably only approximate) (application.fetch() first) - * @type {?number} - */ - this.botInGuildsCount = data.approximate_guild_count; - } - - if ('avatar_decoration' in data) { + if ('avatar_decoration_data' in data) { /** * The user avatar decoration's hash * @type {?string} */ - this.avatarDecoration = data.avatar_decoration; + this.avatarDecoration = data.avatar_decoration_data?.asset; + /** + * The ID of the avatar decoration's SKU + * @type {?Snowflake} + */ + this.avatarDecorationSKUId = data.avatar_decoration_data?.sku_id; } else { this.avatarDecoration ??= null; + this.avatarDecorationSKUId ??= null; } } - /** - * This user is on the same servers as Client User - * The user must be force fetched for this property to be present or be updated - * @type {Collection} - * @readonly - */ - get mutualGuilds() { - return new Collection(this._mutualGuilds.map(obj => [obj.id, obj])); - } - - /** - * Get all mutual friends (Client -> User) - * @type {Promise>} - * @readonly - */ - get mutualFriends() { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async resolve => { - const all = new Collection(); - if (this.bot || this.client.user.id === this.id) return resolve(all); - const data = await this.client.api.users(this.id).relationships.get(); - for (const u of data) { - all.set(u.id, this.client.users._add(u)); - } - return resolve(all); - }); - } - - /** - * Check relationship status (Client -> User) - * @type {RelationshipTypes} - * @readonly - */ - get relationships() { - const i = this.client.relationships.cache.get(this.id) ?? 0; - return RelationshipTypes[parseInt(i)]; - } - - /** - * Check note - * @type {?string} - * @readonly - */ - get note() { - return this.client.user.notes.get(this.id); - } - - /** - * Get friend nickname - * @type {?string} - * @readonly - */ - get nickname() { - return this.client.user.friendNicknames.get(this.id); - } - - /** - * The voice state of this member - * @type {VoiceState} - * @readonly - */ - get voice() { - return ( - this.client.voiceStates.cache.get(this.id) ?? - this.client.guilds.cache.find(g => g?.voiceStates?.cache?.get(this.id))?.voiceStates?.cache?.get(this.id) ?? - new VoiceState({ client: this.client }, { user_id: this.id }) - ); - } - - _ProfilePatch(data) { - if (!data) return; - - this._partial = false; - - if (data.connected_accounts.length > 0) { - this.connectedAccounts = data.connected_accounts; - } - - if ('premium_since' in data) { - const date = new Date(data.premium_since); - this.premiumSince = date.getTime(); - } - - if ('premium_guild_since' in data) { - const date = new Date(data.premium_guild_since); - this.premiumGuildSince = date.getTime(); - } - - if ('premium_type' in data) { - const nitro = NitroType[data.premium_type ?? 0]; - /** - * Nitro type of the user. - * @type {NitroType} - */ - this.nitroType = nitro ?? `UNKNOWN_TYPE_${data.premium_type}`; - } - - if ('user_profile' in data) { - this.bio = data.user_profile.bio; - /** - * The user's theme colors (Profile theme) [Primary, Accent] - * The user must be force fetched for this property to be present or be updated - * @type {?Array} - */ - this.themeColors = data.user_profile.theme_colors; - - this.pronouns = data.user_profile.pronouns; - } - - if ('guild_member_profile' in data && 'guild_member' in data) { - const guild = this.client.guilds.cache.get(data.guild_member_profile.guild_id); - const member = guild?.members._add(data.guild_member); - member._ProfilePatch(data.guild_member_profile); - } - - if ('application' in data) { - this.application = new ClientApplication(this.client, data.application, this); - } - - if ('badges' in data) { - /** - * @callback BadgeIcon - * @returns {string} - */ - - /** - * @typedef {Object} UserBadge - * @property {string} id The id of the badge - * @property {string} description The description of the badge - * @property {string} icon The icon hash of the badge - * @property {?string} link The link of the badge - * @property {BadgeIcon} iconURL The iconURL of the badge - */ - - /** - * User badges (Boost, Slash, AutoMod, etc.) - * @type {?Array} - */ - this.badges = data.badges.map(o => ({ ...o, iconURL: () => this.client.rest.cdn.BadgeIcon(o.icon) })); - } - - if ('guild_badges' in data) { - // Unknown - } - - if ('mutual_guilds' in data) { - this._mutualGuilds = data.mutual_guilds; - } - } - - /** - * Get profile from Discord, if client is in a server with the target. - * @type {User} - * @param {Snowflake | null} guildId The guild id to get the profile from - * @returns {Promise} - */ - async getProfile(guildId) { - if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); - const query = guildId - ? { - with_mutual_guilds: true, - guild_id: guildId, - } - : { - with_mutual_guilds: true, - }; - const data = await this.client.api.users(this.id).profile.get({ - query, - }); - this._ProfilePatch(data); - return this; - } - - /** - * Friends the user [If incoming request] - * @type {boolean} - * @returns {Promise} - */ - setFriend() { - return this.client.relationships.addFriend(this); - } - - /** - * Changes the nickname of the friend - * @param {?string} nickname The nickname to change - * @type {boolean} - * @returns {Promise} - */ - setNickname(nickname) { - return this.client.relationships.setNickname(this.id, nickname); - } - - /** - * Send Friend Request to the user - * @type {boolean} - * @returns {Promise} - */ - sendFriendRequest() { - return this.client.relationships.sendFriendRequest(this.username, this.discriminator); - } - /** - * Blocks the user - * @type {boolean} - * @returns {Promise} - */ - setBlock() { - return this.client.relationships.addBlocked(this); - } - - /** - * Removes the user from your blocks list - * @type {boolean} - * @returns {Promise} - */ - unBlock() { - return this.client.relationships.deleteBlocked(this); - } - - /** - * Removes the user from your friends list - * @type {boolean} - * @returns {Promise} - */ - unFriend() { - return this.client.relationships.deleteFriend(this); - } - /** * Whether this User is a partial * @type {boolean} @@ -508,40 +239,11 @@ class User extends Base { * @returns {?string} */ bannerURL({ format, size, dynamic } = {}) { - if (typeof this.banner === 'undefined') { - throw new Error('USER_BANNER_NOT_FETCHED'); - } + if (typeof this.banner === 'undefined') throw new Error('USER_BANNER_NOT_FETCHED'); if (!this.banner) return null; return this.client.rest.cdn.Banner(this.id, this.banner, format, size, dynamic); } - /** - * Ring the user's phone / PC (call) - * @returns {Promise} - * @deprecated - */ - ring() { - if (this.relationships !== 'FRIEND') return Promise.reject(new Error('USER_NOT_FRIEND')); - if (!this.client.user.voice?.channelId || !this.client.callVoice) { - return Promise.reject(new Error('CLIENT_NO_CALL')); - } - return this.client.api.channels(this.dmChannel.id).call.ring.post({ - data: { - recipients: [this.id], - }, - }); - } - - /** - * The hexadecimal version of the user theme color, with a leading hash [Primary, Accent] - * The user must be force fetched for this property to be present or be updated - * @type {?Array} - * @readonly - */ - get hexThemeColor() { - return this.themeColors?.map(c => `#${c.toString(16).padStart(6, '0')}`) || null; - } - /** * The tag of this user * This user's username, or their legacy tag (e.g. `hydrabolt#0001`) @@ -609,8 +311,7 @@ class User extends Base { this.avatar === user.avatar && this.flags?.bitfield === user.flags?.bitfield && this.banner === user.banner && - this.accentColor === user.accentColor && - this.bio === user.bio + this.accentColor === user.accentColor ); } @@ -652,6 +353,28 @@ class User extends Base { return this.client.users.fetch(this.id, { force }); } + /** + * Returns a user profile object for a given user ID. + * This endpoint requires one of the following: + * - The user is a bot + * - The user shares a mutual guild with the current user + * - The user is a friend of the current user + * - The user is a friend suggestion of the current user + * - The user has an outgoing friend request to the current user + * @param {Snowflake} [guildId] The guild ID to get the user's member profile in + * @returns {Promise} + * @see {@link https://discord-userdoccers.vercel.app/resources/user#response-body} + */ + getProfile(guildId) { + return this.client.api.users(this.id).profile.get({ + query: { + with_mutual_guilds: true, + with_mutual_friends_count: true, + guild_id: guildId, + }, + }); + } + /** * When concatenated with a string, this automatically returns the user's mention instead of the User object. * @returns {string} @@ -680,29 +403,85 @@ class User extends Base { } /** - * Set note to user - * @param {string} note Note to set - * @returns {Promise} + * The function updates the note of a user and returns the updated user. + * @param {string|null|undefined} [note=null] - The `note` parameter is the new value that you want to set for the note of the + * user. It is an optional parameter and its default value is `null`. + * @returns {Promise} The `setNote` method is returning the `User` object. */ async setNote(note = null) { - await this.client.api.users['@me'].notes(this.id).put({ data: { note } }); + await this.client.notes.updateNote(this.id, note); return this; } /** - * Get presence (~ v12) - * @returns {Promise} + * The function returns the note associated with a specific client ID from a cache. + * @type {?string} The note that corresponds to the given id. */ - async presenceFetch() { - let data = null; - await Promise.all( - this.client.guilds.cache.map(async guild => { - const res_ = await guild.presences.resolve(this.id); - if (res_) return (data = res_); - return true; - }), + get note() { + return this.client.notes.cache.get(this.id); + } + + /** + * The voice state of this member + * @type {VoiceState} + * @readonly + */ + get voice() { + return ( + this.client.voiceStates.cache.get(this.id) ?? + this.client.guilds.cache.find(g => g?.voiceStates?.cache?.get(this.id))?.voiceStates?.cache?.get(this.id) ?? + new VoiceState({ client: this.client }, { user_id: this.id }) ); - return data; + } + + /** + * Ring the user's phone / PC (call) + * @returns {Promise} + * @deprecated + */ + ring() { + return this.client.api.channels(this.dmChannel.id).call.ring.post({ + data: { + recipients: [this.id], + }, + }); + } + + /** + * Send Friend Request to the user + * @type {boolean} + * @returns {Promise} + */ + sendFriendRequest() { + return this.client.relationships.sendFriendRequest({ user: this }); + } + + /** + * Unblock / Unfriend / Cancels a friend request + * @type {boolean} + * @returns {Promise} + */ + deleteRelationship() { + return this.client.relationships.deleteRelationship(this); + } + + /** + * Check relationship status (Client -> User) + * @type {RelationshipTypes} + * @readonly + */ + get relationship() { + const i = this.client.relationships.cache.get(this.id) ?? 0; + return RelationshipTypes[parseInt(i)]; + } + + /** + * Get friend nickname + * @type {?string} + * @readonly + */ + get friendNickname() { + return this.client.relationships.friendNicknames.get(this.id); } } diff --git a/src/util/Constants.js b/src/util/Constants.js index f3fd33f..99d4b03 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -1,12 +1,6 @@ 'use strict'; -/* Not used: -const process = require('node:process'); -const Package = (exports.Package = require('../../package.json')); -*/ -const { Error, RangeError, TypeError } = require('../errors'); -exports.defaultUA = - 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9023 Chrome/108.0.5359.215 Electron/22.3.26 Safari/537.36'; +const { Error, RangeError, TypeError } = require('../errors'); /** * Max bulk deletable message age @@ -14,121 +8,8 @@ exports.defaultUA = */ exports.MaxBulkDeletableMessageAge = 1_209_600_000; -/** - * API captcha solver - * * `2captcha` - 2captcha.com - * * `capmonster` - capmonster.cloud - * @typedef {string[]} captchaServices - */ -exports.captchaServices = ['2captcha', 'capmonster', 'custom']; - -/** - * Automatically scan and delete direct messages you receive that contain explicit media content. - * * `NOT_SCAN` - Direct messages will not be scanned for explicit content. - * * `NOT_FRIEND` - Scan direct messages from everyone unless they are a friend. - * * `EVERYONE` - Scan direct messages from everyone. - * @typedef {string} DMScanLevel - */ -exports.DMScanLevel = createEnum(['NOT_SCAN', 'NOT_FRIEND', 'EVERYONE']); - -/** - * This controls when stickers animate: - * * `ALWAYS` - Always animate. - * * `INTERACTION` - On the desktop client, stickers will animate on hover or focus. On mobile clients, stickers will animate on long-press. - * * `NEVER` - Never animate. - * @typedef {string} stickerAnimationMode - */ -exports.stickerAnimationMode = createEnum(['ALWAYS', 'INTERACTION', 'NEVER']); - -/** - * All available nitro types: - * * `NONE` - None - * * `NITRO_CLASSIC` - Nitro Classic - * * `NITRO_BOOST` - Nitro - * * `NITRO_BASIC` - Nitro Basic - * * `UNKNOWN` - Unknown - * @typedef {string} NitroType - * @see {@link https://discord.com/developers/docs/resources/user#user-object-premium-types} - */ -exports.NitroType = createEnum(['NONE', 'NITRO_CLASSIC', 'NITRO_BOOST', 'NITRO_BASIC']); - -/** - * All available HypeSquad types: - * * `LEAVE` - None - * * `HOUSE_BRAVERY` - HypeSquad Bravery - * * `HOUSE_BRILLIANCE` - HypeSquad Brilliance - * * `HOUSE_BALANCE` - HypeSquad Balance - * @typedef {string} HypeSquadType - */ -exports.HypeSquadType = createEnum(['LEAVE', 'HOUSE_BRAVERY', 'HOUSE_BRILLIANCE', 'HOUSE_BALANCE']); - -/** - * All locale codes: - * * `DANISH` - * * `GERMAN` - * * `ENGLISH_UK` - * * `ENGLISH_US` - * * `SPANISH` - * * `FRENCH` - * * `CROATIAN` - * * `ITALIAN` - * * `LITHUANIAN` - * * `HUNGARIAN` - * * `DUTCH` - * * `NORWEGIAN` - * * `POLISH` - * * `BRAZILIAN_PORTUGUESE` - * * `ROMANIA_ROMANIAN` - * * `FINNISH` - * * `SWEDISH` - * * `VIETNAMESE` - * * `TURKISH` - * * `CZECH` - * * `GREEK` - * * `BULGARIAN` - * * `RUSSIAN` - * * `UKRAINIAN` - * * `HINDI` - * * `THAI` - * * `CHINA_CHINESE` - * * `JAPANESE` - * * `TAIWAN_CHINESE` - * * `KOREAN` - * @typedef {Object} localeSetting - * @see {@link https://discord.com/developers/docs/reference#locales} - */ -exports.localeSetting = { - da: 'DANISH', - de: 'GERMAN', - 'en-GB': 'ENGLISH_UK', - 'en-US': 'ENGLISH_US', - 'es-ES': 'SPANISH', - fr: 'FRENCH', - hr: 'CROATIAN', - it: 'ITALIAN', - lt: 'LITHUANIAN', - hu: 'HUNGARIAN', - nl: 'DUTCH', - no: 'NORWEGIAN', - pl: 'POLISH', - 'pt-BR': 'BRAZILIAN_PORTUGUESE', - ro: 'ROMANIA_ROMANIAN', - fi: 'FINNISH', - 'sv-SE': 'SWEDISH', - vi: 'VIETNAMESE', - tr: 'TURKISH', - cs: 'CZECH', - el: 'GREEK', - bg: 'BULGARIAN', - ru: 'RUSSIAN', - uk: 'UKRAINIAN', - hi: 'HINDI', - th: 'THAI', - 'zh-CN': 'CHINA_CHINESE', - ja: 'JAPANESE', - 'zh-TW': 'TAIWAN_CHINESE', - ko: 'KOREAN', -}; +exports.UserAgent = + 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9023 Chrome/108.0.5359.215 Electron/22.3.26 Safari/537.36'; /** * The types of WebSocket error codes: @@ -197,10 +78,6 @@ exports.Endpoints = { if (dynamic && hash.startsWith('a_')) format = 'gif'; return makeImageUrl(`${root}/guilds/${guildId}/users/${memberId}/avatars/${hash}`, { format, size }); }, - GuildMemberBanner: (guildId, memberId, hash, format = 'webp', size, dynamic = false) => { - if (dynamic && hash.startsWith('a_')) format = 'gif'; - return makeImageUrl(`${root}/guilds/${guildId}/users/${memberId}/banners/${hash}`, { format, size }); - }, Banner: (id, hash, format, size, dynamic = false) => { if (dynamic && hash.startsWith('a_')) format = 'gif'; return makeImageUrl(`${root}/banners/${id}/${hash}`, { format, size }); @@ -227,14 +104,11 @@ exports.Endpoints = { makeImageUrl(`${root}/role-icons/${roleId}/${hash}`, { size, format }), guildScheduledEventCover: (scheduledEventId, coverHash, format, size) => makeImageUrl(`${root}/guild-events/${scheduledEventId}/${coverHash}`, { size, format }), - // Test only - BadgeIcon: hash => makeImageUrl(`${root}/badge-icons/${hash}`, { format: 'png' }), }; }, invite: (root, code, eventId) => (eventId ? `${root}/${code}?event=${eventId}` : `${root}/${code}`), scheduledEvent: (root, guildId, eventId) => `${root}/${guildId}/${eventId}`, - botGateway: '/gateway/bot', - userGateway: '/gateway', + botGateway: '/gateway', }; /** @@ -348,7 +222,6 @@ exports.Opcodes = { * * API_RESPONSE: apiResponse * * API_REQUEST: apiRequest * * CLIENT_READY: ready - * * APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE: applicationCommandAutocompleteResponse * * APPLICATION_COMMAND_CREATE: applicationCommandCreate (deprecated) * * APPLICATION_COMMAND_DELETE: applicationCommandDelete (deprecated) * * APPLICATION_COMMAND_UPDATE: applicationCommandUpdate (deprecated) @@ -357,9 +230,6 @@ exports.Opcodes = { * * AUTO_MODERATION_RULE_CREATE: autoModerationRuleCreate * * AUTO_MODERATION_RULE_DELETE: autoModerationRuleDelete * * AUTO_MODERATION_RULE_UPDATE: autoModerationRuleUpdate - * * CALL_CREATE: callCreate - * * CALL_DELETE: callDelete - * * CALL_UPDATE: callUpdate * * GUILD_AVAILABLE: guildAvailable * * GUILD_CREATE: guildCreate * * GUILD_DELETE: guildDelete @@ -385,9 +255,6 @@ exports.Opcodes = { * * CHANNEL_DELETE: channelDelete * * CHANNEL_UPDATE: channelUpdate * * CHANNEL_PINS_UPDATE: channelPinsUpdate - * * CHANNEL_RECIPIENT_REMOVE: channelRecipientRemove - * * CHANNEL_RECIPIENT_ADD: channelRecipientAdd - * * MESSAGE_ACK: messageAck * * MESSAGE_CREATE: messageCreate * * MESSAGE_DELETE: messageDelete * * MESSAGE_UPDATE: messageUpdate @@ -408,7 +275,6 @@ exports.Opcodes = { * * VOICE_STATE_UPDATE: voiceStateUpdate * * TYPING_START: typingStart * * WEBHOOKS_UPDATE: webhookUpdate - * * INTERACTION_CREATE: interactionCreate * * ERROR: error * * WARN: warn * * DEBUG: debug @@ -432,6 +298,16 @@ exports.Opcodes = { * * GUILD_SCHEDULED_EVENT_USER_ADD: guildScheduledEventUserAdd * * GUILD_SCHEDULED_EVENT_USER_REMOVE: guildScheduledEventUserRemove * * GUILD_AUDIT_LOG_ENTRY_CREATE: guildAuditLogEntryCreate + * * UNHANDLED_PACKET: unhandledPacket + * * RELATIONSHIP_ADD: relationshipAdd + * * RELATIONSHIP_REMOVE: relationshipRemove + * * RELATIONSHIP_UPDATE: relationshipUpdate + * * CHANNEL_RECIPIENT_ADD: channelRecipientAdd + * * CHANNEL_RECIPIENT_REMOVE: channelRecipientRemove + * * INTERACTION_MODAL_CREATE: interactionModalCreate + * * CALL_CREATE: callCreate + * * CALL_UPDATE: callUpdate + * * CALL_DELETE: callDelete * @typedef {Object} Events */ exports.Events = { @@ -440,7 +316,6 @@ exports.Events = { API_RESPONSE: 'apiResponse', API_REQUEST: 'apiRequest', CLIENT_READY: 'ready', - APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE: 'applicationCommandAutocompleteResponse', APPLICATION_COMMAND_CREATE: 'applicationCommandCreate', APPLICATION_COMMAND_DELETE: 'applicationCommandDelete', APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate', @@ -449,21 +324,16 @@ exports.Events = { AUTO_MODERATION_RULE_CREATE: 'autoModerationRuleCreate', AUTO_MODERATION_RULE_DELETE: 'autoModerationRuleDelete', AUTO_MODERATION_RULE_UPDATE: 'autoModerationRuleUpdate', - CALL_CREATE: 'callCreate', - CALL_DELETE: 'callDelete', - CALL_UPDATE: 'callUpdate', GUILD_AVAILABLE: 'guildAvailable', GUILD_CREATE: 'guildCreate', GUILD_DELETE: 'guildDelete', GUILD_UPDATE: 'guildUpdate', - GUILD_APPLICATION_COMMANDS_UPDATE: 'guildApplicationCommandUpdate', GUILD_UNAVAILABLE: 'guildUnavailable', GUILD_MEMBER_ADD: 'guildMemberAdd', GUILD_MEMBER_REMOVE: 'guildMemberRemove', GUILD_MEMBER_UPDATE: 'guildMemberUpdate', GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable', GUILD_MEMBERS_CHUNK: 'guildMembersChunk', - GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate', GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate', GUILD_ROLE_CREATE: 'roleCreate', GUILD_ROLE_DELETE: 'roleDelete', @@ -479,9 +349,6 @@ exports.Events = { CHANNEL_DELETE: 'channelDelete', CHANNEL_UPDATE: 'channelUpdate', CHANNEL_PINS_UPDATE: 'channelPinsUpdate', - CHANNEL_RECIPIENT_REMOVE: 'channelRecipientRemove', - CHANNEL_RECIPIENT_ADD: 'channelRecipientAdd', - MESSAGE_ACK: 'messageAck', MESSAGE_CREATE: 'messageCreate', MESSAGE_DELETE: 'messageDelete', MESSAGE_UPDATE: 'messageUpdate', @@ -497,17 +364,11 @@ exports.Events = { THREAD_MEMBER_UPDATE: 'threadMemberUpdate', THREAD_MEMBERS_UPDATE: 'threadMembersUpdate', USER_UPDATE: 'userUpdate', - USER_SETTINGS_UPDATE: 'userSettingsUpdate', - USER_GUILD_SETTINGS_UPDATE: 'userGuildSettingsUpdate', PRESENCE_UPDATE: 'presenceUpdate', VOICE_SERVER_UPDATE: 'voiceServerUpdate', VOICE_STATE_UPDATE: 'voiceStateUpdate', TYPING_START: 'typingStart', WEBHOOKS_UPDATE: 'webhookUpdate', - INTERACTION_CREATE: 'interactionCreate', - INTERACTION_SUCCESS: 'interactionSuccess', - INTERACTION_FAILURE: 'interactionFailure', - INTERACTION_MODAL_CREATE: 'interactionModalCreate', ERROR: 'error', WARN: 'warn', DEBUG: 'debug', @@ -531,18 +392,16 @@ exports.Events = { GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd', GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove', GUILD_AUDIT_LOG_ENTRY_CREATE: 'guildAuditLogEntryCreate', - RELATIONSHIP_ADD: 'relationshipAdd', - RELATIONSHIP_REMOVE: 'relationshipRemove', - RELATIONSHIP_UPDATE: 'relationshipUpdate', UNHANDLED_PACKET: 'unhandledPacket', - CAPTCHA_REQUIRED: 'captchaRequired', - // ! TODO: Add more events - SOUNDBOARD_SOUNDS: 'soundboardSounds', - VOICE_CHANNEL_EFFECT_SEND: 'voiceChannelEffectSend', - GUILD_SOUNDBOARD_SOUND_CREATE: 'guildSoundboardSoundCreate', - GUILD_SOUNDBOARD_SOUND_UPDATE: 'guildSoundboardSoundUpdate', - GUILD_SOUNDBOARD_SOUNDS_UPDATE: 'guildSoundboardSoundsUpdate', - GUILD_SOUNDBOARD_SOUND_DELETE: 'guildSoundboardSoundDelete', + RELATIONSHIP_ADD: 'relationshipAdd', + RELATIONSHIP_UPDATE: 'relationshipUpdate', + RELATIONSHIP_REMOVE: 'relationshipRemove', + CHANNEL_RECIPIENT_ADD: 'channelRecipientAdd', + CHANNEL_RECIPIENT_REMOVE: 'channelRecipientRemove', + INTERACTION_MODAL_CREATE: 'interactionModalCreate', + CALL_CREATE: 'callCreate', + CALL_UPDATE: 'callUpdate', + CALL_DELETE: 'callDelete', }; /** @@ -582,11 +441,10 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', * The type of a WebSocket message event, e.g. `MESSAGE_CREATE`. Here are the available events: * * READY * * RESUMED - * * APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE * * APPLICATION_COMMAND_CREATE (deprecated) * * APPLICATION_COMMAND_DELETE (deprecated) - * * APPLICATION_COMMAND_UPDATE (deprecated) * * APPLICATION_COMMAND_PERMISSIONS_UPDATE + * * APPLICATION_COMMAND_UPDATE (deprecated) * * AUTO_MODERATION_ACTION_EXECUTION * * AUTO_MODERATION_RULE_CREATE * * AUTO_MODERATION_RULE_DELETE @@ -631,7 +489,6 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', * * VOICE_STATE_UPDATE * * VOICE_SERVER_UPDATE * * WEBHOOKS_UPDATE - * * INTERACTION_CREATE * * STAGE_INSTANCE_CREATE * * STAGE_INSTANCE_UPDATE * * STAGE_INSTANCE_DELETE @@ -648,7 +505,6 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', exports.WSEvents = keyMirror([ 'READY', 'RESUMED', - 'APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE', 'APPLICATION_COMMAND_CREATE', 'APPLICATION_COMMAND_DELETE', 'APPLICATION_COMMAND_UPDATE', @@ -697,7 +553,6 @@ exports.WSEvents = keyMirror([ 'VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE', 'WEBHOOKS_UPDATE', - 'INTERACTION_CREATE', 'STAGE_INSTANCE_CREATE', 'STAGE_INSTANCE_UPDATE', 'STAGE_INSTANCE_DELETE', @@ -725,6 +580,7 @@ exports.WSEvents = keyMirror([ * * `guilds.join`: allows the bot to join the user to any guild it is in using Guild#addMember * * `gdm.join`: allows joining the user to a group dm * * `webhook.incoming`: generates a webhook to a channel + * * `role_connections.write`: allows your app to update a user's connection and metadata for the app * @typedef {string} InviteScope * @see {@link https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes} */ @@ -741,6 +597,7 @@ exports.InviteScopes = [ 'guilds.join', 'gdm.join', 'webhook.incoming', + 'role_connections.write', ]; // TODO: change Integration#expireBehavior to this and clean up Integration @@ -892,7 +749,7 @@ exports.ActivityTypes = createEnum(['PLAYING', 'STREAMING', 'LISTENING', 'WATCHI * * `GUILD_PRIVATE_THREAD` - a guild text channel's private thread channel * * `GUILD_STAGE_VOICE` - a guild stage voice channel * * `GUILD_DIRECTORY` - the channel in a hub containing guilds - * * `GUILD_FORUM` - a guild channel that only contains threads + * * `GUILD_FORUM` - a channel that can only contain threads * * `UNKNOWN` - a generic channel of unknown type, could be Channel or GuildChannel * @typedef {string} ChannelType * @see {@link https://discord.com/developers/docs/resources/channel#channel-object-channel-types} @@ -1131,7 +988,6 @@ exports.VerificationLevels = createEnum(['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_ * * MAXIMUM_PINS * * MAXIMUM_RECIPIENTS * * MAXIMUM_ROLES - * * MAXIMUM_USERNAMES * * MAXIMUM_WEBHOOKS * * MAXIMUM_EMOJIS * * MAXIMUM_REACTIONS @@ -1188,7 +1044,6 @@ exports.VerificationLevels = createEnum(['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_ * * INVALID_API_VERSION * * FILE_UPLOADED_EXCEEDS_MAXIMUM_SIZE * * INVALID_FILE_UPLOADED - * * GIFT_CODE_CLAIMED * * CANNOT_SELF_REDEEM_GIFT * * INVALID_GUILD * * INVALID_MESSAGE_TYPE @@ -1203,7 +1058,6 @@ exports.VerificationLevels = createEnum(['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_ * * INSUFFICIENT_BOOSTS * * INVALID_JSON * * TWO_FACTOR_REQUIRED - * * INVALID_TWO_FACTOR_CODE * * NO_USERS_WITH_DISCORDTAG_EXIST * * REACTION_BLOCKED * * RESOURCE_OVERLOADED @@ -1269,27 +1123,20 @@ exports.APIErrors = { UNKNOWN_GUILD_SCHEDULED_EVENT_USER: 10071, BOT_PROHIBITED_ENDPOINT: 20001, BOT_ONLY_ENDPOINT: 20002, - RPC_PROXY_DISALLOWED: 20003, CANNOT_SEND_EXPLICIT_CONTENT: 20009, - ACCOUNT_SCHEDULED_FOR_DELETION: 20011, NOT_AUTHORIZED: 20012, - ACCOUNT_DISABLED: 20013, SLOWMODE_RATE_LIMIT: 20016, ACCOUNT_OWNER_ONLY: 20018, ANNOUNCEMENT_EDIT_LIMIT_EXCEEDED: 20022, - UNDER_MINIMUM_AGE: 20024, - QUARANTINED: 20026, CHANNEL_HIT_WRITE_RATELIMIT: 20028, SERVER_HIT_WRITE_RATELIMIT: 20029, CONTENT_NOT_ALLOWED: 20031, GUILD_PREMIUM_LEVEL_TOO_LOW: 20035, - VANITY_URL_REQUIRED_FOR_PUBLISHED_GUILDS: 20040, MAXIMUM_GUILDS: 30001, MAXIMUM_FRIENDS: 30002, MAXIMUM_PINS: 30003, MAXIMUM_RECIPIENTS: 30004, MAXIMUM_ROLES: 30005, - MAXIMUN_USERNAMES: 30006, MAXIMUM_WEBHOOKS: 30007, MAXIMUM_EMOJIS: 30008, MAXIMUM_REACTIONS: 30010, @@ -1298,7 +1145,6 @@ exports.APIErrors = { MAXIMUM_INVITES: 30016, MAXIMUM_ANIMATED_EMOJIS: 30018, MAXIMUM_SERVER_MEMBERS: 30019, - NOT_ENOUGH_GUILD_MEMBERS: 30029, MAXIMUM_NUMBER_OF_SERVER_CATEGORIES: 30030, GUILD_ALREADY_HAS_TEMPLATE: 30031, MAXIMUM_THREAD_PARTICIPANTS: 30033, @@ -1312,16 +1158,11 @@ exports.APIErrors = { UNAUTHORIZED: 40001, ACCOUNT_VERIFICATION_REQUIRED: 40002, DIRECT_MESSAGES_TOO_FAST: 40003, - SEND_MESSAGE_TEMPORARILY_DISABLED: 40004, REQUEST_ENTITY_TOO_LARGE: 40005, FEATURE_TEMPORARILY_DISABLED: 40006, USER_BANNED: 40007, - CONNECTION_REVOKED: 40012, - DELETE_ACCOUNT_TRANSFER_TEAM_OWNERSHIP: 40028, TARGET_USER_NOT_CONNECTED_TO_VOICE: 40032, ALREADY_CROSSPOSTED: 40033, - TAG_REQUIRED: 40067, - INVITES_DISABLED: 40069, MISSING_ACCESS: 50001, INVALID_ACCOUNT_TYPE: 50002, CANNOT_EXECUTE_ON_DM: 50003, @@ -1338,13 +1179,9 @@ exports.APIErrors = { INVALID_AUTHENTICATION_TOKEN: 50014, NOTE_TOO_LONG: 50015, INVALID_BULK_DELETE_QUANTITY: 50016, - INVALID_MFA_LEVEL: 50017, - INVALID_PASSWORD: 50018, CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: 50019, INVALID_OR_TAKEN_INVITE_CODE: 50020, CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: 50021, - INVALID_PHONE_NUMBER: 50022, - INVALID_CLIENT_ID: 50023, CANNOT_EXECUTE_ON_CHANNEL_TYPE: 50024, INVALID_OAUTH_TOKEN: 50025, MISSING_OAUTH_SCOPE: 50026, @@ -1357,8 +1194,6 @@ exports.APIErrors = { INVALID_API_VERSION: 50041, FILE_UPLOADED_EXCEEDS_MAXIMUM_SIZE: 50045, INVALID_FILE_UPLOADED: 50046, - GIFT_CODE_CLAIMED: 50050, - INVALID_GIFT_REDEMPTION_OWNED: 50051, CANNOT_SELF_REDEEM_GIFT: 50054, INVALID_GUILD: 50055, INVALID_MESSAGE_TYPE: 50068, @@ -1369,15 +1204,9 @@ exports.APIErrors = { INVALID_THREAD_NOTIFICATION_SETTINGS: 50084, PARAMETER_EARLIER_THAN_CREATION: 50085, GUILD_NOT_AVAILABLE_IN_LOCATION: 50095, - INVALID_CANNOT_FRIEND_SELF: 50096, GUILD_MONETIZATION_REQUIRED: 50097, INSUFFICIENT_BOOSTS: 50101, - INVALID_USER_SETTINGS_DATA: 50105, - INVALID_ACTIVITY_LAUNCH_NO_ACCESS: 50106, - INVALID_ACTIVITY_LAUNCH_PREMIUM_TIER: 50107, - INVALID_ACTIVITY_LAUNCH_CONCURRENT_ACTIVITIES: 50108, INVALID_JSON: 50109, - INVALID_FILE_ASSET_SIZE_RESIZE_GIF: 50138, CANNOT_MIX_SUBSCRIPTION_AND_NON_SUBSCRIPTION_ROLES_FOR_EMOJI: 50144, CANNOT_CONVERT_PREMIUM_EMOJI_TO_NORMAL_EMOJI: 50145, VOICE_MESSAGES_DO_NOT_SUPPORT_ADDITIONAL_CONTENT: 50159, @@ -1385,30 +1214,9 @@ exports.APIErrors = { VOICE_MESSAGES_MUST_HAVE_SUPPORTING_METADATA: 50161, VOICE_MESSAGES_CANNOT_BE_EDITED: 50162, YOU_CANNOT_SEND_VOICE_MESSAGES_IN_THIS_CHANNEL: 50173, - TWO_FACTOR_ENABLED: 60001, - TWO_FACTOR_DISABLED: 60002, TWO_FACTOR_REQUIRED: 60003, - TWO_FACTOR_UNVERIFIED: 60004, - TWO_FACTOR_INVALID_SECRET: 60005, - TWO_FACTOR_INVALID_TICKET: 60006, - INVALID_TWO_FACTOR_CODE: 60008, - TWO_FACTOR_INVALID_SESSION: 60009, - PHONE_NUMBER_UNABLE_TO_SEND: 70003, - PHONE_VERIFICATION_REQUIRED: 70007, - RELATIONSHIP_INCOMING_DISABLED: 80000, - RELATIONSHIP_INCOMING_BLOCKED: 80001, - RELATIONSHIP_INVALUD_USER_BOT: 80002, - RELATIONSHIP_INVALID_SELF: 80003, NO_USERS_WITH_DISCORDTAG_EXIST: 80004, - RELATIONSHIP_ALREADY_FRIENDS: 80007, REACTION_BLOCKED: 90001, - INVALID_GIFT_REDEMPTION_SUBSCRIPTION_MANAGED: 100021, - INVALID_GIFT_REDEMPTION_SUBSCRIPTION_INCOMPATIBLE: 100023, - INVALID_GIFT_REDEMPTION_INVOICE_OPEN: 100024, - BILLING_NON_REFUNDABLE_PAYMENT_SOURCE: 100060, - LISTING_ALREADY_JOINED: 120000, - LISTING_TOO_MANY_MEMBERS: 120001, - LISTING_JOIN_BLOCKED: 120002, RESOURCE_OVERLOADED: 130000, STAGE_ALREADY_OPEN: 150006, CANNOT_REPLY_WITHOUT_READ_MESSAGE_HISTORY_PERMISSION: 160002, @@ -1423,12 +1231,8 @@ exports.APIErrors = { LOTTIE_ANIMATION_MAXIMUM_DIMENSIONS_EXCEEDED: 170005, STICKER_FRAME_RATE_IS_TOO_SMALL_OR_TOO_LARGE: 170006, STICKER_ANIMATION_DURATION_EXCEEDS_MAXIMUM_OF_5_SECONDS: 170007, - POGGERMODE_TEMPORARILY_DISABLED: 170008, CANNOT_UPDATE_A_FINISHED_EVENT: 180000, FAILED_TO_CREATE_STAGE_NEEDED_FOR_STAGE_EVENT: 180002, - AUTOMOD_MESSAGE_BLOCKED: 200000, - AUTOMOD_TITLE_BLOCKED: 200001, - HARMFUL_LINK_MESSAGE_BLOCKED: 240000, }; /** @@ -1554,7 +1358,7 @@ exports.ApplicationCommandPermissionTypes = createEnum([null, 'ROLE', 'USER']); * * BOOLEAN_EQUAL * * BOOLEAN_NOT_EQUAL * @typedef {string} ApplicationRoleConnectionMetadataType - * @see {@link https://discord.com/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-type} + * @see{@link https://discord.com/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-type} */ exports.ApplicationRoleConnectionMetadataTypes = createEnum([ null, @@ -1607,7 +1411,6 @@ exports.AutoModerationActionTypes = createEnum([null, 'BLOCK_MESSAGE', 'SEND_ALE */ exports.AutoModerationRuleEventTypes = createEnum([null, 'MESSAGE_SEND']); - /** * The type of an {@link Interaction} object: * * PING @@ -1656,37 +1459,29 @@ exports.InteractionResponseTypes = createEnum([ * The type of a message component * * ACTION_ROW * * BUTTON - * * TEXT_INPUT * * STRING_SELECT + * * TEXT_INPUT * * USER_SELECT * * ROLE_SELECT * * MENTIONABLE_SELECT * * CHANNEL_SELECT - * * SELECT_MENU (deprecated) * @typedef {string} MessageComponentType * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types} */ -exports.MessageComponentTypes = { - ...createEnum([ - null, - 'ACTION_ROW', - 'BUTTON', - 'STRING_SELECT', - 'TEXT_INPUT', - 'USER_SELECT', - 'ROLE_SELECT', - 'MENTIONABLE_SELECT', - 'CHANNEL_SELECT', - ]), - /** @deprecated Use `STRING_SELECT` instead */ - SELECT_MENU: 3, - /** @deprecated Use `STRING_SELECT` instead */ - 3: 'SELECT_MENU', -}; +exports.MessageComponentTypes = createEnum([ + null, + 'ACTION_ROW', + 'BUTTON', + 'STRING_SELECT', + 'TEXT_INPUT', + 'USER_SELECT', + 'ROLE_SELECT', + 'MENTIONABLE_SELECT', + 'CHANNEL_SELECT', +]); /** * The types of components that are select menus. The available types are: - * * SELECT_MENU (deprecated) * * STRING_MENU * * USER_SELECT * * ROLE_SELECT @@ -1695,19 +1490,15 @@ exports.MessageComponentTypes = { * @typedef {string} SelectMenuComponentType * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types} */ -exports.SelectMenuComponentTypes = { - ...createEnum([ - ...new Array(3).fill(null), - 'STRING_MENU', - null, - 'USER_SELECT', - 'ROLE_SELECT', - 'MENTIONABLE_SELECT', - 'CHANNEL_SELECT', - ]), - /** @deprecated Use `STRING_SELECT` instead */ - SELECT_MENU: 3, -}; +exports.SelectMenuComponentTypes = createEnum([ + ...new Array(3).fill(null), + 'STRING_MENU', + null, + 'USER_SELECT', + 'ROLE_SELECT', + 'MENTIONABLE_SELECT', + 'CHANNEL_SELECT', +]); /** * The style of a message button @@ -1767,27 +1558,6 @@ exports.TextInputStyles = createEnum([null, 'SHORT', 'PARAGRAPH']); */ exports.GuildScheduledEventPrivacyLevels = createEnum([null, null, 'GUILD_ONLY']); -/** - * Relationship Enums: - * * 0: NONE - * * 1: FRIEND - * * 2: BLOCKED - * * 3: PENDING_INCOMING - * * 4: PENDING_OUTGOING - * * 5: IMPLICIT - * @typedef {string} RelationshipTypes - * @see {@link https://luna.gitlab.io/discord-unofficial-docs/relationships.html} - */ - -exports.RelationshipTypes = createEnum([ - 'NONE', - 'FRIEND', - 'BLOCKED', - 'PENDING_INCOMING', - 'PENDING_OUTGOING', - 'IMPLICIT', -]); - /** * The premium tier (Server Boost level) of a guild: * * NONE @@ -1821,6 +1591,7 @@ exports.GuildScheduledEventStatuses = createEnum([null, 'SCHEDULED', 'ACTIVE', ' */ exports.GuildScheduledEventEntityTypes = createEnum([null, 'STAGE_INSTANCE', 'VOICE', 'EXTERNAL']); /* eslint-enable max-len */ + /** * The camera video quality mode of a {@link VoiceChannel}: * * AUTO @@ -1831,7 +1602,7 @@ exports.GuildScheduledEventEntityTypes = createEnum([null, 'STAGE_INSTANCE', 'VO exports.VideoQualityModes = createEnum([null, 'AUTO', 'FULL']); /** - * Sort {@link ForumChannel} posts by ? + * Sort {@link ForumChannel} posts by creation time or activity * * LATEST_ACTIVITY * * CREATION_DATE * @typedef {string} SortOrderType @@ -1849,10 +1620,31 @@ exports.SortOrderTypes = createEnum([null, 'LATEST_ACTIVITY', 'CREATION_DATE']); */ exports.ForumLayoutTypes = createEnum(['NOT_SET', 'LIST_VIEW', 'GALLERY_VIEW']); +/** + * Relationship Enums: + * * 0: NONE + * * 1: FRIEND + * * 2: BLOCKED + * * 3: PENDING_INCOMING + * * 4: PENDING_OUTGOING + * * 5: IMPLICIT + * @typedef {string} RelationshipTypes + * @see {@link https://luna.gitlab.io/discord-unofficial-docs/relationships.html} + */ + +exports.RelationshipTypes = createEnum([ + 'NONE', + 'FRIEND', + 'BLOCKED', + 'PENDING_INCOMING', + 'PENDING_OUTGOING', + 'IMPLICIT', +]); + exports._cleanupSymbol = Symbol('djsCleanup'); function keyMirror(arr) { - const tmp = Object.create(null); + let tmp = Object.create(null); for (const value of arr) tmp[value] = value; return tmp; } @@ -1907,12 +1699,11 @@ function createEnum(keys) { * @property {Object} InteractionResponseTypes The type of an interaction response. * @property {Object} InteractionTypes The type of an {@link Interaction} object. * @property {InviteScope[]} InviteScopes The scopes of an invite. - * @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age (Unavailable to selfbots) + * @property {Object} RelationshipTypes Relationship Enums * @property {Object} MembershipStates The value set for a team members membership state. * @property {Object} MessageButtonStyles The style of a message button. * @property {Object} MessageComponentTypes The type of a message component. * @property {MessageType[]} MessageTypes The type of a {@link Message} object. - * @property {Object} SelectMenuComponentTypes The type of any select menu. * @property {Object} MFALevels The required MFA level for a guild. * @property {Object} NSFWLevels NSFW level of a guild. * @property {Opcodes} Opcodes The types of Opcodes sent to the Gateway. @@ -1923,6 +1714,7 @@ function createEnum(keys) { * @property {Object} PrivacyLevels Privacy level of a {@link StageInstance} object. * @property {ShardEvents} ShardEvents The type of events emitted by a Shard. * @property {Status} Status The available statuses of the client. + * @property {Object} SelectMenuComponentTypes The type of any select menu. * @property {Object} StickerFormatTypes The value set for a stickers format type. * @property {Object} StickerTypes The value set for a stickers type. * @property {SweeperKey[]} SweeperKeys The name of an item to be swept in Sweepers. diff --git a/src/util/Options.js b/src/util/Options.js index 020eb5b..9e0846f 100644 --- a/src/util/Options.js +++ b/src/util/Options.js @@ -1,8 +1,8 @@ 'use strict'; -const JSONBig = require('json-bigint'); +const { UserAgent } = require('./Constants'); const Intents = require('./Intents'); -const { defaultUA } = require('../util/Constants'); + /** * Rate limit data * @typedef {Object} RateLimitData @@ -28,28 +28,34 @@ const { defaultUA } = require('../util/Constants'); * @returns {Collection} A Collection used to store the cache of the manager. */ +/** + * @typedef {Function} CaptchaSolver + * @param {Captcha} captcha Discord Captcha + * @param {string} UserAgent Current UserAgent + * @returns {Promise} HCaptcha Token + * @example + * const Captcha = require("2captcha") + * // A new 'solver' instance with our API key + * const solver = new Captcha.Solver("") + * function solveCaptcha(captcha, UA) { + * return solver.hcaptcha(captcha.captcha_sitekey, 'discord.com', { + * invisible: 1, + * userAgent: UA, + * data: captcha.captcha_rqdata, + * }).then(res => res.data) + * } + */ + /** * Options for a client. * @typedef {Object} ClientOptions - * @property {number|number[]|string} [shards] The shard's id to run, or an array of shard ids. If not specified, - * the client will spawn {@link ClientOptions#shardCount} shards. If set to `auto`, it will fetch the - * recommended amount of shards from Discord and spawn that amount + * @property {number} [messageCreateEventGuildTimeout=100] The amount of time in milliseconds that the Client to register for messages with each guild + * @property {number} [DMChannelVoiceStatusSync=0] The amount of time in milliseconds that the Client to register the event with each DM channel (0=Disable) + * @property {number} [captchaRetryLimit=3] Captcha retry limit + * @property {CaptchaSolver} [captchaSolver] Captcha Solver * @property {number} [closeTimeout=5000] The amount of time in milliseconds to wait for the close frame to be received - * from the WebSocket. Don't have this too high/low. Its best to have it between 2_000-6_000 ms. - * @property {boolean} [checkUpdate=true] Display module update information on the screen - * @property {boolean} [syncStatus=true] Sync state with Discord Client - * @property {boolean} [patchVoice=false] Automatically patch @discordjs/voice module (support for call) - * @property {string} [captchaService=null] Captcha service to use for solving captcha {@link captchaServices} - * @property {string} [captchaKey=null] Captcha service key - * @property {string} [captchaRetryLimit=3] Captcha retry limit - * @property {string} [captchaWithProxy=false] Whether to use proxy for captcha solving - * @property {string} [password=null] Your Discord account password - * @property {boolean} [usingNewAttachmentAPI=true] Use new attachment API - * @property {string} [interactionTimeout=15000] The amount of time in milliseconds to wait for an interaction response, before rejecting - * @property {boolean} [autoRedeemNitro=false] Automaticlly redeems nitro codes - * @property {string} [proxy] Proxy to use for the WebSocket + REST connection (proxy-agent uri type) {@link https://www.npmjs.com/package/proxy-agent}. - * @property {boolean} [DMSync=false] Automatically synchronize call status (DM and group) at startup (event synchronization) [Warning: May cause rate limit to gateway) - * @property {number} [shardCount=1] The total amount of shards used by all processes of this bot + * from the WebSocket. + * Don't have this too high/low. It's best to have it between 2000-6000 ms. * (e.g. recommended shard count, shard count of the ShardingManager) * @property {CacheFactory} [makeCache] Function to create a cache. * You can use your own function, or the {@link Options} class to customize the Collection used for the cache. @@ -86,23 +92,12 @@ const { defaultUA } = require('../util/Constants'); * @property {boolean} [failIfNotExists=true] Default value for {@link ReplyMessageOptions#failIfNotExists} * @property {string[]} [userAgentSuffix] An array of additional bot info to be appended to the end of the required * [User Agent](https://discord.com/developers/docs/reference#user-agent) header - * @property {PresenceData} [presence={}] Presence data to use upon login - * @property {IntentsResolvable} [intents=131071] Intents to enable for this connection (but not using) - * @property {number} [waitGuildTimeout=15000] Time in milliseconds that Clients with the GUILDS intent should wait for - * @property {number} [messageCreateEventGuildTimeout=100] Time in milliseconds that Clients to register for messages with each guild - * missing guilds to be received before starting the bot. If not specified, the default is 100 milliseconds. + * @property {PresenceData} [presence={ status: 'online', since: 0, activities: [], afk: false }] Presence data to use upon login + * @property {number} [waitGuildTimeout=15_000] Time in milliseconds that Clients with the GUILDS intent should wait for + * missing guilds to be received before starting the bot. If not specified, the default is 15 seconds. * @property {SweeperOptions} [sweepers={}] Options for cache sweeping * @property {WebsocketOptions} [ws] Options for the WebSocket * @property {HTTPOptions} [http] HTTP options - * @property {CustomCaptchaSolver} [captchaSolver] Function to solve a captcha (custom) - */ - -/** - * Function to solve a captcha - * @typedef {function} CustomCaptchaSolver - * @param {Captcha} captcha The captcha to solve - * @param {string} userAgent The user agent to use for the request - * @returns {Promise} hcaptcha token */ /** @@ -124,6 +119,7 @@ const { defaultUA } = require('../util/Constants'); /** * WebSocket options (these are left as snake_case to match the API) * @typedef {Object} WebsocketOptions + * @property {AgentOptions} [agent={}] HTTPS Agent options (WS Proxy) * @property {boolean} [compress=false] Whether to compress data sent on the connection * @property {WebSocketProperties} [properties] Properties to identify the client with */ @@ -158,23 +154,12 @@ class Options extends null { */ static createDefault() { return { - jsonTransformer: object => JSONBig.stringify(object), - captchaSolver: captcha => Promise.reject(new Error('CAPTCHA_SOLVER_NOT_IMPLEMENTED', captcha)), - closeTimeout: 5_000, - checkUpdate: true, - syncStatus: true, - autoRedeemNitro: false, - captchaService: '', - captchaKey: null, - captchaRetryLimit: 3, - captchaWithProxy: false, - DMSync: false, - patchVoice: false, - password: null, - usingNewAttachmentAPI: true, - interactionTimeout: 15_000, - waitGuildTimeout: 15_000, messageCreateEventGuildTimeout: 100, + DMChannelVoiceStatusSync: 0, + captchaRetryLimit: 3, + captchaSolver: () => Promise.reject(new Error('CAPTCHA_SOLVER_NOT_IMPLEMENTED')), + closeTimeout: 5_000, + waitGuildTimeout: 15_000, shardCount: 1, makeCache: this.cacheWithLimits(this.defaultMakeCacheSettings), messageCacheLifetime: 0, @@ -188,13 +173,11 @@ class Options extends null { retryLimit: 1, restTimeOffset: 500, restSweepInterval: 60, - failIfNotExists: false, + failIfNotExists: true, userAgentSuffix: [], presence: { status: 'online', since: 0, activities: [], afk: false }, sweepers: {}, - proxy: '', ws: { - // eslint-disable-next-line no-undef capabilities: 0, // https://discord-userdoccers.vercel.app/topics/gateway#gateway-capabilities properties: { os: 'Windows', @@ -205,7 +188,7 @@ class Options extends null { os_arch: 'x64', app_arch: 'ia32', system_locale: 'en-US', - browser_user_agent: defaultUA, + browser_user_agent: UserAgent, browser_version: '22.3.26', client_build_number: 244874, native_build_number: 39515, @@ -223,11 +206,12 @@ class Options extends null { api_code_version: 0, }, version: 9, + agent: {}, }, http: { agent: {}, headers: { - 'User-Agent': defaultUA, + 'User-Agent': UserAgent, }, version: 9, api: 'https://discord.com/api', @@ -328,7 +312,6 @@ class Options extends null { static get defaultMakeCacheSettings() { return { MessageManager: 200, - /* ChannelManager: { sweepInterval: 3600, sweepFilter: require('./Util').archivedThreadSweepFilter(), @@ -341,7 +324,6 @@ class Options extends null { sweepInterval: 3600, sweepFilter: require('./Util').archivedThreadSweepFilter(), }, - */ }; } }