hackchat-engine 1.1.9 → 1.1.11

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
@@ -186,7 +186,8 @@ class Client extends EventEmitter {
186
186
 
187
187
  const success = this.ws.connect(gw);
188
188
  if (!success) {
189
- return reject(new Error('Connection failed to start (Socket likely not IDLE)'));
189
+ reject(new Error('Connection failed to start (Socket likely not IDLE)'));
190
+ return;
190
191
  }
191
192
 
192
193
  this.ws.connection.once('error', reject);
@@ -410,7 +411,8 @@ class Client extends EventEmitter {
410
411
  * Send `updateMessage` operation to the server
411
412
  * @param {string} customId The customId of the target message
412
413
  * @param {string} text The text to apply
413
- * @param {string} [mode='overwrite'] The update mode: 'overwrite', 'append', 'prepend', or 'complete'
414
+ * @param {string} [mode='overwrite'] The update mode:
415
+ * 'overwrite', 'append', 'prepend', or 'complete'
414
416
  */
415
417
  updateMessage(customId, text, mode = 'overwrite') {
416
418
  this.ws.send({
@@ -421,6 +423,25 @@ class Client extends EventEmitter {
421
423
  mode,
422
424
  });
423
425
  }
426
+
427
+ /**
428
+ * Request the public wallet address of a specific user
429
+ * @param {number|string} target The userid (number) or nickname (string) of the user
430
+ */
431
+ getWallet(target) {
432
+ const payload = {
433
+ cmd: OPCodes.GET_WALLET,
434
+ channel: this.channel, // @todo Multichannel
435
+ };
436
+
437
+ if (typeof target === 'number') {
438
+ payload.userid = target;
439
+ } else {
440
+ payload.nick = target;
441
+ }
442
+
443
+ this.ws.send(payload);
444
+ }
424
445
  }
425
446
 
426
447
  export default Client;
package/README.md CHANGED
@@ -1,60 +1,181 @@
1
1
  # hackchat-engine
2
2
 
3
- A NodeJS and browser friendly JavaScript library used to interact with a hackchat server.
3
+ A modern, NodeJS and browser-friendly ES6 WebSocket library for interacting with [hack.chat](https://hack.chat) servers. This engine provides an object-oriented interface, automatic keep-alives, and structured event handling.
4
4
 
5
- # Installation
5
+ ## Installation
6
6
 
7
- ## Prerequisites
7
+ ### Prerequisites
8
8
 
9
- - [node.js 14.15](https://nodejs.org/en/download/package-manager/) or higher
10
- - [npm 6](https://nodejs.org/en/download/package-manager/) or higher
9
+ * **Node.js 14+** (This package uses ES Modules)
10
+ * **NPM 6+**
11
11
 
12
- ## Install
12
+ ### Install
13
13
 
14
- `npm i hackchat-engine`
14
+ ```bash
15
+ npm install hackchat-engine
15
16
 
16
- # Usage
17
+ ```
18
+
19
+ ---
17
20
 
18
- ## Minimum Usage
21
+ ## Quick Start
22
+
23
+ This library uses **ES Modules**. You must use `import` syntax.
19
24
 
20
25
  ```javascript
21
- const { Client } = require('hackchat-engine');
22
- const hcClient = new Client();
26
+ import { Client } from 'hackchat-engine';
23
27
 
24
- const testName = 'testBot';
25
- const testPass = 'testBot';
26
- const testChannel = 'programming';
28
+ const botName = 'MyBot';
29
+ const botPass = 'secretPassword';
30
+ const channel = 'programming';
27
31
 
28
- hcClient.on('connected', () => console.log('Connected!'));
32
+ // Initialize the client
33
+ const client = new Client({
34
+ debug: true
35
+ });
29
36
 
30
- hcClient.on('session', (payload) => {
31
- console.log(payload);
32
- hcClient.join(testName, testPass, testChannel);
33
- });
37
+ // Triggered when the WebSocket connects to the gateway
38
+ client.on('connected', () => {
39
+ console.log('Connected to server!');
40
+ client.ws.send({ cmd: 'getchannels' });
41
+ client.join(botName, botPass, channel);
42
+ });
34
43
 
35
- hcClient.on('channelJoined', (payload) => {
36
- console.log(payload);
37
- hcClient.say(testChannel, 'Bep boop i r bot');
38
- });
44
+ // Triggered when the server assigns a session ID
45
+ client.on('session', (session) => {
46
+ console.log(`Session ID: ${session.sessionID}`);
47
+ });
39
48
 
40
- hcClient.on('message', (payload) => {
41
- console.log(payload);
42
- hcClient.say(testChannel, 'No u');
43
- });
44
- ```
49
+ // Triggered when the client successfully joins the channel
50
+ client.on('channelJoined', (data) => {
51
+ console.log(`Joined channel: ${data.channel}`);
52
+ client.say(channel, 'Hello world! I am a bot.');
53
+ });
45
54
 
46
- ## Advanced Usage
55
+ // Triggered on every new message
56
+ client.on('message', (message) => {
57
+ // Ignore our own messages
58
+ if (message.user.isMine) return;
47
59
 
48
- ** Need to update this, still **
60
+ console.log(`[${message.channel}] ${message.user.name}: ${message.content}`);
61
+
62
+ if (message.content === '!ping') {
63
+ // Helper method to reply directly to the channel
64
+ message.reply('Pong!');
65
+ }
66
+ });
67
+
68
+ ```
49
69
 
50
- ### Changing the connection
70
+ ---
51
71
 
52
- By default, this engine will connect to 'wss://hack.chat/chat-ws'. To change this, add a `ws.gateway` property to the `options` object, for example:
72
+ ## Configuration
73
+
74
+ You can pass an options object to the `Client` constructor to customize the connection.
53
75
 
54
76
  ```javascript
55
- const hcClient = new Client({
77
+ const client = new Client({
78
+ // Connection details
56
79
  ws: {
57
- gateway: 'ws://1.1.1.1:6060/',
58
- }
80
+ gateway: 'wss://hack.chat/chat-ws', // Default
81
+ // gateway: 'ws://127.0.0.1:6060/' // For local development
82
+ },
83
+
84
+ // Set to true to see internal logs and raw packets
85
+ debug: false,
86
+
87
+ // If true, identifies as a bot to the server (default: true)
88
+ isBot: true,
89
+ });
90
+
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Events
96
+
97
+ The client extends `EventEmitter`. You can listen for the following events:
98
+
99
+ | Event | Payload | Description |
100
+ | --- | --- | --- |
101
+ | `connected` | `Client` | Socket connection established. |
102
+ | `session` | `SessionStruct` | Server has assigned a session ID. |
103
+ | `channelJoined` | `Object` | Client has successfully joined a channel and received the user list. |
104
+ | `message` | `MessageStruct` | A new message was received. |
105
+ | `userJoined` | `UserStruct` | A user joined the channel. |
106
+ | `userLeft` | `UserStruct` | A user left the channel. |
107
+ | `userUpdate` | `UserStruct` | A user changed their flair, color, or status. |
108
+ | `whisper` | `WhisperStruct` | Received a private whisper. |
109
+ | `invite` | `InviteStruct` | Received an invitation to another channel. |
110
+ | `emote` | `EmoteStruct` | A user sent a `/me` emote. |
111
+ | `warning` | `WarningStruct` | Server sent a warning (e.g., rate limit). |
112
+ | `gotCaptcha` | `CaptchaStruct` | Server requires a captcha solution. |
113
+ | `hackAttempt` | `HackAttemptStruct` | A user attempted a disallowed action (admin alerts). |
114
+ | `updateMessage` | `UpdateMessageStruct` | A previously sent message was edited or deleted. |
115
+
116
+ ---
117
+
118
+ ## Key Methods
119
+
120
+ ### Core
121
+
122
+ * **`client.join(nick, password, channel)`**: Authenticate and join a specific channel.
123
+ * **`client.say(channel, text)`**: Send a message to a channel.
124
+ * **`client.changeColor(hex)`**: Change your nickname color (e.g., `CCCCCC`).
125
+ * **`client.changeUsername(newNick)`**: Request a nickname change.
126
+
127
+ ### Moderation
128
+
129
+ * **`client.kick(channel, user)`**: Kick a user (requires permissions).
130
+ * **`client.ban(channel, user)`**: Ban a user (requires permissions).
131
+ * **`client.enableCaptcha(channel)`**: Turn on captcha for the channel.
132
+ * **`client.lockChannel(channel)`**: Lock the channel (admin only).
133
+
134
+ ### Interactions
135
+
136
+ The `MessageStruct` (passed in the `message` event) has helper methods:
137
+
138
+ * **`message.reply(text)`**: Automatically replies to the channel the message originated from.
139
+
140
+ ### Message Manipulation
141
+
142
+ * **`client.updateMessage(customId, text, mode)`**: Edit a message you previously sent.
143
+ * `mode`: `'overwrite'`, `'append'`, `'prepend'`, or `'complete'` (delete).
144
+
145
+
146
+
147
+ ---
148
+
149
+ ## Advanced Usage
150
+
151
+ ### Handling Custom Commands
152
+
153
+ If the server implements custom opcodes that are not natively handled by the engine, you can register a listener for them:
154
+
155
+ ```javascript
156
+ client.onCommand('customEvent', (payload) => {
157
+ console.log('Received custom packet:', payload);
59
158
  });
159
+
60
160
  ```
161
+
162
+ ### Accessing Users
163
+
164
+ Users are stored in an extended Map for easy access.
165
+
166
+ ```javascript
167
+ // Get a user by ID
168
+ const user = client.users.get(12345);
169
+
170
+ // Find a user by name
171
+ const user = client.users.find(u => u.username === 'Admin');
172
+
173
+ if (user) {
174
+ console.log(`${user.username} is currently ${user.isOnline ? 'Online' : 'Offline'}`);
175
+ }
176
+
177
+ ```
178
+
179
+ ## License
180
+
181
+ MIT
@@ -14,6 +14,8 @@ import PublicChannels from './PublicChannels.js';
14
14
  import HackAttempt from './HackAttempt.js';
15
15
  import SignMessage from './SignMessage.js';
16
16
  import SignTransaction from './SignTransaction.js';
17
+ import UpdateMessage from './UpdateMessage.js';
18
+ import WalletInfo from './WalletInfo.js';
17
19
 
18
20
  /**
19
21
  * This class routes incoming event data to it's proper handler
@@ -43,6 +45,7 @@ class EventsManager {
43
45
  this.SignMessage = new SignMessage(this.client);
44
46
  this.SignTransaction = new SignTransaction(this.client);
45
47
  this.UpdateMessage = new UpdateMessage(this.client);
48
+ this.WalletInfo = new WalletInfo(this.client);
46
49
  }
47
50
  }
48
51
 
@@ -0,0 +1,24 @@
1
+ import AbstractEvent from './AbstractEvent.js';
2
+ import WalletInfoStruct from '../structures/WalletInfoStruct.js';
3
+
4
+ /**
5
+ * This class handles an incoming `walletInfo` event from the server
6
+ * @private
7
+ */
8
+ class WalletInfo 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 info = new WalletInfoStruct(client, data);
17
+
18
+ return {
19
+ info,
20
+ };
21
+ }
22
+ }
23
+
24
+ export default WalletInfo;
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.9"
62
+ "version": "1.1.11"
63
63
  }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * This class handles parsing of the data of a `walletInfo` event
3
+ */
4
+ class WalletInfoStruct {
5
+ /**
6
+ * @param {Client} client Main client reference
7
+ * @param {object} data Incoming event data
8
+ */
9
+ constructor(client, data) {
10
+ /**
11
+ * Add client reference
12
+ * @type {Client}
13
+ * @readonly
14
+ */
15
+ Object.defineProperty(this, 'client', { value: client });
16
+
17
+ if (data) this.setup(data);
18
+ }
19
+
20
+ /**
21
+ * Fill in this structure with provided data
22
+ * @param {object} data Incoming event data
23
+ * @returns {void}
24
+ */
25
+ setup(data) {
26
+ /**
27
+ * The channel where the request originated
28
+ * @type {string}
29
+ */
30
+ this.channel = data.channel;
31
+
32
+ /**
33
+ * The user ID of the target user
34
+ * @type {number}
35
+ */
36
+ this.userid = data.userid;
37
+
38
+ /**
39
+ * The nickname of the target user
40
+ * @type {string}
41
+ */
42
+ this.nick = data.nick;
43
+
44
+ /**
45
+ * The public wallet address of the user
46
+ * @type {string}
47
+ */
48
+ this.address = data.address;
49
+
50
+ /**
51
+ * The user object associated with this wallet info
52
+ * @type {User|undefined}
53
+ */
54
+ this.user = this.client.users.get(data.userid);
55
+ }
56
+ }
57
+
58
+ export default WalletInfoStruct;
package/util/Constants.js CHANGED
@@ -77,6 +77,7 @@ export const OPCodes = {
77
77
  SIGN_SIW: 'signsiw',
78
78
  CONFIRM_TX: 'confirmtx',
79
79
  UPDATE_MESSAGE: 'updateMessage',
80
+ GET_WALLET: 'getwallet',
80
81
  };
81
82
 
82
83
  /**
@@ -108,6 +109,7 @@ export const Events = {
108
109
  HACK_ATTEMPT: 'hackAttempt',
109
110
  SIGN_MESSAGE: 'signMessage',
110
111
  SIGN_TRANSACTION: 'signTransaction',
112
+ WALLET_INFO: 'walletInfo',
111
113
  };
112
114
 
113
115
  /**
@@ -132,4 +134,5 @@ export const WSEvents = {
132
134
  SIGN_MESSAGE: 'signMessage',
133
135
  SIGN_TRANSACTION: 'signTransaction',
134
136
  UPDATE_MESSAGE: 'updateMessage',
137
+ WALLET_INFO: 'walletInfo',
135
138
  };
@@ -194,7 +194,7 @@ class SocketHandler extends EventEmitter {
194
194
  this.client.setTimeout(() => {
195
195
  this.connect(gateway, 0, force);
196
196
  },
197
- after);
197
+ after);
198
198
 
199
199
  return false;
200
200
  }
@@ -22,6 +22,7 @@ import HackAttemptHandler from './handlers/HackAttemptHandler.js';
22
22
  import SignMessageHandler from './handlers/SignMessageHandler.js';
23
23
  import SignTransactionHandler from './handlers/SignTransactionHandler.js';
24
24
  import UpdateMessageHandler from './handlers/UpdateMessageHandler.js';
25
+ import WalletInfoHandler from './handlers/WalletInfoHandler.js';
25
26
 
26
27
  const BeforeReadyWhitelist = [
27
28
  WSEvents.SESSION,
@@ -79,6 +80,7 @@ class PacketRouter {
79
80
  this.registerEvent(WSEvents.SIGN_MESSAGE, SignMessageHandler);
80
81
  this.registerEvent(WSEvents.SIGN_TRANSACTION, SignTransactionHandler);
81
82
  this.registerEvent(WSEvents.UPDATE_MESSAGE, UpdateMessageHandler);
83
+ this.registerEvent(WSEvents.WALLET_INFO, WalletInfoHandler);
82
84
  }
83
85
 
84
86
  /**
@@ -0,0 +1,30 @@
1
+ import AbstractHandler from './AbstractHandler.js';
2
+ import { Events } from '../../../util/Constants.js';
3
+
4
+ /**
5
+ * Handles a walletInfo packet received from the server
6
+ * @private
7
+ */
8
+ class WalletInfoHandler 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.WalletInfo.handle(packet);
17
+
18
+ /**
19
+ * Emitted when wallet information is received
20
+ * @event Client#walletInfo
21
+ * @param {WalletInfoStruct} info The wallet information
22
+ */
23
+ client.emit(Events.WALLET_INFO, response.info);
24
+
25
+ // Emit debug info
26
+ client.emit(Events.DEBUG, `[${Events.WALLET_INFO}]: ${packet}`);
27
+ }
28
+ }
29
+
30
+ export default WalletInfoHandler;