package dispatch // Event types and helper functions for the enhanced dispatch system. // // Reference: Chatwoot event naming conventions // - Chatwoot uses Wisper-style event names like "conversation.created", // "message.created", "contact.created", etc. // - In gochat, channel.EventType already defines these constants. // - This file provides additional event types specific to the dispatch layer // and helper functions for event construction. import ( "fmt" "time" "github.com/gochat/gochat/internal/channel" ) // DispatchEvent defines additional event types specific to the dispatch layer. // These complement the channel.EventType constants already defined in // channel/event.go. type DispatchEvent string const ( // Auto-assignment events (triggered by autoassignment listener) DispatchEventAutoAssign DispatchEvent = "auto_assignment.assign" DispatchEventAutoAssignFailed DispatchEvent = "auto_assignment.assign_failed" DispatchEventAutoAssignSkipped DispatchEvent = "auto_assignment.assign_skipped" // Reporting events (for analytics and reporting) DispatchEventReportGenerated DispatchEvent = "report.generated" DispatchEventReportFailed DispatchEvent = "report.failed" // CSAT survey events DispatchEventCsatSurveySent DispatchEvent = "csat_survey.sent" DispatchEventCsatResponseReceived DispatchEvent = "csat_survey.response_received" // Campaign events DispatchEventCampaignStarted DispatchEvent = "campaign.started" DispatchEventCampaignCompleted DispatchEvent = "campaign.completed" // Webhook/hook events DispatchEventHookTriggered DispatchEvent = "hook.triggered" DispatchEventWebhookSent DispatchEvent = "webhook.sent" DispatchEventWebhookFailed DispatchEvent = "webhook.failed" ) // NewChannelEvent creates a ChannelEvent with the given type and data. // Timestamp is set to the current Unix milliseconds. func NewChannelEvent(eventType string, data map[string]interface{}) *channel.ChannelEvent { if data == nil { data = make(map[string]interface{}) } return &channel.ChannelEvent{ Type: channel.EventType(eventType), Data: data, Timestamp: time.Now().UnixMilli(), } } // NewDispatchEvent creates a ChannelEvent from a DispatchEvent type. func NewDispatchEvent(eventType DispatchEvent, data map[string]interface{}) *channel.ChannelEvent { return NewChannelEvent(string(eventType), data) } // EventDataWithConversation creates event data containing a conversation ID. // This is a common pattern where events need to reference a specific conversation. func EventDataWithConversation(conversationID uint, accountID uint, inboxID uint) map[string]interface{} { return map[string]interface{}{ "conversation_id": conversationID, "account_id": accountID, "inbox_id": inboxID, } } // EventDataWithAssignment creates event data for an assignment event, // including the agent who was assigned. func EventDataWithAssignment(conversationID uint, accountID uint, inboxID uint, assigneeID uint) map[string]interface{} { data := EventDataWithConversation(conversationID, accountID, inboxID) data["assignee_id"] = assigneeID return data } // ExtractConversationID extracts a conversation ID from event data. // Returns 0 and an error if not found or not a valid uint. func ExtractConversationID(data map[string]interface{}) (uint, error) { raw, ok := data["conversation_id"] if !ok { return 0, fmt.Errorf("conversation_id not found in event data") } switch v := raw.(type) { case uint: return v, nil case int: return uint(v), nil case float64: return uint(v), nil default: return 0, fmt.Errorf("unexpected type for conversation_id: %T", raw) } } // ExtractAccountID extracts an account ID from event data. func ExtractAccountID(data map[string]interface{}) (uint, error) { raw, ok := data["account_id"] if !ok { return 0, fmt.Errorf("account_id not found in event data") } switch v := raw.(type) { case uint: return v, nil case int: return uint(v), nil case float64: return uint(v), nil default: return 0, fmt.Errorf("unexpected type for account_id: %T", raw) } } // ExtractInboxID extracts an inbox ID from event data. func ExtractInboxID(data map[string]interface{}) (uint, error) { raw, ok := data["inbox_id"] if !ok { return 0, fmt.Errorf("inbox_id not found in event data") } switch v := raw.(type) { case uint: return v, nil case int: return uint(v), nil case float64: return uint(v), nil default: return 0, fmt.Errorf("unexpected type for inbox_id: %T", raw) } } // ExtractAssigneeID extracts an assignee (agent) ID from event data. func ExtractAssigneeID(data map[string]interface{}) (uint, error) { raw, ok := data["assignee_id"] if !ok { return 0, fmt.Errorf("assignee_id not found in event data") } switch v := raw.(type) { case uint: return v, nil case int: return uint(v), nil case float64: return uint(v), nil default: return 0, fmt.Errorf("unexpected type for assignee_id: %T", raw) } }