Exemple #1
0
        private static string FormatEmojiReaction(DiscordClient client, EmojiReaction er)
        {
            string emoji;

            try {
                emoji = DiscordEmoji.FromName(client, er.Response);
            } catch (ArgumentException) {
                emoji = "404";
            }

            return($"{Formatter.InlineCode($"{er.Id:D4}")} | {emoji} | {Formatter.InlineCode(er.Triggers.JoinWith(", "))}");
        }
        public static async Task MessageEmojiReactionEventHandlerAsync(KioskAppShard shard, MessageCreateEventArgs e)
        {
            if (e.Author.IsBot || e.Channel.IsPrivate || string.IsNullOrWhiteSpace(e.Message?.Content))
            {
                return;
            }

            if (shard.SharedData.BlockedChannels.Contains(e.Channel.Id) || shard.SharedData.BlockedUsers.Contains(e.Author.Id))
            {
                return;
            }

            if (!e.Channel.PermissionsFor(e.Guild.CurrentMember).HasFlag(Permissions.AddReactions))
            {
                return;
            }

            if (!shard.SharedData.EmojiReactions.TryGetValue(e.Guild.Id, out ConcurrentHashSet <EmojiReaction> ereactions))
            {
                return;
            }

            EmojiReaction ereaction = ereactions?
                                      .Where(er => er.IsMatch(e.Message?.Content ?? ""))
                                      .Shuffle()
                                      .FirstOrDefault();

            if (!(ereaction is null))
            {
                try
                {
                    var emoji = DiscordEmoji.FromName(shard.Client, ereaction.Response);
                    await e.Message.CreateReactionAsync(emoji);
                }
                catch (ArgumentException)
                {
                    using (DatabaseContext db = shard.Database.CreateContext())
                    {
                        db.EmojiReactions.RemoveRange(db.EmojiReactions.Where(er => er.GuildId == e.Guild.Id && er.Reaction == ereaction.Response));
                        await db.SaveChangesAsync();
                    }
                }
            }
        }
Exemple #3
0
        public static async Task <IReadOnlyDictionary <ulong, List <EmojiReaction> > > GetEmojiReactionsForAllGuildsAsync(this DBService db)
        {
            var ereactions = new Dictionary <ulong, List <EmojiReaction> >();

            await db.ExecuteCommandAsync(async (cmd) => {
                cmd.CommandText = "SELECT id, gid, trigger, reaction FROM gf.emoji_reactions;";

                using (var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false)) {
                    while (await reader.ReadAsync().ConfigureAwait(false))
                    {
                        ulong gid = (ulong)(long)reader["gid"];

                        if (ereactions.ContainsKey(gid))
                        {
                            if (ereactions[gid] == null)
                            {
                                ereactions[gid] = new List <EmojiReaction>();
                            }
                        }
                        else
                        {
                            ereactions.Add(gid, new List <EmojiReaction>());
                        }

                        int id         = (int)reader["id"];
                        string trigger = (string)reader["trigger"];
                        string emoji   = (string)reader["reaction"];

                        EmojiReaction conflict = ereactions[gid].FirstOrDefault(tr => tr.Response == emoji);
                        if (conflict != null)
                        {
                            conflict.AddTrigger(trigger, isRegex: true);
                        }
                        else
                        {
                            ereactions[gid].Add(new EmojiReaction(id, trigger, emoji, isRegex: true));
                        }
                    }
                }
            });

            return(new ReadOnlyDictionary <ulong, List <EmojiReaction> >(ereactions));
        }
        public static async Task MessageEmojiReactionEventHandlerAsync(TheGodfatherShard shard, MessageCreateEventArgs e)
        {
            if (e.Author.IsBot || e.Channel.IsPrivate || string.IsNullOrWhiteSpace(e.Message?.Content))
            {
                return;
            }

            if (shard.SharedData.BlockedChannels.Contains(e.Channel.Id) || shard.SharedData.BlockedUsers.Contains(e.Author.Id))
            {
                return;
            }

            if (!e.Channel.PermissionsFor(e.Guild.CurrentMember).HasFlag(Permissions.AddReactions))
            {
                return;
            }

            if (!shard.SharedData.EmojiReactions.ContainsKey(e.Guild.Id))
            {
                return;
            }

            EmojiReaction ereaction = shard.SharedData.EmojiReactions[e.Guild.Id]
                                      .Where(er => er.IsMatch(e.Message?.Content ?? ""))
                                      .Shuffle()
                                      .FirstOrDefault();

            if (ereaction != null)
            {
                try {
                    var emoji = DiscordEmoji.FromName(shard.Client.Client, ereaction.Response);
                    await e.Message.CreateReactionAsync(emoji);
                } catch (ArgumentException) {
                    await shard.DatabaseService.RemoveAllTriggersForEmojiReactionAsync(e.Guild.Id, ereaction.Response);
                }
            }
        }
Exemple #5
0
        private async Task AddEmojiReactionAsync(CommandContext ctx, DiscordEmoji emoji, bool regex, params string[] triggers)
        {
            if (triggers == null)
            {
                throw new InvalidCommandUsageException("Missing trigger words!");
            }

            var eb = new StringBuilder();

            foreach (string trigger in triggers.Select(t => t.ToLowerInvariant()))
            {
                if (trigger.Length > 120)
                {
                    eb.AppendLine($"Error: Trigger {Formatter.Bold(trigger)} is too long (120 chars max).");
                    continue;
                }

                if (!this.Shared.EmojiReactions.ContainsKey(ctx.Guild.Id))
                {
                    if (!this.Shared.EmojiReactions.TryAdd(ctx.Guild.Id, new ConcurrentHashSet <EmojiReaction>()))
                    {
                        throw new ConcurrentOperationException("Failed to create emoji reaction data structure");
                    }
                }

                if (regex && !trigger.IsValidRegex())
                {
                    eb.AppendLine($"Error: Trigger {Formatter.Bold(trigger)} is not a valid regular expression.");
                    continue;
                }

                string ename = emoji.GetDiscordName();

                if (this.Shared.EmojiReactions[ctx.Guild.Id].Where(er => er.ContainsTriggerPattern(trigger)).Any(er => er.Response == ename))
                {
                    eb.AppendLine($"Error: Trigger {Formatter.Bold(trigger)} already exists for this emoji.");
                    continue;
                }

                int id = 0;
                try {
                    id = await this.Database.AddEmojiReactionAsync(ctx.Guild.Id, trigger, ename, regex : regex);
                } catch (Exception e) {
                    this.Shared.LogProvider.LogException(LogLevel.Warning, e);
                    eb.AppendLine($"Warning: Failed to add trigger {Formatter.Bold(trigger)} to the database.");
                }

                EmojiReaction reaction = this.Shared.EmojiReactions[ctx.Guild.Id].FirstOrDefault(tr => tr.Response == ename);
                if (reaction != null)
                {
                    if (!reaction.AddTrigger(trigger, isRegex: regex))
                    {
                        throw new CommandFailedException($"Failed to add trigger {Formatter.Bold(trigger)}.");
                    }
                }
                else
                {
                    if (!this.Shared.EmojiReactions[ctx.Guild.Id].Add(new EmojiReaction(id, trigger, ename, isRegex: regex)))
                    {
                        throw new CommandFailedException($"Failed to add trigger {Formatter.Bold(trigger)}.");
                    }
                }
            }

            DiscordChannel logchn = this.Shared.GetLogChannelForGuild((DiscordClientImpl)ctx.Client, ctx.Guild);

            if (logchn != null)
            {
                var emb = new DiscordEmbedBuilder()
                {
                    Title = "New emoji reactions added",
                    Color = this.ModuleColor
                };
                emb.AddField("User responsible", ctx.User.Mention, inline: true);
                emb.AddField("Invoked in", ctx.Channel.Mention, inline: true);
                emb.AddField("Reaction", emoji, inline: true);
                emb.AddField("Triggers", string.Join("\n", triggers));
                if (eb.Length > 0)
                {
                    emb.AddField("With errors", eb.ToString());
                }
                await logchn.SendMessageAsync(embed : emb.Build());
            }

            if (eb.Length > 0)
            {
                await this.InformFailureAsync(ctx, $"Action finished with following warnings/errors:\n\n{eb.ToString()}");
            }
            else
            {
                await this.InformAsync(ctx, "Successfully added all given emoji reactions.", important : false);
            }
        }
