feat: upgrade to .NET 6, refactor everything
This commit is contained in:
@@ -1,9 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Autofac;
|
||||
|
||||
using Myriad.Rest.Types;
|
||||
@@ -12,122 +6,119 @@ using Myriad.Types;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
namespace PluralKit.Bot.Interactive
|
||||
namespace PluralKit.Bot.Interactive;
|
||||
|
||||
public abstract class BaseInteractive
|
||||
{
|
||||
public abstract class BaseInteractive
|
||||
protected readonly List<Button> _buttons = new();
|
||||
protected readonly Context _ctx;
|
||||
protected readonly TaskCompletionSource _tcs = new();
|
||||
protected bool _running;
|
||||
|
||||
protected BaseInteractive(Context ctx)
|
||||
{
|
||||
protected readonly Context _ctx;
|
||||
protected readonly List<Button> _buttons = new();
|
||||
protected readonly TaskCompletionSource _tcs = new();
|
||||
protected Message _message { get; private set; }
|
||||
protected bool _running;
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
protected BaseInteractive(Context ctx)
|
||||
protected Message _message { get; private set; }
|
||||
|
||||
public Duration Timeout { get; set; } = Duration.FromMinutes(5);
|
||||
|
||||
protected Button AddButton(Func<InteractionContext, Task> handler, string? label = null,
|
||||
ButtonStyle style = ButtonStyle.Secondary, bool disabled = false)
|
||||
{
|
||||
var dispatch = _ctx.Services.Resolve<InteractionDispatchService>();
|
||||
var customId = dispatch.Register(handler, Timeout);
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
Label = label,
|
||||
Style = style,
|
||||
Disabled = disabled,
|
||||
CustomId = customId,
|
||||
};
|
||||
_buttons.Add(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
public Duration Timeout { get; set; } = Duration.FromMinutes(5);
|
||||
protected async Task Update(InteractionContext ctx)
|
||||
{
|
||||
await ctx.Respond(InteractionResponse.ResponseType.UpdateMessage,
|
||||
new InteractionApplicationCommandCallbackData { Components = GetComponents() });
|
||||
}
|
||||
|
||||
protected Button AddButton(Func<InteractionContext, Task> handler, string? label = null, ButtonStyle style = ButtonStyle.Secondary, bool disabled = false)
|
||||
{
|
||||
var dispatch = _ctx.Services.Resolve<InteractionDispatchService>();
|
||||
var customId = dispatch.Register(handler, Timeout);
|
||||
protected async Task Finish(InteractionContext? ctx = null)
|
||||
{
|
||||
foreach (var button in _buttons)
|
||||
button.Disabled = true;
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
Label = label,
|
||||
Style = style,
|
||||
Disabled = disabled,
|
||||
CustomId = customId
|
||||
};
|
||||
_buttons.Add(button);
|
||||
return button;
|
||||
}
|
||||
if (ctx != null)
|
||||
await Update(ctx);
|
||||
else
|
||||
await _ctx.Rest.EditMessage(_message.ChannelId, _message.Id,
|
||||
new MessageEditRequest { Components = GetComponents() });
|
||||
|
||||
protected async Task Update(InteractionContext ctx)
|
||||
{
|
||||
await ctx.Respond(InteractionResponse.ResponseType.UpdateMessage,
|
||||
new InteractionApplicationCommandCallbackData
|
||||
{
|
||||
Components = GetComponents()
|
||||
});
|
||||
}
|
||||
_tcs.TrySetResult();
|
||||
}
|
||||
|
||||
protected async Task Finish(InteractionContext? ctx = null)
|
||||
{
|
||||
foreach (var button in _buttons)
|
||||
button.Disabled = true;
|
||||
|
||||
if (ctx != null)
|
||||
await Update(ctx);
|
||||
else
|
||||
await _ctx.Rest.EditMessage(_message.ChannelId, _message.Id, new MessageEditRequest
|
||||
{
|
||||
Components = GetComponents()
|
||||
});
|
||||
|
||||
_tcs.TrySetResult();
|
||||
}
|
||||
|
||||
protected async Task Send(string? content = null, Embed? embed = null, AllowedMentions? mentions = null)
|
||||
{
|
||||
_message = await _ctx.Rest.CreateMessage(_ctx.Channel.Id, new MessageRequest
|
||||
protected async Task Send(string? content = null, Embed? embed = null, AllowedMentions? mentions = null)
|
||||
{
|
||||
_message = await _ctx.Rest.CreateMessage(_ctx.Channel.Id,
|
||||
new MessageRequest
|
||||
{
|
||||
Content = content,
|
||||
Embed = embed,
|
||||
AllowedMentions = mentions,
|
||||
Components = GetComponents()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public MessageComponent[] GetComponents()
|
||||
public MessageComponent[] GetComponents()
|
||||
{
|
||||
return new MessageComponent[]
|
||||
{
|
||||
return new MessageComponent[]
|
||||
new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Type = ComponentType.ActionRow,
|
||||
Components = _buttons.Select(b => b.ToMessageComponent()).ToArray()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Setup(Context ctx)
|
||||
{
|
||||
var dispatch = ctx.Services.Resolve<InteractionDispatchService>();
|
||||
foreach (var button in _buttons)
|
||||
button.CustomId = dispatch.Register(button.Handler, Timeout);
|
||||
}
|
||||
|
||||
public abstract Task Start();
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
if (_running)
|
||||
throw new InvalidOperationException("Action is already running");
|
||||
_running = true;
|
||||
|
||||
await Start();
|
||||
|
||||
var cts = new CancellationTokenSource(Timeout.ToTimeSpan());
|
||||
cts.Token.Register(() => _tcs.TrySetException(new TimeoutException("Action timed out")));
|
||||
|
||||
try
|
||||
{
|
||||
await _tcs.Task;
|
||||
Type = ComponentType.ActionRow,
|
||||
Components = _buttons.Select(b => b.ToMessageComponent()).ToArray()
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void Cleanup()
|
||||
public void Setup(Context ctx)
|
||||
{
|
||||
var dispatch = ctx.Services.Resolve<InteractionDispatchService>();
|
||||
foreach (var button in _buttons)
|
||||
button.CustomId = dispatch.Register(button.Handler, Timeout);
|
||||
}
|
||||
|
||||
public abstract Task Start();
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
if (_running)
|
||||
throw new InvalidOperationException("Action is already running");
|
||||
_running = true;
|
||||
|
||||
await Start();
|
||||
|
||||
var cts = new CancellationTokenSource(Timeout.ToTimeSpan());
|
||||
cts.Token.Register(() => _tcs.TrySetException(new TimeoutException("Action timed out")));
|
||||
|
||||
try
|
||||
{
|
||||
var dispatch = _ctx.Services.Resolve<InteractionDispatchService>();
|
||||
foreach (var button in _buttons)
|
||||
dispatch.Unregister(button.CustomId!);
|
||||
await _tcs.Task;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
protected void Cleanup()
|
||||
{
|
||||
var dispatch = _ctx.Services.Resolve<InteractionDispatchService>();
|
||||
foreach (var button in _buttons)
|
||||
dispatch.Unregister(button.CustomId!);
|
||||
}
|
||||
}
|
@@ -1,25 +1,21 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Myriad.Types;
|
||||
|
||||
namespace PluralKit.Bot.Interactive
|
||||
{
|
||||
public class Button
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public ButtonStyle Style { get; set; } = ButtonStyle.Secondary;
|
||||
public string? CustomId { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
public Func<InteractionContext, Task> Handler { get; init; }
|
||||
namespace PluralKit.Bot.Interactive;
|
||||
|
||||
public MessageComponent ToMessageComponent() => new()
|
||||
{
|
||||
Type = ComponentType.Button,
|
||||
Label = Label,
|
||||
Style = Style,
|
||||
CustomId = CustomId,
|
||||
Disabled = Disabled
|
||||
};
|
||||
}
|
||||
public class Button
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public ButtonStyle Style { get; set; } = ButtonStyle.Secondary;
|
||||
public string? CustomId { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
public Func<InteractionContext, Task> Handler { get; init; }
|
||||
|
||||
public MessageComponent ToMessageComponent() => new()
|
||||
{
|
||||
Type = ComponentType.Button,
|
||||
Label = Label,
|
||||
Style = Style,
|
||||
CustomId = CustomId,
|
||||
Disabled = Disabled
|
||||
};
|
||||
}
|
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac;
|
||||
|
||||
using Myriad.Gateway;
|
||||
using Myriad.Rest.Types;
|
||||
@@ -8,102 +6,99 @@ using Myriad.Types;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
using Autofac;
|
||||
namespace PluralKit.Bot.Interactive;
|
||||
|
||||
namespace PluralKit.Bot.Interactive
|
||||
public class YesNoPrompt: BaseInteractive
|
||||
{
|
||||
public class YesNoPrompt: BaseInteractive
|
||||
public YesNoPrompt(Context ctx) : base(ctx)
|
||||
{
|
||||
public bool? Result { get; private set; }
|
||||
public ulong? User { get; set; }
|
||||
public string Message { get; set; } = "Are you sure?";
|
||||
User = ctx.Author.Id;
|
||||
}
|
||||
|
||||
public string AcceptLabel { get; set; } = "OK";
|
||||
public ButtonStyle AcceptStyle { get; set; } = ButtonStyle.Primary;
|
||||
public bool? Result { get; private set; }
|
||||
public ulong? User { get; set; }
|
||||
public string Message { get; set; } = "Are you sure?";
|
||||
|
||||
public string CancelLabel { get; set; } = "Cancel";
|
||||
public ButtonStyle CancelStyle { get; set; } = ButtonStyle.Secondary;
|
||||
public string AcceptLabel { get; set; } = "OK";
|
||||
public ButtonStyle AcceptStyle { get; set; } = ButtonStyle.Primary;
|
||||
|
||||
public override async Task Start()
|
||||
public string CancelLabel { get; set; } = "Cancel";
|
||||
public ButtonStyle CancelStyle { get; set; } = ButtonStyle.Secondary;
|
||||
|
||||
public override async Task Start()
|
||||
{
|
||||
AddButton(ctx => OnButtonClick(ctx, true), AcceptLabel, AcceptStyle);
|
||||
AddButton(ctx => OnButtonClick(ctx, false), CancelLabel, CancelStyle);
|
||||
|
||||
AllowedMentions mentions = null;
|
||||
|
||||
if (User != _ctx.Author.Id)
|
||||
mentions = new AllowedMentions { Users = new[] { User!.Value } };
|
||||
|
||||
await Send(Message, mentions: mentions);
|
||||
}
|
||||
|
||||
private async Task OnButtonClick(InteractionContext ctx, bool result)
|
||||
{
|
||||
if (ctx.User.Id != User)
|
||||
{
|
||||
AddButton(ctx => OnButtonClick(ctx, true), AcceptLabel, AcceptStyle);
|
||||
AddButton(ctx => OnButtonClick(ctx, false), CancelLabel, CancelStyle);
|
||||
|
||||
AllowedMentions mentions = null;
|
||||
|
||||
if (User != _ctx.Author.Id)
|
||||
mentions = new AllowedMentions { Users = new[] { User!.Value } };
|
||||
|
||||
await Send(Message, mentions: mentions);
|
||||
await Update(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task OnButtonClick(InteractionContext ctx, bool result)
|
||||
{
|
||||
if (ctx.User.Id != User)
|
||||
{
|
||||
await Update(ctx);
|
||||
return;
|
||||
}
|
||||
Result = result;
|
||||
await Finish(ctx);
|
||||
}
|
||||
|
||||
Result = result;
|
||||
await Finish(ctx);
|
||||
private bool MessagePredicate(MessageCreateEvent e)
|
||||
{
|
||||
if (e.ChannelId != _ctx.Channel.Id) return false;
|
||||
if (e.Author.Id != User) return false;
|
||||
|
||||
var response = e.Content.ToLowerInvariant();
|
||||
|
||||
if (response == "y" || response == "yes")
|
||||
{
|
||||
Result = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool MessagePredicate(MessageCreateEvent e)
|
||||
if (response == "n" || response == "no")
|
||||
{
|
||||
if (e.ChannelId != _ctx.Channel.Id) return false;
|
||||
if (e.Author.Id != User) return false;
|
||||
|
||||
var response = e.Content.ToLowerInvariant();
|
||||
|
||||
if (response == "y" || response == "yes")
|
||||
{
|
||||
Result = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (response == "n" || response == "no")
|
||||
{
|
||||
Result = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
Result = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public new async Task Run()
|
||||
return false;
|
||||
}
|
||||
|
||||
public new async Task Run()
|
||||
{
|
||||
// todo: can we split this up somehow so it doesn't need to be *completely* copied from BaseInteractive?
|
||||
|
||||
var cts = new CancellationTokenSource(Timeout.ToTimeSpan());
|
||||
|
||||
if (_running)
|
||||
throw new InvalidOperationException("Action is already running");
|
||||
_running = true;
|
||||
|
||||
var queue = _ctx.Services.Resolve<HandlerQueue<MessageCreateEvent>>();
|
||||
|
||||
var messageDispatch = queue.WaitFor(MessagePredicate, Timeout, cts.Token);
|
||||
|
||||
await Start();
|
||||
|
||||
cts.Token.Register(() => _tcs.TrySetException(new TimeoutException("Action timed out")));
|
||||
|
||||
try
|
||||
{
|
||||
// todo: can we split this up somehow so it doesn't need to be *completely* copied from BaseInteractive?
|
||||
|
||||
var cts = new CancellationTokenSource(Timeout.ToTimeSpan());
|
||||
|
||||
if (_running)
|
||||
throw new InvalidOperationException("Action is already running");
|
||||
_running = true;
|
||||
|
||||
var queue = _ctx.Services.Resolve<HandlerQueue<MessageCreateEvent>>();
|
||||
|
||||
var messageDispatch = queue.WaitFor(MessagePredicate, Timeout, cts.Token);
|
||||
|
||||
await Start();
|
||||
|
||||
cts.Token.Register(() => _tcs.TrySetException(new TimeoutException("Action timed out")));
|
||||
|
||||
try
|
||||
{
|
||||
var doneTask = await Task.WhenAny(_tcs.Task, messageDispatch);
|
||||
if (doneTask == messageDispatch)
|
||||
await Finish();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
var doneTask = await Task.WhenAny(_tcs.Task, messageDispatch);
|
||||
if (doneTask == messageDispatch)
|
||||
await Finish();
|
||||
}
|
||||
|
||||
public YesNoPrompt(Context ctx) : base(ctx)
|
||||
finally
|
||||
{
|
||||
User = ctx.Author.Id;
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user