|
|
@ -30,8 +30,8 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
'o',
|
|
|
|
'o',
|
|
|
|
Description =
|
|
|
|
Description =
|
|
|
|
"Output file or directory path. " +
|
|
|
|
"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 must end with a slash to avoid ambiguity. " +
|
|
|
|
"Directory path should 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."
|
|
|
|
"Supports template tokens, see the documentation for more info."
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
public string OutputPath
|
|
|
|
public string OutputPath
|
|
|
@ -65,13 +65,16 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
"partition",
|
|
|
|
"partition",
|
|
|
|
'p',
|
|
|
|
'p',
|
|
|
|
Description =
|
|
|
|
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;
|
|
|
|
public PartitionLimit PartitionLimit { get; init; } = PartitionLimit.Null;
|
|
|
|
|
|
|
|
|
|
|
|
[CommandOption(
|
|
|
|
[CommandOption(
|
|
|
|
"filter",
|
|
|
|
"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;
|
|
|
|
public MessageFilter MessageFilter { get; init; } = MessageFilter.Null;
|
|
|
|
|
|
|
|
|
|
|
@ -103,7 +106,9 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
|
|
|
|
|
|
|
|
[CommandOption(
|
|
|
|
[CommandOption(
|
|
|
|
"media-dir",
|
|
|
|
"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
|
|
|
|
public string? AssetsDirPath
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -123,6 +128,7 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
"fuck-russia",
|
|
|
|
"fuck-russia",
|
|
|
|
EnvironmentVariable = "FUCK_RUSSIA",
|
|
|
|
EnvironmentVariable = "FUCK_RUSSIA",
|
|
|
|
Description = "Don't print the Support Ukraine message to the console.",
|
|
|
|
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(TruthyBooleanBindingConverter)
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
public bool IsUkraineSupportMessageDisabled { get; init; }
|
|
|
|
public bool IsUkraineSupportMessageDisabled { get; init; }
|
|
|
@ -132,7 +138,7 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
|
|
|
|
|
|
|
|
protected async ValueTask ExecuteAsync(IConsole console, IReadOnlyList<Channel> channels)
|
|
|
|
protected async ValueTask ExecuteAsync(IConsole console, IReadOnlyList<Channel> 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
|
|
|
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/425
|
|
|
|
if (ShouldReuseAssets && !ShouldDownloadAssets)
|
|
|
|
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)
|
|
|
|
if (!string.IsNullOrWhiteSpace(AssetsDirPath) && !ShouldDownloadAssets)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
throw new CommandException(
|
|
|
|
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.
|
|
|
|
// 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.
|
|
|
|
// 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/799
|
|
|
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/917
|
|
|
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/917
|
|
|
|
var isValidOutputPath =
|
|
|
|
var isValidOutputPath =
|
|
|
@ -225,7 +231,7 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
);
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Print result
|
|
|
|
// Print the result
|
|
|
|
using (console.WithForegroundColor(ConsoleColor.White))
|
|
|
|
using (console.WithForegroundColor(ConsoleColor.White))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
await console.Output.WriteLineAsync(
|
|
|
|
await console.Output.WriteLineAsync(
|
|
|
@ -240,28 +246,26 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|
|
|
|
|
|
|
|
|
|
|
using (console.WithForegroundColor(ConsoleColor.Red))
|
|
|
|
using (console.WithForegroundColor(ConsoleColor.Red))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
await console.Output.WriteLineAsync(
|
|
|
|
await console.Error.WriteLineAsync(
|
|
|
|
$"Failed to export {errors.Count} channel(s):"
|
|
|
|
$"Failed to export {errors.Count} channel(s):"
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var (channel, error) in errors)
|
|
|
|
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))
|
|
|
|
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.
|
|
|
|
// 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)
|
|
|
|
if (errors.Count >= channels.Count)
|
|
|
|
{
|
|
|
|
|
|
|
|
throw new CommandException("Export failed.");
|
|
|
|
throw new CommandException("Export failed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected async ValueTask ExecuteAsync(IConsole console, IReadOnlyList<Snowflake> channelIds)
|
|
|
|
protected async ValueTask ExecuteAsync(IConsole console, IReadOnlyList<Snowflake> channelIds)
|
|
|
|