mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
fix(slack): retire replay-filtered debounce keys (#128331)
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
committed by
GitHub
parent
a2e4c277c0
commit
3a3f4e3fd6
@@ -563,6 +563,47 @@ describe("createSlackMessageHandler", () => {
|
||||
expect(flushKeyMock).toHaveBeenCalledWith("slack:default:C111:1709000000.000100:U111");
|
||||
});
|
||||
|
||||
it("retires a buffered key when replay filtering drops every entry", async () => {
|
||||
const handler = createSlackMessageHandler({
|
||||
ctx: createContext(),
|
||||
account: { accountId: "default" } as Parameters<
|
||||
typeof createSlackMessageHandler
|
||||
>[0]["account"],
|
||||
});
|
||||
const bufferedMessage = {
|
||||
type: "message" as const,
|
||||
channel: "C111",
|
||||
user: "U111",
|
||||
ts: "1709000000.000300",
|
||||
text: "duplicate buffered text",
|
||||
};
|
||||
|
||||
await handler(bufferedMessage as never, { source: "message" });
|
||||
const first = enqueueMock.mock.calls[0]?.[0] as Record<string, unknown>;
|
||||
await runOnFlush([first]);
|
||||
|
||||
await handler(bufferedMessage as never, { source: "message" });
|
||||
const duplicate = enqueueMock.mock.calls[1]?.[0] as Record<string, unknown>;
|
||||
await runOnFlush([duplicate]);
|
||||
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
||||
flushKeyMock.mockClear();
|
||||
|
||||
await handler(
|
||||
{
|
||||
type: "message",
|
||||
subtype: "file_share",
|
||||
channel: "C111",
|
||||
user: "U111",
|
||||
ts: "1709000000.000400",
|
||||
text: "file follows",
|
||||
files: [{ id: "F1" }],
|
||||
} as never,
|
||||
{ source: "message" },
|
||||
);
|
||||
|
||||
expect(flushKeyMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("waits for debounced dispatch completion when requested by relay delivery", async () => {
|
||||
const { handler } = createHandlerWithTracker();
|
||||
const handled = handler(
|
||||
|
||||
@@ -208,6 +208,29 @@ export function createSlackMessageHandler(params: {
|
||||
.filter((completion) => completion !== undefined);
|
||||
try {
|
||||
await (async () => {
|
||||
const flushedEntry = entries.at(-1);
|
||||
if (flushedEntry) {
|
||||
const teamId = flushedEntry.opts.eventScope?.teamId;
|
||||
const flushedKey = buildSlackDebounceKey(
|
||||
flushedEntry.message,
|
||||
ctx.accountId,
|
||||
teamId,
|
||||
);
|
||||
const topLevelConversationKey = buildTopLevelSlackConversationKey(
|
||||
flushedEntry.message,
|
||||
ctx.accountId,
|
||||
teamId,
|
||||
);
|
||||
if (flushedKey && topLevelConversationKey) {
|
||||
const pendingKeys = pendingTopLevelDebounceKeys.get(topLevelConversationKey);
|
||||
if (pendingKeys) {
|
||||
pendingKeys.delete(flushedKey);
|
||||
if (pendingKeys.size === 0) {
|
||||
pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Logical-identity claims: Slack sends message + app_mention twins with
|
||||
// distinct event_ids for one post, so the durable queue cannot dedupe
|
||||
// them. Same-flush twins share one claim and one logical message while
|
||||
@@ -271,22 +294,6 @@ export function createSlackMessageHandler(params: {
|
||||
releaseClaims();
|
||||
return;
|
||||
}
|
||||
const teamId = last.opts.eventScope?.teamId;
|
||||
const flushedKey = buildSlackDebounceKey(last.message, ctx.accountId, teamId);
|
||||
const topLevelConversationKey = buildTopLevelSlackConversationKey(
|
||||
last.message,
|
||||
ctx.accountId,
|
||||
teamId,
|
||||
);
|
||||
if (flushedKey && topLevelConversationKey) {
|
||||
const pendingKeys = pendingTopLevelDebounceKeys.get(topLevelConversationKey);
|
||||
if (pendingKeys) {
|
||||
pendingKeys.delete(flushedKey);
|
||||
if (pendingKeys.size === 0) {
|
||||
pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
const combinedText =
|
||||
surviving.length === 1
|
||||
? (last.message.text ?? "")
|
||||
|
||||
Reference in New Issue
Block a user