package line import ( "encoding/json" "io" "net/http" "github.com/gochat/gochat/internal/model" applogger "github.com/gochat/gochat/pkg/logger" ) // WebhookHandler handles LINE webhook HTTP requests. type WebhookHandler struct { pipeline *IncomingProcessor service *LineService } // NewWebhookHandler creates a LINE webhook handler. func NewWebhookHandler(pipeline *IncomingProcessor, service *LineService) *WebhookHandler { return &WebhookHandler{ pipeline: pipeline, service: service, } } // HandleWebhook processes an incoming LINE webhook HTTP request. func (h *WebhookHandler) HandleWebhook(w http.ResponseWriter, r *http.Request, inbox *model.Inbox) { // Read request body for signature verification body, err := io.ReadAll(r.Body) if err != nil { applogger.L().Errorf("LINE HandleWebhook: failed to read body: %v", err) http.Error(w, "Failed to read request body", http.StatusBadRequest) return } defer r.Body.Close() // Verify webhook signature config := parseInboxConfig(inbox.ChannelConfig) channelSecret := configStr(config, "channel_secret", "") signature := r.Header.Get("X-Line-Signature") if channelSecret != "" && signature != "" { if !h.service.VerifySignature(channelSecret, string(body), signature) { applogger.L().Warnf("LINE HandleWebhook: invalid signature for inbox=%d", inbox.ID) http.Error(w, "Invalid signature", http.StatusUnauthorized) return } } // Parse webhook event var webhookEvent WebhookEvent if err := json.Unmarshal(body, &webhookEvent); err != nil { applogger.L().Errorf("LINE HandleWebhook: failed to parse JSON: %v", err) http.Error(w, "Invalid JSON", http.StatusBadRequest) return } // Process each event for _, event := range webhookEvent.Events { ctx := r.Context() incomingMsg, err := h.pipeline.ProcessEvent(ctx, inbox, event) if err != nil { applogger.L().Errorf("LINE HandleWebhook: process event failed: %v", err) continue } if incomingMsg == nil { continue // unhandled event type } // Log processed event applogger.L().Debugf("LINE HandleWebhook: processed event type=%s sender=%s", event.Type, incomingMsg.SenderID) } w.WriteHeader(http.StatusOK) } // HandleWebhookVerification responds to LINE webhook URL verification requests. func (h *WebhookHandler) HandleWebhookVerification(w http.ResponseWriter, r *http.Request) { // LINE webhook verification is handled during the initial setup // The verification endpoint returns a 200 OK to confirm the webhook URL w.WriteHeader(http.StatusOK) }