was added firebase nitifying

This commit is contained in:
Artemy
2026-03-01 16:48:06 +07:00
parent 76d7280c71
commit eab0d746b8
60 changed files with 2335 additions and 1149 deletions
@@ -0,0 +1,107 @@
using FirebaseAdmin.Messaging;
using Govor.Application.Interfaces.PushNotifications;
using Govor.Application.Interfaces.PushNotifications.Models;
namespace Govor.Application.Services.PushNotifications.Providers;
public class FirebasePushProvider : IPushNotificationProvider
{
public string Name => "FCM";
public async Task<SendPushResult> SendToTokenAsync(string token, PushMessage message)
{
var msg = BuildFcmMessage(message, token: token);
try
{
string response = await FirebaseMessaging.DefaultInstance.SendAsync(msg);
return new SendPushResult(1, 0, []);
}
catch (FirebaseMessagingException ex)
{
if (IsInvalidTokenError(ex))
{
return new SendPushResult(0, 1, [token]);
}
throw; // return failed
}
}
public async Task<SendPushResult> SendMulticastAsync(IReadOnlyList<string> tokens, PushMessage message)
{
if (tokens.Count == 0)
return new SendPushResult(0, 0, []);
var multicastMsg = new MulticastMessage
{
Tokens = tokens.ToList(),
Notification = new Notification
{
Title = message.Title,
Body = message.Body
},
Data = message.Data,
Android = message.ChannelId != null ? new AndroidConfig
{
Notification = new AndroidNotification
{
ChannelId = message.ChannelId,
Priority = NotificationPriority.HIGH
}
} : null
};
try
{
var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(multicastMsg);
var failedTokens = new List<string>();
for (int i = 0; i < response.Responses.Count; i++)
{
if (!response.Responses[i].IsSuccess)
{
failedTokens.Add(tokens[i]);
}
}
return new SendPushResult(
response.SuccessCount,
response.FailureCount,
failedTokens
);
}
catch (Exception ex)
{
return new SendPushResult(0, tokens.Count, tokens.ToList());
}
}
private Message BuildFcmMessage(PushMessage pm, string? token = null)
{
var msg = new Message
{
Notification = new Notification { Title = pm.Title, Body = pm.Body },
Data = pm.Data,
Token = token
};
if (pm.ChannelId is not null)
{
msg.Android = new AndroidConfig
{
Notification = new AndroidNotification { ChannelId = pm.ChannelId }
};
}
return msg;
}
private static bool IsInvalidTokenError(FirebaseMessagingException ex)
{
return ex.MessagingErrorCode is MessagingErrorCode.Unregistered
or MessagingErrorCode.InvalidArgument
or MessagingErrorCode.SenderIdMismatch;
}
}
@@ -0,0 +1,19 @@
using Govor.Application.Interfaces.PushNotifications;
using Govor.Application.Interfaces.PushNotifications.Models;
namespace Govor.Application.Services.PushNotifications.Providers;
public class NullPushProvider : IPushNotificationProvider
{
public string Name => "NULL";
public Task<SendPushResult> SendToTokenAsync(string token, PushMessage message)
{
throw new NotImplementedException();
}
public Task<SendPushResult> SendMulticastAsync(IReadOnlyList<string> tokens, PushMessage message)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,95 @@
using FirebaseAdmin.Messaging;
using Govor.Application.Interfaces.PushNotifications;
using Govor.Application.Interfaces.PushNotifications.Models;
using Govor.Core.Repositories.PushTokens;
using Microsoft.Extensions.Logging;
namespace Govor.Application.Services.PushNotifications;
public class PushNotificationService : IPushNotificationService
{
private readonly IPushNotificationProvider _provider;
private readonly IPushTokenRepository _tokenRepo;
private readonly ILogger _logger;
public PushNotificationService(
IPushNotificationProvider provider,
IPushTokenRepository tokenRepo,
ILogger<PushNotificationService> logger)
{
_provider = provider;
_tokenRepo = tokenRepo;
_logger = logger;
}
public async Task SendToUserAsync(Guid userId, string title, string body, string channelId, Dictionary<string, string>? data = null)
{
var tokens = await _tokenRepo.GetActiveTokensAsync(userId);
if (tokens.Count == 0)
{
_logger.LogInformation("No active push tokens for user {UserId}", userId);
return;
}
var message = new PushMessage
{
Title = title,
Body = body,
Data = data ?? new(),
ChannelId = channelId
};
var result = await _provider.SendMulticastAsync(tokens, message);
if (result.FailureCount > 0)
{
await _tokenRepo.RemoveTokensAsync(result.FailedTokens);
_logger.LogWarning("Removed {Count} invalid FCM tokens for user {UserId}", result.FailureCount, userId);
}
}
public async Task SendToUsersAsync(IEnumerable<Guid> userIds, string title, string body, string channelId, Dictionary<string, string>? data = null)
{
if (userIds is null)
throw new ArgumentNullException(nameof(userIds));
var tokens = (await _tokenRepo.GetActiveTokensUsersAsync(userIds)).AsReadOnly();
if (tokens.Count == 0)
return;
var result = await _provider.SendMulticastAsync(tokens, new PushMessage()
{
Title = title,
Body = body,
Data = data ?? new(),
ChannelId = channelId
});
if (result.FailureCount > 0)
{
await _tokenRepo.RemoveTokensAsync(result.FailedTokens);
_logger.LogWarning("Removed {Count} invalid FCM tokens for users {Users}...", result.FailureCount, userIds);
}
}
public async Task SendToSessionAsync(Guid sessionId, string title, string body, string channelId, Dictionary<string, string>? data = null)
{
var token = await _tokenRepo.GetActiveTokenBySessionAsync(sessionId);
if(string.IsNullOrEmpty(token))
return;
var result = await _provider.SendToTokenAsync(token, new PushMessage()
{
Title = title,
Body = body,
Data = data ?? new(),
ChannelId = channelId
});
if (result.FailureCount > 0)
{
await _tokenRepo.RemoveTokensAsync(result.FailedTokens);
_logger.LogWarning("Removed {Count} invalid FCM tokens for session {Session}...", result.FailureCount, sessionId);
}
}
}