package service import ( "context" "encoding/json" "fmt" "github.com/gochat/gochat/internal/model" "github.com/gochat/gochat/internal/repository" applogger "github.com/gochat/gochat/pkg/logger" ) // SlackIntegrationService implements Slack integration business logic. // Reference: Chatwoot Integrations::SlackController + SlackService // Slack integration sends conversation notifications to a Slack channel and // supports Slack slash commands for ticket creation. type SlackIntegrationService struct { hookRepo *repository.IntegrationHookRepo } // NewSlackIntegrationService creates a new SlackIntegrationService. func NewSlackIntegrationService(hookRepo *repository.IntegrationHookRepo) *SlackIntegrationService { return &SlackIntegrationService{hookRepo: hookRepo} } // CreateSlackRequest is the DTO for creating/updating a Slack integration. // Reference: Chatwoot SlackController#create — params: {channel_id, channel_name, slack_token} type CreateSlackRequest struct { ChannelID string `json:"channel_id" validate:"required"` ChannelName string `json:"channel_name,omitempty"` SlackToken string `json:"slack_token,omitempty"` } // UpdateSlackRequest is the DTO for updating a Slack integration. type UpdateSlackRequest struct { ChannelID string `json:"channel_id,omitempty"` ChannelName string `json:"channel_name,omitempty"` SlackToken string `json:"slack_token,omitempty"` } // Create creates a Slack integration hook for an account. func (s *SlackIntegrationService) Create(ctx context.Context, accountID uint, req CreateSlackRequest) (*model.IntegrationHook, error) { settings := model.SlackSettings{ ChannelID: req.ChannelID, ChannelName: req.ChannelName, SlackToken: req.SlackToken, } settingsJSON, err := json.Marshal(settings) if err != nil { return nil, fmt.Errorf("failed to marshal Slack settings: %w", err) } hook := &model.IntegrationHook{ AccountID: accountID, HookType: model.HookTypeSlack, Status: model.HookStatusActive, Settings: settingsJSON, } if err := s.hookRepo.Create(ctx, hook); err != nil { return nil, fmt.Errorf("failed to create Slack integration: %w", err) } applogger.L().Infof("Slack integration created: account=%d, channel=%s", accountID, req.ChannelID) return hook, nil } // Update updates a Slack integration hook. func (s *SlackIntegrationService) Update(ctx context.Context, accountID uint, req UpdateSlackRequest) (*model.IntegrationHook, error) { hooks, err := s.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeSlack) if err != nil || len(hooks) == 0 { return nil, fmt.Errorf("Slack integration not found for account %d", accountID) } hook := &hooks[0] // Update settings var settings model.SlackSettings if hook.Settings != nil { if err := json.Unmarshal(hook.Settings, &settings); err != nil { return nil, fmt.Errorf("failed to unmarshal existing Slack settings: %w", err) } } if req.ChannelID != "" { settings.ChannelID = req.ChannelID } if req.ChannelName != "" { settings.ChannelName = req.ChannelName } if req.SlackToken != "" { settings.SlackToken = req.SlackToken } settingsJSON, err := json.Marshal(settings) if err != nil { return nil, fmt.Errorf("failed to marshal Slack settings: %w", err) } hook.Settings = settingsJSON if err := s.hookRepo.Update(ctx, hook); err != nil { return nil, fmt.Errorf("failed to update Slack integration: %w", err) } applogger.L().Infof("Slack integration updated: account=%d", accountID) return hook, nil } // Delete removes a Slack integration hook for an account. func (s *SlackIntegrationService) Delete(ctx context.Context, accountID uint) error { hooks, err := s.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeSlack) if err != nil || len(hooks) == 0 { return fmt.Errorf("Slack integration not found for account %d", accountID) } for _, hook := range hooks { if err := s.hookRepo.Delete(ctx, hook.ID); err != nil { return fmt.Errorf("failed to delete Slack integration: %w", err) } } applogger.L().Infof("Slack integration deleted: account=%d", accountID) return nil } // ListAllChannels returns available Slack channels (proxy to Slack API). // GET /api/v1/accounts/:account_id/integrations/slack/list_all_channels func (s *SlackIntegrationService) ListAllChannels(ctx context.Context, accountID uint) ([]map[string]interface{}, error) { hooks, err := s.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeSlack) if err != nil || len(hooks) == 0 { return nil, fmt.Errorf("Slack integration not found for account %d", accountID) } var settings model.SlackSettings if err := json.Unmarshal(hooks[0].Settings, &settings); err != nil { return nil, fmt.Errorf("failed to unmarshal Slack settings: %w", err) } // In production, this would call the Slack API to list channels using settings.SlackToken. // Production note: when Slack OAuth is wired, this returns real channel list from Slack API. applogger.L().Infof("Listing Slack channels for account=%d", accountID) return []map[string]interface{}{ {"id": settings.ChannelID, "name": settings.ChannelName}, }, nil }