refactor: replace WSCodes, WSEvents, and InviteScopes with discord-api-types equivalent (#7409)

This commit is contained in:
Almeida
2022-02-09 08:17:45 +00:00
committed by GitHub
parent 3d8c77600b
commit cc25455d2c
8 changed files with 48 additions and 268 deletions

View File

@@ -1,10 +1,10 @@
import { readdir, writeFile } from 'node:fs/promises';
import { Constants } from '../src/index.js';
import { GatewayDispatchEvents } from '../src/index.js';
async function writeWebsocketHandlerImports() {
const lines = ["'use strict';\n", 'const handlers = Object.fromEntries(['];
for (const name of Object.keys(Constants.WSEvents)) {
for (const name of Object.values(GatewayDispatchEvents)) {
lines.push(` ['${name}', require('./${name}')],`);
}

View File

@@ -2,7 +2,7 @@
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const { OAuth2Scopes, Routes } = require('discord-api-types/v9');
const BaseClient = require('./BaseClient');
const ActionsManager = require('./actions/ActionsManager');
const ClientVoiceManager = require('./voice/ClientVoiceManager');
@@ -22,7 +22,6 @@ const StickerPack = require('../structures/StickerPack');
const VoiceRegion = require('../structures/VoiceRegion');
const Webhook = require('../structures/Webhook');
const Widget = require('../structures/Widget');
const { InviteScopes } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Events = require('../util/Events');
const IntentsBitField = require('../util/IntentsBitField');
@@ -388,7 +387,7 @@ class Client extends BaseClient {
/**
* Options for {@link Client#generateInvite}.
* @typedef {Object} InviteGenerationOptions
* @property {InviteScope[]} scopes Scopes that should be requested
* @property {OAuth2Scopes[]} scopes Scopes that should be requested
* @property {PermissionResolvable} [permissions] Permissions to request
* @property {GuildResolvable} [guild] Guild to preselect
* @property {boolean} [disableGuildSelect] Whether to disable the guild selection
@@ -400,7 +399,7 @@ class Client extends BaseClient {
* @returns {string}
* @example
* const link = client.generateInvite({
* scopes: ['applications.commands'],
* scopes: [OAuth2Scopes.ApplicationsCommands],
* });
* console.log(`Generated application invite link: ${link}`);
* @example
@@ -410,7 +409,7 @@ class Client extends BaseClient {
* PermissionFlagsBits.ManageGuild,
* PermissionFlagsBits.MentionEveryone,
* ],
* scopes: ['bot'],
* scopes: [OAuth2Scopes.Bot],
* });
* console.log(`Generated bot invite link: ${link}`);
*/
@@ -429,10 +428,11 @@ class Client extends BaseClient {
if (!Array.isArray(scopes)) {
throw new TypeError('INVALID_TYPE', 'scopes', 'Array of Invite Scopes', true);
}
if (!scopes.some(scope => ['bot', 'applications.commands'].includes(scope))) {
if (!scopes.some(scope => [OAuth2Scopes.Bot, OAuth2Scopes.ApplicationsCommands].includes(scope))) {
throw new TypeError('INVITE_MISSING_SCOPES');
}
const invalidScope = scopes.find(scope => !InviteScopes.includes(scope));
const validScopes = Object.values(OAuth2Scopes);
const invalidScope = scopes.find(scope => !validScopes.includes(scope));
if (invalidScope) {
throw new TypeError('INVALID_ELEMENT', 'Array', 'scopes', invalidScope);
}

View File

@@ -4,31 +4,32 @@ const EventEmitter = require('node:events');
const { setImmediate } = require('node:timers');
const { setTimeout: sleep } = require('node:timers/promises');
const { Collection } = require('@discordjs/collection');
const { Routes, RPCErrorCodes } = require('discord-api-types/v9');
const { GatewayCloseCodes, GatewayDispatchEvents, Routes } = require('discord-api-types/v9');
const WebSocketShard = require('./WebSocketShard');
const PacketHandlers = require('./handlers');
const { Error } = require('../../errors');
const { WSCodes, WSEvents } = require('../../util/Constants');
const Events = require('../../util/Events');
const ShardEvents = require('../../util/ShardEvents');
const Status = require('../../util/Status');
const BeforeReadyWhitelist = [
WSEvents.READY,
WSEvents.RESUMED,
WSEvents.GUILD_CREATE,
WSEvents.GUILD_DELETE,
WSEvents.GUILD_MEMBERS_CHUNK,
WSEvents.GUILD_MEMBER_ADD,
WSEvents.GUILD_MEMBER_REMOVE,
GatewayDispatchEvents.Ready,
GatewayDispatchEvents.Resumed,
GatewayDispatchEvents.GuildCreate,
GatewayDispatchEvents.GuildDelete,
GatewayDispatchEvents.GuildMembersChunk,
GatewayDispatchEvents.GuildMemberAdd,
GatewayDispatchEvents.GuildMemberRemove,
];
const UNRECOVERABLE_CLOSE_CODES = Object.keys(WSCodes).slice(1).map(Number);
const UNRESUMABLE_CLOSE_CODES = [
RPCErrorCodes.UnknownError,
RPCErrorCodes.InvalidPermissions,
RPCErrorCodes.InvalidClientId,
const UNRECOVERABLE_CLOSE_CODES = [
GatewayCloseCodes.AuthenticationFailed,
GatewayCloseCodes.InvalidShard,
GatewayCloseCodes.ShardingRequired,
GatewayCloseCodes.InvalidIntents,
GatewayCloseCodes.DisallowedIntents,
];
const UNRESUMABLE_CLOSE_CODES = [1000, GatewayCloseCodes.AlreadyAuthenticated, GatewayCloseCodes.InvalidSeq];
/**
* The WebSocket manager for this client.
@@ -129,7 +130,7 @@ class WebSocketManager extends EventEmitter {
* @private
*/
async connect() {
const invalidToken = new Error(WSCodes[4004]);
const invalidToken = new Error(GatewayCloseCodes[GatewayCloseCodes.AuthenticationFailed]);
const {
url: gatewayURL,
shards: recommendedShards,
@@ -201,7 +202,7 @@ class WebSocketManager extends EventEmitter {
* @param {number} id The shard id that disconnected
*/
this.client.emit(Events.ShardDisconnect, event, shard.id);
this.debug(WSCodes[event.code], shard);
this.debug(GatewayCloseCodes[event.code], shard);
return;
}
@@ -250,7 +251,7 @@ class WebSocketManager extends EventEmitter {
await shard.connect();
} catch (error) {
if (error?.code && UNRECOVERABLE_CLOSE_CODES.includes(error.code)) {
throw new Error(WSCodes[error.code]);
throw new Error(GatewayCloseCodes[error.code]);
// Undefined if session is invalid, error event for regular closes
} else if (!error || error.code) {
this.debug('Failed to connect to the gateway, requeueing...', shard);

View File

@@ -2,9 +2,8 @@
const EventEmitter = require('node:events');
const { setTimeout, setInterval, clearTimeout, clearInterval } = require('node:timers');
const { GatewayIntentBits, GatewayOpcodes } = require('discord-api-types/v9');
const { GatewayDispatchEvents, GatewayIntentBits, GatewayOpcodes } = require('discord-api-types/v9');
const WebSocket = require('../../WebSocket');
const { WSEvents } = require('../../util/Constants');
const Events = require('../../util/Events');
const IntentsBitField = require('../../util/IntentsBitField');
const ShardEvents = require('../../util/ShardEvents');
@@ -380,7 +379,7 @@ class WebSocketShard extends EventEmitter {
}
switch (packet.t) {
case WSEvents.READY:
case GatewayDispatchEvents.Ready:
/**
* Emitted when the shard receives the READY payload and is now waiting for guilds
* @event WebSocketShard#ready
@@ -394,7 +393,7 @@ class WebSocketShard extends EventEmitter {
this.lastHeartbeatAcked = true;
this.sendHeartbeat('ReadyHeartbeat');
break;
case WSEvents.RESUMED: {
case GatewayDispatchEvents.Resumed: {
/**
* Emitted when the shard resumes successfully
* @event WebSocketShard#resumed
@@ -446,7 +445,7 @@ class WebSocketShard extends EventEmitter {
break;
default:
this.manager.handlePacket(packet, this);
if (this.status === Status.WaitingForGuilds && packet.t === WSEvents.GUILD_CREATE) {
if (this.status === Status.WaitingForGuilds && packet.t === GatewayDispatchEvents.GuildCreate) {
this.expectedGuilds.delete(packet.d.id);
this.checkReady();
}

View File

@@ -148,4 +148,10 @@ const Messages = {
SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function',
};
Messages.AuthenticationFailed = Messages.TOKEN_INVALID;
Messages.InvalidShard = Messages.SHARDING_INVALID;
Messages.ShardingRequired = Messages.SHARDING_REQUIRED;
Messages.InvalidIntents = Messages.INVALID_INTENTS;
Messages.DisallowedIntents = Messages.DISALLOWED_INTENTS;
for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@@ -164,6 +164,8 @@ exports.ApplicationCommandPermissionType = require('discord-api-types/v9').Appli
exports.ButtonStyle = require('discord-api-types/v9').ButtonStyle;
exports.ChannelType = require('discord-api-types/v9').ChannelType;
exports.ComponentType = require('discord-api-types/v9').ComponentType;
exports.GatewayCloseCodes = require('discord-api-types/v9').GatewayCloseCodes;
exports.GatewayDispatchEvents = require('discord-api-types/v9').GatewayDispatchEvents;
exports.GatewayIntentBits = require('discord-api-types/v9').GatewayIntentBits;
exports.GatewayOpcodes = require('discord-api-types/v9').GatewayOpcodes;
exports.GuildFeature = require('discord-api-types/v9').GuildFeature;
@@ -180,6 +182,7 @@ exports.InteractionResponseType = require('discord-api-types/v9').InteractionRes
exports.InviteTargetType = require('discord-api-types/v9').InviteTargetType;
exports.MessageType = require('discord-api-types/v9').MessageType;
exports.MessageFlags = require('discord-api-types/v9').MessageFlags;
exports.OAuth2Scopes = require('discord-api-types/v9').OAuth2Scopes;
exports.PermissionFlagsBits = require('discord-api-types/v9').PermissionFlagsBits;
exports.RESTJSONErrorCodes = require('discord-api-types/v9').RESTJSONErrorCodes;
exports.StageInstancePrivacyLevel = require('discord-api-types/v9').StageInstancePrivacyLevel;

View File

@@ -6,160 +6,6 @@ const Package = (exports.Package = require('../../package.json'));
exports.UserAgent = `DiscordBot (${Package.homepage}, ${Package.version}) Node.js/${process.version}`;
exports.WSCodes = {
1000: 'WS_CLOSE_REQUESTED',
4004: 'TOKEN_INVALID',
4010: 'SHARDING_INVALID',
4011: 'SHARDING_REQUIRED',
4013: 'INVALID_INTENTS',
4014: 'DISALLOWED_INTENTS',
};
/**
* The type of a WebSocket message event, e.g. `MESSAGE_CREATE`. Here are the available events:
* * READY
* * RESUMED
* * GUILD_CREATE
* * GUILD_DELETE
* * GUILD_UPDATE
* * INVITE_CREATE
* * INVITE_DELETE
* * GUILD_MEMBER_ADD
* * GUILD_MEMBER_REMOVE
* * GUILD_MEMBER_UPDATE
* * GUILD_MEMBERS_CHUNK
* * GUILD_INTEGRATIONS_UPDATE
* * GUILD_ROLE_CREATE
* * GUILD_ROLE_DELETE
* * GUILD_ROLE_UPDATE
* * GUILD_BAN_ADD
* * GUILD_BAN_REMOVE
* * GUILD_EMOJIS_UPDATE
* * CHANNEL_CREATE
* * CHANNEL_DELETE
* * CHANNEL_UPDATE
* * CHANNEL_PINS_UPDATE
* * MESSAGE_CREATE
* * MESSAGE_DELETE
* * MESSAGE_UPDATE
* * MESSAGE_DELETE_BULK
* * MESSAGE_REACTION_ADD
* * MESSAGE_REACTION_REMOVE
* * MESSAGE_REACTION_REMOVE_ALL
* * MESSAGE_REACTION_REMOVE_EMOJI
* * THREAD_CREATE
* * THREAD_UPDATE
* * THREAD_DELETE
* * THREAD_LIST_SYNC
* * THREAD_MEMBER_UPDATE
* * THREAD_MEMBERS_UPDATE
* * USER_UPDATE
* * PRESENCE_UPDATE
* * TYPING_START
* * VOICE_STATE_UPDATE
* * VOICE_SERVER_UPDATE
* * WEBHOOKS_UPDATE
* * INTERACTION_CREATE
* * STAGE_INSTANCE_CREATE
* * STAGE_INSTANCE_UPDATE
* * STAGE_INSTANCE_DELETE
* * GUILD_STICKERS_UPDATE
* * GUILD_SCHEDULED_EVENT_CREATE
* * GUILD_SCHEDULED_EVENT_UPDATE
* * GUILD_SCHEDULED_EVENT_DELETE
* * GUILD_SCHEDULED_EVENT_USER_ADD
* * GUILD_SCHEDULED_EVENT_USER_REMOVE
* @typedef {string} WSEventType
* @see {@link https://discord.com/developers/docs/topics/gateway#commands-and-events-gateway-events}
*/
exports.WSEvents = keyMirror([
'READY',
'RESUMED',
'GUILD_CREATE',
'GUILD_DELETE',
'GUILD_UPDATE',
'INVITE_CREATE',
'INVITE_DELETE',
'GUILD_MEMBER_ADD',
'GUILD_MEMBER_REMOVE',
'GUILD_MEMBER_UPDATE',
'GUILD_MEMBERS_CHUNK',
'GUILD_INTEGRATIONS_UPDATE',
'GUILD_ROLE_CREATE',
'GUILD_ROLE_DELETE',
'GUILD_ROLE_UPDATE',
'GUILD_BAN_ADD',
'GUILD_BAN_REMOVE',
'GUILD_EMOJIS_UPDATE',
'CHANNEL_CREATE',
'CHANNEL_DELETE',
'CHANNEL_UPDATE',
'CHANNEL_PINS_UPDATE',
'MESSAGE_CREATE',
'MESSAGE_DELETE',
'MESSAGE_UPDATE',
'MESSAGE_DELETE_BULK',
'MESSAGE_REACTION_ADD',
'MESSAGE_REACTION_REMOVE',
'MESSAGE_REACTION_REMOVE_ALL',
'MESSAGE_REACTION_REMOVE_EMOJI',
'THREAD_CREATE',
'THREAD_UPDATE',
'THREAD_DELETE',
'THREAD_LIST_SYNC',
'THREAD_MEMBER_UPDATE',
'THREAD_MEMBERS_UPDATE',
'USER_UPDATE',
'PRESENCE_UPDATE',
'TYPING_START',
'VOICE_STATE_UPDATE',
'VOICE_SERVER_UPDATE',
'WEBHOOKS_UPDATE',
'INTERACTION_CREATE',
'STAGE_INSTANCE_CREATE',
'STAGE_INSTANCE_UPDATE',
'STAGE_INSTANCE_DELETE',
'GUILD_STICKERS_UPDATE',
'GUILD_SCHEDULED_EVENT_CREATE',
'GUILD_SCHEDULED_EVENT_UPDATE',
'GUILD_SCHEDULED_EVENT_DELETE',
'GUILD_SCHEDULED_EVENT_USER_ADD',
'GUILD_SCHEDULED_EVENT_USER_REMOVE',
]);
/**
* A valid scope to request when generating an invite link.
* <warn>Scopes that require whitelist are not considered valid for this generator</warn>
* * `applications.builds.read`: allows reading build data for a users applications
* * `applications.commands`: allows this bot to create commands in the server
* * `applications.entitlements`: allows reading entitlements for a users applications
* * `applications.store.update`: allows reading and updating of store data for a users applications
* * `bot`: makes the bot join the selected guild
* * `connections`: makes the endpoint for getting a users connections available
* * `email`: allows the `/users/@me` endpoint return with an email
* * `identify`: allows the `/users/@me` endpoint without an email
* * `guilds`: makes the `/users/@me/guilds` endpoint available for a user
* * `guilds.join`: allows the bot to join the user to any guild it is in using Guild#addMember
* * `gdm.join`: allows joining the user to a group dm
* * `webhook.incoming`: generates a webhook to a channel
* @typedef {string} InviteScope
* @see {@link https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes}
*/
exports.InviteScopes = [
'applications.builds.read',
'applications.commands',
'applications.entitlements',
'applications.store.update',
'bot',
'connections',
'email',
'identify',
'guilds',
'guilds.join',
'gdm.join',
'webhook.incoming',
];
/**
* The name of an item to be swept in Sweepers
* * `applicationCommands` - both global and guild commands
@@ -261,14 +107,7 @@ exports.VoiceBasedChannelTypes = [ChannelType.GuildVoice, ChannelType.GuildStage
/* eslint-enable max-len */
function keyMirror(arr) {
let tmp = Object.create(null);
for (const value of arr) tmp[value] = value;
return tmp;
}
/**
* @typedef {Object} Constants Constants that can be used in an enum or object-like way.
* @property {Status} Status The available statuses of the client.
* @property {WSEventType} WSEvents The type of a WebSocket message event.
*/

View File

@@ -52,6 +52,7 @@ import {
ButtonStyle,
ChannelType,
ComponentType,
GatewayDispatchEvents,
GatewayVoiceServerUpdateDispatchData,
GatewayVoiceStateUpdateDispatchData,
GuildFeature,
@@ -62,6 +63,7 @@ import {
InteractionType,
InviteTargetType,
MessageType,
OAuth2Scopes,
RESTPostAPIApplicationCommandsJSONBody,
Snowflake,
StageInstancePrivacyLevel,
@@ -2460,8 +2462,8 @@ export class WebSocketManager extends EventEmitter {
public status: Status;
public readonly ping: number;
public on(event: WSEventType, listener: (data: any, shardId: number) => void): this;
public once(event: WSEventType, listener: (data: any, shardId: number) => void): this;
public on(event: GatewayDispatchEvents, listener: (data: any, shardId: number) => void): this;
public once(event: GatewayDispatchEvents, listener: (data: any, shardId: number) => void): this;
private debug(message: string, shard?: WebSocketShard): void;
private connect(): Promise<void>;
@@ -2604,19 +2606,9 @@ export const Constants: {
[key: string]: unknown;
};
UserAgent: string;
WSCodes: {
1000: 'WS_CLOSE_REQUESTED';
4004: 'TOKEN_INVALID';
4010: 'SHARDING_INVALID';
4011: 'SHARDING_REQUIRED';
};
WSEvents: {
[K in WSEventType]: K;
};
ThreadChannelTypes: ThreadChannelType[];
TextBasedChannelTypes: TextBasedChannelTypes[];
VoiceBasedChannelTypes: VoiceBasedChannelTypes[];
InviteScopes: InviteScope[];
MessageTypes: MessageType[];
SystemMessageTypes: SystemMessageType[];
};
@@ -4421,7 +4413,7 @@ export interface InviteGenerationOptions {
permissions?: PermissionResolvable;
guild?: GuildResolvable;
disableGuildSelect?: boolean;
scopes: InviteScope[];
scopes: OAuth2Scopes[];
}
export type GuildInvitableChannelResolvable =
@@ -4445,20 +4437,6 @@ export interface CreateInviteOptions {
export type InviteResolvable = string;
export type InviteScope =
| 'applications.builds.read'
| 'applications.commands'
| 'applications.entitlements'
| 'applications.store.update'
| 'bot'
| 'connections'
| 'email'
| 'identify'
| 'guilds'
| 'guilds.join'
| 'gdm.join'
| 'webhook.incoming';
export interface LifetimeFilterOptions<K, V> {
excludeFromSweep?: (value: V, key: K, collection: LimitedCollection<K, V>) => boolean;
getComparisonTimestamp?: (value: V, key: K, collection: LimitedCollection<K, V>) => number;
@@ -5040,55 +5018,6 @@ export interface WelcomeScreenEditData {
welcomeChannels?: WelcomeChannelData[];
}
export type WSEventType =
| 'READY'
| 'RESUMED'
| 'GUILD_CREATE'
| 'GUILD_DELETE'
| 'GUILD_UPDATE'
| 'INVITE_CREATE'
| 'INVITE_DELETE'
| 'GUILD_MEMBER_ADD'
| 'GUILD_MEMBER_REMOVE'
| 'GUILD_MEMBER_UPDATE'
| 'GUILD_MEMBERS_CHUNK'
| 'GUILD_ROLE_CREATE'
| 'GUILD_ROLE_DELETE'
| 'GUILD_ROLE_UPDATE'
| 'GUILD_BAN_ADD'
| 'GUILD_BAN_REMOVE'
| 'GUILD_EMOJIS_UPDATE'
| 'GUILD_INTEGRATIONS_UPDATE'
| 'CHANNEL_CREATE'
| 'CHANNEL_DELETE'
| 'CHANNEL_UPDATE'
| 'CHANNEL_PINS_UPDATE'
| 'MESSAGE_CREATE'
| 'MESSAGE_DELETE'
| 'MESSAGE_UPDATE'
| 'MESSAGE_DELETE_BULK'
| 'MESSAGE_REACTION_ADD'
| 'MESSAGE_REACTION_REMOVE'
| 'MESSAGE_REACTION_REMOVE_ALL'
| 'MESSAGE_REACTION_REMOVE_EMOJI'
| 'THREAD_CREATE'
| 'THREAD_UPDATE'
| 'THREAD_DELETE'
| 'THREAD_LIST_SYNC'
| 'THREAD_MEMBER_UPDATE'
| 'THREAD_MEMBERS_UPDATE'
| 'USER_UPDATE'
| 'PRESENCE_UPDATE'
| 'TYPING_START'
| 'VOICE_STATE_UPDATE'
| 'VOICE_SERVER_UPDATE'
| 'WEBHOOKS_UPDATE'
| 'INTERACTION_CREATE'
| 'STAGE_INSTANCE_CREATE'
| 'STAGE_INSTANCE_UPDATE'
| 'STAGE_INSTANCE_DELETE'
| 'GUILD_STICKERS_UPDATE';
export type Serialized<T> = T extends symbol | bigint | (() => any)
? never
: T extends number | string | boolean | undefined
@@ -5145,6 +5074,8 @@ export {
GuildMFALevel,
GuildNSFWLevel,
GuildPremiumTier,
GatewayCloseCodes,
GatewayDispatchEvents,
GatewayIntentBits,
GatewayOpcodes,
GuildScheduledEventEntityType,
@@ -5156,6 +5087,7 @@ export {
InviteTargetType,
MessageType,
MessageFlags,
OAuth2Scopes,
PermissionFlagsBits,
RESTJSONErrorCodes,
StageInstancePrivacyLevel,