示例#1
0
文件: UnWarn.cs 项目: mmattbtw/Kaguya
        private async Task ReactionReply(SocketGuildUser user,
                                         IReadOnlyCollection <WarnedUser> warnings,
                                         Embed embed,
                                         int warnCount,
                                         Server server,
                                         string reason)
        {
            Emoji[] emojis = GlobalProperties.EmojisOneThroughNine();

            var data      = new ReactionCallbackData("", embed, false, false, TimeSpan.FromSeconds(300));
            var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>();

            for (int j = 0; j < warnCount; j++)
            {
                int j1 = j;
                callbacks.Add((emojis[j], async(c, r) =>
                {
                    var uwArgs = new ModeratorEventArgs(server, Context.Guild, user, (SocketGuildUser)Context.User, reason, null);
                    KaguyaEvents.TriggerUnwarn(uwArgs);

                    await DatabaseQueries.DeleteAsync(warnings.ElementAt(j1));
                    await c.Channel.SendMessageAsync($"{r.User.Value.Mention} " +
                                                     $"`Successfully removed warning #{j1 + 1}`");
                }));
            }

            data.SetCallbacks(callbacks);
            await InlineReactionReplyAsync(data);
        }
        protected virtual async Task ReplyInteractiveAsync(ServiceResponse response, string titleSuccess)
        {
            var messageBuilder = new StringBuilder();

            messageBuilder.AppendLine(response.Message);
            var callbackCounter = 1;

            foreach (var resultInterActiveCallback in response.InterActiveCallbacks)
            {
                var emoji = EmojiService.Get(callbackCounter);
                messageBuilder.AppendLine(emoji.Name + " " + resultInterActiveCallback.Key);
                callbackCounter++;
            }
            var embed = BuildEmbed(LocalizationService.Get(typeof(i18n), "Base_Messages_Reply_Interactive"), messageBuilder.ToString(), Color.Orange);
            var reply = new ReactionCallbackData(string.Empty, embed, TimeSpan.FromSeconds(30));

            callbackCounter = 1;
            foreach (var resultInterActiveCallback in response.InterActiveCallbacks)
            {
                var emoji = EmojiService.Get(callbackCounter);
                reply.WithCallback(emoji, c => ReplyWithInteractive(resultInterActiveCallback.Value, titleSuccess));
                callbackCounter++;
            }
            await InlineReactionReplyAsync(reply);
        }
示例#3
0
        public async Task Command([Remainder] string text)
        {
            Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id);

            IEnumerable <Quote> quotes = server.Quotes;
            int quoteCount             = quotes?.Count() ?? 0;

            if (quoteCount > 0)
            {
                if (server.Quotes.Any(x => x.Text.Equals(text)))
                {
                    var cEmbed = new KaguyaEmbedBuilder(EmbedColor.YELLOW)
                    {
                        Description = "A quote with the same text already exists. Do you want to create this one anyway?"
                    };

                    var data = new ReactionCallbackData("", cEmbed.Build(), true, true, TimeSpan.FromSeconds(120));
                    data.AddCallBack(GlobalProperties.CheckMarkEmoji(), async(c, r) => { await InsertQuote(Context, server, text); });

                    data.AddCallBack(GlobalProperties.NoEntryEmoji(),
                                     async(c, r) => { await SendBasicErrorEmbedAsync("Okay, no action will be taken."); });

                    await InlineReactionReplyAsync(data);

                    return;
                }
            }

            await InsertQuote(Context, server, text);
        }
示例#4
0
        public async Task <RuntimeResult> Language()
        {
            if (FergunClient.Languages.Count <= 1)
            {
                return(FergunResult.FromError(Locate("NoAvailableLanguages")));
            }

            var guild = GetGuildConfig() ?? new GuildConfig(Context.Guild.Id);

            bool         hasReacted = false;
            IUserMessage message    = null;
            string       languages  = "";
            var          callbacks  = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>();
            EmbedBuilder builder    = null;
            int          i          = 0;

            foreach (var language in FergunClient.Languages)
            {
                callbacks.Add((new Emoji($"{i + 1}\ufe0f\u20e3"), async(context, reaction) => await HandleLanguageUpdateAsync(language.Key)));
                languages += $"{i + 1}. {Format.Bold(language.Value.EnglishName)} ({language.Value.NativeName})\n";
                i++;
            }

            builder = new EmbedBuilder()
                      .WithTitle(Locate("LanguageSelection"))
                      .WithDescription($"{Locate("LanguagePrompt")}\n\n{languages}")
                      .WithColor(FergunClient.Config.EmbedColor);

            ReactionCallbackData data = new ReactionCallbackData(null, builder.Build(), false, false, TimeSpan.FromMinutes(1),
                                                                 async context => await HandleLanguageUpdateAsync(null)).AddCallbacks(callbacks);

            message = await InlineReactionReplyAsync(data);

            return(FergunResult.FromSuccess());

            async Task HandleLanguageUpdateAsync(string newLanguage)
            {
                if (hasReacted || guild.Language == newLanguage)
                {
                    return;
                }
                hasReacted = true;
                if (newLanguage == null)
                {
                    await message !.ModifyAsync(x => x.Embed = builder.WithDescription($"❌ {Locate("ReactTimeout")}").Build());
                    return;
                }
                guild.Language = newLanguage;
                FergunClient.Database.InsertOrUpdateDocument(Constants.GuildConfigCollection, guild);

                await _logService.LogAsync(new LogMessage(LogSeverity.Verbose, "Command", $"Language: Updated language to: \"{newLanguage}\" in {Context.Guild.Name}"));

                await message !.ModifyAsync(x => x.Embed = builder.WithTitle(Locate("LanguageSelection")).WithDescription($"✅ {Locate("NewLanguage")}").Build());
            }
        }
