Skip to content

Commit a5b400b

Browse files
Updating logic for new cheap viewers bots
1 parent 16a94f5 commit a5b400b

File tree

6 files changed

+98
-12
lines changed

6 files changed

+98
-12
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace Nullinside.Api.TwitchBot.Tests.ChatRules;
2+
3+
public class BestCheapViewersTests
4+
{
5+
[Test]
6+
[TestCase("Best viewers on ***")]
7+
[TestCase("Best viewers on ***")]
8+
[TestCase("Best vie̮wers on ***")]
9+
[TestCase("Best́ viewers on ***")]
10+
[TestCase("Be̩st Viewers on ***")]
11+
[TestCase("Be̾st Viewers on ***")]
12+
[TestCase("B͟est Viewers on ***")]
13+
[TestCase("B̟est viewers on ***")]
14+
[TestCase("Cheap viewers on ***")]
15+
[TestCase("Che̢ap vie̮wers on ***")]
16+
[TestCase("Ch̍eap Viewers on ***")]
17+
[TestCase("Ch͟eap viewers on ***")]
18+
[TestCase("C̀heap Viewers on ***")]
19+
[TestCase("Cheaͧp v̫iewers on ***")]
20+
[TestCase("Cheaͧp v̫iewers on *** ")]
21+
public void TestKnownStrings(string badString)
22+
{
23+
// Need to put interfaces in front of the classes before we can do this.
24+
Assert.Pass();
25+
}
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
15+
<PackageReference Include="NUnit" Version="3.14.0"/>
16+
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
17+
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<Using Include="NUnit.Framework"/>
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\Nullinside.Api.TwitchBot\Nullinside.Api.TwitchBot.csproj" />
26+
</ItemGroup>
27+
28+
</Project>

src/Nullinside.Api.TwitchBot.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nullinside.Api.Model", "nul
2020
EndProject
2121
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nullinside.Api", "nullinside-api\src\Nullinside.Api\Nullinside.Api.csproj", "{B82842B1-7FDA-4F93-B31E-07696A687381}"
2222
EndProject
23+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nullinside.Api.TwitchBot.Tests", "Nullinside.Api.TwitchBot.Tests\Nullinside.Api.TwitchBot.Tests.csproj", "{2CBAC21E-256B-493E-9461-7C779B137926}"
24+
EndProject
2325
Global
2426
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2527
Debug|Any CPU = Debug|Any CPU
@@ -46,5 +48,9 @@ Global
4648
{B82842B1-7FDA-4F93-B31E-07696A687381}.Debug|Any CPU.Build.0 = Debug|Any CPU
4749
{B82842B1-7FDA-4F93-B31E-07696A687381}.Release|Any CPU.ActiveCfg = Release|Any CPU
4850
{B82842B1-7FDA-4F93-B31E-07696A687381}.Release|Any CPU.Build.0 = Release|Any CPU
51+
{2CBAC21E-256B-493E-9461-7C779B137926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52+
{2CBAC21E-256B-493E-9461-7C779B137926}.Debug|Any CPU.Build.0 = Debug|Any CPU
53+
{2CBAC21E-256B-493E-9461-7C779B137926}.Release|Any CPU.ActiveCfg = Release|Any CPU
54+
{2CBAC21E-256B-493E-9461-7C779B137926}.Release|Any CPU.Build.0 = Release|Any CPU
4955
EndGlobalSection
5056
EndGlobal

src/Nullinside.Api.TwitchBot/ChatRules/BestCheapViewers.cs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ namespace Nullinside.Api.TwitchBot.ChatRules;
1010
/// Handles "cheap viewers" spam.
1111
/// </summary>
1212
public class BestCheapViewers : AChatRule {
13+
/// <summary>
14+
/// The strings that we expect to receive if this is a bot.
15+
/// </summary>
16+
public readonly string[] EXPECTED = ["best viewers on", "cheap viewers on"];
17+
1318
/// <inheritdoc />
1419
public override bool ShouldRun(TwitchUserConfig config) {
1520
return config is { Enabled: true, BanKnownBots: true };
@@ -22,13 +27,34 @@ public override async Task<bool> Handle(string channelId, TwitchApiProxy botProx
2227
string normalized = string.Join(' ', message.Message.Split(" ").Where(s => !string.IsNullOrWhiteSpace(s)))
2328
.ToLowerInvariant();
2429

25-
// Message will start with any of these variations.
26-
if (normalized.StartsWith("cheap viewers on") ||
27-
normalized.StartsWith("best and cheap viewers on") ||
28-
normalized.StartsWith("best viewers on")) {
29-
await BanAndLog(channelId, botProxy, new[] { (message.UserId, message.Username) },
30-
"[Bot] Spam (Best Cheap Viewers)", db, stoppingToken);
31-
return false;
30+
// Messages will be one of two variations with random special characters mixed in. Some of those special characters
31+
// will be accent marks. When we receive an accent mark it'll take the position of a real character, hence why we
32+
// need an offset applied only to the incoming string.
33+
foreach (var expected in EXPECTED) {
34+
if (normalized.Length > expected.Length) {
35+
int matches = 0;
36+
int offset = 0;
37+
for (int i = 0; i < expected.Length; i++) {
38+
// If this is a normal character it should be in the correct position.
39+
if (normalized[i + offset] == expected[i]) {
40+
++matches;
41+
}
42+
// If this is an accent mark then the next character should match and the whole string we're evalutating
43+
// will be off by 1 more position.
44+
else if (i + offset + 1 < normalized.Length && normalized[i + offset + 1] == expected[i]) {
45+
++matches;
46+
++offset;
47+
}
48+
}
49+
50+
// If everything matches except 3 characters, take it. We will assume the 3 characters are "special" characters
51+
// used to confuse us.
52+
if (matches > expected.Length - 3) {
53+
await BanAndLog(channelId, botProxy, new[] { (message.UserId, message.Username) },
54+
"[Bot] Spam (Best Cheap Viewers)", db, stoppingToken);
55+
return false;
56+
}
57+
}
3258
}
3359

3460
return true;

src/Nullinside.Api.TwitchBot/Nullinside.Api.TwitchBot.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222

2323
<ItemGroup>
2424
<PackageReference Include="log4net.Ext.Json" Version="2.0.10.1"/>
25-
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.3"/>
26-
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
25+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
26+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.5">
2727
<PrivateAssets>all</PrivateAssets>
2828
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2929
</PackageReference>
3030
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="8.0.0"/>
31-
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.0"/>
32-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
31+
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.5" />
32+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
3333
</ItemGroup>
3434

3535
<ItemGroup>

0 commit comments

Comments
 (0)