@sockethub/irc2as 4.0.0-alpha.4 → 4.0.0-alpha.5
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/README.md +2 -3
- package/package.json +12 -21
- package/src/as-emitter.js +258 -255
- package/src/index.js +273 -230
- package/src/index.test.data.js +492 -108
- package/src/index.test.js +85 -82
- package/coverage/tmp/coverage-39248-1663949492833-0.json +0 -1
package/src/index.js
CHANGED
|
@@ -1,265 +1,308 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import events from "node:events";
|
|
2
|
+
import debug from "debug";
|
|
3
|
+
import { ASEmitter } from "./as-emitter.js";
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
// EVENT_ERROR = 'error',
|
|
7
|
-
EVENT_PONG = 'pong',
|
|
8
|
-
EVENT_PING = 'ping',
|
|
9
|
-
EVENT_UNPROCESSED = 'unprocessed';
|
|
5
|
+
const log = debug("irc2as");
|
|
10
6
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
7
|
+
const EVENT_INCOMING = "incoming";
|
|
8
|
+
// EVENT_ERROR = 'error',
|
|
9
|
+
const EVENT_PONG = "pong";
|
|
10
|
+
const EVENT_PING = "ping";
|
|
11
|
+
const EVENT_UNPROCESSED = "unprocessed";
|
|
12
|
+
|
|
13
|
+
const ERR_BAD_NICK = "432";
|
|
14
|
+
const ERR_CHAN_PRIVS = "482";
|
|
15
|
+
const ERR_NICK_IN_USE = "433";
|
|
16
|
+
const ERR_TEMP_UNAVAIL = "437";
|
|
17
|
+
const ERR_NO_CHANNEL = "403";
|
|
18
|
+
const ERR_NOT_INVITED = "471";
|
|
19
|
+
const ERR_BADMODE = "472";
|
|
20
|
+
const ERR_INVITE_ONLY = "473";
|
|
21
|
+
const ERR_BANNED = "474";
|
|
22
|
+
const ERR_BADKEY = "475";
|
|
23
|
+
const ERR_BADMASK = "476";
|
|
24
|
+
const ERR_NOCHANMODES = "477";
|
|
25
|
+
const ERR_BANLISTFULL = "478";
|
|
26
|
+
const JOIN = "JOIN";
|
|
27
|
+
const MODE = "MODE";
|
|
28
|
+
const MOTD = "372";
|
|
29
|
+
const MOTD_END = "376";
|
|
30
|
+
const NAMES = "353";
|
|
31
|
+
// NAMES_END = "366",
|
|
32
|
+
const NICK = "NICK";
|
|
33
|
+
const NOTICE = "NOTICE";
|
|
34
|
+
const PART = "PART";
|
|
35
|
+
const PING = "PING";
|
|
36
|
+
const PONG = "PONG";
|
|
37
|
+
const PRIVMSG = "PRIVMSG";
|
|
38
|
+
const QUIT = "QUIT";
|
|
39
|
+
const TOPIC_CHANGE = "TOPIC";
|
|
40
|
+
const TOPIC_IS = "332";
|
|
41
|
+
const TOPIC_SET_BY = "333";
|
|
42
|
+
const WHO = "352";
|
|
43
|
+
const WHO_OLD = "354";
|
|
44
|
+
// WHO_END = "315";
|
|
43
45
|
|
|
44
46
|
const ROLE = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
"@": "owner",
|
|
48
|
+
"%": "admin",
|
|
49
|
+
"*": "participant",
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
const MODES = {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
o: "owner",
|
|
54
|
+
h: "admin",
|
|
55
|
+
v: "participant",
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
function getNickFromServer(server) {
|
|
57
|
-
return server.split(/^:/)[1].split(
|
|
59
|
+
return server.split(/^:/)[1].split("!")[0];
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
class IrcToActivityStreams {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
input(incoming) {
|
|
70
|
-
debug(incoming);
|
|
71
|
-
if (typeof incoming !== 'string') {
|
|
72
|
-
debug('unable to process incoming message as it was not a string.');
|
|
73
|
-
return false;
|
|
74
|
-
} else if (incoming.length < 3) {
|
|
75
|
-
debug('unable to process incoming string, length smaller than 3.');
|
|
76
|
-
return false;
|
|
62
|
+
export class IrcToActivityStreams {
|
|
63
|
+
constructor(cfg) {
|
|
64
|
+
const config = cfg || {};
|
|
65
|
+
this.server = config.server;
|
|
66
|
+
this.events = new events.EventEmitter();
|
|
67
|
+
this.__buffer = {};
|
|
68
|
+
this.__buffer[NAMES] = {};
|
|
77
69
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
|
|
71
|
+
input(payload) {
|
|
72
|
+
log(payload);
|
|
73
|
+
if (typeof payload !== "string") {
|
|
74
|
+
log("unable to process incoming message as it was not a string.");
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (payload.length < 3) {
|
|
78
|
+
log("unable to process incoming string, length smaller than 3.");
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const incoming = payload.trim();
|
|
82
|
+
const [metadata, content] = incoming.split(" :");
|
|
83
|
+
const [server, code, pos1, pos2, pos3, ...msg] = metadata.split(" ");
|
|
84
|
+
const channel =
|
|
85
|
+
typeof pos1 === "string" && pos1.startsWith("#")
|
|
86
|
+
? pos1
|
|
87
|
+
: typeof pos2 === "string" && pos2.startsWith("#")
|
|
88
|
+
? pos2
|
|
89
|
+
: typeof pos3 === "string" && pos3.startsWith("#")
|
|
90
|
+
? pos3
|
|
91
|
+
: undefined;
|
|
92
|
+
if (metadata === PING) {
|
|
93
|
+
this.events.emit(EVENT_PING, `${Date.now()}`);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
log(
|
|
97
|
+
`[${code}] server: ${server} channel: ${channel} 1: ${pos1}, 2: ${pos2}, 3: ${pos3}. content: `,
|
|
98
|
+
content,
|
|
99
|
+
);
|
|
100
|
+
this.__processIRCCodes(
|
|
101
|
+
code,
|
|
102
|
+
server,
|
|
103
|
+
channel,
|
|
104
|
+
pos1,
|
|
105
|
+
pos2,
|
|
106
|
+
pos3,
|
|
107
|
+
content,
|
|
108
|
+
msg,
|
|
109
|
+
incoming,
|
|
110
|
+
);
|
|
87
111
|
}
|
|
88
|
-
debug(`[${code}] server: ${server} channel: ${channel} 1: ${pos1}, 2: ${pos2}, 3: ${pos3}.` +
|
|
89
|
-
` content: `, content);
|
|
90
|
-
this.__processIRCCodes(code, server, channel, pos1, pos2, pos3, content, msg, incoming);
|
|
91
|
-
}
|
|
92
112
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
break;
|
|
113
|
+
__processIRCCodes(
|
|
114
|
+
code,
|
|
115
|
+
server,
|
|
116
|
+
channel,
|
|
117
|
+
pos1,
|
|
118
|
+
pos2,
|
|
119
|
+
pos3,
|
|
120
|
+
content,
|
|
121
|
+
msg,
|
|
122
|
+
incoming,
|
|
123
|
+
) {
|
|
124
|
+
const ase = new ASEmitter(this.events, this.server);
|
|
125
|
+
let nick;
|
|
126
|
+
let type;
|
|
127
|
+
let role;
|
|
109
128
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
switch (code) {
|
|
130
|
+
/** */
|
|
131
|
+
case ERR_CHAN_PRIVS:
|
|
132
|
+
case ERR_NOT_INVITED:
|
|
133
|
+
case ERR_BADMODE:
|
|
134
|
+
case ERR_INVITE_ONLY:
|
|
135
|
+
case ERR_BANNED:
|
|
136
|
+
case ERR_BADKEY:
|
|
137
|
+
case ERR_BADMASK:
|
|
138
|
+
case ERR_NOCHANMODES:
|
|
139
|
+
case ERR_BANLISTFULL:
|
|
140
|
+
ase.channelError(channel, pos1, content);
|
|
141
|
+
break;
|
|
115
142
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
/** */
|
|
144
|
+
case ERR_NICK_IN_USE: // nick conflict
|
|
145
|
+
case ERR_BAD_NICK:
|
|
146
|
+
ase.serviceError(pos2, content);
|
|
147
|
+
break;
|
|
120
148
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
149
|
+
/** */
|
|
150
|
+
case ERR_NO_CHANNEL: // no such channel
|
|
151
|
+
ase.joinError(pos2);
|
|
152
|
+
break;
|
|
125
153
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
154
|
+
/** */
|
|
155
|
+
case ERR_TEMP_UNAVAIL: // nick conflict
|
|
156
|
+
ase.nickError(pos2, content);
|
|
157
|
+
break;
|
|
130
158
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
role = MODES[user_mode[1]] || 'member';
|
|
136
|
-
type = 'add';
|
|
137
|
-
if (user_mode[0] === '-') {
|
|
138
|
-
type = 'remove';
|
|
139
|
-
}
|
|
140
|
-
ase.role(type, getNickFromServer(server), pos3, role, channel)
|
|
141
|
-
break;
|
|
159
|
+
/** */
|
|
160
|
+
case JOIN: // room join
|
|
161
|
+
ase.joinRoom(channel, getNickFromServer(server));
|
|
162
|
+
break;
|
|
142
163
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
type
|
|
156
|
-
content: content
|
|
164
|
+
// custom event indicating a channel mode has been updated, used to re-query user or channel
|
|
165
|
+
case MODE: {
|
|
166
|
+
const user_mode = pos2 || content;
|
|
167
|
+
if (!channel) {
|
|
168
|
+
break;
|
|
169
|
+
} // don't handle cases with no channel defined
|
|
170
|
+
if (!pos3) {
|
|
171
|
+
break;
|
|
172
|
+
} // we need target user
|
|
173
|
+
role = MODES[user_mode[1]] || "member";
|
|
174
|
+
type = "add";
|
|
175
|
+
if (user_mode[0] === "-") {
|
|
176
|
+
type = "remove";
|
|
157
177
|
}
|
|
178
|
+
ase.role(type, getNickFromServer(server), pos3, role, channel);
|
|
179
|
+
break;
|
|
158
180
|
}
|
|
159
|
-
} else {
|
|
160
|
-
this.__buffer[MOTD].object.content += " " + content;
|
|
161
|
-
}
|
|
162
|
-
break;
|
|
163
|
-
case MOTD_END: // end of MOTD
|
|
164
|
-
if (! this.__buffer[MOTD]) { break; }
|
|
165
|
-
ase.emitEvent(EVENT_INCOMING, this.__buffer[MOTD]);
|
|
166
|
-
delete this.__buffer[MOTD];
|
|
167
|
-
break;
|
|
168
181
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
/** */
|
|
183
|
+
case MOTD: // MOTD
|
|
184
|
+
if (!this.__buffer[MOTD]) {
|
|
185
|
+
this.__buffer[MOTD] = {
|
|
186
|
+
context: "irc",
|
|
187
|
+
type: "update",
|
|
188
|
+
actor: {
|
|
189
|
+
type: "service",
|
|
190
|
+
id: this.server,
|
|
191
|
+
name: this.server,
|
|
192
|
+
},
|
|
193
|
+
object: {
|
|
194
|
+
type: "topic",
|
|
195
|
+
content: content,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
} else {
|
|
199
|
+
this.__buffer[MOTD].object.content += ` ${content}`;
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
case MOTD_END: // end of MOTD
|
|
203
|
+
if (!this.__buffer[MOTD]) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
ase.emitEvent(EVENT_INCOMING, this.__buffer[MOTD]);
|
|
207
|
+
delete this.__buffer[MOTD];
|
|
208
|
+
break;
|
|
181
209
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
/** */
|
|
211
|
+
case NAMES: // user list
|
|
212
|
+
for (const entry of content.split(" ")) {
|
|
213
|
+
role = "member";
|
|
214
|
+
let username = entry;
|
|
215
|
+
if (ROLE[entry[0]]) {
|
|
216
|
+
username = entry.substr(1);
|
|
217
|
+
role = ROLE[entry[0]];
|
|
218
|
+
}
|
|
219
|
+
ase.presence(username, role, channel);
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
187
222
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
223
|
+
/** */
|
|
224
|
+
case NICK: // nick change
|
|
225
|
+
// log(`- 2 nick: ${nick} from content: ${content}`);
|
|
226
|
+
ase.nickChange(getNickFromServer(server), content);
|
|
227
|
+
break;
|
|
192
228
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
229
|
+
/** */
|
|
230
|
+
case NOTICE: // notice
|
|
231
|
+
ase.notice(pos1, content);
|
|
232
|
+
break;
|
|
197
233
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
234
|
+
/** */
|
|
235
|
+
case PART: // leaving
|
|
236
|
+
ase.userPart(channel, getNickFromServer(server));
|
|
237
|
+
break;
|
|
202
238
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
239
|
+
/** */
|
|
240
|
+
case PONG: // ping response received
|
|
241
|
+
this.events.emit(EVENT_PONG, `${Date.now()}`);
|
|
242
|
+
break;
|
|
207
243
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
244
|
+
/** */
|
|
245
|
+
case PRIVMSG: // msg
|
|
246
|
+
ase.privMsg(getNickFromServer(server), pos1, content);
|
|
247
|
+
break;
|
|
212
248
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
249
|
+
/** */
|
|
250
|
+
case QUIT: // quit user
|
|
251
|
+
ase.userQuit(getNickFromServer(server));
|
|
252
|
+
break;
|
|
217
253
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
type: 'update',
|
|
223
|
-
actor: undefined,
|
|
224
|
-
target: {
|
|
225
|
-
type: 'room',
|
|
226
|
-
id: this.server + '/' + channel,
|
|
227
|
-
name: channel
|
|
228
|
-
},
|
|
229
|
-
object: {
|
|
230
|
-
type: 'topic',
|
|
231
|
-
content: content
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
break;
|
|
235
|
-
case TOPIC_SET_BY: // current topic set by
|
|
236
|
-
if (! this.__buffer[TOPIC_IS]) { break; }
|
|
237
|
-
nick = pos3.split('!')[0];
|
|
238
|
-
this.__buffer[TOPIC_IS].actor = {
|
|
239
|
-
type: 'person',
|
|
240
|
-
id: nick + '@' + this.server,
|
|
241
|
-
name: nick
|
|
242
|
-
};
|
|
243
|
-
this.__buffer[TOPIC_IS].published = msg[0];
|
|
244
|
-
ase.emitEvent(EVENT_INCOMING, this.__buffer[TOPIC_IS]);
|
|
245
|
-
delete this.__buffer[TOPIC_IS];
|
|
246
|
-
break;
|
|
254
|
+
/** */
|
|
255
|
+
case TOPIC_CHANGE: // topic changed now
|
|
256
|
+
ase.topicChange(channel, getNickFromServer(server), content);
|
|
257
|
+
break;
|
|
247
258
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
259
|
+
/** */
|
|
260
|
+
case TOPIC_IS: // topic currently set to
|
|
261
|
+
this.__buffer[TOPIC_IS] = {
|
|
262
|
+
context: "irc",
|
|
263
|
+
type: "update",
|
|
264
|
+
actor: undefined,
|
|
265
|
+
target: {
|
|
266
|
+
type: "room",
|
|
267
|
+
id: `${this.server}/${channel}`,
|
|
268
|
+
name: channel,
|
|
269
|
+
},
|
|
270
|
+
object: {
|
|
271
|
+
type: "topic",
|
|
272
|
+
content: content,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
break;
|
|
276
|
+
case TOPIC_SET_BY: // current topic set by
|
|
277
|
+
if (!this.__buffer[TOPIC_IS]) {
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
nick = pos3.split("!")[0];
|
|
281
|
+
this.__buffer[TOPIC_IS].actor = {
|
|
282
|
+
type: "person",
|
|
283
|
+
id: `${nick}@${this.server}`,
|
|
284
|
+
name: nick,
|
|
285
|
+
};
|
|
286
|
+
this.__buffer[TOPIC_IS].published = msg[0];
|
|
287
|
+
ase.emitEvent(EVENT_INCOMING, this.__buffer[TOPIC_IS]);
|
|
288
|
+
delete this.__buffer[TOPIC_IS];
|
|
289
|
+
break;
|
|
256
290
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
291
|
+
/** */
|
|
292
|
+
case WHO:
|
|
293
|
+
case WHO_OLD:
|
|
294
|
+
nick = msg[3].length <= 2 ? msg[2] : msg[3];
|
|
295
|
+
if (nick === "undefined") {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
role = MODES[pos2[1]] || "member";
|
|
299
|
+
ase.presence(nick, role, channel);
|
|
300
|
+
break;
|
|
264
301
|
|
|
265
|
-
|
|
302
|
+
/** */
|
|
303
|
+
default:
|
|
304
|
+
this.events.emit(EVENT_UNPROCESSED, incoming);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|