Add 'list unwrap' command; remove category unwrapping from export; add [!IMPORTANT] callouts to pipeline doc sections

Agent-Logs-Url: https://github.com/Tyrrrz/DiscordChatExporter/sessions/d2a03a38-0ed4-45c7-b8e7-615ffb35c971

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-04-04 11:23:45 +00:00
committed by GitHub
parent 7f08641974
commit f8ab926074
3 changed files with 97 additions and 34 deletions

View File

@@ -8,7 +8,6 @@ using DiscordChatExporter.Cli.Commands.Base;
using DiscordChatExporter.Cli.Utils.Extensions;
using DiscordChatExporter.Core.Discord;
using DiscordChatExporter.Core.Discord.Data;
using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Cli.Commands;
@@ -19,8 +18,7 @@ public partial class ExportChannelsCommand : ExportCommandBase
0,
Name = "channel-ids",
Description = "Channel ID(s). "
+ "If provided with category ID(s), all channels inside those categories will be exported. "
+ "If not provided, channel IDs are read from standard input (one per line), "
+ "If not provided, channel IDs are read from standard input (one per line or as a JSON array), "
+ "enabling piping from the 'list channels' or 'list channels dm' commands."
)]
public IReadOnlyList<Snowflake> ChannelIds { get; set; } = [];
@@ -66,32 +64,11 @@ public partial class ExportChannelsCommand : ExportCommandBase
await console.Output.WriteLineAsync("Resolving channel(s)...");
var channels = new List<Channel>();
var channelsByGuild = new Dictionary<Snowflake, IReadOnlyList<Channel>>();
foreach (var channelId in channelIds)
{
var channel = await Discord.GetChannelAsync(channelId, cancellationToken);
// Unwrap categories
if (channel.IsCategory)
{
var guildChannels =
channelsByGuild.GetValueOrDefault(channel.GuildId)
?? await Discord.GetGuildChannelsAsync(channel.GuildId, cancellationToken);
foreach (var guildChannel in guildChannels)
{
if (guildChannel.Parent?.Id == channel.Id)
channels.Add(guildChannel);
}
// Cache the guild channels to avoid redundant work
channelsByGuild[channel.GuildId] = guildChannels;
}
else
{
channels.Add(channel);
}
channels.Add(channel);
}
await ExportAsync(console, channels);

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using CliFx.Binding;
using CliFx.Infrastructure;
using DiscordChatExporter.Cli.Commands.Base;
using DiscordChatExporter.Cli.Utils.Extensions;
using DiscordChatExporter.Cli.Utils.Json;
using DiscordChatExporter.Core.Discord;
using DiscordChatExporter.Core.Discord.Data;
using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Cli.Commands;
[Command(
"list unwrap",
Description = "Resolves categories in a channel list to their child channels."
)]
public partial class UnwrapChannelsCommand : DiscordCommandBase
{
public override async ValueTask ExecuteAsync(IConsole console)
{
await base.ExecuteAsync(console);
var cancellationToken = console.RegisterCancellationHandler();
// Read all JSON from stdin (produced by 'list channels' or 'list channels dm')
var sb = new StringBuilder();
await foreach (var line in console.Input.ReadLinesAsync(cancellationToken))
sb.Append(line);
var channels =
JsonSerializer.Deserialize(
sb.ToString().Trim(),
CliJsonSerializerContext.Instance.ChannelArray
) ?? [];
var result = new List<Channel>();
var channelsByGuild = new Dictionary<Snowflake, IReadOnlyList<Channel>>();
foreach (var channel in channels)
{
if (channel.IsCategory)
{
var guildChannels =
channelsByGuild.GetValueOrDefault(channel.GuildId)
?? await Discord.GetGuildChannelsAsync(channel.GuildId, cancellationToken);
foreach (var guildChannel in guildChannels)
{
if (guildChannel.Parent?.Id == channel.Id)
result.Add(guildChannel);
}
channelsByGuild[channel.GuildId] = guildChannels;
}
else
{
result.Add(channel);
}
}
await console.Output.WriteLineAsync(
JsonSerializer.Serialize(
result.ToArray(),
CliJsonSerializerContext.Instance.ChannelArray
)
);
}
}