From 9048557b17ab7f51986458c30126260e9bd73761 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 13 May 2023 21:45:15 +0300 Subject: [PATCH] Short-circuit if trying to export an empty channel or a channel with mismatching boundaries Closes #1024 --- .../Commands/Base/ExportCommandBase.cs | 2 +- DiscordChatExporter.Core/Discord/DiscordClient.cs | 5 ++--- DiscordChatExporter.Core/Discord/Snowflake.cs | 4 ++++ DiscordChatExporter.Core/Exporting/ChannelExporter.cs | 9 ++++++++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index ec40184..cadd4f0 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -169,10 +169,10 @@ public abstract class ExportCommandBase : DiscordCommandBase ); } + // Export var cancellationToken = console.RegisterCancellationHandler(); var errors = new ConcurrentDictionary(); - // Export await console.Output.WriteLineAsync($"Exporting {channels.Count} channel(s)..."); await console.CreateProgressTicker().StartAsync(async progressContext => { diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index ef573e2..02013f9 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -333,10 +333,9 @@ public class DiscordClient IProgress? progress = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - // Get the last message in the specified range. - // This snapshots the boundaries, which means that messages posted after the export started + // Get the last message in the specified range, so we can later calculate progress based on its date. + // This also 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, cancellationToken); if (lastMessage is null || lastMessage.Timestamp < after?.ToDate()) yield break; diff --git a/DiscordChatExporter.Core/Discord/Snowflake.cs b/DiscordChatExporter.Core/Discord/Snowflake.cs index 194b166..f2cb89e 100644 --- a/DiscordChatExporter.Core/Discord/Snowflake.cs +++ b/DiscordChatExporter.Core/Discord/Snowflake.cs @@ -59,4 +59,8 @@ public partial record struct Snowflake : IComparable, IComparable return Value.CompareTo(other.Value); } + + public static bool operator >(Snowflake left, Snowflake right) => left.CompareTo(right) > 0; + + public static bool operator <(Snowflake left, Snowflake right) => left.CompareTo(right) < 0; } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs index 75c319e..b1a8bff 100644 --- a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs +++ b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs @@ -18,13 +18,20 @@ public class ChannelExporter IProgress? progress = null, CancellationToken cancellationToken = default) { + // Check if the channel is empty + if (request.Channel.LastMessageId is null) + throw DiscordChatExporterException.ChannelIsEmpty(); + + // Check if the 'after' boundary is valid + if (request.After is not null && request.Channel.LastMessageId < request.After) + throw DiscordChatExporterException.ChannelIsEmpty(); + // Build context var context = new ExportContext(_discord, request); await context.PopulateChannelsAndRolesAsync(cancellationToken); // Export messages await using var messageExporter = new MessageExporter(context); - await foreach (var message in _discord.GetMessagesAsync( request.Channel.Id, request.After,