matrix-nodejs-homeserver 0.2.0 → 0.3.2
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 +89 -11
- package/dist/adapters/cluster.d.ts +1 -26
- package/dist/adapters/cluster.js +50 -103
- package/dist/api/account.js +407 -354
- package/dist/api/admin.js +82 -61
- package/dist/api/appservice.js +37 -25
- package/dist/api/backup.js +73 -62
- package/dist/api/federation.js +172 -138
- package/dist/api/filters.js +4 -3
- package/dist/api/legacy.js +91 -78
- package/dist/api/media.js +71 -55
- package/dist/api/misc.js +46 -37
- package/dist/api/preview.js +37 -73
- package/dist/api/profile.js +108 -97
- package/dist/api/push.js +114 -99
- package/dist/api/relationships.js +64 -58
- package/dist/api/rooms.js +703 -580
- package/dist/api/rtc.d.ts +0 -1
- package/dist/api/rtc.js +48 -34
- package/dist/api/search.js +133 -127
- package/dist/api/slidingsync.js +41 -31
- package/dist/api/spaces.js +82 -77
- package/dist/api/sync.js +325 -135
- package/dist/api/threadsubs.js +72 -61
- package/dist/api/threepid.js +99 -66
- package/dist/api/uia.js +17 -16
- package/dist/api/user.js +221 -178
- package/dist/core/admin.d.ts +0 -1
- package/dist/core/admin.js +6 -5
- package/dist/core/adminroom.js +9 -8
- package/dist/core/appservices.d.ts +2 -1
- package/dist/core/appservices.js +5 -4
- package/dist/core/audit.d.ts +0 -1
- package/dist/core/authrules.js +26 -25
- package/dist/core/cluster.d.ts +12 -5
- package/dist/core/cluster.js +46 -29
- package/dist/core/delayed.js +2 -1
- package/dist/core/events.js +11 -10
- package/dist/core/html.js +13 -0
- package/dist/core/identity.js +1 -1
- package/dist/core/invitefilter.js +2 -1
- package/dist/core/json.js +10 -1
- package/dist/core/keys.d.ts +10 -1
- package/dist/core/keys.js +62 -42
- package/dist/core/m.d.ts +209 -0
- package/dist/core/m.js +182 -0
- package/dist/core/outbound.d.ts +16 -0
- package/dist/core/outbound.js +172 -0
- package/dist/core/partialstate.js +2 -1
- package/dist/core/policy.js +4 -3
- package/dist/core/push.d.ts +0 -1
- package/dist/core/push.js +41 -28
- package/dist/core/pushrules.js +36 -35
- package/dist/core/ratelimit.d.ts +1 -1
- package/dist/core/rooms.d.ts +10 -1
- package/dist/core/rooms.js +101 -74
- package/dist/core/semaphore.js +23 -0
- package/dist/core/sso.d.ts +15 -1
- package/dist/core/sso.js +38 -2
- package/dist/core/stateres.js +7 -6
- package/dist/core/threepid.js +1 -9
- package/dist/federation/client.js +9 -1
- package/dist/federation/index.d.ts +2 -2
- package/dist/federation/index.js +43 -35
- package/dist/homeserver.d.ts +23 -4
- package/dist/homeserver.js +12 -3
- package/dist/http/handler.js +2 -23
- package/dist/http/input.js +2 -2
- package/dist/http/procedure.js +25 -0
- package/dist/http/router.d.ts +9 -26
- package/dist/http/router.js +10 -30
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/storage/batch.js +11 -0
- package/dist/storage/memory.js +1 -0
- package/dist/storage/schema.js +1 -1
- package/dist/storage/sql.js +20 -1
- package/dist/storage/types.d.ts +16 -0
- package/dist/vendor/zod/LICENSE +21 -0
- package/dist/vendor/zod/v4/classic/errors.js +54 -0
- package/dist/vendor/zod/v4/classic/parse.js +17 -0
- package/dist/vendor/zod/v4/classic/schemas.js +865 -0
- package/dist/vendor/zod/v4/core/api.js +512 -0
- package/dist/vendor/zod/v4/core/checks.js +300 -0
- package/dist/vendor/zod/v4/core/core.js +109 -0
- package/dist/vendor/zod/v4/core/doc.js +35 -0
- package/dist/vendor/zod/v4/core/errors.js +118 -0
- package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
- package/dist/vendor/zod/v4/core/memoizer.js +255 -0
- package/dist/vendor/zod/v4/core/parse.js +206 -0
- package/dist/vendor/zod/v4/core/regexes.js +73 -0
- package/dist/vendor/zod/v4/core/registries.js +48 -0
- package/dist/vendor/zod/v4/core/schemas.js +1378 -0
- package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
- package/dist/vendor/zod/v4/core/util.js +537 -0
- package/dist/vendor/zod/v4/core/versions.js +8 -0
- package/dist/vendor/zod/v4/locales/en.js +110 -0
- package/package.json +8 -6
- package/dist/core/pushrules.d.ts +0 -1
package/dist/api/rtc.d.ts
CHANGED
package/dist/api/rtc.js
CHANGED
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
import { errors } from "../core/errors.js";
|
|
2
|
+
import { string } from "../vendor/zod/v4/classic/schemas.js";
|
|
2
3
|
import { body, fail } from "../http/input.js";
|
|
4
|
+
import { procedure } from "../http/procedure.js";
|
|
3
5
|
import { createHmac } from "node:crypto";
|
|
4
|
-
import { z } from "zod";
|
|
5
6
|
//#region src/api/rtc.ts
|
|
6
7
|
function registerRtcRoutes(r) {
|
|
7
|
-
r.client("GET", "/voip/turnServer",
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
8
|
+
r.client("GET", "/voip/turnServer", procedure({
|
|
9
|
+
auth: "user",
|
|
10
|
+
handler: async (ctx) => {
|
|
11
|
+
const s = ctx.session;
|
|
12
|
+
const turn = ctx.hs.config.rtc.turn;
|
|
13
|
+
if (!turn || !turn.uris.length) return {};
|
|
14
|
+
if (s.isGuest && !turn.allowGuests) throw errors.forbidden("Guests may not use TURN");
|
|
15
|
+
const ttl = turn.ttlSeconds ?? 86400;
|
|
16
|
+
if (turn.sharedSecret) {
|
|
17
|
+
const username = `${Math.floor(Date.now() / 1e3) + ttl}:${s.userId}`;
|
|
18
|
+
return {
|
|
19
|
+
username,
|
|
20
|
+
password: createHmac("sha1", turn.sharedSecret).update(username).digest("base64"),
|
|
21
|
+
uris: turn.uris,
|
|
22
|
+
ttl
|
|
23
|
+
};
|
|
24
|
+
}
|
|
15
25
|
return {
|
|
16
|
-
username,
|
|
17
|
-
password:
|
|
26
|
+
username: turn.username ?? "",
|
|
27
|
+
password: turn.password ?? "",
|
|
18
28
|
uris: turn.uris,
|
|
19
29
|
ttl
|
|
20
30
|
};
|
|
21
31
|
}
|
|
22
|
-
|
|
23
|
-
username: turn.username ?? "",
|
|
24
|
-
password: turn.password ?? "",
|
|
25
|
-
uris: turn.uris,
|
|
26
|
-
ttl
|
|
27
|
-
};
|
|
28
|
-
});
|
|
32
|
+
}));
|
|
29
33
|
const transports = async (ctx) => {
|
|
30
34
|
await ctx.auth();
|
|
31
35
|
return { rtc_transports: ctx.hs.config.rtc.transports ?? [] };
|
|
32
36
|
};
|
|
33
|
-
r.client("GET", "/rtc/transports", transports, ["v1"]);
|
|
34
|
-
r.get("/_matrix/client/unstable/org.matrix.msc4143/rtc/transports", transports);
|
|
37
|
+
r.client("GET", "/rtc/transports", procedure({ handler: transports }), ["v1"]);
|
|
38
|
+
r.get("/_matrix/client/unstable/org.matrix.msc4143/rtc/transports", procedure({ handler: transports }));
|
|
35
39
|
const base = "/_matrix/client/unstable/org.matrix.msc4140/delayed_events";
|
|
36
|
-
r.get(base,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
r.get(base, procedure({
|
|
41
|
+
auth: "user",
|
|
42
|
+
handler: async (ctx) => {
|
|
43
|
+
const s = ctx.session;
|
|
44
|
+
return { delayed_events: await ctx.hs.delayedEvents.list(s.userId) };
|
|
45
|
+
}
|
|
46
|
+
}));
|
|
47
|
+
r.post(`${base}/:delayId`, procedure({
|
|
48
|
+
auth: "user",
|
|
49
|
+
input: body({ action: string(fail("M_MISSING_PARAM", "Missing action")) }),
|
|
50
|
+
handler: async (ctx) => {
|
|
51
|
+
const s = ctx.session;
|
|
52
|
+
await ctx.hs.delayedEvents.update(s.userId, ctx.params.delayId, ctx.input.action);
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
r.post(`${base}/:delayId/:action`, procedure({
|
|
57
|
+
auth: "user",
|
|
58
|
+
handler: async (ctx) => {
|
|
59
|
+
const s = ctx.session;
|
|
60
|
+
await ctx.hs.delayedEvents.update(s.userId, ctx.params.delayId, ctx.params.action);
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
50
64
|
}
|
|
51
65
|
//#endregion
|
|
52
66
|
export { registerRtcRoutes };
|
package/dist/api/search.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { isObject } from "../core/json.js";
|
|
2
|
+
import { M } from "../core/m.js";
|
|
2
3
|
import { getState } from "../core/authrules.js";
|
|
4
|
+
import { looseObject, string, unknown } from "../vendor/zod/v4/classic/schemas.js";
|
|
3
5
|
import { body, fail } from "../http/input.js";
|
|
6
|
+
import { procedure } from "../http/procedure.js";
|
|
4
7
|
import { matchesFilter } from "./filters.js";
|
|
5
8
|
import { paginate, topoToken } from "./rooms.js";
|
|
6
|
-
import { z } from "zod";
|
|
7
9
|
//#region src/api/search.ts
|
|
8
10
|
const DEFAULT_KEYS = [
|
|
9
11
|
"content.body",
|
|
@@ -15,141 +17,145 @@ function valueAt(obj, path) {
|
|
|
15
17
|
}
|
|
16
18
|
function registerSearchRoutes(r) {
|
|
17
19
|
const missingTerm = fail("M_BAD_JSON", "Missing search_categories.room_events.search_term");
|
|
18
|
-
const searchBody = body({ search_categories:
|
|
19
|
-
search_term:
|
|
20
|
-
keys:
|
|
21
|
-
filter:
|
|
22
|
-
order_by:
|
|
20
|
+
const searchBody = body({ search_categories: looseObject({ room_events: looseObject({
|
|
21
|
+
search_term: string(missingTerm),
|
|
22
|
+
keys: unknown().optional(),
|
|
23
|
+
filter: unknown().optional(),
|
|
24
|
+
order_by: unknown().optional()
|
|
23
25
|
}, missingTerm) }, missingTerm) });
|
|
24
|
-
r.client("POST", "/search",
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (row.state_key !== null || !matchesFilter(filter, row.json)) continue;
|
|
52
|
-
let rank = 0;
|
|
53
|
-
const words = [];
|
|
54
|
-
for (const key of keys) {
|
|
55
|
-
const v = valueAt(row.json, key);
|
|
56
|
-
if (typeof v === "string") words.push(...v.toLowerCase().split(/[^\p{L}\p{N}]+/u));
|
|
57
|
-
}
|
|
58
|
-
if (terms.every((t) => words.includes(t))) for (const t of terms) rank += words.filter((w) => w === t).length;
|
|
59
|
-
if (rank > 0) candidates.push({
|
|
60
|
-
ev: hs.rooms.rowToStored(row),
|
|
61
|
-
rank
|
|
26
|
+
r.client("POST", "/search", procedure({
|
|
27
|
+
auth: "user",
|
|
28
|
+
input: searchBody,
|
|
29
|
+
handler: async (ctx) => {
|
|
30
|
+
const s = ctx.session;
|
|
31
|
+
const hs = ctx.hs;
|
|
32
|
+
const criteria = ctx.input.search_categories.room_events;
|
|
33
|
+
const terms = criteria.search_term.toLowerCase().split(/\s+/).filter(Boolean);
|
|
34
|
+
const keys = Array.isArray(criteria.keys) && criteria.keys.length ? criteria.keys : DEFAULT_KEYS;
|
|
35
|
+
const filter = isObject(criteria.filter) ? criteria.filter : {};
|
|
36
|
+
const limit = Math.min(filter.limit ?? 10, 100);
|
|
37
|
+
const orderBy = criteria.order_by === "recent" ? "recent" : "rank";
|
|
38
|
+
const offset = Number.parseInt(ctx.query.get("next_batch") ?? "0", 10) || 0;
|
|
39
|
+
let rooms = await hs.rooms.roomsForUser(s.userId);
|
|
40
|
+
if (filter.rooms) rooms = rooms.filter((r) => filter.rooms.includes(r));
|
|
41
|
+
if (filter.not_rooms) rooms = rooms.filter((r) => !filter.not_rooms.includes(r));
|
|
42
|
+
const hits = [];
|
|
43
|
+
for (const roomId of rooms) {
|
|
44
|
+
const rows = await hs.db.findMany("events", {
|
|
45
|
+
where: [{
|
|
46
|
+
field: "room_id",
|
|
47
|
+
value: roomId
|
|
48
|
+
}],
|
|
49
|
+
sortBy: {
|
|
50
|
+
field: "stream",
|
|
51
|
+
direction: "desc"
|
|
52
|
+
}
|
|
62
53
|
});
|
|
54
|
+
const candidates = [];
|
|
55
|
+
for (const row of rows) {
|
|
56
|
+
if (row.state_key !== null || !matchesFilter(filter, row.json)) continue;
|
|
57
|
+
let rank = 0;
|
|
58
|
+
const words = [];
|
|
59
|
+
for (const key of keys) {
|
|
60
|
+
const v = valueAt(row.json, key);
|
|
61
|
+
if (typeof v === "string") words.push(...v.toLowerCase().split(/[^\p{L}\p{N}]+/u));
|
|
62
|
+
}
|
|
63
|
+
if (terms.every((t) => words.includes(t))) for (const t of terms) rank += words.filter((w) => w === t).length;
|
|
64
|
+
if (rank > 0) candidates.push({
|
|
65
|
+
ev: hs.rooms.rowToStored(row),
|
|
66
|
+
rank
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const visible = new Set((await hs.rooms.filterVisible(s.userId, roomId, candidates.map((c) => c.ev))).map((v) => v.pdu.event_id));
|
|
70
|
+
hits.push(...candidates.filter((c) => visible.has(c.ev.pdu.event_id)));
|
|
63
71
|
}
|
|
64
|
-
|
|
65
|
-
hits.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const viewer = {
|
|
70
|
-
userId: s.userId,
|
|
71
|
-
deviceId: s.deviceId
|
|
72
|
-
};
|
|
73
|
-
const ec = isObject(criteria.event_context) ? criteria.event_context : null;
|
|
74
|
-
const results = [];
|
|
75
|
-
for (const hit of page) {
|
|
76
|
-
const result = {
|
|
77
|
-
rank: hit.rank,
|
|
78
|
-
result: hs.rooms.formatEvent(hit.ev, viewer)
|
|
72
|
+
hits.sort((a, b) => orderBy === "rank" && b.rank !== a.rank ? b.rank - a.rank : b.ev.stream - a.ev.stream);
|
|
73
|
+
const page = hits.slice(offset, offset + limit);
|
|
74
|
+
const viewer = {
|
|
75
|
+
userId: s.userId,
|
|
76
|
+
deviceId: s.deviceId
|
|
79
77
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
const ec = isObject(criteria.event_context) ? criteria.event_context : null;
|
|
79
|
+
const results = [];
|
|
80
|
+
for (const hit of page) {
|
|
81
|
+
const result = {
|
|
82
|
+
rank: hit.rank,
|
|
83
|
+
result: hs.rooms.formatEvent(hit.ev, viewer)
|
|
85
84
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
85
|
+
if (ec) {
|
|
86
|
+
const roomId = hit.ev.pdu.room_id;
|
|
87
|
+
const here = {
|
|
88
|
+
depth: hit.ev.pdu.depth,
|
|
89
|
+
stream: hit.ev.stream
|
|
90
|
+
};
|
|
91
|
+
const before = await paginate(hs, s.userId, roomId, {
|
|
92
|
+
from: here,
|
|
93
|
+
to: null,
|
|
94
|
+
dir: "b",
|
|
95
|
+
limit: ec.before_limit ?? 5
|
|
96
|
+
});
|
|
97
|
+
const after = await paginate(hs, s.userId, roomId, {
|
|
98
|
+
from: here,
|
|
99
|
+
to: null,
|
|
100
|
+
dir: "f",
|
|
101
|
+
limit: ec.after_limit ?? 5
|
|
102
|
+
});
|
|
103
|
+
const context = {
|
|
104
|
+
events_before: before.events.map((e) => hs.rooms.formatEvent(e, viewer)),
|
|
105
|
+
events_after: after.events.map((e) => hs.rooms.formatEvent(e, viewer)),
|
|
106
|
+
start: before.end ?? topoToken(before.events.at(-1) ?? hit.ev),
|
|
107
|
+
end: topoToken(after.events.at(-1) ?? hit.ev)
|
|
108
|
+
};
|
|
109
|
+
if (ec.include_profile) {
|
|
110
|
+
const state = await hs.rooms.currentState(roomId);
|
|
111
|
+
const profile_info = {};
|
|
112
|
+
for (const e of [
|
|
113
|
+
...before.events,
|
|
114
|
+
hit.ev,
|
|
115
|
+
...after.events
|
|
116
|
+
]) {
|
|
117
|
+
const m = getState(state, M.ROOM.MEMBER, e.pdu.sender)?.content;
|
|
118
|
+
profile_info[e.pdu.sender] = {
|
|
119
|
+
displayname: m?.displayname,
|
|
120
|
+
avatar_url: m?.avatar_url
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
context.profile_info = profile_info;
|
|
117
124
|
}
|
|
118
|
-
context
|
|
125
|
+
result.context = context;
|
|
119
126
|
}
|
|
120
|
-
result
|
|
127
|
+
results.push(result);
|
|
121
128
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
129
|
+
const out = {
|
|
130
|
+
results,
|
|
131
|
+
count: hits.length,
|
|
132
|
+
highlights: terms
|
|
133
|
+
};
|
|
134
|
+
if (page.length === limit && offset + limit <= hits.length) out.next_batch = String(offset + limit);
|
|
135
|
+
if (criteria.include_state) {
|
|
136
|
+
out.state = {};
|
|
137
|
+
for (const roomId of new Set(page.map((h) => h.ev.pdu.room_id))) out.state[roomId] = [...(await hs.rooms.currentState(roomId)).values()].map((e) => hs.rooms.formatEvent({
|
|
138
|
+
pdu: e,
|
|
139
|
+
stream: 0,
|
|
140
|
+
snapshot: 0,
|
|
141
|
+
txnId: null,
|
|
142
|
+
txnToken: null
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
if (criteria.groupings?.group_by?.some?.((g) => g?.key === "room_id")) {
|
|
146
|
+
const groups = {};
|
|
147
|
+
hits.forEach((h, i) => {
|
|
148
|
+
(groups[h.ev.pdu.room_id] ??= {
|
|
149
|
+
results: [],
|
|
150
|
+
order: i,
|
|
151
|
+
next_batch: null
|
|
152
|
+
}).results.push(h.ev.pdu.event_id);
|
|
153
|
+
});
|
|
154
|
+
out.groups = { room_id: groups };
|
|
155
|
+
}
|
|
156
|
+
return { search_categories: { room_events: out } };
|
|
150
157
|
}
|
|
151
|
-
|
|
152
|
-
});
|
|
158
|
+
}));
|
|
153
159
|
}
|
|
154
160
|
//#endregion
|
|
155
161
|
export { registerSearchRoutes };
|
package/dist/api/slidingsync.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import { isObject } from "../core/json.js";
|
|
2
|
+
import { M, UNSTABLE } from "../core/m.js";
|
|
2
3
|
import { splitStateKey, toClientEvent } from "../core/events.js";
|
|
3
4
|
import { MatrixError } from "../core/errors.js";
|
|
4
5
|
import { getState } from "../core/authrules.js";
|
|
6
|
+
import { record, string, unknown } from "../vendor/zod/v4/classic/schemas.js";
|
|
5
7
|
import { body, lenient } from "../http/input.js";
|
|
8
|
+
import { procedure } from "../http/procedure.js";
|
|
6
9
|
import { roomEphemeral, roomTimeline, toStripped } from "./sync.js";
|
|
7
|
-
import { z } from "zod";
|
|
8
10
|
//#region src/api/slidingsync.ts
|
|
9
11
|
const slidingSyncBody = body({
|
|
10
|
-
conn_id: lenient(
|
|
11
|
-
lists: lenient(
|
|
12
|
-
room_subscriptions: lenient(
|
|
13
|
-
extensions: lenient(
|
|
12
|
+
conn_id: lenient(string()),
|
|
13
|
+
lists: lenient(record(string(), unknown())),
|
|
14
|
+
room_subscriptions: lenient(record(string(), unknown())),
|
|
15
|
+
extensions: lenient(record(string(), unknown()))
|
|
14
16
|
});
|
|
15
17
|
const BUMP_TYPES = /* @__PURE__ */ new Set([
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
M.ROOM.CREATE,
|
|
19
|
+
M.ROOM.MESSAGE,
|
|
20
|
+
M.ROOM.ENCRYPTED,
|
|
21
|
+
M.STICKER,
|
|
22
|
+
M.CALL.INVITE,
|
|
23
|
+
M.POLL.START,
|
|
24
|
+
M.BEACON_INFO
|
|
23
25
|
]);
|
|
24
26
|
async function loadConnection(hs, s, connId) {
|
|
25
27
|
return hs.db.findOne("sliding_sync_connections", [
|
|
@@ -62,17 +64,25 @@ function matchesRequiredState(required, type, key, userId, lazyMembers) {
|
|
|
62
64
|
if (t !== "*" && t !== type) continue;
|
|
63
65
|
if (k === "*" || k === key) return true;
|
|
64
66
|
if (k === "$ME" && key === userId) return true;
|
|
65
|
-
if (k === "$LAZY" && type ===
|
|
67
|
+
if (k === "$LAZY" && type === M.ROOM.MEMBER && lazyMembers.has(key)) return true;
|
|
66
68
|
}
|
|
67
69
|
return false;
|
|
68
70
|
}
|
|
69
71
|
function registerSlidingSyncRoutes(r) {
|
|
70
|
-
r.post("/_matrix/client/unstable/org.matrix.simplified_msc3575/sync", {
|
|
71
|
-
|
|
72
|
+
r.post("/_matrix/client/unstable/org.matrix.simplified_msc3575/sync", procedure({
|
|
73
|
+
auth: "user",
|
|
74
|
+
input: slidingSyncBody,
|
|
75
|
+
handler: slidingSync
|
|
76
|
+
}));
|
|
77
|
+
r.post("/_matrix/client/v5/sync", procedure({
|
|
78
|
+
auth: "user",
|
|
79
|
+
input: slidingSyncBody,
|
|
80
|
+
handler: slidingSync
|
|
81
|
+
}));
|
|
72
82
|
}
|
|
73
83
|
async function slidingSync(ctx) {
|
|
74
84
|
const hs = ctx.hs;
|
|
75
|
-
const s =
|
|
85
|
+
const s = ctx.session;
|
|
76
86
|
const body = ctx.input;
|
|
77
87
|
const connId = body.conn_id ?? "";
|
|
78
88
|
const posParam = ctx.query.get("pos");
|
|
@@ -143,7 +153,7 @@ async function buildResponse(hs, s, body, conn, since, to) {
|
|
|
143
153
|
},
|
|
144
154
|
{
|
|
145
155
|
field: "type",
|
|
146
|
-
value:
|
|
156
|
+
value: M.DIRECT
|
|
147
157
|
}
|
|
148
158
|
]);
|
|
149
159
|
const dmRooms = new Set(Object.values(direct?.content ?? {}).flat().filter((x) => typeof x === "string"));
|
|
@@ -162,7 +172,7 @@ async function buildResponse(hs, s, body, conn, since, to) {
|
|
|
162
172
|
bump,
|
|
163
173
|
state
|
|
164
174
|
});
|
|
165
|
-
const invite = getState(state,
|
|
175
|
+
const invite = getState(state, M.ROOM.MEMBER, userId);
|
|
166
176
|
if (m.membership === "invite" && invite?.content?.is_direct) dmRooms.add(m.room_id);
|
|
167
177
|
}
|
|
168
178
|
infos.sort((a, b) => b.bump - a.bump);
|
|
@@ -174,8 +184,8 @@ async function buildResponse(hs, s, body, conn, since, to) {
|
|
|
174
184
|
let rooms = infos;
|
|
175
185
|
if (f.is_dm !== void 0) rooms = rooms.filter((r) => dmRooms.has(r.roomId) === f.is_dm);
|
|
176
186
|
if (f.is_invite !== void 0) rooms = rooms.filter((r) => r.membership === "invite" === f.is_invite);
|
|
177
|
-
if (f.is_encrypted !== void 0) rooms = rooms.filter((r) => !!getState(r.state,
|
|
178
|
-
const roomType = (r) => getState(r.state,
|
|
187
|
+
if (f.is_encrypted !== void 0) rooms = rooms.filter((r) => !!getState(r.state, M.ROOM.ENCRYPTION) === f.is_encrypted);
|
|
188
|
+
const roomType = (r) => getState(r.state, M.ROOM.CREATE)?.content?.type ?? null;
|
|
179
189
|
if (f.room_types) rooms = rooms.filter((r) => f.room_types.includes(roomType(r)));
|
|
180
190
|
if (f.not_room_types) rooms = rooms.filter((r) => !f.not_room_types.includes(roomType(r)));
|
|
181
191
|
if (f.spaces) {
|
|
@@ -184,7 +194,7 @@ async function buildResponse(hs, s, body, conn, since, to) {
|
|
|
184
194
|
if (!await hs.rooms.exists(space)) continue;
|
|
185
195
|
for (const key of (await hs.rooms.currentState(space)).keys()) {
|
|
186
196
|
const [t, k] = splitStateKey(key);
|
|
187
|
-
if (t ===
|
|
197
|
+
if (t === M.SPACE.CHILD) children.add(k);
|
|
188
198
|
}
|
|
189
199
|
}
|
|
190
200
|
rooms = rooms.filter((r) => children.has(r.roomId));
|
|
@@ -265,13 +275,13 @@ async function buildRoom(hs, s, info, config, since, to, isDm) {
|
|
|
265
275
|
timeline: [],
|
|
266
276
|
required_state: []
|
|
267
277
|
};
|
|
268
|
-
const name = getState(state,
|
|
278
|
+
const name = getState(state, M.ROOM.NAME)?.content?.name;
|
|
269
279
|
if (typeof name === "string") room.name = name;
|
|
270
|
-
const avatar = getState(state,
|
|
280
|
+
const avatar = getState(state, M.ROOM.AVATAR)?.content?.url;
|
|
271
281
|
if (typeof avatar === "string") room.avatar = avatar;
|
|
272
282
|
if (isDm) room.is_dm = true;
|
|
273
283
|
if (info.membership === "invite" || info.membership === "knock") {
|
|
274
|
-
const own = getState(state,
|
|
284
|
+
const own = getState(state, M.ROOM.MEMBER, userId);
|
|
275
285
|
const ev = own?.event_id ? await hs.rooms.getEvent(own.event_id) : null;
|
|
276
286
|
if (ev) {
|
|
277
287
|
const key = info.membership === "invite" ? "invite_room_state" : "knock_room_state";
|
|
@@ -323,7 +333,7 @@ async function buildRoom(hs, s, info, config, since, to, isDm) {
|
|
|
323
333
|
const heroes = [];
|
|
324
334
|
for (const [key, ev] of state) {
|
|
325
335
|
const [type, sk] = splitStateKey(key);
|
|
326
|
-
if (type !==
|
|
336
|
+
if (type !== M.ROOM.MEMBER) continue;
|
|
327
337
|
const m = ev.content?.membership;
|
|
328
338
|
if (m === "join") joined++;
|
|
329
339
|
if (m === "invite") invited++;
|
|
@@ -410,10 +420,10 @@ async function buildExtensions(hs, s, ext, since, to, roomIds, next) {
|
|
|
410
420
|
if (r.room_id === "") global.push(ev);
|
|
411
421
|
else if (roomIds.includes(r.room_id) || since !== null) (rooms[r.room_id] ??= []).push(ev);
|
|
412
422
|
}
|
|
413
|
-
if (since === null && !global.some((e) => e.type ===
|
|
423
|
+
if (since === null && !global.some((e) => e.type === M.PUSH_RULES)) {
|
|
414
424
|
const { loadRules } = await import("../core/pushrules.js");
|
|
415
425
|
global.push({
|
|
416
|
-
type:
|
|
426
|
+
type: M.PUSH_RULES,
|
|
417
427
|
content: { global: await loadRules(hs, userId) }
|
|
418
428
|
});
|
|
419
429
|
}
|
|
@@ -422,9 +432,9 @@ async function buildExtensions(hs, s, ext, since, to, roomIds, next) {
|
|
|
422
432
|
rooms
|
|
423
433
|
};
|
|
424
434
|
}
|
|
425
|
-
if (ext[
|
|
435
|
+
if (ext[UNSTABLE.IO.ELEMENT.MSC4308.THREAD_SUBSCRIPTIONS]?.enabled) {
|
|
426
436
|
const { threadSubscriptionsExtension } = await import("./threadsubs.js");
|
|
427
|
-
out[
|
|
437
|
+
out[UNSTABLE.IO.ELEMENT.MSC4308.THREAD_SUBSCRIPTIONS] = await threadSubscriptionsExtension(hs, userId, ext[UNSTABLE.IO.ELEMENT.MSC4308.THREAD_SUBSCRIPTIONS], since, to);
|
|
428
438
|
}
|
|
429
439
|
if (ext.receipts?.enabled || ext.typing?.enabled) {
|
|
430
440
|
const p = {
|
|
@@ -444,8 +454,8 @@ async function buildExtensions(hs, s, ext, since, to, roomIds, next) {
|
|
|
444
454
|
for (const roomId of since === null ? roomIds : [...joined]) {
|
|
445
455
|
if (!joined.has(roomId)) continue;
|
|
446
456
|
for (const e of await roomEphemeral(hs, p, roomId)) {
|
|
447
|
-
if (e.type ===
|
|
448
|
-
if (e.type ===
|
|
457
|
+
if (e.type === M.RECEIPT && ext.receipts?.enabled) receipts[roomId] = e;
|
|
458
|
+
if (e.type === M.TYPING && ext.typing?.enabled && (since !== null || e.content.user_ids.length)) typing[roomId] = e;
|
|
449
459
|
}
|
|
450
460
|
}
|
|
451
461
|
if (ext.receipts?.enabled) out.receipts = { rooms: receipts };
|