package v1 import ( "encoding/json" "os" "strings" "github.com/gochat/gochat/internal/model" "gorm.io/gorm" ) func inboxListResponse(inboxes []model.Inbox, db *gorm.DB, isAdmin bool) map[string]any { payload := make([]any, 0, len(inboxes)) for i := range inboxes { payload = append(payload, serializeInbox(&inboxes[i], db, isAdmin)) } return map[string]any{"payload": payload} } func serializeInbox(inbox *model.Inbox, db *gorm.DB, isAdmin bool) map[string]any { config := parseInboxChannelConfig(inbox.ChannelConfig) payload := map[string]any{ "id": inbox.ID, "avatar_url": inbox.AvatarURL, "channel_id": inbox.ChannelID, "name": inbox.Name, "channel_type": chatwootChannelType(inbox.ChannelType), "greeting_enabled": inbox.GreetingEnabled, "greeting_message": inbox.GreetingMessage, "working_hours_enabled": inbox.WorkingHoursEnabled, "enable_email_collect": inbox.EnableEmailCollect, "csat_survey_enabled": inbox.CsatSurveyEnabled, "csat_config": jsonStringObject(inbox.CsatConfig), "enable_auto_assignment": inbox.EnableAutoAssignment, "auto_assignment_config": configValue(config, "auto_assignment_config"), "out_of_office_message": inbox.OutOfOfficeMessage, "working_hours": serializeInboxWorkingHours(inbox, config), "timezone": inbox.Timezone, "callback_webhook_url": callbackWebhookURL(inbox, config), "allow_messages_after_resolved": inbox.AllowMessagesAfterResolved, "lock_to_single_conversation": inbox.LockToSingleConversation, "sender_name_type": chatwootSenderNameType(inbox.SenderNameType), "business_name": inbox.BusinessName, "allowed_domains": configValue(config, "allowed_domains"), "widget_color": configValue(config, "widget_color"), "website_url": configValue(config, "website_url"), "hmac_mandatory": configValue(config, "hmac_mandatory"), "welcome_title": configValue(config, "welcome_title"), "welcome_tagline": configValue(config, "welcome_tagline"), "web_widget_script": configValue(config, "web_widget_script"), "website_token": configValue(config, "website_token"), "selected_feature_flags": configValue(config, "selected_feature_flags"), "reply_time": configValue(config, "reply_time"), "provider": configValue(config, "provider"), } switch chatwootChannelType(inbox.ChannelType) { case "Channel::WebWidget": if isAdmin { payload["hmac_token"] = configValue(config, "hmac_token") } payload["pre_chat_form_enabled"] = configValue(config, "pre_chat_form_enabled") payload["pre_chat_form_options"] = configValue(config, "pre_chat_form_options") payload["continuity_via_email"] = configValue(config, "continuity_via_email") case "Channel::Api": if isAdmin { payload["hmac_token"] = configValue(config, "hmac_token") payload["secret"] = inbox.Secret } payload["webhook_url"] = inbox.WebhookURL payload["inbox_identifier"] = firstConfigValue(config, "inbox_identifier", "identifier") payload["additional_attributes"] = configValue(config, "additional_attributes") case "Channel::Telegram": payload["bot_name"] = configValue(config, "bot_name") case "Channel::FacebookPage": payload["page_id"] = configValue(config, "page_id") payload["reauthorization_required"] = configValue(config, "reauthorization_required") case "Channel::Instagram": payload["instagram_id"] = configValue(config, "instagram_id") payload["reauthorization_required"] = configValue(config, "reauthorization_required") case "Channel::TwitterProfile": payload["tweets_enabled"] = configBoolDefault(config, "tweets_enabled", true) case "Channel::Tiktok": payload["reauthorization_required"] = configValue(config, "reauthorization_required") case "Channel::TwilioSms": payload["messaging_service_sid"] = configValue(config, "messaging_service_sid") payload["phone_number"] = configValue(config, "phone_number") payload["medium"] = configValue(config, "medium") payload["content_templates"] = configValue(config, "content_templates") if isAdmin { payload["auth_token"] = configValue(config, "auth_token") payload["account_sid"] = configValue(config, "account_sid") payload["api_key_sid"] = configValue(config, "api_key_sid") } payload["voice_enabled"] = configValue(config, "voice_enabled") voiceConfigured := configStringPresent(config, "twiml_app_sid") payload["voice_configured"] = voiceConfigured payload["has_api_key_secret"] = configStringPresent(config, "api_key_secret") if voiceConfigured { payload["voice_call_webhook_url"] = configValue(config, "voice_call_webhook_url") payload["voice_status_webhook_url"] = configValue(config, "voice_status_webhook_url") } case "Channel::Email": payload["email"] = configValue(config, "email") forwardingEnabled := strings.TrimSpace(os.Getenv("MAILER_INBOUND_EMAIL_DOMAIN")) != "" payload["forwarding_enabled"] = forwardingEnabled if forwardingEnabled { payload["forward_to_email"] = configValue(config, "forward_to_email") } if isAdmin { payload["imap_login"] = configValue(config, "imap_login") payload["imap_password"] = configValue(config, "imap_password") payload["imap_address"] = configValue(config, "imap_address") payload["imap_port"] = configValue(config, "imap_port") payload["imap_enabled"] = configValue(config, "imap_enabled") payload["imap_enable_ssl"] = configValue(config, "imap_enable_ssl") payload["imap_authentication"] = configValue(config, "imap_authentication") payload["smtp_login"] = configValue(config, "smtp_login") payload["smtp_password"] = configValue(config, "smtp_password") payload["smtp_address"] = configValue(config, "smtp_address") payload["smtp_port"] = configValue(config, "smtp_port") payload["smtp_enabled"] = configValue(config, "smtp_enabled") payload["smtp_domain"] = configValue(config, "smtp_domain") payload["smtp_enable_ssl_tls"] = configValue(config, "smtp_enable_ssl_tls") payload["smtp_enable_starttls_auto"] = configValue(config, "smtp_enable_starttls_auto") payload["smtp_openssl_verify_mode"] = configValue(config, "smtp_openssl_verify_mode") payload["smtp_authentication"] = configValue(config, "smtp_authentication") if emailOauthProvider(config) { payload["reauthorization_required"] = configObjectEmpty(config, "provider_config") || configBoolDefault(config, "reauthorization_required", false) } } case "Channel::Whatsapp": payload["phone_number"] = configValue(config, "phone_number") payload["message_templates"] = configValue(config, "message_templates") if isAdmin { payload["provider_config"] = configValue(config, "provider_config") } payload["reauthorization_required"] = configValue(config, "reauthorization_required") payload["voice_enabled"] = configValue(config, "voice_enabled") case "Channel::Line": payload["line_channel_id"] = configValue(config, "line_channel_id") payload["line_channel_secret"] = configValue(config, "line_channel_secret") payload["line_channel_token"] = configValue(config, "line_channel_token") case "Channel::Sms": payload["phone_number"] = configValue(config, "phone_number") payload["provider_config"] = configValue(config, "provider_config") } if db != nil && inbox.PortalID != nil && *inbox.PortalID != 0 { var portal model.Portal if err := db.Where("id = ?", *inbox.PortalID).First(&portal).Error; err == nil { payload["help_center"] = map[string]any{ "name": portal.Name, "slug": portal.Slug, } } } return payload } func serializeInboxWorkingHours(inbox *model.Inbox, config map[string]any) any { if len(inbox.WorkingHours) == 0 { return configArray(config, "working_hours") } payload := make([]any, 0, len(inbox.WorkingHours)) for _, wh := range inbox.WorkingHours { payload = append(payload, map[string]any{ "day_of_week": wh.DayOfWeek, "closed_all_day": wh.ClosedAllDay, "open_hour": wh.OpenHour, "open_minutes": wh.OpenMinutes, "close_hour": wh.CloseHour, "close_minutes": wh.CloseMinutes, "open_all_day": wh.OpenAllDay, }) } return payload } func chatwootChannelType(channelType string) string { if strings.HasPrefix(channelType, "Channel::") { return channelType } aliases := map[string]string{ "web_widget": "Channel::WebWidget", "facebook": "Channel::FacebookPage", "instagram": "Channel::Instagram", "twitter": "Channel::TwitterProfile", "twilio_sms": "Channel::TwilioSms", "whatsapp": "Channel::Whatsapp", "api": "Channel::Api", "email": "Channel::Email", "telegram": "Channel::Telegram", "line": "Channel::Line", "sms": "Channel::Sms", "tiktok": "Channel::Tiktok", } if mapped, ok := aliases[channelType]; ok { return mapped } return channelType } func chatwootSenderNameType(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { case "", "friendly", "friendly_name": return "friendly" case "professional", "business_name": return "professional" default: return value } } func callbackWebhookURL(inbox *model.Inbox, config map[string]any) any { baseURL := strings.TrimRight(os.Getenv("FRONTEND_URL"), "/") switch chatwootChannelType(inbox.ChannelType) { case "Channel::TwilioSms": return baseURL + "/twilio/callback" case "Channel::Sms": phoneNumber := strings.TrimPrefix(inboxConfigString(config, "phone_number"), "+") return baseURL + "/webhooks/sms/" + phoneNumber case "Channel::Line": return baseURL + "/webhooks/line/" + inboxConfigString(config, "line_channel_id") case "Channel::Whatsapp": return baseURL + "/webhooks/whatsapp/" + inboxConfigString(config, "phone_number") default: return nil } } func parseInboxChannelConfig(raw string) map[string]any { if raw == "" { return map[string]any{} } var config map[string]any if err := json.Unmarshal([]byte(raw), &config); err != nil { return map[string]any{} } return config } func configValue(config map[string]any, key string) any { if value, ok := config[key]; ok { return value } return nil } func inboxConfigString(config map[string]any, key string) string { value, ok := config[key] if !ok || value == nil { return "" } if s, ok := value.(string); ok { return s } return "" } func firstConfigValue(config map[string]any, keys ...string) any { for _, key := range keys { if value, ok := config[key]; ok { return value } } return nil } func configBoolDefault(config map[string]any, key string, fallback bool) bool { value, ok := config[key] if !ok || value == nil { return fallback } if parsed, ok := value.(bool); ok { return parsed } return fallback } func configStringPresent(config map[string]any, key string) bool { value, ok := config[key] if !ok || value == nil { return false } if s, ok := value.(string); ok { return strings.TrimSpace(s) != "" } return true } func configObjectEmpty(config map[string]any, key string) bool { value, ok := config[key] if !ok || value == nil { return true } if object, ok := value.(map[string]any); ok { return len(object) == 0 } return false } func emailOauthProvider(config map[string]any) bool { provider, _ := config["provider"].(string) switch strings.ToLower(strings.TrimSpace(provider)) { case "microsoft", "google", "legacy_google": return true default: return false } } func configArray(config map[string]any, key string) any { if value, ok := config[key]; ok { return value } return []any{} } func jsonStringObject(raw string) any { if raw == "" { return map[string]any{} } var value any if err := json.Unmarshal([]byte(raw), &value); err != nil { return map[string]any{} } return value }