package webhook // TwilioWebhookHandler processes incoming Twilio SMS webhook HTTP requests via Gin. // Reference: Facebook webhook adapter pattern (facebook_webhook.go) // // URL patterns: // /webhooks/twilio/sms/:inbox_id — inbound SMS/MMS // /webhooks/twilio/status/:inbox_id — delivery status callbacks // // Method: POST (Twilio sends form-encoded data, not JSON) import ( "net/http" "strconv" twiliochannel "github.com/gochat/gochat/internal/channel/twilio" "github.com/gochat/gochat/internal/model" applogger "github.com/gochat/gochat/pkg/logger" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // TwilioWebhookHandler processes Twilio SMS webhook requests via Gin. type TwilioWebhookHandler struct { twilioWebhook *twiliochannel.WebhookHandler db *gorm.DB } // NewTwilioWebhookHandler creates a Twilio SMS webhook handler for Gin integration. func NewTwilioWebhookHandler(twilioWebhook *twiliochannel.WebhookHandler, db *gorm.DB) *TwilioWebhookHandler { return &TwilioWebhookHandler{ twilioWebhook: twilioWebhook, db: db, } } // HandleTwilioInboundSMS processes an incoming Twilio SMS webhook Gin request. func (h *TwilioWebhookHandler) HandleTwilioInboundSMS(c *gin.Context) { inboxIDStr := c.Param("inbox_id") inboxID, err := strconv.ParseUint(inboxIDStr, 10, 32) if err != nil { applogger.L().Warnf("Twilio webhook: invalid inbox_id %s", inboxIDStr) c.Data(http.StatusOK, "application/xml", []byte("")) return } // Lookup inbox from database inbox, err := h.lookupInbox(uint(inboxID)) if err != nil { applogger.L().Warnf("Twilio webhook: inbox lookup failed for id %d: %v", inboxID, err) c.Data(http.StatusOK, "application/xml", []byte("")) return } // Dispatch to the channel-level webhook handler // Twilio expects a TwiML XML response, not JSON h.twilioWebhook.HandleInboundSMS(c.Writer, c.Request, inbox) } // HandleTwilioDeliveryStatus processes a Twilio delivery status callback. func (h *TwilioWebhookHandler) HandleTwilioDeliveryStatus(c *gin.Context) { inboxIDStr := c.Param("inbox_id") inboxID, err := strconv.ParseUint(inboxIDStr, 10, 32) if err != nil { applogger.L().Warnf("Twilio status webhook: invalid inbox_id %s", inboxIDStr) c.Status(http.StatusOK) return } // Lookup inbox from database inbox, err := h.lookupInbox(uint(inboxID)) if err != nil { applogger.L().Warnf("Twilio status webhook: inbox lookup failed for id %d: %v", inboxID, err) c.Status(http.StatusOK) return } // Dispatch to the channel-level delivery status handler h.twilioWebhook.HandleDeliveryStatus(c.Writer, c.Request, inbox) } // lookupInbox fetches an Inbox record from the database. func (h *TwilioWebhookHandler) lookupInbox(inboxID uint) (*model.Inbox, error) { var inbox model.Inbox if err := h.db.Where("id = ?", inboxID).First(&inbox).Error; err != nil { return nil, err } return &inbox, nil }