

























@@ -1,4 +1,5 @@
11import type { ExecApprovalReplyDecision } from "openclaw/plugin-sdk/approval-runtime";
2+import { getOptionalMatrixRuntime } from "./runtime.js";
2334const MATRIX_APPROVAL_REACTION_META = {
45"allow-once": {
@@ -21,7 +22,11 @@ const MATRIX_APPROVAL_REACTION_ORDER = [
2122"deny",
2223] as const satisfies readonly ExecApprovalReplyDecision[];
232424-type MatrixApprovalReactionBinding = {
25+const PERSISTENT_NAMESPACE = "matrix.approval-reactions";
26+const PERSISTENT_MAX_ENTRIES = 1000;
27+const DEFAULT_REACTION_TARGET_TTL_MS = 24 * 60 * 60 * 1000;
28+29+export type MatrixApprovalReactionBinding = {
2530decision: ExecApprovalReplyDecision;
2631emoji: string;
2732label: string;
@@ -37,7 +42,24 @@ type MatrixApprovalReactionTarget = {
3742allowedDecisions: readonly ExecApprovalReplyDecision[];
3843};
394445+type PersistedMatrixApprovalReactionTarget = {
46+version: 1;
47+target: MatrixApprovalReactionTarget;
48+};
49+50+type MatrixApprovalReactionStore = {
51+register(
52+key: string,
53+value: PersistedMatrixApprovalReactionTarget,
54+opts?: { ttlMs?: number },
55+): Promise<void>;
56+lookup(key: string): Promise<PersistedMatrixApprovalReactionTarget | undefined>;
57+delete(key: string): Promise<boolean>;
58+};
59+4060const matrixApprovalReactionTargets = new Map<string, MatrixApprovalReactionTarget>();
61+let persistentStore: MatrixApprovalReactionStore | undefined;
62+let persistentStoreDisabled = false;
41634264function buildReactionTargetKey(roomId: string, eventId: string): string | null {
4365const normalizedRoomId = roomId.trim();
@@ -48,6 +70,97 @@ function buildReactionTargetKey(roomId: string, eventId: string): string | null
4870return `${normalizedRoomId}:${normalizedEventId}`;
4971}
507273+function reportPersistentApprovalReactionError(error: unknown): void {
74+try {
75+getOptionalMatrixRuntime()
76+?.logging.getChildLogger({ plugin: "matrix", feature: "approval-reaction-state" })
77+.warn("Matrix persistent approval reaction state failed", { error: String(error) });
78+} catch {
79+// Best effort only: persistent state must never break Matrix reactions.
80+}
81+}
82+83+function disablePersistentApprovalReactionStore(error: unknown): void {
84+persistentStoreDisabled = true;
85+persistentStore = undefined;
86+reportPersistentApprovalReactionError(error);
87+}
88+89+function getPersistentApprovalReactionStore(): MatrixApprovalReactionStore | undefined {
90+if (persistentStoreDisabled) {
91+return undefined;
92+}
93+if (persistentStore) {
94+return persistentStore;
95+}
96+const runtime = getOptionalMatrixRuntime();
97+if (!runtime) {
98+return undefined;
99+}
100+try {
101+persistentStore = runtime.state.openKeyedStore<PersistedMatrixApprovalReactionTarget>({
102+namespace: PERSISTENT_NAMESPACE,
103+maxEntries: PERSISTENT_MAX_ENTRIES,
104+defaultTtlMs: DEFAULT_REACTION_TARGET_TTL_MS,
105+});
106+return persistentStore;
107+} catch (error) {
108+disablePersistentApprovalReactionStore(error);
109+return undefined;
110+}
111+}
112+113+function readPersistedTarget(value: unknown): MatrixApprovalReactionTarget | null {
114+const persisted = value as PersistedMatrixApprovalReactionTarget | undefined;
115+if (
116+persisted?.version !== 1 ||
117+!persisted.target ||
118+typeof persisted.target.approvalId !== "string" ||
119+!Array.isArray(persisted.target.allowedDecisions)
120+) {
121+return null;
122+}
123+return persisted.target;
124+}
125+126+function rememberPersistentApprovalReactionTarget(params: {
127+key: string;
128+target: MatrixApprovalReactionTarget;
129+ttlMs?: number;
130+}): void {
131+const ttlMs = params.ttlMs == null ? DEFAULT_REACTION_TARGET_TTL_MS : Math.max(1, params.ttlMs);
132+const store = getPersistentApprovalReactionStore();
133+if (!store) {
134+return;
135+}
136+void store
137+.register(params.key, { version: 1, target: params.target }, { ttlMs })
138+.catch(disablePersistentApprovalReactionStore);
139+}
140+141+function forgetPersistentApprovalReactionTarget(key: string): void {
142+const store = getPersistentApprovalReactionStore();
143+if (!store) {
144+return;
145+}
146+void store.delete(key).catch(disablePersistentApprovalReactionStore);
147+}
148+149+async function lookupPersistentApprovalReactionTarget(
150+key: string,
151+): Promise<MatrixApprovalReactionTarget | null> {
152+const store = getPersistentApprovalReactionStore();
153+if (!store) {
154+return null;
155+}
156+try {
157+return readPersistedTarget(await store.lookup(key));
158+} catch (error) {
159+disablePersistentApprovalReactionStore(error);
160+return null;
161+}
162+}
163+51164export function listMatrixApprovalReactionBindings(
52165allowedDecisions: readonly ExecApprovalReplyDecision[],
53166): MatrixApprovalReactionBinding[] {
@@ -96,6 +209,7 @@ export function registerMatrixApprovalReactionTarget(params: {
96209eventId: string;
97210approvalId: string;
98211allowedDecisions: readonly ExecApprovalReplyDecision[];
212+ttlMs?: number;
99213}): void {
100214const key = buildReactionTargetKey(params.roomId, params.eventId);
101215const approvalId = params.approvalId.trim();
@@ -110,9 +224,15 @@ export function registerMatrixApprovalReactionTarget(params: {
110224if (!key || !approvalId || allowedDecisions.length === 0) {
111225return;
112226}
113-matrixApprovalReactionTargets.set(key, {
227+const target = {
114228 approvalId,
115229 allowedDecisions,
230+};
231+matrixApprovalReactionTargets.set(key, target);
232+rememberPersistentApprovalReactionTarget({
233+ key,
234+ target,
235+ttlMs: params.ttlMs,
116236});
117237}
118238@@ -125,18 +245,14 @@ export function unregisterMatrixApprovalReactionTarget(params: {
125245return;
126246}
127247matrixApprovalReactionTargets.delete(key);
248+forgetPersistentApprovalReactionTarget(key);
128249}
129250130-export function resolveMatrixApprovalReactionTarget(params: {
131-roomId: string;
132-eventId: string;
251+function resolveTarget(params: {
252+target: MatrixApprovalReactionTarget | null | undefined;
133253reactionKey: string;
134254}): MatrixApprovalReactionResolution | null {
135-const key = buildReactionTargetKey(params.roomId, params.eventId);
136-if (!key) {
137-return null;
138-}
139-const target = matrixApprovalReactionTargets.get(key);
255+const target = params.target;
140256if (!target) {
141257return null;
142258}
@@ -153,6 +269,45 @@ export function resolveMatrixApprovalReactionTarget(params: {
153269};
154270}
155271272+export function resolveMatrixApprovalReactionTarget(params: {
273+roomId: string;
274+eventId: string;
275+reactionKey: string;
276+}): MatrixApprovalReactionResolution | null {
277+const key = buildReactionTargetKey(params.roomId, params.eventId);
278+if (!key) {
279+return null;
280+}
281+return resolveTarget({
282+target: matrixApprovalReactionTargets.get(key),
283+reactionKey: params.reactionKey,
284+});
285+}
286+287+export async function resolveMatrixApprovalReactionTargetWithPersistence(params: {
288+roomId: string;
289+eventId: string;
290+reactionKey: string;
291+}): Promise<MatrixApprovalReactionResolution | null> {
292+const key = buildReactionTargetKey(params.roomId, params.eventId);
293+if (!key) {
294+return null;
295+}
296+const inMemory = resolveTarget({
297+target: matrixApprovalReactionTargets.get(key),
298+reactionKey: params.reactionKey,
299+});
300+if (inMemory) {
301+return inMemory;
302+}
303+return resolveTarget({
304+target: await lookupPersistentApprovalReactionTarget(key),
305+reactionKey: params.reactionKey,
306+});
307+}
308+156309export function clearMatrixApprovalReactionTargetsForTest(): void {
157310matrixApprovalReactionTargets.clear();
311+persistentStore = undefined;
312+persistentStoreDisabled = false;
158313}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。