|
|
|
@ -11,7 +11,6 @@ using DiscordChatExporter.Core.Utils;
|
|
|
|
|
using DiscordChatExporter.Core.Utils.Extensions;
|
|
|
|
|
using JsonExtensions.Http;
|
|
|
|
|
using JsonExtensions.Reading;
|
|
|
|
|
using Tyrrrz.Extensions;
|
|
|
|
|
|
|
|
|
|
namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
{
|
|
|
|
@ -29,7 +28,9 @@ namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DiscordClient(AuthToken token)
|
|
|
|
|
: this(Http.Client, token) {}
|
|
|
|
|
: this(Http.Client, token)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async ValueTask<HttpResponseMessage> GetResponseAsync(string url) =>
|
|
|
|
|
await Http.ResponsePolicy.ExecuteAsync(async () =>
|
|
|
|
@ -64,7 +65,7 @@ namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
|
|
|
|
|
return response.IsSuccessStatusCode
|
|
|
|
|
? await response.Content.ReadAsJsonAsync()
|
|
|
|
|
: (JsonElement?) null;
|
|
|
|
|
: null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async IAsyncEnumerable<Guild> GetUserGuildsAsync()
|
|
|
|
@ -118,29 +119,30 @@ namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
{
|
|
|
|
|
var response = await GetJsonResponseAsync($"guilds/{guildId}/channels");
|
|
|
|
|
|
|
|
|
|
var orderedResponse = response
|
|
|
|
|
var responseOrdered = response
|
|
|
|
|
.EnumerateArray()
|
|
|
|
|
.OrderBy(j => j.GetProperty("position").GetInt32())
|
|
|
|
|
.ThenBy(j => ulong.Parse(j.GetProperty("id").GetString()))
|
|
|
|
|
.ThenBy(j => Snowflake.Parse(j.GetProperty("id").GetString()))
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
var categories = orderedResponse
|
|
|
|
|
.Where(j => j.GetProperty("type").GetInt32() == (int)ChannelType.GuildCategory)
|
|
|
|
|
var categories = responseOrdered
|
|
|
|
|
.Where(j => j.GetProperty("type").GetInt32() == (int) ChannelType.GuildCategory)
|
|
|
|
|
.Select((j, index) => ChannelCategory.Parse(j, index + 1))
|
|
|
|
|
.ToDictionary(j => j.Id.ToString());
|
|
|
|
|
.ToDictionary(j => j.Id.ToString(), StringComparer.Ordinal);
|
|
|
|
|
|
|
|
|
|
var position = 0;
|
|
|
|
|
|
|
|
|
|
foreach (var channelJson in orderedResponse)
|
|
|
|
|
foreach (var channelJson in responseOrdered)
|
|
|
|
|
{
|
|
|
|
|
var parentId = channelJson.GetPropertyOrNull("parent_id")?.GetString();
|
|
|
|
|
|
|
|
|
|
var category = !string.IsNullOrWhiteSpace(parentId)
|
|
|
|
|
? categories.GetValueOrDefault(parentId)
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
var channel = Channel.Parse(channelJson, category, position);
|
|
|
|
|
|
|
|
|
|
// Skip non-text channels
|
|
|
|
|
// We are only interested in text channels
|
|
|
|
|
if (!channel.IsTextChannel)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
@ -178,15 +180,12 @@ namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
var response = await GetJsonResponseAsync($"channels/{channelId}");
|
|
|
|
|
return ChannelCategory.Parse(response);
|
|
|
|
|
}
|
|
|
|
|
/***
|
|
|
|
|
* In some cases, the Discord API returns an empty body when requesting some channel category info.
|
|
|
|
|
* Instead, we use an empty channel category as a fallback.
|
|
|
|
|
*/
|
|
|
|
|
// In some cases, the Discord API returns an empty body when requesting channel category.
|
|
|
|
|
// Instead, we use an empty channel category as a fallback.
|
|
|
|
|
catch (DiscordChatExporterException)
|
|
|
|
|
{
|
|
|
|
|
return ChannelCategory.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async ValueTask<Channel> GetChannelAsync(Snowflake channelId)
|
|
|
|
@ -221,7 +220,7 @@ namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
IProgress<double>? progress = null)
|
|
|
|
|
{
|
|
|
|
|
// Get the last message in the specified range.
|
|
|
|
|
// This snapshots the boundaries, which means that messages posted after the exported started
|
|
|
|
|
// This snapshots the boundaries, which means that messages posted after the export started
|
|
|
|
|
// will not appear in the output.
|
|
|
|
|
// Additionally, it provides the date of the last message, which is used to calculate progress.
|
|
|
|
|
var lastMessage = await TryGetLastMessageAsync(channelId, before);
|
|
|
|
@ -271,7 +270,7 @@ namespace DiscordChatExporter.Core.Discord
|
|
|
|
|
progress.Report(exportedDuration / totalDuration);
|
|
|
|
|
}
|
|
|
|
|
// Avoid division by zero if all messages have the exact same timestamp
|
|
|
|
|
// (which can happen easily if there's only one message in the channel)
|
|
|
|
|
// (which may be the case if there's only one message in the channel)
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
progress.Report(1);
|
|
|
|
|