somratpro commited on
Commit
43f221d
·
1 Parent(s): a73191b

refactor: simplify WhatsApp guardian logic by removing redundant channel status checks and handling session invalidation via error catching

Browse files
Files changed (1) hide show
  1. wa-guardian.js +91 -77
wa-guardian.js CHANGED
@@ -9,8 +9,10 @@
9
 
10
  const fs = require("fs");
11
  const path = require("path");
12
- const { WebSocket } = require('/home/node/.openclaw/openclaw-app/node_modules/ws');
13
- const { randomUUID } = require('node:crypto');
 
 
14
 
15
  const GATEWAY_URL = "ws://127.0.0.1:7860";
16
  const GATEWAY_TOKEN = process.env.GATEWAY_TOKEN || "huggingclaw";
@@ -31,7 +33,8 @@ let last515At = 0;
31
  function extractErrorMessage(msg) {
32
  if (!msg || typeof msg !== "object") return "Unknown error";
33
  if (typeof msg.error === "string") return msg.error;
34
- if (msg.error && typeof msg.error.message === "string") return msg.error.message;
 
35
  if (typeof msg.message === "string") return msg.message;
36
  return "Unknown error";
37
  }
@@ -40,9 +43,13 @@ function writeResetMarker() {
40
  try {
41
  fs.mkdirSync(path.dirname(RESET_MARKER_PATH), { recursive: true });
42
  fs.writeFileSync(RESET_MARKER_PATH, "reset\n");
43
- console.log(`[guardian] Created backup reset marker at ${RESET_MARKER_PATH}`);
 
 
44
  } catch (error) {
45
- console.log(`[guardian] Failed to write backup reset marker: ${error.message}`);
 
 
46
  }
47
  }
48
 
@@ -55,25 +62,32 @@ async function createConnection() {
55
  const msg = JSON.parse(data.toString());
56
 
57
  if (msg.type === "event" && msg.event === "connect.challenge") {
58
- ws.send(JSON.stringify({
59
- type: "req",
60
- id: randomUUID(),
61
- method: "connect",
62
- params: {
63
- minProtocol: 3,
64
- maxProtocol: 3,
65
- client: {
66
- id: "gateway-client",
67
- version: "1.0.0",
68
- platform: "linux",
69
- mode: "backend",
 
 
 
 
 
 
 
 
 
 
 
70
  },
71
- caps: [],
72
- auth: { token: GATEWAY_TOKEN },
73
- role: "operator",
74
- scopes: ["operator.read", "operator.write", "operator.admin", "operator.pairing"],
75
- },
76
- }));
77
  return;
78
  }
79
 
@@ -90,8 +104,15 @@ async function createConnection() {
90
  }
91
  });
92
 
93
- ws.on("error", (e) => { if (!resolved) reject(e); });
94
- setTimeout(() => { if (!resolved) { ws.close(); reject(new Error("Timeout")); } }, 10000);
 
 
 
 
 
 
 
95
  });
96
  }
97
 
@@ -111,7 +132,10 @@ async function callRpc(ws, method, params) {
111
  };
112
  ws.on("message", handler);
113
  ws.send(JSON.stringify({ type: "req", id, method, params }));
114
- setTimeout(() => { ws.removeListener("message", handler); reject(new Error("RPC Timeout")); }, WAIT_TIMEOUT + 5000);
 
 
 
115
  });
116
  }
117
 
