feat(twilio): expose root callback
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gochat/gochat/internal/channel"
|
||||
twiliochannel "github.com/gochat/gochat/internal/channel/twilio"
|
||||
@@ -80,6 +81,43 @@ func (h *TwilioWebhookHandler) HandleTwilioInboundSMS(c *gin.Context) {
|
||||
c.Data(http.StatusOK, "application/xml", []byte("<Response></Response>"))
|
||||
}
|
||||
|
||||
// HandleTwilioCallback processes Chatwoot's root Twilio callback route.
|
||||
// Reference: Chatwoot Twilio::CallbackController#create enqueues the event and
|
||||
// always acknowledges with 204 No Content.
|
||||
func (h *TwilioWebhookHandler) HandleTwilioCallback(c *gin.Context) {
|
||||
if err := c.Request.ParseForm(); err != nil {
|
||||
applogger.L().Errorf("Twilio callback: parse form failed: %v", err)
|
||||
c.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
if h.twilioWebhook == nil {
|
||||
applogger.L().Warn("Twilio callback: Twilio webhook pipeline is not configured")
|
||||
c.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
inbox, err := h.lookupCallbackInbox(c.Request.Form)
|
||||
if err != nil {
|
||||
applogger.L().Warnf("Twilio callback: inbox lookup failed: %v", err)
|
||||
c.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
incomingMsg, err := h.twilioWebhook.ProcessInboundSMS(c.Request, inbox)
|
||||
if err != nil {
|
||||
applogger.L().Errorf("Twilio callback: process inbound SMS failed for inbox %d: %v", inbox.ID, err)
|
||||
c.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
if incomingMsg != nil {
|
||||
if _, persistErr := h.persister.PersistIncoming(c.Request.Context(), inbox, incomingMsg); persistErr != nil {
|
||||
applogger.L().Errorf("Twilio callback: persist inbound SMS failed for inbox %d source_id=%s: %v", inbox.ID, incomingMsg.SourceID, persistErr)
|
||||
}
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// HandleTwilioDeliveryStatus processes a Twilio delivery status callback.
|
||||
func (h *TwilioWebhookHandler) HandleTwilioDeliveryStatus(c *gin.Context) {
|
||||
phoneNumber := c.Param("phone_number")
|
||||
@@ -144,6 +182,44 @@ func (h *TwilioWebhookHandler) lookupDeliveryStatusInbox(params url.Values) (*mo
|
||||
return &inbox, nil
|
||||
}
|
||||
|
||||
func (h *TwilioWebhookHandler) lookupCallbackInbox(params url.Values) (*model.Inbox, error) {
|
||||
if h.db == nil {
|
||||
return nil, fmt.Errorf("twilio webhook database is not configured")
|
||||
}
|
||||
var twilioChannel channelmodel.ChannelTwilioSMS
|
||||
query := h.db
|
||||
if phone := normalizeTwilioPhone(params.Get("To")); phone != "" {
|
||||
if accountSID := params.Get("AccountSid"); accountSID != "" {
|
||||
query = query.Where(&channelmodel.ChannelTwilioSMS{AccountSID: accountSID, PhoneNumber: phone})
|
||||
} else {
|
||||
query = query.Where(&channelmodel.ChannelTwilioSMS{PhoneNumber: phone})
|
||||
}
|
||||
} else if sid := params.Get("MessagingServiceSid"); sid != "" {
|
||||
query = query.Where(&channelmodel.ChannelTwilioSMS{MessagingServiceSID: sid})
|
||||
} else {
|
||||
return nil, fmt.Errorf("twilio callback missing To or MessagingServiceSid")
|
||||
}
|
||||
if err := query.First(&twilioChannel).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var inbox model.Inbox
|
||||
if err := h.db.Where("id = ? AND channel_type IN ?", twilioChannel.InboxID, []string{"twilio_sms", "sms"}).First(&inbox).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &inbox, nil
|
||||
}
|
||||
|
||||
func normalizeTwilioPhone(phone string) string {
|
||||
phone = strings.TrimSpace(phone)
|
||||
if phone == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(phone, "+") {
|
||||
return phone
|
||||
}
|
||||
return "+" + phone
|
||||
}
|
||||
|
||||
func mapTwilioMessageStatus(status string) (model.MessageStatus, bool) {
|
||||
switch status {
|
||||
case "sent", "queued", "accepted", "sending":
|
||||
|
||||
Reference in New Issue
Block a user