mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-31 20:01:36 +00:00
fix(bluebubbles): coalesce URL-only inbound shares
This commit is contained in:
@@ -38,6 +38,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Memory/QMD: honor `memory.qmd.update.embedInterval` even when regular QMD update cadence is disabled or slower by arming a dedicated embed-cadence maintenance timer, while avoiding redundant timers when regular updates are already frequent enough. (#37326) Thanks @barronlroth.
|
||||
- Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
|
||||
- Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
|
||||
- BlueBubbles/iMessage: coalesce URL-only inbound messages with their link-preview balloon again so sharing a bare link no longer drops the URL from agent context. Thanks @vincentkoc.
|
||||
- Sandbox/browser: install `fonts-noto-cjk` in the sandbox browser image so screenshots render Chinese, Japanese, and Korean text correctly instead of tofu boxes. Fixes #35597. Thanks @carrotRakko and @vincentkoc.
|
||||
- Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
|
||||
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
|
||||
|
||||
@@ -150,7 +150,7 @@ export function createBlueBubblesDebounceRegistry(params: {
|
||||
const balloonBundleId = msg.balloonBundleId?.trim();
|
||||
const associatedMessageGuid = msg.associatedMessageGuid?.trim();
|
||||
if (balloonBundleId && associatedMessageGuid) {
|
||||
return `bluebubbles:${account.accountId}:balloon:${associatedMessageGuid}`;
|
||||
return `bluebubbles:${account.accountId}:msg:${associatedMessageGuid}`;
|
||||
}
|
||||
|
||||
const messageId = msg.messageId?.trim();
|
||||
|
||||
@@ -850,6 +850,68 @@ describe("BlueBubbles webhook monitor", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("coalesces URL text with URL balloon webhook events by associatedMessageGuid", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const core = createMockRuntime();
|
||||
installTimingAwareInboundDebouncer(core);
|
||||
const processMessage = vi.fn().mockResolvedValue(undefined);
|
||||
const registry = createBlueBubblesDebounceRegistry({ processMessage });
|
||||
const account = createMockAccount();
|
||||
const target = {
|
||||
account,
|
||||
config: {},
|
||||
runtime: { log: vi.fn(), error: vi.fn() },
|
||||
core,
|
||||
path: "/bluebubbles-webhook",
|
||||
};
|
||||
const debouncer = registry.getOrCreateDebouncer(target);
|
||||
|
||||
const messageId = "url-msg-1";
|
||||
const chatGuid = "iMessage;-;+15551234567";
|
||||
const url = "https://github.com/bitfocus/companion/issues/4047";
|
||||
|
||||
await debouncer.enqueue({
|
||||
message: createDebounceTestMessage({
|
||||
chatGuid,
|
||||
text: url,
|
||||
messageId,
|
||||
}),
|
||||
target,
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(300);
|
||||
|
||||
await debouncer.enqueue({
|
||||
message: createDebounceTestMessage({
|
||||
chatGuid,
|
||||
text: url,
|
||||
messageId: "url-balloon-1",
|
||||
balloonBundleId: "com.apple.messages.URLBalloonProvider",
|
||||
associatedMessageGuid: messageId,
|
||||
}),
|
||||
target,
|
||||
});
|
||||
|
||||
expect(processMessage).not.toHaveBeenCalled();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(600);
|
||||
|
||||
expect(processMessage).toHaveBeenCalledTimes(1);
|
||||
expect(processMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
text: url,
|
||||
messageId,
|
||||
balloonBundleId: undefined,
|
||||
}),
|
||||
target,
|
||||
);
|
||||
expect(target.runtime.error).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("skips null-text entries during flush and still delivers the valid message", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user