mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-05-30 06:25:22 -04:00
[CLI] Remove ExportMultipleCommandBase and allow multiple channel IDs in ExportChannelCommand
This commit is contained in:
parent
390735032e
commit
026927265a
7 changed files with 137 additions and 141 deletions
|
@ -1,12 +1,20 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Exceptions;
|
||||
using DiscordChatExporter.Core.Exporting;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
using Spectre.Console;
|
||||
using Tyrrrz.Extensions;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Commands.Base
|
||||
{
|
||||
|
@ -27,6 +35,9 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
|||
[CommandOption("partition", 'p', Description = "Split output into partitions limited to this number of messages.")]
|
||||
public int? PartitionLimit { get; init; }
|
||||
|
||||
[CommandOption("parallel", Description = "Limits how many channels can be exported in parallel.")]
|
||||
public int ParallelLimit { get; init; } = 1;
|
||||
|
||||
[CommandOption("media", Description = "Download referenced media content.")]
|
||||
public bool ShouldDownloadMedia { get; init; }
|
||||
|
||||
|
@ -39,34 +50,93 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
|||
private ChannelExporter? _channelExporter;
|
||||
protected ChannelExporter Exporter => _channelExporter ??= new ChannelExporter(Discord);
|
||||
|
||||
protected async ValueTask ExportChannelAsync(Guild guild, Channel channel, ProgressContext progressContext)
|
||||
protected async ValueTask ExportAsync(IConsole console, IReadOnlyList<Channel> channels)
|
||||
{
|
||||
var request = new ExportRequest(
|
||||
guild,
|
||||
channel,
|
||||
OutputPath,
|
||||
ExportFormat,
|
||||
After,
|
||||
Before,
|
||||
PartitionLimit,
|
||||
ShouldDownloadMedia,
|
||||
ShouldReuseMedia,
|
||||
DateFormat
|
||||
);
|
||||
await console.Output.WriteLineAsync($"Exporting {channels.Count} channel(s)...");
|
||||
|
||||
var progress = progressContext.AddTask(
|
||||
$"{channel.Category} / {channel.Name}",
|
||||
new ProgressTaskSettings {MaxValue = 1}
|
||||
);
|
||||
var errors = new ConcurrentDictionary<Channel, string>();
|
||||
|
||||
try
|
||||
// Wrap everything in a progress ticker
|
||||
await console.CreateProgressTicker().StartAsync(async progressContext =>
|
||||
{
|
||||
await Exporter.ExportChannelAsync(request, progress);
|
||||
}
|
||||
finally
|
||||
await channels.ParallelForEachAsync(async channel =>
|
||||
{
|
||||
// Export
|
||||
try
|
||||
{
|
||||
var guild = await Discord.GetGuildAsync(channel.GuildId);
|
||||
|
||||
var request = new ExportRequest(
|
||||
guild,
|
||||
channel,
|
||||
OutputPath,
|
||||
ExportFormat,
|
||||
After,
|
||||
Before,
|
||||
PartitionLimit,
|
||||
ShouldDownloadMedia,
|
||||
ShouldReuseMedia,
|
||||
DateFormat
|
||||
);
|
||||
|
||||
var progress = progressContext.AddTask(
|
||||
$"{channel.Category} / {channel.Name}",
|
||||
new ProgressTaskSettings {MaxValue = 1}
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
await Exporter.ExportChannelAsync(request, progress);
|
||||
}
|
||||
finally
|
||||
{
|
||||
progress.StopTask();
|
||||
}
|
||||
}
|
||||
catch (DiscordChatExporterException ex) when (!ex.IsCritical)
|
||||
{
|
||||
errors[channel] = ex.Message;
|
||||
}
|
||||
}, ParallelLimit.ClampMin(1));
|
||||
|
||||
await console.Output.WriteLineAsync();
|
||||
});
|
||||
|
||||
// Print result
|
||||
using (console.WithForegroundColor(ConsoleColor.Green))
|
||||
{
|
||||
progress.StopTask();
|
||||
await console.Output.WriteLineAsync(
|
||||
$"Successfully exported {channels.Count - errors.Count} channel(s)."
|
||||
);
|
||||
}
|
||||
|
||||
// Print errors
|
||||
if (errors.Any())
|
||||
{
|
||||
await console.Output.WriteLineAsync();
|
||||
|
||||
using (console.WithForegroundColor(ConsoleColor.Red))
|
||||
await console.Output.WriteLineAsync($"Failed to export {errors.Count} channel(s):");
|
||||
|
||||
foreach (var (channel, error) in errors)
|
||||
{
|
||||
await console.Output.WriteAsync($"{channel.Category} / {channel.Name}: ");
|
||||
|
||||
using (console.WithForegroundColor(ConsoleColor.Red))
|
||||
await console.Output.WriteLineAsync(error);
|
||||
}
|
||||
|
||||
await console.Output.WriteLineAsync();
|
||||
}
|
||||
|
||||
// Fail the command if ALL channels failed to export.
|
||||
// Having some of the channels fail to export is fine and expected.
|
||||
if (errors.Count >= channels.Count)
|
||||
{
|
||||
throw new CommandException("Export failed.");
|
||||
}
|
||||
|
||||
await console.Output.WriteLineAsync("Done.");
|
||||
}
|
||||
|
||||
public override ValueTask ExecuteAsync(IConsole console)
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Exceptions;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
using Tyrrrz.Extensions;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Commands.Base
|
||||
{
|
||||
public abstract class ExportMultipleCommandBase : ExportCommandBase
|
||||
{
|
||||
[CommandOption("parallel", Description = "Limits how many channels can be exported in parallel.")]
|
||||
public int ParallelLimit { get; init; } = 1;
|
||||
|
||||
protected async ValueTask ExportChannelsAsync(IConsole console, IReadOnlyList<Channel> channels)
|
||||
{
|
||||
await console.Output.WriteLineAsync($"Exporting {channels.Count} channel(s)...");
|
||||
|
||||
var errors = new ConcurrentDictionary<Channel, string>();
|
||||
|
||||
await console.CreateProgressTicker().StartAsync(async progressContext =>
|
||||
{
|
||||
await channels.ParallelForEachAsync(async channel =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = await Discord.GetGuildAsync(channel.GuildId);
|
||||
await ExportChannelAsync(guild, channel, progressContext);
|
||||
}
|
||||
catch (DiscordChatExporterException ex) when (!ex.IsCritical)
|
||||
{
|
||||
errors[channel] = ex.Message;
|
||||
}
|
||||
}, ParallelLimit.ClampMin(1));
|
||||
|
||||
await console.Output.WriteLineAsync();
|
||||
});
|
||||
|
||||
// Print result
|
||||
await console.Output.WriteLineAsync(
|
||||
$"Successfully exported {channels.Count - errors.Count} channel(s)."
|
||||
);
|
||||
|
||||
// Print errors
|
||||
if (errors.Any())
|
||||
{
|
||||
using (console.WithForegroundColor(ConsoleColor.Red))
|
||||
await console.Output.WriteLineAsync($"Failed to export {errors.Count} channel(s):");
|
||||
|
||||
foreach (var (channel, error) in errors)
|
||||
{
|
||||
await console.Output.WriteAsync($"{channel.Category} / {channel.Name}: ");
|
||||
|
||||
using (console.WithForegroundColor(ConsoleColor.Red))
|
||||
await console.Output.WriteLineAsync(error);
|
||||
}
|
||||
|
||||
await console.Output.WriteLineAsync();
|
||||
}
|
||||
|
||||
// Fail the command if ALL channels failed to export.
|
||||
// Having some of the channels fail to export is fine and expected.
|
||||
if (errors.Count >= channels.Count)
|
||||
{
|
||||
throw new CommandException("Export failed.");
|
||||
}
|
||||
|
||||
await console.Output.WriteLineAsync("Done.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ using DiscordChatExporter.Core.Discord.Data;
|
|||
namespace DiscordChatExporter.Cli.Commands
|
||||
{
|
||||
[Command("exportall", Description = "Export all accessible channels.")]
|
||||
public class ExportAllCommand : ExportMultipleCommandBase
|
||||
public class ExportAllCommand : ExportCommandBase
|
||||
{
|
||||
[CommandOption("include-dm", Description = "Include direct message channels.")]
|
||||
public bool IncludeDirectMessages { get; init; } = true;
|
||||
|
@ -36,7 +36,7 @@ namespace DiscordChatExporter.Cli.Commands
|
|||
}
|
||||
|
||||
// Export
|
||||
await ExportChannelsAsync(console, channels);
|
||||
await ExportAsync(console, channels);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Commands.Base;
|
||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Commands
|
||||
{
|
||||
[Command("export", Description = "Export a channel.")]
|
||||
public class ExportChannelCommand : ExportCommandBase
|
||||
{
|
||||
[CommandOption("channel", 'c', IsRequired = true, Description = "Channel ID.")]
|
||||
public Snowflake ChannelId { get; init; }
|
||||
|
||||
public override async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
await base.ExecuteAsync(console);
|
||||
|
||||
// Get channel metadata
|
||||
await console.Output.WriteLineAsync("Fetching channel...");
|
||||
var channel = await Discord.GetChannelAsync(ChannelId);
|
||||
var guild = await Discord.GetGuildAsync(channel.GuildId);
|
||||
|
||||
// Export
|
||||
await console.Output.WriteLineAsync("Exporting...");
|
||||
await console.CreateProgressTicker().StartAsync(async progressContext =>
|
||||
{
|
||||
await ExportChannelAsync(guild, channel, progressContext);
|
||||
});
|
||||
await console.Output.WriteLineAsync("Done.");
|
||||
}
|
||||
}
|
||||
}
|
38
DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs
Normal file
38
DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Commands.Base;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Commands
|
||||
{
|
||||
[Command("export", Description = "Export one or multiple channels.")]
|
||||
public class ExportChannelsCommand : ExportCommandBase
|
||||
{
|
||||
// TODO: change this to plural with a breaking change
|
||||
[CommandOption("channel", 'c', IsRequired = true, Description = "Channel ID(s).")]
|
||||
public IReadOnlyList<Snowflake> ChannelIds { get; init; } = Array.Empty<Snowflake>();
|
||||
|
||||
public override async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
await base.ExecuteAsync(console);
|
||||
|
||||
// Get channel metadata
|
||||
await console.Output.WriteLineAsync("Fetching channel(s)...");
|
||||
|
||||
var channels = new List<Channel>();
|
||||
|
||||
foreach (var channelId in ChannelIds)
|
||||
{
|
||||
var channel = await Discord.GetChannelAsync(channelId);
|
||||
channels.Add(channel);
|
||||
}
|
||||
|
||||
// Export
|
||||
await ExportAsync(console, channels);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ using DiscordChatExporter.Core.Utils.Extensions;
|
|||
namespace DiscordChatExporter.Cli.Commands
|
||||
{
|
||||
[Command("exportdm", Description = "Export all direct message channels.")]
|
||||
public class ExportDirectMessagesCommand : ExportMultipleCommandBase
|
||||
public class ExportDirectMessagesCommand : ExportCommandBase
|
||||
{
|
||||
public override async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ namespace DiscordChatExporter.Cli.Commands
|
|||
var channels = await Discord.GetGuildChannelsAsync(Guild.DirectMessages.Id);
|
||||
|
||||
// Export
|
||||
await ExportChannelsAsync(console, channels);
|
||||
await ExportAsync(console, channels);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ using DiscordChatExporter.Core.Utils.Extensions;
|
|||
namespace DiscordChatExporter.Cli.Commands
|
||||
{
|
||||
[Command("exportguild", Description = "Export all channels within specified guild.")]
|
||||
public class ExportGuildCommand : ExportMultipleCommandBase
|
||||
public class ExportGuildCommand : ExportCommandBase
|
||||
{
|
||||
[CommandOption("guild", 'g', IsRequired = true, Description = "Guild ID.")]
|
||||
public Snowflake GuildId { get; init; }
|
||||
|
@ -22,7 +22,7 @@ namespace DiscordChatExporter.Cli.Commands
|
|||
var channels = await Discord.GetGuildChannelsAsync(GuildId);
|
||||
|
||||
// Export
|
||||
await ExportChannelsAsync(console, channels);
|
||||
await ExportAsync(console, channels);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue