refactored notificatoin / tgsettle responsibility boundaries
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package notificationimp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *NotificationAPI) onConfirmationRequest(ctx context.Context, request *model.ConfirmationRequest) error {
|
||||
if request == nil {
|
||||
return merrors.InvalidArgument("confirmation request is nil", "request")
|
||||
}
|
||||
if a == nil || a.tg == nil {
|
||||
return merrors.Internal("telegram client is not configured")
|
||||
}
|
||||
req := normalizeConfirmationRequest(*request)
|
||||
if req.RequestID == "" {
|
||||
return merrors.InvalidArgument("confirmation request_id is required", "request_id")
|
||||
}
|
||||
if req.TargetChatID == "" {
|
||||
return merrors.InvalidArgument("confirmation target_chat_id is required", "target_chat_id")
|
||||
}
|
||||
if req.RequestedMoney == nil || strings.TrimSpace(req.RequestedMoney.Amount) == "" || strings.TrimSpace(req.RequestedMoney.Currency) == "" {
|
||||
return merrors.InvalidArgument("confirmation requested_money is required", "requested_money")
|
||||
}
|
||||
if req.SourceService == "" {
|
||||
return merrors.InvalidArgument("confirmation source_service is required", "source_service")
|
||||
}
|
||||
|
||||
prompt := confirmationPrompt(&req)
|
||||
sent, err := a.tg.SendText(ctx, req.TargetChatID, prompt, "")
|
||||
if err != nil {
|
||||
a.logger.Warn("Failed to send confirmation prompt to Telegram", zap.Error(err), zap.String("request_id", req.RequestID), zap.String("chat_id", req.TargetChatID))
|
||||
return err
|
||||
}
|
||||
if sent == nil || strings.TrimSpace(sent.MessageID) == "" {
|
||||
return merrors.Internal("telegram confirmation message id is missing")
|
||||
}
|
||||
a.logger.Info("Telegram confirmation prompt sent",
|
||||
zap.String("request_id", req.RequestID),
|
||||
zap.String("chat_id", req.TargetChatID),
|
||||
zap.String("message_id", strings.TrimSpace(sent.MessageID)))
|
||||
|
||||
if a.producer == nil {
|
||||
return merrors.Internal("messaging producer is not configured")
|
||||
}
|
||||
dispatch := &model.ConfirmationRequestDispatch{
|
||||
RequestID: req.RequestID,
|
||||
ChatID: req.TargetChatID,
|
||||
MessageID: strings.TrimSpace(sent.MessageID),
|
||||
SourceService: req.SourceService,
|
||||
Rail: req.Rail,
|
||||
}
|
||||
env := confirmations.ConfirmationDispatch(string(mservice.Notifications), dispatch, req.SourceService, req.Rail)
|
||||
if err := a.producer.SendMessage(env); err != nil {
|
||||
a.logger.Warn("Failed to publish confirmation dispatch", zap.Error(err), zap.String("request_id", req.RequestID), zap.String("message_id", dispatch.MessageID))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *NotificationAPI) onTelegramText(ctx context.Context, request *model.TelegramTextRequest) error {
|
||||
if request == nil {
|
||||
return merrors.InvalidArgument("telegram text request is nil", "request")
|
||||
}
|
||||
if a == nil || a.tg == nil {
|
||||
return merrors.Internal("telegram client is not configured")
|
||||
}
|
||||
request.ChatID = strings.TrimSpace(request.ChatID)
|
||||
request.ReplyToMessageID = strings.TrimSpace(request.ReplyToMessageID)
|
||||
request.Text = strings.TrimSpace(request.Text)
|
||||
if request.ChatID == "" {
|
||||
return merrors.InvalidArgument("telegram chat_id is required", "chat_id")
|
||||
}
|
||||
if request.Text == "" {
|
||||
return merrors.InvalidArgument("telegram text is required", "text")
|
||||
}
|
||||
if _, err := a.tg.SendText(ctx, request.ChatID, request.Text, request.ReplyToMessageID); err != nil {
|
||||
a.logger.Warn("Failed to send telegram text", zap.Error(err),
|
||||
zap.String("request_id", request.RequestID),
|
||||
zap.String("chat_id", request.ChatID),
|
||||
zap.String("reply_to_message_id", request.ReplyToMessageID))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeConfirmationRequest(request model.ConfirmationRequest) model.ConfirmationRequest {
|
||||
request.RequestID = strings.TrimSpace(request.RequestID)
|
||||
request.TargetChatID = strings.TrimSpace(request.TargetChatID)
|
||||
request.PaymentIntentID = strings.TrimSpace(request.PaymentIntentID)
|
||||
request.QuoteRef = strings.TrimSpace(request.QuoteRef)
|
||||
request.SourceService = strings.TrimSpace(request.SourceService)
|
||||
request.Rail = strings.TrimSpace(request.Rail)
|
||||
request.AcceptedUserIDs = normalizeStringList(request.AcceptedUserIDs)
|
||||
if request.RequestedMoney != nil {
|
||||
request.RequestedMoney.Amount = strings.TrimSpace(request.RequestedMoney.Amount)
|
||||
request.RequestedMoney.Currency = strings.TrimSpace(request.RequestedMoney.Currency)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
func normalizeStringList(values []string) []string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]string, 0, len(values))
|
||||
seen := map[string]struct{}{}
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[value]; ok {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
result = append(result, value)
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func confirmationPrompt(req *model.ConfirmationRequest) string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("Payment confirmation required\n")
|
||||
if req.QuoteRef != "" {
|
||||
builder.WriteString("Quote ref: ")
|
||||
builder.WriteString(req.QuoteRef)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
if req.RequestedMoney != nil {
|
||||
amountFloat, err := strconv.ParseFloat(req.RequestedMoney.Amount, 64)
|
||||
if err != nil {
|
||||
amountFloat = 0
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf("\n*Requested: %.2f %s*\n\n", amountFloat, req.RequestedMoney.Currency))
|
||||
}
|
||||
builder.WriteString("Reply with \"<amount> <currency>\" (e.g., 12.34 USD).")
|
||||
return builder.String()
|
||||
}
|
||||
Reference in New Issue
Block a user