nazarovsa/subscription_commands (#6)

Reviewed-on: #6
Co-authored-by: Sergey Nazarov <insight.appdev@gmail.com>
Co-committed-by: Sergey Nazarov <insight.appdev@gmail.com>
This commit is contained in:
Sergey Nazarov 2024-04-16 13:03:31 +00:00 committed by nazarovsa
parent 43dbf5b724
commit 5e99e802ec
33 changed files with 592 additions and 122 deletions

View File

@ -0,0 +1,27 @@
using Insight.TelegramBot;
using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
namespace Nocr.TelegramClient.AppServices.Bots;
public static class BotExtensions
{
public static async Task EditOrSendTextMessage(this IBot bot,
int messageId,
TextMessage message,
ILogger? logger = null,
CancellationToken cancellationToken = default)
{
try
{
await bot.EditMessageTextAsync(messageId, message, cancellationToken);
return;
}
catch (Exception ex)
{
logger?.LogWarning(ex, "Не удалось отредактировать текстовое сообщение");
}
await bot.SendMessageAsync(message, CancellationToken.None);
}
}

View File

@ -0,0 +1,30 @@
using Insight.TelegramBot;
namespace Nocr.TelegramClient.AppServices.Bots;
public class NocrCallbackData : CallbackData<NocrState>
{
public NocrCallbackData(NocrState nextState, params string[] args) : base(nextState, args)
{
}
public static NocrCallbackData ActivateSubscription(long subscriptionId)
{
return new NocrCallbackData(NocrState.ActivateSubscription, subscriptionId.ToString());
}
public static NocrCallbackData DeactivateSubscription(long subscriptionId)
{
return new NocrCallbackData(NocrState.DeactivateSubscription, subscriptionId.ToString());
}
public static NocrCallbackData DeleteSubscription(long subscriptionId)
{
return new NocrCallbackData(NocrState.DeleteSubscription, subscriptionId.ToString());
}
public static NocrCallbackData ViewSubscription(long subscriptionId)
{
return new NocrCallbackData(NocrState.ViewSubscription, subscriptionId.ToString());
}
}

View File

@ -0,0 +1,12 @@
namespace Nocr.TelegramClient.AppServices.Bots;
public enum NocrState
{
// Commands
ActivateSubscription = 1,
DeactivateSubscription = 2,
DeleteSubscription = 3,
// States
ViewSubscription = 4
}

View File

@ -0,0 +1,132 @@
using FormatWith;
using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Keyboards;
using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Matches;
using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
namespace Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
public abstract class ViewSubscriptionHandlerBase
{
protected ILogger Logger { get; }
protected IBot Bot { get; }
protected ITextSubscriptionsController SubscriptionsController { get; }
protected ILocalizer Localizer { get; }
protected IMessageDispatcherQueue MessageQueue { get; }
protected ViewSubscriptionHandlerBase(ILogger logger, ILocalizer localizer, IMessageDispatcherQueue messageQueue,
IBot bot, ITextSubscriptionsController subscriptionsController)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
Bot = bot ?? throw new ArgumentNullException(nameof(bot));
SubscriptionsController =
subscriptionsController ?? throw new ArgumentNullException(nameof(subscriptionsController));
Localizer = localizer ?? throw new ArgumentNullException(nameof(localizer));
MessageQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue));
}
public async Task SendSubscriptionMessage(long receiverId, TextSubscriptionData subscription,
CancellationToken cancellationToken = default)
{
var message = await GetMessage(receiverId, subscription, cancellationToken);
MessageQueue.Enqueue(message);
}
public async Task EditSubscriptionMessage(long receiverId, int messageId, long subscriptionId,
CancellationToken cancellationToken = default)
{
var subscription = await SubscriptionsController.GetById(subscriptionId, cancellationToken);
var message = await GetMessage(receiverId, subscription, cancellationToken);
await Bot.EditOrSendTextMessage(messageId, message, Logger, cancellationToken);
}
private async Task<TextMessage> GetMessage(long receiverId, TextSubscriptionData subscription,
CancellationToken cancellationToken = default)
{
var subscriptions = await SubscriptionsController.GetByUserId(subscription.UserId, cancellationToken);
var ordered = subscriptions.OrderBy(x => x.Id).ToList();
var indexOf = ordered.FindIndex(x => x.Id == subscription.Id);
long? prevId = indexOf > 0 ? ordered[indexOf - 1].Id : null;
long? nextId = indexOf < ordered.Count - 1 ? ordered[indexOf + 1].Id : null;
var textMessage = new TextMessage(receiverId)
{
Text = GetText(subscription),
ParseMode = ParseMode.Html,
ReplyMarkup = BuildKeyBoard(subscription, prevId, nextId).InlineKeyboardMarkup
};
return textMessage;
}
private string GetText(TextSubscriptionData subscription)
{
var activeText = subscription.Active
? Localizer.Get(nameof(ViewSubscriptionHandlerBase), "Active")
: Localizer.Get(nameof(ViewSubscriptionHandlerBase), "Inactive");
var text = Localizer.Get(nameof(ViewSubscriptionHandlerBase), "Text")
.FormatWith(new
{
subscription.Id,
subscription.ChatUsername,
ActiveText = activeText,
Rule = subscription.Rule.TextView(Localizer),
subscription.Template
});
return text;
}
private VerticalKeyboardMarkup BuildKeyBoard(TextSubscriptionData textSubscription, long? prevId, long? nextId)
{
var activeButtonText = textSubscription.Active
? Localizer.Get(nameof(ViewSubscriptionHandlerBase), "DeactivateButton")
: Localizer.Get(nameof(ViewSubscriptionHandlerBase), "ActivateButton");
var deleteButtonText = Localizer.Get("Buttons", "Delete");
var markup = new VerticalKeyboardMarkup();
var row = new List<InlineKeyboardButton>(2);
if (prevId.HasValue)
{
row.Add(new InlineKeyboardButton(Localizer.Get("Buttons", "PreviousPage"))
{
CallbackData = NocrCallbackData.ViewSubscription(prevId.Value).ToString()
});
}
if (nextId.HasValue)
{
row.Add(new InlineKeyboardButton(Localizer.Get("Buttons", "NextPage"))
{
CallbackData = NocrCallbackData.ViewSubscription(nextId.Value).ToString()
});
}
if (row.Count != 0)
markup.AddRow(row);
markup.Add(new InlineKeyboardButton(activeButtonText)
{
CallbackData = textSubscription.Active
? NocrCallbackData.DeactivateSubscription(textSubscription.Id).ToString()
: NocrCallbackData.ActivateSubscription(textSubscription.Id).ToString()
});
markup.Add(new InlineKeyboardButton(deleteButtonText)
{ CallbackData = NocrCallbackData.DeleteSubscription(textSubscription.Id).ToString() });
return markup;
}
}

