116 lines
4.4 KiB
Go
116 lines
4.4 KiB
Go
// Package webhook provides Gin-compatible HTTP handler adapters for external
|
|
// channel webhook endpoints. Each adapter wraps the corresponding channel
|
|
// sub-package's WebhookHandler and exposes methods that can be bound directly
|
|
// to Gin router groups.
|
|
package webhook
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/gochat/gochat/internal/channel"
|
|
"github.com/gochat/gochat/internal/channel/whatsapp"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/worker"
|
|
)
|
|
|
|
// WhatsAppWebhookHandler is a Gin adapter that wraps the WhatsApp
|
|
// sub-package's WebhookHandler. It translates Gin context calls into the
|
|
// underlying handler methods so that WhatsApp webhook routes can be registered
|
|
// on a Gin engine with minimal glue code.
|
|
//
|
|
// Chatwoot-style routes:
|
|
//
|
|
// GET /webhooks/whatsapp/:phone_number → HandleWhatsAppVerification
|
|
// POST /webhooks/whatsapp/:phone_number → HandleWhatsAppWebhook
|
|
//
|
|
// WhatsApp Cloud API webhook verification (GET):
|
|
//
|
|
// The Meta platform sends a GET request with query parameters:
|
|
// - hub.mode = "subscribe"
|
|
// - hub.verify_token = the token configured in the Meta dashboard
|
|
// - hub.challenge = a string the endpoint must echo back verbatim
|
|
//
|
|
// The handler validates hub.verify_token against the stored token and
|
|
// returns hub.challenge as the response body with HTTP 200, or returns
|
|
// HTTP 403 on mismatch.
|
|
//
|
|
// WhatsApp Cloud API webhook events (POST):
|
|
//
|
|
// Meta delivers event payloads as JSON with structure:
|
|
// {
|
|
// "object": "whatsapp_business_account",
|
|
// "entry": [ { "changes": [ ... ] } ]
|
|
// }
|
|
//
|
|
// The handler parses the payload, dispatches incoming messages and status
|
|
// updates, and must respond with HTTP 200 OK within 10 seconds to avoid
|
|
// Meta retrying delivery.
|
|
type WhatsAppWebhookHandler struct {
|
|
provider *whatsapp.WhatsAppProvider
|
|
waWebhook *whatsapp.WebhookHandler
|
|
persister *IncomingPersister
|
|
}
|
|
|
|
// NewWhatsAppWebhookHandler creates a Gin adapter wrapping the WhatsApp
|
|
// sub-package's WebhookHandler. The provider is stored for future use (e.g.
|
|
// health checks or direct API calls) while waWebhook is the core handler that
|
|
// processes verification and event requests.
|
|
func NewWhatsAppWebhookHandler(provider *whatsapp.WhatsAppProvider, waWebhook *whatsapp.WebhookHandler, db *gorm.DB, dispatcher ...*channel.Dispatcher) *WhatsAppWebhookHandler {
|
|
h := &WhatsAppWebhookHandler{
|
|
provider: provider,
|
|
waWebhook: waWebhook,
|
|
persister: NewIncomingPersister(db, dispatcher...),
|
|
}
|
|
if waWebhook != nil && h.persister != nil {
|
|
waWebhook.SetIncomingPersister(whatsAppPersisterAdapter{persister: h.persister})
|
|
}
|
|
return h
|
|
}
|
|
|
|
func (h *WhatsAppWebhookHandler) WithWorkerPool(wp *worker.WorkerPool) *WhatsAppWebhookHandler {
|
|
if h != nil && h.persister != nil {
|
|
h.persister.SetWorkerPool(wp)
|
|
}
|
|
return h
|
|
}
|
|
|
|
type whatsAppPersisterAdapter struct {
|
|
persister *IncomingPersister
|
|
}
|
|
|
|
func (a whatsAppPersisterAdapter) PersistIncoming(ctx context.Context, inbox *model.Inbox, msg *channel.IncomingMessage) (interface{}, error) {
|
|
return a.persister.PersistIncoming(ctx, inbox, msg)
|
|
}
|
|
|
|
func (a whatsAppPersisterAdapter) UpdateMessageStatus(ctx context.Context, inbox *model.Inbox, sourceID string, status model.MessageStatus, occurredAt *time.Time) error {
|
|
return a.persister.UpdateMessageStatus(ctx, inbox, sourceID, status, occurredAt)
|
|
}
|
|
|
|
// HandleWhatsAppVerification handles GET requests for WhatsApp Cloud API
|
|
// webhook verification. Meta sends this request during initial webhook setup
|
|
// and periodic re-verification. It delegates to the underlying
|
|
// waWebhook.HandleVerification method which validates hub.verify_token and
|
|
// echoes hub.challenge on success.
|
|
//
|
|
// Expected Gin route: GET /webhooks/whatsapp/:phone_number
|
|
func (h *WhatsAppWebhookHandler) HandleWhatsAppVerification(c *gin.Context) {
|
|
h.waWebhook.HandleVerification(c)
|
|
}
|
|
|
|
// HandleWhatsAppWebhook handles POST requests carrying WhatsApp Cloud API
|
|
// event payloads (messages, status updates, template events, etc.). It
|
|
// delegates to the underlying waWebhook.HandleWebhookEvent method which
|
|
// parses the JSON body and dispatches events to the appropriate processor.
|
|
//
|
|
// The endpoint must respond with HTTP 200 OK within 10 seconds; Meta retries
|
|
// delivery on timeout or non-200 responses.
|
|
//
|
|
// Expected Gin route: POST /webhooks/whatsapp/:phone_number
|
|
func (h *WhatsAppWebhookHandler) HandleWhatsAppWebhook(c *gin.Context) {
|
|
h.waWebhook.HandleWebhookEvent(c)
|
|
}
|