Skip to content

Commit 66743b8

Browse files
committed
refactor(whatsapp): centralize web monitor state
1 parent cfd9242 commit 66743b8

10 files changed

Lines changed: 324 additions & 112 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { createConnectedChannelStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
2+
import type { WebChannelHealthState, WebChannelStatus } from "./types.js";
3+
4+
function cloneStatus(status: WebChannelStatus): WebChannelStatus {
5+
return {
6+
...status,
7+
lastDisconnect: status.lastDisconnect ? { ...status.lastDisconnect } : null,
8+
};
9+
}
10+
11+
function isTerminalHealthState(healthState: WebChannelHealthState | undefined): boolean {
12+
return healthState === "conflict" || healthState === "logged-out" || healthState === "stopped";
13+
}
14+
15+
export function createWebChannelStatusController(statusSink?: (status: WebChannelStatus) => void) {
16+
const status: WebChannelStatus = {
17+
running: true,
18+
connected: false,
19+
reconnectAttempts: 0,
20+
lastConnectedAt: null,
21+
lastDisconnect: null,
22+
lastInboundAt: null,
23+
lastMessageAt: null,
24+
lastEventAt: null,
25+
lastError: null,
26+
healthState: "starting",
27+
};
28+
29+
const emit = () => {
30+
statusSink?.(cloneStatus(status));
31+
};
32+
33+
return {
34+
emit,
35+
snapshot: () => status,
36+
noteConnected(at = Date.now()) {
37+
Object.assign(status, createConnectedChannelStatusPatch(at));
38+
status.lastError = null;
39+
status.healthState = "healthy";
40+
emit();
41+
},
42+
noteInbound(at = Date.now()) {
43+
status.lastInboundAt = at;
44+
status.lastMessageAt = at;
45+
status.lastEventAt = at;
46+
if (status.connected) {
47+
status.healthState = "healthy";
48+
}
49+
emit();
50+
},
51+
noteWatchdogStale(at = Date.now()) {
52+
status.lastEventAt = at;
53+
if (status.connected) {
54+
status.healthState = "stale";
55+
}
56+
emit();
57+
},
58+
noteReconnectAttempts(reconnectAttempts: number) {
59+
status.reconnectAttempts = reconnectAttempts;
60+
emit();
61+
},
62+
noteClose(params: {
63+
at?: number;
64+
statusCode?: number;
65+
loggedOut?: boolean;
66+
error?: string;
67+
reconnectAttempts: number;
68+
healthState: WebChannelHealthState;
69+
}) {
70+
const at = params.at ?? Date.now();
71+
status.connected = false;
72+
status.lastEventAt = at;
73+
status.lastDisconnect = {
74+
at,
75+
status: params.statusCode,
76+
error: params.error,
77+
loggedOut: Boolean(params.loggedOut),
78+
};
79+
status.lastError = params.error ?? null;
80+
status.reconnectAttempts = params.reconnectAttempts;
81+
status.healthState = params.healthState;
82+
emit();
83+
},
84+
markStopped(at = Date.now()) {
85+
status.running = false;
86+
status.connected = false;
87+
status.lastEventAt = at;
88+
if (!isTerminalHealthState(status.healthState)) {
89+
status.healthState = "stopped";
90+
}
91+
emit();
92+
},
93+
};
94+
}

0 commit comments

Comments
 (0)