Format stuff

This commit is contained in:
Tyrrrz 2024-10-26 21:41:16 +03:00
parent e8192b2b53
commit 09e0b3f133
45 changed files with 156 additions and 174 deletions

View file

@ -56,7 +56,7 @@ public static class ExportWrapper
ExportFormat = format, ExportFormat = format,
OutputPath = filePath, OutputPath = filePath,
Locale = "en-US", Locale = "en-US",
IsUtcNormalizationEnabled = true IsUtcNormalizationEnabled = true,
}.ExecuteAsync(console); }.ExecuteAsync(console);
} }

View file

@ -30,7 +30,7 @@ public class DateRangeSpecs
ChannelIds = [ChannelIds.DateRangeTestCases], ChannelIds = [ChannelIds.DateRangeTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
After = Snowflake.FromDate(after) After = Snowflake.FromDate(after),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -50,7 +50,7 @@ public class DateRangeSpecs
new DateTimeOffset(2021, 07, 24, 14, 52, 38, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 38, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 24, 14, 52, 39, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 39, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero),
new DateTimeOffset(2021, 09, 08, 14, 26, 35, TimeSpan.Zero) new DateTimeOffset(2021, 09, 08, 14, 26, 35, TimeSpan.Zero),
], ],
o => o =>
o.Using<DateTimeOffset>(ctx => o.Using<DateTimeOffset>(ctx =>
@ -74,7 +74,7 @@ public class DateRangeSpecs
ChannelIds = [ChannelIds.DateRangeTestCases], ChannelIds = [ChannelIds.DateRangeTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
Before = Snowflake.FromDate(before) Before = Snowflake.FromDate(before),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -92,7 +92,7 @@ public class DateRangeSpecs
[ [
new DateTimeOffset(2021, 07, 19, 13, 34, 18, TimeSpan.Zero), new DateTimeOffset(2021, 07, 19, 13, 34, 18, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 19, 15, 58, 48, TimeSpan.Zero), new DateTimeOffset(2021, 07, 19, 15, 58, 48, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 19, 17, 23, 58, TimeSpan.Zero) new DateTimeOffset(2021, 07, 19, 17, 23, 58, TimeSpan.Zero),
], ],
o => o =>
o.Using<DateTimeOffset>(ctx => o.Using<DateTimeOffset>(ctx =>
@ -118,7 +118,7 @@ public class DateRangeSpecs
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
Before = Snowflake.FromDate(before), Before = Snowflake.FromDate(before),
After = Snowflake.FromDate(after) After = Snowflake.FromDate(after),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -137,7 +137,7 @@ public class DateRangeSpecs
new DateTimeOffset(2021, 07, 24, 13, 49, 13, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 13, 49, 13, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 24, 14, 52, 38, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 38, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 24, 14, 52, 39, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 39, TimeSpan.Zero),
new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero) new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero),
], ],
o => o =>
o.Using<DateTimeOffset>(ctx => o.Using<DateTimeOffset>(ctx =>

View file

@ -29,7 +29,7 @@ public class FilterSpecs
ChannelIds = [ChannelIds.FilterTestCases], ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("some text") MessageFilter = MessageFilter.Parse("some text"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -54,7 +54,7 @@ public class FilterSpecs
ChannelIds = [ChannelIds.FilterTestCases], ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("from:Tyrrrz") MessageFilter = MessageFilter.Parse("from:Tyrrrz"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -79,7 +79,7 @@ public class FilterSpecs
ChannelIds = [ChannelIds.FilterTestCases], ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("has:image") MessageFilter = MessageFilter.Parse("has:image"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -104,7 +104,7 @@ public class FilterSpecs
ChannelIds = [ChannelIds.FilterTestCases], ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("has:pin") MessageFilter = MessageFilter.Parse("has:pin"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -129,7 +129,7 @@ public class FilterSpecs
ChannelIds = [ChannelIds.FilterTestCases], ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("has:invite") MessageFilter = MessageFilter.Parse("has:invite"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -154,7 +154,7 @@ public class FilterSpecs
ChannelIds = [ChannelIds.FilterTestCases], ChannelIds = [ChannelIds.FilterTestCases],
ExportFormat = ExportFormat.Json, ExportFormat = ExportFormat.Json,
OutputPath = file.Path, OutputPath = file.Path,
MessageFilter = MessageFilter.Parse("mentions:Tyrrrz") MessageFilter = MessageFilter.Parse("mentions:Tyrrrz"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert

View file

@ -28,7 +28,7 @@ public class HtmlGroupingSpecs
Token = Secrets.DiscordToken, Token = Secrets.DiscordToken,
ChannelIds = [ChannelIds.GroupingTestCases], ChannelIds = [ChannelIds.GroupingTestCases],
ExportFormat = ExportFormat.HtmlDark, ExportFormat = ExportFormat.HtmlDark,
OutputPath = file.Path OutputPath = file.Path,
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert

View file

@ -27,7 +27,7 @@ public class PartitioningSpecs
ChannelIds = [ChannelIds.DateRangeTestCases], ChannelIds = [ChannelIds.DateRangeTestCases],
ExportFormat = ExportFormat.HtmlDark, ExportFormat = ExportFormat.HtmlDark,
OutputPath = filePath, OutputPath = filePath,
PartitionLimit = PartitionLimit.Parse("3") PartitionLimit = PartitionLimit.Parse("3"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert
@ -48,7 +48,7 @@ public class PartitioningSpecs
ChannelIds = [ChannelIds.DateRangeTestCases], ChannelIds = [ChannelIds.DateRangeTestCases],
ExportFormat = ExportFormat.HtmlDark, ExportFormat = ExportFormat.HtmlDark,
OutputPath = filePath, OutputPath = filePath,
PartitionLimit = PartitionLimit.Parse("1kb") PartitionLimit = PartitionLimit.Parse("1kb"),
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert

View file

@ -27,7 +27,7 @@ public class SelfContainedSpecs
ChannelIds = [ChannelIds.SelfContainedTestCases], ChannelIds = [ChannelIds.SelfContainedTestCases],
ExportFormat = ExportFormat.HtmlDark, ExportFormat = ExportFormat.HtmlDark,
OutputPath = filePath, OutputPath = filePath,
ShouldDownloadAssets = true ShouldDownloadAssets = true,
}.ExecuteAsync(new FakeConsole()); }.ExecuteAsync(new FakeConsole());
// Assert // Assert

View file

@ -196,7 +196,7 @@ public abstract class ExportCommandBase : DiscordCommandBase
new ParallelOptions new ParallelOptions
{ {
MaxDegreeOfParallelism = Math.Max(1, ParallelLimit), MaxDegreeOfParallelism = Math.Max(1, ParallelLimit),
CancellationToken = cancellationToken CancellationToken = cancellationToken,
}, },
async (channel, innerCancellationToken) => async (channel, innerCancellationToken) =>
{ {

View file

@ -4,5 +4,5 @@ public enum ThreadInclusionMode
{ {
None, None,
Active, Active,
All All,
} }

View file

@ -13,7 +13,7 @@ internal static class ConsoleExtensions
{ {
Ansi = AnsiSupport.Detect, Ansi = AnsiSupport.Detect,
ColorSystem = ColorSystemSupport.Detect, ColorSystem = ColorSystemSupport.Detect,
Out = new AnsiConsoleOutput(console.Output) Out = new AnsiConsoleOutput(console.Output),
} }
); );

View file

@ -16,5 +16,5 @@ public enum ApplicationFlags
Embedded = 131072, Embedded = 131072,
GatewayMessageContent = 262144, GatewayMessageContent = 262144,
GatewayMessageContentLimited = 524288, GatewayMessageContentLimited = 524288,
ApplicationCommandBadge = 8388608 ApplicationCommandBadge = 8388608,
} }

View file

@ -14,5 +14,5 @@ public enum ChannelKind
GuildPrivateThread = 12, GuildPrivateThread = 12,
GuildStageVoice = 13, GuildStageVoice = 13,
GuildDirectory = 14, GuildDirectory = 14,
GuildForum = 15 GuildForum = 15,
} }

View file

@ -7,5 +7,5 @@ public enum EmbedKind
Image, Image,
Video, Video,
Gifv, Gifv,
Link Link,
} }

View file

@ -3548,7 +3548,7 @@ internal static class EmojiIndex
["🇸🇯"] = "flag_sj", ["🇸🇯"] = "flag_sj",
["🇹🇦"] = "flag_ta", ["🇹🇦"] = "flag_ta",
["🇺🇲"] = "flag_um", ["🇺🇲"] = "flag_um",
["🇺🇳"] = "united_nations" ["🇺🇳"] = "united_nations",
}; };
private static Dictionary<string, string> _fromCodes = private static Dictionary<string, string> _fromCodes =
@ -8880,7 +8880,7 @@ internal static class EmojiIndex
["flag_sj"] = "🇸🇯", ["flag_sj"] = "🇸🇯",
["flag_ta"] = "🇹🇦", ["flag_ta"] = "🇹🇦",
["flag_um"] = "🇺🇲", ["flag_um"] = "🇺🇲",
["united_nations"] = "🇺🇳" ["united_nations"] = "🇺🇳",
}; };
public static string? TryGetCode(string name) => _toCodes.GetValueOrDefault(name); public static string? TryGetCode(string name) => _toCodes.GetValueOrDefault(name);

