@sockethub/irc2as 4.0.0-alpha.3

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/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@sockethub/irc2as",
3
+ "version": "4.0.0-alpha.3",
4
+ "description": "IRC to ActivityStreams objects",
5
+ "main": "src/index.js",
6
+ "directories": {
7
+ "test": "test"
8
+ },
9
+ "dependencies": {
10
+ "debug": "^4.3.1",
11
+ "fast-deep-equal": "^3.1.1"
12
+ },
13
+ "devDependencies": {
14
+ "@sockethub/schemas": "^3.0.0-alpha.3",
15
+ "c8": "7.11.0",
16
+ "chai": "4.3.6",
17
+ "mocha": "9.2.1"
18
+ },
19
+ "scripts": {
20
+ "clean": "npx rimraf coverage",
21
+ "clean:deps": "npx rimraf node_modules",
22
+ "compliance": "yarn run test && yarn run coverage",
23
+ "test": "c8 mocha src/**.test.js",
24
+ "coverage": "c8 check-coverage --statements 90 --branches 90 --functions 95 --lines 90"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/sockethub/sockethub.git",
29
+ "directory": "packages/irc2as"
30
+ },
31
+ "keywords": [
32
+ "irc",
33
+ "activitystreams",
34
+ "activity-streams",
35
+ "sockethub"
36
+ ],
37
+ "author": "Nick Jennings <nick@silverbucket.net>",
38
+ "license": "MIT",
39
+ "bugs": {
40
+ "url": "https://github.com/sockethub/sockethub/issues"
41
+ },
42
+ "homepage": "https://github.com/sockethub/sockethub/tree/master/packages/irc2as",
43
+ "gitHead": "f02238a478b7ffd3f31d8deea292eb67e630a86b"
44
+ }
@@ -0,0 +1,274 @@
1
+ const EVENT_INCOMING = 'incoming',
2
+ EVENT_ERROR = 'error';
3
+
4
+ class ASTemplates {
5
+ constructor(events, server) {
6
+ this.server = server;
7
+ this.events = events;
8
+ }
9
+
10
+ emitEvent(code, asObject) {
11
+ if ((typeof asObject === 'object') && (! asObject.published)) {
12
+ asObject.published = `${Date.now()}`;
13
+ }
14
+ this.events.emit(code, asObject);
15
+ }
16
+
17
+ __generalError(nick, content) {
18
+ return {
19
+ context: 'irc',
20
+ type: 'update',
21
+ actor: {
22
+ type: 'person',
23
+ id: nick + '@' + this.server,
24
+ name: nick
25
+ },
26
+ target: {
27
+ type: 'service',
28
+ id: this.server
29
+ },
30
+ error: content
31
+ };
32
+ }
33
+
34
+ presence(nick, role, channel) {
35
+ this.emitEvent(EVENT_INCOMING, {
36
+ context: 'irc',
37
+ type: 'update',
38
+ actor: {
39
+ type: 'person',
40
+ id: `${nick}@${this.server}`,
41
+ name: nick,
42
+ },
43
+ target: {
44
+ type: 'room',
45
+ id: this.server + '/' + channel,
46
+ name: channel
47
+ },
48
+ object: {
49
+ type: 'presence',
50
+ role: role
51
+ }
52
+ });
53
+ };
54
+
55
+ channelError(channel, nick, content) {
56
+ this.emitEvent(EVENT_ERROR, {
57
+ context: 'irc',
58
+ type: 'update',
59
+ actor: {
60
+ type: 'person',
61
+ id: nick + '@' + this.server
62
+ },
63
+ target: {
64
+ type: 'room',
65
+ id: this.server + '/' + channel
66
+ },
67
+ error: content
68
+ });
69
+ }
70
+
71
+ nickError(nick, content) {
72
+ this.emitEvent(EVENT_ERROR, this.__generalError(nick, content));
73
+ }
74
+
75
+ notice(nick, content) {
76
+ this.emitEvent(EVENT_INCOMING, {
77
+ context: 'irc',
78
+ type: 'send',
79
+ actor: {
80
+ type: 'service',
81
+ id: this.server
82
+ },
83
+ object: {
84
+ type: 'message',
85
+ content: content
86
+ },
87
+ target: {
88
+ type: 'person',
89
+ id: nick + '@' + this.server,
90
+ name: nick
91
+ }
92
+ });
93
+ }
94
+
95
+ serviceError(nick, content) {
96
+ this.emitEvent(EVENT_ERROR, this.__generalError(nick, content));
97
+ }
98
+
99
+ joinError(nick) {
100
+ this.emitEvent(EVENT_ERROR, {
101
+ context: 'irc',
102
+ type: 'join',
103
+ actor: {
104
+ id: this.server,
105
+ type: 'service'
106
+ },
107
+ error: 'no such channel ' + nick,
108
+ target: {
109
+ id: nick + '@' + this.server,
110
+ type: 'person'
111
+ }
112
+ });
113
+ }
114
+
115
+ topicChange(channel, nick, content) {
116
+ this.emitEvent(EVENT_INCOMING, {
117
+ context: 'irc',
118
+ type: 'update',
119
+ actor: {
120
+ type: 'person',
121
+ id: nick + '@' + this.server,
122
+ name: nick
123
+ },
124
+ target: {
125
+ type: 'room',
126
+ id: this.server + '/' + channel,
127
+ name: channel
128
+ },
129
+ object: {
130
+ type: 'topic',
131
+ content: content
132
+ }
133
+ });
134
+ }
135
+
136
+ joinRoom(channel, nick) {
137
+ this.emitEvent(EVENT_INCOMING, {
138
+ context: 'irc',
139
+ type: 'join',
140
+ actor: {
141
+ type: 'person',
142
+ id: nick + '@' + this.server,
143
+ name: nick
144
+ },
145
+ target: {
146
+ type: 'room',
147
+ id: this.server + '/' + channel,
148
+ name: channel
149
+ }
150
+ });
151
+ }
152
+
153
+ userQuit(nick) {
154
+ this.emitEvent(EVENT_INCOMING, {
155
+ context: 'irc',
156
+ type: 'leave',
157
+ actor: {
158
+ type: 'person',
159
+ id: nick + '@' + this.server,
160
+ name: nick
161
+ },
162
+ target: {
163
+ type: 'service',
164
+ id: this.server
165
+ },
166
+ object: {
167
+ type: 'message',
168
+ content: 'user has quit'
169
+ }
170
+ });
171
+ }
172
+
173
+ userPart(channel, nick) {
174
+ this.emitEvent(EVENT_INCOMING, {
175
+ context: 'irc',
176
+ type: 'leave',
177
+ actor: {
178
+ type: 'person',
179
+ id: nick + '@' + this.server,
180
+ name: nick
181
+ },
182
+ target: {
183
+ type: 'room',
184
+ id: this.server + '/' + channel,
185
+ name: channel
186
+ },
187
+ object: {
188
+ type: 'message',
189
+ content: 'user has left the channel'
190
+ }
191
+ });
192
+ }
193
+
194
+ privMsg(nick, target, content) {
195
+ let type, message;
196
+ if (content.startsWith('+\u0001ACTION ')) {
197
+ type = 'me';
198
+ message = content.split(/^\+\u0001ACTION\s+/)[1].split(/\u0001$/)[0];
199
+ } else {
200
+ type = 'message';
201
+ message = content;
202
+ }
203
+ this.emitEvent(EVENT_INCOMING, {
204
+ context: 'irc',
205
+ type: 'send',
206
+ actor: {
207
+ type: 'person',
208
+ id: nick + '@' + this.server,
209
+ name: nick
210
+ },
211
+ target: {
212
+ type: target.startsWith('#') ? "room" : "person",
213
+ id: this.server + '/' + target,
214
+ name: target
215
+ },
216
+ object: {
217
+ type: type,
218
+ content: message
219
+ }
220
+ });
221
+ }
222
+
223
+ role(type, nick, target, role, channel) {
224
+ this.emitEvent(EVENT_INCOMING, {
225
+ context: 'irc',
226
+ type: type,
227
+ actor: {
228
+ type: 'person',
229
+ id: nick + '@' + this.server,
230
+ name: nick
231
+ },
232
+ target: {
233
+ type: 'person',
234
+ id: target + '@' + this.server,
235
+ name: target
236
+ },
237
+ object: {
238
+ type: "relationship",
239
+ "relationship": 'role',
240
+ "subject": {
241
+ type: 'presence',
242
+ role: role
243
+ },
244
+ "object": {
245
+ type: 'room',
246
+ id: this.server + '/' + channel,
247
+ name: channel
248
+ }
249
+ }
250
+ });
251
+ }
252
+
253
+ nickChange(nick, content) {
254
+ this.emitEvent(EVENT_INCOMING, {
255
+ context: 'irc',
256
+ type: 'update',
257
+ actor: {
258
+ type: 'person',
259
+ id: nick + '@' + this.server,
260
+ name: nick
261
+ },
262
+ target: {
263
+ type: 'person',
264
+ id: content + '@' + this.server,
265
+ name: content
266
+ },
267
+ object: {
268
+ type: 'address'
269
+ }
270
+ });
271
+ }
272
+ }
273
+
274
+ module.exports = ASTemplates;
package/src/index.js ADDED
@@ -0,0 +1,265 @@
1
+ const events = require('events');
2
+ const debug = require('debug')('irc2as');
3
+ const ASEmitter = require('./as-emitter');
4
+
5
+ const EVENT_INCOMING = 'incoming',
6
+ // EVENT_ERROR = 'error',
7
+ EVENT_PONG = 'pong',
8
+ EVENT_PING = 'ping',
9
+ EVENT_UNPROCESSED = 'unprocessed';
10
+
11
+ const ERR_BAD_NICK = "432",
12
+ ERR_CHAN_PRIVS = "482",
13
+ ERR_NICK_IN_USE = "433",
14
+ ERR_TEMP_UNAVAIL = "437",
15
+ ERR_NO_CHANNEL = "403",
16
+ ERR_NOT_INVITED = "471",
17
+ ERR_BADMODE= "472",
18
+ ERR_INVITE_ONLY = "473",
19
+ ERR_BANNED = "474",
20
+ ERR_BADKEY = "475",
21
+ ERR_BADMASK = "476",
22
+ ERR_NOCHANMODES = "477",
23
+ ERR_BANLISTFULL = "478",
24
+ JOIN = "JOIN",
25
+ MODE = "MODE",
26
+ MOTD = "372",
27
+ MOTD_END = "376",
28
+ NAMES = "353",
29
+ // NAMES_END = "366",
30
+ NICK = "NICK",
31
+ NOTICE = "NOTICE",
32
+ PART = "PART",
33
+ PING = "PING",
34
+ PONG = "PONG",
35
+ PRIVMSG = "PRIVMSG",
36
+ QUIT = "QUIT",
37
+ TOPIC_CHANGE = "TOPIC",
38
+ TOPIC_IS = "332",
39
+ TOPIC_SET_BY = "333",
40
+ WHO = "352",
41
+ WHO_OLD = "354";
42
+ // WHO_END = "315";
43
+
44
+ const ROLE = {
45
+ '@': 'owner',
46
+ '%': 'admin',
47
+ '*': 'participant'
48
+ };
49
+
50
+ const MODES = {
51
+ 'o': 'owner',
52
+ 'h': 'admin',
53
+ 'v': 'participant'
54
+ };
55
+
56
+ function getNickFromServer(server) {
57
+ return server.split(/^:/)[1].split('!')[0];
58
+ }
59
+
60
+ class IrcToActivityStreams {
61
+ constructor(cfg) {
62
+ const config = cfg || {};
63
+ this.server = config.server;
64
+ this.events = new events.EventEmitter();
65
+ this.__buffer = {};
66
+ this.__buffer[NAMES] = {};
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;
77
+ }
78
+ incoming = incoming.trim();
79
+ const [metadata, content] = incoming.split(' :');
80
+ const [server, code, pos1, pos2, pos3, ...msg] = metadata.split(" ");
81
+ const channel = ((typeof pos1 === "string") && (pos1.startsWith('#'))) ? pos1 :
82
+ ((typeof pos2 === "string") && (pos2.startsWith('#'))) ? pos2 :
83
+ ((typeof pos3 === "string") && (pos3.startsWith('#'))) ? pos3 : undefined;
84
+ if (metadata === PING) {
85
+ this.events.emit(EVENT_PING, `${Date.now()}`);
86
+ return true;
87
+ }
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
+
93
+ __processIRCCodes(code, server, channel, pos1, pos2, pos3, content, msg, incoming) {
94
+ const ase = new ASEmitter(this.events, this.server);
95
+ let nick, type, role;
96
+ switch (code) {
97
+ /** */
98
+ case ERR_CHAN_PRIVS:
99
+ case ERR_NOT_INVITED:
100
+ case ERR_BADMODE:
101
+ case ERR_INVITE_ONLY:
102
+ case ERR_BANNED:
103
+ case ERR_BADKEY:
104
+ case ERR_BADMASK:
105
+ case ERR_NOCHANMODES:
106
+ case ERR_BANLISTFULL:
107
+ ase.channelError(channel, pos1, content)
108
+ break;
109
+
110
+ /** */
111
+ case ERR_NICK_IN_USE: // nick conflict
112
+ case ERR_BAD_NICK:
113
+ ase.serviceError(pos2, content)
114
+ break;
115
+
116
+ /** */
117
+ case ERR_NO_CHANNEL: // no such channel
118
+ ase.joinError(pos2)
119
+ break;
120
+
121
+ /** */
122
+ case ERR_TEMP_UNAVAIL: // nick conflict
123
+ ase.nickError(pos2, content);
124
+ break;
125
+
126
+ /** */
127
+ case JOIN: // room join
128
+ ase.joinRoom(channel, getNickFromServer(server))
129
+ break;
130
+
131
+ case MODE: // custom event indicating a channel mode has been updated, used to re-query user or channel
132
+ let user_mode = pos2 || content;
133
+ if (! channel) { break; } // don't handle cases with no channel defined
134
+ if (! pos3) { break; } // we need target user
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;
142
+
143
+ /** */
144
+ case MOTD: // MOTD
145
+ if (! this.__buffer[MOTD]) {
146
+ this.__buffer[MOTD] = {
147
+ context: 'irc',
148
+ type: 'update',
149
+ actor: {
150
+ type: 'service',
151
+ id: this.server,
152
+ name: this.server
153
+ },
154
+ object: {
155
+ type: 'topic',
156
+ content: content
157
+ }
158
+ }
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
+
169
+ /** */
170
+ case NAMES: // user list
171
+ for (let entry of content.split(' ')) {
172
+ role = 'member';
173
+ let username = entry;
174
+ if (ROLE[entry[0]]) {
175
+ username = entry.substr(1);
176
+ role = ROLE[entry[0]];
177
+ }
178
+ ase.presence(username, role, channel);
179
+ }
180
+ break;
181
+
182
+ /** */
183
+ case NICK: // nick change
184
+ // debug(`- 2 nick: ${nick} from content: ${content}`);
185
+ ase.nickChange(getNickFromServer(server), content);
186
+ break;
187
+
188
+ /** */
189
+ case NOTICE: // notice
190
+ ase.notice(pos1, content);
191
+ break;
192
+
193
+ /** */
194
+ case PART: // leaving
195
+ ase.userPart(channel, getNickFromServer(server));
196
+ break;
197
+
198
+ /** */
199
+ case PONG: // ping response received
200
+ this.events.emit(EVENT_PONG, `${Date.now()}`);
201
+ break;
202
+
203
+ /** */
204
+ case PRIVMSG: // msg
205
+ ase.privMsg(getNickFromServer(server), pos1, content);
206
+ break;
207
+
208
+ /** */
209
+ case QUIT: // quit user
210
+ ase.userQuit(getNickFromServer(server))
211
+ break;
212
+
213
+ /** */
214
+ case TOPIC_CHANGE: // topic changed now
215
+ ase.topicChange(channel, getNickFromServer(server), content);
216
+ break;
217
+
218
+ /** */
219
+ case TOPIC_IS: // topic currently set to
220
+ this.__buffer[TOPIC_IS] = {
221
+ context: 'irc',
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;
247
+
248
+ /** */
249
+ case WHO:
250
+ case WHO_OLD:
251
+ nick = (msg[3].length <= 2) ? msg[2] : msg[3];
252
+ if (nick === 'undefined') { break; }
253
+ role = MODES[pos2[1]] || 'member';
254
+ ase.presence(nick, role, channel);
255
+ break;
256
+
257
+ /** */
258
+ default:
259
+ this.events.emit(EVENT_UNPROCESSED, incoming);
260
+ break;
261
+ }
262
+ }
263
+ };
264
+
265
+ module.exports = IrcToActivityStreams;
@@ -0,0 +1,86 @@
1
+ :hitchcock.freenode.net 376 hyper_slvrbckt :End of /MOTD command.
2
+ :hitchcock.freenode.net 372 hyper_slvrbckt :- on the https://freenode.live website for our call for volunteers and call for
3
+ :hitchcock.freenode.net 372 hyper_slvrbckt :- participation. If you are interested in sponsoring next year's event, please
4
+ :hitchcock.freenode.net 372 hyper_slvrbckt :- send us an e-mail to sponsor@freenode.live
5
+ :hitchcock.freenode.net 372 hyper_slvrbckt :-
6
+ :hitchcock.freenode.net 372 hyper_slvrbckt :- Thank you for using freenode!
7
+ :hitchcock.freenode.net 372 hyper_slvrbckt :-
8
+ :hitchcock.freenode.net 372 hyper_slvrbckt :-
9
+ :hitchcock.freenode.net 376 hyper_slvrbckt :End of /MOTD command.
10
+ :hitchcock.freenode.net 315 hyper_slvrbckt undefined :End of /WHO list.
11
+ :hitchcock.freenode.net 353 hyper_slvrbckt @ #kosmos-random :hyper_slvrbckt gregkare hal8000
12
+ :hitchcock.freenode.net 366 hyper_slvrbckt #kosmos-random :End of /NAMES list.
13
+ :hitchcock.freenode.net 352 hyper_slvrbckt #kosmos-random ~hyper_slv 154.65.34.210 hitchcock.freenode.net hyper_slvrbckt H :0 hyper_slvrbckt
14
+ :adams.freenode.net 372 hyper_slvrbckt :-
15
+ :adams.freenode.net 372 hyper_slvrbckt :-
16
+ :adams.freenode.net 376 hyper_slvrbckt :End of /MOTD command.
17
+ :bob MODE hyper_slvrbckt :+i
18
+ :alice MODE #Finnish +o Kilroy
19
+ :bob MODE #room_a +v alice
20
+ :alice MODE #room_b +h bob
21
+ PING :moon.freenode.net
22
+ :adams.freenode.net 352 hyper_slvrbckt * ~undefined 75-140-253-250.dhcp.ftwo.tx.charter.com livingstone.freenode.net undefined H :0 undefined
23
+ :adams.freenode.net 315 hyper_slvrbckt undefined :End of /WHO list.
24
+ :adams.freenode.net 352 hyper_slvrbckt #kosmos-random ~hyper_slv 154.65.34.210 adams.freenode.net hyper_slvrbckt H :0 hyper_slvrbckt
25
+ :adams.freenode.net 315 hyper_slvrbckt hyper_slvrbckt :End of /WHO list.
26
+ :hitchcock.freenode.net 366 hyper_slvrbckt #kosmos-random :End of /NAMES list.
27
+ :karatkievich.freenode.net PONG karatkievich.freenode.net :LAG1516614421150
28
+ :jarlaxl_!~jarlaxl@user123.intek.cz QUIT :Client Quit
29
+ :karatkievich.freenode.net 403 slvrbckt sdfsdfsdfsdfsdf :No such channel
30
+ :slvrbckt!~slvtbckt@154.65.34.210 PART #debian :"Leaving"
31
+ :karatkievich.freenode.net PONG karatkievich.freenode.net :LAG1516615441149
32
+ :slvrbckt!~slvtbckt@154.65.34.210 NICK :donkey2018
33
+ PING :moon.freenode.net
34
+ :donkey2018!~slvtbckt@154.65.34.210 NICK :slvrbckt
35
+ :weber.freenode.net 333 hyper_slvrbckt #kosmos-random lio17!~lio17@ns350827.ip-37-187-174.eu 1516730003
36
+ :karatkievich.freenode.net PONG karatkievich.freenode.net :1516615578917
37
+ :karatkievich.freenode.net 433 slvrbckt nkj :Nickname is already in use.
38
+ :hyper_slvrbckt!~hyper_slv@154.65.34.210 PRIVMSG #kosmos-random :-ssssssss
39
+ :lio17!~lio17@ns350827.ip-37-187-174.eu TOPIC #kosmos-random :testing123
40
+ :card.freenode.net 482 slvrbckt #kosmos-random :You're not a channel operator
41
+ :weber.freenode.net 332 hyper_slvrbckt #kosmos-random :no longer boating in senegal
42
+ :weber.freenode.net 333 hyper_slvrbckt #kosmos-random lio17!~lio17@ns350827.ip-37-187-174.eu 1516730003
43
+ :raucao!~raucao@5.148.180.93 PRIVMSG #kosmos-random :+ACTION is thinking about sending someone to get b33rz
44
+ :verne.freenode.net 255 hyper_slvrbckt :I have 4681 clients and 3 servers
45
+ :verne.freenode.net 265 hyper_slvrbckt 4681 7978 :Current local users 4681, max 7978
46
+ :verne.freenode.net 266 hyper_slvrbckt 89501 97718 :Current global users 89501, max 97718
47
+ :verne.freenode.net 250 hyper_slvrbckt :Highest connection count: 7980 (7978 clients) (4267730 connections received)
48
+ :verne.freenode.net 375 hyper_slvrbckt :- verne.freenode.net Message of the Day -
49
+ :leguin.freenode.net 254 hyper_slvrbckt 50699 :channels formed
50
+ :leguin.freenode.net 255 hyper_slvrbckt :I have 3642 clients and 1 servers
51
+ :leguin.freenode.net 265 hyper_slvrbckt 3642 3657 :Current local users 3642, max 3657
52
+ :leguin.freenode.net 266 hyper_slvrbckt 89518 91820 :Current global users 89518, max 91820
53
+ :leguin.freenode.net 250 hyper_slvrbckt :Highest connection count: 3658 (3657 clients) (32451 connections received)
54
+ :leguin.freenode.net 375 hyper_slvrbckt :- leguin.freenode.net Message of the Day -
55
+ :card.freenode.net 352 hyper_slvrbckt #kosmos-random ~hyper_slv cell01228.qcell.gm card.freenode.net hyper_slvrbckt H :0 hyper_slvrbckt +311ms
56
+ :card.freenode.net 315 hyper_slvrbckt hyper_slvrbckt :End of /WHO list.
57
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~slvtbckt cell01228.qcell.gm leguin.freenode.net slvrbckt H slvrbckt :realname
58
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~lio17 ns350827.ip-37-187-174.eu niven.freenode.net lio17 H lio17 :bumi
59
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos silverbuck gateway/shell/matrix.org/x-kwavgootllgoshlq sinisalo.freenode.net M-silverbucket H 0 :@silverbucket:matrix.org
60
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~nodebot static.171.39.76.144.clients.your-server.de leguin.freenode.net botka1 H 0 :nodeJS IRC client
61
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos sid186430 gateway/web/irccloud.com/x-ztwqqtwigkgoieax kornbluth.freenode.net derbumi H derbumi :bumi
62
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ChanServ services. services. ChanServ H@ 0 :Channel Services
63
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~bkero osuosl/staff/bkero tolkien.freenode.net bkero- H bkero :bkero
64
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~galfert f00bar.de orwell.freenode.net galfert H galfert :galfert
65
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~raucao 5.148.180.93 hitchcock.freenode.net raucao G binbasti :Basti
66
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~nodebot static.171.39.76.144.clients.your-server.de sinisalo.freenode.net hal8000 H hal8000 :nodeJS IRC client
67
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~kare 2a03:b0c0:3:d0::15d:6001 livingstone.freenode.net gregkare H gregkare :kare
68
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos ~bkero osuosl/staff/bkero hitchcock.freenode.net bkero H bkero :bkero
69
+ :leguin.freenode.net 354 slvrbckt 152 #kosmos bumimatrix gateway/shell/matrix.org/x-aeqgcnylcgbinlmx roddenberry.freenode.net bumi[m] H 0 :@bumi:matrix.org
70
+ :leguin.freenode.net 315 slvrbckt #kosmos :End of /WHO list.
71
+ :card.freenode.net 352 hyper_slvrbckt #kosmos-random ~hyper_slv cell01228.qcell.gm card.freenode.net hyper_slvrbckt H :0 hyper_slvrbckt +311ms
72
+ :card.freenode.net 315 hyper_slvrbckt hyper_slvrbckt :End of /WHO list.
73
+ :moon.freenode.net 473 hyper_slvrbckt #freenode-sponsors :Cannot join channel (+i) - you must be invited
74
+ :weber.freenode.net 482 slvrbckt #debian :You're not a channel operator
75
+ :NickServ!NickServ@services. NOTICE boo :This nickname is registered. Please choose a different nickname, or identify via /msg NickServ identify <password>.
76
+ :NickServ!NickServ@services. NOTICE boo :You have 30 seconds to identify to your nickname before it is changed.
77
+ :NickServ!NickServ@services. NOTICE boo :You failed to identify in time for the nickname boo
78
+ :asimov.freenode.net 437 sh-mhJLD boo :Nick/channel is temporarily unavailable
79
+ :wilhelm.freenode.net 433 sh-4a3LB slvrbckt :Nickname is already in use.
80
+ :sh-WjwOE!~sh-WjwOE@194.228.76.203 NICK :woooo
81
+ :SaslServ!SaslServ@services.libera.chat NOTICE slvrbckt :Last login from: ~slvrbckt@localhost on Feb 09 18:45:19 2022 +0000.
82
+ :myuser!~myuser@2a02:8308:7080:4100:41fa:f0ec:594e:771f JOIN #kosmos-random slvrbckt :myuser
83
+ :tungsten.libera.chat 353 myuser @ #kosmos-random :myuser @botka foouser
84
+ :tungsten.libera.chat 366 myuser #kosmos-random :End of /NAMES list.
85
+ :tungsten.libera.chat 324 myuser #kosmos-random +Cnst
86
+ :tungsten.libera.chat 329 myuser #kosmos-random 1642793393