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 +23 -2
- package/README.md +156 -35
- package/events/EventsManager.js +3 -0
- package/events/WalletInfo.js +24 -0
- package/package.json +1 -1
- package/structures/WalletInfoStruct.js +58 -0
- package/util/Constants.js +3 -0
- package/websocket/SocketHandler.js +1 -1
- package/websocket/packets/PacketRouter.js +2 -0
- package/websocket/packets/handlers/WalletInfoHandler.js +30 -0
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Prerequisites
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
* **Node.js 14+** (This package uses ES Modules)
|
|
10
|
+
* **NPM 6+**
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
### Install
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npm install hackchat-engine
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
17
20
|
|
|
18
|
-
##
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
This library uses **ES Modules**. You must use `import` syntax.
|
|
19
24
|
|
|
20
25
|
```javascript
|
|
21
|
-
|
|
22
|
-
const hcClient = new Client();
|
|
26
|
+
import { Client } from 'hackchat-engine';
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const botName = 'MyBot';
|
|
29
|
+
const botPass = 'secretPassword';
|
|
30
|
+
const channel = 'programming';
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
// Initialize the client
|
|
33
|
+
const client = new Client({
|
|
34
|
+
debug: true
|
|
35
|
+
});
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
+
---
|
|
51
71
|
|
|
52
|
-
|
|
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
|
|
77
|
+
const client = new Client({
|
|
78
|
+
// Connection details
|
|
56
79
|
ws: {
|
|
57
|
-
gateway: '
|
|
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
|
package/events/EventsManager.js
CHANGED
|
@@ -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
|
@@ -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
|
};
|
|
@@ -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;
|