fix: await DiscordMessageListener handler for queued messages (#22396)

Co-authored-by: Irene <huangxiyan2311@gmail.com>
This commit is contained in:
Peter Steinberger
2026-02-22 00:41:46 +01:00
parent 8922cb4085
commit 7707e3406c
3 changed files with 30 additions and 8 deletions

View File

@@ -106,6 +106,7 @@ Docs: https://docs.openclaw.ai
- Telegram/Streaming: restore 30-char first-preview debounce and scope `NO_REPLY` prefix suppression to partial sentinel fragments so normal `No...` text is not filtered. (#22613) thanks @obviyus.
- Telegram/Status reactions: refresh stall timers on repeated phase updates and honor ack-reaction scope when lifecycle reactions are enabled, preventing false stall emojis and unwanted group reactions. Thanks @wolly-tundracube and @thewilloftheshadow.
- Telegram/Status reactions: keep lifecycle reactions active when available-reactions lookup fails by falling back to unrestricted variant selection instead of suppressing reaction updates. (#22380) thanks @obviyus.
- Discord/Events: await `DiscordMessageListener` message handlers so regular `MESSAGE_CREATE` traffic is processed through queue ordering/timeout flow instead of fire-and-forget drops. (#22396) Thanks @sIlENtbuffER.
- Discord/Streaming: apply `replyToMode: first` only to the first Discord chunk so block-streamed replies do not spam mention pings. (#20726) Thanks @thewilloftheshadow for the report.
- Discord/Components: map DM channel targets back to user-scoped component sessions so button/select interactions stay in the main DM session. Thanks @thewilloftheshadow.
- Discord/Allowlist: lazy-load guild lists when resolving Discord user allowlists so ID-only entries resolve even if guild fetch fails. (#20208) Thanks @zhangjunmengyang.

View File

@@ -67,7 +67,7 @@ describe("registerDiscordListener", () => {
});
describe("DiscordMessageListener", () => {
it("returns before the handler finishes", async () => {
it("awaits the handler before returning", async () => {
let handlerResolved = false;
let resolveHandler: (() => void) | null = null;
const handlerPromise = new Promise<void>((resolve) => {
@@ -79,19 +79,30 @@ describe("DiscordMessageListener", () => {
const handler = vi.fn(() => handlerPromise);
const listener = new DiscordMessageListener(handler);
await listener.handle(
const handlePromise = listener.handle(
{} as unknown as import("./monitor/listeners.js").DiscordMessageEvent,
{} as unknown as import("@buape/carbon").Client,
);
let handleResolved = false;
void handlePromise.then(() => {
handleResolved = true;
});
// Handler should be called but not yet resolved
expect(handler).toHaveBeenCalledOnce();
expect(handlerResolved).toBe(false);
await Promise.resolve();
expect(handleResolved).toBe(false);
// Release the handler
const release = resolveHandler;
if (typeof release === "function") {
(release as () => void)();
}
await handlerPromise;
// Now await handle() - it should complete only after handler resolves
await handlePromise;
expect(handlerResolved).toBe(true);
});
it("logs handler failures", async () => {
@@ -129,18 +140,29 @@ describe("DiscordMessageListener", () => {
} as unknown as ReturnType<typeof import("../logging/subsystem.js").createSubsystemLogger>;
const listener = new DiscordMessageListener(handler, logger);
await listener.handle(
// Start handle() but don't await yet
const handlePromise = listener.handle(
{} as unknown as import("./monitor/listeners.js").DiscordMessageEvent,
{} as unknown as import("@buape/carbon").Client,
);
let handleResolved = false;
void handlePromise.then(() => {
handleResolved = true;
});
await Promise.resolve();
expect(handleResolved).toBe(false);
// Advance time past the slow listener threshold
vi.setSystemTime(31_000);
// Release the handler
const release = resolveHandler;
if (typeof release === "function") {
(release as () => void)();
}
await handlerPromise;
await Promise.resolve();
// Now await handle() - it should complete and log the slow listener
await handlePromise;
expect(logger.warn).toHaveBeenCalled();
const warnMock = logger.warn as unknown as { mock: { calls: unknown[][] } };

View File

@@ -86,8 +86,7 @@ export class DiscordMessageListener extends MessageCreateListener {
async handle(data: DiscordMessageEvent, client: Client) {
const startedAt = Date.now();
const task = Promise.resolve(this.handler(data, client));
void task
await this.handler(data, client)
.catch((err) => {
const logger = this.logger ?? discordEventQueueLog;
logger.error(danger(`discord handler failed: ${String(err)}`));