Files
PnvPanel/backend/src/PnvPanel.Api/Telegram/TelegramBotHostedService.cs
T
Leonid Pershin df137ca5a7
CI / Backend (build + test) (push) Successful in 1m18s
CI / Frontend (lint + typecheck + build) (push) Successful in 31s
Refactor project files for improved readability and structure
- 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.
2026-07-14 07:24:13 +03:00

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)
{
// Остановка приложения во время паузы перед повтором.
}
}
}
}
}