nazarovsa/message_link (#5)

Reviewed-on: #5
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-15 16:22:58 +00:00 committed by nazarovsa
parent ba8c94d962
commit 43dbf5b724
13 changed files with 188 additions and 73 deletions

View File

@ -1,38 +1,39 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<MicrosoftVersion>8.0.0</MicrosoftVersion> <MicrosoftVersion>8.0.0</MicrosoftVersion>
<InsightTelegramBotVersion>0.16.0</InsightTelegramBotVersion> <InsightTelegramBotVersion>0.16.0</InsightTelegramBotVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Insight.Localizer" Version="1.1.0"/> <PackageVersion Include="FormatWith" Version="3.0.1" />
</ItemGroup> <PackageVersion Include="Insight.Localizer" Version="1.1.0" />
<ItemGroup Label="Nocr"> </ItemGroup>
<PackageVersion Include="Nocr.TextMatcher.Api.Contracts" Version="0.4.25"/> <ItemGroup Label="Nocr">
<PackageVersion Include="Nocr.TextMatcher.Async.Api.Contracts" Version="0.4.25"/> <PackageVersion Include="Nocr.TextMatcher.Api.Contracts" Version="0.5.9" />
<PackageVersion Include="Nocr.Users.Api.Contracts" Version="0.4.23"/> <PackageVersion Include="Nocr.TextMatcher.Async.Api.Contracts" Version="0.5.9" />
</ItemGroup> <PackageVersion Include="Nocr.Users.Api.Contracts" Version="0.4.23" />
<ItemGroup Label="Rebus"> </ItemGroup>
<PackageVersion Include="Rebus" Version="8.2.2"/> <ItemGroup Label="Rebus">
<PackageVersion Include="Rebus.ServiceProvider" Version="10.1.0"/> <PackageVersion Include="Rebus" Version="8.2.2" />
<PackageVersion Include="Rebus.RabbitMq" Version="9.0.1"/> <PackageVersion Include="Rebus.ServiceProvider" Version="10.1.0" />
<PackageVersion Include="Rebus.Serilog" Version="8.0.0"/> <PackageVersion Include="Rebus.RabbitMq" Version="9.0.1" />
</ItemGroup> <PackageVersion Include="Rebus.Serilog" Version="8.0.0" />
<ItemGroup Label="Insight.TelegramBot"> </ItemGroup>
<PackageVersion Include="Insight.TelegramBot" Version="$(InsightTelegramBotVersion)"/> <ItemGroup Label="Insight.TelegramBot">
<PackageVersion Include="Insight.TelegramBot.Hosting.DependencyInjection" Version="$(InsightTelegramBotVersion)"/> <PackageVersion Include="Insight.TelegramBot" Version="$(InsightTelegramBotVersion)" />
<PackageVersion Include="Insight.TelegramBot.Handling" Version="$(InsightTelegramBotVersion)"/> <PackageVersion Include="Insight.TelegramBot.Hosting.DependencyInjection" Version="$(InsightTelegramBotVersion)" />
</ItemGroup> <PackageVersion Include="Insight.TelegramBot.Handling" Version="$(InsightTelegramBotVersion)" />
<ItemGroup Label="Serilog"> </ItemGroup>
<PackageVersion Include="Serilog" Version="3.1.1"/> <ItemGroup Label="Serilog">
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.1"/> <PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.0"/> <PackageVersion Include="Serilog.AspNetCore" Version="8.0.1" />
</ItemGroup> <PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.0" />
<ItemGroup Label="Microsoft"> </ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(MicrosoftVersion)"/> <ItemGroup Label="Microsoft">
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftVersion)"/> <PackageVersion Include="Microsoft.Extensions.Http" Version="$(MicrosoftVersion)" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftVersion)"/> <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftVersion)" />
</ItemGroup> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftVersion)" />
</ItemGroup>
</Project> </Project>

View File

@ -1,7 +1,6 @@
using Insight.TelegramBot; using Insight.TelegramBot;
using Insight.TelegramBot.Models; using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types; using Telegram.Bot.Types;

View File

