refactor: new node features (#5132)

Co-authored-by: Antonio Román <kyradiscord@gmail.com>
This commit is contained in:
Sugden
2021-06-30 21:40:33 +01:00
committed by GitHub
parent f108746c15
commit 1e8f01253e
68 changed files with 305 additions and 360 deletions

View File

@@ -34,14 +34,7 @@ class Client extends BaseClient {
constructor(options) { constructor(options) {
super(Object.assign({ _tokenType: 'Bot' }, options)); super(Object.assign({ _tokenType: 'Bot' }, options));
// Obtain shard details from environment or if present, worker threads const data = require('worker_threads').workerData ?? process.env;
let data = process.env;
try {
// Test if worker threads module is present and used
data = require('worker_threads').workerData || data;
} catch {
// Do nothing
}
if (this.options.shards === DefaultOptions.shards) { if (this.options.shards === DefaultOptions.shards) {
if ('SHARDS' in data) { if ('SHARDS' in data) {
@@ -188,7 +181,7 @@ class Client extends BaseClient {
* @readonly * @readonly
*/ */
get readyTimestamp() { get readyTimestamp() {
return this.readyAt ? this.readyAt.getTime() : null; return this.readyAt?.getTime() ?? null;
} }
/** /**
@@ -341,7 +334,7 @@ class Client extends BaseClient {
channels++; channels++;
messages += channel.messages.cache.sweep( messages += channel.messages.cache.sweep(
message => now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs, message => now - (message.editedTimestamp ?? message.createdTimestamp) > lifetimeMs,
); );
} }

View File

@@ -32,14 +32,14 @@ class GenericAction {
} }
getChannel(data) { getChannel(data) {
const id = data.channel_id || data.id; const id = data.channel_id ?? data.id;
return ( return (
data.channel || data.channel ??
this.getPayload( this.getPayload(
{ {
id, id,
guild_id: data.guild_id, guild_id: data.guild_id,
recipients: [data.author || { id: data.user_id }], recipients: [data.author ?? { id: data.user_id }],
}, },
this.client.channels, this.client.channels,
id, id,
@@ -49,14 +49,14 @@ class GenericAction {
} }
getMessage(data, channel, cache) { getMessage(data, channel, cache) {
const id = data.message_id || data.id; const id = data.message_id ?? data.id;
return ( return (
data.message || data.message ??
this.getPayload( this.getPayload(
{ {
id, id,
channel_id: channel.id, channel_id: channel.id,
guild_id: data.guild_id || (channel.guild ? channel.guild.id : null), guild_id: data.guild_id ?? channel.guild?.id,
}, },
channel.messages, channel.messages,
id, id,
@@ -67,12 +67,12 @@ class GenericAction {
} }
getReaction(data, message, user) { getReaction(data, message, user) {
const id = data.emoji.id || decodeURIComponent(data.emoji.name); const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
return this.getPayload( return this.getPayload(
{ {
emoji: data.emoji, emoji: data.emoji,
count: message.partial ? null : 0, count: message.partial ? null : 0,
me: user ? user.id === this.client.user.id : false, me: user?.id === this.client.user.id,
}, },
message.reactions, message.reactions,
id, id,
@@ -86,11 +86,11 @@ class GenericAction {
getUser(data) { getUser(data) {
const id = data.user_id; const id = data.user_id;
return data.user || this.getPayload({ id }, this.client.users, id, PartialTypes.USER); return data.user ?? this.getPayload({ id }, this.client.users, id, PartialTypes.USER);
} }
getUserFromMember(data) { getUserFromMember(data) {
if (data.guild_id && data.member && data.member.user) { if (data.guild_id && data.member?.user) {
const guild = this.client.guilds.cache.get(data.guild_id); const guild = this.client.guilds.cache.get(data.guild_id);
if (guild) { if (guild) {
return guild.members.add(data.member).user; return guild.members.add(data.member).user;

View File

@@ -12,7 +12,7 @@ class ChannelDeleteAction extends Action {
handle(data) { handle(data) {
const client = this.client; const client = this.client;
let channel = client.channels.cache.get(data.id); const channel = client.channels.cache.get(data.id);
if (channel) { if (channel) {
client.channels.remove(channel.id); client.channels.remove(channel.id);

View File

@@ -53,7 +53,7 @@ class GuildDeleteAction extends Action {
this.deleted.set(guild.id, guild); this.deleted.set(guild.id, guild);
this.scheduleForDeletion(guild.id); this.scheduleForDeletion(guild.id);
} else { } else {
guild = this.deleted.get(data.id) || null; guild = this.deleted.get(data.id) ?? null;
} }
return { guild }; return { guild };

View File

@@ -5,7 +5,7 @@ const Action = require('./Action');
class GuildEmojisUpdateAction extends Action { class GuildEmojisUpdateAction extends Action {
handle(data) { handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id); const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild || !guild.emojis) return; if (!guild?.emojis) return;
const deletions = new Map(guild.emojis.cache); const deletions = new Map(guild.emojis.cache);

View File

@@ -12,7 +12,7 @@ class MessageCreateAction extends Action {
if (existing) return { message: existing }; if (existing) return { message: existing };
const message = channel.messages.add(data); const message = channel.messages.add(data);
const user = message.author; const user = message.author;
let member = message.member; const member = message.member;
channel.lastMessageID = data.id; channel.lastMessageID = data.id;
if (user) { if (user) {
user.lastMessageID = data.id; user.lastMessageID = data.id;

View File

@@ -31,8 +31,8 @@ class MessageReactionAdd extends Action {
// Verify reaction // Verify reaction
if (message.partial && !this.client.options.partials.includes(PartialTypes.REACTION)) return false; if (message.partial && !this.client.options.partials.includes(PartialTypes.REACTION)) return false;
const existing = message.reactions.cache.get(data.emoji.id || data.emoji.name); const existing = message.reactions.cache.get(data.emoji.id ?? data.emoji.name);
if (existing && existing.users.cache.has(user.id)) return { message, reaction: existing, user }; if (existing?.users.cache.has(user.id)) return { message, reaction: existing, user };
const reaction = message.reactions.add({ const reaction = message.reactions.add({
emoji: data.emoji, emoji: data.emoji,
count: message.partial ? null : 0, count: message.partial ? null : 0,

View File

@@ -13,7 +13,7 @@ class MessageReactionRemoveEmoji extends Action {
const reaction = this.getReaction(data, message); const reaction = this.getReaction(data, message);
if (!reaction) return false; if (!reaction) return false;
if (!message.partial) message.reactions.cache.delete(reaction.emoji.id || reaction.emoji.name); if (!message.partial) message.reactions.cache.delete(reaction.emoji.id ?? reaction.emoji.name);
/** /**
* Emitted when a bot removes an emoji reaction from a cached message. * Emitted when a bot removes an emoji reaction from a cached message.

View File

@@ -6,18 +6,17 @@ const { Events } = require('../../util/Constants');
class PresenceUpdateAction extends Action { class PresenceUpdateAction extends Action {
handle(data) { handle(data) {
let user = this.client.users.cache.get(data.user.id); let user = this.client.users.cache.get(data.user.id);
if (!user && data.user.username) user = this.client.users.add(data.user); if (!user && data.user?.username) user = this.client.users.add(data.user);
if (!user) return; if (!user) return;
if (data.user && data.user.username) { if (data.user?.username) {
if (!user.equals(data.user)) this.client.actions.UserUpdate.handle(data.user); if (!user.equals(data.user)) this.client.actions.UserUpdate.handle(data.user);
} }
const guild = this.client.guilds.cache.get(data.guild_id); const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild) return; if (!guild) return;
let oldPresence = guild.presences.cache.get(user.id); const oldPresence = guild.presences.cache.get(user.id)?._clone();
if (oldPresence) oldPresence = oldPresence._clone();
let member = guild.members.cache.get(user.id); let member = guild.members.cache.get(user.id);
if (!member && data.status !== 'offline') { if (!member && data.status !== 'offline') {
member = guild.members.add({ member = guild.members.add({
@@ -28,7 +27,7 @@ class PresenceUpdateAction extends Action {
this.client.emit(Events.GUILD_MEMBER_AVAILABLE, member); this.client.emit(Events.GUILD_MEMBER_AVAILABLE, member);
} }
guild.presences.add(Object.assign(data, { guild })); guild.presences.add(Object.assign(data, { guild }));
if (member && this.client.listenerCount(Events.PRESENCE_UPDATE) && !member.presence.equals(oldPresence)) { if (this.client.listenerCount(Events.PRESENCE_UPDATE) && member && !member.presence.equals(oldPresence)) {
/** /**
* Emitted whenever a guild member's presence (e.g. status, activity) is changed. * Emitted whenever a guild member's presence (e.g. status, activity) is changed.
* @event Client#presenceUpdate * @event Client#presenceUpdate

View File

@@ -11,9 +11,8 @@ class VoiceStateUpdate extends Action {
if (guild) { if (guild) {
const VoiceState = Structures.get('VoiceState'); const VoiceState = Structures.get('VoiceState');
// Update the state // Update the state
const oldState = guild.voiceStates.cache.has(data.user_id) const oldState =
? guild.voiceStates.cache.get(data.user_id)._clone() guild.voiceStates.cache.get(data.user_id)?._clone() ?? new VoiceState(guild, { user_id: data.user_id });
: new VoiceState(guild, { user_id: data.user_id });
const newState = guild.voiceStates.add(data); const newState = guild.voiceStates.add(data);
@@ -21,12 +20,12 @@ class VoiceStateUpdate extends Action {
let member = guild.members.cache.get(data.user_id); let member = guild.members.cache.get(data.user_id);
if (member && data.member) { if (member && data.member) {
member._patch(data.member); member._patch(data.member);
} else if (data.member && data.member.user && data.member.joined_at) { } else if (data.member?.user && data.member.joined_at) {
member = guild.members.add(data.member); member = guild.members.add(data.member);
} }
// Emit event // Emit event
if (member && member.user.id === client.user.id) { if (member?.user.id === client.user.id) {
client.emit('debug', `[VOICE] received voice state update: ${JSON.stringify(data)}`); client.emit('debug', `[VOICE] received voice state update: ${JSON.stringify(data)}`);
client.voice.onVoiceStateUpdate(data); client.voice.onVoiceStateUpdate(data);
} }

View File

@@ -240,7 +240,7 @@ class WebSocketManager extends EventEmitter {
try { try {
await shard.connect(); await shard.connect();
} catch (error) { } catch (error) {
if (error && error.code && UNRECOVERABLE_CLOSE_CODES.includes(error.code)) { if (error?.code && UNRECOVERABLE_CLOSE_CODES.includes(error.code)) {
throw new Error(WSCodes[error.code]); throw new Error(WSCodes[error.code]);
// Undefined if session is invalid, error event for regular closes // Undefined if session is invalid, error event for regular closes
} else if (!error || error.code) { } else if (!error || error.code) {

View File

@@ -176,7 +176,7 @@ class WebSocketShard extends EventEmitter {
connect() { connect() {
const { gateway, client } = this.manager; const { gateway, client } = this.manager;
if (this.connection && this.connection.readyState === WebSocket.OPEN && this.status === Status.READY) { if (this.connection?.readyState === WebSocket.OPEN && this.status === Status.READY) {
return Promise.resolve(); return Promise.resolve();
} }
@@ -216,7 +216,7 @@ class WebSocketShard extends EventEmitter {
this.once(ShardEvents.INVALID_SESSION, onInvalidOrDestroyed); this.once(ShardEvents.INVALID_SESSION, onInvalidOrDestroyed);
this.once(ShardEvents.DESTROYED, onInvalidOrDestroyed); this.once(ShardEvents.DESTROYED, onInvalidOrDestroyed);
if (this.connection && this.connection.readyState === WebSocket.OPEN) { if (this.connection?.readyState === WebSocket.OPEN) {
this.debug('An open connection was found, attempting an immediate identify.'); this.debug('An open connection was found, attempting an immediate identify.');
this.identify(); this.identify();
return; return;
@@ -306,7 +306,7 @@ class WebSocketShard extends EventEmitter {
* @private * @private
*/ */
onError(event) { onError(event) {
const error = event && event.error ? event.error : event; const error = event?.error ?? event;
if (!error) return; if (!error) return;
/** /**
@@ -345,7 +345,7 @@ class WebSocketShard extends EventEmitter {
this.debug(`[CLOSE] this.debug(`[CLOSE]
Event Code: ${event.code} Event Code: ${event.code}
Clean : ${event.wasClean} Clean : ${event.wasClean}
Reason : ${event.reason || 'No reason received'}`); Reason : ${event.reason ?? 'No reason received'}`);
this.setHeartbeatTimer(-1); this.setHeartbeatTimer(-1);
this.setHelloTimeout(-1); this.setHelloTimeout(-1);
@@ -648,7 +648,7 @@ class WebSocketShard extends EventEmitter {
* @private * @private
*/ */
_send(data) { _send(data) {
if (!this.connection || this.connection.readyState !== WebSocket.OPEN) { if (this.connection?.readyState !== WebSocket.OPEN) {
this.debug(`Tried to send packet '${JSON.stringify(data)}' but no WebSocket is available!`); this.debug(`Tried to send packet '${JSON.stringify(data)}' but no WebSocket is available!`);
this.destroy({ closeCode: 4000 }); this.destroy({ closeCode: 4000 });
return; return;

View File

@@ -8,7 +8,7 @@ module.exports = (client, { d: data }) => {
if (channel && !Number.isNaN(time.getTime())) { if (channel && !Number.isNaN(time.getTime())) {
// Discord sends null for last_pin_timestamp if the last pinned message was removed // Discord sends null for last_pin_timestamp if the last pinned message was removed
channel.lastPinTimestamp = time.getTime() || null; channel.lastPinTimestamp = time.getTime() ?? null;
/** /**
* Emitted whenever the pins of a channel are updated. Due to the nature of the WebSocket event, * Emitted whenever the pins of a channel are updated. Due to the nature of the WebSocket event,

View File

@@ -39,7 +39,7 @@ function message(key, args) {
const msg = messages.get(key); const msg = messages.get(key);
if (!msg) throw new Error(`An invalid error message key was used: ${key}.`); if (!msg) throw new Error(`An invalid error message key was used: ${key}.`);
if (typeof msg === 'function') return msg(...args); if (typeof msg === 'function') return msg(...args);
if (args === undefined || args.length === 0) return msg; if (!args?.length) return msg;
args.unshift(msg); args.unshift(msg);
return String(...args); return String(...args);
} }

View File

@@ -41,7 +41,7 @@ class ApplicationCommandManager extends BaseManager {
*/ */
commandPath({ id, guildID } = {}) { commandPath({ id, guildID } = {}) {
let path = this.client.api.applications(this.client.application.id); let path = this.client.api.applications(this.client.application.id);
if (this.guild || guildID) path = path.guilds(this.guild?.id ?? guildID); if (this.guild ?? guildID) path = path.guilds(this.guild?.id ?? guildID);
return id ? path.commands(id) : path.commands; return id ? path.commands(id) : path.commands;
} }
@@ -84,9 +84,7 @@ class ApplicationCommandManager extends BaseManager {
async fetch(id, { guildID, cache = true, force = false } = {}) { async fetch(id, { guildID, cache = true, force = false } = {}) {
if (typeof id === 'object') { if (typeof id === 'object') {
({ guildID, cache = true, force = false } = id); ({ guildID, cache = true, force = false } = id);
id = undefined; } else if (id) {
}
if (id) {
if (!force) { if (!force) {
const existing = this.cache.get(id); const existing = this.cache.get(id);
if (existing) return existing; if (existing) return existing;

View File

@@ -67,7 +67,7 @@ class BaseGuildEmojiManager extends BaseManager {
if (emoji instanceof ReactionEmoji) return emoji.identifier; if (emoji instanceof ReactionEmoji) return emoji.identifier;
if (typeof emoji === 'string') { if (typeof emoji === 'string') {
const res = parseEmoji(emoji); const res = parseEmoji(emoji);
if (res && res.name.length) { if (res?.name.length) {
emoji = `${res.animated ? 'a:' : ''}${res.name}${res.id ? `:${res.id}` : ''}`; emoji = `${res.animated ? 'a:' : ''}${res.name}${res.id ? `:${res.id}` : ''}`;
} }
if (!emoji.includes('%')) return encodeURIComponent(emoji); if (!emoji.includes('%')) return encodeURIComponent(emoji);

View File

@@ -17,7 +17,7 @@ class BaseManager {
* @private * @private
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) || holds }); Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) ?? holds });
/** /**
* The client that instantiated this Manager * The client that instantiated this Manager
@@ -42,12 +42,12 @@ class BaseManager {
} }
add(data, cache = true, { id, extras = [] } = {}) { add(data, cache = true, { id, extras = [] } = {}) {
const existing = this.cache.get(id || data.id); const existing = this.cache.get(id ?? data.id);
if (existing && existing._patch && cache) existing._patch(data); if (cache) existing?._patch(data);
if (existing) return existing; if (existing) return existing;
const entry = this.holds ? new this.holds(this.client, data, ...extras) : data; const entry = this.holds ? new this.holds(this.client, data, ...extras) : data;
if (cache) this.cache.set(id || entry.id, entry); if (cache) this.cache.set(id ?? entry.id, entry);
return entry; return entry;
} }
@@ -58,7 +58,7 @@ class BaseManager {
*/ */
resolve(idOrInstance) { resolve(idOrInstance) {
if (idOrInstance instanceof this.holds) return idOrInstance; if (idOrInstance instanceof this.holds) return idOrInstance;
if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) || null; if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) ?? null;
return null; return null;
} }

View File

@@ -22,10 +22,10 @@ class ChannelManager extends BaseManager {
add(data, guild, cache = true) { add(data, guild, cache = true) {
const existing = this.cache.get(data.id); const existing = this.cache.get(data.id);
if (existing) { if (existing) {
if (existing._patch && cache) existing._patch(data); if (cache) existing._patch(data);
if (guild) guild.channels?.add(existing); guild?.channels?.add(existing);
if (ThreadChannelTypes.includes(existing.type) && typeof existing.parent?.threads !== 'undefined') { if (ThreadChannelTypes.includes(existing.type)) {
existing.parent.threads.add(existing); existing.parent?.threads?.add(existing);
} }
return existing; return existing;
} }

View File

@@ -115,9 +115,10 @@ class GuildChannelManager extends BaseManager {
* ], * ],
* }) * })
*/ */
async create(name, options = {}) { async create(
let { type, topic, nsfw, bitrate, userLimit, parent, permissionOverwrites, position, rateLimitPerUser, reason } = name,
options; { type, topic, nsfw, bitrate, userLimit, parent, permissionOverwrites, position, rateLimitPerUser, reason } = {},
) {
if (parent) parent = this.client.channels.resolveID(parent); if (parent) parent = this.client.channels.resolveID(parent);
if (permissionOverwrites) { if (permissionOverwrites) {
permissionOverwrites = permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); permissionOverwrites = permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));

View File

@@ -269,7 +269,7 @@ class GuildMemberManager extends BaseManager {
* @example * @example
* // Kick a user by ID (or with a user/guild member object) * // Kick a user by ID (or with a user/guild member object)
* guild.members.kick('84484653687267328') * guild.members.kick('84484653687267328')
* .then(user => console.log(`Kicked ${user.username || user.id || user} from ${guild.name}`)) * .then(user => console.log(`Kicked ${user.username ?? user.id ?? user} from ${guild.name}`))
* .catch(console.error); * .catch(console.error);
*/ */
async kick(user, reason) { async kick(user, reason) {
@@ -356,7 +356,7 @@ class GuildMemberManager extends BaseManager {
}, },
}); });
const fetchedMembers = new Collection(); const fetchedMembers = new Collection();
const option = query || limit || presences || user_ids; const option = Boolean(query || limit || presences || user_ids);
let i = 0; let i = 0;
const handler = (members, _, chunk) => { const handler = (members, _, chunk) => {
timeout.refresh(); timeout.refresh();

View File

@@ -68,7 +68,7 @@ class GuildMemberRoleManager {
* @readonly * @readonly
*/ */
get premiumSubscriberRole() { get premiumSubscriberRole() {
return this.cache.find(role => role.tags && role.tags.premiumSubscriberRole) || null; return this.cache.find(role => role.tags?.premiumSubscriberRole) ?? null;
} }
/** /**
@@ -79,7 +79,7 @@ class GuildMemberRoleManager {
*/ */
get botRole() { get botRole() {
if (!this.member.user.bot) return null; if (!this.member.user.bot) return null;
return this.cache.find(role => role.tags && role.tags.botID === this.member.user.id) || null; return this.cache.find(role => role.tags?.botID === this.member.user.id) ?? null;
} }
/** /**

View File

@@ -150,7 +150,7 @@ class MessageManager extends BaseManager {
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post(); const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post();
return this.cache.get(data.id) || this.add(data); return this.cache.get(data.id) ?? this.add(data);
} }
/** /**

View File

@@ -40,7 +40,7 @@ class PresenceManager extends BaseManager {
const presenceResolvable = super.resolve(presence); const presenceResolvable = super.resolve(presence);
if (presenceResolvable) return presenceResolvable; if (presenceResolvable) return presenceResolvable;
const UserResolvable = this.client.users.resolveID(presence); const UserResolvable = this.client.users.resolveID(presence);
return super.resolve(UserResolvable) || null; return super.resolve(UserResolvable);
} }
/** /**

View File

@@ -19,7 +19,7 @@ class ReactionManager extends BaseManager {
} }
add(data, cache) { add(data, cache) {
return super.add(data, cache, { id: data.emoji.id || data.emoji.name, extras: [this.message] }); return super.add(data, cache, { id: data.emoji.id ?? data.emoji.name, extras: [this.message] });
} }
/** /**

View File

@@ -7,9 +7,9 @@
class DiscordAPIError extends Error { class DiscordAPIError extends Error {
constructor(error, status, request) { constructor(error, status, request) {
super(); super();
const flattened = this.constructor.flattenErrors(error.errors || error).join('\n'); const flattened = this.constructor.flattenErrors(error.errors ?? error).join('\n');
this.name = 'DiscordAPIError'; this.name = 'DiscordAPIError';
this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message || flattened; this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message ?? flattened;
/** /**
* The HTTP method used for the request * The HTTP method used for the request
@@ -61,7 +61,7 @@ class DiscordAPIError extends Error {
if (v._errors) { if (v._errors) {
messages.push(`${newKey}: ${v._errors.map(e => e.message).join(' ')}`); messages.push(`${newKey}: ${v._errors.map(e => e.message).join(' ')}`);
} else if (v.code || v.message) { } else if (v.code ?? v.message) {
messages.push(`${v.code ? `${v.code}: ` : ''}${v.message}`.trim()); messages.push(`${v.code ? `${v.code}: ` : ''}${v.message}`.trim());
} else if (typeof v === 'string') { } else if (typeof v === 'string') {
messages.push(v); messages.push(v);

View File

@@ -18,7 +18,7 @@ class HTTPError extends Error {
* HTTP error code returned from the request * HTTP error code returned from the request
* @type {number} * @type {number}
*/ */
this.code = code || 500; this.code = code ?? 500;
/** /**
* The HTTP method used for the request * The HTTP method used for the request

View File

@@ -30,7 +30,7 @@ class RESTManager {
} }
getAuth() { getAuth() {
const token = this.client.token || this.client.accessToken; const token = this.client.token ?? this.client.accessToken;
if (token) return `${this.tokenPrefix} ${token}`; if (token) return `${this.tokenPrefix} ${token}`;
throw new Error('TOKEN_MISSING'); throw new Error('TOKEN_MISSING');
} }

View File

@@ -40,7 +40,7 @@ class Shard extends EventEmitter {
* Arguments for the shard's process (only when {@link ShardingManager#mode} is `process`) * Arguments for the shard's process (only when {@link ShardingManager#mode} is `process`)
* @type {string[]} * @type {string[]}
*/ */
this.args = manager.shardArgs || []; this.args = manager.shardArgs ?? [];
/** /**
* Arguments for the shard's process executable (only when {@link ShardingManager#mode} is `process`) * Arguments for the shard's process executable (only when {@link ShardingManager#mode} is `process`)
@@ -127,14 +127,16 @@ class Shard extends EventEmitter {
this._evals.clear(); this._evals.clear();
this._fetches.clear(); this._fetches.clear();
const child = this.process ?? this.worker;
/** /**
* Emitted upon the creation of the shard's child process/worker. * Emitted upon the creation of the shard's child process/worker.
* @event Shard#spawn * @event Shard#spawn
* @param {ChildProcess|Worker} process Child process/worker that was created * @param {ChildProcess|Worker} process Child process/worker that was created
*/ */
this.emit('spawn', this.process || this.worker); this.emit('spawn', child);
if (timeout === -1 || timeout === Infinity) return this.process || this.worker; if (timeout === -1 || timeout === Infinity) return child;
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const cleanup = () => { const cleanup = () => {
clearTimeout(spawnTimeoutTimer); clearTimeout(spawnTimeoutTimer);
@@ -168,7 +170,7 @@ class Shard extends EventEmitter {
this.once('disconnect', onDisconnect); this.once('disconnect', onDisconnect);
this.once('death', onDeath); this.once('death', onDeath);
}); });
return this.process || this.worker; return child;
} }
/** /**
@@ -242,10 +244,10 @@ class Shard extends EventEmitter {
if (this._fetches.has(prop)) return this._fetches.get(prop); if (this._fetches.has(prop)) return this._fetches.get(prop);
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
const child = this.process || this.worker; const child = this.process ?? this.worker;
const listener = message => { const listener = message => {
if (!message || message._fetchProp !== prop) return; if (message?._fetchProp !== prop) return;
child.removeListener('message', listener); child.removeListener('message', listener);
this._fetches.delete(prop); this._fetches.delete(prop);
resolve(message._result); resolve(message._result);
@@ -276,10 +278,10 @@ class Shard extends EventEmitter {
if (this._evals.has(script)) return this._evals.get(script); if (this._evals.has(script)) return this._evals.get(script);
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
const child = this.process || this.worker; const child = this.process ?? this.worker;
const listener = message => { const listener = message => {
if (!message || message._eval !== script) return; if (message?._eval !== script) return;
child.removeListener('message', listener); child.removeListener('message', listener);
this._evals.delete(script); this._evals.delete(script);
if (!message._error) resolve(message._result); if (!message._error) resolve(message._result);
@@ -388,7 +390,7 @@ class Shard extends EventEmitter {
* @event Shard#death * @event Shard#death
* @param {ChildProcess|Worker} process Child process/worker that exited * @param {ChildProcess|Worker} process Child process/worker that exited
*/ */
this.emit('death', this.process || this.worker); this.emit('death', this.process ?? this.worker);
this.ready = false; this.ready = false;
this.process = null; this.process = null;

View File

@@ -109,10 +109,10 @@ class ShardClientUtil {
*/ */
fetchClientValues(prop, shard) { fetchClientValues(prop, shard) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const parent = this.parentPort || process; const parent = this.parentPort ?? process;
const listener = message => { const listener = message => {
if (!message || message._sFetchProp !== prop || message._sFetchPropShard !== shard) return; if (message?._sFetchProp !== prop || message._sFetchPropShard !== shard) return;
parent.removeListener('message', listener); parent.removeListener('message', listener);
if (!message._error) resolve(message._result); if (!message._error) resolve(message._result);
else reject(Util.makeError(message._error)); else reject(Util.makeError(message._error));
@@ -139,7 +139,7 @@ class ShardClientUtil {
*/ */
broadcastEval(script, options = {}) { broadcastEval(script, options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const parent = this.parentPort || process; const parent = this.parentPort ?? process;
if (typeof script !== 'function') { if (typeof script !== 'function') {
reject(new TypeError('SHARDING_INVALID_EVAL_BROADCAST')); reject(new TypeError('SHARDING_INVALID_EVAL_BROADCAST'));
return; return;
@@ -147,7 +147,7 @@ class ShardClientUtil {
script = `(${script})(this, ${JSON.stringify(options.context)})`; script = `(${script})(this, ${JSON.stringify(options.context)})`;
const listener = message => { const listener = message => {
if (!message || message._sEval !== script || message._sEvalShard !== options.shard) return; if (message?._sEval !== script || message._sEvalShard !== options.shard) return;
parent.removeListener('message', listener); parent.removeListener('message', listener);
if (!message._error) resolve(message._result); if (!message._error) resolve(message._result);
else reject(Util.makeError(message._error)); else reject(Util.makeError(message._error));

View File

@@ -70,7 +70,7 @@ class ShardingManager extends EventEmitter {
* List of shards this sharding manager spawns * List of shards this sharding manager spawns
* @type {string|number[]} * @type {string|number[]}
*/ */
this.shardList = options.shardList || 'auto'; this.shardList = options.shardList ?? 'auto';
if (this.shardList !== 'auto') { if (this.shardList !== 'auto') {
if (!Array.isArray(this.shardList)) { if (!Array.isArray(this.shardList)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array.'); throw new TypeError('CLIENT_INVALID_OPTION', 'shardList', 'an array.');
@@ -132,7 +132,7 @@ class ShardingManager extends EventEmitter {
* Token to use for obtaining the automatic shard count, and passing to shards * Token to use for obtaining the automatic shard count, and passing to shards
* @type {?string} * @type {?string}
*/ */
this.token = options.token ? options.token.replace(/^Bot\s*/i, '') : null; this.token = options.token?.replace(/^Bot\s*/i, '') ?? null;
/** /**
* A collection of shards that this manager has spawned * A collection of shards that this manager has spawned

View File

@@ -29,7 +29,7 @@ class Channel extends Base {
* * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel * * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel
* @type {string} * @type {string}
*/ */
this.type = type ? type.toLowerCase() : 'unknown'; this.type = type?.toLowerCase() ?? 'unknown';
/** /**
* Whether the channel has been deleted * Whether the channel has been deleted
@@ -130,7 +130,8 @@ class Channel extends Base {
channel = new PartialGroupDMChannel(client, data); channel = new PartialGroupDMChannel(client, data);
} }
} else { } else {
guild = guild || client.guilds.cache.get(data.guild_id); if (!guild) guild = client.guilds.cache.get(data.guild_id);
if (guild) { if (guild) {
switch (data.type) { switch (data.type) {
case ChannelTypes.TEXT: { case ChannelTypes.TEXT: {

View File

@@ -10,7 +10,7 @@ class ClientPresence extends Presence {
* @param {APIPresence} [data={}] The data for the client presence * @param {APIPresence} [data={}] The data for the client presence
*/ */
constructor(client, data = {}) { constructor(client, data = {}) {
super(client, Object.assign(data, { status: data.status || 'online', user: { id: null } })); super(client, Object.assign(data, { status: data.status ?? 'online', user: { id: null } }));
} }
set(presence) { set(presence) {
@@ -33,7 +33,7 @@ class ClientPresence extends Presence {
activities: [], activities: [],
afk: typeof afk === 'boolean' ? afk : false, afk: typeof afk === 'boolean' ? afk : false,
since: typeof since === 'number' && !Number.isNaN(since) ? since : null, since: typeof since === 'number' && !Number.isNaN(since) ? since : null,
status: status || this.status, status: status ?? this.status,
}; };
if (activities?.length) { if (activities?.length) {
for (const [i, activity] of activities.entries()) { for (const [i, activity] of activities.entries()) {

View File

@@ -56,8 +56,7 @@ class ClientUser extends Structures.get('User') {
const newData = await this.client.api.users('@me').patch({ data }); const newData = await this.client.api.users('@me').patch({ data });
this.client.token = newData.token; this.client.token = newData.token;
const { updated } = this.client.actions.UserUpdate.handle(newData); const { updated } = this.client.actions.UserUpdate.handle(newData);
if (updated) return updated; return updated ?? this;
return this;
} }
/** /**

View File

@@ -110,17 +110,19 @@ class CommandInteraction extends Interaction {
if ('value' in option) result.value = option.value; if ('value' in option) result.value = option.value;
if ('options' in option) result.options = this._createOptionsCollection(option.options, resolved); if ('options' in option) result.options = this._createOptionsCollection(option.options, resolved);
const user = resolved?.users?.[option.value]; if (resolved) {
if (user) result.user = this.client.users.add(user); const user = resolved.users?.[option.value];
if (user) result.user = this.client.users.add(user);
const member = resolved?.members?.[option.value]; const member = resolved.members?.[option.value];
if (member) result.member = this.guild?.members.add({ user, ...member }) ?? member; if (member) result.member = this.guild?.members.add({ user, ...member }) ?? member;
const channel = resolved?.channels?.[option.value]; const channel = resolved.channels?.[option.value];
if (channel) result.channel = this.client.channels.add(channel, this.guild) ?? channel; if (channel) result.channel = this.client.channels.add(channel, this.guild) ?? channel;
const role = resolved?.roles?.[option.value]; const role = resolved.roles?.[option.value];
if (role) result.role = this.guild?.roles.add(role) ?? role; if (role) result.role = this.guild?.roles.add(role) ?? role;
}
return result; return result;
} }

View File

@@ -59,8 +59,7 @@ class Emoji extends Base {
* @readonly * @readonly
*/ */
get url() { get url() {
if (!this.id) return null; return this.id && this.client.rest.cdn.Emoji(this.id, this.animated ? 'gif' : 'png');
return this.client.rest.cdn.Emoji(this.id, this.animated ? 'gif' : 'png');
} }
/** /**
@@ -69,8 +68,7 @@ class Emoji extends Base {
* @readonly * @readonly
*/ */
get createdTimestamp() { get createdTimestamp() {
if (!this.id) return null; return this.id && SnowflakeUtil.deconstruct(this.id).timestamp;
return SnowflakeUtil.deconstruct(this.id).timestamp;
} }
/** /**
@@ -79,8 +77,7 @@ class Emoji extends Base {
* @readonly * @readonly
*/ */
get createdAt() { get createdAt() {
if (!this.id) return null; return this.id && new Date(this.createdTimestamp);
return new Date(this.createdTimestamp);
} }
/** /**

View File

@@ -147,13 +147,13 @@ class Guild extends AnonymousGuild {
* The full amount of members in this guild * The full amount of members in this guild
* @type {number} * @type {number}
*/ */
this.memberCount = data.member_count || this.memberCount; this.memberCount = data.member_count ?? this.memberCount;
/** /**
* Whether the guild is "large" (has more than large_threshold members, 50 by default) * Whether the guild is "large" (has more than large_threshold members, 50 by default)
* @type {boolean} * @type {boolean}
*/ */
this.large = Boolean('large' in data ? data.large : this.large); this.large = Boolean(data.large ?? this.large);
/** /**
* An array of enabled guild features, here are the possible values: * An array of enabled guild features, here are the possible values:
@@ -282,7 +282,7 @@ class Guild extends AnonymousGuild {
* <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info> * <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
* @type {?number} * @type {?number}
*/ */
this.maximumPresences = data.max_presences || 25000; this.maximumPresences = data.max_presences ?? 25000;
} else if (typeof this.maximumPresences === 'undefined') { } else if (typeof this.maximumPresences === 'undefined') {
this.maximumPresences = null; this.maximumPresences = null;
} }
@@ -406,8 +406,7 @@ class Guild extends AnonymousGuild {
* @returns {?string} * @returns {?string}
*/ */
bannerURL({ format, size } = {}) { bannerURL({ format, size } = {}) {
if (!this.banner) return null; return this.banner && this.client.rest.cdn.Banner(this.id, this.banner, format, size);
return this.client.rest.cdn.Banner(this.id, this.banner, format, size);
} }
/** /**
@@ -425,8 +424,7 @@ class Guild extends AnonymousGuild {
* @returns {?string} * @returns {?string}
*/ */
splashURL({ format, size } = {}) { splashURL({ format, size } = {}) {
if (!this.splash) return null; return this.splash && this.client.rest.cdn.Splash(this.id, this.splash, format, size);
return this.client.rest.cdn.Splash(this.id, this.splash, format, size);
} }
/** /**
@@ -435,8 +433,7 @@ class Guild extends AnonymousGuild {
* @returns {?string} * @returns {?string}
*/ */
discoverySplashURL({ format, size } = {}) { discoverySplashURL({ format, size } = {}) {
if (!this.discoverySplash) return null; return this.discoverySplash && this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size);
return this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size);
} }
/** /**
@@ -462,7 +459,7 @@ class Guild extends AnonymousGuild {
* @readonly * @readonly
*/ */
get afkChannel() { get afkChannel() {
return this.client.channels.cache.get(this.afkChannelID) || null; return this.client.channels.resolve(this.afkChannelID);
} }
/** /**
@@ -471,7 +468,7 @@ class Guild extends AnonymousGuild {
* @readonly * @readonly
*/ */
get systemChannel() { get systemChannel() {
return this.client.channels.cache.get(this.systemChannelID) || null; return this.client.channels.resolve(this.systemChannelID);
} }
/** /**
@@ -480,7 +477,7 @@ class Guild extends AnonymousGuild {
* @readonly * @readonly
*/ */
get widgetChannel() { get widgetChannel() {
return this.client.channels.cache.get(this.widgetChannelID) || null; return this.client.channels.resolve(this.widgetChannelID);
} }
/** /**
@@ -489,7 +486,7 @@ class Guild extends AnonymousGuild {
* @readonly * @readonly
*/ */
get rulesChannel() { get rulesChannel() {
return this.client.channels.cache.get(this.rulesChannelID) || null; return this.client.channels.resolve(this.rulesChannelID);
} }
/** /**
@@ -498,7 +495,7 @@ class Guild extends AnonymousGuild {
* @readonly * @readonly
*/ */
get publicUpdatesChannel() { get publicUpdatesChannel() {
return this.client.channels.cache.get(this.publicUpdatesChannelID) || null; return this.client.channels.resolve(this.publicUpdatesChannelID);
} }
/** /**
@@ -508,7 +505,7 @@ class Guild extends AnonymousGuild {
*/ */
get me() { get me() {
return ( return (
this.members.cache.get(this.client.user.id) || this.members.resolve(this.client.user.id) ??
(this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER)
? this.members.add({ user: { id: this.client.user.id } }, true) ? this.members.add({ user: { id: this.client.user.id } }, true)
: null) : null)
@@ -917,7 +914,7 @@ class Guild extends AnonymousGuild {
const welcome_channels = welcomeChannels?.map(welcomeChannelData => { const welcome_channels = welcomeChannels?.map(welcomeChannelData => {
const emoji = this.emojis.resolve(welcomeChannelData.emoji); const emoji = this.emojis.resolve(welcomeChannelData.emoji);
return { return {
emoji_id: emoji?.id ?? null, emoji_id: emoji && emoji.id,
emoji_name: emoji?.name ?? welcomeChannelData.emoji, emoji_name: emoji?.name ?? welcomeChannelData.emoji,
channel_id: this.channels.resolveID(welcomeChannelData.channel), channel_id: this.channels.resolveID(welcomeChannelData.channel),
description: welcomeChannelData.description, description: welcomeChannelData.description,

View File

@@ -332,7 +332,7 @@ class GuildAuditLogsEntry {
* The reason of this entry * The reason of this entry
* @type {?string} * @type {?string}
*/ */
this.reason = data.reason || null; this.reason = data.reason ?? null;
/** /**
* The user that executed this entry * The user that executed this entry
@@ -354,9 +354,9 @@ class GuildAuditLogsEntry {
/** /**
* Specific property changes * Specific property changes
* @type {AuditLogChange[]} * @type {?(AuditLogChange[])}
*/ */
this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null; this.changes = data.changes?.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) ?? null;
/** /**
* The ID of this entry * The ID of this entry
@@ -443,7 +443,7 @@ class GuildAuditLogsEntry {
this.target = null; this.target = null;
if (targetType === Targets.UNKNOWN) { if (targetType === Targets.UNKNOWN) {
this.target = this.changes.reduce((o, c) => { this.target = this.changes.reduce((o, c) => {
o[c.key] = c.new || c.old; o[c.key] = c.new ?? c.old;
return o; return o;
}, {}); }, {});
this.target.id = data.target_id; this.target.id = data.target_id;
@@ -456,12 +456,12 @@ class GuildAuditLogsEntry {
this.target = guild.client.guilds.cache.get(data.target_id); this.target = guild.client.guilds.cache.get(data.target_id);
} else if (targetType === Targets.WEBHOOK) { } else if (targetType === Targets.WEBHOOK) {
this.target = this.target =
logs.webhooks.get(data.target_id) || logs.webhooks.get(data.target_id) ??
new Webhook( new Webhook(
guild.client, guild.client,
this.changes.reduce( this.changes.reduce(
(o, c) => { (o, c) => {
o[c.key] = c.new || c.old; o[c.key] = c.new ?? c.old;
return o; return o;
}, },
{ {
@@ -473,13 +473,14 @@ class GuildAuditLogsEntry {
} else if (targetType === Targets.INVITE) { } else if (targetType === Targets.INVITE) {
this.target = guild.members.fetch(guild.client.user.id).then(me => { this.target = guild.members.fetch(guild.client.user.id).then(me => {
if (me.permissions.has(Permissions.FLAGS.MANAGE_GUILD)) { if (me.permissions.has(Permissions.FLAGS.MANAGE_GUILD)) {
const change = this.changes.find(c => c.key === 'code'); let change = this.changes.find(c => c.key === 'code');
change = change.new ?? change.old;
return guild.fetchInvites().then(invites => { return guild.fetchInvites().then(invites => {
this.target = invites.find(i => i.code === (change.new || change.old)); this.target = invites.find(i => i.code === change);
}); });
} else { } else {
this.target = this.changes.reduce((o, c) => { this.target = this.changes.reduce((o, c) => {
o[c.key] = c.new || c.old; o[c.key] = c.new ?? c.old;
return o; return o;
}, {}); }, {});
return this.target; return this.target;
@@ -489,16 +490,16 @@ class GuildAuditLogsEntry {
// Discord sends a channel id for the MESSAGE_BULK_DELETE action type. // Discord sends a channel id for the MESSAGE_BULK_DELETE action type.
this.target = this.target =
data.action_type === Actions.MESSAGE_BULK_DELETE data.action_type === Actions.MESSAGE_BULK_DELETE
? guild.channels.cache.get(data.target_id) || { id: data.target_id } ? guild.channels.cache.get(data.target_id) ?? { id: data.target_id }
: guild.client.users.cache.get(data.target_id); : guild.client.users.cache.get(data.target_id);
} else if (targetType === Targets.INTEGRATION) { } else if (targetType === Targets.INTEGRATION) {
this.target = this.target =
logs.integrations.get(data.target_id) || logs.integrations.get(data.target_id) ??
new Integration( new Integration(
guild.client, guild.client,
this.changes.reduce( this.changes.reduce(
(o, c) => { (o, c) => {
o[c.key] = c.new || c.old; o[c.key] = c.new ?? c.old;
return o; return o;
}, },
{ id: data.target_id }, { id: data.target_id },
@@ -507,10 +508,10 @@ class GuildAuditLogsEntry {
); );
} else if (targetType === Targets.CHANNEL) { } else if (targetType === Targets.CHANNEL) {
this.target = this.target =
guild.channels.cache.get(data.target_id) || guild.channels.cache.get(data.target_id) ??
this.changes.reduce( this.changes.reduce(
(o, c) => { (o, c) => {
o[c.key] = c.new || c.old; o[c.key] = c.new ?? c.old;
return o; return o;
}, },
{ id: data.target_id }, { id: data.target_id },
@@ -522,7 +523,7 @@ class GuildAuditLogsEntry {
guild.client, guild.client,
this.changes.reduce( this.changes.reduce(
(o, c) => { (o, c) => {
o[c.key] = c.new || c.old; o[c.key] = c.new ?? c.old;
return o; return o;
}, },
{ {
@@ -533,7 +534,7 @@ class GuildAuditLogsEntry {
), ),
); );
} else if (data.target_id) { } else if (data.target_id) {
this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) || { id: data.target_id }; this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) ?? { id: data.target_id };
} }
} }

View File

@@ -85,7 +85,7 @@ class GuildChannel extends Channel {
* @readonly * @readonly
*/ */
get parent() { get parent() {
return this.guild.channels.cache.get(this.parentID) || null; return this.guild.channels.resolve(this.parentID);
} }
/** /**
@@ -106,10 +106,10 @@ class GuildChannel extends Channel {
// Handle empty overwrite // Handle empty overwrite
if ( if (
(channelVal === undefined && (!channelVal &&
parentVal.deny.bitfield === Permissions.defaultBit && parentVal.deny.bitfield === Permissions.defaultBit &&
parentVal.allow.bitfield === Permissions.defaultBit) || parentVal.allow.bitfield === Permissions.defaultBit) ||
(parentVal === undefined && (!parentVal &&
channelVal.deny.bitfield === Permissions.defaultBit && channelVal.deny.bitfield === Permissions.defaultBit &&
channelVal.allow.bitfield === Permissions.defaultBit) channelVal.allow.bitfield === Permissions.defaultBit)
) { ) {
@@ -118,8 +118,8 @@ class GuildChannel extends Channel {
// Compare overwrites // Compare overwrites
return ( return (
channelVal !== undefined && typeof channelVal !== 'undefined' &&
parentVal !== undefined && typeof parentVal !== 'undefined' &&
channelVal.deny.bitfield === parentVal.deny.bitfield && channelVal.deny.bitfield === parentVal.deny.bitfield &&
channelVal.allow.bitfield === parentVal.allow.bitfield channelVal.allow.bitfield === parentVal.allow.bitfield
); );
@@ -145,15 +145,14 @@ class GuildChannel extends Channel {
const member = this.guild.members.resolve(memberOrRole); const member = this.guild.members.resolve(memberOrRole);
if (member) return this.memberPermissions(member); if (member) return this.memberPermissions(member);
const role = this.guild.roles.resolve(memberOrRole); const role = this.guild.roles.resolve(memberOrRole);
if (role) return this.rolePermissions(role); return role && this.rolePermissions(role);
return null;
} }
overwritesFor(member, verified = false, roles = null) { overwritesFor(member, verified = false, roles = null) {
if (!verified) member = this.guild.members.resolve(member); if (!verified) member = this.guild.members.resolve(member);
if (!member) return []; if (!member) return [];
roles = roles || member.roles.cache; if (!roles) roles = member.roles.cache;
const roleOverwrites = []; const roleOverwrites = [];
let memberOverwrites; let memberOverwrites;
let everyoneOverwrites; let everyoneOverwrites;
@@ -192,20 +191,12 @@ class GuildChannel extends Channel {
const overwrites = this.overwritesFor(member, true, roles); const overwrites = this.overwritesFor(member, true, roles);
return permissions return permissions
.remove(overwrites.everyone ? overwrites.everyone.deny : Permissions.defaultBit) .remove(overwrites.everyone?.deny ?? Permissions.defaultBit)
.add(overwrites.everyone ? overwrites.everyone.allow : Permissions.defaultBit) .add(overwrites.everyone?.allow ?? Permissions.defaultBit)
.remove( .remove(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.deny) : Permissions.defaultBit)
overwrites.roles.length > Permissions.defaultBit .add(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.allow) : Permissions.defaultBit)
? overwrites.roles.map(role => role.deny) .remove(overwrites.member?.deny ?? Permissions.defaultBit)
: Permissions.defaultBit, .add(overwrites.member?.allow ?? Permissions.defaultBit)
)
.add(
overwrites.roles.length > Permissions.defaultBit
? overwrites.roles.map(role => role.allow)
: Permissions.defaultBit,
)
.remove(overwrites.member ? overwrites.member.deny : Permissions.defaultBit)
.add(overwrites.member ? overwrites.member.allow : Permissions.defaultBit)
.freeze(); .freeze();
} }
@@ -222,10 +213,10 @@ class GuildChannel extends Channel {
const roleOverwrites = this.permissionOverwrites.get(role.id); const roleOverwrites = this.permissionOverwrites.get(role.id);
return role.permissions return role.permissions
.remove(everyoneOverwrites ? everyoneOverwrites.deny : Permissions.defaultBit) .remove(everyoneOverwrites?.deny ?? Permissions.defaultBit)
.add(everyoneOverwrites ? everyoneOverwrites.allow : Permissions.defaultBit) .add(everyoneOverwrites?.allow ?? Permissions.defaultBit)
.remove(roleOverwrites ? roleOverwrites.deny : Permissions.defaultBit) .remove(roleOverwrites?.deny ?? Permissions.defaultBit)
.add(roleOverwrites ? roleOverwrites.allow : Permissions.defaultBit) .add(roleOverwrites?.allow ?? Permissions.defaultBit)
.freeze(); .freeze();
} }
@@ -274,7 +265,7 @@ class GuildChannel extends Channel {
* .catch(console.error); * .catch(console.error);
*/ */
async updateOverwrite(userOrRole, options, overwriteOptions = {}) { async updateOverwrite(userOrRole, options, overwriteOptions = {}) {
const userOrRoleID = this.guild.roles.resolveID(userOrRole) || this.client.users.resolveID(userOrRole); const userOrRoleID = this.guild.roles.resolveID(userOrRole) ?? this.client.users.resolveID(userOrRole);
const { reason } = overwriteOptions; const { reason } = overwriteOptions;
const existing = this.permissionOverwrites.get(userOrRoleID); const existing = this.permissionOverwrites.get(userOrRoleID);
if (existing) { if (existing) {
@@ -300,10 +291,10 @@ class GuildChannel extends Channel {
* .catch(console.error); * .catch(console.error);
*/ */
createOverwrite(userOrRole, options, overwriteOptions = {}) { createOverwrite(userOrRole, options, overwriteOptions = {}) {
let userOrRoleID = this.guild.roles.resolveID(userOrRole) || this.client.users.resolveID(userOrRole); let userOrRoleID = this.guild.roles.resolveID(userOrRole) ?? this.client.users.resolveID(userOrRole);
let { type, reason } = overwriteOptions; let { type, reason } = overwriteOptions;
if (typeof type !== 'number') { if (typeof type !== 'number') {
userOrRole = this.guild.roles.resolve(userOrRole) || this.client.users.resolve(userOrRole); userOrRole = this.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole);
if (!userOrRole) return Promise.reject(new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role')); if (!userOrRole) return Promise.reject(new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'));
userOrRoleID = userOrRole.id; userOrRoleID = userOrRole.id;
type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member; type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member;
@@ -410,7 +401,7 @@ class GuildChannel extends Channel {
if (data.lockPermissions) { if (data.lockPermissions) {
if (data.parentID) { if (data.parentID) {
const newParent = this.guild.channels.resolve(data.parentID); const newParent = this.guild.channels.resolve(data.parentID);
if (newParent && newParent.type === 'category') { if (newParent?.type === 'category') {
permission_overwrites = newParent.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); permission_overwrites = newParent.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));
} }
} else if (this.parent) { } else if (this.parent) {
@@ -420,13 +411,13 @@ class GuildChannel extends Channel {
const newData = await this.client.api.channels(this.id).patch({ const newData = await this.client.api.channels(this.id).patch({
data: { data: {
name: (data.name || this.name).trim(), name: (data.name ?? this.name).trim(),
type: ChannelTypes[data.type?.toUpperCase()], type: ChannelTypes[data.type?.toUpperCase()],
topic: data.topic, topic: data.topic,
nsfw: data.nsfw, nsfw: data.nsfw,
bitrate: data.bitrate || this.bitrate, bitrate: data.bitrate ?? this.bitrate,
user_limit: typeof data.userLimit !== 'undefined' ? data.userLimit : this.userLimit, user_limit: data.userLimit ?? this.userLimit,
rtc_region: typeof data.rtcRegion !== 'undefined' ? data.rtcRegion : this.rtcRegion, rtc_region: data.rtcRegion ?? this.rtcRegion,
parent_id: data.parentID, parent_id: data.parentID,
lock_permissions: data.lockPermissions, lock_permissions: data.lockPermissions,
rate_limit_per_user: data.rateLimitPerUser, rate_limit_per_user: data.rateLimitPerUser,
@@ -476,7 +467,7 @@ class GuildChannel extends Channel {
return this.edit( return this.edit(
{ {
// eslint-disable-next-line no-prototype-builtins // eslint-disable-next-line no-prototype-builtins
parentID: channel !== null ? (channel.hasOwnProperty('id') ? channel.id : channel) : null, parentID: channel?.id ?? channel ?? null,
lockPermissions, lockPermissions,
}, },
reason, reason,

View File

@@ -108,7 +108,7 @@ class GuildEmoji extends BaseGuildEmoji {
* .catch(console.error); * .catch(console.error);
*/ */
edit(data, reason) { edit(data, reason) {
const roles = data.roles ? data.roles.map(r => r.id || r) : undefined; const roles = data.roles?.map(r => r.id ?? r);
return this.client.api return this.client.api
.guilds(this.guild.id) .guilds(this.guild.id)
.emojis(this.id) .emojis(this.id)

View File

@@ -85,7 +85,7 @@ class GuildMember extends Base {
if ('nick' in data) this.nickname = data.nick; if ('nick' in data) this.nickname = data.nick;
if ('joined_at' in data) this.joinedTimestamp = new Date(data.joined_at).getTime(); if ('joined_at' in data) this.joinedTimestamp = new Date(data.joined_at).getTime();
if ('premium_since' in data) { if ('premium_since' in data) {
this.premiumSinceTimestamp = data.premium_since === null ? null : new Date(data.premium_since).getTime(); this.premiumSinceTimestamp = data.premium_since ? new Date(data.premium_since).getTime() : null;
} }
if ('roles' in data) this._roles = data.roles; if ('roles' in data) this._roles = data.roles;
this.pending = data.pending ?? false; this.pending = data.pending ?? false;
@@ -121,8 +121,7 @@ class GuildMember extends Base {
* @readonly * @readonly
*/ */
get lastMessage() { get lastMessage() {
const channel = this.guild.channels.cache.get(this.lastMessageChannelID); return this.guild.channels.resolve(this.lastMessageChannelID)?.messages.resolve(this.lastMessageID) ?? null;
return (channel && channel.messages.cache.get(this.lastMessageID)) || null;
} }
/** /**
@@ -133,7 +132,7 @@ class GuildMember extends Base {
get voice() { get voice() {
if (!Structures) Structures = require('../util/Structures'); if (!Structures) Structures = require('../util/Structures');
const VoiceState = Structures.get('VoiceState'); const VoiceState = Structures.get('VoiceState');
return this.guild.voiceStates.cache.get(this.id) || new VoiceState(this.guild, { user_id: this.id }); return this.guild.voiceStates.cache.get(this.id) ?? new VoiceState(this.guild, { user_id: this.id });
} }
/** /**
@@ -163,7 +162,7 @@ class GuildMember extends Base {
if (!Structures) Structures = require('../util/Structures'); if (!Structures) Structures = require('../util/Structures');
const Presence = Structures.get('Presence'); const Presence = Structures.get('Presence');
return ( return (
this.guild.presences.cache.get(this.id) || this.guild.presences.cache.get(this.id) ??
new Presence(this.client, { new Presence(this.client, {
user: { user: {
id: this.id, id: this.id,
@@ -179,8 +178,7 @@ class GuildMember extends Base {
* @readonly * @readonly
*/ */
get displayColor() { get displayColor() {
const role = this.roles.color; return this.roles.color?.color ?? 0;
return (role && role.color) || 0;
} }
/** /**
@@ -189,8 +187,7 @@ class GuildMember extends Base {
* @readonly * @readonly
*/ */
get displayHexColor() { get displayHexColor() {
const role = this.roles.color; return this.roles.color?.hexColor ?? '#000000';
return (role && role.hexColor) || '#000000';
} }
/** /**
@@ -208,7 +205,7 @@ class GuildMember extends Base {
* @readonly * @readonly
*/ */
get displayName() { get displayName() {
return this.nickname || this.user.username; return this.nickname ?? this.user.username;
} }
/** /**

View File

@@ -75,7 +75,7 @@ class GuildPreview extends Base {
* The description for this guild * The description for this guild
* @type {?string} * @type {?string}
*/ */
this.description = data.description || null; this.description = data.description ?? null;
if (!this.emojis) { if (!this.emojis) {
/** /**
@@ -97,8 +97,7 @@ class GuildPreview extends Base {
* @returns {?string} * @returns {?string}
*/ */
splashURL({ format, size } = {}) { splashURL({ format, size } = {}) {
if (!this.splash) return null; return this.splash && this.client.rest.cdn.Splash(this.id, this.splash, format, size);
return this.client.rest.cdn.Splash(this.id, this.splash, format, size);
} }
/** /**
@@ -107,8 +106,7 @@ class GuildPreview extends Base {
* @returns {?string} * @returns {?string}
*/ */
discoverySplashURL({ format, size } = {}) { discoverySplashURL({ format, size } = {}) {
if (!this.discoverySplash) return null; return this.discoverySplash && this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size);
return this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, format, size);
} }
/** /**
@@ -117,8 +115,7 @@ class GuildPreview extends Base {
* @returns {?string} * @returns {?string}
*/ */
iconURL({ format, size, dynamic } = {}) { iconURL({ format, size, dynamic } = {}) {
if (!this.icon) return null; return this.icon && this.client.rest.cdn.Icon(this.id, this.icon, format, size, dynamic);
return this.client.rest.cdn.Icon(this.id, this.icon, format, size, dynamic);
} }
/** /**

View File

@@ -202,7 +202,7 @@ class GuildTemplate extends Base {
* @readonly * @readonly
*/ */
get guild() { get guild() {
return this.client.guilds.cache.get(this.guildID) || null; return this.client.guilds.resolve(this.guildID);
} }
/** /**

View File

@@ -90,7 +90,7 @@ class Integration extends Base {
*/ */
get roles() { get roles() {
const roles = this.guild.roles.cache; const roles = this.guild.roles.cache;
return roles.filter(role => role.tags && role.tags.integrationID === this.id); return roles.filter(role => role.tags?.integrationID === this.id);
} }
_patch(data) { _patch(data) {

View File

@@ -40,37 +40,37 @@ class Invite extends Base {
* The approximate number of online members of the guild this invite is for * The approximate number of online members of the guild this invite is for
* @type {?number} * @type {?number}
*/ */
this.presenceCount = 'approximate_presence_count' in data ? data.approximate_presence_count : null; this.presenceCount = data.approximate_presence_count ?? null;
/** /**
* The approximate total number of members of the guild this invite is for * The approximate total number of members of the guild this invite is for
* @type {?number} * @type {?number}
*/ */
this.memberCount = 'approximate_member_count' in data ? data.approximate_member_count : null; this.memberCount = data.approximate_member_count ?? null;
/** /**
* Whether or not this invite is temporary * Whether or not this invite is temporary
* @type {?boolean} * @type {?boolean}
*/ */
this.temporary = 'temporary' in data ? data.temporary : null; this.temporary = data.temporary ?? null;
/** /**
* The maximum age of the invite, in seconds, 0 if never expires * The maximum age of the invite, in seconds, 0 if never expires
* @type {?number} * @type {?number}
*/ */
this.maxAge = 'max_age' in data ? data.max_age : null; this.maxAge = data.max_age ?? null;
/** /**
* How many times this invite has been used * How many times this invite has been used
* @type {?number} * @type {?number}
*/ */
this.uses = 'uses' in data ? data.uses : null; this.uses = data.uses ?? null;
/** /**
* The maximum uses of this invite * The maximum uses of this invite
* @type {?number} * @type {?number}
*/ */
this.maxUses = 'max_uses' in data ? data.max_uses : null; this.maxUses = data.max_uses ?? null;
/** /**
* The user who created this invite * The user who created this invite
@@ -103,7 +103,7 @@ class Invite extends Base {
* The target type * The target type
* @type {?TargetType} * @type {?TargetType}
*/ */
this.targetType = typeof data.target_type === 'number' ? data.target_type : null; this.targetType = data.target_type ?? null;
/** /**
* The channel the invite is for * The channel the invite is for

View File

@@ -133,13 +133,13 @@ class Message extends Base {
* A list of embeds in the message - e.g. YouTube Player * A list of embeds in the message - e.g. YouTube Player
* @type {MessageEmbed[]} * @type {MessageEmbed[]}
*/ */
this.embeds = (data.embeds || []).map(e => new Embed(e, true)); this.embeds = data.embeds?.map(e => new Embed(e, true)) ?? [];
/** /**
* A list of MessageActionRows in the message * A list of MessageActionRows in the message
* @type {MessageActionRow[]} * @type {MessageActionRow[]}
*/ */
this.components = (data.components ?? []).map(c => BaseMessageComponent.create(c, this.client)); this.components = data.components?.map(c => BaseMessageComponent.create(c, this.client)) ?? [];
/** /**
* A collection of attachments in the message - e.g. Pictures - mapped by their ID * A collection of attachments in the message - e.g. Pictures - mapped by their ID
@@ -180,7 +180,7 @@ class Message extends Base {
* @type {ReactionManager} * @type {ReactionManager}
*/ */
this.reactions = new ReactionManager(this); this.reactions = new ReactionManager(this);
if (data.reactions && data.reactions.length > 0) { if (data.reactions?.length > 0) {
for (const reaction of data.reactions) { for (const reaction of data.reactions) {
this.reactions.add(reaction); this.reactions.add(reaction);
} }
@@ -203,7 +203,7 @@ class Message extends Base {
* ID of the webhook that sent the message, if applicable * ID of the webhook that sent the message, if applicable
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.webhookID = data.webhook_id || null; this.webhookID = data.webhook_id ?? null;
/** /**
* Supplemental application information for group activities * Supplemental application information for group activities
@@ -312,10 +312,6 @@ class Message extends Base {
if ('pinned' in data) this.pinned = data.pinned; if ('pinned' in data) this.pinned = data.pinned;
if ('tts' in data) this.tts = data.tts; if ('tts' in data) this.tts = data.tts;
if ('thread' in data) this.thread = this.client.channels.add(data.thread); if ('thread' in data) this.thread = this.client.channels.add(data.thread);
if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(e, true));
else this.embeds = this.embeds.slice();
if ('components' in data) this.components = data.components.map(c => BaseMessageComponent.create(c, this.client));
else this.components = this.components.slice();
if ('attachments' in data) { if ('attachments' in data) {
this.attachments = new Collection(); this.attachments = new Collection();
@@ -326,16 +322,19 @@ class Message extends Base {
this.attachments = new Collection(this.attachments); this.attachments = new Collection(this.attachments);
} }
this.embeds = data.embeds?.map(e => new Embed(e, true)) ?? this.embeds.slice();
this.components = data.components?.map(c => BaseMessageComponent.create(c, this.client)) ?? this.components.slice();
this.mentions = new Mentions( this.mentions = new Mentions(
this, this,
'mentions' in data ? data.mentions : this.mentions.users, data.mentions ?? this.mentions.users,
'mention_roles' in data ? data.mention_roles : this.mentions.roles, data.mention_roles ?? this.mentions.roles,
'mention_everyone' in data ? data.mention_everyone : this.mentions.everyone, data.mention_everyone ?? this.mentions.everyone,
'mention_channels' in data ? data.mention_channels : this.mentions.crosspostedChannels, data.mention_channels ?? this.mentions.crosspostedChannels,
data.referenced_message?.author ?? this.mentions.repliedUser, data.referenced_message?.author ?? this.mentions.repliedUser,
); );
this.flags = new MessageFlags('flags' in data ? data.flags : 0).freeze(); this.flags = new MessageFlags(data.flags ?? 0).freeze();
return clone; return clone;
} }
@@ -347,7 +346,7 @@ class Message extends Base {
* @readonly * @readonly
*/ */
get member() { get member() {
return this.guild ? this.guild.members.resolve(this.author) || null : null; return this.guild?.members.resolve(this.author) ?? null;
} }
/** /**
@@ -374,7 +373,7 @@ class Message extends Base {
* @readonly * @readonly
*/ */
get guild() { get guild() {
return this.channel.guild || null; return this.channel.guild ?? null;
} }
/** /**
@@ -434,7 +433,7 @@ class Message extends Base {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const collector = this.createReactionCollector(options); const collector = this.createReactionCollector(options);
collector.once('end', (reactions, reason) => { collector.once('end', (reactions, reason) => {
if (options.errors && options.errors.includes(reason)) reject(reactions); if (options.errors?.includes(reason)) reject(reactions);
else resolve(reactions); else resolve(reactions);
}); });
}); });

View File

@@ -45,7 +45,7 @@ class MessageActionRow extends BaseMessageComponent {
* The components in this action row * The components in this action row
* @type {MessageActionRowComponent[]} * @type {MessageActionRowComponent[]}
*/ */
this.components = (data.components ?? []).map(c => BaseMessageComponent.create(c, null, true)); this.components = data.components?.map(c => BaseMessageComponent.create(c, null, true)) ?? [];
} }
/** /**

View File

@@ -72,13 +72,13 @@ class MessageAttachment {
* The height of this attachment (if an image or video) * The height of this attachment (if an image or video)
* @type {?number} * @type {?number}
*/ */
this.height = typeof data.height !== 'undefined' ? data.height : null; this.height = data.height ?? null;
/** /**
* The width of this attachment (if an image or video) * The width of this attachment (if an image or video)
* @type {?number} * @type {?number}
*/ */
this.width = typeof data.width !== 'undefined' ? data.width : null; this.width = data.width ?? null;
/** /**
* This media type of this attachment * This media type of this attachment

View File

@@ -114,7 +114,7 @@ class MessageCollector extends Collector {
* @returns {void} * @returns {void}
*/ */
_handleGuildDeletion(guild) { _handleGuildDeletion(guild) {
if (this.channel.guild && guild.id === this.channel.guild.id) { if (guild.id === this.channel.guild?.id) {
this.stop('guildDelete'); this.stop('guildDelete');
} }
} }

View File

@@ -36,7 +36,7 @@ class MessageComponentInteractionCollector extends Collector {
* The source channel from which to collect message component interactions * The source channel from which to collect message component interactions
* @type {TextChannel|DMChannel|NewsChannel} * @type {TextChannel|DMChannel|NewsChannel}
*/ */
this.channel = this.message ? this.message.channel : source; this.channel = this.message?.channel ?? source;
/** /**
* The users which have interacted to components on this collector * The users which have interacted to components on this collector

View File

@@ -46,25 +46,25 @@ class MessageEmbed {
* @type {string} * @type {string}
* @deprecated * @deprecated
*/ */
this.type = data.type || 'rich'; this.type = data.type ?? 'rich';
/** /**
* The title of this embed * The title of this embed
* @type {?string} * @type {?string}
*/ */
this.title = 'title' in data ? data.title : null; this.title = data.title ?? null;
/** /**
* The description of this embed * The description of this embed
* @type {?string} * @type {?string}
*/ */
this.description = 'description' in data ? data.description : null; this.description = data.description ?? null;
/** /**
* The URL of this embed * The URL of this embed
* @type {?string} * @type {?string}
*/ */
this.url = 'url' in data ? data.url : null; this.url = data.url ?? null;
/** /**
* The color of this embed * The color of this embed
@@ -111,7 +111,7 @@ class MessageEmbed {
this.thumbnail = data.thumbnail this.thumbnail = data.thumbnail
? { ? {
url: data.thumbnail.url, url: data.thumbnail.url,
proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url, proxyURL: data.thumbnail.proxyURL ?? data.thumbnail.proxy_url,
height: data.thumbnail.height, height: data.thumbnail.height,
width: data.thumbnail.width, width: data.thumbnail.width,
} }
@@ -133,7 +133,7 @@ class MessageEmbed {
this.image = data.image this.image = data.image
? { ? {
url: data.image.url, url: data.image.url,
proxyURL: data.image.proxyURL || data.image.proxy_url, proxyURL: data.image.proxyURL ?? data.image.proxy_url,
height: data.image.height, height: data.image.height,
width: data.image.width, width: data.image.width,
} }
@@ -156,7 +156,7 @@ class MessageEmbed {
this.video = data.video this.video = data.video
? { ? {
url: data.video.url, url: data.video.url,
proxyURL: data.video.proxyURL || data.video.proxy_url, proxyURL: data.video.proxyURL ?? data.video.proxy_url,
height: data.video.height, height: data.video.height,
width: data.video.width, width: data.video.width,
} }
@@ -179,8 +179,8 @@ class MessageEmbed {
? { ? {
name: data.author.name, name: data.author.name,
url: data.author.url, url: data.author.url,
iconURL: data.author.iconURL || data.author.icon_url, iconURL: data.author.iconURL ?? data.author.icon_url,
proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url, proxyIconURL: data.author.proxyIconURL ?? data.author.proxy_icon_url,
} }
: null; : null;
@@ -217,8 +217,8 @@ class MessageEmbed {
this.footer = data.footer this.footer = data.footer
? { ? {
text: data.footer.text, text: data.footer.text,
iconURL: data.footer.iconURL || data.footer.icon_url, iconURL: data.footer.iconURL ?? data.footer.icon_url,
proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url, proxyIconURL: data.footer.proxyIconURL ?? data.footer.proxy_icon_url,
} }
: null; : null;
} }
@@ -395,24 +395,20 @@ class MessageEmbed {
type: 'rich', type: 'rich',
description: this.description, description: this.description,
url: this.url, url: this.url,
timestamp: this.timestamp ? new Date(this.timestamp) : null, timestamp: this.timestamp && new Date(this.timestamp),
color: this.color, color: this.color,
fields: this.fields, fields: this.fields,
thumbnail: this.thumbnail, thumbnail: this.thumbnail,
image: this.image, image: this.image,
author: this.author author: this.author && {
? { name: this.author.name,
name: this.author.name, url: this.author.url,
url: this.author.url, icon_url: this.author.iconURL,
icon_url: this.author.iconURL, },
} footer: this.footer && {
: null, text: this.footer.text,
footer: this.footer icon_url: this.footer.iconURL,
? { },
text: this.footer.text,
icon_url: this.footer.iconURL,
}
: null,
}; };
} }
@@ -447,11 +443,7 @@ class MessageEmbed {
return fields return fields
.flat(2) .flat(2)
.map(field => .map(field =>
this.normalizeField( this.normalizeField(field.name, field.value, typeof field.inline === 'boolean' ? field.inline : false),
field && field.name,
field && field.value,
field && typeof field.inline === 'boolean' ? field.inline : false,
),
); );
} }
} }

View File

@@ -117,7 +117,7 @@ class MessageMentions {
this.crosspostedChannels.set(d.id, { this.crosspostedChannels.set(d.id, {
channelID: d.id, channelID: d.id,
guildID: d.guild_id, guildID: d.guild_id,
type: type ? type.toLowerCase() : 'unknown', type: type?.toLowerCase() ?? 'unknown',
name: d.name, name: d.name,
}); });
} }
@@ -191,11 +191,9 @@ class MessageMentions {
if (!ignoreDirect) { if (!ignoreDirect) {
const id = const id =
this.client.users.resolveID(data) || this.guild?.roles.resolveID(data) ?? this.client.channels.resolveID(data) ?? this.client.users.resolveID(data);
(this.guild && this.guild.roles.resolveID(data)) ||
this.client.channels.resolveID(data);
return this.users.has(id) || this.channels.has(id) || this.roles.has(id); return typeof id === 'string' && (this.users.has(id) || this.channels.has(id) || this.roles.has(id));
} }
return false; return false;

View File

@@ -147,7 +147,7 @@ class MessagePayload {
let username; let username;
let avatarURL; let avatarURL;
if (isWebhook) { if (isWebhook) {
username = this.options.username || this.target.name; username = this.options.username ?? this.target.name;
if (this.options.avatarURL) avatarURL = this.options.avatarURL; if (this.options.avatarURL) avatarURL = this.options.avatarURL;
} }
@@ -239,7 +239,7 @@ class MessagePayload {
name = findName(attachment); name = findName(attachment);
} else { } else {
attachment = fileLike.attachment; attachment = fileLike.attachment;
name = fileLike.name || findName(attachment); name = fileLike.name ?? findName(attachment);
} }
const resource = await DataResolver.resolveFile(attachment); const resource = await DataResolver.resolveFile(attachment);

View File

@@ -105,9 +105,9 @@ class MessageReaction {
*/ */
async fetch() { async fetch() {
const message = await this.message.fetch(); const message = await this.message.fetch();
const existing = message.reactions.cache.get(this.emoji.id || this.emoji.name); const existing = message.reactions.cache.get(this.emoji.id ?? this.emoji.name);
// The reaction won't get set when it has been completely removed // The reaction won't get set when it has been completely removed
this._patch(existing || { count: 0 }); this._patch(existing ?? { count: 0 });
return this; return this;
} }
@@ -128,7 +128,7 @@ class MessageReaction {
if (!this.me || user.id !== this.message.client.user.id) this.count--; if (!this.me || user.id !== this.message.client.user.id) this.count--;
if (user.id === this.message.client.user.id) this.me = false; if (user.id === this.message.client.user.id) this.me = false;
if (this.count <= 0 && this.users.cache.size === 0) { if (this.count <= 0 && this.users.cache.size === 0) {
this.message.reactions.cache.delete(this.emoji.id || this.emoji.name); this.message.reactions.cache.delete(this.emoji.id ?? this.emoji.name);
} }
} }
} }

View File

@@ -30,8 +30,7 @@ class PartialGroupDMChannel extends Channel {
* @returns {?string} * @returns {?string}
*/ */
iconURL({ format, size } = {}) { iconURL({ format, size } = {}) {
if (!this.icon) return null; return this.icon && this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size);
return this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size);
} }
delete() { delete() {

View File

@@ -181,7 +181,7 @@ class PermissionOverwrites {
}; };
} }
const userOrRole = guild.roles.resolve(overwrite.id) || guild.client.users.resolve(overwrite.id); const userOrRole = guild.roles.resolve(overwrite.id) ?? guild.client.users.resolve(overwrite.id);
if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role'); if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
const type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member; const type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member;

View File

@@ -55,7 +55,7 @@ class Presence {
* The guild of this presence * The guild of this presence
* @type {?Guild} * @type {?Guild}
*/ */
this.guild = data.guild || null; this.guild = data.guild ?? null;
this.patch(data); this.patch(data);
} }
@@ -66,7 +66,7 @@ class Presence {
* @readonly * @readonly
*/ */
get user() { get user() {
return this.client.users.cache.get(this.userID) || null; return this.client.users.resolve(this.userID);
} }
/** /**
@@ -75,7 +75,7 @@ class Presence {
* @readonly * @readonly
*/ */
get member() { get member() {
return this.guild.members.cache.get(this.userID) || null; return this.guild.members.resolve(this.userID);
} }
patch(data) { patch(data) {
@@ -83,19 +83,13 @@ class Presence {
* The status of this presence * The status of this presence
* @type {PresenceStatus} * @type {PresenceStatus}
*/ */
this.status = data.status || this.status || 'offline'; this.status = data.status ?? this.status ?? 'offline';
if (data.activities) { /**
/** * The activities of this presence
* The activities of this presence * @type {Activity[]}
* @type {Activity[]} */
*/ this.activities = data.activities?.map(activity => new Activity(this, activity)) ?? [];
this.activities = data.activities.map(activity => new Activity(this, activity));
} else if (data.activity || data.game) {
this.activities = [new Activity(this, data.game || data.activity)];
} else {
this.activities = [];
}
/** /**
* The devices this presence is on * The devices this presence is on
@@ -104,14 +98,14 @@ class Presence {
* @property {?ClientPresenceStatus} mobile The current presence in the mobile application * @property {?ClientPresenceStatus} mobile The current presence in the mobile application
* @property {?ClientPresenceStatus} desktop The current presence in the desktop application * @property {?ClientPresenceStatus} desktop The current presence in the desktop application
*/ */
this.clientStatus = data.client_status || null; this.clientStatus = data.client_status ?? null;
return this; return this;
} }
_clone() { _clone() {
const clone = Object.assign(Object.create(this), this); const clone = Object.assign(Object.create(this), this);
if (this.activities) clone.activities = this.activities.map(activity => activity._clone()); clone.activities = this.activities.map(activity => activity._clone());
return clone; return clone;
} }
@@ -127,9 +121,9 @@ class Presence {
this.status === presence.status && this.status === presence.status &&
this.activities.length === presence.activities.length && this.activities.length === presence.activities.length &&
this.activities.every((activity, index) => activity.equals(presence.activities[index])) && this.activities.every((activity, index) => activity.equals(presence.activities[index])) &&
this.clientStatus.web === presence.clientStatus.web && this.clientStatus?.web === presence.clientStatus?.web &&
this.clientStatus.mobile === presence.clientStatus.mobile && this.clientStatus?.mobile === presence.clientStatus?.mobile &&
this.clientStatus.desktop === presence.clientStatus.desktop) this.clientStatus?.desktop === presence.clientStatus?.desktop)
); );
} }
@@ -175,25 +169,25 @@ class Activity {
* If the activity is being streamed, a link to the stream * If the activity is being streamed, a link to the stream
* @type {?string} * @type {?string}
*/ */
this.url = data.url || null; this.url = data.url ?? null;
/** /**
* Details about the activity * Details about the activity
* @type {?string} * @type {?string}
*/ */
this.details = data.details || null; this.details = data.details ?? null;
/** /**
* State of the activity * State of the activity
* @type {?string} * @type {?string}
*/ */
this.state = data.state || null; this.state = data.state ?? null;
/** /**
* Application ID associated with this activity * Application ID associated with this activity
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.applicationID = data.application_id || null; this.applicationID = data.application_id ?? null;
/** /**
* Timestamps for the activity * Timestamps for the activity
@@ -226,7 +220,7 @@ class Activity {
* @property {?string} id ID of the party * @property {?string} id ID of the party
* @property {number[]} size Size of the party as `[current, max]` * @property {number[]} size Size of the party as `[current, max]`
*/ */
this.party = data.party || null; this.party = data.party ?? null;
/** /**
* Assets for rich presence * Assets for rich presence
@@ -315,25 +309,25 @@ class RichPresenceAssets {
* Hover text for the large image * Hover text for the large image
* @type {?string} * @type {?string}
*/ */
this.largeText = assets.large_text || null; this.largeText = assets.large_text ?? null;
/** /**
* Hover text for the small image * Hover text for the small image
* @type {?string} * @type {?string}
*/ */
this.smallText = assets.small_text || null; this.smallText = assets.small_text ?? null;
/** /**
* ID of the large image asset * ID of the large image asset
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.largeImage = assets.large_image || null; this.largeImage = assets.large_image ?? null;
/** /**
* ID of the small image asset * ID of the small image asset
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.smallImage = assets.small_image || null; this.smallImage = assets.small_image ?? null;
} }
/** /**
@@ -342,11 +336,13 @@ class RichPresenceAssets {
* @returns {?string} The small image URL * @returns {?string} The small image URL
*/ */
smallImageURL({ format, size } = {}) { smallImageURL({ format, size } = {}) {
if (!this.smallImage) return null; return (
return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.smallImage, { this.smallImage &&
format, this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationID, this.smallImage, {
size, format,
}); size,
})
);
} }
/** /**

View File

@@ -183,7 +183,7 @@ class ReactionCollector extends Collector {
* @returns {void} * @returns {void}
*/ */
_handleGuildDeletion(guild) { _handleGuildDeletion(guild) {
if (this.message.guild && guild.id === this.message.guild.id) { if (guild.id === this.message.guild?.id) {
this.stop('guildDelete'); this.stop('guildDelete');
} }
} }
@@ -194,7 +194,7 @@ class ReactionCollector extends Collector {
* @returns {Snowflake|string} * @returns {Snowflake|string}
*/ */
static key(reaction) { static key(reaction) {
return reaction.emoji.id || reaction.emoji.name; return reaction.emoji.id ?? reaction.emoji.name;
} }
} }

View File

@@ -215,11 +215,11 @@ class Role extends Base {
return this.client.api.guilds[this.guild.id].roles[this.id] return this.client.api.guilds[this.guild.id].roles[this.id]
.patch({ .patch({
data: { data: {
name: data.name || this.name, name: data.name ?? this.name,
color: data.color !== null ? Util.resolveColor(data.color || this.color) : null, color: data.color !== null ? Util.resolveColor(data.color ?? this.color) : null,
hoist: typeof data.hoist !== 'undefined' ? data.hoist : this.hoist, hoist: data.hoist ?? this.hoist,
permissions: typeof data.permissions !== 'undefined' ? new Permissions(data.permissions) : this.permissions, permissions: typeof data.permissions !== 'undefined' ? new Permissions(data.permissions) : this.permissions,
mentionable: typeof data.mentionable !== 'undefined' ? data.mentionable : this.mentionable, mentionable: data.mentionable ?? this.mentionable,
}, },
reason, reason,
}) })

View File

@@ -32,13 +32,13 @@ class Team extends Base {
* The Team's icon hash * The Team's icon hash
* @type {?string} * @type {?string}
*/ */
this.icon = data.icon || null; this.icon = data.icon ?? null;
/** /**
* The Team's owner id * The Team's owner id
* @type {?string} * @type {?string}
*/ */
this.ownerID = data.owner_user_id || null; this.ownerID = data.owner_user_id ?? null;
/** /**
* The Team's members * The Team's members
@@ -58,7 +58,7 @@ class Team extends Base {
* @readonly * @readonly
*/ */
get owner() { get owner() {
return this.members.get(this.ownerID) || null; return this.members.get(this.ownerID) ?? null;
} }
/** /**

View File

@@ -197,7 +197,7 @@ class ThreadChannel extends Channel {
async edit(data, reason) { async edit(data, reason) {
const newData = await this.client.api.channels(this.id).patch({ const newData = await this.client.api.channels(this.id).patch({
data: { data: {
name: (data.name || this.name).trim(), name: (data.name ?? this.name).trim(),
archived: data.archived, archived: data.archived,
auto_archive_duration: data.autoArchiveDuration, auto_archive_duration: data.autoArchiveDuration,
rate_limit_per_user: data.rateLimitPerUser, rate_limit_per_user: data.rateLimitPerUser,

View File

@@ -141,8 +141,7 @@ class User extends Base {
* @readonly * @readonly
*/ */
get lastMessage() { get lastMessage() {
const channel = this.client.channels.cache.get(this.lastMessageChannelID); return this.client.channels.resolve(this.lastMessageChannelID)?.messages.resolve(this.lastMessageID) ?? null;
return (channel && channel.messages.cache.get(this.lastMessageID)) || null;
} }
/** /**
@@ -185,7 +184,7 @@ class User extends Base {
* @returns {string} * @returns {string}
*/ */
displayAvatarURL(options) { displayAvatarURL(options) {
return this.avatarURL(options) || this.defaultAvatarURL; return this.avatarURL(options) ?? this.defaultAvatarURL;
} }
/** /**
@@ -203,8 +202,7 @@ class User extends Base {
* @returns {boolean} * @returns {boolean}
*/ */
typingIn(channel) { typingIn(channel) {
channel = this.client.channels.resolve(channel); return this.client.channels.resolve(channel)._typing.has(this.id);
return channel._typing.has(this.id);
} }
/** /**
@@ -223,8 +221,7 @@ class User extends Base {
* @returns {number} * @returns {number}
*/ */
typingDurationIn(channel) { typingDurationIn(channel) {
channel = this.client.channels.resolve(channel); return this.client.channels.resolve(channel)._typing.get(this.id)?.elapsedTime ?? -1;
return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1;
} }
/** /**
@@ -233,7 +230,7 @@ class User extends Base {
* @readonly * @readonly
*/ */
get dmChannel() { get dmChannel() {
return this.client.channels.cache.find(c => c.type === 'dm' && c.recipient.id === this.id) || null; return this.client.channels.cache.find(c => c.type === 'dm' && c.recipient.id === this.id) ?? null;
} }
/** /**

View File

@@ -31,42 +31,42 @@ class VoiceState extends Base {
* Whether this member is deafened server-wide * Whether this member is deafened server-wide
* @type {?boolean} * @type {?boolean}
*/ */
this.serverDeaf = 'deaf' in data ? data.deaf : null; this.serverDeaf = data.deaf ?? null;
/** /**
* Whether this member is muted server-wide * Whether this member is muted server-wide
* @type {?boolean} * @type {?boolean}
*/ */
this.serverMute = 'mute' in data ? data.mute : null; this.serverMute = data.mute ?? null;
/** /**
* Whether this member is self-deafened * Whether this member is self-deafened
* @type {?boolean} * @type {?boolean}
*/ */
this.selfDeaf = 'self_deaf' in data ? data.self_deaf : null; this.selfDeaf = data.self_deaf ?? null;
/** /**
* Whether this member is self-muted * Whether this member is self-muted
* @type {?boolean} * @type {?boolean}
*/ */
this.selfMute = 'self_mute' in data ? data.self_mute : null; this.selfMute = data.self_mute ?? null;
/** /**
* Whether this member's camera is enabled * Whether this member's camera is enabled
* @type {?boolean} * @type {?boolean}
*/ */
this.selfVideo = 'self_video' in data ? data.self_video : null; this.selfVideo = data.self_video ?? null;
/** /**
* The session ID of this member's connection * The session ID of this member's connection
* @type {?string} * @type {?string}
*/ */
this.sessionID = 'session_id' in data ? data.session_id : null; this.sessionID = data.session_id ?? null;
/** /**
* Whether this member is streaming using "Go Live" * Whether this member is streaming using "Go Live"
* @type {boolean} * @type {boolean}
*/ */
this.streaming = data.self_stream || false; this.streaming = data.self_stream ?? false;
/** /**
* The ID of the voice or stage channel that this member is in * The ID of the voice or stage channel that this member is in
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.channelID = data.channel_id || null; this.channelID = data.channel_id ?? null;
/** /**
* Whether this member is suppressed from speaking. This property is specific to stage channels only. * Whether this member is suppressed from speaking. This property is specific to stage channels only.
* @type {boolean} * @type {boolean}
@@ -88,7 +88,7 @@ class VoiceState extends Base {
* @readonly * @readonly
*/ */
get member() { get member() {
return this.guild.members.cache.get(this.id) || null; return this.guild.members.cache.get(this.id) ?? null;
} }
/** /**
@@ -97,7 +97,7 @@ class VoiceState extends Base {
* @readonly * @readonly
*/ */
get channel() { get channel() {
return this.guild.channels.cache.get(this.channelID) || null; return this.guild.channels.cache.get(this.channelID) ?? null;
} }
/** /**
@@ -125,7 +125,7 @@ class VoiceState extends Base {
* @returns {Promise<GuildMember>} * @returns {Promise<GuildMember>}
*/ */
setMute(mute, reason) { setMute(mute, reason) {
return this.member ? this.member.edit({ mute }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER')); return this.member?.edit({ mute }, reason) ?? Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
} }
/** /**
@@ -135,7 +135,7 @@ class VoiceState extends Base {
* @returns {Promise<GuildMember>} * @returns {Promise<GuildMember>}
*/ */
setDeaf(deaf, reason) { setDeaf(deaf, reason) {
return this.member ? this.member.edit({ deaf }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER')); return this.member?.edit({ deaf }, reason) ?? Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
} }
/** /**
@@ -155,9 +155,7 @@ class VoiceState extends Base {
* @returns {Promise<GuildMember>} * @returns {Promise<GuildMember>}
*/ */
setChannel(channel, reason) { setChannel(channel, reason) {
return this.member return this.member?.edit({ channel }, reason) ?? Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
? this.member.edit({ channel }, reason)
: Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER'));
} }
/** /**
@@ -173,8 +171,7 @@ class VoiceState extends Base {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async setRequestToSpeak(request) { async setRequestToSpeak(request) {
const channel = this.channel; if (this.channel?.type !== 'stage') throw new Error('VOICE_NOT_STAGE_CHANNEL');
if (channel?.type !== 'stage') throw new Error('VOICE_NOT_STAGE_CHANNEL');
if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN'); if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
@@ -206,8 +203,7 @@ class VoiceState extends Base {
async setSuppressed(suppressed) { async setSuppressed(suppressed) {
if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed'); if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
const channel = this.channel; if (this.channel?.type !== 'stage') throw new Error('VOICE_NOT_STAGE_CHANNEL');
if (channel?.type !== 'stage') throw new Error('VOICE_NOT_STAGE_CHANNEL');
const target = this.client.user.id === this.id ? '@me' : this.id; const target = this.client.user.id === this.id ? '@me' : this.id;

View File

@@ -34,7 +34,7 @@ class Webhook {
* @name Webhook#token * @name Webhook#token
* @type {?string} * @type {?string}
*/ */
Object.defineProperty(this, 'token', { value: data.token || null, writable: true, configurable: true }); Object.defineProperty(this, 'token', { value: data.token ?? null, writable: true, configurable: true });
/** /**
* The avatar for the webhook * The avatar for the webhook
@@ -175,11 +175,7 @@ class Webhook {
query: { thread_id: messagePayload.options.threadID, wait: true }, query: { thread_id: messagePayload.options.threadID, wait: true },
auth: false, auth: false,
}) })
.then(d => { .then(d => this.client.channels?.cache.get(d.channel_id)?.messages.add(d, false) ?? d);
const channel = this.client.channels ? this.client.channels.cache.get(d.channel_id) : undefined;
if (!channel) return d;
return channel.messages.add(d, false);
});
} }
/** /**

View File

@@ -212,11 +212,11 @@ class Collector extends EventEmitter {
resetTimer({ time, idle } = {}) { resetTimer({ time, idle } = {}) {
if (this._timeout) { if (this._timeout) {
this.client.clearTimeout(this._timeout); this.client.clearTimeout(this._timeout);
this._timeout = this.client.setTimeout(() => this.stop('time'), time || this.options.time); this._timeout = this.client.setTimeout(() => this.stop('time'), time ?? this.options.time);
} }
if (this._idletimeout) { if (this._idletimeout) {
this.client.clearTimeout(this._idletimeout); this.client.clearTimeout(this._idletimeout);
this._idletimeout = this.client.setTimeout(() => this.stop('idle'), idle || this.options.idle); this._idletimeout = this.client.setTimeout(() => this.stop('idle'), idle ?? this.options.idle);
} }
} }

View File

@@ -39,7 +39,7 @@ class TextBasedChannel {
* @readonly * @readonly
*/ */
get lastMessage() { get lastMessage() {
return this.messages.cache.get(this.lastMessageID) || null; return this.messages.resolve(this.lastMessageID);
} }
/** /**
@@ -185,7 +185,7 @@ class TextBasedChannel {
if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT'); if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT');
if (this.client.user._typing.has(this.id)) { if (this.client.user._typing.has(this.id)) {
const entry = this.client.user._typing.get(this.id); const entry = this.client.user._typing.get(this.id);
entry.count = count || entry.count + 1; entry.count = count ?? entry.count + 1;
return entry.promise; return entry.promise;
} }
@@ -193,7 +193,7 @@ class TextBasedChannel {
entry.promise = new Promise((resolve, reject) => { entry.promise = new Promise((resolve, reject) => {
const endpoint = this.client.api.channels[this.id].typing; const endpoint = this.client.api.channels[this.id].typing;
Object.assign(entry, { Object.assign(entry, {
count: count || 1, count: count ?? 1,
interval: this.client.setInterval(() => { interval: this.client.setInterval(() => {
endpoint.post().catch(error => { endpoint.post().catch(error => {
this.client.clearInterval(entry.interval); this.client.clearInterval(entry.interval);
@@ -252,8 +252,7 @@ class TextBasedChannel {
* @readonly * @readonly
*/ */
get typingCount() { get typingCount() {
if (this.client.user._typing.has(this.id)) return this.client.user._typing.get(this.id).count; return this.client.user._typing.get(this.id)?.count ?? 0;
return 0;
} }
/** /**
@@ -294,7 +293,7 @@ class TextBasedChannel {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const collector = this.createMessageCollector(options); const collector = this.createMessageCollector(options);
collector.once('end', (collection, reason) => { collector.once('end', (collection, reason) => {
if (options.errors && options.errors.includes(reason)) { if (options.errors?.includes(reason)) {
reject(collection); reject(collection);
} else { } else {
resolve(collection); resolve(collection);
@@ -355,7 +354,7 @@ class TextBasedChannel {
*/ */
async bulkDelete(messages, filterOld = false) { async bulkDelete(messages, filterOld = false) {
if (Array.isArray(messages) || messages instanceof Collection) { if (Array.isArray(messages) || messages instanceof Collection) {
let messageIDs = messages instanceof Collection ? messages.keyArray() : messages.map(m => m.id || m); let messageIDs = messages instanceof Collection ? messages.keyArray() : messages.map(m => m.id ?? m);
if (filterOld) { if (filterOld) {
messageIDs = messageIDs.filter(id => Date.now() - SnowflakeUtil.deconstruct(id).timestamp < 1209600000); messageIDs = messageIDs.filter(id => Date.now() - SnowflakeUtil.deconstruct(id).timestamp < 1209600000);
} }

View File

@@ -85,7 +85,7 @@ class Structures {
if (!(extended.prototype instanceof structures[structure])) { if (!(extended.prototype instanceof structures[structure])) {
const prototype = Object.getPrototypeOf(extended); const prototype = Object.getPrototypeOf(extended);
const received = `${extended.name || 'unnamed'}${prototype.name ? ` extends ${prototype.name}` : ''}`; const received = `${extended.name ?? 'unnamed'}${prototype.name ? ` extends ${prototype.name}` : ''}`;
throw new Error( throw new Error(
'The class/prototype returned from the extender function must extend the existing structure class/prototype' + 'The class/prototype returned from the extender function must extend the existing structure class/prototype' +
` (received function ${received}; expected extension of ${structures[structure].name}).`, ` (received function ${received}; expected extension of ${structures[structure].name}).`,

View File

@@ -289,9 +289,8 @@ class Util {
static parseEmoji(text) { static parseEmoji(text) {
if (text.includes('%')) text = decodeURIComponent(text); if (text.includes('%')) text = decodeURIComponent(text);
if (!text.includes(':')) return { animated: false, name: text, id: null }; if (!text.includes(':')) return { animated: false, name: text, id: null };
const m = text.match(/<?(?:(a):)?(\w{2,32}):(\d{17,19})?>?/); const match = text.match(/<?(?:(a):)?(\w{2,32}):(\d{17,19})?>?/);
if (!m) return null; return match && { animated: Boolean(match[1]), name: match[2], id: match[3] ?? null };
return { animated: Boolean(m[1]), name: m[2], id: m[3] || null };
} }
/** /**
@@ -460,7 +459,7 @@ class Util {
if (typeof color === 'string') { if (typeof color === 'string') {
if (color === 'RANDOM') return Math.floor(Math.random() * (0xffffff + 1)); if (color === 'RANDOM') return Math.floor(Math.random() * (0xffffff + 1));
if (color === 'DEFAULT') return 0; if (color === 'DEFAULT') return 0;
color = Colors[color] || parseInt(color.replace('#', ''), 16); color = Colors[color] ?? parseInt(color.replace('#', ''), 16);
} else if (Array.isArray(color)) { } else if (Array.isArray(color)) {
color = (color[0] << 16) + (color[1] << 8) + color[2]; color = (color[0] << 16) + (color[1] << 8) + color[2];
} }
@@ -511,7 +510,7 @@ class Util {
* @private * @private
*/ */
static basename(path, ext) { static basename(path, ext) {
let res = parse(path); const res = parse(path);
return ext && res.ext.startsWith(ext) ? res.name : res.base.split('?')[0]; return ext && res.ext.startsWith(ext) ? res.name : res.base.split('?')[0];
} }

View File

@@ -40,7 +40,7 @@ client.on('message', m => {
if (!m.guild) return; if (!m.guild) return;
if (m.author.id !== '66564597481480192') return; if (m.author.id !== '66564597481480192') return;
if (m.content.startsWith('/join')) { if (m.content.startsWith('/join')) {
const channel = m.guild.channels.cache.get(m.content.split(' ')[1]) || m.member.voice.channel; const channel = m.guild.channels.cache.get(m.content.split(' ')[1]) ?? m.member.voice.channel;
if (channel && channel.type === 'voice') { if (channel && channel.type === 'voice') {
channel.join().then(conn => { channel.join().then(conn => {
conn.receiver.createStream(m.author, true).on('data', b => console.log(b.toString())); conn.receiver.createStream(m.author, true).on('data', b => console.log(b.toString()));