diff --git a/Directory.Packages.props b/Directory.Packages.props index ebbc7454..45405453 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,7 +10,7 @@ - + @@ -26,7 +26,10 @@ - + diff --git a/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs index 049d573a..55204343 100644 --- a/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using CliFx; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Utils; @@ -17,23 +17,22 @@ public abstract class DiscordCommandBase : ICommand EnvironmentVariable = "DISCORD_TOKEN", Description = "Authentication token." )] - public required string Token { get; init; } + public required string Token { get; set; } - [Obsolete("This option doesn't do anything. Kept for backwards compatibility.")] [CommandOption( "bot", 'b', EnvironmentVariable = "DISCORD_TOKEN_BOT", Description = "This option doesn't do anything. Kept for backwards compatibility." )] - public bool IsBotToken { get; init; } = false; + public bool IsBotToken { get; set; } = false; [CommandOption( "respect-rate-limits", Description = "Whether to respect advisory rate limits. " + "If disabled, only hard rate limits (i.e. 429 responses) will be respected." )] - public bool ShouldRespectRateLimits { get; init; } = true; + public bool ShouldRespectRateLimits { get; set; } = true; [field: AllowNull, MaybeNull] protected DiscordClient Discord => diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index 070c8e88..2b3c8400 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -5,8 +5,8 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; -using CliFx.Exceptions; +using CliFx; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Converters; using DiscordChatExporter.Cli.Commands.Shared; @@ -37,23 +37,23 @@ public abstract class ExportCommandBase : DiscordCommandBase get; // Handle ~/ in paths on Unix systems // https://github.com/Tyrrrz/DiscordChatExporter/pull/903 - init => field = Path.GetFullPath(value); + set => field = Path.GetFullPath(value); } = Directory.GetCurrentDirectory(); [CommandOption("format", 'f', Description = "Export format.")] - public ExportFormat ExportFormat { get; init; } = ExportFormat.HtmlDark; + public ExportFormat ExportFormat { get; set; } = ExportFormat.HtmlDark; [CommandOption( "after", Description = "Only include messages sent after this date or message ID." )] - public Snowflake? After { get; init; } + public Snowflake? After { get; set; } [CommandOption( "before", Description = "Only include messages sent before this date or message ID." )] - public Snowflake? Before { get; init; } + public Snowflake? Before { get; set; } [CommandOption( "partition", @@ -61,51 +61,51 @@ public abstract class ExportCommandBase : DiscordCommandBase Description = "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; + public PartitionLimit PartitionLimit { get; set; } = PartitionLimit.Null; [CommandOption( "include-threads", Description = "Which types of threads should be included.", - Converter = typeof(ThreadInclusionModeBindingConverter) + Converter = typeof(ThreadInclusionModeInputConverter) )] - public ThreadInclusionMode ThreadInclusionMode { get; init; } = ThreadInclusionMode.None; + public ThreadInclusionMode ThreadInclusionMode { get; set; } = ThreadInclusionMode.None; [CommandOption( "filter", Description = "Only include messages that satisfy this filter. " + "See the documentation for more info." )] - public MessageFilter MessageFilter { get; init; } = MessageFilter.Null; + public MessageFilter MessageFilter { get; set; } = MessageFilter.Null; [CommandOption( "parallel", Description = "Limits how many channels can be exported in parallel." )] - public int ParallelLimit { get; init; } = 1; + public int ParallelLimit { get; set; } = 1; [CommandOption( "reverse", Description = "Export messages in reverse chronological order (newest first)." )] - public bool IsReverseMessageOrder { get; init; } + public bool IsReverseMessageOrder { get; set; } [CommandOption( "markdown", Description = "Process markdown, mentions, and other special tokens." )] - public bool ShouldFormatMarkdown { get; init; } = true; + public bool ShouldFormatMarkdown { get; set; } = true; [CommandOption( "media", Description = "Download assets referenced by the export (user avatars, attached files, embedded images, etc.)." )] - public bool ShouldDownloadAssets { get; init; } + public bool ShouldDownloadAssets { get; set; } [CommandOption( "reuse-media", Description = "Reuse previously downloaded assets to avoid redundant requests." )] - public bool ShouldReuseAssets { get; init; } = false; + public bool ShouldReuseAssets { get; set; } = false; [CommandOption( "media-dir", @@ -117,34 +117,33 @@ public abstract class ExportCommandBase : DiscordCommandBase get; // Handle ~/ in paths on Unix systems // https://github.com/Tyrrrz/DiscordChatExporter/pull/903 - init => field = value is not null ? Path.GetFullPath(value) : null; + set => field = value is not null ? Path.GetFullPath(value) : null; } - [Obsolete("This option doesn't do anything. Kept for backwards compatibility.")] [CommandOption( "dateformat", Description = "This option doesn't do anything. Kept for backwards compatibility." )] - public string DateFormat { get; init; } = "MM/dd/yyyy h:mm tt"; + public string DateFormat { get; set; } = "MM/dd/yyyy h:mm tt"; [CommandOption( "locale", Description = "Locale to use when formatting dates and numbers. " + "If not specified, the default system locale will be used." )] - public string? Locale { get; init; } + public string? Locale { get; set; } [CommandOption("utc", Description = "Normalize all timestamps to UTC+0.")] - public bool IsUtcNormalizationEnabled { get; init; } = false; + public bool IsUtcNormalizationEnabled { get; set; } = false; [CommandOption( "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) + Converter = typeof(TruthyBooleanInputConverter) )] - public bool IsUkraineSupportMessageDisabled { get; init; } = false; + public bool IsUkraineSupportMessageDisabled { get; set; } = false; [field: AllowNull, MaybeNull] protected ChannelExporter Exporter => field ??= new ChannelExporter(Discord); diff --git a/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeBindingConverter.cs b/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeInputConverter.cs similarity index 86% rename from DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeBindingConverter.cs rename to DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeInputConverter.cs index 7857024f..716e2539 100644 --- a/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeBindingConverter.cs +++ b/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeInputConverter.cs @@ -1,10 +1,10 @@ using System; -using CliFx.Extensibility; +using CliFx.Activation; using DiscordChatExporter.Cli.Commands.Shared; namespace DiscordChatExporter.Cli.Commands.Converters; -internal class ThreadInclusionModeBindingConverter : BindingConverter +internal class ThreadInclusionModeInputConverter : ScalarInputConverter { public override ThreadInclusionMode Convert(string? rawValue) { diff --git a/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanBindingConverter.cs b/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanInputConverter.cs similarity index 86% rename from DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanBindingConverter.cs rename to DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanInputConverter.cs index 5f4abd20..33977b9c 100644 --- a/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanBindingConverter.cs +++ b/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanInputConverter.cs @@ -1,9 +1,9 @@ using System.Globalization; -using CliFx.Extensibility; +using CliFx.Activation; namespace DiscordChatExporter.Cli.Commands.Converters; -internal class TruthyBooleanBindingConverter : BindingConverter +internal class TruthyBooleanInputConverter : ScalarInputConverter { public override bool Convert(string? rawValue) { diff --git a/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs b/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs index 5d156e84..464ecf0a 100644 --- a/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Cli.Utils.Extensions; @@ -14,23 +14,23 @@ using Spectre.Console; namespace DiscordChatExporter.Cli.Commands; [Command("exportall", Description = "Exports all accessible channels.")] -public class ExportAllCommand : ExportCommandBase +public partial class ExportAllCommand : ExportCommandBase { [CommandOption("include-dm", Description = "Include direct message channels.")] - public bool IncludeDirectChannels { get; init; } = true; + public bool IncludeDirectChannels { get; set; } = true; [CommandOption("include-guilds", Description = "Include server channels.")] - public bool IncludeGuildChannels { get; init; } = true; + public bool IncludeGuildChannels { get; set; } = true; [CommandOption("include-vc", Description = "Include voice channels.")] - public bool IncludeVoiceChannels { get; init; } = true; + public bool IncludeVoiceChannels { get; set; } = true; [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." )] - public string? DataPackageFilePath { get; init; } + public string? DataPackageFilePath { get; set; } public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs index a0e1e94c..3e963cad 100644 --- a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord; @@ -10,7 +10,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("export", Description = "Exports one or multiple channels.")] -public class ExportChannelsCommand : ExportCommandBase +public partial class ExportChannelsCommand : ExportCommandBase { // TODO: change this to plural (breaking change) [CommandOption( @@ -19,7 +19,7 @@ public class ExportChannelsCommand : ExportCommandBase Description = "Channel ID(s). " + "If provided with category ID(s), all channels inside those categories will be exported." )] - public required IReadOnlyList ChannelIds { get; init; } + public required IReadOnlyList ChannelIds { get; set; } public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs b/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs index 69c80f79..39f41f40 100644 --- a/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord.Data; @@ -8,7 +8,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("exportdm", Description = "Exports all direct message channels.")] -public class ExportDirectMessagesCommand : ExportCommandBase +public partial 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 90eb3d79..037d8c58 100644 --- a/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Cli.Utils.Extensions; @@ -11,13 +11,13 @@ using Spectre.Console; namespace DiscordChatExporter.Cli.Commands; [Command("exportguild", Description = "Exports all channels within the specified server.")] -public class ExportGuildCommand : ExportCommandBase +public partial class ExportGuildCommand : ExportCommandBase { [CommandOption("guild", 'g', Description = "Server ID.")] - public required Snowflake GuildId { get; init; } + public required Snowflake GuildId { get; set; } [CommandOption("include-vc", Description = "Include voice channels.")] - public bool IncludeVoiceChannels { get; init; } = true; + public bool IncludeVoiceChannels { get; set; } = true; public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs index 60b152fe..b0116ed7 100644 --- a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Cli.Commands.Converters; @@ -12,20 +12,20 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("channels", Description = "Get the list of channels in a server.")] -public class GetChannelsCommand : DiscordCommandBase +public partial class GetChannelsCommand : DiscordCommandBase { [CommandOption("guild", 'g', Description = "Server ID.")] - public required Snowflake GuildId { get; init; } + public required Snowflake GuildId { get; set; } [CommandOption("include-vc", Description = "Include voice channels.")] - public bool IncludeVoiceChannels { get; init; } = true; + public bool IncludeVoiceChannels { get; set; } = true; [CommandOption( "include-threads", Description = "Which types of threads should be included.", - Converter = typeof(ThreadInclusionModeBindingConverter) + Converter = typeof(ThreadInclusionModeInputConverter) )] - public ThreadInclusionMode ThreadInclusionMode { get; init; } = ThreadInclusionMode.None; + public ThreadInclusionMode ThreadInclusionMode { get; set; } = ThreadInclusionMode.None; public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs index daedf096..06ea69b1 100644 --- a/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord.Data; @@ -10,7 +10,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("dm", Description = "Gets the list of all direct message channels.")] -public class GetDirectChannelsCommand : DiscordCommandBase +public partial 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 42642c41..6d8b5410 100644 --- a/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord.Data; @@ -10,7 +10,7 @@ using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("guilds", Description = "Gets the list of accessible servers.")] -public class GetGuildsCommand : DiscordCommandBase +public partial 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 f4bd09cf..c0270c5d 100644 --- a/DiscordChatExporter.Cli/Commands/GuideCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GuideCommand.cs @@ -1,13 +1,13 @@ using System; using System.Threading.Tasks; using CliFx; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; namespace DiscordChatExporter.Cli.Commands; [Command("guide", Description = "Explains how to obtain the token, server or channel ID.")] -public class GuideCommand : ICommand +public partial class GuideCommand : ICommand { public ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj index 6317bcca..9e4b02c8 100644 --- a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj +++ b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj @@ -6,12 +6,6 @@ false - - - false - false - - diff --git a/DiscordChatExporter.Cli/Program.cs b/DiscordChatExporter.Cli/Program.cs index 2dfa24ba..776d2e0b 100644 --- a/DiscordChatExporter.Cli/Program.cs +++ b/DiscordChatExporter.Cli/Program.cs @@ -1,41 +1,13 @@ -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; +using System.Threading.Tasks; using CliFx; -using DiscordChatExporter.Cli.Commands; -using DiscordChatExporter.Cli.Commands.Converters; -using DiscordChatExporter.Core.Exporting.Filtering; -using DiscordChatExporter.Core.Exporting.Partitioning; namespace DiscordChatExporter.Cli; public static class Program { - // Explicit references because CliFx relies on reflection and we're publishing with trimming enabled - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportAllCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportChannelsCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportDirectMessagesCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportGuildCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GetChannelsCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GetDirectChannelsCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GetGuildsCommand))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GuideCommand))] - [DynamicDependency( - DynamicallyAccessedMemberTypes.All, - typeof(ThreadInclusionModeBindingConverter) - )] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TruthyBooleanBindingConverter))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PartitionLimit))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(MessageFilter))] public static async Task Main(string[] args) => - await new CliApplicationBuilder() - .AddCommand() - .AddCommand() - .AddCommand() - .AddCommand() - .AddCommand() - .AddCommand() - .AddCommand() - .AddCommand() + await new CommandLineApplicationBuilder() + .AddCommandsFromThisAssembly() .Build() .RunAsync(args); } diff --git a/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj b/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj index b8950ea5..a35c363c 100644 --- a/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj +++ b/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj @@ -21,12 +21,6 @@ false - - - false - false - - diff --git a/DiscordChatExporter.Gui/PublishMacOSBundle.csx b/DiscordChatExporter.Gui/PublishMacOSBundle.csx index 080d5f24..3b2d7e80 100755 --- a/DiscordChatExporter.Gui/PublishMacOSBundle.csx +++ b/DiscordChatExporter.Gui/PublishMacOSBundle.csx @@ -2,16 +2,16 @@ #:package CliFx using CliFx; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; -return await new CliApplicationBuilder() - .AddCommand() +return await new CommandLineApplicationBuilder() + .AddCommandsFromThisAssembly() .Build() .RunAsync(args); [Command(Description = "Publishes the GUI app as a macOS .app bundle.")] -public class PublishMacOSBundleCommand : ICommand +public partial class PublishMacOSBundleCommand : ICommand { private const string BundleName = "DiscordChatExporter.app"; private const string AppName = "DiscordChatExporter"; @@ -21,16 +21,16 @@ public class PublishMacOSBundleCommand : ICommand private const string AppIconName = "AppIcon"; [CommandOption("publish-dir", Description = "Path to the publish output directory.")] - public required string PublishDirPath { get; init; } + public required string PublishDirPath { get; set; } [CommandOption("icons-file", Description = "Path to the .icns icons file.")] - public required string IconsFilePath { get; init; } + public required string IconsFilePath { get; set; } [CommandOption("full-version", Description = "Full version string (e.g. '1.2.3.4').")] - public required string FullVersion { get; init; } + public required string FullVersion { get; set; } [CommandOption("short-version", Description = "Short version string (e.g. '1.2.3').")] - public required string ShortVersion { get; init; } + public required string ShortVersion { get; set; } public async ValueTask ExecuteAsync(IConsole console) {