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.
|
- 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: 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.
|
- 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).
|
- 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`.
|
- 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.
|
- 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).
|
- 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 `"*"`.
|
- Open: requires `whatsapp.allowFrom` to include `"*"`.
|
||||||
- Self messages are always allowed; “self-chat mode” still requires `whatsapp.allowFrom` to include your own number.
|
- 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`).
|
- **Group policy**: `whatsapp.groupPolicy` controls group handling (`open|disabled|allowlist`).
|
||||||
- `allowlist` uses `whatsapp.groupAllowFrom` (fallback: explicit `whatsapp.allowFrom`).
|
- `allowlist` uses `whatsapp.groupAllowFrom` (fallback: explicit `whatsapp.allowFrom`).
|
||||||
- **Self-chat mode**: avoids auto read receipts and ignores mention JIDs.
|
- **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(
|
function setWhatsAppSelfChatMode(
|
||||||
cfg: ClawdbotConfig,
|
cfg: ClawdbotConfig,
|
||||||
selfChatMode?: boolean,
|
selfChatMode?: boolean,
|
||||||
@@ -403,6 +416,7 @@ async function promptWhatsAppAllowFrom(
|
|||||||
const existingAllowFrom = cfg.whatsapp?.allowFrom ?? [];
|
const existingAllowFrom = cfg.whatsapp?.allowFrom ?? [];
|
||||||
const existingLabel =
|
const existingLabel =
|
||||||
existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
|
existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
|
||||||
|
const existingResponsePrefix = cfg.messages?.responsePrefix;
|
||||||
|
|
||||||
await prompter.note(
|
await prompter.note(
|
||||||
[
|
[
|
||||||
@@ -418,6 +432,56 @@ async function promptWhatsAppAllowFrom(
|
|||||||
"WhatsApp DM access",
|
"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({
|
const policy = (await prompter.select({
|
||||||
message: "WhatsApp DM policy",
|
message: "WhatsApp DM policy",
|
||||||
options: [
|
options: [
|
||||||
@@ -428,7 +492,8 @@ async function promptWhatsAppAllowFrom(
|
|||||||
],
|
],
|
||||||
})) as DmPolicy;
|
})) as DmPolicy;
|
||||||
|
|
||||||
let next = setWhatsAppDmPolicy(cfg, policy);
|
let next = setWhatsAppSelfChatMode(cfg, false);
|
||||||
|
next = setWhatsAppDmPolicy(next, policy);
|
||||||
if (policy === "open") {
|
if (policy === "open") {
|
||||||
next = setWhatsAppAllowFrom(next, ["*"]);
|
next = setWhatsAppAllowFrom(next, ["*"]);
|
||||||
}
|
}
|
||||||
@@ -490,12 +555,7 @@ async function promptWhatsAppAllowFrom(
|
|||||||
next = setWhatsAppAllowFrom(next, unique);
|
next = setWhatsAppAllowFrom(next, unique);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selfChatMode = await prompter.confirm({
|
return next;
|
||||||
message:
|
|
||||||
"Same-phone setup? (using your personal WhatsApp number for Clawdbot)",
|
|
||||||
initialValue: next.whatsapp?.selfChatMode ?? false,
|
|
||||||
});
|
|
||||||
return setWhatsAppSelfChatMode(next, selfChatMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetupProvidersOptions = {
|
type SetupProvidersOptions = {
|
||||||
|
|||||||
Reference in New Issue
Block a user