@gakr-gakr/matrix 0.1.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/CHANGELOG.md +285 -0
- package/SPEC-SUPPORT.md +116 -0
- package/api.ts +38 -0
- package/auth-presence.ts +56 -0
- package/autobot.plugin.json +28 -0
- package/channel-plugin-api.ts +3 -0
- package/cli-metadata.ts +11 -0
- package/contract-api.ts +17 -0
- package/doctor-contract-api.ts +1 -0
- package/helper-api.ts +3 -0
- package/index.ts +55 -0
- package/package.json +101 -0
- package/plugin-entry.handlers.runtime.ts +1 -0
- package/runtime-api.ts +72 -0
- package/runtime-heavy-api.ts +1 -0
- package/runtime-setter-api.ts +3 -0
- package/secret-contract-api.ts +5 -0
- package/setup-entry.ts +17 -0
- package/setup-plugin-api.ts +3 -0
- package/src/account-selection.ts +223 -0
- package/src/actions.ts +346 -0
- package/src/approval-auth.ts +25 -0
- package/src/approval-handler.runtime.ts +595 -0
- package/src/approval-ids.ts +6 -0
- package/src/approval-native.ts +348 -0
- package/src/approval-reaction-auth.ts +45 -0
- package/src/approval-reactions.ts +313 -0
- package/src/auth-precedence.ts +61 -0
- package/src/channel-account-paths.ts +97 -0
- package/src/channel.runtime.ts +17 -0
- package/src/channel.setup.ts +48 -0
- package/src/channel.ts +667 -0
- package/src/cli-metadata.ts +19 -0
- package/src/cli.ts +2298 -0
- package/src/config-adapter.ts +41 -0
- package/src/config-schema.ts +159 -0
- package/src/config-ui-hints.ts +56 -0
- package/src/directory-live.ts +238 -0
- package/src/doctor-contract.ts +287 -0
- package/src/doctor.ts +262 -0
- package/src/env-vars.ts +92 -0
- package/src/exec-approval-resolver.ts +23 -0
- package/src/exec-approvals.ts +293 -0
- package/src/group-mentions.ts +41 -0
- package/src/legacy-crypto-inspector-availability.ts +60 -0
- package/src/legacy-crypto.ts +531 -0
- package/src/legacy-state.ts +156 -0
- package/src/matrix/account-config.ts +175 -0
- package/src/matrix/accounts.ts +194 -0
- package/src/matrix/actions/client.ts +31 -0
- package/src/matrix/actions/devices.ts +34 -0
- package/src/matrix/actions/limits.ts +6 -0
- package/src/matrix/actions/messages.ts +129 -0
- package/src/matrix/actions/pins.ts +63 -0
- package/src/matrix/actions/polls.ts +109 -0
- package/src/matrix/actions/profile.ts +37 -0
- package/src/matrix/actions/reactions.ts +59 -0
- package/src/matrix/actions/room.ts +71 -0
- package/src/matrix/actions/summary.ts +88 -0
- package/src/matrix/actions/types.ts +63 -0
- package/src/matrix/actions/verification.ts +589 -0
- package/src/matrix/actions.ts +37 -0
- package/src/matrix/active-client.ts +26 -0
- package/src/matrix/async-lock.ts +18 -0
- package/src/matrix/backup-health.ts +124 -0
- package/src/matrix/client/config-runtime-api.ts +9 -0
- package/src/matrix/client/config-secret-input.runtime.ts +1 -0
- package/src/matrix/client/config.ts +853 -0
- package/src/matrix/client/create-client.ts +105 -0
- package/src/matrix/client/env-auth.ts +95 -0
- package/src/matrix/client/file-sync-store.ts +289 -0
- package/src/matrix/client/logging.ts +140 -0
- package/src/matrix/client/migration-snapshot.runtime.ts +1 -0
- package/src/matrix/client/private-network-host.ts +1 -0
- package/src/matrix/client/runtime.ts +4 -0
- package/src/matrix/client/shared.ts +316 -0
- package/src/matrix/client/storage.ts +543 -0
- package/src/matrix/client/types.ts +50 -0
- package/src/matrix/client/url-validation.ts +76 -0
- package/src/matrix/client-bootstrap.ts +173 -0
- package/src/matrix/client.ts +23 -0
- package/src/matrix/config-paths.ts +31 -0
- package/src/matrix/config-update.ts +292 -0
- package/src/matrix/credentials-read.ts +207 -0
- package/src/matrix/credentials-write.runtime.ts +35 -0
- package/src/matrix/credentials.ts +95 -0
- package/src/matrix/deps.ts +309 -0
- package/src/matrix/device-health.ts +31 -0
- package/src/matrix/direct-management.ts +349 -0
- package/src/matrix/direct-room.ts +128 -0
- package/src/matrix/draft-stream.ts +225 -0
- package/src/matrix/encryption-guidance.ts +24 -0
- package/src/matrix/errors.ts +21 -0
- package/src/matrix/format.ts +426 -0
- package/src/matrix/legacy-crypto-inspector.ts +95 -0
- package/src/matrix/media-errors.ts +20 -0
- package/src/matrix/media-text.ts +162 -0
- package/src/matrix/monitor/access-state.ts +145 -0
- package/src/matrix/monitor/ack-config.ts +27 -0
- package/src/matrix/monitor/allowlist.ts +92 -0
- package/src/matrix/monitor/auto-join.ts +86 -0
- package/src/matrix/monitor/config.ts +569 -0
- package/src/matrix/monitor/context-summary.ts +43 -0
- package/src/matrix/monitor/direct.ts +296 -0
- package/src/matrix/monitor/events.ts +397 -0
- package/src/matrix/monitor/handler.ts +2271 -0
- package/src/matrix/monitor/inbound-dedupe.ts +267 -0
- package/src/matrix/monitor/index.ts +540 -0
- package/src/matrix/monitor/legacy-crypto-restore.ts +139 -0
- package/src/matrix/monitor/location.ts +108 -0
- package/src/matrix/monitor/media.ts +119 -0
- package/src/matrix/monitor/mentions.ts +256 -0
- package/src/matrix/monitor/reaction-events.ts +197 -0
- package/src/matrix/monitor/recent-invite.ts +30 -0
- package/src/matrix/monitor/replies.ts +136 -0
- package/src/matrix/monitor/reply-context.ts +92 -0
- package/src/matrix/monitor/room-history.ts +301 -0
- package/src/matrix/monitor/room-info.ts +126 -0
- package/src/matrix/monitor/rooms.ts +52 -0
- package/src/matrix/monitor/route.ts +179 -0
- package/src/matrix/monitor/runtime-api.ts +28 -0
- package/src/matrix/monitor/startup-verification.ts +237 -0
- package/src/matrix/monitor/startup.ts +218 -0
- package/src/matrix/monitor/status.ts +120 -0
- package/src/matrix/monitor/sync-lifecycle.ts +91 -0
- package/src/matrix/monitor/task-runner.ts +38 -0
- package/src/matrix/monitor/test-events.ts +21 -0
- package/src/matrix/monitor/thread-context.ts +108 -0
- package/src/matrix/monitor/threads.ts +85 -0
- package/src/matrix/monitor/types.ts +30 -0
- package/src/matrix/monitor/verification-events.ts +643 -0
- package/src/matrix/monitor/verification-utils.ts +46 -0
- package/src/matrix/outbound-media-runtime.ts +1 -0
- package/src/matrix/poll-summary.ts +110 -0
- package/src/matrix/poll-types.ts +429 -0
- package/src/matrix/probe.runtime.ts +4 -0
- package/src/matrix/probe.ts +97 -0
- package/src/matrix/profile.ts +184 -0
- package/src/matrix/reaction-common.ts +147 -0
- package/src/matrix/sdk/crypto-bootstrap.ts +438 -0
- package/src/matrix/sdk/crypto-facade.ts +242 -0
- package/src/matrix/sdk/crypto-node.runtime.ts +17 -0
- package/src/matrix/sdk/crypto-runtime.ts +14 -0
- package/src/matrix/sdk/decrypt-bridge.ts +410 -0
- package/src/matrix/sdk/event-helpers.ts +83 -0
- package/src/matrix/sdk/http-client.ts +87 -0
- package/src/matrix/sdk/idb-persistence-lock.ts +51 -0
- package/src/matrix/sdk/idb-persistence.ts +286 -0
- package/src/matrix/sdk/logger.ts +108 -0
- package/src/matrix/sdk/read-response-with-limit.ts +19 -0
- package/src/matrix/sdk/recovery-key-store.ts +453 -0
- package/src/matrix/sdk/timeout-abort-signal.ts +1 -0
- package/src/matrix/sdk/transport-runtime-api.ts +18 -0
- package/src/matrix/sdk/transport.ts +352 -0
- package/src/matrix/sdk/types.ts +245 -0
- package/src/matrix/sdk/verification-manager.ts +795 -0
- package/src/matrix/sdk/verification-status.ts +23 -0
- package/src/matrix/sdk.ts +2152 -0
- package/src/matrix/send/client.ts +93 -0
- package/src/matrix/send/formatting.ts +189 -0
- package/src/matrix/send/media.ts +244 -0
- package/src/matrix/send/targets.ts +104 -0
- package/src/matrix/send/types.ts +131 -0
- package/src/matrix/send.ts +660 -0
- package/src/matrix/session-store-metadata.ts +108 -0
- package/src/matrix/startup-abort.ts +44 -0
- package/src/matrix/subagent-hooks.ts +308 -0
- package/src/matrix/sync-state.ts +27 -0
- package/src/matrix/target-ids.ts +79 -0
- package/src/matrix/thread-bindings-shared.ts +206 -0
- package/src/matrix/thread-bindings.ts +580 -0
- package/src/matrix-migration.runtime.ts +9 -0
- package/src/migration-config.ts +243 -0
- package/src/migration-snapshot-backup.ts +116 -0
- package/src/migration-snapshot.ts +53 -0
- package/src/onboarding.ts +775 -0
- package/src/outbound.ts +248 -0
- package/src/plugin-entry.runtime.js +115 -0
- package/src/plugin-entry.runtime.ts +70 -0
- package/src/profile-update.ts +71 -0
- package/src/record-shared.ts +3 -0
- package/src/resolve-targets.ts +175 -0
- package/src/resolver.runtime.ts +5 -0
- package/src/resolver.ts +21 -0
- package/src/runtime-api.ts +106 -0
- package/src/runtime.ts +13 -0
- package/src/secret-contract.ts +174 -0
- package/src/session-route.ts +126 -0
- package/src/setup-bootstrap.ts +102 -0
- package/src/setup-config.ts +222 -0
- package/src/setup-contract.ts +90 -0
- package/src/setup-core.ts +146 -0
- package/src/setup-dm-policy.ts +15 -0
- package/src/setup-surface.ts +4 -0
- package/src/startup-maintenance.ts +114 -0
- package/src/storage-paths.ts +92 -0
- package/src/thread-binding-api.ts +23 -0
- package/src/tool-actions.runtime.ts +1 -0
- package/src/tool-actions.ts +498 -0
- package/src/types.ts +257 -0
- package/subagent-hooks-api.ts +31 -0
- package/test-api.ts +21 -0
- package/thread-binding-api.ts +4 -0
- package/thread-bindings-runtime.ts +4 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeLowercaseStringOrEmpty,
|
|
3
|
+
normalizeOptionalString,
|
|
4
|
+
} from "autobot/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import type { LocationMessageEventContent } from "../sdk.js";
|
|
6
|
+
import { formatLocationText, toLocationContext, type NormalizedLocation } from "./runtime-api.js";
|
|
7
|
+
import { EventType } from "./types.js";
|
|
8
|
+
|
|
9
|
+
export type MatrixLocationPayload = {
|
|
10
|
+
text: string;
|
|
11
|
+
context: ReturnType<typeof toLocationContext>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type GeoUriParams = {
|
|
15
|
+
latitude: number;
|
|
16
|
+
longitude: number;
|
|
17
|
+
accuracy?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function decodeGeoUriParamValue(value: string): string {
|
|
21
|
+
try {
|
|
22
|
+
return decodeURIComponent(value);
|
|
23
|
+
} catch {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseGeoUri(value: string): GeoUriParams | null {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
if (!trimmed) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (!normalizeLowercaseStringOrEmpty(trimmed).startsWith("geo:")) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const payload = trimmed.slice(4);
|
|
37
|
+
const [coordsPart, ...paramParts] = payload.split(";");
|
|
38
|
+
const coords = coordsPart.split(",");
|
|
39
|
+
if (coords.length < 2) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const latitude = Number.parseFloat(coords[0] ?? "");
|
|
43
|
+
const longitude = Number.parseFloat(coords[1] ?? "");
|
|
44
|
+
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const params = new Map<string, string>();
|
|
49
|
+
for (const part of paramParts) {
|
|
50
|
+
const segment = part.trim();
|
|
51
|
+
if (!segment) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const eqIndex = segment.indexOf("=");
|
|
55
|
+
const rawKey = eqIndex === -1 ? segment : segment.slice(0, eqIndex);
|
|
56
|
+
const rawValue = eqIndex === -1 ? "" : segment.slice(eqIndex + 1);
|
|
57
|
+
const key = normalizeLowercaseStringOrEmpty(rawKey);
|
|
58
|
+
if (!key) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const valuePart = rawValue.trim();
|
|
62
|
+
params.set(key, valuePart ? decodeGeoUriParamValue(valuePart) : "");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const accuracyRaw = params.get("u");
|
|
66
|
+
const accuracy = accuracyRaw ? Number.parseFloat(accuracyRaw) : undefined;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
latitude,
|
|
70
|
+
longitude,
|
|
71
|
+
accuracy: Number.isFinite(accuracy) ? accuracy : undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolveMatrixLocation(params: {
|
|
76
|
+
eventType: string;
|
|
77
|
+
content: LocationMessageEventContent;
|
|
78
|
+
}): MatrixLocationPayload | null {
|
|
79
|
+
const { eventType, content } = params;
|
|
80
|
+
const isLocation =
|
|
81
|
+
eventType === EventType.Location ||
|
|
82
|
+
(eventType === EventType.RoomMessage && content.msgtype === EventType.Location);
|
|
83
|
+
if (!isLocation) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const geoUri = normalizeOptionalString(content.geo_uri) ?? "";
|
|
87
|
+
if (!geoUri) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const parsed = parseGeoUri(geoUri);
|
|
91
|
+
if (!parsed) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const caption = normalizeOptionalString(content.body) ?? "";
|
|
95
|
+
const location: NormalizedLocation = {
|
|
96
|
+
latitude: parsed.latitude,
|
|
97
|
+
longitude: parsed.longitude,
|
|
98
|
+
accuracy: parsed.accuracy,
|
|
99
|
+
caption: caption || undefined,
|
|
100
|
+
source: "pin",
|
|
101
|
+
isLive: false,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
text: formatLocationText(location),
|
|
106
|
+
context: toLocationContext(location),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { getMatrixRuntime } from "../../runtime.js";
|
|
2
|
+
import { MatrixMediaSizeLimitError, isMatrixMediaSizeLimitError } from "../media-errors.js";
|
|
3
|
+
import type { MatrixClient } from "../sdk.js";
|
|
4
|
+
|
|
5
|
+
// Type for encrypted file info
|
|
6
|
+
type EncryptedFile = {
|
|
7
|
+
url: string;
|
|
8
|
+
key: {
|
|
9
|
+
kty: string;
|
|
10
|
+
key_ops: string[];
|
|
11
|
+
alg: string;
|
|
12
|
+
k: string;
|
|
13
|
+
ext: boolean;
|
|
14
|
+
};
|
|
15
|
+
iv: string;
|
|
16
|
+
hashes: Record<string, string>;
|
|
17
|
+
v: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const MATRIX_MEDIA_DOWNLOAD_IDLE_TIMEOUT_MS = 30_000;
|
|
21
|
+
|
|
22
|
+
async function fetchMatrixMediaBuffer(params: {
|
|
23
|
+
client: MatrixClient;
|
|
24
|
+
mxcUrl: string;
|
|
25
|
+
maxBytes: number;
|
|
26
|
+
}): Promise<{ buffer: Buffer } | null> {
|
|
27
|
+
try {
|
|
28
|
+
const buffer = await params.client.downloadContent(params.mxcUrl, {
|
|
29
|
+
maxBytes: params.maxBytes,
|
|
30
|
+
readIdleTimeoutMs: MATRIX_MEDIA_DOWNLOAD_IDLE_TIMEOUT_MS,
|
|
31
|
+
});
|
|
32
|
+
return { buffer };
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (isMatrixMediaSizeLimitError(err)) {
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Matrix media download failed: ${String(err)}`, { cause: err });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Download and decrypt encrypted media from a Matrix room.
|
|
43
|
+
* Uses the Matrix crypto adapter's decryptMedia helper.
|
|
44
|
+
*/
|
|
45
|
+
async function fetchEncryptedMediaBuffer(params: {
|
|
46
|
+
client: MatrixClient;
|
|
47
|
+
file: EncryptedFile;
|
|
48
|
+
maxBytes: number;
|
|
49
|
+
}): Promise<{ buffer: Buffer } | null> {
|
|
50
|
+
if (!params.client.crypto) {
|
|
51
|
+
throw new Error("Cannot decrypt media: crypto not enabled");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const decrypted = await params.client.crypto.decryptMedia(
|
|
55
|
+
params.file as Parameters<typeof params.client.crypto.decryptMedia>[0],
|
|
56
|
+
{
|
|
57
|
+
maxBytes: params.maxBytes,
|
|
58
|
+
readIdleTimeoutMs: MATRIX_MEDIA_DOWNLOAD_IDLE_TIMEOUT_MS,
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (decrypted.byteLength > params.maxBytes) {
|
|
63
|
+
throw new MatrixMediaSizeLimitError();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { buffer: decrypted };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function downloadMatrixMedia(params: {
|
|
70
|
+
client: MatrixClient;
|
|
71
|
+
mxcUrl: string;
|
|
72
|
+
contentType?: string;
|
|
73
|
+
sizeBytes?: number;
|
|
74
|
+
maxBytes: number;
|
|
75
|
+
file?: EncryptedFile;
|
|
76
|
+
originalFilename?: string;
|
|
77
|
+
}): Promise<{
|
|
78
|
+
path: string;
|
|
79
|
+
contentType?: string;
|
|
80
|
+
placeholder: string;
|
|
81
|
+
} | null> {
|
|
82
|
+
let fetched: { buffer: Buffer; headerType?: string } | null;
|
|
83
|
+
if (typeof params.sizeBytes === "number" && params.sizeBytes > params.maxBytes) {
|
|
84
|
+
throw new MatrixMediaSizeLimitError();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (params.file) {
|
|
88
|
+
// Encrypted media
|
|
89
|
+
fetched = await fetchEncryptedMediaBuffer({
|
|
90
|
+
client: params.client,
|
|
91
|
+
file: params.file,
|
|
92
|
+
maxBytes: params.maxBytes,
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
// Unencrypted media
|
|
96
|
+
fetched = await fetchMatrixMediaBuffer({
|
|
97
|
+
client: params.client,
|
|
98
|
+
mxcUrl: params.mxcUrl,
|
|
99
|
+
maxBytes: params.maxBytes,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!fetched) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const headerType = params.contentType ?? undefined;
|
|
107
|
+
const saved = await getMatrixRuntime().channel.media.saveMediaBuffer(
|
|
108
|
+
fetched.buffer,
|
|
109
|
+
headerType,
|
|
110
|
+
"inbound",
|
|
111
|
+
params.maxBytes,
|
|
112
|
+
params.originalFilename,
|
|
113
|
+
);
|
|
114
|
+
return {
|
|
115
|
+
path: saved.path,
|
|
116
|
+
contentType: saved.contentType,
|
|
117
|
+
placeholder: "[matrix media]",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { normalizeLowercaseStringOrEmpty } from "autobot/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { escapeRegExp } from "autobot/plugin-sdk/text-utility-runtime";
|
|
3
|
+
import { getMatrixRuntime } from "../../runtime.js";
|
|
4
|
+
import type { RoomMessageEventContent } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const HTML_ENTITY_REPLACEMENTS: Readonly<Record<string, string>> = {
|
|
7
|
+
amp: "&",
|
|
8
|
+
apos: "'",
|
|
9
|
+
gt: ">",
|
|
10
|
+
lt: "<",
|
|
11
|
+
nbsp: " ",
|
|
12
|
+
quot: '"',
|
|
13
|
+
};
|
|
14
|
+
const MAX_UNICODE_SCALAR_VALUE = 0x10ffff;
|
|
15
|
+
|
|
16
|
+
function decodeNumericHtmlEntity(match: string, rawValue: string, radix: 10 | 16): string {
|
|
17
|
+
const codePoint = Number.parseInt(rawValue, radix);
|
|
18
|
+
if (
|
|
19
|
+
!Number.isSafeInteger(codePoint) ||
|
|
20
|
+
codePoint < 0 ||
|
|
21
|
+
codePoint > MAX_UNICODE_SCALAR_VALUE ||
|
|
22
|
+
(codePoint >= 0xd800 && codePoint <= 0xdfff)
|
|
23
|
+
) {
|
|
24
|
+
return match;
|
|
25
|
+
}
|
|
26
|
+
return String.fromCodePoint(codePoint);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function decodeHtmlEntities(value: string): string {
|
|
30
|
+
return value.replace(/&(#x?[0-9a-f]+|\w+);/gi, (match, entity: string) => {
|
|
31
|
+
const normalized = normalizeLowercaseStringOrEmpty(entity);
|
|
32
|
+
if (normalized.startsWith("#x")) {
|
|
33
|
+
return decodeNumericHtmlEntity(match, normalized.slice(2), 16);
|
|
34
|
+
}
|
|
35
|
+
if (normalized.startsWith("#")) {
|
|
36
|
+
return decodeNumericHtmlEntity(match, normalized.slice(1), 10);
|
|
37
|
+
}
|
|
38
|
+
return HTML_ENTITY_REPLACEMENTS[normalized] ?? match;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeVisibleMentionText(value: string): string {
|
|
43
|
+
return normalizeLowercaseStringOrEmpty(
|
|
44
|
+
decodeHtmlEntities(
|
|
45
|
+
value.replace(/<[^>]+>/g, " ").replace(/[\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, ""),
|
|
46
|
+
).replace(/\s+/g, " "),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractVisibleMentionText(value?: string): string {
|
|
51
|
+
return normalizeVisibleMentionText(value ?? "");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveMatrixUserLocalpart(userId: string): string | null {
|
|
55
|
+
const trimmed = userId.trim();
|
|
56
|
+
if (!trimmed.startsWith("@")) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const colonIndex = trimmed.indexOf(":");
|
|
60
|
+
if (colonIndex <= 1) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return trimmed.slice(1, colonIndex).trim() || null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveMatrixMentionPrefixCandidates(params: {
|
|
67
|
+
userId?: string | null;
|
|
68
|
+
displayName?: string | null;
|
|
69
|
+
}): string[] {
|
|
70
|
+
const candidates: string[] = [];
|
|
71
|
+
const seen = new Set<string>();
|
|
72
|
+
|
|
73
|
+
const append = (candidate?: string | null) => {
|
|
74
|
+
const trimmed = candidate?.trim();
|
|
75
|
+
if (!trimmed) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const normalized = normalizeLowercaseStringOrEmpty(trimmed);
|
|
79
|
+
if (seen.has(normalized)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
seen.add(normalized);
|
|
83
|
+
candidates.push(trimmed);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
append(params.userId);
|
|
87
|
+
const localpart = params.userId ? resolveMatrixUserLocalpart(params.userId) : null;
|
|
88
|
+
append(localpart ? `@${localpart}` : null);
|
|
89
|
+
append(params.displayName);
|
|
90
|
+
append(params.displayName ? `@${params.displayName}` : null);
|
|
91
|
+
|
|
92
|
+
return candidates;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stripMatchedMatrixMentionPrefix(text: string, pattern: RegExp): string | null {
|
|
96
|
+
const match = text.match(pattern);
|
|
97
|
+
if (!match) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
return text.slice(match[0].length).trimStart();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function stripNativeMatrixMentionPrefix(text: string, candidate: string): string | null {
|
|
104
|
+
const pattern = new RegExp(`^\\s*${escapeRegExp(candidate)}(?:\\s*[:,])?(?:\\s+|$)`, "i");
|
|
105
|
+
return stripMatchedMatrixMentionPrefix(text, pattern);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function stripRegexMatrixMentionPrefix(text: string, pattern: RegExp): string | null {
|
|
109
|
+
const flags = pattern.flags.replace(/[gy]/g, "");
|
|
110
|
+
const anchored = new RegExp(`^\\s*(?:${pattern.source})(?:\\s*[:,])?(?:\\s+|$)`, flags);
|
|
111
|
+
return stripMatchedMatrixMentionPrefix(text, anchored);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function stripMatrixMentionPrefix(params: {
|
|
115
|
+
text: string;
|
|
116
|
+
userId?: string | null;
|
|
117
|
+
displayName?: string | null;
|
|
118
|
+
mentionRegexes?: RegExp[];
|
|
119
|
+
}): string {
|
|
120
|
+
const text = params.text;
|
|
121
|
+
if (!text) {
|
|
122
|
+
return text;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const candidate of resolveMatrixMentionPrefixCandidates(params)) {
|
|
126
|
+
const stripped = stripNativeMatrixMentionPrefix(text, candidate);
|
|
127
|
+
if (stripped !== null) {
|
|
128
|
+
return stripped;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const pattern of params.mentionRegexes ?? []) {
|
|
132
|
+
const stripped = stripRegexMatrixMentionPrefix(text, pattern);
|
|
133
|
+
if (stripped !== null) {
|
|
134
|
+
return stripped;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isVisibleMentionLabel(params: {
|
|
141
|
+
text: string;
|
|
142
|
+
userId: string;
|
|
143
|
+
mentionRegexes: RegExp[];
|
|
144
|
+
displayName?: string | null;
|
|
145
|
+
}): boolean {
|
|
146
|
+
const cleaned = extractVisibleMentionText(params.text);
|
|
147
|
+
if (!cleaned) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (params.mentionRegexes.some((pattern) => pattern.test(cleaned))) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
const localpart = resolveMatrixUserLocalpart(params.userId);
|
|
154
|
+
const candidates = [
|
|
155
|
+
extractVisibleMentionText(params.userId),
|
|
156
|
+
localpart ? extractVisibleMentionText(localpart) : null,
|
|
157
|
+
localpart ? extractVisibleMentionText(`@${localpart}`) : null,
|
|
158
|
+
params.displayName ? extractVisibleMentionText(params.displayName) : null,
|
|
159
|
+
params.displayName ? extractVisibleMentionText(`@${params.displayName}`) : null,
|
|
160
|
+
].filter((value): value is string => Boolean(value));
|
|
161
|
+
return candidates.includes(cleaned);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function hasVisibleRoomMention(value?: string): boolean {
|
|
165
|
+
const cleaned = extractVisibleMentionText(value);
|
|
166
|
+
return /(^|[^a-z0-9_])@room\b/i.test(cleaned);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if formatted_body contains a matrix.to link whose visible label still
|
|
171
|
+
* looks like a real mention for the given user. Do not trust href alone, since
|
|
172
|
+
* senders can hide arbitrary matrix.to links behind unrelated link text.
|
|
173
|
+
* Many Matrix clients (including Element) use HTML links in formatted_body instead of
|
|
174
|
+
* or in addition to the m.mentions field.
|
|
175
|
+
*/
|
|
176
|
+
function checkFormattedBodyMention(params: {
|
|
177
|
+
formattedBody?: string;
|
|
178
|
+
userId: string;
|
|
179
|
+
displayName?: string | null;
|
|
180
|
+
mentionRegexes: RegExp[];
|
|
181
|
+
}): boolean {
|
|
182
|
+
if (!params.formattedBody || !params.userId) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const anchorPattern = /<a\b[^>]*href=(["'])(https:\/\/matrix\.to\/#[^"']+)\1[^>]*>(.*?)<\/a>/gis;
|
|
186
|
+
for (const match of params.formattedBody.matchAll(anchorPattern)) {
|
|
187
|
+
const href = match[2];
|
|
188
|
+
const visibleLabel = match[3] ?? "";
|
|
189
|
+
if (!href) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const parsed = new URL(href);
|
|
194
|
+
const fragmentTarget = decodeURIComponent(parsed.hash.replace(/^#\/?/, "").trim());
|
|
195
|
+
if (fragmentTarget !== params.userId.trim()) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (
|
|
199
|
+
isVisibleMentionLabel({
|
|
200
|
+
text: visibleLabel,
|
|
201
|
+
userId: params.userId,
|
|
202
|
+
mentionRegexes: params.mentionRegexes,
|
|
203
|
+
displayName: params.displayName,
|
|
204
|
+
})
|
|
205
|
+
) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function resolveMentions(params: {
|
|
216
|
+
content: RoomMessageEventContent;
|
|
217
|
+
userId?: string | null;
|
|
218
|
+
displayName?: string | null;
|
|
219
|
+
text?: string;
|
|
220
|
+
mentionRegexes: RegExp[];
|
|
221
|
+
}) {
|
|
222
|
+
const mentions = params.content["m.mentions"];
|
|
223
|
+
const mentionedUsers = Array.isArray(mentions?.user_ids)
|
|
224
|
+
? new Set(mentions.user_ids)
|
|
225
|
+
: new Set<string>();
|
|
226
|
+
const textMentioned = getMatrixRuntime().channel.mentions.matchesMentionPatterns(
|
|
227
|
+
params.text ?? "",
|
|
228
|
+
params.mentionRegexes,
|
|
229
|
+
);
|
|
230
|
+
const visibleRoomMention =
|
|
231
|
+
hasVisibleRoomMention(params.text) || hasVisibleRoomMention(params.content.formatted_body);
|
|
232
|
+
|
|
233
|
+
// Check formatted_body for matrix.to mention links (legacy/alternative mention format)
|
|
234
|
+
const mentionedInFormattedBody = params.userId
|
|
235
|
+
? checkFormattedBodyMention({
|
|
236
|
+
formattedBody: params.content.formatted_body,
|
|
237
|
+
userId: params.userId,
|
|
238
|
+
displayName: params.displayName,
|
|
239
|
+
mentionRegexes: params.mentionRegexes,
|
|
240
|
+
})
|
|
241
|
+
: false;
|
|
242
|
+
// Matrix clients can mention users through m.mentions metadata plus a visible
|
|
243
|
+
// Matrix URI label in formatted_body. Keep the visible-mention requirement so
|
|
244
|
+
// hidden metadata-only mentions do not trigger the handler.
|
|
245
|
+
const metadataBackedUserMention = Boolean(
|
|
246
|
+
params.userId &&
|
|
247
|
+
mentionedUsers.has(params.userId) &&
|
|
248
|
+
(mentionedInFormattedBody || textMentioned),
|
|
249
|
+
);
|
|
250
|
+
const metadataBackedRoomMention = Boolean(mentions?.room) && visibleRoomMention;
|
|
251
|
+
const explicitMention =
|
|
252
|
+
mentionedInFormattedBody || metadataBackedUserMention || metadataBackedRoomMention;
|
|
253
|
+
|
|
254
|
+
const wasMentioned = explicitMention || textMentioned || visibleRoomMention;
|
|
255
|
+
return { wasMentioned, hasExplicitMention: explicitMention };
|
|
256
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { getSessionBindingService } from "autobot/plugin-sdk/session-binding-runtime";
|
|
2
|
+
import {
|
|
3
|
+
resolveMatrixApprovalReactionTargetWithPersistence,
|
|
4
|
+
unregisterMatrixApprovalReactionTarget,
|
|
5
|
+
} from "../../approval-reactions.js";
|
|
6
|
+
import type { CoreConfig } from "../../types.js";
|
|
7
|
+
import { resolveMatrixAccountConfig } from "../account-config.js";
|
|
8
|
+
import { extractMatrixReactionAnnotation } from "../reaction-common.js";
|
|
9
|
+
import type { MatrixClient } from "../sdk.js";
|
|
10
|
+
import { resolveMatrixInboundRoute } from "./route.js";
|
|
11
|
+
import type { PluginRuntime } from "./runtime-api.js";
|
|
12
|
+
import { resolveMatrixThreadRootId, resolveMatrixThreadRouting } from "./threads.js";
|
|
13
|
+
import type { MatrixRawEvent, RoomMessageEventContent } from "./types.js";
|
|
14
|
+
|
|
15
|
+
let approvalReactionAuthPromise:
|
|
16
|
+
| Promise<typeof import("../../approval-reaction-auth.js")>
|
|
17
|
+
| undefined;
|
|
18
|
+
let execApprovalResolverPromise:
|
|
19
|
+
| Promise<typeof import("../../exec-approval-resolver.js")>
|
|
20
|
+
| undefined;
|
|
21
|
+
|
|
22
|
+
function loadApprovalReactionAuth(): Promise<typeof import("../../approval-reaction-auth.js")> {
|
|
23
|
+
approvalReactionAuthPromise ??= import("../../approval-reaction-auth.js");
|
|
24
|
+
return approvalReactionAuthPromise;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadExecApprovalResolver(): Promise<typeof import("../../exec-approval-resolver.js")> {
|
|
28
|
+
execApprovalResolverPromise ??= import("../../exec-approval-resolver.js");
|
|
29
|
+
return execApprovalResolverPromise;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type MatrixReactionNotificationMode = "off" | "own";
|
|
33
|
+
|
|
34
|
+
export function resolveMatrixReactionNotificationMode(params: {
|
|
35
|
+
cfg: CoreConfig;
|
|
36
|
+
accountId: string;
|
|
37
|
+
}): MatrixReactionNotificationMode {
|
|
38
|
+
const matrixConfig = params.cfg.channels?.matrix;
|
|
39
|
+
const accountConfig = resolveMatrixAccountConfig({
|
|
40
|
+
cfg: params.cfg,
|
|
41
|
+
accountId: params.accountId,
|
|
42
|
+
});
|
|
43
|
+
return accountConfig.reactionNotifications ?? matrixConfig?.reactionNotifications ?? "own";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function maybeResolveMatrixApprovalReaction(params: {
|
|
47
|
+
cfg: CoreConfig;
|
|
48
|
+
accountId: string;
|
|
49
|
+
senderId: string;
|
|
50
|
+
target: Awaited<ReturnType<typeof resolveMatrixApprovalReactionTargetWithPersistence>>;
|
|
51
|
+
targetEventId: string;
|
|
52
|
+
roomId: string;
|
|
53
|
+
logVerboseMessage: (message: string) => void;
|
|
54
|
+
}): Promise<boolean> {
|
|
55
|
+
if (!params.target) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const approvalKind = params.target.approvalId.startsWith("plugin:") ? "plugin" : "exec";
|
|
59
|
+
const { isMatrixApprovalReactionAuthorizedSender } = await loadApprovalReactionAuth();
|
|
60
|
+
if (!isMatrixApprovalReactionAuthorizedSender({ ...params, approvalKind })) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const { isApprovalNotFoundError, resolveMatrixApproval } = await loadExecApprovalResolver();
|
|
64
|
+
try {
|
|
65
|
+
await resolveMatrixApproval({
|
|
66
|
+
cfg: params.cfg,
|
|
67
|
+
approvalId: params.target.approvalId,
|
|
68
|
+
decision: params.target.decision,
|
|
69
|
+
senderId: params.senderId,
|
|
70
|
+
});
|
|
71
|
+
params.logVerboseMessage(
|
|
72
|
+
`matrix: approval reaction resolved id=${params.target.approvalId} sender=${params.senderId} decision=${params.target.decision}`,
|
|
73
|
+
);
|
|
74
|
+
return true;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (isApprovalNotFoundError(err)) {
|
|
77
|
+
unregisterMatrixApprovalReactionTarget({
|
|
78
|
+
roomId: params.roomId,
|
|
79
|
+
eventId: params.targetEventId,
|
|
80
|
+
});
|
|
81
|
+
params.logVerboseMessage(
|
|
82
|
+
`matrix: approval reaction ignored for expired approval id=${params.target.approvalId} sender=${params.senderId}`,
|
|
83
|
+
);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
params.logVerboseMessage(
|
|
87
|
+
`matrix: approval reaction failed id=${params.target.approvalId} sender=${params.senderId}: ${String(err)}`,
|
|
88
|
+
);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function handleInboundMatrixReaction(params: {
|
|
94
|
+
client: MatrixClient;
|
|
95
|
+
core: PluginRuntime;
|
|
96
|
+
cfg: CoreConfig;
|
|
97
|
+
accountId: string;
|
|
98
|
+
roomId: string;
|
|
99
|
+
event: MatrixRawEvent;
|
|
100
|
+
senderId: string;
|
|
101
|
+
senderLabel: string;
|
|
102
|
+
selfUserId: string;
|
|
103
|
+
isDirectMessage: boolean;
|
|
104
|
+
logVerboseMessage: (message: string) => void;
|
|
105
|
+
}): Promise<void> {
|
|
106
|
+
const reaction = extractMatrixReactionAnnotation(params.event.content);
|
|
107
|
+
if (!reaction?.eventId) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (params.senderId === params.selfUserId) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const approvalTarget = await resolveMatrixApprovalReactionTargetWithPersistence({
|
|
114
|
+
roomId: params.roomId,
|
|
115
|
+
eventId: reaction.eventId,
|
|
116
|
+
reactionKey: reaction.key,
|
|
117
|
+
});
|
|
118
|
+
if (
|
|
119
|
+
await maybeResolveMatrixApprovalReaction({
|
|
120
|
+
cfg: params.cfg,
|
|
121
|
+
accountId: params.accountId,
|
|
122
|
+
senderId: params.senderId,
|
|
123
|
+
target: approvalTarget,
|
|
124
|
+
targetEventId: reaction.eventId,
|
|
125
|
+
roomId: params.roomId,
|
|
126
|
+
logVerboseMessage: params.logVerboseMessage,
|
|
127
|
+
})
|
|
128
|
+
) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const notificationMode = resolveMatrixReactionNotificationMode({
|
|
132
|
+
cfg: params.cfg,
|
|
133
|
+
accountId: params.accountId,
|
|
134
|
+
});
|
|
135
|
+
if (notificationMode === "off") {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const targetEvent = await params.client.getEvent(params.roomId, reaction.eventId).catch((err) => {
|
|
140
|
+
params.logVerboseMessage(
|
|
141
|
+
`matrix: failed resolving reaction target room=${params.roomId} id=${reaction.eventId}: ${String(err)}`,
|
|
142
|
+
);
|
|
143
|
+
return null;
|
|
144
|
+
});
|
|
145
|
+
const targetSender =
|
|
146
|
+
targetEvent && typeof targetEvent.sender === "string" ? targetEvent.sender.trim() : "";
|
|
147
|
+
if (!targetSender) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (notificationMode === "own" && targetSender !== params.selfUserId) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const targetContent =
|
|
155
|
+
targetEvent && targetEvent.content && typeof targetEvent.content === "object"
|
|
156
|
+
? (targetEvent.content as RoomMessageEventContent)
|
|
157
|
+
: undefined;
|
|
158
|
+
const threadRootId = targetContent
|
|
159
|
+
? resolveMatrixThreadRootId({
|
|
160
|
+
event: targetEvent as MatrixRawEvent,
|
|
161
|
+
content: targetContent,
|
|
162
|
+
})
|
|
163
|
+
: undefined;
|
|
164
|
+
const accountConfig = resolveMatrixAccountConfig({
|
|
165
|
+
cfg: params.cfg,
|
|
166
|
+
accountId: params.accountId,
|
|
167
|
+
});
|
|
168
|
+
const thread = resolveMatrixThreadRouting({
|
|
169
|
+
isDirectMessage: params.isDirectMessage,
|
|
170
|
+
threadReplies: accountConfig.threadReplies ?? "inbound",
|
|
171
|
+
dmThreadReplies: accountConfig.dm?.threadReplies,
|
|
172
|
+
messageId: reaction.eventId,
|
|
173
|
+
threadRootId,
|
|
174
|
+
});
|
|
175
|
+
const { route, runtimeBindingId } = resolveMatrixInboundRoute({
|
|
176
|
+
cfg: params.cfg,
|
|
177
|
+
accountId: params.accountId,
|
|
178
|
+
roomId: params.roomId,
|
|
179
|
+
senderId: params.senderId,
|
|
180
|
+
isDirectMessage: params.isDirectMessage,
|
|
181
|
+
dmSessionScope: accountConfig.dm?.sessionScope ?? "per-user",
|
|
182
|
+
threadId: thread.threadId,
|
|
183
|
+
eventTs: params.event.origin_server_ts,
|
|
184
|
+
resolveAgentRoute: params.core.channel.routing.resolveAgentRoute,
|
|
185
|
+
});
|
|
186
|
+
if (runtimeBindingId) {
|
|
187
|
+
getSessionBindingService().touch(runtimeBindingId, params.event.origin_server_ts);
|
|
188
|
+
}
|
|
189
|
+
const text = `Matrix reaction added: ${reaction.key} by ${params.senderLabel} on msg ${reaction.eventId}`;
|
|
190
|
+
params.core.system.enqueueSystemEvent(text, {
|
|
191
|
+
sessionKey: route.sessionKey,
|
|
192
|
+
contextKey: `matrix:reaction:add:${params.roomId}:${reaction.eventId}:${params.senderId}:${reaction.key}`,
|
|
193
|
+
});
|
|
194
|
+
params.logVerboseMessage(
|
|
195
|
+
`matrix: reaction event enqueued room=${params.roomId} target=${reaction.eventId} sender=${params.senderId} emoji=${reaction.key}`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MatrixRoomConfig } from "../../types.js";
|
|
2
|
+
import type { MatrixRoomInfo } from "./room-info.js";
|
|
3
|
+
import { resolveMatrixRoomConfig } from "./rooms.js";
|
|
4
|
+
|
|
5
|
+
export function shouldPromoteRecentInviteRoom(params: {
|
|
6
|
+
roomId: string;
|
|
7
|
+
roomInfo: Pick<
|
|
8
|
+
MatrixRoomInfo,
|
|
9
|
+
"name" | "canonicalAlias" | "altAliases" | "nameResolved" | "aliasesResolved"
|
|
10
|
+
>;
|
|
11
|
+
rooms?: Record<string, MatrixRoomConfig>;
|
|
12
|
+
}): boolean {
|
|
13
|
+
if (!params.roomInfo.nameResolved || !params.roomInfo.aliasesResolved) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const roomAliases = [params.roomInfo.canonicalAlias ?? "", ...params.roomInfo.altAliases].filter(
|
|
18
|
+
Boolean,
|
|
19
|
+
);
|
|
20
|
+
if ((params.roomInfo.name?.trim() ?? "") || roomAliases.length > 0) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const roomConfig = resolveMatrixRoomConfig({
|
|
25
|
+
rooms: params.rooms,
|
|
26
|
+
roomId: params.roomId,
|
|
27
|
+
aliases: roomAliases,
|
|
28
|
+
});
|
|
29
|
+
return roomConfig.matchSource === undefined;
|
|
30
|
+
}
|