diff --git a/.eslintrc.yml b/.eslintrc.yml index 4db6bea8..d6960da3 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,15 +1,12 @@ --- env: - es6: true + es2022: true node: true plugins: - "sort-class-members" extends: "eslint:recommended" globals: - BigInt: true window: true -parserOptions: - ecmaVersion: 2020 rules: array-bracket-spacing: - 2 @@ -65,12 +62,14 @@ rules: sort-class-members/sort-class-members: - 2 - order: + - "[alphabetical-properties]" - constructor - update - "[alphabetical-getters]" - "[alphabetical-methods]" - "[alphabetical-conventional-private-methods]" - "[everything-else]" + - "[alphabetical-private-methods]" - "[custom-inspect-method]" - toString - toJSON @@ -81,10 +80,16 @@ rules: alphabetical-methods: - type: method sort: alphabetical + alphabetical-properties: + - type: property + sort: alphabetical alphabetical-conventional-private-methods: - name: "/_.+/" type: method sort: alphabetical + alphabetical-private-methods: + - type: method + sort: alphabetical custom-inspect-method: - name: "[util.inspect.custom]" type: method diff --git a/README.md b/README.md index b4bce93f..da9131fe 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A fork of [Eris](https://github.com/abalabahaha/eris), a Node.js wrapper for int Installing ---------- -You will need NodeJS 10.4+. Voice support requires [additional software](https://github.com/nodejs/node-gyp#installation). +You will need Node.js 18+. Voice support requires [additional software](https://github.com/nodejs/node-gyp#installation). ``` npm install --omit=optional @projectdysnomia/dysnomia diff --git a/lib/Client.js b/lib/Client.js index 816416c0..670cce42 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -31,13 +31,13 @@ const AutoModerationRule = require("./structures/AutoModerationRule"); let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events"); +} catch{ + EventEmitter = require("node:events"); } let Erlpack; try { Erlpack = require("erlpack"); -} catch(err) { // eslint-disable no-empty +} catch{ // eslint-disable no-empty } const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); @@ -65,6 +65,27 @@ const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); * @prop {Collection} voiceConnections Extended collection of active VoiceConnections the bot has */ class Client extends EventEmitter { + channelGuildMap = {}; + guilds = new Collection(Guild); + guildShardMap = {}; + lastConnect = 0; + lastReconnectDelay = 0; + presence = { + activities: null, + afk: false, + since: null, + status: "offline" + }; + privateChannelMap = {}; + privateChannels = new Collection(PrivateChannel); + ready = false; + reconnectAttempts = 0; + startTime = 0; + threadGuildMap = {}; + unavailableGuilds = new Collection(UnavailableGuild); + users = new Collection(User); + voiceConnections = new VoiceConnectionManager(); + /** * Create a Client * @arg {String} token The auth token to use. Bot tokens should be prefixed with `Bot` (e.g. `Bot MTExIHlvdSAgdHJpZWQgMTEx.O5rKAA.dQw4w9WgXcQ_wpV-gGA4PSk_bm8`). @@ -134,8 +155,8 @@ class Client extends EventEmitter { throw new TypeError(`Invalid default image size: ${defaultImageSize}`); } // Set HTTP Agent on Websockets if not already set - if(this.options.rest.agent && !(this.options.ws && this.options.ws.agent)) { - this.options.ws = this.options.ws || {}; + if(this.options.rest.agent && !this.options.ws?.agent) { + this.options.ws ??= {}; this.options.ws.agent = this.options.rest.agent; } @@ -152,29 +173,9 @@ class Client extends EventEmitter { this.shards = new ShardManager(this, this.options.gateway); delete this.options.gateway; - this.ready = false; this.bot = this._token.startsWith("Bot "); - this.startTime = 0; - this.lastConnect = 0; - this.channelGuildMap = {}; - this.threadGuildMap = {}; - this.guilds = new Collection(Guild); - this.privateChannelMap = {}; - this.privateChannels = new Collection(PrivateChannel); - this.guildShardMap = {}; - this.unavailableGuilds = new Collection(UnavailableGuild); - this.users = new Collection(User); - this.presence = { - activities: null, - afk: false, - since: null, - status: "offline" - }; - this.voiceConnections = new VoiceConnectionManager(); this.connect = this.connect.bind(this); - this.lastReconnectDelay = 0; - this.reconnectAttempts = 0; } get uptime() { @@ -343,12 +344,10 @@ class Client extends EventEmitter { throw new Error("Failed to autoshard due to lack of data from Discord."); } this.shards.options.maxShards = data.shards; - if(this.shards.options.lastShardID === undefined) { - this.shards.options.lastShardID = data.shards - 1; - } + this.shards.options.lastShardID ??= data.shards - 1; } - if(this.shards.options.shardConcurrency === "auto" && data.session_start_limit && typeof data.session_start_limit.max_concurrency === "number") { + if(this.shards.options.shardConcurrency === "auto" && typeof data.session_start_limit?.max_concurrency === "number") { this.shards.options.maxConcurrency = data.session_start_limit.max_concurrency; } @@ -423,7 +422,7 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.GUILD_CHANNELS(guildID), true, { name: name, type: type, - available_tags: options.availableTags && options.availableTags.map((tag) => ({ + available_tags: options.availableTags?.map((tag) => ({ id: tag.id, name: tag.name, moderated: tag.moderated, @@ -1211,7 +1210,7 @@ class Client extends EventEmitter { return this.requestHandler.request("PATCH", Endpoints.CHANNEL(channelID), true, { archived: options.archived, auto_archive_duration: options.autoArchiveDuration, - available_tags: options.availableTags && options.availableTags.map((tag) => ({ + available_tags: options.availableTags?.map((tag) => ({ id: tag.id, name: tag.name, moderated: tag.moderated, @@ -1477,7 +1476,7 @@ class Client extends EventEmitter { */ editGuildMember(guildID, memberID, options, reason) { return this.requestHandler.request("PATCH", Endpoints.GUILD_MEMBER(guildID, memberID), true, { - roles: options.roles && options.roles.filter((roleID, index) => options.roles.indexOf(roleID) === index), + roles: options.roles?.filter((roleID, index) => options.roles.indexOf(roleID) === index), nick: options.nick, mute: options.mute, deaf: options.deaf, @@ -2099,9 +2098,7 @@ class Client extends EventEmitter { * @returns {Promise<{autoModerationRules: Array, commands: Array, entries: Array, events: Array, integrations: Array, threads: Array, users: Array, webhooks: Array}>} */ getGuildAuditLog(guildID, options = {}) { - if(options.limit === undefined) { // Legacy behavior - options.limit = 50; - } + options.limit ??= 50; // Legacy behavior if(options.actionType !== undefined) { options.action_type = options.actionType; } @@ -2405,9 +2402,7 @@ class Client extends EventEmitter { if(reaction === decodeURI(reaction)) { reaction = encodeURIComponent(reaction); } - if(options.limit === undefined) { // Legacy behavior - options.limit = 100; - } + options.limit ??= 100; // Legacy behavior return this.requestHandler.request("GET", Endpoints.CHANNEL_MESSAGE_REACTION(channelID, messageID, reaction), true, options).then((users) => users.map((user) => new User(user, this))); } @@ -2422,9 +2417,7 @@ class Client extends EventEmitter { * @returns {Promise>} */ async getMessages(channelID, options = {}) { - if(options.limit === undefined) { // Legacy behavior - options.limit = 50; - } + options.limit ??= 50; // Legacy behavior let limit = options.limit; if(limit && limit > 100) { let logs = []; @@ -2797,7 +2790,7 @@ class Client extends EventEmitter { if(!channel) { return Promise.reject(new Error("Channel not found")); } - if(channel.guild && channel.guild.members.has(this.user.id) && !(channel.permissionsOf(this.user.id).allow & Constants.Permissions.voiceConnect)) { + if(channel.guild?.members.has(this.user.id) && !(channel.permissionsOf(this.user.id).allow & Constants.Permissions.voiceConnect)) { return Promise.reject(new Error("Insufficient permission to connect to voice channel")); } this.shards.get(this.guildShardMap[this.channelGuildMap[channelID]] || 0).sendWS(Constants.GatewayOPCodes.VOICE_STATE_UPDATE, { @@ -2806,9 +2799,7 @@ class Client extends EventEmitter { self_mute: options.selfMute || false, self_deaf: options.selfDeaf || false }); - if(options.opusOnly === undefined) { - options.opusOnly = this.options.opusOnly; - } + options.opusOnly ??= this.options.opusOnly; return this.voiceConnections.join(this.channelGuildMap[channelID], channelID, options); } diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 1ec76ab9..16f53d6a 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -1,6 +1,6 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); const Bucket = require("../util/Bucket"); const Channel = require("../structures/Channel"); @@ -25,21 +25,21 @@ const WebSocket = typeof window !== "undefined" ? require("../util/BrowserWebSoc let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events").EventEmitter; +} catch{ + EventEmitter = require("node:events").EventEmitter; } let Erlpack; try { Erlpack = require("erlpack"); -} catch(err) { // eslint-disable no-empty +} catch{ // eslint-disable no-empty } let ZlibSync; try { ZlibSync = require("zlib-sync"); -} catch(err) { +} catch{ try { ZlibSync = require("pako"); - } catch(err) { // eslint-disable no-empty + } catch{ // eslint-disable no-empty } } @@ -56,6 +56,12 @@ try { * @prop {String} status The status of the shard. "disconnected"/"connecting"/"handshaking"/"ready"/"identifying"/"resuming" */ class Shard extends EventEmitter { + #onWSClose; + #onWSError; + #onWSMessage; + #onWSOpen; + #token; + #zlibSync; constructor(id, client) { super(); @@ -63,10 +69,10 @@ class Shard extends EventEmitter { this.client = client; this.onPacket = this.onPacket.bind(this); - this._onWSOpen = this._onWSOpen.bind(this); - this._onWSMessage = this._onWSMessage.bind(this); - this._onWSError = this._onWSError.bind(this); - this._onWSClose = this._onWSClose.bind(this); + this.#onWSOpen = this.#onWSOpenUnbound.bind(this); + this.#onWSMessage = this.#onWSMessageUnbound.bind(this); + this.#onWSError = this.#onWSErrorUnbound.bind(this); + this.#onWSClose = this.#onWSCloseUnbound.bind(this); this.hardReset(); } @@ -126,8 +132,8 @@ class Shard extends EventEmitter { } if(this.ws.readyState !== WebSocket.CLOSED) { - this.ws.removeListener("message", this._onWSMessage); - this.ws.removeListener("close", this._onWSClose); + this.ws.removeListener("message", this.#onWSMessage); + this.ws.removeListener("close", this.#onWSClose); try { if(options.reconnect && this.sessionID) { if(this.ws.readyState === WebSocket.OPEN) { @@ -246,12 +252,7 @@ class Shard extends EventEmitter { this.globalBucket = new Bucket(120, 60000, {reservedTokens: 5}); this.presenceUpdateBucket = new Bucket(5, 20000); this.presence = JSON.parse(JSON.stringify(this.client.presence)); // Fast copy - Object.defineProperty(this, "_token", { - configurable: true, - enumerable: false, - writable: true, - value: this.client._token - }); + this.#token = this.client._token; } heartbeat(normal) { @@ -291,7 +292,7 @@ class Shard extends EventEmitter { } this.status = "identifying"; const identify = { - token: this._token, + token: this.#token, v: GATEWAY_VERSION, compress: !!this.client.shards.options.compress, large_threshold: this.client.shards.options.largeThreshold, @@ -312,14 +313,14 @@ class Shard extends EventEmitter { } initializeWS() { - if(!this._token) { + if(!this.#token) { return this.disconnect(null, new Error("Token not specified")); } this.status = "connecting"; if(this.client.shards.options.compress) { this.emit("debug", "Initializing zlib-sync-based compression"); - this._zlibSync = new ZlibSync.Inflate({ + this.#zlibSync = new ZlibSync.Inflate({ chunkSize: 128 * 1024 }); } @@ -331,10 +332,10 @@ class Shard extends EventEmitter { } else { this.ws = new WebSocket(this.client.gatewayURL, this.client.options.ws); } - this.ws.on("open", this._onWSOpen); - this.ws.on("message", this._onWSMessage); - this.ws.on("error", this._onWSError); - this.ws.on("close", this._onWSClose); + this.ws.on("open", this.#onWSOpen); + this.ws.on("message", this.#onWSMessage); + this.ws.on("error", this.#onWSError); + this.ws.on("close", this.#onWSClose); this.connectTimeout = setTimeout(() => { if(this.connecting) { @@ -442,11 +443,11 @@ class Shard extends EventEmitter { requestGuildMembers(guildID, options) { const opts = { guild_id: guildID, - limit: (options && options.limit) || 0, - user_ids: options && options.userIDs, - query: options && options.query, + limit: options?.limit || 0, + user_ids: options?.userIDs, + query: options?.query, nonce: Date.now().toString() + Math.random().toString(36), - presences: options && options.presences + presences: options?.presences }; if(!opts.user_ids && !opts.query) { opts.query = ""; @@ -457,7 +458,7 @@ class Shard extends EventEmitter { if(opts.presences && (this.client.shards.options.intents && !(this.client.shards.options.intents & Constants.Intents.guildPresences))) { throw new Error("Cannot request members presences without guildPresences intent"); } - if(opts.user_ids && opts.user_ids.length > 100) { + if(opts.user_ids?.length > 100) { throw new Error("Cannot request more than 100 users by their ID"); } this.sendWS(GatewayOPCodes.REQUEST_GUILD_MEMBERS, opts); @@ -468,7 +469,7 @@ class Shard extends EventEmitter { timeout: setTimeout(() => { res(this.requestMembersPromise[opts.nonce].members); delete this.requestMembersPromise[opts.nonce]; - }, (options && options.timeout) || this.client.shards.options.requestTimeout) + }, options?.timeout || this.client.shards.options.requestTimeout) }); } @@ -516,7 +517,7 @@ class Shard extends EventEmitter { resume() { this.status = "resuming"; this.sendWS(GatewayOPCodes.RESUME, { - token: this._token, + token: this.#token, session_id: this.sessionID, seq: this.seq }); @@ -532,11 +533,11 @@ class Shard extends EventEmitter { } sendWS(op, _data, priority = false) { - if(this.ws && this.ws.readyState === WebSocket.OPEN) { + if(this.ws?.readyState === WebSocket.OPEN) { let i = 0; let waitFor = 1; const func = () => { - if(++i >= waitFor && this.ws && this.ws.readyState === WebSocket.OPEN) { + if(++i >= waitFor && this.ws?.readyState === WebSocket.OPEN) { const data = Erlpack ? Erlpack.pack({op: op, d: _data}) : JSON.stringify({op: op, d: _data}); this.ws.send(data); if(_data.token) { @@ -900,7 +901,7 @@ class Shard extends EventEmitter { * @event Client#messageDelete * @prop {Message | Object} message The message object. If the message is not cached, this will be an object with `id` and `channel` keys. If the channel is not cached, channel will be an object with an `id` key. If the uncached message is from a guild, the message will also contain a `guildID` key, and the channel will contain a `guild` with an `id` key. No other property is guaranteed. */ - this.emit("messageDelete", (channel && channel.messages.remove(packet.d)) || { + this.emit("messageDelete", channel?.messages.remove(packet.d) || { id: packet.d.id, channel: channel || { id: packet.d.channel_id, @@ -921,7 +922,7 @@ class Shard extends EventEmitter { * @event Client#messageDeleteBulk * @prop {Array | Array} messages An array of (potentially partial) message objects. If a message is not cached, it will be an object with `id` and `channel` keys If the uncached messages are from a guild, the messages will also contain a `guildID` key, and the channel will contain a `guild` with an `id` key. No other property is guaranteed */ - this.emit("messageDeleteBulk", packet.d.ids.map((id) => (channel && channel.messages.remove({ + this.emit("messageDeleteBulk", packet.d.ids.map((id) => (channel?.messages.remove({ id }) || { id: id, @@ -932,16 +933,13 @@ class Shard extends EventEmitter { } case "MESSAGE_REACTION_ADD": { const channel = this.client.getChannel(packet.d.channel_id); - let message; + let message = channel?.messages.get(packet.d.message_id); let member; - if(channel) { - message = channel.messages.get(packet.d.message_id); - if(channel.guild) { - if(packet.d.member) { - // Updates the member cache with this member for future events. - packet.d.member.id = packet.d.user_id; - member = channel.guild.members.update(packet.d.member, channel.guild); - } + if(channel?.guild) { + if(packet.d.member) { + // Updates the member cache with this member for future events. + packet.d.member.id = packet.d.user_id; + member = channel.guild.members.update(packet.d.member, channel.guild); } } if(message) { @@ -965,9 +963,7 @@ class Shard extends EventEmitter { if(packet.d.guild_id) { message.guildID = packet.d.guild_id; - if(!message.channel.guild) { - message.channel.guild = {id: packet.d.guild_id}; - } + message.channel.guild ??= {id: packet.d.guild_id}; } } /** @@ -985,10 +981,7 @@ class Shard extends EventEmitter { } case "MESSAGE_REACTION_REMOVE": { const channel = this.client.getChannel(packet.d.channel_id); - let message; - if(channel) { - message = channel.messages.get(packet.d.message_id); - } + let message = channel?.messages.get(packet.d.message_id); if(message) { const reaction = packet.d.emoji.id ? `${packet.d.emoji.name}:${packet.d.emoji.id}` : packet.d.emoji.name; const reactionObj = message.reactions[reaction]; @@ -1008,9 +1001,7 @@ class Shard extends EventEmitter { if(packet.d.guild_id) { message.guildID = packet.d.guild_id; - if(!message.channel.guild) { - message.channel.guild = {id: packet.d.guild_id}; - } + message.channel.guild ??= {id: packet.d.guild_id}; } } /** @@ -1028,12 +1019,9 @@ class Shard extends EventEmitter { } case "MESSAGE_REACTION_REMOVE_ALL": { const channel = this.client.getChannel(packet.d.channel_id); - let message; - if(channel) { - message = channel.messages.get(packet.d.message_id); - if(message) { - message.reactions = {}; - } + let message = channel?.messages.get(packet.d.message_id); + if(message) { + message.reactions = {}; } if(!message) { message = { @@ -1042,9 +1030,7 @@ class Shard extends EventEmitter { }; if(packet.d.guild_id) { message.guildID = packet.d.guild_id; - if(!message.channel.guild) { - message.channel.guild = {id: packet.d.guild_id}; - } + message.channel.guild ??= {id: packet.d.guild_id}; } } /** @@ -1057,13 +1043,10 @@ class Shard extends EventEmitter { } case "MESSAGE_REACTION_REMOVE_EMOJI": { const channel = this.client.getChannel(packet.d.channel_id); - let message; - if(channel) { - message = channel.messages.get(packet.d.message_id); - if(message) { - const reaction = packet.d.emoji.id ? `${packet.d.emoji.name}:${packet.d.emoji.id}` : packet.d.emoji.name; - delete message.reactions[reaction]; - } + let message = channel?.messages.get(packet.d.message_id); + if(message) { + const reaction = packet.d.emoji.id ? `${packet.d.emoji.name}:${packet.d.emoji.id}` : packet.d.emoji.name; + delete message.reactions[reaction]; } if(!message) { message = { @@ -1072,9 +1055,7 @@ class Shard extends EventEmitter { }; if(packet.d.guild_id) { message.guildID = packet.d.guild_id; - if(!message.channel.guild) { - message.channel.guild = {id: packet.d.guild_id}; - } + message.channel.guild ??= {id: packet.d.guild_id}; } } /** @@ -1304,11 +1285,9 @@ class Shard extends EventEmitter { delete this.client.guildShardMap[packet.d.id]; const guild = this.client.guilds.remove(packet.d); - if(guild) { // Discord sends GUILD_DELETE for guilds that were previously unavailable in READY - guild.channels.forEach((channel) => { - delete this.client.channelGuildMap[channel.id]; - }); - } + guild?.channels.forEach((channel) => { // Discord sends GUILD_DELETE for guilds that were previously unavailable in READY + delete this.client.channelGuildMap[channel.id]; + }); if(packet.d.unavailable) { /** * Fired when a guild becomes unavailable @@ -1483,12 +1462,10 @@ class Shard extends EventEmitter { case "CHANNEL_CREATE": { const channel = Channel.from(packet.d, this.client); if(packet.d.guild_id) { + channel.guild ??= this.client.guilds.get(packet.d.guild_id); if(!channel.guild) { - channel.guild = this.client.guilds.get(packet.d.guild_id); - if(!channel.guild) { - this.emit("debug", `Received CHANNEL_CREATE for channel in missing guild ${packet.d.guild_id}`); - break; - } + this.emit("debug", `Received CHANNEL_CREATE for channel in missing guild ${packet.d.guild_id}`); + break; } channel.guild.channels.add(channel, this.client); this.client.channelGuildMap[packet.d.id] = packet.d.guild_id; @@ -1635,14 +1612,10 @@ class Shard extends EventEmitter { return guild.members.add(member, guild); }); - if(packet.d.presences) { - packet.d.presences.forEach((presence) => { - const member = guild.members.get(presence.user.id); - if(member) { - member.update(presence); - } - }); - } + packet.d.presences?.forEach((presence) => { + const member = guild.members.get(presence.user.id); + member?.update(presence); + }); if(this.requestMembersPromise.hasOwnProperty(packet.d.nonce)) { this.requestMembersPromise[packet.d.nonce].members.push(...members); @@ -1838,15 +1811,13 @@ class Shard extends EventEmitter { } case "THREAD_CREATE": { const channel = Channel.from(packet.d, this.client); + channel.guild ??= this.client.guilds.get(packet.d.guild_id); if(!channel.guild) { - channel.guild = this.client.guilds.get(packet.d.guild_id); - if(!channel.guild) { - this.emit("debug", `Received THREAD_CREATE for channel in missing guild ${packet.d.guild_id}`); - break; - } + this.emit("debug", `Received THREAD_CREATE for channel in missing guild ${packet.d.guild_id}`); + break; } const parent = this.client.getChannel(packet.d.parent_id); - if(parent && parent.type === ChannelTypes.GUILD_FORUM) { + if(parent?.type === ChannelTypes.GUILD_FORUM) { parent.lastThreadID = packet.d.id; } @@ -1973,31 +1944,25 @@ class Shard extends EventEmitter { break; } channel.update(packet.d); - let addedMembers; - let removedMembers; - if(packet.d.added_members) { - addedMembers = packet.d.added_members.map((m) => { - if(m.presence) { - m.presence.id = m.presence.user.id; - this.client.users.update(m.presence.user, this.client); - } + const addedMembers = packet.d.added_members?.map((m) => { + if(m.presence) { + m.presence.id = m.presence.user.id; + this.client.users.update(m.presence.user, this.client); + } - m.thread_id = m.id; - m.id = m.user_id; - m.member.id = m.member.user.id; - const guild = this.client.guilds.get(packet.d.guild_id); - if(guild) { - if(m.presence) { - guild.members.update(m.presence, guild); - } - guild.members.update(m.member, guild); + m.thread_id = m.id; + m.id = m.user_id; + m.member.id = m.member.user.id; + const guild = this.client.guilds.get(packet.d.guild_id); + if(guild) { + if(m.presence) { + guild.members.update(m.presence, guild); } - return channel.members.update(m, this.client); - }); - } - if(packet.d.removed_member_ids) { - removedMembers = packet.d.removed_member_ids.map((id) => channel.members.remove({id}) || {id}); - } + guild.members.update(m.member, guild); + } + return channel.members.update(m, this.client); + }); + const removedMembers = packet.d.removed_member_ids?.map((id) => channel.members.remove({id}) || {id}); /** * Fired when anyone is added or removed from a thread. If the `guildMembers` intent is not specified, this will only apply for the current user * @event Client#threadMembersUpdate @@ -2293,7 +2258,7 @@ class Shard extends EventEmitter { } /* eslint-enable no-redeclare */ } - _onWSClose(code, reason) { + #onWSCloseUnbound(code, reason) { reason = reason.toString(); this.emit("debug", "WS disconnected: " + JSON.stringify({ code: code, @@ -2317,7 +2282,7 @@ class Shard extends EventEmitter { this.sessionID = null; this.resumeURL = null; reconnect = false; - this.emit("error", new Error(`Invalid token: ${this._token}`)); + this.emit("error", new Error(`Invalid token: ${this.#token}`)); } else if(code === 4005) { err = new Error("Already authenticated"); } else if(code === 4006 || code === 4009) { @@ -2365,11 +2330,11 @@ class Shard extends EventEmitter { }, err); } - _onWSError(err) { + #onWSErrorUnbound(err) { this.emit("error", err, this.id); } - _onWSMessage(data) { + #onWSMessageUnbound(data) { try { if(data instanceof ArrayBuffer) { if(this.client.shards.options.compress || Erlpack) { @@ -2380,13 +2345,13 @@ class Shard extends EventEmitter { } if(this.client.shards.options.compress) { if(data.length >= 4 && data.readUInt32BE(data.length - 4) === 0xFFFF) { - this._zlibSync.push(data, ZlibSync.Z_SYNC_FLUSH); - if(this._zlibSync.err) { - this.emit("error", new Error(`zlib error ${this._zlibSync.err}: ${this._zlibSync.msg}`)); + this.#zlibSync.push(data, ZlibSync.Z_SYNC_FLUSH); + if(this.#zlibSync.err) { + this.emit("error", new Error(`zlib error ${this.#zlibSync.err}: ${this.#zlibSync.msg}`)); return; } - data = Buffer.from(this._zlibSync.result); + data = Buffer.from(this.#zlibSync.result); try { if(Erlpack) { @@ -2401,7 +2366,7 @@ class Shard extends EventEmitter { return this.onPacket(data); } else { - this._zlibSync.push(data, false); + this.#zlibSync.push(data, false); } } else if(Erlpack) { return this.onPacket(Erlpack.unpack(data)); @@ -2413,7 +2378,7 @@ class Shard extends EventEmitter { } } - _onWSOpen() { + #onWSOpenUnbound() { this.status = "handshaking"; /** * Fired when the shard establishes a connection diff --git a/lib/gateway/ShardManager.js b/lib/gateway/ShardManager.js index 5f273dce..c7b28dca 100644 --- a/lib/gateway/ShardManager.js +++ b/lib/gateway/ShardManager.js @@ -7,16 +7,21 @@ const Constants = require("../Constants"); let ZlibSync; try { ZlibSync = require("zlib-sync"); -} catch(err) { +} catch{ try { ZlibSync = require("pako"); - } catch(err) { // eslint-disable no-empty + } catch{ // eslint-disable no-empty } } class ShardManager extends Collection { + #client; + buckets = new Map(); + connectQueue = []; + connectTimeout = null; + constructor(client, options = {}) { super(Shard); - this._client = client; + this.#client = client; this.options = Object.assign({ autoreconnect: true, compress: false, @@ -46,7 +51,7 @@ class ShardManager extends Collection { } else if(typeof intent === "number") { bitmask |= intent; } else { - this._client.emit("warn", `Unknown intent: ${intent}`); + this.#client.emit("warn", `Unknown intent: ${intent}`); } } this.options.intents = bitmask; @@ -65,10 +70,6 @@ class ShardManager extends Collection { if(typeof window !== "undefined" || !ZlibSync) { this.options.compress = false; // zlib does not like Blobs, Pako is not here } - - this.buckets = new Map(); - this.connectQueue = []; - this.connectTimeout = null; } connect(shard) { @@ -79,15 +80,15 @@ class ShardManager extends Collection { spawn(id) { let shard = this.get(id); if(!shard) { - shard = this.add(new Shard(id, this._client)); + shard = this.add(new Shard(id, this.#client)); shard.on("ready", () => { /** * Fired when a shard turns ready * @event Client#shardReady * @prop {Number} id The ID of the shard */ - this._client.emit("shardReady", shard.id); - if(this._client.ready) { + this.#client.emit("shardReady", shard.id); + if(this.#client.ready) { return; } for(const other of this.values()) { @@ -95,21 +96,21 @@ class ShardManager extends Collection { return; } } - this._client.ready = true; - this._client.startTime = Date.now(); + this.#client.ready = true; + this.#client.startTime = Date.now(); /** * Fired when all shards turn ready * @event Client#ready */ - this._client.emit("ready"); + this.#client.emit("ready"); }).on("resume", () => { /** * Fired when a shard resumes * @event Client#shardResume * @prop {Number} id The ID of the shard */ - this._client.emit("shardResume", shard.id); - if(this._client.ready) { + this.#client.emit("shardResume", shard.id); + if(this.#client.ready) { return; } for(const other of this.values()) { @@ -117,9 +118,9 @@ class ShardManager extends Collection { return; } } - this._client.ready = true; - this._client.startTime = Date.now(); - this._client.emit("ready"); + this.#client.ready = true; + this.#client.startTime = Date.now(); + this.#client.emit("ready"); }).on("disconnect", (error) => { /** * Fired when a shard disconnects @@ -127,19 +128,19 @@ class ShardManager extends Collection { * @prop {Error?} error The error, if any * @prop {Number} id The ID of the shard */ - this._client.emit("shardDisconnect", error, shard.id); + this.#client.emit("shardDisconnect", error, shard.id); for(const other of this.values()) { if(other.ready) { return; } } - this._client.ready = false; - this._client.startTime = 0; + this.#client.ready = false; + this.#client.startTime = 0; /** * Fired when all shards disconnect * @event Client#disconnect */ - this._client.emit("disconnect"); + this.#client.emit("disconnect"); }); } if(shard.status === "disconnected") { diff --git a/lib/rest/RequestHandler.js b/lib/rest/RequestHandler.js index 4d118f8b..ae549b8e 100644 --- a/lib/rest/RequestHandler.js +++ b/lib/rest/RequestHandler.js @@ -1,20 +1,25 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); const DiscordHTTPError = require("../errors/DiscordHTTPError"); const DiscordRESTError = require("../errors/DiscordRESTError"); const Endpoints = require("./Endpoints"); -const HTTPS = require("https"); -const HTTP = require("http"); +const HTTPS = require("node:https"); +const HTTP = require("node:http"); const MultipartData = require("../util/MultipartData"); const SequentialBucket = require("../util/SequentialBucket"); -const Zlib = require("zlib"); +const Zlib = require("node:zlib"); /** * Handles API requests */ class RequestHandler { + #client; + globalBlock = false; + ratelimits = {}; + readyQueue = []; + userAgent = `DiscordBot (https://github.com/projectdysnomia/dysnomia, ${require("../../package.json").version})`; constructor(client, options) { this.options = options = Object.assign({ agent: null, @@ -27,9 +32,7 @@ class RequestHandler { requestTimeout: 15000 }, options); - this._client = client; - this.userAgent = `DiscordBot (https://github.com/projectdysnomia/dysnomia, ${require("../../package.json").version})`; - this.ratelimits = {}; + this.#client = client; this.latencyRef = { latency: this.options.ratelimiterOffset, raw: new Array(10).fill(this.options.ratelimiterOffset), @@ -37,11 +40,9 @@ class RequestHandler { timeOffsets: new Array(10).fill(0), lastTimeOffsetCheck: 0 }; - this.globalBlock = false; - this.readyQueue = []; if(this.options.forceQueueing) { this.globalBlock = true; - this._client.once("shardPreReady", () => this.globalUnblock()); + this.#client.once("shardPreReady", () => this.globalUnblock()); } } @@ -83,12 +84,11 @@ class RequestHandler { try { if(auth) { - headers.Authorization = this._client._token; + headers.Authorization = this.#client._token; } - if(body && body.reason) { // Audit log reason sniping + if(body?.reason) { // Audit log reason sniping headers["X-Audit-Log-Reason"] = encodeURIComponent(body.reason); delete body.reason; - } if(file) { if(Array.isArray(file)) { @@ -184,7 +184,7 @@ class RequestHandler { this.latencyRef.latency = this.latencyRef.latency - ~~(this.latencyRef.raw.shift() / 10) + ~~(latency / 10); } - if(this._client.listeners("rawREST").length) { + if(this.#client.listeners("rawREST").length) { /** * Fired when the Client's RequestHandler receives a response * @event Client#rawREST @@ -201,14 +201,14 @@ class RequestHandler { * @prop {Boolean} request.short Whether or not the request was prioritized in its ratelimiting queue * @prop {String} request.url URL of the endpoint */ - this._client.emit("rawREST", {method, url, auth, body, file, route, short, resp, latency}); + this.#client.emit("rawREST", {method, url, auth, body, file, route, short, resp, latency}); } const headerNow = Date.parse(resp.headers["date"]); if(this.latencyRef.lastTimeOffsetCheck < Date.now() - 5000) { const timeOffset = headerNow + 500 - (this.latencyRef.lastTimeOffsetCheck = Date.now()); if(this.latencyRef.timeOffset - this.latencyRef.latency >= this.options.latencyThreshold && timeOffset - this.latencyRef.latency >= this.options.latencyThreshold) { - this._client.emit("warn", new Error(`Your clock is ${this.latencyRef.timeOffset}ms behind Discord's server clock. Please check your connection and system time.`)); + this.#client.emit("warn", new Error(`Your clock is ${this.latencyRef.timeOffset}ms behind Discord's server clock. Please check your connection and system time.`)); } this.latencyRef.timeOffset = this.latencyRef.timeOffset - ~~(this.latencyRef.timeOffsets.shift() / 10) + ~~(timeOffset / 10); this.latencyRef.timeOffsets.push(timeOffset); @@ -245,7 +245,7 @@ class RequestHandler { } if(method !== "GET" && (resp.headers["x-ratelimit-remaining"] == undefined || resp.headers["x-ratelimit-limit"] == undefined) && this.ratelimits[route].limit !== 1) { - this._client.emit("debug", `Missing ratelimit headers for SequentialBucket(${this.ratelimits[route].remaining}/${this.ratelimits[route].limit}) with non-default limit\n` + this.#client.emit("debug", `Missing ratelimit headers for SequentialBucket(${this.ratelimits[route].remaining}/${this.ratelimits[route].limit}) with non-default limit\n` + `${resp.statusCode} ${resp.headers["content-type"]}: ${method} ${route} | ${resp.headers["cf-ray"]}\n` + "content-type = " + + "\n" + "x-ratelimit-remaining = " + resp.headers["x-ratelimit-remaining"] + "\n" @@ -276,7 +276,7 @@ class RequestHandler { if(resp.statusCode !== 429) { const content = typeof body === "object" ? `${body.content} ` : ""; - this._client.emit("debug", `${content}${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${this.ratelimits[route].reset} (${this.ratelimits[route].reset - now}ms left)`); + this.#client.emit("debug", `${content}${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${this.ratelimits[route].reset} (${this.ratelimits[route].reset - now}ms left)`); } if(resp.statusCode >= 300) { @@ -291,7 +291,7 @@ class RequestHandler { return; } } - this._client.emit("debug", `${resp.headers["x-ratelimit-global"] ? "Global" : "Unexpected"} 429 (╯°□°)╯︵ ┻━┻: ${response}\n${content} ${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${delay} (${this.ratelimits[route].reset - now}ms left) | Scope ${resp.headers["x-ratelimit-scope"]}`); + this.#client.emit("debug", `${resp.headers["x-ratelimit-global"] ? "Global" : "Unexpected"} 429 (╯°□°)╯︵ ┻━┻: ${response}\n${content} ${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${delay} (${this.ratelimits[route].reset - now}ms left) | Scope ${resp.headers["x-ratelimit-scope"]}`); if(delay) { setTimeout(() => { cb(); @@ -304,7 +304,7 @@ class RequestHandler { return; } } else if(resp.statusCode === 502 && ++attempts < 4) { - this._client.emit("debug", "A wild 502 appeared! Thanks CloudFlare!"); + this.#client.emit("debug", "A wild 502 appeared! Thanks CloudFlare!"); setTimeout(() => { this.request(method, url, auth, body, file, route, true).then(resolve).catch(reject); }, Math.floor(Math.random() * 1900 + 100)); @@ -371,15 +371,11 @@ class RequestHandler { if(this.globalBlock && auth) { this.readyQueue.push(() => { - if(!this.ratelimits[route]) { - this.ratelimits[route] = new SequentialBucket(1, this.latencyRef); - } + this.ratelimits[route] ??= new SequentialBucket(1, this.latencyRef); this.ratelimits[route].queue(actualCall, short); }); } else { - if(!this.ratelimits[route]) { - this.ratelimits[route] = new SequentialBucket(1, this.latencyRef); - } + this.ratelimits[route] ??= new SequentialBucket(1, this.latencyRef); this.ratelimits[route].queue(actualCall, short); } }); diff --git a/lib/structures/ApplicationCommand.js b/lib/structures/ApplicationCommand.js index 20ef540e..ac601482 100644 --- a/lib/structures/ApplicationCommand.js +++ b/lib/structures/ApplicationCommand.js @@ -17,9 +17,10 @@ const Base = require("./Base"); * @prop {String} version The id of the version of this command */ class ApplicationCommand extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.applicationID = data.application_id; this.name = data.name; this.description = data.description; @@ -60,7 +61,7 @@ class ApplicationCommand extends Base { * @returns {Promise} */ delete() { - return this.guildID === undefined ? this._client.deleteCommand.call(this._client, this.id) : this._client.deleteGuildCommand.call(this._client, this.guildID, this.id); + return this.guildID === undefined ? this.#client.deleteCommand.call(this.#client, this.id) : this.#client.deleteGuildCommand.call(this.#client, this.guildID, this.id); } /** @@ -76,7 +77,7 @@ class ApplicationCommand extends Base { * @returns {Promise} */ edit(options) { - return this.guildID === undefined ? this._client.editCommand.call(this._client, this.id, options) : this._client.editGuildCommand.call(this._client, this.id, this.guildID, options); + return this.guildID === undefined ? this.#client.editCommand.call(this.#client, this.id, options) : this.#client.editGuildCommand.call(this.#client, this.id, this.guildID, options); } toJSON(props = []) { diff --git a/lib/structures/AutoModerationRule.js b/lib/structures/AutoModerationRule.js index c15496bd..a616e055 100644 --- a/lib/structures/AutoModerationRule.js +++ b/lib/structures/AutoModerationRule.js @@ -17,9 +17,10 @@ const Base = require("./Base"); * @prop {Number} triggerType The rule [trigger type](https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) */ class AutoModerationRule extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.actions = data.actions.map((action) => ({ type: action.type, @@ -42,7 +43,7 @@ class AutoModerationRule extends Base { * @returns {Promise} */ delete() { - return this._client.deleteAutoModerationRule.call(this._client, this.guildID, this.id); + return this.#client.deleteAutoModerationRule.call(this.#client, this.guildID, this.id); } /** @@ -59,7 +60,7 @@ class AutoModerationRule extends Base { * @returns {Promise} */ edit(options) { - return this._client.editAutoModerationRule.call(this._client, this.guildID, this.id, options); + return this.#client.editAutoModerationRule.call(this.#client, this.guildID, this.id, options); } } diff --git a/lib/structures/AutocompleteInteraction.js b/lib/structures/AutocompleteInteraction.js index 40f2ed8e..19319176 100644 --- a/lib/structures/AutocompleteInteraction.js +++ b/lib/structures/AutocompleteInteraction.js @@ -22,10 +22,12 @@ const {InteractionResponseTypes} = require("../Constants"); * @prop {Array?} data.options[].options Sub-options (Mutually exclusive with value, subcommand/subcommandgroup) */ class AutocompleteInteraction extends Interaction { + #client; constructor(data, client) { super(data, client); + this.#client = client; - this.channel = this._client.getChannel(data.channel_id) || { + this.channel = this.#client.getChannel(data.channel_id) || { id: data.channel_id }; @@ -40,13 +42,13 @@ class AutocompleteInteraction extends Interaction { data.member.id = data.member.user.id; this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this.#client.guilds.get(data.guild_id); this.member = guild.members.update(data.member, guild); } } if(data.user !== undefined) { - this.user = this._client.users.update(data.user, client); + this.user = this.#client.users.update(data.user, client); } if(data.app_permissions !== undefined) { @@ -78,7 +80,7 @@ class AutocompleteInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT, data: {choices} }).then(() => this.update()); diff --git a/lib/structures/Base.js b/lib/structures/Base.js index a6271148..07c9541b 100644 --- a/lib/structures/Base.js +++ b/lib/structures/Base.js @@ -1,6 +1,6 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); /** * Provides utilities for working with many Discord structures diff --git a/lib/structures/CategoryChannel.js b/lib/structures/CategoryChannel.js index 928974fb..e73e0b5e 100644 --- a/lib/structures/CategoryChannel.js +++ b/lib/structures/CategoryChannel.js @@ -32,7 +32,7 @@ class CategoryChannel extends GuildChannel { get channels() { const channels = new Collection(GuildChannel); - if(this.guild && this.guild.channels) { + if(this.guild?.channels) { for(const channel of this.guild.channels.values()) { if(channel.parentID === this.id) { channels.add(channel); diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index c6f7cb3d..58df5d68 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -35,10 +35,12 @@ const {InteractionResponseTypes} = require("../Constants"); * @prop {Collection?} data.resolved.users resolved users */ class CommandInteraction extends Interaction { + #client; constructor(data, client) { super(data, client); + this.#client = client; - this.channel = this._client.getChannel(data.channel_id) || { + this.channel = this.#client.getChannel(data.channel_id) || { id: data.channel_id }; @@ -49,7 +51,7 @@ class CommandInteraction extends Interaction { if(data.data.resolved.users !== undefined) { const usermap = new Collection(User); Object.entries(data.data.resolved.users).forEach(([id, user]) => { - usermap.set(id, this._client.users.update(user, client)); + usermap.set(id, this.#client.users.update(user, client)); }); this.data.resolved.users = usermap; } @@ -62,7 +64,7 @@ class CommandInteraction extends Interaction { if(this.channel.guild) { membermap.set(id, this.channel.guild.members.update(member, this.channel.guild)); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this.#client.guilds.get(data.guild_id); membermap.set(id, guild.members.update(member, guild)); } }); @@ -80,7 +82,7 @@ class CommandInteraction extends Interaction { if(data.data.resolved.channels !== undefined) { const channelmap = new Collection(Channel); Object.entries(data.data.resolved.channels).forEach(([id, channel]) => { - channelmap.set(id, Channel.from(channel, this._client) || new Channel(channel, this._client)); + channelmap.set(id, Channel.from(channel, this.#client) || new Channel(channel, this.#client)); }); this.data.resolved.channels = channelmap; } @@ -88,7 +90,7 @@ class CommandInteraction extends Interaction { if(data.data.resolved.messages !== undefined) { const messagemap = new Collection(Message); Object.entries(data.data.resolved.messages).forEach(([id, message]) => { - messagemap.set(id, new Message(message, this._client)); + messagemap.set(id, new Message(message, this.#client)); }); this.data.resolved.messages = messagemap; } @@ -111,13 +113,13 @@ class CommandInteraction extends Interaction { data.member.id = data.member.user.id; this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this.#client.guilds.get(data.guild_id); this.member = guild.members.update(data.member, guild); } } if(data.user !== undefined) { - this.user = this._client.users.update(data.user, client); + this.user = this.#client.users.update(data.user, client); } if(data.app_permissions !== undefined) { @@ -166,7 +168,7 @@ class CommandInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.executeWebhook.call(this._client, this.applicationID, this.token, Object.assign({wait: true}, content)); + return this.#client.executeWebhook.call(this.#client, this.applicationID, this.token, Object.assign({wait: true}, content)); } /** @@ -200,14 +202,14 @@ class CommandInteraction extends Interaction { content.content = "" + content.content; } if(content.content !== undefined || content.embeds || content.allowedMentions) { - content.allowed_mentions = this._client._formatAllowedMentions(content.allowedMentions); + content.allowed_mentions = this.#client._formatAllowedMentions(content.allowedMentions); } } - const {files, attachments} = this._client._processAttachments(content.attachments); + const {files, attachments} = this.#client._processAttachments(content.attachments); content.attachments = attachments; - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE, data: content }, files).then(() => this.update()); @@ -222,7 +224,7 @@ class CommandInteraction extends Interaction { * @returns {Promise} */ async createModal(content) { - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.MODAL, data: content }).then(() => this.update()); @@ -238,7 +240,7 @@ class CommandInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, data: { flags: flags || 0 @@ -255,7 +257,7 @@ class CommandInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("deleteMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first."); } - return this._client.deleteWebhookMessage.call(this._client, this.applicationID, this.token, messageID); + return this.#client.deleteWebhookMessage.call(this.#client, this.applicationID, this.token, messageID); } /** @@ -267,7 +269,7 @@ class CommandInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("deleteOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first."); } - return this._client.deleteWebhookMessage.call(this._client, this.applicationID, this.token, "@original"); + return this.#client.deleteWebhookMessage.call(this.#client, this.applicationID, this.token, "@original"); } /** @@ -302,7 +304,7 @@ class CommandInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.editWebhookMessage.call(this._client, this.applicationID, this.token, messageID, content); + return this.#client.editWebhookMessage.call(this.#client, this.applicationID, this.token, messageID, content); } /** @@ -336,7 +338,7 @@ class CommandInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.editWebhookMessage.call(this._client, this.applicationID, this.token, "@original", content); + return this.#client.editWebhookMessage.call(this.#client, this.applicationID, this.token, "@original", content); } /** @@ -348,7 +350,7 @@ class CommandInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("getOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first."); } - return this._client.getWebhookMessage.call(this._client, this.applicationID, this.token, "@original"); + return this.#client.getWebhookMessage.call(this.#client, this.applicationID, this.token, "@original"); } } diff --git a/lib/structures/ComponentInteraction.js b/lib/structures/ComponentInteraction.js index 3ff4b33f..bc6c2101 100644 --- a/lib/structures/ComponentInteraction.js +++ b/lib/structures/ComponentInteraction.js @@ -31,10 +31,12 @@ const {InteractionResponseTypes} = require("../Constants"); * @prop {User?} user The user who triggered the interaction (This is only sent when the interaction is invoked within a dm) */ class ComponentInteraction extends Interaction { + #client; constructor(data, client) { super(data, client); + this.#client = client; - this.channel = this._client.getChannel(data.channel_id) || { + this.channel = this.#client.getChannel(data.channel_id) || { id: data.channel_id }; @@ -45,7 +47,7 @@ class ComponentInteraction extends Interaction { if(data.data.resolved.users !== undefined) { const usermap = new Collection(User); Object.entries(data.data.resolved.users).forEach(([id, user]) => { - usermap.set(id, this._client.users.update(user, client)); + usermap.set(id, this.#client.users.update(user, client)); }); this.data.resolved.users = usermap; } @@ -58,7 +60,7 @@ class ComponentInteraction extends Interaction { if(this.channel.guild) { membermap.set(id, this.channel.guild.members.update(member, this.channel.guild)); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this.#client.guilds.get(data.guild_id); membermap.set(id, guild.members.update(member, guild)); } }); @@ -76,7 +78,7 @@ class ComponentInteraction extends Interaction { if(data.data.resolved.channels !== undefined) { const channelmap = new Collection(Channel); Object.entries(data.data.resolved.channels).forEach(([id, channel]) => { - channelmap.set(id, Channel.from(channel, this._client) || new Channel(channel, this._client)); + channelmap.set(id, Channel.from(channel, this.#client) || new Channel(channel, this.#client)); }); this.data.resolved.channels = channelmap; } @@ -91,17 +93,17 @@ class ComponentInteraction extends Interaction { data.member.id = data.member.user.id; this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this.#client.guilds.get(data.guild_id); this.member = guild.members.update(data.member, guild); } } if(data.message !== undefined) { - this.message = new Message(data.message, this._client); + this.message = new Message(data.message, this.#client); } if(data.user !== undefined) { - this.user = this._client.users.update(data.user, client); + this.user = this.#client.users.update(data.user, client); } if(data.app_permissions !== undefined) { @@ -148,7 +150,7 @@ class ComponentInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.executeWebhook.call(this._client, this.applicationID, this.token, Object.assign({wait: true}, content)); + return this.#client.executeWebhook.call(this.#client, this.applicationID, this.token, Object.assign({wait: true}, content)); } /** @@ -182,14 +184,14 @@ class ComponentInteraction extends Interaction { content.content = "" + content.content; } if(content.content !== undefined || content.embeds || content.allowedMentions) { - content.allowed_mentions = this._client._formatAllowedMentions(content.allowedMentions); + content.allowed_mentions = this.#client._formatAllowedMentions(content.allowedMentions); } } - const {files, attachments} = this._client._processAttachments(content.attachments); + const {files, attachments} = this.#client._processAttachments(content.attachments); content.attachments = attachments; - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE, data: content }, files).then(() => this.update()); @@ -204,7 +206,7 @@ class ComponentInteraction extends Interaction { * @returns {Promise} */ async createModal(content) { - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.MODAL, data: content }).then(() => this.update()); @@ -220,7 +222,7 @@ class ComponentInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, data: { flags: flags || 0 @@ -237,7 +239,7 @@ class ComponentInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.DEFERRED_UPDATE_MESSAGE }).then(() => this.update()); } @@ -251,7 +253,7 @@ class ComponentInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("deleteMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first."); } - return this._client.deleteWebhookMessage.call(this._client, this.applicationID, this.token, messageID); + return this.#client.deleteWebhookMessage.call(this.#client, this.applicationID, this.token, messageID); } /** @@ -263,7 +265,7 @@ class ComponentInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("deleteOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first."); } - return this._client.deleteWebhookMessage.call(this._client, this.applicationID, this.token, "@original"); + return this.#client.deleteWebhookMessage.call(this.#client, this.applicationID, this.token, "@original"); } /** @@ -298,7 +300,7 @@ class ComponentInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.editWebhookMessage.call(this._client, this.applicationID, this.token, messageID, content); + return this.#client.editWebhookMessage.call(this.#client, this.applicationID, this.token, messageID, content); } /** @@ -332,7 +334,7 @@ class ComponentInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.editWebhookMessage.call(this._client, this.applicationID, this.token, "@original", content); + return this.#client.editWebhookMessage.call(this.#client, this.applicationID, this.token, "@original", content); } /** @@ -370,14 +372,14 @@ class ComponentInteraction extends Interaction { content.content = "" + content.content; } if(content.content !== undefined || content.embeds || content.allowedMentions) { - content.allowed_mentions = this._client._formatAllowedMentions(content.allowedMentions); + content.allowed_mentions = this.#client._formatAllowedMentions(content.allowedMentions); } } - const {files, attachments} = this._client._processAttachments(content.attachments); + const {files, attachments} = this.#client._processAttachments(content.attachments); content.attachments = attachments; - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.UPDATE_MESSAGE, data: content }, files).then(() => this.update()); @@ -392,7 +394,7 @@ class ComponentInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("getOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first."); } - return this._client.getWebhookMessage.call(this._client, this.applicationID, this.token, "@original"); + return this.#client.getWebhookMessage.call(this.#client, this.applicationID, this.token, "@original"); } } diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index b8b8a7fd..5782a8ce 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -72,9 +72,10 @@ const ThreadChannel = require("./ThreadChannel"); * @prop {Boolean?} widgetEnabled Whether the guild widget is enabled. REST only. */ class Guild extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.shard = client.shards.get(client.guildShardMap[this.id] || (Base.getDiscordEpoch(data.id) % client.shards.options.maxShards) || 0); this.unavailable = !!data.unavailable; this.joinedAt = Date.parse(data.joined_at); @@ -166,15 +167,13 @@ class Guild extends Base { try { const channel = this.channels.get(voiceState.channel_id); const member = this.members.update(voiceState); - if(channel && channel.voiceMembers) { - channel.voiceMembers.add(member); - } + channel?.voiceMembers?.add(member); } catch(err) { client.emit("error", err, this.shard.id); continue; } if(client.options.seedVoiceConnections && voiceState.id === client.user.id && !client.voiceConnections.get(this.id)) { - process.nextTick(() => this._client.joinVoiceChannel(voiceState.channel_id)); + process.nextTick(() => this.#client.joinVoiceChannel(voiceState.channel_id)); } } } @@ -278,7 +277,7 @@ class Guild extends Base { if(data.welcome_screen !== undefined) { this.welcomeScreen = { description: data.welcome_screen.description, - welcomeChannels: data.welcome_screen.welcome_channels && data.welcome_screen.welcome_channels.map((c) => { + welcomeChannels: data.welcome_screen.welcome_channels?.map((c) => { return { channelID: c.channel, description: c.description, @@ -294,19 +293,19 @@ class Guild extends Base { } get bannerURL() { - return this.banner ? this._client._formatImage(Endpoints.BANNER(this.id, this.banner)) : null; + return this.banner ? this.#client._formatImage(Endpoints.BANNER(this.id, this.banner)) : null; } get iconURL() { - return this.icon ? this._client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon)) : null; + return this.icon ? this.#client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon)) : null; } get splashURL() { - return this.splash ? this._client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash)) : null; + return this.splash ? this.#client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash)) : null; } get discoverySplashURL() { - return this.discoverySplash ? this._client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash)) : null; + return this.discoverySplash ? this.#client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash)) : null; } /** @@ -320,7 +319,7 @@ class Guild extends Base { * @arg {Boolean} [options.deaf] Whether the user should be deafened */ addMember(userID, accessToken, options = {}) { - return this._client.addGuildMember.call(this._client, this.id, userID, accessToken, options); + return this.#client.addGuildMember.call(this.#client, this.id, userID, accessToken, options); } /** @@ -331,7 +330,7 @@ class Guild extends Base { * @returns {Promise} */ addMemberRole(memberID, roleID, reason) { - return this._client.addGuildMemberRole.call(this._client, this.id, memberID, roleID, reason); + return this.#client.addGuildMemberRole.call(this.#client, this.id, memberID, roleID, reason); } /** @@ -342,7 +341,7 @@ class Guild extends Base { * @returns {Promise} */ banMember(userID, options) { - return this._client.banGuildMember.call(this._client, this.id, userID, options); + return this.#client.banGuildMember.call(this.#client, this.id, userID, options); } /** @@ -352,7 +351,7 @@ class Guild extends Base { * @returns {Promise>} Returns an array of [GuildApplicationCommandPermissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) objects. */ bulkEditCommandPermissions(permissions) { - return this._client.bulkEditCommandPermissions.call(this._client, this.id, permissions); + return this.#client.bulkEditCommandPermissions.call(this.#client, this.id, permissions); } /** @@ -361,7 +360,7 @@ class Guild extends Base { * @returns {Promise} Resolves with a commands object */ bulkEditCommands(commands) { - return this._client.bulkEditGuildCommands.call(this._client, this.id, commands); + return this.#client.bulkEditGuildCommands.call(this.#client, this.id, commands); } /** @@ -379,7 +378,7 @@ class Guild extends Base { * @returns {Promise} */ createAutoModerationRule(options) { - return this._client.createAutoModerationRule.call(this._client, this.id, options); + return this.#client.createAutoModerationRule.call(this.#client, this.id, options); } /** @@ -405,7 +404,7 @@ class Guild extends Base { * @returns {Promise} */ createChannel(name, type, options) { - return this._client.createChannel.call(this._client, this.id, name, type, options); + return this.#client.createChannel.call(this.#client, this.id, name, type, options); } /** @@ -421,7 +420,7 @@ class Guild extends Base { * @returns {Promise} */ createCommand(command) { - return this._client.createGuildCommand.call(this._client, this.id, command); + return this.#client.createGuildCommand.call(this.#client, this.id, command); } /** @@ -434,7 +433,7 @@ class Guild extends Base { * @returns {Promise} A guild emoji object */ createEmoji(options, reason) { - return this._client.createGuildEmoji.call(this._client, this.id, options, reason); + return this.#client.createGuildEmoji.call(this.#client, this.id, options, reason); } /** @@ -451,7 +450,7 @@ class Guild extends Base { * @returns {Promise} */ createRole(options, reason) { - return this._client.createRole.call(this._client, this.id, options, reason); + return this.#client.createRole.call(this.#client, this.id, options, reason); } /** @@ -471,7 +470,7 @@ class Guild extends Base { * @returns {Promise} */ createScheduledEvent(event, reason) { - return this._client.createGuildScheduledEvent.call(this._client, this.id, event, reason); + return this.#client.createGuildScheduledEvent.call(this.#client, this.id, event, reason); } /** @@ -487,7 +486,7 @@ class Guild extends Base { * @returns {Promise} A sticker object */ createSticker(options, reason) { - return this._client.createGuildSticker.call(this._client, this.id, options, reason); + return this.#client.createGuildSticker.call(this.#client, this.id, options, reason); } /** @@ -497,7 +496,7 @@ class Guild extends Base { * @returns {Promise} */ createTemplate(name, description) { - return this._client.createGuildTemplate.call(this._client, this.id, name, description); + return this.#client.createGuildTemplate.call(this.#client, this.id, name, description); } /** @@ -505,7 +504,7 @@ class Guild extends Base { * @returns {Promise} */ delete() { - return this._client.deleteGuild.call(this._client, this.id); + return this.#client.deleteGuild.call(this.#client, this.id); } /** @@ -515,7 +514,7 @@ class Guild extends Base { * @returns {Promise} */ deleteAutoModerationRule(ruleID, reason) { - return this._client.deleteAutoModerationRule.call(this._client, this.id, ruleID, reason); + return this.#client.deleteAutoModerationRule.call(this.#client, this.id, ruleID, reason); } /** @@ -524,7 +523,7 @@ class Guild extends Base { * @returns {Promise} */ deleteCommand(commandID) { - return this._client.deleteGuildCommand.call(this._client, this.id, commandID); + return this.#client.deleteGuildCommand.call(this.#client, this.id, commandID); } /** @@ -534,7 +533,7 @@ class Guild extends Base { * @returns {Promise} */ deleteEmoji(emojiID, reason) { - return this._client.deleteGuildEmoji.call(this._client, this.id, emojiID, reason); + return this.#client.deleteGuildEmoji.call(this.#client, this.id, emojiID, reason); } /** @@ -543,7 +542,7 @@ class Guild extends Base { * @returns {Promise} */ deleteIntegration(integrationID) { - return this._client.deleteGuildIntegration.call(this._client, this.id, integrationID); + return this.#client.deleteGuildIntegration.call(this.#client, this.id, integrationID); } /** @@ -553,7 +552,7 @@ class Guild extends Base { * @returns {Promise} */ deleteRole(roleID, reason) { - return this._client.deleteRole.call(this._client, this.id, roleID, reason); + return this.#client.deleteRole.call(this.#client, this.id, roleID, reason); } /** @@ -562,7 +561,7 @@ class Guild extends Base { * @returns {Promise} */ deleteScheduledEvent(eventID) { - return this._client.deleteGuildScheduledEvent.call(this._client, this.id, eventID); + return this.#client.deleteGuildScheduledEvent.call(this.#client, this.id, eventID); } /** @@ -572,7 +571,7 @@ class Guild extends Base { * @returns {Promise} */ deleteSticker(stickerID, reason) { - return this._client.deleteGuildSticker.call(this._client, this.id, stickerID, reason); + return this.#client.deleteGuildSticker.call(this.#client, this.id, stickerID, reason); } /** @@ -581,7 +580,7 @@ class Guild extends Base { * @returns {Promise} */ deleteTemplate(code) { - return this._client.deleteGuildTemplate.call(this._client, this.id, code); + return this.#client.deleteGuildTemplate.call(this.#client, this.id, code); } /** @@ -591,7 +590,7 @@ class Guild extends Base { * @returns {String?} */ dynamicBannerURL(format, size) { - return this.banner ? this._client._formatImage(Endpoints.BANNER(this.id, this.banner), format, size) : null; + return this.banner ? this.#client._formatImage(Endpoints.BANNER(this.id, this.banner), format, size) : null; } /** @@ -601,7 +600,7 @@ class Guild extends Base { * @returns {String?} */ dynamicDiscoverySplashURL(format, size) { - return this.discoverySplash ? this._client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash), format, size) : null; + return this.discoverySplash ? this.#client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash), format, size) : null; } /** @@ -611,7 +610,7 @@ class Guild extends Base { * @returns {String?} */ dynamicIconURL(format, size) { - return this.icon ? this._client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon), format, size) : null; + return this.icon ? this.#client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon), format, size) : null; } /** @@ -621,7 +620,7 @@ class Guild extends Base { * @returns {String?} */ dynamicSplashURL(format, size) { - return this.splash ? this._client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash), format, size) : null; + return this.splash ? this.#client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash), format, size) : null; } /** @@ -650,7 +649,7 @@ class Guild extends Base { * @returns {Promise} */ edit(options, reason) { - return this._client.editGuild.call(this._client, this.id, options, reason); + return this.#client.editGuild.call(this.#client, this.id, options, reason); } /** @@ -668,7 +667,7 @@ class Guild extends Base { * @returns {Promise} */ editAutoModerationRule(ruleID, options) { - return this._client.editAutoModerationRule.call(this._client, this.id, ruleID, options); + return this.#client.editAutoModerationRule.call(this.#client, this.id, ruleID, options); } /** @@ -681,7 +680,7 @@ class Guild extends Base { * @returns {Promise} */ editChannelPositions(channelPositions) { - return this._client.editChannelPositions.call(this._client, this.id, channelPositions); + return this.#client.editChannelPositions.call(this.#client, this.id, channelPositions); } /** @@ -697,7 +696,7 @@ class Guild extends Base { * @returns {Promise} */ editCommand(commandID, command) { - return this._client.editGuildCommand.call(this._client, this.id, commandID, command); + return this.#client.editGuildCommand.call(this.#client, this.id, commandID, command); } /** @@ -708,7 +707,7 @@ class Guild extends Base { * @returns {Promise} Resolves with a [GuildApplicationCommandPermissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. */ editCommandPermissions(commandID, permissions) { - return this._client.editCommandPermissions.call(this._client, this.id, commandID, permissions); + return this.#client.editCommandPermissions.call(this.#client, this.id, commandID, permissions); } /** @@ -721,7 +720,7 @@ class Guild extends Base { * @returns {Promise} A guild emoji object */ editEmoji(emojiID, options, reason) { - return this._client.editGuildEmoji.call(this._client, this.id, emojiID, options, reason); + return this.#client.editGuildEmoji.call(this.#client, this.id, emojiID, options, reason); } /** @@ -739,7 +738,7 @@ class Guild extends Base { * @returns {Promise} */ editMember(memberID, options, reason) { - return this._client.editGuildMember.call(this._client, this.id, memberID, options, reason); + return this.#client.editGuildMember.call(this.#client, this.id, memberID, options, reason); } /** @@ -750,7 +749,7 @@ class Guild extends Base { * @returns {Promise} Returns the new MFA level */ editMFALevel(options) { - return this._client.editGuildMFALevel.call(this._client, this.id, options); + return this.#client.editGuildMFALevel.call(this.#client, this.id, options); } /** @@ -768,7 +767,7 @@ class Guild extends Base { * @returns {Promise} */ editRole(roleID, options, reason) { - return this._client.editRole.call(this._client, this.id, roleID, options, reason); + return this.#client.editRole.call(this.#client, this.id, roleID, options, reason); } /** @@ -790,7 +789,7 @@ class Guild extends Base { * @returns {Promise} */ editScheduledEvent(eventID, event, reason) { - return this._client.editGuildScheduledEvent.call(this._client, this.id, eventID, event, reason); + return this.#client.editGuildScheduledEvent.call(this.#client, this.id, eventID, event, reason); } /** @@ -804,7 +803,7 @@ class Guild extends Base { * @returns {Promise} A sticker object */ editSticker(stickerID, options, reason) { - return this._client.editGuildSticker.call(this._client, this.id, stickerID, options, reason); + return this.#client.editGuildSticker.call(this.#client, this.id, stickerID, options, reason); } /** @@ -816,7 +815,7 @@ class Guild extends Base { * @returns {Promise} */ editTemplate(code, options) { - return this._client.editGuildTemplate.call(this._client, this.id, code, options); + return this.#client.editGuildTemplate.call(this.#client, this.id, code, options); } /** @@ -829,7 +828,7 @@ class Guild extends Base { * @returns {Promise} */ editVoiceState(options, userID) { - return this._client.editGuildVoiceState.call(this._client, this.id, options, userID); + return this.#client.editGuildVoiceState.call(this.#client, this.id, options, userID); } /** @@ -845,7 +844,7 @@ class Guild extends Base { * @returns {Promise} */ editWelcomeScreen(options) { - return this._client.editGuildWelcomeScreen.call(this._client, this.id, options); + return this.#client.editGuildWelcomeScreen.call(this.#client, this.id, options); } /** @@ -854,7 +853,7 @@ class Guild extends Base { * @returns {Promise} A guild widget object */ editWidget(options) { - return this._client.editGuildWidget.call(this._client, this.id, options); + return this.#client.editGuildWidget.call(this.#client, this.id, options); } /** @@ -876,7 +875,7 @@ class Guild extends Base { * @returns {Promise} An object containing an array of `threads` and an array of `members` */ getActiveThreads() { - return this._client.getActiveGuildThreads.call(this._client, this.id); + return this.#client.getActiveGuildThreads.call(this.#client, this.id); } /** @@ -890,7 +889,7 @@ class Guild extends Base { * @returns {Promise<{entries: Array, integrations: Array, threads: Array, users: Array, webhooks: Array}>} */ getAuditLog(options) { - return this._client.getGuildAuditLog.call(this._client, this.id, options); + return this.#client.getGuildAuditLog.call(this.#client, this.id, options); } /** @@ -900,7 +899,7 @@ class Guild extends Base { * @returns {Promise} */ getAutoModerationRule(ruleID) { - return this._client.getAutoModerationRule.call(this._client, this.id, ruleID); + return this.#client.getAutoModerationRule.call(this.#client, this.id, ruleID); } /** @@ -909,7 +908,7 @@ class Guild extends Base { * @returns {Promise} */ getAutoModerationRules() { - return this._client.getAutoModerationRules.call(this._client, this.id); + return this.#client.getAutoModerationRules.call(this.#client, this.id); } /** @@ -918,7 +917,7 @@ class Guild extends Base { * @returns {Promise} Resolves with {reason: String, user: User} */ getBan(userID) { - return this._client.getGuildBan.call(this._client, this.id, userID); + return this.#client.getGuildBan.call(this.#client, this.id, userID); } /** @@ -930,7 +929,7 @@ class Guild extends Base { * @returns {Promise>} Resolves with an array of { reason: String, user: User } */ getBans(options) { - return this._client.getGuildBans.call(this._client, this.id, options); + return this.#client.getGuildBans.call(this.#client, this.id, options); } /** @@ -940,7 +939,7 @@ class Guild extends Base { * @returns {Promise} Resolves with a command object */ getCommand(commandID, withLocalizations) { - return this._client.getGuildCommand.call(this._client, this.id, commandID, withLocalizations); + return this.#client.getGuildCommand.call(this.#client, this.id, commandID, withLocalizations); } /** @@ -949,7 +948,7 @@ class Guild extends Base { * @returns {Promise} Resolves with a guild application command permissions object. */ getCommandPermissions(commandID) { - return this._client.getCommandPermissions.call(this._client, this.id, commandID); + return this.#client.getCommandPermissions.call(this.#client, this.id, commandID); } /** @@ -958,7 +957,7 @@ class Guild extends Base { * @returns {Promise>} Resolves with an array of command objects */ getCommands(withLocalizations) { - return this._client.getGuildCommands.call(this._client, this.id, withLocalizations); + return this.#client.getGuildCommands.call(this.#client, this.id, withLocalizations); } /** @@ -966,7 +965,7 @@ class Guild extends Base { * @returns {Promise>} Resolves with an array of guild application command permissions objects. */ getGuildCommandPermissions() { - return this._client.getGuildCommandPermissions.call(this._client, this.id); + return this.#client.getGuildCommandPermissions.call(this.#client, this.id); } /** @@ -974,7 +973,7 @@ class Guild extends Base { * @returns {Promise>} */ getIntegrations() { - return this._client.getGuildIntegrations.call(this._client, this.id); + return this.#client.getGuildIntegrations.call(this.#client, this.id); } /** @@ -982,7 +981,7 @@ class Guild extends Base { * @returns {Promise>} */ getInvites() { - return this._client.getGuildInvites.call(this._client, this.id); + return this.#client.getGuildInvites.call(this.#client, this.id); } /** @@ -993,7 +992,7 @@ class Guild extends Base { * @returns {Promise} Resolves with the number of members that would be pruned */ getPruneCount(options) { - return this._client.getPruneCount.call(this._client, this.id, options); + return this.#client.getPruneCount.call(this.#client, this.id, options); } /** @@ -1001,7 +1000,7 @@ class Guild extends Base { * @returns {Promise>} */ getRESTChannels() { - return this._client.getRESTGuildChannels.call(this._client, this.id); + return this.#client.getRESTGuildChannels.call(this.#client, this.id); } /** @@ -1010,7 +1009,7 @@ class Guild extends Base { * @returns {Promise} An emoji object */ getRESTEmoji(emojiID) { - return this._client.getRESTGuildEmoji.call(this._client, this.id, emojiID); + return this.#client.getRESTGuildEmoji.call(this.#client, this.id, emojiID); } /** @@ -1018,7 +1017,7 @@ class Guild extends Base { * @returns {Promise>} An array of guild emoji objects */ getRESTEmojis() { - return this._client.getRESTGuildEmojis.call(this._client, this.id); + return this.#client.getRESTGuildEmojis.call(this.#client, this.id); } /** @@ -1027,7 +1026,7 @@ class Guild extends Base { * @returns {Promise} */ getRESTMember(memberID) { - return this._client.getRESTGuildMember.call(this._client, this.id, memberID); + return this.#client.getRESTGuildMember.call(this.#client, this.id, memberID); } /** @@ -1038,7 +1037,7 @@ class Guild extends Base { * @returns {Promise>} */ getRESTMembers(options) { - return this._client.getRESTGuildMembers.call(this._client, this.id, options); + return this.#client.getRESTGuildMembers.call(this.#client, this.id, options); } /** @@ -1046,7 +1045,7 @@ class Guild extends Base { * @returns {Promise>} */ getRESTRoles() { - return this._client.getRESTGuildRoles.call(this._client, this.id); + return this.#client.getRESTGuildRoles.call(this.#client, this.id); } /** @@ -1057,7 +1056,7 @@ class Guild extends Base { * @returns {Promise} */ getRESTScheduledEvent(eventID, options) { - return this._client.getRESTGuildScheduledEvent.call(this._client, this.id, eventID, options); + return this.#client.getRESTGuildScheduledEvent.call(this.#client, this.id, eventID, options); } /** @@ -1066,7 +1065,7 @@ class Guild extends Base { * @returns {Promise} A sticker object */ getRESTSticker(stickerID) { - return this._client.getRESTGuildSticker.call(this._client, this.id, stickerID); + return this.#client.getRESTGuildSticker.call(this.#client, this.id, stickerID); } /** @@ -1074,7 +1073,7 @@ class Guild extends Base { * @returns {Promise>} An array of guild sticker objects */ getRESTStickers() { - return this._client.getRESTGuildStickers.call(this._client, this.id); + return this.#client.getRESTGuildStickers.call(this.#client, this.id); } /** @@ -1084,7 +1083,7 @@ class Guild extends Base { * @returns {Promise>} */ getScheduledEvents(options) { - return this._client.getGuildScheduledEvents.call(this._client, this.id, options); + return this.#client.getGuildScheduledEvents.call(this.#client, this.id, options); } /** @@ -1098,7 +1097,7 @@ class Guild extends Base { * @returns {Promise>} */ getScheduledEventUsers(eventID, options) { - return this._client.getGuildScheduledEventUsers.call(this._client, this.id, eventID, options); + return this.#client.getGuildScheduledEventUsers.call(this.#client, this.id, eventID, options); } /** @@ -1106,7 +1105,7 @@ class Guild extends Base { * @returns {Promise>} */ getTemplates() { - return this._client.getGuildTemplates.call(this._client, this.id); + return this.#client.getGuildTemplates.call(this.#client, this.id); } /** @@ -1114,7 +1113,7 @@ class Guild extends Base { * @returns {Promise} */ getVanity() { - return this._client.getGuildVanity.call(this._client, this.id); + return this.#client.getGuildVanity.call(this.#client, this.id); } /** @@ -1122,7 +1121,7 @@ class Guild extends Base { * @returns {Promise>} Resolves with an array of voice region objects */ getVoiceRegions() { - return this._client.getVoiceRegions.call(this._client, this.id); + return this.#client.getVoiceRegions.call(this.#client, this.id); } /** @@ -1130,7 +1129,7 @@ class Guild extends Base { * @returns {Promise>} Resolves with an array of webhook objects */ getWebhooks() { - return this._client.getGuildWebhooks.call(this._client, this.id); + return this.#client.getGuildWebhooks.call(this.#client, this.id); } /** @@ -1138,7 +1137,7 @@ class Guild extends Base { * @returns {Promise} */ getWelcomeScreen() { - return this._client.getGuildWelcomeScreen.call(this._client, this.id); + return this.#client.getGuildWelcomeScreen.call(this.#client, this.id); } /** @@ -1146,7 +1145,7 @@ class Guild extends Base { * @returns {Promise} A guild widget object */ getWidget() { - return this._client.getGuildWidget.call(this._client, this.id); + return this.#client.getGuildWidget.call(this.#client, this.id); } /** @@ -1154,7 +1153,7 @@ class Guild extends Base { * @returns {Promise} A guild widget settings object */ getWidgetSettings() { - return this._client.getGuildWidgetSettings.call(this._client, this.id); + return this.#client.getGuildWidgetSettings.call(this.#client, this.id); } /** @@ -1164,7 +1163,7 @@ class Guild extends Base { * @returns {Promise} */ kickMember(userID, reason) { - return this._client.kickGuildMember.call(this._client, this.id, userID, reason); + return this.#client.kickGuildMember.call(this.#client, this.id, userID, reason); } /** @@ -1172,14 +1171,14 @@ class Guild extends Base { * @returns {Promise} */ leave() { - return this._client.leaveGuild.call(this._client, this.id); + return this.#client.leaveGuild.call(this.#client, this.id); } /** * Leaves the voice channel in this guild */ leaveVoiceChannel() { - this._client.closeVoiceConnection.call(this._client, this.id); + this.#client.closeVoiceConnection.call(this.#client, this.id); } /** @@ -1224,7 +1223,7 @@ class Guild extends Base { * @returns {Promise} Resolves with the number of pruned members */ pruneMembers(options) { - return this._client.pruneMembers.call(this._client, this.id, options); + return this.#client.pruneMembers.call(this.#client, this.id, options); } /** @@ -1235,7 +1234,7 @@ class Guild extends Base { * @returns {Promise} */ removeMemberRole(memberID, roleID, reason) { - return this._client.removeGuildMemberRole.call(this._client, this.id, memberID, roleID, reason); + return this.#client.removeGuildMemberRole.call(this.#client, this.id, memberID, roleID, reason); } /** @@ -1245,7 +1244,7 @@ class Guild extends Base { * @returns {Promise>} */ searchMembers(query, limit) { - return this._client.searchGuildMembers.call(this._client, this.id, query, limit); + return this.#client.searchGuildMembers.call(this.#client, this.id, query, limit); } /** @@ -1254,7 +1253,7 @@ class Guild extends Base { * @returns {Promise} */ syncTemplate(code) { - return this._client.syncGuildTemplate.call(this._client, this.id, code); + return this.#client.syncGuildTemplate.call(this.#client, this.id, code); } /** @@ -1264,7 +1263,7 @@ class Guild extends Base { * @returns {Promise} */ unbanMember(userID, reason) { - return this._client.unbanGuildMember.call(this._client, this.id, userID, reason); + return this.#client.unbanGuildMember.call(this.#client, this.id, userID, reason); } toJSON(props = []) { diff --git a/lib/structures/GuildAuditLogEntry.js b/lib/structures/GuildAuditLogEntry.js index 495d28fe..8632f4c9 100644 --- a/lib/structures/GuildAuditLogEntry.js +++ b/lib/structures/GuildAuditLogEntry.js @@ -89,7 +89,7 @@ class GuildAuditLogEntry extends Base { this.channel = guild.threads.get(data.options.channel_id) || guild.channels.get(data.options.channel_id); if(data.options.message_id) { - this.message = this.channel && this.channel.messages.get(data.options.message_id) || {id: data.options.message_id}; + this.message = this.channel?.messages.get(data.options.message_id) || {id: data.options.message_id}; } } if(data.options.delete_member_days) { @@ -115,14 +115,14 @@ class GuildAuditLogEntry extends Base { if(this.actionType < 10) { // Guild return this.guild; } else if(this.actionType < 20) { // Channel - return this.guild && this.guild.channels.get(this.targetID); + return this.guild?.channels.get(this.targetID); } else if(this.actionType < 30) { // Member if(this.actionType === AuditLogActions.MEMBER_MOVE || this.actionType === AuditLogActions.MEMBER_DISCONNECT) { // MEMBER_MOVE / MEMBER_DISCONNECT return null; } - return this.guild && this.guild.members.get(this.targetID); + return this.guild?.members.get(this.targetID); } else if(this.actionType < 40) { // Role - return this.guild && this.guild.roles.get(this.targetID); + return this.guild?.roles.get(this.targetID); } else if(this.actionType < 50) { // Invite const changes = this.actionType === 42 ? this.before : this.after; // Apparently the meaning of life is a deleted invite return new Invite({ @@ -135,29 +135,29 @@ class GuildAuditLogEntry extends Base { max_uses: changes.max_uses, max_age: changes.max_age, temporary: changes.temporary - }, this.guild && this.guild.shard.client); + }, this.guild?.shard.client); } else if(this.actionType < 60) { // Webhook return null; // Go get the webhook yourself } else if(this.actionType < 70) { // Emoji - return this.guild && this.guild.emojis.find((emoji) => emoji.id === this.targetID); + return this.guild?.emojis.find((emoji) => emoji.id === this.targetID); } else if(this.actionType < 80) { // Message - return this.guild && this.guild.shard.client.users.get(this.targetID); + return this.guild?.shard.client.users.get(this.targetID); } else if(this.actionType < 83) { // Integrations return null; } else if(this.actionType < 90) { // Stage Instances - return this.guild && this.guild.stageInstances.get(this.targetID); + return this.guild?.stageInstances.get(this.targetID); } else if(this.actionType < 100) { // Sticker - return this.guild && this.guild.stickers.find((sticker) => sticker.id === this.targetID); + return this.guild?.stickers.find((sticker) => sticker.id === this.targetID); } else if(this.actionType < 110) { // Guild Scheduled Events - return this.guild && this.guild.events.get(this.targetID); + return this.guild?.events.get(this.targetID); } else if(this.actionType < 120) { // Thread - return this.guild && this.guild.threads.get(this.targetID); + return this.guild?.threads.get(this.targetID); } else if(this.actionType < 140) { // Application Command return null; } else if(this.actionType < 143) { // Auto Moderation Rule Updates return null; } else if(this.actionType < 146) { // Auto Moderation Actions - return this.guild && this.guild.shard.client.users.get(this.targetID); + return this.guild?.shard.client.users.get(this.targetID); } else { throw new Error("Unrecognized action type: " + this.actionType); } diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index f02570eb..05b0ce14 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -130,20 +130,20 @@ class GuildChannel extends Channel { return new Permission(Permissions.all); } const channel = this instanceof ThreadChannel ? this.guild.channels.get(this.parentID) : this; - let overwrite = channel && channel.permissionOverwrites.get(this.guild.id); + let overwrite = channel?.permissionOverwrites.get(this.guild.id); if(overwrite) { permission = (permission & ~overwrite.deny) | overwrite.allow; } let deny = 0n; let allow = 0n; for(const roleID of member.roles) { - if((overwrite = channel && channel.permissionOverwrites.get(roleID))) { + if((overwrite = channel?.permissionOverwrites.get(roleID))) { deny |= overwrite.deny; allow |= overwrite.allow; } } permission = (permission & ~deny) | allow; - overwrite = channel && channel.permissionOverwrites.get(member.id); + overwrite = channel?.permissionOverwrites.get(member.id); if(overwrite) { permission = (permission & ~overwrite.deny) | overwrite.allow; } diff --git a/lib/structures/GuildPreview.js b/lib/structures/GuildPreview.js index b3b54f0b..cdccd9f7 100644 --- a/lib/structures/GuildPreview.js +++ b/lib/structures/GuildPreview.js @@ -22,9 +22,10 @@ const Endpoints = require("../rest/Endpoints.js"); * @prop {Array} stickers An array of guild sticker objects */ class GuildPreview extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.name = data.name; this.icon = data.icon; @@ -39,15 +40,15 @@ class GuildPreview extends Base { } get iconURL() { - return this.icon ? this._client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon)) : null; + return this.icon ? this.#client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon)) : null; } get splashURL() { - return this.splash ? this._client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash)) : null; + return this.splash ? this.#client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash)) : null; } get discoverySplashURL() { - return this.discoverySplash ? this._client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash)) : null; + return this.discoverySplash ? this.#client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash)) : null; } /** @@ -57,7 +58,7 @@ class GuildPreview extends Base { * @returns {String?} */ dynamicDiscoverySplashURL(format, size) { - return this.discoverySplash ? this._client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash), format, size) : null; + return this.discoverySplash ? this.#client._formatImage(Endpoints.GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash), format, size) : null; } /** @@ -67,7 +68,7 @@ class GuildPreview extends Base { * @returns {String?} */ dynamicIconURL(format, size) { - return this.icon ? this._client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon), format, size) : null; + return this.icon ? this.#client._formatImage(Endpoints.GUILD_ICON(this.id, this.icon), format, size) : null; } /** @@ -77,7 +78,7 @@ class GuildPreview extends Base { * @returns {String?} */ dynamicSplashURL(format, size) { - return this.splash ? this._client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash), format, size) : null; + return this.splash ? this.#client._formatImage(Endpoints.GUILD_SPLASH(this.id, this.splash), format, size) : null; } toJSON(props = []) { diff --git a/lib/structures/GuildScheduledEvent.js b/lib/structures/GuildScheduledEvent.js index 19c244e9..f83004f8 100644 --- a/lib/structures/GuildScheduledEvent.js +++ b/lib/structures/GuildScheduledEvent.js @@ -24,10 +24,11 @@ const Endpoints = require("../rest/Endpoints"); * @prop {Number?} userCount The number of users subscribed to the event */ class GuildScheduledEvent extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; if(data.creator !== undefined) { this.creator = client.users.update(data.creator, this.client); } else { @@ -43,11 +44,7 @@ class GuildScheduledEvent extends Base { update(data) { if(data.channel_id !== undefined) { if(data.channel_id !== null) { - if(this._client.guilds.get(data.guild_id)) { - this.channel = this._client.guilds.get(data.guild_id).channels.get(data.channel_id) || {id: data.channel_id}; - } else { - this.channel = {id: data.channel_id}; - } + this.channel = this.#client.guilds.get(data.guild_id)?.channels.get(data.channel_id) || {id: data.channel_id}; } else { this.channel = null; } @@ -88,7 +85,7 @@ class GuildScheduledEvent extends Base { } get imageURL() { - return this.image ? this._client._formatImage(Endpoints.GUILD_SCHEDULED_EVENT_COVER(this.id, this.image)) : null; + return this.image ? this.#client._formatImage(Endpoints.GUILD_SCHEDULED_EVENT_COVER(this.id, this.image)) : null; } /** @@ -96,7 +93,7 @@ class GuildScheduledEvent extends Base { * @returns {Promise} */ delete() { - return this._client.deleteGuildScheduledEvent.call(this._client, this.guildID, this.id); + return this.#client.deleteGuildScheduledEvent.call(this.#client, this.guildID, this.id); } /** @@ -117,7 +114,7 @@ class GuildScheduledEvent extends Base { * @returns {Promise} */ edit(event, reason) { - return this._client.editGuildScheduledEvent.call(this._client, this.guildID, this.id, event, reason); + return this.#client.editGuildScheduledEvent.call(this.#client, this.guildID, this.id, event, reason); } /** @@ -130,7 +127,7 @@ class GuildScheduledEvent extends Base { * @returns {Promise>} */ getUsers(options) { - return this._client.getGuildScheduledEventUsers.call(this._client, this.guild.id, this.id, options); + return this.#client.getGuildScheduledEventUsers.call(this.#client, this.guild.id, this.id, options); } toJSON(props = []) { diff --git a/lib/structures/GuildTemplate.js b/lib/structures/GuildTemplate.js index 97b0125c..ce6e9911 100644 --- a/lib/structures/GuildTemplate.js +++ b/lib/structures/GuildTemplate.js @@ -15,8 +15,9 @@ const Guild = require("./Guild"); * @prop {Number} usageCount Number of times this template has been used */ class GuildTemplate { + #client; constructor(data, client) { - this._client = client; + this.#client = client; this.code = data.code; this.createdAt = Date.parse(data.created_at); this.creator = client.users.update(data.creator, client); @@ -36,7 +37,7 @@ class GuildTemplate { * @returns {Promise} */ createGuild(name, icon) { - return this._client.createGuildFromTemplate.call(this._client, this.code, name, icon); + return this.#client.createGuildFromTemplate.call(this.#client, this.code, name, icon); } /** @@ -44,7 +45,7 @@ class GuildTemplate { * @returns {Promise} */ delete() { - return this._client.deleteGuildTemplate.call(this._client, this.sourceGuild.id, this.code); + return this.#client.deleteGuildTemplate.call(this.#client, this.sourceGuild.id, this.code); } /** @@ -55,7 +56,7 @@ class GuildTemplate { * @returns {Promise} */ edit(options) { - return this._client.editGuildTemplate.call(this._client, this.sourceGuild.id, this.code, options); + return this.#client.editGuildTemplate.call(this.#client, this.sourceGuild.id, this.code, options); } /** @@ -63,7 +64,7 @@ class GuildTemplate { * @returns {Promise} */ sync() { - return this._client.syncGuildTemplate.call(this._client, this.sourceGuild.id, this.code); + return this.#client.syncGuildTemplate.call(this.#client, this.sourceGuild.id, this.code); } toJSON(props = []) { diff --git a/lib/structures/Interaction.js b/lib/structures/Interaction.js index 9776edfa..15acf534 100644 --- a/lib/structures/Interaction.js +++ b/lib/structures/Interaction.js @@ -23,9 +23,10 @@ const Permission = require("./Permission"); * @prop {Number} version The interaction version */ class Interaction extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.applicationID = data.application_id; this.token = data.token; @@ -34,7 +35,7 @@ class Interaction extends Base { this.acknowledged = false; if(data.channel_id !== undefined) { - this.channel = this._client.getChannel(data.channel_id) || { + this.channel = this.#client.getChannel(data.channel_id) || { id: data.channel_id }; } @@ -52,14 +53,14 @@ class Interaction extends Base { data.member.id = data.member.user.id; this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); - this.member = new Member(data.member, guild, this._client); + const guild = this.#client.guilds.get(data.guild_id); + this.member = new Member(data.member, guild, this.#client); } this.user = this.member.user; } if(data.user !== undefined) { - this.user = this._client.users.update(data.user, client); + this.user = this.#client.users.update(data.user, client); } if(data.locale !== undefined) { diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js index 9f444623..5440e2a9 100644 --- a/lib/structures/Invite.js +++ b/lib/structures/Invite.js @@ -33,9 +33,11 @@ const GuildScheduledEvent = require("./GuildScheduledEvent"); * @prop {Number?} uses The number of invite uses */ class Invite extends Base { + #client; + #createdAt; constructor(data, client) { super(); - this._client = client; + this.#client = client; this.code = data.code; if(data.guild && client.guilds.has(data.guild.id)) { this.channel = client.guilds.get(data.guild.id).channels.update(data.channel, client); @@ -56,7 +58,7 @@ class Invite extends Base { this.maxUses = data.max_uses !== undefined ? data.max_uses : null; this.maxAge = data.max_age !== undefined ? data.max_age : null; this.temporary = data.temporary !== undefined ? data.temporary : null; - this._createdAt = data.created_at !== undefined ? data.created_at : null; + this.#createdAt = data.created_at !== undefined ? data.created_at : null; this.presenceCount = data.approximate_presence_count !== undefined ? data.approximate_presence_count : null; this.memberCount = data.approximate_member_count !== undefined ? data.approximate_member_count : null; if(data.stage_instance !== undefined) { @@ -80,7 +82,7 @@ class Invite extends Base { this.targetType = data.target_type; } if(data.target_user !== undefined) { - this.targetUser = client.users.update(data.target_user, this._client); + this.targetUser = client.users.update(data.target_user, this.#client); } if(data.expires_at !== undefined) { this.expiresAt = Date.parse(data.expires_at); @@ -91,7 +93,7 @@ class Invite extends Base { } get createdAt() { - return Date.parse(this._createdAt); + return Date.parse(this.#createdAt); } /** @@ -100,7 +102,7 @@ class Invite extends Base { * @returns {Promise} */ delete(reason) { - return this._client.deleteInvite.call(this._client, this.code, reason); + return this.#client.deleteInvite.call(this.#client, this.code, reason); } toString() { diff --git a/lib/structures/Member.js b/lib/structures/Member.js index 21cfa53e..cd2787f6 100644 --- a/lib/structures/Member.js +++ b/lib/structures/Member.js @@ -176,17 +176,13 @@ class Member extends Base { } get voiceState() { - if(this.guild && this.guild.voiceStates.has(this.id)) { - return this.guild.voiceStates.get(this.id); - } else { - return new VoiceState({ - id: this.id - }); - } + return this.guild?.voiceStates.get(this.id) || new VoiceState({ + id: this.id + }); } get game() { - return this.activities && this.activities.length > 0 ? this.activities[0] : null; + return this.activities?.length > 0 ? this.activities[0] : null; } /** diff --git a/lib/structures/Message.js b/lib/structures/Message.js index bf8b81ff..dfec2ea8 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -54,13 +54,15 @@ const Collection = require("../util/Collection"); * @prop {String?} webhookID ID of the webhook that sent the message */ class Message extends Base { + #channelMentions; + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.type = data.type || 0; this.attachments = new Collection(Attachment); this.timestamp = Date.parse(data.timestamp); - this.channel = this._client.getChannel(data.channel_id) || { + this.channel = this.#client.getChannel(data.channel_id) || { id: data.channel_id }; this.content = ""; @@ -82,19 +84,19 @@ class Message extends Base { if(data.author) { if(data.author.discriminator !== "0000") { - this.author = this._client.users.update(data.author, client); + this.author = this.#client.users.update(data.author, client); } else { this.author = new User(data.author, client); } } else { - this._client.emit("error", new Error("MESSAGE_CREATE but no message author:\n" + JSON.stringify(data, null, 2))); + this.#client.emit("error", new Error("MESSAGE_CREATE but no message author:\n" + JSON.stringify(data, null, 2))); } if(data.referenced_message) { - const channel = this._client.getChannel(data.referenced_message.channel_id); + const channel = this.#client.getChannel(data.referenced_message.channel_id); if(channel) { - this.referencedMessage = channel.messages.update(data.referenced_message, this._client); + this.referencedMessage = channel.messages.update(data.referenced_message, this.#client); } else { - this.referencedMessage = new Message(data.referenced_message, this._client); + this.referencedMessage = new Message(data.referenced_message, this.#client); } } else { this.referencedMessage = data.referenced_message; @@ -103,7 +105,7 @@ class Message extends Base { if(data.interaction) { this.interaction = data.interaction; let interactionMember; - const interactionUser = this._client.users.update(data.interaction.user, client); + const interactionUser = this.#client.users.update(data.interaction.user, client); if(data.interaction.member) { data.interaction.member.id = data.interaction.user.id; if(this.channel.guild) { @@ -111,7 +113,7 @@ class Message extends Base { } else { interactionMember = data.interaction.member; } - } else if(this.channel.guild && this.channel.guild.members.has(data.interaction.user.id)) { + } else if(this.channel.guild?.members.has(data.interaction.user.id)) { interactionMember = this.channel.guild.members.get(data.interaction.user.id); } else { interactionMember = null; @@ -135,9 +137,7 @@ class Message extends Base { this.member = null; } - if(!this.guildID) { - this.guildID = this.channel.guild.id; - } + this.guildID ??= this.channel.guild.id; if(data.thread) { this.thread = this.channel.guild.threads.update(data.thread, client); @@ -268,7 +268,7 @@ class Message extends Base { break; } default: { - this._client.emit("warn", `Unhandled MESSAGE_CREATE type: ${JSON.stringify(data, null, 2)}`); + this.#client.emit("warn", `Unhandled MESSAGE_CREATE type: ${JSON.stringify(data, null, 2)}`); break; } } @@ -282,7 +282,7 @@ class Message extends Base { this.mentionEveryone = !!data.mention_everyone; this.mentions = data.mentions.map((mention) => { - const user = this._client.users.add(mention, client); + const user = this.#client.users.add(mention, client); if(mention.member && this.channel.guild) { mention.member.id = mention.id; this.channel.guild.members.update(mention.member, this.channel.guild); @@ -340,7 +340,7 @@ class Message extends Base { if(data.sticker_items !== undefined) { this.stickerItems = data.sticker_items.map((sticker) => { if(sticker.user) { - sticker.user = this._client.users.update(sticker.user, client); + sticker.user = this.#client.users.update(sticker.user, client); } return sticker; @@ -366,48 +366,38 @@ class Message extends Base { } get channelMentions() { - if(this._channelMentions) { - return this._channelMentions; - } - - return (this._channelMentions = (this.content && this.content.match(/<#[0-9]+>/g) || []).map((mention) => mention.substring(2, mention.length - 1))); + return this.#channelMentions ?? (this.#channelMentions = (this.content?.match(/<#[0-9]+>/g) || []).map((mention) => mention.substring(2, mention.length - 1))); } get cleanContent() { - let cleanContent = this.content && this.content.replace(//g, "$1") || ""; + let cleanContent = this.content?.replace(//g, "$1") || ""; let authorName = this.author.username; - if(this.channel.guild) { - const member = this.channel.guild.members.get(this.author.id); - if(member && member.nick) { - authorName = member.nick; - } + const member = this.channel.guild?.members.get(this.author.id); + if(member?.nick) { + authorName = member.nick; } cleanContent = cleanContent.replace(new RegExp(`<@!?${this.author.id}>`, "g"), "@\u200b" + authorName); - if(this.mentions) { - this.mentions.forEach((mention) => { - if(this.channel.guild) { - const member = this.channel.guild.members.get(mention.id); - if(member && member.nick) { - cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), "@\u200b" + member.nick); - } - } - cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), "@\u200b" + mention.username); - }); - } + this.mentions?.forEach((mention) => { + const member = this.channel.guild?.members.get(mention.id); + if(member?.nick) { + cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), "@\u200b" + member.nick); + } + cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), "@\u200b" + mention.username); + }); if(this.channel.guild && this.roleMentions) { for(const roleID of this.roleMentions) { const role = this.channel.guild.roles.get(roleID); - const roleName = role ? role.name : "deleted-role"; + const roleName = role?.name ?? "deleted-role"; cleanContent = cleanContent.replace(new RegExp(`<@&${roleID}>`, "g"), "@\u200b" + roleName); } } this.channelMentions.forEach((id) => { - const channel = this._client.getChannel(id); - if(channel && channel.name && channel.mention) { + const channel = this.#client.getChannel(id); + if(channel?.name && channel?.mention) { cleanContent = cleanContent.replace(channel.mention, "#" + channel.name); } }); @@ -428,7 +418,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot have reactions"); } - return this._client.addMessageReaction.call(this._client, this.channel.id, this.id, reaction); + return this.#client.addMessageReaction.call(this.#client, this.channel.id, this.id, reaction); } /** @@ -439,7 +429,7 @@ class Message extends Base { * @returns {Promise} */ createThreadWithMessage(options) { - return this._client.createThreadWithMessage.call(this._client, this.channel.id, this.id, options); + return this.#client.createThreadWithMessage.call(this.#client, this.channel.id, this.id, options); } /** @@ -450,7 +440,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot be crossposted"); } - return this._client.crosspostMessage.call(this._client, this.channel.id, this.id); + return this.#client.crosspostMessage.call(this.#client, this.channel.id, this.id); } /** @@ -462,7 +452,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot be deleted"); } - return this._client.deleteMessage.call(this._client, this.channel.id, this.id, reason); + return this.#client.deleteMessage.call(this.#client, this.channel.id, this.id, reason); } /** @@ -477,7 +467,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot be deleted"); } - return this._client.deleteWebhookMessage.call(this._client, this.webhookID, token, this.id); + return this.#client.deleteWebhookMessage.call(this.#client, this.webhookID, token, this.id); } /** @@ -502,7 +492,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot be edited via this method"); } - return this._client.editMessage.call(this._client, this.channel.id, this.id, content); + return this.#client.editMessage.call(this.#client, this.channel.id, this.id, content); } /** @@ -528,7 +518,7 @@ class Message extends Base { if(!this.webhookID) { throw new Error("Message is not a webhook"); } - return this._client.editWebhookMessage.call(this._client, this.webhookID, token, this.id, options); + return this.#client.editWebhookMessage.call(this.#client, this.webhookID, token, this.id, options); } /** @@ -543,7 +533,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot have reactions"); } - return this._client.getMessageReaction.call(this._client, this.channel.id, this.id, reaction, options); + return this.#client.getMessageReaction.call(this.#client, this.channel.id, this.id, reaction, options); } /** @@ -554,7 +544,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot be pinned"); } - return this._client.pinMessage.call(this._client, this.channel.id, this.id); + return this.#client.pinMessage.call(this.#client, this.channel.id, this.id); } /** @@ -567,7 +557,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot have reactions"); } - return this._client.removeMessageReaction.call(this._client, this.channel.id, this.id, reaction, userID); + return this.#client.removeMessageReaction.call(this.#client, this.channel.id, this.id, reaction, userID); } /** @@ -579,7 +569,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot have reactions"); } - return this._client.removeMessageReactionEmoji.call(this._client, this.channel.id, this.id, reaction); + return this.#client.removeMessageReactionEmoji.call(this.#client, this.channel.id, this.id, reaction); } /** @@ -590,7 +580,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot have reactions"); } - return this._client.removeMessageReactions.call(this._client, this.channel.id, this.id); + return this.#client.removeMessageReactions.call(this.#client, this.channel.id, this.id); } /** @@ -601,7 +591,7 @@ class Message extends Base { if(this.flags & MessageFlags.EPHEMERAL) { throw new Error("Ephemeral messages cannot be pinned"); } - return this._client.unpinMessage.call(this._client, this.channel.id, this.id); + return this.#client.unpinMessage.call(this.#client, this.channel.id, this.id); } toJSON(props = []) { diff --git a/lib/structures/ModalSubmitInteraction.js b/lib/structures/ModalSubmitInteraction.js index 8198c81d..ed0accf9 100644 --- a/lib/structures/ModalSubmitInteraction.js +++ b/lib/structures/ModalSubmitInteraction.js @@ -16,10 +16,12 @@ const {InteractionResponseTypes} = require("../Constants"); * @prop {User?} user The user who triggered the interaction (This is only sent when the interaction is invoked within a dm) */ class ModalSubmitInteraction extends Interaction { + #client; constructor(data, client) { super(data, client); + this.#client = client; - this.channel = this._client.getChannel(data.channel_id) || { + this.channel = this.#client.getChannel(data.channel_id) || { id: data.channel_id }; @@ -34,13 +36,13 @@ class ModalSubmitInteraction extends Interaction { data.member.id = data.member.user.id; this.member = this.channel.guild.members.update(data.member, this.channel.guild); } else { - const guild = this._client.guilds.get(data.guild_id); + const guild = this.#client.guilds.get(data.guild_id); this.member = guild.members.update(data.member, guild); } } if(data.user !== undefined) { - this.user = this._client.users.update(data.user, client); + this.user = this.#client.users.update(data.user, client); } } @@ -84,7 +86,7 @@ class ModalSubmitInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.executeWebhook.call(this._client, this.applicationID, this.token, Object.assign({wait: true}, content)); + return this.#client.executeWebhook.call(this.#client, this.applicationID, this.token, Object.assign({wait: true}, content)); } /** @@ -118,14 +120,14 @@ class ModalSubmitInteraction extends Interaction { content.content = "" + content.content; } if(content.content !== undefined || content.embeds || content.allowedMentions) { - content.allowed_mentions = this._client._formatAllowedMentions(content.allowedMentions); + content.allowed_mentions = this.#client._formatAllowedMentions(content.allowedMentions); } } - const {files, attachments} = this._client._processAttachments(content.attachments); + const {files, attachments} = this.#client._processAttachments(content.attachments); content.attachments = attachments; - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE, data: content }, files).then(() => this.update()); @@ -141,7 +143,7 @@ class ModalSubmitInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, data: { flags: flags || 0 @@ -158,7 +160,7 @@ class ModalSubmitInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.DEFERRED_UPDATE_MESSAGE }).then(() => this.update()); } @@ -172,7 +174,7 @@ class ModalSubmitInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("deleteMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first."); } - return this._client.deleteWebhookMessage.call(this._client, this.applicationID, this.token, messageID); + return this.#client.deleteWebhookMessage.call(this.#client, this.applicationID, this.token, messageID); } /** @@ -184,7 +186,7 @@ class ModalSubmitInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("deleteOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first."); } - return this._client.deleteWebhookMessage.call(this._client, this.applicationID, this.token, "@original"); + return this.#client.deleteWebhookMessage.call(this.#client, this.applicationID, this.token, "@original"); } /** @@ -219,7 +221,7 @@ class ModalSubmitInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.editWebhookMessage.call(this._client, this.applicationID, this.token, messageID, content); + return this.#client.editWebhookMessage.call(this.#client, this.applicationID, this.token, messageID, content); } /** @@ -253,7 +255,7 @@ class ModalSubmitInteraction extends Interaction { content.content = "" + content.content; } } - return this._client.editWebhookMessage.call(this._client, this.applicationID, this.token, "@original", content); + return this.#client.editWebhookMessage.call(this.#client, this.applicationID, this.token, "@original", content); } /** @@ -290,14 +292,14 @@ class ModalSubmitInteraction extends Interaction { content.content = "" + content.content; } if(content.content !== undefined || content.embeds || content.allowedMentions) { - content.allowed_mentions = this._client._formatAllowedMentions(content.allowedMentions); + content.allowed_mentions = this.#client._formatAllowedMentions(content.allowedMentions); } } - const {files, attachments} = this._client._processAttachments(content.attachments); + const {files, attachments} = this.#client._processAttachments(content.attachments); content.attachments = attachments; - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.UPDATE_MESSAGE, data: content }, files).then(() => this.update()); @@ -312,7 +314,7 @@ class ModalSubmitInteraction extends Interaction { if(this.acknowledged === false) { throw new Error("getOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first."); } - return this._client.getWebhookMessage.call(this._client, this.applicationID, this.token, "@original"); + return this.#client.getWebhookMessage.call(this.#client, this.applicationID, this.token, "@original"); } } diff --git a/lib/structures/Permission.js b/lib/structures/Permission.js index 79b2b475..ee0be342 100644 --- a/lib/structures/Permission.js +++ b/lib/structures/Permission.js @@ -21,6 +21,7 @@ const {Permissions} = require("../Constants"); * [A full list of permission nodes can be found in Constants](https://github.com/projectdysnomia/dysnomia/blob/dev/lib/Constants.js#L442) */ class Permission extends Base { + #json; constructor(allow, deny = 0) { super(); this.allow = BigInt(allow); @@ -28,19 +29,19 @@ class Permission extends Base { } get json() { - if(!this._json) { - this._json = {}; + if(!this.#json) { + this.#json = {}; for(const perm of Object.keys(Permissions)) { if(!perm.startsWith("all")) { if(this.allow & Permissions[perm]) { - this._json[perm] = true; + this.#json[perm] = true; } else if(this.deny & Permissions[perm]) { - this._json[perm] = false; + this.#json[perm] = false; } } } } - return this._json; + return this.#json; } /** diff --git a/lib/structures/PingInteraction.js b/lib/structures/PingInteraction.js index 3a4ea9ca..dc3277eb 100644 --- a/lib/structures/PingInteraction.js +++ b/lib/structures/PingInteraction.js @@ -8,6 +8,11 @@ const {InteractionResponseTypes} = require("../Constants"); * @extends Interaction */ class PingInteraction extends Interaction { + #client; + constructor(data, client) { + super(data, client); + this.#client = client; + } /** * Acknowledges the ping interaction with a pong response. @@ -27,7 +32,7 @@ class PingInteraction extends Interaction { if(this.acknowledged === true) { throw new Error("You have already acknowledged this interaction."); } - return this._client.createInteractionResponse.call(this._client, this.id, this.token, { + return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, { type: InteractionResponseTypes.PONG }).then(() => this.update()); } diff --git a/lib/structures/StageInstance.js b/lib/structures/StageInstance.js index b8ee76fc..f9e7580e 100644 --- a/lib/structures/StageInstance.js +++ b/lib/structures/StageInstance.js @@ -13,12 +13,13 @@ const Base = require("./Base"); * @prop {String} topic The stage instance topic */ class StageInstance extends Base { + #client; constructor(data, client) { super(data.id); - this._client = client; + this.#client = client; this.channel = client.getChannel(data.channel_id) || {id: data.channel_id}; this.guild = client.guilds.get(data.guild_id) || {id: data.guild_id}; - this.guildScheduledEvent = (this.guild.events && this.guild.events.get(data.guild_scheduled_event_id)) || {id: data.guild_scheduled_event_id}; + this.guildScheduledEvent = this.guild.events?.get(data.guild_scheduled_event_id) || {id: data.guild_scheduled_event_id}; this.update(data); } @@ -39,7 +40,7 @@ class StageInstance extends Base { * @returns {Promise} */ delete() { - return this._client.deleteStageInstance.call(this._client, this.channel.id); + return this.#client.deleteStageInstance.call(this.#client, this.channel.id); } /** @@ -50,7 +51,7 @@ class StageInstance extends Base { * @returns {Promise} */ edit(options) { - return this._client.editStageInstance.call(this._client, this.channel.id, options); + return this.#client.editStageInstance.call(this.#client, this.channel.id, options); } } diff --git a/lib/structures/ThreadMember.js b/lib/structures/ThreadMember.js index c765456f..7b78e53d 100644 --- a/lib/structures/ThreadMember.js +++ b/lib/structures/ThreadMember.js @@ -12,9 +12,10 @@ const Member = require("./Member"); * @prop {String} threadID The ID of the thread this member is a part of */ class ThreadMember extends Base { + #client; constructor(data, client) { super(data.user_id); - this._client = client; + this.#client = client; this.flags = data.flags; this.threadID = data.thread_id || data.id; // Thanks Discord this.joinTimestamp = Date.parse(data.join_timestamp); @@ -24,7 +25,7 @@ class ThreadMember extends Base { data.member.id = this.id; } - const guild = this._client.guilds.get(this._client.threadGuildMap[this.threadID]); + const guild = this.#client.guilds.get(this.#client.threadGuildMap[this.threadID]); this.guildMember = guild ? guild.members.update(data.member, guild) : new Member(data.member, guild, client); if(data.presence !== undefined) { this.guildMember.update(data.presence); @@ -45,7 +46,7 @@ class ThreadMember extends Base { * @returns {Promise} */ leave() { - return this._client.leaveThread.call(this._client, this.threadID, this.id); + return this.#client.leaveThread.call(this.#client, this.threadID, this.id); } toJSON(props = []) { diff --git a/lib/structures/User.js b/lib/structures/User.js index 3fee5570..ac7db902 100644 --- a/lib/structures/User.js +++ b/lib/structures/User.js @@ -23,12 +23,14 @@ const Endpoints = require("../rest/Endpoints"); * @prop {String} username The username of the user */ class User extends Base { + #client; + #missingClientError; constructor(data, client) { super(data.id); if(!client) { - this._missingClientError = new Error("Missing client in constructor"); // Preserve constructor callstack + this.#missingClientError = new Error("Missing client in constructor"); // Preserve constructor callstack } - this._client = client; + this.#client = client; this.bot = !!data.bot; this.system = !!data.system; this.update(data); @@ -56,20 +58,20 @@ class User extends Base { } get avatarURL() { - if(this._missingClientError) { - throw this._missingClientError; + if(this.#missingClientError) { + throw this.#missingClientError; } - return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar)) : this.defaultAvatarURL; + return this.avatar ? this.#client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar)) : this.defaultAvatarURL; } get bannerURL() { if(!this.banner) { return null; } - if(this._missingClientError) { - throw this._missingClientError; + if(this.#missingClientError) { + throw this.#missingClientError; } - return this._client._formatImage(Endpoints.BANNER(this.id, this.banner)); + return this.#client._formatImage(Endpoints.BANNER(this.id, this.banner)); } get defaultAvatar() { @@ -85,10 +87,10 @@ class User extends Base { } get staticAvatarURL() { - if(this._missingClientError) { - throw this._missingClientError; + if(this.#missingClientError) { + throw this.#missingClientError; } - return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), "jpg") : this.defaultAvatarURL; + return this.avatar ? this.#client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), "jpg") : this.defaultAvatarURL; } /** @@ -101,10 +103,10 @@ class User extends Base { if(!this.avatar) { return this.defaultAvatarURL; } - if(this._missingClientError) { - throw this._missingClientError; + if(this.#missingClientError) { + throw this.#missingClientError; } - return this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), format, size); + return this.#client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), format, size); } /** @@ -117,10 +119,10 @@ class User extends Base { if(!this.banner) { return null; } - if(this._missingClientError) { - throw this._missingClientError; + if(this.#missingClientError) { + throw this.#missingClientError; } - return this._client._formatImage(Endpoints.BANNER(this.id, this.banner), format, size); + return this.#client._formatImage(Endpoints.BANNER(this.id, this.banner), format, size); } /** @@ -128,7 +130,7 @@ class User extends Base { * @returns {Promise} */ getDMChannel() { - return this._client.getDMChannel.call(this._client, this.id); + return this.#client.getDMChannel.call(this.#client, this.id); } toJSON(props = []) { diff --git a/lib/util/BrowserWebSocket.js b/lib/util/BrowserWebSocket.js index b585a690..7078e35b 100644 --- a/lib/util/BrowserWebSocket.js +++ b/lib/util/BrowserWebSocket.js @@ -1,11 +1,11 @@ -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events").EventEmitter; +} catch{ + EventEmitter = require("node:events").EventEmitter; } class BrowserWebSocketError extends Error { @@ -21,6 +21,7 @@ class BrowserWebSocketError extends Error { * @prop {String} url The URL to connect to */ class BrowserWebSocket extends EventEmitter { + #ws; constructor(url) { super(); @@ -28,19 +29,19 @@ class BrowserWebSocket extends EventEmitter { throw new Error("BrowserWebSocket cannot be used outside of a browser environment"); } - this._ws = new window.WebSocket(url); - this._ws.onopen = () => this.emit("open"); - this._ws.onmessage = this._onMessage.bind(this); - this._ws.onerror = (event) => this.emit("error", new BrowserWebSocketError("Unknown error", event)); - this._ws.onclose = (event) => this.emit("close", event.code, event.reason); + this.#ws = new window.WebSocket(url); + this.#ws.onopen = () => this.emit("open"); + this.#ws.onmessage = this.#onMessage.bind(this); + this.#ws.onerror = (event) => this.emit("error", new BrowserWebSocketError("Unknown error", event)); + this.#ws.onclose = (event) => this.emit("close", event.code, event.reason); } get readyState() { - return this._ws.readyState; + return this.#ws.readyState; } close(code, reason) { - return this._ws.close(code, reason); + return this.#ws.close(code, reason); } removeEventListener(type, listener) { @@ -48,14 +49,14 @@ class BrowserWebSocket extends EventEmitter { } send(data) { - return this._ws.send(data); + return this.#ws.send(data); } terminate() { - return this._ws.close(); + return this.#ws.close(); } - async _onMessage(event) { + async #onMessage(event) { if(event.data instanceof window.Blob) { this.emit("message", await event.data.arrayBuffer()); } else { diff --git a/lib/util/Bucket.js b/lib/util/Bucket.js index cca9e38c..8be046fd 100644 --- a/lib/util/Bucket.js +++ b/lib/util/Bucket.js @@ -1,6 +1,6 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); /** @@ -12,6 +12,10 @@ const Base = require("../structures/Base"); * @prop {Number} tokens How many tokens the bucket has consumed in this interval */ class Bucket { + #queue = []; + lastReset = 0; + lastSend = 0; + tokens = 0; /** * Construct a Bucket * @arg {Number} tokenLimit The max number of tokens the bucket can consume per interval @@ -25,13 +29,11 @@ class Bucket { this.tokenLimit = tokenLimit; this.interval = interval; this.latencyRef = options.latencyRef || {latency: 0}; - this.lastReset = this.tokens = this.lastSend = 0; this.reservedTokens = options.reservedTokens || 0; - this._queue = []; } check() { - if(this.timeout || this._queue.length === 0) { + if(this.timeout || this.#queue.length === 0) { return; } if(this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency < Date.now()) { @@ -42,11 +44,11 @@ class Bucket { let val; let tokensAvailable = this.tokens < this.tokenLimit; let unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens); - while(this._queue.length > 0 && (unreservedTokensAvailable || (tokensAvailable && this._queue[0].priority))) { + while(this.#queue.length > 0 && (unreservedTokensAvailable || (tokensAvailable && this.#queue[0].priority))) { this.tokens++; tokensAvailable = this.tokens < this.tokenLimit; unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens); - const item = this._queue.shift(); + const item = this.#queue.shift(); val = this.latencyRef.latency - Date.now() + this.lastSend; if(this.latencyRef.latency === 0 || val <= 0) { item.func(); @@ -59,7 +61,7 @@ class Bucket { } } - if(this._queue.length > 0 && !this.timeout) { + if(this.#queue.length > 0 && !this.timeout) { this.timeout = setTimeout(() => { this.timeout = null; this.check(); @@ -74,9 +76,9 @@ class Bucket { */ queue(func, priority=false) { if(priority) { - this._queue.unshift({func, priority}); + this.#queue.unshift({func, priority}); } else { - this._queue.push({func, priority}); + this.#queue.push({func, priority}); } this.check(); } diff --git a/lib/util/MultipartData.js b/lib/util/MultipartData.js index e9870cb4..e60f6016 100644 --- a/lib/util/MultipartData.js +++ b/lib/util/MultipartData.js @@ -1,10 +1,8 @@ "use strict"; class MultipartData { - constructor() { - this.boundary = "----------------Dysnomia"; - this.bufs = []; - } + boundary = "----------------Dysnomia"; + bufs = []; attach(fieldName, data, filename) { if(data === undefined) { diff --git a/lib/util/Opus.js b/lib/util/Opus.js index 3a94a75e..f9b40431 100644 --- a/lib/util/Opus.js +++ b/lib/util/Opus.js @@ -7,10 +7,10 @@ module.exports.createOpus = function createOpus(samplingRate, channels, bitrate) if(!NativeOpus && !OpusScript) { try { NativeOpus = require("@discordjs/opus"); - } catch(err) { + } catch{ try { OpusScript = require("opusscript"); - } catch(err) { // eslint-disable no-empty + } catch{ // eslint-disable no-empty } } } diff --git a/lib/util/SequentialBucket.js b/lib/util/SequentialBucket.js index 4806f5d4..1bd4dcff 100644 --- a/lib/util/SequentialBucket.js +++ b/lib/util/SequentialBucket.js @@ -1,6 +1,6 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); /** @@ -12,6 +12,9 @@ const Base = require("../structures/Base"); * @prop {Number} reset Timestamp of next reset */ class SequentialBucket { + #queue = []; + processing = false; + reset = 0; /** * Construct a SequentialBucket * @arg {Number} limit The max number of tokens the bucket can consume per interval @@ -20,14 +23,11 @@ class SequentialBucket { */ constructor(limit, latencyRef = {latency: 0}) { this.limit = this.remaining = limit; - this.reset = 0; - this.processing = false; this.latencyRef = latencyRef; - this._queue = []; } check(override) { - if(this._queue.length === 0) { + if(this.#queue.length === 0) { if(this.processing) { clearTimeout(this.processing); this.processing = false; @@ -53,8 +53,8 @@ class SequentialBucket { } --this.remaining; this.processing = true; - this._queue.shift()(() => { - if(this._queue.length > 0) { + this.#queue.shift()(() => { + if(this.#queue.length > 0) { this.check(true); } else { this.processing = false; @@ -68,9 +68,9 @@ class SequentialBucket { */ queue(func, short) { if(short) { - this._queue.unshift(func); + this.#queue.unshift(func); } else { - this._queue.push(func); + this.#queue.push(func); } this.check(); } diff --git a/lib/voice/Piper.js b/lib/voice/Piper.js index f4f7f7b4..3bddd371 100644 --- a/lib/voice/Piper.js +++ b/lib/voice/Piper.js @@ -1,25 +1,25 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); const DCAOpusTransformer = require("./streams/DCAOpusTransformer"); const FFmpegOggTransformer = require("./streams/FFmpegOggTransformer"); const FFmpegPCMTransformer = require("./streams/FFmpegPCMTransformer"); -const FS = require("fs"); -const HTTP = require("http"); -const HTTPS = require("https"); +const FS = require("node:fs"); +const HTTP = require("node:http"); +const HTTPS = require("node:https"); const OggOpusTransformer = require("./streams/OggOpusTransformer"); -const PassThroughStream = require("stream").PassThrough; +const PassThroughStream = require("node:stream").PassThrough; const PCMOpusTransformer = require("./streams/PCMOpusTransformer"); -const Stream = require("stream").Stream; +const Stream = require("node:stream").Stream; const VolumeTransformer = require("./streams/VolumeTransformer"); const WebmOpusTransformer = require("./streams/WebmOpusTransformer"); let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events").EventEmitter; +} catch{ + EventEmitter = require("node:events").EventEmitter; } function resolveHTTPType(link) { @@ -45,40 +45,39 @@ function webGetter(link, depth = 8) { } class Piper extends EventEmitter { + #dataPacketMax = 30; + #dataPacketMin = 15; + #dataPackets = []; + #endStream; + #retransformer = []; + encoding = false; + libopus = true; + opus = null; + volumeLevel = 1; + constructor(converterCommand, opusFactory) { super(); this.reset(); this.converterCommand = converterCommand; - this._dataPackets = []; - this._dataPacketMax = 30; - this._dataPacketMin = 15; - this.encoding = false; - this.libopus = true; - this.opusFactory = opusFactory; - this.opus = null; - - this.volumeLevel = 1; - - this._retransformer = []; this.addDataPacket = this.addDataPacket.bind(this); } get dataPacketCount() { - return this._dataPackets.length; + return this.#dataPackets.length; } addDataPacket(packet) { if(!this.encoding) { return; } - if(this._dataPackets.push(packet) < this._dataPacketMax && this._endStream && this._endStream.manualCB) { + if(this.#dataPackets.push(packet) < this.#dataPacketMax && this.#endStream?.manualCB) { process.nextTick(() => { - if(this._endStream && this._endStream.manualCB) { - this._endStream.transformCB(); + if(this.#endStream?.manualCB) { + this.#endStream.transformCB(); } }); } @@ -116,8 +115,8 @@ class Piper extends EventEmitter { return false; } - this._dataPacketMax = 30; - this._dataPacketMin = 15; + this.#dataPacketMax = 30; + this.#dataPacketMin = 15; if(typeof source !== "string") { this.streams.push(source.once("error", (e) => this.stop(e))); @@ -160,8 +159,8 @@ class Piper extends EventEmitter { frameSize: options.frameSize, pcmSize: options.pcmSize })).once("error", (e) => this.stop(e))); - this._dataPacketMax = 1; // Live volume updating - this._dataPacketMin = 4; + this.#dataPacketMax = 1; // Live volume updating + this.#dataPacketMin = 4; } else { if(this.libopus) { if(typeof source === "string") { @@ -211,13 +210,13 @@ class Piper extends EventEmitter { return false; } - this._endStream = this.streams[this.streams.length - 1]; - if(this._endStream.hasOwnProperty("manualCB")) { - this._endStream.manualCB = true; + this.#endStream = this.streams[this.streams.length - 1]; + if(this.#endStream.hasOwnProperty("manualCB")) { + this.#endStream.manualCB = true; } - this._endStream.on("data", this.addDataPacket); - this._endStream.once("end", () => this.stop(null, source)); + this.#endStream.on("data", this.addDataPacket); + this.#endStream.once("end", () => this.stop(null, source)); this.emit("start"); @@ -225,20 +224,18 @@ class Piper extends EventEmitter { } getDataPacket() { - if(this._dataPackets.length < this._dataPacketMin && this._endStream && this._endStream.manualCB) { - this._endStream.transformCB(); + if(this.#dataPackets.length < this.#dataPacketMin && this.#endStream?.manualCB) { + this.#endStream.transformCB(); } - if(this._retransformer.length === 0) { - return this._dataPackets.shift(); + if(this.#retransformer.length === 0) { + return this.#dataPackets.shift(); } else { // If we don't have an opus instance yet, create one. - if(!this.opus) { - this.opus = this.opusFactory(); - } + this.opus ??= this.opusFactory(); - const packet = this.opus.decode(this._dataPackets.shift()); + const packet = this.opus.decode(this.#dataPackets.shift()); for(let i = 0, num; i < packet.length - 1; i += 2) { - num = ~~(this._retransformer.shift() * packet.readInt16LE(i)); + num = ~~(this.#retransformer.shift() * packet.readInt16LE(i)); packet.writeInt16LE(num >= 32767 ? 32767 : num <= -32767 ? -32767 : num, i); } return this.opus.encode(packet, 3840 / 2 / 2); @@ -257,17 +254,17 @@ class Piper extends EventEmitter { } this.streams = []; - this._endStream = null; + this.#endStream = null; this.volume = null; } resetPackets() { // We no longer need this to convert inline volume, so... let it go. if(this.opus) { - this.opus.delete && this.opus.delete(); + this.opus.delete?.(); this.opus = null; } - this._dataPackets = []; + this.#dataPackets = []; } setVolume(volume) { @@ -296,9 +293,7 @@ class Piper extends EventEmitter { return; } - if(this._endStream) { - this._endStream.removeAllListeners("data"); - } + this.#endStream?.removeAllListeners("data"); this.reset(); if(this.encoding) { diff --git a/lib/voice/SharedStream.js b/lib/voice/SharedStream.js index d4dc133b..0e23d529 100644 --- a/lib/voice/SharedStream.js +++ b/lib/voice/SharedStream.js @@ -1,6 +1,6 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); const Piper = require("./Piper"); const VoiceConnection = require("./VoiceConnection"); @@ -10,8 +10,8 @@ const {createOpus} = require("../util/Opus"); let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events").EventEmitter; +} catch{ + EventEmitter = require("node:events").EventEmitter; } /** @@ -24,16 +24,19 @@ try { * @prop {Number} volume The current volume level of the connection */ class SharedStream extends EventEmitter { + #send; + bitrate = 64_000; + channels = 2; + ended = true; + frameDuration = 20; + playing = false; + samplingRate = 48_000; + speaking = false; + voiceConnections = new Collection(VoiceConnection); + constructor() { super(); - this.samplingRate = 48000; - this.frameDuration = 20; - this.channels = 2; - this.bitrate = 64000; - - this.voiceConnections = new Collection(VoiceConnection); - if(!VoiceConnection._converterCommand.cmd) { VoiceConnection._converterCommand.pickCommand(); } @@ -49,11 +52,7 @@ class SharedStream extends EventEmitter { this.piper.libopus = false; } - this.ended = true; - this.playing = false; - this.speaking = false; - - this._send = this._send.bind(this); + this.#send = this.#sendUnbound.bind(this); } get volume() { @@ -126,7 +125,7 @@ class SharedStream extends EventEmitter { */ this.emit("start"); - this._send(); + this.#send(); } /** @@ -162,7 +161,7 @@ class SharedStream extends EventEmitter { return; } this.ended = true; - if(this.current && this.current.timeout) { + if(this.current?.timeout) { clearTimeout(this.current.timeout); this.current.timeout = null; } @@ -179,26 +178,26 @@ class SharedStream extends EventEmitter { this.emit("end"); } - _incrementSequences() { + #incrementSequences() { for(const vc of this.voiceConnections.values()) { vc.sequence = (vc.sequence + 1) & 0xFFFF; } } - _incrementTimestamps(val) { + #incrementTimestamps(val) { for(const vc of this.voiceConnections.values()) { vc.timestamp = (vc.timestamp + val) >>> 0; } } - _send() { + #sendUnbound() { if(!this.piper.encoding && this.piper.dataPacketCount === 0) { return this.stopPlaying(); } - this._incrementTimestamps(this.current.options.frameSize); + this.#incrementTimestamps(this.current.options.frameSize); - this._incrementSequences(); + this.#incrementSequences(); if((this.current.buffer = this.piper.getDataPacket())) { if(this.current.startTime === 0) { @@ -213,8 +212,8 @@ class SharedStream extends EventEmitter { this.setSpeaking(false); } else { this.current.pausedTime += 4 * this.current.options.frameDuration; - this._incrementTimestamps(3 * this.current.options.frameSize); - this.current.timeout = setTimeout(this._send, 4 * this.current.options.frameDuration); + this.#incrementTimestamps(3 * this.current.options.frameSize); + this.current.timeout = setTimeout(this.#send, 4 * this.current.options.frameDuration); return; } } else { @@ -227,7 +226,7 @@ class SharedStream extends EventEmitter { } }); this.current.playTime += this.current.options.frameDuration; - this.current.timeout = setTimeout(this._send, this.current.startTime + this.current.pausedTime + this.current.playTime - Date.now()); + this.current.timeout = setTimeout(this.#send, this.current.startTime + this.current.pausedTime + this.current.playTime - Date.now()); } [util.inspect.custom]() { diff --git a/lib/voice/VoiceConnection.js b/lib/voice/VoiceConnection.js index 3c545343..d643600b 100644 --- a/lib/voice/VoiceConnection.js +++ b/lib/voice/VoiceConnection.js @@ -1,11 +1,11 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../structures/Base"); -const ChildProcess = require("child_process"); +const ChildProcess = require("node:child_process"); const {VoiceOPCodes, GatewayOPCodes} = require("../Constants"); -const Dgram = require("dgram"); -const Net = require("net"); +const Dgram = require("node:dgram"); +const Net = require("node:net"); const Piper = require("./Piper"); const VoiceDataStream = require("./VoiceDataStream"); const {createOpus} = require("../util/Opus"); @@ -15,8 +15,8 @@ const WebSocket = typeof window !== "undefined" ? require("../util/BrowserWebSoc let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events").EventEmitter; +} catch{ + EventEmitter = require("node:events").EventEmitter; } let Sodium = null; @@ -77,6 +77,31 @@ converterCommand.pickCommand = function pickCommand() { * @prop {Number} volume The current volume level of the connection */ class VoiceConnection extends EventEmitter { + #send; + bitrate = 64_000; + channelID = null; + channels = 2; + connecting = false; + connectionTimeout = null; + frameDuration = 20; + paused = true; + ready = false; + reconnecting = false; + samplingRate = 48_000; + sendBuffer = Buffer.allocUnsafe(16 + 32 + MAX_FRAME_SIZE); + sendNonce = Buffer.alloc(24); + + sequence = 0; + speaking = false; + ssrcUserMap = {}; + timestamp = 0; + + // frameSize and pcmSize must come after the properties they reference to get initialized correctly + /* eslint-disable sort-class-members/sort-class-members */ + frameSize = this.samplingRate * this.frameDuration / 1_000; + pcmSize = this.frameSize * this.channels * 2; + /* eslint-enable sort-class-members/sort-class-members */ + constructor(id, options = {}) { super(); @@ -87,22 +112,16 @@ class VoiceConnection extends EventEmitter { if(!Sodium && !NaCl) { try { Sodium = require("sodium-native"); - } catch(err) { + } catch{ try { NaCl = require("tweetnacl"); - } catch(err) { // eslint-disable no-empty + } catch{ // eslint-disable no-empty throw new Error("Error loading tweetnacl/libsodium, voice not available"); } } } this.id = id; - this.samplingRate = 48000; - this.channels = 2; - this.frameDuration = 20; - this.frameSize = this.samplingRate * this.frameDuration / 1000; - this.pcmSize = this.frameSize * this.channels * 2; - this.bitrate = 64000; this.shared = !!options.shared; this.shard = options.shard || {}; this.opusOnly = !!options.opusOnly; @@ -111,19 +130,6 @@ class VoiceConnection extends EventEmitter { this.opus = {}; } - this.channelID = null; - this.paused = true; - this.speaking = false; - this.sequence = 0; - this.timestamp = 0; - this.ssrcUserMap = {}; - this.connectionTimeout = null; - this.connecting = false; - this.reconnecting = false; - this.ready = false; - - this.sendBuffer = Buffer.allocUnsafe(16 + 32 + MAX_FRAME_SIZE); - this.sendNonce = Buffer.alloc(24); this.sendNonce[0] = 0x80; this.sendNonce[1] = 0x78; @@ -144,7 +150,7 @@ class VoiceConnection extends EventEmitter { } } - this._send = this._send.bind(this); + this.#send = this.#sendUnbound.bind(this); } get volume() { @@ -326,9 +332,7 @@ class VoiceConnection extends EventEmitter { case VoiceOPCodes.CLIENT_DISCONNECT: { if(this.opus) { // opusscript requires manual cleanup - if(this.opus[packet.d.user_id] && this.opus[packet.d.user_id].delete) { - this.opus[packet.d.user_id].delete(); - } + this.opus[packet.d.user_id]?.delete?.(); delete this.opus[packet.d.user_id]; } @@ -533,7 +537,7 @@ class VoiceConnection extends EventEmitter { */ this.emit("start"); - this._send(); + this.#send(); } /** @@ -608,9 +612,7 @@ class VoiceConnection extends EventEmitter { } if(this.receiveStreamPCM) { const userID = this.ssrcUserMap[nonce.readUIntBE(8, 4)]; - if(!this.opus[userID]) { - this.opus[userID] = createOpus(this.samplingRate, this.channels, this.bitrate); - } + this.opus[userID] ??= createOpus(this.samplingRate, this.channels, this.bitrate); data = this.opus[userID].decode(data, this.frameSize); if(!data) { @@ -632,7 +634,7 @@ class VoiceConnection extends EventEmitter { this.current.pausedTime += Date.now() - this.current.pausedTimestamp; this.current.pausedTimestamp = 0; } - this._send(); + this.#send(); } else { this.setSpeaking(0); } @@ -647,7 +649,7 @@ class VoiceConnection extends EventEmitter { this.timestamp = (this.timestamp + frameSize) >>> 0; this.sequence = (this.sequence + 1) & 0xFFFF; - return this._sendAudioFrame(frame); + return this.#sendAudioFrame(frame); } /** @@ -665,7 +667,7 @@ class VoiceConnection extends EventEmitter { } sendWS(op, data) { - if(this.ws && this.ws.readyState === WebSocket.OPEN) { + if(this.ws?.readyState === WebSocket.OPEN) { data = JSON.stringify({op: op, d: data}); this.ws.send(data); this.emit("debug", data); @@ -697,7 +699,7 @@ class VoiceConnection extends EventEmitter { return; } this.ended = true; - if(this.current && this.current.timeout) { + if(this.current?.timeout) { clearTimeout(this.current.timeout); this.current.timeout = null; } @@ -747,20 +749,18 @@ class VoiceConnection extends EventEmitter { * @arg {Boolean} selfDeaf Whether the bot deafened itself or not (audio sending is unaffected) */ updateVoiceState(selfMute, selfDeaf) { - if(this.shard.sendWS) { - this.shard.sendWS(GatewayOPCodes.VOICE_STATE_UPDATE, { - guild_id: this.id, - channel_id: this.channelID || null, - self_mute: !!selfMute, - self_deaf: !!selfDeaf - }); - } + this.shard.sendWS?.(GatewayOPCodes.VOICE_STATE_UPDATE, { + guild_id: this.id, + channel_id: this.channelID || null, + self_mute: !!selfMute, + self_deaf: !!selfDeaf + }); } _destroy() { if(this.opus) { for(const key in this.opus) { - this.opus[key].delete && this.opus[key].delete(); + this.opus[key].delete?.(); delete this.opus[key]; } } @@ -775,7 +775,7 @@ class VoiceConnection extends EventEmitter { } } - _send() { + #sendUnbound() { if(!this.piper.encoding && this.piper.dataPacketCount === 0) { return this.stopPlaying(); } @@ -794,7 +794,7 @@ class VoiceConnection extends EventEmitter { } this.current.pausedTime += 4 * this.current.options.frameDuration; this.timestamp = (this.timestamp + 3 * this.current.options.frameSize) >>> 0; - this.current.timeout = setTimeout(this._send, 4 * this.current.options.frameDuration); + this.current.timeout = setTimeout(this.#send, 4 * this.current.options.frameDuration); return; } else { return this.stopPlaying(); @@ -802,10 +802,10 @@ class VoiceConnection extends EventEmitter { this.sendAudioFrame(this.current.buffer, this.current.options.frameSize); this.current.playTime += this.current.options.frameDuration; - this.current.timeout = setTimeout(this._send, this.current.startTime + this.current.pausedTime + this.current.playTime - Date.now()); + this.current.timeout = setTimeout(this.#send, this.current.startTime + this.current.pausedTime + this.current.playTime - Date.now()); } - _sendAudioFrame(frame) { + #sendAudioFrame(frame) { this.sendNonce.writeUInt16BE(this.sequence, 2); this.sendNonce.writeUInt32BE(this.timestamp, 4); diff --git a/lib/voice/VoiceConnectionManager.js b/lib/voice/VoiceConnectionManager.js index 49143026..90555489 100644 --- a/lib/voice/VoiceConnectionManager.js +++ b/lib/voice/VoiceConnectionManager.js @@ -4,14 +4,14 @@ const Base = require("../structures/Base"); const Collection = require("../util/Collection"); class VoiceConnectionManager extends Collection { + pendingGuilds = {}; constructor(vcObject) { super(vcObject || require("./VoiceConnection")); - this.pendingGuilds = {}; } join(guildID, channelID, options) { const connection = this.get(guildID); - if(connection && connection.ws) { + if(connection?.ws) { connection.switchChannel(channelID); if(connection.ready) { return Promise.resolve(connection); @@ -63,14 +63,11 @@ class VoiceConnectionManager extends Collection { switch(guildID, channelID) { const connection = this.get(guildID); - if(!connection) { - return; - } - connection.switch(channelID); + connection?.switch(channelID); } voiceServerUpdate(data) { - if(this.pendingGuilds[data.guild_id] && this.pendingGuilds[data.guild_id].timeout) { + if(this.pendingGuilds[data.guild_id]?.timeout) { clearTimeout(this.pendingGuilds[data.guild_id].timeout); this.pendingGuilds[data.guild_id].timeout = null; } diff --git a/lib/voice/VoiceDataStream.js b/lib/voice/VoiceDataStream.js index e7743d6d..18597f51 100644 --- a/lib/voice/VoiceDataStream.js +++ b/lib/voice/VoiceDataStream.js @@ -3,8 +3,8 @@ let EventEmitter; try { EventEmitter = require("eventemitter3"); -} catch(err) { - EventEmitter = require("events").EventEmitter; +} catch{ + EventEmitter = require("node:events").EventEmitter; } /** diff --git a/lib/voice/streams/BaseTransformer.js b/lib/voice/streams/BaseTransformer.js index 7160c6ce..a1b342f1 100644 --- a/lib/voice/streams/BaseTransformer.js +++ b/lib/voice/streams/BaseTransformer.js @@ -1,34 +1,31 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../../structures/Base"); -const TransformStream = require("stream").Transform; +const TransformStream = require("node:stream").Transform; class BaseTransformer extends TransformStream { + #transformCB; + manualCB = false; constructor(options = {}) { - if(options.allowHalfOpen === undefined) { - options.allowHalfOpen = true; - } - if(options.highWaterMark === undefined) { - options.highWaterMark = 0; - } + options.allowHalfOpen ??= true; + options.highWaterMark ??= 0; super(options); - this.manualCB = false; } setTransformCB(cb) { if(this.manualCB) { this.transformCB(); - this._transformCB = cb; + this.#transformCB = cb; } else { cb(); } } transformCB() { - if(this._transformCB) { - this._transformCB(); - this._transformCB = null; + if(this.#transformCB) { + this.#transformCB(); + this.#transformCB = null; } } diff --git a/lib/voice/streams/DCAOpusTransformer.js b/lib/voice/streams/DCAOpusTransformer.js index 6c44afb9..29ec084a 100644 --- a/lib/voice/streams/DCAOpusTransformer.js +++ b/lib/voice/streams/DCAOpusTransformer.js @@ -3,11 +3,7 @@ const BaseTransformer = require("./BaseTransformer"); class DCAOpusTransformer extends BaseTransformer { - constructor(options = {}) { - super(options); - - this._remainder = null; - } + #remainder = null; process(buffer) { if(buffer.length - buffer._index < 2) { @@ -26,14 +22,14 @@ class DCAOpusTransformer extends BaseTransformer { } _transform(chunk, enc, cb) { - if(this._remainder) { - chunk = Buffer.concat([this._remainder, chunk]); - this._remainder = null; + if(this.#remainder) { + chunk = Buffer.concat([this.#remainder, chunk]); + this.#remainder = null; } if(!this.head) { if(chunk.length < 4) { - this._remainder = chunk; + this.#remainder = chunk; return cb(); } else { const dcaVersion = chunk.subarray(0, 4); @@ -41,12 +37,12 @@ class DCAOpusTransformer extends BaseTransformer { this.head = true; // Attempt to play as if it were a DCA0 file } else if(dcaVersion[3] === 49) { // DCA1 if(chunk.length < 8) { - this._remainder = chunk; + this.#remainder = chunk; return cb(); } const jsonLength = chunk.subarray(4, 8).readInt32LE(0); if(chunk.length < 8 + jsonLength) { - this._remainder = chunk; + this.#remainder = chunk; return cb(); } const jsonMetadata = chunk.subarray(8, 8 + jsonLength); @@ -65,7 +61,7 @@ class DCAOpusTransformer extends BaseTransformer { const offset = chunk._index; const ret = this.process(chunk); if(ret) { - this._remainder = chunk.subarray(offset); + this.#remainder = chunk.subarray(offset); cb(); return; } diff --git a/lib/voice/streams/FFmpegDuplex.js b/lib/voice/streams/FFmpegDuplex.js index 91a491a3..42d6f5d6 100644 --- a/lib/voice/streams/FFmpegDuplex.js +++ b/lib/voice/streams/FFmpegDuplex.js @@ -1,9 +1,9 @@ "use strict"; -const util = require("util"); +const util = require("node:util"); const Base = require("../../structures/Base"); -const ChildProcess = require("child_process"); -const DuplexStream = require("stream").Duplex; -const PassThroughStream = require("stream").PassThrough; +const ChildProcess = require("node:child_process"); +const DuplexStream = require("node:stream").Duplex; +const PassThroughStream = require("node:stream").PassThrough; const delegateEvents = { readable: "_reader", @@ -14,20 +14,23 @@ const delegateEvents = { }; class FFmpegDuplex extends DuplexStream { + #onError; + #process; + #stderr; + #stdin; + #stdout; constructor(command, options = {}) { - if(options.highWaterMark === undefined) { - options.highWaterMark = 0; - } + options.highWaterMark ??= 0; super(options); this.command = command; this._reader = new PassThroughStream(options); this._writer = new PassThroughStream(options); - this._onError = this.emit.bind(this, "error"); + this.#onError = this.emit.bind(this, "error"); - this._reader.on("error", this._onError); - this._writer.on("error", this._onError); + this._reader.on("error", this.#onError); + this._writer.on("error", this.#onError); this._readableState = this._reader._readableState; this._writableState = this._writer._writableState; @@ -88,10 +91,10 @@ class FFmpegDuplex extends DuplexStream { }; const cleanup = () => { - this._process = - this._stderr = - this._stdout = - this._stdin = + this.#process = + this.#stderr = + this.#stdout = + this.#stdin = stderr = ex = killed = null; @@ -117,7 +120,7 @@ class FFmpegDuplex extends DuplexStream { } else { // Everything else ex = new Error("Command failed: " + Buffer.concat(stderr).toString("utf8")); - ex.killed = this._process.killed || killed; + ex.killed = this.#process.killed || killed; ex.code = code; ex.signal = signal; this.emit("error", ex); @@ -129,8 +132,8 @@ class FFmpegDuplex extends DuplexStream { const onError = (err) => { ex = err; - this._stdout.destroy(); - this._stderr.destroy(); + this.#stdout.destroy(); + this.#stderr.destroy(); onExit(); }; @@ -138,41 +141,41 @@ class FFmpegDuplex extends DuplexStream { if(killed) { return; } - this._stdout.destroy(); - this._stderr.destroy(); + this.#stdout.destroy(); + this.#stderr.destroy(); killed = true; try { - this._process.kill(options.killSignal || "SIGTERM"); - setTimeout(() => this._process && this._process.kill("SIGKILL"), 2000); + this.#process.kill(options.killSignal || "SIGTERM"); + setTimeout(() => this.#process && this.#process.kill("SIGKILL"), 2000); } catch(e) { ex = e; onExit(); } }; - this._process = ChildProcess.spawn(this.command, args, options); - this._stdin = this._process.stdin; - this._stdout = this._process.stdout; - this._stderr = this._process.stderr; - this._writer.pipe(this._stdin); - this._stdout.pipe(this._reader, { + this.#process = ChildProcess.spawn(this.command, args, options); + this.#stdin = this.#process.stdin; + this.#stdout = this.#process.stdout; + this.#stderr = this.#process.stderr; + this._writer.pipe(this.#stdin); + this.#stdout.pipe(this._reader, { end: false }); this.kill = this.destroy = kill; - this._stderr.on("data", onStderrData); + this.#stderr.on("data", onStderrData); // In some cases ECONNRESET can be emitted by stdin because the process is not interested in any // more data but the _writer is still piping. Forget about errors emitted on stdin and stdout - this._stdin.on("error", this.noop); - this._stdout.on("error", this.noop); + this.#stdin.on("error", this.noop); + this.#stdout.on("error", this.noop); - this._stdout.on("end", onStdoutEnd); + this.#stdout.on("end", onStdoutEnd); - this._process.once("close", onExit); - this._process.once("error", onError); + this.#process.once("close", onExit); + this.#process.once("error", onError); return this; } diff --git a/lib/voice/streams/FFmpegOggTransformer.js b/lib/voice/streams/FFmpegOggTransformer.js index e42a8130..cb3f2c51 100644 --- a/lib/voice/streams/FFmpegOggTransformer.js +++ b/lib/voice/streams/FFmpegOggTransformer.js @@ -6,9 +6,7 @@ module.exports = function(options = {}) { if(!options.command) { throw new Error("Invalid converter command"); } - if(options.frameDuration === undefined) { - options.frameDuration = 60; - } + options.frameDuration ??= 60; let inputArgs = [ "-analyzeduration", "0", "-loglevel", "24" diff --git a/lib/voice/streams/FFmpegPCMTransformer.js b/lib/voice/streams/FFmpegPCMTransformer.js index 3ee0362a..4b887202 100644 --- a/lib/voice/streams/FFmpegPCMTransformer.js +++ b/lib/voice/streams/FFmpegPCMTransformer.js @@ -6,9 +6,7 @@ module.exports = function(options = {}) { if(!options.command) { throw new Error("Invalid converter command"); } - if(options.samplingRate === undefined) { - options.samplingRate = 48000; - } + options.samplingRate ??= 48_000; const inputArgs = [ "-analyzeduration", "0", "-loglevel", "24" diff --git a/lib/voice/streams/OggOpusTransformer.js b/lib/voice/streams/OggOpusTransformer.js index 6c4baed3..e4c7eb0f 100644 --- a/lib/voice/streams/OggOpusTransformer.js +++ b/lib/voice/streams/OggOpusTransformer.js @@ -3,12 +3,8 @@ const BaseTransformer = require("./BaseTransformer"); class OggOpusTransformer extends BaseTransformer { - constructor(options = {}) { - super(options); - - this._remainder = null; - this._bitstream = null; - } + #bitstream = null; + #remainder = null; process(buffer) { if(buffer.length - buffer._index <= 26) { @@ -61,11 +57,11 @@ class OggOpusTransformer extends BaseTransformer { if(this.head) { if(byte === "OpusTags") { this.emit("debug", segment.toString()); - } else if(bitstream === this._bitstream) { + } else if(bitstream === this.#bitstream) { this.push(segment); } } else if(byte === "OpusHead") { - this._bitstream = bitstream; + this.#bitstream = bitstream; this.emit("debug", (this.head = segment.toString())); } else { this.emit("debug", "Invalid codec: " + byte); @@ -74,15 +70,15 @@ class OggOpusTransformer extends BaseTransformer { } _final() { - if(!this._bitstream) { + if(!this.#bitstream) { this.emit("error", new Error("No Opus stream was found")); } } _transform(chunk, enc, cb) { - if(this._remainder) { - chunk = Buffer.concat([this._remainder, chunk]); - this._remainder = null; + if(this.#remainder) { + chunk = Buffer.concat([this.#remainder, chunk]); + this.#remainder = null; } chunk._index = 0; @@ -91,7 +87,7 @@ class OggOpusTransformer extends BaseTransformer { const offset = chunk._index; const ret = this.process(chunk); if(ret) { - this._remainder = chunk.subarray(offset); + this.#remainder = chunk.subarray(offset); if(ret instanceof Error) { this.emit("error", ret); } diff --git a/lib/voice/streams/PCMOpusTransformer.js b/lib/voice/streams/PCMOpusTransformer.js index ba7cd38e..4a1512d5 100644 --- a/lib/voice/streams/PCMOpusTransformer.js +++ b/lib/voice/streams/PCMOpusTransformer.js @@ -3,43 +3,40 @@ const BaseTransformer = require("./BaseTransformer"); class PCMOpusTransformer extends BaseTransformer { + #remainder = null; constructor(options = {}) { super(options); this.opus = options.opusFactory(); this.frameSize = options.frameSize || 2880; this.pcmSize = options.pcmSize || 11520; - - this._remainder = null; } _destroy(...args) { - if(this.opus.delete) { - this.opus.delete(); - } + this.opus.delete?.(); return super._destroy(...args); } _flush(cb) { - if(this._remainder) { + if(this.#remainder) { const buf = Buffer.allocUnsafe(this.pcmSize); - this._remainder.copy(buf); - buf.fill(0, this._remainder.length); + this.#remainder.copy(buf); + buf.fill(0, this.#remainder.length); this.push(this.opus.encode(buf, this.frameSize)); - this._remainder = null; + this.#remainder = null; } cb(); } _transform(chunk, enc, cb) { - if(this._remainder) { - chunk = Buffer.concat([this._remainder, chunk]); - this._remainder = null; + if(this.#remainder) { + chunk = Buffer.concat([this.#remainder, chunk]); + this.#remainder = null; } if(chunk.length < this.pcmSize) { - this._remainder = chunk; + this.#remainder = chunk; return cb(); } @@ -51,7 +48,7 @@ class PCMOpusTransformer extends BaseTransformer { } if(chunk._index < chunk.length) { - this._remainder = chunk.subarray(chunk._index); + this.#remainder = chunk.subarray(chunk._index); } this.setTransformCB(cb); diff --git a/lib/voice/streams/VolumeTransformer.js b/lib/voice/streams/VolumeTransformer.js index f5a1a7f1..7245987a 100644 --- a/lib/voice/streams/VolumeTransformer.js +++ b/lib/voice/streams/VolumeTransformer.js @@ -3,10 +3,10 @@ const BaseTransformer = require("./BaseTransformer"); class VolumeTransformer extends BaseTransformer { + #remainder = null; constructor(options = {}) { super(options); - this._remainder = null; this.setVolume(1.0); } @@ -19,9 +19,9 @@ class VolumeTransformer extends BaseTransformer { } _transform(chunk, enc, cb) { - if(this._remainder) { - chunk = Buffer.concat([this._remainder, chunk]); - this._remainder = null; + if(this.#remainder) { + chunk = Buffer.concat([this.#remainder, chunk]); + this.#remainder = null; } if(chunk.length < 2) { @@ -30,7 +30,7 @@ class VolumeTransformer extends BaseTransformer { let buf; if(chunk.length & 1) { - this._remainder = chunk.subarray(chunk.length - 1); + this.#remainder = chunk.subarray(chunk.length - 1); buf = Buffer.allocUnsafe(chunk.length - 1); } else { buf = Buffer.allocUnsafe(chunk.length); diff --git a/lib/voice/streams/WebmOpusTransformer.js b/lib/voice/streams/WebmOpusTransformer.js index 9af7eb3c..a5812f26 100644 --- a/lib/voice/streams/WebmOpusTransformer.js +++ b/lib/voice/streams/WebmOpusTransformer.js @@ -16,13 +16,10 @@ const TAG_TYPE_TAG = 2; const TRACKTYPE_AUDIO = 2; // EBML spec: https://www.matroska.org/technical/specs/index.html#TrackType class WebmOpusTransformer extends BaseTransformer { - constructor(options = {}) { - super(options); - - this._tag_stack = []; - this._state = STATE_TAG; - this._total = 0; - } + #remainder; + #state = STATE_TAG; + #tag_stack = []; + #total = 0; getVIntLength(buffer, index) { let length = 1; @@ -97,11 +94,11 @@ class WebmOpusTransformer extends BaseTransformer { } readContent(buffer) { - const tagObj = this._tag_stack[this._tag_stack.length - 1]; + const tagObj = this.#tag_stack[this.#tag_stack.length - 1]; if(tagObj.type === "m") { this.process(TAG_TYPE_START, tagObj); - this._state = STATE_TAG; + this.#state = STATE_TAG; return true; } @@ -111,18 +108,18 @@ class WebmOpusTransformer extends BaseTransformer { tagObj.data = buffer.subarray(buffer._index, buffer._index + tagObj.size); buffer._index += tagObj.size; - this._total += tagObj.size; - this._state = STATE_TAG; + this.#total += tagObj.size; + this.#state = STATE_TAG; - this._tag_stack.pop(); + this.#tag_stack.pop(); this.process(TAG_TYPE_TAG, tagObj); - while(this._tag_stack.length > 0) { - if(this._total < this._tag_stack[this._tag_stack.length - 1].end) { + while(this.#tag_stack.length > 0) { + if(this.#total < this.#tag_stack[this.#tag_stack.length - 1].end) { break; } - this.process(TAG_TYPE_END, this._tag_stack.pop()); + this.process(TAG_TYPE_END, this.#tag_stack.pop()); } return true; @@ -144,7 +141,7 @@ class WebmOpusTransformer extends BaseTransformer { const tagObj = { type: "unknown", name: "unknown", - end: this._total + tagSize + end: this.#total + tagSize }; if(schema[tagStr]) { tagObj.type = schema[tagStr].type; @@ -167,33 +164,33 @@ class WebmOpusTransformer extends BaseTransformer { tagObj.size = value; buffer._index += size; - this._total += tagSize + size; - this._state = STATE_CONTENT; + this.#total += tagSize + size; + this.#state = STATE_CONTENT; - this._tag_stack.push(tagObj); + this.#tag_stack.push(tagObj); return true; } _transform(chunk, enc, cb) { - if(this._remainder) { - chunk = Buffer.concat([this._remainder, chunk]); - this._remainder = null; + if(this.#remainder) { + chunk = Buffer.concat([this.#remainder, chunk]); + this.#remainder = null; } chunk._index = 0; while(chunk._index < chunk.length) { - if(this._state === STATE_TAG && !this.readTag(chunk)) { + if(this.#state === STATE_TAG && !this.readTag(chunk)) { break; } - if(this._state === STATE_CONTENT && !this.readContent(chunk)) { + if(this.#state === STATE_CONTENT && !this.readContent(chunk)) { break; } } if(chunk._index < chunk.length) { - this._remainder = chunk.subarray(chunk._index); + this.#remainder = chunk.subarray(chunk._index); } this.setTransformCB(cb); diff --git a/package.json b/package.json index 8f2e4f4c..1d180f82 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "typings": "./index.d.ts", "engines": { - "node": ">=10.4.0" + "node": ">=18.0.0" }, "scripts": { "lint:js": "eslint -c .eslintrc.yml lib examples *.js",