# WhatsHooked A service that connects to WhatsApp via the whatsmeow API and forwards messages to registered webhooks. Enables two-way communication by allowing webhooks to respond with messages to be sent through WhatsApp. ![1.00](./assets/image/whatshooked.jpg) [TODO LIST](TODO.md) - Things I still need to do [Rules when using AI](AI_USE.md) ## Phase 1 Features - **Multi-Account Support**: Connect to multiple WhatsApp accounts simultaneously - **Webhook Integration**: Register multiple webhooks to receive WhatsApp messages - **Two-Way Communication**: Webhooks can respond with messages to send back to WhatsApp - **Instance/Config Level Hooks**: Global hooks that receive all messages from all accounts - **Media Support**: Send and receive images, videos, and documents - **CLI Management**: Command-line tool for managing accounts and hooks - **Structured Logging**: JSON-based logging with configurable log levels - **Authentication**: HTTP Basic Auth and API key authentication for server endpoints ## Architecture The project uses an event-driven architecture with the following packages: - **internal/config**: Configuration management and persistence - **internal/logging**: Structured logging using Go's slog package - **internal/events**: Event bus for publish/subscribe messaging between components - **internal/whatsapp**: WhatsApp client management using whatsmeow - **internal/hooks**: Webhook management and message forwarding - **cmd/server**: Main server application - **cmd/cli**: Command-line interface for management ### Event-Driven Architecture The system uses a central event bus to decouple components: 1. **WhatsApp Events** → Event Bus → Hook Manager - Connection/disconnection events - Message received events - Message sent/failed events 2. **Hook Events** → Event Bus → WhatsApp Manager - Hook triggered events - Hook success/failure events - Webhook responses trigger message sends This architecture enables: - Loose coupling between WhatsApp and webhooks - Easy addition of new event subscribers - Centralized event logging and monitoring - Two-way communication through event responses - Context propagation for cancellation and timeout handling - Proper request lifecycle management across components ## Installation ### Build from source ```bash make build ``` Or manually: ```bash mkdir -p bin go build -o bin/whatshook-server ./cmd/server go build -o bin/whatshook-cli ./cmd/cli ``` ## Configuration Create a `config.json` file based on the example: ```bash cp config.example.json config.json ``` Edit the configuration file to add your WhatsApp accounts and webhooks: ```json { "server": { "host": "localhost", "port": 8080, "default_country_code": "27" }, "whatsapp": [ { "id": "account1", "phone_number": "+1234567890", "session_path": "./sessions/account1" } ], "hooks": [ { "id": "hook1", "name": "My Webhook", "url": "https://example.com/webhook", "method": "POST", "headers": { "Authorization": "Bearer token" }, "active": true, "description": "Webhook description" } ], "log_level": "info" } ``` ### Configuration Options **Server Configuration:** - `host`: Server hostname (default: "localhost") - `port`: Server port (default: 8080) - `default_country_code`: Default country code for phone number formatting (e.g., "27" for South Africa, "1" for US/Canada) - `username`: Username for HTTP Basic Authentication (optional) - `password`: Password for HTTP Basic Authentication (optional) - `auth_key`: API key for x-api-key header or Authorization Bearer token authentication (optional) **WhatsApp Account Configuration:** - `id`: Unique identifier for this account - `phone_number`: Phone number with country code - `session_path`: Path to store session data **Hook Configuration:** - `id`: Unique identifier for this hook - `name`: Human-readable name - `url`: Webhook URL to call - `method`: HTTP method (usually "POST") - `headers`: Optional HTTP headers - `active`: Whether this hook is enabled - `description`: Optional description ### Server Authentication The server supports two authentication methods to protect API endpoints: #### 1. HTTP Basic Authentication Set both `username` and `password` in the server configuration: ```json { "server": { "host": "localhost", "port": 8080, "username": "admin", "password": "secure_password" } } ``` Clients must provide credentials in the Authorization header: ```bash curl -u admin:secure_password http://localhost:8080/api/hooks ``` #### 2. API Key Authentication Set `auth_key` in the server configuration: ```json { "server": { "host": "localhost", "port": 8080, "auth_key": "your-secret-api-key" } } ``` Clients can provide the API key using either: - **x-api-key header**: ```bash curl -H "x-api-key: your-secret-api-key" http://localhost:8080/api/hooks ``` - **Authorization Bearer token**: ```bash curl -H "Authorization: Bearer your-secret-api-key" http://localhost:8080/api/hooks ``` #### Authentication Notes - If no authentication is configured (all fields empty), the server operates without authentication - The `/health` endpoint is always accessible without authentication - All `/api/*` endpoints require authentication when enabled - Both authentication methods can be configured simultaneously - the server will accept either valid credentials or a valid API key ## Usage ### Starting the Server ```bash ./bin/whatshook-server -config config.json ``` On first run, you'll need to pair your WhatsApp account. The QR code will be displayed directly in the terminal for easy scanning: ``` ======================================== WhatsApp QR Code for account: account1 Phone: +1234567890 ======================================== Scan this QR code with WhatsApp on your phone: [QR CODE DISPLAYED HERE] ======================================== ``` The QR code is also published as an event (`whatsapp.qr.code`) so you can handle it programmatically if needed. ### Using the CLI The CLI uses Cobra and supports configuration from multiple sources with the following priority: 1. Command-line flags (highest priority) 2. Environment variables 3. Configuration file (lowest priority) #### Configuration Create a CLI configuration file (optional): ```bash cp .whatshooked-cli.example.json .whatshooked-cli.json ``` Or set via environment variable: ```bash export WHATSHOOKED_SERVER_URL=http://localhost:8080 ``` Or use command-line flag: ```bash ./bin/whatshook-cli --server http://localhost:8080 health ``` #### Commands Get help: ```bash ./bin/whatshook-cli --help ./bin/whatshook-cli hooks --help ``` Check server health: ```bash ./bin/whatshook-cli health ``` List all hooks: ```bash ./bin/whatshook-cli hooks list # or just ./bin/whatshook-cli hooks ``` Add a new hook: ```bash ./bin/whatshook-cli hooks add ``` Remove a hook: ```bash ./bin/whatshook-cli hooks remove ``` List WhatsApp accounts: ```bash ./bin/whatshook-cli accounts list # or just ./bin/whatshook-cli accounts ``` Add a WhatsApp account: ```bash ./bin/whatshook-cli accounts add ``` Send a message: ```bash ./bin/whatshook-cli send ``` #### Configuration Priority The CLI loads configuration with the following priority (highest to lowest): 1. **Command-line flags**: `--server http://example.com:8080` 2. **Environment variables**: `WHATSHOOKED_SERVER_URL=http://example.com:8080` 3. **Config file**: `.whatshooked-cli.json` in current directory or `$HOME/.whatshooked/cli.json` 4. **Defaults**: `http://localhost:8080` ## Webhook Integration ### Incoming Message Format When a WhatsApp message is received, all active webhooks receive a POST request with the following JSON payload: ```json { "account_id": "account1", "message_id": "3EB0123456789ABCDEF", "from": "1234567890@s.whatsapp.net", "to": "9876543210@s.whatsapp.net", "text": "Hello, World!", "timestamp": "2025-12-28T10:30:00Z", "is_group": false, "group_name": "", "sender_name": "" } ``` ### Webhook Response Format Webhooks can respond with a JSON payload to send a message back to WhatsApp: ```json { "send_message": true, "to": "0834606792", "text": "This is a response message", "account_id": "account1" } ``` Or using full JID format: ```json { "send_message": true, "to": "27834606792@s.whatsapp.net", "text": "This is a response message", "account_id": "account1" } ``` Fields: - `send_message`: Set to `true` to send a message - `to`: Recipient phone number or JID. Can be: - Plain phone number (e.g., `"0834606792"`) - will be formatted using `default_country_code` - Phone number with country code (e.g., `"27834606792"`) - Full JID format (e.g., `"27834606792@s.whatsapp.net"`) - `text`: Message text to send - `account_id`: (Optional) Which WhatsApp account to use. If not specified, uses the account that received the original message #### Phone Number Formatting The server automatically formats phone numbers to WhatsApp JID format: 1. If the number contains `@`, it's used as-is (already in JID format) 2. Otherwise, formatting rules apply: - Removes all non-digit characters (spaces, dashes, parentheses, etc.) - **If starts with 0**: Assumes no country code and replaces the 0 with the `default_country_code` - **If starts with +**: Assumes it already has a country code - **Otherwise**: Adds country code if configured and not already present - Appends `@s.whatsapp.net` suffix Examples with `default_country_code: "27"`: - `0834606792` → `27834606792@s.whatsapp.net` (replaces leading 0 with 27) - `083-460-6792` → `27834606792@s.whatsapp.net` (removes dashes, replaces 0) - `27834606792` → `27834606792@s.whatsapp.net` (already has country code) - `+27834606792` → `27834606792@s.whatsapp.net` (+ indicates country code present) - `27834606792@s.whatsapp.net` → `27834606792@s.whatsapp.net` (unchanged, already JID) ## API Endpoints The server exposes the following HTTP endpoints: - `GET /health` - Health check (no authentication required) - `GET /api/hooks` - List all hooks (requires authentication if enabled) - `POST /api/hooks/add` - Add a new hook (requires authentication if enabled) - `POST /api/hooks/remove` - Remove a hook (requires authentication if enabled) - `GET /api/accounts` - List all WhatsApp accounts (requires authentication if enabled) - `POST /api/accounts/add` - Add a new WhatsApp account (requires authentication if enabled) - `POST /api/send` - Send a message (requires authentication if enabled) - `POST /api/send/image` - Send an image (requires authentication if enabled) - `POST /api/send/video` - Send a video (requires authentication if enabled) - `POST /api/send/document` - Send a document (requires authentication if enabled) - `GET /api/media/{accountID}/{filename}` - Serve media files (requires authentication if enabled) ## WhatsApp JID Format WhatsApp uses JID (Jabber ID) format for addressing: - Individual: `1234567890@s.whatsapp.net` - Group: `123456789-1234567890@g.us` The server accepts both full JID format and plain phone numbers. When using plain phone numbers, they are automatically formatted to JID format based on the `default_country_code` configuration. See [Phone Number Formatting](#phone-number-formatting) for details. ## Development ### Project Structure ``` whatshooked/ ├── cmd/ │ ├── server/ # Main server application │ └── cli/ # CLI tool ├── internal/ │ ├── config/ # Configuration management │ ├── events/ # Event bus and event types │ ├── logging/ # Structured logging │ ├── whatsapp/ # WhatsApp client management │ ├── hooks/ # Webhook management │ └── utils/ # Utility functions (phone formatting, etc.) ├── config.example.json # Example configuration └── go.mod # Go module definition ``` ### Event Types The system publishes the following event types: **WhatsApp Events:** - `whatsapp.connected` - WhatsApp client connected - `whatsapp.disconnected` - WhatsApp client disconnected - `whatsapp.pair.success` - Device pairing successful - `whatsapp.pair.failed` - Device pairing failed - `whatsapp.qr.code` - QR code generated for pairing (includes qr_code data) - `whatsapp.qr.timeout` - QR code expired - `whatsapp.qr.error` - QR code generation error - `whatsapp.pair.event` - Generic pairing event **Message Events:** - `message.received` - New message received from WhatsApp - `message.sent` - Message successfully sent to WhatsApp - `message.failed` - Message send failed **Hook Events:** - `hook.triggered` - Webhook is being called - `hook.success` - Webhook responded successfully - `hook.failed` - Webhook call failed ### Testing ```bash go test ./... ``` ### Building ```bash go build ./... ``` ## Future Phases ### Phase 2 (Planned) - User level hooks and WhatsApp accounts - Web server with frontend UI - Enhanced authentication with user roles and permissions ## License See LICENSE file for details.