Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cloudstorm

Package Overview
Dependencies
Maintainers
2
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cloudstorm - npm Package Compare versions

Comparing version 0.4.0 to 0.4.1

22

dist/Client.js

@@ -7,9 +7,2 @@ "use strict";

const events_1 = require("events");
let Erlpack;
try {
Erlpack = require("erlpack");
}
catch (e) {
Erlpack = null;
}
const Constants_1 = __importDefault(require("./Constants"));

@@ -28,5 +21,4 @@ const snowtransfer_1 = require("snowtransfer");

super();
if (!token) {
if (!token)
throw new Error("Missing token!");
}
this.options = {

@@ -42,3 +34,3 @@ largeGuildThreshold: 250,

compress: true,
socket: undefined
encoding: "json"
}

@@ -101,4 +93,3 @@ };

async presenceUpdate(data) {
await this.shardManager.presenceUpdate(data);
void undefined;
return this.shardManager.presenceUpdate(data);
}

@@ -166,5 +157,4 @@ /**

requestGuildMembers(shardId, data) {
if (!data.guild_id) {
if (!data.guild_id)
throw new Error("You need to pass a guild_id");
}
return this.shardManager.requestGuildMembers(shardId, data);

@@ -177,4 +167,4 @@ }

_updateEndpoint(gatewayUrl) {
var _a;
this.options.endpoint = `${gatewayUrl}?v=${Constants_1.default.GATEWAY_VERSION}&encoding=${Erlpack ? "etf" : "json"}${((_a = this.options.ws) === null || _a === void 0 ? void 0 : _a.compress) ? "&compress=zlib-stream" : ""}`;
var _a, _b;
this.options.endpoint = `${gatewayUrl}?v=${Constants_1.default.GATEWAY_VERSION}&encoding=${((_a = this.options.ws) === null || _a === void 0 ? void 0 : _a.encoding) === "etf" ? "etf" : "json"}${((_b = this.options.ws) === null || _b === void 0 ? void 0 : _b.compress) ? "&compress=zlib-stream" : ""}`;
}

@@ -181,0 +171,0 @@ }

@@ -8,4 +8,4 @@ /// <reference types="node" />

ready: [boolean];
error: [string];
disconnect: [number, string, boolean];
stateChange: ["connecting" | "identifying" | "resuming" | "ready" | "disconnected"];
}

@@ -37,3 +37,3 @@ interface DiscordConnector {

reconnect: boolean;
betterWs: BetterWs | null;
betterWs: BetterWs;
heartbeatTimeout: NodeJS.Timeout | null;

@@ -43,3 +43,3 @@ heartbeatInterval: number;

seq: number;
status: string;
status: "connecting" | "identifying" | "resuming" | "ready" | "disconnected";
sessionId: string | null;

@@ -59,3 +59,3 @@ lastACKAt: number;

*/
connect(): void;
connect(): Promise<void>;
/**

@@ -62,0 +62,0 @@ * Close the websocket connection and disconnect.

@@ -9,3 +9,2 @@ "use strict";

const Intents_1 = __importDefault(require("../Intents"));
const ws_1 = __importDefault(require("ws"));
let reconnecting = false;

@@ -25,3 +24,2 @@ /**

super();
this.betterWs = null;
this.heartbeatTimeout = null;

@@ -31,3 +29,3 @@ this.heartbeatInterval = 0;

this.seq = 0;
this.status = "init";
this.status = "disconnected";
this.sessionId = null;

@@ -41,16 +39,6 @@ this.lastACKAt = 0;

this.reconnect = this.options.reconnect || true;
}
/**
* Connect to Discord.
*/
connect() {
if (!this.betterWs) {
this.betterWs = new BetterWs_1.default(this.options.endpoint, this.options.ws);
}
else {
this.betterWs.removeAllListeners();
this.betterWs.recreateWs(this.options.endpoint, this.options.ws);
}
this.betterWs = new BetterWs_1.default(this.options.endpoint, this.options.ws);
this.betterWs.on("ws_open", () => {
this.status = "connecting";
this.emit("stateChange", "connecting");
reconnecting = false;

@@ -60,15 +48,16 @@ });

this.betterWs.on("ws_close", (code, reason) => this.handleWsClose(code, reason));
this.betterWs.on("debug", event => {
this.client.emit("debug", event);
});
this.betterWs.on("debug_send", data => {
this.client.emit("rawSend", data);
});
this.betterWs.on("debug", event => this.client.emit("debug", event));
this.betterWs.on("debug_send", data => this.client.emit("rawSend", data));
}
/**
* Connect to Discord.
*/
connect() {
return this.betterWs.connect();
}
/**
* Close the websocket connection and disconnect.
*/
async disconnect() {
var _a;
return (_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.close(1000, "Disconnected by User");
return this.betterWs.close();
}

@@ -104,5 +93,4 @@ /**

case Constants_1.GATEWAY_OP_CODES.INVALID_SESSION:
if (message.d && this.sessionId) {
if (message.d && this.sessionId)
this.resume();
}
else {

@@ -125,5 +113,4 @@ this.seq = 0;

}
else {
else
this.heartbeat();
}
}, this.heartbeatInterval);

@@ -147,20 +134,11 @@ this._trace = message.d._trace;

async _reconnect(resume = false) {
var _a, _b, _c;
if (resume)
reconnecting = true;
if (((_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.ws.readyState) === ws_1.default.CONNECTING) {
this.emit("error", `Client was attempting to ${resume ? "resume" : "reconnect"} while the WebSocket was still in the connecting state. This should never happen.${this.options.reconnect ? " Restarting the connect loop." : ""}`);
this.reset();
if (this.options.reconnect)
this.connect();
}
// This is for instances where the gateway asks the client to reconnect. The ws would be closed by the time the code reaches here.
if (((_b = this.betterWs) === null || _b === void 0 ? void 0 : _b.ws.readyState) === ws_1.default.OPEN)
await ((_c = this.betterWs) === null || _c === void 0 ? void 0 : _c.close(resume ? 4000 : 1012, "reconnecting"));
if (resume) {
if (this.betterWs.status === 2)
void this.client.emit("error", `Client was attempting to ${resume ? "resume" : "reconnect"} while the WebSocket was still in the connecting state. This should never happen.`);
await this.betterWs.close();
if (resume)
this.clearHeartBeat();
}
else {
else
this.reset();
}
this.connect();

@@ -192,6 +170,8 @@ }

async identify(force) {
var _a;
if (this.sessionId && !force) {
if (this.betterWs.status !== 1)
void this.client.emit("debug", "Client was attempting to identify when the ws was not open");
if (this.sessionId && !force)
return this.resume();
}
this.status = "identifying";
this.emit("stateChange", "identifying");
const data = {

@@ -213,3 +193,3 @@ op: Constants_1.GATEWAY_OP_CODES.IDENTIFY,

Object.assign(data.d, { presence: this._checkPresenceData(this.options.initialPresence) });
return (_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.sendMessage(data);
return this.betterWs.sendMessage(data);
}

@@ -220,4 +200,7 @@ /**

async resume() {
var _a;
return (_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.sendMessage({
if (this.betterWs.status !== 1)
void this.client.emit("debug", "Client was attempting to resume when the ws was not open");
this.status = "resuming";
this.emit("stateChange", "resuming");
return this.betterWs.sendMessage({
op: Constants_1.GATEWAY_OP_CODES.RESUME,

@@ -231,6 +214,5 @@ d: { seq: this.seq, token: this.options.token, session_id: this.sessionId }

heartbeat() {
var _a, _b;
if (((_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.ws.readyState) !== ws_1.default.OPEN)
return;
(_b = this.betterWs) === null || _b === void 0 ? void 0 : _b.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.HEARTBEAT, d: this.seq });
if (this.betterWs.status !== 1)
void this.client.emit("debug", "Client was attempting to heartbeat when the ws was not open");
this.betterWs.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.HEARTBEAT, d: this.seq });
this.lastHeartbeatSend = Date.now();

@@ -246,6 +228,6 @@ }

case "RESUMED":
if (message.t === "READY") {
if (message.t === "READY")
this.sessionId = message.d.session_id;
}
this.status = "ready";
this.emit("stateChange", "ready");
this._trace = message.d._trace;

@@ -265,29 +247,24 @@ this.emit("ready", message.t === "RESUMED");

handleWsClose(code, reason) {
var _a;
let gracefulClose = false;
this.status = "disconnected";
this.emit("stateChange", "disconnected");
// Disallowed Intents.
if (code === 4014) {
this.emit("error", "Disallowed Intents, check your client options and application page.");
}
if (code === 4014)
this.client.emit("error", "Disallowed Intents, check your client options and application page.");
// Invalid Intents.
if (code === 4013) {
this.emit("error", "Invalid Intents data, check your client options.");
}
if (code === 4013)
this.client.emit("error", "Invalid Intents data, check your client options.");
// Invalid API version.
if (code === 4012) {
this.emit("error", "Invalid API version.");
}
if (code === 4012)
this.client.emit("error", "Invalid API version.");
// Sharding required.
if (code === 4011) {
this.emit("error", "Shard would be on over 2500 guilds. Add more shards.");
}
if (code === 4011)
this.client.emit("error", "Shard would be on over 2500 guilds. Add more shards.");
// Invalid shard.
if (code === 4010) {
this.emit("error", "Invalid sharding data, check your client options.");
}
if (code === 4010)
this.client.emit("error", "Invalid sharding data, check your client options.");
// Session timed out.
// force identify if the session is marked as invalid.
if (code === 4009) {
this.emit("error", "Session timed out.");
this.client.emit("error", "Session timed out.");
this.clearHeartBeat();

@@ -298,3 +275,3 @@ this.connect();

if (code === 4008) {
this.emit("error", "You are being rate limited. Wait before sending more packets.");
this.client.emit("error", "You are being rate limited. Wait before sending more packets.");
this.clearHeartBeat();

@@ -305,3 +282,3 @@ this.connect();

if (code === 4007) {
this.emit("error", "Invalid sequence. Reconnecting and starting a new session.");
this.client.emit("error", "Invalid sequence. Reconnecting and starting a new session.");
this.reset();

@@ -312,3 +289,3 @@ this.connect();

if (code === 4005) {
this.emit("error", "You sent more than one OP 2 IDENTIFY payload while the websocket was open.");
this.client.emit("error", "You sent more than one OP 2 IDENTIFY payload while the websocket was open.");
this.clearHeartBeat();

@@ -318,8 +295,7 @@ this.connect();

// Authentication failed.
if (code === 4004) {
this.emit("error", "Tried to connect with an invalid token");
}
if (code === 4004)
this.client.emit("error", "Tried to connect with an invalid token");
// Not authenticated.
if (code === 4003) {
this.emit("error", "You tried to send a packet before sending an OP 2 IDENTIFY or OP 6 RESUME.");
this.client.emit("error", "You tried to send a packet before sending an OP 2 IDENTIFY or OP 6 RESUME.");
this.clearHeartBeat();

@@ -330,3 +306,3 @@ this.connect();

if (code === 4002) {
this.emit("error", "You sent an invalid payload");
this.client.emit("error", "You sent an invalid payload");
this.clearHeartBeat();

@@ -337,3 +313,3 @@ this.connect();

if (code === 4001) {
this.emit("error", "You sent an invalid opcode or invalid payload for an opcode");
this.client.emit("error", "You sent an invalid opcode or invalid payload for an opcode");
this.clearHeartBeat();

@@ -344,7 +320,6 @@ this.connect();

if (code === 4000) {
if (reconnecting) {
if (reconnecting)
gracefulClose = true;
}
else {
this.emit("error", "Error code 4000 received. Attempting to resume");
this.client.emit("error", "Error code 4000 received. Attempting to resume");
this.clearHeartBeat();

@@ -355,8 +330,7 @@ this.connect();

// Don't try to reconnect when true
if (code === 1000 && reason === "Disconnected by User") {
if (code === 1000 && reason === "Disconnected by User")
gracefulClose = true;
}
if (gracefulClose) {
this.clearHeartBeat();
(_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
this.betterWs.removeAllListeners();
}

@@ -370,4 +344,3 @@ this.emit("disconnect", code, reason, gracefulClose);

async presenceUpdate(data) {
var _a;
return (_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.PRESENCE_UPDATE, d: this._checkPresenceData(data) });
return this.betterWs.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.PRESENCE_UPDATE, d: this._checkPresenceData(data) });
}

@@ -379,7 +352,5 @@ /**

async voiceStateUpdate(data) {
var _a;
if (!data) {
if (!data)
return Promise.resolve();
}
return (_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.VOICE_STATE_UPDATE, d: this._checkVoiceStateUpdateData(data) });
return this.betterWs.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.VOICE_STATE_UPDATE, d: this._checkVoiceStateUpdateData(data) });
}

@@ -391,4 +362,3 @@ /**

async requestGuildMembers(data) {
var _a;
return (_a = this.betterWs) === null || _a === void 0 ? void 0 : _a.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.REQUEST_GUILD_MEMBERS, d: this._checkRequestGuildMembersData(data) });
return this.betterWs.sendMessage({ op: Constants_1.GATEWAY_OP_CODES.REQUEST_GUILD_MEMBERS, d: this._checkRequestGuildMembersData(data) });
}

@@ -395,0 +365,0 @@ /**

@@ -17,2 +17,3 @@ export declare const flags: {

DIRECT_MESSAGE_TYPING: number;
MESSAGE_CONTENT: number;
};

@@ -19,0 +20,0 @@ export declare const privileged: number;

@@ -20,4 +20,5 @@ "use strict";

DIRECT_MESSAGE_TYPING: 1 << 14,
MESSAGE_CONTENT: 1 << 15,
};
exports.privileged = exports.flags.GUILD_MEMBERS | exports.flags.GUILD_PRESENCES | exports.flags.GUILD_MESSAGES;
exports.privileged = exports.flags.GUILD_MEMBERS | exports.flags.GUILD_PRESENCES | exports.flags.GUILD_MESSAGES | exports.flags.MESSAGE_CONTENT;
exports.all = Object.values(exports.flags).reduce((acc, p) => acc | p, 0);

@@ -24,0 +25,0 @@ exports.non_privileged = exports.all & ~exports.privileged;

@@ -6,3 +6,2 @@ /// <reference types="node" />

disconnect: [number, string, boolean];
error: [string];
ready: [boolean];

@@ -9,0 +8,0 @@ queueIdentify: [number];

@@ -43,11 +43,4 @@ "use strict";

});
this.connector.on("error", (err) => {
this.emit("error", err);
});
this.connector.on("ready", (resume) => {
this.emit("ready", resume);
});
this.connector.on("queueIdentify", () => {
this.emit("queueIdentify", this.id);
});
this.connector.on("ready", (resume) => this.emit("ready", resume));
this.connector.on("queueIdentify", () => this.emit("queueIdentify", this.id));
}

@@ -54,0 +47,0 @@ /**

@@ -18,5 +18,4 @@ "use strict";

this.options = client.options;
if (!this.options.connectQueueInterval) {
if (!this.options.connectQueueInterval)
this.options.connectQueueInterval = 1000 * 5;
}
this.shards = {};

@@ -96,5 +95,2 @@ this.connectQueue = [];

});
shard.on("error", (error) => {
this.client.emit("error", error);
});
shard.on("disconnect", (code, reason, gracefulClose) => {

@@ -122,5 +118,4 @@ this.client.emit("debug", `Websocket of shard ${shard.id} closed with code ${code} and reason: ${reason ? reason : "None"}`);

if (this.shards[shardId]) {
if (!this.shards[shardId].ready) {
if (!this.shards[shardId].ready)
return;
}
}

@@ -136,5 +131,4 @@ }

if (this.shards[shardId]) {
if (this.shards[shardId].connector.status !== "disconnected") {
if (this.shards[shardId].connector.status !== "disconnected")
return;
}
}

@@ -164,10 +158,6 @@ }

const shard = this.shards[shardId];
if (!shard) {
if (!shard)
rej(new Error(`Shard ${shardId} does not exist`));
}
if (!shard.ready) {
shard.once("ready", () => {
shard.presenceUpdate(data).then(result => res(result)).catch(e => rej(e));
});
}
if (!shard.ready)
shard.once("ready", () => shard.presenceUpdate(data).then(result => res(result)).catch(e => rej(e)));
shard.presenceUpdate(data).then(result => res(result)).catch(e => rej(e));

@@ -184,10 +174,6 @@ });

const shard = this.shards[shardId];
if (!shard) {
if (!shard)
rej(new Error(`Shard ${shardId} does not exist`));
}
if (!shard.ready) {
shard.once("ready", () => {
shard.voiceStateUpdate(data).then(result => res(result)).catch(e => rej(e));
});
}
if (!shard.ready)
shard.once("ready", () => shard.voiceStateUpdate(data).then(result => res(result)).catch(e => rej(e)));
shard.voiceStateUpdate(data).then(result => res(result)).catch(e => rej(e));

@@ -204,10 +190,6 @@ });

const shard = this.shards[shardId];
if (!shard) {
if (!shard)
rej(new Error(`Shard ${shardId} does not exist`));
}
if (!shard.ready) {
shard.once("ready", () => {
shard.requestGuildMembers(data).then(result => res(result)).catch(e => rej(e));
});
}
if (!shard.ready)
shard.once("ready", () => shard.requestGuildMembers(data).then(result => res(result)).catch(e => rej(e)));
shard.requestGuildMembers(data).then(result => res(result)).catch(e => rej(e));

@@ -214,0 +196,0 @@ });

/// <reference types="node" />
import { EventEmitter } from "events";
import zlib from "zlib-sync";
import WebSocket from "ws";
import RatelimitBucket from "./RatelimitBucket";
interface BWSEvents {
error: [Error | string];
ws_open: [];

@@ -33,56 +30,22 @@ ws_close: [number, string];

declare class BetterWs extends EventEmitter {
ws: WebSocket;
encoding: "etf" | "json";
compress: boolean;
address: string;
options: import("../Types").IClientWSOptions;
wsBucket: RatelimitBucket;
presenceBucket: RatelimitBucket;
zlibInflate: zlib.Inflate | null;
options: import("../Types").IClientWSOptions;
compress: boolean;
static readonly default: typeof BetterWs;
/**
* Create a new BetterWs instance.
*/
constructor(address: string, options?: import("../Types").IClientWSOptions);
/**
* Get the raw websocket connection currently used.
*/
get rawWs(): WebSocket;
/**
* Add eventlisteners to a passed websocket connection.
* @param ws Websocket.
*/
private bindWs;
/**
* Create a new websocket connection if the old one was closed/destroyed.
* @param address Address to connect to.
* @param options Options used by the websocket connection.
*/
recreateWs(address: string, options?: import("../Types").IClientWSOptions): void;
/**
* Called upon opening of the websocket connection.
*/
private onOpen;
/**
* Called once a websocket message is received,
* uncompresses the message using zlib and parses it via Erlpack or JSON.parse.
* @param message Message received by websocket.
*/
private onMessage;
/**
* Called when the websocket connection closes for some reason.
* @param code Websocket close code.
* @param reason Reason of the close if any.
*/
private onClose;
/**
* Send a message to the Discord gateway.
* @param data Data to send.
*/
sendMessage(data: any): Promise<void>;
/**
* Close the current websocket connection.
* @param code Websocket close code to use.
* @param reason Reason of the disconnect.
*/
close(code?: number, reason?: string): Promise<void>;
private _socket;
private _internal;
private _connecting;
constructor(address: string, options: import("../Types").IClientWSOptions);
get status(): 1 | 2 | 3 | 4;
connect(): Promise<void>;
close(): Promise<void>;
sendMessage(data: import("../Types").IWSMessage): Promise<void>;
private _write;
private _onError;
private _onClose;
private _onReadable;
private _processFrame;
}
export = BetterWs;

