name: sync-data-whatsapp description: > Fetch WhatsApp conversations from browser into a JSON file. No classification, no actions — just data. Requires browser. allowed-tools: Read, Write, Edit, Glob, Grep, Bash(python3:), mcp__claude-in-chrome__
Sync WhatsApp Data
WhatsApp ingestion via browser automation. No classification, no triage. Slow — requires browser. Cannot run in parallel with other browser-based syncs.
Output File
outputs/sync-data/whatsapp.json — full replacement on every run.
Schema
{
"version": 1,
"last_synced": "2026-02-11T09:05:00-08:00",
"conversation_count": 30,
"conversations": [
{
"name": "Alvin Calma",
"isGroup": false,
"lastActivity": "2026-02-11 08:30",
"unreadCount": 0,
"lastMessagePreview": "Can you approve the payment?",
"messages": [
{
"sender": "Alvin Calma",
"timestamp": "2026-02-11 08:30",
"content": "Can you approve the payment?"
}
]
}
]
}
Steps
- Get browser tab context via
tabs_context_mcp. - Use an existing WhatsApp tab or create a new one. Navigate to
web.whatsapp.com. - Wait for chat list to load. If QR code shown, ask Master to scan.
- Make sure the "All" filter tab is selected (not "Unread" or "Groups").
- Extract the chat list (first 30 conversations) using JavaScript:
const rows = document.querySelectorAll('[aria-label="Chat list"] [role="row"]');
// For each row, extract: span[title] for name, unread badge, timestamp, preview
For each conversation, capture list-level metadata:
name: contact or group nameisGroup: true if group chat indicator present or name contains "Community"/"Group"lastActivity: timestamp of last messageunreadCount: unread badge count (0 if none)lastMessagePreview: the preview text visible in the chat list
Open EVERY conversation and extract messages. For each conversation in the list:
a. Use
findtool to locate the chat by name, thenleft_clickon the returned ref. b. Wait 1 second for messages to load. c. Verify the chat opened by checking the header matches:const header = document.querySelector('#main header span[title]'); const openedChat = header ? header.getAttribute('title') : null;If
#maindoesn't exist or header doesn't match, the click failed — retry once. d. Extract the last 5-10 visible messages:const panel = document.querySelector('#main'); const rows = panel.querySelectorAll('[role="row"]'); // For each row: .copyable-text[data-pre-plain-text] for sender/timestamp // .copyable-text span.selectable-text span (use innerText) for message content // If text is empty but row contains img[data-plain-text-quote="image"] or // an <img> inside the message bubble, set content to "[image]"Each message:
{ sender, timestamp, content }If content is empty after extraction, check for image/video/audio elements in the row and use[image],[video], or[audio]as placeholder. If none detected, use[media].Only include conversations currently visible in the chat list. The output is a full replacement — if a conversation from a previous sync is no longer in the chat list, the user archived it. Do NOT carry it forward. Output reflects exactly what's in the chat list now.
Build the output object with
version,last_synced(current ISO timestamp with timezone offset),conversation_count, andconversations.Write to
outputs/sync-data/whatsapp.json(full replacement).Log: "Synced {M} WhatsApp conversations at {timestamp}"
Known Issues
- WhatsApp Web can show "open in another window" dialog — click "Use here" but this may cause browser extension disconnect. If disconnected, proceed with partial data.
- WhatsApp virtualizes the chat list — only visible rows have DOM content. Scroll to load more if needed.
- Message extraction via
span.selectable-text spanwithinnerTextworks;textContentonselectable-textalone may return empty. - Group chats show participant names in the header (e.g., "Alvin, Richard, You") not the group name. The group name is in the chat list, not the header. Verify by checking the chat list row is highlighted/selected rather than matching header text exactly.
Notes
- This skill does NOT classify or triage. That's
alfred-whatsapp-triage. - The file is always the latest snapshot. No historical accumulation.
- Requires exclusive browser access — cannot run in parallel with other browser-based skills.