From 31c7ae93120276899048df8063658b3483d86f51 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 20 May 2023 07:09:19 +0300 Subject: [PATCH] Refactor --- .../Infra/ExportWrapper.cs | 32 ++++++++-------- .../Specs/CsvContentSpecs.cs | 2 +- .../Specs/DateRangeSpecs.cs | 6 +-- .../Specs/FilterSpecs.cs | 10 ++--- .../Specs/HtmlAttachmentSpecs.cs | 8 ++-- .../Specs/HtmlContentSpecs.cs | 2 +- .../Specs/HtmlEmbedSpecs.cs | 20 +++++----- .../Specs/HtmlGroupingSpecs.cs | 2 +- .../Specs/HtmlMarkdownSpecs.cs | 18 ++++----- .../Specs/HtmlMentionSpecs.cs | 8 ++-- .../Specs/HtmlReplySpecs.cs | 8 ++-- .../Specs/HtmlStickerSpecs.cs | 4 +- .../Specs/JsonAttachmentSpecs.cs | 36 ++++++++++-------- .../Specs/JsonContentSpecs.cs | 2 +- .../Specs/JsonEmbedSpecs.cs | 2 +- .../Specs/JsonMentionSpecs.cs | 8 ++-- .../Specs/JsonStickerSpecs.cs | 4 +- .../Specs/PartitioningSpecs.cs | 4 +- .../Specs/PlainTextContentSpecs.cs | 2 +- .../Specs/SelfContainedSpecs.cs | 2 +- .../Commands/Base/ExportCommandBase.cs | 38 ++++++++++--------- .../Commands/ExportAllCommand.cs | 10 +++-- .../Commands/ExportChannelsCommand.cs | 6 ++- .../Commands/ExportDirectMessagesCommand.cs | 2 +- .../Commands/ExportGuildCommand.cs | 2 +- .../Commands/GetChannelsCommand.cs | 2 +- .../Commands/GetDirectChannelsCommand.cs | 2 +- .../Commands/GetGuildsCommand.cs | 2 +- .../Commands/GuideCommand.cs | 2 +- .../Discord/Data/ChannelKind.cs | 1 - .../Discord/Data/ChannelThread.cs | 3 +- .../Data/Common/IdBasedEqualityComparer.cs | 12 ------ .../Discord/Data/Emoji.cs | 3 +- .../Discord/Data/MessageKind.cs | 2 +- .../Discord/DiscordClient.cs | 12 +++--- DiscordChatExporter.Core/Discord/Snowflake.cs | 4 -- .../Exporting/CsvMessageWriter.cs | 8 +++- .../Exporting/ExportAssetDownloader.cs | 7 ++-- .../Exporting/ExportContext.cs | 11 ++++-- .../Exporting/HtmlMessageWriter.cs | 6 +-- .../Exporting/MessageExporter.cs | 6 +-- DiscordChatExporter.Core/Utils/Http.cs | 2 +- DiscordChatExporter.Gui/Bootstrapper.cs | 7 +--- .../Services/SettingsService.cs | 2 +- DiscordChatExporter.Gui/Utils/ProcessEx.cs | 12 +++--- .../Components/DashboardViewModel.cs | 15 +++----- .../ViewModels/Framework/DialogManager.cs | 2 +- .../Messages/NotificationMessage.cs | 7 +--- .../ViewModels/RootViewModel.cs | 9 ++--- .../Views/Dialogs/ExportSetupView.xaml | 2 +- 50 files changed, 181 insertions(+), 198 deletions(-) delete mode 100644 DiscordChatExporter.Core/Discord/Data/Common/IdBasedEqualityComparer.cs diff --git a/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs b/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs index e30536e..76b877f 100644 --- a/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs +++ b/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs @@ -51,7 +51,7 @@ public static class ExportWrapper // Lock separately for each channel and format using (await Locker.LockAsync(filePath)) { - // Perform export only if it hasn't been done before + // Perform the export only if it hasn't been done before if (!File.Exists(filePath)) { await new ExportChannelsCommand @@ -94,14 +94,13 @@ public static class ExportWrapper public static async ValueTask GetMessageAsHtmlAsync(Snowflake channelId, Snowflake messageId) { - var message = (await GetMessagesAsHtmlAsync(channelId)) - .SingleOrDefault(e => - string.Equals( - e.GetAttribute("data-message-id"), - messageId.ToString(), - StringComparison.OrdinalIgnoreCase - ) - ); + var message = (await GetMessagesAsHtmlAsync(channelId)).SingleOrDefault(e => + string.Equals( + e.GetAttribute("data-message-id"), + messageId.ToString(), + StringComparison.OrdinalIgnoreCase + ) + ); if (message is null) { @@ -115,14 +114,13 @@ public static class ExportWrapper public static async ValueTask GetMessageAsJsonAsync(Snowflake channelId, Snowflake messageId) { - var message = (await GetMessagesAsJsonAsync(channelId)) - .SingleOrDefault(j => - string.Equals( - j.GetProperty("id").GetString(), - messageId.ToString(), - StringComparison.OrdinalIgnoreCase - ) - ); + var message = (await GetMessagesAsJsonAsync(channelId)).SingleOrDefault(j => + string.Equals( + j.GetProperty("id").GetString(), + messageId.ToString(), + StringComparison.OrdinalIgnoreCase + ) + ); if (message.ValueKind == JsonValueKind.Undefined) { diff --git a/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs index e0f047d..fa24154 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs @@ -8,7 +8,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class CsvContentSpecs { [Fact] - public async Task Messages_are_exported_correctly() + public async Task I_can_export_a_channel_in_the_CSV_format() { // Act var document = await ExportWrapper.ExportAsCsvAsync(ChannelIds.DateRangeTestCases); diff --git a/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs index 4bea979..4ac92db 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs @@ -17,7 +17,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class DateRangeSpecs { [Fact] - public async Task Messages_filtered_after_specific_date_only_include_messages_sent_after_that_date() + public async Task I_can_filter_the_export_to_only_include_messages_sent_after_the_specified_date() { // Arrange var after = new DateTimeOffset(2021, 07, 24, 0, 0, 0, TimeSpan.Zero); @@ -61,7 +61,7 @@ public class DateRangeSpecs } [Fact] - public async Task Messages_filtered_before_specific_date_only_include_messages_sent_before_that_date() + public async Task I_can_filter_the_export_to_only_include_messages_sent_before_the_specified_date() { // Arrange var before = new DateTimeOffset(2021, 07, 24, 0, 0, 0, TimeSpan.Zero); @@ -103,7 +103,7 @@ public class DateRangeSpecs } [Fact] - public async Task Messages_filtered_between_specific_dates_only_include_messages_sent_between_those_dates() + public async Task I_can_filter_the_export_to_only_include_messages_sent_between_the_specified_dates() { // Arrange var after = new DateTimeOffset(2021, 07, 24, 0, 0, 0, TimeSpan.Zero); diff --git a/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs index 85b8483..ae13271 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs @@ -16,7 +16,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class FilterSpecs { [Fact] - public async Task Messages_filtered_by_text_only_include_messages_that_contain_that_text() + public async Task I_can_filter_the_export_to_only_include_messages_that_contain_the_specified_text() { // Arrange using var file = TempFile.Create(); @@ -42,7 +42,7 @@ public class FilterSpecs } [Fact] - public async Task Messages_filtered_by_author_only_include_messages_sent_by_that_author() + public async Task I_can_filter_the_export_to_only_include_messages_that_were_sent_by_the_specified_author() { // Arrange using var file = TempFile.Create(); @@ -68,7 +68,7 @@ public class FilterSpecs } [Fact] - public async Task Messages_filtered_by_content_only_include_messages_that_have_that_content() + public async Task I_can_filter_the_export_to_only_include_messages_that_contain_the_specified_content() { // Arrange using var file = TempFile.Create(); @@ -94,7 +94,7 @@ public class FilterSpecs } [Fact] - public async Task Messages_filtered_by_pin_only_include_messages_that_have_been_pinned() + public async Task I_can_filter_the_export_to_only_include_messages_that_have_been_pinned() { // Arrange using var file = TempFile.Create(); @@ -120,7 +120,7 @@ public class FilterSpecs } [Fact] - public async Task Messages_filtered_by_mention_only_include_messages_that_have_that_mention() + public async Task I_can_filter_the_export_to_only_include_messages_that_contain_the_specified_mention() { // Arrange using var file = TempFile.Create(); diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs index b3a9b7c..0bae2cc 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs @@ -11,7 +11,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlAttachmentSpecs { [Fact] - public async Task Message_with_a_generic_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_generic_attachment() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -36,7 +36,7 @@ public class HtmlAttachmentSpecs } [Fact] - public async Task Message_with_an_image_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_image_attachment() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -57,7 +57,7 @@ public class HtmlAttachmentSpecs } [Fact] - public async Task Message_with_a_video_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_video_attachment() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/333 @@ -77,7 +77,7 @@ public class HtmlAttachmentSpecs } [Fact] - public async Task Message_with_an_audio_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_audio_attachment() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/333 diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs index dc89674..9c3584d 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs @@ -11,7 +11,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlContentSpecs { [Fact] - public async Task Messages_are_exported_correctly() + public async Task I_can_export_a_channel_in_the_HTML_format() { // Act var messages = await ExportWrapper.GetMessagesAsHtmlAsync(ChannelIds.DateRangeTestCases); diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs index d4f4736..84070bd 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs @@ -12,7 +12,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlEmbedSpecs { [Fact] - public async Task Message_with_an_embed_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_rich_embed() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -33,7 +33,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_an_image_link_is_rendered_with_an_image_embed() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_image_embed() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/537 @@ -54,7 +54,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_an_image_link_and_nothing_else_is_rendered_without_text_content() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_image_embed_and_the_text_is_hidden_if_it_only_contains_the_image_link() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/682 @@ -70,7 +70,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_video_link_is_rendered_with_a_video_embed() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_video_embed() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -89,7 +89,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_GIFV_link_is_rendered_with_a_video_embed() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_GIFV_embed() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -108,7 +108,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_GIFV_link_and_nothing_else_is_rendered_without_text_content() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_GIFV_embed_and_the_text_is_hidden_if_it_only_contains_the_video_link() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -122,7 +122,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_Spotify_track_link_is_rendered_with_a_track_embed() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_Spotify_track_embed() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/657 @@ -138,7 +138,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_YouTube_video_link_is_rendered_with_a_video_embed() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_YouTube_video_embed() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/570 @@ -154,7 +154,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_Twitter_post_link_with_multiple_images_is_rendered_as_a_single_embed() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_Twitter_post_embed_that_includes_multiple_images() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/695 @@ -180,7 +180,7 @@ public class HtmlEmbedSpecs } [Fact] - public async Task Message_with_a_guild_invite_link_is_rendered_with_a_widget() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_guild_invite() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/649 diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs index 052683b..27d59a8 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs @@ -15,7 +15,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlGroupingSpecs { [Fact] - public async Task Messages_are_grouped_correctly() + public async Task I_can_export_a_channel_and_the_messages_are_grouped_according_to_their_author_and_timestamps() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/152 diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs index e889db3..9f4539a 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs @@ -12,7 +12,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlMarkdownSpecs { [Fact] - public async Task Message_with_a_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -36,7 +36,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_short_time_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_short_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -60,7 +60,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_long_time_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_long_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -84,7 +84,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_short_date_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_short_date_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -108,7 +108,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_long_date_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_long_date_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -132,7 +132,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_full_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_full_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -156,7 +156,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_full_long_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_full_long_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -180,7 +180,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_a_relative_timestamp_is_rendered_as_the_default_timestamp() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_relative_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); @@ -204,7 +204,7 @@ public class HtmlMarkdownSpecs } [Fact] - public async Task Message_with_an_invalid_timestamp_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_invalid_timestamp_marker() { // Date formatting code relies on the local time zone, so we need to set it to a fixed value TimeZoneInfoEx.SetLocal(TimeSpan.FromHours(+2)); diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs index 34efad3..6fe7045 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs @@ -10,7 +10,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlMentionSpecs { [Fact] - public async Task Message_with_a_user_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_user_mention() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -24,7 +24,7 @@ public class HtmlMentionSpecs } [Fact] - public async Task Message_with_a_text_channel_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_text_channel_mention() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -37,7 +37,7 @@ public class HtmlMentionSpecs } [Fact] - public async Task Message_with_a_voice_channel_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_voice_channel_mention() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -50,7 +50,7 @@ public class HtmlMentionSpecs } [Fact] - public async Task Message_with_a_role_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_role_mention() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs index d2051c4..a0e61e3 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs @@ -10,7 +10,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlReplySpecs { [Fact] - public async Task Message_with_a_reply_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_that_replies_to_another_message() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -24,7 +24,7 @@ public class HtmlReplySpecs } [Fact] - public async Task Message_with_a_reply_to_a_deleted_message_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_that_replies_to_a_deleted_message() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/645 @@ -42,7 +42,7 @@ public class HtmlReplySpecs } [Fact] - public async Task Message_with_a_reply_to_an_empty_message_with_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_that_replies_to_an_empty_message_with_an_attachment() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/634 @@ -58,7 +58,7 @@ public class HtmlReplySpecs } [Fact] - public async Task Message_with_a_reply_to_an_interaction_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_that_replies_to_an_interaction() { // https://github.com/Tyrrrz/DiscordChatExporter/issues/569 diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs index 1cbc069..7d826dd 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs @@ -9,7 +9,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class HtmlStickerSpecs { [Fact] - public async Task Message_with_a_PNG_based_sticker_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_PNG_sticker() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( @@ -23,7 +23,7 @@ public class HtmlStickerSpecs } [Fact] - public async Task Message_with_a_Lottie_based_sticker_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_Lottie_sticker() { // Act var message = await ExportWrapper.GetMessageAsHtmlAsync( diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs index 583e9fe..95122ab 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs @@ -10,7 +10,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class JsonAttachmentSpecs { [Fact] - public async Task Message_with_a_generic_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_generic_attachment() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -23,15 +23,16 @@ public class JsonAttachmentSpecs var attachments = message.GetProperty("attachments").EnumerateArray().ToArray(); attachments.Should().HaveCount(1); - attachments.Single().GetProperty("url").GetString().Should().Be( + + attachments[0].GetProperty("url").GetString().Should().Be( "https://cdn.discordapp.com/attachments/885587741654536192/885587844964417596/Test.txt" ); - attachments.Single().GetProperty("fileName").GetString().Should().Be("Test.txt"); - attachments.Single().GetProperty("fileSizeBytes").GetInt64().Should().Be(11); + attachments[0].GetProperty("fileName").GetString().Should().Be("Test.txt"); + attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(11); } [Fact] - public async Task Message_with_an_image_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_image_attachment() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -44,15 +45,16 @@ public class JsonAttachmentSpecs var attachments = message.GetProperty("attachments").EnumerateArray().ToArray(); attachments.Should().HaveCount(1); - attachments.Single().GetProperty("url").GetString().Should().Be( + + attachments[0].GetProperty("url").GetString().Should().Be( "https://cdn.discordapp.com/attachments/885587741654536192/885654862430359613/bird-thumbnail.png" ); - attachments.Single().GetProperty("fileName").GetString().Should().Be("bird-thumbnail.png"); - attachments.Single().GetProperty("fileSizeBytes").GetInt64().Should().Be(466335); + attachments[0].GetProperty("fileName").GetString().Should().Be("bird-thumbnail.png"); + attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(466335); } [Fact] - public async Task Message_with_a_video_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_video_attachment() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -65,15 +67,16 @@ public class JsonAttachmentSpecs var attachments = message.GetProperty("attachments").EnumerateArray().ToArray(); attachments.Should().HaveCount(1); - attachments.Single().GetProperty("url").GetString().Should().Be( + + attachments[0].GetProperty("url").GetString().Should().Be( "https://cdn.discordapp.com/attachments/885587741654536192/885655761512968233/file_example_MP4_640_3MG.mp4" ); - attachments.Single().GetProperty("fileName").GetString().Should().Be("file_example_MP4_640_3MG.mp4"); - attachments.Single().GetProperty("fileSizeBytes").GetInt64().Should().Be(3114374); + attachments[0].GetProperty("fileName").GetString().Should().Be("file_example_MP4_640_3MG.mp4"); + attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(3114374); } [Fact] - public async Task Message_with_an_audio_attachment_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_an_audio_attachment() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -86,10 +89,11 @@ public class JsonAttachmentSpecs var attachments = message.GetProperty("attachments").EnumerateArray().ToArray(); attachments.Should().HaveCount(1); - attachments.Single().GetProperty("url").GetString().Should().Be( + + attachments[0].GetProperty("url").GetString().Should().Be( "https://cdn.discordapp.com/attachments/885587741654536192/885656175348187146/file_example_MP3_1MG.mp3" ); - attachments.Single().GetProperty("fileName").GetString().Should().Be("file_example_MP3_1MG.mp3"); - attachments.Single().GetProperty("fileSizeBytes").GetInt64().Should().Be(1087849); + attachments[0].GetProperty("fileName").GetString().Should().Be("file_example_MP3_1MG.mp3"); + attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(1087849); } } \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs index c9d3883..9540982 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs @@ -9,7 +9,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class JsonContentSpecs { [Fact] - public async Task Messages_are_exported_correctly() + public async Task I_can_export_a_channel_in_the_JSON_format() { // Act var messages = await ExportWrapper.GetMessagesAsJsonAsync(ChannelIds.DateRangeTestCases); diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs index 562688e..8c6350a 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs @@ -10,7 +10,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class JsonEmbedSpecs { [Fact] - public async Task Message_with_an_embed_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_rich_embed() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs index dad6654..48953b6 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs @@ -10,7 +10,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class JsonMentionSpecs { [Fact] - public async Task Message_with_a_user_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_user_mention() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -30,7 +30,7 @@ public class JsonMentionSpecs } [Fact] - public async Task Message_with_a_text_channel_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_text_channel_mention() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -43,7 +43,7 @@ public class JsonMentionSpecs } [Fact] - public async Task Message_with_a_voice_channel_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_voice_channel_mention() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -56,7 +56,7 @@ public class JsonMentionSpecs } [Fact] - public async Task Message_with_a_role_mention_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_role_mention() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs index 87dd1d7..c825801 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs @@ -10,7 +10,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class JsonStickerSpecs { [Fact] - public async Task Message_with_a_PNG_based_sticker_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_PNG_sticker() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( @@ -31,7 +31,7 @@ public class JsonStickerSpecs } [Fact] - public async Task Message_with_a_Lottie_based_sticker_is_rendered_correctly() + public async Task I_can_export_a_channel_that_contains_a_message_with_a_Lottie_sticker() { // Act var message = await ExportWrapper.GetMessageAsJsonAsync( diff --git a/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs index 41ba695..0d730d5 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs @@ -14,7 +14,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class PartitioningSpecs { [Fact] - public async Task Messages_partitioned_by_count_are_split_into_a_corresponding_number_of_files() + public async Task I_can_export_a_channel_with_partitioning_based_on_message_count() { // Arrange using var dir = TempDir.Create(); @@ -37,7 +37,7 @@ public class PartitioningSpecs } [Fact] - public async Task Messages_partitioned_by_file_size_are_split_into_a_corresponding_number_of_files() + public async Task I_can_export_a_channel_with_partitioning_based_on_file_size() { // Arrange using var dir = TempDir.Create(); diff --git a/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs index c4fbc7c..a46a021 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs @@ -8,7 +8,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class PlainTextContentSpecs { [Fact] - public async Task Messages_are_exported_correctly() + public async Task I_can_export_a_channel_in_the_TXT_format() { // Act var document = await ExportWrapper.ExportAsPlainTextAsync(ChannelIds.DateRangeTestCases); diff --git a/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs index 9bf4a8a..5e5411f 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs @@ -14,7 +14,7 @@ namespace DiscordChatExporter.Cli.Tests.Specs; public class SelfContainedSpecs { [Fact] - public async Task Messages_in_self_contained_export_only_reference_local_file_resources() + public async Task I_can_export_a_channel_and_download_all_referenced_assets() { // Arrange using var dir = TempDir.Create(); diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index cadd4f0..503b8db 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -30,8 +30,8 @@ public abstract class ExportCommandBase : DiscordCommandBase 'o', Description = "Output file or directory path. " + - "If a directory is specified, file names will be generated automatically based on the channel names and other parameters. " + - "Directory path should end with a slash to avoid ambiguity. " + + "Directory path must end with a slash to avoid ambiguity. " + + "If a directory is specified, file names will be generated automatically. " + "Supports template tokens, see the documentation for more info." )] public string OutputPath @@ -65,13 +65,16 @@ public abstract class ExportCommandBase : DiscordCommandBase "partition", 'p', Description = - "Split output into partitions, each limited to this number of messages (e.g. '100') or file size (e.g. '10mb')." + "Split the output into partitions, each limited to the specified " + + "number of messages (e.g. '100') or file size (e.g. '10mb')." )] public PartitionLimit PartitionLimit { get; init; } = PartitionLimit.Null; [CommandOption( "filter", - Description = "Only include messages that satisfy this filter (e.g. 'from:foo#1234' or 'has:image')." + Description = + "Only include messages that satisfy this filter. " + + "See the documentation for more info." )] public MessageFilter MessageFilter { get; init; } = MessageFilter.Null; @@ -103,7 +106,9 @@ public abstract class ExportCommandBase : DiscordCommandBase [CommandOption( "media-dir", - Description = "Download assets to this directory. If not specified, the asset directory path will be derived from the output path." + Description = + "Download assets to this directory. " + + "If not specified, the asset directory path will be derived from the output path." )] public string? AssetsDirPath { @@ -123,6 +128,7 @@ public abstract class ExportCommandBase : DiscordCommandBase "fuck-russia", EnvironmentVariable = "FUCK_RUSSIA", Description = "Don't print the Support Ukraine message to the console.", + // Use a converter to accept '1' as 'true' to reuse the existing environment variable Converter = typeof(TruthyBooleanBindingConverter) )] public bool IsUkraineSupportMessageDisabled { get; init; } @@ -132,7 +138,7 @@ public abstract class ExportCommandBase : DiscordCommandBase protected async ValueTask ExecuteAsync(IConsole console, IReadOnlyList channels) { - // Reuse assets option should only be used when the download assets option is set. + // Asset reuse can only be enabled if the download assets option is set // https://github.com/Tyrrrz/DiscordChatExporter/issues/425 if (ShouldReuseAssets && !ShouldDownloadAssets) { @@ -141,7 +147,7 @@ public abstract class ExportCommandBase : DiscordCommandBase ); } - // Assets directory should only be specified when the download assets option is set + // Assets directory can only be specified if the download assets option is set if (!string.IsNullOrWhiteSpace(AssetsDirPath) && !ShouldDownloadAssets) { throw new CommandException( @@ -149,8 +155,8 @@ public abstract class ExportCommandBase : DiscordCommandBase ); } - // Make sure the user does not try to export all channels into a single file. - // Output path must either be a directory, or contain template tokens. + // Make sure the user does not try to export multiple channels into one file. + // Output path must either be a directory or contain template tokens for this to work. // https://github.com/Tyrrrz/DiscordChatExporter/issues/799 // https://github.com/Tyrrrz/DiscordChatExporter/issues/917 var isValidOutputPath = @@ -225,7 +231,7 @@ public abstract class ExportCommandBase : DiscordCommandBase ); }); - // Print result + // Print the result using (console.WithForegroundColor(ConsoleColor.White)) { await console.Output.WriteLineAsync( @@ -240,28 +246,26 @@ public abstract class ExportCommandBase : DiscordCommandBase using (console.WithForegroundColor(ConsoleColor.Red)) { - await console.Output.WriteLineAsync( + await console.Error.WriteLineAsync( $"Failed to export {errors.Count} channel(s):" ); } foreach (var (channel, error) in errors) { - await console.Output.WriteAsync($"{channel.Category.Name} / {channel.Name}: "); + await console.Error.WriteAsync($"{channel.Category.Name} / {channel.Name}: "); using (console.WithForegroundColor(ConsoleColor.Red)) - await console.Output.WriteLineAsync(error); + await console.Error.WriteLineAsync(error); } - await console.Output.WriteLineAsync(); + await console.Error.WriteLineAsync(); } // Fail the command only if ALL channels failed to export. - // Having some of the channels fail to export is expected. + // If only some channels failed to export, it's okay. if (errors.Count >= channels.Count) - { throw new CommandException("Export failed."); - } } protected async ValueTask ExecuteAsync(IConsole console, IReadOnlyList channelIds) diff --git a/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs b/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs index 4b58df3..96abb0e 100644 --- a/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs @@ -13,7 +13,7 @@ using JsonExtensions.Reading; namespace DiscordChatExporter.Cli.Commands; -[Command("exportall", Description = "Export all accessible channels.")] +[Command("exportall", Description = "Exports all accessible channels.")] public class ExportAllCommand : ExportCommandBase { [CommandOption( @@ -30,7 +30,9 @@ public class ExportAllCommand : ExportCommandBase [CommandOption( "data-package", - Description = "Path to the personal data package (ZIP file) requested from Discord. If provided, only channels referenced in the dump will be exported." + Description = + "Path to the personal data package (ZIP file) requested from Discord. " + + "If provided, only channels referenced in the dump will be exported." )] public string? DataPackageFilePath { get; init; } @@ -62,7 +64,7 @@ public class ExportAllCommand : ExportCommandBase var entry = archive.GetEntry("messages/index.json"); if (entry is null) - throw new CommandException("Cannot find channel index inside the data package."); + throw new CommandException("Could not find channel index inside the data package."); await using var stream = entry.Open(); using var document = await JsonDocument.ParseAsync(stream, default, cancellationToken); @@ -85,7 +87,7 @@ public class ExportAllCommand : ExportCommandBase } catch (DiscordChatExporterException) { - await console.Output.WriteLineAsync($"Channel '{channelName}' ({channelId}) is inaccessible."); + await console.Error.WriteLineAsync($"Channel '{channelName}' ({channelId}) is inaccessible."); } } } diff --git a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs index 1e76d79..79352d2 100644 --- a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs @@ -7,14 +7,16 @@ using DiscordChatExporter.Core.Discord; namespace DiscordChatExporter.Cli.Commands; -[Command("export", Description = "Export one or multiple channels.")] +[Command("export", Description = "Exports one or multiple channels.")] public class ExportChannelsCommand : ExportCommandBase { // TODO: change this to plural (breaking change) [CommandOption( "channel", 'c', - Description = "Channel ID(s). If provided with category IDs, all channels inside those categories will be exported." + Description = + "Channel ID(s). " + + "If provided with category ID(s), all channels inside those categories will be exported." )] public required IReadOnlyList ChannelIds { get; init; } diff --git a/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs b/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs index 608adcb..b6f7862 100644 --- a/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs @@ -7,7 +7,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; -[Command("exportdm", Description = "Export all direct message channels.")] +[Command("exportdm", Description = "Exports all direct message channels.")] public class ExportDirectMessagesCommand : ExportCommandBase { public override async ValueTask ExecuteAsync(IConsole console) diff --git a/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs b/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs index 346172f..b3dde50 100644 --- a/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs @@ -9,7 +9,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; -[Command("exportguild", Description = "Export all channels within specified guild.")] +[Command("exportguild", Description = "Exports all channels within the specified guild.")] public class ExportGuildCommand : ExportCommandBase { [CommandOption( diff --git a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs index 10bdabc..f8f0ec3 100644 --- a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs @@ -22,7 +22,7 @@ public class GetChannelsCommand : DiscordCommandBase [CommandOption( "include-threads", - Description = "Display threads alongside channels." + Description = "Include threads in the output." )] public bool IncludeThreads { get; init; } diff --git a/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs index 7bb0304..5271de7 100644 --- a/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs @@ -9,7 +9,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; -[Command("dm", Description = "Get the list of direct message channels.")] +[Command("dm", Description = "Gets the list of all direct message channels.")] public class GetDirectChannelsCommand : DiscordCommandBase { public override async ValueTask ExecuteAsync(IConsole console) diff --git a/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs b/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs index 4bd38c0..e5efe3e 100644 --- a/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs @@ -9,7 +9,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; -[Command("guilds", Description = "Get the list of accessible guilds.")] +[Command("guilds", Description = "Gets the list of accessible guilds.")] public class GetGuildsCommand : DiscordCommandBase { public override async ValueTask ExecuteAsync(IConsole console) diff --git a/DiscordChatExporter.Cli/Commands/GuideCommand.cs b/DiscordChatExporter.Cli/Commands/GuideCommand.cs index e9f08b8..e7ab2b1 100644 --- a/DiscordChatExporter.Cli/Commands/GuideCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GuideCommand.cs @@ -6,7 +6,7 @@ using CliFx.Infrastructure; namespace DiscordChatExporter.Cli.Commands; -[Command("guide", Description = "Explains how to obtain token, guild or channel ID.")] +[Command("guide", Description = "Explains how to obtain the token, guild or channel ID.")] public class GuideCommand : ICommand { public ValueTask ExecuteAsync(IConsole console) diff --git a/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs b/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs index f6b73c3..41e68b8 100644 --- a/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs +++ b/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs @@ -1,7 +1,6 @@ namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/channel#channel-object-channel-types -// Order of enum fields needs to match the order in the docs. public enum ChannelKind { GuildTextChat = 0, diff --git a/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs b/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs index 0687380..680afed 100644 --- a/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs +++ b/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Text.Json; +using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; diff --git a/DiscordChatExporter.Core/Discord/Data/Common/IdBasedEqualityComparer.cs b/DiscordChatExporter.Core/Discord/Data/Common/IdBasedEqualityComparer.cs deleted file mode 100644 index 42468ae..0000000 --- a/DiscordChatExporter.Core/Discord/Data/Common/IdBasedEqualityComparer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace DiscordChatExporter.Core.Discord.Data.Common; - -public class IdBasedEqualityComparer : IEqualityComparer -{ - public static IdBasedEqualityComparer Instance { get; } = new(); - - public bool Equals(IHasId? x, IHasId? y) => x?.Id == y?.Id; - - public int GetHashCode(IHasId obj) => obj.Id.GetHashCode(); -} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Discord/Data/Emoji.cs b/DiscordChatExporter.Core/Discord/Data/Emoji.cs index 2138155..408b130 100644 --- a/DiscordChatExporter.Core/Discord/Data/Emoji.cs +++ b/DiscordChatExporter.Core/Discord/Data/Emoji.cs @@ -34,8 +34,7 @@ public partial record Emoji if (!string.IsNullOrWhiteSpace(name)) return ImageCdn.GetStandardEmojiUrl(name); - // Either ID or name should be set - throw new ApplicationException("Emoji has neither ID nor name set."); + throw new InvalidOperationException("Either the emoji ID or name should be provided."); } public static Emoji Parse(JsonElement json) diff --git a/DiscordChatExporter.Core/Discord/Data/MessageKind.cs b/DiscordChatExporter.Core/Discord/Data/MessageKind.cs index d605bc9..6d55348 100644 --- a/DiscordChatExporter.Core/Discord/Data/MessageKind.cs +++ b/DiscordChatExporter.Core/Discord/Data/MessageKind.cs @@ -17,5 +17,5 @@ public enum MessageKind public static class MessageKindExtensions { - public static bool IsSystemNotification(this MessageKind c) => (int)c is >= 1 and <= 18; + public static bool IsSystemNotification(this MessageKind kind) => (int)kind is >= 1 and <= 18; } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index fff6109..4ea2395 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -238,11 +238,8 @@ public class DiscordClient ? categories.GetValueOrDefault(parentId) : null; - var channel = Channel.Parse(channelJson, category, position); - + yield return Channel.Parse(channelJson, category, position); position++; - - yield return channel; } } } @@ -288,8 +285,8 @@ public class DiscordClient var response = await GetJsonResponseAsync($"channels/{channelId}", cancellationToken); return ChannelCategory.Parse(response); } - // In some cases, the Discord API returns an empty body when requesting a channel. - // Return an empty channel category as fallback in these cases. + // In some cases, Discord API returns an empty body when requesting a channel. + // Use an empty channel category as fallback for these cases. catch (DiscordChatExporterException) { return new ChannelCategory(channelId, "Unknown Category", 0); @@ -371,8 +368,9 @@ public class DiscordClient if (lastMessage is null || lastMessage.Timestamp < after?.ToDate()) yield break; - // Keep track of the first message in range in order to calculate progress + // Keep track of the first message in range in order to calculate the progress var firstMessage = default(Message); + var currentAfter = after ?? Snowflake.Zero; while (true) { diff --git a/DiscordChatExporter.Core/Discord/Snowflake.cs b/DiscordChatExporter.Core/Discord/Snowflake.cs index f2cb89e..18f9e50 100644 --- a/DiscordChatExporter.Core/Discord/Snowflake.cs +++ b/DiscordChatExporter.Core/Discord/Snowflake.cs @@ -29,15 +29,11 @@ public partial record struct Snowflake // As number if (ulong.TryParse(str, NumberStyles.None, formatProvider, out var value)) - { return new Snowflake(value); - } // As date if (DateTimeOffset.TryParse(str, formatProvider, DateTimeStyles.None, out var instant)) - { return FromDate(instant); - } return null; } diff --git a/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs b/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs index 9f2846d..e506742 100644 --- a/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs @@ -89,11 +89,15 @@ internal partial class CsvMessageWriter : MessageWriter // Message content if (message.Kind.IsSystemNotification()) { - await _writer.WriteAsync(CsvEncode(message.GetFallbackContent())); + await _writer.WriteAsync(CsvEncode( + message.GetFallbackContent() + )); } else { - await _writer.WriteAsync(CsvEncode(await FormatMarkdownAsync(message.Content, cancellationToken))); + await _writer.WriteAsync(CsvEncode( + await FormatMarkdownAsync(message.Content, cancellationToken) + )); } await _writer.WriteAsync(','); diff --git a/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs b/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs index 046e173..2a0ed91 100644 --- a/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs +++ b/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs @@ -75,9 +75,8 @@ internal partial class ExportAssetDownloader catch { // This can apparently fail for some reason. + // Updating the file date is not a critical task, so we'll just ignore exceptions thrown here. // https://github.com/Tyrrrz/DiscordChatExporter/issues/585 - // Updating file dates is not a critical task, so we'll just - // ignore exceptions thrown here. } }); @@ -98,7 +97,7 @@ internal partial class ExportAssetDownloader { var urlHash = GetUrlHash(url); - // Try to extract file name from URL + // Try to extract the file name from URL var fileName = Regex.Match(url, @".+/([^?]*)").Groups[1].Value; // If it's not there, just use the URL hash as the file name @@ -110,7 +109,7 @@ internal partial class ExportAssetDownloader var fileExtension = Path.GetExtension(fileName); // Probably not a file extension, just a dot in a long file name - // https://github.com/Tyrrrz/DiscordChatExporter/issues/708 + // https://github.com/Tyrrrz/DiscordChatExporter/pull/812 if (fileExtension.Length > 41) { fileNameWithoutExtension = fileName; diff --git a/DiscordChatExporter.Core/Exporting/ExportContext.cs b/DiscordChatExporter.Core/Exporting/ExportContext.cs index 1b359a4..9051f97 100644 --- a/DiscordChatExporter.Core/Exporting/ExportContext.cs +++ b/DiscordChatExporter.Core/Exporting/ExportContext.cs @@ -55,10 +55,13 @@ internal class ExportContext var member = await Discord.TryGetGuildMemberAsync(Request.Guild.Id, id, cancellationToken); - // User may have left the guild since they were mentioned + // User may have left the guild since they were mentioned. + // Create a dummy member object based on the user info. if (member is null) { var user = fallbackUser ?? await Discord.TryGetUserAsync(id, cancellationToken); + + // User may have been deleted since they were mentioned if (user is not null) member = Member.CreateDefault(user); } @@ -114,7 +117,7 @@ internal class ExportContext var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath); // Prefer relative paths so that the output files can be copied around without breaking references. - // If the assets path is outside of the export directory, use the absolute path instead. + // If the assets path is outside of the export directory, use an absolute path instead. var optimalFilePath = relativeFilePath.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal) || relativeFilePath.StartsWith(".." + Path.AltDirectorySeparatorChar, StringComparison.Ordinal) @@ -135,8 +138,8 @@ internal class ExportContext // https://github.com/Tyrrrz/DiscordChatExporter/issues/372 catch (Exception ex) when (ex is HttpRequestException or OperationCanceledException) { - // TODO: add logging so we can be more liberal with catching exceptions - // We don't want this to crash the exporting process in case of failure + // We don't want this to crash the exporting process in case of failure. + // TODO: add logging so we can be more liberal with catching exceptions. return url; } } diff --git a/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs b/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs index f4c0edf..2ac4647 100644 --- a/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs @@ -52,12 +52,12 @@ internal class HtmlMessageWriter : MessageWriter if ((message.Timestamp - lastMessage.Timestamp).Duration().TotalMinutes > 7) return false; - // Messages must be from the same author + // Messages must be sent by the same author if (message.Author.Id != lastMessage.Author.Id) return false; - // If the user changed their name after the last message, their new messages - // cannot join an existing group. + // If the author changed their name after the last message, their new messages + // cannot join the existing group. if (!string.Equals(message.Author.FullName, lastMessage.Author.FullName, StringComparison.Ordinal)) return false; } diff --git a/DiscordChatExporter.Core/Exporting/MessageExporter.cs b/DiscordChatExporter.Core/Exporting/MessageExporter.cs index a5ad396..b0d992c 100644 --- a/DiscordChatExporter.Core/Exporting/MessageExporter.cs +++ b/DiscordChatExporter.Core/Exporting/MessageExporter.cs @@ -39,7 +39,7 @@ internal partial class MessageExporter : IAsyncDisposable private async ValueTask GetWriterAsync(CancellationToken cancellationToken = default) { - // Ensure partition limit has not been reached + // Ensure that the partition limit has not been reached if (_writer is not null && _context.Request.PartitionLimit.IsReached(_writer.MessagesWritten, _writer.BytesWritten)) { @@ -74,11 +74,11 @@ internal partial class MessageExporter { private static string GetPartitionFilePath(string baseFilePath, int partitionIndex) { - // First partition, don't change file name + // First partition, don't change the file name if (partitionIndex <= 0) return baseFilePath; - // Inject partition index into file name + // Inject partition index into the file name var fileNameWithoutExt = Path.GetFileNameWithoutExtension(baseFilePath); var fileExt = Path.GetExtension(baseFilePath); var fileName = $"{fileNameWithoutExt} [part {partitionIndex + 1}]{fileExt}"; diff --git a/DiscordChatExporter.Core/Utils/Http.cs b/DiscordChatExporter.Core/Utils/Http.cs index ecc3900..84b87c8 100644 --- a/DiscordChatExporter.Core/Utils/Http.cs +++ b/DiscordChatExporter.Core/Utils/Http.cs @@ -16,7 +16,7 @@ public static class Http private static bool IsRetryableStatusCode(HttpStatusCode statusCode) => statusCode is HttpStatusCode.TooManyRequests or HttpStatusCode.RequestTimeout || - // Treat all server-side errors as retryable. + // Treat all server-side errors as retryable // https://github.com/Tyrrrz/DiscordChatExporter/issues/908 (int)statusCode >= 500; diff --git a/DiscordChatExporter.Gui/Bootstrapper.cs b/DiscordChatExporter.Gui/Bootstrapper.cs index 9024665..b638706 100644 --- a/DiscordChatExporter.Gui/Bootstrapper.cs +++ b/DiscordChatExporter.Gui/Bootstrapper.cs @@ -17,8 +17,8 @@ public class Bootstrapper : Bootstrapper { base.OnStart(); - // Set default theme - // (preferred theme will be set later, once the settings are loaded) + // Set the default theme. + // Preferred theme will be set later, once the settings are loaded. App.SetLightTheme(); } @@ -26,10 +26,7 @@ public class Bootstrapper : Bootstrapper { base.ConfigureIoC(builder); - // Bind settings as singleton builder.Bind().ToSelf().InSingletonScope(); - - // Bind view model factory builder.Bind().ToAbstractFactory(); } diff --git a/DiscordChatExporter.Gui/Services/SettingsService.cs b/DiscordChatExporter.Gui/Services/SettingsService.cs index 07d718f..8b8a519 100644 --- a/DiscordChatExporter.Gui/Services/SettingsService.cs +++ b/DiscordChatExporter.Gui/Services/SettingsService.cs @@ -45,7 +45,7 @@ public partial class SettingsService : SettingsBase public override void Save() { - // Clear token if it's not supposed to be persisted + // Clear the token if it's not supposed to be persisted if (!IsTokenPersisted) LastToken = null; diff --git a/DiscordChatExporter.Gui/Utils/ProcessEx.cs b/DiscordChatExporter.Gui/Utils/ProcessEx.cs index 7be814c..263a40e 100644 --- a/DiscordChatExporter.Gui/Utils/ProcessEx.cs +++ b/DiscordChatExporter.Gui/Utils/ProcessEx.cs @@ -6,13 +6,15 @@ internal static class ProcessEx { public static void StartShellExecute(string path) { - var startInfo = new ProcessStartInfo(path) + using var process = new Process { - UseShellExecute = true + StartInfo = new ProcessStartInfo + { + FileName = path, + UseShellExecute = true + } }; - using (Process.Start(startInfo)) - { - } + process.Start(); } } \ No newline at end of file diff --git a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs index 4044d5c..1d8b36a 100644 --- a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs @@ -156,14 +156,11 @@ public class DashboardViewModel : PropertyChangedBase var exporter = new ChannelExporter(_discord); - var channelProgressPairs = dialog - .Channels! - .Select(c => new - { - Channel = c, - Progress = _progressMuxer.CreateInput() - }) - .ToArray(); + var channelProgressPairs = dialog.Channels!.Select(c => new + { + Channel = c, + Progress = _progressMuxer.CreateInput() + }).ToArray(); var successfulExportCount = 0; @@ -213,7 +210,7 @@ public class DashboardViewModel : PropertyChangedBase } ); - // Notify of overall completion + // Notify of the overall completion if (successfulExportCount > 0) { _eventAggregator.Publish( diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs b/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs index 051c0f9..9091e99 100644 --- a/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs +++ b/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs @@ -25,7 +25,7 @@ public class DialogManager : IDisposable void OnDialogOpened(object? openSender, DialogOpenedEventArgs openArgs) { - void OnScreenClosed(object? closeSender, EventArgs args) + void OnScreenClosed(object? closeSender, EventArgs closeArgs) { try { diff --git a/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs b/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs index 099bbb2..1137b8c 100644 --- a/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs +++ b/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs @@ -1,8 +1,3 @@ namespace DiscordChatExporter.Gui.ViewModels.Messages; -public class NotificationMessage -{ - public string Text { get; } - - public NotificationMessage(string text) => Text = text; -} \ No newline at end of file +public record NotificationMessage(string Text); \ No newline at end of file diff --git a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs index 4ed36db..775ee64 100644 --- a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs @@ -62,9 +62,7 @@ public class RootViewModel : Screen, IHandle, IDisposable _settingsService.Save(); if (await _dialogManager.ShowDialogAsync(dialog) == true) - { ProcessEx.StartShellExecute("https://tyrrrz.me/ukraine?source=discordchatexporter"); - } } private async ValueTask CheckForUpdatesAsync() @@ -106,7 +104,7 @@ public class RootViewModel : Screen, IHandle, IDisposable _settingsService.Load(); - // Sync theme with settings + // Sync the theme with settings if (_settingsService.IsDarkModeEnabled) { App.SetDarkTheme(); @@ -116,7 +114,7 @@ public class RootViewModel : Screen, IHandle, IDisposable App.SetLightTheme(); } - // App has just been updated, display changelog + // App has just been updated, display the changelog if (_settingsService.LastAppVersion is not null && _settingsService.LastAppVersion != App.Version) { Notifications.Enqueue( @@ -137,8 +135,7 @@ public class RootViewModel : Screen, IHandle, IDisposable _updateService.FinalizeUpdate(false); } - public void Handle(NotificationMessage message) => - Notifications.Enqueue(message.Text); + public void Handle(NotificationMessage message) => Notifications.Enqueue(message.Text); public void Dispose() => Notifications.Dispose(); } \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml index ee5152a..9c9708a 100644 --- a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml +++ b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml @@ -221,7 +221,7 @@ materialDesign:HintAssist.IsFloating="True" Style="{DynamicResource MaterialDesignOutlinedTextBox}" Text="{Binding PartitionLimitValue}" - ToolTip="Split output into partitions, each limited to this number of messages (e.g. '100') or file size (e.g. '10mb')" /> + ToolTip="Split the output into partitions, each limited to the specified number of messages (e.g. '100') or file size (e.g. '10mb')" />