@@ -121,58 +145,18 @@ async function checkStatus() {
121
  let ws;
122
  try {
123
  ws = await createConnection();
124
-
125
- // Check if WhatsApp channel exists and its status
126
- const statusRes = await callRpc(ws, "channels.status", {});
127
- const channels = (statusRes.payload || statusRes.result)?.channels || {};
128
- const wa = channels.whatsapp;
129
-
130
- if (!wa) {
131
- ws.close();
132
- return;
133
- }
134
-
135
- const lastError = String(wa.lastError || "").toLowerCase();
136
- const recentlySaw515 = Date.now() - last515At < POST_515_NO_LOGOUT_MS;
137
- const needsLogout = wa.linked && !wa.connected && !recentlySaw515 &&
138
- (
139
- lastError.includes("401") ||
140
- lastError.includes("unauthorized") ||
141
- lastError.includes("logged out") ||
142
- lastError.includes("440") ||
143
- lastError.includes("conflict")
144
- );
145
-
146
- if (needsLogout) {
147
- console.log("[guardian] Clearing invalid WhatsApp session so a fresh QR can be used...");
148
- try {
149
- await callRpc(ws, "channels.logout", { channel: "whatsapp" });
150
- writeResetMarker();
151
- hasShownWaitMessage = false;
152
- console.log("[guardian] Logged out invalid WhatsApp session.");
153
- } catch (error) {
154
- console.log(`[guardian] Failed to log out invalid session: ${error.message}`);
155
- }
156
- ws.close();
157
- return;
158
- }
159
-
160
- // If connected, we are good
161
- if (wa.connected) {
162
- hasShownWaitMessage = false;
163
- ws.close();
164
- return;
165
- }
166
-
167
- // If "Ready to pair", we wait for the scan
168
  isWaiting = true;
169
  if (!hasShownWaitMessage) {
170
- console.log("\n[guardian] 📱 WhatsApp pairing in progress. Please scan the QR code in the Control UI.");
 
 
171
  hasShownWaitMessage = true;
172
  }
173
 
174
  console.log("[guardian] Waiting for pairing completion...");
175
- const waitRes = await callRpc(ws, "web.login.wait", { timeoutMs: WAIT_TIMEOUT });
 
 
176
  const result = waitRes.payload || waitRes.result;
177
  const message = result?.message || "";
178
  const linkedAfter515 = !result?.connected && message.includes("515");
@@ -185,21 +169,49 @@ async function checkStatus() {
185
  hasShownWaitMessage = false;
186
 
187
  if (linkedAfter515) {
188
- console.log("[guardian] 515 after scan: credentials saved, reloading config to start WhatsApp...");
 
 
189
  } else {
190
  console.log("[guardian] ✅ Pairing completed! Reloading config...");
191
  }
192
 
193
  const getRes = await callRpc(ws, "config.get", {});
194
  if (getRes.payload?.raw && getRes.payload?.hash) {
195
- await callRpc(ws, "config.apply", { raw: getRes.payload.raw, baseHash: getRes.payload.hash });
 
 
 
196
  console.log("[guardian] Configuration re-applied.");
197
  }
198
- } else if (!message.includes("No active") && !message.includes("Still waiting")) {
 
 
 
199
  console.log(`[guardian] Wait result: ${message}`);
200
  }
201
-
202
  } catch (e) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  // Normal timeout or gateway starting up; retry on the next interval.
204
  } finally {
205
  isWaiting = false;
@@ -207,6 +219,8 @@ async function checkStatus() {
207
  }
208
  }
209
 
210
- console.log("[guardian] ⚔️ WhatsApp Guardian active. Monitoring pairing status...");
 
 
211
  setInterval(checkStatus, CHECK_INTERVAL);
212
  setTimeout(checkStatus, 15000);
 
9
 
10
  const fs = require("fs");
11
  const path = require("path");
12
+ const {
13
+ WebSocket,
14
+ } = require("/home/node/.openclaw/openclaw-app/node_modules/ws");
15
+ const { randomUUID } = require("node:crypto");
16
 
17
  const GATEWAY_URL = "ws://127.0.0.1:7860";
18
  const GATEWAY_TOKEN = process.env.GATEWAY_TOKEN || "huggingclaw";
 
33
  function extractErrorMessage(msg) {
34
  if (!msg || typeof msg !== "object") return "Unknown error";
35
  if (typeof msg.error === "string") return msg.error;
36
+ if (msg.error && typeof msg.error.message === "string")
37
+ return msg.error.message;
38
  if (typeof msg.message === "string") return msg.message;
39
  return "Unknown error";
40
  }
 
43
  try {
44
  fs.mkdirSync(path.dirname(RESET_MARKER_PATH), { recursive: true });
45
  fs.writeFileSync(RESET_MARKER_PATH, "reset\n");
46
+ console.log(
47
+ `[guardian] Created backup reset marker at ${RESET_MARKER_PATH}`,
48
+ );
49
  } catch (error) {
50
+ console.log(
51
+ `[guardian] Failed to write backup reset marker: ${error.message}`,
52
+ );
53
  }
54
  }
55
 
 
62
  const msg = JSON.parse(data.toString());
63
 
