Пример #1
0
        private async Task <(bool success, DiscordMessage message)> EditFilterPropertiesAsync(CommandContext ctx, BotDb db, Piracystring filter)
        {
            var interact     = ctx.Client.GetInteractivity();
            var abort        = DiscordEmoji.FromUnicode("🛑");
            var lastPage     = DiscordEmoji.FromUnicode("↪");
            var firstPage    = DiscordEmoji.FromUnicode("↩");
            var previousPage = DiscordEmoji.FromUnicode("⏪");
            var nextPage     = DiscordEmoji.FromUnicode("⏩");
            var trash        = DiscordEmoji.FromUnicode("🗑");
            var saveEdit     = DiscordEmoji.FromUnicode("💾");

            var letterC = DiscordEmoji.FromUnicode("🇨");
            var letterL = DiscordEmoji.FromUnicode("🇱");
            var letterR = DiscordEmoji.FromUnicode("🇷");
            var letterW = DiscordEmoji.FromUnicode("🇼");
            var letterM = DiscordEmoji.FromUnicode("🇲");
            var letterE = DiscordEmoji.FromUnicode("🇪");

            DiscordMessage msg      = null;
            string         errorMsg = null;
            DiscordMessage txt;
            MessageReactionAddEventArgs emoji;

step1:
            // step 1: define trigger string
            var embed = FormatFilter(filter, errorMsg, 1)
                        .WithDescription(
                "Any simple string that is used to flag potential content for a check using Validation regex.\n" +
                "**Must** be sufficiently long to reduce the number of checks."
                );

            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **trigger**", embed : embed).ConfigureAwait(false);

            errorMsg          = null;
            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, lastPage, nextPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == lastPage)
                {
                    if (filter.Actions.HasFlag(FilterAction.ShowExplain))
                    {
                        goto step6;
                    }

                    if (filter.Actions.HasFlag(FilterAction.SendMessage))
                    {
                        goto step5;
                    }

                    goto step4;
                }
            }
            else if (txt?.Content != null)
            {
                if (txt.Content.Length < Config.MinimumPiracyTriggerLength)
                {
                    errorMsg = "Trigger is too short";
                    goto step1;
                }

                filter.String = txt.Content;
            }
            else
            {
                return(false, msg);
            }

step2:
            // step 2: context of the filter where it is applicable
            embed = FormatFilter(filter, errorMsg, 2)
                    .WithDescription(
                "Context of the filter indicates where it is applicable.\n" +
                $"**`C`** = **`{FilterContext.Chat}`** will apply it in filtering discord messages.\n" +
                $"**`L`** = **`{FilterContext.Log}`** will apply it during log parsing.\n" +
                "Reactions will toggle the context, text message will set the specified flags."
                );
            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **context(s)**", embed : embed).ConfigureAwait(false);

            errorMsg          = null;
            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, letterC, letterL, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == previousPage)
                {
                    goto step1;
                }

                if (emoji.Emoji == letterC)
                {
                    filter.Context ^= FilterContext.Chat;
                    goto step2;
                }

                if (emoji.Emoji == letterL)
                {
                    filter.Context ^= FilterContext.Log;
                    goto step2;
                }
            }
            else if (txt != null)
            {
                var           flagsTxt = txt.Content.Split(Separators, StringSplitOptions.RemoveEmptyEntries);
                FilterContext newCtx   = 0;
                foreach (var f in flagsTxt)
                {
                    switch (f.ToUpperInvariant())
                    {
                    case "C":
                    case "CHAT":
                        newCtx |= FilterContext.Chat;
                        break;

                    case "L":
                    case "LOG":
                    case "LOGS":
                        newCtx |= FilterContext.Log;
                        break;

                    case "ABORT":
                        return(false, msg);

                    case "-":
                    case "SKIP":
                    case "NEXT":
                        break;

                    default:
                        errorMsg = $"Unknown context `{f}`.";
                        goto step2;
                    }
                }
                filter.Context = newCtx;
            }
            else
            {
                return(false, msg);
            }

