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(); } } } }
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); } } }
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); } }
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); } ); }