Add SQL outbox for MAX sends

This commit is contained in:
sevenhill
2026-07-05 13:47:07 +03:00
parent 5d33320e3d
commit 6dd5c7c6fb
8 changed files with 462 additions and 123 deletions
+2 -58
View File
@@ -223,21 +223,14 @@ public sealed class ChatsController(
return BadRequest("Chat is not linked to MAX.");
}
var result = await maxBridge.SendTextAsync(chat.ExternalId, text, cancellationToken);
if (!result.Success)
{
return MaxSendFailure(result, "text message");
}
var message = new Message
{
ChatId = chat.Id,
ExternalId = result.ExternalMessageId,
Direction = MessageDirection.Outgoing,
Text = text,
ReplyToMessageId = request.ReplyToMessageId,
SentAt = DateTimeOffset.UtcNow,
DeliveryState = MessageDeliveryState.Sent,
DeliveryState = MessageDeliveryState.Sending,
SenderName = "You"
};
db.Messages.Add(message);
@@ -507,14 +500,6 @@ public sealed class ChatsController(
});
}
var result = await SendForwardedToMaxAsync(target.ExternalId, message, cancellationToken);
if (!result.Success)
{
return MaxSendFailure(result, "forward");
}
message.ExternalId = result.ExternalMessageId;
message.DeliveryState = MessageDeliveryState.Sent;
db.Messages.Add(message);
target.LastMessagePreview = ForwardPreview(message);
target.LastMessageAt = message.SentAt;
@@ -548,21 +533,15 @@ public sealed class ChatsController(
}
var stored = await storage.SaveAsync(file, cancellationToken);
var result = await maxBridge.SendAttachmentAsync(chat.ExternalId, storage.GetPath(stored.StorageFileName), caption ?? "", cancellationToken);
if (!result.Success)
{
return MaxSendFailure(result, "attachment");
}
var message = new Message
{
ChatId = chat.Id,
ExternalId = result.ExternalMessageId,
Direction = MessageDirection.Outgoing,
Text = caption,
ReplyToMessageId = replyToMessageId,
SentAt = DateTimeOffset.UtcNow,
DeliveryState = MessageDeliveryState.Sent,
DeliveryState = MessageDeliveryState.Sending,
SenderName = "You",
Attachments =
[
@@ -652,13 +631,6 @@ public sealed class ChatsController(
return PhysicalFile(path, attachment.ContentType, attachment.OriginalFileName, enableRangeProcessing: true);
}
private ObjectResult MaxSendFailure(MaxSendResult result, string itemName)
{
return Problem(
result.Error ?? $"MAX did not accept the {itemName}.",
statusCode: StatusCodes.Status502BadGateway);
}
private async Task<ActionResult?> TryApplyMaxActionAsync(
Message message,
Func<string, string, Task<MaxActionResult>> action,
@@ -750,34 +722,6 @@ public sealed class ChatsController(
throw new InvalidOperationException($"Cannot forward attachment {attachment.Id}: content is unavailable.");
}
private async Task<MaxSendResult> SendForwardedToMaxAsync(string externalChatId, Message message, CancellationToken cancellationToken)
{
var attachments = message.Attachments.OrderBy(x => x.SortOrder).ToArray();
if (attachments.Length == 0)
{
return string.IsNullOrWhiteSpace(message.Text)
? new MaxSendResult(false, null, "Forwarded message is empty.")
: await maxBridge.SendTextAsync(externalChatId, message.Text, cancellationToken);
}
MaxSendResult? lastResult = null;
var errors = new List<string>();
foreach (var attachment in attachments)
{
var caption = attachment.SortOrder == 0 ? message.Text ?? "" : "";
var result = await maxBridge.SendAttachmentAsync(externalChatId, storage.GetPath(attachment.StorageFileName), caption, cancellationToken);
lastResult = result;
if (!result.Success && !string.IsNullOrWhiteSpace(result.Error))
{
errors.Add(result.Error);
}
}
return errors.Count == 0
? new MaxSendResult(true, lastResult?.ExternalMessageId, null)
: new MaxSendResult(false, lastResult?.ExternalMessageId, string.Join("; ", errors.Distinct()));
}
private static string? ResolveForwardedFrom(Message source)
{
return source.SenderName?.Trim().Length > 0