一键导入
add-whatsapp
Add WhatsApp as a channel. Can replace other channels entirely or run alongside them. Uses QR code or pairing code for authentication.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Add WhatsApp as a channel. Can replace other channels entirely or run alongside them. Uses QR code or pairing code for authentication.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | add-whatsapp |
| description | Add WhatsApp as a channel. Can replace other channels entirely or run alongside them. Uses QR code or pairing code for authentication. |
This skill adds WhatsApp support to NanoClaw. It installs the WhatsApp channel code, dependencies, and guides through authentication, registration, and configuration.
Check if WhatsApp is already configured. If store/auth/ exists with credential files, skip to Phase 4 (Registration) or Phase 5 (Verify).
ls store/auth/creds.json 2>/dev/null && echo "WhatsApp auth exists" || echo "No WhatsApp auth"
Check whether the environment is headless (no display server):
[[ -z "$DISPLAY" && -z "$WAYLAND_DISPLAY" && "$OSTYPE" != darwin* ]] && echo "IS_HEADLESS=true" || echo "IS_HEADLESS=false"
Use AskUserQuestion to collect configuration. Adapt auth options based on environment:
If IS_HEADLESS=true AND not WSL → AskUserQuestion: How do you want to authenticate WhatsApp?
Otherwise (macOS, desktop Linux, or WSL) → AskUserQuestion: How do you want to authenticate WhatsApp?
If they chose pairing code:
AskUserQuestion: What is your phone number? (Digits only — country code followed by your 10-digit number, no + prefix, spaces, or dashes. Example: 14155551234 where 1 is the US country code and 4155551234 is the phone number.)
Check if src/channels/whatsapp.ts already exists. If it does, skip to Phase 3 (Authentication).
git remote -v
If whatsapp is missing, add it:
git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
git fetch whatsapp main
git merge whatsapp/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git merge --continue
}
This merges in:
src/channels/whatsapp.ts (WhatsAppChannel class with self-registration via registerChannel)src/channels/whatsapp.test.ts (41 unit tests)src/whatsapp-auth.ts (standalone WhatsApp authentication script)setup/whatsapp-auth.ts (WhatsApp auth setup step)import './whatsapp.js' appended to the channel barrel file src/channels/index.ts'whatsapp-auth' step added to setup/index.ts@whiskeysockets/baileys, qrcode, qrcode-terminal npm dependencies in package.jsonASSISTANT_HAS_OWN_NUMBER in .env.exampleIf the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
npm install
npm run build
npx vitest run src/channels/whatsapp.test.ts
All tests must pass and build must be clean before proceeding.
rm -rf store/auth/
For QR code in browser (recommended):
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
(Bash timeout: 150000ms)
Tell the user:
A browser window will open with a QR code.
- Open WhatsApp > Settings > Linked Devices > Link a Device
- Scan the QR code in the browser
- The page will show "Authenticated!" when done
For QR code in terminal:
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
Tell the user to run npm run auth in another terminal, then:
- Open WhatsApp > Settings > Linked Devices > Link a Device
- Scan the QR code displayed in the terminal
For pairing code:
Tell the user to have WhatsApp open on Settings > Linked Devices > Link a Device, ready to tap "Link with phone number instead" — the code expires in ~60 seconds and must be entered immediately.
Run the auth process in the background and poll store/pairing-code.txt for the code:
rm -f store/pairing-code.txt && npx tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
Then immediately poll for the code (do NOT wait for the background command to finish):
for i in $(seq 1 20); do [ -f store/pairing-code.txt ] && cat store/pairing-code.txt && break; sleep 1; done
Display the code to the user the moment it appears. Tell them:
Enter this code now — it expires in ~60 seconds.
- Open WhatsApp > Settings > Linked Devices > Link a Device
- Tap Link with phone number instead
- Enter the code immediately
After the user enters the code, poll for authentication to complete:
for i in $(seq 1 60); do grep -q 'AUTH_STATUS: authenticated' /tmp/wa-auth.log 2>/dev/null && echo "authenticated" && break; grep -q 'AUTH_STATUS: failed' /tmp/wa-auth.log 2>/dev/null && echo "failed" && break; sleep 2; done
If failed: qr_timeout → re-run. logged_out → delete store/auth/ and re-run. 515 → re-run. timeout → ask user, offer retry.
test -f store/auth/creds.json && echo "Authentication successful" || echo "Authentication failed"
Channels auto-enable when their credentials are present — WhatsApp activates when store/auth/creds.json exists.
Sync to container environment:
mkdir -p data/env && cp .env data/env/env
Get the bot's WhatsApp number: node -e "const c=require('./store/auth/creds.json');console.log(c.me.id.split(':')[0].split('@')[0])"
AskUserQuestion: Is this a shared phone number (personal WhatsApp) or a dedicated number (separate device)?
Remember the user's choice — if dedicated number, pass --dedicated-number to the register step below.
AskUserQuestion: What trigger word should activate the assistant?
AskUserQuestion: What should the assistant call itself?
AskUserQuestion: Where do you want to chat with the assistant?
Shared number options:
Dedicated number options:
Self-chat: JID = your phone number with @s.whatsapp.net. Extract from auth credentials:
node -e "const c=JSON.parse(require('fs').readFileSync('store/auth/creds.json','utf-8'));console.log(c.me?.id?.split(':')[0]+'@s.whatsapp.net')"
DM with bot: Ask for the bot's phone number. JID = NUMBER@s.whatsapp.net
Group (solo, existing): Run group sync and list available groups:
npx tsx setup/index.ts --step groups
npx tsx setup/index.ts --step groups --list
The output shows JID|GroupName pairs. Present candidates as AskUserQuestion (names only, not JIDs).
npx tsx setup/index.ts --step register \
--jid "<jid>" \
--name "<chat-name>" \
--trigger "@<trigger>" \
--folder "whatsapp_main" \
--channel whatsapp \
--assistant-name "<name>" \
--is-main \
--no-trigger-required \ # Only for main/self-chat
--dedicated-number # Only if user chose dedicated number
For additional groups (trigger-required):
npx tsx setup/index.ts --step register \
--jid "<group-jid>" \
--name "<group-name>" \
--trigger "@<trigger>" \
--folder "whatsapp_<group-name>" \
--channel whatsapp
npm run build
Restart the service:
# macOS (launchd)
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
# Linux (systemd)
systemctl --user restart nanoclaw
# Linux (nohup fallback)
bash start-nanoclaw.sh
Tell the user:
Send a message to your registered WhatsApp chat:
- For self-chat / main: Any message works
- For groups: Use the trigger word (e.g., "@Andy hello")
The assistant should respond within a few seconds.
tail -f logs/nanoclaw.log
QR codes expire after ~60 seconds. Re-run the auth command:
rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts
Codes expire in ~60 seconds. To retry:
rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts --pairing-code --phone <phone>
Enter the code immediately when it appears. Also ensure:
+ prefix (e.g., 14155551234 where 1 is country code, 4155551234 is the number)If pairing code keeps failing, switch to QR-browser auth instead:
rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
This happens when two instances connect with the same credentials. Ensure only one NanoClaw process is running:
pkill -f "node dist/index.js"
# Then restart
Check:
ls store/auth/creds.jsonsqlite3 store/messages.db "SELECT * FROM registered_groups WHERE jid LIKE '%whatsapp%' OR jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"launchctl list | grep nanoclaw (macOS) or systemctl --user status nanoclaw (Linux)tail -50 logs/nanoclaw.logRun group metadata sync:
npx tsx setup/index.ts --step groups
This fetches all group names from WhatsApp. Runs automatically every 24 hours.
If running npm run dev while the service is active:
# macOS:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
npm run dev
# When done testing:
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
# Linux:
# systemctl --user stop nanoclaw
# npm run dev
# systemctl --user start nanoclaw
To remove WhatsApp integration:
rm -rf store/auth/sqlite3 store/messages.db "DELETE FROM registered_groups WHERE jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"mkdir -p data/env && cp .env data/env/envnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw (macOS) or npm run build && systemctl --user restart nanoclaw (Linux)