@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 +242 -48
- package/dist/incoming-handlers.d.ts +25 -0
- package/dist/index.d.ts +168 -0
- package/dist/index.js +26692 -26563
- package/dist/index.js.map +130 -131
- package/dist/schema.d.ts +2 -0
- package/dist/types.d.ts +33 -0
- package/dist/utils.d.ts +19 -0
- package/package.json +14 -11
- package/src/incoming-handlers.js +0 -355
- package/src/incoming-handlers.test.data.js +0 -312
- package/src/incoming-handlers.test.js +0 -115
- package/src/index.js +0 -774
- package/src/index.test.js +0 -635
- package/src/schema.js +0 -64
- package/src/utils.js +0 -21
- package/src/utils.test.js +0 -58
package/dist/schema.d.ts
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ActivityActor, CredentialsObject, Logger, PlatformSendToClient, PlatformSession } from "@sockethub/schemas";
|
|
2
|
+
export interface XmppPlatformSession extends PlatformSession {
|
|
3
|
+
id: string;
|
|
4
|
+
}
|
|
5
|
+
export interface XmppCredentialsObject extends CredentialsObject {
|
|
6
|
+
object: {
|
|
7
|
+
type: string;
|
|
8
|
+
userAddress: string;
|
|
9
|
+
password: string;
|
|
10
|
+
server?: string;
|
|
11
|
+
port?: number;
|
|
12
|
+
resource: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface XmppBuiltCredentials {
|
|
16
|
+
service: string;
|
|
17
|
+
username: string;
|
|
18
|
+
password: string;
|
|
19
|
+
resource?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Narrow interface for what IncomingHandlers uses from the XMPP platform instance.
|
|
23
|
+
* Avoids a circular import between incoming-handlers.ts and index.ts.
|
|
24
|
+
*/
|
|
25
|
+
export interface XmppHandlerSession {
|
|
26
|
+
log: Logger;
|
|
27
|
+
sendToClient: PlatformSendToClient;
|
|
28
|
+
__knownRooms: Set<string>;
|
|
29
|
+
actor?: ActivityActor;
|
|
30
|
+
connection?: {
|
|
31
|
+
disconnect?: () => void;
|
|
32
|
+
};
|
|
33
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { XmppElement } from "@xmpp/client";
|
|
2
|
+
import type { XmppBuiltCredentials, XmppCredentialsObject } from "./types.js";
|
|
3
|
+
interface XDataFieldResult {
|
|
4
|
+
var: string;
|
|
5
|
+
field: {
|
|
6
|
+
type: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
value: string | number | boolean | string[] | null;
|
|
9
|
+
options?: Array<{
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare const utils: {
|
|
16
|
+
buildXmppCredentials: (credentials: XmppCredentialsObject) => XmppBuiltCredentials;
|
|
17
|
+
parseXDataField: (field: XmppElement) => XDataFieldResult | null;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sockethub/platform-xmpp",
|
|
3
3
|
"description": "A sockethub platform module implementing XMPP functionality",
|
|
4
|
-
"version": "5.0.0-alpha.
|
|
4
|
+
"version": "5.0.0-alpha.13",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Nick Jennings <nick@silverbucket.net>",
|
|
@@ -12,17 +12,16 @@
|
|
|
12
12
|
"main": "dist/index.js",
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
|
15
|
-
"
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
16
|
"import": "./dist/index.js",
|
|
17
17
|
"default": "./dist/index.js"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
-
"src/",
|
|
22
21
|
"dist/"
|
|
23
22
|
],
|
|
24
23
|
"engines": {
|
|
25
|
-
"
|
|
24
|
+
"node": ">=20"
|
|
26
25
|
},
|
|
27
26
|
"keywords": [
|
|
28
27
|
"sockethub",
|
|
@@ -40,27 +39,31 @@
|
|
|
40
39
|
},
|
|
41
40
|
"homepage": "https://github.com/sockethub/sockethub/tree/master/packages/platform-xmpp",
|
|
42
41
|
"scripts": {
|
|
43
|
-
"build": "bun build src/index.
|
|
42
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm --sourcemap=external",
|
|
44
43
|
"clean": "rm -rf dist",
|
|
45
44
|
"clean:deps": "rm -rf node_modules",
|
|
46
|
-
"doc": "
|
|
45
|
+
"doc": "typedoc --options typedoc.json"
|
|
47
46
|
},
|
|
48
47
|
"dependencies": {
|
|
48
|
+
"@sockethub/util": "^1.0.0-alpha.1",
|
|
49
49
|
"@xmpp/client": "^0.13.6"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@sockethub/schemas": "^3.0.0-alpha.
|
|
52
|
+
"@sockethub/schemas": "^3.0.0-alpha.13",
|
|
53
|
+
"@types/bun": "latest",
|
|
53
54
|
"@xmpp/xml": "^0.13.2",
|
|
54
|
-
"
|
|
55
|
-
"sinon": "^17.0.2"
|
|
55
|
+
"ajv": "^8.17.1",
|
|
56
|
+
"sinon": "^17.0.2",
|
|
57
|
+
"typedoc": "^0.28.14"
|
|
56
58
|
},
|
|
57
59
|
"peerDependencies": {
|
|
58
|
-
"@sockethub/server": "^5.0.0-alpha.
|
|
60
|
+
"@sockethub/server": "^5.0.0-alpha.13"
|
|
59
61
|
},
|
|
60
62
|
"peerDependenciesMeta": {
|
|
61
63
|
"@sockethub/server": {
|
|
62
64
|
"optional": true
|
|
63
65
|
}
|
|
64
66
|
},
|
|
65
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "864733e5b34449ef39542eceb4ff11aa308081bc",
|
|
68
|
+
"types": "./dist/index.d.ts"
|
|
66
69
|
}
|
package/src/incoming-handlers.js
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import { buildCanonicalContext } from "@sockethub/schemas";
|
|
2
|
-
import { PlatformSchema } from "./schema.js";
|
|
3
|
-
|
|
4
|
-
const XMPP_CONTEXT = buildCanonicalContext(PlatformSchema.contextUrl);
|
|
5
|
-
|
|
6
|
-
function getMessageBody(stanza) {
|
|
7
|
-
for (const elem of stanza.children) {
|
|
8
|
-
if (elem.name === "body") {
|
|
9
|
-
return elem.children.join(" ");
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getMessageTimestamp(stanza) {
|
|
15
|
-
try {
|
|
16
|
-
const delay = stanza.children.find((c) => c.name === "delay");
|
|
17
|
-
return delay.attrs.stamp;
|
|
18
|
-
} catch (_e) {
|
|
19
|
-
// no timestamp
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function getMessageId(stanza) {
|
|
25
|
-
try {
|
|
26
|
-
return stanza.attrs.id;
|
|
27
|
-
} catch (_e) {
|
|
28
|
-
// no message id
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getMessageStanzaId(stanza) {
|
|
34
|
-
try {
|
|
35
|
-
const stanzaId = stanza.children.find((c) => c.name === "stanza-id");
|
|
36
|
-
return stanzaId.attrs.id;
|
|
37
|
-
} catch (_e) {
|
|
38
|
-
// no stanza id
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getMessageReplaceId(stanza) {
|
|
44
|
-
try {
|
|
45
|
-
const replaceEl = stanza.children.find((c) => c.name === "replace");
|
|
46
|
-
return replaceEl.attrs.id;
|
|
47
|
-
} catch (_e) {
|
|
48
|
-
// no origin id
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function getPresence(stanza) {
|
|
54
|
-
if (stanza.getChild("show")) {
|
|
55
|
-
return stanza.getChild("show").getText();
|
|
56
|
-
}
|
|
57
|
-
return stanza.attrs.type === "unavailable" ? "offline" : "online";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class IncomingHandlers {
|
|
61
|
-
constructor(session) {
|
|
62
|
-
this.session = session;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
close() {
|
|
66
|
-
if (!this.session) {
|
|
67
|
-
console.debug("close event received but session is undefined");
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
this.session.log.debug(
|
|
72
|
-
"received close event with no handler specified",
|
|
73
|
-
);
|
|
74
|
-
if (this.session.actor && this.session.sendToClient) {
|
|
75
|
-
this.session.sendToClient({
|
|
76
|
-
"@context": XMPP_CONTEXT,
|
|
77
|
-
type: "close",
|
|
78
|
-
actor: this.session.actor,
|
|
79
|
-
target: this.session.actor,
|
|
80
|
-
});
|
|
81
|
-
this.session.log.debug(
|
|
82
|
-
`**** xmpp this.session.for ${this.session.actor.id} closed`,
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
if (
|
|
86
|
-
this.session.connection &&
|
|
87
|
-
typeof this.session.connection.disconnect === "function"
|
|
88
|
-
) {
|
|
89
|
-
this.session.connection.disconnect();
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
error(err) {
|
|
94
|
-
try {
|
|
95
|
-
this.session.sendToClient({
|
|
96
|
-
"@context": XMPP_CONTEXT,
|
|
97
|
-
type: "error",
|
|
98
|
-
error: err.text || err.toString(),
|
|
99
|
-
object: {
|
|
100
|
-
type: "message",
|
|
101
|
-
condition: err.condition || "unknown",
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
} catch (e) {
|
|
105
|
-
this.session.log.debug("*** XMPP ERROR (rl catch): ", e);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
presence(stanza) {
|
|
110
|
-
const obj = {
|
|
111
|
-
"@context": XMPP_CONTEXT,
|
|
112
|
-
type: "update",
|
|
113
|
-
actor: {
|
|
114
|
-
type: "person",
|
|
115
|
-
id: stanza.attrs.from,
|
|
116
|
-
},
|
|
117
|
-
object: {
|
|
118
|
-
type: "presence",
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
if (stanza.getChildText("status")) {
|
|
122
|
-
obj.object.content = stanza.getChildText("status");
|
|
123
|
-
}
|
|
124
|
-
obj.object.presence = getPresence(stanza);
|
|
125
|
-
if (stanza.attrs.to) {
|
|
126
|
-
obj.target = { id: stanza.attrs.to, type: "person" };
|
|
127
|
-
} else {
|
|
128
|
-
obj.actor.name = stanza.attrs.from.split("/")[1];
|
|
129
|
-
}
|
|
130
|
-
this.session.log.debug(
|
|
131
|
-
`received contact presence update from ${stanza.attrs.from}`,
|
|
132
|
-
);
|
|
133
|
-
this.session.sendToClient(obj);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
subscribe(to, from, name) {
|
|
137
|
-
this.session.log.debug(`received subscribe request from ${from}`);
|
|
138
|
-
const actor = { id: from, type: "person" };
|
|
139
|
-
if (name) {
|
|
140
|
-
actor.name = name;
|
|
141
|
-
}
|
|
142
|
-
this.session.sendToClient({
|
|
143
|
-
"@context": XMPP_CONTEXT,
|
|
144
|
-
type: "request-friend",
|
|
145
|
-
actor: actor,
|
|
146
|
-
target: to,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// unsubscribe(from) {
|
|
151
|
-
// this.session.log.debug('received unsubscribe request from ' + from);
|
|
152
|
-
// this.session.sendToClient({
|
|
153
|
-
// type: "remove-friend",
|
|
154
|
-
// actor: { id: from },
|
|
155
|
-
// target: this.session.actor
|
|
156
|
-
// });
|
|
157
|
-
// }
|
|
158
|
-
|
|
159
|
-
notifyChatMessage(stanza) {
|
|
160
|
-
const message = getMessageBody(stanza);
|
|
161
|
-
if (!message) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
const from = stanza.attrs.from;
|
|
165
|
-
const timestamp = getMessageTimestamp(stanza);
|
|
166
|
-
const messageId = getMessageId(stanza);
|
|
167
|
-
const type = stanza.attrs.type === "groupchat" ? "room" : "person";
|
|
168
|
-
|
|
169
|
-
const activity = {
|
|
170
|
-
"@context": XMPP_CONTEXT,
|
|
171
|
-
type: "send",
|
|
172
|
-
actor: {
|
|
173
|
-
type: "person",
|
|
174
|
-
id: from,
|
|
175
|
-
},
|
|
176
|
-
target: {
|
|
177
|
-
type: type,
|
|
178
|
-
id: stanza.attrs.to,
|
|
179
|
-
},
|
|
180
|
-
object: {
|
|
181
|
-
type: "message",
|
|
182
|
-
id: messageId,
|
|
183
|
-
content: message,
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const messageStanzaId = getMessageStanzaId(stanza);
|
|
188
|
-
if (messageStanzaId) {
|
|
189
|
-
activity.object["xmpp:stanza-id"] = messageStanzaId;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const messageReplaceId = getMessageReplaceId(stanza);
|
|
193
|
-
if (messageReplaceId) {
|
|
194
|
-
activity.object["xmpp:replace"] = { id: messageReplaceId };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (type === "room") {
|
|
198
|
-
[activity.target.id, activity.actor.name] = from.split("/");
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (timestamp) {
|
|
202
|
-
activity.published = new Date(timestamp).toISOString();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
this.session.sendToClient(activity);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
notifyError(stanza) {
|
|
209
|
-
const error = stanza.getChild("error");
|
|
210
|
-
let message = stanza.toString();
|
|
211
|
-
let type = "message";
|
|
212
|
-
if (stanza.is("presence")) {
|
|
213
|
-
type = "update";
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (error) {
|
|
217
|
-
message = error.toString();
|
|
218
|
-
if (error.getChild("remote-server-not-found")) {
|
|
219
|
-
// when we get this.session.type of return message, we know it was a response from a join
|
|
220
|
-
type = "join";
|
|
221
|
-
message = `remote server not found ${stanza.attrs.from}`;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
this.session.sendToClient({
|
|
226
|
-
"@context": XMPP_CONTEXT,
|
|
227
|
-
type: type,
|
|
228
|
-
actor: {
|
|
229
|
-
id: stanza.attrs.from,
|
|
230
|
-
type: "room",
|
|
231
|
-
},
|
|
232
|
-
error: message,
|
|
233
|
-
target: {
|
|
234
|
-
id: stanza.attrs.to,
|
|
235
|
-
type: "person",
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
notifyRoomAttendance(stanza) {
|
|
241
|
-
const query = stanza.getChild("query");
|
|
242
|
-
if (query) {
|
|
243
|
-
const members = [];
|
|
244
|
-
const entries = query.getChildren("item");
|
|
245
|
-
for (const e in entries) {
|
|
246
|
-
if (!Object.hasOwn(entries, e)) {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
members.push(entries[e].attrs.name);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
this.session.sendToClient({
|
|
253
|
-
"@context": XMPP_CONTEXT,
|
|
254
|
-
type: "query",
|
|
255
|
-
actor: {
|
|
256
|
-
id: stanza.attrs.from,
|
|
257
|
-
type: "room",
|
|
258
|
-
},
|
|
259
|
-
target: {
|
|
260
|
-
id: stanza.attrs.to,
|
|
261
|
-
type: "person",
|
|
262
|
-
},
|
|
263
|
-
object: {
|
|
264
|
-
type: "attendance",
|
|
265
|
-
members: members,
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
online() {
|
|
272
|
-
this.session.log.debug("online");
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Handles all unknown conditions that we don't have an explicit handler for
|
|
277
|
-
**/
|
|
278
|
-
stanza(stanza) {
|
|
279
|
-
// console.log("incoming stanza ", stanza);
|
|
280
|
-
if (stanza.attrs.type === "error") {
|
|
281
|
-
this.notifyError(stanza);
|
|
282
|
-
} else if (stanza.is("message")) {
|
|
283
|
-
this.notifyChatMessage(stanza);
|
|
284
|
-
} else if (stanza.is("presence")) {
|
|
285
|
-
this.presence(stanza);
|
|
286
|
-
} else if (stanza.is("iq")) {
|
|
287
|
-
if (
|
|
288
|
-
stanza.attrs.id === "muc_id" &&
|
|
289
|
-
stanza.attrs.type === "result"
|
|
290
|
-
) {
|
|
291
|
-
this.session.log.debug("got room attendance list");
|
|
292
|
-
return this.notifyRoomAttendance(stanza);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// todo: clean up this area, unsure of what these are
|
|
296
|
-
const query = stanza.getChild("query");
|
|
297
|
-
if (query) {
|
|
298
|
-
const entries = query.getChildren("item");
|
|
299
|
-
for (const e in entries) {
|
|
300
|
-
if (!entries.hasOwn(e)) {
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
this.session.log.debug("STANZA ATTRS: ", entries[e].attrs);
|
|
304
|
-
if (entries[e].attrs.subscription === "both") {
|
|
305
|
-
this.session.sendToClient({
|
|
306
|
-
"@context": XMPP_CONTEXT,
|
|
307
|
-
type: "update",
|
|
308
|
-
actor: {
|
|
309
|
-
id: entries[e].attrs.jid,
|
|
310
|
-
name: entries[e].attrs.name,
|
|
311
|
-
},
|
|
312
|
-
target: this.session.actor,
|
|
313
|
-
object: {
|
|
314
|
-
type: "presence",
|
|
315
|
-
status: "",
|
|
316
|
-
presence: getPresence(entries[e]),
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
} else if (
|
|
320
|
-
entries[e].attrs.subscription === "from" &&
|
|
321
|
-
entries[e].attrs.ask &&
|
|
322
|
-
entries[e].attrs.ask === "subscribe"
|
|
323
|
-
) {
|
|
324
|
-
this.session.sendToClient({
|
|
325
|
-
"@context": XMPP_CONTEXT,
|
|
326
|
-
type: "update",
|
|
327
|
-
actor: {
|
|
328
|
-
id: entries[e].attrs.jid,
|
|
329
|
-
name: entries[e].attrs.name,
|
|
330
|
-
},
|
|
331
|
-
target: this.session.actor,
|
|
332
|
-
object: {
|
|
333
|
-
type: "presence",
|
|
334
|
-
statusText: "",
|
|
335
|
-
presence: "notauthorized",
|
|
336
|
-
},
|
|
337
|
-
});
|
|
338
|
-
} else {
|
|
339
|
-
/**
|
|
340
|
-
* can't figure out how to know if one of these query stanzas are from
|
|
341
|
-
* added contacts or pending requests
|
|
342
|
-
*/
|
|
343
|
-
this.subscribe(
|
|
344
|
-
this.session.actor,
|
|
345
|
-
entries[e].attrs.jid,
|
|
346
|
-
entries[e].attrs.name,
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
} else {
|
|
352
|
-
this.session.log.debug(`got XMPP unknown stanza... ${stanza}`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|