Files
whatshooked/README.md
Hein ea1209c84c
Some checks failed
CI / Test (1.22) (push) Failing after -23m51s
CI / Test (1.23) (push) Failing after -23m51s
CI / Lint (push) Has been cancelled
CI / Build (push) Has been cancelled
Release / Build and Release (push) Successful in -18m25s
mqtt
2025-12-29 23:36:22 +02:00

1108 lines
33 KiB
Markdown

# WhatsHooked
A Go library and service that connects to WhatsApp and forwards messages to registered webhooks. Supports both personal WhatsApp accounts (via whatsmeow) and WhatsApp Business API. Enables two-way communication by allowing webhooks to respond with messages to be sent through WhatsApp.
**Use WhatsHooked as:**
- 📦 **Go Library** - Import into your own applications for programmatic WhatsApp integration
- 🚀 **Standalone Server** - Run as a service with HTTP API and CLI management
- 🔧 **Custom Integration** - Mount individual handlers in your existing HTTP servers
![1.00](./assets/image/whatshooked.jpg)
## Documentation
- [TODO List](TODO.md) - Current tasks and planned improvements
- [AI Usage Guidelines](AI_USE.md) - Rules when using AI tools with this project
- [Project Plan](PLAN.md) - Development plan and architecture decisions
- [CLI Documentation](CLI.md) - Command-line interface usage guide
- [Event Logger](EVENT_LOGGER.md) - Event logging system documentation
- [Docker Guide](DOCKER.md) - Docker deployment and configuration
- [MQTT Configuration Example](MQTT_CONFIG_EXAMPLE.md) - MQTT integration example
## Features
- **Multi-Account Support**: Connect to multiple WhatsApp accounts simultaneously
- **Dual Client Types**: Support for both personal WhatsApp (whatsmeow) and WhatsApp Business API
- **QR Code Pairing**: Browser-based QR code display for easy device pairing with PNG image endpoint
- **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
- **HTTPS/TLS Support**: Three certificate modes - self-signed, custom certificates, and Let's Encrypt autocert
- **Event Logging**: Optional event persistence to file, SQLite, or PostgreSQL
- **Library Mode**: Use WhatsHooked as a Go library in your own applications
- **Flexible Handlers**: Mount individual HTTP handlers in custom servers
## Quick Start
### As a Library
```go
import "git.warky.dev/wdevs/whatshooked/pkg/whatshooked"
// File-based configuration
wh, err := whatshooked.NewFromFile("config.json")
if err != nil {
panic(err)
}
defer wh.Close()
// Start built-in server
wh.StartServer()
```
Or with programmatic configuration:
```go
wh, err := whatshooked.New(
whatshooked.WithServer("0.0.0.0", 8080),
whatshooked.WithAuth("my-api-key", "", ""),
whatshooked.WithWhatsmeowAccount("personal", "+1234567890", "./session", true),
)
defer wh.Close()
wh.StartServer()
```
### As a Standalone Server
```bash
make build
./bin/whatshook-server -config config.json
```
## Pairing WhatsApp Accounts
When using personal WhatsApp accounts (whatsmeow), you'll need to pair the device on first launch. The QR code will be displayed in two ways:
### Terminal Display
The QR code is shown as ASCII art directly in the terminal:
```
========================================
WhatsApp QR Code for account: personal
Phone: +1234567890
========================================
Scan this QR code with WhatsApp on your phone:
[ASCII QR Code displayed here]
Or open in browser: http://localhost:8080/api/qr/personal
========================================
```
### Browser Display
For easier scanning, open the provided URL in your browser to view a larger PNG image:
- URL format: `http://localhost:8080/api/qr/{account_id}`
- No authentication required for this endpoint
- The QR code updates automatically when a new code is generated
### Webhook Events
The QR code URL is also included in the `whatsapp.qr.code` event:
```json
{
"type": "whatsapp.qr.code",
"data": {
"account_id": "personal",
"qr_code": "2@...",
"qr_url": "http://localhost:8080/api/qr/personal"
}
}
```
This allows your webhooks to programmatically display or forward QR codes for remote device pairing.
## Architecture
The project uses an event-driven architecture with the following packages:
### Library Packages (pkg/)
- **pkg/whatshooked**: Main library entry point with NewFromFile() and New() constructors
- **pkg/config**: Configuration management and persistence
- **pkg/logging**: Pluggable structured logging interface
- **pkg/events**: Event bus for publish/subscribe messaging between components
- **pkg/whatsapp**: WhatsApp client management (supports both whatsmeow and Business API)
- **whatsmeow/**: Personal WhatsApp client implementation
- **businessapi/**: WhatsApp Business API client implementation
- **pkg/hooks**: Webhook management and message forwarding
- **pkg/handlers**: HTTP handlers that can be mounted in any server
- **pkg/eventlogger**: Event persistence to file/SQLite/PostgreSQL
- **pkg/utils**: Utility functions (phone formatting, etc.)
### Application Packages (cmd/)
- **cmd/server**: Standalone server application (thin wrapper around library)
- **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
## Using WhatsHooked as a Library
### Installation
```bash
go get git.warky.dev/wdevs/whatshooked/pkg/whatshooked
```
### Example 1: Custom Server with Individual Handlers
Mount WhatsHooked handlers at custom paths in your existing HTTP server:
```go
package main
import (
"net/http"
"git.warky.dev/wdevs/whatshooked/pkg/whatshooked"
)
func main() {
// Initialize from config file
wh, err := whatshooked.NewFromFile("config.json")
if err != nil {
panic(err)
}
defer wh.Close()
// Get handlers
h := wh.Handlers()
// Custom HTTP server with your own routing
mux := http.NewServeMux()
// Mount WhatsHooked handlers at custom paths
mux.HandleFunc("/api/v1/whatsapp/send", h.Auth(h.SendMessage))
mux.HandleFunc("/api/v1/whatsapp/send/image", h.Auth(h.SendImage))
mux.HandleFunc("/api/v1/accounts", h.Auth(h.Accounts))
mux.HandleFunc("/healthz", h.Health)
// Your own handlers
mux.HandleFunc("/api/v1/custom", yourCustomHandler)
http.ListenAndServe(":8080", mux)
}
```
### Example 2: Programmatic Configuration
Configure WhatsHooked entirely in code without a config file:
```go
wh, err := whatshooked.New(
whatshooked.WithServer("0.0.0.0", 8080),
whatshooked.WithAuth("my-api-key", "", ""),
whatshooked.WithWhatsmeowAccount(
"personal",
"+1234567890",
"./sessions/personal",
true, // show QR
),
whatshooked.WithBusinessAPIAccount(
"business",
"+9876543210",
"phone-number-id",
"access-token",
"verify-token",
),
whatshooked.WithHook(config.Hook{
ID: "webhook1",
Name: "My Webhook",
URL: "https://example.com/webhook",
Method: "POST",
Active: true,
Events: []string{"message.received"},
}),
whatshooked.WithEventLogger([]string{"file", "sqlite"}, "./events"),
whatshooked.WithLogLevel("debug"),
)
if err != nil {
panic(err)
}
defer wh.Close()
// Use built-in server
wh.StartServer()
```
### Example 3: Embedded Library (No HTTP Server)
Use WhatsHooked purely as a library for programmatic WhatsApp access:
```go
wh, err := whatshooked.NewFromFile("config.json")
if err != nil {
panic(err)
}
defer wh.Close()
// Connect to WhatsApp accounts
ctx := context.Background()
if err := wh.ConnectAll(ctx); err != nil {
panic(err)
}
// Listen for incoming messages
wh.EventBus().Subscribe(events.EventMessageReceived, func(e events.Event) {
fmt.Printf("Received message: %s from %s\n",
e.Data["text"], e.Data["from"])
// Process message in your application
processMessage(e.Data)
})
// Send messages programmatically
jid, _ := types.ParseJID("27834606792@s.whatsapp.net")
err = wh.Manager().SendTextMessage(ctx, "account1", jid, "Hello from code!")
```
### Example 4: Custom Authentication
Replace the default authentication with your own (e.g., JWT):
```go
wh, err := whatshooked.NewFromFile("config.json")
if err != nil {
panic(err)
}
h := wh.Handlers()
// Use custom JWT authentication
h.WithAuthConfig(&handlers.AuthConfig{
Validator: func(r *http.Request) bool {
token := r.Header.Get("Authorization")
return validateJWTToken(token) // your JWT validation
},
})
// Or disable auth entirely
h.WithAuthConfig(&handlers.AuthConfig{
Disabled: true,
})
```
### Library API
```go
// Main WhatsHooked instance
type WhatsHooked struct { ... }
// Constructors
func NewFromFile(configPath string) (*WhatsHooked, error)
func New(opts ...Option) (*WhatsHooked, error)
// Methods
func (wh *WhatsHooked) Handlers() *handlers.Handlers // Get HTTP handlers
func (wh *WhatsHooked) Manager() *whatsapp.Manager // Get WhatsApp manager
func (wh *WhatsHooked) EventBus() *events.EventBus // Get event bus
func (wh *WhatsHooked) HookManager() *hooks.Manager // Get hook manager
func (wh *WhatsHooked) Config() *config.Config // Get configuration
func (wh *WhatsHooked) ConnectAll(ctx context.Context) error // Connect all accounts
func (wh *WhatsHooked) StartServer() error // Start built-in HTTP server
func (wh *WhatsHooked) StopServer(ctx context.Context) error // Stop server
func (wh *WhatsHooked) Close() error // Graceful shutdown
// Configuration Options
func WithServer(host string, port int) Option
func WithAuth(apiKey, username, password string) Option
func WithWhatsmeowAccount(id, phoneNumber, sessionPath string, showQR bool) Option
func WithBusinessAPIAccount(id, phoneNumber, phoneNumberID, accessToken, verifyToken string) Option
func WithHook(hook config.Hook) Option
func WithEventLogger(targets []string, fileDir string) Option
func WithMedia(dataPath, mode, baseURL string) Option
func WithLogLevel(level string) Option
func WithDatabase(dbType, host string, port int, username, password, database string) Option
func WithSQLiteDatabase(sqlitePath string) Option
```
## Installation (Standalone Server)
### 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
```
Or use one of the HTTPS examples:
```bash
# Self-signed certificate (development)
cp config.https-self-signed.example.json config.json
# Custom certificate (production)
cp config.https-custom.example.json config.json
# Let's Encrypt autocert (production)
cp config.https-letsencrypt.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": "personal",
"type": "whatsmeow",
"phone_number": "+1234567890",
"session_path": "./sessions/personal",
"show_qr": true
},
{
"id": "business",
"type": "business-api",
"phone_number": "+9876543210",
"business_api": {
"phone_number_id": "123456789012345",
"access_token": "EAAxxxxxxxxxxxx",
"business_account_id": "987654321098765",
"api_version": "v21.0",
"verify_token": "my-secure-verify-token"
}
}
],
"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
- `type`: Client type - `"whatsmeow"` for personal or `"business-api"` for Business API (defaults to "whatsmeow")
- `phone_number`: Phone number with country code
**For whatsmeow (personal) accounts:**
- `session_path`: Path to store session data (default: `./sessions/{id}`)
- `show_qr`: Display QR code in terminal for pairing (default: false)
**For business-api accounts:**
- `business_api`: Business API configuration object
- `phone_number_id`: WhatsApp Business Phone Number ID from Meta
- `access_token`: Access token from Meta Business Manager
- `business_account_id`: Business Account ID (optional)
- `api_version`: Graph API version (default: "v21.0")
- `verify_token`: Token for webhook verification (required for receiving messages)
**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
- `events`: List of event types to subscribe to (optional, defaults to all)
- `description`: Optional description
### HTTPS/TLS Configuration
WhatsHooked supports HTTPS with three certificate modes for secure connections:
#### 1. Self-Signed Certificates (Development/Testing)
Automatically generates and manages self-signed certificates. Ideal for development and testing environments.
```json
{
"server": {
"host": "localhost",
"port": 8443,
"tls": {
"enabled": true,
"mode": "self-signed",
"cert_dir": "./data/certs"
}
}
}
```
Or programmatically:
```go
wh, err := whatshooked.New(
whatshooked.WithServer("0.0.0.0", 8443),
whatshooked.WithSelfSignedTLS("./data/certs"),
)
```
**Features:**
- Automatically generates certificates on first run
- Certificates valid for 1 year
- Auto-renewal when expiring within 30 days
- Supports both IP addresses and hostnames as SANs
- No external dependencies
**Note:** Browsers will show security warnings for self-signed certificates. This is normal and expected for development environments.
#### 2. Custom Certificates (Production)
Use your own certificate files from a trusted Certificate Authority (CA) or an existing certificate.
```json
{
"server": {
"host": "0.0.0.0",
"port": 8443,
"tls": {
"enabled": true,
"mode": "custom",
"cert_file": "/etc/ssl/certs/myserver.crt",
"key_file": "/etc/ssl/private/myserver.key"
}
}
}
```
Or programmatically:
```go
wh, err := whatshooked.New(
whatshooked.WithServer("0.0.0.0", 8443),
whatshooked.WithCustomTLS("/etc/ssl/certs/myserver.crt", "/etc/ssl/private/myserver.key"),
)
```
**Features:**
- Use certificates from any CA (Let's Encrypt, DigiCert, etc.)
- Full control over certificate lifecycle
- Validates certificate files on startup
- Supports PKCS1, PKCS8, and EC private keys
#### 3. Let's Encrypt with Autocert (Production)
Automatically obtains and renews SSL certificates from Let's Encrypt. Best for production deployments with a registered domain.
```json
{
"server": {
"host": "0.0.0.0",
"port": 443,
"tls": {
"enabled": true,
"mode": "autocert",
"domain": "whatshooked.example.com",
"email": "admin@example.com",
"cache_dir": "./data/autocert",
"production": true
}
}
}
```
Or programmatically:
```go
wh, err := whatshooked.New(
whatshooked.WithServer("0.0.0.0", 443),
whatshooked.WithAutocertTLS("whatshooked.example.com", "admin@example.com", true),
)
```
**Features:**
- Automatic certificate provisioning from Let's Encrypt
- Automatic certificate renewal before expiration
- Fully managed - no manual intervention required
- Automatically starts HTTP challenge server on port 80 (when using port 443)
- Production and staging modes available
**Requirements:**
- Server must be publicly accessible
- Port 443 (HTTPS) must be open
- Port 80 (HTTP) must be open for ACME challenges
- Valid domain name pointing to your server
- Email address for Let's Encrypt notifications
**Important Notes:**
- Set `production: false` for testing to use Let's Encrypt staging environment (avoids rate limits)
- Set `production: true` for production deployments to get trusted certificates
- Ensure your domain's DNS A/AAAA record points to your server's IP
- Let's Encrypt has rate limits: 50 certificates per domain per week
#### TLS Configuration Reference
All TLS configuration options:
```json
{
"server": {
"tls": {
"enabled": true, // Enable HTTPS (default: false)
"mode": "self-signed", // Mode: "self-signed", "custom", or "autocert" (required if enabled)
// Self-signed mode options
"cert_dir": "./data/certs", // Directory for generated certificates (default: ./data/certs)
// Custom mode options
"cert_file": "/path/to/cert", // Path to certificate file (required for custom mode)
"key_file": "/path/to/key", // Path to private key file (required for custom mode)
// Autocert mode options
"domain": "example.com", // Domain name (required for autocert mode)
"email": "admin@example.com", // Email for Let's Encrypt notifications (optional)
"cache_dir": "./data/autocert", // Cache directory for certificates (default: ./data/autocert)
"production": true // Use Let's Encrypt production (default: false/staging)
}
}
}
```
#### Switching Between HTTP and HTTPS
To disable HTTPS and use HTTP, set `enabled: false` or omit the `tls` section entirely:
```json
{
"server": {
"host": "localhost",
"port": 8080
}
}
```
### 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
## WhatsApp Business API Setup
WhatsHooked supports the official WhatsApp Business Cloud API alongside personal WhatsApp accounts. This allows you to use official business phone numbers with enhanced features and reliability.
### Prerequisites
1. **Meta Business Account**: Sign up at [Meta Business Suite](https://business.facebook.com/)
2. **WhatsApp Business App**: Create a WhatsApp Business app in the [Meta for Developers](https://developers.facebook.com/) console
3. **Phone Number**: Register a business phone number with WhatsApp Business API
### Getting Your Credentials
1. Go to [Meta for Developers](https://developers.facebook.com/) and select your app
2. Navigate to **WhatsApp** → **API Setup**
3. Obtain the following:
- **Phone Number ID**: Found in the API Setup page
- **WhatsApp Business Account ID**: Found in the API Setup page (optional but recommended)
- **Access Token**: Generate a permanent token (not the temporary 24-hour token)
- **API Version**: Use the current stable version (e.g., `v21.0`)
### Configuring the Account
Add a Business API account to your `config.json`:
```json
{
"whatsapp": [
{
"id": "business",
"type": "business-api",
"phone_number": "+1234567890",
"business_api": {
"phone_number_id": "123456789012345",
"access_token": "EAAxxxxxxxxxxxx_your_permanent_token",
"business_account_id": "987654321098765",
"api_version": "v21.0",
"verify_token": "my-secure-random-token-12345"
}
}
]
}
```
**Important Notes:**
- Use a **permanent access token**, not the temporary 24-hour token
- The `verify_token` is a random string you create - it will be used to verify Meta's webhook requests
- Keep your access token secure and never commit it to version control
### Setting Up Webhooks (Required for Receiving Messages)
To receive incoming messages from WhatsApp Business API, you must register your webhook with Meta:
1. **Start the WhatsHooked server** with your Business API configuration
2. **Ensure your server is publicly accessible** (use ngrok for testing):
```bash
ngrok http 8080
```
3. **In Meta for Developers**, go to **WhatsApp** → **Configuration**
4. **Add Webhook URL**:
- **Callback URL**: `https://your-domain.com/webhooks/whatsapp/{accountID}`
- Replace `your-domain.com` with your public domain or ngrok URL
- Replace `{accountID}` with your account ID from config (e.g., `business`)
- Example: `https://abc123.ngrok.io/webhooks/whatsapp/business`
- **Verify Token**: Enter the same `verify_token` from your config
5. **Subscribe to Webhook Fields**:
- Check **messages** (required for receiving messages)
- Check **message_status** (optional, for delivery/read receipts)
6. Click **Verify and Save**
### Testing Your Business API Connection
Once configured, start the server and the Business API account will connect automatically:
```bash
./bin/whatshook-server -config config.json
```
Look for logs indicating successful connection:
```
Business API client connected account_id=business phone=+1234567890
```
Send a test message:
```bash
./bin/whatshook-cli send
# Select your business account
# Enter recipient phone number
# Type your message
```
### Business API Features
**Supported:**
- ✅ Send/receive text messages
- ✅ Send/receive images with captions
- ✅ Send/receive videos with captions
- ✅ Send/receive documents with filenames
- ✅ Media upload via Meta CDN
- ✅ Delivery and read receipts
- ✅ Event publishing to webhooks (same format as whatsmeow)
**Differences from whatsmeow:**
- No QR code pairing (uses access token authentication)
- Rate limits apply based on your Meta Business tier
- Official support from Meta
- Better reliability for business use cases
- Costs apply based on conversation pricing
### Running Both Client Types Simultaneously
You can run both personal (whatsmeow) and Business API accounts at the same time:
```json
{
"whatsapp": [
{
"id": "personal",
"type": "whatsmeow",
"phone_number": "+1234567890",
"session_path": "./sessions/personal"
},
{
"id": "business",
"type": "business-api",
"phone_number": "+9876543210",
"business_api": {
"phone_number_id": "123456789012345",
"access_token": "EAAxxxxxxxxxxxx"
}
}
]
}
```
Both accounts will:
- Receive messages independently
- Trigger the same webhooks
- Publish identical event formats
- Support the same API endpoints
## 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 <hook_id>
```
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:
**Public Endpoints:**
- `GET /health` - Health check (no authentication required)
- `GET/POST /webhooks/whatsapp/{accountID}` - Business API webhook verification and events (no authentication, validated by Meta's verify_token)
**Protected Endpoints (require authentication if enabled):**
- `GET /api/hooks` - List all hooks
- `POST /api/hooks/add` - Add a new hook
- `POST /api/hooks/remove` - Remove a hook
- `GET /api/accounts` - List all WhatsApp accounts
- `POST /api/accounts/add` - Add a new WhatsApp account
- `POST /api/send` - Send a message
- `POST /api/send/image` - Send an image
- `POST /api/send/video` - Send a video
- `POST /api/send/document` - Send a document
- `GET /api/media/{accountID}/{filename}` - Serve media files
## 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/ # Standalone server (thin wrapper)
│ │ └── main.go
│ └── cli/ # CLI tool
│ ├── main.go
│ └── commands_*.go
├── pkg/ # Public library packages
│ ├── whatshooked/ # Main library entry point
│ │ ├── whatshooked.go # NewFromFile(), New()
│ │ ├── options.go # Functional options
│ │ └── server.go # Built-in HTTP server
│ ├── handlers/ # HTTP handlers
│ │ ├── handlers.go # Handler struct
│ │ ├── middleware.go # Auth middleware
│ │ ├── send.go # Send handlers
│ │ ├── accounts.go # Account handlers
│ │ ├── hooks.go # Hook handlers
│ │ ├── media.go # Media handlers
│ │ ├── health.go # Health handler
│ │ └── businessapi.go # Business API webhook
│ ├── config/ # Configuration types
│ ├── events/ # Event bus
│ ├── logging/ # Pluggable logging
│ ├── whatsapp/ # WhatsApp client management
│ │ ├── interface.go # Client interface
│ │ ├── manager.go # Multi-client manager
│ │ ├── whatsmeow/ # Personal WhatsApp
│ │ │ └── client.go
│ │ └── businessapi/ # WhatsApp Business API
│ │ ├── client.go
│ │ ├── types.go
│ │ ├── events.go
│ │ └── media.go
│ ├── hooks/ # Webhook management
│ ├── eventlogger/ # Event persistence
│ └── utils/ # Utility functions
├── 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 Plans
- User level hooks and WhatsApp accounts
- Web server with frontend UI
- Enhanced authentication with user roles and permissions
## License
See LICENSE file for details.