Stop repeated preview push notifications
This commit is contained in:
@@ -5,6 +5,7 @@ using QMax.Api.Data.Entities;
|
||||
using QMax.Api.Infrastructure.Hubs;
|
||||
using QMax.Api.Infrastructure.Max;
|
||||
using QMax.Api.Infrastructure.Storage;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
|
||||
namespace QMax.Api.Services;
|
||||
@@ -15,6 +16,8 @@ public sealed class MaxBridgeSyncService(
|
||||
IHubContext<QMaxHub> hubContext,
|
||||
ILogger<MaxBridgeSyncService> logger)
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DateTimeOffset> PreviewNotifications = new();
|
||||
|
||||
public async Task<int> SyncOnceAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -268,7 +271,7 @@ public sealed class MaxBridgeSyncService(
|
||||
|
||||
var previousPreview = chat.LastMessagePreview;
|
||||
var previousPreviewAt = chat.LastMessageAt;
|
||||
var previewAt = ResolveListPreviewAt(update);
|
||||
var previewAt = ResolveListPreviewAt(update, previousPreviewAt ?? chat.UpdatedAt);
|
||||
var samePreview = string.Equals(previousPreview, preview, StringComparison.Ordinal);
|
||||
var samePreviewAt = previousPreviewAt is not null &&
|
||||
Math.Abs((previousPreviewAt.Value - previewAt).TotalSeconds) < 1;
|
||||
@@ -281,11 +284,13 @@ public sealed class MaxBridgeSyncService(
|
||||
chat.LastMessageAt = previewAt;
|
||||
chat.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
|
||||
var shouldNotify = !chatWasCreated &&
|
||||
var shouldNotifyCandidate = !chatWasCreated &&
|
||||
!string.IsNullOrWhiteSpace(previousPreview) &&
|
||||
update.LastMessageIsOutgoing != true &&
|
||||
update.Messages.Count == 0 &&
|
||||
IsFreshListPreview(update, previewAt);
|
||||
var shouldNotify = shouldNotifyCandidate &&
|
||||
RememberPreviewNotification(chat.Id, preview, previewAt);
|
||||
|
||||
if (incrementUnread && shouldNotify)
|
||||
{
|
||||
@@ -313,16 +318,36 @@ public sealed class MaxBridgeSyncService(
|
||||
return true;
|
||||
}
|
||||
|
||||
private static DateTimeOffset ResolveListPreviewAt(MaxChatUpdate update)
|
||||
private static bool RememberPreviewNotification(Guid chatId, string preview, DateTimeOffset previewAt)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
if (PreviewNotifications.Count > 4096)
|
||||
{
|
||||
var cutoff = now.AddHours(-2);
|
||||
foreach (var item in PreviewNotifications)
|
||||
{
|
||||
if (item.Value < cutoff)
|
||||
{
|
||||
PreviewNotifications.TryRemove(item.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var previewMinute = previewAt.ToUniversalTime().Ticks / TimeSpan.TicksPerMinute;
|
||||
var key = $"{chatId:N}|{previewMinute}|{preview.Trim().ToLowerInvariant()}";
|
||||
return PreviewNotifications.TryAdd(key, now);
|
||||
}
|
||||
|
||||
private static DateTimeOffset ResolveListPreviewAt(MaxChatUpdate update, DateTimeOffset? fallback = null)
|
||||
{
|
||||
return TryParseMoscowListTime(update.LastMessageTimeText, out var parsed)
|
||||
? parsed
|
||||
: update.LastMessageAt ?? update.UpdatedAt;
|
||||
: fallback ?? (update.Messages.Count > 0 ? update.LastMessageAt : null) ?? update.UpdatedAt;
|
||||
}
|
||||
|
||||
private static bool IsFreshListPreview(MaxChatUpdate update, DateTimeOffset previewAt)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(update.LastMessageTimeText))
|
||||
if (!HasListClockTime(update.LastMessageTimeText))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -331,6 +356,11 @@ public sealed class MaxBridgeSyncService(
|
||||
return previewAt >= now.AddMinutes(-15) && previewAt <= now.AddMinutes(10);
|
||||
}
|
||||
|
||||
private static bool HasListClockTime(string? value)
|
||||
{
|
||||
return System.Text.RegularExpressions.Regex.IsMatch(value?.Trim() ?? "", @"\b\d{1,2}:\d{2}\b");
|
||||
}
|
||||
|
||||
private static bool TryParseMoscowListTime(string? value, out DateTimeOffset parsed)
|
||||
{
|
||||
parsed = default;
|
||||
|
||||
Reference in New Issue
Block a user