示例#5
0
        // if the user supplies a tagname that doesn't exist, search the database to see if there are
        // any tags containing the user-supplied text

        // if there are any results from the database, post a list of the results & add emoji reactions
        // corresponding to each item in the list

        // when the user selects a emoji, it will pass the corresponding tagName and any extra passed
        // parameters to the RetryCommandUsingFoundTag function

        // afterwards, save a pairing of the calling user object and the searchResults message into a
        // dictionary, for use by the RetryCommandUsingFoundTag function
        private async Task FindTagAndRetry(string functionToRetry, params string[] args)
        {
            var tagName = args[0];

            var searchResponse = await DatabaseTags.SearchTagsInDatabase(Context, tagName);

            // if single result, just use that
            if (searchResponse.Count == 1)
            {
                await RetryCommandUsingFoundTag(searchResponse[0], functionToRetry, args);

                return;
            }

            // if the single-result check didn't catch but there are results
            if (searchResponse.Any())
            {
                string[] numbers      = new[] { "0⃣", "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣" };
                var      numberEmojis = new List <Emoji>();

                EmbedBuilder  embedBuilder  = new EmbedBuilder();
                StringBuilder stringBuilder = new StringBuilder();

                // add the number of emojis we need to the emojis list, and build our string-list of search results
                for (int i = 0; i < searchResponse.Count && i < numbers.Length; i++)
                {
                    numberEmojis.Add(new Emoji(numbers[i]));
                    stringBuilder.AppendLine($"{numbers[i]} - {searchResponse[i]}");
                }

                embedBuilder.WithDescription(stringBuilder.ToString());
                embedBuilder.WithColor(Color.Blue);

                // build a message and add reactions to it
                // reactions will be watched, and the one selected will fire the HandleFindTagReactionResult method, passing
                // that reaction's corresponding tagname and the function passed into this parameter
                var messageContents = new ReactionCallbackData("Did you mean... ", embedBuilder.Build());
                for (int i = 0; i < searchResponse.Count; i++)
                {
                    var counter = i;
                    messageContents.AddCallBack(numberEmojis[counter], (c, r) => RetryCommandUsingFoundTag(searchResponse[counter], functionToRetry, args));
                }

                var message = await InlineReactionReplyAsync(messageContents);

                // add calling user and searchResults embed to a dict as a pair
                // this way we can hold multiple users' reaction messages and operate on them separately
                _dictFindTagUserEmbedPairs.Add(Context.User, message);
            }
            else
            {
                await ReplyAsync("I can't find any tags like what you're looking for.");
            }
        }
示例#6
0
        public async Task <IUserMessage> SendMessageWithReactionCallbacksAsync(SocketCommandContext context,
                                                                               ReactionCallbackData reactionCallbackData,
                                                                               bool fromSourceUser = true)
        {
            var criterion = new Criteria <SocketReaction>();

            if (fromSourceUser)
            {
                criterion.AddCriterion(new EnsureReactionFromSourceUserCriterion());
            }

            var callback = new InlineReactionCallback(this, context, reactionCallbackData, criterion);
            await callback.DisplayAsync().ConfigureAwait(false);

            return(callback.Message);
        }
示例#7
0
        private static ReactionCallbackData GenerateColorRole(ColorRole colorRole)
        {
            var embedBuilder = new EmbedBuilder()
                               .WithTitle($"Color Role")
                               .WithColor(new Color(colorRole.r, colorRole.g, colorRole.b))
                               .WithDescription(
                $"Do you like this color? HEX: {colorRole.HexColor.ToUpper()} / RGB: {colorRole.r}, {colorRole.g}, {colorRole.b}" + Environment.NewLine
                + "React with your choice."
                );

            var rcbd = new ReactionCallbackData("", embedBuilder.Build(), true, true, reactDuration, async c => await ReactionEndedAsync(c, colorRole).ConfigureAwait(false));

            foreach (IEmote answerEmoji in colorRole.Answers.Select(x => x.AnswerEmoji))
            {
                _ = rcbd.WithCallback(answerEmoji, (c, r) => ColorPicker(r, colorRole));
            }
            return(rcbd);
        }
示例#8
0
        private static ReactionCallbackData GeneratePoll(Poll poll)
        {
            string answers = string.Join(Environment.NewLine, poll.Answers.Select(x => $"{x.AnswerEmoji} {x.Answer}"));

            var embedBuilder = new EmbedBuilder()
                               .WithTitle($"New Poll: {poll.Question}")
                               .WithColor(new Color(20, 20, 20))
                               .WithDescription(
                "- Pick an option by clicking on the corresponding Emoji" + Environment.NewLine
                + "- Only your first pick counts!" + Environment.NewLine
                + $"- You have {pollDuration.Humanize()} to cast your vote"
                )
                               .AddField("Pick one", answers);

            var rcbd = new ReactionCallbackData("", embedBuilder.Build(), false, true, true, pollDuration, async c => await PollEndedAsync(c, poll).ConfigureAwait(false));

            foreach (var answerEmoji in poll.Answers.Select(x => x.AnswerEmoji))
            {
                rcbd.WithCallback(answerEmoji, (c, r) => AddVoteCount(r, poll));
            }
            return(rcbd);
        }
        public async Task Test_OptionsReply(int count)
        {
            string[] numbers      = new[] { "0⃣", "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣" };
            var      numberEmojis = new List <Emoji>();

            for (int i = 0; i < numbers.Length; i++)
            {
                numberEmojis.Add(new Emoji(numbers[i]));
            }

            var one = new Emoji("1?");

            var reply = new ReactionCallbackData("Option Count: " + count);

            for (int i = 1; i <= count; i++)
            {
                var counter = i;
                reply.WithCallback(numberEmojis[counter], c => c.Channel.SendMessageAsync("You have choosen option: " + counter));
            }

            await InlineReactionReplyAsync(reply);
        }
示例#10
0
        private async Task MultipleMatchingRolesHandler(SocketGuild guild, string roleName, IReadOnlyCollection <SocketRole> roles)
        {
            List <SocketRole> matchingRoles = roles.Where(x => x.Name.ToLower() == roleName.ToLower()).ToList();
            int matchCount = matchingRoles.Count;

            if (matchCount > 9)
            {
                matchCount = 9;
            }

            var emojis = new Emoji[]
            {
                new Emoji("1⃣"),
                new Emoji("2⃣"),
                new Emoji("3⃣"),
                new Emoji("4⃣"),
                new Emoji("5⃣"),
                new Emoji("6⃣"),
                new Emoji("7⃣"),
                new Emoji("8⃣"),
                new Emoji("9⃣")
            };

            var embed = new KaguyaEmbedBuilder
            {
                Description = $"I found `{matchCount.ToWords()}` roles that match this name. Please " +
                              $"select the role that you want to display.",
                Fields = new List <EmbedFieldBuilder>()
            };

            var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>();

            for (int i = 0; i < matchCount; i++)
            {
                int i1 = i;
                if (i1 == matchCount)
                {
                    i1 = matchCount - 1;
                }

                SocketRole                    role          = matchingRoles.ElementAt(i1);
                List <GuildPermission>        rolePerms     = matchingRoles[i].Permissions.ToList();
                IEnumerable <SocketGuildUser> usersWithRole = guild.Users.Where(x => x.Roles.Contains(role));

                embed.Fields.Add(new EmbedFieldBuilder
                {
                    Name  = $"Role #{i + 1}",
                    Value = $"Exact Name: `{role.Name}`\nNumber of users who have the role: {usersWithRole.Count()}" +
                            $"\nPermissions: `{rolePerms.Count}`\n" +
                            $"Created: `{role.CreatedAt.Humanize()}`\n" +
                            $"Position in role list (higher number = higher position): `{role.Position}`"
                });

                callbacks.Add((emojis[i], async(c, r) =>
                {
                    var pager = new PaginatedMessage
                    {
                        Pages = Pages(guild, role),
                        Color = Color.Blue
                    };

                    await PagedReplyAsync(pager, new ReactionList
                    {
                        Backward = true,
                        First = true,
                        Forward = true,
                        Jump = true,
                        Last = true,
                        Trash = true
                    });
                }
                               ));
            }

            var data = new ReactionCallbackData("", embed.Build(), false, false, TimeSpan.FromSeconds(120),
                                                c => c.Channel.SendMessageAsync("Role selection has timed out. Please try again."));

            data.SetCallbacks(callbacks);
            await InlineReactionReplyAsync(data);
        }
