Skip to content

Commit e938461

Browse files
LiuwqGitsteipete
andauthored
fix(discord): use configured statusReactions.timing instead of DEFAULT_TIMING (#94736)
* fix(discord): use configured statusReactions.timing instead of DEFAULT_TIMING Makes the Discord message finalizer respect user-configured doneHoldMs/errorHoldMs instead of always using hardcoded DEFAULT_TIMING values. Brings Discord to parity with Telegram and Slack which already use configured timing. Fixes #78431 * test(discord): strengthen configured-timing regression with old-default assertion Add a no-cleanup assertion at DEFAULT_TIMING.doneHoldMs (1500ms) before advancing to the configured 2000ms hold, so the test fails against the old hardcoded-default behavior. Add a matching errorHoldMs regression test using failedCounts to drive the error terminal path. Address ClawSweeper [P2] review finding on #94736: the previous test advanced straight to 2000ms and would pass without the runtime fix because main already removes the done reaction at 1500ms. * refactor(discord): unify status reaction timing proof --------- Co-authored-by: Peter Steinberger <[email protected]>
1 parent e3671e0 commit e938461

2 files changed

Lines changed: 72 additions & 4 deletions

File tree

extensions/discord/src/monitor/message-handler.process.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,70 @@ describe("processDiscordMessage ack reactions", () => {
12551255
removeAckAfterReply: true,
12561256
});
12571257
});
1258+
1259+
it.each([
1260+
{
1261+
outcome: "done",
1262+
timingKey: "doneHoldMs",
1263+
configuredHoldMs: 2_000,
1264+
terminalEmoji: DEFAULT_EMOJIS.done,
1265+
},
1266+
{
1267+
outcome: "error",
1268+
timingKey: "errorHoldMs",
1269+
configuredHoldMs: 4_000,
1270+
terminalEmoji: DEFAULT_EMOJIS.error,
1271+
},
1272+
] as const)(
1273+
"uses configured statusReactions.timing.$timingKey for $outcome cleanup",
1274+
async ({ outcome, timingKey, configuredHoldMs, terminalEmoji }) => {
1275+
vi.useFakeTimers();
1276+
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
1277+
if (outcome === "done") {
1278+
await params?.replyOptions?.onReasoningStream?.();
1279+
return createNoQueuedDispatchResult();
1280+
}
1281+
return {
1282+
queuedFinal: false,
1283+
counts: { final: 0, tool: 0, block: 0 },
1284+
failedCounts: { final: 1 },
1285+
};
1286+
});
1287+
1288+
const ctx = await createAutomaticSourceDeliveryContext({
1289+
cfg: {
1290+
messages: {
1291+
ackReaction: "👀",
1292+
removeAckAfterReply: true,
1293+
statusReactions: {
1294+
timing: { [timingKey]: configuredHoldMs, debounceMs: 0 },
1295+
},
1296+
},
1297+
session: { store: "/tmp/openclaw-discord-process-test-sessions.json" },
1298+
},
1299+
});
1300+
1301+
await runProcessDiscordMessage(ctx);
1302+
expect(getReactionEmojis()).toContain(terminalEmoji);
1303+
1304+
await vi.advanceTimersByTimeAsync(configuredHoldMs - 1);
1305+
expect(sendMocks.removeReactionDiscord).not.toHaveBeenCalledWith(
1306+
expect.anything(),
1307+
expect.anything(),
1308+
terminalEmoji,
1309+
expect.anything(),
1310+
);
1311+
1312+
await vi.advanceTimersByTimeAsync(1);
1313+
await vi.runAllTimersAsync();
1314+
expect(sendMocks.removeReactionDiscord).toHaveBeenCalledWith(
1315+
expect.anything(),
1316+
expect.anything(),
1317+
terminalEmoji,
1318+
expect.anything(),
1319+
);
1320+
},
1321+
);
12581322
});
12591323

12601324
describe("processDiscordMessage session routing", () => {

extensions/discord/src/monitor/message-handler.process.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,18 @@ async function processDiscordMessageInner(
301301
messageId: message.id,
302302
reactionContext: ackReactionContext,
303303
});
304+
const statusReactionTiming = {
305+
...DEFAULT_TIMING,
306+
...cfg.messages?.statusReactions?.timing,
307+
};
304308
let statusReactionTarget = `${messageChannelId}/${message.id}`;
305309
let statusReactionsActive = statusReactionsEnabled;
306310
let statusReactions = createStatusReactionController({
307311
enabled: statusReactionsEnabled,
308312
adapter: discordAdapter,
309313
initialEmoji: ackReaction,
310314
emojis: cfg.messages?.statusReactions?.emojis,
311-
timing: cfg.messages?.statusReactions?.timing,
315+
timing: statusReactionTiming,
312316
onError: (err) => {
313317
logAckFailure({
314318
log: logVerbose,
@@ -393,7 +397,7 @@ async function processDiscordMessageInner(
393397
adapter: trackedAdapter,
394398
initialEmoji: emoji,
395399
emojis: cfg.messages?.statusReactions?.emojis,
396-
timing: cfg.messages?.statusReactions?.timing,
400+
timing: statusReactionTiming,
397401
onError: (err) => {
398402
logAckFailure({
399403
log: logVerbose,
@@ -1332,8 +1336,8 @@ async function processDiscordMessageInner(
13321336
void (async () => {
13331337
await sleep(
13341338
dispatchError || finalDeliveryFailed
1335-
? DEFAULT_TIMING.errorHoldMs
1336-
: DEFAULT_TIMING.doneHoldMs,
1339+
? statusReactionTiming.errorHoldMs
1340+
: statusReactionTiming.doneHoldMs,
13371341
);
13381342
await statusReactions.clear();
13391343
})();

0 commit comments

Comments
 (0)