@sockethub/platform-irc 4.0.0-alpha.8 → 4.0.0-beta.0

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 CHANGED
@@ -8,7 +8,7 @@ This platform provides IRC client functionality, allowing web applications to co
8
8
  IRC servers, join channels, send messages, and handle IRC events through ActivityStreams
9
9
  messages.
10
10
 
11
- ## Implemented Verbs (`@type`)
11
+ ## Implemented Verbs (`type`)
12
12
 
13
13
  * **send** - Send messages to channels or users
14
14
  * **join** - Join IRC channels
@@ -18,20 +18,31 @@ messages.
18
18
 
19
19
  ## Usage
20
20
 
21
+ IRC room targets use `#channel@server` in `target.id` (for example
22
+ `#general@irc.libera.chat`). When you include `target.name`, use the channel
23
+ name as it appears on IRC (for example `#general`).
24
+
21
25
  ### Send Message Example
22
26
 
23
27
  ```json
24
28
  {
25
- "@type": "send",
26
- "context": "irc",
29
+ "type": "send",
30
+ "@context": [
31
+ "https://www.w3.org/ns/activitystreams",
32
+ "https://sockethub.org/ns/context/v1.jsonld",
33
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
34
+ ],
27
35
  "actor": {
28
- "@id": "mynick@irc.libera.chat"
36
+ "id": "mynick@irc.libera.chat",
37
+ "type": "person"
29
38
  },
30
39
  "target": {
31
- "@id": "#general@irc.libera.chat"
40
+ "id": "#general@irc.libera.chat",
41
+ "type": "room",
42
+ "name": "#general"
32
43
  },
33
44
  "object": {
34
- "@type": "Note",
45
+ "type": "message",
35
46
  "content": "Hello IRC channel!"
36
47
  }
37
48
  }
@@ -41,13 +52,20 @@ messages.
41
52
 
42
53
  ```json
43
54
  {
44
- "@type": "join",
45
- "context": "irc",
55
+ "type": "join",
56
+ "@context": [
57
+ "https://www.w3.org/ns/activitystreams",
58
+ "https://sockethub.org/ns/context/v1.jsonld",
59
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
60
+ ],
46
61
  "actor": {
47
- "@id": "mynick@irc.libera.chat"
62
+ "id": "mynick@irc.libera.chat",
63
+ "type": "person"
48
64
  },
49
65
  "target": {
50
- "@id": "#general@irc.libera.chat"
66
+ "id": "#general@irc.libera.chat",
67
+ "type": "room",
68
+ "name": "#general"
51
69
  }
52
70
  }
53
71
  ```
@@ -56,21 +74,318 @@ messages.
56
74
 
57
75
  ```json
58
76
  {
59
- "@type": "send",
60
- "context": "irc",
77
+ "type": "send",
78
+ "@context": [
79
+ "https://www.w3.org/ns/activitystreams",
80
+ "https://sockethub.org/ns/context/v1.jsonld",
81
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
82
+ ],
61
83
  "actor": {
62
- "@id": "mynick@irc.libera.chat"
84
+ "id": "mynick@irc.libera.chat",
85
+ "type": "person"
63
86
  },
64
87
  "target": {
65
- "@id": "friend@irc.libera.chat"
88
+ "id": "friend@irc.libera.chat",
89
+ "type": "person"
66
90
  },
67
91
  "object": {
68
- "@type": "Note",
92
+ "type": "message",
69
93
  "content": "Hello friend!"
70
94
  }
71
95
  }
