Add Telegram notification for news publication to users
- Implemented NotifyUsersNewsPublishedAsync method in TelegramNotifier to send notifications to activated Telegram users when a news post is published. - Updated CreatePostCommandHandler to invoke the new Telegram notification method after creating a news post. - Enhanced IIdentityService to retrieve activated linked Telegram user IDs for notifications. - Updated ITelegramNotifier interface to include the new notification method documentation.
This commit is contained in:
@@ -97,6 +97,35 @@ internal sealed class TelegramNotifier(ITelegramBotClient botClient, IIdentitySe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task NotifyUsersNewsPublishedAsync(string title, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var text = $"📰 Новая новость: <b>{Escape(title)}</b>";
|
||||||
|
|
||||||
|
InlineKeyboardMarkup? keyboard = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.Value.PublicSiteUrl))
|
||||||
|
{
|
||||||
|
var url = $"{options.Value.PublicSiteUrl.TrimEnd('/')}/news";
|
||||||
|
keyboard = new InlineKeyboardMarkup(new[] { InlineKeyboardButton.WithUrl("🌐 Открыть на сайте", url) });
|
||||||
|
}
|
||||||
|
|
||||||
|
var telegramUserIds = await identityService.GetActivatedLinkedTelegramUserIdsAsync(cancellationToken);
|
||||||
|
foreach (var telegramUserId in telegramUserIds)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await botClient.SendMessage(
|
||||||
|
telegramUserId, text, parseMode: ParseMode.Html, replyMarkup: keyboard, cancellationToken: cancellationToken);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Пользователь мог заблокировать бота — не критично, продолжаем рассылку остальным.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task NotifyUserAsync(Guid userId, string message, CancellationToken cancellationToken)
|
public async Task NotifyUserAsync(Guid userId, string message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
|
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using PnvPanel.Domain.News;
|
|||||||
|
|
||||||
namespace PnvPanel.Application.Admin.News;
|
namespace PnvPanel.Application.Admin.News;
|
||||||
|
|
||||||
public sealed class CreatePostCommandHandler(IAppDbContext dbContext, IRealtimeNotifier notifier)
|
public sealed class CreatePostCommandHandler(IAppDbContext dbContext, IRealtimeNotifier notifier, ITelegramNotifier telegramNotifier)
|
||||||
: ICommandHandler<CreatePostCommand, Result<NewsPostDto>>
|
: ICommandHandler<CreatePostCommand, Result<NewsPostDto>>
|
||||||
{
|
{
|
||||||
public async Task<Result<NewsPostDto>> Handle(CreatePostCommand command, CancellationToken cancellationToken)
|
public async Task<Result<NewsPostDto>> Handle(CreatePostCommand command, CancellationToken cancellationToken)
|
||||||
@@ -15,6 +15,7 @@ public sealed class CreatePostCommandHandler(IAppDbContext dbContext, IRealtimeN
|
|||||||
dbContext.NewsPosts.Add(post);
|
dbContext.NewsPosts.Add(post);
|
||||||
|
|
||||||
await notifier.NotifyNewsPublishedAsync(post.Id, post.Title, post.CreatedAt, cancellationToken);
|
await notifier.NotifyNewsPublishedAsync(post.Id, post.Title, post.CreatedAt, cancellationToken);
|
||||||
|
await telegramNotifier.NotifyUsersNewsPublishedAsync(post.Title, cancellationToken);
|
||||||
|
|
||||||
return Result.Success(NewsPostDto.FromDomain(post));
|
return Result.Success(NewsPostDto.FromDomain(post));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,4 +60,8 @@ public interface IIdentityService
|
|||||||
Task<Guid?> FindUserIdByTelegramUserIdAsync(long telegramUserId, CancellationToken cancellationToken);
|
Task<Guid?> FindUserIdByTelegramUserIdAsync(long telegramUserId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task<TelegramLinkInfo> GetTelegramLinkInfoAsync(Guid userId, CancellationToken cancellationToken);
|
Task<TelegramLinkInfo> GetTelegramLinkInfoAsync(Guid userId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>Telegram ID активных (активированных, не заблокированных) пользователей с привязкой —
|
||||||
|
/// для рассылки уведомлений вроде публикации новости.</summary>
|
||||||
|
Task<IReadOnlyCollection<long>> GetActivatedLinkedTelegramUserIdsAsync(CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,4 +21,8 @@ public interface ITelegramNotifier
|
|||||||
/// <summary>Заявка на роль — инлайн-кнопки «Одобрить/Отклонить», решается полностью в Telegram.</summary>
|
/// <summary>Заявка на роль — инлайн-кнопки «Одобрить/Отклонить», решается полностью в Telegram.</summary>
|
||||||
Task NotifyAdminsRoleRequestCreatedAsync(
|
Task NotifyAdminsRoleRequestCreatedAsync(
|
||||||
Guid ticketId, string userName, string roleDescription, string justification, CancellationToken cancellationToken);
|
Guid ticketId, string userName, string roleDescription, string justification, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>Рассылка о публикации новости всем активированным пользователям с привязанным Telegram
|
||||||
|
/// (кнопка-ссылка на сайт, где новость показана целиком).</summary>
|
||||||
|
Task NotifyUsersNewsPublishedAsync(string title, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,6 +252,14 @@ internal sealed class IdentityService(UserManager<AppUser> userManager, SignInMa
|
|||||||
: new TelegramLinkInfo(false, null, null);
|
: new TelegramLinkInfo(false, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<long>> GetActivatedLinkedTelegramUserIdsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await userManager.Users.AsNoTracking()
|
||||||
|
.Where(u => u.TelegramUserId != null && u.IsActivated && !u.IsBlocked)
|
||||||
|
.Select(u => u.TelegramUserId!.Value)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> GetPrimaryRoleNameAsync(AppUser user)
|
private async Task<string> GetPrimaryRoleNameAsync(AppUser user)
|
||||||
{
|
{
|
||||||
var roles = await userManager.GetRolesAsync(user);
|
var roles = await userManager.GetRolesAsync(user);
|
||||||
|
|||||||
Reference in New Issue
Block a user