View file

@ -14,5 +14,5 @@ public enum MessageFlags
Urgent = 16, Urgent = 16,
HasThread = 32, HasThread = 32,
Ephemeral = 64, Ephemeral = 64,
Loading = 128 Loading = 128,
} }

View file

@ -12,5 +12,5 @@ public enum MessageKind
ChannelPinnedMessage = 6, ChannelPinnedMessage = 6,
GuildMemberJoin = 7, GuildMemberJoin = 7,
ThreadCreated = 18, ThreadCreated = 18,
Reply = 19 Reply = 19,
} }

View file

@ -28,7 +28,7 @@ public partial record Sticker
StickerFormat.Apng => "png", StickerFormat.Apng => "png",
StickerFormat.Lottie => "json", StickerFormat.Lottie => "json",
StickerFormat.Gif => "gif", StickerFormat.Gif => "gif",
_ => throw new InvalidOperationException($"Unknown sticker format '{format}'.") _ => throw new InvalidOperationException($"Unknown sticker format '{format}'."),
} }
); );

View file

@ -5,5 +5,5 @@ public enum StickerFormat
Png = 1, Png = 1,
Apng = 2, Apng = 2,
Lottie = 3, Lottie = 3,
Gif = 4 Gif = 4,
} }

View file

@ -133,25 +133,21 @@ public class DiscordClient(string token)
{ {
throw response.StatusCode switch throw response.StatusCode switch
{ {
HttpStatusCode.Unauthorized HttpStatusCode.Unauthorized => throw new DiscordChatExporterException(
=> throw new DiscordChatExporterException( "Authentication token is invalid.",
"Authentication token is invalid.", true
true ),
),
HttpStatusCode.Forbidden HttpStatusCode.Forbidden => throw new DiscordChatExporterException(
=> throw new DiscordChatExporterException( $"Request to '{url}' failed: forbidden."
$"Request to '{url}' failed: forbidden." ),
),
HttpStatusCode.NotFound HttpStatusCode.NotFound => throw new DiscordChatExporterException(
=> throw new DiscordChatExporterException( $"Request to '{url}' failed: not found."
$"Request to '{url}' failed: not found." ),
),
_ _ => throw new DiscordChatExporterException(
=> throw new DiscordChatExporterException( $"""
$"""
Request to '{url}' failed: {response Request to '{url}' failed: {response
.StatusCode.ToString() .StatusCode.ToString()
.ToSpaceSeparatedWords() .ToSpaceSeparatedWords()
@ -160,8 +156,8 @@ public class DiscordClient(string token)
cancellationToken cancellationToken
)} )}
""", """,
true true
) ),
}; };
} }

View file

@ -3,5 +3,5 @@
public enum TokenKind public enum TokenKind
{ {
User, User,
Bot Bot,
} }

View file

@ -8,7 +8,7 @@ public enum ExportFormat
HtmlDark, HtmlDark,
HtmlLight, HtmlLight,
Csv, Csv,
Json Json,
} }
public static class ExportFormatExtensions public static class ExportFormatExtensions
@ -21,7 +21,7 @@ public static class ExportFormatExtensions
ExportFormat.HtmlLight => "html", ExportFormat.HtmlLight => "html",
ExportFormat.Csv => "csv", ExportFormat.Csv => "csv",
ExportFormat.Json => "json", ExportFormat.Json => "json",
_ => throw new ArgumentOutOfRangeException(nameof(format)) _ => throw new ArgumentOutOfRangeException(nameof(format)),
}; };
public static string GetDisplayName(this ExportFormat format) => public static string GetDisplayName(this ExportFormat format) =>
@ -32,6 +32,6 @@ public static class ExportFormatExtensions
ExportFormat.HtmlLight => "HTML (Light)", ExportFormat.HtmlLight => "HTML (Light)",
ExportFormat.Csv => "CSV", ExportFormat.Csv => "CSV",
ExportFormat.Json => "JSON", ExportFormat.Json => "JSON",
_ => throw new ArgumentOutOfRangeException(nameof(format)) _ => throw new ArgumentOutOfRangeException(nameof(format)),
}; };
} }