View File

@ -0,0 +1,61 @@
using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Telegram.Bot.Types;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.ActivateSubscription;
public class ActivateSubscriptionHandler : ViewSubscriptionHandlerBase, IMatchingUpdateHandler<ActivateSubscriptionMatcher>
{
public ActivateSubscriptionHandler(ILogger<ActivateSubscriptionHandler> logger,
ILocalizer localizer,
IMessageDispatcherQueue messageQueue,
IBot bot,
ITextSubscriptionsController subscriptionsController)
: base(logger, localizer, messageQueue, bot, subscriptionsController)
{
}
public async Task Handle(Update update, CancellationToken cancellationToken = default)
{
var callbackData = NocrCallbackData.Parse(update.CallbackQuery.Data);
var from = update.CallbackQuery.From.Id;
if (callbackData.Args.Count != 1 || !long.TryParse(callbackData.Args.First(), out var subscriptionId))
{
Logger.LogWarning(
"Не удалось извлечь идентификатор подписки. CallbackData: {@CallbackData}, ChatId: {ChatId}, MessageId {MessageId}",
callbackData, from, update.CallbackQuery.Message.Chat.Id);
SendErrorMessage(from);
return;
}
try
{
await SubscriptionsController.Activate(subscriptionId, cancellationToken);
}
catch (Exception ex)
{
Logger.LogError(ex, "Не удалось деактивировать подписку");
SendErrorMessage(from);
throw;
}
await EditSubscriptionMessage(from, update.CallbackQuery.Message.MessageId, subscriptionId,
CancellationToken.None);
}
private void SendErrorMessage(long from)
{
MessageQueue.Enqueue(new TextMessage(from)
{
Text = Localizer.Get(nameof(ActivateSubscriptionHandler), "FailedToActivateSubscription")
});
}
}

View File

@ -0,0 +1,12 @@
using Insight.TelegramBot.Handling.Matchers.CallbackQueryMatchers;
using Nocr.TelegramClient.AppServices.Bots;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.ActivateSubscription;
public sealed class ActivateSubscriptionMatcher : StateCallbackQueryMatcher<NocrState>
{
public ActivateSubscriptionMatcher()
{
ExpectingState = NocrState.ActivateSubscription;
}
}

View File

@ -0,0 +1,62 @@
using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Telegram.Bot.Types;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.DeactivateSubscription;
public class DeactivateSubscriptionHandler : ViewSubscriptionHandlerBase,
IMatchingUpdateHandler<DeactivateSubscriptionMatcher>
{
public DeactivateSubscriptionHandler(ILogger<DeactivateSubscriptionHandler> logger,
ILocalizer localizer,
IMessageDispatcherQueue messageQueue,
IBot bot,
ITextSubscriptionsController subscriptionsController)
: base(logger, localizer, messageQueue, bot, subscriptionsController)
{
}
public async Task Handle(Update update, CancellationToken cancellationToken = default)
{
var callbackData = NocrCallbackData.Parse(update.CallbackQuery.Data);
var from = update.CallbackQuery.From.Id;
if (callbackData.Args.Count != 1 || !long.TryParse(callbackData.Args.First(), out var subscriptionId))
{
Logger.LogWarning(
"Не удалось извлечь идентификатор подписки. CallbackData: {@CallbackData}, ChatId: {ChatId}, MessageId {MessageId}",
callbackData, from, update.CallbackQuery.Message.Chat.Id);
SendErrorMessage(from);
return;
}
try
{
await SubscriptionsController.Disable(subscriptionId, cancellationToken);
}
catch (Exception ex)
{
Logger.LogError(ex, "Не удалось деактивировать подписку");
SendErrorMessage(from);
throw;
}
await EditSubscriptionMessage(from, update.CallbackQuery.Message.MessageId, subscriptionId, CancellationToken.None);
}
private void SendErrorMessage(long from)
{
MessageQueue.Enqueue(new TextMessage(from)
{
Text = Localizer.Get(nameof(DeactivateSubscriptionHandler), "FailedToDeactivateSubscription")
});
}
}

View File

@ -0,0 +1,12 @@
using Insight.TelegramBot.Handling.Matchers.CallbackQueryMatchers;
using Nocr.TelegramClient.AppServices.Bots;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.DeactivateSubscription;
public sealed class DeactivateSubscriptionMatcher : StateCallbackQueryMatcher<NocrState>
{
public DeactivateSubscriptionMatcher()
{
ExpectingState = NocrState.DeactivateSubscription;
}
}

View File

@ -0,0 +1,69 @@
using FormatWith;
using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
using Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.DeactivateSubscription;
using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Telegram.Bot.Types;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.DeleteSubscription;
public class DeleteSubscriptionHandler : ViewSubscriptionHandlerBase,
IMatchingUpdateHandler<DeleteSubscriptionMatcher>
{
public DeleteSubscriptionHandler(ILogger<DeleteSubscriptionHandler> logger,
ILocalizer localizer,
IMessageDispatcherQueue messageQueue,
IBot bot,
ITextSubscriptionsController subscriptionsController)
: base(logger, localizer, messageQueue, bot, subscriptionsController)
{
}
public async Task Handle(Update update, CancellationToken cancellationToken = default)
{
var callbackData = NocrCallbackData.Parse(update.CallbackQuery.Data);
var from = update.CallbackQuery.From.Id;
if (callbackData.Args.Count != 1 || !long.TryParse(callbackData.Args.First(), out var subscriptionId))
{
Logger.LogWarning(
"Не удалось извлечь идентификатор подписки. CallbackData: {@CallbackData}, ChatId: {ChatId}, MessageId {MessageId}",
callbackData, from, update.CallbackQuery.Message.Chat.Id);
SendErrorMessage(from);
return;
}
try
{
await SubscriptionsController.Delete(subscriptionId, cancellationToken);
}
catch (Exception ex)
{
Logger.LogError(ex, "Не удалось удалить подписку");
SendErrorMessage(from);
throw;
}
var message = new TextMessage(from)
{
Text = Localizer.Get(nameof(DeleteSubscriptionHandler), "Text")
.FormatWith(new { Id = subscriptionId })
};
await Bot.EditOrSendTextMessage(update.CallbackQuery.Message.MessageId, message, Logger, CancellationToken.None);
}
private void SendErrorMessage(long from)
{
MessageQueue.Enqueue(new TextMessage(from)
{
Text = Localizer.Get(nameof(DeactivateSubscriptionHandler), "FailedToDeleteSubscription")
});
}
}

