mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-08-25 05:15:22 +02:00
Compare commits
2 Commits
b11a57a825
...
2.47.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 05f8df51e9 | |||
| e2c633b004 |
@@ -13,4 +13,5 @@ public enum MessageKind
|
|||||||
GuildMemberJoin = 7,
|
GuildMemberJoin = 7,
|
||||||
ThreadCreated = 18,
|
ThreadCreated = 18,
|
||||||
Reply = 19,
|
Reply = 19,
|
||||||
|
ThreadStarterMessage = 21,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ public class DiscordClient(
|
|||||||
string url,
|
string url,
|
||||||
TokenKind tokenKind,
|
TokenKind tokenKind,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
) =>
|
||||||
{
|
await Http.ResponseResiliencePipeline.ExecuteAsync(
|
||||||
return await Http.ResponseResiliencePipeline.ExecuteAsync(
|
|
||||||
async innerCancellationToken =>
|
async innerCancellationToken =>
|
||||||
{
|
{
|
||||||
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(_baseUri, url));
|
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(_baseUri, url));
|
||||||
@@ -91,7 +90,6 @@ public class DiscordClient(
|
|||||||
},
|
},
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask<TokenKind> ResolveTokenKindAsync(
|
private async ValueTask<TokenKind> ResolveTokenKindAsync(
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
@@ -364,6 +362,7 @@ public class DiscordClient(
|
|||||||
$"guilds/{guildId}/members/{memberId}",
|
$"guilds/{guildId}/members/{memberId}",
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
return response?.Pipe(j => Member.Parse(j, guildId));
|
return response?.Pipe(j => Member.Parse(j, guildId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,14 +411,12 @@ public class DiscordClient(
|
|||||||
?.GetNonWhiteSpaceStringOrNull()
|
?.GetNonWhiteSpaceStringOrNull()
|
||||||
?.Pipe(Snowflake.Parse);
|
?.Pipe(Snowflake.Parse);
|
||||||
|
|
||||||
Channel? parent = null;
|
// It's possible for the parent channel to be inaccessible, despite the
|
||||||
if (parentId is not null)
|
// child channel being accessible.
|
||||||
{
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1108
|
||||||
// It's possible for the parent channel to be inaccessible, despite the
|
var parent = parentId is not null
|
||||||
// child channel being accessible.
|
? await TryGetChannelAsync(parentId.Value, cancellationToken)
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1108
|
: null;
|
||||||
parent = await TryGetChannelAsync(parentId.Value, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Channel.Parse(response.Value, parent);
|
return Channel.Parse(response.Value, parent);
|
||||||
}
|
}
|
||||||
@@ -607,8 +604,12 @@ public class DiscordClient(
|
|||||||
.SetQueryParameter("after", (after ?? Snowflake.Zero).ToString())
|
.SetQueryParameter("after", (after ?? Snowflake.Zero).ToString())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var response = await GetJsonResponseAsync(url, cancellationToken);
|
// Can be null on channels that the user cannot access
|
||||||
var message = response.EnumerateArray().Select(Message.Parse).FirstOrDefault();
|
var response = await TryGetJsonResponseAsync(url, cancellationToken);
|
||||||
|
if (response is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var message = response.Value.EnumerateArray().Select(Message.Parse).FirstOrDefault();
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@@ -625,8 +626,39 @@ public class DiscordClient(
|
|||||||
.SetQueryParameter("before", before?.ToString())
|
.SetQueryParameter("before", before?.ToString())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var response = await GetJsonResponseAsync(url, cancellationToken);
|
// Can be null on channels that the user cannot access
|
||||||
return response.EnumerateArray().Select(Message.Parse).LastOrDefault();
|
var response = await TryGetJsonResponseAsync(url, cancellationToken);
|
||||||
|
if (response is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return response.Value.EnumerateArray().Select(Message.Parse).LastOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Message?> TryGetMessageAsync(
|
||||||
|
Snowflake channelId,
|
||||||
|
Snowflake messageId,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Use the regular message listing endpoint with the 'around' parameter instead of the
|
||||||
|
// dedicated single-message endpoint, because the latter is not accessible to user tokens.
|
||||||
|
var url = new UrlBuilder()
|
||||||
|
.SetPath($"channels/{channelId}/messages")
|
||||||
|
.SetQueryParameter("around", messageId.ToString())
|
||||||
|
.SetQueryParameter("limit", "1")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Can be null on channels that the user cannot access
|
||||||
|
var response = await TryGetJsonResponseAsync(url, cancellationToken);
|
||||||
|
if (response is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// The endpoint returns messages around the requested ID, so make sure to only return
|
||||||
|
// the message that exactly matches it (it may be absent if it has been deleted).
|
||||||
|
return response
|
||||||
|
.Value.EnumerateArray()
|
||||||
|
.Select(Message.Parse)
|
||||||
|
.FirstOrDefault(m => m.Id == messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async IAsyncEnumerable<Message> GetMessagesAsync(
|
public async IAsyncEnumerable<Message> GetMessagesAsync(
|
||||||
@@ -701,7 +733,21 @@ public class DiscordClient(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return message;
|
// Some messages, for example thread starter messages, are returned by the API as content-less references.
|
||||||
|
// Try to resolve them to the actual message so that they appear as they do in the Discord client.
|
||||||
|
var actualMessage =
|
||||||
|
message.Kind == MessageKind.ThreadStarterMessage
|
||||||
|
&& message.Reference?.ChannelId is { } referencedChannelId
|
||||||
|
&& message.Reference?.MessageId is { } referencedMessageId
|
||||||
|
? await TryGetMessageAsync(
|
||||||
|
referencedChannelId,
|
||||||
|
referencedMessageId,
|
||||||
|
cancellationToken
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
yield return actualMessage ?? message;
|
||||||
|
|
||||||
currentAfter = message.Id;
|
currentAfter = message.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -769,7 +815,20 @@ public class DiscordClient(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return message;
|
// Some messages, for example thread starter messages, are returned by the API as content-less references.
|
||||||
|
// Try to resolve them to the actual message so that they appear as they do in the Discord client.
|
||||||
|
var actualMessage =
|
||||||
|
message.Kind == MessageKind.ThreadStarterMessage
|
||||||
|
&& message.Reference?.ChannelId is { } referencedChannelId
|
||||||
|
&& message.Reference?.MessageId is { } referencedMessageId
|
||||||
|
? await TryGetMessageAsync(
|
||||||
|
referencedChannelId,
|
||||||
|
referencedMessageId,
|
||||||
|
cancellationToken
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
yield return actualMessage ?? message;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentBefore = messages.Last().Id;
|
currentBefore = messages.Last().Id;
|
||||||
|
|||||||
Reference in New Issue
Block a user