mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 08:34:27 -05:00 
			
		
		
		
	Greatly unboilerplated and simplified nadeko interaction button construction
This commit is contained in:
		@@ -0,0 +1,8 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public interface INadekoInteractionService
 | 
			
		||||
{
 | 
			
		||||
    public NadekoInteraction Create<T>(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        SimpleInteraction<T> inter);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed class NadekoButtonActionInteraction : NadekoButtonOwnInteraction
 | 
			
		||||
{
 | 
			
		||||
    private readonly NadekoInteractionData _data;
 | 
			
		||||
    private readonly Func<SocketMessageComponent, Task> _action;
 | 
			
		||||
 | 
			
		||||
    public NadekoButtonActionInteraction(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        NadekoInteractionData data,
 | 
			
		||||
        Func<SocketMessageComponent, Task> action
 | 
			
		||||
    )
 | 
			
		||||
        : base(client, authorId)
 | 
			
		||||
    {
 | 
			
		||||
        _data = data;
 | 
			
		||||
        _action = action;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override string Name
 | 
			
		||||
        => _data.CustomId;
 | 
			
		||||
    protected override IEmote Emote
 | 
			
		||||
        => _data.Emote;
 | 
			
		||||
    protected override string? Text
 | 
			
		||||
        => _data.Text;
 | 
			
		||||
 | 
			
		||||
    public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
			
		||||
        => _action(smc);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +1,30 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public abstract class NadekoButtonInteraction
 | 
			
		||||
public sealed class NadekoInteraction
 | 
			
		||||
{
 | 
			
		||||
    // improvements:
 | 
			
		||||
    //  - state in OnAction
 | 
			
		||||
    //  - configurable delay
 | 
			
		||||
    //  - 
 | 
			
		||||
    protected abstract string Name { get; }
 | 
			
		||||
    protected abstract IEmote Emote { get; }
 | 
			
		||||
    protected virtual string? Text { get; } = null;
 | 
			
		||||
 | 
			
		||||
    private readonly ulong _authorId;
 | 
			
		||||
    private readonly ButtonBuilder _button;
 | 
			
		||||
    private readonly Func<SocketMessageComponent, Task> _onClick;
 | 
			
		||||
    private readonly bool _onlyAuthor;
 | 
			
		||||
    public DiscordSocketClient Client { get; }
 | 
			
		||||
 | 
			
		||||
    protected readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
			
		||||
    private readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
			
		||||
 | 
			
		||||
    protected IUserMessage message = null!;
 | 
			
		||||
    private IUserMessage message = null!;
 | 
			
		||||
 | 
			
		||||
    protected NadekoButtonInteraction(DiscordSocketClient client)
 | 
			
		||||
    public NadekoInteraction(DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        Func<SocketMessageComponent, Task> onClick,
 | 
			
		||||
        bool onlyAuthor)
 | 
			
		||||
    {
 | 
			
		||||
        Client = client;
 | 
			
		||||
        _authorId = authorId;
 | 
			
		||||
        _button = button;
 | 
			
		||||
        _onClick = onClick;
 | 
			
		||||
        _onlyAuthor = onlyAuthor;
 | 
			
		||||
        _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
			
		||||
        
 | 
			
		||||
        Client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task RunAsync(IUserMessage msg)
 | 
			
		||||
@@ -27,13 +32,12 @@ public abstract class NadekoButtonInteraction
 | 
			
		||||
        message = msg;
 | 
			
		||||
 | 
			
		||||
        Client.InteractionCreated += OnInteraction;
 | 
			
		||||
        await Task.WhenAny(Task.Delay(10_000), _interactionCompletedSource.Task);
 | 
			
		||||
        await Task.WhenAny(Task.Delay(15_000), _interactionCompletedSource.Task);
 | 
			
		||||
        Client.InteractionCreated -= OnInteraction;
 | 
			
		||||
 | 
			
		||||
        await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract ValueTask<bool> Validate(SocketMessageComponent smc);
 | 
			
		||||
    
 | 
			
		||||
    private async Task OnInteraction(SocketInteraction arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (arg is not SocketMessageComponent smc)
 | 
			
		||||
@@ -42,14 +46,11 @@ public abstract class NadekoButtonInteraction
 | 
			
		||||
        if (smc.Message.Id != message.Id)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (smc.Data.CustomId != Name)
 | 
			
		||||
        if (_onlyAuthor && smc.User.Id != _authorId)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!await Validate(smc))
 | 
			
		||||
        {
 | 
			
		||||
            await smc.DeferAsync();
 | 
			
		||||
        if (smc.Data.CustomId != _button.CustomId)
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
@@ -66,18 +67,14 @@ public abstract class NadekoButtonInteraction
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public virtual MessageComponent CreateComponent()
 | 
			
		||||
    public MessageComponent CreateComponent()
 | 
			
		||||
    {
 | 
			
		||||
        var comp = new ComponentBuilder()
 | 
			
		||||
            .WithButton(GetButtonBuilder());
 | 
			
		||||
            .WithButton(_button);
 | 
			
		||||
 | 
			
		||||
        return comp.Build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ButtonBuilder GetButtonBuilder()
 | 
			
		||||
        => new ButtonBuilder(style: ButtonStyle.Secondary, emote: Emote, customId: Name, label: Text);
 | 
			
		||||
 | 
			
		||||
    public abstract Task ExecuteOnActionAsync(SocketMessageComponent smc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// this is all so wrong ...
 | 
			
		||||
    public Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
			
		||||
        => _onClick(smc);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
// namespace NadekoBot;
 | 
			
		||||
//
 | 
			
		||||
// public class NadekoButtonInteractionArray : NadekoButtonInteraction
 | 
			
		||||
// {
 | 
			
		||||
//     private readonly ButtonBuilder[] _bbs;
 | 
			
		||||
//     private readonly NadekoButtonInteraction[] _inters;
 | 
			
		||||
//
 | 
			
		||||
//     public NadekoButtonInteractionArray(params NadekoButtonInteraction[] inters)
 | 
			
		||||
//         : base(inters[0].Client)
 | 
			
		||||
//     {
 | 
			
		||||
//         _inters = inters;
 | 
			
		||||
//         _bbs = inters.Map(x => x.GetButtonBuilder());
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
//     protected override string Name
 | 
			
		||||
//         => throw new NotSupportedException();
 | 
			
		||||
//     protected override IEmote Emote
 | 
			
		||||
//         => throw new NotSupportedException();
 | 
			
		||||
//
 | 
			
		||||
//     protected override ValueTask<bool> Validate(SocketMessageComponent smc)
 | 
			
		||||
//         => new(true);
 | 
			
		||||
//
 | 
			
		||||
//     public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
			
		||||
//     {
 | 
			
		||||
//         for (var i = 0; i < _bbs.Length; i++)
 | 
			
		||||
//         {
 | 
			
		||||
//             if (_bbs[i].CustomId == smc.Data.CustomId)
 | 
			
		||||
//                 return _inters[i].ExecuteOnActionAsync(smc);
 | 
			
		||||
//         }
 | 
			
		||||
//
 | 
			
		||||
//         return Task.CompletedTask;
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
//     public override MessageComponent CreateComponent()
 | 
			
		||||
//     {
 | 
			
		||||
//         var comp = new ComponentBuilder();
 | 
			
		||||
//
 | 
			
		||||
//         foreach (var bb in _bbs)
 | 
			
		||||
//             comp.WithButton(bb);
 | 
			
		||||
//         
 | 
			
		||||
//         return comp.Build();
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Builder class for NadekoInteractions
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class NadekoInteractionBuilder
 | 
			
		||||
{
 | 
			
		||||
    private NadekoInteractionData? iData;
 | 
			
		||||
    private Func<SocketMessageComponent, Task>? action;
 | 
			
		||||
    // private bool isOwn;
 | 
			
		||||
 | 
			
		||||
    public NadekoInteractionBuilder WithData<T>(in T data)
 | 
			
		||||
        where T : NadekoInteractionData
 | 
			
		||||
    {
 | 
			
		||||
        iData = data;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // public NadekoOwnInteractionBuiler WithIsOwn(bool isOwn = true)
 | 
			
		||||
    // {
 | 
			
		||||
    //     this.isOwn = isOwn;
 | 
			
		||||
    //     return this;
 | 
			
		||||
    
 | 
			
		||||
    // }
 | 
			
		||||
    
 | 
			
		||||
    public NadekoInteractionBuilder WithAction(in Func<SocketMessageComponent, Task> fn)
 | 
			
		||||
    {
 | 
			
		||||
        this.action = fn;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NadekoButtonActionInteraction Build(DiscordSocketClient client, ulong userId)
 | 
			
		||||
    {
 | 
			
		||||
        if (iData is null)
 | 
			
		||||
            throw new InvalidOperationException("You have to specify the data before building the interaction");
 | 
			
		||||
 | 
			
		||||
        if (action is null)
 | 
			
		||||
            throw new InvalidOperationException("You have to specify the action before building the interaction");
 | 
			
		||||
 | 
			
		||||
        return new(client, userId, iData, action);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/NadekoBot/Common/Interaction/NadekoInteractionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/NadekoBot/Common/Interaction/NadekoInteractionService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public class NadekoInteractionService : INadekoInteractionService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
 | 
			
		||||
    public NadekoInteractionService(DiscordSocketClient client)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction Create<T>(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        SimpleInteraction<T> inter)
 | 
			
		||||
        => new NadekoInteraction(_client,
 | 
			
		||||
            userId,
 | 
			
		||||
            inter.Button,
 | 
			
		||||
            inter.TriggerAsync,
 | 
			
		||||
            onlyAuthor: true);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Interaction which only the author can use
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class NadekoButtonOwnInteraction : NadekoButtonInteraction
 | 
			
		||||
{
 | 
			
		||||
    protected readonly ulong _authorId;
 | 
			
		||||
 | 
			
		||||
    protected NadekoButtonOwnInteraction(DiscordSocketClient client, ulong authorId) : base(client)
 | 
			
		||||
        => _authorId = authorId;
 | 
			
		||||
 | 
			
		||||
    protected override ValueTask<bool> Validate(SocketMessageComponent smc)
 | 
			
		||||
        => new(smc.User.Id == _authorId);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
public abstract class NInteraction
 | 
			
		||||
{
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly ulong _userId;
 | 
			
		||||
    private readonly Func<SocketMessageComponent, Task> _action;
 | 
			
		||||
 | 
			
		||||
    protected abstract NadekoInteractionData Data { get; }
 | 
			
		||||
 | 
			
		||||
    public NInteraction(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        Func<SocketMessageComponent, Task> action)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _userId = userId;
 | 
			
		||||
        _action = action;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NadekoButtonInteraction GetInteraction()
 | 
			
		||||
        => new NadekoInteractionBuilder()
 | 
			
		||||
           .WithData(Data)
 | 
			
		||||
           .WithAction(_action)
 | 
			
		||||
           .Build(_client, _userId);
 | 
			
		||||
}
 | 
			
		||||
@@ -18,6 +18,7 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
    public CommandHandler _cmdHandler { get; set; }
 | 
			
		||||
    public ILocalization _localization { get; set; }
 | 
			
		||||
    public IEmbedBuilderService _eb { get; set; }
 | 
			
		||||
    public INadekoInteractionService _inter { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected string prefix
 | 
			
		||||
        => _cmdHandler.GetPrefix(ctx.Guild);
 | 
			
		||||
@@ -36,7 +37,7 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
        string error,
 | 
			
		||||
        string url = null,
 | 
			
		||||
        string footer = null, 
 | 
			
		||||
        NadekoButtonInteraction inter = null)
 | 
			
		||||
        NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
 | 
			
		||||
    
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(
 | 
			
		||||
@@ -47,32 +48,32 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
        => ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
 | 
			
		||||
 | 
			
		||||
    // 
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(string text, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(string text, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
 | 
			
		||||
    public Task<IUserMessage> SendPendingAsync(string text, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> SendPendingAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    // localized normal
 | 
			
		||||
    public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendErrorAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendPendingAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendConfirmAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    // localized replies
 | 
			
		||||
    public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
 | 
			
		||||
    public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user