ai-chatbot/lib/openclaw-chat/viewer-session.ts

107 lines
3.1 KiB
TypeScript

const OPENCLAW_3D_VIEWER_SESSION_PREFIX = "openclaw-3d-viewer:v1:";
const OPENCLAW_3D_VIEWER_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1000;
export type OpenClaw3DViewerSession = {
messageId: string;
explodeAmount: number;
selectedNodeName: string | null;
updatedAt: number;
};
type ViewerSessionStorage = Pick<Storage, "getItem" | "setItem" | "removeItem">;
function getViewerSessionKey(chatId: string) {
return `${OPENCLAW_3D_VIEWER_SESSION_PREFIX}${chatId}`;
}
function removeViewerSession(storage: ViewerSessionStorage, key: string) {
try {
storage.removeItem(key);
} catch {
// Storage can be unavailable under strict browser privacy policies.
}
}
function normalizeExplodeAmount(value: unknown) {
const amount = typeof value === "number" ? value : Number(value);
if (!Number.isFinite(amount)) return 0;
return Math.max(0, Math.min(1, amount));
}
export function readOpenClaw3DViewerSession(params: {
storage: ViewerSessionStorage;
chatId: string;
now?: number;
}): OpenClaw3DViewerSession | null {
const key = getViewerSessionKey(params.chatId);
try {
const raw = params.storage.getItem(key);
if (!raw) return null;
const value = JSON.parse(raw) as Record<string, unknown>;
const messageId =
typeof value.messageId === "string" ? value.messageId.trim() : "";
const updatedAt = Number(value.updatedAt);
const now = params.now ?? Date.now();
if (
!messageId ||
!Number.isFinite(updatedAt) ||
updatedAt <= 0 ||
now - updatedAt > OPENCLAW_3D_VIEWER_SESSION_MAX_AGE_MS
) {
removeViewerSession(params.storage, key);
return null;
}
return {
messageId,
explodeAmount: normalizeExplodeAmount(value.explodeAmount),
selectedNodeName:
typeof value.selectedNodeName === "string" &&
value.selectedNodeName.trim()
? value.selectedNodeName.trim()
: null,
updatedAt,
};
} catch {
removeViewerSession(params.storage, key);
return null;
}
}
export function writeOpenClaw3DViewerSession(params: {
storage: ViewerSessionStorage;
chatId: string;
messageId: string;
explodeAmount: number;
selectedNodeName?: string | null;
now?: number;
}) {
const messageId = params.messageId.trim();
if (!messageId) return;
try {
params.storage.setItem(
getViewerSessionKey(params.chatId),
JSON.stringify({
messageId,
explodeAmount: normalizeExplodeAmount(params.explodeAmount),
selectedNodeName: params.selectedNodeName?.trim() || null,
updatedAt: params.now ?? Date.now(),
} satisfies OpenClaw3DViewerSession)
);
} catch {
// Private browsing and storage policies may disable sessionStorage.
}
}
export function clearOpenClaw3DViewerSession(params: {
storage: ViewerSessionStorage;
chatId: string;
messageId: string;
}) {
const current = readOpenClaw3DViewerSession({
storage: params.storage,
chatId: params.chatId,
});
if (current?.messageId !== params.messageId) return;
removeViewerSession(params.storage, getViewerSessionKey(params.chatId));
}