Add support for has:invite filter that matches messages with guild invites (#1188)

This commit is contained in:
Oleksii Holub 2024-01-18 20:24:25 +02:00 committed by GitHub
parent 4e69ff317b
commit d8e43d89be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 56 additions and 15 deletions

View file

@ -37,7 +37,7 @@ public class FilterSpecs
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
.Should()
.ContainSingle("Some random text");
.AllBe("Some random text");
}
[Fact]
@ -66,7 +66,7 @@ public class FilterSpecs
}
[Fact]
public async Task I_can_filter_the_export_to_only_include_messages_that_contain_the_specified_content()
public async Task I_can_filter_the_export_to_only_include_messages_that_contain_images()
{
// Arrange
using var file = TempFile.Create();
@ -87,7 +87,32 @@ public class FilterSpecs
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
.Should()
.ContainSingle("This has image");
.AllBe("This has image");
}
[Fact]
public async Task I_can_filter_the_export_to_only_include_messages_that_contain_guild_invites()
{
// Arrange
using var file = TempFile.Create();
// Act
await new ExportChannelsCommand
{
Token = Secrets.DiscordToken,
ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json,
OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("has:invite")
}.ExecuteAsync(new FakeConsole());
// Assert
Json.Parse(await File.ReadAllTextAsync(file.Path))
.GetProperty("messages")
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
.Should()
.AllBe("This has invite");
}
[Fact]
@ -112,7 +137,7 @@ public class FilterSpecs
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
.Should()
.ContainSingle("This is pinned");
.AllBe("This is pinned");
}
[Fact]
@ -137,6 +162,6 @@ public class FilterSpecs
.EnumerateArray()
.Select(j => j.GetProperty("content").GetString())
.Should()
.ContainSingle("This has mention");
.AllBe("This has mention");
}
}

View file

@ -1,7 +1,7 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using DiscordChatExporter.Core.Discord.Data;
using DiscordChatExporter.Core.Markdown.Parsing;
namespace DiscordChatExporter.Core.Exporting.Filtering;
@ -10,14 +10,19 @@ internal class HasMessageFilter(MessageContentMatchKind kind) : MessageFilter
public override bool IsMatch(Message message) =>
kind switch
{
MessageContentMatchKind.Link
=> Regex.IsMatch(message.Content, "https?://\\S*[^\\.,:;\"\'\\s]"),
MessageContentMatchKind.Link => MarkdownParser.ExtractLinks(message.Content).Any(),
MessageContentMatchKind.Embed => message.Embeds.Any(),
MessageContentMatchKind.File => message.Attachments.Any(),
MessageContentMatchKind.Video => message.Attachments.Any(file => file.IsVideo),
MessageContentMatchKind.Image => message.Attachments.Any(file => file.IsImage),
MessageContentMatchKind.Sound => message.Attachments.Any(file => file.IsAudio),
MessageContentMatchKind.Pin => message.IsPinned,
MessageContentMatchKind.Invite
=> MarkdownParser
.ExtractLinks(message.Content)
.Select(l => l.Url)
.Select(Invite.TryGetCodeFromUrl)
.Any(c => !string.IsNullOrWhiteSpace(c)),
_
=> throw new InvalidOperationException(
$"Unknown message content match kind '{kind}'."

View file

@ -8,5 +8,6 @@ internal enum MessageContentMatchKind
Video,
Image,
Sound,
Pin
Pin,
Invite
}

View file

@ -61,18 +61,28 @@ internal static class FilterGrammar
.IgnoreThen(
Parse.OneOf(
Span.EqualToIgnoreCase("link")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Link)),
.IgnoreThen(Parse.Return(MessageContentMatchKind.Link))
.Try(),
Span.EqualToIgnoreCase("embed")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Embed)),
.IgnoreThen(Parse.Return(MessageContentMatchKind.Embed))
.Try(),
Span.EqualToIgnoreCase("file")
.IgnoreThen(Parse.Return(MessageContentMatchKind.File)),
.IgnoreThen(Parse.Return(MessageContentMatchKind.File))
.Try(),
Span.EqualToIgnoreCase("video")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Video)),
.IgnoreThen(Parse.Return(MessageContentMatchKind.Video))
.Try(),
Span.EqualToIgnoreCase("image")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Image)),
.IgnoreThen(Parse.Return(MessageContentMatchKind.Image))
.Try(),
Span.EqualToIgnoreCase("sound")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Sound)),
Span.EqualToIgnoreCase("pin").IgnoreThen(Parse.Return(MessageContentMatchKind.Pin))
Span.EqualToIgnoreCase("pin")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Pin))
.Try(),
Span.EqualToIgnoreCase("invite")
.IgnoreThen(Parse.Return(MessageContentMatchKind.Invite))
.Try()
)
)
.Select(k => (MessageFilter)new HasMessageFilter(k))