Skip to content

Latest commit

 

History

History
199 lines (138 loc) · 6.56 KB

File metadata and controls

199 lines (138 loc) · 6.56 KB

wha-cli (WhatsApp TUI)

A terminal user interface (TUI) for WhatsApp built with Go, Bubble Tea, and the whatsmeow library.

Note: This project stores WhatsApp session data and message history locally in SQLite databases (whatsapp.db, messages.db). Treat these files as sensitive.

Overview

wha-cli lets you read and send WhatsApp messages from your terminal in a keyboard-driven UI. It connects to WhatsApp using whatsmeow (companion-device style login via QR code), renders a two-pane chat/messages experience with Bubble Tea + Lip Gloss, and persists chat/message data locally so the UI can load quickly on subsequent runs.

What problem does it solve?

If you live in the terminal, switching to a phone/desktop app to reply to messages is disruptive. wha-cli provides a lightweight, distraction-minimizing interface to:

  • See your available chats (contacts + selected groups)
  • Open a chat and read recent history
  • Send replies without leaving the terminal

Features

Core

  • QR-based login on first run (session persisted in whatsapp.db)
  • Chat list from WhatsApp contact store + joined groups
  • Local caching of chats + messages in messages.db (SQLite)
  • Loads recent history per chat (currently last 50 messages)
  • Unread badge counter (tracked in local DB/UI)
  • Automatic reconnect with backoff when the WhatsApp connection drops

Messaging

  • Receive and display incoming messages in real time
  • Send text messages from the input panel
  • Basic handling for non-text message types (rendered as placeholders like 📷 [Image], 🎥 [Video], etc.)
  • Message deduplication at the database layer (INSERT OR IGNORE on (id, chat_jid))

TUI / UX

  • Bubble Tea alt-screen UI with three focus areas: chat list, messages, input
  • Keyboard navigation and message scrolling
  • Timestamps per message and “older/newer messages” scroll hints

Tech stack

  • Language: Go
  • TUI: github.com/charmbracelet/bubbletea, github.com/charmbracelet/lipgloss
  • WhatsApp client: go.mau.fi/whatsmeow
  • Storage: SQLite (github.com/mattn/go-sqlite3)
  • Config: .env loader (github.com/joho/godotenv)
  • QR rendering: github.com/mdp/qrterminal/v3

Architecture

Folder structure

cmd/
  main.go                # Entry point: config + DB + WhatsApp + TUI wiring
internal/
  client/                # whatsmeow wrapper, event conversion, chat filtering
  config/                # reads contacts.env (ALLOWED_GROUP_*)
  store/                 # SQLite schema + chat/message queries
  sync/                  # background event loop + reconnect + Bubble Tea commands
  types/                 # shared types and Bubble Tea messages
  ui/                    # Bubble Tea model/update/view + rendering

High-level flow

startup
  -> load config (contacts.env)
  -> open messages.db (migrate schema)
  -> open whatsapp.db (whatsmeow device/session store)
  -> connect (QR on first login)
  -> load chats:
       messages.db first
       else WhatsApp contacts + joined groups (filtered) -> persist to messages.db
  -> start Bubble Tea program (TUI)
  -> start background sync loop:
       WhatsApp events -> persist -> send Bubble Tea messages -> update UI

Installation & setup

Prerequisites

  • Go (see go.mod for the configured version)
  • A working C toolchain for CGO (required by github.com/mattn/go-sqlite3)
    • macOS: Xcode Command Line Tools
    • Linux: gcc/clang + standard build tools
    • Windows: MSVC build tools

Build

git clone "https://github.com/Dharshan2208/wha-cli"
cd wha-cli
go mod download
go build -o wha-cli ./cmd

First run (QR login)

./wha-cli

On the first run, the app prints a QR code to the terminal. Scan it with WhatsApp (Linked Devices) to authorize.

Local files created

By default, databases are created in the current working directory:

  • whatsapp.db: WhatsApp session/device store (managed by whatsmeow)
  • messages.db: app cache for chats/messages (managed by this project)

These files are ignored by .gitignore and should not be committed.

Usage

Keyboard controls

  • Tab: cycle focus (Chats → Messages → Input)
  • / :
    • In Chats: move selection (with scrolling)
    • In Messages: scroll older/newer messages
  • Enter:
    • In Chats: open selected chat (loads history, clears unread)
    • In Input: send typed message
  • Esc: return to chat list
  • Backspace: delete character (Input focus)
  • q: quit (only when not typing)
  • Ctrl+C: quit

Configuration

Chat filtering (contacts.env)

On startup, the app loads contacts.env from the repository root (via godotenv.Load("contacts.env")).

Supported variables:

  • ALLOWED_GROUP_JIDS: comma-separated group JIDs (e.g. 1203...@g.us)
  • ALLOWED_GROUP_NAMES: comma-separated group names (case-insensitive)

Example:

ALLOWED_GROUP_JIDS=1212234567890@g.us,12036333028324722@g.us
ALLOWED_GROUP_NAMES=E204-Roommates,RandomInfo

Notes / assumptions based on current code:

  • Groups are shown only if they match an allowed JID or name.
  • Contacts are shown only if WhatsApp has a display name for them (push name or full name). On fresh sessions, some contacts may be hidden until WhatsApp metadata is populated.

Database / storage

messages.db contains two tables:

  • chats: per-chat metadata (name, last message preview, unread count)
  • messages: per-chat messages (id + timestamp + sender + text + from-me flag)

Key details:

  • messages(chat_jid) has a foreign key to chats(jid).
  • Messages are deduplicated with a composite primary key: (id, chat_jid).
  • An index supports fetching recent history efficiently: idx_messages_chat_jid (chat_jid, timestamp DESC).

Contributing

Contributions are welcome.

Known issues / limitations

Based on the current implementation:

  • Message history loading is fixed to the most recent 50 messages per chat (no pagination UI yet, even though the store supports GetMessagesBefore).
  • Sending is text-only (no media sending).
  • Group listing is opt-in via ALLOWED_GROUP_*; if you don’t configure it, groups won’t appear.
  • Contact filtering may hide contacts without populated names in the WhatsApp store on first login.
  • Groups name is still not coming
  • Groups not in .env file is still appearing in the chatlist

Roadmap

Ideas that fit the current design:

  • Paginated history loading (“load older messages” using GetMessagesBefore)
  • Search/filter in chat list
  • Better contact/group metadata sync (names, avatars)
  • Media preview improvements and/or media download

License

This project is licensed under the AGPL-3.0 License — see the LICENSE file for details.