Files
whatshooked/README.md
2025-12-29 06:23:16 +02:00

641 lines
19 KiB
Markdown

# WhatsHooked
A 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.
![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
- **Dual Client Types**: Support for both personal WhatsApp (whatsmeow) and WhatsApp Business API
- **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 (supports both whatsmeow and Business API)
- **whatsmeow/**: Personal WhatsApp client implementation
- **businessapi/**: WhatsApp Business API client implementation
- **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": "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
- `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
## 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/ # Main server application
│ │ ├── main.go
│ │ ├── routes.go
│ │ ├── routes_*.go # Route handlers
│ │ └── routes_businessapi.go # Business API webhooks
│ └── cli/ # CLI tool
├── internal/
│ ├── config/ # Configuration management
│ ├── events/ # Event bus and event types
│ ├── logging/ # Structured logging
│ ├── whatsapp/ # WhatsApp client management
│ │ ├── interface.go # Client interface
│ │ ├── manager.go # Multi-client manager
│ │ ├── whatsmeow/ # Personal WhatsApp (QR code)
│ │ │ └── client.go
│ │ └── businessapi/ # WhatsApp Business API
│ │ ├── client.go # API client
│ │ ├── types.go # Request/response types
│ │ ├── events.go # Webhook processing
│ │ └── media.go # Media upload/download
│ ├── 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.