Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

F/180 rewrite cyclic services to jobs #236

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Devscord.DiscordFramework.Commands.Parsing.Models;
using Devscord.DiscordFramework.Commands.Parsing.Models;
using Devscord.DiscordFramework.Middlewares.Contexts;
using Devscord.DiscordFramework.Services;
using Devscord.DiscordFramework.Services.Models;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Devscord.DiscordFramework.Commands.AntiSpam.Models
{
public interface IServerMessagesCacheService : ICyclicService
public interface IServerMessagesCacheService
{
void OverwriteMessages(IEnumerable<SmallMessage> smallMessages);
void AddMessage(SmallMessage smallMessage);
Expand All @@ -19,18 +17,7 @@ public interface IServerMessagesCacheService : ICyclicService

public class ServerMessagesCacheService : IServerMessagesCacheService
{
private static Dictionary<ulong, List<SmallMessage>> _usersMessages;

static ServerMessagesCacheService()
{
_usersMessages = new Dictionary<ulong, List<SmallMessage>>();
//todo remove logic from constructor
RemoveOldMessagesCyclic();
}

public ServerMessagesCacheService()
{
}
private static Dictionary<ulong, List<SmallMessage>> _usersMessages = new();

public void OverwriteMessages(IEnumerable<SmallMessage> smallMessages)
{
Expand All @@ -42,11 +29,9 @@ public void AddMessage(SmallMessage smallMessage)
if (_usersMessages.ContainsKey(smallMessage.UserId))
{
_usersMessages[smallMessage.UserId].Add(smallMessage);
return;
}
else
{
_usersMessages.Add(smallMessage.UserId, new List<SmallMessage> { smallMessage });
}
_usersMessages.Add(smallMessage.UserId, new List<SmallMessage> { smallMessage });
}

public void AddMessage(DiscordRequest request, Contexts contexts)
Expand All @@ -62,20 +47,17 @@ public IEnumerable<SmallMessage> GetLastUserMessages(ulong userId, ulong serverI
: new List<SmallMessage>();
}

public Task Refresh()
{
RemoveOldMessagesCyclic();
return Task.CompletedTask;
}

private static void RemoveOldMessagesCyclic()
public static void RemoveOldMessagesCyclic()
{
var minTimeInPast = DateTime.UtcNow.AddMinutes(-15);
var smallMessages = _usersMessages.Values.Select(list =>
{
list.RemoveAll(message => message.SentAt < minTimeInPast);
return list;
}).Where(x => x.Any()).ToList();
})
.Where(x => x.Any())
.ToList();

if (!smallMessages.Any())
{
return;
Expand Down
53 changes: 8 additions & 45 deletions Watchman.Discord/Areas/Muting/Services/UnmutingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,69 +14,32 @@

namespace Watchman.Discord.Areas.Muting.Services
{
public interface IUnmutingService : ICyclicService
public interface IUnmutingService
{
void UnmuteInFuture(Contexts contexts, MuteEvent muteEvent, UserContext userToUnmute);
bool ShouldBeConsideredAsShortMute(MuteEvent muteEvent);
}

public class UnmutingService : IUnmutingService
{
private const int SHORT_MUTE_TIME_IN_MINUTES = 15;
private readonly ICommandBus commandBus;
private readonly IMutingService mutingService;
private readonly IUsersService _usersService;
private readonly IDiscordServersService _discordServersService;
private readonly HashSet<Guid> _muteEventsAlreadyBeingHandled = new HashSet<Guid>();
private readonly ICommandBus _commandBus;
private readonly HashSet<Guid> _muteEventsAlreadyBeingHandled = new();

public UnmutingService(ICommandBus commandBus, IMutingService mutingService, IUsersService usersService, IDiscordServersService discordServersService)
public UnmutingService(ICommandBus commandBus)
{
this.commandBus = commandBus;
this.mutingService = mutingService;
this._usersService = usersService;
this._discordServersService = discordServersService;
}

public async Task Refresh()
{
await foreach (var server in this._discordServersService.GetDiscordServersAsync())
{
var serverMuteEvents = this.mutingService.GetNotUnmutedMuteEvents(server.Id).ToList();
if (!serverMuteEvents.Any())
{
continue;
}
var contexts = new Contexts();
contexts.SetContext(server);
var textChannels = server.GetTextChannels().ToList();
foreach (var muteEvent in serverMuteEvents.Where(this.ShouldBeConsideredAsShortMute))
{
var user = await this._usersService.GetUserByIdAsync(server, muteEvent.UserId);
var channel = textChannels.FirstOrDefault(x => x.Id == muteEvent.MutedOnChannelId);
if (user == null)
{
await this.mutingService.MarkAsUnmuted(muteEvent);
continue;
}
contexts.SetContext(user);
if (channel != null)
{
contexts.SetContext(channel);
}
this.UnmuteInShortTime(contexts, muteEvent, user);
}
}
this._commandBus = commandBus;
}

public void UnmuteInFuture(Contexts contexts, MuteEvent muteEvent, UserContext userToUnmute)
{
if (this.ShouldBeConsideredAsShortMute(muteEvent))
{
this.UnmuteInShortTime(contexts, muteEvent, userToUnmute);
return;
}
}

private bool ShouldBeConsideredAsShortMute(MuteEvent muteEvent)
public bool ShouldBeConsideredAsShortMute(MuteEvent muteEvent)
{
return muteEvent.TimeRange.End < DateTime.UtcNow.AddMinutes(SHORT_MUTE_TIME_IN_MINUTES);
}
Expand All @@ -96,7 +59,7 @@ private async void UnmuteInShortTime(Contexts contexts, MuteEvent muteEvent, Use
this._muteEventsAlreadyBeingHandled.Add(muteEvent.Id);
await Task.Delay(muteEvent.TimeRange.End - DateTime.UtcNow);
}
await this.commandBus.ExecuteAsync(new UnmuteSpecificEventCommand(contexts, muteEvent, userToUnmute));
await this._commandBus.ExecuteAsync(new UnmuteSpecificEventCommand(contexts, muteEvent, userToUnmute));
this._muteEventsAlreadyBeingHandled.Remove(muteEvent.Id);
}
}
Expand Down
49 changes: 9 additions & 40 deletions Watchman.Web/HangfireJobsService.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
using System;
using Autofac;
using Devscord.DiscordFramework.Services.Models;
using Hangfire;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac;
using Devscord.DiscordFramework.Commands.AntiSpam.Models;
using Devscord.DiscordFramework.Services;
using Devscord.DiscordFramework.Services.Models;
using Hangfire;
using Watchman.Discord.Areas.Muting.Services;
using Watchman.Discord.Areas.Responses.Services;
using Watchman.Web.Jobs;
using Watchman.DomainModel.Configuration.Services;

namespace Watchman.Web
{
public interface IHangfireJobsService
{
void AddJobs(IContainer container, IRecurringJobManager recurringJobManager);
void AddServices(IContainer container, IRecurringJobManager recurringJobManager);
void SetDefaultJobs(IContainer container);
}

Expand All @@ -26,42 +19,18 @@ public class HangfireJobsService : IHangfireJobsService
public void SetDefaultJobs(IContainer container)
{
var recurringJobManager = container.Resolve<IRecurringJobManager>();
this.AddServices(container, recurringJobManager);
this.AddJobs(container, recurringJobManager);
}

public void AddServices(IContainer container, IRecurringJobManager recurringJobManager) //TODO maybe rewrite CyclicServices to Jobs would be good idea
{
var generators = new List<(ICyclicService, RefreshFrequent, bool shouldTriggerNow)>
{
(container.Resolve<ServerMessagesCacheService>(), RefreshFrequent.Quarterly, false),
(container.Resolve<ResponsesCleanupService>(), RefreshFrequent.Daily, false),
(container.Resolve<UnmutingService>(), RefreshFrequent.Quarterly, true) // if RefreshFrequent changed remember to change SHORT_MUTE_TIME_IN_MINUTES in unmutingService!
};
foreach (var (generator, refreshFrequent, shouldTrigger) in generators)
{
var cronExpression = this.GetCronExpression(refreshFrequent);
recurringJobManager.AddOrUpdate(this.FixJobName(generator.GetType().Name), () => generator.Refresh(), cronExpression);
if (shouldTrigger)
{
recurringJobManager.Trigger(this.FixJobName(generator.GetType().Name));
}
}
var configurationService = container.Resolve<ConfigurationService>();
recurringJobManager.AddOrUpdate(nameof(ConfigurationService), () => configurationService.Refresh(), this.GetCronExpression(RefreshFrequent.Minutely));
}

public void AddJobs(IContainer container, IRecurringJobManager recurringJobManager)
{
Assembly.GetAssembly(typeof(HangfireJobsService)).GetTypes()
.Where(x => x.IsAssignableTo<IHangfireJob>() && !x.IsInterface)
.Select(x => (x.Name, Job: (IHangfireJob)container.Resolve(x))).ToList()
.Select(x => (x.Name, Job: (IHangfireJob)container.Resolve(x)))
.ToList()
.ForEach(x =>
{
recurringJobManager.AddOrUpdate(this.FixJobName(x.Name), () => x.Job.Do(), this.GetCronExpression(x.Job.Frequency));
var jobName = this.FixJobName(x.Name);
recurringJobManager.AddOrUpdate(jobName, () => x.Job.Do(), this.GetCronExpression(x.Job.Frequency));
if (x.Job.RunOnStart)
{
recurringJobManager.Trigger(x.Name);
recurringJobManager.Trigger(jobName);
}
});
}
Expand Down
3 changes: 0 additions & 3 deletions Watchman.Web/Jobs/IhangfireJob.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using Devscord.DiscordFramework.Services.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Watchman.Web.Jobs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
using System.Linq;
using System.Linq;
using System.Threading.Tasks;
using Devscord.DiscordFramework.Services;
using Devscord.DiscordFramework.Services.Models;
using Watchman.Discord.Areas.Responses.Services;
using Watchman.DomainModel.Responses;

namespace Watchman.Discord.Areas.Responses.Services
namespace Watchman.Web.Jobs
{
public class ResponsesCleanupService : ICyclicService
public class ResponsesCleanupJob : IHangfireJob
{
public RefreshFrequent Frequency => RefreshFrequent.Daily;
public bool RunOnStart => false;

private readonly ResponsesGetterService _responsesGetterService;
private readonly CustomResponsesService _responsesService;

public ResponsesCleanupService(ResponsesGetterService responsesGetterService, CustomResponsesService responsesService)
public ResponsesCleanupJob(ResponsesGetterService responsesGetterService, CustomResponsesService responsesService)
{
this._responsesGetterService = responsesGetterService;
this._responsesService = responsesService;
}

public async Task Refresh() => await this.CleanDuplicatedResponses();
public async Task Do()
=> await this.CleanDuplicatedResponses();

public async Task CleanDuplicatedResponses()
private async Task CleanDuplicatedResponses()
{
var allResponses = this._responsesGetterService.GetResponsesFromBase().ToList();
var defaultResponses = allResponses.Where(resp => resp.ServerId == 0);
Expand All @@ -32,10 +37,10 @@ public async Task CleanDuplicatedResponses()
}
}

private static bool CompareResponses(Response serverResponse, Response defaultResponse)
private static bool CompareResponses(Response serverResponse, Response defaultResponse)
{
return defaultResponse.Message == serverResponse.Message
return defaultResponse.Message == serverResponse.Message
&& defaultResponse.OnEvent == serverResponse.OnEvent;
}
}
}
}
19 changes: 19 additions & 0 deletions Watchman.Web/Jobs/ServerMessagesCleanupJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Devscord.DiscordFramework.Commands.AntiSpam.Models;
using Devscord.DiscordFramework.Services.Models;
using System.Threading.Tasks;

namespace Watchman.Web.Jobs
{
public class ServerMessagesCleanupJob : IHangfireJob
{
public RefreshFrequent Frequency => RefreshFrequent.Quarterly;

public bool RunOnStart => false;

public Task Do()
{
ServerMessagesCacheService.RemoveOldMessagesCyclic();
return Task.CompletedTask;
}
}
}
60 changes: 60 additions & 0 deletions Watchman.Web/Jobs/UnmutingUsersInFutureJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Devscord.DiscordFramework.Middlewares.Contexts;
using Devscord.DiscordFramework.Services;
using Devscord.DiscordFramework.Services.Models;
using System.Linq;
using System.Threading.Tasks;
using Watchman.Discord.Areas.Muting.Services;
using Watchman.DomainModel.Muting.Services;

namespace Watchman.Web.Jobs
{
public class UnmutingUsersInFutureJob : IHangfireJob
{
public RefreshFrequent Frequency => RefreshFrequent.Quarterly; // if RefreshFrequent changed remember to change SHORT_MUTE_TIME_IN_MINUTES in unmutingService!
public bool RunOnStart => true;

private readonly IUnmutingService _unmutingService;
private readonly IMutingService _mutingService;
private readonly IUsersService _usersService;
private readonly IDiscordServersService _discordServersService;

public UnmutingUsersInFutureJob(IUnmutingService unmutingService, IMutingService mutingService, IUsersService usersService, IDiscordServersService discordServersService)
{
this._unmutingService = unmutingService;
this._mutingService = mutingService;
this._usersService = usersService;
this._discordServersService = discordServersService;
}

public async Task Do()
{
await foreach (var server in this._discordServersService.GetDiscordServersAsync())
{
var serverMuteEvents = this._mutingService.GetNotUnmutedMuteEvents(server.Id).ToList();
if (!serverMuteEvents.Any())
{
continue;
}
var contexts = new Contexts();
contexts.SetContext(server);
var textChannels = server.GetTextChannels().ToList();
foreach (var muteEvent in serverMuteEvents.Where(this._unmutingService.ShouldBeConsideredAsShortMute))
{
var user = await this._usersService.GetUserByIdAsync(server, muteEvent.UserId);
var channel = textChannels.FirstOrDefault(x => x.Id == muteEvent.MutedOnChannelId);
if (user == null)
{
await this._mutingService.MarkAsUnmuted(muteEvent);
continue;
}
contexts.SetContext(user);
if (channel != null)
{
contexts.SetContext(channel);
}
this._unmutingService.UnmuteInFuture(contexts, muteEvent, user);
}
}
}
}
}