147 lines
5.0 KiB
Go
147 lines
5.0 KiB
Go
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
|
|
}
|