cloudstorm 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +7 -7
- package/README.md +61 -47
- package/dist/Client.d.ts +150 -0
- package/dist/Client.js +171 -0
- package/dist/Constants.d.ts +97 -0
- package/dist/Constants.js +51 -0
- package/dist/Intents.d.ts +29 -0
- package/dist/Intents.js +71 -0
- package/dist/Shard.d.ts +69 -0
- package/dist/Shard.js +93 -0
- package/dist/ShardManager.d.ts +78 -0
- package/dist/ShardManager.js +210 -0
- package/dist/Types.d.ts +65 -0
- package/dist/Types.js +2 -0
- package/dist/connector/DiscordConnector.d.ts +136 -0
- package/dist/connector/DiscordConnector.js +413 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +23 -0
- package/dist/structures/BetterWs.d.ts +85 -0
- package/dist/structures/BetterWs.js +172 -0
- package/dist/structures/RatelimitBucket.d.ts +39 -0
- package/{src → dist}/structures/RatelimitBucket.js +76 -89
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +40 -30
- package/.eslintrc.json +0 -28
- package/.jsdoc.json +0 -18
- package/.travis.yml +0 -17
- package/docma.config.json +0 -51
- package/example/amqp/index.js +0 -31
- package/example/basic/index.js +0 -18
- package/index.d.ts +0 -220
- package/index.js +0 -10
- package/src/Client.js +0 -229
- package/src/Constants.js +0 -16
- package/src/Shard.js +0 -174
- package/src/ShardManager.js +0 -309
- package/src/connector/DiscordConnector.js +0 -403
- package/src/structures/BetterWs.js +0 -234
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
let EventEmitter;
|
|
3
|
-
try {
|
|
4
|
-
EventEmitter = require('eventemitter3');
|
|
5
|
-
} catch (e) {
|
|
6
|
-
EventEmitter = require('events').EventEmitter;
|
|
7
|
-
}
|
|
8
|
-
const BetterWs = require('../structures/BetterWs');
|
|
9
|
-
const OP = require('../Constants').GATEWAY_OP_CODES;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @typedef DiscordConnector
|
|
13
|
-
* @description Class used for acting based on received events
|
|
14
|
-
*
|
|
15
|
-
* This class is automatically instantiated by the library and is documented for reference
|
|
16
|
-
* @property {String} id - id of the shard that created this class
|
|
17
|
-
* @property {Client} client - Main client instance
|
|
18
|
-
* @property {Object} options - options passed from the main client instance
|
|
19
|
-
* @property {Boolean} reconnect - whether autoreconnect is enabled
|
|
20
|
-
* @property {BetterWs} betterWs - Websocket class used for connecting to discord
|
|
21
|
-
* @property {Object} heartbeatInterval - interval within which heartbeats should be sent to discord
|
|
22
|
-
* @property {String[]} _trace - trace of servers used when connecting to discord
|
|
23
|
-
* @property {Number} seq - sequence value used on RESUMES and heartbeats
|
|
24
|
-
* @property {String} status - status of this connector
|
|
25
|
-
* @property {String} sessionId - session id of the current session, used in RESUMES
|
|
26
|
-
* @property {Boolean} forceIdentify - whether the connector should just IDENTIFY again and don't try to resume
|
|
27
|
-
*/
|
|
28
|
-
class DiscordConnector extends EventEmitter {
|
|
29
|
-
/**
|
|
30
|
-
* Create a new Discord Connector
|
|
31
|
-
* @param {String} id - id of the shard that created this class
|
|
32
|
-
* @param {Client} client - Main client instance
|
|
33
|
-
* @private
|
|
34
|
-
*/
|
|
35
|
-
constructor(id, client) {
|
|
36
|
-
super();
|
|
37
|
-
this.id = id;
|
|
38
|
-
this.client = client;
|
|
39
|
-
this.options = client.options;
|
|
40
|
-
this.reconnect = this.options.reconnect;
|
|
41
|
-
this.betterWs = null;
|
|
42
|
-
this.heartbeatInterval = null;
|
|
43
|
-
this._trace = null;
|
|
44
|
-
this.seq = 0;
|
|
45
|
-
this.status = 'init';
|
|
46
|
-
this.sessionId = null;
|
|
47
|
-
this.forceIdentify = false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Connect to discord
|
|
52
|
-
* @protected
|
|
53
|
-
*/
|
|
54
|
-
connect() {
|
|
55
|
-
if (!this.betterWs) {
|
|
56
|
-
this.betterWs = new BetterWs(this.options.endpoint);
|
|
57
|
-
} else {
|
|
58
|
-
this.betterWs.removeAllListeners();
|
|
59
|
-
this.betterWs.recreateWs(this.options.endpoint);
|
|
60
|
-
}
|
|
61
|
-
this.betterWs.on('ws_open', () => {
|
|
62
|
-
this.status = 'connecting';
|
|
63
|
-
});
|
|
64
|
-
this.betterWs.on('ws_message', (msg) => {
|
|
65
|
-
this.messageAction(msg);
|
|
66
|
-
});
|
|
67
|
-
this.betterWs.on('ws_close', (code, reason) => {
|
|
68
|
-
this.client.emit('debug', `Websocket of shard ${this.id} closed with code ${code} and reason: ${reason}`);
|
|
69
|
-
this.handleWsClose(code, reason);
|
|
70
|
-
});
|
|
71
|
-
this.betterWs.on('debug', event => {
|
|
72
|
-
/**
|
|
73
|
-
* @event Client#debug
|
|
74
|
-
* @type {Object}
|
|
75
|
-
* @description Debug event used for debugging the library
|
|
76
|
-
* @private
|
|
77
|
-
*/
|
|
78
|
-
this.client.emit('debug', event);
|
|
79
|
-
});
|
|
80
|
-
this.betterWs.on('debug_send', data => {
|
|
81
|
-
/**
|
|
82
|
-
* @event Client#rawSend
|
|
83
|
-
* @type {Object}
|
|
84
|
-
* @description Websocket payload which was sent to discord, this event is emitted on **every single** websocket message that was sent.
|
|
85
|
-
*/
|
|
86
|
-
this.client.emit('rawSend', data);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Close the websocket connection and disconnect
|
|
92
|
-
* @returns {Promise.<void>}
|
|
93
|
-
* @protected
|
|
94
|
-
*/
|
|
95
|
-
disconnect() {
|
|
96
|
-
return this.betterWs.close(1000, 'Disconnect from User');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Called with a parsed Websocket message to execute further actions
|
|
101
|
-
* @param {Object} message - message that was received
|
|
102
|
-
* @protected
|
|
103
|
-
*/
|
|
104
|
-
messageAction(message) {
|
|
105
|
-
/**
|
|
106
|
-
* @event Client#rawReceive
|
|
107
|
-
* @type {Object}
|
|
108
|
-
* @description Websocket message received from discord, this event is emitted on **every single** websocket message you may receive.
|
|
109
|
-
*/
|
|
110
|
-
this.client.emit('rawReceive', message);
|
|
111
|
-
if (message.s) {
|
|
112
|
-
if (message.s > this.seq + 1) {
|
|
113
|
-
this.client.emit('debug', `Shard ${this.id}, invalid sequence: current: ${this.seq} message: ${message.s}`);
|
|
114
|
-
this.seq = message.s;
|
|
115
|
-
this.resume();
|
|
116
|
-
}
|
|
117
|
-
this.seq = message.s;
|
|
118
|
-
}
|
|
119
|
-
switch (message.op) {
|
|
120
|
-
case OP.DISPATCH:
|
|
121
|
-
this.handleDispatch(message);
|
|
122
|
-
break;
|
|
123
|
-
case OP.HELLO:
|
|
124
|
-
this.heartbeat();
|
|
125
|
-
this.heartbeatInterval = setInterval(() => {
|
|
126
|
-
this.heartbeat(message.d.heartbeat_interval);
|
|
127
|
-
}, message.d.heartbeat_interval - 5000);
|
|
128
|
-
this._trace = message.d._trace;
|
|
129
|
-
this.identify();
|
|
130
|
-
this.client.emit('debug', `Shard ${this.id} received HELLO`);
|
|
131
|
-
break;
|
|
132
|
-
case OP.HEARTBEAT:
|
|
133
|
-
this.heartbeat();
|
|
134
|
-
break;
|
|
135
|
-
case OP.HEARTBEAT_ACK:
|
|
136
|
-
break;
|
|
137
|
-
case OP.RECONNECT:
|
|
138
|
-
this.reset();
|
|
139
|
-
this.betterWs.close();
|
|
140
|
-
break;
|
|
141
|
-
case OP.INVALID_SESSION:
|
|
142
|
-
if (message.d && this.sessionId) {
|
|
143
|
-
this.resume();
|
|
144
|
-
} else {
|
|
145
|
-
this.seq = 0;
|
|
146
|
-
this.sessionId = '';
|
|
147
|
-
/**
|
|
148
|
-
* @event DiscordConnector#queueIdentify
|
|
149
|
-
* @type {Number}
|
|
150
|
-
* @description Emitted when the connector received an op9 code
|
|
151
|
-
* @private
|
|
152
|
-
*/
|
|
153
|
-
this.emit('queueIdentify', this.id);
|
|
154
|
-
}
|
|
155
|
-
break;
|
|
156
|
-
default:
|
|
157
|
-
/**
|
|
158
|
-
* @event DiscordConnector#event
|
|
159
|
-
* @type {Object}
|
|
160
|
-
* @description Forward the event
|
|
161
|
-
* @private
|
|
162
|
-
*/
|
|
163
|
-
this.emit('event', message);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Reset this connector
|
|
169
|
-
* @protected
|
|
170
|
-
*/
|
|
171
|
-
reset() {
|
|
172
|
-
this.sessionId = null;
|
|
173
|
-
this.seq = 0;
|
|
174
|
-
this._trace = null;
|
|
175
|
-
clearInterval(this.heartbeatInterval);
|
|
176
|
-
this.heartbeatInterval = null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Send a identify payload to the gateway
|
|
181
|
-
* @param {Boolean} force - Whether CloudStorm should send an IDENTIFY even if there's a session that could be resumed
|
|
182
|
-
* @returns {Promise.<void>}
|
|
183
|
-
* @protected
|
|
184
|
-
*/
|
|
185
|
-
identify(force) {
|
|
186
|
-
if (this.sessionId && !this.forceIdentify && !force) {
|
|
187
|
-
return this.resume();
|
|
188
|
-
}
|
|
189
|
-
let data = {
|
|
190
|
-
op: OP.IDENTIFY, d: {
|
|
191
|
-
token: this.options.token,
|
|
192
|
-
properties: {
|
|
193
|
-
os: process.platform,
|
|
194
|
-
browser: 'CloudStorm',
|
|
195
|
-
device: 'CloudStorm'
|
|
196
|
-
},
|
|
197
|
-
large_threshold: this.options.largeGuildThreshold,
|
|
198
|
-
shard: [this.id, this.options.shardAmount],
|
|
199
|
-
presence: this.options.initialPresence ? this._checkPresenceData(this.options.initialPresence) : null
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
this.forceIdentify = false;
|
|
203
|
-
return this.betterWs.sendMessage(data);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Send a resume payload to the gateway
|
|
208
|
-
* @returns {Promise.<void>}
|
|
209
|
-
* @protected
|
|
210
|
-
*/
|
|
211
|
-
resume() {
|
|
212
|
-
return this.betterWs.sendMessage({
|
|
213
|
-
op: OP.RESUME,
|
|
214
|
-
d: {seq: this.seq, token: this.options.token, session_id: this.sessionId}
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Send a heartbeat to discord
|
|
220
|
-
* @protected
|
|
221
|
-
*/
|
|
222
|
-
heartbeat() {
|
|
223
|
-
this.betterWs.sendMessage({op: OP.HEARTBEAT, d: this.seq});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Handle dispatch events
|
|
228
|
-
* @param {Object} message - message received from the websocket
|
|
229
|
-
* @protected
|
|
230
|
-
*/
|
|
231
|
-
handleDispatch(message) {
|
|
232
|
-
switch (message.t) {
|
|
233
|
-
case 'READY':
|
|
234
|
-
case 'RESUMED':
|
|
235
|
-
if (message.t === 'READY') {
|
|
236
|
-
this.sessionId = message.d.session_id;
|
|
237
|
-
}
|
|
238
|
-
this.status = 'ready';
|
|
239
|
-
this._trace = message.d._trace;
|
|
240
|
-
/**
|
|
241
|
-
* @event DiscordConnector#ready
|
|
242
|
-
* @type {void}
|
|
243
|
-
* @description Emitted once the connector is ready (again)
|
|
244
|
-
* @private
|
|
245
|
-
*/
|
|
246
|
-
this.emit('ready', message.t === 'RESUMED');
|
|
247
|
-
/**
|
|
248
|
-
* @event DiscordConnector#event
|
|
249
|
-
* @type {Object}
|
|
250
|
-
* @description Emitted once an event was received from discord
|
|
251
|
-
* @private
|
|
252
|
-
*/
|
|
253
|
-
this.emit('event', message);
|
|
254
|
-
break;
|
|
255
|
-
default:
|
|
256
|
-
/**
|
|
257
|
-
* @event DiscordConnector#event
|
|
258
|
-
* @type {Object}
|
|
259
|
-
* @description Emitted once an event was received from discord
|
|
260
|
-
* @private
|
|
261
|
-
*/
|
|
262
|
-
this.emit('event', message);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Handle a close from the underlying websocket
|
|
268
|
-
* @param {Number} code - websocket close code
|
|
269
|
-
* @param {String} reason - close reason if any
|
|
270
|
-
* @protected
|
|
271
|
-
*/
|
|
272
|
-
handleWsClose(code, reason) {
|
|
273
|
-
let forceIdentify = false;
|
|
274
|
-
let gracefulClose = false;
|
|
275
|
-
this.status = 'disconnected';
|
|
276
|
-
if (code === 4004) {
|
|
277
|
-
/**
|
|
278
|
-
* @event DiscordConnector#error
|
|
279
|
-
* @type {String}
|
|
280
|
-
* @description Emitted when the token was invalid
|
|
281
|
-
* @private
|
|
282
|
-
*/
|
|
283
|
-
this.emit('error', 'Tried to connect with an invalid token');
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
if (code === 4010) {
|
|
287
|
-
/**
|
|
288
|
-
* @event DiscordConnector#error
|
|
289
|
-
* @type {String}
|
|
290
|
-
* @description Emitted when the user tried to connect with bad sharding data
|
|
291
|
-
* @private
|
|
292
|
-
*/
|
|
293
|
-
this.emit('error', 'Invalid sharding data, check your client options');
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
if (code === 4011) {
|
|
297
|
-
/**
|
|
298
|
-
* @event DiscordConnector#error
|
|
299
|
-
* @type {String}
|
|
300
|
-
* @description Emitted when the shard would be on over 2500 guilds
|
|
301
|
-
* @private
|
|
302
|
-
*/
|
|
303
|
-
this.emit('error', 'Shard would be on over 2500 guilds. Add more shards');
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
// force identify if the session is marked as invalid
|
|
307
|
-
if (code === 4009) {
|
|
308
|
-
forceIdentify = true;
|
|
309
|
-
}
|
|
310
|
-
// don't try to reconnect when true
|
|
311
|
-
if (code === 1000 && reason === 'Disconnect from User') {
|
|
312
|
-
gracefulClose = true;
|
|
313
|
-
}
|
|
314
|
-
clearInterval(this.heartbeatInterval);
|
|
315
|
-
this.betterWs.removeAllListeners();
|
|
316
|
-
/**
|
|
317
|
-
* @event DiscordConnector#disconnect
|
|
318
|
-
* @type {Object}
|
|
319
|
-
* @property {Number} code - websocket disconnect code
|
|
320
|
-
* @private
|
|
321
|
-
*/
|
|
322
|
-
this.emit('disconnect', code, reason, forceIdentify, gracefulClose);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Send a status update payload to discord
|
|
327
|
-
* @param {Presence} data - presence data to send
|
|
328
|
-
* @protected
|
|
329
|
-
*/
|
|
330
|
-
statusUpdate(data = {}) {
|
|
331
|
-
return this.betterWs.sendMessage({op: OP.STATUS_UPDATE, d: this._checkPresenceData(data)});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Send a voice state update payload to discord
|
|
336
|
-
* @param {VoiceStateUpdate} data - voice state update data to send
|
|
337
|
-
* @returns {Promise.<void>}
|
|
338
|
-
* @protected
|
|
339
|
-
*/
|
|
340
|
-
voiceStateUpdate(data) {
|
|
341
|
-
if (!data) {
|
|
342
|
-
return Promise.resolve();
|
|
343
|
-
}
|
|
344
|
-
return this.betterWs.sendMessage({op: OP.VOICE_STATE_UPDATE, d: this._checkVoiceStateUpdateData(data)});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Send a request guild members payload to discord
|
|
349
|
-
* @param {RequestGuildMembers} data - data to send
|
|
350
|
-
* @returns {Promise.<void>}
|
|
351
|
-
* @protected
|
|
352
|
-
*/
|
|
353
|
-
requestGuildMembers(data = {}) {
|
|
354
|
-
return this.betterWs.sendMessage({op: OP.REQUEST_GUILD_MEMBERS, d: this._checkRequestGuildMembersData(data)});
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Checks the presence data and fills in missing elements
|
|
359
|
-
* @param {Object} data - data to send
|
|
360
|
-
* @returns {Object} data after it's fixed/checked
|
|
361
|
-
* @private
|
|
362
|
-
*/
|
|
363
|
-
_checkPresenceData(data) {
|
|
364
|
-
data.status = data.status || 'online';
|
|
365
|
-
data.game = data.game || null;
|
|
366
|
-
if (data.game && !data.game.type) {
|
|
367
|
-
data.game.type = data.game.url ? 1 : 0;
|
|
368
|
-
}
|
|
369
|
-
if (data.game && !data.game.name) {
|
|
370
|
-
data.game = null;
|
|
371
|
-
}
|
|
372
|
-
data.afk = data.afk || false;
|
|
373
|
-
data.since = data.since || false;
|
|
374
|
-
return data;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Checks the voice state update data and fills in missing elements
|
|
379
|
-
* @param {Object} data - data to send
|
|
380
|
-
* @returns {Object} data after it's fixed/checked
|
|
381
|
-
* @private
|
|
382
|
-
*/
|
|
383
|
-
_checkVoiceStateUpdateData(data) {
|
|
384
|
-
data.channel_id = data.channel_id || null;
|
|
385
|
-
data.self_mute = data.self_mute || false;
|
|
386
|
-
data.self_deaf = data.self_deaf || false;
|
|
387
|
-
return data;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Checks the request guild members data and fills in missing elements
|
|
392
|
-
* @param {Object} data - data to send
|
|
393
|
-
* @returns {Object} data after it's fixed/checked
|
|
394
|
-
* @private
|
|
395
|
-
*/
|
|
396
|
-
_checkRequestGuildMembersData(data) {
|
|
397
|
-
data.query = data.query || '';
|
|
398
|
-
data.limit = data.limit || 0;
|
|
399
|
-
return data;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
module.exports = DiscordConnector;
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
let EventEmitter;
|
|
3
|
-
try {
|
|
4
|
-
EventEmitter = require('eventemitter3');
|
|
5
|
-
} catch (e) {
|
|
6
|
-
EventEmitter = require('events').EventEmitter;
|
|
7
|
-
}
|
|
8
|
-
const zlib = require('zlib-sync');
|
|
9
|
-
let Erlpack;
|
|
10
|
-
try {
|
|
11
|
-
Erlpack = require('erlpack');
|
|
12
|
-
} catch (e) {// eslint-disable-next-line no-empty
|
|
13
|
-
}
|
|
14
|
-
const GATEWAY_OP_CODES = require('../Constants').GATEWAY_OP_CODES;
|
|
15
|
-
let WebSocket = require('ws');
|
|
16
|
-
let RatelimitBucket = require('./RatelimitBucket');
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef BetterWs
|
|
20
|
-
* @description Helper Class for simplifying the websocket connection to discord
|
|
21
|
-
* @property {WebSocket} ws - the raw websocket connection
|
|
22
|
-
* @property {RatelimitBucket} wsBucket - ratelimit bucket for the general websocket connection
|
|
23
|
-
* @property {RatelimitBucket} statusBucket - ratelimit bucket for the 5/60s status update ratelimit
|
|
24
|
-
* @property {zlib} zlibInflate - shared zlibInflate context for inflating zlib compressed messages received from discord
|
|
25
|
-
* @private
|
|
26
|
-
*/
|
|
27
|
-
class BetterWs extends EventEmitter {
|
|
28
|
-
/**
|
|
29
|
-
* Create a new BetterWs instance
|
|
30
|
-
* @param {String} address
|
|
31
|
-
* @param {Object} options
|
|
32
|
-
* @private
|
|
33
|
-
*/
|
|
34
|
-
constructor(address, options = {}) {
|
|
35
|
-
super();
|
|
36
|
-
this.ws = new WebSocket(address, options);
|
|
37
|
-
this.bindWs(this.ws);
|
|
38
|
-
this.wsBucket = new RatelimitBucket(120, 60000);
|
|
39
|
-
this.statusBucket = new RatelimitBucket(5, 60000);
|
|
40
|
-
this.zlibInflate = new zlib.Inflate({
|
|
41
|
-
chunkSize: 65535,
|
|
42
|
-
flush: zlib.Z_SYNC_FLUSH,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get the raw websocket connection currently used
|
|
48
|
-
* @returns {WebSocket}
|
|
49
|
-
* @protected
|
|
50
|
-
*/
|
|
51
|
-
get rawWs() {
|
|
52
|
-
return this.ws;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Add eventlisteners to a passed websocket connection
|
|
57
|
-
* @param {WebSocket} ws - websocket
|
|
58
|
-
* @protected
|
|
59
|
-
*/
|
|
60
|
-
bindWs(ws) {
|
|
61
|
-
ws.on('message', (msg) => {
|
|
62
|
-
this.onMessage(msg);
|
|
63
|
-
});
|
|
64
|
-
ws.on('close', (code, reason) => this.onClose(code, reason));
|
|
65
|
-
ws.on('error', (err) => {
|
|
66
|
-
/**
|
|
67
|
-
* @event BetterWs#error
|
|
68
|
-
* @type {Error}
|
|
69
|
-
* @description Emitted upon errors from the underlying websocket
|
|
70
|
-
* @private
|
|
71
|
-
*/
|
|
72
|
-
this.emit('error', err);
|
|
73
|
-
});
|
|
74
|
-
ws.on('open', () => this.onOpen());
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Create a new Websocket Connection if the old one was closed/destroyed
|
|
79
|
-
* @param {String} address - address to connect to
|
|
80
|
-
* @param {Object} options - options used by the websocket connection
|
|
81
|
-
* @protected
|
|
82
|
-
*/
|
|
83
|
-
recreateWs(address, options = {}) {
|
|
84
|
-
this.ws.removeAllListeners();
|
|
85
|
-
this.zlibInflate = new zlib.Inflate({
|
|
86
|
-
chunkSize: 65535,
|
|
87
|
-
flush: zlib.Z_SYNC_FLUSH,
|
|
88
|
-
});
|
|
89
|
-
this.ws = new WebSocket(address, options);
|
|
90
|
-
this.options = options;
|
|
91
|
-
this.wsBucket.dropQueue();
|
|
92
|
-
this.wsBucket = new RatelimitBucket(120, 60000);
|
|
93
|
-
this.statusBucket = new RatelimitBucket(5, 60000);
|
|
94
|
-
this.bindWs(this.ws);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Called upon opening of the websocket connection
|
|
99
|
-
* @protected
|
|
100
|
-
*/
|
|
101
|
-
onOpen() {
|
|
102
|
-
/**
|
|
103
|
-
* @event BetterWs#ws_open
|
|
104
|
-
* @type {void}
|
|
105
|
-
* @description Emitted once the underlying websocket connection has opened
|
|
106
|
-
* @private
|
|
107
|
-
*/
|
|
108
|
-
this.emit('ws_open');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Called once a websocket message is received,
|
|
113
|
-
* uncompresses the message using zlib and parses it via Erlpack or JSON.parse
|
|
114
|
-
* @param {Object|Buffer|String} message - message received by websocket
|
|
115
|
-
* @protected
|
|
116
|
-
*/
|
|
117
|
-
onMessage(message) {
|
|
118
|
-
try {
|
|
119
|
-
const length = message.length;
|
|
120
|
-
const flush = length >= 4 &&
|
|
121
|
-
message[length - 4] === 0x00 &&
|
|
122
|
-
message[length - 3] === 0x00 &&
|
|
123
|
-
message[length - 2] === 0xFF &&
|
|
124
|
-
message[length - 1] === 0xFF;
|
|
125
|
-
this.zlibInflate.push(message, flush && zlib.Z_SYNC_FLUSH);
|
|
126
|
-
if (!flush) return;
|
|
127
|
-
if (Erlpack) {
|
|
128
|
-
message = Erlpack.unpack(this.zlibInflate.result);
|
|
129
|
-
} else {
|
|
130
|
-
message = JSON.parse(this.zlibInflate.result);
|
|
131
|
-
}
|
|
132
|
-
} catch (e) {
|
|
133
|
-
/**
|
|
134
|
-
* @event BetterWs#error
|
|
135
|
-
* @type {String}
|
|
136
|
-
* @description Emitted upon parse errors of messages
|
|
137
|
-
* @private
|
|
138
|
-
*/
|
|
139
|
-
this.emit('error', `Message: ${message} was not parseable`);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* @event BetterWs#ws_message
|
|
144
|
-
* @type {Object}
|
|
145
|
-
* @description Emitted upon successful parsing of a message with the parsed Message
|
|
146
|
-
* @private
|
|
147
|
-
*/
|
|
148
|
-
this.emit('ws_message', message);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Called when the websocket connection closes for some reason
|
|
153
|
-
* @param {Number} code - websocket close code
|
|
154
|
-
* @param {String} reason - reason of the close if any
|
|
155
|
-
* @protected
|
|
156
|
-
*/
|
|
157
|
-
onClose(code, reason) {
|
|
158
|
-
/**
|
|
159
|
-
* @event BetterWs#ws_close
|
|
160
|
-
* @type {void}
|
|
161
|
-
* @param {Number} code - websocket close code
|
|
162
|
-
* @param {String} reason - websocket close reason
|
|
163
|
-
* @private
|
|
164
|
-
*/
|
|
165
|
-
this.emit('ws_close', code, reason);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Send a message to the discord gateway
|
|
170
|
-
* @param {Object} data - data to send
|
|
171
|
-
* @returns {Promise.<void>}
|
|
172
|
-
* @protected
|
|
173
|
-
*/
|
|
174
|
-
sendMessage(data) {
|
|
175
|
-
/**
|
|
176
|
-
* @event BetterWs#debug_send
|
|
177
|
-
* @type {object}
|
|
178
|
-
* @description Used for debugging the messages sent to discord's gateway
|
|
179
|
-
* @private
|
|
180
|
-
*/
|
|
181
|
-
this.emit('debug_send', data);
|
|
182
|
-
return new Promise((res, rej) => {
|
|
183
|
-
let status = data.op === GATEWAY_OP_CODES.STATUS_UPDATE;
|
|
184
|
-
try {
|
|
185
|
-
if (Erlpack) {
|
|
186
|
-
data = Erlpack.pack(data);
|
|
187
|
-
} else {
|
|
188
|
-
data = JSON.stringify(data);
|
|
189
|
-
}
|
|
190
|
-
} catch (e) {
|
|
191
|
-
return rej(e);
|
|
192
|
-
}
|
|
193
|
-
let sendMsg = () => {
|
|
194
|
-
// The promise from wsBucket is ignored, since the method passed to it does not return a promise
|
|
195
|
-
this.wsBucket.queue(() => {
|
|
196
|
-
this.ws.send(data, {}, (e) => {
|
|
197
|
-
if (e) {
|
|
198
|
-
return rej(e);
|
|
199
|
-
}
|
|
200
|
-
res();
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
};
|
|
204
|
-
if (status) {
|
|
205
|
-
// same here
|
|
206
|
-
this.statusBucket.queue(sendMsg);
|
|
207
|
-
} else {
|
|
208
|
-
sendMsg();
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Close the current connection
|
|
215
|
-
* @param {Number} code=1000 - websocket close code to use
|
|
216
|
-
* @param {String} reason - reason of the disconnect
|
|
217
|
-
* @returns {Promise.<void>}
|
|
218
|
-
* @protected
|
|
219
|
-
*/
|
|
220
|
-
close(code = 1000, reason = '') {
|
|
221
|
-
return new Promise((res, rej) => {
|
|
222
|
-
this.ws.close(code, reason);
|
|
223
|
-
this.ws.once('close', () => {
|
|
224
|
-
return res();
|
|
225
|
-
});
|
|
226
|
-
setTimeout(() => {
|
|
227
|
-
return rej('Websocket not closed within 5 seconds');
|
|
228
|
-
}, 5 * 1000);
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
module.exports = BetterWs;
|