step3:
            // step 3: actions that should be performed on match
            embed = FormatFilter(filter, errorMsg, 3)
                    .WithDescription(
                "Actions that will be executed on positive match.\n" +
                $"**`R`** = **`{FilterAction.RemoveContent}`** will remove the message / log.\n" +
                $"**`W`** = **`{FilterAction.IssueWarning}`** will issue a warning to the user.\n" +
                $"**`M`** = **`{FilterAction.SendMessage}`** send _a_ message with an explanation of why it was removed.\n" +
                $"**`E`** = **`{FilterAction.ShowExplain}`** show `explain` for the specified term (**not implemented**).\n" +
                "Reactions will toggle the action, text message will set the specified flags."
                );
            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **action(s)**", embed : embed).ConfigureAwait(false);

            errorMsg          = null;
            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, letterR, letterW, letterM, letterE, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == previousPage)
                {
                    goto step2;
                }

                if (emoji.Emoji == letterR)
                {
                    filter.Actions ^= FilterAction.RemoveContent;
                    goto step3;
                }

                if (emoji.Emoji == letterW)
                {
                    filter.Actions ^= FilterAction.IssueWarning;
                    goto step3;
                }

                if (emoji.Emoji == letterM)
                {
                    filter.Actions ^= FilterAction.SendMessage;
                    goto step3;
                }

                if (emoji.Emoji == letterE)
                {
                    filter.Actions ^= FilterAction.ShowExplain;
                    goto step3;
                }
            }
            else if (txt != null)
            {
                var flagsTxt = txt.Content.ToUpperInvariant().Split(Separators, StringSplitOptions.RemoveEmptyEntries);
                if (flagsTxt.Length == 1 &&
                    flagsTxt[0].Length <= Enum.GetValues(typeof(FilterAction)).Length)
                {
                    flagsTxt = flagsTxt[0].Select(c => c.ToString()).ToArray();
                }
                FilterAction newActions = 0;
                foreach (var f in flagsTxt)
                {
                    switch (f)
                    {
                    case "R":
                    case "REMOVE":
                    case "REMOVEMESSAGE":
                        newActions |= FilterAction.RemoveContent;
                        break;

                    case "W":
                    case "WARN":
                    case "WARNING":
                    case "ISSUEWARNING":
                        newActions |= FilterAction.IssueWarning;
                        break;

                    case "M":
                    case "MSG":
                    case "MESSAGE":
                    case "SENDMESSAGE":
                        newActions |= FilterAction.SendMessage;
                        break;

                    case "E":
                    case "X":
                    case "EXPLAIN":
                    case "SHOWEXPLAIN":
                    case "SENDEXPLAIN":
                        newActions |= FilterAction.ShowExplain;
                        break;

                    case "ABORT":
                        return(false, msg);

                    case "-":
                    case "SKIP":
                    case "NEXT":
                        break;

                    default:
                        errorMsg = $"Unknown action `{f.ToLowerInvariant()}`.";
                        goto step2;
                    }
                }
                filter.Actions = newActions;
            }
            else
            {
                return(false, msg);
            }

step4:
            // step 4: validation regex to filter out false positives of the plaintext triggers
            embed = FormatFilter(filter, errorMsg, 4)
                    .WithDescription(
                "Validation [regex](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) to optionally perform more strict trigger check.\n" +
                "**Please [test](https://regex101.com/) your regex**. Following flags are enabled: Multiline, IgnoreCase.\n" +
                "Additional validation can help reduce false positives of a plaintext trigger match."
                );
            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **validation regex**", embed : embed).ConfigureAwait(false);

            errorMsg = null;
            var next = (filter.Actions & (FilterAction.SendMessage | FilterAction.ShowExplain)) == 0 ? firstPage : nextPage;

            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, next, (string.IsNullOrEmpty(filter.ValidatingRegex) ? null : trash), (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == previousPage)
                {
                    goto step3;
                }

                if (emoji.Emoji == firstPage)
                {
                    goto step1;
                }

                if (emoji.Emoji == trash)
                {
                    filter.ValidatingRegex = null;
                }
            }
            else if (txt != null)
            {
                if (string.IsNullOrWhiteSpace(txt.Content) || txt.Content == "-" || txt.Content == ".*")
                {
                    filter.ValidatingRegex = null;
                }
                else
                {
                    try
                    {
                        Regex.IsMatch("test", txt.Content, RegexOptions.Multiline | RegexOptions.IgnoreCase);
                    }
                    catch (Exception e)
                    {
                        errorMsg = "Invalid regex expression: " + e.Message;
                        goto step4;
                    }

                    filter.ValidatingRegex = txt.Content;
                }
            }
            else
            {
                return(false, msg);
            }

            if (filter.Actions.HasFlag(FilterAction.SendMessage))
            {
                goto step5;
            }
            else if (filter.Actions.HasFlag(FilterAction.ShowExplain))
            {
                goto step6;
            }
            else
            {
                goto stepConfirm;
            }

