Using PeriodicTimer instead of threading.timer in some services

This commit is contained in:
Kwoth
2022-01-10 17:19:48 +01:00
parent 73901158ab
commit 9de75d9109
5 changed files with 86 additions and 55 deletions

View File

@@ -349,3 +349,4 @@ resharper_csharp_wrap_before_extends_colon = true
resharper_csharp_place_constructor_initializer_on_same_line = false resharper_csharp_place_constructor_initializer_on_same_line = false
resharper_force_attribute_style = separate resharper_force_attribute_style = separate
resharper_braces_for_ifelse = required_for_multiline resharper_braces_for_ifelse = required_for_multiline
resharper_arrange_redundant_parentheses_highlighting = hint

View File

@@ -1,5 +1,6 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.TypeReaders.Models; using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Db; using NadekoBot.Db;
using NadekoBot.Modules.Permissions.Services; using NadekoBot.Modules.Permissions.Services;
@@ -8,32 +9,45 @@ using Newtonsoft.Json;
namespace NadekoBot.Modules.Administration.Services; namespace NadekoBot.Modules.Administration.Services;
public class UserPunishService : INService public class UserPunishService : INService, IReadyExecutor
{ {
private readonly MuteService _mute; private readonly MuteService _mute;
private readonly DbService _db; private readonly DbService _db;
private readonly BlacklistService _blacklistService; private readonly BlacklistService _blacklistService;
private readonly BotConfigService _bcs; private readonly BotConfigService _bcs;
private readonly Timer _warnExpiryTimer; private readonly DiscordSocketClient _client;
public UserPunishService( public UserPunishService(
MuteService mute, MuteService mute,
DbService db, DbService db,
BlacklistService blacklistService, BlacklistService blacklistService,
BotConfigService bcs) BotConfigService bcs,
DiscordSocketClient client)
{ {
_mute = mute; _mute = mute;
_db = db; _db = db;
_blacklistService = blacklistService; _blacklistService = blacklistService;
_bcs = bcs; _bcs = bcs;
_client = client;
_warnExpiryTimer = new(async _ => }
public async Task OnReadyAsync()
{
if (_client.ShardId != 0)
return;
var expiryTimer = new PeriodicTimer(TimeSpan.FromHours(12));
do
{
try
{ {
await CheckAllWarnExpiresAsync(); await CheckAllWarnExpiresAsync();
}, }
null, catch (Exception ex)
TimeSpan.FromSeconds(0), {
TimeSpan.FromHours(12)); Log.Error(ex, "Unexpected error while checking for warn expiries: {ErrorMessage}", ex.Message);
}
} while (await expiryTimer.WaitForNextTickAsync());
} }
public async Task<WarningPunishment> Warn( public async Task<WarningPunishment> Warn(

View File

@@ -1,4 +1,5 @@
using Ayu.Discord.Voice; using Ayu.Discord.Voice;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
@@ -54,7 +55,7 @@ public sealed class MusicPlayer : IMusicPlayer
_songBuffer = new PoopyBufferImmortalized(_vc.InputLength); _songBuffer = new PoopyBufferImmortalized(_vc.InputLength);
_thread = new(async () => _thread = new(async() =>
{ {
await PlayLoop(); await PlayLoop();
}); });

View File

@@ -1,5 +1,6 @@
#nullable disable #nullable disable
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Modules.Gambling.Services; using NadekoBot.Modules.Gambling.Services;
using NadekoBot.Modules.Utility.Common.Patreon; using NadekoBot.Modules.Utility.Common.Patreon;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
@@ -10,14 +11,12 @@ using System.Text.Json;
namespace NadekoBot.Modules.Utility; namespace NadekoBot.Modules.Utility;
public class PatreonRewardsService : INService public class PatreonRewardsService : INService, IReadyExecutor
{ {
public TimeSpan Interval { get; } = TimeSpan.FromMinutes(3); public TimeSpan Interval { get; } = TimeSpan.FromMinutes(3);
public DateTime LastUpdate { get; private set; } = DateTime.UtcNow; public DateTime LastUpdate { get; private set; } = DateTime.UtcNow;
private readonly SemaphoreSlim _getPledgesLocker = new(1, 1);
private readonly Timer _updater;
private readonly SemaphoreSlim _claimLockJustInCase = new(1, 1); private readonly SemaphoreSlim _claimLockJustInCase = new(1, 1);
private readonly DbService _db; private readonly DbService _db;
private readonly ICurrencyService _currency; private readonly ICurrencyService _currency;
@@ -46,9 +45,25 @@ public class PatreonRewardsService : INService
_httpFactory = factory; _httpFactory = factory;
_eb = eb; _eb = eb;
_client = client; _client = client;
}
if (client.ShardId == 0) public async Task OnReadyAsync()
_updater = new(async _ => await RefreshPledges(_credsProvider.GetCreds()), null, TimeSpan.Zero, Interval); {
if (_client.ShardId != 0)
return;
var t = new PeriodicTimer(Interval);
do
{
try
{
await RefreshPledges(_credsProvider.GetCreds());
}
catch (Exception ex)
{
Log.Error(ex, "Unexpected error refreshing patreon pledges: {ErrorMessage}", ex.Message);
}
} while (await t.WaitForNextTickAsync());
} }
private DateTime LastAccessTokenUpdate(IBotCredentials creds) private DateTime LastAccessTokenUpdate(IBotCredentials creds)
@@ -118,7 +133,7 @@ public class PatreonRewardsService : INService
var lastUpdate = LastAccessTokenUpdate(creds); var lastUpdate = LastAccessTokenUpdate(creds);
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
if (lastUpdate.Year != now.Year if (lastUpdate.Year != now.Year
|| lastUpdate.Month != now.Month || lastUpdate.Month != now.Month
|| string.IsNullOrWhiteSpace(creds.Patreon.AccessToken)) || string.IsNullOrWhiteSpace(creds.Patreon.AccessToken))
@@ -135,7 +150,6 @@ public class PatreonRewardsService : INService
} }
LastUpdate = DateTime.UtcNow; LastUpdate = DateTime.UtcNow;
await _getPledgesLocker.WaitAsync();
try try
{ {
var members = new List<PatreonMember>(); var members = new List<PatreonMember>();
@@ -196,10 +210,6 @@ public class PatreonRewardsService : INService
{ {
Log.Warning(ex, "Error refreshing patreon pledges"); Log.Warning(ex, "Error refreshing patreon pledges");
} }
finally
{
_getPledgesLocker.Release();
}
} }
public async Task<int> ClaimReward(ulong userId, string patreonUserId, int cents) public async Task<int> ClaimReward(ulong userId, string patreonUserId, int cents)

View File

@@ -1,16 +1,17 @@
#nullable disable #nullable disable
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Modules.Utility.Common; using NadekoBot.Modules.Utility.Common;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace NadekoBot.Modules.Utility.Services; namespace NadekoBot.Modules.Utility.Services;
public class ConverterService : INService public class ConverterService : INService, IReadyExecutor
{ {
public ConvertUnit[] Units public ConvertUnit[] Units
=> _cache.Redis.GetDatabase().StringGet("converter_units").ToString().MapJson<ConvertUnit[]>(); => _cache.Redis.GetDatabase().StringGet("converter_units").ToString().MapJson<ConvertUnit[]>();
private readonly Timer _currencyUpdater;
private readonly TimeSpan _updateInterval = new(12, 0, 0); private readonly TimeSpan _updateInterval = new(12, 0, 0);
private readonly DiscordSocketClient _client;
private readonly IDataCache _cache; private readonly IDataCache _cache;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
@@ -19,14 +20,28 @@ public class ConverterService : INService
IDataCache cache, IDataCache cache,
IHttpClientFactory factory) IHttpClientFactory factory)
{ {
_client = client;
_cache = cache; _cache = cache;
_httpFactory = factory; _httpFactory = factory;
}
if (client.ShardId == 0) public async Task OnReadyAsync()
_currencyUpdater = new(async shouldLoad => await UpdateCurrency((bool)shouldLoad!), {
client.ShardId == 0, if (_client.ShardId != 0)
TimeSpan.Zero, return;
_updateInterval);
var timer = new PeriodicTimer(_updateInterval);
do
{
try
{
await UpdateCurrency();
}
catch
{
// ignored
}
} while (await timer.WaitForNextTickAsync());
} }
private async Task<Rates> GetCurrencyRates() private async Task<Rates> GetCurrencyRates()
@@ -36,37 +51,27 @@ public class ConverterService : INService
return JsonConvert.DeserializeObject<Rates>(res); return JsonConvert.DeserializeObject<Rates>(res);
} }
private async Task UpdateCurrency(bool shouldLoad) private async Task UpdateCurrency()
{ {
try var unitTypeString = "currency";
var currencyRates = await GetCurrencyRates();
var baseType = new ConvertUnit
{ {
var unitTypeString = "currency"; Triggers = new[] { currencyRates.Base }, Modifier = decimal.One, UnitType = unitTypeString
if (shouldLoad) };
{ var range = currencyRates.ConversionRates.Select(u => new ConvertUnit
var currencyRates = await GetCurrencyRates(); {
var baseType = new ConvertUnit Triggers = new[] { u.Key }, Modifier = u.Value, UnitType = unitTypeString
{ })
Triggers = new[] { currencyRates.Base }, Modifier = decimal.One, UnitType = unitTypeString .ToArray();
};
var range = currencyRates.ConversionRates.Select(u => new ConvertUnit
{
Triggers = new[] { u.Key }, Modifier = u.Value, UnitType = unitTypeString
})
.ToArray();
var fileData = JsonConvert.DeserializeObject<ConvertUnit[]>(File.ReadAllText("data/units.json")) var fileData = JsonConvert.DeserializeObject<ConvertUnit[]>(File.ReadAllText("data/units.json"))
?.Where(x => x.UnitType != "currency"); ?.Where(x => x.UnitType != "currency");
if (fileData is null) if (fileData is null)
return; return;
var data = JsonConvert.SerializeObject(range.Append(baseType).Concat(fileData).ToList()); var data = JsonConvert.SerializeObject(range.Append(baseType).Concat(fileData).ToList());
_cache.Redis.GetDatabase().StringSet("converter_units", data); _cache.Redis.GetDatabase().StringSet("converter_units", data);
}
}
catch
{
// ignored
}
} }
} }