View File

@ -0,0 +1,12 @@
using Insight.TelegramBot.Handling.Matchers.CallbackQueryMatchers;
using Nocr.TelegramClient.AppServices.Bots;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.DeleteSubscription;
public sealed class DeleteSubscriptionMatcher : StateCallbackQueryMatcher<NocrState>
{
public DeleteSubscriptionMatcher()
{
ExpectingState = NocrState.DeleteSubscription;
}
}

View File

@ -0,0 +1,49 @@
using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Telegram.Bot.Types;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.ViewSubscription;
public class ViewSubscriptionHandler : ViewSubscriptionHandlerBase, IMatchingUpdateHandler<ViewSubscriptionMatcher>
{
public ViewSubscriptionHandler(ILogger<ViewSubscriptionHandler> logger,
ILocalizer localizer,
IMessageDispatcherQueue messageQueue,
IBot bot,
ITextSubscriptionsController subscriptionsController) :
base(logger, localizer, messageQueue, bot, subscriptionsController)
{
}
public async Task Handle(Update update, CancellationToken cancellationToken = default)
{
var callbackData = NocrCallbackData.Parse(update.CallbackQuery.Data);
var from = update.CallbackQuery.From.Id;
if (callbackData.Args.Count != 1 || !long.TryParse(callbackData.Args.First(), out var subscriptionId))
{
Logger.LogWarning(
"Не удалось извлечь идентификатор подписки. CallbackData: {@CallbackData}, ChatId: {ChatId}, MessageId {MessageId}",
callbackData, from, update.CallbackQuery.Message.Chat.Id);
SendErrorMessage(from);
return;
}
await EditSubscriptionMessage(from, update.CallbackQuery.Message.MessageId, subscriptionId, cancellationToken);
}
private void SendErrorMessage(long from)
{
MessageQueue.Enqueue(new TextMessage(from)
{
Text = Localizer.Get(nameof(ViewSubscriptionHandler), "FailedToViewSubscription")
});
}
}

View File

@ -0,0 +1,12 @@
using Insight.TelegramBot.Handling.Matchers.CallbackQueryMatchers;
using Nocr.TelegramClient.AppServices.Bots;
namespace Nocr.TelegramClient.AppServices.Handlers.CallbackQueries.ViewSubscription;
public sealed class ViewSubscriptionMatcher : StateCallbackQueryMatcher<NocrState>
{
public ViewSubscriptionMatcher()
{
ExpectingState = NocrState.ViewSubscription;
}
}

View File

@ -23,7 +23,7 @@ public class StartMessageHandler : IMatchingUpdateHandler<StartMessageMatcher>
var telegramId = update.Message.From.Id; var telegramId = update.Message.From.Id;
var message = new TextMessage(telegramId) var message = new TextMessage(telegramId)
{ {
Text = _localizer.Get("messages", "StartMessage"), Text = _localizer.Get(nameof(StartMessageHandler), "Text"),
ParseMode = ParseMode.Html ParseMode = ParseMode.Html
}; };

View File

