Major refactor to library

This commit is contained in:
2025-12-29 09:51:16 +02:00
parent ae169f81e4
commit 767a9e211f
38 changed files with 1073 additions and 492 deletions

299
README.md
View File

@@ -1,15 +1,19 @@
# 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.
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)
[TODO LIST](TODO.md) - Things I still need to do
[Rules when using AI](AI_USE.md)
## Phase 1 Features
## Features
- **Multi-Account Support**: Connect to multiple WhatsApp accounts simultaneously
- **Dual Client Types**: Support for both personal WhatsApp (whatsmeow) and WhatsApp Business API
@@ -20,19 +24,68 @@ A service that connects to WhatsApp and forwards messages to registered webhooks
- **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
- **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
```
## 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)
### 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
- **internal/hooks**: Webhook management and message forwarding
- **cmd/server**: Main server application
- **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
@@ -57,7 +110,186 @@ This architecture enables:
- Context propagation for cancellation and timeout handling
- Proper request lifecycle management across components
## Installation
## 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
@@ -161,6 +393,7 @@ Edit the configuration file to add your WhatsApp accounts and webhooks:
- `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
### Server Authentication
@@ -566,28 +799,41 @@ The server accepts both full JID format and plain phone numbers. When using plai
```
whatshooked/
├── cmd/
│ ├── server/ # Main server application
│ │ ── main.go
│ │ ├── routes.go
│ │ ├── routes_*.go # Route handlers
│ │ └── routes_businessapi.go # Business API webhooks
│ ├── server/ # Standalone server (thin wrapper)
│ │ ── main.go
│ └── cli/ # CLI tool
├── internal/
├── config/ # Configuration management
│ ├── events/ # Event bus and event types
│ ├── logging/ # Structured logging
│ ├── 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 (QR code)
│ │ ├── whatsmeow/ # Personal WhatsApp
│ │ │ └── client.go
│ │ └── businessapi/ # WhatsApp Business API
│ │ ├── client.go # API client
│ │ ├── types.go # Request/response types
│ │ ├── events.go # Webhook processing
│ │ └── media.go # Media upload/download
│ │ ├── client.go
│ │ ├── types.go
│ │ ├── events.go
│ │ └── media.go
│ ├── hooks/ # Webhook management
── utils/ # Utility functions (phone formatting, etc.)
── eventlogger/ # Event persistence
│ └── utils/ # Utility functions
├── config.example.json # Example configuration
└── go.mod # Go module definition
```
@@ -628,9 +874,8 @@ go test ./...
go build ./...
```
## Future Phases
## Future Plans
### Phase 2 (Planned)
- User level hooks and WhatsApp accounts
- Web server with frontend UI
- Enhanced authentication with user roles and permissions