initial commit
This commit is contained in:
451
README.md
451
README.md
@@ -1,3 +1,450 @@
|
||||
# whatshooked
|
||||
# WhatsHooked
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
|
||||
## 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 <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:
|
||||
|
||||
- `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.
|
||||
|
||||
Reference in New Issue
Block a user