chore: import clean Agent Desk snapshot (3/7)

This commit is contained in:
2026-10-16 11:58:36 +00:00
parent 89c5231c4c
commit fe4a99cf4b
127 changed files with 29641 additions and 0 deletions

View 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(),
})
}