@ -1,7 +1,12 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FormatWith;
using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers; using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models; using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
using Nocr.TelegramClient.AppServices.Users; using Nocr.TelegramClient.AppServices.Users;
using Nocr.TextMatcher.Api.Contracts.TextMatches; using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Nocr.TextMatcher.Api.Contracts.TextMatches.Requests; using Nocr.TextMatcher.Api.Contracts.TextMatches.Requests;
@ -10,11 +15,9 @@ using Telegram.Bot.Types;
namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscribeMessage; namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscribeMessage;
public class SubscribeMessageHandler : IMatchingUpdateHandler<SubscribeMessageMatcher> public class SubscribeMessageHandler : ViewSubscriptionHandlerBase, IMatchingUpdateHandler<SubscribeMessageMatcher>
{ {
private readonly IMessageDispatcherQueue _messageQueue;
private readonly IUsersService _usersService; private readonly IUsersService _usersService;
private readonly ITextSubscriptionsController _textSubscriptionsController;
/// <summary> /// <summary>
/// Regex to match command "/subscribe @username match_type keywords". <br/> /// Regex to match command "/subscribe @username match_type keywords". <br/>
@ -24,26 +27,28 @@ public class SubscribeMessageHandler : IMatchingUpdateHandler<SubscribeMessageMa
new Regex(@"/subscribe (.*\B@(?=\w{5,32}\b)[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*.*) (\d{1}) (.*)", new Regex(@"/subscribe (.*\B@(?=\w{5,32}\b)[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*.*) (\d{1}) (.*)",
RegexOptions.Compiled); RegexOptions.Compiled);
public SubscribeMessageHandler(IMessageDispatcherQueue messageQueue, IUsersService usersService, public SubscribeMessageHandler(
ITextSubscriptionsController textSubscriptionsController) ILogger<SubscribeMessageHandler> logger,
ILocalizer localizer,
IMessageDispatcherQueue messageQueue,
IUsersService usersService,
IBot bot,
ITextSubscriptionsController subscriptionsController)
: base(logger, localizer, messageQueue, bot, subscriptionsController)
{ {
_messageQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue));
_usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService));
_textSubscriptionsController =
textSubscriptionsController ?? throw new ArgumentNullException(nameof(textSubscriptionsController));
} }
public async Task Handle(Update update, CancellationToken cancellationToken = default) public async Task Handle(Update update, CancellationToken cancellationToken = default)
{ {
var telegramId = update.Message.From.Id; var receiverId = update.Message.From.Id;
var match = _commandRegex.Match(update.Message.Text); var match = _commandRegex.Match(update.Message.Text);
if (!match.Success) if (!match.Success)
{ {
_messageQueue.Enqueue(new TextMessage(telegramId) MessageQueue.Enqueue(new TextMessage(receiverId)
{ {
// TODO: Add localizer Text = Localizer.Get(nameof(SubscribeMessageHandler), "CommandHasWrongFormat")
Text = "Команда не удовлетворяет формату"
}); });
return; return;
} }
@ -51,18 +56,17 @@ public class SubscribeMessageHandler : IMatchingUpdateHandler<SubscribeMessageMa
var username = match.Groups[1].Value.TrimStart('@'); var username = match.Groups[1].Value.TrimStart('@');
if (!Enum.TryParse<TextSubscriptionRule>(match.Groups[2].Value, true, out var rule)) if (!Enum.TryParse<TextSubscriptionRule>(match.Groups[2].Value, true, out var rule))
{ {
_messageQueue.Enqueue(new TextMessage(telegramId) MessageQueue.Enqueue(new TextMessage(receiverId)
{ {
// TODO: Add localizer Text = Localizer.Get(nameof(SubscribeMessageHandler), "WrongSubscriptionType")
Text = "Не удалось извлечь тип подписки"
}); });
return; return;
} }
var template = match.Groups[3].Value; var template = match.Groups[3].Value;
var user = await _usersService.GetOrCreate(telegramId, update.Message.From.Username, cancellationToken); var user = await _usersService.GetOrCreate(receiverId, update.Message.From.Username, cancellationToken);
var subscriptionId = await _textSubscriptionsController.Create(new CreateTextSubscriptionRequest var subscriptionId = await SubscriptionsController.Create(new CreateTextSubscriptionRequest
{ {
UserId = user.Id, UserId = user.Id,
ChatUsername = username, ChatUsername = username,
@ -70,10 +74,13 @@ public class SubscribeMessageHandler : IMatchingUpdateHandler<SubscribeMessageMa
Template = template, Template = template,
}, cancellationToken); }, cancellationToken);
_messageQueue.Enqueue(new TextMessage(telegramId) MessageQueue.Enqueue(new TextMessage(receiverId)
{ {
// TODO: Add localizer Text = Localizer.Get(nameof(SubscribeMessageHandler), "Text")
Text = $"Подписка создана: {subscriptionId}" .FormatWith(new { Id = subscriptionId })
}); });
var subscription = await SubscriptionsController.GetById(subscriptionId, CancellationToken.None);
await SendSubscriptionMessage(receiverId, subscription, CancellationToken.None);
} }
} }

