1109 lines
33 KiB
Markdown
1109 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
|
|
|
|

|
|
|
|
## Documentation
|
|
|
|
- [WhatsApp Business API Setup](WHATSAPP_BUSINESS.md) - Complete guide for configuring WhatsApp Business API credentials
|
|
- [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.
|