refactor(util): make utility functions top level (#8052)

* refactor(util): make functions top level

* types: make channel typeguards more strict

* chore: make requested changes
This commit is contained in:
Suneet Tipirneni
2022-06-13 14:04:53 -04:00
committed by GitHub
parent 51eadf3737
commit e53d162198
39 changed files with 808 additions and 788 deletions

View File

@@ -4,7 +4,7 @@ const EventEmitter = require('node:events');
const { REST } = require('@discordjs/rest');
const { TypeError } = require('../errors');
const Options = require('../util/Options');
const Util = require('../util/Util');
const { mergeDefault, flatten } = require('../util/Util');
/**
* The base class for all clients.
@@ -22,7 +22,7 @@ class BaseClient extends EventEmitter {
* The options the client was instantiated with
* @type {ClientOptions}
*/
this.options = Util.mergeDefault(Options.createDefault(), options);
this.options = mergeDefault(Options.createDefault(), options);
/**
* The REST manager of the client
@@ -63,7 +63,7 @@ class BaseClient extends EventEmitter {
}
toJSON(...props) {
return Util.flatten(this, { domain: false }, ...props);
return flatten(this, { domain: false }, ...props);
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
const Action = require('./Action');
const { Channel } = require('../../structures/Channel');
const { createChannel } = require('../../util/Channels');
class ChannelUpdateAction extends Action {
handle(data) {
@@ -12,7 +12,7 @@ class ChannelUpdateAction extends Action {
const old = channel._update(data);
if (channel.type !== data.type) {
const newChannel = Channel.create(this.client, data, channel.guild);
const newChannel = createChannel(this.client, data, channel.guild);
for (const [id, message] of channel.messages.cache) newChannel.messages.cache.set(id, message);
channel = newChannel;
this.client.channels.cache.set(channel.id, channel);

View File

@@ -34,7 +34,7 @@ exports.Sweepers = require('./util/Sweepers');
exports.SystemChannelFlagsBitField = require('./util/SystemChannelFlagsBitField');
exports.ThreadMemberFlagsBitField = require('./util/ThreadMemberFlagsBitField');
exports.UserFlagsBitField = require('./util/UserFlagsBitField');
exports.Util = require('./util/Util');
__exportStar(require('./util/Util.js'), exports);
exports.version = require('../package.json').version;
// Managers

View File

@@ -4,6 +4,7 @@ const process = require('node:process');
const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager');
const { Channel } = require('../structures/Channel');
const { createChannel } = require('../util/Channels');
const { ThreadChannelTypes } = require('../util/Constants');
const Events = require('../util/Events');
@@ -46,7 +47,7 @@ class ChannelManager extends CachedManager {
return existing;
}
const channel = Channel.create(this.client, data, guild, { allowUnknownGuild, fromInteraction });
const channel = createChannel(this.client, data, guild, { allowUnknownGuild, fromInteraction });
if (!channel) {
this.client.emit(Events.Debug, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`);

View File

@@ -12,7 +12,7 @@ const ThreadChannel = require('../structures/ThreadChannel');
const Webhook = require('../structures/Webhook');
const { ThreadChannelTypes } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Util = require('../util/Util');
const { setPosition } = require('../util/Util');
let cacheWarningEmitted = false;
@@ -296,7 +296,7 @@ class GuildChannelManager extends CachedManager {
async setPosition(channel, position, { relative, reason } = {}) {
channel = this.resolve(channel);
if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
const updatedChannels = await Util.setPosition(
const updatedChannels = await setPosition(
channel,
position,
relative,

View File

@@ -7,7 +7,7 @@ const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const { Message } = require('../structures/Message');
const MessagePayload = require('../structures/MessagePayload');
const Util = require('../util/Util');
const { resolvePartialEmoji } = require('../util/Util');
/**
* Manages API methods for Messages and holds their cache.
@@ -223,7 +223,7 @@ class MessageManager extends CachedManager {
message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
emoji = Util.resolvePartialEmoji(emoji);
emoji = resolvePartialEmoji(emoji);
if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable');
const emojiId = emoji.id

View File

@@ -8,8 +8,7 @@ const { TypeError } = require('../errors');
const { Role } = require('../structures/Role');
const DataResolver = require('../util/DataResolver');
const PermissionsBitField = require('../util/PermissionsBitField');
const { resolveColor } = require('../util/Util');
const Util = require('../util/Util');
const { setPosition, resolveColor } = require('../util/Util');
let cacheWarningEmitted = false;
@@ -246,7 +245,7 @@ class RoleManager extends CachedManager {
async setPosition(role, position, { relative, reason } = {}) {
role = this.resolve(role);
if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable');
const updatedRoles = await Util.setPosition(
const updatedRoles = await setPosition(
role,
position,
relative,

View File

@@ -6,7 +6,7 @@ const process = require('node:process');
const { setTimeout, clearTimeout } = require('node:timers');
const { setTimeout: sleep } = require('node:timers/promises');
const { Error } = require('../errors');
const Util = require('../util/Util');
const { makeError, makePlainError } = require('../util/Util');
let childProcess = null;
let Worker = null;
@@ -252,7 +252,7 @@ class Shard extends EventEmitter {
this.decrementMaxListeners(child);
this._fetches.delete(prop);
if (!message._error) resolve(message._result);
else reject(Util.makeError(message._error));
else reject(makeError(message._error));
};
this.incrementMaxListeners(child);
@@ -295,7 +295,7 @@ class Shard extends EventEmitter {
this.decrementMaxListeners(child);
this._evals.delete(_eval);
if (!message._error) resolve(message._result);
else reject(Util.makeError(message._error));
else reject(makeError(message._error));
};
this.incrementMaxListeners(child);
@@ -358,7 +358,7 @@ class Shard extends EventEmitter {
const resp = { _sFetchProp: message._sFetchProp, _sFetchPropShard: message._sFetchPropShard };
this.manager.fetchClientValues(message._sFetchProp, message._sFetchPropShard).then(
results => this.send({ ...resp, _result: results }),
err => this.send({ ...resp, _error: Util.makePlainError(err) }),
err => this.send({ ...resp, _error: makePlainError(err) }),
);
return;
}
@@ -368,7 +368,7 @@ class Shard extends EventEmitter {
const resp = { _sEval: message._sEval, _sEvalShard: message._sEvalShard };
this.manager._performOnShards('eval', [message._sEval], message._sEvalShard).then(
results => this.send({ ...resp, _result: results }),
err => this.send({ ...resp, _error: Util.makePlainError(err) }),
err => this.send({ ...resp, _error: makePlainError(err) }),
);
return;
}

View File

@@ -3,7 +3,7 @@
const process = require('node:process');
const { Error } = require('../errors');
const Events = require('../util/Events');
const Util = require('../util/Util');
const { makeError, makePlainError } = require('../util/Util');
/**
* Helper class for sharded clients spawned as a child process/worker, such as from a {@link ShardingManager}.
@@ -113,7 +113,7 @@ class ShardClientUtil {
parent.removeListener('message', listener);
this.decrementMaxListeners(parent);
if (!message._error) resolve(message._result);
else reject(Util.makeError(message._error));
else reject(makeError(message._error));
};
this.incrementMaxListeners(parent);
parent.on('message', listener);
@@ -151,7 +151,7 @@ class ShardClientUtil {
parent.removeListener('message', listener);
this.decrementMaxListeners(parent);
if (!message._error) resolve(message._result);
else reject(Util.makeError(message._error));
else reject(makeError(message._error));
};
this.incrementMaxListeners(parent);
parent.on('message', listener);
@@ -187,13 +187,13 @@ class ShardClientUtil {
for (const prop of props) value = value[prop];
this._respond('fetchProp', { _fetchProp: message._fetchProp, _result: value });
} catch (err) {
this._respond('fetchProp', { _fetchProp: message._fetchProp, _error: Util.makePlainError(err) });
this._respond('fetchProp', { _fetchProp: message._fetchProp, _error: makePlainError(err) });
}
} else if (message._eval) {
try {
this._respond('eval', { _eval: message._eval, _result: await this.client._eval(message._eval) });
} catch (err) {
this._respond('eval', { _eval: message._eval, _error: Util.makePlainError(err) });
this._respond('eval', { _eval: message._eval, _error: makePlainError(err) });
}
}
}

View File

@@ -8,7 +8,7 @@ const { setTimeout: sleep } = require('node:timers/promises');
const { Collection } = require('@discordjs/collection');
const Shard = require('./Shard');
const { Error, TypeError, RangeError } = require('../errors');
const Util = require('../util/Util');
const { mergeDefault, fetchRecommendedShards } = require('../util/Util');
/**
* This is a utility class that makes multi-process sharding of a bot an easy and painless experience.
@@ -47,7 +47,7 @@ class ShardingManager extends EventEmitter {
*/
constructor(file, options = {}) {
super();
options = Util.mergeDefault(
options = mergeDefault(
{
totalShards: 'auto',
mode: 'process',
@@ -183,7 +183,7 @@ class ShardingManager extends EventEmitter {
async spawn({ amount = this.totalShards, delay = 5500, timeout = 30_000 } = {}) {
// Obtain/verify the number of shards to spawn
if (amount === 'auto') {
amount = await Util.fetchRecommendedShards(this.token);
amount = await fetchRecommendedShards(this.token);
} else {
if (typeof amount !== 'number' || isNaN(amount)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'Amount of shards', 'a number.');

View File

@@ -2,7 +2,7 @@
const { isJSONEncodable } = require('@discordjs/builders');
const Component = require('./Component');
const Components = require('../util/Components');
const { createComponent } = require('../util/Components');
/**
* Represents an action row
@@ -17,7 +17,7 @@ class ActionRow extends Component {
* @type {Component[]}
* @readonly
*/
this.components = components.map(c => Components.createComponent(c));
this.components = components.map(c => createComponent(c));
}
/**

View File

@@ -1,8 +1,8 @@
'use strict';
const { ActionRowBuilder: BuildersActionRow, ComponentBuilder, isJSONEncodable } = require('@discordjs/builders');
const Components = require('../util/Components');
const Transformers = require('../util/Transformers');
const { createComponentBuilder } = require('../util/Components');
const { toSnakeCase } = require('../util/Transformers');
/**
* Represents an action row builder.
@@ -11,8 +11,8 @@ const Transformers = require('../util/Transformers');
class ActionRowBuilder extends BuildersActionRow {
constructor({ components, ...data } = {}) {
super({
...Transformers.toSnakeCase(data),
components: components?.map(c => (c instanceof ComponentBuilder ? c : Components.createComponentBuilder(c))),
...toSnakeCase(data),
components: components?.map(c => (c instanceof ComponentBuilder ? c : createComponentBuilder(c))),
});
}

View File

@@ -1,6 +1,6 @@
'use strict';
const Util = require('../util/Util');
const { basename, flatten } = require('../util/Util');
/**
* @typedef {Object} AttachmentPayload
@@ -107,11 +107,11 @@ class Attachment {
* @readonly
*/
get spoiler() {
return Util.basename(this.url ?? this.name).startsWith('SPOILER_');
return basename(this.url ?? this.name).startsWith('SPOILER_');
}
toJSON() {
return Util.flatten(this);
return flatten(this);
}
}

View File

@@ -1,6 +1,6 @@
'use strict';
const Util = require('../util/Util');
const { basename, flatten } = require('../util/Util');
/**
* Represents an attachment builder
@@ -82,11 +82,11 @@ class AttachmentBuilder {
* @readonly
*/
get spoiler() {
return Util.basename(this.name).startsWith('SPOILER_');
return basename(this.name).startsWith('SPOILER_');
}
toJSON() {
return Util.flatten(this);
return flatten(this);
}
/**

View File

@@ -1,6 +1,6 @@
'use strict';
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* Represents a data model that is identifiable by a Snowflake (i.e. Discord API data models).
@@ -32,7 +32,7 @@ class Base {
}
toJSON(...props) {
return Util.flatten(this, ...props);
return flatten(this, ...props);
}
valueOf() {

View File

@@ -1,8 +1,8 @@
'use strict';
const { ButtonBuilder: BuildersButton, isJSONEncodable } = require('@discordjs/builders');
const Transformers = require('../util/Transformers');
const Util = require('../util/Util');
const { toSnakeCase } = require('../util/Transformers');
const { parseEmoji } = require('../util/Util');
/**
* Represents a button builder.
@@ -10,9 +10,7 @@ const Util = require('../util/Util');
*/
class ButtonBuilder extends BuildersButton {
constructor({ emoji, ...data } = {}) {
super(
Transformers.toSnakeCase({ ...data, emoji: emoji && typeof emoji === 'string' ? Util.parseEmoji(emoji) : emoji }),
);
super(toSnakeCase({ ...data, emoji: emoji && typeof emoji === 'string' ? parseEmoji(emoji) : emoji }));
}
/**
@@ -22,7 +20,7 @@ class ButtonBuilder extends BuildersButton {
*/
setEmoji(emoji) {
if (typeof emoji === 'string') {
return super.setEmoji(Util.parseEmoji(emoji));
return super.setEmoji(parseEmoji(emoji));
}
return super.setEmoji(emoji);
}

View File

@@ -4,14 +4,6 @@ const { DiscordSnowflake } = require('@sapphire/snowflake');
const { ChannelType, Routes } = require('discord-api-types/v10');
const Base = require('./Base');
const { ThreadChannelTypes } = require('../util/Constants');
let CategoryChannel;
let DMChannel;
let NewsChannel;
let StageChannel;
let TextChannel;
let ThreadChannel;
let VoiceChannel;
let DirectoryChannel;
/**
* Represents any channel on Discord.
@@ -142,66 +134,6 @@ class Channel extends Base {
return 'bitrate' in this;
}
static create(client, data, guild, { allowUnknownGuild, fromInteraction } = {}) {
CategoryChannel ??= require('./CategoryChannel');
DMChannel ??= require('./DMChannel');
NewsChannel ??= require('./NewsChannel');
StageChannel ??= require('./StageChannel');
TextChannel ??= require('./TextChannel');
ThreadChannel ??= require('./ThreadChannel');
VoiceChannel ??= require('./VoiceChannel');
DirectoryChannel ??= require('./DirectoryChannel');
let channel;
if (!data.guild_id && !guild) {
if ((data.recipients && data.type !== ChannelType.GroupDM) || data.type === ChannelType.DM) {
channel = new DMChannel(client, data);
} else if (data.type === ChannelType.GroupDM) {
const PartialGroupDMChannel = require('./PartialGroupDMChannel');
channel = new PartialGroupDMChannel(client, data);
}
} else {
guild ??= client.guilds.cache.get(data.guild_id);
if (guild || allowUnknownGuild) {
switch (data.type) {
case ChannelType.GuildText: {
channel = new TextChannel(guild, data, client);
break;
}
case ChannelType.GuildVoice: {
channel = new VoiceChannel(guild, data, client);
break;
}
case ChannelType.GuildCategory: {
channel = new CategoryChannel(guild, data, client);
break;
}
case ChannelType.GuildNews: {
channel = new NewsChannel(guild, data, client);
break;
}
case ChannelType.GuildStageVoice: {
channel = new StageChannel(guild, data, client);
break;
}
case ChannelType.GuildNewsThread:
case ChannelType.GuildPublicThread:
case ChannelType.GuildPrivateThread: {
channel = new ThreadChannel(guild, data, client, fromInteraction);
if (!allowUnknownGuild) channel.parent?.threads.cache.set(channel.id, channel);
break;
}
case ChannelType.GuildDirectory:
channel = new DirectoryChannel(guild, data, client);
break;
}
if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel);
}
}
return channel;
}
toJSON(...props) {
return super.toJSON({ createdTimestamp: true }, ...props);
}

View File

@@ -1,8 +1,8 @@
'use strict';
const { EmbedBuilder: BuildersEmbed, isJSONEncodable } = require('@discordjs/builders');
const Transformers = require('../util/Transformers');
const Util = require('../util/Util');
const { toSnakeCase } = require('../util/Transformers');
const { resolveColor } = require('../util/Util');
/**
* Represents an embed builder.
@@ -10,7 +10,7 @@ const Util = require('../util/Util');
*/
class EmbedBuilder extends BuildersEmbed {
constructor(data) {
super(Transformers.toSnakeCase(data));
super(toSnakeCase(data));
}
/**
@@ -19,7 +19,7 @@ class EmbedBuilder extends BuildersEmbed {
* @returns {EmbedBuilder}
*/
setColor(color) {
return super.setColor(color && Util.resolveColor(color));
return super.setColor(color && resolveColor(color));
}
/**

View File

@@ -27,7 +27,7 @@ const VoiceStateManager = require('../managers/VoiceStateManager');
const DataResolver = require('../util/DataResolver');
const Status = require('../util/Status');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
const Util = require('../util/Util');
const { discordSort } = require('../util/Util');
/**
* Represents a guild (or a server) on Discord.
@@ -1253,7 +1253,7 @@ class Guild extends AnonymousGuild {
* @private
*/
_sortedRoles() {
return Util.discordSort(this.roles.cache);
return discordSort(this.roles.cache);
}
/**
@@ -1265,7 +1265,7 @@ class Guild extends AnonymousGuild {
_sortedChannels(channel) {
const category = channel.type === ChannelType.GuildCategory;
const channelTypes = [ChannelType.GuildText, ChannelType.GuildNews];
return Util.discordSort(
return discordSort(
this.channels.cache.filter(
c =>
(channelTypes.includes(channel.type) ? channelTypes.includes(c.type) : c.type === channel.type) &&

View File

@@ -5,7 +5,7 @@ const ApplicationCommand = require('./ApplicationCommand');
const GuildAuditLogsEntry = require('./GuildAuditLogsEntry');
const Integration = require('./Integration');
const Webhook = require('./Webhook');
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* The target type of an entry. Here are the available types:
@@ -92,7 +92,7 @@ class GuildAuditLogs {
}
toJSON() {
return Util.flatten(this);
return flatten(this);
}
}

View File

@@ -9,7 +9,7 @@ const { StageInstance } = require('./StageInstance');
const { Sticker } = require('./Sticker');
const Webhook = require('./Webhook');
const Partials = require('../util/Partials');
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
const Targets = {
All: 'All',
@@ -459,7 +459,7 @@ class GuildAuditLogsEntry {
}
toJSON() {
return Util.flatten(this, { createdTimestamp: true });
return flatten(this, { createdTimestamp: true });
}
}

View File

@@ -20,11 +20,11 @@ const ReactionCollector = require('./ReactionCollector');
const { Sticker } = require('./Sticker');
const { Error } = require('../errors');
const ReactionManager = require('../managers/ReactionManager');
const Components = require('../util/Components');
const { createComponent } = require('../util/Components');
const { NonSystemMessageTypes } = require('../util/Constants');
const MessageFlagsBitField = require('../util/MessageFlagsBitField');
const PermissionsBitField = require('../util/PermissionsBitField');
const Util = require('../util/Util');
const { cleanContent, resolvePartialEmoji } = require('../util/Util');
/**
* Represents a message on Discord.
@@ -146,7 +146,7 @@ class Message extends Base {
* A list of MessageActionRows in the message
* @type {ActionRow[]}
*/
this.components = data.components.map(c => Components.createComponent(c));
this.components = data.components.map(c => createComponent(c));
} else {
this.components = this.components?.slice() ?? [];
}
@@ -441,7 +441,7 @@ class Message extends Base {
*/
get cleanContent() {
// eslint-disable-next-line eqeqeq
return this.content != null ? Util.cleanContent(this.content, this.channel) : null;
return this.content != null ? cleanContent(this.content, this.channel) : null;
}
/**
@@ -737,7 +737,7 @@ class Message extends Base {
user: this.client.user,
channel: this.channel,
message: this,
emoji: Util.resolvePartialEmoji(emoji),
emoji: resolvePartialEmoji(emoji),
},
true,
).reaction;

View File

@@ -1,7 +1,7 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* Keeps track of mentions in a {@link Message}.
@@ -233,7 +233,7 @@ class MessageMentions {
}
toJSON() {
return Util.flatten(this, {
return flatten(this, {
members: true,
channels: true,
});

View File

@@ -7,7 +7,7 @@ const ActionRowBuilder = require('./ActionRowBuilder');
const { RangeError } = require('../errors');
const DataResolver = require('../util/DataResolver');
const MessageFlagsBitField = require('../util/MessageFlagsBitField');
const Util = require('../util/Util');
const { basename, cloneObject, verifyString } = require('../util/Util');
/**
* Represents a message to be sent to the API.
@@ -105,7 +105,7 @@ class MessagePayload {
if (this.options.content === null) {
content = '';
} else if (typeof this.options.content !== 'undefined') {
content = Util.verifyString(this.options.content, RangeError, 'MESSAGE_CONTENT_TYPE', true);
content = verifyString(this.options.content, RangeError, 'MESSAGE_CONTENT_TYPE', true);
}
return content;
@@ -164,7 +164,7 @@ class MessagePayload {
: this.options.allowedMentions;
if (allowedMentions) {
allowedMentions = Util.cloneObject(allowedMentions);
allowedMentions = cloneObject(allowedMentions);
allowedMentions.replied_user = allowedMentions.repliedUser;
delete allowedMentions.repliedUser;
}
@@ -234,11 +234,11 @@ class MessagePayload {
const findName = thing => {
if (typeof thing === 'string') {
return Util.basename(thing);
return basename(thing);
}
if (thing.path) {
return Util.basename(thing.path);
return basename(thing.path);
}
return 'file.jpg';

View File

@@ -4,7 +4,7 @@ const { Routes } = require('discord-api-types/v10');
const GuildEmoji = require('./GuildEmoji');
const ReactionEmoji = require('./ReactionEmoji');
const ReactionUserManager = require('../managers/ReactionUserManager');
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* Represents a reaction to a message.
@@ -114,7 +114,7 @@ class MessageReaction {
}
toJSON() {
return Util.flatten(this, { emoji: 'emojiId', message: 'messageId' });
return flatten(this, { emoji: 'emojiId', message: 'messageId' });
}
_add(user) {

View File

@@ -1,7 +1,7 @@
'use strict';
const { ModalBuilder: BuildersModal, ComponentBuilder, isJSONEncodable } = require('@discordjs/builders');
const Transformers = require('../util/Transformers');
const { toSnakeCase } = require('../util/Transformers');
/**
* Represents a modal builder.
@@ -10,8 +10,8 @@ const Transformers = require('../util/Transformers');
class ModalBuilder extends BuildersModal {
constructor({ components, ...data } = {}) {
super({
...Transformers.toSnakeCase(data),
components: components?.map(c => (c instanceof ComponentBuilder ? c : Transformers.toSnakeCase(c))),
...toSnakeCase(data),
components: components?.map(c => (c instanceof ComponentBuilder ? c : toSnakeCase(c))),
});
}

View File

@@ -3,7 +3,7 @@
const Base = require('./Base');
const { Emoji } = require('./Emoji');
const ActivityFlagsBitField = require('../util/ActivityFlagsBitField');
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* Activity sent in a message.
@@ -132,7 +132,7 @@ class Presence extends Base {
}
toJSON() {
return Util.flatten(this);
return flatten(this);
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
const { Emoji } = require('./Emoji');
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* Represents a limited emoji set used for both custom and unicode emojis. Custom emojis
@@ -20,7 +20,7 @@ class ReactionEmoji extends Emoji {
}
toJSON() {
return Util.flatten(this, { identifier: true });
return flatten(this, { identifier: true });
}
valueOf() {

View File

@@ -1,8 +1,8 @@
'use strict';
const { SelectMenuBuilder: BuildersSelectMenu, isJSONEncodable, normalizeArray } = require('@discordjs/builders');
const Transformers = require('../util/Transformers');
const Util = require('../util/Util');
const { toSnakeCase } = require('../util/Transformers');
const { parseEmoji } = require('../util/Util');
/**
* Class used to build select menu components to be sent through the API
@@ -11,11 +11,11 @@ const Util = require('../util/Util');
class SelectMenuBuilder extends BuildersSelectMenu {
constructor({ options, ...data } = {}) {
super(
Transformers.toSnakeCase({
toSnakeCase({
...data,
options: options?.map(({ emoji, ...option }) => ({
...option,
emoji: emoji && typeof emoji === 'string' ? Util.parseEmoji(emoji) : emoji,
emoji: emoji && typeof emoji === 'string' ? parseEmoji(emoji) : emoji,
})),
}),
);
@@ -30,7 +30,7 @@ class SelectMenuBuilder extends BuildersSelectMenu {
return super.addOptions(
normalizeArray(options).map(({ emoji, ...option }) => ({
...option,
emoji: emoji && typeof emoji === 'string' ? Util.parseEmoji(emoji) : emoji,
emoji: emoji && typeof emoji === 'string' ? parseEmoji(emoji) : emoji,
})),
);
}
@@ -44,7 +44,7 @@ class SelectMenuBuilder extends BuildersSelectMenu {
return super.setOptions(
normalizeArray(options).map(({ emoji, ...option }) => ({
...option,
emoji: emoji && typeof emoji === 'string' ? Util.parseEmoji(emoji) : emoji,
emoji: emoji && typeof emoji === 'string' ? parseEmoji(emoji) : emoji,
})),
);
}

View File

@@ -1,8 +1,8 @@
'use strict';
const { SelectMenuOptionBuilder: BuildersSelectMenuOption, isJSONEncodable } = require('@discordjs/builders');
const Transformers = require('../util/Transformers');
const Util = require('../util/Util');
const { toSnakeCase } = require('../util/Transformers');
const { parseEmoji } = require('../util/Util');
/**
* Represents a select menu option builder.
@@ -11,9 +11,9 @@ const Util = require('../util/Util');
class SelectMenuOptionBuilder extends BuildersSelectMenuOption {
constructor({ emoji, ...data } = {}) {
super(
Transformers.toSnakeCase({
toSnakeCase({
...data,
emoji: emoji && typeof emoji === 'string' ? Util.parseEmoji(emoji) : emoji,
emoji: emoji && typeof emoji === 'string' ? parseEmoji(emoji) : emoji,
}),
);
}
@@ -24,7 +24,7 @@ class SelectMenuOptionBuilder extends BuildersSelectMenuOption {
*/
setEmoji(emoji) {
if (typeof emoji === 'string') {
return super.setEmoji(Util.parseEmoji(emoji));
return super.setEmoji(parseEmoji(emoji));
}
return super.setEmoji(emoji);
}

View File

@@ -1,7 +1,7 @@
'use strict';
const { TextInputBuilder: BuildersTextInput, isJSONEncodable } = require('@discordjs/builders');
const Transformers = require('../util/Transformers');
const { toSnakeCase } = require('../util/Transformers');
/**
* Represents a text input builder.
@@ -9,7 +9,7 @@ const Transformers = require('../util/Transformers');
*/
class TextInputBuilder extends BuildersTextInput {
constructor(data) {
super(Transformers.toSnakeCase(data));
super(toSnakeCase(data));
}
/**

View File

@@ -1,6 +1,6 @@
'use strict';
const Util = require('../util/Util');
const { flatten } = require('../util/Util');
/**
* Represents a Discord voice region for guilds.
@@ -39,7 +39,7 @@ class VoiceRegion {
}
toJSON() {
return Util.flatten(this);
return flatten(this);
}
}

View File

@@ -4,7 +4,7 @@ const EventEmitter = require('node:events');
const { setTimeout, clearTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection');
const { TypeError } = require('../../errors');
const Util = require('../../util/Util');
const { flatten } = require('../../util/Util');
/**
* Filter to be applied to the collector.
@@ -281,7 +281,7 @@ class Collector extends EventEmitter {
}
toJSON() {
return Util.flatten(this);
return flatten(this);
}
/* eslint-disable no-empty-function */

View File

@@ -0,0 +1,72 @@
'use strict';
const { ChannelType } = require('discord-api-types/v10');
const { lazy } = require('./Util');
const getCategoryChannel = lazy(() => require('../structures/CategoryChannel'));
const getDMChannel = lazy(() => require('../structures/DMChannel'));
const getNewsChannel = lazy(() => require('../structures/NewsChannel'));
const getStageChannel = lazy(() => require('../structures/StageChannel'));
const getTextChannel = lazy(() => require('../structures/TextChannel'));
const getThreadChannel = lazy(() => require('../structures/ThreadChannel'));
const getVoiceChannel = lazy(() => require('../structures/VoiceChannel'));
const getDirectoryChannel = lazy(() => require('../structures/DirectoryChannel'));
const getPartialGroupDMChannel = lazy(() => require('../structures/PartialGroupDMChannel'));
// eslint-disable-next-line valid-jsdoc
/**
* @private
*/
function createChannel(client, data, guild, { allowUnknownGuild, fromInteraction } = {}) {
let channel;
if (!data.guild_id && !guild) {
if ((data.recipients && data.type !== ChannelType.GroupDM) || data.type === ChannelType.DM) {
channel = new (getDMChannel())(client, data);
} else if (data.type === ChannelType.GroupDM) {
channel = new (getPartialGroupDMChannel())(client, data);
}
} else {
guild ??= client.guilds.cache.get(data.guild_id);
if (guild || allowUnknownGuild) {
switch (data.type) {
case ChannelType.GuildText: {
channel = new (getTextChannel())(guild, data, client);
break;
}
case ChannelType.GuildVoice: {
channel = new (getVoiceChannel())(guild, data, client);
break;
}
case ChannelType.GuildCategory: {
channel = new (getCategoryChannel())(guild, data, client);
break;
}
case ChannelType.GuildNews: {
channel = new (getNewsChannel())(guild, data, client);
break;
}
case ChannelType.GuildStageVoice: {
channel = new (getStageChannel())(guild, data, client);
break;
}
case ChannelType.GuildNewsThread:
case ChannelType.GuildPublicThread:
case ChannelType.GuildPrivateThread: {
channel = new (getThreadChannel())(guild, data, client, fromInteraction);
if (!allowUnknownGuild) channel.parent?.threads.cache.set(channel.id, channel);
break;
}
case ChannelType.GuildDirectory:
channel = new (getDirectoryChannel())(guild, data, client);
break;
}
if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel);
}
}
return channel;
}
module.exports = {
createChannel,
};

View File

@@ -67,57 +67,55 @@ const { ComponentType } = require('discord-api-types/v10');
* @typedef {APIMessageComponentEmoji|string} ComponentEmojiResolvable
*/
class Components extends null {
/**
* Transforms API data into a component
* @param {APIMessageComponent|Component} data The data to create the component from
* @returns {Component}
*/
static createComponent(data) {
if (data instanceof Component) {
return data;
}
switch (data.type) {
case ComponentType.ActionRow:
return new ActionRow(data);
case ComponentType.Button:
return new ButtonComponent(data);
case ComponentType.SelectMenu:
return new SelectMenuComponent(data);
case ComponentType.TextInput:
return new TextInputComponent(data);
default:
throw new Error(`Found unknown component type: ${data.type}`);
}
/**
* Transforms API data into a component
* @param {APIMessageComponent|Component} data The data to create the component from
* @returns {Component}
*/
function createComponent(data) {
if (data instanceof Component) {
return data;
}
/**
* Transforms API data into a component builder
* @param {APIMessageComponent|ComponentBuilder} data The data to create the component from
* @returns {ComponentBuilder}
*/
static createComponentBuilder(data) {
if (data instanceof ComponentBuilder) {
return data;
}
switch (data.type) {
case ComponentType.ActionRow:
return new ActionRowBuilder(data);
case ComponentType.Button:
return new ButtonBuilder(data);
case ComponentType.SelectMenu:
return new SelectMenuBuilder(data);
case ComponentType.TextInput:
return new TextInputComponent(data);
default:
throw new Error(`Found unknown component type: ${data.type}`);
}
switch (data.type) {
case ComponentType.ActionRow:
return new ActionRow(data);
case ComponentType.Button:
return new ButtonComponent(data);
case ComponentType.SelectMenu:
return new SelectMenuComponent(data);
case ComponentType.TextInput:
return new TextInputComponent(data);
default:
throw new Error(`Found unknown component type: ${data.type}`);
}
}
module.exports = Components;
/**
* Transforms API data into a component builder
* @param {APIMessageComponent|ComponentBuilder} data The data to create the component from
* @returns {ComponentBuilder}
*/
function createComponentBuilder(data) {
if (data instanceof ComponentBuilder) {
return data;
}
switch (data.type) {
case ComponentType.ActionRow:
return new ActionRowBuilder(data);
case ComponentType.Button:
return new ButtonBuilder(data);
case ComponentType.SelectMenu:
return new SelectMenuBuilder(data);
case ComponentType.TextInput:
return new TextInputComponent(data);
default:
throw new Error(`Found unknown component type: ${data.type}`);
}
}
module.exports = { createComponent, createComponentBuilder };
const ActionRow = require('../structures/ActionRow');
const ActionRowBuilder = require('../structures/ActionRowBuilder');

View File

@@ -2,7 +2,7 @@
const process = require('node:process');
const { DefaultRestOptions } = require('@discordjs/rest');
const Transformers = require('./Transformers');
const { toSnakeCase } = require('./Transformers');
/**
* @typedef {Function} CacheFactory
@@ -93,7 +93,7 @@ class Options extends null {
version: 10,
},
rest: DefaultRestOptions,
jsonTransformer: Transformers.toSnakeCase,
jsonTransformer: toSnakeCase,
};
}

View File

@@ -2,19 +2,15 @@
const snakeCase = require('lodash.snakecase');
class Transformers extends null {
/**
* Transforms camel-cased keys into snake cased keys
* @param {*} obj The object to transform
* @returns {*}
*/
static toSnakeCase(obj) {
if (typeof obj !== 'object' || !obj) return obj;
if (Array.isArray(obj)) return obj.map(Transformers.toSnakeCase);
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [snakeCase(key), Transformers.toSnakeCase(value)]),
);
}
/**
* Transforms camel-cased keys into snake cased keys
* @param {*} obj The object to transform
* @returns {*}
*/
function toSnakeCase(obj) {
if (typeof obj !== 'object' || !obj) return obj;
if (Array.isArray(obj)) return obj.map(toSnakeCase);
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [snakeCase(key), toSnakeCase(value)]));
}
module.exports = Transformers;
module.exports = { toSnakeCase };

File diff suppressed because it is too large Load Diff

View File

@@ -119,6 +119,7 @@ import {
LocaleString,
MessageActivityType,
APIAttachment,
APIChannel,
} from 'discord-api-types/v10';
import { ChildProcess } from 'node:child_process';
import { EventEmitter } from 'node:events';
@@ -2613,43 +2614,40 @@ export class UserFlagsBitField extends BitField<UserFlagsString> {
public static resolve(bit?: BitFieldResolvable<UserFlagsString, number>): number;
}
export class Util extends null {
private constructor();
public static basename(path: string, ext?: string): string;
public static cleanContent(str: string, channel: TextBasedChannel): string;
public static cloneObject(obj: unknown): unknown;
public static discordSort<K, V extends { rawPosition: number; id: Snowflake }>(
collection: Collection<K, V>,
): Collection<K, V>;
public static escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string;
public static escapeCodeBlock(text: string): string;
public static escapeInlineCode(text: string): string;
public static escapeBold(text: string): string;
public static escapeItalic(text: string): string;
public static escapeUnderline(text: string): string;
public static escapeStrikethrough(text: string): string;
public static escapeSpoiler(text: string): string;
public static cleanCodeBlockContent(text: string): string;
public static fetchRecommendedShards(token: string, options?: FetchRecommendedShardsOptions): Promise<number>;
public static flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown;
public static makeError(obj: MakeErrorOptions): Error;
public static makePlainError(err: Error): MakeErrorOptions;
public static mergeDefault(def: unknown, given: unknown): unknown;
public static moveElementInArray(array: unknown[], element: unknown, newIndex: number, offset?: boolean): number;
public static parseEmoji(text: string): { animated: boolean; name: string; id: Snowflake | null } | null;
public static resolveColor(color: ColorResolvable): number;
public static resolvePartialEmoji(emoji: EmojiIdentifierResolvable): Partial<APIPartialEmoji> | null;
public static verifyString(data: string, error?: typeof Error, errorMessage?: string, allowEmpty?: boolean): string;
public static setPosition<T extends AnyChannel | Role>(
item: T,
position: number,
relative: boolean,
sorted: Collection<Snowflake, T>,
client: Client,
route: string,
reason?: string,
): Promise<{ id: Snowflake; position: number }[]>;
}
export function basename(path: string, ext?: string): string;
export function cleanContent(str: string, channel: TextBasedChannel): string;
export function cloneObject(obj: unknown): unknown;
export function discordSort<K, V extends { rawPosition: number; id: Snowflake }>(
collection: Collection<K, V>,
): Collection<K, V>;
export function escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string;
export function escapeCodeBlock(text: string): string;
export function escapeInlineCode(text: string): string;
export function escapeBold(text: string): string;
export function escapeItalic(text: string): string;
export function escapeUnderline(text: string): string;
export function escapeStrikethrough(text: string): string;
export function escapeSpoiler(text: string): string;
export function cleanCodeBlockContent(text: string): string;
export function fetchRecommendedShards(token: string, options?: FetchRecommendedShardsOptions): Promise<number>;
export function flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown;
export function makeError(obj: MakeErrorOptions): Error;
export function makePlainError(err: Error): MakeErrorOptions;
export function mergeDefault(def: unknown, given: unknown): unknown;
export function moveElementInArray(array: unknown[], element: unknown, newIndex: number, offset?: boolean): number;
export function parseEmoji(text: string): { animated: boolean; name: string; id: Snowflake | null } | null;
export function resolveColor(color: ColorResolvable): number;
export function resolvePartialEmoji(emoji: EmojiIdentifierResolvable): Partial<APIPartialEmoji> | null;
export function verifyString(data: string, error?: typeof Error, errorMessage?: string, allowEmpty?: boolean): string;
export function setPosition<T extends AnyChannel | Role>(
item: T,
position: number,
relative: boolean,
sorted: Collection<Snowflake, T>,
client: Client,
route: string,
reason?: string,
): Promise<{ id: Snowflake; position: number }[]>;
export interface MappedComponentBuilderTypes {
[ComponentType.Button]: ButtonBuilder;
@@ -2665,19 +2663,24 @@ export interface MappedComponentTypes {
[ComponentType.TextInput]: TextInputComponent;
}
export class Components extends null {
public static createComponent<T extends keyof MappedComponentTypes>(
data: APIMessageComponent & { type: T },
): MappedComponentTypes[T];
public static createComponent<C extends Component>(data: C): C;
public static createComponent(data: APIMessageComponent | Component): Component;
public static createComponentBuilder<T extends keyof MappedComponentBuilderTypes>(
data: APIMessageComponent & { type: T },
): MappedComponentBuilderTypes[T];
public static createComponentBuilder<C extends ComponentBuilder>(data: C): C;
public static createComponentBuilder(data: APIMessageComponent | ComponentBuilder): ComponentBuilder;
export interface CreateChannelOptions {
allowFromUnknownGuild?: boolean;
fromInteraction?: boolean;
}
export function createChannel(client: Client, data: APIChannel, options?: CreateChannelOptions): AnyChannel;
export function createComponent<T extends keyof MappedComponentTypes>(
data: APIMessageComponent & { type: T },
): MappedComponentTypes[T];
export function createComponent<C extends Component>(data: C): C;
export function createComponent(data: APIMessageComponent | Component): Component;
export function createComponentBuilder<T extends keyof MappedComponentBuilderTypes>(
data: APIMessageComponent & { type: T },
): MappedComponentBuilderTypes[T];
export function createComponentBuilder<C extends ComponentBuilder>(data: C): C;
export function createComponentBuilder(data: APIMessageComponent | ComponentBuilder): ComponentBuilder;
export class Formatters extends null {
public static blockQuote: typeof blockQuote;
public static bold: typeof bold;