mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:00:50 +00:00
fix: end WhatsApp sockets during teardown
This commit is contained in:
@@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- WhatsApp: close long-lived web sockets through Baileys `end(error)` before falling back to raw websocket close, so listener teardown runs Baileys cleanup instead of leaving zombie sockets. Fixes #52442. Thanks @essendigitalgroup-cyber.
|
||||
- Gateway/sessions: move hot transcript reads and mirror appends onto async bounded IO with serialized parent-linked writes, keeping large session histories from stalling Gateway requests and channel replies. Fixes #75656. Thanks @DerFlash.
|
||||
- macOS/Voice Wake: accept trigger-only phrases in the built-in Voice Wake test, matching the settings UI and runtime trigger-only path instead of requiring extra command text after the wake word. Fixes #64986. Thanks @zoiks65.
|
||||
- Cron/TTS: run cron announce payloads through the normal TTS directive transform before outbound delivery, so scheduled `[[tts]]` replies generate voice payloads instead of leaking raw tags. Fixes #52125. Thanks @kenchen3000.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { getRegisteredWhatsAppConnectionController } from "./connection-controller-registry.js";
|
||||
import { WhatsAppConnectionController } from "./connection-controller.js";
|
||||
import { closeWaSocket, WhatsAppConnectionController } from "./connection-controller.js";
|
||||
import type { WhatsAppSendKind, WhatsAppSendResult } from "./inbound/send-result.js";
|
||||
import { createWaSocket, waitForWaConnection } from "./session.js";
|
||||
|
||||
@@ -40,6 +40,7 @@ function createSocketWithTransportEmitter() {
|
||||
const ws = new EventEmitter() as EventEmitter & { close: ReturnType<typeof vi.fn> };
|
||||
ws.close = vi.fn();
|
||||
return {
|
||||
end: vi.fn(),
|
||||
ws,
|
||||
};
|
||||
}
|
||||
@@ -74,6 +75,7 @@ describe("WhatsAppConnectionController", () => {
|
||||
|
||||
it("closes the socket when open fails before listener creation", async () => {
|
||||
const sock = {
|
||||
end: vi.fn(),
|
||||
ws: {
|
||||
close: vi.fn(),
|
||||
},
|
||||
@@ -91,11 +93,21 @@ describe("WhatsAppConnectionController", () => {
|
||||
).rejects.toThrow("handshake failed");
|
||||
|
||||
expect(createListener).not.toHaveBeenCalled();
|
||||
expect(sock.ws.close).toHaveBeenCalledOnce();
|
||||
expect(sock.end).toHaveBeenCalledOnce();
|
||||
expect(sock.end).toHaveBeenCalledWith(expect.any(Error));
|
||||
expect(sock.ws.close).not.toHaveBeenCalled();
|
||||
expect(controller.socketRef.current).toBeNull();
|
||||
expect(controller.getActiveListener()).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to raw websocket close when Baileys end is unavailable", () => {
|
||||
const sock = { ws: { close: vi.fn() } };
|
||||
|
||||
closeWaSocket(sock);
|
||||
|
||||
expect(sock.ws.close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("lets createWaSocket own the auth barrier before opening a socket", async () => {
|
||||
const callOrder: string[] = [];
|
||||
createWaSocketMock.mockImplementationOnce(async () => {
|
||||
|
||||
@@ -131,8 +131,20 @@ function createLiveConnection(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export function closeWaSocket(sock: { ws?: { close?: () => void } } | null | undefined): void {
|
||||
export function closeWaSocket(
|
||||
sock:
|
||||
| {
|
||||
end?: (error: Error | undefined) => void;
|
||||
ws?: { close?: () => void };
|
||||
}
|
||||
| null
|
||||
| undefined,
|
||||
): void {
|
||||
try {
|
||||
if (typeof sock?.end === "function") {
|
||||
sock.end(new Error("OpenClaw WhatsApp socket close"));
|
||||
return;
|
||||
}
|
||||
sock?.ws?.close?.();
|
||||
} catch {
|
||||
// ignore best-effort shutdown failures
|
||||
@@ -140,7 +152,13 @@ export function closeWaSocket(sock: { ws?: { close?: () => void } } | null | und
|
||||
}
|
||||
|
||||
export function closeWaSocketSoon(
|
||||
sock: { ws?: { close?: () => void } } | null | undefined,
|
||||
sock:
|
||||
| {
|
||||
end?: (error: Error | undefined) => void;
|
||||
ws?: { close?: () => void };
|
||||
}
|
||||
| null
|
||||
| undefined,
|
||||
delayMs = 500,
|
||||
): void {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -7,6 +7,7 @@ type OffCapableEmitter = {
|
||||
};
|
||||
|
||||
type ClosableSocket = {
|
||||
end?: (error: Error | undefined) => void;
|
||||
ws?: {
|
||||
close?: () => void;
|
||||
};
|
||||
@@ -30,5 +31,9 @@ export function attachEmitterListener(
|
||||
}
|
||||
|
||||
export function closeInboundMonitorSocket(sock: ClosableSocket): void {
|
||||
if (typeof sock.end === "function") {
|
||||
sock.end(new Error("OpenClaw WhatsApp listener close"));
|
||||
return;
|
||||
}
|
||||
sock.ws?.close?.();
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ describe("web monitor inbox", () => {
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("detaches inbound listeners and closes the socket on close()", async () => {
|
||||
it("detaches inbound listeners and ends the socket on close()", async () => {
|
||||
const listener = await openMonitor(vi.fn());
|
||||
const sock = getSock();
|
||||
|
||||
@@ -135,7 +135,9 @@ describe("web monitor inbox", () => {
|
||||
|
||||
expect(sock.ev.listenerCount("messages.upsert")).toBe(0);
|
||||
expect(sock.ev.listenerCount("connection.update")).toBe(0);
|
||||
expect(sock.ws.close).toHaveBeenCalledTimes(1);
|
||||
expect(sock.end).toHaveBeenCalledTimes(1);
|
||||
expect(sock.end).toHaveBeenCalledWith(expect.any(Error));
|
||||
expect(sock.ws.close).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("logs inbound bodies through the inbound child logger", async () => {
|
||||
|
||||
@@ -34,6 +34,7 @@ export const upsertPairingRequestMock = pairingUpsertPairingRequestMock;
|
||||
|
||||
export type MockSock = {
|
||||
ev: EventEmitter;
|
||||
end: AnyMockFn;
|
||||
ws: { close: AnyMockFn };
|
||||
sendPresenceUpdate: AnyMockFn;
|
||||
sendMessage: AnyMockFn;
|
||||
@@ -107,6 +108,7 @@ function createMockSock(): MockSock {
|
||||
const ev = new EventEmitter();
|
||||
return {
|
||||
ev,
|
||||
end: vi.fn(),
|
||||
ws: { close: vi.fn() },
|
||||
sendPresenceUpdate: createResolvedMock(),
|
||||
sendMessage: createResolvedMock(),
|
||||
|
||||
Reference in New Issue
Block a user