@openclaw/matrix 2026.3.13 → 2026.5.9-beta.1
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/dist/account-config-D2W-V1eQ.js +96 -0
- package/dist/account-selection-BWwIruri.js +158 -0
- package/dist/accounts-Bm90Rzvp.js +130 -0
- package/dist/active-client-uhlxdhEy.js +20 -0
- package/dist/allowlist-sTzpCn5d.js +68 -0
- package/dist/api.js +12 -0
- package/dist/approval-handler.runtime-DWTQfd4m.js +370 -0
- package/dist/approval-ids-DoC2z7tR.js +7 -0
- package/dist/approval-reaction-auth-DbcA1gGd.js +27 -0
- package/dist/approval-reactions-o2_tuH8D.js +162 -0
- package/dist/async-lock-uQfhfQIY.js +19 -0
- package/dist/auth-presence.js +26 -0
- package/dist/backup-health-Cabu_WQC.js +60 -0
- package/dist/channel-DJNir3Rb.js +1116 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BQu0hTih.js +246 -0
- package/dist/cli-BmfTmg7x.js +1340 -0
- package/dist/cli-metadata-B-PCEzrA.js +22 -0
- package/dist/cli-metadata.js +2 -0
- package/dist/client-DkcXnm0X.js +25 -0
- package/dist/client-_hckQNGW.js +31 -0
- package/dist/client-bootstrap-Rb8oHvhH.js +114 -0
- package/dist/config--5-S2Akv.js +452 -0
- package/dist/config-paths-nsVaysCu.js +19 -0
- package/dist/config-schema-nPLpEgHl.js +200 -0
- package/dist/config-secret-input.runtime-DiKFehsE.js +2 -0
- package/dist/config-update-wZX-HLMn.js +143 -0
- package/dist/contract-api.js +9 -0
- package/dist/create-client-DCnqDaqd.js +64 -0
- package/dist/credentials-DV6fWXhC.js +56 -0
- package/dist/credentials-read-cmHgousK.js +112 -0
- package/dist/credentials-write.runtime-zniTq-Gr.js +17 -0
- package/dist/crypto-node.runtime-pihzdpY7.js +12 -0
- package/dist/crypto-runtime-ZI0zAtn3.js +1214 -0
- package/dist/deps-C6WqKY7m.js +235 -0
- package/dist/device-health-UVYpbA_W.js +16 -0
- package/dist/direct-management-DMMMgtTB.js +249 -0
- package/dist/direct-room-XkutHjES.js +76 -0
- package/dist/directory-live-DmOtMhyr.js +150 -0
- package/dist/doctor-C4__7c-U.js +153 -0
- package/dist/doctor-contract-D4-64QuJ.js +246 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/draft-stream-BE2QevQQ.js +144 -0
- package/dist/encryption-guidance-BPi3A_m3.js +15 -0
- package/dist/env-auth-BJqGI8M6.js +63 -0
- package/dist/env-vars-C7uQCTKn.js +63 -0
- package/dist/errors-CTcpEDq-.js +17 -0
- package/dist/exec-approval-resolver-Bza9Dhlm.js +15 -0
- package/dist/exec-approvals-Crnh543m.js +196 -0
- package/dist/helper-api.js +4 -0
- package/dist/http-client-C7AeVJay.js +319 -0
- package/dist/index.js +46 -0
- package/dist/legacy-crypto-inspector-poDWldgy.js +41 -0
- package/dist/legacy-crypto-restore-Biw-w2ng.js +85 -0
- package/dist/logger-CnZRVrux.js +78 -0
- package/dist/logging-DZHSPP5N.js +99 -0
- package/dist/matrix-migration.runtime-WY6ffcrf.js +525 -0
- package/dist/media-text-DU6nWZuj.js +146 -0
- package/dist/messages-BpihMh82.js +140 -0
- package/dist/migration-snapshot-backup-DaCHTp8C.js +69 -0
- package/dist/migration-snapshot.runtime-CKHE3xF9.js +2 -0
- package/dist/monitor-C_81r_Ck.js +4125 -0
- package/dist/plugin-entry.handlers.runtime.js +51 -0
- package/dist/probe.runtime-BvAzYAIe.js +3 -0
- package/dist/profile-BlHu0wDX.js +111 -0
- package/dist/profile-update-DjeBNgIV.js +69 -0
- package/dist/reaction-common-ejrL19w-.js +71 -0
- package/dist/reaction-events-CiARZfjk.js +121 -0
- package/dist/record-shared-CHWJCTWf.js +2 -0
- package/dist/recovery-key-store-BTJ6jz5v.js +294 -0
- package/dist/resolve-targets-YtJnw1Tb.js +140 -0
- package/dist/resolver.runtime-D9piiGEl.js +5 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/route-D6rg-iXN.js +161 -0
- package/dist/runtime-C6X4h_SJ.js +6 -0
- package/dist/runtime-Dog86njy.js +8 -0
- package/dist/runtime-api-BXWBFIqm.js +25 -0
- package/dist/runtime-api.js +25 -0
- package/dist/runtime-heavy-api.js +3 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/sdk-B2vZA27-.js +1416 -0
- package/dist/secret-contract-DcrJWCQI.js +120 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/send-Bo0DU1ca.js +1200 -0
- package/dist/session-store-metadata-DI5SCofx.js +77 -0
- package/dist/setup-bootstrap-ImenBsMt.js +62 -0
- package/dist/setup-core-CfZy05oW.js +116 -0
- package/dist/setup-dm-policy-2-r1FrQh.js +194 -0
- package/dist/setup-entry.js +19 -0
- package/dist/setup-plugin-api.js +44 -0
- package/dist/setup-surface-CqT_o61M.js +540 -0
- package/dist/shared-CpMoYKm1.js +195 -0
- package/dist/startup-abort-56edvmbM.js +32 -0
- package/dist/startup-verification-Demyp0bP.js +132 -0
- package/dist/storage-paths-BJLdnCjV.js +52 -0
- package/dist/storage-tC3ujLiW.js +281 -0
- package/dist/subagent-hooks-DQbyqq9V.js +149 -0
- package/dist/subagent-hooks-api.js +23 -0
- package/dist/sync-state-C_beeevA.js +12 -0
- package/dist/target-ids-80nQ2gql.js +77 -0
- package/dist/test-api.js +4 -0
- package/dist/thread-binding-api-Cq_E-E1K.js +17 -0
- package/dist/thread-binding-api.js +2 -0
- package/dist/thread-bindings-B9mesxXk.js +352 -0
- package/dist/thread-bindings-runtime.js +2 -0
- package/dist/thread-bindings-shared-DK-d-oYX.js +97 -0
- package/dist/timeout-abort-signal-CtaIaP1v.js +2 -0
- package/dist/tool-actions.runtime-BIH49vRr.js +532 -0
- package/dist/url-validation-DiK9j7jz.js +36 -0
- package/dist/verification-CZ2rDeHL.js +345 -0
- package/openclaw.plugin.json +788 -1
- package/package.json +82 -16
- package/CHANGELOG.md +0 -104
- package/index.ts +0 -22
- package/src/actions.ts +0 -195
- package/src/channel.directory.test.ts +0 -135
- package/src/channel.ts +0 -461
- package/src/config-schema.test.ts +0 -26
- package/src/config-schema.ts +0 -62
- package/src/directory-live.test.ts +0 -85
- package/src/directory-live.ts +0 -209
- package/src/group-mentions.ts +0 -52
- package/src/matrix/accounts.test.ts +0 -131
- package/src/matrix/accounts.ts +0 -114
- package/src/matrix/actions/client.ts +0 -47
- package/src/matrix/actions/limits.test.ts +0 -15
- package/src/matrix/actions/limits.ts +0 -6
- package/src/matrix/actions/messages.ts +0 -126
- package/src/matrix/actions/pins.test.ts +0 -74
- package/src/matrix/actions/pins.ts +0 -84
- package/src/matrix/actions/reactions.test.ts +0 -109
- package/src/matrix/actions/reactions.ts +0 -102
- package/src/matrix/actions/room.ts +0 -85
- package/src/matrix/actions/summary.ts +0 -75
- package/src/matrix/actions/types.ts +0 -85
- package/src/matrix/actions.ts +0 -15
- package/src/matrix/active-client.ts +0 -32
- package/src/matrix/client/config.ts +0 -245
- package/src/matrix/client/create-client.ts +0 -125
- package/src/matrix/client/logging.ts +0 -46
- package/src/matrix/client/runtime.ts +0 -4
- package/src/matrix/client/shared.test.ts +0 -85
- package/src/matrix/client/shared.ts +0 -210
- package/src/matrix/client/startup.test.ts +0 -49
- package/src/matrix/client/startup.ts +0 -29
- package/src/matrix/client/storage.ts +0 -131
- package/src/matrix/client/types.ts +0 -34
- package/src/matrix/client-bootstrap.ts +0 -47
- package/src/matrix/client.test.ts +0 -56
- package/src/matrix/client.ts +0 -14
- package/src/matrix/credentials.ts +0 -125
- package/src/matrix/deps.test.ts +0 -74
- package/src/matrix/deps.ts +0 -126
- package/src/matrix/format.test.ts +0 -33
- package/src/matrix/format.ts +0 -22
- package/src/matrix/index.ts +0 -11
- package/src/matrix/monitor/access-policy.ts +0 -126
- package/src/matrix/monitor/allowlist.test.ts +0 -45
- package/src/matrix/monitor/allowlist.ts +0 -94
- package/src/matrix/monitor/auto-join.ts +0 -72
- package/src/matrix/monitor/direct.test.ts +0 -396
- package/src/matrix/monitor/direct.ts +0 -152
- package/src/matrix/monitor/events.test.ts +0 -186
- package/src/matrix/monitor/events.ts +0 -168
- package/src/matrix/monitor/handler.body-for-agent.test.ts +0 -196
- package/src/matrix/monitor/handler.ts +0 -768
- package/src/matrix/monitor/inbound-body.test.ts +0 -73
- package/src/matrix/monitor/inbound-body.ts +0 -28
- package/src/matrix/monitor/index.test.ts +0 -18
- package/src/matrix/monitor/index.ts +0 -414
- package/src/matrix/monitor/location.ts +0 -100
- package/src/matrix/monitor/media.test.ts +0 -86
- package/src/matrix/monitor/media.ts +0 -118
- package/src/matrix/monitor/mentions.test.ts +0 -154
- package/src/matrix/monitor/mentions.ts +0 -62
- package/src/matrix/monitor/replies.test.ts +0 -184
- package/src/matrix/monitor/replies.ts +0 -124
- package/src/matrix/monitor/room-info.ts +0 -55
- package/src/matrix/monitor/rooms.test.ts +0 -124
- package/src/matrix/monitor/rooms.ts +0 -47
- package/src/matrix/monitor/threads.ts +0 -68
- package/src/matrix/monitor/types.ts +0 -39
- package/src/matrix/poll-types.test.ts +0 -21
- package/src/matrix/poll-types.ts +0 -167
- package/src/matrix/probe.ts +0 -69
- package/src/matrix/sdk-runtime.ts +0 -18
- package/src/matrix/send/client.ts +0 -99
- package/src/matrix/send/formatting.ts +0 -93
- package/src/matrix/send/media.ts +0 -230
- package/src/matrix/send/targets.test.ts +0 -98
- package/src/matrix/send/targets.ts +0 -150
- package/src/matrix/send/types.ts +0 -110
- package/src/matrix/send-queue.test.ts +0 -145
- package/src/matrix/send-queue.ts +0 -28
- package/src/matrix/send.test.ts +0 -319
- package/src/matrix/send.ts +0 -267
- package/src/onboarding.ts +0 -462
- package/src/outbound.test.ts +0 -159
- package/src/outbound.ts +0 -58
- package/src/resolve-targets.test.ts +0 -68
- package/src/resolve-targets.ts +0 -125
- package/src/runtime.ts +0 -6
- package/src/secret-input.ts +0 -13
- package/src/test-mocks.ts +0 -53
- package/src/tool-actions.ts +0 -164
- package/src/types.ts +0 -118
package/src/matrix/probe.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { BaseProbeResult } from "openclaw/plugin-sdk/matrix";
|
|
2
|
-
import { createMatrixClient, isBunRuntime } from "./client.js";
|
|
3
|
-
|
|
4
|
-
export type MatrixProbe = BaseProbeResult & {
|
|
5
|
-
status?: number | null;
|
|
6
|
-
elapsedMs: number;
|
|
7
|
-
userId?: string | null;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export async function probeMatrix(params: {
|
|
11
|
-
homeserver: string;
|
|
12
|
-
accessToken: string;
|
|
13
|
-
userId?: string;
|
|
14
|
-
timeoutMs: number;
|
|
15
|
-
}): Promise<MatrixProbe> {
|
|
16
|
-
const started = Date.now();
|
|
17
|
-
const result: MatrixProbe = {
|
|
18
|
-
ok: false,
|
|
19
|
-
status: null,
|
|
20
|
-
error: null,
|
|
21
|
-
elapsedMs: 0,
|
|
22
|
-
};
|
|
23
|
-
if (isBunRuntime()) {
|
|
24
|
-
return {
|
|
25
|
-
...result,
|
|
26
|
-
error: "Matrix probe requires Node (bun runtime not supported)",
|
|
27
|
-
elapsedMs: Date.now() - started,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
if (!params.homeserver?.trim()) {
|
|
31
|
-
return {
|
|
32
|
-
...result,
|
|
33
|
-
error: "missing homeserver",
|
|
34
|
-
elapsedMs: Date.now() - started,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
if (!params.accessToken?.trim()) {
|
|
38
|
-
return {
|
|
39
|
-
...result,
|
|
40
|
-
error: "missing access token",
|
|
41
|
-
elapsedMs: Date.now() - started,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const client = await createMatrixClient({
|
|
46
|
-
homeserver: params.homeserver,
|
|
47
|
-
userId: params.userId ?? "",
|
|
48
|
-
accessToken: params.accessToken,
|
|
49
|
-
localTimeoutMs: params.timeoutMs,
|
|
50
|
-
});
|
|
51
|
-
// @vector-im/matrix-bot-sdk uses getUserId() which calls whoami internally
|
|
52
|
-
const userId = await client.getUserId();
|
|
53
|
-
result.ok = true;
|
|
54
|
-
result.userId = userId ?? null;
|
|
55
|
-
|
|
56
|
-
result.elapsedMs = Date.now() - started;
|
|
57
|
-
return result;
|
|
58
|
-
} catch (err) {
|
|
59
|
-
return {
|
|
60
|
-
...result,
|
|
61
|
-
status:
|
|
62
|
-
typeof err === "object" && err && "statusCode" in err
|
|
63
|
-
? Number((err as { statusCode?: number }).statusCode)
|
|
64
|
-
: result.status,
|
|
65
|
-
error: err instanceof Error ? err.message : String(err),
|
|
66
|
-
elapsedMs: Date.now() - started,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
type MatrixSdkRuntime = typeof import("@vector-im/matrix-bot-sdk");
|
|
4
|
-
|
|
5
|
-
let cachedMatrixSdkRuntime: MatrixSdkRuntime | null = null;
|
|
6
|
-
|
|
7
|
-
export function loadMatrixSdk(): MatrixSdkRuntime {
|
|
8
|
-
if (cachedMatrixSdkRuntime) {
|
|
9
|
-
return cachedMatrixSdkRuntime;
|
|
10
|
-
}
|
|
11
|
-
const req = createRequire(import.meta.url);
|
|
12
|
-
cachedMatrixSdkRuntime = req("@vector-im/matrix-bot-sdk") as MatrixSdkRuntime;
|
|
13
|
-
return cachedMatrixSdkRuntime;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getMatrixLogService() {
|
|
17
|
-
return loadMatrixSdk().LogService;
|
|
18
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
3
|
-
import { getMatrixRuntime } from "../../runtime.js";
|
|
4
|
-
import type { CoreConfig } from "../../types.js";
|
|
5
|
-
import { getActiveMatrixClient, getAnyActiveMatrixClient } from "../active-client.js";
|
|
6
|
-
import { createPreparedMatrixClient } from "../client-bootstrap.js";
|
|
7
|
-
import { isBunRuntime, resolveMatrixAuth, resolveSharedMatrixClient } from "../client.js";
|
|
8
|
-
|
|
9
|
-
const getCore = () => getMatrixRuntime();
|
|
10
|
-
|
|
11
|
-
export function ensureNodeRuntime() {
|
|
12
|
-
if (isBunRuntime()) {
|
|
13
|
-
throw new Error("Matrix support requires Node (bun runtime not supported)");
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Look up account config with case-insensitive key fallback. */
|
|
18
|
-
function findAccountConfig(
|
|
19
|
-
accounts: Record<string, unknown> | undefined,
|
|
20
|
-
accountId: string,
|
|
21
|
-
): Record<string, unknown> | undefined {
|
|
22
|
-
if (!accounts) return undefined;
|
|
23
|
-
const normalized = normalizeAccountId(accountId);
|
|
24
|
-
// Direct lookup first
|
|
25
|
-
if (accounts[normalized]) return accounts[normalized] as Record<string, unknown>;
|
|
26
|
-
// Case-insensitive fallback
|
|
27
|
-
for (const key of Object.keys(accounts)) {
|
|
28
|
-
if (normalizeAccountId(key) === normalized) {
|
|
29
|
-
return accounts[key] as Record<string, unknown>;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function resolveMediaMaxBytes(accountId?: string, cfg?: CoreConfig): number | undefined {
|
|
36
|
-
const resolvedCfg = cfg ?? (getCore().config.loadConfig() as CoreConfig);
|
|
37
|
-
// Check account-specific config first (case-insensitive key matching)
|
|
38
|
-
const accountConfig = findAccountConfig(
|
|
39
|
-
resolvedCfg.channels?.matrix?.accounts as Record<string, unknown> | undefined,
|
|
40
|
-
accountId ?? "",
|
|
41
|
-
);
|
|
42
|
-
if (typeof accountConfig?.mediaMaxMb === "number") {
|
|
43
|
-
return (accountConfig.mediaMaxMb as number) * 1024 * 1024;
|
|
44
|
-
}
|
|
45
|
-
// Fall back to top-level config
|
|
46
|
-
if (typeof resolvedCfg.channels?.matrix?.mediaMaxMb === "number") {
|
|
47
|
-
return resolvedCfg.channels.matrix.mediaMaxMb * 1024 * 1024;
|
|
48
|
-
}
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function resolveMatrixClient(opts: {
|
|
53
|
-
client?: MatrixClient;
|
|
54
|
-
timeoutMs?: number;
|
|
55
|
-
accountId?: string;
|
|
56
|
-
cfg?: CoreConfig;
|
|
57
|
-
}): Promise<{ client: MatrixClient; stopOnDone: boolean }> {
|
|
58
|
-
ensureNodeRuntime();
|
|
59
|
-
if (opts.client) {
|
|
60
|
-
return { client: opts.client, stopOnDone: false };
|
|
61
|
-
}
|
|
62
|
-
const accountId =
|
|
63
|
-
typeof opts.accountId === "string" && opts.accountId.trim().length > 0
|
|
64
|
-
? normalizeAccountId(opts.accountId)
|
|
65
|
-
: undefined;
|
|
66
|
-
// Try to get the client for the specific account
|
|
67
|
-
const active = getActiveMatrixClient(accountId);
|
|
68
|
-
if (active) {
|
|
69
|
-
return { client: active, stopOnDone: false };
|
|
70
|
-
}
|
|
71
|
-
// When no account is specified, try the default account first; only fall back to
|
|
72
|
-
// any active client as a last resort (prevents sending from an arbitrary account).
|
|
73
|
-
if (!accountId) {
|
|
74
|
-
const defaultClient = getActiveMatrixClient(DEFAULT_ACCOUNT_ID);
|
|
75
|
-
if (defaultClient) {
|
|
76
|
-
return { client: defaultClient, stopOnDone: false };
|
|
77
|
-
}
|
|
78
|
-
const anyActive = getAnyActiveMatrixClient();
|
|
79
|
-
if (anyActive) {
|
|
80
|
-
return { client: anyActive, stopOnDone: false };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const shouldShareClient = Boolean(process.env.OPENCLAW_GATEWAY_PORT);
|
|
84
|
-
if (shouldShareClient) {
|
|
85
|
-
const client = await resolveSharedMatrixClient({
|
|
86
|
-
timeoutMs: opts.timeoutMs,
|
|
87
|
-
accountId,
|
|
88
|
-
cfg: opts.cfg,
|
|
89
|
-
});
|
|
90
|
-
return { client, stopOnDone: false };
|
|
91
|
-
}
|
|
92
|
-
const auth = await resolveMatrixAuth({ accountId, cfg: opts.cfg });
|
|
93
|
-
const client = await createPreparedMatrixClient({
|
|
94
|
-
auth,
|
|
95
|
-
timeoutMs: opts.timeoutMs,
|
|
96
|
-
accountId,
|
|
97
|
-
});
|
|
98
|
-
return { client, stopOnDone: true };
|
|
99
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { getMatrixRuntime } from "../../runtime.js";
|
|
2
|
-
import { markdownToMatrixHtml } from "../format.js";
|
|
3
|
-
import {
|
|
4
|
-
MsgType,
|
|
5
|
-
RelationType,
|
|
6
|
-
type MatrixFormattedContent,
|
|
7
|
-
type MatrixMediaMsgType,
|
|
8
|
-
type MatrixRelation,
|
|
9
|
-
type MatrixReplyRelation,
|
|
10
|
-
type MatrixTextContent,
|
|
11
|
-
type MatrixThreadRelation,
|
|
12
|
-
} from "./types.js";
|
|
13
|
-
|
|
14
|
-
const getCore = () => getMatrixRuntime();
|
|
15
|
-
|
|
16
|
-
export function buildTextContent(body: string, relation?: MatrixRelation): MatrixTextContent {
|
|
17
|
-
const content: MatrixTextContent = relation
|
|
18
|
-
? {
|
|
19
|
-
msgtype: MsgType.Text,
|
|
20
|
-
body,
|
|
21
|
-
"m.relates_to": relation,
|
|
22
|
-
}
|
|
23
|
-
: {
|
|
24
|
-
msgtype: MsgType.Text,
|
|
25
|
-
body,
|
|
26
|
-
};
|
|
27
|
-
applyMatrixFormatting(content, body);
|
|
28
|
-
return content;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function applyMatrixFormatting(content: MatrixFormattedContent, body: string): void {
|
|
32
|
-
const formatted = markdownToMatrixHtml(body ?? "");
|
|
33
|
-
if (!formatted) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
content.format = "org.matrix.custom.html";
|
|
37
|
-
content.formatted_body = formatted;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function buildReplyRelation(replyToId?: string): MatrixReplyRelation | undefined {
|
|
41
|
-
const trimmed = replyToId?.trim();
|
|
42
|
-
if (!trimmed) {
|
|
43
|
-
return undefined;
|
|
44
|
-
}
|
|
45
|
-
return { "m.in_reply_to": { event_id: trimmed } };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function buildThreadRelation(threadId: string, replyToId?: string): MatrixThreadRelation {
|
|
49
|
-
const trimmed = threadId.trim();
|
|
50
|
-
return {
|
|
51
|
-
rel_type: RelationType.Thread,
|
|
52
|
-
event_id: trimmed,
|
|
53
|
-
is_falling_back: true,
|
|
54
|
-
"m.in_reply_to": { event_id: replyToId?.trim() || trimmed },
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function resolveMatrixMsgType(contentType?: string, _fileName?: string): MatrixMediaMsgType {
|
|
59
|
-
const kind = getCore().media.mediaKindFromMime(contentType ?? "");
|
|
60
|
-
switch (kind) {
|
|
61
|
-
case "image":
|
|
62
|
-
return MsgType.Image;
|
|
63
|
-
case "audio":
|
|
64
|
-
return MsgType.Audio;
|
|
65
|
-
case "video":
|
|
66
|
-
return MsgType.Video;
|
|
67
|
-
default:
|
|
68
|
-
return MsgType.File;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function resolveMatrixVoiceDecision(opts: {
|
|
73
|
-
wantsVoice: boolean;
|
|
74
|
-
contentType?: string;
|
|
75
|
-
fileName?: string;
|
|
76
|
-
}): { useVoice: boolean } {
|
|
77
|
-
if (!opts.wantsVoice) {
|
|
78
|
-
return { useVoice: false };
|
|
79
|
-
}
|
|
80
|
-
if (isMatrixVoiceCompatibleAudio(opts)) {
|
|
81
|
-
return { useVoice: true };
|
|
82
|
-
}
|
|
83
|
-
return { useVoice: false };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function isMatrixVoiceCompatibleAudio(opts: { contentType?: string; fileName?: string }): boolean {
|
|
87
|
-
// Matrix currently shares the core voice compatibility policy.
|
|
88
|
-
// Keep this wrapper as the seam if Matrix policy diverges later.
|
|
89
|
-
return getCore().media.isVoiceCompatibleAudio({
|
|
90
|
-
contentType: opts.contentType,
|
|
91
|
-
fileName: opts.fileName,
|
|
92
|
-
});
|
|
93
|
-
}
|
package/src/matrix/send/media.ts
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
DimensionalFileInfo,
|
|
3
|
-
EncryptedFile,
|
|
4
|
-
FileWithThumbnailInfo,
|
|
5
|
-
MatrixClient,
|
|
6
|
-
TimedFileInfo,
|
|
7
|
-
VideoFileInfo,
|
|
8
|
-
} from "@vector-im/matrix-bot-sdk";
|
|
9
|
-
import { getMatrixRuntime } from "../../runtime.js";
|
|
10
|
-
import { applyMatrixFormatting } from "./formatting.js";
|
|
11
|
-
import {
|
|
12
|
-
type MatrixMediaContent,
|
|
13
|
-
type MatrixMediaInfo,
|
|
14
|
-
type MatrixMediaMsgType,
|
|
15
|
-
type MatrixRelation,
|
|
16
|
-
type MediaKind,
|
|
17
|
-
} from "./types.js";
|
|
18
|
-
|
|
19
|
-
const getCore = () => getMatrixRuntime();
|
|
20
|
-
type IFileInfo = import("music-metadata").IFileInfo;
|
|
21
|
-
|
|
22
|
-
export function buildMatrixMediaInfo(params: {
|
|
23
|
-
size: number;
|
|
24
|
-
mimetype?: string;
|
|
25
|
-
durationMs?: number;
|
|
26
|
-
imageInfo?: DimensionalFileInfo;
|
|
27
|
-
}): MatrixMediaInfo | undefined {
|
|
28
|
-
const base: FileWithThumbnailInfo = {};
|
|
29
|
-
if (Number.isFinite(params.size)) {
|
|
30
|
-
base.size = params.size;
|
|
31
|
-
}
|
|
32
|
-
if (params.mimetype) {
|
|
33
|
-
base.mimetype = params.mimetype;
|
|
34
|
-
}
|
|
35
|
-
if (params.imageInfo) {
|
|
36
|
-
const dimensional: DimensionalFileInfo = {
|
|
37
|
-
...base,
|
|
38
|
-
...params.imageInfo,
|
|
39
|
-
};
|
|
40
|
-
if (typeof params.durationMs === "number") {
|
|
41
|
-
const videoInfo: VideoFileInfo = {
|
|
42
|
-
...dimensional,
|
|
43
|
-
duration: params.durationMs,
|
|
44
|
-
};
|
|
45
|
-
return videoInfo;
|
|
46
|
-
}
|
|
47
|
-
return dimensional;
|
|
48
|
-
}
|
|
49
|
-
if (typeof params.durationMs === "number") {
|
|
50
|
-
const timedInfo: TimedFileInfo = {
|
|
51
|
-
...base,
|
|
52
|
-
duration: params.durationMs,
|
|
53
|
-
};
|
|
54
|
-
return timedInfo;
|
|
55
|
-
}
|
|
56
|
-
if (Object.keys(base).length === 0) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
return base;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function buildMediaContent(params: {
|
|
63
|
-
msgtype: MatrixMediaMsgType;
|
|
64
|
-
body: string;
|
|
65
|
-
url?: string;
|
|
66
|
-
filename?: string;
|
|
67
|
-
mimetype?: string;
|
|
68
|
-
size: number;
|
|
69
|
-
relation?: MatrixRelation;
|
|
70
|
-
isVoice?: boolean;
|
|
71
|
-
durationMs?: number;
|
|
72
|
-
imageInfo?: DimensionalFileInfo;
|
|
73
|
-
file?: EncryptedFile;
|
|
74
|
-
}): MatrixMediaContent {
|
|
75
|
-
const info = buildMatrixMediaInfo({
|
|
76
|
-
size: params.size,
|
|
77
|
-
mimetype: params.mimetype,
|
|
78
|
-
durationMs: params.durationMs,
|
|
79
|
-
imageInfo: params.imageInfo,
|
|
80
|
-
});
|
|
81
|
-
const base: MatrixMediaContent = {
|
|
82
|
-
msgtype: params.msgtype,
|
|
83
|
-
body: params.body,
|
|
84
|
-
filename: params.filename,
|
|
85
|
-
info: info ?? undefined,
|
|
86
|
-
};
|
|
87
|
-
// Encrypted media should only include the "file" payload, not top-level "url".
|
|
88
|
-
if (!params.file && params.url) {
|
|
89
|
-
base.url = params.url;
|
|
90
|
-
}
|
|
91
|
-
// For encrypted files, add the file object
|
|
92
|
-
if (params.file) {
|
|
93
|
-
base.file = params.file;
|
|
94
|
-
}
|
|
95
|
-
if (params.isVoice) {
|
|
96
|
-
base["org.matrix.msc3245.voice"] = {};
|
|
97
|
-
if (typeof params.durationMs === "number") {
|
|
98
|
-
base["org.matrix.msc1767.audio"] = {
|
|
99
|
-
duration: params.durationMs,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (params.relation) {
|
|
104
|
-
base["m.relates_to"] = params.relation;
|
|
105
|
-
}
|
|
106
|
-
applyMatrixFormatting(base, params.body);
|
|
107
|
-
return base;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const THUMBNAIL_MAX_SIDE = 800;
|
|
111
|
-
const THUMBNAIL_QUALITY = 80;
|
|
112
|
-
|
|
113
|
-
export async function prepareImageInfo(params: {
|
|
114
|
-
buffer: Buffer;
|
|
115
|
-
client: MatrixClient;
|
|
116
|
-
}): Promise<DimensionalFileInfo | undefined> {
|
|
117
|
-
const meta = await getCore()
|
|
118
|
-
.media.getImageMetadata(params.buffer)
|
|
119
|
-
.catch(() => null);
|
|
120
|
-
if (!meta) {
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
const imageInfo: DimensionalFileInfo = { w: meta.width, h: meta.height };
|
|
124
|
-
const maxDim = Math.max(meta.width, meta.height);
|
|
125
|
-
if (maxDim > THUMBNAIL_MAX_SIDE) {
|
|
126
|
-
try {
|
|
127
|
-
const thumbBuffer = await getCore().media.resizeToJpeg({
|
|
128
|
-
buffer: params.buffer,
|
|
129
|
-
maxSide: THUMBNAIL_MAX_SIDE,
|
|
130
|
-
quality: THUMBNAIL_QUALITY,
|
|
131
|
-
withoutEnlargement: true,
|
|
132
|
-
});
|
|
133
|
-
const thumbMeta = await getCore()
|
|
134
|
-
.media.getImageMetadata(thumbBuffer)
|
|
135
|
-
.catch(() => null);
|
|
136
|
-
const thumbUri = await params.client.uploadContent(
|
|
137
|
-
thumbBuffer,
|
|
138
|
-
"image/jpeg",
|
|
139
|
-
"thumbnail.jpg",
|
|
140
|
-
);
|
|
141
|
-
imageInfo.thumbnail_url = thumbUri;
|
|
142
|
-
if (thumbMeta) {
|
|
143
|
-
imageInfo.thumbnail_info = {
|
|
144
|
-
w: thumbMeta.width,
|
|
145
|
-
h: thumbMeta.height,
|
|
146
|
-
mimetype: "image/jpeg",
|
|
147
|
-
size: thumbBuffer.byteLength,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
} catch {
|
|
151
|
-
// Thumbnail generation failed, continue without it
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return imageInfo;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export async function resolveMediaDurationMs(params: {
|
|
158
|
-
buffer: Buffer;
|
|
159
|
-
contentType?: string;
|
|
160
|
-
fileName?: string;
|
|
161
|
-
kind: MediaKind;
|
|
162
|
-
}): Promise<number | undefined> {
|
|
163
|
-
if (params.kind !== "audio" && params.kind !== "video") {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
const { parseBuffer } = await import("music-metadata");
|
|
168
|
-
const fileInfo: IFileInfo | string | undefined =
|
|
169
|
-
params.contentType || params.fileName
|
|
170
|
-
? {
|
|
171
|
-
mimeType: params.contentType,
|
|
172
|
-
size: params.buffer.byteLength,
|
|
173
|
-
path: params.fileName,
|
|
174
|
-
}
|
|
175
|
-
: undefined;
|
|
176
|
-
const metadata = await parseBuffer(params.buffer, fileInfo, {
|
|
177
|
-
duration: true,
|
|
178
|
-
skipCovers: true,
|
|
179
|
-
});
|
|
180
|
-
const durationSeconds = metadata.format.duration;
|
|
181
|
-
if (typeof durationSeconds === "number" && Number.isFinite(durationSeconds)) {
|
|
182
|
-
return Math.max(0, Math.round(durationSeconds * 1000));
|
|
183
|
-
}
|
|
184
|
-
} catch {
|
|
185
|
-
// Duration is optional; ignore parse failures.
|
|
186
|
-
}
|
|
187
|
-
return undefined;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async function uploadFile(
|
|
191
|
-
client: MatrixClient,
|
|
192
|
-
file: Buffer,
|
|
193
|
-
params: {
|
|
194
|
-
contentType?: string;
|
|
195
|
-
filename?: string;
|
|
196
|
-
},
|
|
197
|
-
): Promise<string> {
|
|
198
|
-
return await client.uploadContent(file, params.contentType, params.filename);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Upload media with optional encryption for E2EE rooms.
|
|
203
|
-
*/
|
|
204
|
-
export async function uploadMediaMaybeEncrypted(
|
|
205
|
-
client: MatrixClient,
|
|
206
|
-
roomId: string,
|
|
207
|
-
buffer: Buffer,
|
|
208
|
-
params: {
|
|
209
|
-
contentType?: string;
|
|
210
|
-
filename?: string;
|
|
211
|
-
},
|
|
212
|
-
): Promise<{ url: string; file?: EncryptedFile }> {
|
|
213
|
-
// Check if room is encrypted and crypto is available
|
|
214
|
-
const isEncrypted = client.crypto && (await client.crypto.isRoomEncrypted(roomId));
|
|
215
|
-
|
|
216
|
-
if (isEncrypted && client.crypto) {
|
|
217
|
-
// Encrypt the media before uploading
|
|
218
|
-
const encrypted = await client.crypto.encryptMedia(buffer);
|
|
219
|
-
const mxc = await client.uploadContent(encrypted.buffer, params.contentType, params.filename);
|
|
220
|
-
const file: EncryptedFile = { url: mxc, ...encrypted.file };
|
|
221
|
-
return {
|
|
222
|
-
url: mxc,
|
|
223
|
-
file,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Upload unencrypted
|
|
228
|
-
const mxc = await uploadFile(client, buffer, params);
|
|
229
|
-
return { url: mxc };
|
|
230
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { EventType } from "./types.js";
|
|
4
|
-
|
|
5
|
-
let resolveMatrixRoomId: typeof import("./targets.js").resolveMatrixRoomId;
|
|
6
|
-
let normalizeThreadId: typeof import("./targets.js").normalizeThreadId;
|
|
7
|
-
|
|
8
|
-
beforeEach(async () => {
|
|
9
|
-
vi.resetModules();
|
|
10
|
-
({ resolveMatrixRoomId, normalizeThreadId } = await import("./targets.js"));
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe("resolveMatrixRoomId", () => {
|
|
14
|
-
it("uses m.direct when available", async () => {
|
|
15
|
-
const userId = "@user:example.org";
|
|
16
|
-
const client = {
|
|
17
|
-
getAccountData: vi.fn().mockResolvedValue({
|
|
18
|
-
[userId]: ["!room:example.org"],
|
|
19
|
-
}),
|
|
20
|
-
getJoinedRooms: vi.fn(),
|
|
21
|
-
getJoinedRoomMembers: vi.fn(),
|
|
22
|
-
setAccountData: vi.fn(),
|
|
23
|
-
} as unknown as MatrixClient;
|
|
24
|
-
|
|
25
|
-
const roomId = await resolveMatrixRoomId(client, userId);
|
|
26
|
-
|
|
27
|
-
expect(roomId).toBe("!room:example.org");
|
|
28
|
-
// oxlint-disable-next-line typescript/unbound-method
|
|
29
|
-
expect(client.getJoinedRooms).not.toHaveBeenCalled();
|
|
30
|
-
// oxlint-disable-next-line typescript/unbound-method
|
|
31
|
-
expect(client.setAccountData).not.toHaveBeenCalled();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("falls back to joined rooms and persists m.direct", async () => {
|
|
35
|
-
const userId = "@fallback:example.org";
|
|
36
|
-
const roomId = "!room:example.org";
|
|
37
|
-
const setAccountData = vi.fn().mockResolvedValue(undefined);
|
|
38
|
-
const client = {
|
|
39
|
-
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
|
|
40
|
-
getJoinedRooms: vi.fn().mockResolvedValue([roomId]),
|
|
41
|
-
getJoinedRoomMembers: vi.fn().mockResolvedValue(["@bot:example.org", userId]),
|
|
42
|
-
setAccountData,
|
|
43
|
-
} as unknown as MatrixClient;
|
|
44
|
-
|
|
45
|
-
const resolved = await resolveMatrixRoomId(client, userId);
|
|
46
|
-
|
|
47
|
-
expect(resolved).toBe(roomId);
|
|
48
|
-
expect(setAccountData).toHaveBeenCalledWith(
|
|
49
|
-
EventType.Direct,
|
|
50
|
-
expect.objectContaining({ [userId]: [roomId] }),
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("continues when a room member lookup fails", async () => {
|
|
55
|
-
const userId = "@continue:example.org";
|
|
56
|
-
const roomId = "!good:example.org";
|
|
57
|
-
const setAccountData = vi.fn().mockResolvedValue(undefined);
|
|
58
|
-
const getJoinedRoomMembers = vi
|
|
59
|
-
.fn()
|
|
60
|
-
.mockRejectedValueOnce(new Error("boom"))
|
|
61
|
-
.mockResolvedValueOnce(["@bot:example.org", userId]);
|
|
62
|
-
const client = {
|
|
63
|
-
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
|
|
64
|
-
getJoinedRooms: vi.fn().mockResolvedValue(["!bad:example.org", roomId]),
|
|
65
|
-
getJoinedRoomMembers,
|
|
66
|
-
setAccountData,
|
|
67
|
-
} as unknown as MatrixClient;
|
|
68
|
-
|
|
69
|
-
const resolved = await resolveMatrixRoomId(client, userId);
|
|
70
|
-
|
|
71
|
-
expect(resolved).toBe(roomId);
|
|
72
|
-
expect(setAccountData).toHaveBeenCalled();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("allows larger rooms when no 1:1 match exists", async () => {
|
|
76
|
-
const userId = "@group:example.org";
|
|
77
|
-
const roomId = "!group:example.org";
|
|
78
|
-
const client = {
|
|
79
|
-
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
|
|
80
|
-
getJoinedRooms: vi.fn().mockResolvedValue([roomId]),
|
|
81
|
-
getJoinedRoomMembers: vi
|
|
82
|
-
.fn()
|
|
83
|
-
.mockResolvedValue(["@bot:example.org", userId, "@extra:example.org"]),
|
|
84
|
-
setAccountData: vi.fn().mockResolvedValue(undefined),
|
|
85
|
-
} as unknown as MatrixClient;
|
|
86
|
-
|
|
87
|
-
const resolved = await resolveMatrixRoomId(client, userId);
|
|
88
|
-
|
|
89
|
-
expect(resolved).toBe(roomId);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe("normalizeThreadId", () => {
|
|
94
|
-
it("returns null for empty thread ids", () => {
|
|
95
|
-
expect(normalizeThreadId(" ")).toBeNull();
|
|
96
|
-
expect(normalizeThreadId("$thread")).toBe("$thread");
|
|
97
|
-
});
|
|
98
|
-
});
|