diff --git a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs index f8f0ec3..23e506c 100644 --- a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs @@ -36,6 +36,12 @@ public class GetChannelsCommand : DiscordCommandBase .ThenBy(c => c.Name) .ToArray(); + var threads = IncludeThreads + ? (await Discord.GetGuildThreadsAsync(GuildId, cancellationToken)) + .OrderBy(c => c.Name) + .ToArray() + : Array.Empty(); + foreach (var channel in channels) { // Channel ID @@ -51,31 +57,24 @@ public class GetChannelsCommand : DiscordCommandBase using (console.WithForegroundColor(ConsoleColor.White)) await console.Output.WriteLineAsync($"{channel.Category.Name} / {channel.Name}"); - if (IncludeThreads) + foreach (var thread in threads.Where(t => t.ParentId == channel.Id)) { - var threads = (await Discord.GetChannelThreadsAsync(channel.Id, cancellationToken)) - .OrderBy(c => c.Name) - .ToArray(); - - foreach (var thread in threads) - { - // Indent - await console.Output.WriteAsync('\t'); + // Indent + await console.Output.WriteAsync('\t'); - // Thread ID - await console.Output.WriteAsync( - thread.Id.ToString().PadRight(18, ' ') - ); + // Thread ID + await console.Output.WriteAsync( + thread.Id.ToString().PadRight(18, ' ') + ); - // Separator - using (console.WithForegroundColor(ConsoleColor.DarkGray)) - await console.Output.WriteAsync(" | "); + // Separator + using (console.WithForegroundColor(ConsoleColor.DarkGray)) + await console.Output.WriteAsync(" | "); - // Thread name - using (console.WithForegroundColor(ConsoleColor.White)) - await console.Output.WriteLineAsync($"Thread / {thread.Name}"); - } - } + // Thread name + using (console.WithForegroundColor(ConsoleColor.White)) + await console.Output.WriteLineAsync($"Thread / {thread.Name}"); + } } } } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs b/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs index 680afed..985ea9d 100644 --- a/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs +++ b/DiscordChatExporter.Core/Discord/Data/ChannelThread.cs @@ -10,6 +10,7 @@ public record ChannelThread( Snowflake Id, ChannelKind Kind, Snowflake GuildId, + Snowflake ParentId, string Name, Snowflake? LastMessageId) : IHasId { @@ -18,6 +19,7 @@ public record ChannelThread( var id = json.GetProperty("id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse); var kind = (ChannelKind)json.GetProperty("type").GetInt32(); var guildId = json.GetProperty("guild_id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse); + var parentId = json.GetProperty("parent_id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse); var name = json.GetProperty("name").GetNonWhiteSpaceString(); var lastMessageId = json @@ -29,6 +31,7 @@ public record ChannelThread( id, kind, guildId, + parentId, name, lastMessageId ); diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index 4ea2395..a6adbd8 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -311,30 +311,68 @@ public class DiscordClient return Channel.Parse(response, category); } - public async IAsyncEnumerable GetChannelThreadsAsync( - Snowflake channelId, + public async IAsyncEnumerable GetGuildThreadsAsync( + Snowflake guildId, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var currentOffset = 0; - while (true) + var channels = (await GetGuildChannelsAsync(guildId, cancellationToken)) + .ToArray(); + var tokenKind = _resolvedTokenKind ??= await GetTokenKindAsync(cancellationToken); + + if (tokenKind == TokenKind.Bot) { - var url = new UrlBuilder() - .SetPath($"channels/{channelId}/threads/search") - .SetQueryParameter("offset", currentOffset.ToString()) - .Build(); - - var response = await TryGetJsonResponseAsync(url, cancellationToken); - if (response is null) - break; - - foreach (var threadJson in response.Value.GetProperty("threads").EnumerateArray()) + var response = await GetJsonResponseAsync($"guilds/{guildId}/threads/active", cancellationToken); + foreach (var threadJson in response.GetProperty("threads").EnumerateArray()) { yield return ChannelThread.Parse(threadJson); - currentOffset++; } + } + foreach (var channel in channels) + { + if (tokenKind == TokenKind.User) + { + var currentOffset = 0; + while (true) + { + var url = new UrlBuilder() + .SetPath($"channels/{channel.Id}/threads/search") + .SetQueryParameter("offset", currentOffset.ToString()) + .Build(); + + var response = await TryGetJsonResponseAsync(url, cancellationToken); + if (response is null) + break; + + foreach (var threadJson in response.Value.GetProperty("threads").EnumerateArray()) + { + yield return ChannelThread.Parse(threadJson); + currentOffset++; + } + + if (!response.Value.GetProperty("has_more").GetBoolean()) + break; + } + } + else + { + var responsePublic = await TryGetJsonResponseAsync($"channels/{channel.Id}/threads/archived/public", cancellationToken); + var responsePrivate = await TryGetJsonResponseAsync($"channels/{channel.Id}/threads/archived/private", cancellationToken); - if (!response.Value.GetProperty("has_more").GetBoolean()) - break; + if (responsePublic is not null) + { + foreach (var threadJson in responsePublic.Value.GetProperty("threads").EnumerateArray()) + { + yield return ChannelThread.Parse(threadJson); + } + } + if (responsePrivate is not null) + { + foreach (var threadJson in responsePrivate.Value.GetProperty("threads").EnumerateArray()) + { + yield return ChannelThread.Parse(threadJson); + } + } + } } }