View file

@ -172,24 +172,21 @@ public partial class ExportRequest
"%C" => channel.Name, "%C" => channel.Name,
"%p" => channel.Position?.ToString(CultureInfo.InvariantCulture) ?? "0", "%p" => channel.Position?.ToString(CultureInfo.InvariantCulture) ?? "0",
"%P" "%P" => channel.Parent?.Position?.ToString(CultureInfo.InvariantCulture)
=> channel.Parent?.Position?.ToString(CultureInfo.InvariantCulture) ?? "0",
?? "0",
"%a" "%a" => after?.ToDate().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
=> after?.ToDate().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) ?? "",
?? "", "%b" => before
"%b" ?.ToDate()
=> before?.ToDate().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) .ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) ?? "",
?? "", "%d" => DateTimeOffset.Now.ToString(
"%d" "yyyy-MM-dd",
=> DateTimeOffset.Now.ToString( CultureInfo.InvariantCulture
"yyyy-MM-dd", ),
CultureInfo.InvariantCulture
),
"%%" => "%", "%%" => "%",
_ => m.Value _ => m.Value,
} }
) )
); );

View file

@ -3,5 +3,5 @@
internal enum BinaryExpressionKind internal enum BinaryExpressionKind
{ {
Or, Or,
And And,
} }

View file

@ -14,6 +14,6 @@ internal class BinaryExpressionMessageFilter(
{ {
BinaryExpressionKind.Or => first.IsMatch(message) || second.IsMatch(message), BinaryExpressionKind.Or => first.IsMatch(message) || second.IsMatch(message),
BinaryExpressionKind.And => first.IsMatch(message) && second.IsMatch(message), BinaryExpressionKind.And => first.IsMatch(message) && second.IsMatch(message),
_ => throw new InvalidOperationException($"Unknown binary expression kind '{kind}'.") _ => throw new InvalidOperationException($"Unknown binary expression kind '{kind}'."),
}; };
} }

View file

@ -17,15 +17,13 @@ internal class HasMessageFilter(MessageContentMatchKind kind) : MessageFilter
MessageContentMatchKind.Image => message.Attachments.Any(file => file.IsImage), MessageContentMatchKind.Image => message.Attachments.Any(file => file.IsImage),
MessageContentMatchKind.Sound => message.Attachments.Any(file => file.IsAudio), MessageContentMatchKind.Sound => message.Attachments.Any(file => file.IsAudio),
MessageContentMatchKind.Pin => message.IsPinned, MessageContentMatchKind.Pin => message.IsPinned,
MessageContentMatchKind.Invite MessageContentMatchKind.Invite => MarkdownParser
=> MarkdownParser .ExtractLinks(message.Content)
.ExtractLinks(message.Content) .Select(l => l.Url)
.Select(l => l.Url) .Select(Invite.TryGetCodeFromUrl)
.Select(Invite.TryGetCodeFromUrl) .Any(c => !string.IsNullOrWhiteSpace(c)),
.Any(c => !string.IsNullOrWhiteSpace(c)), _ => throw new InvalidOperationException(
_ $"Unknown message content match kind '{kind}'."
=> throw new InvalidOperationException( ),
$"Unknown message content match kind '{kind}'."
)
}; };
} }

View file

@ -9,5 +9,5 @@ internal enum MessageContentMatchKind
Image, Image,
Sound, Sound,
Pin, Pin,
Invite Invite,
} }

View file

@ -125,7 +125,7 @@ internal static class FilterGrammar
op switch op switch
{ {
'|' => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.Or), '|' => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.Or),
_ => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.And) _ => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.And),
} }
); );

View file

@ -34,58 +34,51 @@ internal partial class HtmlMarkdownVisitor(
{ {
var (openingTag, closingTag) = formatting.Kind switch var (openingTag, closingTag) = formatting.Kind switch
{ {
FormattingKind.Bold FormattingKind.Bold => (
=> ( // lang=html
// lang=html "<strong>",
"<strong>", // lang=html
// lang=html "</strong>"
"</strong>" ),
),
FormattingKind.Italic FormattingKind.Italic => (
=> ( // lang=html
// lang=html "<em>",
"<em>", // lang=html
// lang=html "</em>"
"</em>" ),
),
FormattingKind.Underline FormattingKind.Underline => (
=> ( // lang=html
// lang=html "<u>",
"<u>", // lang=html
// lang=html "</u>"
"</u>" ),
),
FormattingKind.Strikethrough FormattingKind.Strikethrough => (
=> ( // lang=html
// lang=html "<s>",
"<s>", // lang=html
// lang=html "</s>"
"</s>" ),
),
FormattingKind.Spoiler FormattingKind.Spoiler => (
=> ( // lang=html
// lang=html """<span class="chatlog__markdown-spoiler chatlog__markdown-spoiler--hidden" onclick="showSpoiler(event, this)">""",
"""<span class="chatlog__markdown-spoiler chatlog__markdown-spoiler--hidden" onclick="showSpoiler(event, this)">""", // lang=html
// lang=html """</span>"""
"""</span>""" ),
),
FormattingKind.Quote FormattingKind.Quote => (
=> ( // lang=html
// lang=html """<div class="chatlog__markdown-quote"><div class="chatlog__markdown-quote-border"></div><div class="chatlog__markdown-quote-content">""",
"""<div class="chatlog__markdown-quote"><div class="chatlog__markdown-quote-border"></div><div class="chatlog__markdown-quote-content">""", // lang=html
// lang=html """</div></div>"""
"""</div></div>""" ),
),
_ _ => throw new InvalidOperationException(
=> throw new InvalidOperationException( $"Unknown formatting kind '{formatting.Kind}'."
$"Unknown formatting kind '{formatting.Kind}'." ),
)
}; };
buffer.Append(openingTag); buffer.Append(openingTag);

View file

@ -90,7 +90,7 @@ internal class HtmlMessageWriter(Stream stream, ExportContext context, string th
await new MessageGroupTemplate await new MessageGroupTemplate
{ {
Context = Context, Context = Context,
Messages = messages Messages = messages,
}.RenderAsync(cancellationToken) }.RenderAsync(cancellationToken)
) )
); );
@ -131,7 +131,7 @@ internal class HtmlMessageWriter(Stream stream, ExportContext context, string th
await new PostambleTemplate await new PostambleTemplate
{ {
Context = Context, Context = Context,
MessagesWritten = MessagesWritten MessagesWritten = MessagesWritten,
}.RenderAsync(cancellationToken) }.RenderAsync(cancellationToken)
) )
); );

View file

@ -25,7 +25,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
Indented = true, Indented = true,
// Validation errors may mask actual failures // Validation errors may mask actual failures
// https://github.com/Tyrrrz/DiscordChatExporter/issues/413 // https://github.com/Tyrrrz/DiscordChatExporter/issues/413
SkipValidation = true SkipValidation = true,
} }
); );

View file

@ -100,13 +100,15 @@ internal partial class MessageExporter
ExportFormat.PlainText => new PlainTextMessageWriter(File.Create(filePath), context), ExportFormat.PlainText => new PlainTextMessageWriter(File.Create(filePath), context),
ExportFormat.Csv => new CsvMessageWriter(File.Create(filePath), context), ExportFormat.Csv => new CsvMessageWriter(File.Create(filePath), context),
ExportFormat.HtmlDark => new HtmlMessageWriter(File.Create(filePath), context, "Dark"), ExportFormat.HtmlDark => new HtmlMessageWriter(File.Create(filePath), context, "Dark"),
ExportFormat.HtmlLight ExportFormat.HtmlLight => new HtmlMessageWriter(
=> new HtmlMessageWriter(File.Create(filePath), context, "Light"), File.Create(filePath),
context,
"Light"
),
ExportFormat.Json => new JsonMessageWriter(File.Create(filePath), context), ExportFormat.Json => new JsonMessageWriter(File.Create(filePath), context),
_ _ => throw new ArgumentOutOfRangeException(
=> throw new ArgumentOutOfRangeException( nameof(format),
nameof(format), $"Unknown export format '{format}'."
$"Unknown export format '{format}'." ),
)
}; };
} }

View file

@ -37,7 +37,7 @@ public partial class PartitionLimit
"M" => 1_000_000, "M" => 1_000_000,
"K" => 1_000, "K" => 1_000,
"" => 1, "" => 1,
_ => -1 _ => -1,
}; };
if (magnitude < 0) if (magnitude < 0)

