package v1 import ( "net/http" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/automation" "github.com/gochat/gochat/pkg/response" ) // BotTriggerConfigHandler handles bot trigger condition config API endpoints. // Reference: Chatwoot AutomationRulesController trigger conditions pattern adapted for AgentBot. type BotTriggerConfigHandler struct { svc *automation.BotTriggerConfigService } // NewBotTriggerConfigHandler creates a new BotTriggerConfigHandler. func NewBotTriggerConfigHandler(svc *automation.BotTriggerConfigService) *BotTriggerConfigHandler { return &BotTriggerConfigHandler{svc: svc} } // List retrieves all trigger configs for an account. // GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs func (h *BotTriggerConfigHandler) List(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } configs, svcErr := h.svc.ListByAccount(c.Request.Context(), accountID) if svcErr != nil { handleServiceError(c, svcErr) return } c.JSON(http.StatusOK, gin.H{ "trigger_configs": configs, "meta": gin.H{"count": len(configs)}, }) } // ListByBot retrieves all trigger configs for a specific AgentBot. // GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs func (h *BotTriggerConfigHandler) ListByBot(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } agentBotID, err := parseUintParam(c, "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid agent_bot_id") return } configs, svcErr := h.svc.ListByAgentBot(c.Request.Context(), accountID, agentBotID) if svcErr != nil { handleServiceError(c, svcErr) return } c.JSON(http.StatusOK, gin.H{ "trigger_configs": configs, "meta": gin.H{"count": len(configs)}, }) } // Get retrieves a single trigger config by ID. // GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id func (h *BotTriggerConfigHandler) Get(c *gin.Context) { id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } config, svcErr := h.svc.GetByID(c.Request.Context(), id) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, config) } // Create creates a new trigger config for an AgentBot. // POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs func (h *BotTriggerConfigHandler) Create(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } agentBotID, err := parseUintParam(c, "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid agent_bot_id") return } var req struct { Name string `json:"name" binding:"required"` Description string `json:"description"` EventName automation.BotRuleEventType `json:"event_name" binding:"required"` Conditions automation.TriggerConditions `json:"conditions"` QueryOperator string `json:"query_operator"` Active bool `json:"active"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } config := &automation.BotTriggerConfig{ AccountID: accountID, AgentBotID: agentBotID, Name: req.Name, Description: req.Description, EventName: req.EventName, Conditions: req.Conditions, QueryOperator: req.QueryOperator, Active: req.Active, } // Default query_operator to "and" if config.QueryOperator == "" { config.QueryOperator = "and" } // Default to active if not explicitly set if !config.Active && req.Active == false { // Only default to active when the field was not provided in JSON // (JSON deserialization defaults bool to false) config.Active = true } if svcErr := h.svc.Create(c.Request.Context(), config); svcErr != nil { handleServiceError(c, svcErr) return } response.Created(c, config) } // Update updates an existing trigger config. // PUT /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id func (h *BotTriggerConfigHandler) Update(c *gin.Context) { id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } existing, svcErr := h.svc.GetByID(c.Request.Context(), id) if svcErr != nil { handleServiceError(c, svcErr) return } var req struct { Name string `json:"name"` Description string `json:"description"` EventName automation.BotRuleEventType `json:"event_name"` Conditions automation.TriggerConditions `json:"conditions"` QueryOperator string `json:"query_operator"` Active bool `json:"active"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } // Apply updates if req.Name != "" { existing.Name = req.Name } if req.Description != "" { existing.Description = req.Description } if req.EventName != "" { existing.EventName = req.EventName } if req.Conditions != nil { existing.Conditions = req.Conditions } if req.QueryOperator != "" { existing.QueryOperator = req.QueryOperator } existing.Active = req.Active if svcErr := h.svc.Update(c.Request.Context(), existing); svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, existing) } // Delete soft-deletes a trigger config. // DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id func (h *BotTriggerConfigHandler) Delete(c *gin.Context) { id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } if svcErr := h.svc.Delete(c.Request.Context(), id); svcErr != nil { handleServiceError(c, svcErr) return } response.NoContent(c) } // ToggleActive toggles the active state of a trigger config. // PATCH /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id/active func (h *BotTriggerConfigHandler) ToggleActive(c *gin.Context) { id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } var req struct { Active bool `json:"active" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } if svcErr := h.svc.ToggleActive(c.Request.Context(), id, req.Active); svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, gin.H{"id": id, "active": req.Active}) }