hackchat-engine 1.1.15 → 1.1.17
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/.eslintrc.json +27 -27
- package/Client.js +8 -10
- package/README.md +174 -181
- package/events/OnlineSet.js +26 -9
- package/events/Session.js +0 -1
- package/events/UpdateUser.js +7 -1
- package/events/UserJoin.js +4 -4
- package/events/UserLeave.js +7 -1
- package/example.js +261 -0
- package/index.js +2 -1
- package/package.json +63 -63
- package/structures/SessionStruct.js +0 -1
- package/structures/UserStruct.js +44 -33
- package/structures/WarningStruct.js +0 -4
- package/structures/WhisperStruct.js +6 -0
- package/websocket/packets/PacketRouter.js +7 -4
- package/websocket/packets/handlers/OnlineSetHandler.js +1 -0
- package/websocket/packets/handlers/SessionHandler.js +11 -9
- package/test.js +0 -45
package/.eslintrc.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"env": {
|
|
3
|
-
"browser": true,
|
|
4
|
-
"es6": true,
|
|
5
|
-
"node": true
|
|
6
|
-
},
|
|
7
|
-
"extends": [
|
|
8
|
-
"airbnb-base"
|
|
9
|
-
],
|
|
10
|
-
"globals": {
|
|
11
|
-
"Atomics": "readonly",
|
|
12
|
-
"SharedArrayBuffer": "readonly"
|
|
13
|
-
},
|
|
14
|
-
"parserOptions": {
|
|
15
|
-
"ecmaVersion": 2018,
|
|
16
|
-
"sourceType": "module"
|
|
17
|
-
},
|
|
18
|
-
"rules": {
|
|
19
|
-
"import/extensions": [
|
|
20
|
-
"error",
|
|
21
|
-
"ignorePackages",
|
|
22
|
-
{
|
|
23
|
-
"js": "always"
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": true,
|
|
4
|
+
"es6": true,
|
|
5
|
+
"node": true
|
|
6
|
+
},
|
|
7
|
+
"extends": [
|
|
8
|
+
"airbnb-base"
|
|
9
|
+
],
|
|
10
|
+
"globals": {
|
|
11
|
+
"Atomics": "readonly",
|
|
12
|
+
"SharedArrayBuffer": "readonly"
|
|
13
|
+
},
|
|
14
|
+
"parserOptions": {
|
|
15
|
+
"ecmaVersion": 2018,
|
|
16
|
+
"sourceType": "module"
|
|
17
|
+
},
|
|
18
|
+
"rules": {
|
|
19
|
+
"import/extensions": [
|
|
20
|
+
"error",
|
|
21
|
+
"ignorePackages",
|
|
22
|
+
{
|
|
23
|
+
"js": "always"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
package/Client.js
CHANGED
|
@@ -150,9 +150,6 @@ class Client extends EventEmitter {
|
|
|
150
150
|
throw new Error(Errors.INVALID_CHANNEL);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
// @todo: this will be changed with the multichannel patch
|
|
154
|
-
this.channel = channel;
|
|
155
|
-
|
|
156
153
|
this.ws.send({
|
|
157
154
|
cmd: OPCodes.JOIN,
|
|
158
155
|
nick: name,
|
|
@@ -411,13 +408,13 @@ class Client extends EventEmitter {
|
|
|
411
408
|
* Send `updateMessage` operation to the server
|
|
412
409
|
* @param {string} customId The customId of the target message
|
|
413
410
|
* @param {string} text The text to apply
|
|
414
|
-
* @param {string}
|
|
415
|
-
*
|
|
411
|
+
* @param {string} channel Target channel
|
|
412
|
+
* @param {string} [mode='overwrite|append|prepend|complete'] The update mode
|
|
416
413
|
*/
|
|
417
|
-
updateMessage(customId, text, mode = 'overwrite') {
|
|
414
|
+
updateMessage(customId, text, channel, mode = 'overwrite') {
|
|
418
415
|
this.ws.send({
|
|
419
416
|
cmd: OPCodes.UPDATE_MESSAGE,
|
|
420
|
-
channel
|
|
417
|
+
channel,
|
|
421
418
|
customId,
|
|
422
419
|
text,
|
|
423
420
|
mode,
|
|
@@ -427,11 +424,12 @@ class Client extends EventEmitter {
|
|
|
427
424
|
/**
|
|
428
425
|
* Request the public wallet address of a specific user
|
|
429
426
|
* @param {number|string} target The userid (number) or nickname (string) of the user
|
|
427
|
+
* @param {string} channel Target channel
|
|
430
428
|
*/
|
|
431
|
-
getWallet(target) {
|
|
429
|
+
getWallet(target, channel) {
|
|
432
430
|
const payload = {
|
|
433
431
|
cmd: OPCodes.GET_WALLET,
|
|
434
|
-
channel
|
|
432
|
+
channel,
|
|
435
433
|
};
|
|
436
434
|
|
|
437
435
|
if (typeof target === 'number') {
|
|
@@ -450,7 +448,7 @@ class Client extends EventEmitter {
|
|
|
450
448
|
leave(channel) {
|
|
451
449
|
this.ws.send({
|
|
452
450
|
cmd: OPCodes.LEAVE,
|
|
453
|
-
channel
|
|
451
|
+
channel,
|
|
454
452
|
});
|
|
455
453
|
}
|
|
456
454
|
}
|
package/README.md
CHANGED
|
@@ -1,181 +1,174 @@
|
|
|
1
|
-
# hackchat-engine
|
|
2
|
-
|
|
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
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
### Prerequisites
|
|
8
|
-
|
|
9
|
-
* **Node.js 14+** (This package uses ES Modules)
|
|
10
|
-
* **NPM 6+**
|
|
11
|
-
|
|
12
|
-
### Install
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install hackchat-engine
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Quick Start
|
|
22
|
-
|
|
23
|
-
This library uses **ES Modules**. You must use `import` syntax.
|
|
24
|
-
|
|
25
|
-
```javascript
|
|
26
|
-
import { Client } from 'hackchat-engine';
|
|
27
|
-
|
|
28
|
-
const botName = 'MyBot';
|
|
29
|
-
const botPass = 'secretPassword';
|
|
30
|
-
const channel = 'programming';
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
const client = new Client({
|
|
34
|
-
debug:
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
client.on(
|
|
39
|
-
console.log(
|
|
40
|
-
|
|
41
|
-
client.join(botName, botPass, channel);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
client.on(
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
client.on(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
###
|
|
121
|
-
|
|
122
|
-
* **`client.
|
|
123
|
-
* **`client.
|
|
124
|
-
* **`client.
|
|
125
|
-
* **`client.
|
|
126
|
-
|
|
127
|
-
###
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
* **`
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## License
|
|
180
|
-
|
|
181
|
-
MIT
|
|
1
|
+
# hackchat-engine
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
* **Node.js 14+** (This package uses ES Modules)
|
|
10
|
+
* **NPM 6+**
|
|
11
|
+
|
|
12
|
+
### Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install hackchat-engine
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
This library uses **ES Modules**. You must use `import` syntax.
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { Client, Events } from 'hackchat-engine';
|
|
27
|
+
|
|
28
|
+
const botName = 'MyBot';
|
|
29
|
+
const botPass = 'secretPassword';
|
|
30
|
+
const channel = 'programming';
|
|
31
|
+
|
|
32
|
+
// initialize the client
|
|
33
|
+
const client = new Client({
|
|
34
|
+
debug: false,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// triggered when the server establishes a session
|
|
38
|
+
client.on(Events.SESSION, (session) => {
|
|
39
|
+
console.log(`Session established! Token: ${session.token}`);
|
|
40
|
+
// join the channel once the session is ready
|
|
41
|
+
client.join(botName, botPass, channel);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// triggered when the client successfully joins the channel
|
|
45
|
+
client.on(Events.ONLINE_SET, (data) => {
|
|
46
|
+
client.say(data.channel, 'Hello world! I am a bot.');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// triggered on every new message
|
|
50
|
+
client.on(Events.NEW_MESSAGE, (message) => {
|
|
51
|
+
if (message.user.username === botName) return;
|
|
52
|
+
|
|
53
|
+
if (message.content === '!ping') {
|
|
54
|
+
client.say(message.channel, 'Pong!');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For a more in-depth example:
|
|
61
|
+
|
|
62
|
+
**[./example.js](./example.js)**
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
You can pass an options object to the `Client` constructor to customize the connection.
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
const client = new Client({
|
|
71
|
+
// Connection details
|
|
72
|
+
ws: {
|
|
73
|
+
gateway: 'wss://hack.chat/chat-ws', // Default
|
|
74
|
+
// gateway: 'ws://127.0.0.1:6060/' // For local development
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Set to true to see internal logs and raw packets (default: false)
|
|
78
|
+
debug: false,
|
|
79
|
+
|
|
80
|
+
// If true, identifies as a bot to the server (default: true)
|
|
81
|
+
isBot: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Events
|
|
89
|
+
|
|
90
|
+
The client extends `EventEmitter`. You can listen for the following events:
|
|
91
|
+
|
|
92
|
+
| Event | Payload | Description |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| `connected` | `Client` | Socket connection established. |
|
|
95
|
+
| `session` | `SessionStruct` | Server has assigned a session ID. |
|
|
96
|
+
| `channelJoined` | `Object` | Client has successfully joined a channel and received the user list. |
|
|
97
|
+
| `message` | `MessageStruct` | A new message was received. |
|
|
98
|
+
| `userJoined` | `UserStruct` | A user joined the channel. |
|
|
99
|
+
| `userLeft` | `UserStruct` | A user left the channel. |
|
|
100
|
+
| `userUpdate` | `UserStruct` | A user changed their flair, color, or status. |
|
|
101
|
+
| `whisper` | `WhisperStruct` | Received a private whisper. |
|
|
102
|
+
| `invite` | `InviteStruct` | Received an invitation to another channel. |
|
|
103
|
+
| `emote` | `EmoteStruct` | A user sent a `/me` emote. |
|
|
104
|
+
| `warning` | `WarningStruct` | Server sent a warning (e.g., rate limit). |
|
|
105
|
+
| `gotCaptcha` | `CaptchaStruct` | Server requires a captcha solution. |
|
|
106
|
+
| `hackAttempt` | `HackAttemptStruct` | A client upgrade request attempt. |
|
|
107
|
+
| `updateMessage` | `UpdateMessageStruct` | A previously sent message was edited or deleted. |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Key Methods
|
|
112
|
+
|
|
113
|
+
### Core
|
|
114
|
+
|
|
115
|
+
* **`client.join(nick, password, channel)`**: Authenticate and join a specific channel.
|
|
116
|
+
* **`client.say(channel, text)`**: Send a message to a channel.
|
|
117
|
+
* **`client.changeColor(hex)`**: Change your nickname color (e.g., `CCCCCC`).
|
|
118
|
+
* **`client.changeUsername(newNick)`**: Request a nickname change.
|
|
119
|
+
|
|
120
|
+
### Moderation
|
|
121
|
+
|
|
122
|
+
* **`client.kick(channel, user)`**: Kick a user (requires permissions).
|
|
123
|
+
* **`client.ban(channel, user)`**: Ban a user (requires permissions).
|
|
124
|
+
* **`client.enableCaptcha(channel)`**: Turn on captcha for the channel.
|
|
125
|
+
* **`client.lockChannel(channel)`**: Lock the channel (admin only).
|
|
126
|
+
|
|
127
|
+
### Interactions
|
|
128
|
+
|
|
129
|
+
The `MessageStruct` (passed in the `message` event) has helper methods:
|
|
130
|
+
|
|
131
|
+
* **`message.reply(text)`**: Automatically replies to the channel the message originated from.
|
|
132
|
+
|
|
133
|
+
### Message Manipulation
|
|
134
|
+
|
|
135
|
+
* **`client.updateMessage(customId, text, mode)`**: Edit a message you previously sent.
|
|
136
|
+
* `mode`: `'overwrite'`, `'append'`, `'prepend'`, or `'complete'` (delete).
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Advanced Usage
|
|
143
|
+
|
|
144
|
+
### Handling Custom Commands
|
|
145
|
+
|
|
146
|
+
If the server implements custom opcodes that are not natively handled by the engine, you can register a listener for them:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
client.onCommand('customEvent', (payload) => {
|
|
150
|
+
console.log('Received custom packet:', payload);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Accessing Users
|
|
156
|
+
|
|
157
|
+
Users are stored in an extended Map for easy access.
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
// Get a user by ID
|
|
161
|
+
const user = client.users.get(12345);
|
|
162
|
+
|
|
163
|
+
// Find a user by name
|
|
164
|
+
const user = client.users.find(u => u.username === 'Admin');
|
|
165
|
+
|
|
166
|
+
if (user) {
|
|
167
|
+
console.log(`${user.username} is currently ${user.isOnline ? 'Online' : 'Offline'}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT
|
package/events/OnlineSet.js
CHANGED
|
@@ -4,8 +4,6 @@ import UserStruct from '../structures/UserStruct.js';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* This class handles an incoming `onlineSet` event from the server
|
|
7
|
-
* @todo Change user handling for multi-channel patch. What if we removed
|
|
8
|
-
* this event entirely and funneled everything through the `join` event?
|
|
9
7
|
* @private
|
|
10
8
|
*/
|
|
11
9
|
class OnlineSet extends AbstractEvent {
|
|
@@ -16,20 +14,39 @@ class OnlineSet extends AbstractEvent {
|
|
|
16
14
|
*/
|
|
17
15
|
handle(data) {
|
|
18
16
|
const { client } = this;
|
|
17
|
+
const incomingChannel = data.channel;
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
* Patch user array into online users
|
|
22
|
-
* @todo This will be changed with the multi-channel patch
|
|
23
|
-
* @legacy
|
|
24
|
-
*/
|
|
25
|
-
data.users.map((userData) => {
|
|
19
|
+
data.users.forEach((userData) => {
|
|
26
20
|
if (userData.isme) {
|
|
27
21
|
if (!client.myUser) {
|
|
22
|
+
// eslint-disable-next-line no-param-reassign
|
|
23
|
+
userData.channel = incomingChannel;
|
|
28
24
|
client.myUser = new ClientStruct(client, userData);
|
|
25
|
+
} else {
|
|
26
|
+
if (client.myUser.channels) {
|
|
27
|
+
client.myUser.channels.add(incomingChannel);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
client.myUser.updateUser(userData);
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
const existingUser = client.users.get(userData.userid);
|
|
35
|
+
|
|
36
|
+
if (existingUser) {
|
|
37
|
+
if (existingUser.channels) {
|
|
38
|
+
existingUser.channels.add(incomingChannel);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
existingUser.updateUser(userData);
|
|
42
|
+
existingUser.online = true;
|
|
43
|
+
} else {
|
|
44
|
+
// eslint-disable-next-line no-param-reassign
|
|
45
|
+
userData.channel = incomingChannel;
|
|
46
|
+
|
|
47
|
+
const newUser = new UserStruct(client, userData);
|
|
48
|
+
client.users.set(userData.userid, newUser);
|
|
49
|
+
}
|
|
33
50
|
});
|
|
34
51
|
}
|
|
35
52
|
}
|
package/events/Session.js
CHANGED
package/events/UpdateUser.js
CHANGED
|
@@ -14,7 +14,13 @@ class UpdateUser extends AbstractEvent {
|
|
|
14
14
|
const { client } = this;
|
|
15
15
|
const user = client.users.get(data.userid);
|
|
16
16
|
|
|
17
|
-
user
|
|
17
|
+
if (user) {
|
|
18
|
+
user.updateUser(data);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (client.myUser && client.myUser.userid === data.userid) {
|
|
22
|
+
client.myUser.updateUser(data);
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
return user;
|
|
20
26
|
}
|
package/events/UserJoin.js
CHANGED
|
@@ -13,17 +13,17 @@ class UserJoin extends AbstractEvent {
|
|
|
13
13
|
*/
|
|
14
14
|
handle(data) {
|
|
15
15
|
const { client } = this;
|
|
16
|
-
// Check if user is already stored
|
|
17
16
|
const user = client.users.get(data.userid);
|
|
18
17
|
|
|
19
|
-
// If stored, set them to online
|
|
20
18
|
if (user) {
|
|
21
|
-
user.
|
|
19
|
+
user.channels.add(data.channel);
|
|
20
|
+
user.online = true;
|
|
21
|
+
user.channel = data.channel;
|
|
22
22
|
return user;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// If not, then add them to the records
|
|
26
25
|
const newUser = new User(client, data);
|
|
26
|
+
newUser.channel = data.channel;
|
|
27
27
|
client.users.set(data.userid, newUser);
|
|
28
28
|
return newUser;
|
|
29
29
|
}
|
package/events/UserLeave.js
CHANGED
|
@@ -14,7 +14,13 @@ class UserLeave extends AbstractEvent {
|
|
|
14
14
|
const { client } = this;
|
|
15
15
|
const user = client.users.get(data.userid);
|
|
16
16
|
|
|
17
|
-
user
|
|
17
|
+
if (user) {
|
|
18
|
+
user.channels.delete(data.channel);
|
|
19
|
+
|
|
20
|
+
if (user.channels.size === 0) {
|
|
21
|
+
user.online = false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
return user;
|
|
20
26
|
}
|
package/example.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
// remember to npm install hackchat-engine
|
|
4
|
+
// import { Client, Events } from 'hackchat-engine'; <-- use this one
|
|
5
|
+
// import { Client as HcClient, Events as HcEvents } from 'hackchat-engine'; <-- or this one
|
|
6
|
+
import { Client, Events } from './index.js'; // <-- not this one
|
|
7
|
+
|
|
8
|
+
const CONFIG = {
|
|
9
|
+
gateway: 'wss://hack.chat/chat-ws',
|
|
10
|
+
botName: 'ExampleBot',
|
|
11
|
+
password: 'botpassword',
|
|
12
|
+
homeChannel: 'test',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// init
|
|
16
|
+
let sessionToken = null;
|
|
17
|
+
|
|
18
|
+
const client = new Client({
|
|
19
|
+
ws: { gateway: CONFIG.gateway },
|
|
20
|
+
debug: false, // set to true to see internal debug logs
|
|
21
|
+
session: sessionToken,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log(`Starting ${CONFIG.botName}`);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 1. Connection & session events
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fired when the WebSocket successfully opens
|
|
32
|
+
*/
|
|
33
|
+
client.on(Events.CONNECTED, () => {
|
|
34
|
+
console.log('[Connection] WebSocket connected. Negotiating session...');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Fired when the server provides a session token
|
|
39
|
+
* This is the "login success" event
|
|
40
|
+
*/
|
|
41
|
+
client.on(Events.SESSION, (payload) => {
|
|
42
|
+
if (payload.token) {
|
|
43
|
+
sessionToken = payload.token;
|
|
44
|
+
console.log(`[Session] Token received: ${payload.token.substring(0, 10)}...`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// join the home channel once we have a session
|
|
48
|
+
console.log(`[System] Joining ${CONFIG.homeChannel}...`);
|
|
49
|
+
client.join(CONFIG.botName, CONFIG.password, CONFIG.homeChannel);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fired when the client is fully ready
|
|
54
|
+
*/
|
|
55
|
+
client.on(Events.READY, () => {
|
|
56
|
+
console.log('[System] Client is Ready');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Fired when the socket disconnects
|
|
61
|
+
*/
|
|
62
|
+
client.on(Events.DISCONNECT, (event) => {
|
|
63
|
+
console.log(`[Connection] Disconnected. Code: ${event.code}`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fired when the client attempts to auto-reconnect
|
|
68
|
+
*/
|
|
69
|
+
client.on(Events.RECONNECTING, () => {
|
|
70
|
+
console.log('[Connection] Connection lost. Attempting to reconnect...');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 2. Channel & user state events
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Fired when the client successfully joins a channel
|
|
79
|
+
* Contains the channel name and the initial list of users
|
|
80
|
+
*/
|
|
81
|
+
client.on(Events.ONLINE_SET, (payload) => {
|
|
82
|
+
console.log(`[Channel] Joined '${payload.channel}'. Users: ${payload.users.length}`);
|
|
83
|
+
|
|
84
|
+
// optional: announce arrival
|
|
85
|
+
client.say(payload.channel, `Hello! I am ${CONFIG.botName}.`);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Fired when a user joins a channel
|
|
90
|
+
*/
|
|
91
|
+
client.on(Events.USER_JOIN, (user) => {
|
|
92
|
+
console.log(`[Presence] ${user.username} joined ${user.channel}.`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Fired when a user leaves a channel
|
|
97
|
+
*/
|
|
98
|
+
client.on(Events.USER_LEAVE, (user) => {
|
|
99
|
+
console.log(`[Presence] ${user.username} left ${user.channel}.`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Fired when a user updates their details (change nick, color, trip, etc)
|
|
104
|
+
*/
|
|
105
|
+
client.on(Events.USER_UPDATE, (user) => {
|
|
106
|
+
console.log(`[Presence] User updated: ${user.username} (Trip: ${user.trip || 'None'})`);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Fired when the server sends the list of public channels
|
|
111
|
+
*/
|
|
112
|
+
client.on(Events.PUB_CHANS, (payload) => {
|
|
113
|
+
console.log(`[Info] Received public channel list: ${payload.list.length} channels.`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Fired when the server sends an informational message
|
|
118
|
+
*/
|
|
119
|
+
client.on(Events.CHANNEL_INFO, (info) => {
|
|
120
|
+
console.log(`[Info] Server sez: "${info.text}" in ${info.channel}`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 3. Chat & interaction events
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Fired on standard chat messages
|
|
129
|
+
*/
|
|
130
|
+
client.on(Events.NEW_MESSAGE, (msg) => {
|
|
131
|
+
// ignore our own messages
|
|
132
|
+
if (msg.user.username === CONFIG.botName) return;
|
|
133
|
+
|
|
134
|
+
console.log(`[Chat][${msg.channel}] ${msg.user.username}: ${msg.content}`);
|
|
135
|
+
|
|
136
|
+
// example command handling
|
|
137
|
+
if (msg.content === '!ping') {
|
|
138
|
+
client.say(msg.channel, `@${msg.user.username} Pong!`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Fired when a user performs an emote
|
|
144
|
+
*/
|
|
145
|
+
client.on(Events.CHANNEL_EMOTE, (emote) => {
|
|
146
|
+
console.log(`[Emote][${emote.channel}] * ${emote.user.username} ${emote.content}`);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Fired when the bot receives a private whisper
|
|
151
|
+
*/
|
|
152
|
+
client.on(Events.CHANNEL_WHISPER, (msg) => {
|
|
153
|
+
console.log(`[Whisper] ${msg.from.username} -> Me: ${msg.content}`);
|
|
154
|
+
|
|
155
|
+
// auto-reply example
|
|
156
|
+
msg.reply(`I received your whisper: "${msg.content}"`);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Fired when the bot is invited to another channel
|
|
161
|
+
*/
|
|
162
|
+
client.on(Events.CHANNEL_INVITE, (invite) => {
|
|
163
|
+
console.log(`[Invite] ${invite.from.username} invited us to '${invite.targetChannel}'`);
|
|
164
|
+
// client.join(CONFIG.botName, CONFIG.password, invite.targetChannel);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Fired when a message is edited/updated (e.g. overwriting the last line)
|
|
169
|
+
*/
|
|
170
|
+
client.on(Events.UPDATE_MESSAGE, (update) => {
|
|
171
|
+
console.log(`[Edit] ${update.user.username} updated a message in ${update.channel}. Mode: ${update.mode}`);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 4. Security & system events
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Fired when the server issues a warning (e.g., spamming too fast)
|
|
180
|
+
*/
|
|
181
|
+
client.on(Events.CHANNEL_WARN, (warning) => {
|
|
182
|
+
console.warn(`[Warning] ID: ${warning.id} | Text: ${warning.text}`);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Fired when the server requires a CAPTCHA to join/chat
|
|
187
|
+
*/
|
|
188
|
+
client.on(Events.CHANNEL_CAPTCHA, (captcha) => {
|
|
189
|
+
console.log(`[Captcha] Captcha required in ${captcha.channel}`);
|
|
190
|
+
console.log(captcha.text); // prints the ascii art
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Fired when a 3rd party code suggestion arrives
|
|
195
|
+
*/
|
|
196
|
+
client.on(Events.HACK_ATTEMPT, (suggestion) => {
|
|
197
|
+
console.warn(`[Extension] 3rd party code suggested from ${suggestion.fromNick}, library: ${suggestion.lib}`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Fired on internal errors
|
|
202
|
+
*/
|
|
203
|
+
client.on(Events.ERROR, (err) => {
|
|
204
|
+
console.error('[Error]', err);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Fired on non-critical internal warnings
|
|
209
|
+
*/
|
|
210
|
+
client.on(Events.WARN, (msg) => {
|
|
211
|
+
console.warn('[System Warn]', msg);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Fired for debug logs (if debug: true is set in Client options)
|
|
216
|
+
* Uncomment to see verbose logs
|
|
217
|
+
*/
|
|
218
|
+
/*
|
|
219
|
+
client.on(Events.DEBUG, (msg) => {
|
|
220
|
+
console.log('[Debug]', msg);
|
|
221
|
+
});
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 5. Web3 / wallet events
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Fired when a wallet address lookup (getWallet) returns data
|
|
230
|
+
*/
|
|
231
|
+
client.on(Events.WALLET_INFO, (info) => {
|
|
232
|
+
console.log(`[Wallet] User: ${info.nick}, Address: ${info.address}`);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Fired when the server requests the client to Sign-In With Solana (SIWS)
|
|
237
|
+
*/
|
|
238
|
+
client.on(Events.SIGN_MESSAGE, (payload) => {
|
|
239
|
+
console.log(`[Web3] Sign Message Request. Wallet: ${payload.wallet}`);
|
|
240
|
+
console.log(`[Web3] Message to sign: "${payload.message}"`);
|
|
241
|
+
// logic to sign message with private key would go here
|
|
242
|
+
// @todo show reply flow
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Fired when the server requests the client to sign a transaction
|
|
247
|
+
*/
|
|
248
|
+
client.on(Events.SIGN_TRANSACTION, (payload) => {
|
|
249
|
+
console.log(`[Web3] Sign Transaction Request. Type: ${payload.type}`);
|
|
250
|
+
console.log(`[Web3] TX Payload: ${payload.tx.substring(0, 20)}...`);
|
|
251
|
+
// @todo show reply flow
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Process cleanup
|
|
256
|
+
*/
|
|
257
|
+
process.on('SIGINT', () => {
|
|
258
|
+
console.log('\n[System] Disconnecting...');
|
|
259
|
+
client.destroy();
|
|
260
|
+
process.exit(0);
|
|
261
|
+
});
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
{
|
|
2
|
-
"_from": "hackchat-engine@^1.0.9",
|
|
3
|
-
"_inBundle": false,
|
|
4
|
-
"_integrity": "sha512-uxyU4HBAs8L4hQktdtvTP/3drbJHQvjkeQDdonglAffHIPtZ1pgU9W/ScNXNCFEyqDAo/QSVikfXp0wMmLtzeQ==",
|
|
5
|
-
"_location": "/hackchat-engine",
|
|
6
|
-
"_phantomChildren": {},
|
|
7
|
-
"_requested": {
|
|
8
|
-
"type": "range",
|
|
9
|
-
"registry": true,
|
|
10
|
-
"raw": "hackchat-engine@^1.0.9",
|
|
11
|
-
"name": "hackchat-engine",
|
|
12
|
-
"escapedName": "hackchat-engine",
|
|
13
|
-
"rawSpec": "^1.0.9",
|
|
14
|
-
"saveSpec": null,
|
|
15
|
-
"fetchSpec": "^1.0.9"
|
|
16
|
-
},
|
|
17
|
-
"_requiredBy": [
|
|
18
|
-
"#USER",
|
|
19
|
-
"/"
|
|
20
|
-
],
|
|
21
|
-
"_resolved": "https://registry.npmjs.org/hackchat-engine/-/hackchat-engine-1.0.9.tgz",
|
|
22
|
-
"_shasum": "536ea142dbfca44d3853c5767b7797c303642cdb",
|
|
23
|
-
"_spec": "hackchat-engine@^1.0.9",
|
|
24
|
-
"_where": "C:\\Users\\Owner\\Documents\\GitHub\\hcClientBeta",
|
|
25
|
-
"author": {
|
|
26
|
-
"name": "Marzavec"
|
|
27
|
-
},
|
|
28
|
-
"bugs": {
|
|
29
|
-
"url": "https://github.com/hack-chat/hackchat-engine/issues"
|
|
30
|
-
},
|
|
31
|
-
"bundleDependencies": [],
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"node-fetch": "^2.6.0",
|
|
34
|
-
"ws": "^7.2.0"
|
|
35
|
-
},
|
|
36
|
-
"deprecated": false,
|
|
37
|
-
"description": "hack.chat chat engine",
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"eslint": "^7.12.1",
|
|
40
|
-
"eslint-config-airbnb-base": "^14.2.0",
|
|
41
|
-
"eslint-plugin-import": "^2.22.1"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://github.com/hack-chat/hackchat-engine#readme",
|
|
44
|
-
"keywords": [
|
|
45
|
-
"hack.chat",
|
|
46
|
-
"chat",
|
|
47
|
-
"client"
|
|
48
|
-
],
|
|
49
|
-
"type": "module",
|
|
50
|
-
"license": "WTFPL",
|
|
51
|
-
"main": "index.js",
|
|
52
|
-
"name": "hackchat-engine",
|
|
53
|
-
"repository": {
|
|
54
|
-
"type": "git",
|
|
55
|
-
"url": "git+https://github.com/hack-chat/hackchat-engine.git"
|
|
56
|
-
},
|
|
57
|
-
"scripts": {
|
|
58
|
-
"lint": "eslint --ignore-path .gitignore .",
|
|
59
|
-
"lintfix": "eslint --fix --ignore-path .gitignore .",
|
|
60
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
61
|
-
},
|
|
62
|
-
"version": "1.1.
|
|
63
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"_from": "hackchat-engine@^1.0.9",
|
|
3
|
+
"_inBundle": false,
|
|
4
|
+
"_integrity": "sha512-uxyU4HBAs8L4hQktdtvTP/3drbJHQvjkeQDdonglAffHIPtZ1pgU9W/ScNXNCFEyqDAo/QSVikfXp0wMmLtzeQ==",
|
|
5
|
+
"_location": "/hackchat-engine",
|
|
6
|
+
"_phantomChildren": {},
|
|
7
|
+
"_requested": {
|
|
8
|
+
"type": "range",
|
|
9
|
+
"registry": true,
|
|
10
|
+
"raw": "hackchat-engine@^1.0.9",
|
|
11
|
+
"name": "hackchat-engine",
|
|
12
|
+
"escapedName": "hackchat-engine",
|
|
13
|
+
"rawSpec": "^1.0.9",
|
|
14
|
+
"saveSpec": null,
|
|
15
|
+
"fetchSpec": "^1.0.9"
|
|
16
|
+
},
|
|
17
|
+
"_requiredBy": [
|
|
18
|
+
"#USER",
|
|
19
|
+
"/"
|
|
20
|
+
],
|
|
21
|
+
"_resolved": "https://registry.npmjs.org/hackchat-engine/-/hackchat-engine-1.0.9.tgz",
|
|
22
|
+
"_shasum": "536ea142dbfca44d3853c5767b7797c303642cdb",
|
|
23
|
+
"_spec": "hackchat-engine@^1.0.9",
|
|
24
|
+
"_where": "C:\\Users\\Owner\\Documents\\GitHub\\hcClientBeta",
|
|
25
|
+
"author": {
|
|
26
|
+
"name": "Marzavec"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/hack-chat/hackchat-engine/issues"
|
|
30
|
+
},
|
|
31
|
+
"bundleDependencies": [],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"node-fetch": "^2.6.0",
|
|
34
|
+
"ws": "^7.2.0"
|
|
35
|
+
},
|
|
36
|
+
"deprecated": false,
|
|
37
|
+
"description": "hack.chat chat engine",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"eslint": "^7.12.1",
|
|
40
|
+
"eslint-config-airbnb-base": "^14.2.0",
|
|
41
|
+
"eslint-plugin-import": "^2.22.1"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/hack-chat/hackchat-engine#readme",
|
|
44
|
+
"keywords": [
|
|
45
|
+
"hack.chat",
|
|
46
|
+
"chat",
|
|
47
|
+
"client"
|
|
48
|
+
],
|
|
49
|
+
"type": "module",
|
|
50
|
+
"license": "WTFPL",
|
|
51
|
+
"main": "index.js",
|
|
52
|
+
"name": "hackchat-engine",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/hack-chat/hackchat-engine.git"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"lint": "eslint --ignore-path .gitignore .",
|
|
59
|
+
"lintfix": "eslint --fix --ignore-path .gitignore .",
|
|
60
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
61
|
+
},
|
|
62
|
+
"version": "1.1.17"
|
|
63
|
+
}
|
package/structures/UserStruct.js
CHANGED
|
@@ -37,7 +37,16 @@ class User {
|
|
|
37
37
|
* @type {string}
|
|
38
38
|
* @legacy Will be array or set with multichannel patch
|
|
39
39
|
*/
|
|
40
|
-
this.
|
|
40
|
+
this.channel = data.channel || 'unknown';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The channels this user is in
|
|
44
|
+
* @type {string}
|
|
45
|
+
*/
|
|
46
|
+
this.channels = new Set();
|
|
47
|
+
if (data.channel) {
|
|
48
|
+
this.channels.add(data.channel);
|
|
49
|
+
}
|
|
41
50
|
|
|
42
51
|
/**
|
|
43
52
|
* This users name
|
|
@@ -71,10 +80,9 @@ class User {
|
|
|
71
80
|
|
|
72
81
|
/**
|
|
73
82
|
* Whether or not the user is a bot
|
|
74
|
-
* @todo
|
|
75
83
|
* @type {boolean}
|
|
76
84
|
*/
|
|
77
|
-
this.
|
|
85
|
+
this.botting = data.isBot || false;
|
|
78
86
|
|
|
79
87
|
/**
|
|
80
88
|
* The Message object of the last message sent by the user, if one was sent
|
|
@@ -128,15 +136,6 @@ class User {
|
|
|
128
136
|
return this.userid;
|
|
129
137
|
}
|
|
130
138
|
|
|
131
|
-
/**
|
|
132
|
-
* User name
|
|
133
|
-
* @type {string}
|
|
134
|
-
* @readonly
|
|
135
|
-
*/
|
|
136
|
-
get channel() {
|
|
137
|
-
return this.userchannel;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
139
|
/**
|
|
141
140
|
* User name
|
|
142
141
|
* @type {string}
|
|
@@ -167,7 +166,6 @@ class User {
|
|
|
167
166
|
/**
|
|
168
167
|
* User level , returns 'user', 'mod', 'admin'
|
|
169
168
|
* @type {number}
|
|
170
|
-
* @todo Change from string to numeric levels
|
|
171
169
|
* @readonly
|
|
172
170
|
*/
|
|
173
171
|
get level() {
|
|
@@ -186,11 +184,10 @@ class User {
|
|
|
186
184
|
/**
|
|
187
185
|
* Bot status
|
|
188
186
|
* @type {boolean}
|
|
189
|
-
* @todo
|
|
190
187
|
* @readonly
|
|
191
188
|
*/
|
|
192
189
|
get isBot() {
|
|
193
|
-
return this.
|
|
190
|
+
return this.botting;
|
|
194
191
|
}
|
|
195
192
|
|
|
196
193
|
/**
|
|
@@ -262,25 +259,22 @@ class User {
|
|
|
262
259
|
* @type {string}
|
|
263
260
|
*/
|
|
264
261
|
updateUser(data) {
|
|
262
|
+
if (data.channel) {
|
|
263
|
+
this.channels.add(data.channel);
|
|
264
|
+
this.channel = data.channel;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (typeof data.trip !== 'undefined') this.usertrip = data.trip;
|
|
268
|
+
if (typeof data.hash !== 'undefined') this.userhash = data.hash;
|
|
269
|
+
|
|
265
270
|
this.username = data.nick;
|
|
266
271
|
this.userlevel = data.uType;
|
|
267
|
-
this.
|
|
272
|
+
this.botting = data.isBot;
|
|
268
273
|
this.nickColor = data.color;
|
|
269
274
|
this.flair = data.flair;
|
|
270
275
|
this.permissionLevel = data.level;
|
|
271
276
|
}
|
|
272
277
|
|
|
273
|
-
/**
|
|
274
|
-
* Sets this user either online or offline
|
|
275
|
-
* @param {string} channel Channel that the user joined or left
|
|
276
|
-
* @returns {boolean}
|
|
277
|
-
*/
|
|
278
|
-
// @todo Multichannel
|
|
279
|
-
toggleOnline(channel) { // eslint-disable-line no-unused-vars
|
|
280
|
-
this.online = !this.online;
|
|
281
|
-
return this.online;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
278
|
/**
|
|
285
279
|
* Sets this user either online or offline
|
|
286
280
|
* @param {Message} message This user's latest message
|
|
@@ -302,15 +296,16 @@ class User {
|
|
|
302
296
|
|
|
303
297
|
/**
|
|
304
298
|
* Send chat text using this user's channel
|
|
299
|
+
* @param {string} channel The specific channel to send to
|
|
305
300
|
* @param {string} text Text to send
|
|
306
301
|
* @returns {boolean}
|
|
307
302
|
*/
|
|
308
|
-
sendMessage(text) {
|
|
303
|
+
sendMessage(channel, text) {
|
|
309
304
|
try {
|
|
310
305
|
return new Promise((resolve) => {
|
|
311
306
|
this.client.ws.send({
|
|
312
307
|
cmd: OPCodes.CHAT,
|
|
313
|
-
channel
|
|
308
|
+
channel,
|
|
314
309
|
text,
|
|
315
310
|
});
|
|
316
311
|
resolve(this);
|
|
@@ -322,17 +317,17 @@ class User {
|
|
|
322
317
|
|
|
323
318
|
/**
|
|
324
319
|
* Send a whisper through this users channel to the user
|
|
320
|
+
* @param {string} channel The specific channel to send to
|
|
325
321
|
* @param {string} text Text to send
|
|
326
|
-
* @todo Finish whisper functions on server
|
|
327
322
|
* @returns {Promise}
|
|
328
323
|
*/
|
|
329
|
-
sendWhisper(text) {
|
|
324
|
+
sendWhisper(channel, text) {
|
|
330
325
|
try {
|
|
331
326
|
return new Promise((resolve) => {
|
|
332
327
|
this.client.ws.send({
|
|
333
328
|
cmd: OPCodes.WHISPER,
|
|
334
|
-
channel
|
|
335
|
-
|
|
329
|
+
channel,
|
|
330
|
+
nick: this.username, /* @todo legacy should be this.userid */
|
|
336
331
|
text,
|
|
337
332
|
});
|
|
338
333
|
resolve(this);
|
|
@@ -344,6 +339,8 @@ class User {
|
|
|
344
339
|
|
|
345
340
|
/**
|
|
346
341
|
* Send `invite` event to this user
|
|
342
|
+
* @param {string} channelName Channel name to use
|
|
343
|
+
* @param {string} to Username as string
|
|
347
344
|
* @returns {void}
|
|
348
345
|
*/
|
|
349
346
|
sendInvite(channel, to = '') {
|
|
@@ -362,6 +359,8 @@ class User {
|
|
|
362
359
|
|
|
363
360
|
/**
|
|
364
361
|
* Send `kick` operation to the server
|
|
362
|
+
* @param {string} channelName Channel name to use
|
|
363
|
+
* @param {string} to Username as string
|
|
365
364
|
* @returns {void}
|
|
366
365
|
*/
|
|
367
366
|
kick(channel, to = '') {
|
|
@@ -380,6 +379,7 @@ class User {
|
|
|
380
379
|
|
|
381
380
|
/**
|
|
382
381
|
* Send `ban` operation to the server
|
|
382
|
+
* @param {string} channelName Channel name to use
|
|
383
383
|
* @returns {void}
|
|
384
384
|
*/
|
|
385
385
|
ban(channel) {
|
|
@@ -392,6 +392,7 @@ class User {
|
|
|
392
392
|
|
|
393
393
|
/**
|
|
394
394
|
* Send `mute` operation to the server
|
|
395
|
+
* @param {string} channelName Channel name to use
|
|
395
396
|
* @returns {void}
|
|
396
397
|
*/
|
|
397
398
|
mute(channel) {
|
|
@@ -404,6 +405,7 @@ class User {
|
|
|
404
405
|
|
|
405
406
|
/**
|
|
406
407
|
* Send `unmute` operation to the server
|
|
408
|
+
* @param {string} channelName Channel name to use
|
|
407
409
|
* @returns {void}
|
|
408
410
|
*/
|
|
409
411
|
unmute(channel) {
|
|
@@ -414,6 +416,15 @@ class User {
|
|
|
414
416
|
});
|
|
415
417
|
}
|
|
416
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Is user in channel
|
|
421
|
+
* @param {string} channelName Channel name to use
|
|
422
|
+
* @returns {boolean}
|
|
423
|
+
*/
|
|
424
|
+
inChannel(channelName) {
|
|
425
|
+
return this.channels.has(channelName);
|
|
426
|
+
}
|
|
427
|
+
|
|
417
428
|
/**
|
|
418
429
|
* When referenced as a string, output this users name instead of an object type
|
|
419
430
|
* @returns {string}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This class handles parsing of the data of a `warning` event
|
|
3
|
-
* @todo Warnings will be numeric events in the future
|
|
4
3
|
*/
|
|
5
4
|
class WarningStruct {
|
|
6
5
|
/**
|
|
@@ -32,8 +31,6 @@ class WarningStruct {
|
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
33
|
* Text included with warning
|
|
35
|
-
* @todo Common warnings will be made integer based to allow i18n
|
|
36
|
-
* This will be removed
|
|
37
34
|
* @legacy
|
|
38
35
|
* @type {string}
|
|
39
36
|
*/
|
|
@@ -41,7 +38,6 @@ class WarningStruct {
|
|
|
41
38
|
|
|
42
39
|
/**
|
|
43
40
|
* Channel the warning was issued from
|
|
44
|
-
* @todo Touches multi-channel patch
|
|
45
41
|
* @type {string}
|
|
46
42
|
*/
|
|
47
43
|
this.channel = data.channel;
|
|
@@ -26,6 +26,7 @@ import WalletInfoHandler from './handlers/WalletInfoHandler.js';
|
|
|
26
26
|
|
|
27
27
|
const BeforeReadyWhitelist = [
|
|
28
28
|
WSEvents.SESSION,
|
|
29
|
+
WSEvents.ONLINE_SET,
|
|
29
30
|
WSEvents.PUB_CHANS,
|
|
30
31
|
];
|
|
31
32
|
|
|
@@ -116,10 +117,12 @@ class PacketRouter {
|
|
|
116
117
|
* @returns {void}
|
|
117
118
|
*/
|
|
118
119
|
processQueue() {
|
|
119
|
-
this.queue.
|
|
120
|
-
this.
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
while (this.queue.length > 0) {
|
|
121
|
+
const packet = this.queue.shift();
|
|
122
|
+
if (packet) {
|
|
123
|
+
this.route(packet, true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AbstractHandler from './AbstractHandler.js';
|
|
2
|
-
import { Events } from '../../../util/Constants.js';
|
|
2
|
+
import { Events, Status } from '../../../util/Constants.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Handles a new chat message that has been broadcast within a channel
|
|
@@ -15,19 +15,21 @@ class SessionHandler extends AbstractHandler {
|
|
|
15
15
|
const { client } = this.packetRouter;
|
|
16
16
|
|
|
17
17
|
client.readyAt = new Date();
|
|
18
|
-
const t = client.setTimeout(() => {
|
|
19
|
-
client.ws.connection.triggerReady();
|
|
20
|
-
}, 1);
|
|
21
18
|
|
|
22
|
-
client.
|
|
23
|
-
client.
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
if (client.status !== Status.READY) {
|
|
20
|
+
const t = client.setTimeout(() => {
|
|
21
|
+
client.ws.connection.triggerReady();
|
|
22
|
+
}, 1);
|
|
23
|
+
|
|
24
|
+
client.once(Events.READY, () => {
|
|
25
|
+
client.clearTimeout(t);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
26
28
|
|
|
27
29
|
const response = client.events.Session.handle(packet);
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
|
-
* Emitted when a
|
|
32
|
+
* Emitted when a session packet is received
|
|
31
33
|
* @event Client#session
|
|
32
34
|
* @param {SessionStruct} session Full session details
|
|
33
35
|
*/
|
package/test.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/* eslint no-console: 0 */
|
|
2
|
-
import Client from './Client.js';
|
|
3
|
-
|
|
4
|
-
const testName = 'testBot';
|
|
5
|
-
const testPass = 'testBot';
|
|
6
|
-
const testChannel = 'programming';
|
|
7
|
-
|
|
8
|
-
const hcClient = new Client({
|
|
9
|
-
ws: {
|
|
10
|
-
gateway: 'ws://127.0.0.1:6060/',
|
|
11
|
-
},
|
|
12
|
-
debug: true,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
hcClient.on('connected', () => console.log('Connected!'));
|
|
16
|
-
|
|
17
|
-
hcClient.on('session', (payload) => {
|
|
18
|
-
console.log(payload);
|
|
19
|
-
hcClient.join(testName, testPass, testChannel);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
hcClient.on('channelJoined', (payload) => {
|
|
23
|
-
console.log(payload);
|
|
24
|
-
hcClient.say(testChannel, 'Bep boop i r bot');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
hcClient.on('debug', (payload) => console.log(payload));
|
|
28
|
-
|
|
29
|
-
hcClient.on('userJoined', (payload) => console.log(payload));
|
|
30
|
-
|
|
31
|
-
hcClient.on('userLeft', (payload) => console.log(payload));
|
|
32
|
-
|
|
33
|
-
hcClient.on('warning', (payload) => console.log(payload));
|
|
34
|
-
|
|
35
|
-
hcClient.on('gotCaptcha', (payload) => console.log(payload));
|
|
36
|
-
|
|
37
|
-
hcClient.on('information', (payload) => console.log(payload));
|
|
38
|
-
|
|
39
|
-
hcClient.on('emote', (payload) => console.log(payload));
|
|
40
|
-
|
|
41
|
-
hcClient.on('invite', (payload) => console.log(payload));
|
|
42
|
-
|
|
43
|
-
hcClient.on('whisper', (payload) => console.log(payload));
|
|
44
|
-
|
|
45
|
-
hcClient.on('message', (payload) => console.log(payload));
|