mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix(bluebubbles): cache prefixed reply context aliases
* fix: BlueBubbles reply-context fallback cache-key regression * fix(clawsweeper): address review for clawsweeper-commit-openclaw-openclaw-76930da7ebc7 (1) --------- Co-authored-by: openclaw-clawsweeper[bot] <280122609+openclaw-clawsweeper[bot]@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { BlueBubblesClient, createBlueBubblesClientFromParts } from "./client.js";
|
||||
import {
|
||||
_resetBlueBubblesShortIdState,
|
||||
getShortIdForUuid,
|
||||
resolveReplyContextFromCache,
|
||||
} from "./monitor-reply-cache.js";
|
||||
import {
|
||||
@@ -138,6 +139,53 @@ describe("fetchBlueBubblesReplyContext", () => {
|
||||
expect(requestCalls[0]?.path).toBe("/api/v1/message/msg-bare-guid");
|
||||
});
|
||||
|
||||
it("populates the reply cache for the original prefixed reply id", async () => {
|
||||
const { factory } = makeFakeClient([
|
||||
jsonResponse({ data: { text: "cached prefix", handle: { address: "+15551112222" } } }),
|
||||
]);
|
||||
await fetchBlueBubblesReplyContext({
|
||||
...baseParams,
|
||||
replyToId: "p:0/msg-prefixed-cache",
|
||||
chatGuid: "iMessage;-;+15551112222",
|
||||
clientFactory: factory,
|
||||
});
|
||||
const cached = resolveReplyContextFromCache({
|
||||
accountId: "default",
|
||||
replyToId: "p:0/msg-prefixed-cache",
|
||||
chatGuid: "iMessage;-;+15551112222",
|
||||
});
|
||||
expect(cached?.body).toBe("cached prefix");
|
||||
expect(cached?.senderLabel).toBe("+15551112222");
|
||||
});
|
||||
|
||||
it("does not cache non-part-index slash prefixes as aliases", async () => {
|
||||
const { factory, requestCalls } = makeFakeClient([
|
||||
jsonResponse({ data: { text: "cached bare only", handle: { address: "+15551112222" } } }),
|
||||
]);
|
||||
await fetchBlueBubblesReplyContext({
|
||||
...baseParams,
|
||||
replyToId: "../etc/passwd",
|
||||
chatGuid: "iMessage;-;+15551112222",
|
||||
clientFactory: factory,
|
||||
});
|
||||
expect(requestCalls[0]?.path).toBe("/api/v1/message/passwd");
|
||||
expect(
|
||||
resolveReplyContextFromCache({
|
||||
accountId: "default",
|
||||
replyToId: "passwd",
|
||||
chatGuid: "iMessage;-;+15551112222",
|
||||
})?.body,
|
||||
).toBe("cached bare only");
|
||||
expect(
|
||||
resolveReplyContextFromCache({
|
||||
accountId: "default",
|
||||
replyToId: "../etc/passwd",
|
||||
chatGuid: "iMessage;-;+15551112222",
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(getShortIdForUuid("../etc/passwd")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("fetches the BB API and returns body + normalized sender on success", async () => {
|
||||
const { factory, requestCalls } = makeFakeClient([
|
||||
jsonResponse({
|
||||
|
||||
@@ -17,6 +17,8 @@ const DEFAULT_REPLY_FETCH_TIMEOUT_MS = 5_000;
|
||||
// punctuation set below; 128 chars is comfortable headroom (CWE-20).
|
||||
const REPLY_TO_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
||||
const REPLY_TO_ID_MAX_LENGTH = 128;
|
||||
const PART_INDEX_REPLY_TO_ID_PATTERN = /^p:\d{1,10}\/([A-Za-z0-9._:-]+)$/;
|
||||
const PART_INDEX_REPLY_TO_ID_MAX_LENGTH = REPLY_TO_ID_MAX_LENGTH + "p:".length + 10 + "/".length;
|
||||
|
||||
export type BlueBubblesReplyFetchResult = {
|
||||
body?: string;
|
||||
@@ -110,6 +112,18 @@ function sanitizeReplyToId(raw: string): string | null {
|
||||
return bare;
|
||||
}
|
||||
|
||||
function normalizePartIndexReplyToIdAlias(raw: string, bareReplyToId: string): string | null {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.length > PART_INDEX_REPLY_TO_ID_MAX_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
const match = PART_INDEX_REPLY_TO_ID_PATTERN.exec(trimmed);
|
||||
if (!match || match[1] !== bareReplyToId) {
|
||||
return null;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
async function runFetch(
|
||||
params: FetchBlueBubblesReplyContextParams,
|
||||
replyToId: string,
|
||||
@@ -152,7 +166,7 @@ async function runFetch(
|
||||
if (!body && !sender) {
|
||||
return null;
|
||||
}
|
||||
rememberBlueBubblesReplyCache({
|
||||
const cacheEntry = {
|
||||
accountId: params.accountId,
|
||||
messageId: replyToId,
|
||||
chatGuid: params.chatGuid,
|
||||
@@ -161,7 +175,15 @@ async function runFetch(
|
||||
senderLabel: sender,
|
||||
body,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
};
|
||||
rememberBlueBubblesReplyCache(cacheEntry);
|
||||
const partIndexReplyToId = normalizePartIndexReplyToIdAlias(params.replyToId, replyToId);
|
||||
if (partIndexReplyToId) {
|
||||
rememberBlueBubblesReplyCache({
|
||||
...cacheEntry,
|
||||
messageId: partIndexReplyToId,
|
||||
});
|
||||
}
|
||||
return { body, sender };
|
||||
} catch {
|
||||
// Best-effort: swallow network/parse errors. Caller proceeds with empty
|
||||
|
||||
Reference in New Issue
Block a user