From cfc4a18fd5bef466ffa8f13524cb524ea4ec179c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BD=D1=8E=D1=82=D0=B8=D0=BD=20=D0=9C=D0=B0=D0=BA?= =?UTF-8?q?=D1=81=D0=B8=D0=BC=20=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5?= =?UTF-8?q?=D0=B2=D0=B8=D1=87?= Date: Mon, 31 Jul 2023 02:38:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...vider.cs => BotsLongPoolUpdatesHandler.cs} | 122 +++++++++++------- .../IBotsLongPoolUpdatesHandler.cs | 17 +++ 2 files changed, 89 insertions(+), 50 deletions(-) rename VkNet/Utils/BotsLongPool/{BotsLongPoolUpdatesProvider.cs => BotsLongPoolUpdatesHandler.cs} (52%) create mode 100644 VkNet/Utils/BotsLongPool/IBotsLongPoolUpdatesHandler.cs diff --git a/VkNet/Utils/BotsLongPool/BotsLongPoolUpdatesProvider.cs b/VkNet/Utils/BotsLongPool/BotsLongPoolUpdatesHandler.cs similarity index 52% rename from VkNet/Utils/BotsLongPool/BotsLongPoolUpdatesProvider.cs rename to VkNet/Utils/BotsLongPool/BotsLongPoolUpdatesHandler.cs index f9dc39cdb..99b688dea 100644 --- a/VkNet/Utils/BotsLongPool/BotsLongPoolUpdatesProvider.cs +++ b/VkNet/Utils/BotsLongPool/BotsLongPoolUpdatesHandler.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -18,14 +19,20 @@ namespace VkNet.Utils.BotsLongPool; /// Реализация лонгпула для бота в сообществе /// [UsedImplicitly] -public class BotsLongPoolUpdatesProvider +public class BotsLongPoolUpdatesHandler : IBotsLongPoolUpdatesHandler { - private readonly BotsLongPoolUpdatesProviderParams _params; + private readonly BotsLongPoolUpdatesHandlerParams _params; - private LongPollServerResponse? _lpResponse; + private ulong? _currentTs; - /// Параметры группового лонгпула - public BotsLongPoolUpdatesProvider(BotsLongPoolUpdatesProviderParams @params) => _params = @params; + private string? _currentSessionKey; + + private string? _currentServer; + + /// + /// Инициализирует новый экземпляр класса + /// + public BotsLongPoolUpdatesHandler(BotsLongPoolUpdatesHandlerParams @params) => _params = @params; /// /// Запуск отслеживания событий @@ -34,13 +41,15 @@ public class BotsLongPoolUpdatesProvider [UsedImplicitly] public async Task RunAsync(CancellationToken token = default) { - while (true) + while (!token.IsCancellationRequested) { while (_params.GetPause?.Invoke() is not true) { token.ThrowIfCancellationRequested(); await NextLongPoolHistoryAsync(token: token); } + + await Task.Delay(_params.DelayBetweenUpdates, token); } } @@ -48,39 +57,42 @@ private async Task NextLongPoolHistoryAsync(CancellationToken token = default) { try { - if (_lpResponse is null) + if (_currentTs is null) { - await UpdateLongPoolServerAsync(true, token); + await InitCurrentTsAsync(token); - if (_lpResponse is null) + if (_currentTs is null) { - throw new($"{nameof(_lpResponse)} is null"); + throw new($"{nameof(_currentTs)} is null"); } + + VkErrors.ThrowIfNullOrEmpty(() => _currentServer); + VkErrors.ThrowIfNullOrEmpty(() => _currentSessionKey); } var response = await _params.Api.Groups.GetBotsLongPollHistoryAsync>(new() { - Key = _lpResponse.Key, - Server = _lpResponse.Server, - Ts = _lpResponse.Ts, - Wait = 25 + Key = _currentSessionKey, + Server = _currentServer, + Ts = _currentTs.Value, + Wait = _params.WaitTimeout }, token); - var prevTs = long.Parse(_lpResponse.Ts); - var currentTs = long.Parse(response.Ts); + var previousTs = _currentTs; + var currentTs = response.Ts; // если ВК внезапно пришлёт старый номер события, то мы его проигнорим и прибавим 1 к ts - if (currentTs <= prevTs && Math.Abs(currentTs - prevTs) <= 500_000) + if (currentTs <= previousTs && Math.Abs((long) (currentTs - previousTs.Value)) <= _params.MaxDifferenceTs) { - _params.OnWarn?.Invoke(new($"ВКонтакте прислал устаревший ts. Текущий ts: {currentTs}. Прошлый ts: {prevTs}.")); + _params.OnWarn?.Invoke(new($"ВКонтакте прислал устаревший ts. Текущий ts: {_currentTs}. Прошлый ts: {previousTs}.")); + IncTs(); } else { - _lpResponse.Ts = response.Ts; + _currentTs = currentTs; } - var updates = BotsLongPoolHelpers.GetGroupUpdateEvents(response.Updates) - .ToList(); + var updates = BotsLongPoolHelpers.GetGroupUpdateEvents(response.Updates); if (!updates.Any()) { @@ -95,35 +107,35 @@ private async Task NextLongPoolHistoryAsync(CancellationToken token = default) } catch (System.Exception ex) { - await HandleException(ex, token); + await HandleExceptionAsync(ex, token); } } - private async Task HandleException(System.Exception ex, CancellationToken token = default) + private async Task HandleExceptionAsync(System.Exception exception, CancellationToken token) { - switch (ex) + switch (exception) { case LongPollException longPollException: - await HandleLongPoolException(longPollException, token); + await HandleLongPoolExceptionAsync(longPollException, token); return; case JsonReaderException or JsonSerializationException: - _params.OnException?.Invoke(ex); + _params.OnException?.Invoke(exception); IncTs(); return; case HttpRequestException or PublicServerErrorException: - _params.OnWarn?.Invoke(ex); + _params.OnWarn?.Invoke(exception); return; - case System.Net.Sockets.SocketException or TaskCanceledException or IOException or WebException: + case SocketException or TaskCanceledException or IOException or WebException: return; default: - _params.OnException?.Invoke(ex); + _params.OnException?.Invoke(exception); break; } @@ -134,52 +146,62 @@ private async Task HandleException(System.Exception ex, CancellationToken token /// /// Ошибка, связанная с лонгпулом /// Токен отмены операции - private async Task HandleLongPoolException(LongPollException exception, CancellationToken token = default) + private async Task HandleLongPoolExceptionAsync(LongPollException exception, CancellationToken token) { switch (exception) { case LongPollOutdateException outdatedException: - if (_lpResponse is null) + if (_currentTs is null) { - throw new($"{nameof(_lpResponse)} is null"); + throw new($"{nameof(_currentTs)} is null"); } - _lpResponse.Ts = outdatedException.Ts; + _currentTs = outdatedException.Ts; break; default: try { - await UpdateLongPoolServerAsync(false, token); + await UpdateLongPoolServerAsync(token); } catch (System.Exception ex) { - await HandleException(ex, token); + await HandleExceptionAsync(ex, token); } break; } } - private async Task UpdateLongPoolServerAsync(bool isInit, CancellationToken token = default) + private async Task InitCurrentTsAsync(CancellationToken token) { try { - var prevTs = _lpResponse?.Ts; - _lpResponse = await _params.Api.Groups.GetLongPollServerAsync(_params.GroupId, token); + var response = await _params.Api.Groups.GetLongPollServerAsync(_params.GroupId, token); - if (!isInit && prevTs is not null) - { - _lpResponse.Ts = prevTs; - } else if (_params.Ts is not null) - { - _lpResponse.Ts = _params.Ts; - } + _currentSessionKey = response.Key; + _currentServer = response.Server; + _currentTs = _params.Ts ?? response.Ts; + } + catch (System.Exception ex) + { + await HandleExceptionAsync(ex, token); + } + } + + private async Task UpdateLongPoolServerAsync(CancellationToken token) + { + try + { + var response = await _params.Api.Groups.GetLongPollServerAsync(_params.GroupId, token); + _currentSessionKey = response.Key; + _currentServer = response.Server; + _currentTs ??= response.Ts; } catch (System.Exception ex) { - await HandleException(ex, token); + await HandleExceptionAsync(ex, token); } } @@ -188,14 +210,14 @@ private async Task UpdateLongPoolServerAsync(bool isInit, CancellationToken toke /// А так же это пригодится, если вылезет ошибка JsonSerializationException. /// В таком случае мы не сможем получить новый TS и только прибавив 1 мы сможем получить следующий массив обновлений без текущей ошибки. /// - /// Этот метод может быть вызван только после вызова UpdateLongPoolServerAsync + /// Этот метод может быть вызван только после вызова InitCurrentTsAsync private void IncTs() { - if (_lpResponse is null) + if (_currentTs is null) { - throw new($"{nameof(_lpResponse)} is null"); + throw new($"{nameof(_currentTs)} is null"); } - _lpResponse.Ts = $"{long.Parse(_lpResponse.Ts) + 1}"; + _currentTs++; } } \ No newline at end of file diff --git a/VkNet/Utils/BotsLongPool/IBotsLongPoolUpdatesHandler.cs b/VkNet/Utils/BotsLongPool/IBotsLongPoolUpdatesHandler.cs new file mode 100644 index 000000000..3f0b2de79 --- /dev/null +++ b/VkNet/Utils/BotsLongPool/IBotsLongPoolUpdatesHandler.cs @@ -0,0 +1,17 @@ +#nullable enable +using System.Threading; +using System.Threading.Tasks; + +namespace VkNet.Utils.BotsLongPool; + +/// +/// Обработчик лонгпула групповых сообщений +/// +public interface IBotsLongPoolUpdatesHandler +{ + /// + /// Запуск отслеживания событий + /// + /// Токен отмены операции + Task RunAsync(CancellationToken token = default); +} \ No newline at end of file