mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-03 16:24:27 -05:00 
			
		
		
		
	fix: Fixed .h not working on some commands
add: Added select menu for the .mdls command dev: Reworked the way interactions are created and sent. It is much better but far from perfect
This commit is contained in:
		@@ -12,7 +12,7 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
 | 
				
			|||||||
    private TypedKey<KeepReport> _keepReportKey = new("cleanup:report");
 | 
					    private TypedKey<KeepReport> _keepReportKey = new("cleanup:report");
 | 
				
			||||||
    private TypedKey<bool> _keepTriggerKey = new("cleanup:trigger");
 | 
					    private TypedKey<bool> _keepTriggerKey = new("cleanup:trigger");
 | 
				
			||||||
    private readonly DiscordSocketClient _client;
 | 
					    private readonly DiscordSocketClient _client;
 | 
				
			||||||
    private ConcurrentDictionary<int, ulong[]> guildIds;
 | 
					    private ConcurrentDictionary<int, ulong[]> guildIds = new();
 | 
				
			||||||
    private readonly IBotCredsProvider _creds;
 | 
					    private readonly IBotCredsProvider _creds;
 | 
				
			||||||
    private readonly DbService _db;
 | 
					    private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,9 +68,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Cmd]
 | 
					    [Cmd]
 | 
				
			||||||
    public async Task ExprAdd(string key, [Leftover] string message)
 | 
					    public async Task ExprAdd(string trigger, [Leftover] string response)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
 | 
					        if (string.IsNullOrWhiteSpace(response) || string.IsNullOrWhiteSpace(trigger))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -81,7 +81,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ExprAddInternalAsync(key, message);
 | 
					        await ExprAddInternalAsync(trigger, response);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Cmd]
 | 
					    [Cmd]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,7 +74,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
        var stats = await _gamblingTxTracker.GetAllAsync();
 | 
					        var stats = await _gamblingTxTracker.GetAllAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var eb = _sender.CreateEmbed()
 | 
					        var eb = _sender.CreateEmbed()
 | 
				
			||||||
            .WithOkColor();
 | 
					                        .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var str = "` Feature `|`   Bet  `|`Paid Out`|`  RoI  `\n";
 | 
					        var str = "` Feature `|`   Bet  `|`Paid Out`|`  RoI  `\n";
 | 
				
			||||||
        str += "――――――――――――――――――――\n";
 | 
					        str += "――――――――――――――――――――\n";
 | 
				
			||||||
@@ -119,15 +119,15 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // [21:03] Bob Page: Kinda remids me of US economy
 | 
					        // [21:03] Bob Page: Kinda remids me of US economy
 | 
				
			||||||
        var embed = _sender.CreateEmbed()
 | 
					        var embed = _sender.CreateEmbed()
 | 
				
			||||||
                    .WithTitle(GetText(strs.economy_state))
 | 
					                           .WithTitle(GetText(strs.economy_state))
 | 
				
			||||||
                    .AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
 | 
					                           .AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
 | 
				
			||||||
                    .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
 | 
					                           .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
 | 
				
			||||||
                    .AddField(GetText(strs.currency_planted), N(ec.Planted))
 | 
					                           .AddField(GetText(strs.currency_planted), N(ec.Planted))
 | 
				
			||||||
                    .AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
 | 
					                           .AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
 | 
				
			||||||
                    .AddField(GetText(strs.bot_currency), N(ec.Bot))
 | 
					                           .AddField(GetText(strs.bot_currency), N(ec.Bot))
 | 
				
			||||||
                    .AddField(GetText(strs.bank_accounts), N(ec.Bank))
 | 
					                           .AddField(GetText(strs.bank_accounts), N(ec.Bank))
 | 
				
			||||||
                    .AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
 | 
					                           .AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
 | 
				
			||||||
                    .WithOkColor();
 | 
					                           .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
 | 
					        // ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
 | 
				
			||||||
        await Response().Embed(embed).SendAsync();
 | 
					        await Response().Embed(embed).SendAsync();
 | 
				
			||||||
@@ -155,17 +155,14 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private NadekoInteraction CreateRemindMeInteraction(int period)
 | 
					    private NadekoInteraction CreateRemindMeInteraction(int period)
 | 
				
			||||||
    {
 | 
					        => _inter
 | 
				
			||||||
        return _inter
 | 
					 | 
				
			||||||
            .Create(ctx.User.Id,
 | 
					            .Create(ctx.User.Id,
 | 
				
			||||||
                new SimpleInteraction<DateTime>(
 | 
					                new ButtonBuilder(
 | 
				
			||||||
                    new ButtonBuilder(
 | 
					                    label: "Remind me",
 | 
				
			||||||
                        label: "Remind me",
 | 
					                    emote: Emoji.Parse("⏰"),
 | 
				
			||||||
                        emote: Emoji.Parse("⏰"),
 | 
					                    customId: "timely:remind_me"),
 | 
				
			||||||
                        customId: "timely:remind_me"),
 | 
					                (smc) => RemindTimelyAction(smc, DateTime.UtcNow.Add(TimeSpan.FromHours(period)))
 | 
				
			||||||
                    RemindTimelyAction,
 | 
					            );
 | 
				
			||||||
                    DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Cmd]
 | 
					    [Cmd]
 | 
				
			||||||
    public async Task Timely()
 | 
					    public async Task Timely()
 | 
				
			||||||
@@ -311,9 +308,9 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var embed = _sender.CreateEmbed()
 | 
					        var embed = _sender.CreateEmbed()
 | 
				
			||||||
                    .WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
 | 
					                           .WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
 | 
				
			||||||
                                                         ?? $"{userId}")))
 | 
					                                                                ?? $"{userId}")))
 | 
				
			||||||
                    .WithOkColor();
 | 
					                           .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var sb = new StringBuilder();
 | 
					        var sb = new StringBuilder();
 | 
				
			||||||
        foreach (var tr in trs)
 | 
					        foreach (var tr in trs)
 | 
				
			||||||
