Merge pull request #4 from isbeorn/main
Replace timer with Background Task - lock slash command to prevent concurrency issues
This commit is contained in:
@@ -19,8 +19,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MareSynchronosServer.Discord
|
namespace MareSynchronosServer.Discord {
|
||||||
{
|
|
||||||
public class DiscordBot : IHostedService
|
public class DiscordBot : IHostedService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider services;
|
private readonly IServiceProvider services;
|
||||||
@@ -31,14 +30,20 @@ namespace MareSynchronosServer.Discord
|
|||||||
DiscordSocketClient discordClient;
|
DiscordSocketClient discordClient;
|
||||||
ConcurrentDictionary<ulong, string> DiscordLodestoneMapping = new();
|
ConcurrentDictionary<ulong, string> DiscordLodestoneMapping = new();
|
||||||
private Timer _timer;
|
private Timer _timer;
|
||||||
private Timer _queueTimer;
|
private CancellationTokenSource verificationTaskCts;
|
||||||
|
private Task verificationTaskWorker;
|
||||||
private readonly string[] LodestoneServers = new[] { "eu", "na", "jp", "fr", "de" };
|
private readonly string[] LodestoneServers = new[] { "eu", "na", "jp", "fr", "de" };
|
||||||
private readonly ConcurrentQueue<(Func<Task<Embed>>, SocketSlashCommand)> verificationQueue = new();
|
private readonly ConcurrentQueue<SocketSlashCommand> verificationQueue = new();
|
||||||
|
|
||||||
|
private SemaphoreSlim semaphore;
|
||||||
|
|
||||||
public DiscordBot(IServiceProvider services, IConfiguration configuration, ILogger<DiscordBot> logger)
|
public DiscordBot(IServiceProvider services, IConfiguration configuration, ILogger<DiscordBot> logger)
|
||||||
{
|
{
|
||||||
this.services = services;
|
this.services = services;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.verificationQueue = new ConcurrentQueue<SocketSlashCommand>();
|
||||||
|
this.semaphore = new SemaphoreSlim(1);
|
||||||
|
|
||||||
random = new();
|
random = new();
|
||||||
authToken = configuration.GetValue<string>("DiscordBotToken");
|
authToken = configuration.GetValue<string>("DiscordBotToken");
|
||||||
@@ -53,10 +58,10 @@ namespace MareSynchronosServer.Discord
|
|||||||
|
|
||||||
private async Task DiscordClient_SlashCommandExecuted(SocketSlashCommand arg)
|
private async Task DiscordClient_SlashCommandExecuted(SocketSlashCommand arg)
|
||||||
{
|
{
|
||||||
if (arg.Data.Name == "register")
|
await semaphore.WaitAsync();
|
||||||
{
|
try {
|
||||||
if (arg.Data.Options.FirstOrDefault(f => f.Name == "overwrite_old_account") != null)
|
if (arg.Data.Name == "register") {
|
||||||
{
|
if (arg.Data.Options.FirstOrDefault(f => f.Name == "overwrite_old_account") != null) {
|
||||||
await DeletePreviousUserAccount(arg.User.Id);
|
await DeletePreviousUserAccount(arg.User.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,32 +70,27 @@ namespace MareSynchronosServer.Discord
|
|||||||
modal.WithCustomId("register_modal");
|
modal.WithCustomId("register_modal");
|
||||||
modal.AddTextInput("Enter the Lodestone URL of your Character", "lodestoneurl", TextInputStyle.Short, "https://*.finalfantasyxiv.com/lodestone/character/<CHARACTERID>/", required: true);
|
modal.AddTextInput("Enter the Lodestone URL of your Character", "lodestoneurl", TextInputStyle.Short, "https://*.finalfantasyxiv.com/lodestone/character/<CHARACTERID>/", required: true);
|
||||||
await arg.RespondWithModalAsync(modal.Build());
|
await arg.RespondWithModalAsync(modal.Build());
|
||||||
}
|
} else if (arg.Data.Name == "verify") {
|
||||||
else if (arg.Data.Name == "verify")
|
|
||||||
{
|
|
||||||
EmbedBuilder eb = new();
|
EmbedBuilder eb = new();
|
||||||
if (verificationQueue.Any(u => u.Item2.Id == arg.User.Id))
|
if (verificationQueue.Any(u => u.User.Id == arg.User.Id)) {
|
||||||
{
|
|
||||||
eb.WithTitle("Already queued for verfication");
|
eb.WithTitle("Already queued for verfication");
|
||||||
eb.WithDescription("You are already queued for verification. Please wait.");
|
eb.WithDescription("You are already queued for verification. Please wait.");
|
||||||
await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true);
|
await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true);
|
||||||
}
|
} else if (!DiscordLodestoneMapping.ContainsKey(arg.User.Id)) {
|
||||||
else if (!DiscordLodestoneMapping.ContainsKey(arg.User.Id))
|
|
||||||
{
|
|
||||||
eb.WithTitle("Cannot verify registration");
|
eb.WithTitle("Cannot verify registration");
|
||||||
eb.WithDescription("You need to **/register** first before you can **/verify**");
|
eb.WithDescription("You need to **/register** first before you can **/verify**");
|
||||||
await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true);
|
await arg.RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
await arg.DeferAsync(ephemeral: true);
|
await arg.DeferAsync(ephemeral: true);
|
||||||
verificationQueue.Enqueue((async () => await HandleVerifyAsync(arg.User.Id), arg));
|
verificationQueue.Enqueue(arg);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
await arg.RespondAsync("idk what you did to get here to start, just follow the instructions as provided.", ephemeral: true);
|
await arg.RespondAsync("idk what you did to get here to start, just follow the instructions as provided.", ephemeral: true);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeletePreviousUserAccount(ulong id)
|
private async Task DeletePreviousUserAccount(ulong id)
|
||||||
@@ -352,10 +352,12 @@ namespace MareSynchronosServer.Discord
|
|||||||
discordClient.Disconnected += DiscordClient_Disconnected;
|
discordClient.Disconnected += DiscordClient_Disconnected;
|
||||||
|
|
||||||
_timer = new Timer(UpdateStatus, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
|
_timer = new Timer(UpdateStatus, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
|
||||||
_queueTimer = new Timer(ProcessQueue, null, TimeSpan.Zero, TimeSpan.FromSeconds(2));
|
|
||||||
|
verificationTaskWorker = ProcessQueueWork();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task DiscordClient_Disconnected(Exception arg)
|
private async Task DiscordClient_Disconnected(Exception arg)
|
||||||
{
|
{
|
||||||
authToken = configuration.GetValue<string>("DiscordBotToken");
|
authToken = configuration.GetValue<string>("DiscordBotToken");
|
||||||
@@ -364,13 +366,23 @@ namespace MareSynchronosServer.Discord
|
|||||||
await discordClient.StartAsync();
|
await discordClient.StartAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ProcessQueue(object state)
|
private async Task ProcessQueueWork()
|
||||||
{
|
{
|
||||||
if (verificationQueue.TryDequeue(out var queueitem))
|
verificationTaskCts = new CancellationTokenSource();
|
||||||
{
|
while(!verificationTaskCts.IsCancellationRequested) {
|
||||||
var dataEmbed = await queueitem.Item1.Invoke();
|
|
||||||
await queueitem.Item2.FollowupAsync(embed: dataEmbed, ephemeral: true);
|
if (verificationQueue.TryDequeue(out var queueitem)) {
|
||||||
|
try {
|
||||||
|
var dataEmbed = await HandleVerifyAsync(queueitem.User.Id);
|
||||||
|
await queueitem.FollowupAsync(embed: dataEmbed, ephemeral: true);
|
||||||
|
|
||||||
logger.LogInformation("Sent login information to user");
|
logger.LogInformation("Sent login information to user");
|
||||||
|
} catch(Exception e) {
|
||||||
|
logger.LogError(e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(2), verificationTaskCts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +399,7 @@ namespace MareSynchronosServer.Discord
|
|||||||
public async Task StopAsync(CancellationToken cancellationToken)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_timer?.Change(Timeout.Infinite, 0);
|
_timer?.Change(Timeout.Infinite, 0);
|
||||||
_queueTimer?.Change(Timeout.Infinite, 0);
|
verificationTaskCts?.Cancel();
|
||||||
|
|
||||||
await discordClient.LogoutAsync();
|
await discordClient.LogoutAsync();
|
||||||
await discordClient.StopAsync();
|
await discordClient.StopAsync();
|
||||||
|
|||||||
Reference in New Issue
Block a user