197 lines
7.4 KiB
C#
197 lines
7.4 KiB
C#
using System.Globalization;
|
|
using GmRelay.Shared.Platform;
|
|
using Telegram.Bot;
|
|
using Telegram.Bot.Types;
|
|
using Telegram.Bot.Types.Enums;
|
|
using Telegram.Bot.Types.ReplyMarkups;
|
|
|
|
namespace GmRelay.Bot.Infrastructure.Telegram;
|
|
|
|
public sealed class TelegramPlatformMessenger(
|
|
ITelegramBotClient bot,
|
|
ILogger<TelegramPlatformMessenger> logger) : IPlatformMessenger
|
|
{
|
|
public async Task<PlatformMessageRef> SendScheduleAsync(PlatformScheduleMessage message, CancellationToken ct)
|
|
{
|
|
EnsureTelegram(message.Group.Platform);
|
|
|
|
var chatId = ParseLong(message.Group.ExternalGroupId);
|
|
var threadId = ParseNullableInt(message.Group.ExternalThreadId);
|
|
var renderResult = TelegramSessionBatchRenderer.Render(message.View);
|
|
Message sentMessage;
|
|
|
|
if (!string.IsNullOrWhiteSpace(message.ImageReference) && renderResult.Text.Length <= 1024)
|
|
{
|
|
try
|
|
{
|
|
sentMessage = await bot.SendPhoto(
|
|
chatId: chatId,
|
|
messageThreadId: threadId,
|
|
photo: InputFile.FromString(message.ImageReference),
|
|
caption: renderResult.Text,
|
|
parseMode: ParseMode.Html,
|
|
replyMarkup: renderResult.Markup,
|
|
cancellationToken: ct);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning(ex, "Failed to send Telegram schedule image for group {ExternalGroupId}", message.Group.ExternalGroupId);
|
|
sentMessage = await SendScheduleTextMessage(chatId, threadId, renderResult.Text, renderResult.Markup, ct);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(message.ImageReference))
|
|
{
|
|
await TrySendScheduleImageOnly(chatId, threadId, message.View.Title, message.ImageReference, ct);
|
|
}
|
|
|
|
sentMessage = await SendScheduleTextMessage(chatId, threadId, renderResult.Text, renderResult.Markup, ct);
|
|
}
|
|
|
|
return new PlatformMessageRef(
|
|
PlatformKind.Telegram,
|
|
message.Group.ExternalGroupId,
|
|
message.Group.ExternalThreadId,
|
|
sentMessage.MessageId.ToString(CultureInfo.InvariantCulture));
|
|
}
|
|
|
|
public async Task UpdateScheduleAsync(PlatformScheduleMessage message, CancellationToken ct)
|
|
{
|
|
EnsureTelegram(message.Group.Platform);
|
|
var existingMessage = message.ExistingMessage;
|
|
if (existingMessage is null)
|
|
{
|
|
throw new ArgumentException("Existing schedule message reference is required.", nameof(message));
|
|
}
|
|
|
|
EnsureTelegram(existingMessage.Platform);
|
|
if (!string.Equals(message.Group.ExternalGroupId, existingMessage.ExternalGroupId, StringComparison.Ordinal) ||
|
|
!string.Equals(message.Group.ExternalThreadId, existingMessage.ExternalThreadId, StringComparison.Ordinal))
|
|
{
|
|
throw new ArgumentException("Existing schedule message reference must match the schedule group.", nameof(message));
|
|
}
|
|
|
|
var renderResult = TelegramSessionBatchRenderer.Render(message.View);
|
|
await BatchMessageEditor.EditBatchMessageAsync(
|
|
bot,
|
|
chatId: ParseLong(existingMessage.ExternalGroupId),
|
|
messageId: ParseInt(existingMessage.ExternalMessageId),
|
|
text: renderResult.Text,
|
|
replyMarkup: renderResult.Markup,
|
|
ct);
|
|
}
|
|
|
|
public Task SendGroupMessageAsync(PlatformGroup group, string htmlText, CancellationToken ct)
|
|
{
|
|
EnsureTelegram(group.Platform);
|
|
return bot.SendMessage(
|
|
chatId: ParseLong(group.ExternalGroupId),
|
|
messageThreadId: ParseNullableInt(group.ExternalThreadId),
|
|
text: htmlText,
|
|
parseMode: ParseMode.Html,
|
|
cancellationToken: ct);
|
|
}
|
|
|
|
public Task SendPrivateMessageAsync(PlatformPrivateMessage message, CancellationToken ct)
|
|
{
|
|
EnsureTelegram(message.Recipient.Platform);
|
|
return bot.SendMessage(
|
|
chatId: ParseLong(message.Recipient.ExternalUserId),
|
|
text: message.HtmlText,
|
|
parseMode: ParseMode.Html,
|
|
cancellationToken: ct);
|
|
}
|
|
|
|
public Task AnswerInteractionAsync(PlatformInteractionReply reply, CancellationToken ct) =>
|
|
bot.AnswerCallbackQuery(
|
|
callbackQueryId: reply.InteractionId,
|
|
text: reply.Text,
|
|
showAlert: reply.ShowAlert,
|
|
cancellationToken: ct);
|
|
|
|
public async Task SendCalendarFileAsync(PlatformCalendarFile file, CancellationToken ct)
|
|
{
|
|
EnsureTelegram(file.Group.Platform);
|
|
|
|
using var stream = new MemoryStream(file.Content);
|
|
await bot.SendDocument(
|
|
chatId: ParseLong(file.Group.ExternalGroupId),
|
|
messageThreadId: ParseNullableInt(file.Group.ExternalThreadId),
|
|
document: InputFile.FromStream(stream, file.FileName),
|
|
caption: file.CaptionHtml,
|
|
parseMode: ParseMode.Html,
|
|
replyMarkup: BuildActionsMarkup(file.Actions),
|
|
cancellationToken: ct);
|
|
}
|
|
|
|
private async Task<Message> SendScheduleTextMessage(
|
|
long chatId,
|
|
int? threadId,
|
|
string text,
|
|
InlineKeyboardMarkup markup,
|
|
CancellationToken ct) =>
|
|
await bot.SendMessage(
|
|
chatId: chatId,
|
|
messageThreadId: threadId,
|
|
text: text,
|
|
parseMode: ParseMode.Html,
|
|
replyMarkup: markup,
|
|
cancellationToken: ct);
|
|
|
|
private async Task TrySendScheduleImageOnly(
|
|
long chatId,
|
|
int? threadId,
|
|
string title,
|
|
string imageReference,
|
|
CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
await bot.SendPhoto(
|
|
chatId: chatId,
|
|
messageThreadId: threadId,
|
|
photo: InputFile.FromString(imageReference),
|
|
caption: $"🎲 {System.Net.WebUtility.HtmlEncode(title)}",
|
|
parseMode: ParseMode.Html,
|
|
cancellationToken: ct);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning(ex, "Failed to send Telegram schedule image for chat {ChatId}", chatId);
|
|
}
|
|
}
|
|
|
|
private static InlineKeyboardMarkup? BuildActionsMarkup(IReadOnlyList<PlatformMessageAction> actions)
|
|
{
|
|
if (actions.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new InlineKeyboardMarkup(
|
|
actions.Select(action => new[]
|
|
{
|
|
Uri.TryCreate(action.Payload, UriKind.Absolute, out var uri) &&
|
|
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
|
? InlineKeyboardButton.WithUrl(action.Label, action.Payload)
|
|
: InlineKeyboardButton.WithCallbackData(action.Label, action.Payload)
|
|
}));
|
|
}
|
|
|
|
private static void EnsureTelegram(PlatformKind platform)
|
|
{
|
|
if (platform != PlatformKind.Telegram)
|
|
{
|
|
throw new NotSupportedException($"Telegram messenger cannot send messages for platform {platform}.");
|
|
}
|
|
}
|
|
|
|
private static long ParseLong(string value) => long.Parse(value, CultureInfo.InvariantCulture);
|
|
|
|
private static int ParseInt(string value) => int.Parse(value, CultureInfo.InvariantCulture);
|
|
|
|
private static int? ParseNullableInt(string? value) =>
|
|
string.IsNullOrWhiteSpace(value) ? null : int.Parse(value, CultureInfo.InvariantCulture);
|
|
}
|