@sockethub/platform-xmpp 5.0.0-alpha.12 → 5.0.0-alpha.13

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
@@ -10,79 +10,272 @@ updates through ActivityStreams messages.
10
10
 
11
11
  ## Implemented Verbs (`type`)
12
12
 
13
+ * **connect** - Establish a connection to the XMPP server
14
+ * **disconnect** - Close the XMPP connection
15
+ * **join** - Join an XMPP chat room (MUC)
16
+ * **leave** - Leave an XMPP chat room
13
17
  * **send** - Send messages to contacts or chat rooms
14
- * **join** - Join XMPP chat rooms (MUCs)
15
- * **observe** - Subscribe to presence updates
16
- * **request-friend** - Send contact/buddy requests
17
- * **remove-friend** - Remove contacts from roster
18
- * **make-friend** - Accept contact requests
19
18
  * **update** - Update presence status and information
19
+ * **request-friend** - Send a contact/buddy request
20
+ * **remove-friend** - Remove a contact from the roster
21
+ * **make-friend** - Accept a contact request
22
+ * **query** - Query room attendance and room information via service discovery
23
+
24
+ ## Authentication
25
+
26
+ Credentials are sent to Sockethub via a `credentials` message before the
27
+ `connect` verb. The XMPP platform accepts a single `password` field.
28
+
29
+ ### Credentials object
30
+
31
+ | Field | Type | Required | Description |
32
+ |---|---|---|---|
33
+ | `type` | string | yes | Must be `"credentials"`. |
34
+ | `userAddress` | string | yes | Bare JID, e.g. `user@jabber.net`. |
35
+ | `resource` | string | yes | XMPP resource identifier (e.g. `"phone"`). |
36
+ | `password` | string | yes | SASL password. Use this field for PLAIN-slot tokens too. |
37
+ | `server` | string | no | Overrides the hostname from `userAddress`. |
38
+ | `port` | number | no | Overrides the default port. |
39
+
40
+ ### Password authentication
41
+
42
+ Use a standard XMPP account password. The server negotiates the strongest
43
+ available SASL mechanism (typically SCRAM-SHA-1, falling back to PLAIN).
44
+ The examples below assume you already have an initialized Sockethub client
45
+ instance named `sc`.
46
+
47
+ ```javascript
48
+ {
49
+ type: "credentials",
50
+ "@context": sc.contextFor("xmpp"),
51
+ actor: { id: "user@jabber.net", type: "person" },
52
+ object: {
53
+ type: "credentials",
54
+ userAddress: "user@jabber.net",
55
+ password: "s3cret",
56
+ resource: "phone"
57
+ }
58
+ }
59
+ ```
60
+
61
+ If your deployment expects a bearer-style token in the SASL PLAIN password
62
+ slot, pass that token string as `password`. This is only a narrow
63
+ compatibility mode. Dedicated token SASL mechanisms such as ejabberd
64
+ `X-OAUTH2`, Prosody `OAUTHBEARER`, Prosody community `X-TOKEN`, and SASL2 FAST
65
+ are not implemented by the bundled `@xmpp/client`, which only supports
66
+ `SCRAM-SHA-1`, `PLAIN`, and `ANONYMOUS` and prefers `SCRAM-SHA-1` when both
67
+ SCRAM and PLAIN are offered.
68
+
69
+ A failed authentication with either a traditional password or a token string
70
+ surfaces the same `SASLError: not-authorized` and is treated as a
71
+ non-recoverable connection error.
20
72
 
21
73
  ## Usage
22
74
 
75
+ ### Connect Example
76
+
77
+ ```javascript
78
+ {
79
+ type: "connect",
80
+ "@context": sc.contextFor("xmpp"),
81
+ actor: {
82
+ id: "user@example.org/web",
83
+ type: "person"
84
+ }
85
+ }
86
+ ```
87
+
23
88
  ### Send Message Example
24
89
 