64
  if (msg.type === "event" && msg.event === "connect.challenge") {
65
+ ws.send(
66
+ JSON.stringify({
67
+ type: "req",
68
+ id: randomUUID(),
69
+ method: "connect",
70
+ params: {
71
+ minProtocol: 3,
72
+ maxProtocol: 3,
73
+ client: {
74
+ id: "gateway-client",
75
+ version: "1.0.0",
76
+ platform: "linux",
77
+ mode: "backend",
78
+ },
79
+ caps: [],
80
+ auth: { token: GATEWAY_TOKEN },
81
+ role: "operator",
82
+ scopes: [
83
+ "operator.read",
84
+ "operator.write",
85
+ "operator.admin",
86
+ "operator.pairing",
87
+ ],
88
  },
89
+ }),
90
+ );
 
 
 
 
91
  return;
92
  }
93
 
 
104
  }
105
  });
106
 
107
+ ws.on("error", (e) => {
108
+ if (!resolved) reject(e);
109
+ });
110
+ setTimeout(() => {
111
+ if (!resolved) {
112
+ ws.close();
113
+ reject(new Error("Timeout"));
114
+ }
115
+ }, 10000);
116
  });
117
  }
118
 
 
132
  };
133
  ws.on("message", handler);
134
  ws.send(JSON.stringify({ type: "req", id, method, params }));
135
+ setTimeout(() => {
136
+ ws.removeListener("message", handler);
137
+ reject(new Error("RPC Timeout"));
138
+ }, WAIT_TIMEOUT + 5000);
139
  });
140
  }
141
 
 
145
  let ws;
146
  try {
147
  ws = await createConnection();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  isWaiting = true;
149
  if (!hasShownWaitMessage) {
150
+ console.log(
151
+ "\n[guardian] 📱 WhatsApp pairing in progress. Please scan the QR code in the Control UI.",
152
+ );
153
  hasShownWaitMessage = true;
154
  }
155
 
156
  console.log("[guardian] Waiting for pairing completion...");
157
+ const waitRes = await callRpc(ws, "web.login.wait", {
158
+ timeoutMs: WAIT_TIMEOUT,
159
+ });
160
  const result = waitRes.payload || waitRes.result;
161
  const message = result?.message || "";
162
  const linkedAfter515 = !result?.connected && message.includes("515");
 
169
  hasShownWaitMessage = false;
170
 
171
  if (linkedAfter515) {
172
+ console.log(
173
+ "[guardian] 515 after scan: credentials saved, reloading config to start WhatsApp...",
174
+ );
175
  } else {
176
  console.log("[guardian] ✅ Pairing completed! Reloading config...");
177
  }
178
 
179
  const getRes = await callRpc(ws, "config.get", {});
180
  if (getRes.payload?.raw && getRes.payload?.hash) {
181
+ await callRpc(ws, "config.apply", {
182
+ raw: getRes.payload.raw,
183
+ baseHash: getRes.payload.hash,
184
+ });
185
  console.log("[guardian] Configuration re-applied.");
186
  }
187
+ } else if (
188
+ !message.includes("No active") &&
189
+ !message.includes("Still waiting")
190
+ ) {
191
  console.log(`[guardian] Wait result: ${message}`);
192
  }
 
193
  } catch (e) {
194
+ const message = e && e.message ? e.message : "";
195
+ if (
196
+ /401|unauthorized|logged out|440|conflict/i.test(message) &&
197
+ Date.now() - last515At >= POST_515_NO_LOGOUT_MS
198
+ ) {
199
+ console.log(
200
+ "[guardian] Clearing invalid WhatsApp session so a fresh QR can be used...",
201
+ );
202
+ try {
203
+ if (ws) {
204
+ await callRpc(ws, "channels.logout", { channel: "whatsapp" });
205
+ writeResetMarker();
206
+ hasShownWaitMessage = false;
207
+ console.log("[guardian] Logged out invalid WhatsApp session.");
208
+ }
209
+ } catch (error) {
210
+ console.log(
211
+ `[guardian] Failed to log out invalid session: ${error.message}`,
212
+ );
213
+ }
214
+ }
215
  // Normal timeout or gateway starting up; retry on the next interval.
216
  } finally {
217
  isWaiting = false;
 
219
  }
220
  }
221
 
222
+ console.log(
223
+ "[guardian] ⚔️ WhatsApp Guardian active. Monitoring pairing status...",
224
+ );
225
  setInterval(checkStatus, CHECK_INTERVAL);
226
  setTimeout(checkStatus, 15000);