@@ -408,7 +405,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
        await Response().Confirm(strs.has(Format.Code(userId.ToString()), cur)).SendAsync();
 | 
					        await Response().Confirm(strs.has(Format.Code(userId.ToString()), cur)).SendAsync();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task BankAction(SocketMessageComponent smc, object _)
 | 
					    private async Task BankAction(SocketMessageComponent smc)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var balance = await _bank.GetBalanceAsync(ctx.User.Id);
 | 
					        var balance = await _bank.GetBalanceAsync(ctx.User.Id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -419,11 +416,11 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private NadekoInteraction CreateCashInteraction()
 | 
					    private NadekoInteraction CreateCashInteraction()
 | 
				
			||||||
        => _inter.Create<object>(ctx.User.Id,
 | 
					        => _inter.Create(ctx.User.Id,
 | 
				
			||||||
            new(new(
 | 
					            new ButtonBuilder(
 | 
				
			||||||
                    customId: "cash:bank_show_balance",
 | 
					                customId: "cash:bank_show_balance",
 | 
				
			||||||
                    emote: new Emoji("🏦")),
 | 
					                emote: new Emoji("🏦")),
 | 
				
			||||||
                BankAction));
 | 
					            BankAction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Cmd]
 | 
					    [Cmd]
 | 
				
			||||||
    [Priority(1)]
 | 
					    [Priority(1)]
 | 
				
			||||||
@@ -732,10 +729,10 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var eb = _sender.CreateEmbed()
 | 
					        var eb = _sender.CreateEmbed()
 | 
				
			||||||
                 .WithAuthor(ctx.User)
 | 
					                        .WithAuthor(ctx.User)
 | 
				
			||||||
                 .WithDescription(Format.Bold(str))
 | 
					                        .WithDescription(Format.Bold(str))
 | 
				
			||||||
                 .AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
 | 
					                        .AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
 | 
				
			||||||
                 .WithOkColor();
 | 
					                        .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await Response().Embed(eb).SendAsync();
 | 
					        await Response().Embed(eb).SendAsync();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -799,8 +796,9 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
              .CurrentPage(page)
 | 
					              .CurrentPage(page)
 | 
				
			||||||
              .Page((toSend, curPage) =>
 | 
					              .Page((toSend, curPage) =>
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
                  var embed = _sender.CreateEmbed().WithOkColor()
 | 
					                  var embed = _sender.CreateEmbed()
 | 
				
			||||||
                                                .WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
 | 
					                                     .WithOkColor()
 | 
				
			||||||
 | 
					                                     .WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                  if (!toSend.Any())
 | 
					                  if (!toSend.Any())
 | 
				
			||||||
                  {
 | 
					                  {
 | 
				
			||||||
@@ -923,11 +921,11 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var eb = _sender.CreateEmbed()
 | 
					        var eb = _sender.CreateEmbed()
 | 
				
			||||||
                 .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                 .WithDescription(sb.ToString())
 | 
					                        .WithDescription(sb.ToString())
 | 
				
			||||||
                 .AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
 | 
					                        .AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
 | 
				
			||||||
                 .AddField(GetText(strs.won), $"{(long)result.Won}", true)
 | 
					                        .AddField(GetText(strs.won), $"{(long)result.Won}", true)
 | 
				
			||||||
                 .WithAuthor(ctx.User);
 | 
					                        .WithAuthor(ctx.User);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await Response().Embed(eb).SendAsync();
 | 
					        await Response().Embed(eb).SendAsync();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,9 +76,12 @@ public partial class Gambling
 | 
				
			|||||||
                .WithOkColor();
 | 
					                .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
 | 
					            var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
 | 
				
			||||||
            var si = new SimpleInteraction<long>(bb, (_, amount) => Slot(amount), amount);
 | 
					            var inter = _inter.Create(ctx.User.Id, bb, smc =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                smc.DeferAsync();
 | 
				
			||||||
 | 
					                return Slot(amount);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var inter = _inter.Create(ctx.User.Id, si);
 | 
					 | 
				
			||||||
            var msg = await ctx.Channel.SendFileAsync(imgStream,
 | 
					            var msg = await ctx.Channel.SendFileAsync(imgStream,
 | 
				
			||||||
                "result.png",
 | 
					                "result.png",
 | 
				
			||||||
                embed: eb.Build(),
 | 
					                embed: eb.Build(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ using NadekoBot.Modules.Help.Services;
 | 
				
			|||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using Nadeko.Common.Medusa;
 | 
					using Nadeko.Common.Medusa;
 | 
				
			||||||
 | 
					using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Modules.Help;
 | 
					namespace NadekoBot.Modules.Help;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,11 +87,31 @@ public sealed partial class Help : NadekoModule<HelpService>
 | 
				
			|||||||
                topLevelModules.Add(m);
 | 
					                topLevelModules.Add(m);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var menu = new SelectMenuBuilder()
 | 
				
			||||||
 | 
					                   .WithPlaceholder("Select a module to see its commands")
 | 
				
			||||||
 | 
					                   .WithCustomId("modules");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach (var m in topLevelModules)
 | 
				
			||||||
 | 
					            menu.AddOption(m.Name, m.Name, GetModuleEmoji(m.Name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var inter = _inter.Create(ctx.User.Id,
 | 
				
			||||||
 | 
					            menu,
 | 
				
			||||||
 | 
					            async (smc) =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await smc.DeferAsync();
 | 
				
			||||||
 | 
					                var val = smc.Data.Values.FirstOrDefault();
 | 
				
			||||||
 | 
					                if (val is null)
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                await Commands(val);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await Response()
 | 
					        await Response()
 | 
				
			||||||
              .Paginated()
 | 
					              .Paginated()
 | 
				
			||||||
              .Items(topLevelModules)
 | 
					              .Items(topLevelModules)
 | 
				
			||||||
              .PageSize(12)
 | 
					              .PageSize(12)
 | 
				
			||||||
              .CurrentPage(page)
 | 
					              .CurrentPage(page)
 | 
				
			||||||
 | 
					              .Interaction(inter)
 | 
				
			||||||
              .AddFooter(false)
 | 
					              .AddFooter(false)
 | 
				
			||||||
              .Page((items, _) =>
 | 
					              .Page((items, _) =>
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
@@ -442,7 +463,7 @@ public sealed partial class Help : NadekoModule<HelpService>
 | 
				
			|||||||
                 .SendAsync();
 | 
					                 .SendAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Task SelfhostAction(SocketMessageComponent smc, object _)
 | 
					    private Task SelfhostAction(SocketMessageComponent smc)
 | 
				
			||||||
        => smc.RespondConfirmAsync(_sender,
 | 
					        => smc.RespondConfirmAsync(_sender,
 | 
				
			||||||
            """
 | 
					            """
 | 
				
			||||||
            - In case you don't want or cannot Donate to NadekoBot project, but you
 | 
					            - In case you don't want or cannot Donate to NadekoBot project, but you
 | 
				
			||||||
@@ -460,11 +481,11 @@ public sealed partial class Help : NadekoModule<HelpService>
 | 
				
			|||||||
    public async Task Donate()
 | 
					    public async Task Donate()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var selfhostInter = _inter.Create(ctx.User.Id,
 | 
					        var selfhostInter = _inter.Create(ctx.User.Id,
 | 
				
			||||||
            new SimpleInteraction<object>(new ButtonBuilder(
 | 
					            new ButtonBuilder(
 | 
				
			||||||
                    emote: new Emoji("🖥️"),
 | 
					                emote: new Emoji("🖥️"),
 | 
				
			||||||
                    customId: "donate:selfhosting",
 | 
					                customId: "donate:selfhosting",
 | 
				
			||||||
                    label: "Selfhosting"),
 | 
					                label: "Selfhosting"),
 | 
				
			||||||
                SelfhostAction));
 | 
					            SelfhostAction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var eb = _sender.CreateEmbed()
 | 
					        var eb = _sender.CreateEmbed()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -117,35 +117,35 @@ public partial class Utility
 | 
				
			|||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [UserPerm(GuildPerm.ManageMessages)]
 | 
					        [UserPerm(GuildPerm.ManageMessages)]
 | 
				
			||||||
        [Priority(0)]
 | 
					        [Priority(0)]
 | 
				
			||||||
        public Task Repeat(ITextChannel ch, StoopidTime interval, [Leftover] string message)
 | 
					        public Task Repeat(ITextChannel channel, StoopidTime interval, [Leftover] string message)
 | 
				
			||||||
            => Repeat(ch, null, interval, message);
 | 
					            => Repeat(channel, null, interval, message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [UserPerm(GuildPerm.ManageMessages)]
 | 
					        [UserPerm(GuildPerm.ManageMessages)]
 | 
				
			||||||
        [Priority(1)]
 | 
					        [Priority(1)]
 | 
				
			||||||
        public Task Repeat(GuildDateTime dt, [Leftover] string message)
 | 
					        public Task Repeat(GuildDateTime timeOfDay, [Leftover] string message)
 | 
				
			||||||
            => Repeat(dt, null, message);
 | 
					            => Repeat(timeOfDay, null, message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [UserPerm(GuildPerm.ManageMessages)]
 | 
					        [UserPerm(GuildPerm.ManageMessages)]
 | 
				
			||||||
        [Priority(1)]
 | 
					        [Priority(1)]
 | 
				
			||||||
        public Task Repeat(ITextChannel channel, GuildDateTime dt, [Leftover] string message)
 | 
					        public Task Repeat(ITextChannel channel, GuildDateTime timeOfDay, [Leftover] string message)
 | 
				
			||||||
            => Repeat(channel, dt, null, message);
 | 
					            => Repeat(channel, timeOfDay, null, message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [UserPerm(GuildPerm.ManageMessages)]
 | 
					        [UserPerm(GuildPerm.ManageMessages)]
 | 
				
			||||||
        [Priority(2)]
 | 
					        [Priority(2)]
 | 
				
			||||||
        public Task Repeat(GuildDateTime? dt, StoopidTime? interval, [Leftover] string message)
 | 
					        public Task Repeat(GuildDateTime? timeOfDay, StoopidTime? interval, [Leftover] string message)
 | 
				
			||||||
            => Repeat(ctx.Channel, dt, interval, message);
 | 
					            => Repeat(ctx.Channel, timeOfDay, interval, message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [UserPerm(GuildPerm.ManageMessages)]
 | 
					        [UserPerm(GuildPerm.ManageMessages)]
 | 
				
			||||||
        [Priority(3)]
 | 
					        [Priority(3)]
 | 
				
			||||||
        public async Task Repeat(IMessageChannel channel, GuildDateTime? dt, StoopidTime? interval,
 | 
					        public async Task Repeat(IMessageChannel channel, GuildDateTime? timeOfDay, StoopidTime? interval,
 | 
				
			||||||
            [Leftover] string message)
 | 
					            [Leftover] string message)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (channel is not ITextChannel txtCh || txtCh.GuildId != ctx.Guild.Id)
 | 
					            if (channel is not ITextChannel txtCh || txtCh.GuildId != ctx.Guild.Id)
 | 
				
			||||||
@@ -155,7 +155,7 @@ public partial class Utility
 | 
				
			|||||||
            if (!perms.SendMessages)
 | 
					            if (!perms.SendMessages)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var startTimeOfDay = dt?.InputTimeUtc.TimeOfDay;
 | 
					            var startTimeOfDay = timeOfDay?.InputTimeUtc.TimeOfDay;
 | 
				
			||||||
            // if interval not null, that means user specified it (don't change it)
 | 
					            // if interval not null, that means user specified it (don't change it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // if interval is null set the default to:
 | 
					            // if interval is null set the default to:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -475,7 +475,8 @@ public partial class Xp : NadekoModule<XpService>
 | 
				
			|||||||
                          emote: Emoji.Parse("👐"),
 | 
					                          emote: Emoji.Parse("👐"),
 | 
				
			||||||
                          isDisabled: ownedItem.IsUsing);
 | 
					                          isDisabled: ownedItem.IsUsing);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                      var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
 | 
					                      var inter = _inter.Create(
 | 
				
			||||||
 | 
					                          ctx.User.Id,
 | 
				
			||||||
                          button,
 | 
					                          button,
 | 
				
			||||||
                          OnShopUse,
 | 
					                          OnShopUse,
 | 
				
			||||||
                          (key, itemType));
 | 
					                          (key, itemType));
 | 
				
			||||||
@@ -488,7 +489,8 @@ public partial class Xp : NadekoModule<XpService>
 | 
				
			|||||||
                          "xpshop:buy",
 | 
					                          "xpshop:buy",
 | 
				
			||||||
                          emote: Emoji.Parse("💰"));
 | 
					                          emote: Emoji.Parse("💰"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                      var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
 | 
					                      var inter = _inter.Create(
 | 
				
			||||||
 | 
					                          ctx.User.Id,
 | 
				
			||||||
                          button,
 | 
					                          button,
 | 
				
			||||||
                          OnShopBuy,
 | 
					                          OnShopBuy,
 | 
				
			||||||
                          (key, itemType));
 | 
					                          (key, itemType));
 | 
				
			||||||
@@ -507,10 +509,10 @@ public partial class Xp : NadekoModule<XpService>
 | 
				
			|||||||
        NadekoInteraction GetUseInteraction()
 | 
					        NadekoInteraction GetUseInteraction()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return _inter.Create(ctx.User.Id,
 | 
					            return _inter.Create(ctx.User.Id,
 | 
				
			||||||
                new SimpleInteraction<object>(
 | 
					                new(label: "Use", customId: "xpshop:use_item", emote: Emoji.Parse("👐")),
 | 
				
			||||||
                    new ButtonBuilder(label: "Use", customId: "xpshop:use_item", emote: Emoji.Parse("👐")),
 | 
					                async (_, state) => await XpShopUse(state.type, state.key),
 | 
				
			||||||
                    async (smc, _) => await XpShopUse(type, key)
 | 
					                (type, key)
 | 
				
			||||||
                ));
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (result != BuyResult.Success)
 | 
					        if (result != BuyResult.Success)
 | 
				
			||||||
@@ -551,11 +553,8 @@ public partial class Xp : NadekoModule<XpService>
 | 
				
			|||||||
        await ctx.OkAsync();
 | 
					        await ctx.OkAsync();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task OnShopUse(SocketMessageComponent smc, (string? key, XpShopItemType type)? maybeState)
 | 
					    private async Task OnShopUse(SocketMessageComponent smc, (string key, XpShopItemType type) state)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (maybeState is not { } state)
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var (key, type) = state;
 | 
					        var (key, type) = state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var result = await _service.UseShopItemAsync(ctx.User.Id, type, key);
 | 
					        var result = await _service.UseShopItemAsync(ctx.User.Id, type, key);
 | 
				
			||||||
@@ -567,11 +566,8 @@ public partial class Xp : NadekoModule<XpService>
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task OnShopBuy(SocketMessageComponent smc, (string? key, XpShopItemType type)? maybeState)
 | 
					    private async Task OnShopBuy(SocketMessageComponent smc, (string key, XpShopItemType type) state)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (maybeState is not { } state)
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var (key, type) = state;
 | 
					        var (key, type) = state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var result = await _service.BuyShopItemAsync(ctx.User.Id, type, key);
 | 
					        var result = await _service.BuyShopItemAsync(ctx.User.Id, type, key);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public interface INadekoInteractionService
 | 
					public interface INadekoInteractionService
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    public NadekoInteraction Create(
 | 
				
			||||||
 | 
					        ulong userId,
 | 
				
			||||||
 | 
					        ButtonBuilder button,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, Task> onTrigger,
 | 
				
			||||||
 | 
					        bool singleUse = true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public NadekoInteraction Create<T>(
 | 
					    public NadekoInteraction Create<T>(
 | 
				
			||||||
        ulong userId,
 | 
					        ulong userId,
 | 
				
			||||||
        SimpleInteraction<T> inter);
 | 
					        ButtonBuilder button,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, T, Task> onTrigger,
 | 
				
			||||||
 | 
					        in T state,
 | 
				
			||||||
 | 
					        bool singleUse = true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    NadekoInteraction Create(
 | 
				
			||||||
 | 
					        ulong userId,
 | 
				
			||||||
 | 
					        SelectMenuBuilder menu,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, Task> onTrigger,
 | 
				
			||||||
 | 
					        bool singleUse = true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								src/NadekoBot/_common/Interaction/InteractionHelpers.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/NadekoBot/_common/Interaction/InteractionHelpers.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public static class InteractionHelpers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static readonly IEmote ArrowLeft = Emote.Parse("<:x:1232256519844790302>");
 | 
				
			||||||
 | 
					    public static readonly IEmote ArrowRight = Emote.Parse("<:x:1232256515298295838>");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public sealed class NadekoButtonInteraction : NadekoInteraction
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public NadekoButtonInteraction(
 | 
				
			||||||
 | 
					        DiscordSocketClient client,
 | 
				
			||||||
 | 
					        ulong authorId,
 | 
				
			||||||
 | 
					        ButtonBuilder button,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, Task> onClick,
 | 
				
			||||||
 | 
					        bool onlyAuthor,
 | 
				
			||||||
 | 
					        bool singleUse = true)
 | 
				
			||||||
 | 
					        : base(client, authorId, button.CustomId, onClick, onlyAuthor, singleUse)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Button = button;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ButtonBuilder Button { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void AddTo(ComponentBuilder cb)
 | 
				
			||||||
 | 
					        => cb.WithButton(Button);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public static class NadekoInteractionExtensions
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static MessageComponent CreateComponent(
 | 
				
			||||||
 | 
					        this NadekoInteraction nadekoInteraction
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var cb = new ComponentBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nadekoInteraction.AddTo(cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cb.Build();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public sealed class NadekoSelectInteraction : NadekoInteraction
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public NadekoSelectInteraction(
 | 
				
			||||||
 | 
					        DiscordSocketClient client,
 | 
				
			||||||
 | 
					        ulong authorId,
 | 
				
			||||||
 | 
					        SelectMenuBuilder menu,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, Task> onClick,
 | 
				
			||||||
 | 
					        bool onlyAuthor,
 | 
				
			||||||
 | 
					        bool singleUse = true)
 | 
				
			||||||
 | 
					        : base(client, authorId, menu.CustomId, onClick, onlyAuthor, singleUse)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Menu = menu;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SelectMenuBuilder Menu { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override void AddTo(ComponentBuilder cb)
 | 
				
			||||||
 | 
					        => cb.WithSelectMenu(Menu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,9 +1,8 @@
 | 
				
			|||||||
namespace NadekoBot;
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public sealed class NadekoInteraction
 | 
					public abstract class NadekoInteraction
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private readonly ulong _authorId;
 | 
					    private readonly ulong _authorId;
 | 
				
			||||||
    private readonly ButtonBuilder _button;
 | 
					 | 
				
			||||||
    private readonly Func<SocketMessageComponent, Task> _onClick;
 | 
					    private readonly Func<SocketMessageComponent, Task> _onClick;
 | 
				
			||||||
    private readonly bool _onlyAuthor;
 | 
					    private readonly bool _onlyAuthor;
 | 
				
			||||||
    public DiscordSocketClient Client { get; }
 | 
					    public DiscordSocketClient Client { get; }
 | 
				
			||||||
@@ -11,17 +10,22 @@ public sealed class NadekoInteraction
 | 
				
			|||||||
    private readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
					    private readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private IUserMessage message = null!;
 | 
					    private IUserMessage message = null!;
 | 
				
			||||||
 | 
					    private readonly string _customId;
 | 
				
			||||||
 | 
					    private readonly bool _singleUse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public NadekoInteraction(DiscordSocketClient client,
 | 
					    public NadekoInteraction(
 | 
				
			||||||
 | 
					        DiscordSocketClient client,
 | 
				
			||||||
        ulong authorId,
 | 
					        ulong authorId,
 | 
				
			||||||
        ButtonBuilder button,
 | 
					        string customId,
 | 
				
			||||||
        Func<SocketMessageComponent, Task> onClick,
 | 
					        Func<SocketMessageComponent, Task> onClick,
 | 
				
			||||||
        bool onlyAuthor)
 | 
					        bool onlyAuthor,
 | 
				
			||||||
 | 
					        bool singleUse = true)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _authorId = authorId;
 | 
					        _authorId = authorId;
 | 
				
			||||||
        _button = button;
 | 
					        _customId = customId;
 | 
				
			||||||
        _onClick = onClick;
 | 
					        _onClick = onClick;
 | 
				
			||||||
        _onlyAuthor = onlyAuthor;
 | 
					        _onlyAuthor = onlyAuthor;
 | 
				
			||||||
 | 
					        _singleUse = singleUse;
 | 
				
			||||||
        _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
					        _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Client = client;
 | 
					        Client = client;
 | 
				
			||||||
@@ -32,7 +36,10 @@ public sealed class NadekoInteraction
 | 
				
			|||||||
        message = msg;
 | 
					        message = msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Client.InteractionCreated += OnInteraction;
 | 
					        Client.InteractionCreated += OnInteraction;
 | 
				
			||||||
        await Task.WhenAny(Task.Delay(15_000), _interactionCompletedSource.Task);
 | 
					        if (_singleUse)
 | 
				
			||||||
 | 
					            await Task.WhenAny(Task.Delay(30_000), _interactionCompletedSource.Task);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            await Task.Delay(30_000);
 | 
				
			||||||
        Client.InteractionCreated -= OnInteraction;
 | 
					        Client.InteractionCreated -= OnInteraction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
 | 
					        await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
 | 
				
			||||||
@@ -49,15 +56,13 @@ public sealed class NadekoInteraction
 | 
				
			|||||||
        if (_onlyAuthor && smc.User.Id != _authorId)
 | 
					        if (_onlyAuthor && smc.User.Id != _authorId)
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (smc.Data.CustomId != _button.CustomId)
 | 
					        if (smc.Data.CustomId != _customId)
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _ = Task.Run(async () =>
 | 
					        _ = Task.Run(async () =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await ExecuteOnActionAsync(smc);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // this should only be a thing on single-response buttons
 | 
					 | 
				
			||||||
            _interactionCompletedSource.TrySetResult(true);
 | 
					            _interactionCompletedSource.TrySetResult(true);
 | 
				
			||||||
 | 
					            await ExecuteOnActionAsync(smc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!smc.HasResponded)
 | 
					            if (!smc.HasResponded)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -69,13 +74,7 @@ public sealed class NadekoInteraction
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MessageComponent CreateComponent()
 | 
					    public abstract void AddTo(ComponentBuilder cb);
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var comp = new ComponentBuilder()
 | 
					 | 
				
			||||||
            .WithButton(_button);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return comp.Build();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
					    public Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
				
			||||||
        => _onClick(smc);
 | 
					        => _onClick(smc);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
namespace NadekoBot;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// <summary>
 | 
					 | 
				
			||||||
/// Represents essential interacation data
 | 
					 | 
				
			||||||
/// </summary>
 | 
					 | 
				
			||||||
/// <param name="Emote">Emote which will show on a button</param>
 | 
					 | 
				
			||||||
/// <param name="CustomId">Custom interaction id</param>
 | 
					 | 
				
			||||||
public record NadekoInteractionData(IEmote Emote, string CustomId, string? Text = null);
 | 
					 | 
				
			||||||
@@ -9,12 +9,39 @@ public class NadekoInteractionService : INadekoInteractionService, INService
 | 
				
			|||||||
        _client = client;
 | 
					        _client = client;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NadekoInteraction Create(
 | 
				
			||||||
 | 
					        ulong userId,
 | 
				
			||||||
 | 
					        ButtonBuilder button,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, Task> onTrigger,
 | 
				
			||||||
 | 
					        bool singleUse = true)
 | 
				
			||||||
 | 
					        => new NadekoButtonInteraction(_client,
 | 
				
			||||||
 | 
					            userId,
 | 
				
			||||||
 | 
					            button,
 | 
				
			||||||
 | 
					            onTrigger,
 | 
				
			||||||
 | 
					            onlyAuthor: true,
 | 
				
			||||||
 | 
					            singleUse: singleUse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public NadekoInteraction Create<T>(
 | 
					    public NadekoInteraction Create<T>(
 | 
				
			||||||
        ulong userId,
 | 
					        ulong userId,
 | 
				
			||||||
        SimpleInteraction<T> inter)
 | 
					        ButtonBuilder button,
 | 
				
			||||||
        => new NadekoInteraction(_client,
 | 
					        Func<SocketMessageComponent, T, Task> onTrigger,
 | 
				
			||||||
 | 
					        in T state,
 | 
				
			||||||
 | 
					        bool singleUse = true)
 | 
				
			||||||
 | 
					        => Create(userId,
 | 
				
			||||||
 | 
					            button,
 | 
				
			||||||
 | 
					            ((Func<T, Func<SocketMessageComponent, Task>>)((data)
 | 
				
			||||||
 | 
					                => smc => onTrigger(smc, data)))(state),
 | 
				
			||||||
 | 
					            singleUse);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public NadekoInteraction Create(
 | 
				
			||||||
 | 
					        ulong userId,
 | 
				
			||||||
 | 
					        SelectMenuBuilder menu,
 | 
				
			||||||
 | 
					        Func<SocketMessageComponent, Task> onTrigger,
 | 
				
			||||||
 | 
					        bool singleUse = true)
 | 
				
			||||||
 | 
					        => new NadekoSelectInteraction(_client,
 | 
				
			||||||
            userId,
 | 
					            userId,
 | 
				
			||||||
            inter.Button,
 | 
					            menu,
 | 
				
			||||||
            inter.TriggerAsync,
 | 
					            onTrigger,
 | 
				
			||||||
            onlyAuthor: true);
 | 
					            onlyAuthor: true,
 | 
				
			||||||
 | 
					            singleUse: singleUse);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,32 +0,0 @@
 | 
				
			|||||||
namespace NadekoBot;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public static class InteractionHelpers
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public static readonly IEmote ArrowLeft = Emote.Parse("<:x:1232256519844790302>");
 | 
					 | 
				
			||||||
    public static readonly IEmote ArrowRight = Emote.Parse("<:x:1232256515298295838>");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public abstract class SimpleInteractionBase
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public abstract Task TriggerAsync(SocketMessageComponent smc);
 | 
					 | 
				
			||||||
    public abstract ButtonBuilder Button { get; }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class SimpleInteraction<T> : SimpleInteractionBase
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public override ButtonBuilder Button { get; }
 | 
					 | 
				
			||||||
    private readonly Func<SocketMessageComponent, T, Task> _onClick;
 | 
					 | 
				
			||||||
    private readonly T? _state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public SimpleInteraction(ButtonBuilder button, Func<SocketMessageComponent, T?, Task> onClick, T? state = default)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Button = button;
 | 
					 | 
				
			||||||
        _onClick = onClick;
 | 
					 | 
				
			||||||
        _state = state;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public override async Task TriggerAsync(SocketMessageComponent smc)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        await _onClick(smc, _state!);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -34,34 +34,79 @@ public partial class ResponseBuilder
 | 
				
			|||||||
            if (_paginationBuilder.AddPaginatedFooter)
 | 
					            if (_paginationBuilder.AddPaginatedFooter)
 | 
				
			||||||
                embed.AddPaginatedFooter(currentPage, lastPage);
 | 
					                embed.AddPaginatedFooter(currentPage, lastPage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            SimpleInteractionBase? maybeInter = null;
 | 
					            NadekoInteraction? maybeInter = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            async Task<ComponentBuilder> GetComponentBuilder()
 | 
					            var model = await _builder.BuildAsync(ephemeral);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            async Task<(NadekoButtonInteraction left, NadekoInteraction? extra, NadekoButtonInteraction right)>
 | 
				
			||||||
 | 
					                GetInteractions()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var cb = new ComponentBuilder();
 | 
					                var leftButton = new ButtonBuilder()
 | 
				
			||||||
 | 
					                                 .WithStyle(ButtonStyle.Primary)
 | 
				
			||||||
 | 
					                                 .WithCustomId(BUTTON_LEFT)
 | 
				
			||||||
 | 
					                                 .WithDisabled(lastPage == 0)
 | 
				
			||||||
 | 
					                                 .WithEmote(InteractionHelpers.ArrowLeft)
 | 
				
			||||||
 | 
					                                 .WithDisabled(currentPage <= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                cb.WithButton(new ButtonBuilder()
 | 
					                var leftBtnInter = new NadekoButtonInteraction(_client,
 | 
				
			||||||
                              .WithStyle(ButtonStyle.Primary)
 | 
					                    model.User?.Id ?? 0,
 | 
				
			||||||
                              .WithCustomId(BUTTON_LEFT)
 | 
					                    leftButton,
 | 
				
			||||||
                              .WithDisabled(lastPage == 0)
 | 
					                    (smc) =>
 | 
				
			||||||
                              .WithEmote(InteractionHelpers.ArrowLeft)
 | 
					                    {
 | 
				
			||||||
                              .WithDisabled(currentPage <= 0));
 | 
					                        try
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (currentPage > 0)
 | 
				
			||||||
 | 
					                                currentPage--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            _ = UpdatePageAsync(smc);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        catch (Exception ex)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Log.Error(ex, "Error in pagination: {ErrorMessage}", ex.Message);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    true,
 | 
				
			||||||
 | 
					                    singleUse: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (_paginationBuilder.InteractionFunc is not null)
 | 
					                if (_paginationBuilder.InteractionFunc is not null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    maybeInter = await _paginationBuilder.InteractionFunc(currentPage);
 | 
					                    maybeInter = await _paginationBuilder.InteractionFunc(currentPage);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (maybeInter is not null)
 | 
					 | 
				
			||||||
                        cb.WithButton(maybeInter.Button);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                cb.WithButton(new ButtonBuilder()
 | 
					                var rightButton = new ButtonBuilder()
 | 
				
			||||||
                              .WithStyle(ButtonStyle.Primary)
 | 
					                                  .WithStyle(ButtonStyle.Primary)
 | 
				
			||||||
                              .WithCustomId(BUTTON_RIGHT)
 | 
					                                  .WithCustomId(BUTTON_RIGHT)
 | 
				
			||||||
                              .WithDisabled(lastPage is not null && (lastPage == 0 || currentPage >= lastPage))
 | 
					                                  .WithDisabled(lastPage == 0)
 | 
				
			||||||
                              .WithEmote(InteractionHelpers.ArrowRight));
 | 
					                                  .WithEmote(InteractionHelpers.ArrowRight)
 | 
				
			||||||
 | 
					                                  .WithDisabled(lastPage == 0 || currentPage > lastPage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return cb;
 | 
					                var rightBtnInter = new NadekoButtonInteraction(_client,
 | 
				
			||||||
 | 
					                    model.User?.Id ?? 0,
 | 
				
			||||||
 | 
					                    rightButton,
 | 
				
			||||||
 | 
					                    (smc) =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        try
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (currentPage >= lastPage)
 | 
				
			||||||
 | 
					                                return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            currentPage++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            _ = UpdatePageAsync(smc);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        catch (Exception ex)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Log.Error(ex, "Error in pagination: {ErrorMessage}", ex.Message);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    true,
 | 
				
			||||||
 | 
					                    singleUse: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return (leftBtnInter, maybeInter, rightBtnInter);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            async Task UpdatePageAsync(SocketMessageComponent smc)
 | 
					            async Task UpdatePageAsync(SocketMessageComponent smc)
 | 
				
			||||||
@@ -71,75 +116,37 @@ public partial class ResponseBuilder
 | 
				
			|||||||
                if (_paginationBuilder.AddPaginatedFooter)
 | 
					                if (_paginationBuilder.AddPaginatedFooter)
 | 
				
			||||||
                    toSend.AddPaginatedFooter(currentPage, lastPage);
 | 
					                    toSend.AddPaginatedFooter(currentPage, lastPage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var component = (await GetComponentBuilder()).Build();
 | 
					                var (left, extra, right) = (await GetInteractions());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var cb = new ComponentBuilder();
 | 
				
			||||||
 | 
					                left.AddTo(cb);
 | 
				
			||||||
 | 
					                right.AddTo(cb);
 | 
				
			||||||
 | 
					                extra?.AddTo(cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await smc.ModifyOriginalResponseAsync(x =>
 | 
					                await smc.ModifyOriginalResponseAsync(x =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    x.Embed = toSend.Build();
 | 
					                    x.Embed = toSend.Build();
 | 
				
			||||||
                    x.Components = component;
 | 
					                    x.Components = cb.Build();
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var model = await _builder.BuildAsync(ephemeral);
 | 
					            var (left, extra, right) = await GetInteractions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var cb = new ComponentBuilder();
 | 
				
			||||||
 | 
					            left.AddTo(cb);
 | 
				
			||||||
 | 
					            right.AddTo(cb);
 | 
				
			||||||
 | 
					            extra?.AddTo(cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var component = (await GetComponentBuilder()).Build();
 | 
					 | 
				
			||||||
            var msg = await model.TargetChannel
 | 
					            var msg = await model.TargetChannel
 | 
				
			||||||
                                 .SendMessageAsync(model.Text,
 | 
					                                 .SendMessageAsync(model.Text,
 | 
				
			||||||
                                     embed: embed.Build(),
 | 
					                                     embed: embed.Build(),
 | 
				
			||||||
                                     components: component,
 | 
					                                     components: cb.Build(),
 | 
				
			||||||
                                     messageReference: model.MessageReference);
 | 
					                                     messageReference: model.MessageReference);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            async Task OnInteractionAsync(SocketInteraction si)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (si is not SocketMessageComponent smc)
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (smc.Message.Id != msg.Id)
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    await si.DeferAsync();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (smc.User.Id != model.User?.Id)
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (smc.Data.CustomId == BUTTON_LEFT)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (currentPage == 0)
 | 
					 | 
				
			||||||
                            return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        --currentPage;
 | 
					 | 
				
			||||||
                        _ = UpdatePageAsync(smc);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (smc.Data.CustomId == BUTTON_RIGHT)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (currentPage >= lastPage)
 | 
					 | 
				
			||||||
                            return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        ++currentPage;
 | 
					 | 
				
			||||||
                        _ = UpdatePageAsync(smc);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (maybeInter is { } inter && inter.Button.CustomId == smc.Data.CustomId)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        await inter.TriggerAsync(smc);
 | 
					 | 
				
			||||||
                        _ = UpdatePageAsync(smc);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch (Exception ex)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Log.Error(ex, "Error in pagination: {ErrorMessage}", ex.Message);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (lastPage == 0 && _paginationBuilder.InteractionFunc is null)
 | 
					            if (lastPage == 0 && _paginationBuilder.InteractionFunc is null)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _client.InteractionCreated += OnInteractionAsync;
 | 
					            await Task.WhenAll(left.RunAsync(msg), extra?.RunAsync(msg) ?? Task.CompletedTask, right.RunAsync(msg));
 | 
				
			||||||
 | 
					 | 
				
			||||||
            await Task.Delay(30_000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _client.InteractionCreated -= OnInteractionAsync;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build());
 | 
					            await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -395,7 +395,7 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
 | 
				
			|||||||
        return Task.FromResult<IReadOnlyCollection<T>>(ReadOnlyCollection<T>.Empty);
 | 
					        return Task.FromResult<IReadOnlyCollection<T>>(ReadOnlyCollection<T>.Empty);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Func<int, Task<SimpleInteractionBase>>? InteractionFunc { get; private set; }
 | 
					    public Func<int, Task<NadekoInteraction>>? InteractionFunc { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public int? Elems { get; private set; } = 1;
 | 
					    public int? Elems { get; private set; } = 1;
 | 
				
			||||||
    public int ItemsPerPage { get; private set; } = 9;
 | 
					    public int ItemsPerPage { get; private set; } = 9;
 | 
				
			||||||
@@ -478,13 +478,13 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
 | 
				
			|||||||
        return paginationSender.SendAsync(IsEphemeral);
 | 
					        return paginationSender.SendAsync(IsEphemeral);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SourcedPaginatedResponseBuilder<T> Interaction(Func<int, Task<SimpleInteractionBase>> func)
 | 
					    public SourcedPaginatedResponseBuilder<T> Interaction(Func<int, Task<NadekoInteraction>> func)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        InteractionFunc = func; //async (i) => await func(i);
 | 
					        InteractionFunc = func; //async (i) => await func(i);
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SourcedPaginatedResponseBuilder<T> Interaction(SimpleInteractionBase inter)
 | 
					    public SourcedPaginatedResponseBuilder<T> Interaction(NadekoInteraction inter)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        InteractionFunc = _ => Task.FromResult(inter);
 | 
					        InteractionFunc = _ => Task.FromResult(inter);
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,19 +40,29 @@ public sealed class CommandsUtilityService : ICommandsUtilityService, INService
 | 
				
			|||||||
        var culture = _loc.GetCultureInfo(guild);
 | 
					        var culture = _loc.GetCultureInfo(guild);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var em = _sender.CreateEmbed()
 | 
					        var em = _sender.CreateEmbed()
 | 
				
			||||||
                    .AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
 | 
					                        .AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides);
 | 
					        _dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides);
 | 
				
			||||||
        var reqs = GetCommandRequirements(com, (GuildPermission?)overrides);
 | 
					        var reqs = GetCommandRequirements(com, (GuildPermission?)overrides);
 | 
				
			||||||
        if (reqs.Any())
 | 
					        if (reqs.Any())
 | 
				
			||||||
            em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs));
 | 
					            em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var paramList = _strings.GetCommandStrings(com.Name, culture)?.Params;
 | 
				
			||||||
        em
 | 
					        em
 | 
				
			||||||
            .WithOkColor()
 | 
					            .WithOkColor()
 | 
				
			||||||
            .AddField(_strings.GetText(strs.usage),
 | 
					            .AddField(_strings.GetText(strs.usage),
 | 
				
			||||||
                string.Join("\n", com.RealRemarksArr(_strings, _medusae, culture, prefix).Map(arg => Format.Code(arg))))
 | 
					                string.Join("\n", com.RealRemarksArr(_strings, _medusae, culture, prefix).Map(arg => Format.Code(arg))))
 | 
				
			||||||
            .WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild));
 | 
					            .WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (paramList is not null and not [])
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var pl = paramList
 | 
				
			||||||
 | 
					                     .Select(x => Format.Code($"{prefix}{com.Name} {x.Keys.Select(y => $"<{y}>").Join(' ')}"))
 | 
				
			||||||
 | 
					                     .Join('\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            em.AddField(GetText(strs.overloads, guild), pl);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var opt = GetNadekoOptionType(com.Attributes);
 | 
					        var opt = GetNadekoOptionType(com.Attributes);
 | 
				
			||||||
        if (opt is not null)
 | 
					        if (opt is not null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
h:
 | 
					h:
 | 
				
			||||||
  - help
 | 
					 | 
				
			||||||
  - h
 | 
					  - h
 | 
				
			||||||
 | 
					  - help
 | 
				
			||||||
gencmdlist:
 | 
					gencmdlist:
 | 
				
			||||||
  - gencmdlist
 | 
					  - gencmdlist
 | 
				
			||||||
donate:
 | 
					donate:
 | 
				
			||||||
@@ -37,7 +37,7 @@ boost:
 | 
				
			|||||||
boostmsg:
 | 
					boostmsg:
 | 
				
			||||||
  - boostmsg
 | 
					  - boostmsg
 | 
				
			||||||
boostdel:
 | 
					boostdel:
 | 
				
			||||||
  - boostde
 | 
					  - boostdel
 | 
				
			||||||
logserver:
 | 
					logserver:
 | 
				
			||||||
  - logserver
 | 
					  - logserver
 | 
				
			||||||
logignore:
 | 
					logignore:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,23 +211,23 @@ repeat:
 | 
				
			|||||||
        desc: "The amount of time between each repetition."
 | 
					        desc: "The amount of time between each repetition."
 | 
				
			||||||
      message:
 | 
					      message:
 | 
				
			||||||
        desc: "The text to be repeated at the specified intervals or times."
 | 
					        desc: "The text to be repeated at the specified intervals or times."
 | 
				
			||||||
    - ch:
 | 
					    - channel:
 | 
				
			||||||
        desc: "The channel where the message will be sent."
 | 
					        desc: "The channel where the message will be sent."
 | 
				
			||||||
      interval:
 | 
					      interval:
 | 
				
			||||||
        desc: "The amount of time between each repetition."
 | 
					        desc: "The amount of time between each repetition."
 | 
				
			||||||
      message:
 | 
					      message:
 | 
				
			||||||
        desc: "The text to be repeated at the specified intervals or times."
 | 
					        desc: "The text to be repeated at the specified intervals or times."
 | 
				
			||||||
    - dt:
 | 
					    - timeOfDay:
 | 
				
			||||||
        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
					        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
				
			||||||
      message:
 | 
					      message:
 | 
				
			||||||
        desc: "The text to be repeated at the specified intervals or times."
 | 
					        desc: "The text to be repeated at the specified intervals or times."
 | 
				
			||||||
    - channel:
 | 
					    - channel:
 | 
				
			||||||
        desc: "The channel where the message will be repeated."
 | 
					        desc: "The channel where the message will be repeated."
 | 
				
			||||||
      dt:
 | 
					      timeOfDay:
 | 
				
			||||||
        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
					        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
				
			||||||
      message:
 | 
					      message:
 | 
				
			||||||
        desc: "The text to be repeated at the specified intervals or times."
 | 
					        desc: "The text to be repeated at the specified intervals or times."
 | 
				
			||||||
    - dt:
 | 
					    - timeOfDay:
 | 
				
			||||||
        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
					        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
				
			||||||
      interval:
 | 
					      interval:
 | 
				
			||||||
        desc: "The amount of time between each repetition."
 | 
					        desc: "The amount of time between each repetition."
 | 
				
			||||||
@@ -235,7 +235,7 @@ repeat:
 | 
				
			|||||||
        desc: "The text to be repeated at the specified intervals or times."
 | 
					        desc: "The text to be repeated at the specified intervals or times."
 | 
				
			||||||
    - channel:
 | 
					    - channel:
 | 
				
			||||||
        desc: "The channel where the message will be repeated."
 | 
					        desc: "The channel where the message will be repeated."
 | 
				
			||||||
      dt:
 | 
					      timeOfDay:
 | 
				
			||||||
        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
					        desc: "The time at which the message should be repeated, either once every specified amount of time or at a specific time of day."
 | 
				
			||||||
      interval:
 | 
					      interval:
 | 
				
			||||||
        desc: "The amount of time between each repetition."
 | 
					        desc: "The amount of time between each repetition."
 | 
				
			||||||
@@ -370,10 +370,10 @@ expradd:
 | 
				
			|||||||
  ex:
 | 
					  ex:
 | 
				
			||||||
    - '"hello" Hi there %user.mention%'
 | 
					    - '"hello" Hi there %user.mention%'
 | 
				
			||||||
  params:
 | 
					  params:
 | 
				
			||||||
    - key:
 | 
					    - trigger:
 | 
				
			||||||
        desc: "The trigger word that sets off the response when typed by a user."
 | 
					        desc: "The trigger word that sets off the response when typed by a user."
 | 
				
			||||||
      message:
 | 
					      response:
 | 
				
			||||||
        desc: "The text of the message that triggers the response when typed by a user."
 | 
					        desc: "The text of the message that shows up when a user types the trigger word."
 | 
				
			||||||
expraddserver:
 | 
					expraddserver:
 | 
				
			||||||
  desc: 'Add an expression with a trigger and a response in this server. Bot will post a response whenever someone types the trigger word. Guide here: <https://nadekobot.readthedocs.io/en/latest/custom-reactions/>'
 | 
					  desc: 'Add an expression with a trigger and a response in this server. Bot will post a response whenever someone types the trigger word. Guide here: <https://nadekobot.readthedocs.io/en/latest/custom-reactions/>'
 | 
				
			||||||
  ex:
 | 
					  ex:
 | 
				
			||||||
@@ -1257,7 +1257,7 @@ quoteshow:
 | 
				
			|||||||
  ex:
 | 
					  ex:
 | 
				
			||||||
    - 123
 | 
					    - 123
 | 
				
			||||||
  params:
 | 
					  params:
 | 
				
			||||||
    - id:
 | 
					    - quoteId:
 | 
				
			||||||
        desc: "The unique identifier for the quote being queried."
 | 
					        desc: "The unique identifier for the quote being queried."
 | 
				
			||||||
quotesearch:
 | 
					quotesearch:
 | 
				
			||||||
  desc: 'Shows a random quote given a search query. Partially matches in several ways: 1) Only content of any quote, 2) only by author, 3) keyword and content, 3) or keyword and author'
 | 
					  desc: 'Shows a random quote given a search query. Partially matches in several ways: 1) Only content of any quote, 2) only by author, 3) keyword and content, 3) or keyword and author'
 | 
				
			||||||
@@ -1278,14 +1278,14 @@ quoteid:
 | 
				
			|||||||
  ex:
 | 
					  ex:
 | 
				
			||||||
    - 123456
 | 
					    - 123456
 | 
				
			||||||
  params:
 | 
					  params:
 | 
				
			||||||
    - id:
 | 
					    - quoteId:
 | 
				
			||||||
        desc: "The unique identifier for the quote to be displayed."
 | 
					        desc: "The unique identifier for the quote to be displayed."
 | 
				
			||||||
quotedelete:
 | 
					quotedelete:
 | 
				
			||||||
  desc: Deletes a quote with the specified ID. You have to either have the Manage Messages permission or be the creator of the quote to delete it.
 | 
					  desc: Deletes a quote with the specified ID. You have to either have the Manage Messages permission or be the creator of the quote to delete it.
 | 
				
			||||||
  ex:
 | 
					  ex:
 | 
				
			||||||
    - 123456
 | 
					    - 123456
 | 
				
			||||||
  params:
 | 
					  params:
 | 
				
			||||||
    - id:
 | 
					    - quoteId:
 | 
				
			||||||
        desc: "The unique identifier for the quote being deleted."
 | 
					        desc: "The unique identifier for the quote being deleted."
 | 
				
			||||||
quotedeleteauthor:
 | 
					quotedeleteauthor:
 | 
				
			||||||
  desc: Deletes all quotes by the specified author. If the author is not you, then ManageMessage server permission is required.
 | 
					  desc: Deletes all quotes by the specified author. If the author is not you, then ManageMessage server permission is required.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1099,5 +1099,6 @@
 | 
				
			|||||||
  "todo_archive_not_found": "Archived todo list not found.",
 | 
					  "todo_archive_not_found": "Archived todo list not found.",
 | 
				
			||||||
  "todo_archived_list": "Archived Todo List",
 | 
					  "todo_archived_list": "Archived Todo List",
 | 
				
			||||||
  "search_results": "Search results",
 | 
					  "search_results": "Search results",
 | 
				
			||||||
  "queue_search_results": "Type the number of the search result to queue up that track."
 | 
					  "queue_search_results": "Type the number of the search result to queue up that track.",
 | 
				
			||||||
 | 
					  "overloads": "Overloads"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user