mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-03-21 06:11:28 +00:00
Embrace Snowflake as first class citizen
This commit is contained in:
@@ -1,49 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Utilities;
|
||||
using DiscordChatExporter.Domain.Discord;
|
||||
using DiscordChatExporter.Domain.Discord.Models;
|
||||
using DiscordChatExporter.Domain.Exporting;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Commands.Base
|
||||
{
|
||||
public abstract partial class ExportCommandBase : TokenCommandBase
|
||||
public abstract class ExportCommandBase : TokenCommandBase
|
||||
{
|
||||
[CommandOption("output", 'o',
|
||||
Description = "Output file or directory path.")]
|
||||
public string OutputPath { get; set; } = Directory.GetCurrentDirectory();
|
||||
[CommandOption("output", 'o', Description = "Output file or directory path.")]
|
||||
public string OutputPath { get; init; } = Directory.GetCurrentDirectory();
|
||||
|
||||
[CommandOption("format", 'f',
|
||||
Description = "Export format.")]
|
||||
public ExportFormat ExportFormat { get; set; } = ExportFormat.HtmlDark;
|
||||
[CommandOption("format", 'f', Description = "Export format.")]
|
||||
public ExportFormat ExportFormat { get; init; } = ExportFormat.HtmlDark;
|
||||
|
||||
[CommandOption("after",
|
||||
Description = "Only include messages sent after this date. Alternatively, provide the ID of a message.")]
|
||||
public string? After { get; set; }
|
||||
[CommandOption("after", Description = "Only include messages sent after this date or message ID.")]
|
||||
public Snowflake? After { get; init; }
|
||||
|
||||
[CommandOption("before",
|
||||
Description = "Only include messages sent before this date. Alternatively, provide the ID of a message.")]
|
||||
public string? Before { get; set; }
|
||||
[CommandOption("before", Description = "Only include messages sent before this date or message ID.")]
|
||||
public Snowflake? Before { get; init; }
|
||||
|
||||
[CommandOption("partition", 'p',
|
||||
Description = "Split output into partitions limited to this number of messages.")]
|
||||
public int? PartitionLimit { get; set; }
|
||||
[CommandOption("partition", 'p', Description = "Split output into partitions limited to this number of messages.")]
|
||||
public int? PartitionLimit { get; init; }
|
||||
|
||||
[CommandOption("media",
|
||||
Description = "Download referenced media content.")]
|
||||
public bool ShouldDownloadMedia { get; set; }
|
||||
[CommandOption("media", Description = "Download referenced media content.")]
|
||||
public bool ShouldDownloadMedia { get; init; }
|
||||
|
||||
[CommandOption("reuse-media",
|
||||
Description = "Reuse already existing media content to skip redundant downloads.")]
|
||||
public bool ShouldReuseMedia { get; set; }
|
||||
[CommandOption("reuse-media", Description = "Reuse already existing media content to skip redundant downloads.")]
|
||||
public bool ShouldReuseMedia { get; init; }
|
||||
|
||||
[CommandOption("dateformat",
|
||||
Description = "Format used when writing dates.")]
|
||||
public string DateFormat { get; set; } = "dd-MMM-yy hh:mm tt";
|
||||
[CommandOption("dateformat", Description = "Format used when writing dates.")]
|
||||
public string DateFormat { get; init; } = "dd-MMM-yy hh:mm tt";
|
||||
|
||||
protected ChannelExporter GetChannelExporter() => new(GetDiscordClient());
|
||||
|
||||
@@ -57,8 +48,8 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
||||
channel,
|
||||
OutputPath,
|
||||
ExportFormat,
|
||||
ParseRangeOption(After, "--after"),
|
||||
ParseRangeOption(Before, "--before"),
|
||||
After,
|
||||
Before,
|
||||
PartitionLimit,
|
||||
ShouldDownloadMedia,
|
||||
ShouldReuseMedia,
|
||||
@@ -77,7 +68,7 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
||||
await ExportAsync(console, guild, channel);
|
||||
}
|
||||
|
||||
protected async ValueTask ExportAsync(IConsole console, string channelId)
|
||||
protected async ValueTask ExportAsync(IConsole console, Snowflake channelId)
|
||||
{
|
||||
var channel = await GetDiscordClient().GetChannelAsync(channelId);
|
||||
await ExportAsync(console, channel);
|
||||
@@ -93,29 +84,4 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class ExportCommandBase : TokenCommandBase
|
||||
{
|
||||
protected static DateTimeOffset? ParseRangeOption(string? value, string optionName)
|
||||
{
|
||||
if (value == null) return null;
|
||||
|
||||
var isSnowflake = Regex.IsMatch(value, @"^\d{18}$");
|
||||
var isDate = DateTimeOffset.TryParse(value, out var datetime);
|
||||
|
||||
if (!isSnowflake && !isDate)
|
||||
{
|
||||
throw new ArgumentException($"Value for ${optionName} must be either a date or a message ID.");
|
||||
}
|
||||
|
||||
return isSnowflake ? ExtractDateTimeFromSnowflake() : datetime;
|
||||
|
||||
DateTimeOffset ExtractDateTimeFromSnowflake()
|
||||
{
|
||||
var unixTimestampMs = (long.Parse(value) / 4194304 + 1420070400000);
|
||||
return DateTimeOffset.FromUnixTimeMilliseconds(unixTimestampMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,9 +17,8 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
||||
{
|
||||
public abstract class ExportMultipleCommandBase : ExportCommandBase
|
||||
{
|
||||
[CommandOption("parallel",
|
||||
Description = "Limits how many channels can be exported in parallel.")]
|
||||
public int ParallelLimit { get; set; } = 1;
|
||||
[CommandOption("parallel", Description = "Limits how many channels can be exported in parallel.")]
|
||||
public int ParallelLimit { get; init; } = 1;
|
||||
|
||||
protected async ValueTask ExportMultipleAsync(IConsole console, IReadOnlyList<Channel> channels)
|
||||
{
|
||||
@@ -47,8 +46,8 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
||||
channel,
|
||||
OutputPath,
|
||||
ExportFormat,
|
||||
ParseRangeOption(After, "--after"),
|
||||
ParseRangeOption(Before, "--before"),
|
||||
After,
|
||||
Before,
|
||||
PartitionLimit,
|
||||
ShouldDownloadMedia,
|
||||
ShouldReuseMedia,
|
||||
|
||||
@@ -7,15 +7,11 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
||||
{
|
||||
public abstract class TokenCommandBase : ICommand
|
||||
{
|
||||
[CommandOption("token", 't', IsRequired = true,
|
||||
EnvironmentVariableName = "DISCORD_TOKEN",
|
||||
Description = "Authorization token.")]
|
||||
public string TokenValue { get; set; } = "";
|
||||
[CommandOption("token", 't', IsRequired = true, EnvironmentVariableName = "DISCORD_TOKEN", Description = "Authorization token.")]
|
||||
public string TokenValue { get; init; } = "";
|
||||
|
||||
[CommandOption("bot", 'b',
|
||||
EnvironmentVariableName = "DISCORD_TOKEN_BOT",
|
||||
Description = "Authorize as a bot.")]
|
||||
public bool IsBotToken { get; set; }
|
||||
[CommandOption("bot", 'b', EnvironmentVariableName = "DISCORD_TOKEN_BOT", Description = "Authorize as a bot.")]
|
||||
public bool IsBotToken { get; init; }
|
||||
|
||||
protected AuthToken GetAuthToken() => new(
|
||||
IsBotToken
|
||||
|
||||
Reference in New Issue
Block a user