示例#11
0
        public async Task Challenge(IGuildUser user)
        {
            if (user == Context.User)
            {
                await ReplyAsync("You cannot choose yourself.");

                return;
            }

            if (user.IsBot)
            {
                await ReplyAsync("You can only challenge a human.");

                return;
            }

            if (user.RoleIds.Any(x => x == 347300513110294528))
            {
                await ReplyAsync("You cannot challenge a VIP in this server");

                return;
            }

            var lastDaySeries  = seriesService.SeriesHistory.Reverse().TakeWhile(x => (DateTime.Now - x.Date).TotalHours < 12);
            var playerIds      = new[] { user.Id.ToString(), Context.User.Id.ToString() };
            var conflictSeries = lastDaySeries.FirstOrDefault(x => playerIds.Contains(x.WinnerId) && playerIds.Contains(x.LoserId));

            if (conflictSeries != default)
            {
                await ReplyAsync($"You already played this person less than a day ago, " +
                                 $"please wait for {(conflictSeries.Date.AddHours(12) - DateTime.Now).AsRoundedDuration()}.");

                return;
            }

            var player1    = rankingService.SeriesPlayers.FirstOrDefault(x => x.Id == Context.User.Id.ToString());
            var player2    = rankingService.SeriesPlayers.FirstOrDefault(x => x.Id == user.Id.ToString());
            var player1Elo = player1?.Elo ?? 1200;
            var player2Elo = player2?.Elo ?? 1200;
            var lower      = player1Elo < player2Elo ? player1 : player2;

            if ((player1Elo > 1300 || player2Elo > 1300) && Math.Abs(player1Elo - player2Elo) >= 300)
            {
                if ((lower?.Points ?? 0) < 5)
                {
                    await ReplyAsync(
                        "If a player's elo is more than 1300 and the Elo difference of the players is more than 300, " +
                        "the lower Elo player needs to have at least 5 points earned to challenge."
                        );

                    return;
                }
            }

            var challengeEmbed = new EmbedBuilder()
                                 .WithColor(Color.Orange)
                                 .WithAuthor(Context.User)
                                 .WithTitle("Series Challenge")
                                 .WithDescription(
                Context.User.Mention + " is challenging " + user.Mention + " on a series.\n" +
                "Engineer is banned by default unless [rule 7](https://mobilekoth.github.io/system) is applied.\n" +
                "Please decide if to ban more towers. Both must agree or there will be no other towers banned.\n" +
                "<:ban:683258477421920342> Ban Towers ⭕ No Ban Towers 🚶 Reject Challenge"
                )
                                 .WithFooter($"Both of you have {TowerBanManager.MAX_SESSION_SECONDS} seconds to decide.");

            bool leftAgree = false, rightAgree = false, denied = false;
            int  voteCount = 0;

            var reactionCallBackData = new ReactionCallbackData(
                string.Empty,
                embed: challengeEmbed.Build(),
                false, // Expires after use
                true,  // Single use per user
                TimeSpan.FromSeconds(TowerBanManager.MAX_SESSION_SECONDS),
                async timeOut =>
            {
                if (voteCount == 2 || denied)
                {
                    return;
                }
                await ReplyAsync(embed: new EmbedBuilder()
                                 .WithColor(Color.Orange)
                                 .WithDescription($"{Context.User.Mention} {user.Mention} Challenge session has timed out.")
                                 .Build());
            }).WithCallback(BanEmote, async(c, e) =>
            {
                if (!(e.UserId == Context.User.Id || e.UserId == user.Id) || denied)
                {
                    return;
                }
                leftAgree  = leftAgree || e.UserId == Context.User.Id;
                rightAgree = rightAgree || e.UserId == user.Id;
                voteCount++;
                await handleVote();
            })
                                       .WithCallback(new Emoji("⭕"), async(c, e) =>
            {
                if (!(e.UserId == Context.User.Id || e.UserId == user.Id) || denied)
                {
                    return;
                }
                voteCount++;
                await handleVote();
            })
                                       .WithCallback(new Emoji("🚶"), async(c, e) =>
            {
                if (!(e.UserId == Context.User.Id || e.UserId == user.Id) || denied)
                {
                    return;
                }
                if (e.UserId == Context.User.Id || e.UserId == user.Id)
                {
                    voteCount++;
                    denied = true;
                    await ReplyAsync(embed: new EmbedBuilder()
                                     .WithColor(Color.Orange)
                                     .WithDescription($"{e.User.Value.Mention} has rejected the challenge")
                                     .Build());
                }
            });

            async Task handleVote()
            {
                if (voteCount != 2)
                {
                    return;
                }
                if (leftAgree && rightAgree)
                {
                    await BanTowerSession(user);
                }
                else
                {
                    await ReplyAsync(embed : new EmbedBuilder()
                                     .WithColor(Color.Orange)
                                     .WithDescription(
                                         $"{Context.User.Mention} {user.Mention} " +
                                         $"There is at least 1 disagreement to have towers banned. " +
                                         $"You may begin your series with no tower banned."
                                         )
                                     .Build());
                }
            }

            await InlineReactionReplyAsync(reactionCallBackData, false);
        }
示例#12
0
        public async Task RemoveRole([Remainder] string targetRole)
        {
            var roles = new List <SocketRole>();

            roles = Context.Guild.Roles.Where(r => r.Name.ToLower() == targetRole.ToLower()).ToList();

            if (roles.Count > 1)
            {
                var emojis = new Emoji[]
                {
                    new Emoji("1⃣"),
                    new Emoji("2⃣"),
                    new Emoji("3⃣"),
                    new Emoji("4⃣"),
                    new Emoji("5⃣"),
                    new Emoji("6⃣"),
                    new Emoji("7⃣"),
                    new Emoji("8⃣"),
                    new Emoji("9⃣")
                };

                var embed = new KaguyaEmbedBuilder
                {
                    Description = $"I found `{roles.Count.ToWords()}` roles that match this name. Please " +
                                  $"select the role that you want to delete, or use the ⛔ reaction " +
                                  $"to delete all roles with this name.",
                    Fields = new List <EmbedFieldBuilder>()
                };

                var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>();

                for (int i = 0; i < roles.Count; i++)
                {
                    int        roleIndex = i + 1;
                    SocketRole role      = roles.ElementAt(i);

                    embed.Fields.Add(new EmbedFieldBuilder
                    {
                        Name  = $"Role #{roleIndex}",
                        Value = $"Exact Name: `{role.Name}`\n" +
                                $"Number of users who have this role: " +
                                $"`{Context.Guild.Users.Count(x => x.Roles.Contains(role))}`\n" +
                                $"Permissions: `{roles.Count}`\n" +
                                $"Created: `{role.CreatedAt.Humanize()}`\n" +
                                $"Position in role list (higher number = higher position): `{role.Position}`"
                    });

                    callbacks.Add((emojis[i], async(c, r) =>
                    {
                        await role.DeleteAsync();
                        await ReplyAsync($"{Context.User.Mention} `Successfully deleted Role #{roleIndex}`");
                    }
                                   ));
                }

                callbacks.Add((new Emoji("⛔"), async(c, r) =>
                {
                    foreach (SocketRole role in roles)
                    {
                        await role.DeleteAsync();
                    }

                    await ReplyAsync($"{Context.User.Mention} Successfully deleted `{roles.Count.ToWords()}` roles.");
                }
                               ));

                var data = new ReactionCallbackData("", embed.Build(), false, false, TimeSpan.FromSeconds(120));
                data.SetCallbacks(callbacks);
                await InlineReactionReplyAsync(data);
            }
            else if (roles.Count == 1)
            {
                SocketRole role  = roles.First();
                var        embed = new KaguyaEmbedBuilder
                {
                    Description = $"{Context.User.Mention} Successfully deleted role `{role.Name}`"
                };

                await role.DeleteAsync();

                await ReplyAsync(embed : embed.Build());
            }
            else
            {
                var embed = new KaguyaEmbedBuilder
                {
                    Description = $"I could not find the specified role."
                };

                embed.SetColor(EmbedColor.RED);

                await ReplyAsync(embed : embed.Build());
            }
        }
示例#13
0
        /// <summary>
        ///     Sends a Message with reaction callback
        /// </summary>
        /// <param name="context"></param>
        /// <param name="callbacks"></param>
        /// <param name="fromSourceUser"></param>
        /// <returns></returns>
        public async Task <IUserMessage> SendMessageWithReactionCallbacksAsync(SocketCommandContext context, ReactionCallbackData callbacks, bool fromSourceUser = true)
        {
            if (callbacks.Text == null)
            {
                throw new Exception("Input text must not be null. Use an empty string instead.");
            }

            var criterion = new Criteria <SocketReaction>();

            if (fromSourceUser)
            {
                criterion.AddCriterion(new EnsureReactionFromSourceUserCriterion());
            }
            var callback = new InlineReactionCallback(new InteractiveService(Discord.GetShardFor(context.Guild)), context, callbacks, criterion);

            await callback.DisplayAsync().ConfigureAwait(false);

            return(callback.Message);
        }
示例#14
0
        public async Task GetInfo([Remainder, Name("termi")] string query)
        {
            var normalized = StringUtils.NormalizeQuery(query);

            if (normalized == null)
            {
                await ReplyAsync($"Termi **{query}** nuk u gjet.");

                return;
            }

            var obj = JsonUtils.LookupObject(Data, normalized);

            if (obj != null)
            {
                await ReplyAsync(
                    $"Informatat për **{query}**",
                    embed : JsonUtils.EmbedObject(obj));

                return;
            }

            const int threshold = 8;
            const int count     = 3;

            // No match, try finding suggestions.
            var matches = JsonUtils.FindClosest(Data, normalized, threshold, count);

            if (matches.Length != 0)
            {
                var emojis = new[] { "\u0031\u20E3", "\u0032\u20E3", "\u0033\u20E3" };

                // Give suggestions and listen for reactions.
                var text = $"Termi **{query}** nuk u gjet.\n\n"
                           + "Mos keni menduar për ndonjërën nga:\n"
                           + matches.Select((match, i) => i + 1 + ") " + match["_label"]).Join("\n");

                var callback = new ReactionCallbackData(
                    text,
                    embed: null,
                    expiresAfterUse: true,
                    singleUsePerUser: true,
                    timeout: TimeSpan.FromSeconds(30d));

                for (var i = 0; i < matches.Length; i++)
                {
                    var term = matches[i]["_label"].ToString();
                    callback.WithCallback(
                        new Emoji(emojis[i]), async(c, r) =>
                    {
                        var newObj = TryLookup(term);
                        if (newObj != null)
                        {
                            await c.Channel.SendMessageAsync(
                                $"Informatat për **{term}**",
                                embed: JsonUtils.EmbedObject(newObj));
                        }
                        else
                        {
                            await c.Channel.SendMessageAsync("Fatkeqësisht ka ndodhur një gabim. Ju lutem provoni përsëri.");
                        }
                    });
                }

                await InlineReactionReplyAsync(callback, fromSourceUser : true);
            }
            else
            {
                // No suggestions.
                await ReplyAsync($"Termi **{query}** nuk u gjet.");
            }
        }
示例#15
0
        public async Task Command()
        {
            User user = await DatabaseQueries.GetOrCreateUserAsync(Context.User.Id);

            Reminder[] reminders = user.Reminders.Where(x => x.Expiration > DateTime.Now.ToOADate()).ToArray();
            var        embed     = new KaguyaEmbedBuilder();

            int i = 0;

            if (!(reminders.Length == 0))
            {
                foreach (Reminder reminder in reminders)
                {
                    i++;

                    string expirationStr = DateTime.FromOADate(reminder.Expiration).Humanize(false);

                    var fSb = new StringBuilder();
                    fSb.AppendLine($"Reminder: `{reminder.Text}`");
                    fSb.AppendLine($"Expires: `{expirationStr}`");

                    var field = new EmbedFieldBuilder
                    {
                        IsInline = false,
                        Name     = $"#{i}",
                        Value    = fSb.ToString()
                    };

                    embed.AddField(field);
                }

                embed.Footer = new EmbedFooterBuilder
                {
                    Text = "To delete a reminder, click the corresponding reaction."
                };
            }

            else
            {
                var field = new EmbedFieldBuilder
                {
                    Name  = "No reminders active",
                    Value = "You currently don't have any active reminders."
                };

                embed.AddField(field);
            }

            int j    = 0;
            var data = new ReactionCallbackData("", embed.Build());

            foreach (Reminder reminder in reminders)
            {
                data.AddCallBack(GlobalProperties.EmojisOneThroughNine()[j],
                                 async(c, r) =>
                {
                    await DatabaseQueries.DeleteAsync(reminder);
                    await ReplyAsync($"{Context.User.Mention} Successfully deleted reminder #{j}.");
                });

                j++;
            }

            await InlineReactionReplyAsync(data);
        }
示例#16
0
        public async Task GetQuestion(int id = -100)
        {
            // question which will be fetch from server database
            Question question = null;

            // if question was already seen
            bool wasSeen = false;

            // Get server informations
            Guild guild = Guilds.GetGuild(Context.Guild.Id);

            // Get user account
            UserAccount userAccount = UserAccounts.GetUserAccount(Context.User.Id, guild.UserAccounts, guild.Categories);

            // check if question id is valid
            if (id >= 0)
            {
                // check if user already seen this question
                if (userAccount.SeenQuestionsIds.Contains(id))
                {
                    wasSeen = true;
                }

                // get question with id from database
                question = Questions.GetQuestion(id, guild.Categories);
            }
            else
            {
                Random          random       = new Random(DateTime.Now.Millisecond);
                List <Question> allQuestions = new List <Question>();

                // Get maximum question id
                foreach (Category category in guild.Categories)
                {
                    allQuestions.AddRange(category.Questions); // and all questions as well
                }

                // get all unseen questions
                IEnumerable <Question> unseenQuestions = from q in allQuestions
                                                         where !userAccount.SeenQuestionsIds.Contains(q.Id)
                                                         select q;

                int unseenQuestionsCount = unseenQuestions.ToList().Count;

                // if list of unseen questions is empty
                if (unseenQuestionsCount == 0)
                {
                    // send random question
                    wasSeen  = true;
                    question = allQuestions.ElementAt(random.Next(0, allQuestions.Count));
                }
                else
                {
                    // send unseen question
                    question = unseenQuestions.ElementAt(random.Next(0, unseenQuestionsCount));
                }

                id = question.Id;
            }

            // Create embed for message with question
            EmbedBuilder embedBuild = new EmbedBuilder
            {
                Title       = $"Pytanie #{question.Id}",
                Description = question.Description
            };

            // add possible answers
            for (int i = 0; i < question.PossibleAnswers.Count; i++)
            {
                embedBuild.AddField(Util.AnswersEmojis.ToList()[i].Name, question.PossibleAnswers[i], true);
            }

            // add image
            if (question.ImageURL != null)
            {
                embedBuild.ImageUrl = question.ImageURL;
            }

            // build embed
            Embed embed = embedBuild.Build();

            // ReactionCallbackData(message content, embed, expires after first use, if command should react to only one answer from 1 user,
            //                                                                                          command timeout, what to do if command expires)
            ReactionCallbackData reactionData = new ReactionCallbackData("", embed, true, true, TimeSpan.FromSeconds(120), (c) => Timeout(c.Channel));

            // Iterate all possible answers and add answer placeholder
            for (int i = 0; i < question.PossibleAnswers.Count; i++)
            {
                // check if this iteration answer is right
                if (i == question.RightAnswer)
                {
                    // Add answer placecholder with information what if user select this answer
                    // reactionData.WithCallback(answer emoji, what to do if user select answer)
                    // CheckScore(if question was seen, is this good answer, question id, question category, user account, source channel)
                    reactionData.WithCallback(Util.AnswersEmojis.ToList()[i], (c, r) => CheckScore(wasSeen, true, id, GetCategoryContainingQuestionId(id, guild.Categories), userAccount, c.Channel));
                }
                else
                {
                    reactionData.WithCallback(Util.AnswersEmojis.ToList()[i], (c, r) => CheckScore(wasSeen, false, id, GetCategoryContainingQuestionId(id, guild.Categories), userAccount, c.Channel));
                }
            }

            // Send quiz message and await user selection
            await InlineReactionReplyAsync(reactionData, true);
        }
示例#17
0
        /// <summary>
        /// Sends a message with reaction callback
        /// </summary>
        /// <param name="context"></param>
        /// <param name="callbacks"></param>
        /// <param name="fromSourceUser"></param>
        /// <returns></returns>
        public async Task <IUserMessage> SendMessageWithReactionCallbacksAsync(SocketCommandContext context, ReactionCallbackData callbacks, bool fromSourceUser = true)
        {
            var criterion = new Criteria <SocketReaction>();

            if (fromSourceUser)
            {
                criterion.AddCriterion(new EnsureReactionFromSourceUserCriterion());
            }
            var callback = new InlineReactionCallback(new InteractiveService(Discord.GetShardFor(context.Guild)), context, callbacks, criterion);
            await callback.DisplayAsync().ConfigureAwait(false);

            return(callback.Message);
        }
示例#18
0
 /// <summary>
 ///     Sends a Message that will do a custom action upon reactions
 /// </summary>
 /// <param name="data">The main settings used for the Message</param>
 /// <param name="fromSourceUser">True = Only the user who invoked this method can invoke the callback</param>
 /// <returns>The Message sent</returns>
 public Task <IUserMessage> InlineReactionReplyAsync(ReactionCallbackData data, bool fromSourceUser = true)
 {
     return(Interactive.SendMessageWithReactionCallbacksAsync(SocketContext(), data, fromSourceUser));
 }
示例#19
0
        /// <summary>
        ///     Searches the specified <see cref="SearchProvider" /> for the provided <see cref="query" />.
        ///     This method also adds the song to the guild's player queue and will even join the user's voice
        ///     channel automatically.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="query">The song to search for, user input.</param>
        /// <param name="playFirst"></param>
        /// <param name="provider"></param>
        /// <returns></returns>
        public async Task <ReactionCallbackData> SearchAndPlayAsync(ShardedCommandContext context,
                                                                    string query,
                                                                    bool playFirst          = false,
                                                                    SearchProvider provider = SearchProvider.YOU_TUBE)
        {
            User user = await DatabaseQueries.GetOrCreateUserAsync(context.User.Id);

            Server server = await DatabaseQueries.GetOrCreateServerAsync(context.Guild.Id);

            LavaNode           node  = ConfigProperties.LavaNode;
            SocketVoiceChannel curVc = (context.User as SocketGuildUser).VoiceChannel;

            await ConsoleLogger.LogAsync($"Found node and voice channel for guild {context.Guild.Id}.", LogLvl.TRACE);

            if (curVc == null)
            {
                await context.Channel.SendMessageAsync($"{context.User.Mention} You must be in a voice " +
                                                       "channel to use this command.");

                await ConsoleLogger.LogAsync("User was not in voice channel, cancelling music search operation.", LogLvl.TRACE);

                return(null);
            }

            SearchResponse result = provider switch
            {
                SearchProvider.YOU_TUBE => await node.SearchYouTubeAsync(query),
                SearchProvider.SOUNDCLOUD => await node.SearchSoundCloudAsync(query),
                _ => await node.SearchAsync(query)
            };

            if (provider == SearchProvider.TWITCH)
            {
                const string PROVIDER_URL = "www.twitch.tv";
                string       errorString  = "Your search returned no results. Ensure you are only " +
                                            "typing the name of the streamer who you want to watch or a direct link to their stream.\n\n" +
                                            "Note: The streamer must be live for this feature to work.";

                if (!query.ToLower().Contains(PROVIDER_URL))
                {
                    result = await node.SearchAsync($"https://{PROVIDER_URL}/{query}");

                    if (result.Tracks.Count == 0)
                    {
                        await context.Channel.SendBasicErrorEmbedAsync(errorString);

                        await ConsoleLogger.LogAsync($"No livestream found for search {query} in guild {context.Guild.Id}.", LogLvl.TRACE);

                        return(null);
                    }
                }
                else
                {
                    if ((await node.SearchAsync($"https://{PROVIDER_URL}/{query.Split('\\').Last()}")).Tracks.Count == 0 &&
                        (await node.SearchAsync(query)).Tracks.Count == 0)
                    {
                        await context.Channel.SendBasicErrorEmbedAsync(errorString);

                        await ConsoleLogger.LogAsync($"No livestream found for search {query} in guild {context.Guild.Id}.", LogLvl.TRACE);

                        return(null);
                    }
                }
            }

            var tracks = new List <LavaTrack>();

            if (user.IsPremium || server.IsPremium)
            {
                if (result.Tracks.Any())
                {
                    tracks.AddRange(result.Tracks);
                }
            }
            else
            {
                // Limit track duration to 10 minutes for non-premium servers/users.
                if (result.Tracks.Any())
                {
                    tracks.AddRange(result.Tracks.Where(x => x.Duration.TotalMinutes < 10).ToList());
                }
            }

            if (!tracks.Any())
            {
                string suppString = user.IsPremium
                    ? ""
                    : "If you are " +
                                    $"not a [Kaguya Premium Subscriber]({ConfigProperties.KAGUYA_STORE_URL}), " +
                                    "you are only limited to playing songs less than `10 minutes` in duration.";

                await context.Channel.SendBasicErrorEmbedAsync($"Your requested search returned no results. {suppString}");

                await ConsoleLogger.LogAsync("Search request returned no usable " +
                                             $"results in guild {Context.Guild.Id} for query {query}", LogLvl.TRACE);
            }

            var fields    = new List <EmbedFieldBuilder>();
            var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>();

            Emoji[] emojiNums = GlobalProperties.EmojisOneThroughNine();

            LavaPlayer player = node.HasPlayer(context.Guild)
                ? node.GetPlayer(context.Guild)
                : await node.JoinAsync(curVc);

            await ConsoleLogger.LogAsync($"Player found for guild {context.Guild.Id}. Connected to voice channel.", LogLvl.TRACE);

            #region If the track is a livestream:
            if (tracks.Any(x => x.IsStream))
            {
                LavaTrack trackSel   = tracks.First(x => x.IsStream); // Gathers the first stream from the collection.
                string    twitchName = (await ConfigProperties.TwitchApi.V5.Users.GetUserByNameAsync(trackSel.Author)).Matches[0].DisplayName;
                string    playString = player.PlayerState == PlayerState.Playing
                    ? $"Queued stream into position {player.Queue.Count}."
                    : $"Now playing `{twitchName}`'s stream.";

                if (player.PlayerState == PlayerState.Playing)
                {
                    try
                    {
                        player.Queue.Enqueue(trackSel);
                        await ConsoleLogger.LogAsync($"Enqueued livestream {trackSel.Title} in guild {context.Guild.Id}",
                                                     LogLvl.TRACE);
                    }
                    catch (Exception e)
                    {
                        await ConsoleLogger.LogAsync("An exception was thrown when trying to enqueue the livestream " +
                                                     $"{trackSel.Title} in guild {Context.Guild.Id}.\n" +
                                                     $"Exception Message: {e.Message}\n" +
                                                     $"Stack Trace: {e.StackTrace}", LogLvl.WARN);
                    }
                }
                else
                {
                    try
                    {
                        await player.PlayAsync(trackSel);

                        await ConsoleLogger.LogAsync($"Playing livestream {trackSel.Title} in guild {context.Guild.Id}",
                                                     LogLvl.TRACE);
                    }
                    catch (Exception e)
                    {
                        await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " +
                                                     $"{trackSel.Title} in guild {Context.Guild.Id}.\n" +
                                                     $"Exception Message: {e.Message}\n" +
                                                     $"Stack Trace: {e.StackTrace}", LogLvl.WARN);
                    }
                }

                var field = new EmbedFieldBuilder
                {
                    Name  = $"`{twitchName}`'s Stream",
                    Value = $"{playString}\n" // We get rid of backticks for formatting.
                };

                var embed = new KaguyaEmbedBuilder
                {
                    Fields = new List <EmbedFieldBuilder>
                    {
                        field
                    }
                };

                await context.Channel.SendEmbedAsync(embed);

                return(null);
            }
            #endregion

            #region If we have chosen to only play the default track (via $play).
            if (playFirst && tracks.Any())
            {
                LavaTrack trackSel = tracks[0];
                var       field    = new EmbedFieldBuilder
                {
                    Name  = "Track #1.",
                    Value = $"Title: `{trackSel.Title.Replace("`", "")}`\n" + // We get rid of backticks for formatting.
                            $"Duration: `{trackSel.Duration.Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 3)}`\n" +
                            $"Uploader: `{trackSel.Author}`"
                };

                string playString = player.PlayerState == PlayerState.Playing && !player.Track.IsStream
                    ? $"Queued track #1 into position {player.Queue.Count + 1}."
                    : "Now playing track #1.";

                if (player.PlayerState == PlayerState.Playing)
                {
                    if (player.Queue.Count() == 50 && !server.IsPremium)
                    {
                        await ConsoleLogger.LogAsync($"Queue is full in {context.Guild.Id}, sending error.", LogLvl.TRACE);
                        await SendBasicErrorEmbedAsync("Your queue is full! `50 songs` is the maximum " +
                                                       $"for non [Kaguya Premium]({ConfigProperties.KAGUYA_STORE_URL}) " +
                                                       "servers.");
                    }
                    else
                    {
                        player.Queue.Enqueue(trackSel);
                        await ConsoleLogger.LogAsync($"Enqueued track {trackSel.Title} in guild {context.Guild.Id}.", LogLvl.TRACE);

                        if (player.Track.IsStream)
                        {
                            await player.SkipAsync();

                            await ConsoleLogger.LogAsync($"Skipped livestream to play incoming track in guild {context.Guild.Id}.", LogLvl.TRACE);
                        }
                    }
                }
                else
                {
                    try
                    {
                        await player.PlayAsync(trackSel);

                        await ConsoleLogger.LogAsync($"Playing track {trackSel.Title} in guild {context.Guild.Id}",
                                                     LogLvl.TRACE);
                    }
                    catch (Exception e)
                    {
                        await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " +
                                                     $"{trackSel.Title} in guild {Context.Guild.Id}.\n" +
                                                     $"Exception Message: {e.Message}\n" +
                                                     $"Stack Trace: {e.StackTrace}", LogLvl.WARN);
                    }
                }

                if (player.Volume == 0 && player.PlayerState == PlayerState.Playing)
                {
                    await player.UpdateVolumeAsync(75); // Sets the volume back to default if it is muted.

                    await ConsoleLogger.LogAsync($"Automatically set player volume to 75 in guild {context.Guild.Id}.", LogLvl.TRACE);
                }

                var embed = new KaguyaEmbedBuilder
                {
                    Title        = $"Kaguya Music {Centvrio.Emoji.Music.Notes}",
                    Description  = playString,
                    ThumbnailUrl = await trackSel.FetchArtworkAsync(),
                    Fields       = new List <EmbedFieldBuilder>
                    {
                        field
                    }
                };

                await SendEmbedAsync(embed, context);

                return(null);
            }
            #endregion

            int h = tracks.Count;
            for (int i = 0; i < (h < 7 ? h : 7); i++)
            {
                int       i1       = i;
                LavaTrack trackSel = tracks[i];
                var       field    = new EmbedFieldBuilder
                {
                    Name  = $"Track {i1 + 1}.",
                    Value = $"Title: `{trackSel.Title.Replace("`", "")}`\n" + // We get rid of backticks for formatting.
                            $"Duration: `{trackSel.Duration.Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 3)}`\n" +
                            $"Uploader: `{trackSel.Author}`"
                };

                fields.Add(field);
                callbacks.Add((emojiNums[i], async(c, r) =>
                {
                    string playString = player.PlayerState == PlayerState.Playing && !player.Track.IsStream
                                ? $"Queued track #{i1 + 1} into position {player.Queue.Count + 1}."
                                : $"Now playing track #{i1 + 1}.";

                    if (player.PlayerState == PlayerState.Playing)
                    {
                        if (player.Queue.Count() == 50 && !server.IsPremium)
                        {
                            await ConsoleLogger.LogAsync($"Queue was full in guild {context.Guild.Id}. Sending error message.", LogLvl.TRACE);
                            await SendBasicErrorEmbedAsync("Your queue is full! `50 songs` is the maximum " +
                                                           $"for non [Kaguya Premium]({ConfigProperties.KAGUYA_STORE_URL}) " +
                                                           "servers.");

                            return;
                        }

                        player.Queue.Enqueue(trackSel);
                        await ConsoleLogger.LogAsync($"Enqueued track {trackSel} in guild {context.Guild.Id}", LogLvl.TRACE);

                        if (player.Track.IsStream)
                        {
                            await player.SkipAsync();
                            await ConsoleLogger.LogAsync("Automatically skipped livestream to play" +
                                                         $" incoming track in guild {context.Guild.Id}", LogLvl.TRACE);
                        }
                    }
                    else
                    {
                        try
                        {
                            await player.PlayAsync(trackSel);
                            await ConsoleLogger.LogAsync($"Playing track {trackSel.Title} in guild {context.Guild.Id}",
                                                         LogLvl.TRACE);
                        }
                        catch (Exception e)
                        {
                            await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " +
                                                         $"{trackSel.Title} in guild {Context.Guild.Id}.\n" +
                                                         $"Exception Message: {e.Message}\n" +
                                                         $"Stack Trace: {e.StackTrace}", LogLvl.WARN);
                        }
                    }

                    if (player.Volume == 0 && player.PlayerState == PlayerState.Playing)
                    {
                        await player.UpdateVolumeAsync(75);         // Sets the volume back to default if it is muted.
                        await ConsoleLogger.LogAsync($"Automatically set volume to 75 in guild {context.Guild.Id}", LogLvl.TRACE);
                    }

                    var embed = new KaguyaEmbedBuilder
                    {
                        Title = $"Kaguya Music {Centvrio.Emoji.Music.Notes}",
                        Description = playString,
                        ThumbnailUrl = await trackSel.FetchArtworkAsync(),
                        Fields = new List <EmbedFieldBuilder>
                        {
                            field
                        }
                    };

                    await SendEmbedAsync(embed);
                }
                               ));
            }

            callbacks.Add((GlobalProperties.NoEntryEmoji(), async(c, r) =>
            {
                await c.Message.DeleteAsync();
                await r.Message.Value.DeleteAsync();
            }));

            string s = tracks.Count == 1 ? "" : "s";
            var    songDisplayEmbed = new KaguyaEmbedBuilder
            {
                Title       = "Kaguya Music Search Results",
                Description = $" I found {tracks.Count} track{s} from {provider}, " +
                              $"{(tracks.Count > 5 ? "but here are the top 5" : "here they are")}. " +
                              "Please select a track to play.",
                Fields = fields
            };

            var data = new ReactionCallbackData("", songDisplayEmbed.Build(), false, false,
                                                TimeSpan.FromSeconds(60));

            data.SetCallbacks(callbacks);

            return(data);
        }
    }
示例#20
0
        public async Task HelpCommand()
        {
            var timeoutSeconds = 60;
            var embed          = new EmbedBuilder()
                                 .WithColor(Color.Orange)
                                 .WithTitle("❓ User Guide")
                                 .WithDescription("Here is the list of command modules.\n" +
                                                  "A module is a catergory for a group of related commands.\n" +
                                                  $"Press the corresponding emote or enter `{prefix}help <module>` to view the commands in a module.\n")
                                 .WithFooter($"Press the respective emote to expand the module list (expire in {timeoutSeconds} seconds).");

            var moduleEmoteOrders = commands.Modules
                                    .OrderBy(x => x.Remarks ?? "Module Z")
                                    .Zip(EmojiPresets.Numbers.Skip(1), (x, y) => new KeyValuePair <Emoji, ModuleInfo>(y, x));

            embed.Fields = getOriginalFields();

            IUserMessage msg = default;
            var          reactionCallbacksData = new ReactionCallbackData(string.Empty, embed.Build(), false, false, TimeSpan.FromSeconds(timeoutSeconds), c => onExpire());

            foreach (var item in moduleEmoteOrders)
            {
                reactionCallbacksData = reactionCallbacksData.WithCallback(item.Key, (c, r) =>
                {
                    embed.Description = $"Enter `{prefix}help {prefix}<command>` to view the details of a command";
                    embed.Fields      = getOriginalFields();
                    embed.Fields.Single(x => x.Name.Contains(item.Value.Name)).Value = GetCommnadListFormatted(moduleEmoteOrders.Single(x => x.Key.Name == r.Emote.Name).Value);
                    modifyHelp(c, r.MessageId, embed.Build(), r);
                    return(Task.CompletedTask);
                });
            }
            msg = await InlineReactionReplyAsync(reactionCallbacksData);

            List <EmbedFieldBuilder> getOriginalFields()
            => moduleEmoteOrders.Select(x => new EmbedFieldBuilder()
                                        .WithName($"{x.Key.Name} {x.Value.Name}")
                                        .WithValue(x.Value.Summary ?? "In Development".MarkdownCodeBlock()))
            .ToList();

            async Task onExpire()
            {
                var expireEmbed = Context.Channel.GetMessageAsync(msg.Id).Result.Embeds.First()
                                  .ToEmbedBuilder()
                                  .WithFooter("Emote interactive expired");

                expireEmbed.Description = expireEmbed.Description.Replace("Press the corresponding emote or e", "E");
                _ = msg.RemoveAllReactionsAsync();
                await msg.ModifyAsync(x => x.Embed = expireEmbed.Build());
            }

            void modifyHelp(SocketCommandContext modifyingContext, ulong messageId, Embed expandedEmbed, SocketReaction reaction)
            {
                var reactionMsg = (IUserMessage)modifyingContext.Channel.GetMessageAsync(messageId).Result;

                reactionMsg.RemoveReactionAsync(reaction.Emote, reaction.User.Value);
                reactionMsg.ModifyAsync(x =>
                {
                    x.Embed = expandedEmbed;
                });
            }
        }