@ -1,5 +1,4 @@
using Insight.Localizer; 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 Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;

View File

@ -1,5 +1,4 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Insight.TelegramBot;
using Insight.TelegramBot.Handling.Handlers; using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models; using Insight.TelegramBot.Models;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
@ -7,7 +6,6 @@ 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;
using Nocr.TextMatcher.Contracts; using Nocr.TextMatcher.Contracts;
using Nocr.Users.Api.Contracts.Users.Dto;
using Telegram.Bot.Types; using Telegram.Bot.Types;
namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscribeMessage; namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscribeMessage;

View File

@ -1,11 +1,10 @@
using Insight.TelegramBot; using Insight.Localizer;
using Insight.TelegramBot.Handling.Handlers; using Insight.TelegramBot.Handling.Handlers;
using Insight.TelegramBot.Models; using Insight.TelegramBot.Models;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Matches; using Nocr.TelegramClient.AppServices.Matches;
using Nocr.TelegramClient.AppServices.Users; using Nocr.TelegramClient.AppServices.Users;
using Nocr.TextMatcher.Api.Contracts.TextMatches; using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Nocr.Users.Api.Contracts.Users.Dto;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
@ -13,16 +12,21 @@ namespace Nocr.TelegramClient.AppServices.Handlers.Messages.SubscriptionsMessage
public class SubscriptionsMessageHandler : IMatchingUpdateHandler<SubscriptionsMessageMatcher> public class SubscriptionsMessageHandler : IMatchingUpdateHandler<SubscriptionsMessageMatcher>
{ {
private readonly ILocalizer _localizer;
private readonly IMessageDispatcherQueue _messageQueue; private readonly IMessageDispatcherQueue _messageQueue;
private readonly IUsersService _usersService; private readonly IUsersService _usersService;
private readonly ITextSubscriptionsController _textSubscriptionsController; private readonly ITextSubscriptionsController _textSubscriptionsController;
public SubscriptionsMessageHandler(IMessageDispatcherQueue messageQueue, IUsersService usersService, public SubscriptionsMessageHandler(ILocalizer localizer,
IMessageDispatcherQueue messageQueue,
IUsersService usersService,
ITextSubscriptionsController textSubscriptionsController) ITextSubscriptionsController textSubscriptionsController)
{ {
_localizer = localizer ?? throw new ArgumentNullException(nameof(localizer));
_messageQueue = messageQueue ?? throw new ArgumentNullException(nameof(messageQueue)); _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)); _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)
@ -36,11 +40,10 @@ public class SubscriptionsMessageHandler : IMatchingUpdateHandler<SubscriptionsM
{ {
var textMessage = new TextMessage(telegramId) var textMessage = new TextMessage(telegramId)
{ {
// TODO: Add localizer Text = subscription.TextView(_localizer),
Text = subscription.TextView(),
ParseMode = ParseMode.Html ParseMode = ParseMode.Html
}; };
_messageQueue.Enqueue(textMessage); _messageQueue.Enqueue(textMessage);
} }
} }

View File

@ -0,0 +1,68 @@
namespace Nocr.TelegramClient.AppServices.Links;
/// <summary>
///
/// </summary>
public readonly struct PublicTelegramMessageLink
{
// https://t.me/baraholka_tbi/1186860
// tg://resolve?domain=baraholka_tbi&post=1186860
// t.me/<username>/<id>?single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
/// <summary>
/// Username чата или канала
/// </summary>
public string Username { get; }
/// <summary>
/// Идентификатор сообщения
/// </summary>
public long MessageId { get; }
/// <summary>
/// Идентификатор треда
/// </summary>
public long? ThreadId { get; }
/// <summary>
/// Отображать в web-preview одно изображения из поста или несколько
/// </summary>
public bool SingleView { get; }
/// <summary>
/// https://
/// </summary>
public string WebLink { get; }
/// <summary>
/// tg://
/// </summary>
public string TelegramLink { get; }
public PublicTelegramMessageLink(bool singleView, string username, long messageId, long? threadId = null)
{
if (string.IsNullOrWhiteSpace(username))
{
throw new ArgumentNullException(nameof(username));
}
SingleView = singleView;
Username = username;
MessageId = messageId;
ThreadId = threadId;
(WebLink, TelegramLink) = BuildLinks();
}
private (string, string) BuildLinks()
{
var webLink = ThreadId.HasValue
? $"https://t.me/{Username}/{ThreadId.Value}/{MessageId}?{(SingleView ? "single" : string.Empty)}"
: $"https://t.me/{Username}/{MessageId}?{(SingleView ? "single" : string.Empty)}";
var telegramLink = ThreadId.HasValue
? $"tg://resolve?domain={Username}&post={MessageId}{(SingleView ? "&single" : string.Empty)}&thread={ThreadId.Value}"
: $"tg://resolve?domain={Username}&post={MessageId}{(SingleView ? "&single" : string.Empty)}";
return (webLink, telegramLink);
}
}

View File

@ -1,8 +1,10 @@
using Insight.TelegramBot; using FormatWith;
using Insight.Localizer;
using Insight.TelegramBot.Models; using Insight.TelegramBot.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Links;
using Nocr.TelegramClient.AppServices.Users; using Nocr.TelegramClient.AppServices.Users;
using Nocr.TextMatcher.Api.Contracts.TextMatches;
using Nocr.TextMatcher.Async.Api.Contracts; using Nocr.TextMatcher.Async.Api.Contracts;
using Nocr.Users.Api.Contracts.Users; using Nocr.Users.Api.Contracts.Users;
using Rebus.Handlers; using Rebus.Handlers;
@ -13,22 +15,29 @@ namespace Nocr.TelegramClient.AppServices.Matches.Handlers;
public class TextMatchMatchedHandler : IHandleMessages<TextSubscriptionMatched> public class TextMatchMatchedHandler : IHandleMessages<TextSubscriptionMatched>
{ {
private readonly ILogger<TextMatchMatchedHandler> _logger; private readonly ILogger<TextMatchMatchedHandler> _logger;
private readonly IBot _bot; private readonly ILocalizer _localizer;
private readonly IMessageDispatcherQueue _messageDispatcherQueue;
private readonly IUsersService _usersService; private readonly IUsersService _usersService;
public TextMatchMatchedHandler(ILogger<TextMatchMatchedHandler> logger, IBot bot, IUsersService usersService) public TextMatchMatchedHandler(ILogger<TextMatchMatchedHandler> logger, ILocalizer localizer,
IMessageDispatcherQueue messageDispatcherQueue, IUsersService usersService)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_bot = bot ?? throw new ArgumentNullException(nameof(bot)); _localizer = localizer ?? throw new ArgumentNullException(nameof(localizer));
_messageDispatcherQueue =
messageDispatcherQueue ?? throw new ArgumentNullException(nameof(messageDispatcherQueue));
_usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService));
} }
public async Task Handle(TextSubscriptionMatched message) public async Task Handle(TextSubscriptionMatched message)
{ {
// TODO: Set from user context
Localizer.CurrentCulture = "ru-ru";
var user = await _usersService.GetById(message.SubscriptionUserId); var user = await _usersService.GetById(message.SubscriptionUserId);
if (user == null) if (user == null)
{ {
_logger.LogWarning("User [{UserId}] of [{MatchId}] from message {MessageId} not found", message.SubscriptionUserId, _logger.LogWarning("User [{UserId}] of [{MatchId}] from message {MessageId} not found",
message.SubscriptionUserId,
message.SubscriptionId, message.Id); message.SubscriptionId, message.Id);
return; return;
} }
@ -41,13 +50,25 @@ public class TextMatchMatchedHandler : IHandleMessages<TextSubscriptionMatched>
return; return;
} }
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")
.FormatWith(new
{
SubscriptionRule = message.Rule.TextView(_localizer),
message.Template,
FromUsername = fromUsername,
message.ChatUsername,
MessageText = message.Text,
Link = link.WebLink
});
var textMessage = new TextMessage(long.Parse(identity.Identity)) var textMessage = new TextMessage(long.Parse(identity.Identity))
{ {
Text = $"[{message.PublishedDateTime:MM.dd.yyyy HH:mm:ss}] Найдено совпадение.\nТип совпадения: <b>'{message.Rule.TextView()}'</b>\nШаблон: <b>'{message.Template}'</b>\n{fromUsername} в @{message.ChatUsername}: <i>{message.Text}</i>", Text = text,
ParseMode = ParseMode.Html ParseMode = ParseMode.Html
}; };
_messageDispatcherQueue.Enqueue(textMessage);
await _bot.SendMessageAsync(textMessage);
} }
} }

View File

@ -1,4 +1,5 @@
using Nocr.TextMatcher.Api.Contracts.TextMatches; using FormatWith;
using Insight.Localizer;
using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto;
using Nocr.TextMatcher.Contracts; using Nocr.TextMatcher.Contracts;
@ -6,26 +7,39 @@ namespace Nocr.TelegramClient.AppServices.Matches;
public static class TextMatchExtensions public static class TextMatchExtensions
{ {
public static string TextView(this TextSubscriptionData textMatch) public static string TextView(this TextSubscriptionData textSubscription, ILocalizer localizer)
{ {
var activeText = textMatch.Active ? "Активна" : "Не активна"; var activeText = textSubscription.Active
var activeCommandText = textMatch.Active ? localizer.Get(nameof(TextSubscriptionData), "Active")
? $"Деактивировать: /deactivate_{textMatch.Id}" : localizer.Get(nameof(TextSubscriptionData), "Inactive");
: $"Активировать: /activate_{textMatch.Id}"; var activeCommandText = textSubscription.Active
var deleteCommandText = $"Удалить: /delete_subscription_{textMatch.Id}"; ? GetFormattedCommand("Deactivate", textSubscription.Id)
return $"[{textMatch.Id}] (@{textMatch.ChatUsername}) {activeText}: '{textMatch.Rule.TextView()}' > '{textMatch.Template}'\n{activeCommandText}\n{deleteCommandText}"; : GetFormattedCommand("Activate", textSubscription.Id);
}
var deleteCommandText = GetFormattedCommand("Delete", textSubscription.Id);
public static string TextView(this TextSubscriptionRule rule) 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) switch (rule)
{ {
case TextSubscriptionRule.Full: case TextSubscriptionRule.Full:
return "Полное"; return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.Full));
case TextSubscriptionRule.AllWords: case TextSubscriptionRule.AllWords:
return "Все слова из списка"; return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.AllWords));
case TextSubscriptionRule.AnyWord: case TextSubscriptionRule.AnyWord:
return "Одно слово из списка"; return localizer.Get(nameof(TextSubscriptionRule), nameof(TextSubscriptionRule.AnyWord));
default: default:
throw new IndexOutOfRangeException(nameof(rule)); throw new IndexOutOfRangeException(nameof(rule));
} }

View File

@ -4,6 +4,7 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FormatWith" />
<PackageReference Include="Insight.Localizer" /> <PackageReference Include="Insight.Localizer" />
<PackageReference Include="Insight.TelegramBot" /> <PackageReference Include="Insight.TelegramBot" />
<PackageReference Include="Insight.TelegramBot.Handling" /> <PackageReference Include="Insight.TelegramBot.Handling" />

View File

@ -1,9 +1,7 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Nocr.TelegramClient.AppServices.Bots;
using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher; using Nocr.TelegramClient.AppServices.Bots.MessageDispatcher;
using Nocr.TelegramClient.AppServices.Matches;
using Nocr.TelegramClient.AppServices.Matches.Handlers; using Nocr.TelegramClient.AppServices.Matches.Handlers;
using Nocr.TelegramClient.AppServices.Options; using Nocr.TelegramClient.AppServices.Options;
using Nocr.TelegramClient.AppServices.Users; using Nocr.TelegramClient.AppServices.Users;

View File

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

View File

@ -0,0 +1,5 @@
{
"Full": "Полное",
"AllWords": "Все слова из списка",
"AnyWord": "Одно слово из списка"
}

View File

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