View File

@ -1,32 +1,30 @@
using Insight.Localizer; using Insight.Localizer;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers; using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models; using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Matches; using Nocr.TelegramClient.AppServices.Handlers.BaseHandlers;
using Nocr.TelegramClient.AppServices.Users; using Nocr.TelegramClient.AppServices.Users;
using Nocr.TextMatcher.Api.Contracts.TextMatches; using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscriptionsMessage; namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscriptionsMessage;
public class SubscriptionsMessageHandler : IMatchingUpdateHandler<SubscriptionsMessageMatcher> public class SubscriptionsMessageHandler : ViewSubscriptionHandlerBase,
IMatchingUpdateHandler<SubscriptionsMessageMatcher>
{ {
private readonly ILocalizer _localizer;
private readonly IMessageDispatcherQueue _messageQueue;
private readonly IUsersService _usersService; private readonly IUsersService _usersService;
private readonly ITextSubscriptionsController _textSubscriptionsController;
public SubscriptionsMessageHandler(ILocalizer localizer, public SubscriptionsMessageHandler(
ILogger<SubscriptionsMessageHandler> logger,
ILocalizer localizer,
IMessageDispatcherQueue messageQueue, IMessageDispatcherQueue messageQueue,
IUsersService usersService, IUsersService usersService,
ITextSubscriptionsController textSubscriptionsController) IBot bot,
ITextSubscriptionsController subscriptionsController)
: base(logger, localizer, messageQueue, bot, subscriptionsController)
{ {
_localizer = localizer ?? throw new ArgumentNullException(nameof(localizer));
_messageQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue));
_usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService));
_textSubscriptionsController = textSubscriptionsController ??
throw new ArgumentNullException(nameof(textSubscriptionsController));
} }
public async Task Handle(Update update, CancellationToken cancellationToken = default) public async Task Handle(Update update, CancellationToken cancellationToken = default)
@ -34,17 +32,9 @@ public class SubscriptionsMessageHandler : IMatchingUpdateHandler<SubscriptionsM
var telegramId = update.Message.From.Id; var telegramId = update.Message.From.Id;
var user = await _usersService.GetOrCreate(telegramId, update.Message.From.Username, cancellationToken); var user = await _usersService.GetOrCreate(telegramId, update.Message.From.Username, cancellationToken);
var subscriptions = await _textSubscriptionsController.GetByUserId(user.Id, cancellationToken); var subscriptions = await SubscriptionsController.GetByUserId(user.Id, cancellationToken);
var subscription = subscriptions.MinBy(x => x.Id);
foreach (var subscription in subscriptions) await SendSubscriptionMessage(telegramId, subscription, CancellationToken.None);
{
var textMessage = new TextMessage(telegramId)
{
Text = subscription.TextView(_localizer),
ParseMode = ParseMode.Html
};
_messageQueue.Enqueue(textMessage);
}
} }
} }

View File

