hackchat-engine 1.1.6 → 1.1.8

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/Client.js CHANGED
@@ -10,10 +10,8 @@ import SocketController from './websocket/SocketController.js';
10
10
  import EventsManager from './events/EventsManager.js';
11
11
  import ExtMap from './util/ExtMap.js';
12
12
 
13
- const util = new Util();
14
-
15
13
  /**
16
- * Main client interface
14
+ * Main client interface for the hack.chat protocol
17
15
  * @extends {EventEmitter}
18
16
  */
19
17
  class Client extends EventEmitter {
@@ -33,26 +31,26 @@ class Client extends EventEmitter {
33
31
  };
34
32
 
35
33
  /**
36
- * Create the websocket manager for this client
37
- * @type {SocketController}
38
- */
34
+ * Create the websocket manager for this client
35
+ * @type {SocketController}
36
+ */
39
37
  this.ws = new SocketController(this);
40
38
 
41
39
  /**
42
- * Create the event manager for this client
40
+ * Create the event manager for this client, responsible for data structure mapping
43
41
  * @type {EventsManager}
44
42
  */
45
43
  this.events = new EventsManager(this);
46
44
 
47
45
  /**
48
46
  * Stored time that the client was last ready
49
- * @type {Date}
47
+ * @type {Date|null}
50
48
  */
51
49
  this.readyAt = null;
52
50
 
53
51
  /**
54
52
  * User info for this client
55
- * @type {ClientStruct}
53
+ * @type {ClientStruct|null}
56
54
  */
57
55
  this.myUser = null;
58
56
 
@@ -69,23 +67,25 @@ class Client extends EventEmitter {
69
67
  this.channels = new ExtMap();
70
68
 
71
69
  /**
72
- * Active timeouts set on this client
73
- * @type {Set}
70
+ * Active timeouts set on this client, managed for destruction
71
+ * @type {Set<Timeout>}
74
72
  */
75
73
  this.timeouts = new Set();
76
74
 
77
75
  /**
78
- * Active intervals set on this client
76
+ * Active intervals set on this client, managed for destruction
79
77
  * @type {Set<Timeout>}
80
78
  */
81
79
  this.intervals = new Set();
82
80
 
83
81
  /**
84
82
  * Internal check to see if this code is run in node or a browser
85
- * @type {Set<Timeout>}
83
+ * @type {boolean}
86
84
  */
87
85
  this.isBrowser = typeof window !== 'undefined';
88
86
 
87
+ this.util = new Util();
88
+
89
89
  /**
90
90
  * Initiate websocket connection
91
91
  */
@@ -108,7 +108,7 @@ class Client extends EventEmitter {
108
108
 
109
109
  /**
110
110
  * How long this client has been connected, in milliseconds
111
- * @type {number}
111
+ * @type {number|null}
112
112
  * @readonly
113
113
  */
114
114
  get uptime() {
@@ -117,7 +117,7 @@ class Client extends EventEmitter {
117
117
 
118
118
  /**
119
119
  * Timestamp of the time the client when it was last ready
120
- * @type {number}
120
+ * @type {number|null}
121
121
  * @readonly
122
122
  */
123
123
  get readyTimestamp() {
@@ -137,7 +137,7 @@ class Client extends EventEmitter {
137
137
  * Sends a join event to the hackchat server
138
138
  * @param {string} name Name to join with
139
139
  * @param {string} password Optional password to create trip code
140
- * @param {string} channel Channel to auto join
140
+ * @param {string} channel Channel to join
141
141
  * @returns {void}
142
142
  */
143
143
  join(name = false, password = '', channel = false) {
@@ -167,11 +167,11 @@ class Client extends EventEmitter {
167
167
 
168
168
  /**
169
169
  * Establishes communication with a hackchat server
170
+ * @returns {Promise<void>}
170
171
  */
171
172
  async connectToWebSocket() {
172
173
  try {
173
- // eslint-disable-next-line no-async-promise-executor
174
- return new Promise(async (resolve, reject) => { // anti-pattern my ass
174
+ return new Promise((resolve, reject) => {
175
175
  this.emit(Events.DEBUG, '[client] Initiating connection. . .');
176
176
 
177
177
  const timeout = this.setTimeout(
@@ -179,23 +179,32 @@ class Client extends EventEmitter {
179
179
  1000 * 300,
180
180
  );
181
181
 
182
- let { gateway } = this.options.ws;
182
+ const { gateway } = this.options.ws;
183
183
 
184
- if (this.browser) {
185
- this.emit(Events.DEBUG, '[client] Fetching ws config');
186
- const conf = await util.fetchWebsocketConfig();
187
- gateway = conf.gateway;
188
- }
184
+ const initiateConnection = (gw) => {
185
+ this.emit(Events.DEBUG, `[client] Using gateway ${gw}`);
189
186
 
190
- this.emit(Events.DEBUG, `[client] Using gateway ${gateway}`);
187
+ const success = this.ws.connect(gw);
188
+ if (!success) {
189
+ return reject(new Error('Connection failed to start (Socket likely not IDLE)'));
190
+ }
191
191
 
192
- this.ws.connect(gateway);
193
- this.ws.connection.once('error', reject);
192
+ this.ws.connection.once('error', reject);
194
193
 
195
- this.once(Events.READY, () => {
196
- this.clearTimeout(timeout);
197
- resolve();
198
- });
194
+ this.once(Events.READY, () => {
195
+ this.clearTimeout(timeout);
196
+ resolve();
197
+ });
198
+ };
199
+
200
+ if (this.browser) {
201
+ this.emit(Events.DEBUG, '[client] Fetching ws config');
202
+ this.util.fetchWebsocketConfig()
203
+ .then((conf) => initiateConnection(conf.gateway))
204
+ .catch(reject);
205
+ } else {
206
+ initiateConnection(gateway);
207
+ }
199
208
  });
200
209
  } catch (e) {
201
210
  this.destroy();
@@ -205,20 +214,21 @@ class Client extends EventEmitter {
205
214
 
206
215
  /**
207
216
  * Terminates the websocket connection and destroys the client
208
- * @returns {Promise}
217
+ * @returns {Promise<void>}
209
218
  */
210
219
  destroy() {
211
220
  for (const t of this.timeouts) clearTimeout(t); // eslint-disable-line no-restricted-syntax
212
221
  for (const i of this.intervals) clearInterval(i); // eslint-disable-line no-restricted-syntax
213
222
  this.timeouts.clear();
214
223
  this.intervals.clear();
224
+ this.ws.destroy();
215
225
  return Promise.resolve();
216
226
  }
217
227
 
218
228
  /**
219
229
  * Create a managed timeout
220
230
  * @param {Function} func Target function to call
221
- * @param {number} delay Wait time before calling func
231
+ * @param {number} delay Wait time before calling func (in ms)
222
232
  * @param {...*} args Optional arguments to pass to func
223
233
  * @returns {Timeout}
224
234
  */
@@ -245,7 +255,7 @@ class Client extends EventEmitter {
245
255
  /**
246
256
  * Create a managed interval
247
257
  * @param {Function} func Target function to call
248
- * @param {number} delay Wait time before calling func
258
+ * @param {number} delay Wait time before calling func (in ms)
249
259
  * @param {...*} args Optional arguments to pass to func
250
260
  * @returns {Timeout}
251
261
  */
@@ -333,6 +343,68 @@ class Client extends EventEmitter {
333
343
  channel,
334
344
  });
335
345
  }
346
+
347
+ /**
348
+ * Registers a handler for a specific, unmapped command (cmd)
349
+ * This allows the client to process custom packets without needing the 'raw' event
350
+ * @param {string} cmd The command string from the server (e.g., 'signMessage')
351
+ * @param {function(object): void} handler The function to execute when the command is received
352
+ *
353
+ * @example
354
+ * import { Client } from 'hackchat-engine';
355
+ * const hcClient = new Client({
356
+ * isBot: false,
357
+ * });
358
+ * hcClient.onCommand('example', (payload) => {
359
+ * // This function only fires when { cmd: 'example' } is received
360
+ * console.log(payload);
361
+ * });
362
+ */
363
+ onCommand(cmd, handler) {
364
+ if (!this.ws.connection || !this.ws.connection.packetRouter) {
365
+ this.emit(Events.WARN, `Cannot register command '${cmd}': Router not initialized.`);
366
+ return;
367
+ }
368
+
369
+ this.ws.connection.packetRouter.registerCustomCommand(cmd, handler);
370
+ }
371
+
372
+ /**
373
+ * Sends a Solana signature and the signed message back to the server for verification
374
+ * @param {string} signature The base64 or hex encoded signature from the client's wallet
375
+ * @param {string} signedMessage The original human-readable message that was signed
376
+ */
377
+ requestSiw(wallet, address) {
378
+ this.ws.send({
379
+ cmd: OPCodes.REQUEST_SIW,
380
+ wallet,
381
+ address,
382
+ });
383
+ }
384
+
385
+ /**
386
+ * Sends a Solana signature and the signed message back to the server for verification
387
+ * @param {string} signature The base64 or hex encoded signature from the client's wallet
388
+ * @param {string} signedMessage The original human-readable message that was signed
389
+ */
390
+ sendSignature(signature, signedMessage) {
391
+ this.ws.send({
392
+ cmd: OPCodes.SIGN_SIW,
393
+ signature,
394
+ signedMessage,
395
+ });
396
+ }
397
+
398
+ /**
399
+ * Sends a signed transaction back to the server for broadcasting
400
+ * @param {string} signedTransaction The base64 encoded signed transaction object
401
+ */
402
+ sendSignedTransaction(signedTransaction) {
403
+ this.ws.send({
404
+ cmd: OPCodes.CONFIRM_TX,
405
+ signedTransaction,
406
+ });
407
+ }
336
408
  }
337
409
 
338
410
  export default Client;
@@ -10,6 +10,10 @@ import UserLeave from './UserLeave.js';
10
10
  import UpdateUser from './UpdateUser.js';
11
11
  import Warning from './Warning.js';
12
12
  import Whisper from './Whisper.js';
13
+ import PublicChannels from './PublicChannels.js';
14
+ import HackAttempt from './HackAttempt.js';
15
+ import SignMessage from './SignMessage.js';
16
+ import SignTransaction from './SignTransaction.js';
13
17
 
14
18
  /**
15
19
  * This class routes incoming event data to it's proper handler
@@ -34,6 +38,10 @@ class EventsManager {
34
38
  this.UpdateUser = new UpdateUser(this.client);
35
39
  this.Warning = new Warning(this.client);
36
40
  this.Whisper = new Whisper(this.client);
41
+ this.PublicChannels = new PublicChannels(this.client);
42
+ this.HackAttempt = new HackAttempt(this.client);
43
+ this.SignMessage = new SignMessage(this.client);
44
+ this.SignTransaction = new SignTransaction(this.client);
37
45
  }
38
46
  }
39
47
 
@@ -0,0 +1,24 @@
1
+ import AbstractEvent from './AbstractEvent.js';
2
+ import HackAttemptStruct from '../structures/HackAttemptStruct.js';
3
+
4
+ /**
5
+ * This class handles an incoming `hackattempt` event from the server
6
+ * @private
7
+ */
8
+ class PublicChannels extends AbstractEvent {
9
+ /**
10
+ * Event handler function
11
+ * @param {object} data Incoming event data
12
+ * @returns {object}
13
+ */
14
+ handle(data) {
15
+ const { client } = this;
16
+ const message = new HackAttemptStruct(client, data);
17
+
18
+ return {
19
+ message,
20
+ };
21
+ }
22
+ }
23
+
24
+ export default PublicChannels;
@@ -0,0 +1,24 @@
1
+ import AbstractEvent from './AbstractEvent.js';
2
+ import PubChannelsStruct from '../structures/PubChannelsStruct.js';
3
+
4
+ /**
5
+ * This class handles an incoming `publicchannels` event from the server
6
+ * @private
7
+ */
8
+ class PublicChannels extends AbstractEvent {
9
+ /**
10
+ * Event handler function
11
+ * @param {object} data Incoming event data
12
+ * @returns {object}
13
+ */
14
+ handle(data) {
15
+ const { client } = this;
16
+ const message = new PubChannelsStruct(client, data);
17
+
18
+ return {
19
+ message,
20
+ };
21
+ }
22
+ }
23
+
24
+ export default PublicChannels;
@@ -0,0 +1,23 @@
1
+ import AbstractEvent from './AbstractEvent.js';
2
+
3
+ /**
4
+ * This class handles an incoming `signMessage` event from the server
5
+ * @private
6
+ */
7
+ class SignMessage extends AbstractEvent {
8
+ /**
9
+ * Event handler function
10
+ * @param {object} data Incoming event data
11
+ * @returns {object}
12
+ */
13
+ handle(data) {
14
+ const { client } = this;
15
+ client.lastMsg = data;
16
+
17
+ return {
18
+ data,
19
+ };
20
+ }
21
+ }
22
+
23
+ export default SignMessage;
@@ -0,0 +1,23 @@
1
+ import AbstractEvent from './AbstractEvent.js';
2
+
3
+ /**
4
+ * This class handles an incoming `signTransaction` event from the server
5
+ * @private
6
+ */
7
+ class SignTransaction extends AbstractEvent {
8
+ /**
9
+ * Event handler function
10
+ * @param {object} data Incoming event data
11
+ * @returns {object}
12
+ */
13
+ handle(data) {
14
+ const { client } = this;
15
+ client.lastSign = data;
16
+
17
+ return {
18
+ data,
19
+ };
20
+ }
21
+ }
22
+
23
+ export default SignTransaction;
package/package.json CHANGED
@@ -59,5 +59,5 @@
59
59
  "lintfix": "eslint --fix --ignore-path .gitignore .",
60
60
  "test": "echo \"Error: no test specified\" && exit 1"
61
61
  },
62
- "version": "1.1.6"
62
+ "version": "1.1.8"
63
63
  }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * This class handles parsing of the data of a `hackAttempt` event
3
+ */
4
+ class HackAttemptStruct {
5
+ /**
6
+ * @param {Channel} channel Channel that the hackAttempt was sent through
7
+ * @param {Number} fromId User id of requester
8
+ * @param {User} from User whomst'd sent the hackAttempt
9
+ * @param {String} fromNick User name of whomst'd sent the hackAttempt
10
+ * @param {String} lib Target library
11
+ * @param {Client} client Main client reference
12
+ */
13
+ constructor(client, data) {
14
+ /**
15
+ * Add client reference
16
+ * @type {Client}
17
+ * @readonly
18
+ */
19
+ Object.defineProperty(this, 'client', { value: client });
20
+
21
+ if (data) this.setup(data);
22
+ }
23
+
24
+ /**
25
+ * Fill in this structure with provided data
26
+ * @param {object} data Incoming event data
27
+ * @returns {void}
28
+ */
29
+ setup(data) {
30
+ /**
31
+ * Origin channel of the request
32
+ * @type {string}
33
+ */
34
+ this.channel = data.channel;
35
+
36
+ /**
37
+ * Userid of request sender
38
+ * @type {number}
39
+ */
40
+ this.fromId = data.from;
41
+
42
+ /**
43
+ * The user who sent the whisper
44
+ * @type {User}
45
+ */
46
+ this.from = this.client.users.find((val) => val.userid === data.from);
47
+
48
+ /**
49
+ * Name of request sender
50
+ * @type {string}
51
+ */
52
+ this.fromNick = data.fromNick;
53
+
54
+ /**
55
+ * URL to requested library
56
+ * @type {string}
57
+ */
58
+ this.lib = data.lib;
59
+
60
+ /**
61
+ * The timestamp the publicchannels was sent at
62
+ * @type {number}
63
+ */
64
+ this.timestamp = new Date();
65
+ }
66
+
67
+ /**
68
+ * When referenced as a string, output the content instead of an object type
69
+ * @returns {string}
70
+ */
71
+ toString() {
72
+ return `${this.fromNick} suggested ${this.lib}`;
73
+ }
74
+ }
75
+
76
+ export default HackAttemptStruct;
@@ -62,7 +62,7 @@ class MessageStruct {
62
62
  }
63
63
 
64
64
  /**
65
- * Reply to the message.
65
+ * Reply to the message
66
66
  * @param {string} text The content for the message
67
67
  * @returns {Promise}
68
68
  * @example
@@ -0,0 +1,47 @@
1
+ /**
2
+ * This class handles parsing of the data of a `publicchannels` event
3
+ */
4
+ class PubChannelsStruct {
5
+ /**
6
+ * @param {Array} list Array of objects containing channel name and population
7
+ */
8
+ constructor(client, data) {
9
+ /**
10
+ * Add client reference
11
+ * @type {Client}
12
+ * @readonly
13
+ */
14
+ Object.defineProperty(this, 'client', { value: client });
15
+
16
+ if (data) this.setup(data);
17
+ }
18
+
19
+ /**
20
+ * Fill in this structure with provided data
21
+ * @param {object} data Incoming event data
22
+ * @returns {void}
23
+ */
24
+ setup(data) {
25
+ /**
26
+ * Array of objects containing channel name and population
27
+ * @type {array}
28
+ */
29
+ this.list = data.list;
30
+
31
+ /**
32
+ * The timestamp the publicchannels was sent at
33
+ * @type {number}
34
+ */
35
+ this.timestamp = new Date();
36
+ }
37
+
38
+ /**
39
+ * When referenced as a string, output the content instead of an object type
40
+ * @returns {string}
41
+ */
42
+ toString() {
43
+ return this.list.join(', ');
44
+ }
45
+ }
46
+
47
+ export default PubChannelsStruct;
@@ -106,6 +106,12 @@ class User {
106
106
  */
107
107
  this.nickColor = data.color || false;
108
108
 
109
+ /**
110
+ * User's color
111
+ * @type {string}
112
+ */
113
+ this.flair = data.flair || '';
114
+
109
115
  /**
110
116
  * Numeric permission level
111
117
  * @type {number}
@@ -260,6 +266,7 @@ class User {
260
266
  this.userlevel = data.uType;
261
267
  this.bot = data.isBot;
262
268
  this.nickColor = data.color;
269
+ this.flair = data.flair;
263
270
  this.permissionLevel = data.level;
264
271
  }
265
272
 
package/util/Constants.js CHANGED
@@ -73,6 +73,9 @@ export const OPCodes = {
73
73
  LOCK_ROOM: 'lockroom',
74
74
  UNLOCK_ROOM: 'unlockroom',
75
75
  WHISPER: 'whisper',
76
+ REQUEST_SIW: 'siw',
77
+ SIGN_SIW: 'signsiw',
78
+ CONFIRM_TX: 'confirmtx',
76
79
  };
77
80
 
78
81
  /**
@@ -100,6 +103,10 @@ export const Events = {
100
103
  ERROR: 'error',
101
104
  WARN: 'warn',
102
105
  DEBUG: 'debug',
106
+ PUB_CHANS: 'publicchannels',
107
+ HACK_ATTEMPT: 'hackAttempt',
108
+ SIGN_MESSAGE: 'signMessage',
109
+ SIGN_TRANSACTION: 'signTransaction',
103
110
  };
104
111
 
105
112
  /**
@@ -108,6 +115,7 @@ export const Events = {
108
115
  */
109
116
  export const WSEvents = {
110
117
  SESSION: 'session',
118
+ PUB_CHANS: 'publicchannels',
111
119
  NEW_MESSAGE: 'chat',
112
120
  CHANNEL_INFO: 'info',
113
121
  CHANNEL_EMOTE: 'emote',
@@ -119,4 +127,7 @@ export const WSEvents = {
119
127
  CHANNEL_CAPTCHA: 'captcha',
120
128
  CHANNEL_INVITE: 'invite',
121
129
  CHANNEL_WHISPER: 'whisper',
130
+ HACK_ATTEMPT: 'hackAttempt',
131
+ SIGN_MESSAGE: 'signMessage',
132
+ SIGN_TRANSACTION: 'signTransaction',
122
133
  };
@@ -168,6 +168,11 @@ class SocketHandler extends EventEmitter {
168
168
  * @returns {void}
169
169
  */
170
170
  send(data) {
171
+ if (this.packetQueue.queue.length > 500) {
172
+ this.debug('Packet queue full, dropping message');
173
+ return;
174
+ }
175
+
171
176
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
172
177
  this.debug(`[socket handler] No connection, failed to send: ${data}`);
173
178
  return;
@@ -189,7 +194,7 @@ class SocketHandler extends EventEmitter {
189
194
  this.client.setTimeout(() => {
190
195
  this.connect(gateway, 0, force);
191
196
  },
192
- after);
197
+ after);
193
198
 
194
199
  return false;
195
200
  }
@@ -221,7 +226,7 @@ class SocketHandler extends EventEmitter {
221
226
  }
222
227
 
223
228
  /**
224
- * Destroys the connection.
229
+ * Destroys the connection
225
230
  * @returns {boolean}
226
231
  */
227
232
  destroy() {
@@ -290,9 +295,9 @@ class SocketHandler extends EventEmitter {
290
295
  this.debug(`[socket handler] Connected to gateway ${this.gateway}`);
291
296
 
292
297
  /**
293
- * Emitted when websocket has been connected
294
- * @event Client#connected
295
- */
298
+ * Emitted when websocket has been connected
299
+ * @event Client#connected
300
+ */
296
301
  this.client.emit(Events.CONNECTED, this.client);
297
302
 
298
303
  this.sessionStart();
@@ -337,7 +342,7 @@ class SocketHandler extends EventEmitter {
337
342
  this.closeExpected = false;
338
343
 
339
344
  /**
340
- * Emitted when the client's WebSocket disconnects and will no longer attempt to reconnect.
345
+ * Emitted when the client's WebSocket disconnects and will no longer attempt to reconnect
341
346
  * @event Client#disconnect
342
347
  * @param {CloseEvent} event Close event
343
348
  */
@@ -1,5 +1,9 @@
1
1
  /* eslint global-require: 0 */
2
- import { Status, WSEvents } from '../../util/Constants.js';
2
+ import {
3
+ Status,
4
+ WSEvents,
5
+ Events,
6
+ } from '../../util/Constants.js';
3
7
 
4
8
  import SessionHandler from './handlers/SessionHandler.js';
5
9
  import ChatHandler from './handlers/ChatHandler.js';
@@ -13,9 +17,14 @@ import UserLeaveHandler from './handlers/UserLeaveHandler.js';
13
17
  import UpdateUserHandler from './handlers/UpdateUserHandler.js';
14
18
  import CaptchaHandler from './handlers/CaptchaHandler.js';
15
19
  import WhisperHandler from './handlers/WhisperHandler.js';
20
+ import PubChannelsHandler from './handlers/PubChannelsHandler.js';
21
+ import HackAttemptHandler from './handlers/HackAttemptHandler.js';
22
+ import SignMessageHandler from './handlers/SignMessageHandler.js';
23
+ import SignTransactionHandler from './handlers/SignTransactionHandler.js';
16
24
 
17
25
  const BeforeReadyWhitelist = [
18
26
  WSEvents.SESSION,
27
+ WSEvents.PUB_CHANS,
19
28
  ];
20
29
 
21
30
  /**
@@ -45,6 +54,12 @@ class PacketRouter {
45
54
  */
46
55
  this.queue = [];
47
56
 
57
+ /**
58
+ * Custom handlers registered by the user for unmapped commands
59
+ * @type {Map<string, function(object): void>}
60
+ */
61
+ this.customHandlers = new Map();
62
+
48
63
  // Register all events
49
64
  this.registerEvent(WSEvents.SESSION, SessionHandler);
50
65
  this.registerEvent(WSEvents.NEW_MESSAGE, ChatHandler);
@@ -58,6 +73,10 @@ class PacketRouter {
58
73
  this.registerEvent(WSEvents.USER_UPDATE, UpdateUserHandler);
59
74
  this.registerEvent(WSEvents.CHANNEL_CAPTCHA, CaptchaHandler);
60
75
  this.registerEvent(WSEvents.CHANNEL_WHISPER, WhisperHandler);
76
+ this.registerEvent(WSEvents.PUB_CHANS, PubChannelsHandler);
77
+ this.registerEvent(WSEvents.HACK_ATTEMPT, HackAttemptHandler);
78
+ this.registerEvent(WSEvents.SIGN_MESSAGE, SignMessageHandler);
79
+ this.registerEvent(WSEvents.SIGN_TRANSACTION, SignTransactionHandler);
61
80
  }
62
81
 
63
82
  /**
@@ -69,6 +88,15 @@ class PacketRouter {
69
88
  return this.ws.client;
70
89
  }
71
90
 
91
+ /**
92
+ * Registers a custom command handler
93
+ * @param {string} cmd The command string
94
+ * @param {function(object): void} handler The function to execute
95
+ */
96
+ registerCustomCommand(cmd, handler) {
97
+ this.customHandlers.set(cmd, handler);
98
+ }
99
+
72
100
  /**
73
101
  * Register an event handler module
74
102
  * @param {string} eventname Event name constant
@@ -107,7 +135,17 @@ class PacketRouter {
107
135
  }
108
136
 
109
137
  if (!queue && this.queue.length > 0) this.processQueue();
138
+
110
139
  if (this.handlers[packet.cmd]) return this.handlers[packet.cmd].handle(packet);
140
+
141
+ const customHandler = this.customHandlers.get(packet.cmd);
142
+ if (customHandler) {
143
+ this.client.emit(Events.DEBUG, `[router] Executing custom handler for cmd: ${packet.cmd}`);
144
+ customHandler(packet);
145
+
146
+ return true;
147
+ }
148
+
111
149
  return false;
112
150
  }
113
151
  }
@@ -16,7 +16,7 @@ class EmoteHandler extends AbstractHandler {
16
16
  const response = client.events.Emote.handle(packet);
17
17
 
18
18
  /**
19
- * Emitted when the client recives an emote packet
19
+ * Emitted when the client receives an emote packet
20
20
  * @event Client#emote
21
21
  * @param {Emote} message The created message
22
22
  */
@@ -0,0 +1,30 @@
1
+ import AbstractHandler from './AbstractHandler.js';
2
+ import { Events } from '../../../util/Constants.js';
3
+
4
+ /**
5
+ * Handles an incoming hackAttempt
6
+ * @private
7
+ */
8
+ class HackAttemptHandler extends AbstractHandler {
9
+ /**
10
+ * Parses incoming packet data and emits related events
11
+ * @param {object} packet Incoming packet data
12
+ * @returns {void}
13
+ */
14
+ handle(packet) {
15
+ const { client } = this.packetRouter;
16
+ const response = client.events.HackAttempt.handle(packet);
17
+
18
+ /**
19
+ * Emitted when the client receives a hackAttempt packet
20
+ * @event Client#hackAttempt
21
+ * @param {HackAttemptStruct} message The hack attempt event
22
+ */
23
+ client.emit(Events.HACK_ATTEMPT, response.message);
24
+
25
+ // Emit debug info
26
+ client.emit(Events.DEBUG, `[${Events.HACK_ATTEMPT}]: ${packet}`);
27
+ }
28
+ }
29
+
30
+ export default HackAttemptHandler;
@@ -16,7 +16,7 @@ class InfoHandler extends AbstractHandler {
16
16
  const response = client.events.Info.handle(packet);
17
17
 
18
18
  /**
19
- * Emitted when the client recives an info packet
19
+ * Emitted when the client receives an info packet
20
20
  * @event Client#information
21
21
  * @param {Information} message The created message
22
22
  */
@@ -16,7 +16,7 @@ class InviteHandler extends AbstractHandler {
16
16
  const response = client.events.Invite.handle(packet);
17
17
 
18
18
  /**
19
- * Emitted when the client recives an invite packet
19
+ * Emitted when the client receives an invite packet
20
20
  * @event Client#invite
21
21
  * @param {Invite} message The created message
22
22
  */
@@ -0,0 +1,30 @@
1
+ import AbstractHandler from './AbstractHandler.js';
2
+ import { Events } from '../../../util/Constants.js';
3
+
4
+ /**
5
+ * Handles an incoming public channels event
6
+ * @private
7
+ */
8
+ class PubChannelsHandler extends AbstractHandler {
9
+ /**
10
+ * Parses incoming packet data and emits related events
11
+ * @param {object} packet Incoming packet data
12
+ * @returns {void}
13
+ */
14
+ handle(packet) {
15
+ const { client } = this.packetRouter;
16
+ const response = client.events.PublicChannels.handle(packet);
17
+
18
+ /**
19
+ * Emitted when the client receives a publicchannels packet
20
+ * @event Client#publicchannels
21
+ * @param {PubChannelsStruct} message The publicchannels event
22
+ */
23
+ client.emit(Events.PUB_CHANS, response.message);
24
+
25
+ // Emit debug info
26
+ client.emit(Events.DEBUG, `[${Events.PUB_CHANS}]: ${packet}`);
27
+ }
28
+ }
29
+
30
+ export default PubChannelsHandler;
@@ -0,0 +1,34 @@
1
+ import AbstractHandler from './AbstractHandler.js';
2
+ import { Events } from '../../../util/Constants.js';
3
+
4
+ /**
5
+ * Handles an incoming custom `signMessage` event from the server
6
+ * @private
7
+ */
8
+ class SignMessageHandler extends AbstractHandler {
9
+ /**
10
+ * Parses incoming packet data and emits related events
11
+ * @param {object} packet Incoming packet data
12
+ * @returns {void}
13
+ */
14
+ handle(packet) {
15
+ const { client } = this.packetRouter;
16
+
17
+ /**
18
+ * Emitted when the server requests the client to sign a message
19
+ * This is used for Sign-In with Solana (SIWS) authentication
20
+ * @event Client#signMessage
21
+ * @param {object} payload The payload containing the wallet and message
22
+ * @param {string} payload.wallet The public key of the wallet to sign with
23
+ * @param {string} payload.message The human-readable message to be signed
24
+ */
25
+ client.emit(Events.SIGN_MESSAGE, {
26
+ wallet: packet.wallet,
27
+ message: packet.message,
28
+ });
29
+
30
+ client.emit(Events.DEBUG, `[${Events.SIGN_MESSAGE}]: Request received for wallet ${packet.wallet}`);
31
+ }
32
+ }
33
+
34
+ export default SignMessageHandler;
@@ -0,0 +1,36 @@
1
+ import AbstractHandler from './AbstractHandler.js';
2
+ import { Events } from '../../../util/Constants.js';
3
+
4
+ /**
5
+ * Handles an incoming custom `signTransaction` event from the server
6
+ * @private
7
+ */
8
+ class SignTransactionHandler extends AbstractHandler {
9
+ /**
10
+ * Parses incoming packet data and emits related events
11
+ * @param {object} packet Incoming packet data
12
+ * @returns {void}
13
+ */
14
+ handle(packet) {
15
+ const { client } = this.packetRouter;
16
+
17
+ /**
18
+ * Emitted when the server requests the client to sign a transaction
19
+ * @event Client#signTransaction
20
+ * @param {object} payload The payload containing the tx and type
21
+ * @param {string} payload.tx The base64 encoded transaction payload
22
+ * @param {string} payload.type The transaction type
23
+ * @param {string} payload.channel The channel which invoked the event
24
+ */
25
+ client.emit(Events.SIGN_TRANSACTION, {
26
+ tx: packet.tx,
27
+ type: packet.type,
28
+ channel: packet.channel,
29
+ from: packet.from,
30
+ });
31
+
32
+ client.emit(Events.DEBUG, `[${Events.SIGN_TRANSACTION}]: signTransaction requested`);
33
+ }
34
+ }
35
+
36
+ export default SignTransactionHandler;
@@ -16,7 +16,7 @@ class WhisperHandler extends AbstractHandler {
16
16
  const response = client.events.Whisper.handle(packet);
17
17
 
18
18
  /**
19
- * Emitted when the client recives a whisper packet
19
+ * Emitted when the client receives a whisper packet
20
20
  * @event Client#whisper
21
21
  * @param {WhisperStruct} message The whisper event
22
22
  */