@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,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeLowercaseStringOrEmpty,
|
|
3
|
+
normalizeOptionalString,
|
|
4
|
+
} from "autobot/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import type { MatrixClient } from "./sdk.js";
|
|
6
|
+
|
|
7
|
+
export const MATRIX_PROFILE_AVATAR_MAX_BYTES = 10 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
type MatrixProfileClient = Pick<
|
|
10
|
+
MatrixClient,
|
|
11
|
+
"getUserProfile" | "setDisplayName" | "setAvatarUrl" | "uploadContent"
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
type MatrixProfileLoadResult = {
|
|
15
|
+
buffer: Buffer;
|
|
16
|
+
contentType?: string;
|
|
17
|
+
fileName?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type MatrixProfileSyncResult = {
|
|
21
|
+
skipped: boolean;
|
|
22
|
+
displayNameUpdated: boolean;
|
|
23
|
+
avatarUpdated: boolean;
|
|
24
|
+
resolvedAvatarUrl: string | null;
|
|
25
|
+
uploadedAvatarSource: "http" | "path" | null;
|
|
26
|
+
convertedAvatarFromHttp: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function isMatrixMxcUri(value: string): boolean {
|
|
30
|
+
return normalizeLowercaseStringOrEmpty(normalizeOptionalString(value)).startsWith("mxc://");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isMatrixHttpAvatarUri(value: string): boolean {
|
|
34
|
+
const normalized = normalizeLowercaseStringOrEmpty(normalizeOptionalString(value));
|
|
35
|
+
return normalized.startsWith("https://") || normalized.startsWith("http://");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isSupportedMatrixAvatarSource(value: string): boolean {
|
|
39
|
+
return isMatrixMxcUri(value) || isMatrixHttpAvatarUri(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function uploadAvatarMedia(params: {
|
|
43
|
+
client: MatrixProfileClient;
|
|
44
|
+
avatarSource: string;
|
|
45
|
+
avatarMaxBytes: number;
|
|
46
|
+
loadAvatar: (source: string, maxBytes: number) => Promise<MatrixProfileLoadResult>;
|
|
47
|
+
}): Promise<string> {
|
|
48
|
+
const media = await params.loadAvatar(params.avatarSource, params.avatarMaxBytes);
|
|
49
|
+
return await params.client.uploadContent(
|
|
50
|
+
media.buffer,
|
|
51
|
+
media.contentType,
|
|
52
|
+
media.fileName || "avatar",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function resolveAvatarUrl(params: {
|
|
57
|
+
client: MatrixProfileClient;
|
|
58
|
+
avatarUrl: string | null;
|
|
59
|
+
avatarPath?: string | null;
|
|
60
|
+
avatarMaxBytes: number;
|
|
61
|
+
loadAvatarFromUrl?: (url: string, maxBytes: number) => Promise<MatrixProfileLoadResult>;
|
|
62
|
+
loadAvatarFromPath?: (path: string, maxBytes: number) => Promise<MatrixProfileLoadResult>;
|
|
63
|
+
}): Promise<{
|
|
64
|
+
resolvedAvatarUrl: string | null;
|
|
65
|
+
uploadedAvatarSource: "http" | "path" | null;
|
|
66
|
+
convertedAvatarFromHttp: boolean;
|
|
67
|
+
}> {
|
|
68
|
+
const avatarPath = normalizeOptionalString(params.avatarPath) ?? null;
|
|
69
|
+
if (avatarPath) {
|
|
70
|
+
if (!params.loadAvatarFromPath) {
|
|
71
|
+
throw new Error("Matrix avatar path upload requires a media loader.");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
resolvedAvatarUrl: await uploadAvatarMedia({
|
|
75
|
+
client: params.client,
|
|
76
|
+
avatarSource: avatarPath,
|
|
77
|
+
avatarMaxBytes: params.avatarMaxBytes,
|
|
78
|
+
loadAvatar: params.loadAvatarFromPath,
|
|
79
|
+
}),
|
|
80
|
+
uploadedAvatarSource: "path",
|
|
81
|
+
convertedAvatarFromHttp: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const avatarUrl = normalizeOptionalString(params.avatarUrl) ?? null;
|
|
86
|
+
if (!avatarUrl) {
|
|
87
|
+
return {
|
|
88
|
+
resolvedAvatarUrl: null,
|
|
89
|
+
uploadedAvatarSource: null,
|
|
90
|
+
convertedAvatarFromHttp: false,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (isMatrixMxcUri(avatarUrl)) {
|
|
95
|
+
return {
|
|
96
|
+
resolvedAvatarUrl: avatarUrl,
|
|
97
|
+
uploadedAvatarSource: null,
|
|
98
|
+
convertedAvatarFromHttp: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!isMatrixHttpAvatarUri(avatarUrl)) {
|
|
103
|
+
throw new Error("Matrix avatar URL must be an mxc:// URI or an http(s) URL.");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!params.loadAvatarFromUrl) {
|
|
107
|
+
throw new Error("Matrix avatar URL conversion requires a media loader.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
resolvedAvatarUrl: await uploadAvatarMedia({
|
|
112
|
+
client: params.client,
|
|
113
|
+
avatarSource: avatarUrl,
|
|
114
|
+
avatarMaxBytes: params.avatarMaxBytes,
|
|
115
|
+
loadAvatar: params.loadAvatarFromUrl,
|
|
116
|
+
}),
|
|
117
|
+
uploadedAvatarSource: "http",
|
|
118
|
+
convertedAvatarFromHttp: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function syncMatrixOwnProfile(params: {
|
|
123
|
+
client: MatrixProfileClient;
|
|
124
|
+
userId: string;
|
|
125
|
+
displayName?: string | null;
|
|
126
|
+
avatarUrl?: string | null;
|
|
127
|
+
avatarPath?: string | null;
|
|
128
|
+
avatarMaxBytes?: number;
|
|
129
|
+
loadAvatarFromUrl?: (url: string, maxBytes: number) => Promise<MatrixProfileLoadResult>;
|
|
130
|
+
loadAvatarFromPath?: (path: string, maxBytes: number) => Promise<MatrixProfileLoadResult>;
|
|
131
|
+
}): Promise<MatrixProfileSyncResult> {
|
|
132
|
+
const desiredDisplayName = normalizeOptionalString(params.displayName) ?? null;
|
|
133
|
+
const avatar = await resolveAvatarUrl({
|
|
134
|
+
client: params.client,
|
|
135
|
+
avatarUrl: params.avatarUrl ?? null,
|
|
136
|
+
avatarPath: params.avatarPath ?? null,
|
|
137
|
+
avatarMaxBytes: params.avatarMaxBytes ?? MATRIX_PROFILE_AVATAR_MAX_BYTES,
|
|
138
|
+
loadAvatarFromUrl: params.loadAvatarFromUrl,
|
|
139
|
+
loadAvatarFromPath: params.loadAvatarFromPath,
|
|
140
|
+
});
|
|
141
|
+
const desiredAvatarUrl = avatar.resolvedAvatarUrl;
|
|
142
|
+
|
|
143
|
+
if (!desiredDisplayName && !desiredAvatarUrl) {
|
|
144
|
+
return {
|
|
145
|
+
skipped: true,
|
|
146
|
+
displayNameUpdated: false,
|
|
147
|
+
avatarUpdated: false,
|
|
148
|
+
resolvedAvatarUrl: null,
|
|
149
|
+
uploadedAvatarSource: avatar.uploadedAvatarSource,
|
|
150
|
+
convertedAvatarFromHttp: avatar.convertedAvatarFromHttp,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let currentDisplayName: string | undefined;
|
|
155
|
+
let currentAvatarUrl: string | undefined;
|
|
156
|
+
try {
|
|
157
|
+
const currentProfile = await params.client.getUserProfile(params.userId);
|
|
158
|
+
currentDisplayName = normalizeOptionalString(currentProfile.displayname);
|
|
159
|
+
currentAvatarUrl = normalizeOptionalString(currentProfile.avatar_url);
|
|
160
|
+
} catch {
|
|
161
|
+
// If profile fetch fails, attempt writes directly.
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let displayNameUpdated = false;
|
|
165
|
+
let avatarUpdated = false;
|
|
166
|
+
|
|
167
|
+
if (desiredDisplayName && currentDisplayName !== desiredDisplayName) {
|
|
168
|
+
await params.client.setDisplayName(desiredDisplayName);
|
|
169
|
+
displayNameUpdated = true;
|
|
170
|
+
}
|
|
171
|
+
if (desiredAvatarUrl && currentAvatarUrl !== desiredAvatarUrl) {
|
|
172
|
+
await params.client.setAvatarUrl(desiredAvatarUrl);
|
|
173
|
+
avatarUpdated = true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
skipped: false,
|
|
178
|
+
displayNameUpdated,
|
|
179
|
+
avatarUpdated,
|
|
180
|
+
resolvedAvatarUrl: desiredAvatarUrl,
|
|
181
|
+
uploadedAvatarSource: avatar.uploadedAvatarSource,
|
|
182
|
+
convertedAvatarFromHttp: avatar.convertedAvatarFromHttp,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { normalizeOptionalString } from "autobot/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
|
|
3
|
+
export const MATRIX_ANNOTATION_RELATION_TYPE = "m.annotation";
|
|
4
|
+
export const MATRIX_REACTION_EVENT_TYPE = "m.reaction";
|
|
5
|
+
|
|
6
|
+
type MatrixReactionEventContent = {
|
|
7
|
+
"m.relates_to": {
|
|
8
|
+
rel_type: typeof MATRIX_ANNOTATION_RELATION_TYPE;
|
|
9
|
+
event_id: string;
|
|
10
|
+
key: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type MatrixReactionSummary = {
|
|
15
|
+
key: string;
|
|
16
|
+
count: number;
|
|
17
|
+
users: string[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type MatrixReactionAnnotation = {
|
|
21
|
+
key: string;
|
|
22
|
+
eventId?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type MatrixReactionEventLike = {
|
|
26
|
+
content?: unknown;
|
|
27
|
+
sender?: string | null;
|
|
28
|
+
event_id?: string | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function normalizeMatrixReactionMessageId(messageId: string): string {
|
|
32
|
+
const normalized = messageId.trim();
|
|
33
|
+
if (!normalized) {
|
|
34
|
+
throw new Error("Matrix reaction requires a messageId");
|
|
35
|
+
}
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeMatrixReactionEmoji(emoji: string): string {
|
|
40
|
+
const normalized = emoji.trim();
|
|
41
|
+
if (!normalized) {
|
|
42
|
+
throw new Error("Matrix reaction requires an emoji");
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function buildMatrixReactionContent(
|
|
48
|
+
messageId: string,
|
|
49
|
+
emoji: string,
|
|
50
|
+
): MatrixReactionEventContent {
|
|
51
|
+
return {
|
|
52
|
+
"m.relates_to": {
|
|
53
|
+
rel_type: MATRIX_ANNOTATION_RELATION_TYPE,
|
|
54
|
+
event_id: normalizeMatrixReactionMessageId(messageId),
|
|
55
|
+
key: normalizeMatrixReactionEmoji(emoji),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function buildMatrixReactionRelationsPath(roomId: string, messageId: string): string {
|
|
61
|
+
return `/_matrix/client/v1/rooms/${encodeURIComponent(roomId)}/relations/${encodeURIComponent(normalizeMatrixReactionMessageId(messageId))}/${MATRIX_ANNOTATION_RELATION_TYPE}/${MATRIX_REACTION_EVENT_TYPE}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function extractMatrixReactionAnnotation(
|
|
65
|
+
content: unknown,
|
|
66
|
+
): MatrixReactionAnnotation | undefined {
|
|
67
|
+
if (!content || typeof content !== "object") {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const relatesTo = (
|
|
71
|
+
content as {
|
|
72
|
+
"m.relates_to"?: {
|
|
73
|
+
rel_type?: unknown;
|
|
74
|
+
event_id?: unknown;
|
|
75
|
+
key?: unknown;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
)["m.relates_to"];
|
|
79
|
+
if (!relatesTo || typeof relatesTo !== "object") {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
if (
|
|
83
|
+
typeof relatesTo.rel_type === "string" &&
|
|
84
|
+
relatesTo.rel_type !== MATRIX_ANNOTATION_RELATION_TYPE
|
|
85
|
+
) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
const key = normalizeOptionalString(relatesTo.key) ?? "";
|
|
89
|
+
if (!key) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const eventId = normalizeOptionalString(relatesTo.event_id) ?? "";
|
|
93
|
+
return {
|
|
94
|
+
key,
|
|
95
|
+
eventId: eventId || undefined,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function extractMatrixReactionKey(content: unknown): string | undefined {
|
|
100
|
+
return extractMatrixReactionAnnotation(content)?.key;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function summarizeMatrixReactionEvents(
|
|
104
|
+
events: Iterable<Pick<MatrixReactionEventLike, "content" | "sender">>,
|
|
105
|
+
): MatrixReactionSummary[] {
|
|
106
|
+
const summaries = new Map<string, MatrixReactionSummary>();
|
|
107
|
+
for (const event of events) {
|
|
108
|
+
const key = extractMatrixReactionKey(event.content);
|
|
109
|
+
if (!key) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const sender = normalizeOptionalString(event.sender) ?? "";
|
|
113
|
+
const entry = summaries.get(key) ?? { key, count: 0, users: [] };
|
|
114
|
+
entry.count += 1;
|
|
115
|
+
if (sender && !entry.users.includes(sender)) {
|
|
116
|
+
entry.users.push(sender);
|
|
117
|
+
}
|
|
118
|
+
summaries.set(key, entry);
|
|
119
|
+
}
|
|
120
|
+
return Array.from(summaries.values());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function selectOwnMatrixReactionEventIds(
|
|
124
|
+
events: Iterable<Pick<MatrixReactionEventLike, "content" | "event_id" | "sender">>,
|
|
125
|
+
userId: string,
|
|
126
|
+
emoji?: string,
|
|
127
|
+
): string[] {
|
|
128
|
+
const senderId = normalizeOptionalString(userId) ?? "";
|
|
129
|
+
if (!senderId) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const targetEmoji = normalizeOptionalString(emoji);
|
|
133
|
+
const ids: string[] = [];
|
|
134
|
+
for (const event of events) {
|
|
135
|
+
if ((normalizeOptionalString(event.sender) ?? "") !== senderId) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (targetEmoji && extractMatrixReactionKey(event.content) !== targetEmoji) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const eventId = normalizeOptionalString(event.event_id);
|
|
142
|
+
if (eventId) {
|
|
143
|
+
ids.push(eventId);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return ids;
|
|
147
|
+
}
|