step5:
            // step 5: optional custom message for the user
            embed = FormatFilter(filter, errorMsg, 5)
                    .WithDescription(
                "Optional custom message sent to the user.\n" +
                "If left empty, default piracy warning message will be used."
                );
            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **validation regex**", embed : embed).ConfigureAwait(false);

            errorMsg          = null;
            next              = (filter.Actions.HasFlag(FilterAction.ShowExplain) ? nextPage : firstPage);
            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, next, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == previousPage)
                {
                    goto step4;
                }

                if (emoji.Emoji == firstPage)
                {
                    goto step1;
                }
            }
            else if (txt != null)
            {
                if (string.IsNullOrWhiteSpace(txt.Content) || txt.Content == "-")
                {
                    filter.CustomMessage = null;
                }
                else
                {
                    filter.CustomMessage = txt.Content;
                }
            }
            else
            {
                return(false, msg);
            }

            if (filter.Actions.HasFlag(FilterAction.ShowExplain))
            {
                goto step6;
            }
            else
            {
                goto stepConfirm;
            }

step6:
            // step 6: show explanation for the term
            embed = FormatFilter(filter, errorMsg, 6)
                    .WithDescription(
                "Explanation term that is used to show an explanation.\n" +
                "**__Currently not implemented__**."
                );
            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **explanation term**", embed : embed).ConfigureAwait(false);

            errorMsg          = null;
            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == previousPage)
                {
                    if (filter.Actions.HasFlag(FilterAction.SendMessage))
                    {
                        goto step5;
                    }
                    else
                    {
                        goto step4;
                    }
                }

                if (emoji.Emoji == firstPage)
                {
                    goto step1;
                }
            }
            else if (txt != null)
            {
                if (string.IsNullOrWhiteSpace(txt.Content) || txt.Content == "-")
                {
                    filter.ExplainTerm = null;
                }
                else
                {
                    var existingTerm = await db.Explanation.FirstOrDefaultAsync(exp => exp.Keyword == txt.Content.ToLowerInvariant()).ConfigureAwait(false);

                    if (existingTerm == null)
                    {
                        errorMsg = $"Term `{txt.Content.ToLowerInvariant().Sanitize()}` is not defined.";
                        goto step6;
                    }

                    filter.ExplainTerm = txt.Content;
                }
            }
            else
            {
                return(false, msg);
            }

stepConfirm:
            // last step: confirm
            if (errorMsg == null && !filter.IsComplete())
            {
                errorMsg = "Some required properties are not defined";
            }
            embed = FormatFilter(filter, errorMsg);
            msg   = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Does this look good? (y/n)", embed : embed.Build()).ConfigureAwait(false);

            errorMsg          = null;
            (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false);

            if (emoji != null)
            {
                if (emoji.Emoji == abort)
                {
                    return(false, msg);
                }

                if (emoji.Emoji == saveEdit)
                {
                    return(true, msg);
                }

                if (emoji.Emoji == previousPage)
                {
                    if (filter.Actions.HasFlag(FilterAction.ShowExplain))
                    {
                        goto step6;
                    }

                    if (filter.Actions.HasFlag(FilterAction.SendMessage))
                    {
                        goto step5;
                    }

                    goto step4;
                }

                if (emoji.Emoji == firstPage)
                {
                    goto step1;
                }
            }
            else if (!string.IsNullOrEmpty(txt?.Content))
            {
                if (!filter.IsComplete())
                {
                    goto step5;
                }

                switch (txt.Content.ToLowerInvariant())
                {
                case "yes":
                case "y":
                case "✅":
                case "☑":
                case "✔":
                case "👌":
                case "👍":
                    return(true, msg);

                case "no":
                case "n":
                case "❎":
                case "❌":
                case "👎":
                    return(false, msg);

                default:
                    errorMsg = "I don't know what you mean, so I'll just abort";
                    if (filter.Actions.HasFlag(FilterAction.ShowExplain))
                    {
                        goto step6;
                    }

                    if (filter.Actions.HasFlag(FilterAction.SendMessage))
                    {
                        goto step5;
                    }

                    goto step4;
                }
            }
            else
            {
                return(false, msg);
            }
            return(false, msg);
        }