mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-06-03 08:08:40 -04:00
Refactor
This commit is contained in:
parent
03c5c1bc5e
commit
31c7ae9312
50 changed files with 181 additions and 198 deletions
|
@ -1,7 +1,6 @@
|
|||
namespace DiscordChatExporter.Core.Discord.Data;
|
||||
|
||||
// https://discord.com/developers/docs/resources/channel#channel-object-channel-types
|
||||
// Order of enum fields needs to match the order in the docs.
|
||||
public enum ChannelKind
|
||||
{
|
||||
GuildTextChat = 0,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
using JsonExtensions.Reading;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscordChatExporter.Core.Discord.Data.Common;
|
||||
|
||||
public class IdBasedEqualityComparer : IEqualityComparer<IHasId>
|
||||
{
|
||||
public static IdBasedEqualityComparer Instance { get; } = new();
|
||||
|
||||
public bool Equals(IHasId? x, IHasId? y) => x?.Id == y?.Id;
|
||||
|
||||
public int GetHashCode(IHasId obj) => obj.Id.GetHashCode();
|
||||
}
|
|
@ -34,8 +34,7 @@ public partial record Emoji
|
|||
if (!string.IsNullOrWhiteSpace(name))
|
||||
return ImageCdn.GetStandardEmojiUrl(name);
|
||||
|
||||
// Either ID or name should be set
|
||||
throw new ApplicationException("Emoji has neither ID nor name set.");
|
||||
throw new InvalidOperationException("Either the emoji ID or name should be provided.");
|
||||
}
|
||||
|
||||
public static Emoji Parse(JsonElement json)
|
||||
|
|
|
@ -17,5 +17,5 @@ public enum MessageKind
|
|||
|
||||
public static class MessageKindExtensions
|
||||
{
|
||||
public static bool IsSystemNotification(this MessageKind c) => (int)c is >= 1 and <= 18;
|
||||
public static bool IsSystemNotification(this MessageKind kind) => (int)kind is >= 1 and <= 18;
|
||||
}
|
|
@ -238,11 +238,8 @@ public class DiscordClient
|
|||
? categories.GetValueOrDefault(parentId)
|
||||
: null;
|
||||
|
||||
var channel = Channel.Parse(channelJson, category, position);
|
||||
|
||||
yield return Channel.Parse(channelJson, category, position);
|
||||
position++;
|
||||
|
||||
yield return channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -288,8 +285,8 @@ public class DiscordClient
|
|||
var response = await GetJsonResponseAsync($"channels/{channelId}", cancellationToken);
|
||||
return ChannelCategory.Parse(response);
|
||||
}
|
||||
// In some cases, the Discord API returns an empty body when requesting a channel.
|
||||
// Return an empty channel category as fallback in these cases.
|
||||
// In some cases, Discord API returns an empty body when requesting a channel.
|
||||
// Use an empty channel category as fallback for these cases.
|
||||
catch (DiscordChatExporterException)
|
||||
{
|
||||
return new ChannelCategory(channelId, "Unknown Category", 0);
|
||||
|
@ -371,8 +368,9 @@ public class DiscordClient
|
|||
if (lastMessage is null || lastMessage.Timestamp < after?.ToDate())
|
||||
yield break;
|
||||
|
||||
// Keep track of the first message in range in order to calculate progress
|
||||
// Keep track of the first message in range in order to calculate the progress
|
||||
var firstMessage = default(Message);
|
||||
|
||||
var currentAfter = after ?? Snowflake.Zero;
|
||||
while (true)
|
||||
{
|
||||
|
|
|
@ -29,15 +29,11 @@ public partial record struct Snowflake
|
|||
|
||||
// As number
|
||||
if (ulong.TryParse(str, NumberStyles.None, formatProvider, out var value))
|
||||
{
|
||||
return new Snowflake(value);
|
||||
}
|
||||
|
||||
// As date
|
||||
if (DateTimeOffset.TryParse(str, formatProvider, DateTimeStyles.None, out var instant))
|
||||
{
|
||||
return FromDate(instant);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -89,11 +89,15 @@ internal partial class CsvMessageWriter : MessageWriter
|
|||
// Message content
|
||||
if (message.Kind.IsSystemNotification())
|
||||
{
|
||||
await _writer.WriteAsync(CsvEncode(message.GetFallbackContent()));
|
||||
await _writer.WriteAsync(CsvEncode(
|
||||
message.GetFallbackContent()
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
await _writer.WriteAsync(CsvEncode(await FormatMarkdownAsync(message.Content, cancellationToken)));
|
||||
await _writer.WriteAsync(CsvEncode(
|
||||
await FormatMarkdownAsync(message.Content, cancellationToken)
|
||||
));
|
||||
}
|
||||
|
||||
await _writer.WriteAsync(',');
|
||||
|
|
|
@ -75,9 +75,8 @@ internal partial class ExportAssetDownloader
|
|||
catch
|
||||
{
|
||||
// This can apparently fail for some reason.
|
||||
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
|
||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
|
||||
// Updating file dates is not a critical task, so we'll just
|
||||
// ignore exceptions thrown here.
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -98,7 +97,7 @@ internal partial class ExportAssetDownloader
|
|||
{
|
||||
var urlHash = GetUrlHash(url);
|
||||
|
||||
// Try to extract file name from URL
|
||||
// Try to extract the file name from URL
|
||||
var fileName = Regex.Match(url, @".+/([^?]*)").Groups[1].Value;
|
||||
|
||||
// If it's not there, just use the URL hash as the file name
|
||||
|
@ -110,7 +109,7 @@ internal partial class ExportAssetDownloader
|
|||
var fileExtension = Path.GetExtension(fileName);
|
||||
|
||||
// Probably not a file extension, just a dot in a long file name
|
||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/708
|
||||
// https://github.com/Tyrrrz/DiscordChatExporter/pull/812
|
||||
if (fileExtension.Length > 41)
|
||||
{
|
||||
fileNameWithoutExtension = fileName;
|
||||
|
|
|
@ -55,10 +55,13 @@ internal class ExportContext
|
|||
|
||||
var member = await Discord.TryGetGuildMemberAsync(Request.Guild.Id, id, cancellationToken);
|
||||
|
||||
// User may have left the guild since they were mentioned
|
||||
// User may have left the guild since they were mentioned.
|
||||
// Create a dummy member object based on the user info.
|
||||
if (member is null)
|
||||
{
|
||||
var user = fallbackUser ?? await Discord.TryGetUserAsync(id, cancellationToken);
|
||||
|
||||
// User may have been deleted since they were mentioned
|
||||
if (user is not null)
|
||||
member = Member.CreateDefault(user);
|
||||
}
|
||||
|
@ -114,7 +117,7 @@ internal class ExportContext
|
|||
var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath);
|
||||
|
||||
// Prefer relative paths so that the output files can be copied around without breaking references.
|
||||
// If the assets path is outside of the export directory, use the absolute path instead.
|
||||
// If the assets path is outside of the export directory, use an absolute path instead.
|
||||
var optimalFilePath =
|
||||
relativeFilePath.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal) ||
|
||||
relativeFilePath.StartsWith(".." + Path.AltDirectorySeparatorChar, StringComparison.Ordinal)
|
||||
|
@ -135,8 +138,8 @@ internal class ExportContext
|
|||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/372
|
||||
catch (Exception ex) when (ex is HttpRequestException or OperationCanceledException)
|
||||
{
|
||||
// TODO: add logging so we can be more liberal with catching exceptions
|
||||
// We don't want this to crash the exporting process in case of failure
|
||||
// We don't want this to crash the exporting process in case of failure.
|
||||
// TODO: add logging so we can be more liberal with catching exceptions.
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,12 +52,12 @@ internal class HtmlMessageWriter : MessageWriter
|
|||
if ((message.Timestamp - lastMessage.Timestamp).Duration().TotalMinutes > 7)
|
||||
return false;
|
||||
|
||||
// Messages must be from the same author
|
||||
// Messages must be sent by the same author
|
||||
if (message.Author.Id != lastMessage.Author.Id)
|
||||
return false;
|
||||
|
||||
// If the user changed their name after the last message, their new messages
|
||||
// cannot join an existing group.
|
||||
// If the author changed their name after the last message, their new messages
|
||||
// cannot join the existing group.
|
||||
if (!string.Equals(message.Author.FullName, lastMessage.Author.FullName, StringComparison.Ordinal))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ internal partial class MessageExporter : IAsyncDisposable
|
|||
|
||||
private async ValueTask<MessageWriter> GetWriterAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Ensure partition limit has not been reached
|
||||
// Ensure that the partition limit has not been reached
|
||||
if (_writer is not null &&
|
||||
_context.Request.PartitionLimit.IsReached(_writer.MessagesWritten, _writer.BytesWritten))
|
||||
{
|
||||
|
@ -74,11 +74,11 @@ internal partial class MessageExporter
|
|||
{
|
||||
private static string GetPartitionFilePath(string baseFilePath, int partitionIndex)
|
||||
{
|
||||
// First partition, don't change file name
|
||||
// First partition, don't change the file name
|
||||
if (partitionIndex <= 0)
|
||||
return baseFilePath;
|
||||
|
||||
// Inject partition index into file name
|
||||
// Inject partition index into the file name
|
||||
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(baseFilePath);
|
||||
var fileExt = Path.GetExtension(baseFilePath);
|
||||
var fileName = $"{fileNameWithoutExt} [part {partitionIndex + 1}]{fileExt}";
|
||||
|
|
|
@ -16,7 +16,7 @@ public static class Http
|
|||
|
||||
private static bool IsRetryableStatusCode(HttpStatusCode statusCode) =>
|
||||
statusCode is HttpStatusCode.TooManyRequests or HttpStatusCode.RequestTimeout ||
|
||||
// Treat all server-side errors as retryable.
|
||||
// Treat all server-side errors as retryable
|
||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/908
|
||||
(int)statusCode >= 500;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue