mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-27 00:52:05 +00:00
fix(feishu): suppress stale replay typing indicators (#30709) (thanks @arkyu2077)
This commit is contained in:
@@ -116,6 +116,59 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
|
||||
expect(addTypingIndicatorMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips typing indicator for stale replayed messages", async () => {
|
||||
createFeishuReplyDispatcher({
|
||||
cfg: {} as never,
|
||||
agentId: "agent",
|
||||
runtime: {} as never,
|
||||
chatId: "oc_chat",
|
||||
replyToMessageId: "om_parent",
|
||||
messageCreateTimeMs: Date.now() - 3 * 60_000,
|
||||
});
|
||||
|
||||
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];
|
||||
await options.onReplyStart?.();
|
||||
|
||||
expect(addTypingIndicatorMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("treats second-based timestamps as stale for typing suppression", async () => {
|
||||
createFeishuReplyDispatcher({
|
||||
cfg: {} as never,
|
||||
agentId: "agent",
|
||||
runtime: {} as never,
|
||||
chatId: "oc_chat",
|
||||
replyToMessageId: "om_parent",
|
||||
messageCreateTimeMs: Math.floor((Date.now() - 3 * 60_000) / 1000),
|
||||
});
|
||||
|
||||
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];
|
||||
await options.onReplyStart?.();
|
||||
|
||||
expect(addTypingIndicatorMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps typing indicator for fresh messages", async () => {
|
||||
createFeishuReplyDispatcher({
|
||||
cfg: {} as never,
|
||||
agentId: "agent",
|
||||
runtime: {} as never,
|
||||
chatId: "oc_chat",
|
||||
replyToMessageId: "om_parent",
|
||||
messageCreateTimeMs: Date.now() - 30_000,
|
||||
});
|
||||
|
||||
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];
|
||||
await options.onReplyStart?.();
|
||||
|
||||
expect(addTypingIndicatorMock).toHaveBeenCalledTimes(1);
|
||||
expect(addTypingIndicatorMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messageId: "om_parent",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps auto mode plain text on non-streaming send path", async () => {
|
||||
createFeishuReplyDispatcher({
|
||||
cfg: {} as never,
|
||||
|
||||
@@ -25,6 +25,16 @@ function shouldUseCard(text: string): boolean {
|
||||
/** Maximum age (ms) for a message to receive a typing indicator reaction.
|
||||
* Messages older than this are likely replays after context compaction (#30418). */
|
||||
const TYPING_INDICATOR_MAX_AGE_MS = 2 * 60_000;
|
||||
const MS_EPOCH_MIN = 1_000_000_000_000;
|
||||
|
||||
function normalizeEpochMs(timestamp: number | undefined): number | undefined {
|
||||
if (!Number.isFinite(timestamp) || timestamp === undefined || timestamp <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
// Defensive normalization: some payloads use seconds, others milliseconds.
|
||||
// Values below 1e12 are treated as epoch-seconds.
|
||||
return timestamp < MS_EPOCH_MIN ? timestamp * 1000 : timestamp;
|
||||
}
|
||||
|
||||
export type CreateFeishuReplyDispatcherParams = {
|
||||
cfg: ClawdbotConfig;
|
||||
@@ -72,9 +82,10 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
}
|
||||
// Skip typing indicator for old messages — likely replays after context
|
||||
// compaction that would flood users with stale notifications (#30418).
|
||||
const messageCreateTimeMs = normalizeEpochMs(params.messageCreateTimeMs);
|
||||
if (
|
||||
params.messageCreateTimeMs &&
|
||||
Date.now() - params.messageCreateTimeMs > TYPING_INDICATOR_MAX_AGE_MS
|
||||
messageCreateTimeMs !== undefined &&
|
||||
Date.now() - messageCreateTimeMs > TYPING_INDICATOR_MAX_AGE_MS
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user