Exemple #6
0
        private async Task AddEmojiReactionAsync(CommandContext ctx, DiscordEmoji emoji, bool regex, params string[] triggers)
        {
            if (emoji is DiscordGuildEmoji && !ctx.Guild.Emojis.Values.Contains(emoji))
            {
                throw new CommandFailedException("The reaction has to be an emoji from this guild.");
            }

            if (triggers is null || !triggers.Any())
            {
                throw new InvalidCommandUsageException("Missing trigger words!");
            }

            if (!this.Shared.EmojiReactions.ContainsKey(ctx.Guild.Id))
            {
                if (!this.Shared.EmojiReactions.TryAdd(ctx.Guild.Id, new ConcurrentHashSet <EmojiReaction>()))
                {
                    throw new ConcurrentOperationException("Failed to create emoji reaction data structure");
                }
            }

            int id;

            using (DatabaseContext db = this.Database.CreateContext()) {
                DatabaseEmojiReaction dber = db.EmojiReactions.FirstOrDefault(er => er.GuildId == ctx.Guild.Id && er.Reaction == emoji.GetDiscordName());
                if (dber is null)
                {
                    dber = new DatabaseEmojiReaction {
                        GuildId  = ctx.Guild.Id,
                        Reaction = emoji.GetDiscordName()
                    };
                    db.EmojiReactions.Add(dber);
                    await db.SaveChangesAsync();
                }

                foreach (string trigger in triggers)
                {
                    dber.DbTriggers.Add(new DatabaseEmojiReactionTrigger {
                        ReactionId = dber.Id, Trigger = regex ? trigger : Regex.Escape(trigger)
                    });
                }

                await db.SaveChangesAsync();

                id = dber.Id;
            }

            var eb = new StringBuilder();

            foreach (string trigger in triggers.Select(t => t.ToLowerInvariant()))
            {
                if (trigger.Length > 120)
                {
                    eb.AppendLine($"Error: Trigger {Formatter.Bold(trigger)} is too long (120 chars max).");
                    continue;
                }

                if (regex && !trigger.IsValidRegex())
                {
                    eb.AppendLine($"Error: Trigger {Formatter.Bold(trigger)} is not a valid regular expression.");
                    continue;
                }

                ConcurrentHashSet <EmojiReaction> ereactions = this.Shared.EmojiReactions[ctx.Guild.Id];

                string ename = emoji.GetDiscordName();
                if (ereactions.Where(er => er.ContainsTriggerPattern(trigger)).Any(er => er.Response == ename))
                {
                    eb.AppendLine($"Error: Trigger {Formatter.Bold(trigger)} already exists for this emoji.");
                    continue;
                }

                EmojiReaction reaction = ereactions.FirstOrDefault(tr => tr.Response == ename);
                if (reaction is null)
                {
                    if (!ereactions.Add(new EmojiReaction(id, trigger, ename, isRegex: regex)))
                    {
                        throw new CommandFailedException($"Failed to add trigger {Formatter.Bold(trigger)}.");
                    }
                }
                else
                {
                    if (!reaction.AddTrigger(trigger, isRegex: regex))
                    {
                        throw new CommandFailedException($"Failed to add trigger {Formatter.Bold(trigger)}.");
                    }
                }
            }

            DiscordChannel logchn = this.Shared.GetLogChannelForGuild(ctx.Client, ctx.Guild);

            if (!(logchn is null))
            {
                var emb = new DiscordEmbedBuilder {
                    Title = "New emoji reactions added",
                    Color = this.ModuleColor
                };
                emb.AddField("User responsible", ctx.User.Mention, inline: true);
                emb.AddField("Invoked in", ctx.Channel.Mention, inline: true);
                emb.AddField("Reaction", emoji, inline: true);
                emb.AddField("Triggers", string.Join("\n", triggers));
                if (eb.Length > 0)
                {
                    emb.AddField("With errors", eb.ToString());
                }
                await logchn.SendMessageAsync(embed : emb.Build());
            }

            if (eb.Length > 0)
            {
                await this.InformFailureAsync(ctx, $"Action finished with following warnings/errors:\n\n{eb.ToString()}");
            }
            else
            {
                await this.InformAsync(ctx, "Successfully added all given emoji reactions.", important : false);
            }
        }
        public async Task RemoveEmojiReactionTriggersTests()
        {
            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                int removed = await this.Service.RemoveEmojiReactionTriggersAsync(
                    MockData.Ids[0],
                    ers.Where(er => er.Response == Emojis.Cloud.GetDiscordName()),
                    new[] { "cde" });
                Assert.That(removed, Is.Zero);
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value)).Items);
                EmojiReaction dber = db.EmojiReactions
                                     .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                                     .AsEnumerable()
                                     .Single(er => er.Response == Emojis.Cloud.GetDiscordName());
                Assert.That(dber.DbTriggers.Single().Trigger, Is.EqualTo("abc"));
                Assert.That(this.Service.GetGuildEmojiReactions(MockData.Ids[0]), Has.Exactly(5).Items);
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                int removed = await this.Service.RemoveEmojiReactionTriggersAsync(
                    MockData.Ids[0],
                    ers.Where(er => er.Response == Emojis.Joystick.GetDiscordName()),
                    new[] { "not" });
                Assert.That(removed, Is.EqualTo(1));
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value) - 1).Items);
                Assert.That(this.Service.GetGuildEmojiReactions(MockData.Ids[0]), Has.Exactly(4).Items);
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                int removed = await this.Service.RemoveEmojiReactionTriggersAsync(
                    MockData.Ids[0],
                    ers.Where(er => er.Response == Emojis.Joystick.GetDiscordName()),
                    new[] { "NO MATCHES" });
                Assert.That(removed, Is.Zero);
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value)).Items);
                Assert.That(this.Service.GetGuildEmojiReactions(MockData.Ids[0]), Has.Exactly(this.erCount[0]).Items);
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[1]);
                int removed = await this.Service.RemoveEmojiReactionTriggersAsync(
                    MockData.Ids[1],
                    ers,
                    new[] { "abc" });
                Assert.That(removed, Is.EqualTo(3));
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value) - 3).Items);
                Assert.That(this.Service.GetGuildEmojiReactions(MockData.Ids[0]), Has.Exactly(this.erCount[0]).Items);
                Assert.That(this.Service.GetGuildEmojiReactions(MockData.Ids[1]), Has.Exactly(this.erCount[1] - 3).Items);
                return(Task.CompletedTask);
            }
                );
        }
        public async Task AddEmojiReactionTests()
        {
            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "test" }, false),
                    Is.EqualTo(1)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value) + 1).Items);
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers, Has.Exactly(this.erCount[0] + 1).Items);
                Assert.That(ers.Select(er => er.Id), Is.Unique);
                IEnumerable <EmojiReaction> x = db.EmojiReactions
                                                .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                                                .Include(er => er.DbTriggers)
                                                .AsEnumerable();
                Assert.That(
                    x.Single(er => er.Response == Emojis.Information.GetDiscordName() && er.DbTriggers.Single().Trigger == "test"),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.AlterAndVerifyAsync(
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "test" }, false),
                    Is.EqualTo(1)
                    );
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[1], Emojis.Information.GetDiscordName(), new[] { "testing" }, false),
                    Is.EqualTo(1)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(2).Items);
                IReadOnlyCollection <EmojiReaction> ers0 = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers0, Has.Exactly(1).Items);
                Assert.That(ers0.First().Triggers, Has.Exactly(1).Items);
                Assert.That(ers0.First().IsMatch("This is a tEst"));
                IReadOnlyCollection <EmojiReaction> ers1 = this.Service.GetGuildEmojiReactions(MockData.Ids[1]);
                Assert.That(ers1, Has.Exactly(1).Items);
                Assert.That(ers1.First().Triggers, Has.Exactly(1).Items);
                Assert.That(ers1.First().IsMatch("This is another -teSting example."));

                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Information.GetDiscordName() &&
                            er.DbTriggers.Single().Trigger == "test"),
                    Is.Not.Null
                    );
                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[1])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Information.GetDiscordName() &&
                            er.DbTriggers.Single().Trigger == "testing"),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.AlterAndVerifyAsync(
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "test" }, false),
                    Is.EqualTo(1)
                    );
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.AlarmClock.GetDiscordName(), new[] { "regex(es)? (much)+" }, false),
                    Is.EqualTo(1)
                    );
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "testing" }, false),
                    Is.EqualTo(1)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(2).Items);
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers, Has.Exactly(2).Items);
                EmojiReaction info = ers.Single(e => e.Response == Emojis.Information.GetDiscordName());
                Assert.That(info.Triggers, Has.Exactly(2).Items);
                Assert.That(info.IsMatch("This is a tEst."));
                Assert.That(info.IsMatch("This is a -tEsting."));
                Assert.That(info.IsMatch("This is an alarm"), Is.False);
                Assert.That(info.IsMatch("This is a protEsting."), Is.False);
                Assert.That(ers.Any(e => e.IsMatch("here regex(es)? (much)+ will match because this is literal string interpretation")));

                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Information.GetDiscordName() &&
                            er.DbTriggers.Count == 2
                            ),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.AlterAndVerifyAsync(
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "test(ing)? regex(es)?" }, true),
                    Is.EqualTo(1)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(1).Items);
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers, Has.Exactly(1).Items);
                EmojiReaction info = ers.Single(e => e.Response == Emojis.Information.GetDiscordName());
                Assert.That(info.Triggers, Has.Exactly(1).Items);
                Assert.That(info.IsMatch("This is a tEsting regexes example which passes"));
                Assert.That(info.IsMatch("This is another tEst regex example which passes"));
                Assert.That(info.IsMatch("This is another TEST rEGex example which passes"));
                Assert.That(info.IsMatch("This is a protesting regexes example which should not pass due to wb check"), Is.False);
                Assert.That(info.IsMatch("This is a tEst which wont pass"), Is.False);
                Assert.That(info.IsMatch("This is a literal test(ing)? regex(es)? string which wont pass"), Is.False);

                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Information.GetDiscordName() &&
                            er.DbTriggers.Count == 1
                            ),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "test(ing)? regex(es)?" }, true),
                    Is.EqualTo(1)
                    );
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Information.GetDiscordName(), new[] { "another test" }, false),
                    Is.EqualTo(1)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value) + 1).Items);
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers, Has.Exactly(this.erCount[0] + 1).Items);
                EmojiReaction info = ers.Single(e => e.Response == Emojis.Information.GetDiscordName());
                Assert.That(info.Triggers, Has.Exactly(2).Items);
                Assert.That(info.IsMatch("This is a tEsting regexes example which passes"));
                Assert.That(info.IsMatch("This is another tEst example which passes"));
                Assert.That(info.IsMatch("This is another tEst regex example which passes"));
                Assert.That(info.IsMatch("This is a another protesting regexes example which should not pass due to wb check"), Is.False);
                Assert.That(info.IsMatch("This is a tEst which wont pass"), Is.False);
                Assert.That(info.IsMatch("This is a literal test(ing)? regex(es)? string which wont pass"), Is.False);

                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Information.GetDiscordName() &&
                            er.DbTriggers.Count == 2
                            ),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(
                        MockData.Ids[0],
                        Emojis.Information.GetDiscordName(),
                        new[] { "test(ing)? regex(es)?", "another test" },
                        true
                        ),
                    Is.EqualTo(2)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value) + 1).Items);
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers, Has.Exactly(this.erCount[0] + 1).Items);
                EmojiReaction info = ers.Single(e => e.Response == Emojis.Information.GetDiscordName());
                Assert.That(info.Triggers, Has.Exactly(2).Items);
                Assert.That(info.IsMatch("This is a tEsting regexes example which passes"));
                Assert.That(info.IsMatch("This is another tEst regex example which passes"));
                Assert.That(info.IsMatch("This is a tEst which wont pass"), Is.False);
                Assert.That(info.IsMatch("This is a another protesting regexes example which should not pass due to wb check"), Is.False);
                Assert.That(info.IsMatch("This is a literal test(ing)? regex(es)? string which wont pass"), Is.False);

                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Information.GetDiscordName() &&
                            er.DbTriggers.Count == 2
                            ),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.SetupAlterAndVerifyAsync(
                setup : db => {
                this.AddMockReactions(db);
                return(Task.CompletedTask);
            },
                alter : async db => {
                this.UpdateEmojiReactionCount(db);
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Chicken.GetDiscordName(), new[] { "test(ing)? regex(es)?" }, true),
                    Is.EqualTo(1)
                    );
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Chicken.GetDiscordName(), new[] { "another test" }, true),
                    Is.EqualTo(1)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(this.erCount.Sum(kvp => kvp.Value)).Items);
                IReadOnlyCollection <EmojiReaction> ers = this.Service.GetGuildEmojiReactions(MockData.Ids[0]);
                Assert.That(ers, Has.Exactly(this.erCount[0]).Items);
                EmojiReaction info = ers.Single(e => e.Response == Emojis.Chicken.GetDiscordName());
                Assert.That(info.Triggers, Has.Exactly(3).Items);
                Assert.That(info.IsMatch("This is old abc abc test which passes"));
                Assert.That(info.IsMatch("This is a tEsting regexes example which passes"));
                Assert.That(info.IsMatch("This is another tEst regex example which passes"));
                Assert.That(info.IsMatch("This is a tEst which wont pass"), Is.False);
                Assert.That(info.IsMatch("This is a another protesting regexes example which should not pass due to wb check"), Is.False);
                Assert.That(info.IsMatch("This is a literal test(ing)? regex(es)? string which wont pass"), Is.False);

                Assert.That(
                    db.EmojiReactions
                    .Where(er => er.GuildIdDb == (long)MockData.Ids[0])
                    .AsEnumerable()
                    .Single(er => er.Response == Emojis.Chicken.GetDiscordName() &&
                            er.DbTriggers.Count == 3
                            ),
                    Is.Not.Null
                    );
                return(Task.CompletedTask);
            }
                );

            await TestDbProvider.AlterAndVerifyAsync(
                alter : async db => {
                this.Service.LoadData();
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Chicken.GetDiscordName(), new[] { "test(ing)? regex(es)?" }, true),
                    Is.EqualTo(1)
                    );
                Assert.That(
                    await this.Service.AddEmojiReactionAsync(MockData.Ids[0], Emojis.Chicken.GetDiscordName(), new[] { "test(ing)? regex(es)?" }, false),
                    Is.EqualTo(0)
                    );
            },
                verify : db => {
                Assert.That(db.EmojiReactions, Has.Exactly(1).Items);
                Assert.That(this.Service.GetGuildEmojiReactions(MockData.Ids[0]), Has.Exactly(1).Items);
                return(Task.CompletedTask);
            }
                );
        }