@ -5,21 +5,20 @@ using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Links; using Nocr.TelegramClient.AppServices.Links;
using Nocr.TelegramClient.AppServices.Users; using Nocr.TelegramClient.AppServices.Users;
using Nocr.TextMatcher.Async.Api.Contracts;
using Nocr.Users.Api.Contracts.Users; using Nocr.Users.Api.Contracts.Users;
using Rebus.Handlers; using Rebus.Handlers;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
namespace Nocr.TelegramClient.AppServices.Matches.Handlers; namespace Nocr.TelegramClient.AppServices.Matches.Handlers;
public class TextMatchMatchedHandler : IHandleMessages<TextSubscriptionMatched> public class TextSubscriptionMatched : IHandleMessages<TextMatcher.Async.Api.Contracts.TextSubscriptionMatched>
{ {
private readonly ILogger<TextMatchMatchedHandler> _logger; private readonly ILogger<TextSubscriptionMatched> _logger;
private readonly ILocalizer _localizer; private readonly ILocalizer _localizer;
private readonly IMessageDispatcherQueue _messageDispatcherQueue; private readonly IMessageDispatcherQueue _messageDispatcherQueue;
private readonly IUsersService _usersService; private readonly IUsersService _usersService;
public TextMatchMatchedHandler(ILogger<TextMatchMatchedHandler> logger, ILocalizer localizer, public TextSubscriptionMatched(ILogger<TextSubscriptionMatched> logger, ILocalizer localizer,
IMessageDispatcherQueue messageDispatcherQueue, IUsersService usersService) IMessageDispatcherQueue messageDispatcherQueue, IUsersService usersService)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
@ -29,7 +28,7 @@ public class TextMatchMatchedHandler : IHandleMessages<TextSubscriptionMatched>
_usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService));
} }
public async Task Handle(TextSubscriptionMatched message) public async Task Handle(TextMatcher.Async.Api.Contracts.TextSubscriptionMatched message)
{ {
// TODO: Set from user context // TODO: Set from user context
Localizer.CurrentCulture = "ru-ru"; Localizer.CurrentCulture = "ru-ru";
@ -52,7 +51,7 @@ public class TextMatchMatchedHandler : IHandleMessages<TextSubscriptionMatched>
var link = new PublicTelegramMessageLink(false, message.ChatUsername, message.MessageId); var link = new PublicTelegramMessageLink(false, message.ChatUsername, message.MessageId);
var fromUsername = string.IsNullOrWhiteSpace(message.From) ? "anonymous" : message.From; var fromUsername = string.IsNullOrWhiteSpace(message.From) ? "anonymous" : message.From;
var text = _localizer.Get("messages", "TextSubscriptionMatched") var text = _localizer.Get(nameof(TextSubscriptionMatched), "Text")
.FormatWith(new .FormatWith(new
{ {
SubscriptionRule = message.Rule.TextView(_localizer), SubscriptionRule = message.Rule.TextView(_localizer),

View File

@ -1,47 +0,0 @@
using FormatWith;
using Insight.Localizer;
using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto;
using Nocr.TextMatcher.Contracts;
namespace Nocr.TelegramClient.AppServices.Matches;
public static class TextMatchExtensions
{
public static string TextView(this TextSubscriptionData textSubscription, ILocalizer localizer)
{
var activeText = textSubscription.Active
? localizer.Get(nameof(TextSubscriptionData), "Active")
: localizer.Get(nameof(TextSubscriptionData), "Inactive");
var activeCommandText = textSubscription.Active
? GetFormattedCommand("Deactivate", textSubscription.Id)
: GetFormattedCommand("Activate", textSubscription.Id);
var deleteCommandText = GetFormattedCommand("Delete", textSubscription.Id);
return
$"[{textSubscription.Id}] (@{textSubscription.ChatUsername}) {activeText}: '{textSubscription.Rule.TextView(localizer)}' > '{textSubscription.Template}'\n{activeCommandText}\n{deleteCommandText}";
string GetFormattedCommand(string key, long subscriptionId)
{
return localizer.Get(nameof(TextSubscriptionData), key)
.FormatWith(new
{
SubscriptionId = subscriptionId
});
}
}
public static string TextView(this TextSubscriptionRule rule, ILocalizer localizer)
{
switch (rule)
{
case TextSubscriptionRule.Full:
return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.Full));
case TextSubscriptionRule.AllWords:
return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.AllWords));
case TextSubscriptionRule.AnyWord:
return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.AnyWord));
default:
throw new IndexOutOfRangeException(nameof(rule));
}
}
}

View File

