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,