More PeriodicTimer's instead of System.Threading.Timers

This commit is contained in:
Kwoth
2022-02-02 06:28:33 +01:00
parent b4a493971a
commit 41653a317b
10 changed files with 124 additions and 110 deletions

View File

@@ -29,7 +29,7 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
public async Task OnReadyAsync()
{
var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
var index = 0;
while (await timer.WaitForNextTickAsync())
{

View File

@@ -21,8 +21,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
private readonly GuildTimezoneService _tz;
private readonly IEmbedBuilderService _eb;
private readonly IMemoryCache _memoryCache;
private readonly Timer _clearTimer;
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new();
public LogCommandService(
@@ -80,21 +79,25 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
_prot.OnAntiProtectionTriggered += TriggeredAntiProtection;
_clearTimer = new(_ =>
{
_ignoreMessageIds.Clear();
},
null,
TimeSpan.FromHours(1),
TimeSpan.FromHours(1));
#endif
}
public async Task OnReadyAsync()
public Task OnReadyAsync()
=> Task.WhenAll(PresenceUpdateTask(), IgnoreMessageIdsClearTask());
private async Task IgnoreMessageIdsClearTask()
{
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
while (await timer.WaitForNextTickAsync())
{
_ignoreMessageIds.Clear();
}
}
private async Task PresenceUpdateTask()
{
#if !GLOBAL_NADEKO
var timer = new PeriodicTimer(TimeSpan.FromSeconds(15));
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(15));
while (await timer.WaitForNextTickAsync())
{
try
@@ -105,6 +108,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
{
if (!((SocketGuild)key.Guild).CurrentUser.GetPermissions(key).SendMessages)
return Task.CompletedTask;
if (PresenceUpdates.TryRemove(key, out var msgs))
{
var title = GetText(key.Guild, strs.presence_updates);

View File

@@ -36,7 +36,7 @@ public class UserPunishService : INService, IReadyExecutor
if (_client.ShardId != 0)
return;
var expiryTimer = new PeriodicTimer(TimeSpan.FromHours(12));
using var expiryTimer = new PeriodicTimer(TimeSpan.FromHours(12));
do
{
try

View File

@@ -1,5 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Modules.Gambling.Common;
using NadekoBot.Modules.Gambling.Common.Connect4;
@@ -9,7 +10,7 @@ using Newtonsoft.Json;
namespace NadekoBot.Modules.Gambling.Services;
public class GamblingService : INService
public class GamblingService : INService, IReadyExecutor
{
public ConcurrentDictionary<(ulong, ulong), RollDuelGame> Duels { get; } = new();
public ConcurrentDictionary<ulong, Connect4Game> Connect4Games { get; } = new();
@@ -20,8 +21,6 @@ public class GamblingService : INService
private readonly IDataCache _cache;
private readonly GamblingConfigService _gss;
private readonly Timer _decayTimer;
public GamblingService(
DbService db,
Bot bot,
@@ -36,32 +35,38 @@ public class GamblingService : INService
_client = client;
_cache = cache;
_gss = gss;
}
if (_bot.Client.ShardId == 0)
_decayTimer = new(_ =>
{
var config = _gss.Data;
var maxDecay = config.Decay.MaxDecay;
if (config.Decay.Percent is <= 0 or > 1 || maxDecay < 0)
return;
public async Task OnReadyAsync()
{
if (_bot.Client.ShardId != 0)
return;
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
while (await timer.WaitForNextTickAsync())
{
var config = _gss.Data;
var maxDecay = config.Decay.MaxDecay;
if (config.Decay.Percent is <= 0 or > 1 || maxDecay < 0)
continue;
using var uow = _db.GetDbContext();
var lastCurrencyDecay = _cache.GetLastCurrencyDecay();
await using var uow = _db.GetDbContext();
var lastCurrencyDecay = _cache.GetLastCurrencyDecay();
if (DateTime.UtcNow - lastCurrencyDecay < TimeSpan.FromHours(config.Decay.HourInterval))
return;
if (DateTime.UtcNow - lastCurrencyDecay < TimeSpan.FromHours(config.Decay.HourInterval))
continue;
Log.Information(@"Decaying users' currency - decay: {ConfigDecayPercent}%
Log.Information(@"Decaying users' currency - decay: {ConfigDecayPercent}%
| max: {MaxDecay}
| threshold: {DecayMinTreshold}",
config.Decay.Percent * 100,
maxDecay,
config.Decay.MinThreshold);
config.Decay.Percent * 100,
maxDecay,
config.Decay.MinThreshold);
if (maxDecay == 0)
maxDecay = int.MaxValue;
if (maxDecay == 0)
maxDecay = int.MaxValue;
uow.Database.ExecuteSqlInterpolated($@"
await uow.Database.ExecuteSqlInterpolatedAsync($@"
UPDATE DiscordUser
SET CurrencyAmount=
CASE WHEN
@@ -73,12 +78,9 @@ SET CurrencyAmount=
END
WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentUser.Id};");
_cache.SetLastCurrencyDecay();
uow.SaveChanges();
},
null,
TimeSpan.FromMinutes(5),
TimeSpan.FromMinutes(5));
_cache.SetLastCurrencyDecay();
await uow.SaveChangesAsync();
}
}
public async Task<SlotResponse> SlotAsync(ulong userId, long amount)

View File

@@ -1,5 +1,6 @@
#nullable disable
using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Modules.Games.Common;
using NadekoBot.Modules.Games.Common.Acrophobia;
using NadekoBot.Modules.Games.Common.Nunchi;
@@ -8,7 +9,7 @@ using Newtonsoft.Json;
namespace NadekoBot.Modules.Games.Services;
public class GamesService : INService
public class GamesService : INService, IReadyExecutor
{
private const string TYPING_ARTICLES_PATH = "data/typing_articles3.json";
@@ -29,7 +30,6 @@ public class GamesService : INService
public AsyncLazy<RatingTexts> Ratings { get; }
private readonly GamesConfigService _gamesConfig;
private readonly Timer _t;
private readonly IHttpClientFactory _httpFactory;
private readonly IMemoryCache _8BallCache;
private readonly Random _rng;
@@ -46,15 +46,6 @@ public class GamesService : INService
Ratings = new(GetRatingTexts);
_rng = new NadekoRandom();
//girl ratings
_t = new(_ =>
{
GirlRatings.Clear();
},
null,
TimeSpan.FromDays(1),
TimeSpan.FromDays(1));
try
{
TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(TYPING_ARTICLES_PATH));
@@ -66,6 +57,16 @@ public class GamesService : INService
}
}
public async Task OnReadyAsync()
{
// reset rating once a day
using var timer = new PeriodicTimer(TimeSpan.FromDays(1));
while (await timer.WaitForNextTickAsync())
{
GirlRatings.Clear();
}
}
private async Task<RatingTexts> GetRatingTexts()
{
using var http = _httpFactory.CreateClient();

View File

@@ -1,5 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Searches.Common;
@@ -9,7 +10,7 @@ using StackExchange.Redis;
namespace NadekoBot.Modules.Searches.Services;
public sealed class StreamNotificationService : INService
public sealed class StreamNotificationService : INService, IReadyExecutor
{
private readonly DbService _db;
private readonly IBotStrings _strings;
@@ -26,7 +27,6 @@ public sealed class StreamNotificationService : INService
private readonly IPubSub _pubSub;
private readonly IEmbedBuilderService _eb;
private readonly Timer _notifCleanupTimer;
private readonly TypedKey<List<StreamData>> _streamsOnlineKey;
private readonly TypedKey<List<StreamData>> _streamsOfflineKey;
@@ -114,49 +114,6 @@ public sealed class StreamNotificationService : INService
_streamTracker.OnStreamsOffline += OnStreamsOffline;
_streamTracker.OnStreamsOnline += OnStreamsOnline;
_ = _streamTracker.RunAsync();
_notifCleanupTimer = new(_ =>
{
try
{
var errorLimit = TimeSpan.FromHours(12);
var failingStreams = _streamTracker.GetFailingStreams(errorLimit, true).ToList();
if (!failingStreams.Any())
return;
var deleteGroups = failingStreams.GroupBy(x => x.Type)
.ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList());
using var uow = _db.GetDbContext();
foreach (var kvp in deleteGroups)
{
Log.Information(
"Deleting {StreamCount} {Platform} streams because they've been erroring for more than {ErrorLimit}: {RemovedList}",
kvp.Value.Count,
kvp.Key,
errorLimit,
string.Join(", ", kvp.Value));
var toDelete = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
.ToList();
uow.RemoveRange(toDelete);
uow.SaveChanges();
foreach (var loginToDelete in kvp.Value)
_streamTracker.UntrackStreamByKey(new(kvp.Key, loginToDelete));
}
}
catch (Exception ex)
{
Log.Error(ex, "Error cleaning up FollowedStreams");
}
},
null,
TimeSpan.FromMinutes(30),
TimeSpan.FromMinutes(30));
_pubSub.Sub(_streamFollowKey, HandleFollowStream);
_pubSub.Sub(_streamUnfollowKey, HandleUnfollowStream);
@@ -166,6 +123,54 @@ public sealed class StreamNotificationService : INService
client.LeftGuild += ClientOnLeftGuild;
}
public async Task OnReadyAsync()
{
if (_client.ShardId != 0)
return;
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(30));
while (await timer.WaitForNextTickAsync())
{
try
{
var errorLimit = TimeSpan.FromHours(12);
var failingStreams = _streamTracker.GetFailingStreams(errorLimit, true).ToList();
if (!failingStreams.Any())
continue;
var deleteGroups = failingStreams.GroupBy(x => x.Type)
.ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList());
await using var uow = _db.GetDbContext();
foreach (var kvp in deleteGroups)
{
Log.Information(
"Deleting {StreamCount} {Platform} streams because they've been erroring for more than {ErrorLimit}: {RemovedList}",
kvp.Value.Count,
kvp.Key,
errorLimit,
string.Join(", ", kvp.Value));
var toDelete = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
.ToList();
uow.RemoveRange(toDelete);
await uow.SaveChangesAsync();
foreach (var loginToDelete in kvp.Value)
_streamTracker.UntrackStreamByKey(new(kvp.Key, loginToDelete));
}
}
catch (Exception ex)
{
Log.Error(ex, "Error cleaning up FollowedStreams");
}
}
}
/// <summary>
/// Handles follow stream pubs to keep the counter up to date.
/// When counter reaches 0, stream is removed from tracking because

View File

@@ -52,7 +52,7 @@ public class PatreonRewardsService : INService, IReadyExecutor
if (_client.ShardId != 0)
return;
var t = new PeriodicTimer(Interval);
using var t = new PeriodicTimer(Interval);
do
{
try

View File

@@ -30,7 +30,7 @@ public class ConverterService : INService, IReadyExecutor
if (_client.ShardId != 0)
return;
var timer = new PeriodicTimer(_updateInterval);
using var timer = new PeriodicTimer(_updateInterval);
do
{
try

View File

@@ -1,5 +1,6 @@
#nullable disable
using NadekoBot.Common.Configs;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using System.Collections.Immutable;
using ExecuteResult = Discord.Commands.ExecuteResult;
@@ -7,7 +8,7 @@ using PreconditionResult = Discord.Commands.PreconditionResult;
namespace NadekoBot.Services;
public class CommandHandler : INService
public class CommandHandler : INService, IReadyExecutor
{
private const int GLOBAL_COMMANDS_COOLDOWN = 750;
@@ -30,7 +31,6 @@ public class CommandHandler : INService
private readonly IServiceProvider _services;
private readonly ConcurrentDictionary<ulong, string> _prefixes;
private readonly Timer _clearUsersOnShortCooldown;
private readonly DbService _db;
// private readonly InteractionService _interactions;
@@ -53,19 +53,21 @@ public class CommandHandler : INService
_services = services;
// _interactions = interactions;
_clearUsersOnShortCooldown = new(_ =>
{
UsersOnShortCooldown.Clear();
},
null,
GLOBAL_COMMANDS_COOLDOWN,
GLOBAL_COMMANDS_COOLDOWN);
_prefixes = bot.AllGuildConfigs.Where(x => x.Prefix is not null)
.ToDictionary(x => x.GuildId, x => x.Prefix)
.ToConcurrent();
}
public async Task OnReadyAsync()
{
// clear users on short cooldown every GLOBAL_COMMANDS_COOLDOWN miliseconds
using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(GLOBAL_COMMANDS_COOLDOWN));
while (await timer.WaitForNextTickAsync())
{
UsersOnShortCooldown.Clear();
}
}
public string GetPrefix(IGuild guild)
=> GetPrefix(guild?.Id);

View File

@@ -139,7 +139,7 @@ public class StatsService : IStatsService, IReadyExecutor, INService, IDisposabl
textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel));
voiceChannels = guilds.Sum(g => g.Channels.Count(cx => cx is IVoiceChannel));
var timer = new PeriodicTimer(TimeSpan.FromHours(1));
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
do
{
if (string.IsNullOrWhiteSpace(_creds.BotListToken))