mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-06-18 14:35:37 -04:00
Clean up
This commit is contained in:
parent
540ba34fb4
commit
08718425f1
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx;
|
using CliFx;
|
||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
@ -34,9 +35,9 @@ public abstract class DiscordCommandBase : ICommand
|
|||||||
)]
|
)]
|
||||||
public bool ShouldRespectRateLimits { get; init; } = true;
|
public bool ShouldRespectRateLimits { get; init; } = true;
|
||||||
|
|
||||||
private DiscordClient? _discordClient;
|
[field: AllowNull, MaybeNull]
|
||||||
protected DiscordClient Discord =>
|
protected DiscordClient Discord =>
|
||||||
_discordClient ??= new DiscordClient(
|
field ??= new DiscordClient(
|
||||||
Token,
|
Token,
|
||||||
ShouldRespectRateLimits ? RateLimitPreference.RespectAll : RateLimitPreference.IgnoreAll
|
ShouldRespectRateLimits ? RateLimitPreference.RespectAll : RateLimitPreference.IgnoreAll
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -16,7 +17,6 @@ using DiscordChatExporter.Core.Exceptions;
|
|||||||
using DiscordChatExporter.Core.Exporting;
|
using DiscordChatExporter.Core.Exporting;
|
||||||
using DiscordChatExporter.Core.Exporting.Filtering;
|
using DiscordChatExporter.Core.Exporting.Filtering;
|
||||||
using DiscordChatExporter.Core.Exporting.Partitioning;
|
using DiscordChatExporter.Core.Exporting.Partitioning;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
|
||||||
using Gress;
|
using Gress;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
@ -24,8 +24,6 @@ namespace DiscordChatExporter.Cli.Commands.Base;
|
|||||||
|
|
||||||
public abstract class ExportCommandBase : DiscordCommandBase
|
public abstract class ExportCommandBase : DiscordCommandBase
|
||||||
{
|
{
|
||||||
private readonly string _outputPath = Directory.GetCurrentDirectory();
|
|
||||||
|
|
||||||
[CommandOption(
|
[CommandOption(
|
||||||
"output",
|
"output",
|
||||||
'o',
|
'o',
|
||||||
@ -36,11 +34,11 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|||||||
)]
|
)]
|
||||||
public string OutputPath
|
public string OutputPath
|
||||||
{
|
{
|
||||||
get => _outputPath;
|
get;
|
||||||
// Handle ~/ in paths on Unix systems
|
// Handle ~/ in paths on Unix systems
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/pull/903
|
// https://github.com/Tyrrrz/DiscordChatExporter/pull/903
|
||||||
init => _outputPath = Path.GetFullPath(value);
|
init => field = Path.GetFullPath(value);
|
||||||
}
|
} = Directory.GetCurrentDirectory();
|
||||||
|
|
||||||
[CommandOption("format", 'f', Description = "Export format.")]
|
[CommandOption("format", 'f', Description = "Export format.")]
|
||||||
public ExportFormat ExportFormat { get; init; } = ExportFormat.HtmlDark;
|
public ExportFormat ExportFormat { get; init; } = ExportFormat.HtmlDark;
|
||||||
@ -103,8 +101,6 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|||||||
)]
|
)]
|
||||||
public bool ShouldReuseAssets { get; init; } = false;
|
public bool ShouldReuseAssets { get; init; } = false;
|
||||||
|
|
||||||
private readonly string? _assetsDirPath;
|
|
||||||
|
|
||||||
[CommandOption(
|
[CommandOption(
|
||||||
"media-dir",
|
"media-dir",
|
||||||
Description = "Download assets to this directory. "
|
Description = "Download assets to this directory. "
|
||||||
@ -112,10 +108,10 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|||||||
)]
|
)]
|
||||||
public string? AssetsDirPath
|
public string? AssetsDirPath
|
||||||
{
|
{
|
||||||
get => _assetsDirPath;
|
get;
|
||||||
// Handle ~/ in paths on Unix systems
|
// Handle ~/ in paths on Unix systems
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/pull/903
|
// https://github.com/Tyrrrz/DiscordChatExporter/pull/903
|
||||||
init => _assetsDirPath = value is not null ? Path.GetFullPath(value) : null;
|
init => field = value is not null ? Path.GetFullPath(value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This option doesn't do anything. Kept for backwards compatibility.")]
|
[Obsolete("This option doesn't do anything. Kept for backwards compatibility.")]
|
||||||
@ -144,8 +140,8 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|||||||
)]
|
)]
|
||||||
public bool IsUkraineSupportMessageDisabled { get; init; } = false;
|
public bool IsUkraineSupportMessageDisabled { get; init; } = false;
|
||||||
|
|
||||||
private ChannelExporter? _channelExporter;
|
[field: AllowNull, MaybeNull]
|
||||||
protected ChannelExporter Exporter => _channelExporter ??= new ChannelExporter(Discord);
|
protected ChannelExporter Exporter => field ??= new ChannelExporter(Discord);
|
||||||
|
|
||||||
protected async ValueTask ExportAsync(IConsole console, IReadOnlyList<Channel> channels)
|
protected async ValueTask ExportAsync(IConsole console, IReadOnlyList<Channel> channels)
|
||||||
{
|
{
|
||||||
@ -353,44 +349,6 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
|||||||
throw new CommandException("Export failed.");
|
throw new CommandException("Export failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async ValueTask ExportAsync(IConsole console, IReadOnlyList<Snowflake> channelIds)
|
|
||||||
{
|
|
||||||
var cancellationToken = console.RegisterCancellationHandler();
|
|
||||||
|
|
||||||
await console.Output.WriteLineAsync("Resolving channel(s)...");
|
|
||||||
|
|
||||||
var channels = new List<Channel>();
|
|
||||||
var channelsByGuild = new Dictionary<Snowflake, IReadOnlyList<Channel>>();
|
|
||||||
|
|
||||||
foreach (var channelId in channelIds)
|
|
||||||
{
|
|
||||||
var channel = await Discord.GetChannelAsync(channelId, cancellationToken);
|
|
||||||
|
|
||||||
// Unwrap categories
|
|
||||||
if (channel.IsCategory)
|
|
||||||
{
|
|
||||||
var guildChannels =
|
|
||||||
channelsByGuild.GetValueOrDefault(channel.GuildId)
|
|
||||||
?? await Discord.GetGuildChannelsAsync(channel.GuildId, cancellationToken);
|
|
||||||
|
|
||||||
foreach (var guildChannel in guildChannels)
|
|
||||||
{
|
|
||||||
if (guildChannel.Parent?.Id == channel.Id)
|
|
||||||
channels.Add(guildChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the guild channels to avoid redundant work
|
|
||||||
channelsByGuild[channel.GuildId] = guildChannels;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
channels.Add(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await ExportAsync(console, channels);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async ValueTask ExecuteAsync(IConsole console)
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
{
|
{
|
||||||
// Support Ukraine callout
|
// Support Ukraine callout
|
||||||
|
@ -5,8 +5,6 @@ using System.Threading.Tasks;
|
|||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using DiscordChatExporter.Cli.Commands.Base;
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
using DiscordChatExporter.Cli.Commands.Converters;
|
|
||||||
using DiscordChatExporter.Cli.Commands.Shared;
|
|
||||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
using DiscordChatExporter.Cli.Utils.Extensions;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
using DiscordChatExporter.Core.Discord.Dump;
|
using DiscordChatExporter.Core.Discord.Dump;
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using DiscordChatExporter.Cli.Commands.Base;
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
using DiscordChatExporter.Cli.Commands.Converters;
|
|
||||||
using DiscordChatExporter.Cli.Commands.Shared;
|
|
||||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
|
||||||
using DiscordChatExporter.Core.Discord;
|
using DiscordChatExporter.Core.Discord;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using Spectre.Console;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Commands;
|
namespace DiscordChatExporter.Cli.Commands;
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ using System.Threading.Tasks;
|
|||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using DiscordChatExporter.Cli.Commands.Base;
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
using DiscordChatExporter.Cli.Commands.Converters;
|
|
||||||
using DiscordChatExporter.Cli.Commands.Shared;
|
|
||||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
using DiscordChatExporter.Cli.Utils.Extensions;
|
||||||
using DiscordChatExporter.Core.Discord;
|
using DiscordChatExporter.Core.Discord;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
@ -28,7 +26,6 @@ public class ExportGuildCommand : ExportCommandBase
|
|||||||
var cancellationToken = console.RegisterCancellationHandler();
|
var cancellationToken = console.RegisterCancellationHandler();
|
||||||
var channels = new List<Channel>();
|
var channels = new List<Channel>();
|
||||||
|
|
||||||
// Regular channels
|
|
||||||
await console.Output.WriteLineAsync("Fetching channels...");
|
await console.Output.WriteLineAsync("Fetching channels...");
|
||||||
|
|
||||||
var fetchedChannelsCount = 0;
|
var fetchedChannelsCount = 0;
|
||||||
|
@ -321,6 +321,73 @@ public class DiscordClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async IAsyncEnumerable<Role> GetGuildRolesAsync(
|
||||||
|
Snowflake guildId,
|
||||||
|
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (guildId == Guild.DirectMessages.Id)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var response = await GetJsonResponseAsync($"guilds/{guildId}/roles", cancellationToken);
|
||||||
|
foreach (var roleJson in response.EnumerateArray())
|
||||||
|
yield return Role.Parse(roleJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Member?> TryGetGuildMemberAsync(
|
||||||
|
Snowflake guildId,
|
||||||
|
Snowflake memberId,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (guildId == Guild.DirectMessages.Id)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var response = await TryGetJsonResponseAsync(
|
||||||
|
$"guilds/{guildId}/members/{memberId}",
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
return response?.Pipe(j => Member.Parse(j, guildId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Invite?> TryGetInviteAsync(
|
||||||
|
string code,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var response = await TryGetJsonResponseAsync($"invites/{code}", cancellationToken);
|
||||||
|
return response?.Pipe(Invite.Parse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<Channel> GetChannelAsync(
|
||||||
|
Snowflake channelId,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var response = await GetJsonResponseAsync($"channels/{channelId}", cancellationToken);
|
||||||
|
|
||||||
|
var parentId = response
|
||||||
|
.GetPropertyOrNull("parent_id")
|
||||||
|
?.GetNonWhiteSpaceStringOrNull()
|
||||||
|
?.Pipe(Snowflake.Parse);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var parent = parentId is not null
|
||||||
|
? await GetChannelAsync(parentId.Value, cancellationToken)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return Channel.Parse(response, parent);
|
||||||
|
}
|
||||||
|
// It's possible for the parent channel to be inaccessible, despite the
|
||||||
|
// child channel being accessible.
|
||||||
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1108
|
||||||
|
catch (DiscordChatExporterException)
|
||||||
|
{
|
||||||
|
return Channel.Parse(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async IAsyncEnumerable<Channel> GetChannelThreadsAsync(
|
public async IAsyncEnumerable<Channel> GetChannelThreadsAsync(
|
||||||
IEnumerable<Channel> channels,
|
IEnumerable<Channel> channels,
|
||||||
bool includeArchived = false,
|
bool includeArchived = false,
|
||||||
@ -329,7 +396,7 @@ public class DiscordClient(
|
|||||||
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Channel[] filteredChannels = channels
|
var filteredChannels = channels
|
||||||
// Categories cannot have threads
|
// Categories cannot have threads
|
||||||
.Where(c => !c.IsCategory)
|
.Where(c => !c.IsCategory)
|
||||||
// Voice channels cannot have threads
|
// Voice channels cannot have threads
|
||||||
@ -478,73 +545,6 @@ public class DiscordClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async IAsyncEnumerable<Role> GetGuildRolesAsync(
|
|
||||||
Snowflake guildId,
|
|
||||||
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (guildId == Guild.DirectMessages.Id)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
var response = await GetJsonResponseAsync($"guilds/{guildId}/roles", cancellationToken);
|
|
||||||
foreach (var roleJson in response.EnumerateArray())
|
|
||||||
yield return Role.Parse(roleJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<Member?> TryGetGuildMemberAsync(
|
|
||||||
Snowflake guildId,
|
|
||||||
Snowflake memberId,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (guildId == Guild.DirectMessages.Id)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var response = await TryGetJsonResponseAsync(
|
|
||||||
$"guilds/{guildId}/members/{memberId}",
|
|
||||||
cancellationToken
|
|
||||||
);
|
|
||||||
return response?.Pipe(j => Member.Parse(j, guildId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<Invite?> TryGetInviteAsync(
|
|
||||||
string code,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var response = await TryGetJsonResponseAsync($"invites/{code}", cancellationToken);
|
|
||||||
return response?.Pipe(Invite.Parse);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<Channel> GetChannelAsync(
|
|
||||||
Snowflake channelId,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var response = await GetJsonResponseAsync($"channels/{channelId}", cancellationToken);
|
|
||||||
|
|
||||||
var parentId = response
|
|
||||||
.GetPropertyOrNull("parent_id")
|
|
||||||
?.GetNonWhiteSpaceStringOrNull()
|
|
||||||
?.Pipe(Snowflake.Parse);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var parent = parentId is not null
|
|
||||||
? await GetChannelAsync(parentId.Value, cancellationToken)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return Channel.Parse(response, parent);
|
|
||||||
}
|
|
||||||
// It's possible for the parent channel to be inaccessible, despite the
|
|
||||||
// child channel being accessible.
|
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1108
|
|
||||||
catch (DiscordChatExporterException)
|
|
||||||
{
|
|
||||||
return Channel.Parse(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask<Message?> TryGetLastMessageAsync(
|
private async ValueTask<Message?> TryGetLastMessageAsync(
|
||||||
Snowflake channelId,
|
Snowflake channelId,
|
||||||
Snowflake? before = null,
|
Snowflake? before = null,
|
||||||
|
Loading…
Reference in New Issue
Block a user