fix: refine whatsapp personal phone onboarding
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
- macOS: prevent gateway launchd startup race where the app could kill a just-started gateway; avoid unnecessary `bootout` and ensure the job is enabled at login. Fixes #306. Thanks @gupsammy for PR #387.
|
||||
- Pairing: generate DM pairing codes with CSPRNG, expire pending codes after 1 hour, and avoid re-sending codes for already pending requests.
|
||||
- Pairing: lock + atomically write pairing stores with 0600 perms and stop logging pairing codes in provider logs.
|
||||
- WhatsApp: add self-phone mode to suppress pairing replies for outbound DMs and prompt during onboarding.
|
||||
- WhatsApp: add self-phone mode (no pairing replies for outbound DMs) and onboarding prompt for personal vs separate numbers (auto allowlist + response prefix for personal).
|
||||
- Discord: include all inbound attachments in `MediaPaths`/`MediaUrls` (back-compat `MediaPath`/`MediaUrl` still first).
|
||||
- Sandbox: add `agent.sandbox.workspaceAccess` (`none`/`ro`/`rw`) to control agent workspace visibility inside the container; `ro` hard-disables `write`/`edit`.
|
||||
- Routing: allow per-agent sandbox overrides (including `workspaceAccess` and `sandbox.tools`) plus per-agent tool policies in multi-agent configs. Thanks @pasogott for PR #380.
|
||||
|
||||
@@ -61,7 +61,25 @@ WhatsApp requires a real mobile number for verification. VoIP and virtual number
|
||||
- Pairing: unknown senders get a pairing code (approve via `clawdbot pairing approve --provider whatsapp <code>`; codes expire after 1 hour).
|
||||
- Open: requires `whatsapp.allowFrom` to include `"*"`.
|
||||
- Self messages are always allowed; “self-chat mode” still requires `whatsapp.allowFrom` to include your own number.
|
||||
- **Same-phone mode**: set `whatsapp.selfChatMode=true` when Clawdbot runs on your personal WhatsApp number. This suppresses pairing replies for outbound DMs.
|
||||
|
||||
### Same-phone mode (personal number)
|
||||
If you run Clawdbot on your **personal WhatsApp number**, set:
|
||||
|
||||
```json
|
||||
{
|
||||
"whatsapp": {
|
||||
"selfChatMode": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Behavior:
|
||||
- Suppresses pairing replies for **outbound DMs** (prevents spamming contacts).
|
||||
- Inbound unknown senders still follow `whatsapp.dmPolicy`.
|
||||
|
||||
Recommended for personal numbers:
|
||||
- Set `whatsapp.dmPolicy="allowlist"` and add your number to `whatsapp.allowFrom`.
|
||||
- Set `messages.responsePrefix` (for example, `[clawdbot]`) so replies are clearly labeled.
|
||||
- **Group policy**: `whatsapp.groupPolicy` controls group handling (`open|disabled|allowlist`).
|
||||
- `allowlist` uses `whatsapp.groupAllowFrom` (fallback: explicit `whatsapp.allowFrom`).
|
||||
- **Self-chat mode**: avoids auto read receipts and ignores mention JIDs.
|
||||
|
||||
@@ -202,6 +202,19 @@ function setWhatsAppAllowFrom(cfg: ClawdbotConfig, allowFrom?: string[]) {
|
||||
};
|
||||
}
|
||||
|
||||
function setMessagesResponsePrefix(
|
||||
cfg: ClawdbotConfig,
|
||||
responsePrefix?: string,
|
||||
) {
|
||||
return {
|
||||
...cfg,
|
||||
messages: {
|
||||
...cfg.messages,
|
||||
responsePrefix,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function setWhatsAppSelfChatMode(
|
||||
cfg: ClawdbotConfig,
|
||||
selfChatMode?: boolean,
|
||||
@@ -403,6 +416,7 @@ async function promptWhatsAppAllowFrom(
|
||||
const existingAllowFrom = cfg.whatsapp?.allowFrom ?? [];
|
||||
const existingLabel =
|
||||
existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
|
||||
const existingResponsePrefix = cfg.messages?.responsePrefix;
|
||||
|
||||
await prompter.note(
|
||||
[
|
||||
@@ -418,6 +432,56 @@ async function promptWhatsAppAllowFrom(
|
||||
"WhatsApp DM access",
|
||||
);
|
||||
|
||||
const phoneMode = (await prompter.select({
|
||||
message: "WhatsApp phone setup",
|
||||
options: [
|
||||
{ value: "personal", label: "This is my personal phone number" },
|
||||
{ value: "separate", label: "Separate phone just for Clawdbot" },
|
||||
],
|
||||
})) as "personal" | "separate";
|
||||
|
||||
if (phoneMode === "personal") {
|
||||
const entry = await prompter.text({
|
||||
message: "Your WhatsApp number (E.164)",
|
||||
placeholder: "+15555550123",
|
||||
initialValue: existingAllowFrom[0],
|
||||
validate: (value) => {
|
||||
const raw = String(value ?? "").trim();
|
||||
if (!raw) return "Required";
|
||||
const normalized = normalizeE164(raw);
|
||||
if (!normalized) return `Invalid number: ${raw}`;
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
const normalized = normalizeE164(String(entry).trim());
|
||||
const merged = [
|
||||
...existingAllowFrom
|
||||
.filter((item) => item !== "*")
|
||||
.map((item) => normalizeE164(item))
|
||||
.filter(Boolean),
|
||||
normalized,
|
||||
];
|
||||
const unique = [...new Set(merged.filter(Boolean))];
|
||||
let next = setWhatsAppSelfChatMode(cfg, true);
|
||||
next = setWhatsAppDmPolicy(next, "allowlist");
|
||||
next = setWhatsAppAllowFrom(next, unique);
|
||||
if (existingResponsePrefix === undefined) {
|
||||
next = setMessagesResponsePrefix(next, "[clawdbot]");
|
||||
}
|
||||
await prompter.note(
|
||||
[
|
||||
"Personal phone mode enabled.",
|
||||
"- dmPolicy set to allowlist (pairing skipped)",
|
||||
`- allowFrom includes ${normalized}`,
|
||||
existingResponsePrefix === undefined
|
||||
? "- responsePrefix set to [clawdbot]"
|
||||
: "- responsePrefix left unchanged",
|
||||
].join("\n"),
|
||||
"WhatsApp personal phone",
|
||||
);
|
||||
return next;
|
||||
}
|
||||
|
||||
const policy = (await prompter.select({
|
||||
message: "WhatsApp DM policy",
|
||||
options: [
|
||||
@@ -428,7 +492,8 @@ async function promptWhatsAppAllowFrom(
|
||||
],
|
||||
})) as DmPolicy;
|
||||
|
||||
let next = setWhatsAppDmPolicy(cfg, policy);
|
||||
let next = setWhatsAppSelfChatMode(cfg, false);
|
||||
next = setWhatsAppDmPolicy(next, policy);
|
||||
if (policy === "open") {
|
||||
next = setWhatsAppAllowFrom(next, ["*"]);
|
||||
}
|
||||
@@ -490,12 +555,7 @@ async function promptWhatsAppAllowFrom(
|
||||
next = setWhatsAppAllowFrom(next, unique);
|
||||
}
|
||||
|
||||
const selfChatMode = await prompter.confirm({
|
||||
message:
|
||||
"Same-phone setup? (using your personal WhatsApp number for Clawdbot)",
|
||||
initialValue: next.whatsapp?.selfChatMode ?? false,
|
||||
});
|
||||
return setWhatsAppSelfChatMode(next, selfChatMode);
|
||||
return next;
|
||||
}
|
||||
|
||||
type SetupProvidersOptions = {
|
||||
|
||||
Reference in New Issue
Block a user