[BREAKGLASS] WhatsApp CLI https://wacli.sh/
Go to file
SimDamDev 70f2afd470 fix: enable UseRetryMessageStore so retry-receipts are handled
Symptom
-------
Recipients whose Signal session hasn't been freshly bootstrapped — most
commonly the sender's own other linked devices (e.g. WhatsApp Desktop
linked to the same account, or other linked devices of the recipient)
— receive messages sent by wacli but cannot decrypt them, and see
"Waiting for this message" indefinitely in WhatsApp.

Cause
-----
When a recipient device fails to decrypt, it sends a retry-receipt.
whatsmeow's retry handler looks up the original plaintext to re-encrypt
it with fresh session state. The lookup path is:

    in-memory recentMessages cache
      → GetMessageForRetry() callback (unset in wacli)
      → retry_message_store DB table (only if UseRetryMessageStore=true)

The `wacli send …` subcommand sends the message, then exits — the
in-memory cache dies with the process. `wacli sync` is a separate
process that never saw the outgoing message, so its in-memory cache
is empty too. Neither can answer the retry receipt, and whatsmeow
logs:

    Failed to handle retry receipt for <jid>/<id> from <jid>:<n>: couldn't find message <id>

Fix
---
Setting `c.client.UseRetryMessageStore = true` tells whatsmeow to
persist outgoing messages to its `retry_message_store` SQLite table
during Send(). A later wacli process (e.g. `wacli sync` restarting
after a `wacli send …` subcommand) then finds the plaintext via the
shared store, re-encrypts, and the recipient's "Waiting for this
message" resolves to the real content.

Reproduction
------------
1. Authenticate wacli via `wacli auth` (linked device pairing)
2. Send: `wacli send text --to <your own number> --message "hi"` so
   the target account has multiple linked devices
3. Observe: at least one recipient device shows "Waiting for this
   message. Check your phone." with the "retry receipt" log line
   above in the sender's journal.
4. With this patch applied: message is decrypted correctly on every
   recipient device, including ones that weren't online at send time.

Scope
-----
Six-line change. No new dependencies. whatsmeow handles the DB schema
(creates the `retry_message_store` table on first use); no migration
needed on existing installs.
2026-04-21 01:24:28 +01:00
.github docs: add maintainers section and CODEOWNERS (#163) 2026-04-15 11:28:37 -07:00
cmd/wacli fix: bound reconnect duration to prevent indefinite lock holding (#113) 2026-04-14 14:45:18 -07:00
docs ci: add multi-os release workflow 2026-01-23 00:49:33 +00:00
internal fix: enable UseRetryMessageStore so retry-receipts are handled 2026-04-21 01:24:28 +01:00
.gitignore Add pnpm scripts and run via CI 2025-12-12 14:59:48 +00:00
.goreleaser-linux-windows.yaml ci: add multi-os release workflow 2026-01-23 00:49:33 +00:00
.goreleaser.yaml ci: add multi-os release workflow 2026-01-23 00:49:33 +00:00
CHANGELOG.md chore: update dependencies 2026-04-21 01:22:05 +01:00
go.mod chore: update dependencies 2026-04-21 01:22:05 +01:00
go.sum chore: update dependencies 2026-04-21 01:22:05 +01:00
LICENSE chore: update copyright year to 2026 2026-01-01 14:23:00 +01:00
package.json fix: preserve CGO_CFLAGS in build script (#8) (thanks @ramarivera) 2026-01-23 01:50:03 +00:00
README.md docs: add maintainers section and CODEOWNERS (#163) 2026-04-15 11:28:37 -07:00

🗃️ wacli — WhatsApp CLI: sync, search, send.

WhatsApp CLI built on top of whatsmeow, focused on:

  • Best-effort local sync of message history + continuous capture
  • Fast offline search
  • Sending messages
  • Contact + group management

This is a third-party tool that uses the WhatsApp Web protocol via whatsmeow and is not affiliated with WhatsApp.

Status

Core implementation is in place. See docs/spec.md for the full design notes.

Recent updates (0.2.0)

  • Messages: search/list includes display text for reactions, replies, and media types.
  • Send: wacli send file --filename to override the display name.
  • Auth: optional WACLI_DEVICE_LABEL / WACLI_DEVICE_PLATFORM env overrides.

Install / Build

Choose one of the following options.
If you install via Homebrew, you can skip the local build step.

Option A: Install via Homebrew (tap)

  • brew install steipete/tap/wacli

Option B: Build locally

  • go build -tags sqlite_fts5 -o ./dist/wacli ./cmd/wacli

Run (local build only):

  • ./dist/wacli --help

Quick start

Default store directory is ~/.wacli (override with --store DIR).

# 1) Authenticate (shows QR), then bootstrap sync
pnpm wacli auth
# or: ./dist/wacli auth (after pnpm build)

# 2) Keep syncing (never shows QR; requires prior auth)
pnpm wacli sync --follow

# Diagnostics
pnpm wacli doctor

# Search messages
pnpm wacli messages search "meeting"

# Backfill older messages for a chat (best-effort; requires your primary device online)
pnpm wacli history backfill --chat 1234567890@s.whatsapp.net --requests 10 --count 50

# Download media for a message (after syncing)
./wacli media download --chat 1234567890@s.whatsapp.net --id <message-id>

# Send a message
pnpm wacli send text --to 1234567890 --message "hello"

# Send a file
./wacli send file --to 1234567890 --file ./pic.jpg --caption "hi"
# Or override display name
./wacli send file --to 1234567890 --file /tmp/abc123 --filename report.pdf

# List groups and manage participants
pnpm wacli groups list
pnpm wacli groups rename --jid 123456789@g.us --name "New name"

Prior Art / Credit

This project is heavily inspired by (and learns from) the excellent whatsapp-cli by Vicente Reig:

High-level UX

  • wacli auth: interactive login (shows QR code), then immediately performs initial data sync.
  • wacli sync: non-interactive sync loop (never shows QR; errors if not authenticated).
  • Output is human-readable by default; pass --json for machine-readable output.

Storage

Defaults to ~/.wacli (override with --store DIR).

Environment overrides

  • WACLI_DEVICE_LABEL: set the linked device label (shown in WhatsApp).
  • WACLI_DEVICE_PLATFORM: override the linked device platform (defaults to CHROME if unset or invalid).

Backfilling older history

wacli sync stores whatever WhatsApp Web sends opportunistically. To try to fetch older messages, use on-demand history sync requests to your primary device (your phone).

Important notes:

  • This is best-effort: WhatsApp may not return full history.
  • Your primary device must be online.
  • Requests are per chat (DM or group). wacli uses the oldest locally stored message in that chat as the anchor.
  • Recommended --count is 50 per request.

Backfill one chat

pnpm wacli history backfill --chat 1234567890@s.whatsapp.net --requests 10 --count 50

Backfill all chats (script)

This loops through chats already known in your local DB:

pnpm -s wacli -- --json chats list --limit 100000 \
  | jq -r '.[].JID' \
  | while read -r jid; do
      pnpm -s wacli -- history backfill --chat "$jid" --requests 3 --count 50
    done

License

See LICENSE.

Maintainers