mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(whatsapp): preserve live connections across reloads (#110762)
This commit is contained in:
@@ -12,7 +12,7 @@ vi.mock("openclaw/plugin-sdk/channel-runtime-context", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./runtime.js", () => ({
|
||||
getOptionalWhatsAppRuntime: () => ({ channel: runtimeContextMocks.channelRuntime }),
|
||||
getOptionalWhatsAppChannelRuntime: () => runtimeContextMocks.channelRuntime,
|
||||
}));
|
||||
|
||||
const WHATSAPP_ACTIVE_LISTENER_TEST_CFG = {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { WASocket } from "baileys";
|
||||
import { getChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
||||
import type { WhatsAppSelfIdentity } from "./identity.js";
|
||||
import type { ActiveWebListener } from "./inbound/types.js";
|
||||
import { getOptionalWhatsAppRuntime } from "./runtime.js";
|
||||
import { getOptionalWhatsAppChannelRuntime } from "./runtime.js";
|
||||
|
||||
export const WHATSAPP_CONNECTION_CONTROLLER_CAPABILITY = "connection-controller";
|
||||
export const WHATSAPP_CONNECTION_OWNER_PENDING_CAPABILITY = "connection-owner-pending";
|
||||
@@ -18,7 +18,7 @@ export function getWhatsAppConnectionController(
|
||||
accountId: string,
|
||||
): WhatsAppConnectionControllerHandle | null {
|
||||
const context = getChannelRuntimeContext({
|
||||
channelRuntime: getOptionalWhatsAppRuntime()?.channel,
|
||||
channelRuntime: getOptionalWhatsAppChannelRuntime() ?? undefined,
|
||||
channelId: "whatsapp",
|
||||
accountId,
|
||||
capability: WHATSAPP_CONNECTION_CONTROLLER_CAPABILITY,
|
||||
@@ -29,7 +29,7 @@ export function getWhatsAppConnectionController(
|
||||
export function hasPendingWhatsAppConnectionOwner(accountId: string): boolean {
|
||||
return Boolean(
|
||||
getChannelRuntimeContext({
|
||||
channelRuntime: getOptionalWhatsAppRuntime()?.channel,
|
||||
channelRuntime: getOptionalWhatsAppChannelRuntime() ?? undefined,
|
||||
channelId: "whatsapp",
|
||||
accountId,
|
||||
capability: WHATSAPP_CONNECTION_OWNER_PENDING_CAPABILITY,
|
||||
|
||||
@@ -51,7 +51,7 @@ vi.mock("openclaw/plugin-sdk/channel-runtime-context", () => {
|
||||
});
|
||||
|
||||
vi.mock("./runtime.js", () => ({
|
||||
getWhatsAppRuntime: () => ({ channel: runtimeContextMocks.channelRuntime }),
|
||||
getWhatsAppChannelRuntime: () => runtimeContextMocks.channelRuntime,
|
||||
}));
|
||||
|
||||
vi.mock("./connection-owner.js", () => ({
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { resolveComparableIdentity, type WhatsAppSelfIdentity } from "./identity.js";
|
||||
import type { ActiveWebListener, WebListenerCloseReason } from "./inbound/types.js";
|
||||
import { computeBackoff, sleepWithAbort, type ReconnectPolicy } from "./reconnect.js";
|
||||
import { getWhatsAppRuntime } from "./runtime.js";
|
||||
import { getWhatsAppChannelRuntime } from "./runtime.js";
|
||||
import {
|
||||
createWaSocket,
|
||||
formatError,
|
||||
@@ -692,7 +692,7 @@ export class WhatsAppConnectionController {
|
||||
// Publish only after the listener is ready. Lease tokens make disposal of this
|
||||
// controller's previous registration unable to remove a newer replacement.
|
||||
this.runtimeContextLease = registerChannelRuntimeContext({
|
||||
channelRuntime: getWhatsAppRuntime().channel,
|
||||
channelRuntime: getWhatsAppChannelRuntime(),
|
||||
channelId: "whatsapp",
|
||||
accountId: this.accountId,
|
||||
capability: WHATSAPP_CONNECTION_CONTROLLER_CAPABILITY,
|
||||
@@ -1011,7 +1011,7 @@ export class WhatsAppConnectionController {
|
||||
// Keep a pending marker separate from the ready controller capability. This
|
||||
// blocks a second socket without replacing a still-working controller on reload.
|
||||
this.pendingOwnerContextLease = registerChannelRuntimeContext({
|
||||
channelRuntime: getWhatsAppRuntime().channel,
|
||||
channelRuntime: getWhatsAppChannelRuntime(),
|
||||
channelId: "whatsapp",
|
||||
accountId: this.accountId,
|
||||
capability: WHATSAPP_CONNECTION_OWNER_PENDING_CAPABILITY,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
|
||||
// Whatsapp tests cover runtime injection across plugin registry reloads.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
getOptionalWhatsAppChannelRuntime,
|
||||
getWhatsAppChannelRuntime,
|
||||
getWhatsAppRuntime,
|
||||
setWhatsAppRuntime,
|
||||
} from "./runtime.js";
|
||||
|
||||
describe("WhatsApp runtime", () => {
|
||||
it("preserves the channel context owner when the injected runtime changes", () => {
|
||||
const originalChannelRuntime = getOptionalWhatsAppChannelRuntime();
|
||||
const first = { channel: { runtimeContexts: { id: "first" } } } as unknown as PluginRuntime;
|
||||
const second = { channel: { runtimeContexts: { id: "second" } } } as unknown as PluginRuntime;
|
||||
|
||||
setWhatsAppRuntime(first);
|
||||
setWhatsAppRuntime(second);
|
||||
|
||||
expect(getWhatsAppRuntime()).toBe(second);
|
||||
expect(getWhatsAppChannelRuntime()).toBe(originalChannelRuntime ?? first.channel);
|
||||
});
|
||||
});
|
||||
@@ -2,12 +2,34 @@
|
||||
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
|
||||
const {
|
||||
setRuntime: setWhatsAppRuntime,
|
||||
getRuntime: getWhatsAppRuntime,
|
||||
tryGetRuntime: getOptionalWhatsAppRuntime,
|
||||
} = createPluginRuntimeStore<PluginRuntime>({
|
||||
const runtimeStore = createPluginRuntimeStore<PluginRuntime>({
|
||||
pluginId: "whatsapp",
|
||||
errorMessage: "WhatsApp runtime not initialized",
|
||||
});
|
||||
export { getOptionalWhatsAppRuntime, getWhatsAppRuntime, setWhatsAppRuntime };
|
||||
const channelRuntimeStore = createPluginRuntimeStore<PluginRuntime["channel"]>({
|
||||
key: "plugin-runtime:whatsapp:channel-context-owner",
|
||||
errorMessage: "WhatsApp channel runtime not initialized",
|
||||
});
|
||||
|
||||
/** Injects current helpers while preserving the process-lifetime channel context owner. */
|
||||
function setWhatsAppRuntime(next: PluginRuntime): void {
|
||||
// Plugin registry reloads create fresh runtime objects. Live connection leases must remain
|
||||
// readable by outbound sends until their account task explicitly disposes them.
|
||||
if (!channelRuntimeStore.tryGetRuntime()) {
|
||||
channelRuntimeStore.setRuntime(next.channel);
|
||||
}
|
||||
runtimeStore.setRuntime(next);
|
||||
}
|
||||
|
||||
const getWhatsAppRuntime = runtimeStore.getRuntime;
|
||||
const getOptionalWhatsAppRuntime = runtimeStore.tryGetRuntime;
|
||||
const getWhatsAppChannelRuntime = channelRuntimeStore.getRuntime;
|
||||
const getOptionalWhatsAppChannelRuntime = channelRuntimeStore.tryGetRuntime;
|
||||
|
||||
export {
|
||||
getOptionalWhatsAppChannelRuntime,
|
||||
getOptionalWhatsAppRuntime,
|
||||
getWhatsAppChannelRuntime,
|
||||
getWhatsAppRuntime,
|
||||
setWhatsAppRuntime,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user