mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-05-10 14:23:51 +00:00
list commands always output JSON; add CliJsonSerializerContext + SnowflakeJsonConverter in CLI
Agent-Logs-Url: https://github.com/Tyrrrz/DiscordChatExporter/sessions/58698f45-e22e-4bd4-aec4-31f801051467 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
89407c121f
commit
b0ee4ba646
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx;
|
||||
using CliFx.Binding;
|
||||
@@ -35,7 +36,23 @@ public partial class ExportChannelsCommand : ExportCommandBase
|
||||
if (channelIds.Count == 0 && console.IsInputRedirected)
|
||||
{
|
||||
await foreach (var line in console.Input.ReadLinesAsync(cancellationToken))
|
||||
channelIds.Add(Snowflake.Parse(line.Trim()));
|
||||
{
|
||||
var trimmed = line.Trim();
|
||||
if (string.IsNullOrEmpty(trimmed))
|
||||
continue;
|
||||
|
||||
// JSON array produced by 'list channels' / 'list channels dm'
|
||||
if (trimmed.StartsWith('['))
|
||||
{
|
||||
using var doc = JsonDocument.Parse(trimmed);
|
||||
foreach (var element in doc.RootElement.EnumerateArray())
|
||||
channelIds.Add(Snowflake.Parse(element.GetProperty("id").GetString()!));
|
||||
}
|
||||
else
|
||||
{
|
||||
channelIds.Add(Snowflake.Parse(trimmed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channelIds.Count == 0)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Binding;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Commands.Base;
|
||||
using DiscordChatExporter.Cli.Commands.Converters;
|
||||
using DiscordChatExporter.Cli.Commands.Shared;
|
||||
using DiscordChatExporter.Cli.Utils.Json;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
@@ -35,6 +36,8 @@ public partial class GetChannelsCommand : DiscordCommandBase
|
||||
|
||||
var cancellationToken = console.RegisterCancellationHandler();
|
||||
|
||||
var allChannels = new List<Channel>();
|
||||
|
||||
foreach (var guildId in GuildIds)
|
||||
{
|
||||
var channels = (await Discord.GetGuildChannelsAsync(guildId, cancellationToken))
|
||||
@@ -59,86 +62,18 @@ public partial class GetChannelsCommand : DiscordCommandBase
|
||||
.ToArray()
|
||||
: [];
|
||||
|
||||
// If output is redirected, print only channel IDs (one per line) for easy piping
|
||||
if (console.IsOutputRedirected)
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
await console.Output.WriteLineAsync(channel.Id.ToString());
|
||||
foreach (var channelThread in threads.Where(t => t.Parent?.Id == channel.Id))
|
||||
await console.Output.WriteLineAsync(channelThread.Id.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show server header when listing multiple servers
|
||||
if (GuildIds.Count > 1)
|
||||
{
|
||||
var guild = await Discord.GetGuildAsync(guildId, cancellationToken);
|
||||
|
||||
using (console.WithForegroundColor(ConsoleColor.Cyan))
|
||||
await console.Output.WriteLineAsync($"{guild.Id} | {guild.Name}");
|
||||
}
|
||||
|
||||
var channelIdMaxLength = channels
|
||||
.Select(c => c.Id.ToString().Length)
|
||||
.OrderDescending()
|
||||
.FirstOrDefault();
|
||||
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
// Channel ID
|
||||
await console.Output.WriteAsync(
|
||||
channel.Id.ToString().PadRight(channelIdMaxLength, ' ')
|
||||
);
|
||||
|
||||
// Separator
|
||||
using (console.WithForegroundColor(ConsoleColor.DarkGray))
|
||||
await console.Output.WriteAsync(" | ");
|
||||
|
||||
// Channel name
|
||||
using (console.WithForegroundColor(ConsoleColor.White))
|
||||
await console.Output.WriteLineAsync(channel.GetHierarchicalName());
|
||||
|
||||
var channelThreads = threads.Where(t => t.Parent?.Id == channel.Id).ToArray();
|
||||
var channelThreadIdMaxLength = channelThreads
|
||||
.Select(t => t.Id.ToString().Length)
|
||||
.OrderDescending()
|
||||
.FirstOrDefault();
|
||||
|
||||
foreach (var channelThread in channelThreads)
|
||||
{
|
||||
// Indent
|
||||
await console.Output.WriteAsync(" * ");
|
||||
|
||||
// Thread ID
|
||||
await console.Output.WriteAsync(
|
||||
channelThread.Id.ToString().PadRight(channelThreadIdMaxLength, ' ')
|
||||
);
|
||||
|
||||
// Separator
|
||||
using (console.WithForegroundColor(ConsoleColor.DarkGray))
|
||||
await console.Output.WriteAsync(" | ");
|
||||
|
||||
// Thread name
|
||||
using (console.WithForegroundColor(ConsoleColor.White))
|
||||
await console.Output.WriteAsync($"Thread / {channelThread.Name}");
|
||||
|
||||
// Separator
|
||||
using (console.WithForegroundColor(ConsoleColor.DarkGray))
|
||||
await console.Output.WriteAsync(" | ");
|
||||
|
||||
// Thread status
|
||||
using (console.WithForegroundColor(ConsoleColor.White))
|
||||
await console.Output.WriteLineAsync(
|
||||
channelThread.IsArchived ? "Archived" : "Active"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (GuildIds.Count > 1)
|
||||
await console.Output.WriteLineAsync();
|
||||
allChannels.Add(channel);
|
||||
allChannels.AddRange(threads.Where(t => t.Parent?.Id == channel.Id));
|
||||
}
|
||||
}
|
||||
|
||||
await console.Output.WriteLineAsync(
|
||||
JsonSerializer.Serialize(
|
||||
allChannels.ToArray(),
|
||||
CliJsonSerializerContext.Instance.ChannelArray
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Binding;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Commands.Base;
|
||||
using DiscordChatExporter.Cli.Utils.Json;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
|
||||
@@ -25,34 +26,8 @@ public partial class GetDirectChannelsCommand : DiscordCommandBase
|
||||
.ThenBy(c => c.Name)
|
||||
.ToArray();
|
||||
|
||||
var channelIdMaxLength = channels
|
||||
.Select(c => c.Id.ToString().Length)
|
||||
.OrderDescending()
|
||||
.FirstOrDefault();
|
||||
|
||||
// If output is redirected, print only channel IDs (one per line) for easy piping
|
||||
if (console.IsOutputRedirected)
|
||||
{
|
||||
foreach (var channel in channels)
|
||||
await console.Output.WriteLineAsync(channel.Id.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
// Channel ID
|
||||
await console.Output.WriteAsync(
|
||||
channel.Id.ToString().PadRight(channelIdMaxLength, ' ')
|
||||
);
|
||||
|
||||
// Separator
|
||||
using (console.WithForegroundColor(ConsoleColor.DarkGray))
|
||||
await console.Output.WriteAsync(" | ");
|
||||
|
||||
// Channel name
|
||||
using (console.WithForegroundColor(ConsoleColor.White))
|
||||
await console.Output.WriteLineAsync(channel.GetHierarchicalName());
|
||||
}
|
||||
}
|
||||
await console.Output.WriteLineAsync(
|
||||
JsonSerializer.Serialize(channels, CliJsonSerializerContext.Instance.ChannelArray)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Binding;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Commands.Base;
|
||||
using DiscordChatExporter.Cli.Utils.Json;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
|
||||
@@ -24,23 +25,8 @@ public partial class GetGuildsCommand : DiscordCommandBase
|
||||
.ThenBy(g => g.Name)
|
||||
.ToArray();
|
||||
|
||||
var guildIdMaxLength = guilds
|
||||
.Select(g => g.Id.ToString().Length)
|
||||
.OrderDescending()
|
||||
.FirstOrDefault();
|
||||
|
||||
foreach (var guild in guilds)
|
||||
{
|
||||
// Guild ID
|
||||
await console.Output.WriteAsync(guild.Id.ToString().PadRight(guildIdMaxLength, ' '));
|
||||
|
||||
// Separator
|
||||
using (console.WithForegroundColor(ConsoleColor.DarkGray))
|
||||
await console.Output.WriteAsync(" | ");
|
||||
|
||||
// Guild name
|
||||
using (console.WithForegroundColor(ConsoleColor.White))
|
||||
await console.Output.WriteLineAsync(guild.Name);
|
||||
}
|
||||
await console.Output.WriteLineAsync(
|
||||
JsonSerializer.Serialize(guilds, CliJsonSerializerContext.Instance.GuildArray)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Utils.Json;
|
||||
|
||||
[JsonSourceGenerationOptions(
|
||||
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||
GenerationMode = JsonSourceGenerationMode.Metadata
|
||||
)]
|
||||
[JsonSerializable(typeof(Channel[]))]
|
||||
[JsonSerializable(typeof(Guild[]))]
|
||||
internal partial class CliJsonSerializerContext : JsonSerializerContext
|
||||
{
|
||||
// Instance pre-configured with converters for Snowflake (serialised as a string)
|
||||
// and all enum types (serialised as their name). Defined here so the Core types
|
||||
// are never touched.
|
||||
public static CliJsonSerializerContext Instance { get; } =
|
||||
new(
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
Converters = { new SnowflakeJsonConverter(), new JsonStringEnumConverter() },
|
||||
}
|
||||
);
|
||||
}
|
||||
21
DiscordChatExporter.Cli/Utils/Json/SnowflakeJsonConverter.cs
Normal file
21
DiscordChatExporter.Cli/Utils/Json/SnowflakeJsonConverter.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Utils.Json;
|
||||
|
||||
internal class SnowflakeJsonConverter : JsonConverter<Snowflake>
|
||||
{
|
||||
public override Snowflake Read(
|
||||
ref Utf8JsonReader reader,
|
||||
Type typeToConvert,
|
||||
JsonSerializerOptions options
|
||||
) => Snowflake.Parse(reader.GetString()!);
|
||||
|
||||
public override void Write(
|
||||
Utf8JsonWriter writer,
|
||||
Snowflake value,
|
||||
JsonSerializerOptions options
|
||||
) => writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
Reference in New Issue
Block a user