No Description

softword 8229b35a98 feat(keys)!: use function keys for commands (F3/F4/F10) 2 days ago
openwiki 484f1487ed feat: add initial configuration template for OpenAI API and system prompts 2 days ago
src 8229b35a98 feat(keys)!: use function keys for commands (F3/F4/F10) 2 days ago
.gitignore 299473ef60 Add initial session database configuration file 3 days ago
AGENTS.md 484f1487ed feat: add initial configuration template for OpenAI API and system prompts 2 days ago
Cargo.lock ec7ea4eb1d feat(web): add optional web control panel enabled by web_port 2 days ago
Cargo.toml ec7ea4eb1d feat(web): add optional web control panel enabled by web_port 2 days ago
README.md 8229b35a98 feat(keys)!: use function keys for commands (F3/F4/F10) 2 days ago
config.toml.global.template 5b03485cf4 docs: document all config keys, key bindings and web panel 2 days ago

README.md

wh

Terminal WhatsApp client (ratatui) with per-chat and global AI autoreply via an OpenAI-compatible API.

Build

cargo build --release

Cross-compile: Linux x86_64 static (musl)

Built from Windows with cargo-zigbuild. Zig provides the C cross-compiler and linker for the bundled C code (SQLite, aws-lc-sys, ring).

One-time prerequisites:

rustup target add x86_64-unknown-linux-musl
winget install -e --id zig.zig      # or: pip install ziglang
cargo install --locked cargo-zigbuild

Build:

cargo zigbuild --release --target x86_64-unknown-linux-musl

Output: target/x86_64-unknown-linux-musl/release/wh

Verify on Linux:

file wh   # ELF 64-bit x86-64, statically linked
chmod +x wh && ./wh

Data directory

On Linux, config and databases live in ~/.config/wh/ (config.toml, session.db, history.db); on Windows in %APPDATA%\wh.

Copy session.db from Windows to reuse the linked session instead of scanning the QR again.

Configuration

Settings live in config.toml in the data directory. Start from config.toml.global.template: copy it there and fill openai_api_key and system_prompt. A missing file means defaults; without openai_api_key and system_prompt the app runs as a plain WhatsApp client.

Key Default Description
openai_api_key unset API key of the OpenAI-compatible endpoint (required for AI)
openai_base_url https://ollama.com/v1 endpoint base URL
openai_model deepseek-v4.1-flash model for autoreplies
openai_vision_model unset vision model for images; unset disables image understanding
system_prompt unset responder identity (required for AI)
ai_log false append every AI request to ai_requests.log
cooldown_seconds 30 min seconds between autoreplies in a private chat / to a group message addressed to you (0 = off)
group_cooldown_seconds 300 same, group messages not addressed to you (0 = off)
catch_up_seconds 7200 after a restart, answer the last unanswered private message up to this age (0 = off)
ai_retries 2 retries of a failed AI request, 2s, 4s, ... backoff (0 = off; HTTP 4xx except 429 never retried)
web_port unset port of the web control panel (1-65535); unset disables it

Keys

Key Action
Tab focus list/input
Enter open chat / send
F2 chats/contacts
typing (list focused) filter names; Backspace edits, Esc clears
Canc/Delete twice delete chat locally (history only, not on WhatsApp)
F3 AI autoreply for the open chat
F4 AI autoreply for all chats (persistent; footer shortcut turns green when on)
F10 quit

Only function keys are used for commands, so they work inside GNU screen, tmux and over SSH.

Web control panel

  • Enabled by web_port in config.toml (e.g. web_port = 8080); open http://<host>:<port>/.
  • Listens on 0.0.0.0 without authentication: use it only on a trusted network (e.g. a VPN).
  • The page (HTML/CSS/JS) is embedded in the binary: no extra files to deploy.
  • Features: "AI tutte" switch (same as F4), per-chat AI toggle grid (private chats and groups only), "Chiudi bot" button to quit. The page refreshes every 5 seconds; changes show up in the TUI too.
  • API (for scripts): GET /api/state (JSON {global_autoreply, chats:[{jid,name,autoreply}], status}); POST /api/global; POST /api/chat with the jid as plain-text body; POST /api/quit. Every POST requires the header X-Wh-Request: 1 (otherwise 403), which blocks cross-site requests from other pages.

Example:

curl -X POST -H "X-Wh-Request: 1" http://host:8080/api/global