View file

@ -10,20 +10,18 @@ internal static class PlainTextMessageExtensions
public static string GetFallbackContent(this Message message) => public static string GetFallbackContent(this Message message) =>
message.Kind switch message.Kind switch
{ {
MessageKind.RecipientAdd MessageKind.RecipientAdd => message.MentionedUsers.Any()
=> message.MentionedUsers.Any() ? $"Added {message.MentionedUsers.First().DisplayName} to the group."
? $"Added {message.MentionedUsers.First().DisplayName} to the group." : "Added a recipient.",
: "Added a recipient.",
MessageKind.RecipientRemove MessageKind.RecipientRemove => message.MentionedUsers.Any()
=> message.MentionedUsers.Any() ? message.Author.Id == message.MentionedUsers.First().Id
? message.Author.Id == message.MentionedUsers.First().Id ? "Left the group."
? "Left the group." : $"Removed {message.MentionedUsers.First().DisplayName} from the group."
: $"Removed {message.MentionedUsers.First().DisplayName} from the group." : "Removed a recipient.",
: "Removed a recipient.",
MessageKind.Call MessageKind.Call =>
=> $"Started a call that lasted { $"Started a call that lasted {
message message
.CallEndedTimestamp? .CallEndedTimestamp?
.Pipe(t => t - message.Timestamp) .Pipe(t => t - message.Timestamp)
@ -31,16 +29,15 @@ internal static class PlainTextMessageExtensions
.ToString("n0", CultureInfo.InvariantCulture) ?? "0" .ToString("n0", CultureInfo.InvariantCulture) ?? "0"
} minutes.", } minutes.",
MessageKind.ChannelNameChange MessageKind.ChannelNameChange => !string.IsNullOrWhiteSpace(message.Content)
=> !string.IsNullOrWhiteSpace(message.Content) ? $"Changed the channel name: {message.Content}"
? $"Changed the channel name: {message.Content}" : "Changed the channel name.",
: "Changed the channel name.",
MessageKind.ChannelIconChange => "Changed the channel icon.", MessageKind.ChannelIconChange => "Changed the channel icon.",
MessageKind.ChannelPinnedMessage => "Pinned a message.", MessageKind.ChannelPinnedMessage => "Pinned a message.",
MessageKind.ThreadCreated => "Started a thread.", MessageKind.ThreadCreated => "Started a thread.",
MessageKind.GuildMemberJoin => "Joined the server.", MessageKind.GuildMemberJoin => "Joined the server.",
_ => message.Content _ => message.Content,
}; };
} }

View file

@ -7,5 +7,5 @@ internal enum FormattingKind
Underline, Underline,
Strikethrough, Strikethrough,
Spoiler, Spoiler,
Quote Quote,
} }

View file

@ -6,5 +6,5 @@ internal enum MentionKind
Here, Here,
User, User,
Channel, Channel,
Role Role,
} }

View file

@ -377,10 +377,9 @@ internal static partial class MarkdownParser
null => null, null => null,
// Unknown format: throw an exception to consider this timestamp invalid // Unknown format: throw an exception to consider this timestamp invalid
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1156 // https://github.com/Tyrrrz/DiscordChatExporter/issues/1156
var f var f => throw new InvalidOperationException(
=> throw new InvalidOperationException( $"Unknown timestamp format '{f}'."
$"Unknown timestamp format '{f}'." ),
)
}; };
return new TimestampNode(instant, format); return new TimestampNode(instant, format);

View file

@ -39,7 +39,7 @@ public static class Http
ShouldHandle = new PredicateBuilder().Handle<Exception>(IsRetryableException), ShouldHandle = new PredicateBuilder().Handle<Exception>(IsRetryableException),
MaxRetryAttempts = 4, MaxRetryAttempts = 4,
BackoffType = DelayBackoffType.Exponential, BackoffType = DelayBackoffType.Exponential,
Delay = TimeSpan.FromSeconds(1) Delay = TimeSpan.FromSeconds(1),
} }
) )
.Build(); .Build();
@ -68,7 +68,7 @@ public static class Http
return ValueTask.FromResult<TimeSpan?>( return ValueTask.FromResult<TimeSpan?>(
TimeSpan.FromSeconds(Math.Pow(2, args.AttemptNumber) + 1) TimeSpan.FromSeconds(Math.Pow(2, args.AttemptNumber) + 1)
); );
} },
} }
) )
.Build(); .Build();

