Files
GmRelayBot/src/GmRelay.Bot/Infrastructure/Telegram/TelegramBotService.cs
T
Toutsu 4d6651827b
Deploy Telegram Bot / build-and-push (push) Successful in 4m24s
Deploy Telegram Bot / deploy (push) Successful in 18s
fix: skip stale pending updates on startup
2026-04-23 20:42:16 +03:00

88 lines
2.7 KiB
C#

using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace GmRelay.Bot.Infrastructure.Telegram;
/// <summary>
/// Long polling loop for Telegram Bot API.
/// Stateless — all state is in PostgreSQL. Safe to restart at any time.
/// </summary>
public sealed class TelegramBotService(
ITelegramUpdateSource updateSource,
ITelegramUpdateHandler updateHandler,
ILogger<TelegramBotService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("Telegram bot polling started");
var offset = await GetStartupOffsetAsync(stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
try
{
var updates = await updateSource.GetUpdatesAsync(
offset: offset,
timeout: 30,
allowedUpdates: [UpdateType.Message, UpdateType.CallbackQuery],
cancellationToken: stoppingToken);
foreach (var update in updates)
{
try
{
await updateHandler.RouteAsync(update, stoppingToken);
}
catch (Exception ex)
{
logger.LogError(ex, "Error handling update {UpdateId}", update.Id);
}
offset = update.Id + 1;
}
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
catch (Exception ex)
{
logger.LogError(ex, "Polling error, retrying in 5s");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
logger.LogInformation("Telegram bot polling stopped");
}
private async Task<int> GetStartupOffsetAsync(CancellationToken stoppingToken)
{
try
{
var pending = await updateSource.GetUpdatesAsync(
offset: -1,
limit: 1,
cancellationToken: stoppingToken);
if (pending.Length == 0)
{
return 0;
}
var startupOffset = pending[^1].Id + 1;
logger.LogInformation(
"Skipping pending updates through {LastPendingUpdateId}; starting polling from offset {StartupOffset}",
pending[^1].Id,
startupOffset);
return startupOffset;
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to determine startup offset, continuing from offset 0");
return 0;
}
}
}