示例#21
0
        public async Task <RuntimeResult> Privacy()
        {
            string listToShow = "";

            string[] configList = Locate("PrivacyConfigList").Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);
            for (int i = 0; i < configList.Length; i++)
            {
                listToShow += $"**{i + 1}.** {configList[i]}\n";
            }

            var userConfig = GuildUtils.UserConfigCache.GetValueOrDefault(Context.User.Id, new UserConfig(Context.User.Id));

            string valueList = Locate(userConfig !.IsOptedOutSnipe);

            IUserMessage message = null;

            var builder = new EmbedBuilder()
                          .WithTitle(Locate("PrivacyPolicy"))
                          .AddField(Locate("WhatDataWeCollect"), Locate("WhatDataWeCollectList"))
                          .AddField(Locate("WhenWeCollectData"), Locate("WhenWeCollectDataList"))
                          .AddField(Locate("PrivacyConfig"), Locate("PrivacyConfigInfo"))
                          .AddField(Locate("Option"), listToShow, true)
                          .AddField(Locate("Value"), valueList, true)
                          .WithColor(FergunClient.Config.EmbedColor);

            var data = new ReactionCallbackData(null, builder.Build(), false, false, TimeSpan.FromMinutes(5))
                       .AddCallBack(new Emoji("1️⃣"), async(_, reaction) =>
            {
                userConfig.IsOptedOutSnipe = !userConfig.IsOptedOutSnipe;
                await HandleReactionAsync(reaction);
            })
                       .AddCallBack(new Emoji("❌"), async(_, reaction) =>
            {
                await message.TryDeleteAsync();
            });

            message = await InlineReactionReplyAsync(data);

            return(FergunResult.FromSuccess());

            async Task HandleReactionAsync(SocketReaction reaction)
            {
                FergunClient.Database.InsertOrUpdateDocument(Constants.UserConfigCollection, userConfig);
                GuildUtils.UserConfigCache[Context.User.Id] = userConfig;
                valueList = Locate(userConfig.IsOptedOutSnipe);

                if (reaction.Emote.Name == "1️⃣" && userConfig.IsOptedOutSnipe)
                {
                    var toPurge = FergunClient.MessageCache.Where(p => p.Value.Author.Id == reaction.UserId).ToList();
                    var removed = toPurge.Count(p => FergunClient.MessageCache.TryRemove(p.Key, out _));
                    if (removed > 0)
                    {
                        await _logService.LogAsync(new LogMessage(LogSeverity.Verbose, "Command",
                                                                  $"Privacy: Removed {removed} deleted/edited messages from the cache from user {reaction.UserId}."));
                    }
                }

                builder.Fields[4] = new EmbedFieldBuilder {
                    Name = Locate("Value"), Value = valueList, IsInline = true
                };
                _ = message !.RemoveReactionAsync(reaction.Emote, reaction.UserId);
                await message.ModifyAsync(x => x.Embed = builder.Build());
            }
        }