- Cleaned up whitespace in Directory.Build.props and Directory.Packages.props for consistency. - Reformatted project file references in PnvPanel.Api.csproj for better clarity. - Enhanced code readability in various endpoint files by adjusting line breaks and indentation. - Standardized method signatures and improved formatting in ResultExtensions and multiple endpoint classes for better maintainability.
69 lines
2.9 KiB
C#
69 lines
2.9 KiB
C#
using Microsoft.Extensions.Options;
|
|
using PnvPanel.Infrastructure.Telegram;
|
|
using Telegram.Bot;
|
|
using Telegram.Bot.Polling;
|
|
using Telegram.Bot.Types;
|
|
using Telegram.Bot.Types.Enums;
|
|
|
|
namespace PnvPanel.Api.Telegram;
|
|
|
|
/// <summary>
|
|
/// Бот — presentation-адаптер, хостится в процессе Api (long polling). Если BotToken не задан,
|
|
/// не стартует — панель работает без бота. Апдейты обрабатывает PnvBotUpdateHandler, который
|
|
/// вызывает те же CQRS-команды, что и веб, через собственный ISender.
|
|
/// </summary>
|
|
public sealed class TelegramBotHostedService(
|
|
ITelegramBotClient botClient,
|
|
PnvBotUpdateHandler updateHandler,
|
|
IOptions<TelegramOptions> options,
|
|
ILogger<TelegramBotHostedService> logger
|
|
) : BackgroundService
|
|
{
|
|
private static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(30);
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
|
|
{
|
|
logger.LogWarning("Telegram__BotToken is not set — bot will not start.");
|
|
return;
|
|
}
|
|
|
|
var receiverOptions = new ReceiverOptions
|
|
{
|
|
AllowedUpdates = [UpdateType.Message, UpdateType.CallbackQuery],
|
|
DropPendingUpdates = true,
|
|
};
|
|
|
|
logger.LogInformation("Telegram bot starting (long polling)...");
|
|
|
|
// ReceiveAsync может упасть с сетевой ошибкой (Telegram API недоступен/заблокирован) —
|
|
// это НЕ должно ронять весь хост (BackgroundServiceExceptionBehavior.StopHost по умолчанию
|
|
// убивает всё приложение при необработанном исключении в BackgroundService). Панель обязана
|
|
// работать без бота, поэтому ловим, логируем и переподключаемся с паузой (см. CLAUDE.md).
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
await botClient.ReceiveAsync(updateHandler, receiverOptions, stoppingToken);
|
|
}
|
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
|
{
|
|
// Штатная остановка вместе с приложением.
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Telegram bot polling failed, retrying in {Delay}", RetryDelay);
|
|
try
|
|
{
|
|
await Task.Delay(RetryDelay, stoppingToken);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// Остановка приложения во время паузы перед повтором.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|