@lofa199419/waha-v2 2026.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +92 -0
- package/openclaw-waha-v2-2026.3.2.tgz +0 -0
- package/openclaw.plugin.json +10 -0
- package/package.json +35 -0
- package/skills/waha-v2/SKILL.md +183 -0
- package/src/accounts.ts +117 -0
- package/src/channel.ts +747 -0
- package/src/client.ts +390 -0
- package/src/config-schema.ts +167 -0
- package/src/gateway.ts +136 -0
- package/src/login.ts +57 -0
- package/src/outbound.ts +76 -0
- package/src/probe.ts +23 -0
- package/src/routes.ts +258 -0
- package/src/runtime.ts +54 -0
- package/src/types.ts +129 -0
- package/src/webhook.ts +264 -0
package/index.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
import { wahaV2Plugin } from "./src/channel.js";
|
|
4
|
+
import { setWahaV2Runtime } from "./src/runtime.js";
|
|
5
|
+
import { handleWahaV2WebhookRequest } from "./src/webhook.js";
|
|
6
|
+
import {
|
|
7
|
+
handleWahaV2QrRoute,
|
|
8
|
+
handleWahaV2RequestCodeRoute,
|
|
9
|
+
handleWahaV2StartRoute,
|
|
10
|
+
handleWahaV2StatusRoute,
|
|
11
|
+
handleWahaV2WaitRoute,
|
|
12
|
+
WAHA_V2_ROUTE_QR,
|
|
13
|
+
WAHA_V2_ROUTE_REQUEST_CODE,
|
|
14
|
+
WAHA_V2_ROUTE_START,
|
|
15
|
+
WAHA_V2_ROUTE_STATUS,
|
|
16
|
+
WAHA_V2_ROUTE_WAIT,
|
|
17
|
+
} from "./src/routes.js";
|
|
18
|
+
import { WAHA_V2_WEBHOOK_BASE } from "./src/types.js";
|
|
19
|
+
|
|
20
|
+
const plugin = {
|
|
21
|
+
id: "waha-v2",
|
|
22
|
+
name: "WAHA v2 (WhatsApp HTTP API)",
|
|
23
|
+
configSchema: emptyPluginConfigSchema(),
|
|
24
|
+
description:
|
|
25
|
+
"Independent WAHA WhatsApp HTTP API channel plugin. " +
|
|
26
|
+
"Uses waha-node for outbound; OpenClaw plugin-sdk for inbound, routing, and policies.",
|
|
27
|
+
|
|
28
|
+
register(api: OpenClawPluginApi) {
|
|
29
|
+
// Store the runtime and logger for use in the webhook handler, gateway, and routes.
|
|
30
|
+
setWahaV2Runtime(api.runtime, api.logger);
|
|
31
|
+
|
|
32
|
+
// Register the channel (config, gateway, outbound, status, etc.).
|
|
33
|
+
api.registerChannel({ plugin: wahaV2Plugin });
|
|
34
|
+
|
|
35
|
+
// Inbound webhook — matches both:
|
|
36
|
+
// /webhooks/waha-v2 (legacy, routes by session name in payload)
|
|
37
|
+
// /webhooks/waha-v2/{accountId} (preferred, unambiguous — accountId from path)
|
|
38
|
+
// Using registerHttpHandler so we can capture the dynamic {accountId} segment.
|
|
39
|
+
api.registerHttpHandler(async (req, res) => {
|
|
40
|
+
const url = req.url ?? "";
|
|
41
|
+
if (!url.startsWith(WAHA_V2_WEBHOOK_BASE)) return false;
|
|
42
|
+
// Extract optional accountId from the path suffix.
|
|
43
|
+
const suffix = url.slice(WAHA_V2_WEBHOOK_BASE.length).split("?")[0] ?? "";
|
|
44
|
+
const accountId = suffix.startsWith("/") ? suffix.slice(1) || undefined : undefined;
|
|
45
|
+
await handleWahaV2WebhookRequest(req, res, api.runtime.config.loadConfig(), accountId);
|
|
46
|
+
return true;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Session management API routes — used by UI, CLI, and setup flows.
|
|
50
|
+
api.registerHttpRoute({
|
|
51
|
+
path: WAHA_V2_ROUTE_STATUS,
|
|
52
|
+
handler: async (req, res) => {
|
|
53
|
+
await handleWahaV2StatusRoute(req, res, api.runtime.config.loadConfig());
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
api.registerHttpRoute({
|
|
58
|
+
path: WAHA_V2_ROUTE_START,
|
|
59
|
+
handler: async (req, res) => {
|
|
60
|
+
await handleWahaV2StartRoute(req, res, api.runtime.config.loadConfig());
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
api.registerHttpRoute({
|
|
65
|
+
path: WAHA_V2_ROUTE_QR,
|
|
66
|
+
handler: async (req, res) => {
|
|
67
|
+
await handleWahaV2QrRoute(req, res, api.runtime.config.loadConfig());
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
api.registerHttpRoute({
|
|
72
|
+
path: WAHA_V2_ROUTE_REQUEST_CODE,
|
|
73
|
+
handler: async (req, res) => {
|
|
74
|
+
await handleWahaV2RequestCodeRoute(req, res, api.runtime.config.loadConfig());
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
api.registerHttpRoute({
|
|
79
|
+
path: WAHA_V2_ROUTE_WAIT,
|
|
80
|
+
handler: async (req, res) => {
|
|
81
|
+
await handleWahaV2WaitRoute(req, res, api.runtime.config.loadConfig());
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
api.logger.info(
|
|
86
|
+
`waha-v2: registered channel + webhook ${WAHA_V2_WEBHOOK_BASE}/{accountId} + ` +
|
|
87
|
+
`session API routes (status, start, qr, request-code, wait)`,
|
|
88
|
+
);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default plugin;
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lofa199419/waha-v2",
|
|
3
|
+
"version": "2026.3.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "OpenClaw WAHA v2 channel plugin — independent WhatsApp HTTP API integration",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"axios": "1.6.0",
|
|
9
|
+
"waha-node": "1.0.0"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"openclaw": "workspace:*"
|
|
13
|
+
},
|
|
14
|
+
"openclaw": {
|
|
15
|
+
"extensions": [
|
|
16
|
+
"./index.ts"
|
|
17
|
+
],
|
|
18
|
+
"channel": {
|
|
19
|
+
"id": "waha-v2",
|
|
20
|
+
"label": "WAHA",
|
|
21
|
+
"selectionLabel": "WAHA v2 (plugin)",
|
|
22
|
+
"detailLabel": "WAHA v2",
|
|
23
|
+
"docsPath": "/channels/whatsapp",
|
|
24
|
+
"docsLabel": "whatsapp",
|
|
25
|
+
"blurb": "Independent WAHA WhatsApp HTTP API plugin — no Baileys dependency.",
|
|
26
|
+
"order": 13,
|
|
27
|
+
"systemImage": "qrcode"
|
|
28
|
+
},
|
|
29
|
+
"install": {
|
|
30
|
+
"npmSpec": "@lofa199419/waha-v2",
|
|
31
|
+
"localPath": "extensions/waha-v2",
|
|
32
|
+
"defaultChoice": "npm"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: waha-v2
|
|
3
|
+
description: "Full WhatsApp agent via WAHA HTTP API — send messages, read chat history, manage contacts, groups, status stories, and WA channels."
|
|
4
|
+
metadata: { "openclaw": { "emoji": "💬", "requires": { "config": ["channels.waha-v2"] } } }
|
|
5
|
+
allowed-tools: ["message"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# WAHA v2 — Full WhatsApp Agent
|
|
9
|
+
|
|
10
|
+
Use the `message` tool with `channel: "waha-v2"`. You have complete WhatsApp access: messaging, chat history, contacts, groups, status stories, and WhatsApp Channels.
|
|
11
|
+
|
|
12
|
+
## Musts
|
|
13
|
+
|
|
14
|
+
- Always set `channel: "waha-v2"`.
|
|
15
|
+
- Phone numbers as JIDs: `"<digits>@c.us"` for DMs, `"<digits>@g.us"` for groups.
|
|
16
|
+
- Strip all non-digits before appending `@c.us` (e.g. `+1 (555) 000-1234` → `"15550001234@c.us"`).
|
|
17
|
+
- Never send streaming or partial text to WhatsApp — only send the final, complete reply.
|
|
18
|
+
- Multi-account: pass `accountId` when the user has more than one WAHA instance configured.
|
|
19
|
+
|
|
20
|
+
## Session States
|
|
21
|
+
|
|
22
|
+
| State | Meaning |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `WORKING` / `CONNECTED` | Ready — go ahead |
|
|
25
|
+
| `STOPPED` / `FAILED` | Call `start-session` first |
|
|
26
|
+
| `SCAN_QR_CODE` / `PAIRING` | Waiting for user to scan QR or enter code |
|
|
27
|
+
| `CONNECTING` | Starting up — wait briefly |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Sending Messages
|
|
32
|
+
|
|
33
|
+
### Text
|
|
34
|
+
```json
|
|
35
|
+
{ "action": "send", "channel": "waha-v2", "to": "15550001234@c.us", "message": "Hello!" }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Media (image / video / audio / file)
|
|
39
|
+
```json
|
|
40
|
+
{ "action": "send", "channel": "waha-v2", "to": "15550001234@c.us", "media": "https://…/file.pdf", "message": "Your report" }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Poll
|
|
44
|
+
```json
|
|
45
|
+
{ "action": "poll", "channel": "waha-v2", "to": "15550001234@c.us", "pollQuestion": "Which day?", "pollOption": ["Mon","Wed","Fri"], "pollMulti": false }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Location
|
|
49
|
+
```json
|
|
50
|
+
{ "action": "send-location", "channel": "waha-v2", "to": "15550001234@c.us", "latitude": 37.77, "longitude": -122.41, "title": "SF Office" }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Contact card
|
|
54
|
+
```json
|
|
55
|
+
{ "action": "send-contact", "channel": "waha-v2", "to": "15550001234@c.us", "contacts": [{ "vcard": "BEGIN:VCARD\nVERSION:3.0\nFN:Alice\nTEL:+14155559999\nEND:VCARD" }] }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Forward a message
|
|
59
|
+
```json
|
|
60
|
+
{ "action": "forward-message", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Message Management
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{ "action": "react", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>", "emoji": "👍" }
|
|
69
|
+
{ "action": "send-seen", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
70
|
+
{ "action": "edit", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>", "text": "corrected" }
|
|
71
|
+
{ "action": "delete", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
72
|
+
{ "action": "pin", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
73
|
+
{ "action": "unpin", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
74
|
+
{ "action": "star-message", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
75
|
+
{ "action": "unstar-message", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Reading Chat History
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{ "action": "list-chats", "channel": "waha-v2", "limit": 50 }
|
|
84
|
+
{ "action": "get-chat-messages", "channel": "waha-v2", "to": "15550001234@c.us", "limit": 30 }
|
|
85
|
+
{ "action": "get-message", "channel": "waha-v2", "to": "15550001234@c.us", "messageId": "<id>" }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Chat Management
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{ "action": "archive-chat", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
92
|
+
{ "action": "unarchive-chat", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
93
|
+
{ "action": "delete-chat", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
94
|
+
{ "action": "mark-chat-unread", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Contacts
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{ "action": "list-contacts", "channel": "waha-v2", "limit": 100 }
|
|
103
|
+
{ "action": "get-contact", "channel": "waha-v2", "contactId": "15550001234@c.us" }
|
|
104
|
+
{ "action": "check-contact", "channel": "waha-v2", "phone": "15550001234" }
|
|
105
|
+
{ "action": "block-contact", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
106
|
+
{ "action": "unblock-contact", "channel": "waha-v2", "to": "15550001234@c.us" }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Groups
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{ "action": "list-groups", "channel": "waha-v2" }
|
|
115
|
+
{ "action": "get-group", "channel": "waha-v2", "groupId": "120363012345@g.us" }
|
|
116
|
+
{ "action": "create-group", "channel": "waha-v2", "subject": "Team Sync", "participants": ["15550001234@c.us"] }
|
|
117
|
+
{ "action": "get-group-participants", "channel": "waha-v2", "groupId": "120363012345@g.us" }
|
|
118
|
+
{ "action": "add-group-participants", "channel": "waha-v2", "groupId": "120363012345@g.us", "participants": ["15550001234@c.us"] }
|
|
119
|
+
{ "action": "remove-group-participants", "channel": "waha-v2", "groupId": "120363012345@g.us", "participants": ["15550001234@c.us"] }
|
|
120
|
+
{ "action": "promote-group-admin", "channel": "waha-v2", "groupId": "120363012345@g.us", "participants": ["15550001234@c.us"] }
|
|
121
|
+
{ "action": "demote-group-admin", "channel": "waha-v2", "groupId": "120363012345@g.us", "participants": ["15550001234@c.us"] }
|
|
122
|
+
{ "action": "renameGroup", "channel": "waha-v2", "groupId": "120363012345@g.us", "name": "New Name" }
|
|
123
|
+
{ "action": "update-group-description", "channel": "waha-v2", "groupId": "120363012345@g.us", "description": "Weekly standups" }
|
|
124
|
+
{ "action": "get-group-invite-code", "channel": "waha-v2", "groupId": "120363012345@g.us" }
|
|
125
|
+
{ "action": "revoke-group-invite-code", "channel": "waha-v2", "groupId": "120363012345@g.us" }
|
|
126
|
+
{ "action": "leaveGroup", "channel": "waha-v2", "groupId": "120363012345@g.us" }
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Status / Stories
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{ "action": "send-status", "channel": "waha-v2", "text": "Good morning! 🌅" }
|
|
135
|
+
{ "action": "delete-status", "channel": "waha-v2", "messageId": "<statusMessageId>" }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## WhatsApp Channels (Newsletters)
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{ "action": "list-wa-channels", "channel": "waha-v2" }
|
|
144
|
+
{ "action": "get-wa-channel", "channel": "waha-v2", "channelId": "<id>" }
|
|
145
|
+
{ "action": "create-wa-channel", "channel": "waha-v2", "name": "My Channel", "description": "Daily updates" }
|
|
146
|
+
{ "action": "get-wa-channel-messages", "channel": "waha-v2", "channelId": "<id>", "limit": 20 }
|
|
147
|
+
{ "action": "delete-wa-channel", "channel": "waha-v2", "channelId": "<id>" }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Session Management & Login Flow
|
|
153
|
+
|
|
154
|
+
### Full QR login sequence
|
|
155
|
+
1. `start-session` — if `alreadyConnected: true`, done.
|
|
156
|
+
2. `get-qr` — returns `{ data }` base64 PNG; show to user to scan in WhatsApp → Linked Devices.
|
|
157
|
+
3. User scans, then call `start-session` again to confirm `connected: true`.
|
|
158
|
+
|
|
159
|
+
### Pairing code (no phone camera)
|
|
160
|
+
1. `start-session`
|
|
161
|
+
2. `request-code` with `phoneNumber: "15550001234"` — returns 8-digit code.
|
|
162
|
+
3. User enters code in WhatsApp → Linked Devices → Link with phone number.
|
|
163
|
+
4. `start-session` to confirm.
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{ "action": "logout-session", "channel": "waha-v2" }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Multi-Account
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{ "action": "send", "channel": "waha-v2", "accountId": "secondary", "to": "15550001234@c.us", "message": "Hi" }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Each account gets its own webhook: `{webhookUrl}/{accountId}`.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## WhatsApp Markdown
|
|
182
|
+
|
|
183
|
+
`*bold*` `_italic_` `~strikethrough~` `` `mono` `` — no HTML, no tables.
|
package/src/accounts.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import {
|
|
3
|
+
WAHA_V2_CHANNEL_ID,
|
|
4
|
+
WAHA_V2_DEFAULT_ACCOUNT_ID,
|
|
5
|
+
type ResolvedWahaV2Account,
|
|
6
|
+
type WahaV2AccountConfig,
|
|
7
|
+
type WahaV2RootConfig,
|
|
8
|
+
} from "./types.js";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Config extraction
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export function readWahaV2Root(cfg: OpenClawConfig): WahaV2RootConfig {
|
|
15
|
+
const channels = (cfg as Record<string, unknown>).channels;
|
|
16
|
+
if (!channels || typeof channels !== "object" || Array.isArray(channels)) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
const root = (channels as Record<string, unknown>)[WAHA_V2_CHANNEL_ID];
|
|
20
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
return root as WahaV2RootConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Account list
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
export function listWahaV2AccountIds(cfg: OpenClawConfig): string[] {
|
|
31
|
+
const root = readWahaV2Root(cfg);
|
|
32
|
+
const ids = Object.keys(root.accounts ?? {}).filter(Boolean);
|
|
33
|
+
// If no named accounts are configured, expose a single implicit "default" account.
|
|
34
|
+
return ids.length > 0 ? ids.toSorted((a, b) => a.localeCompare(b)) : [WAHA_V2_DEFAULT_ACCOUNT_ID];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Account resolution — account-level fields override root-level
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
export function resolveWahaV2Account(
|
|
42
|
+
cfg: OpenClawConfig,
|
|
43
|
+
accountId?: string | null,
|
|
44
|
+
): ResolvedWahaV2Account {
|
|
45
|
+
const resolvedId = accountId?.trim() || WAHA_V2_DEFAULT_ACCOUNT_ID;
|
|
46
|
+
const root = readWahaV2Root(cfg);
|
|
47
|
+
// Merge: root provides defaults; per-account config overrides.
|
|
48
|
+
const account: WahaV2AccountConfig =
|
|
49
|
+
resolvedId === WAHA_V2_DEFAULT_ACCOUNT_ID
|
|
50
|
+
? root
|
|
51
|
+
: { ...root, ...(root.accounts?.[resolvedId] ?? {}) };
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
accountId: resolvedId,
|
|
55
|
+
name: account.name?.trim() || undefined,
|
|
56
|
+
baseUrl: account.baseUrl?.trim() || "http://localhost:3000",
|
|
57
|
+
apiKey: account.apiKey?.trim() || "",
|
|
58
|
+
session: account.session?.trim() || "default",
|
|
59
|
+
webhookUrl: account.webhookUrl?.trim() || undefined,
|
|
60
|
+
enabled: account.enabled !== false,
|
|
61
|
+
dmPolicy: account.dmPolicy,
|
|
62
|
+
allowFrom: account.allowFrom,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Find the account whose session name matches the given WAHA session string. */
|
|
67
|
+
export function resolveWahaV2AccountBySession(
|
|
68
|
+
cfg: OpenClawConfig,
|
|
69
|
+
session: string,
|
|
70
|
+
): ResolvedWahaV2Account | undefined {
|
|
71
|
+
for (const accountId of listWahaV2AccountIds(cfg)) {
|
|
72
|
+
const account = resolveWahaV2Account(cfg, accountId);
|
|
73
|
+
if (account.session === session.trim()) {
|
|
74
|
+
return account;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Config mutation helpers (used by ChannelConfigAdapter)
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
export function setWahaV2ChannelConfig(
|
|
85
|
+
cfg: OpenClawConfig,
|
|
86
|
+
patch: Partial<WahaV2RootConfig>,
|
|
87
|
+
): OpenClawConfig {
|
|
88
|
+
const base = cfg as Record<string, unknown>;
|
|
89
|
+
const channels = { ...((base.channels as Record<string, unknown>) ?? {}) };
|
|
90
|
+
const current = (channels[WAHA_V2_CHANNEL_ID] ?? {}) as Record<string, unknown>;
|
|
91
|
+
channels[WAHA_V2_CHANNEL_ID] = { ...current, ...patch };
|
|
92
|
+
return { ...base, channels } as OpenClawConfig;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function setWahaV2AccountEnabled(
|
|
96
|
+
cfg: OpenClawConfig,
|
|
97
|
+
accountId: string,
|
|
98
|
+
enabled: boolean,
|
|
99
|
+
): OpenClawConfig {
|
|
100
|
+
const root = readWahaV2Root(cfg);
|
|
101
|
+
if (accountId === WAHA_V2_DEFAULT_ACCOUNT_ID && !root.accounts?.[accountId]) {
|
|
102
|
+
// Single-account mode — set enabled on root.
|
|
103
|
+
return setWahaV2ChannelConfig(cfg, { ...root, enabled });
|
|
104
|
+
}
|
|
105
|
+
const accounts = { ...(root.accounts ?? {}), [accountId]: { ...(root.accounts?.[accountId] ?? {}), enabled } };
|
|
106
|
+
return setWahaV2ChannelConfig(cfg, { ...root, accounts });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function deleteWahaV2Account(cfg: OpenClawConfig, accountId: string): OpenClawConfig {
|
|
110
|
+
const root = readWahaV2Root(cfg);
|
|
111
|
+
if (!root.accounts?.[accountId]) {
|
|
112
|
+
return cfg;
|
|
113
|
+
}
|
|
114
|
+
const accounts = { ...root.accounts };
|
|
115
|
+
delete accounts[accountId];
|
|
116
|
+
return setWahaV2ChannelConfig(cfg, { ...root, accounts: Object.keys(accounts).length > 0 ? accounts : undefined });
|
|
117
|
+
}
|