Skip to content

Commit

Permalink
Рефакторинг
Browse files Browse the repository at this point in the history
  • Loading branch information
inyutin-maxim committed Jul 30, 2023
1 parent eff5347 commit cfc4a18
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,14 +19,20 @@ namespace VkNet.Utils.BotsLongPool;
/// Реализация лонгпула для бота в сообществе
/// </summary>
[UsedImplicitly]
public class BotsLongPoolUpdatesProvider
public class BotsLongPoolUpdatesHandler : IBotsLongPoolUpdatesHandler
{
private readonly BotsLongPoolUpdatesProviderParams _params;
private readonly BotsLongPoolUpdatesHandlerParams _params;

private LongPollServerResponse? _lpResponse;
private ulong? _currentTs;

/// <param name="params">Параметры группового лонгпула</param>
public BotsLongPoolUpdatesProvider(BotsLongPoolUpdatesProviderParams @params) => _params = @params;
private string? _currentSessionKey;

private string? _currentServer;

/// <summary>
/// Инициализирует новый экземпляр класса <see cref="BotsLongPoolUpdatesHandler" />
/// </summary>
public BotsLongPoolUpdatesHandler(BotsLongPoolUpdatesHandlerParams @params) => _params = @params;

/// <summary>
/// Запуск отслеживания событий
Expand All @@ -34,53 +41,58 @@ 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);
}
}

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<BotsLongPollHistoryResponse<JObject>>(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())
{
Expand All @@ -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;
}
Expand All @@ -134,52 +146,62 @@ private async Task HandleException(System.Exception ex, CancellationToken token
/// </summary>
/// <param name="exception">Ошибка, связанная с лонгпулом</param>
/// <param name="token">Токен отмены операции</param>
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);
}
}

Expand All @@ -188,14 +210,14 @@ private async Task UpdateLongPoolServerAsync(bool isInit, CancellationToken toke
/// А так же это пригодится, если вылезет ошибка JsonSerializationException.
/// В таком случае мы не сможем получить новый TS и только прибавив 1 мы сможем получить следующий массив обновлений без текущей ошибки.
/// </summary>
/// <exception cref="Exception">Этот метод может быть вызван только после вызова UpdateLongPoolServerAsync</exception>
/// <exception cref="Exception">Этот метод может быть вызван только после вызова InitCurrentTsAsync</exception>
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++;
}
}
17 changes: 17 additions & 0 deletions VkNet/Utils/BotsLongPool/IBotsLongPoolUpdatesHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable enable
using System.Threading;
using System.Threading.Tasks;

namespace VkNet.Utils.BotsLongPool;

/// <summary>
/// Обработчик лонгпула групповых сообщений
/// </summary>
public interface IBotsLongPoolUpdatesHandler
{
/// <summary>
/// Запуск отслеживания событий
/// </summary>
/// <param name="token">Токен отмены операции</param>
Task RunAsync(CancellationToken token = default);
}

0 comments on commit cfc4a18

Please sign in to comment.