Добавьте файлы проекта.
This commit is contained in:
17
BotPages.Telegram/BotPages.Telegram.csproj
Normal file
17
BotPages.Telegram/BotPages.Telegram.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Telegram.Bot" Version="22.7.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BotPages.Core\BotPages.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
89
BotPages.Telegram/TelegramClientAdapter.cs
Normal file
89
BotPages.Telegram/TelegramClientAdapter.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using BotPages.Core;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
namespace BotPages.Telegram
|
||||
{
|
||||
/// <summary>
|
||||
/// Адаптер клиента для Telegram Bot API.
|
||||
/// </summary>
|
||||
public sealed class TelegramClientAdapter : IChatClient
|
||||
{
|
||||
private readonly ITelegramBotClient _bot;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт адаптер на основе ITelegramBotClient.
|
||||
/// </summary>
|
||||
public TelegramClientAdapter(ITelegramBotClient bot) => _bot = bot;
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет текстовое сообщение с опциональной клавиатурой.
|
||||
/// </summary>
|
||||
public Task SendTextAsync(long chatId, PageMessage message, IEnumerable<PageAction>? actions, CancellationToken ct)
|
||||
{
|
||||
ReplyMarkup? replyMarkup = null;
|
||||
|
||||
if (actions is { })
|
||||
{
|
||||
var inlineGroups = actions
|
||||
.Where(a => a.Placement == ActionPlacement.Inline)
|
||||
.GroupBy(a => a.Row)
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g => g.Select(a => InlineKeyboardButton.WithCallbackData(a.Label, a.Value)).ToArray())
|
||||
.ToArray();
|
||||
|
||||
var replyGroups = actions
|
||||
.Where(a => a.Placement == ActionPlacement.Reply)
|
||||
.GroupBy(a => a.Row)
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g => g.Select(a => new KeyboardButton(a.Label)).ToArray())
|
||||
.ToArray();
|
||||
|
||||
if (inlineGroups.Any())
|
||||
replyMarkup = new InlineKeyboardMarkup(inlineGroups);
|
||||
else if (replyGroups.Any())
|
||||
replyMarkup = new ReplyKeyboardMarkup(replyGroups) { ResizeKeyboard = true };
|
||||
}
|
||||
|
||||
var parseMode = message.Format switch
|
||||
{
|
||||
MessageFormat.Markdown => ParseMode.MarkdownV2,
|
||||
MessageFormat.Html => ParseMode.Html,
|
||||
_ => ParseMode.None,
|
||||
};
|
||||
|
||||
|
||||
return _bot.SendMessage(new ChatId(chatId),
|
||||
message.Text,
|
||||
parseMode: parseMode,
|
||||
replyMarkup: replyMarkup,
|
||||
disableNotification: message.IsSilent,
|
||||
cancellationToken: ct
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет файлы как документы (по одному или пачкой).
|
||||
/// </summary>
|
||||
public async Task SendFilesAsync(long chatId, IEnumerable<FileDescriptor> files, CancellationToken ct)
|
||||
{
|
||||
foreach (var f in files)
|
||||
{
|
||||
if (f.Content is not null)
|
||||
{
|
||||
var input = new InputFileStream(f.Content, f.Name);
|
||||
await _bot.SendDocument(chatId, input, cancellationToken: ct);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Если контент не загружен, и есть FileId — отправляем по Id
|
||||
await _bot.SendDocument(chatId, new InputFileId(f.Id), cancellationToken: ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
BotPages.Telegram/TelegramFileService.cs
Normal file
59
BotPages.Telegram/TelegramFileService.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using BotPages.Core;
|
||||
using Telegram.Bot;
|
||||
|
||||
namespace BotPages.Telegram
|
||||
{
|
||||
/// <summary>
|
||||
/// FileService для Telegram: загрузка по FileId.
|
||||
/// </summary>
|
||||
public sealed class TelegramFileService : IFileService
|
||||
{
|
||||
private readonly ITelegramBotClient _bot;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт файловый сервис для Telegram.
|
||||
/// </summary>
|
||||
public TelegramFileService(ITelegramBotClient bot) => _bot = bot;
|
||||
|
||||
/// <summary>
|
||||
/// Загружает файл по идентификатору Telegram FileId.
|
||||
/// </summary>
|
||||
public async Task<FileDescriptor> DownloadAsync(string fileId, CancellationToken ct)
|
||||
{
|
||||
var file = await _bot.GetFile(fileId, ct);
|
||||
var ms = new MemoryStream();
|
||||
await _bot.DownloadFile(file.FilePath!, ms, ct);
|
||||
ms.Position = 0;
|
||||
var name = System.IO.Path.GetFileName(file.FilePath!);
|
||||
return new FileDescriptor(file.FileId, name, "application/octet-stream", ms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Загружает несколько файлов по их идентификаторам.
|
||||
/// </summary>
|
||||
public async Task<IReadOnlyList<FileDescriptor>> DownloadManyAsync(IEnumerable<string> fileIds, CancellationToken ct)
|
||||
{
|
||||
var res = new List<FileDescriptor>();
|
||||
foreach (var id in fileIds)
|
||||
res.Add(await DownloadAsync(id, ct));
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет один файл в чат.
|
||||
/// </summary>
|
||||
public async Task SendAsync(IChatClient client, long chatId, FileDescriptor file, CancellationToken ct)
|
||||
{
|
||||
await client.SendFilesAsync(chatId, new[] { file }, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет несколько файлов в чат.
|
||||
/// </summary>
|
||||
public Task SendManyAsync(IChatClient client, long chatId, IEnumerable<FileDescriptor> files, CancellationToken ct)
|
||||
{
|
||||
return client.SendFilesAsync(chatId, files, ct);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
46
BotPages.Telegram/TelegramUpdateMapper.cs
Normal file
46
BotPages.Telegram/TelegramUpdateMapper.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using BotPages.Core;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace BotPages.Telegram
|
||||
{
|
||||
/// <summary>
|
||||
/// Утилиты для извлечения контекста из Telegram Update.
|
||||
/// </summary>
|
||||
public static class TelegramUpdateMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Преобразует Telegram Update в универсальный UpdateContext.
|
||||
/// </summary>
|
||||
public static UpdateContext Map(ITelegramBotClient bot, INavigationService nav, IStateStore store, Update update)
|
||||
{
|
||||
var chat = update.Message?.Chat ?? update.CallbackQuery?.Message?.Chat;
|
||||
var user = update.Message?.From ?? update.CallbackQuery?.From;
|
||||
|
||||
var text = update.Message?.Text ?? update.CallbackQuery?.Data;
|
||||
|
||||
var files = new List<FileDescriptor>();
|
||||
if (update.Message?.Document is { } doc)
|
||||
{
|
||||
files.Add(new FileDescriptor(doc.FileId, doc.FileName ?? "file", doc.MimeType ?? "application/octet-stream"));
|
||||
}
|
||||
if (update.Message?.Photo is { } photos && photos.Count() > 0)
|
||||
{
|
||||
var largest = photos.OrderBy(p => p.FileSize).Last();
|
||||
files.Add(new FileDescriptor(largest.FileId, "photo.jpg", "image/jpeg"));
|
||||
}
|
||||
|
||||
return new UpdateContext
|
||||
{
|
||||
Client = new TelegramClientAdapter(bot),
|
||||
Chat = new ChatContext { Id = chat!.Id, Title = chat.Title },
|
||||
User = new UserContext { Id = user!.Id, DisplayName = $"{user.FirstName} {user.LastName}" },
|
||||
Text = text,
|
||||
IncomingFiles = files,
|
||||
RawUpdate = update,
|
||||
Nav = nav,
|
||||
State = store
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user