72
96
  ```
73
97
 
98
+ ### Update Topic Example
99
+
100
+ ```json
101
+ {
102
+ "type": "update",
103
+ "@context": [
104
+ "https://www.w3.org/ns/activitystreams",
105
+ "https://sockethub.org/ns/context/v1.jsonld",
106
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
107
+ ],
108
+ "actor": {
109
+ "id": "mynick@irc.libera.chat",
110
+ "type": "person"
111
+ },
112
+ "target": {
113
+ "id": "#general@irc.libera.chat",
114
+ "type": "room",
115
+ "name": "#general"
116
+ },
117
+ "object": {
118
+ "type": "topic",
119
+ "content": "New channel topic"
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### Update Nickname Example
125
+
126
+ ```json
127
+ {
128
+ "type": "update",
129
+ "@context": [
130
+ "https://www.w3.org/ns/activitystreams",
131
+ "https://sockethub.org/ns/context/v1.jsonld",
132
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
133
+ ],
134
+ "actor": {
135
+ "id": "mynick@irc.libera.chat",
136
+ "type": "person"
137
+ },
138
+ "target": {
139
+ "id": "newnick@irc.libera.chat",
140
+ "type": "person"
141
+ },
142
+ "object": {
143
+ "type": "address"
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Query Attendance Example
149
+
150
+ ```json
151
+ {
152
+ "type": "query",
153
+ "@context": [
154
+ "https://www.w3.org/ns/activitystreams",
155
+ "https://sockethub.org/ns/context/v1.jsonld",
156
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
157
+ ],
158
+ "actor": {
159
+ "id": "mynick@irc.libera.chat",
160
+ "type": "person"
161
+ },
162
+ "target": {
163
+ "id": "#general@irc.libera.chat",
164
+ "type": "room",
165
+ "name": "#general"
166
+ },
167
+ "object": {
168
+ "type": "attendance"
169
+ }
170
+ }
171
+ ```
172
+
173
+ ### Query Attendance Response Example
174
+
175
+ ```json
176
+ {
177
+ "type": "query",
178
+ "@context": [
179
+ "https://www.w3.org/ns/activitystreams",
180
+ "https://sockethub.org/ns/context/v1.jsonld",
181
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
182
+ ],
183
+ "actor": {
184
+ "id": "#general@irc.libera.chat",
185
+ "type": "room",
186
+ "name": "#general"
187
+ },
188
+ "target": {
189
+ "id": "mynick@irc.libera.chat",
190
+ "type": "person",
191
+ "name": "mynick"
192
+ },
193
+ "object": {
194
+ "type": "attendance",
195
+ "members": ["alice", "bob", "carol"],
196
+ "roles": { "alice": "owner", "bob": "participant" }
197
+ }
198
+ }
199
+ ```
200
+
201
+ `roles` maps a nick to `owner` (channel op, admin or founder), `admin` (half-op)
202
+ or `participant` (voiced). Nicks absent from it are plain members.
203
+
204
+ ### Query Channels Example
205
+
206
+ Lists channels on the server. `object.name` is an optional `LIST` mask (one
207
+ parameter, no spaces). Rows use the server's `CHANTYPES` (`#` and `&` by
208
+ default). The server still sends every row; the response is capped at 5000
209
+ items and includes `"truncated": true` when rows were dropped.
210
+
211
+ ```json
212
+ {
213
+ "type": "query",
214
+ "@context": [
215
+ "https://www.w3.org/ns/activitystreams",
216
+ "https://sockethub.org/ns/context/v1.jsonld",
217
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
218
+ ],
219
+ "actor": {
220
+ "id": "mynick@irc.libera.chat",
221
+ "type": "person"
222
+ },
223
+ "target": {
224
+ "id": "irc.libera.chat",
225
+ "type": "service"
226
+ },
227
+ "object": {
228
+ "type": "channels",
229
+ "name": "#sockethub*"
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### Query Channels Response Example
235
+
236
+ One message is sent once the server finishes the list.
237
+
238
+ ```json
239
+ {
240
+ "type": "query",
241
+ "@context": [
242
+ "https://www.w3.org/ns/activitystreams",
243
+ "https://sockethub.org/ns/context/v1.jsonld",
244
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
245
+ ],
246
+ "actor": {
247
+ "id": "irc.libera.chat",
248
+ "type": "service",
249
+ "name": "irc.libera.chat"
250
+ },
251
+ "object": {
252
+ "type": "channels",
253
+ "items": [
254
+ {
255
+ "id": "#sockethub@irc.libera.chat",
256
+ "type": "room",
257
+ "name": "#sockethub",
258
+ "members": 12,
259
+ "summary": "Sockethub development"
260
+ }
261
+ ]
262
+ }
263
+ }
264
+ ```
265
+
266
+ ### Disconnect Example
267
+
268
+ ```json
269
+ {
270
+ "type": "disconnect",
271
+ "@context": [
272
+ "https://www.w3.org/ns/activitystreams",
273
+ "https://sockethub.org/ns/context/v1.jsonld",
274
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
275
+ ],
276
+ "actor": {
277
+ "id": "mynick@irc.libera.chat",
278
+ "type": "person"
279
+ }
280
+ }
281
+ ```
282
+
283
+ ## Authentication
284
+
285
+ Authenticated connections use SASL. Two mechanisms are supported:
286
+
287
+ * **`PLAIN`** — sends a username and secret via SASL PLAIN. This is the
288
+ default mechanism, and is supported by most IRC networks (Libera.Chat,
289
+ OFTC, etc.). Both `password` and `token` use PLAIN by default. Use
290
+ `token` for personal access tokens (e.g. Libera.Chat NickServ tokens)
291
+ to avoid storing primary account passwords.
292
+ * **`OAUTHBEARER`** — sends an OAuth 2.0 access token instead of a password
293
+ ([RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628)). Requires
294
+ `saslMechanism: "OAUTHBEARER"` to be set explicitly. Adoption on
295
+ public IRC networks is still limited; the main deployment is SourceHut's
296
+ `chat.sr.ht` (via the [soju](https://soju.im/) bouncer). Major networks
297
+ such as Libera.Chat, OFTC, and Hackint do not currently advertise
298
+ OAUTHBEARER. See emersion's [IRC × OAuth 2.0](https://emersion.fr/blog/2022/irc-and-oauth2/)
299
+ writeup for background. How to obtain the token is provider-specific and
300
+ outside the scope of this module.
301
+
302
+ `password` and `token` are mutually exclusive. Both default to SASL PLAIN;
303
+ set `saslMechanism: "OAUTHBEARER"` explicitly for OAuth 2.0 bearer tokens.
304
+ Secure connections validate the IRC server's TLS certificate by default. The
305
+ optional `allowInvalidCert` field defaults to `false`; when set to `true`, it
306
+ disables TLS certificate validation for secure connections. Use this
307
+ security-relevant opt-out only for trusted self-signed IRC networks.
308
+
309
+ ### Credentials with password (SASL PLAIN)
310
+
311
+ ```json
312
+ {
313
+ "type": "credentials",
314
+ "@context": [
315
+ "https://www.w3.org/ns/activitystreams",
316
+ "https://sockethub.org/ns/context/v1.jsonld",
317
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
318
+ ],
319
+ "actor": {
320
+ "id": "mynick@irc.libera.chat",
321
+ "type": "person"
322
+ },
323
+ "object": {
324
+ "type": "credentials",
325
+ "nick": "mynick",
326
+ "server": "irc.libera.chat",
327
+ "password": "secret",
328
+ "port": 6697,
329
+ "secure": true
330
+ }
331
+ }
332
+ ```
333
+
334
+ ### Credentials with personal access token (SASL PLAIN)
335
+
336
+ Networks like Libera.Chat accept NickServ personal access tokens via
337
+ SASL PLAIN. Use the `token` field instead of `password` to make the
338
+ distinction clear:
339
+
340
+ ```json
341
+ {
342
+ "type": "credentials",
343
+ "@context": [
344
+ "https://www.w3.org/ns/activitystreams",
345
+ "https://sockethub.org/ns/context/v1.jsonld",
346
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
347
+ ],
348
+ "actor": {
349
+ "id": "mynick@irc.libera.chat",
350
+ "type": "person"
351
+ },
352
+ "object": {
353
+ "type": "credentials",
354
+ "nick": "mynick",
355
+ "server": "irc.libera.chat",
356
+ "token": "my-personal-access-token",
357
+ "port": 6697,
358
+ "secure": true
359
+ }
360
+ }
361
+ ```
362
+
363
+ ### Credentials with OAuth token (SASL OAUTHBEARER)
364
+
365
+ ```json
366
+ {
367
+ "type": "credentials",
368
+ "@context": [
369
+ "https://www.w3.org/ns/activitystreams",
370
+ "https://sockethub.org/ns/context/v1.jsonld",
371
+ "https://sockethub.org/ns/context/platform/irc/v1.jsonld"
372
+ ],
373
+ "actor": {
374
+ "id": "mynick@chat.sr.ht",
375
+ "type": "person"
376
+ },
377
+ "object": {
378
+ "type": "credentials",
379
+ "nick": "mynick",
380
+ "server": "chat.sr.ht",
381
+ "token": "oauth-access-token",
382
+ "saslMechanism": "OAUTHBEARER",
383
+ "port": 6697,
384
+ "secure": true
385
+ }
386
+ }
387
+ ```
388
+
74
389
  ## Features
75
390
 
76
391
  * **Channel communication**: Join and participate in IRC channels
@@ -88,4 +403,5 @@ messages.
88
403
 
89
404
  ## API
90
405
 
91
- Detailed API documentation can be found [here](API.md)
406
+ API reference docs are generated from the source JSDoc and are not committed to this repository.
407
+ Run `bun run doc` in this package to generate them locally.
@@ -0,0 +1,155 @@
1
+ /**
2
+ * This is a platform for Sockethub implementing IRC functionality.
3
+ *
4
+ * Developed by Nick Jennings (https://github.com/silverbucket)
5
+ *
6
+ * Sockethub is licensed under the LGPLv3.
7
+ * See the LICENSE file for details.
8
+ *
9
+ * The latest version of this module can be found here:
10
+ * git://github.com/sockethub/sockethub.git
11
+ *
12
+ * For more information about Sockethub visit http://sockethub.org/.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ */
18
+ import type { ActivityStream, PersistentPlatformConfig, PersistentPlatformInterface, PlatformCallback, PlatformSchemaStruct, PlatformSession } from "@sockethub/schemas";
19
+ import { type IrcSocketInstance } from "irc-socket-sasl";
20
+ import type { PlatformIrcCredentialsObject } from "./types.js";
21
+ export type { IrcSocketInstance } from "irc-socket-sasl";
22
+ export type { PlatformIrcCredentialsObject } from "./types.js";
23
+ export declare function flattenConnectOptions(options: Record<string, unknown>): Record<string, unknown>;
24
+ export type GetClientCallback = (err: string | null, client?: IrcSocketInstance) => void;
25
+ /**
26
+ * Handles all actions related to communication via the IRC protocol.
27
+ */
28
+ export declare class IRC implements PersistentPlatformInterface {
29
+ private readonly log;
30
+ credentialsHash: string | undefined;
31
+ config: PersistentPlatformConfig;
32
+ private readonly updateActor;
33
+ private readonly prepareActorUpdate?;
34
+ private readonly sendToClient;
35
+ private irc2as;
36
+ private forceDisconnect;
37
+ private clientConnecting;
38
+ private initialized;
39
+ private client?;
40
+ private jobQueue;
41
+ private pongAcksToSkip;
42
+ private channels;
43
+ private handledActors;
44
+ private credentials?;
45
+ constructor(session: PlatformSession);
46
+ /**
47
+ * JSON schema defining the types this platform accepts.
48
+ *
49
+ * `password` and `token` are mutually exclusive. Both default to SASL
50
+ * PLAIN; set `saslMechanism: 'OAUTHBEARER'` explicitly for OAuth 2.0
51
+ * bearer tokens (RFC 7628). See the package README for canonical
52
+ * credentials payload examples.
53
+ */
54
+ get schema(): PlatformSchemaStruct;
55
+ /**
56
+ * Returns whether the platform is ready to handle jobs.
57
+ * For IRC, this means we have successfully connected to the server.
58
+ */
59
+ isInitialized(): boolean;
60
+ /**
61
+ * Function: connect
62
+ *
63
+ * Connect to an IRC server.
64
+ *
65
+ * @param {object} job activity streams object
66
+ * @param {object} credentials credentials object
67
+ * @param {object} done callback when job is done
68
+ */
69
+ connect(job: ActivityStream, credentials: PlatformIrcCredentialsObject, done: PlatformCallback): void;
70
+ /**
71
+ * Function: join
72
+ *
73
+ * Join a room or private conversation.
74
+ *
75
+ * @param {object} job activity streams object
76
+ * @param {object} done callback when job is done
77
+ */
78
+ join(job: ActivityStream, done: PlatformCallback): any;
79
+ /**
80
+ * Function leave
81
+ *
82
+ * Leave a room or private conversation.
83
+ *
84
+ * @param {object} job activity streams object
85
+ * @param {object} done callback when job is done
86
+ */
87
+ leave(job: ActivityStream, done: PlatformCallback): any;
88
+ /**
89
+ * Function: send
90
+ *
91
+ * Send a message to a room or private conversation.
92
+ *
93
+ * @param {object} job activity streams object
94
+ * @param {object} done callback when job is done
95
+ */
96
+ send(job: ActivityStream, done: PlatformCallback): any;
97
+ /**
98
+ * Function: update
99
+ *
100
+ * Indicate a change (i.e. room topic update, or nickname change).
101
+ *
102
+ * @param {object} job activity streams object
103
+ * @param {object} credentials credentials to verify this user is the right one
104
+ * @param {object} done callback when job is done
105
+ */
106
+ update(job: ActivityStream, credentials: PlatformIrcCredentialsObject, done: PlatformCallback): any;
107
+ /**
108
+ * Function: query
109
+ *
110
+ * Indicate an intent to query something (e.g. get a list of users in a room).
111
+ *
112
+ * @param {object} job activity streams object
113
+ * @param {object} done callback when job is done
114
+ */
115
+ query(job: ActivityStream, done: PlatformCallback): any;
116
+ /**
117
+ * Disconnect IRC client
118
+ * @param {object} job activity streams object
119
+ * @param done
120
+ */
121
+ disconnect(job: ActivityStream, done: PlatformCallback): void;
122
+ cleanup(done: PlatformCallback): any;
123
+ /**
124
+ * Resolve an activity target to its IRC recipient (channel or nick).
125
+ *
126
+ * Targets are server-qualified for consistency with what the platform emits
127
+ * (see irc2as): rooms are addressed as `#channel@server` and users
128
+ * (private messages) as
129
+ * `nick@server` — the same `@server` suffix for both. Returns `null` for a
130
+ * `room` target that isn't server-qualified (a bare channel), which callers
131
+ * reject — bare channel names are no longer accepted.
132
+ *
133
+ * @param target activity stream target (`job.target`)
134
+ * @returns the IRC channel (`#channel`) or nick, or `null` for a bare room
135
+ */
136
+ private resolveIrcTarget;
137
+ private isJoined;
138
+ private hasJoined;
139
+ private hasLeft;
140
+ /**
141
+ * A socket holds exactly one nick. Replace whatever handledActors held
142
+ * rather than deleting the actor a job was addressed as: after a rename
143
+ * the store rejected, the client still addresses jobs as the old actor,
144
+ * so deleting by job actor leaves the nick the socket released behind
145
+ * and whoever takes it next is treated as this connection.
146
+ */
147
+ private setSocketNick;
148
+ private getClient;
149
+ private ircConnect;
150
+ private completeJob;
151
+ private isNickAck;
152
+ private adoptForcedNickChange;
153
+ private registerListeners;
154
+ }
155
+ export default IRC;