View file

@ -9,7 +9,7 @@ public static class PathEx
{ {
private static readonly HashSet<char> InvalidFileNameChars = private static readonly HashSet<char> InvalidFileNameChars =
[ [
.. Path.GetInvalidFileNameChars() .. Path.GetInvalidFileNameChars(),
]; ];
public static string EscapeFileName(string path) public static string EscapeFileName(string path)

View file

@ -61,7 +61,7 @@ public class App : Application, IDisposable
{ {
ThemeVariant.Light => Avalonia.Styling.ThemeVariant.Light, ThemeVariant.Light => Avalonia.Styling.ThemeVariant.Light,
ThemeVariant.Dark => Avalonia.Styling.ThemeVariant.Dark, ThemeVariant.Dark => Avalonia.Styling.ThemeVariant.Dark,
_ => Avalonia.Styling.ThemeVariant.Default _ => Avalonia.Styling.ThemeVariant.Default,
}; };
InitializeTheme(); InitializeTheme();
@ -86,7 +86,7 @@ public class App : Application, IDisposable
{ {
"Light" => PlatformThemeVariant.Light, "Light" => PlatformThemeVariant.Light,
"Dark" => PlatformThemeVariant.Dark, "Dark" => PlatformThemeVariant.Dark,
_ => PlatformSettings?.GetColorValues().ThemeVariant ?? PlatformThemeVariant.Light _ => PlatformSettings?.GetColorValues().ThemeVariant ?? PlatformThemeVariant.Light,
}; };
this.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = this.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme =

View file

@ -56,7 +56,7 @@ public class DialogManager : IDisposable
{ {
FileTypeChoices = fileTypes, FileTypeChoices = fileTypes,
SuggestedFileName = defaultFilePath, SuggestedFileName = defaultFilePath,
DefaultExtension = Path.GetExtension(defaultFilePath).TrimStart('.') DefaultExtension = Path.GetExtension(defaultFilePath).TrimStart('.'),
} }
); );
@ -77,7 +77,7 @@ public class DialogManager : IDisposable
new FolderPickerOpenOptions new FolderPickerOpenOptions
{ {
AllowMultiple = false, AllowMultiple = false,
SuggestedStartLocation = startLocation SuggestedStartLocation = startLocation,
} }
); );

View file

@ -4,5 +4,5 @@ public enum ThemeVariant
{ {
System, System,
Light, Light,
Dark Dark,
} }

View file

@ -19,7 +19,7 @@ public partial class ViewManager
ExportSetupViewModel => new ExportSetupView(), ExportSetupViewModel => new ExportSetupView(),
MessageBoxViewModel => new MessageBoxView(), MessageBoxViewModel => new MessageBoxView(),
SettingsViewModel => new SettingsView(), SettingsViewModel => new SettingsView(),
_ => null _ => null,
}; };
public Control? TryBindView(ViewModelBase viewModel) public Control? TryBindView(ViewModelBase viewModel)

View file

@ -4,5 +4,5 @@ public enum ThreadInclusionMode
{ {
None, None,
Active, Active,
All All,
} }

View file

@ -253,7 +253,7 @@ public partial class DashboardViewModel : ViewModelBase
channelProgressPairs, channelProgressPairs,
new ParallelOptions new ParallelOptions
{ {
MaxDegreeOfParallelism = Math.Max(1, _settingsService.ParallelLimit) MaxDegreeOfParallelism = Math.Max(1, _settingsService.ParallelLimit),
}, },
async (pair, cancellationToken) => async (pair, cancellationToken) =>
{ {

View file

@ -138,8 +138,8 @@ public partial class ExportSetupViewModel(
[ [
new FilePickerFileType($"{extension.ToUpperInvariant()} file") new FilePickerFileType($"{extension.ToUpperInvariant()} file")
{ {
Patterns = [$"*.{extension}"] Patterns = [$"*.{extension}"],
} },
], ],
defaultFileName defaultFileName
); );

View file

@ -85,7 +85,7 @@ public class SettingsViewModel : DialogViewModelBase
"zh-CN", "zh-CN",
"ja-JP", "ja-JP",
"zh-TW", "zh-TW",
"ko-KR" "ko-KR",
]; ];
// This has to be non-nullable because Avalonia ComboBox doesn't allow a null value to be selected // This has to be non-nullable because Avalonia ComboBox doesn't allow a null value to be selected