@ -0,0 +1,22 @@
using Insight.Localizer;
using Nocr.TextMatcher.Contracts;
namespace Nocr.TelegramClient.AppServices.Matches;
public static class TextSubscriptionRuleExtensions
{
public static string TextView(this TextSubscriptionRule rule, ILocalizer localizer)
{
switch (rule)
{
case TextSubscriptionRule.Full:
return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.Full));
case TextSubscriptionRule.AllWords:
return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.AllWords));
case TextSubscriptionRule.AnyWord:
return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.AnyWord));
default:
throw new IndexOutOfRangeException(nameof(rule));
}
}
}

View File

@ -19,5 +19,4 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Nocr.TelegramClient.Core\Nocr.TelegramClient.Core.csproj" /> <ProjectReference Include="..\Nocr.TelegramClient.Core\Nocr.TelegramClient.Core.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -20,7 +20,7 @@ public static class ServiceCollectionExtensions
throw new ArgumentNullException(nameof(services)); throw new ArgumentNullException(nameof(services));
// Add registrations here // Add registrations here
services.AddRebusHandler<TextMatchMatchedHandler>(); services.AddRebusHandler<TextSubscriptionMatched>();
services.AddHttpClient(); services.AddHttpClient();
services.Configure<UsersRestEaseOptions>(configuration.GetSection(nameof(UsersRestEaseOptions))); services.Configure<UsersRestEaseOptions>(configuration.GetSection(nameof(UsersRestEaseOptions)));

View File

@ -25,4 +25,5 @@
<Content Include="Resources" /> <Content Include="Resources" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,7 +0,0 @@
{
"Active": "Активна",
"Inactive": "Не активна",
"Activate": "Активировать: /activate_subscription_{SubscriptionId}",
"Deactivate": "Деактивировать: /deactivate_subscription_{SubscriptionId}",
"Delete": "Удалить: /delete_subscription_{SubscriptionId}"
}

View File

@ -1,4 +0,0 @@
{
"InvalidTimeFormat": "Time <b>{ReceivedTime}</b> does not correspond to the acceptable formats!",
"InvalidDateFormat": "Date <b>{ReceivedDate}</b> does not correspond to the acceptable formats!"
}

View File

@ -1,4 +0,0 @@
{
"InvalidTimeFormat": "Время <b>{ReceivedTime}</b> не соответствует допустимым форматам!",
"InvalidDateFormat": "Дата <b>{ReceivedDate}</b> не соответствует допустимым форматам!"
}

View File

@ -0,0 +1,3 @@
{
"Text": "Тип совпадения: <b>{SubscriptionRule}</b>\nШаблон: <b>{Template}</b>\nСообщение: {Link}\n{FromUsername} в @{ChatUsername}"
}

View File

@ -0,0 +1,8 @@
{
"Active": "Активна",
"Inactive": "Не активна",
"Text": "Подписка #{Id}\nЧат: (@{ChatUsername})\nСтатус: {ActiveText}\nПравило: {Rule}\nШаблон: {Template}",
"ActivateButton": "Активировать",
"DeactivateButton": "Деактивировать"
}

View File

@ -0,0 +1,3 @@
{
"FailedToActivateSubscription": "Не удалось активировать подписку"
}

View File

@ -0,0 +1,3 @@
{
"FailedToDeactivateSubscription": "Не удалось деактивировать подписку"
}

View File

@ -0,0 +1,4 @@
{
"FailedToDeleteSubscription": "Не удалось удалить подписку",
"Text": "Подписка #{Id} удалена"
}

View File

@ -0,0 +1,3 @@
{
"Text": "Привет! Я, Nocr 🤖!"
}

View File

@ -0,0 +1,6 @@
{
"Text": "Подписка #{Id} создана",
"CommandHasWrongFormat": "Команда не удовлетворяет формату",
"WrongSubscriptionType": "Не удалось извлечь тип подписки"
}

View File

@ -1,4 +0,0 @@
{
"StartMessage": "Привет! Я, Nocr 🤖!",
"TextSubscriptionMatched": "Тип совпадения: <b>{SubscriptionRule}</b>\nШаблон: <b>{Template}</b>\n{FromUsername} в @{ChatUsername}:\n<i>{MessageText}</i>\n\n{Link}"
}