Python CLI that monitors a WhatsApp user's device activity using RTT-based side-channel analysis. Sends desktop notifications when the target is detected as online, typing, recording, on a call, or reading your chat. Logs all activity to CSV and can generate visual reports.
Based on the Careless Whisper research paper (USENIX Security '24).
- Connects to WhatsApp Web multi-device protocol via neonize (Python bindings for whatsmeow)
- Sends silent "delete" probes (revoke requests for non-existent message IDs) to the target every ~1–3 seconds
- Measures Round-Trip Time (RTT) from the delivery acknowledgment
- Auto-calibrates a threshold from the running median RTT (or accepts a manual value via
--threshold) - Classifies device state per-device:
- Online — moving average of last 3 RTTs is below threshold
- Standby — moving average is above threshold
- OFFLINE — no ACK received within 10 seconds
- Subscribes to WhatsApp presence events to detect composing, recording, and calls
- Monitors read receipts to detect when the target opens your chat
- Logs every state change to a CSV file for later analysis
- Renders a live RTT chart in the terminal using Unicode block characters
- Python >= 3.10
libmagicsystem library (required by neonize)
# macOS
brew install libmagic
# Debian/Ubuntu
sudo apt-get install libmagic1# Clone and set up
git clone https://github.com/milouk/whatsapp-tracker.git
cd whatsapp-tracker
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtOptional — for rendering profile pictures in the terminal (half-block mode):
pip install Pillow# First run — shows a QR code in the terminal, scan it with WhatsApp on your phone
python3 -m wa_tracker --phone 3069XXXXXXXX
# Subsequent runs reuse the saved session (no QR needed)
python3 -m wa_tracker --phone 3069XXXXXXXX
# Set a manual RTT threshold (ms) instead of auto-calibration
python3 -m wa_tracker --phone 3069XXXXXXXX --threshold 1000
# Only get notified on typing, recording, and calls (skip RTT-based online detection)
python3 -m wa_tracker --phone 3069XXXXXXXX --presence-only
# Silent operation (no sounds, no desktop notifications — terminal output only)
python3 -m wa_tracker --phone 3069XXXXXXXX --no-notifyAfter collecting data, generate a 3-panel activity report from the CSV log:
# Open graph in an interactive window
python3 -m wa_tracker --graph 3069XXXXXXXX
# Save to a file
python3 -m wa_tracker --graph 3069XXXXXXXX --graph-output report.pngThe graph includes:
- Activity Timeline — color-coded bar (green = Online, yellow = Standby, red = Offline) with typing/recording markers
- Hourly Distribution — minutes spent online per hour of day
- Session Durations — bar chart of each online session length with average line
Data is read from wa_activity_<phone>.csv which is created automatically during monitoring.
| Flag | Default | Description |
|---|---|---|
--phone |
(required for monitoring) | Target phone number with country code (e.g. 3069XXXXXXXX) |
--threshold |
auto (median × 0.9) | RTT threshold in ms — below = Online, above = Standby |
--method |
delete |
Probe method: delete (completely silent) or reaction (low visibility) |
--db |
wa_tracker.sqlite3 |
Path to the session database file |
--debug |
off | Print verbose debug output (event codes, probe IDs, RTT per-ACK) |
--no-sound |
off | Disable notification sounds (still shows desktop notifications) |
--no-notify |
off | Disable all desktop notifications and sounds |
--presence-only |
off | Only notify on typing, recording, calls, and read receipts — skip RTT-based online detection |
--mask |
off | Mask phone number in all output (e.g. 3069XXXXXXXX) — useful for screenshots and recordings |
--graph PHONE |
— | Generate an activity graph from CSV and exit (does not start monitoring) |
--graph-output FILE |
— | Save graph to file instead of opening an interactive window |
On macOS: native Notification Center alerts + system sound (Ping.aiff).
On Linux/Windows: terminal bell (\a) + printed message.
Notification triggers:
- Target device goes online (RTT drops below threshold)
- Target starts typing
- Target starts recording audio
- Target starts a voice or video call
- Target opens your chat (read receipt detected)
Cross-type deduplication ensures you aren't spammed — e.g. an "online" notification clears when the target goes to standby, so you'll be notified again next time they come online.
The monitor redraws a status box in-place showing real-time metrics, per-device state, and an RTT sparkline chart:
╔════════════════════════════════════════════════════════════════╗
║ 🟢 Active — 14:32:15 ║
╠════════════════════════════════════════════════════════════════╣
║ Target: 3069XXXXXXXX ║
║ Median: 412ms ║
║ Threshold: 371ms (auto) ║
║ Activity: Typing ║
║ ──────────────────────────────────────────────────────────────║
║ 🟢 Phone RTT: 234ms EMA: 287ms Active ║
║ 🟡 Companion #1 RTT: 890ms EMA: 756ms Idle ║
║ ──────────────────────────────────────────────────────────────║
║ 480│┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ ║
║ │ ║
║ 371│┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄▃▃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ ║
║ │ ▅▆▇█▆▅▃▂▁ ║
║ 200│▁▂▃▄▅▆▇█ ║
║ └───────────────────────────────────────────────────── ║
╚════════════════════════════════════════════════════════════════╝
- Header — overall state icon + label + timestamp
- Metrics — target number, running median, auto/manual threshold, current activity
- Per-device rows — each linked device (Phone, Companion #1, etc.) with its own RTT, EMA, and state
- RTT chart — auto-scaled sparkline with dashed threshold line; green bars = below threshold, yellow = above
On first connect the monitor also fetches and displays the target's profile info (name, about, business name, profile picture, linked devices).
| Method | Flag | Visibility | How |
|---|---|---|---|
| Delete | --method delete |
Completely silent | Sends a "revoke" for a non-existent message ID. Target sees nothing. |
| Reaction | --method reaction |
Low visibility | Sends a reaction to a non-existent message. May briefly flash on the target's device. |
The delete method is the default and recommended — it produces zero visible artifacts on the target's device.
# Build the image
docker build -t wa-tracker .
# First run — interactive for QR code scanning, persist session via a named volume
docker run -it -v wa_tracker_data:/app/data wa-tracker --phone 3069XXXXXXXX
# Subsequent runs — detached, session is already saved in the volume
docker run -d -v wa_tracker_data:/app/data wa-tracker --phone 3069XXXXXXXX
# With additional flags
docker run -d -v wa_tracker_data:/app/data wa-tracker \
--phone 3069XXXXXXXX --threshold 1000 --presence-only
# Follow live output
docker logs -f <container_id>Note: Desktop notifications are not available inside Docker. The container still prints all status updates to stdout — use
docker logs -fto follow along.
During monitoring, all state transitions are appended to wa_activity_<phone>.csv:
timestamp,state,presence_event
2025-02-13T14:32:15.123,Online,
2025-02-13T14:32:18.456,Online,composing
2025-02-13T14:35:02.789,Standby,
2025-02-13T14:40:11.012,OFFLINE,| Column | Description |
|---|---|
timestamp |
ISO 8601 with milliseconds |
state |
Online, Standby, or OFFLINE (RTT-based) |
presence_event |
composing, recording, on_call, reading, or empty |
The file is created automatically on first run and appended to on subsequent runs. Use --graph to visualize it.
whatsapp-tracker/
├── wa_tracker/ # Python package
│ ├── __init__.py # Package docstring
│ ├── __main__.py # Entry point (python -m wa_tracker)
│ ├── cli.py # Argument parser
│ ├── main.py # Client setup, event wiring, banner
│ ├── monitor.py # Core WAMonitor (probes, state, events, calls)
│ ├── constants.py # Timing, thresholds, protocol enums
│ ├── models.py # DeviceState dataclass
│ ├── notify.py # OS notifications and sound
│ ├── logger.py # CSV activity logger
│ ├── graph.py # Matplotlib activity report (3-panel)
│ ├── chart.py # Live terminal RTT chart (Unicode blocks)
│ └── rendering.py # Profile picture rendering (iTerm2 / half-blocks)
├── requirements.txt # Python dependencies
├── Dockerfile # Container image definition
├── .gitignore
└── .dockerignore
Auto-generated files (gitignored):
| File | Description |
|---|---|
wa_tracker.sqlite3 |
WhatsApp session database — delete to re-scan QR |
wa_activity_*.csv |
Per-target activity logs |