25
- ```json
90
+ ```javascript
91
+ {
92
+ type: "send",
93
+ "@context": sc.contextFor("xmpp"),
94
+ actor: {
95
+ id: "user@example.org",
96
+ type: "person"
97
+ },
98
+ target: {
99
+ id: "friend@jabber.net",
100
+ type: "person"
101
+ },
102
+ object: {
103
+ type: "message",
104
+ content: "Hello from Sockethub!"
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### Send Room Message Example
110
+
111
+ ```javascript
112
+ {
113
+ type: "send",
114
+ "@context": sc.contextFor("xmpp"),
115
+ actor: {
116
+ id: "user@example.org/web",
117
+ type: "person"
118
+ },
119
+ target: {
120
+ id: "room@conference.example.org",
121
+ type: "room"
122
+ },
123
+ object: {
124
+ type: "message",
125
+ content: "Hello room"
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Update Presence Example
131
+
132
+ ```javascript
133
+ {
134
+ type: "update",
135
+ "@context": sc.contextFor("xmpp"),
136
+ actor: {
137
+ id: "user@example.org/web",
138
+ type: "person"
139
+ },
140
+ object: {
141
+ type: "presence",
142
+ presence: "away",
143
+ content: "Out for lunch"
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Query Room Attendance Response Example
149
+
150
+ ```javascript
151
+ {
152
+ type: "query",
153
+ "@context": sc.contextFor("xmpp"),
154
+ actor: {
155
+ id: "room@conference.example.org",
156
+ type: "room"
157
+ },
158
+ target: {
159
+ id: "user@example.org/web",
160
+ type: "person"
161
+ },
162
+ object: {
163
+ type: "attendance",
164
+ members: ["alice", "bob", "carol"]
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### Query Room Attendance Example
170
+
171
+ ```javascript
172
+ {
173
+ type: "query",
174
+ "@context": sc.contextFor("xmpp"),
175
+ actor: {
176
+ id: "user@example.org/web",
177
+ type: "person"
178
+ },
179
+ target: {
180
+ id: "room@conference.example.org",
181
+ type: "room"
182
+ },
183
+ object: {
184
+ type: "attendance"
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Query Room Information Example
190
+
191
+ ```javascript
192
+ {
193
+ type: "query",
194
+ "@context": sc.contextFor("xmpp"),
195
+ actor: {
196
+ id: "user@example.org/web",
197
+ type: "person"
198
+ },
199
+ target: {
200
+ id: "room@conference.example.org",
201
+ type: "room"
202
+ },
203
+ object: {
204
+ type: "room-info"
205
+ }
206
+ }
207
+ ```
208
+
209
+ ### Query Room Information Response Example
210
+
211
+ ```javascript
26
212
  {
27
- "type": "send",
28
- "@context": [
29
- "https://www.w3.org/ns/activitystreams",
30
- "https://sockethub.org/ns/context/v1.jsonld",
31
- "https://sockethub.org/ns/context/platform/xmpp/v1.jsonld"
32
- ],
33
- "actor": {
34
- "id": "user@example.org",
35
- "type": "person"
213
+ type: "query",
214
+ "@context": sc.contextFor("xmpp"),
215
+ actor: {
216
+ id: "room@conference.example.org",
217
+ type: "room",
218
+ name: "Room Display Name"
36
219
  },
37
- "target": {
38
- "id": "friend@jabber.net",
39
- "type": "person"
220
+ target: {
221
+ id: "user@example.org/web",
222
+ type: "person"
40
223
  },
41
- "object": {
42
- "type": "message",
43
- "content": "Hello from Sockethub!"
224
+ object: {
225
+ type: "room-info",
226
+ features: ["http://jabber.org/protocol/muc", "muc_passwordprotected"],
227
+ identities: [{
228
+ category: "conference",
229
+ type: "text",
230
+ name: "Room Display Name"
231
+ }]
232
+ }
233
+ }
234
+ ```
235
+
236
+ ### Disconnect Example
237
+
238
+ ```javascript
239
+ {
240
+ type: "disconnect",
241
+ "@context": sc.contextFor("xmpp"),
242
+ actor: {
243
+ id: "user@example.org/web",
244
+ type: "person"
44
245
  }
45
246
  }
46
247
  ```
47
248
 
48
249
  ### Join Chat Room Example
49
250
 
50
- ```json
251
+ ```javascript
51
252
  {
52
- "type": "join",
53
- "@context": [
54
- "https://www.w3.org/ns/activitystreams",
55
- "https://sockethub.org/ns/context/v1.jsonld",
56
- "https://sockethub.org/ns/context/platform/xmpp/v1.jsonld"
57
- ],
58
- "actor": {
59
- "id": "user@example.org",
60
- "type": "person"
253
+ type: "join",
254
+ "@context": sc.contextFor("xmpp"),
255
+ actor: {
256
+ id: "user@example.org",
257
+ type: "person"
61
258
  },
62
- "target": {
63
- "id": "room@conference.example.org",
64
- "type": "room"
259
+ target: {
260
+ id: "room@conference.example.org",
261
+ type: "room"
65
262
  }
66
263
  }
67
264
  ```
68
265
 
69
266
  ### Request Friend Example
70
267
 
71
- ```json
268
+ ```javascript
72
269
  {
73
- "type": "request-friend",
74
- "@context": [
75
- "https://www.w3.org/ns/activitystreams",
76
- "https://sockethub.org/ns/context/v1.jsonld",
77
- "https://sockethub.org/ns/context/platform/xmpp/v1.jsonld"
78
- ],
79
- "actor": {
80
- "id": "user@example.org",
81
- "type": "person"
270
+ type: "request-friend",
271
+ "@context": sc.contextFor("xmpp"),
272
+ actor: {
273
+ id: "user@example.org",
274
+ type: "person"
82
275
  },
83
- "target": {
84
- "id": "friend@jabber.net",
85
- "type": "person"
276
+ target: {
277
+ id: "friend@jabber.net",
278
+ type: "person"
86
279
  }
87
280
  }
88
281
  ```
@@ -104,4 +297,5 @@ updates through ActivityStreams messages.
104
297
 
105
298
  ## API
106
299
 
107
- Detailed API documentation can be found [here](API.md)
300
+ API reference docs are generated from the source JSDoc and are not committed to this repository.
301
+ Run `bun run doc` in this package to generate them locally.
@@ -0,0 +1,25 @@
1
+ import type { XmppElement } from "@xmpp/client";
2
+ import type { XmppHandlerSession } from "./types.js";
3
+ export declare class IncomingHandlers {
4
+ /** @internal Exposed to allow test access for null-handling validation. */
5
+ session: XmppHandlerSession | undefined;
6
+ constructor(session?: XmppHandlerSession);
7
+ close(): void;
8
+ error(err: {
9
+ text?: string;
10
+ condition?: string;
11
+ toString(): string;
12
+ }): void;
13
+ presence(stanza: XmppElement): void;
14
+ subscribe(to: {
15
+ id: string;
16
+ type: string;
17
+ }, from: string, name?: string): void;
18
+ notifyChatMessage(stanza: XmppElement): void;
19
+ notifyError(stanza: XmppElement): void;
20
+ notifyRoomAttendance(stanza: XmppElement): void;
21
+ notifyRoomInfo(stanza: XmppElement): void;
22
+ notifyRoomInfoError(stanza: XmppElement): void;
23
+ online(): void;
24
+ stanza(stanza: XmppElement): void;
25
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * This is a platform for Sockethub implementing XMPP 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, Logger, PersistentPlatformConfig, PersistentPlatformInterface, PlatformCallback, PlatformSchemaStruct, PlatformSendToClient } from "@sockethub/schemas";
19
+ import { type XmppClientInstance, type XmppClientOptions, type XmppElement } from "@xmpp/client";
20
+ import type { XmppCredentialsObject, XmppPlatformSession } from "./types.js";
21
+ export type { XmppCredentialsObject, XmppPlatformSession } from "./types.js";
22
+ /**
23
+ * Handles all actions related to communication via. the XMPP protocol.
24
+ *
25
+ * Uses `xmpp.js` as a base tool for interacting with XMPP.
26
+ *
27
+ * {@link https://github.com/xmppjs/xmpp.js}
28
+ */
29
+ export default class XMPP implements PersistentPlatformInterface {
30
+ credentialsHash: string | undefined;
31
+ readonly config: PersistentPlatformConfig;
32
+ readonly log: Logger;
33
+ readonly sendToClient: PlatformSendToClient;
34
+ /** @internal Set of bare JIDs for rooms this client has joined. */
35
+ __knownRooms: Set<string>;
36
+ protected __clientConstructor: (options: XmppClientOptions) => XmppClientInstance;
37
+ protected __xml: (name: string, attrs?: Record<string, string | undefined>, ...children: (XmppElement | undefined)[]) => XmppElement;
38
+ private readonly id;
39
+ private __initialized;
40
+ /** @internal Exposed for test access and IncomingHandlers. */
41
+ __client: XmppClientInstance | undefined;
42
+ /**
43
+ * Constructor called from the Sockethub `Platform` instance, passing in a
44
+ * session object.
45
+ * @param session - {@link XmppPlatformSession}
46
+ */
47
+ constructor(session: XmppPlatformSession);
48
+ protected createClient(): void;
49
+ protected createXml(): void;
50
+ /**
51
+ * Mark the platform as disconnected and uninitialized
52
+ * @param stopReconnection - If true, stop automatic reconnection
53
+ */
54
+ private __markDisconnected;
55
+ /**
56
+ * Classify error to determine if reconnection should be attempted
57
+ * @param err - The error from XMPP client
58
+ * @returns 'RECOVERABLE' or 'NON_RECOVERABLE'
59
+ */
60
+ private __classifyError;
61
+ /**
62
+ * Check if the XMPP client is properly connected and can send messages
63
+ * @returns true if client is connected and operational
64
+ */
65
+ private __isClientConnected;
66
+ /**
67
+ * @description
68
+ * JSON schema defining the types this platform accepts.
69
+ *
70
+ * Deployments that accept a bearer-style token in the SASL PLAIN password
71
+ * slot should still pass that token string as `password`. Dedicated token
72
+ * SASL mechanisms such as ejabberd `X-OAUTH2`, Prosody `OAUTHBEARER`,
73
+ * Prosody community `X-TOKEN`, and SASL2 FAST are not implemented by this
74
+ * client. See the package README for canonical credentials and usage
75
+ * payload examples.
76
+ **/
77
+ get schema(): PlatformSchemaStruct;
78
+ /**
79
+ * Returns whether the platform is ready to handle jobs.
80
+ * For XMPP, this means we have successfully connected to the server.
81
+ * During temporary network interruptions with automatic reconnection,
82
+ * remains true to allow queued jobs to retry rather than fail.
83
+ * @returns true if ready to handle jobs
84
+ */
85
+ isInitialized(): boolean;
86
+ /**
87
+ * Connect to the XMPP server.
88
+ *
89
+ * @param job - activity streams object
90
+ * @param credentials - credentials object
91
+ * @param done - callback when job is done
92
+ */
93
+ connect(job: ActivityStream, credentials: XmppCredentialsObject, done: PlatformCallback): void;
94
+ /**
95
+ * Join a room, optionally defining a display name for that room.
96
+ *
97
+ * @param job - activity streams object
98
+ * @param done - callback when job is done
99
+ */
100
+ join(job: ActivityStream, done: PlatformCallback): Promise<void>;
101
+ /**
102
+ * Leave a room
103
+ *
104
+ * @param job - activity streams object
105
+ * @param done - callback when job is done
106
+ */
107
+ leave(job: ActivityStream, done: PlatformCallback): void;
108
+ /**
109
+ * Send a message to a room or private conversation.
110
+ *
111
+ * @param job - activity streams object
112
+ * @param done - callback when job is done
113
+ */
114
+ send(job: ActivityStream, done: PlatformCallback): void;
115
+ /**
116
+ * @description
117
+ * Indicate presence and status message.
118
+ * Valid presence values are "away", "chat", "dnd", "xa", "offline", "online".
119
+ *
120
+ * @param job - activity streams object
121
+ * @param done - callback when job is done
122
+ */
123
+ update(job: ActivityStream, done: PlatformCallback): void;
124
+ /**
125
+ * @description
126
+ * Send friend request
127
+ *
128
+ * @param job - activity streams object
129
+ * @param done - callback when job is done
130
+ */
131
+ "request-friend"(job: ActivityStream, done: PlatformCallback): void;
132
+ /**
133
+ * @description
134
+ * Send a remove friend request
135
+ *
136
+ * @param job - activity streams object
137
+ * @param done - callback when job is done
138
+ */
139
+ "remove-friend"(job: ActivityStream, done: PlatformCallback): void;
140
+ /**
141
+ * @description
142
+ * Confirm a friend request
143
+ *
144
+ * @param job - activity streams object
145
+ * @param done - callback when job is done
146
+ */
147
+ "make-friend"(job: ActivityStream, done: PlatformCallback): void;
148
+ /**
149
+ * Indicate an intent to query something (e.g. get room attendance or room information).
150
+ *
151
+ * @param job - activity streams object
152
+ * @param done - callback when job is done
153
+ */
154
+ query(job: ActivityStream, done: PlatformCallback): void;
155
+ /**
156
+ * Disconnect XMPP client
157
+ * @param _job - activity streams object
158
+ * @param done - callback when done
159
+ */
160
+ disconnect(_job: ActivityStream, done: PlatformCallback): void;
161
+ /**
162
+ * Called when it's time to close any connections or clean data before being wiped
163
+ * forcefully.
164
+ * @param done - callback when complete
165
+ */
166
+ cleanup(done: PlatformCallback): void;
167
+ private __registerHandlers;
168
+ }