package channel import ( "context" "time" "github.com/gochat/gochat/internal/model" ) // ChannelType enumerates supported channel types. // Reference: Chatwoot app/models/inbox.rb channel_type enum // P2D: 使用 snake_case 标识符,替代 Chatwoot 的 Channel::PascalCase type ChannelType string const ( ChannelWebWidget ChannelType = "web_widget" ChannelTelegram ChannelType = "telegram" ChannelFacebook ChannelType = "facebook" ChannelInstagram ChannelType = "instagram" ChannelWhatsApp ChannelType = "whatsapp" ChannelEmail ChannelType = "email" ChannelTwilioSMS ChannelType = "twilio_sms" ChannelTwilioWA ChannelType = "twilio_whatsapp" ChannelLine ChannelType = "line" ChannelSlack ChannelType = "slack" ChannelAPI ChannelType = "api" ChannelTikTok ChannelType = "tiktok" ChannelMicrosoft ChannelType = "microsoft" ) // ChannelProvider is the core interface that all channel providers must implement. // Reference: P2D §3.2 — Chatwoot's Channelable concern + polymorphic channel association // // Design principles: // - Interface methods follow message lifecycle (create→send→receive→callback→destroy) // - Each Provider is stateless; channel state is persisted via Inbox.ChannelConfig + GORM models // - context.Context is used for timeout/cancel/tracing propagation type ChannelProvider interface { // === Identity & Metadata === // Type returns the channel type identifier Type() ChannelType // Name returns the human-readable channel name (e.g. "Telegram", "Web Widget") Name() string // Description returns a short description of the channel Description() string // === Configuration & Validation === // ConfigSchema returns the JSON Schema definition for channel configuration // Used for frontend dynamic form rendering and backend config validation // Corresponds to Chatwoot's EDITABLE_ATTRS per channel ConfigSchema() *ConfigSchemaDefinition // ValidateConfig validates channel configuration validity // Called on Inbox create/update, replaces Chatwoot's channel model before_validation ValidateConfig(ctx context.Context, config ChannelConfig) error // DefaultConfig returns default configuration with preset values DefaultConfig() ChannelConfig // === Lifecycle: Create & Destroy === // OnCreate callback after channel creation // Corresponds to Chatwoot's before_save :setup_telegram_webhook etc. // Returns possibly updated config (e.g. webhook URL, token) OnCreate(ctx context.Context, inbox *model.Inbox, config ChannelConfig) (ChannelConfig, error) // OnDestroy callback before channel destruction // Cleanup external resources (delete Telegram webhook, cancel FB Page subscription etc.) OnDestroy(ctx context.Context, inbox *model.Inbox, config ChannelConfig) error // === Messaging: Inbound === // ProcessIncoming transforms raw external payload into IncomingMessage // From webhook callback or polling data → IncomingMessage // Corresponds to Chatwoot's WebhooksController + MessageBuilder per channel ProcessIncoming(ctx context.Context, inbox *model.Inbox, rawPayload []byte) (*IncomingMessage, error) // ValidateWebhookRequest verifies webhook callback authenticity // Each Provider implements its own signature verification (FB signature, TG token, WA HMAC etc.) ValidateWebhookRequest(ctx context.Context, inbox *model.Inbox, request *WebhookRequest) error // === Messaging: Outbound === // SendMessage sends a message to the external channel // Corresponds to Chatwoot's SendOn*Service per channel // Returns external channel message ID for correlation tracking SendMessage(ctx context.Context, inbox *model.Inbox, message *model.Message, contact *model.Contact) (*SendResult, error) // === Contact Info === // GetContactProfile fetches contact profile from external channel (avatar, name etc.) // Corresponds to Chatwoot's Telegram.get_telegram_profile_image / Facebook.get_user_profile etc. GetContactProfile(ctx context.Context, inbox *model.Inbox, contactSource string) (*ContactProfile, error) // === Capability Declaration === // Capabilities returns the set of features this channel supports // Used by frontend to determine available Inbox features (attachments, typing indicators etc.) Capabilities() ChannelCapabilities } // OAuthProvider is the sub-interface for channels requiring OAuth authentication. // Reference: P2D §3.3 — Chatwoot's Reauthorizable concern + RefreshOauthTokenService // // Applicable channels: Facebook, Instagram, WhatsApp(360dialog), Slack type OAuthProvider interface { ChannelProvider // embed base interface // OAuthConfig returns OAuth configuration requirements definition OAuthConfig() *OAuthConfigDefinition // BuildAuthURL constructs the OAuth authorization redirect URL // Corresponds to Chatwoot's omniauth authorize URL BuildAuthURL(ctx context.Context, accountID uint, redirectURL string) (string, error) // ExchangeToken exchanges OAuth code for access_token // Corresponds to Chatwoot's omniauth callback processing ExchangeToken(ctx context.Context, code string, redirectURL string) (*OAuthTokenResult, error) // RefreshToken refreshes an expired access_token // Corresponds to Chatwoot's RefreshOauthTokenService per channel RefreshToken(ctx context.Context, inbox *model.Inbox, config ChannelConfig) (*OAuthTokenResult, error) // CheckAuthorizationError checks if an API call returned authorization error // Corresponds to Chatwoot Reauthorizable.authorization_error! CheckAuthorizationError(ctx context.Context, apiError error) bool // OnReauthorization callback when re-authorization is needed // Corresponds to Chatwoot's prompt_reauthorization! → email notification + UI hint OnReauthorization(ctx context.Context, inbox *model.Inbox) error } // PollingProvider is the sub-interface for channels requiring active message polling. // Reference: P2D §3.4 — Chatwoot's Imap::FetchService / Scheduler periodic fetch // // Applicable channels: Email (IMAP) type PollingProvider interface { ChannelProvider // embed base interface // PollInterval returns the polling interval duration PollInterval() time.Duration // PollMessages fetches new messages from external channel // Corresponds to Chatwoot's Imap::FetchService / Email::ProcessMailService PollMessages(ctx context.Context, inbox *model.Inbox) ([]*IncomingMessage, error) } // --- Domain Types --- // ChannelConfig stores per-inbox channel configuration as typed map. // Reference: P2D §7 — replaces Chatwoot's per-channel DB models with unified JSON config type ChannelConfig map[string]interface{} // ConfigSchemaDefinition defines the JSON Schema for channel configuration validation. // Reference: P2D §7 — replaces Chatwoot's EDITABLE_ATTRS constants type ConfigSchemaDefinition struct { Type string `json:"type"` // "object" Properties map[string]ConfigProperty `json:"properties"` // field definitions Required []string `json:"required"` // mandatory fields } // ConfigProperty defines a single configuration field. type ConfigProperty struct { Type string `json:"type"` // "string", "number", "boolean", "array" Description string `json:"description"` // human-readable field description Default interface{} `json:"default,omitempty"` // default value Enum []string `json:"enum,omitempty"` // allowed values (for enum fields) Pattern string `json:"pattern,omitempty"` // regex pattern for validation Format string `json:"format,omitempty"` // "url", "email", "uri" etc. Secret bool `json:"secret,omitempty"` // true for sensitive fields (bot_token etc.) Required bool `json:"required,omitempty"` // whether this property is required } // OAuthConfigDefinition defines OAuth requirements for a channel. // Reference: P2D §3.3 type OAuthConfigDefinition struct { Provider string `json:"provider"` // "facebook", "google", "slack" etc. Scopes []string `json:"scopes"` // required OAuth scopes AuthorizeURL string `json:"authorize_url"` // OAuth authorize endpoint TokenURL string `json:"token_url"` // OAuth token exchange endpoint RefreshURL string `json:"refresh_url"` // OAuth token refresh endpoint RequiresRefresh bool `json:"requires_refresh"` // whether token needs periodic refresh TokenExpiry int `json:"token_expiry"` // token expiry in seconds (0 = no expiry) } // OAuthTokenResult contains the result of OAuth token exchange/refresh. type OAuthTokenResult struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token,omitempty"` ExpiresAt time.Time `json:"expires_at"` Scope string `json:"scope,omitempty"` Extra ChannelConfig `json:"extra,omitempty"` // provider-specific extra data } // IncomingMessage represents a message received from an external channel. // Reference: P2D §8 — unified cross-channel inbound message structure // Corresponds to Chatwoot's IncomingMessageService input type IncomingMessage struct { // Source identification ChannelType ChannelType `json:"channel_type"` SourceID string `json:"source_id"` // external message ID (TG message_id, FB mid etc.) ConversationID string `json:"conversation_id"` // external conversation/thread ID // Sender identification SenderID string `json:"sender_id"` // external sender ID (TG user_id, FB sender_id etc.) SenderName string `json:"sender_name"` // sender display name SenderType SenderType `json:"sender_type"` // contact / agent / system // Message content Content string `json:"content"` // text content ContentType ContentType `json:"content_type"` // text / image / file / audio / video / location / email Attachments []Attachment `json:"attachments"` // media attachments ReplyToID string `json:"reply_to_id,omitempty"` // replied-to message source ID // Metadata InboxID uint `json:"inbox_id"` AccountID uint `json:"account_id"` ReceivedAt time.Time `json:"received_at"` Extra ChannelConfig `json:"extra,omitempty"` // channel-specific metadata SenderExtra ChannelConfig `json:"sender_extra,omitempty"` // sender-specific metadata (e.g., Telegram user details) ConversationExtra ChannelConfig `json:"conversation_extra,omitempty"` // conversation-specific metadata (e.g., group info) } // SenderType identifies the type of message sender. type SenderType string const ( SenderContact SenderType = "contact" SenderAgent SenderType = "agent" SenderSystem SenderType = "system" ) // ContentType identifies the type of message content. // Reference: Chatwoot message_type enum + content_type classification type ContentType string const ( ContentText ContentType = "text" ContentImage ContentType = "image" ContentFile ContentType = "file" ContentAudio ContentType = "audio" ContentVideo ContentType = "video" ContentLocation ContentType = "location" ContentEmail ContentType = "email" ContentTemplate ContentType = "template" // WhatsApp template messages ) // Attachment represents a media attachment in a message. type Attachment struct { URL string `json:"url"` // download URL ContentType string `json:"content_type"` // MIME type Filename string `json:"filename,omitempty"` // original filename FileSize int64 `json:"file_size,omitempty"` // file size in bytes ThumbnailURL string `json:"thumbnail_url,omitempty"` // thumbnail URL (for images/videos) Extra ChannelConfig `json:"extra,omitempty"` // channel-specific metadata } // SendResult contains the result of an outbound message send operation. type SendResult struct { ExternalID string `json:"external_id"` // external channel message ID DeliveredAt time.Time `json:"delivered_at"` // delivery timestamp Extra ChannelConfig `json:"extra,omitempty"` // channel-specific response data } // WebhookRequest wraps an incoming webhook HTTP request. // Reference: P2D §6 — unified webhook entry point type WebhookRequest struct { ChannelType ChannelType `json:"channel_type"` // identifies which provider handles this Identifier string `json:"identifier"` // inbox identifier (TG bot token, FB page ID etc.) Headers map[string]string `json:"headers"` // HTTP headers for signature verification Body []byte `json:"body"` // raw request body QueryParams map[string]string `json:"query_params"` // URL query parameters Method string `json:"method"` // HTTP method (GET/POST) } // ContactProfile contains contact profile info fetched from external channel. type ContactProfile struct { Name string `json:"name"` AvatarURL string `json:"avatar_url,omitempty"` Extra ChannelConfig `json:"extra,omitempty"` // channel-specific profile data } // ChannelCapabilities declares what features a channel supports. // Reference: P2D §3.2 — replaces Chatwoot's per-channel feature flags type ChannelCapabilities struct { SupportsAttachments bool `json:"supports_attachments"` SupportsLocation bool `json:"supports_location"` SupportsTypingIndicator bool `json:"supports_typing_indicator"` SupportsDeliveryStatus bool `json:"supports_delivery_status"` SupportsReplies bool `json:"supports_replies"` // reply-to specific messages SupportsEmojiReactions bool `json:"supports_emoji_reactions"` SupportsVoiceMessages bool `json:"supports_voice_messages"` SupportsVideoCalls bool `json:"supports_video_calls"` SupportsCustomCards bool `json:"supports_custom_cards"` // rich message cards SupportsTemplates bool `json:"supports_templates"` // WhatsApp message templates SupportsEmailHeaders bool `json:"supports_email_headers"` // Email subject/cc/bcc MaxAttachmentSize int64 `json:"max_attachment_size"` // max attachment size in bytes MaxTextLength int `json:"max_text_length"` // max text message length }