chore: import clean Agent Desk snapshot (3/7)
This commit is contained in:
150
internal/services/telegram_outbound_service.go
Normal file
150
internal/services/telegram_outbound_service.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/repositories"
|
||||
"agent-desk/internal/telegram"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
)
|
||||
|
||||
const (
|
||||
telegramOutboxBatchSize = 20
|
||||
telegramOutboxMaxRetry = 5
|
||||
)
|
||||
|
||||
var TelegramOutboundService = newTelegramOutboundService()
|
||||
|
||||
func newTelegramOutboundService() *telegramOutboundService {
|
||||
return &telegramOutboundService{}
|
||||
}
|
||||
|
||||
type telegramOutboundService struct{}
|
||||
|
||||
func (s *telegramOutboundService) DispatchPendingOutbox() int {
|
||||
return s.doDispatchPendingOutbox(telegramOutboxBatchSize)
|
||||
}
|
||||
|
||||
func (s *telegramOutboundService) doDispatchPendingOutbox(limit int) int {
|
||||
if limit <= 0 {
|
||||
limit = telegramOutboxBatchSize
|
||||
}
|
||||
items := ChannelMessageOutboxService.ListPending(enums.ChannelTypeTelegram, limit)
|
||||
if len(items) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
successCount := 0
|
||||
for i := range items {
|
||||
if err := s.processOutbox(items[i].ID); err != nil {
|
||||
slog.Warn("process telegram outbox failed",
|
||||
"outbox_id", items[i].ID,
|
||||
"error", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
successCount++
|
||||
}
|
||||
return successCount
|
||||
}
|
||||
|
||||
func (s *telegramOutboundService) processOutbox(outboxID int64) error {
|
||||
outbox := ChannelMessageOutboxService.Get(outboxID)
|
||||
if outbox == nil {
|
||||
return nil
|
||||
}
|
||||
if outbox.ChannelType != enums.ChannelTypeTelegram {
|
||||
return nil
|
||||
}
|
||||
if outbox.SendStatus == string(enums.ChannelMessageOutboxStatusSent) {
|
||||
return nil
|
||||
}
|
||||
if outbox.NextRetryAt != nil && outbox.NextRetryAt.After(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ChannelMessageOutboxService.Updates(outbox.ID, map[string]any{
|
||||
"send_status": string(enums.ChannelMessageOutboxStatusSending),
|
||||
"updated_at": time.Now(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message := MessageService.Get(outbox.MessageID)
|
||||
if message == nil {
|
||||
return s.markOutboxFailed(outbox, "message not found")
|
||||
}
|
||||
conversation := ConversationService.Get(outbox.ConversationID)
|
||||
if conversation == nil {
|
||||
return s.markOutboxFailed(outbox, "conversation not found")
|
||||
}
|
||||
|
||||
channel := ChannelService.Get(conversation.ChannelID)
|
||||
if channel == nil || channel.Status != enums.StatusOk {
|
||||
return s.markOutboxFailed(outbox, "telegram channel not found or disabled")
|
||||
}
|
||||
cfg, err := ChannelService.ParseTelegramChannelConfig(channel.ConfigJSON)
|
||||
if err != nil || cfg == nil || cfg.BotToken == "" {
|
||||
return s.markOutboxFailed(outbox, "telegram bot token not configured")
|
||||
}
|
||||
|
||||
// Resolve target Telegram ChatID
|
||||
var chatID int64
|
||||
customerIdentity := repositories.CustomerIdentityRepository.FindOne(sqls.DB(), sqls.NewCnd().
|
||||
Eq("customer_id", conversation.CustomerID).
|
||||
Eq("external_source", enums.ExternalSourceTelegram))
|
||||
if customerIdentity != nil {
|
||||
if id, err := strconv.ParseInt(customerIdentity.ExternalID, 10, 64); err == nil {
|
||||
chatID = id
|
||||
}
|
||||
}
|
||||
if chatID == 0 {
|
||||
return s.markOutboxFailed(outbox, "unable to resolve telegram chat_id")
|
||||
}
|
||||
|
||||
// Send message via Telegram Client
|
||||
client := telegram.NewClient(cfg.BotToken)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, sendErr := client.SendMessage(ctx, telegram.SendMessageRequest{
|
||||
ChatID: chatID,
|
||||
Text: message.Content,
|
||||
})
|
||||
|
||||
if sendErr != nil {
|
||||
return s.markOutboxFailed(outbox, sendErr.Error())
|
||||
}
|
||||
|
||||
return ChannelMessageOutboxService.Updates(outbox.ID, map[string]any{
|
||||
"send_status": string(enums.ChannelMessageOutboxStatusSent),
|
||||
"sent_at": time.Now(),
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *telegramOutboundService) markOutboxFailed(outbox *models.ChannelMessageOutbox, errMsg string) error {
|
||||
if outbox == nil {
|
||||
return nil
|
||||
}
|
||||
retryCount := outbox.RetryCount + 1
|
||||
status := string(enums.ChannelMessageOutboxStatusFailed)
|
||||
if retryCount >= telegramOutboxMaxRetry {
|
||||
status = string(enums.ChannelMessageOutboxStatusIgnored)
|
||||
}
|
||||
nextRetryAt := time.Now().Add(time.Duration(retryCount*30) * time.Second)
|
||||
|
||||
return ChannelMessageOutboxService.Updates(outbox.ID, map[string]any{
|
||||
"send_status": status,
|
||||
"retry_count": retryCount,
|
||||
"next_retry_at": &nextRetryAt,
|
||||
"last_error": errMsg,
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user