mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-05-18 23:05:08 +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.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx;
|
using CliFx;
|
||||||
using CliFx.Binding;
|
using CliFx.Binding;
|
||||||
@@ -35,7 +36,23 @@ public partial class ExportChannelsCommand : ExportCommandBase
|
|||||||
if (channelIds.Count == 0 && console.IsInputRedirected)
|
if (channelIds.Count == 0 && console.IsInputRedirected)
|
||||||
{
|
{
|
||||||
await foreach (var line in console.Input.ReadLinesAsync(cancellationToken))
|
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)
|
if (channelIds.Count == 0)
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Binding;
|
using CliFx.Binding;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using DiscordChatExporter.Cli.Commands.Base;
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
using DiscordChatExporter.Cli.Commands.Converters;
|
using DiscordChatExporter.Cli.Commands.Converters;
|
||||||
using DiscordChatExporter.Cli.Commands.Shared;
|
using DiscordChatExporter.Cli.Commands.Shared;
|
||||||
|
using DiscordChatExporter.Cli.Utils.Json;
|
||||||
using DiscordChatExporter.Core.Discord;
|
using DiscordChatExporter.Core.Discord;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
@@ -35,6 +36,8 @@ public partial class GetChannelsCommand : DiscordCommandBase
|
|||||||
|
|
||||||
var cancellationToken = console.RegisterCancellationHandler();
|
var cancellationToken = console.RegisterCancellationHandler();
|
||||||
|
|
||||||
|
var allChannels = new List<Channel>();
|
||||||
|
|
||||||
foreach (var guildId in GuildIds)
|
foreach (var guildId in GuildIds)
|
||||||
{
|
{
|
||||||
var channels = (await Discord.GetGuildChannelsAsync(guildId, cancellationToken))
|
var channels = (await Discord.GetGuildChannelsAsync(guildId, cancellationToken))
|
||||||
@@ -59,86 +62,18 @@ public partial class GetChannelsCommand : DiscordCommandBase
|
|||||||
.ToArray()
|
.ToArray()
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// If output is redirected, print only channel IDs (one per line) for easy piping
|
foreach (var channel in channels)
|
||||||
if (console.IsOutputRedirected)
|
|
||||||
{
|
{
|
||||||
foreach (var channel in channels)
|
allChannels.Add(channel);
|
||||||
{
|
allChannels.AddRange(threads.Where(t => t.Parent?.Id == channel.Id));
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 System.Threading.Tasks;
|
||||||
using CliFx.Binding;
|
using CliFx.Binding;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using DiscordChatExporter.Cli.Commands.Base;
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
|
using DiscordChatExporter.Cli.Utils.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
|
||||||
@@ -25,34 +26,8 @@ public partial class GetDirectChannelsCommand : DiscordCommandBase
|
|||||||
.ThenBy(c => c.Name)
|
.ThenBy(c => c.Name)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var channelIdMaxLength = channels
|
await console.Output.WriteLineAsync(
|
||||||
.Select(c => c.Id.ToString().Length)
|
JsonSerializer.Serialize(channels, CliJsonSerializerContext.Instance.ChannelArray)
|
||||||
.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System.Linq;
|
||||||
using System.Linq;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Binding;
|
using CliFx.Binding;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using DiscordChatExporter.Cli.Commands.Base;
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
|
using DiscordChatExporter.Cli.Utils.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
|
||||||
@@ -24,23 +25,8 @@ public partial class GetGuildsCommand : DiscordCommandBase
|
|||||||
.ThenBy(g => g.Name)
|
.ThenBy(g => g.Name)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var guildIdMaxLength = guilds
|
await console.Output.WriteLineAsync(
|
||||||
.Select(g => g.Id.ToString().Length)
|
JsonSerializer.Serialize(guilds, CliJsonSerializerContext.Instance.GuildArray)
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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