mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	add: Added .quteedit command
add: Added an edit button to .quoteshow and .exprshow commands that opens a modal which lets you edit the quote or expr in question
This commit is contained in:
		@@ -1,5 +1,7 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
 | 
			
		||||
[Name("Expressions")]
 | 
			
		||||
@@ -34,12 +36,12 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
 | 
			
		||||
        await Response()
 | 
			
		||||
              .Embed(_sender.CreateEmbed()
 | 
			
		||||
                     .WithOkColor()
 | 
			
		||||
                     .WithTitle(GetText(strs.expr_new))
 | 
			
		||||
                     .WithDescription($"#{new kwum(ex.Id)}")
 | 
			
		||||
                     .AddField(GetText(strs.trigger), key)
 | 
			
		||||
                     .AddField(GetText(strs.response),
 | 
			
		||||
                         message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle(GetText(strs.expr_new))
 | 
			
		||||
                            .WithDescription($"#{new kwum(ex.Id)}")
 | 
			
		||||
                            .AddField(GetText(strs.trigger), key)
 | 
			
		||||
                            .AddField(GetText(strs.response),
 | 
			
		||||
                                message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
 | 
			
		||||
              .SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -93,8 +95,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((channel is null && !_creds.IsOwner(ctx.User))
 | 
			
		||||
            || (channel is not null && !((IGuildUser)ctx.User).GuildPermissions.Administrator))
 | 
			
		||||
        if (!IsValidExprEditor())
 | 
			
		||||
        {
 | 
			
		||||
            await Response().Error(strs.expr_insuff_perms).SendAsync();
 | 
			
		||||
            return;
 | 
			
		||||
@@ -105,12 +106,12 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
        {
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Embed(_sender.CreateEmbed()
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle(GetText(strs.expr_edited))
 | 
			
		||||
                         .WithDescription($"#{id}")
 | 
			
		||||
                         .AddField(GetText(strs.trigger), ex.Trigger)
 | 
			
		||||
                         .AddField(GetText(strs.response),
 | 
			
		||||
                             message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
 | 
			
		||||
                                .WithOkColor()
 | 
			
		||||
                                .WithTitle(GetText(strs.expr_edited))
 | 
			
		||||
                                .WithDescription($"#{id}")
 | 
			
		||||
                                .AddField(GetText(strs.trigger), ex.Trigger)
 | 
			
		||||
                                .AddField(GetText(strs.response),
 | 
			
		||||
                                    message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
@@ -119,6 +120,10 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private bool IsValidExprEditor()
 | 
			
		||||
        => (ctx.Guild is not null && ((IGuildUser)ctx.User).GuildPermissions.Administrator)
 | 
			
		||||
           || (ctx.Guild is null && _creds.IsOwner(ctx.User));
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public async Task ExprList(int page = 1)
 | 
			
		||||
@@ -132,7 +137,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
                                     .OrderBy(x => x.Trigger)
 | 
			
		||||
                                     .ToArray();
 | 
			
		||||
 | 
			
		||||
        if (allExpressions is null || !allExpressions.Any())
 | 
			
		||||
        if (!allExpressions.Any())
 | 
			
		||||
        {
 | 
			
		||||
            await Response().Error(strs.expr_no_found).SendAsync();
 | 
			
		||||
            return;
 | 
			
		||||
@@ -171,16 +176,48 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var inter = CreateEditInteraction(id, found);
 | 
			
		||||
 | 
			
		||||
        await Response()
 | 
			
		||||
              .Interaction(IsValidExprEditor() ? inter : null)
 | 
			
		||||
              .Embed(_sender.CreateEmbed()
 | 
			
		||||
                     .WithOkColor()
 | 
			
		||||
                     .WithDescription($"#{id}")
 | 
			
		||||
                     .AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
 | 
			
		||||
                     .AddField(GetText(strs.response),
 | 
			
		||||
                         found.Response.TrimTo(1000).Replace("](", "]\\(")))
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithDescription($"#{id}")
 | 
			
		||||
                            .AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
 | 
			
		||||
                            .AddField(GetText(strs.response),
 | 
			
		||||
                                found.Response.TrimTo(1000).Replace("](", "]\\(")))
 | 
			
		||||
              .SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoInteractionBase CreateEditInteraction(kwum id, NadekoExpression found)
 | 
			
		||||
    {
 | 
			
		||||
        var modal = new ModalBuilder()
 | 
			
		||||
                    .WithCustomId("expr:edit_modal")
 | 
			
		||||
                    .WithTitle($"Edit expression {id}")
 | 
			
		||||
                    .AddTextInput(new TextInputBuilder()
 | 
			
		||||
                                  .WithLabel(GetText(strs.response))
 | 
			
		||||
                                  .WithValue(found.Response)
 | 
			
		||||
                                  .WithMinLength(1)
 | 
			
		||||
                                  .WithCustomId("expr:edit_modal:response")
 | 
			
		||||
                                  .WithStyle(TextInputStyle.Paragraph));
 | 
			
		||||
 | 
			
		||||
        var inter = _inter.Create(ctx.User.Id,
 | 
			
		||||
            new ButtonBuilder()
 | 
			
		||||
                .WithEmote(Emoji.Parse("📝"))
 | 
			
		||||
                .WithLabel("Edit")
 | 
			
		||||
                .WithStyle(ButtonStyle.Primary)
 | 
			
		||||
                .WithCustomId("test"),
 | 
			
		||||
            modal,
 | 
			
		||||
            async (sm) =>
 | 
			
		||||
            {
 | 
			
		||||
                var msg = sm.Data.Components.FirstOrDefault()?.Value;
 | 
			
		||||
 | 
			
		||||
                await ExprEdit(id, msg);
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        return inter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task ExprDeleteInternalAsync(kwum id)
 | 
			
		||||
    {
 | 
			
		||||
        var ex = await _service.DeleteAsync(ctx.Guild?.Id, id);
 | 
			
		||||
@@ -189,11 +226,11 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
        {
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Embed(_sender.CreateEmbed()
 | 
			
		||||
                         .WithOkColor()
 | 
			
		||||
                         .WithTitle(GetText(strs.expr_deleted))
 | 
			
		||||
                         .WithDescription($"#{id}")
 | 
			
		||||
                         .AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
 | 
			
		||||
                         .AddField(GetText(strs.response), ex.Response.TrimTo(1024)))
 | 
			
		||||
                                .WithOkColor()
 | 
			
		||||
                                .WithTitle(GetText(strs.expr_deleted))
 | 
			
		||||
                                .WithDescription($"#{id}")
 | 
			
		||||
                                .AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
 | 
			
		||||
                                .AddField(GetText(strs.response), ex.Response.TrimTo(1024)))
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
@@ -340,8 +377,8 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
    public async Task ExprClear()
 | 
			
		||||
    {
 | 
			
		||||
        if (await PromptUserConfirmAsync(_sender.CreateEmbed()
 | 
			
		||||
                                         .WithTitle("Expression clear")
 | 
			
		||||
                                         .WithDescription("This will delete all expressions on this server.")))
 | 
			
		||||
                                                .WithTitle("Expression clear")
 | 
			
		||||
                                                .WithDescription("This will delete all expressions on this server.")))
 | 
			
		||||
        {
 | 
			
		||||
            var count = _service.DeleteAllExpressions(ctx.Guild.Id);
 | 
			
		||||
            await Response().Confirm(strs.exprs_cleared(count)).SendAsync();
 | 
			
		||||
 
 | 
			
		||||
@@ -154,7 +154,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        await smc.RespondConfirmAsync(_sender, GetText(strs.remind_timely(tt)), ephemeral: true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoInteraction CreateRemindMeInteraction(int period)
 | 
			
		||||
    private NadekoInteractionBase CreateRemindMeInteraction(int period)
 | 
			
		||||
        => _inter
 | 
			
		||||
            .Create(ctx.User.Id,
 | 
			
		||||
                new ButtonBuilder(
 | 
			
		||||
@@ -415,7 +415,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
              .Pipe(text => smc.RespondConfirmAsync(_sender, text, ephemeral: true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoInteraction CreateCashInteraction()
 | 
			
		||||
    private NadekoInteractionBase CreateCashInteraction()
 | 
			
		||||
        => _inter.Create(ctx.User.Id,
 | 
			
		||||
            new ButtonBuilder(
 | 
			
		||||
                customId: "cash:bank_show_balance",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
#nullable disable warnings
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common.Yml;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
@@ -133,23 +135,54 @@ public partial class Utility
 | 
			
		||||
            await ShowQuoteData(quote);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task ShowQuoteData(Quote data)
 | 
			
		||||
        private NadekoInteractionBase CreateEditInteraction(kwum id, Quote found)
 | 
			
		||||
        {
 | 
			
		||||
            var modal = new ModalBuilder()
 | 
			
		||||
                        .WithCustomId("quote:edit_modal")
 | 
			
		||||
                        .WithTitle($"Edit expression {id}")
 | 
			
		||||
                        .AddTextInput(new TextInputBuilder()
 | 
			
		||||
                                      .WithLabel(GetText(strs.response))
 | 
			
		||||
                                      .WithValue(found.Text)
 | 
			
		||||
                                      .WithMinLength(1)
 | 
			
		||||
                                      .WithCustomId("quote:edit_modal:response")
 | 
			
		||||
                                      .WithStyle(TextInputStyle.Paragraph));
 | 
			
		||||
 | 
			
		||||
            var inter = _inter.Create(ctx.User.Id,
 | 
			
		||||
                new ButtonBuilder()
 | 
			
		||||
                    .WithEmote(Emoji.Parse("📝"))
 | 
			
		||||
                    .WithLabel("Edit")
 | 
			
		||||
                    .WithStyle(ButtonStyle.Primary)
 | 
			
		||||
                    .WithCustomId("test"),
 | 
			
		||||
                modal,
 | 
			
		||||
                async (sm) =>
 | 
			
		||||
                {
 | 
			
		||||
                    var msg = sm.Data.Components.FirstOrDefault()?.Value;
 | 
			
		||||
                    
 | 
			
		||||
                    if(!string.IsNullOrWhiteSpace(msg)) 
 | 
			
		||||
                        await QuoteEdit(id, msg);
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
            return inter;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task ShowQuoteData(Quote quote)
 | 
			
		||||
        {
 | 
			
		||||
            var inter = CreateEditInteraction(quote.Id, quote);
 | 
			
		||||
            var eb = _sender.CreateEmbed()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle($"{GetText(strs.quote_id($"`{new kwum(data.Id)}"))}`")
 | 
			
		||||
                            .WithDescription(Format.Sanitize(data.Text).Replace("](", "]\\(").TrimTo(4096))
 | 
			
		||||
                            .AddField(GetText(strs.trigger), data.Keyword)
 | 
			
		||||
                            .WithTitle($"{GetText(strs.quote_id($"`{new kwum(quote.Id)}"))}`")
 | 
			
		||||
                            .WithDescription(Format.Sanitize(quote.Text).Replace("](", "]\\(").TrimTo(4096))
 | 
			
		||||
                            .AddField(GetText(strs.trigger), quote.Keyword)
 | 
			
		||||
                            .WithFooter(
 | 
			
		||||
                                GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})")));
 | 
			
		||||
                                GetText(strs.created_by($"{quote.AuthorName} ({quote.AuthorId})")));
 | 
			
		||||
 | 
			
		||||
            if (!(data.Text.Length > 4096))
 | 
			
		||||
            if (!(quote.Text.Length > 4096))
 | 
			
		||||
            {
 | 
			
		||||
                await Response().Embed(eb).SendAsync();
 | 
			
		||||
                await Response().Embed(eb).Interaction(quote.AuthorId == ctx.User.Id ? inter : null).SendAsync();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await using var textStream = await data.Text.ToStream();
 | 
			
		||||
            await using var textStream = await quote.Text.ToStream();
 | 
			
		||||
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Embed(eb)
 | 
			
		||||
@@ -255,6 +288,47 @@ public partial class Utility
 | 
			
		||||
            await Response().Confirm(strs.quote_added_new(Format.Code(new kwum(q.Id).ToString()))).SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [RequireContext(ContextType.Guild)]
 | 
			
		||||
        public async Task QuoteEdit(kwum quoteId, [Leftover] string text)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(text))
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Quote q;
 | 
			
		||||
            await using (var uow = _db.GetDbContext())
 | 
			
		||||
            {
 | 
			
		||||
                var intId = (int)quoteId;
 | 
			
		||||
                var result = await uow.GetTable<Quote>()
 | 
			
		||||
                                      .Where(x => x.Id == intId && x.AuthorId == ctx.User.Id)
 | 
			
		||||
                                      .Set(x => x.Text, text)
 | 
			
		||||
                                      .UpdateWithOutputAsync((del, ins) => ins);
 | 
			
		||||
 | 
			
		||||
                q = result.FirstOrDefault();
 | 
			
		||||
                
 | 
			
		||||
                await uow.SaveChangesAsync();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (q is not null)
 | 
			
		||||
            {
 | 
			
		||||
                await Response()
 | 
			
		||||
                      .Embed(_sender.CreateEmbed()
 | 
			
		||||
                                    .WithOkColor()
 | 
			
		||||
                                    .WithTitle(GetText(strs.quote_edited))
 | 
			
		||||
                                    .WithDescription($"#{quoteId}")
 | 
			
		||||
                                    .AddField(GetText(strs.trigger), q.Keyword)
 | 
			
		||||
                                    .AddField(GetText(strs.response),
 | 
			
		||||
                                        text.Length > 1024 ? GetText(strs.redacted_too_long) : text))
 | 
			
		||||
                      .SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await Response().Error(strs.expr_no_found_id).SendAsync();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [RequireContext(ContextType.Guild)]
 | 
			
		||||
        public async Task QuoteDelete(kwum quoteId)
 | 
			
		||||
 
 | 
			
		||||
@@ -506,7 +506,7 @@ public partial class Xp : NadekoModule<XpService>
 | 
			
		||||
    {
 | 
			
		||||
        var result = await _service.BuyShopItemAsync(ctx.User.Id, (XpShopItemType)type, key);
 | 
			
		||||
 | 
			
		||||
        NadekoInteraction GetUseInteraction()
 | 
			
		||||
        NadekoInteractionBase GetUseInteraction()
 | 
			
		||||
        {
 | 
			
		||||
            return _inter.Create(ctx.User.Id,
 | 
			
		||||
                new(label: "Use", customId: "xpshop:use_item", emote: Emoji.Parse("👐")),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
    <PropertyGroup>
 | 
			
		||||
        <TargetFramework>net8.0</TargetFramework>
 | 
			
		||||
        <Nullable>enable</Nullable>
 | 
			
		||||
@@ -31,9 +30,9 @@
 | 
			
		||||
        <PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/>
 | 
			
		||||
        <PackageReference Include="CommandLineParser" Version="2.9.1"/>
 | 
			
		||||
        <PackageReference Include="Discord.Net" Version="3.204.0"/>
 | 
			
		||||
        <PackageReference Include="CoreCLR-NCalc" Version="3.1.246" />
 | 
			
		||||
        <PackageReference Include="CoreCLR-NCalc" Version="3.1.246"/>
 | 
			
		||||
        <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
 | 
			
		||||
        <PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414" />
 | 
			
		||||
        <PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414"/>
 | 
			
		||||
        <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084"/>
 | 
			
		||||
        <!--        <PackageReference Include="Grpc.AspNetCore" Version="2.62.0" />-->
 | 
			
		||||
        <PackageReference Include="Google.Protobuf" Version="3.26.1"/>
 | 
			
		||||
@@ -52,11 +51,11 @@
 | 
			
		||||
        <PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0"/>
 | 
			
		||||
 | 
			
		||||
        <!-- DI -->
 | 
			
		||||
<!--        <PackageReference Include="Ninject" Version="3.3.6"/>-->
 | 
			
		||||
<!--        <PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0"/>-->
 | 
			
		||||
        <!--        <PackageReference Include="Ninject" Version="3.3.6"/>-->
 | 
			
		||||
        <!--        <PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0"/>-->
 | 
			
		||||
        <!--        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />-->
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1"/>
 | 
			
		||||
        <PackageReference Include="DryIoc.dll" Version="5.4.3" />
 | 
			
		||||
        <PackageReference Include="DryIoc.dll" Version="5.4.3"/>
 | 
			
		||||
        <!--        <PackageReference Include="Scrutor" Version="4.2.0" />-->
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0"/>
 | 
			
		||||
@@ -68,14 +67,14 @@
 | 
			
		||||
        <PackageReference Include="OneOf" Version="3.0.263"/>
 | 
			
		||||
        <PackageReference Include="OneOf.SourceGenerator" Version="3.0.263"/>
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1"/>
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Seq" Version="7.0.1" />
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Seq" Version="7.0.1"/>
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp" Version="2.1.8"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009"/>
 | 
			
		||||
        <PackageReference Include="StackExchange.Redis" Version="2.7.33"/>
 | 
			
		||||
        <PackageReference Include="YamlDotNet" Version="15.1.4" />
 | 
			
		||||
        <PackageReference Include="YamlDotNet" Version="15.1.4"/>
 | 
			
		||||
        <PackageReference Include="SharpToken" Version="2.0.2"/>
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="JetBrains.Annotations" Version="2023.3.0"/>
 | 
			
		||||
@@ -100,7 +99,7 @@
 | 
			
		||||
        <PackageReference Include="TwitchLib.Api" Version="3.4.1"/>
 | 
			
		||||
 | 
			
		||||
        <!-- sqlselectcsv and stock -->
 | 
			
		||||
        <PackageReference Include="CsvHelper" Version="32.0.3" />
 | 
			
		||||
        <PackageReference Include="CsvHelper" Version="32.0.3"/>
 | 
			
		||||
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
 | 
			
		||||
@@ -109,6 +108,8 @@
 | 
			
		||||
        <ProjectReference Include="..\NadekoBot.Voice\NadekoBot.Voice.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\NadekoBot.Generators\NadekoBot.Generators.csproj" OutputItemType="Analyzer"/>
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <AdditionalFiles Include="data\strings\responses\responses.en-US.json"/>
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
@@ -131,10 +132,6 @@
 | 
			
		||||
        </None>
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
      <Folder Include="Modules\Utility\GuildColors\" />
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
 | 
			
		||||
    <PropertyGroup Condition=" '$(Configuration)' == 'GlobalNadeko' ">
 | 
			
		||||
        <!-- Define trace doesn't seem to affect the build at all so I had to remove $(DefineConstants)-->
 | 
			
		||||
        <DefineTrace>false</DefineTrace>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,22 +2,30 @@
 | 
			
		||||
 | 
			
		||||
public interface INadekoInteractionService
 | 
			
		||||
{
 | 
			
		||||
    public NadekoInteraction Create(
 | 
			
		||||
    public NadekoInteractionBase Create(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        Func<SocketMessageComponent, Task> onTrigger,
 | 
			
		||||
        bool singleUse = true);
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction Create<T>(
 | 
			
		||||
    public NadekoInteractionBase Create<T>(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        Func<SocketMessageComponent, T, Task> onTrigger,
 | 
			
		||||
        in T state,
 | 
			
		||||
        bool singleUse = true);
 | 
			
		||||
 | 
			
		||||
    NadekoInteraction Create(
 | 
			
		||||
    NadekoInteractionBase Create(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        SelectMenuBuilder menu,
 | 
			
		||||
        Func<SocketMessageComponent, Task> onTrigger,
 | 
			
		||||
        bool singleUse = true);
 | 
			
		||||
    
 | 
			
		||||
    NadekoInteractionBase Create(
 | 
			
		||||
        ulong userId, 
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        ModalBuilder modal,
 | 
			
		||||
        Func<SocketModal, Task> onTrigger,
 | 
			
		||||
        bool singleUse = true);
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed class NadekoButtonInteraction : NadekoInteraction
 | 
			
		||||
public sealed class NadekoButtonInteractionHandler : NadekoInteractionBase
 | 
			
		||||
{
 | 
			
		||||
    public NadekoButtonInteraction(
 | 
			
		||||
    public NadekoButtonInteractionHandler(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,12 @@
 | 
			
		||||
public static class NadekoInteractionExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static MessageComponent CreateComponent(
 | 
			
		||||
        this NadekoInteraction nadekoInteraction
 | 
			
		||||
        this NadekoInteractionBase nadekoInteractionBase
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        var cb = new ComponentBuilder();
 | 
			
		||||
 | 
			
		||||
        nadekoInteraction.AddTo(cb);
 | 
			
		||||
        nadekoInteractionBase.AddTo(cb);
 | 
			
		||||
 | 
			
		||||
        return cb.Build();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed class NadekoSelectInteraction : NadekoInteraction
 | 
			
		||||
public sealed class NadekoButtonSelectInteractionHandler : NadekoInteractionBase
 | 
			
		||||
{
 | 
			
		||||
    public NadekoSelectInteraction(
 | 
			
		||||
    public NadekoButtonSelectInteractionHandler(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        SelectMenuBuilder menu,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public abstract class NadekoInteraction
 | 
			
		||||
public abstract class NadekoInteractionBase
 | 
			
		||||
{
 | 
			
		||||
    private readonly ulong _authorId;
 | 
			
		||||
    private readonly Func<SocketMessageComponent, Task> _onAction;
 | 
			
		||||
@@ -13,7 +13,7 @@ public abstract class NadekoInteraction
 | 
			
		||||
    private readonly string _customId;
 | 
			
		||||
    private readonly bool _singleUse;
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction(
 | 
			
		||||
    public NadekoInteractionBase(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        string customId,
 | 
			
		||||
@@ -85,4 +85,80 @@ public abstract class NadekoInteraction
 | 
			
		||||
 | 
			
		||||
    public Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
			
		||||
        => _onAction(smc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public sealed class NadekoModalSubmitHandler
 | 
			
		||||
{
 | 
			
		||||
    private readonly ulong _authorId;
 | 
			
		||||
    private readonly Func<SocketModal, Task> _onAction;
 | 
			
		||||
    private readonly bool _onlyAuthor;
 | 
			
		||||
    public DiscordSocketClient Client { get; }
 | 
			
		||||
 | 
			
		||||
    private readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
			
		||||
 | 
			
		||||
    private IUserMessage message = null!;
 | 
			
		||||
    private readonly string _customId;
 | 
			
		||||
 | 
			
		||||
    public NadekoModalSubmitHandler(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        string customId,
 | 
			
		||||
        Func<SocketModal, Task> onAction,
 | 
			
		||||
        bool onlyAuthor)
 | 
			
		||||
    {
 | 
			
		||||
        _authorId = authorId;
 | 
			
		||||
        _customId = customId;
 | 
			
		||||
        _onAction = onAction;
 | 
			
		||||
        _onlyAuthor = onlyAuthor;
 | 
			
		||||
        _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
			
		||||
 | 
			
		||||
        Client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task RunAsync(IUserMessage msg)
 | 
			
		||||
    {
 | 
			
		||||
        message = msg;
 | 
			
		||||
 | 
			
		||||
        Client.ModalSubmitted += OnInteraction;
 | 
			
		||||
        await Task.WhenAny(Task.Delay(300_000), _interactionCompletedSource.Task);
 | 
			
		||||
        Client.ModalSubmitted -= OnInteraction;
 | 
			
		||||
 | 
			
		||||
        await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task OnInteraction(SocketModal sm)
 | 
			
		||||
    {
 | 
			
		||||
        if (sm.Message.Id != message.Id)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        if (_onlyAuthor && sm.User.Id != _authorId)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        if (sm.Data.CustomId != _customId)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                _interactionCompletedSource.TrySetResult(true);
 | 
			
		||||
                await ExecuteOnActionAsync(sm);
 | 
			
		||||
 | 
			
		||||
                if (!sm.HasResponded)
 | 
			
		||||
                {
 | 
			
		||||
                    await sm.DeferAsync();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Log.Warning(ex, "An exception occured while handling a: {Message}", ex.Message);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Task ExecuteOnActionAsync(SocketModal smd)
 | 
			
		||||
        => _onAction(smd);
 | 
			
		||||
}
 | 
			
		||||
@@ -9,19 +9,19 @@ public class NadekoInteractionService : INadekoInteractionService, INService
 | 
			
		||||
        _client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction Create(
 | 
			
		||||
    public NadekoInteractionBase Create(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        Func<SocketMessageComponent, Task> onTrigger,
 | 
			
		||||
        bool singleUse = true)
 | 
			
		||||
        => new NadekoButtonInteraction(_client,
 | 
			
		||||
        => new NadekoButtonInteractionHandler(_client,
 | 
			
		||||
            userId,
 | 
			
		||||
            button,
 | 
			
		||||
            onTrigger,
 | 
			
		||||
            onlyAuthor: true,
 | 
			
		||||
            singleUse: singleUse);
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction Create<T>(
 | 
			
		||||
    public NadekoInteractionBase Create<T>(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        Func<SocketMessageComponent, T, Task> onTrigger,
 | 
			
		||||
@@ -32,16 +32,46 @@ public class NadekoInteractionService : INadekoInteractionService, INService
 | 
			
		||||
            ((Func<T, Func<SocketMessageComponent, Task>>)((data)
 | 
			
		||||
                => smc => onTrigger(smc, data)))(state),
 | 
			
		||||
            singleUse);
 | 
			
		||||
    
 | 
			
		||||
    public NadekoInteraction Create(
 | 
			
		||||
 | 
			
		||||
    public NadekoInteractionBase Create(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        SelectMenuBuilder menu,
 | 
			
		||||
        Func<SocketMessageComponent, Task> onTrigger,
 | 
			
		||||
        bool singleUse = true)
 | 
			
		||||
        => new NadekoSelectInteraction(_client,
 | 
			
		||||
        => new NadekoButtonSelectInteractionHandler(_client,
 | 
			
		||||
            userId,
 | 
			
		||||
            menu,
 | 
			
		||||
            onTrigger,
 | 
			
		||||
            onlyAuthor: true,
 | 
			
		||||
            singleUse: singleUse);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create an interaction which opens a modal
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId">Id of the author</param>
 | 
			
		||||
    /// <param name="button">Button builder for the button that will open the modal</param>
 | 
			
		||||
    /// <param name="modal">Modal</param>
 | 
			
		||||
    /// <param name="onTrigger">The function that will be called when the modal is submitted</param>
 | 
			
		||||
    /// <param name="singleUse">Whether the button is single use</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public NadekoInteractionBase Create(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        ModalBuilder modal,
 | 
			
		||||
        Func<SocketModal, Task> onTrigger,
 | 
			
		||||
        bool singleUse = true)
 | 
			
		||||
        => Create(userId,
 | 
			
		||||
            button,
 | 
			
		||||
            async (smc) =>
 | 
			
		||||
            {
 | 
			
		||||
                await smc.RespondWithModalAsync(modal.Build());
 | 
			
		||||
                var modalHandler = new NadekoModalSubmitHandler(_client,
 | 
			
		||||
                    userId,
 | 
			
		||||
                    modal.CustomId,
 | 
			
		||||
                    onTrigger,
 | 
			
		||||
                    true);
 | 
			
		||||
                await modalHandler.RunAsync(smc.Message);
 | 
			
		||||
            },
 | 
			
		||||
            singleUse: singleUse);
 | 
			
		||||
}
 | 
			
		||||
@@ -34,11 +34,11 @@ public partial class ResponseBuilder
 | 
			
		||||
            if (_paginationBuilder.AddPaginatedFooter)
 | 
			
		||||
                embed.AddPaginatedFooter(currentPage, lastPage);
 | 
			
		||||
 | 
			
		||||
            NadekoInteraction? maybeInter = null;
 | 
			
		||||
            NadekoInteractionBase? maybeInter = null;
 | 
			
		||||
 | 
			
		||||
            var model = await _builder.BuildAsync(ephemeral);
 | 
			
		||||
 | 
			
		||||
            async Task<(NadekoButtonInteraction left, NadekoInteraction? extra, NadekoButtonInteraction right)>
 | 
			
		||||
            async Task<(NadekoButtonInteractionHandler left, NadekoInteractionBase? extra, NadekoButtonInteractionHandler right)>
 | 
			
		||||
                GetInteractions()
 | 
			
		||||
            {
 | 
			
		||||
                var leftButton = new ButtonBuilder()
 | 
			
		||||
@@ -47,7 +47,7 @@ public partial class ResponseBuilder
 | 
			
		||||
                                 .WithEmote(InteractionHelpers.ArrowLeft)
 | 
			
		||||
                                 .WithDisabled(lastPage == 0 || currentPage <= 0);
 | 
			
		||||
 | 
			
		||||
                var leftBtnInter = new NadekoButtonInteraction(_client,
 | 
			
		||||
                var leftBtnInter = new NadekoButtonInteractionHandler(_client,
 | 
			
		||||
                    model.User?.Id ?? 0,
 | 
			
		||||
                    leftButton,
 | 
			
		||||
                    (smc) =>
 | 
			
		||||
@@ -80,7 +80,7 @@ public partial class ResponseBuilder
 | 
			
		||||
                                  .WithEmote(InteractionHelpers.ArrowRight)
 | 
			
		||||
                                  .WithDisabled(lastPage == 0 || currentPage >= lastPage);
 | 
			
		||||
 | 
			
		||||
                var rightBtnInter = new NadekoButtonInteraction(_client,
 | 
			
		||||
                var rightBtnInter = new NadekoButtonInteractionHandler(_client,
 | 
			
		||||
                    model.User?.Id ?? 0,
 | 
			
		||||
                    rightButton,
 | 
			
		||||
                    (smc) =>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ public sealed partial class ResponseBuilder
 | 
			
		||||
    private readonly IBotStrings _bs;
 | 
			
		||||
    private readonly BotConfigService _bcs;
 | 
			
		||||
    private EmbedBuilder? embedBuilder;
 | 
			
		||||
    private NadekoInteraction? inter;
 | 
			
		||||
    private NadekoInteractionBase? inter;
 | 
			
		||||
    private Stream? fileStream;
 | 
			
		||||
    private string? fileName;
 | 
			
		||||
    private EmbedColor color = EmbedColor.Ok;
 | 
			
		||||
@@ -340,7 +340,7 @@ public sealed partial class ResponseBuilder
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResponseBuilder Interaction(NadekoInteraction? interaction)
 | 
			
		||||
    public ResponseBuilder Interaction(NadekoInteractionBase? interaction)
 | 
			
		||||
    {
 | 
			
		||||
        inter = interaction;
 | 
			
		||||
        return this;
 | 
			
		||||
@@ -395,7 +395,7 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
 | 
			
		||||
        return Task.FromResult<IReadOnlyCollection<T>>(ReadOnlyCollection<T>.Empty);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public Func<int, Task<NadekoInteraction>>? InteractionFunc { get; private set; }
 | 
			
		||||
    public Func<int, Task<NadekoInteractionBase>>? InteractionFunc { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public int? Elems { get; private set; } = 1;
 | 
			
		||||
    public int ItemsPerPage { get; private set; } = 9;
 | 
			
		||||
@@ -478,13 +478,13 @@ public sealed class SourcedPaginatedResponseBuilder<T> : PaginatedResponseBuilde
 | 
			
		||||
        return paginationSender.SendAsync(IsEphemeral);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SourcedPaginatedResponseBuilder<T> Interaction(Func<int, Task<NadekoInteraction>> func)
 | 
			
		||||
    public SourcedPaginatedResponseBuilder<T> Interaction(Func<int, Task<NadekoInteractionBase>> func)
 | 
			
		||||
    {
 | 
			
		||||
        InteractionFunc = func; //async (i) => await func(i);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SourcedPaginatedResponseBuilder<T> Interaction(NadekoInteraction inter)
 | 
			
		||||
    public SourcedPaginatedResponseBuilder<T> Interaction(NadekoInteractionBase inter)
 | 
			
		||||
    {
 | 
			
		||||
        InteractionFunc = _ => Task.FromResult(inter);
 | 
			
		||||
        return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,5 +8,5 @@
 | 
			
		||||
    public required AllowedMentions SanitizeMentions { get; set; }
 | 
			
		||||
    public IUser? User { get; set; }
 | 
			
		||||
    public bool Ephemeral { get; set; }
 | 
			
		||||
    public NadekoInteraction? Interaction { get; set; }
 | 
			
		||||
    public NadekoInteractionBase? Interaction { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -341,6 +341,9 @@ allcmdcooldowns:
 | 
			
		||||
quoteadd:
 | 
			
		||||
  - quoteadd
 | 
			
		||||
  - .
 | 
			
		||||
quoteedit:
 | 
			
		||||
  - quoteedit
 | 
			
		||||
  - qedit
 | 
			
		||||
quoteprint:
 | 
			
		||||
  - quoteprint
 | 
			
		||||
  - ..
 | 
			
		||||
 
 | 
			
		||||
@@ -1245,6 +1245,15 @@ quoteadd:
 | 
			
		||||
        desc: "The name of the quote used to retrieve the quote."
 | 
			
		||||
      text:
 | 
			
		||||
        desc: "The message of the quote."
 | 
			
		||||
quoteedit:
 | 
			
		||||
  desc: Edits a quote with the specified ID.
 | 
			
		||||
  ex:
 | 
			
		||||
    - 55 This is the new response.
 | 
			
		||||
  params:
 | 
			
		||||
    - quoteId:
 | 
			
		||||
        desc: "The ID of the quote being edited."
 | 
			
		||||
      text:
 | 
			
		||||
        desc: "The new message of the quote."
 | 
			
		||||
quoteprint:
 | 
			
		||||
  desc: Prints a random quote with a specified name.
 | 
			
		||||
  ex:
 | 
			
		||||
 
 | 
			
		||||
@@ -617,6 +617,7 @@
 | 
			
		||||
  "quotes_remove_none": "No quotes found which you can remove.",
 | 
			
		||||
  "quote_added_new": "Quote #{0} added.",
 | 
			
		||||
  "quote_deleted": "Quote #{0} deleted.",
 | 
			
		||||
  "quote_edited": "Quote Edited",
 | 
			
		||||
  "region": "Region",
 | 
			
		||||
  "remind": "I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})`",
 | 
			
		||||
  "remind_timely": "I will remind you about your timely reward {0}",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user