@@ -6,12 +6,8 @@ "use strict";

const events_1 = require("events");
const zlib_sync_1 = __importDefault(require("zlib-sync"));
let Erlpack;
try {
Erlpack = require("erlpack");
}
catch (e) {
Erlpack = null;
}
const crypto_1 = require("crypto");
const zlib_1 = require("zlib");
const https_1 = require("https");
const util_1 = __importDefault(require("util"));
const Z_SYNC_FLUSH = zlib_1.constants.Z_SYNC_FLUSH;
const Constants_1 = require("../Constants");
const ws_1 = __importDefault(require("ws"));
const RatelimitBucket_1 = __importDefault(require("./RatelimitBucket"));

@@ -22,175 +18,525 @@ /**

class BetterWs extends events_1.EventEmitter {
/**
* Create a new BetterWs instance.
*/
constructor(address, options = {}) {
constructor(address, options) {
super();
this.zlibInflate = null;
this.ws = new ws_1.default(address, options.socket);
this.bindWs(this.ws);
this.wsBucket = new RatelimitBucket_1.default(120, 60000);
this.presenceBucket = new RatelimitBucket_1.default(5, 20000);
if (options.compress) {
this.zlibInflate = new zlib_sync_1.default.Inflate({ chunkSize: 65535 });
this.compress = true;
}
else
this.compress = false;
this.presenceBucket = new RatelimitBucket_1.default(5, 60000);
this._connecting = false;
this.encoding = options.encoding === "etf" ? "etf" : "json";
this.compress = options.compress || false;
this.address = address;
this.options = options;
this._socket = null;
this._internal = {
closePromise: null,
zlib: null,
};
}
/**
* Get the raw websocket connection currently used.
*/
get rawWs() {
return this.ws;
get status() {
const internal = this._internal;
if (this._connecting)
return 2;
if (internal.closePromise)
return 3; // closing
if (!this._socket)
return 4; // closed
return 1; // connected
}
/**
* Add eventlisteners to a passed websocket connection.
* @param ws Websocket.
*/
bindWs(ws) {
ws.on("message", (msg) => {
this.onMessage(msg);
connect() {
if (this._socket)
return Promise.resolve(void 0);
const key = (0, crypto_1.randomBytes)(16).toString("base64");
this.emit("debug", "Creating connection");
const url = new URL(this.address);
const req = (0, https_1.request)({
hostname: url.hostname,
path: `${url.pathname}${url.search}`,
headers: {
"Connection": "Upgrade",
"Upgrade": "websocket",
"Sec-WebSocket-Key": key,
"Sec-WebSocket-Version": "13",
}
});
ws.on("close", (code, reason) => this.onClose(code, reason.toString()));
ws.on("error", (err) => {
this.emit("error", err);
this._connecting = true;
return new Promise((resolve, reject) => {
req.on("upgrade", (res, socket) => {
const hash = (0, crypto_1.createHash)("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
const accept = res.headers["sec-websocket-accept"];
if (hash !== accept) {
socket.end(() => {
this.emit("debug", "Failed websocket-key validation");
this._connecting = false;
reject(new Error(`Invalid Sec-Websocket-Accept | expected: ${hash} | received: ${accept}`));
});
return;
}
socket.on("error", this._onError.bind(this));
socket.on("close", this._onClose.bind(this));
socket.on("readable", this._onReadable.bind(this));
this._socket = socket;
this._connecting = false;
if (this.compress) {
const z = (0, zlib_1.createInflate)();
// @ts-ignore
z._c = z.close;
// @ts-ignore
z._h = z._handle;
// @ts-ignore
z._hc = z._handle.close;
// @ts-ignore
z._v = () => void 0;
this._internal.zlib = z;
}
this.emit("ws_open");
resolve(void 0);
});
req.on("error", e => {
this._connecting = false;
reject(e);
});
req.end();
});
ws.on("open", () => this.onOpen());
}
/**
* Create a new websocket connection if the old one was closed/destroyed.
* @param address Address to connect to.
* @param options Options used by the websocket connection.
*/
recreateWs(address, options = {}) {
this.ws.removeAllListeners();
if (options.compress) {
this.zlibInflate = new zlib_sync_1.default.Inflate({ chunkSize: 65535 });
this.compress = true;
async close() {
const internal = this._internal;
if (internal.closePromise)
return internal.closePromise;
if (!this._socket)
return Promise.resolve(void 0);
let resolver;
const promise = new Promise(resolve => {
resolver = resolve;
this._write(Buffer.allocUnsafe(0), 8);
}).then(() => {
internal.closePromise = null;
});
// @ts-ignore
promise.resolve = resolver;
internal.closePromise = promise;
}
sendMessage(data) {
if (!isValidRequest(data))
return Promise.reject(new Error("Invalid request"));
return new Promise(res => {
const presence = data.op === Constants_1.GATEWAY_OP_CODES.PRESENCE_UPDATE;
const sendMsg = () => {
this.wsBucket.queue(() => {
this.emit("debug_send", data);
if (this.encoding === "json")
this._write(Buffer.from(JSON.stringify(data)), 1);
else {
const etf = writeETF(data);
this._write(etf, 2);
}
res(void 0);
});
};
if (presence)
this.presenceBucket.queue(sendMsg);
else
sendMsg();
});
}
_write(packet, opcode) {
const socket = this._socket;
if (!socket || !socket.writable)
return;
const length = packet.length;
let frame;
if (length < 126) {
frame = Buffer.allocUnsafe(6 + length);
frame[1] = 128 + length;
}
else if (length < (1 << 16)) {
frame = Buffer.allocUnsafe(8 + length);
frame[1] = 254;
frame[2] = length >> 8;
frame[3] = length & 255;
}
else {
this.zlibInflate = null;
this.compress = false;
frame = Buffer.allocUnsafe(14 + length);
frame[1] = 255;
frame.writeBigUInt64BE(BigInt(length), 2);
}
this.ws = new ws_1.default(address, options.socket);
this.options = options;
frame[0] = 128 + opcode;
frame.writeUInt32BE(0, frame.length - length - 4);
frame.set(packet, frame.length - length);
socket.write(frame);
}
_onError(error) {
if (!this._socket)
return;
this.emit("debug", util_1.default.inspect(error, true, 1, false));
this._write(Buffer.allocUnsafe(0), 8);
}
_onClose() {
const socket = this._socket;
const internal = this._internal;
if (!socket)
return;
this.emit("debug", "Connection closed");
socket.removeListener("data", this._onReadable);
socket.removeListener("error", this._onError);
socket.removeListener("close", this._onClose);
this.wsBucket.dropQueue();
this.presenceBucket.dropQueue();
this.wsBucket = new RatelimitBucket_1.default(120, 60000);
this.presenceBucket = new RatelimitBucket_1.default(5, 60000);
this.bindWs(this.ws);
this._socket = null;
if (internal.zlib) {
internal.zlib.close();
internal.zlib = null;
}
if (internal.closePromise) {
// @ts-ignore
internal.closePromise.resolve(void 0);
}
}
/**
* Called upon opening of the websocket connection.
*/
onOpen() {
this.emit("ws_open");
}
/**
* Called once a websocket message is received,
* uncompresses the message using zlib and parses it via Erlpack or JSON.parse.
* @param message Message received by websocket.
*/
onMessage(message) {
let parsed;
try {
let msg;
if (this.compress && this.zlibInflate) {
const length = message.length;
const flush = length >= 4 &&
message[length - 4] === 0x00 &&
message[length - 3] === 0x00 &&
message[length - 2] === 0xFF &&
message[length - 1] === 0xFF;
this.zlibInflate.push(message, flush ? zlib_sync_1.default.Z_SYNC_FLUSH : false);
if (!flush)
_onReadable() {
const socket = this._socket;
while (socket.readableLength > 1) {
let length = readRange(socket, 1, 1) & 127;
let bytes = 0;
if (length > 125) {
bytes = length === 126 ? 2 : 8;
if (socket.readableLength < 2 + bytes)
return;
msg = this.zlibInflate.result;
length = readRange(socket, 2, bytes);
}
else
msg = message;
if (Erlpack) {
parsed = Erlpack.unpack(msg);
const frame = socket.read(2 + bytes + length);
if (!frame)
return;
const fin = frame[0] >> 7;
const opcode = frame[0] & 15;
if (fin !== 1 || opcode === 0)
this.emit("debug", "discord actually does send messages with fin=0. if you see this error let me know");
const payload = frame.slice(2 + bytes);
this._processFrame(opcode, payload);
}
}
_processFrame(opcode, message) {
const internal = this._internal;
switch (opcode) {
case 1: {
const packet = JSON.parse(message.toString());
this.emit("ws_message", packet);
break;
}
else {
parsed = JSON.parse(String(msg));
case 2: {
let packet;
if (this.compress) {
const z = internal.zlib;
let error = null;
let data = null;
// @ts-ignore
z.close = z._handle.close = z._v;
try {
// @ts-ignore
data = z._processChunk(message, Z_SYNC_FLUSH);
}
catch (e) {
error = e;
}
const l = message.length;
if (message[l - 4] !== 0 || message[l - 3] !== 0 || message[l - 2] !== 255 || message[l - 1] !== 255)
this.emit("debug", "discord actually does send fragmented zlib messages. if you see this error let me know");
// @ts-ignore
z.close = z._c;
// @ts-ignore
z._handle = z._h;
// @ts-ignore
z._handle.close = z._hc;
// @ts-ignore
z._events.error = void 0;
// @ts-ignore
z._eventCount--;
z.removeAllListeners("error");
if (error) {
this.emit("debug", "Zlib error");
this._write(Buffer.allocUnsafe(0), 8);
return;
}
if (!data)
return; // This should never run, but TS is lame
packet = this.encoding === "json" ? JSON.parse(String(data)) : readETF(data, 1);
}
else if (this.encoding === "json") {
const data = (0, zlib_1.inflateSync)(message);
packet = JSON.parse(data.toString());
}
else
packet = readETF(message, 1);
this.emit("ws_message", packet);
break;
}
case 8: {
const code = message.length > 1 ? (message[0] << 8) + message[1] : 0;
const reason = message.length > 2 ? message.slice(2).toString() : "";
this.emit("ws_close", code, reason);
this._write(Buffer.from([code >> 8, code & 255]), 8);
break;
}
case 9: {
this._write(message, 10);
break;
}
}
catch (e) {
this.emit("error", `Message: ${message} was not parseable`);
return;
}
}
function isValidRequest(value) {
return value && typeof value === "object" && Number.isInteger(value.op) && typeof value.d !== "undefined";
}
function readRange(socket, index, bytes) {
// @ts-ignore
let head = socket._readableState.buffer.head;
let cursor = 0;
let read = 0;
let num = 0;
do {
for (let i = 0; i < head.data.length; i++) {
if (++cursor > index) {
num *= 256;
num += head.data[i];
if (++read === bytes)
return num;
}
}
this.emit("ws_message", parsed);
}
/**
* Called when the websocket connection closes for some reason.
* @param code Websocket close code.
* @param reason Reason of the close if any.
*/
onClose(code, reason) {
this.emit("ws_close", code, reason);
}
/**
* Send a message to the Discord gateway.
* @param data Data to send.
*/
sendMessage(data) {
if (this.ws.readyState !== ws_1.default.OPEN)
return Promise.reject(new Error("WS is not open"));
this.emit("debug_send", data);
return new Promise((res, rej) => {
const presence = data.op === Constants_1.GATEWAY_OP_CODES.PRESENCE_UPDATE;
try {
if (Erlpack) {
data = Erlpack.pack(data);
} while ((head = head.next));
throw new Error("readRange failed?");
}
function readETF(data, start) {
let view;
let x = start;
const loop = () => {
const type = data[x++];
switch (type) {
case 97: {
return data[x++];
}
case 98: {
const int = data.readInt32BE(x);
x += 4;
return int;
}
case 100: {
const length = data.readUInt16BE(x);
let atom = "";
if (length > 30) {
// @ts-ignore
atom = data.latin1Slice(x += 2, x + length);
}
else {
data = JSON.stringify(data);
for (let i = x += 2; i < x + length; i++) {
atom += String.fromCharCode(data[i]);
}
}
x += length;
if (!atom)
return undefined;
if (atom === "nil" || atom === "null")
return null;
if (atom === "true")
return true;
if (atom === "false")
return false;
return atom;
}
catch (e) {
return rej(e);
case 108:
case 106: {
const array = [];
if (type === 108) {
const length = data.readUInt32BE(x);
x += 4;
for (let i = 0; i < length; i++) {
array.push(loop());
}
x++;
}
return array;
}
const sendMsg = () => {
// The promise from wsBucket is ignored, since the method passed to it does not return a promise
this.wsBucket.queue(() => {
this.ws.send(data, {}, (e) => {
if (e) {
return rej(e);
}
res();
});
});
};
if (presence) {
// same here
this.presenceBucket.queue(sendMsg);
case 107: {
const array = [];
const length = data.readUInt16BE(x);
x += 2;
for (let i = 0; i < length; i++) {
array.push(data[x++]);
}
return array;
}
else {
sendMsg();
case 109: {
const length = data.readUInt32BE(x);
let str = "";
if (length > 30) {
// @ts-ignore
str = data.utf8Slice(x += 4, x + length);
}
else {
let i = x += 4;
const l = x + length;
while (i < l) {
const byte = data[i++];
if (byte < 128)
str += String.fromCharCode(byte);
else if (byte < 224)
str += String.fromCharCode(((byte & 31) << 6) + (data[i++] & 63));
else if (byte < 240)
str += String.fromCharCode(((byte & 15) << 12) + ((data[i++] & 63) << 6) + (data[i++] & 63));
else
str += String.fromCodePoint(((byte & 7) << 18) + ((data[i++] & 63) << 12) + ((data[i++] & 63) << 6) + (data[i++] & 63));
}
}
x += length;
return str;
}
});
}
/**
* Close the current websocket connection.
* @param code Websocket close code to use.
* @param reason Reason of the disconnect.
*/
close(code = 1000, reason = "Unknown") {
if (this.ws.readyState === ws_1.default.CLOSING || this.ws.readyState === ws_1.default.CLOSED)
return Promise.reject(new Error("WS is already closing or is closed"));
return new Promise((res, rej) => {
const timeout = setTimeout(() => {
return rej("Websocket not closed within 5 seconds");
}, 5 * 1000);
this.ws.once("close", () => {
clearTimeout(timeout);
return res();
});
this.ws.close(code, reason);
});
}
case 110: {
// @ts-ignore
if (!view)
view = new DataView(data.buffer, data.offset, data.byteLength);
const length = data[x++];
const sign = data[x++];
let left = length;
let num = BigInt(0);
while (left > 0) {
if (left >= 8) {
num <<= BigInt(64);
num += view.getBigUint64(x + (left -= 8), true);
}
else if (left >= 4) {
num <<= BigInt(32);
// @ts-ignore
num += BigInt(view.getUint32(x + (left -= 4)), true);
}
else if (left >= 2) {
num <<= BigInt(16);
// @ts-ignore
num += BigInt(view.getUint16(x + (left -= 2)), true);
}
else {
num <<= BigInt(8);
num += BigInt(data[x]);
left--;
}
}
x += length;
return (sign ? -num : num).toString();
}
case 116: {
const obj = {};
const length = data.readUInt32BE(x);
x += 4;
for (let i = 0; i < length; i++) {
const key = loop();
// @ts-ignore
obj[key] = loop();
}
return obj;
}
}
throw new Error(`Missing etf type: ${type}`);
};
return loop();
}
BetterWs.default = BetterWs;
function writeETF(data) {
const b = Buffer.allocUnsafe(1 << 12);
b[0] = 131;
let i = 1;
const loop = (obj) => {
const type = typeof obj;
switch (type) {
case "boolean": {
b[i++] = 100;
if (obj) {
b.writeUInt16BE(4, i);
// @ts-ignore
b.latin1Write("true", i += 2);
i += 4;
}
else {
b.writeUInt16BE(5, i);
// @ts-ignore
b.latin1Write("false", i += 2);
i += 5;
}
break;
}
case "string": {
const length = Buffer.byteLength(obj);
b[i++] = 109;
b.writeUInt32BE(length, i);
// @ts-ignore
b.utf8Write(obj, i += 4);
i += length;
break;
}
case "number": {
if (Number.isInteger(obj)) {
const abs = Math.abs(obj);
if (abs < 2147483648) {
b[i++] = 98;
b.writeInt32BE(obj, i);
i += 4;
}
else if (abs < Number.MAX_SAFE_INTEGER) {
b[i++] = 110;
b[i++] = 8;
b[i++] = Number(obj < 0);
b.writeBigUInt64LE(BigInt(abs), i);
i += 8;
break;
}
else {
b[i++] = 70;
b.writeDoubleBE(obj, i);
i += 8;
}
}
else {
b[i++] = 70;
b.writeDoubleBE(obj, i);
i += 8;
}
break;
}
case "bigint": {
b[i++] = 110;
b[i++] = 8;
b[i++] = Number(obj < 0);
b.writeBigUInt64LE(obj, i);
i += 8;
break;
}
case "object": {
if (obj === null) {
b[i++] = 100;
b.writeUInt16BE(3, i);
// @ts-ignore
b.latin1Write("nil", i += 2);
i += 3;
}
else if (Array.isArray(obj)) {
if (obj.length) {
b[i++] = 108;
b.writeUInt32BE(obj.length, i);
i += 4;
for (const item of obj) {
loop(item);
}
}
b[i++] = 106;
}
else {
const entries = Object.entries(obj).filter(x => typeof x[1] !== "undefined");
b[i++] = 116;
b.writeUInt32BE(entries.length, i);
i += 4;
for (const [key, value] of entries) {
loop(key);
loop(value);
}
}
break;
}
}
};
loop(data);
return Buffer.from(b.slice(0, i));
}
module.exports = BetterWs;

@@ -39,5 +39,4 @@ "use strict";

}
if (this.remaining !== 0) {
if (this.remaining !== 0)
this.checkQueue().catch(rej);
}
if (fn instanceof Promise) {

@@ -56,10 +55,7 @@ return fn.then(res).catch((e) => {

if (this.remaining === 0) {
this.fnQueue.push({
fn, callback: wrapFn, error
});
this.fnQueue.push({ fn, callback: wrapFn, error });
this.checkQueue().catch(rej);
}
else {
else
wrapFn();
}
});

@@ -66,0 +62,0 @@ }

@@ -1,2 +0,1 @@

import Constants from "./Constants";
export interface IntentFlags {

@@ -21,6 +20,6 @@ GUILDS: number;

export interface IWSMessage {
op: typeof Constants["GATEWAY_OP_CODES"][keyof typeof Constants.GATEWAY_OP_CODES];
op: import("discord-typings").GatewayOpcode;
d?: any;
s?: number;
t?: string;
t?: import("discord-typings").GatewayEvent;
}

@@ -30,13 +29,2 @@ export interface IGatewayMessage extends IWSMessage {

}
export interface IPresenceActivity {
name: string;
type?: 0 | 1 | 2 | 3 | 5;
url?: string;
}
export interface IPresence {
status?: "online" | "idle" | "dnd" | "offline";
afk?: boolean;
since?: boolean;
activities?: Array<IPresenceActivity> | null;
}
export interface IClientOptions {

@@ -48,3 +36,3 @@ largeGuildThreshold?: number;

reconnect?: boolean;
initialPresence?: IPresence;
initialPresence?: import("discord-typings").GatewayPresenceUpdate;
intents?: IntentResolvable;

@@ -55,20 +43,5 @@ connectQueueInterval?: number;

}
export interface IVoiceStateUpdate {
guild_id: string;
channel_id?: string | null;
self_mute?: boolean;
self_deaf?: boolean;
}
export interface IRequestGuildMembers {
guild_id: string;
query?: string | null;
limit?: number;
}
export interface IShardReady {
id: number;
ready: boolean;
}
export interface IClientWSOptions {
compress?: boolean;
socket?: import("ws").ClientOptions;
encoding?: "etf" | "json";
}
{
"name": "cloudstorm",
"version": "0.4.0",
"version": "0.4.1",
"description": "Minimalistic Discord Gateway library",

@@ -17,20 +17,15 @@ "main": "./dist/index.js",

"dependencies": {
"snowtransfer": "^0.4.x",
"ws": "^8.5.0",
"zlib-sync": "^0.1.7"
"snowtransfer": "^0.4.x"
},
"devDependencies": {
"@types/node": "16.7.1",
"@types/ws": "^8.2.3",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"eslint": "^8.9.0",
"typedoc": "^0.22.12",
"@types/centra": "^2.2.0",
"@types/node": "17.0.23",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.16.0",
"eslint": "^8.11.0",
"typedoc": "^0.22.13",
"typedoc-plugin-mdn-links": "^1.0.5",
"typedoc-plugin-missing-exports": "^0.22.6",
"typescript": "^4.5.5"
"typescript": "^4.6.2"
},
"optionalDependencies": {
"erlpack": "github:discordapp/erlpack"
},
"files": [

@@ -37,0 +32,0 @@ "dist",

@@ -20,5 +20,3 @@ # A minimal discord gateway library

await bot.connect();
bot.on("ready", () => {
console.log("Bot received ready event");
});
bot.on("ready", () => console.log("Bot received ready event"););
};

@@ -48,3 +46,3 @@ startup().catch(e => {

"user": {
"id": "id"
"id": "id"
}

@@ -51,0 +49,0 @@ }

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc