From c63d188289eb961a99ae7a52c7c8e09eae488aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=E2=96=88=E2=96=88=E2=96=88=E2=96=88=E2=96=88?= Date: Tue, 8 Apr 2025 10:57:43 -0400 Subject: [PATCH] feat: New bot rules --- .../ChatRules/BestCheapViewersTests.cs | 2 ++ .../ChatRules/BotsisterTests.cs | 28 +++++++++++++++ .../ChatRules/DiscordTests.cs | 1 + .../ChatRules/Botsister.cs | 36 +++++++++++++++++++ .../ChatRules/Discord.cs | 3 +- 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/Nullinside.Api.TwitchBot.Tests/ChatRules/BotsisterTests.cs create mode 100644 src/Nullinside.Api.TwitchBot/ChatRules/Botsister.cs diff --git a/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BestCheapViewersTests.cs b/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BestCheapViewersTests.cs index 7799a91..31af63b 100644 --- a/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BestCheapViewersTests.cs +++ b/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BestCheapViewersTests.cs @@ -15,6 +15,8 @@ public class BestCheapViewersTests : AChatRuleUnitTestBase { /// /// The string that should fail. [Test] + [TestCase("B\u0366est vie\u031fwers on streamboo .com ( remove the space ) @IBjZQcD1")] + [TestCase("Cheap \u0314Viewers on streamboo .com ( remove the space ) @kX9lOgg1")] [TestCase("B͐est vi̯ewers o͎n on streamboo .com ( remove the space ) @9F3Wnft0")] [TestCase("Ch̚eͅap viewers on *** @STGPMoLg")] [TestCase("C̭heap viewe͌rs on̆ *** @R1QXrXPM")] diff --git a/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BotsisterTests.cs b/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BotsisterTests.cs new file mode 100644 index 0000000..2a1d3ad --- /dev/null +++ b/src/Nullinside.Api.TwitchBot.Tests/ChatRules/BotsisterTests.cs @@ -0,0 +1,28 @@ +using Moq; + +using Nullinside.Api.Common.Twitch; +using Nullinside.Api.TwitchBot.ChatRules; +using Nullinside.Api.TwitchBot.Model; + +namespace Nullinside.Api.TwitchBot.Tests.ChatRules; + +/// +/// Tests the class. +/// +public class BotsisterTests : AChatRuleUnitTestBase { + /// + /// Tests the strings typed in chats. + /// + /// The string that should fail. + [Test] + [TestCase("Your chat’s so empty, it’s giving ‘no signal’ vibes. Fix it with botsister .com @uFi5gGov")] + public async Task TestKnownStrings(string badString) { + var rule = new Botsister(); + var botProxy = new Mock(); + var chat = new TwitchChatMessage(true, badString, "123", "456"); + + // Process the message and assert that we fail the message. + bool result = await rule.Handle("123", botProxy.Object, chat, _db); + Assert.That(result, Is.False); + } +} \ No newline at end of file diff --git a/src/Nullinside.Api.TwitchBot.Tests/ChatRules/DiscordTests.cs b/src/Nullinside.Api.TwitchBot.Tests/ChatRules/DiscordTests.cs index 6c99848..c11e31f 100644 --- a/src/Nullinside.Api.TwitchBot.Tests/ChatRules/DiscordTests.cs +++ b/src/Nullinside.Api.TwitchBot.Tests/ChatRules/DiscordTests.cs @@ -15,6 +15,7 @@ public class DiscordTests : AChatRuleUnitTestBase { /// /// The string that should fail. [Test] + [TestCase("Yo bruh \u2764\ufe0f let's sometimes play together and share tips add up on discord \ud83d\udc49 willam0340")] [TestCase("Hello mate you stream pretty cool that's why I follow I would love to be a fan of yours if you don't mind kindly add me on Discord: 👉👉lacaster5")] [TestCase("Hello, I just recently found your channel and can already tell that your content is great, and I would love to stick with you long term. If you're open to it, I'd be willing to connect with you on Discord. My username is teecash1000")] [TestCase("Hey there 👋 You stream pretty cool that’s why I followed you. I will like to make you a friend and be a fan, if you don’t mind Kindly chat me on Discord, my Discord username is tuckzay")] diff --git a/src/Nullinside.Api.TwitchBot/ChatRules/Botsister.cs b/src/Nullinside.Api.TwitchBot/ChatRules/Botsister.cs new file mode 100644 index 0000000..ff6cd31 --- /dev/null +++ b/src/Nullinside.Api.TwitchBot/ChatRules/Botsister.cs @@ -0,0 +1,36 @@ +using Nullinside.Api.Common.Twitch; +using Nullinside.Api.Model; +using Nullinside.Api.TwitchBot.Model; + +using TwitchLib.Client.Models; + +using TwitchUserConfig = Nullinside.Api.Model.Ddl.TwitchUserConfig; + +namespace Nullinside.Api.TwitchBot.ChatRules; + +/// +/// Handles "botsister" spam. +/// +public class Botsister : AChatRule { + /// + public override bool ShouldRun(TwitchUserConfig config) { + return config is { Enabled: true, BanKnownBots: true }; + } + + /// + public override async Task Handle(string channelId, ITwitchApiProxy botProxy, TwitchChatMessage message, + INullinsideContext db, CancellationToken stoppingToken = new()) { + // The number of spaces per message may chance, so normalize that and lowercase it for comparison. + string normalized = string.Concat(message.Message.Split(" ").Where(s => !string.IsNullOrWhiteSpace(s))) + .ToLowerInvariant(); + + // Message will start with any of these variations. + if (message.IsFirstMessage && normalized.Contains("botsister")) { + await BanAndLog(channelId, botProxy, new[] { (message.UserId, message.Username) }, + "[Bot] Spam (Botsister)", db, stoppingToken); + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/Nullinside.Api.TwitchBot/ChatRules/Discord.cs b/src/Nullinside.Api.TwitchBot/ChatRules/Discord.cs index ca037d1..e3dbf0f 100644 --- a/src/Nullinside.Api.TwitchBot/ChatRules/Discord.cs +++ b/src/Nullinside.Api.TwitchBot/ChatRules/Discord.cs @@ -18,7 +18,8 @@ public class Discord : AChatRule { "chat with me", "my discord username", "my username", - "connect on discord" + "connect on discord", + "add up" ]; ///