mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-06-18 06:25:34 -04:00
Clean up
This commit is contained in:
parent
540ba34fb4
commit
08718425f1
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx;
|
||||
using CliFx.Attributes;
|
||||
@ -34,9 +35,9 @@ public abstract class DiscordCommandBase : ICommand
|
||||
)]
|
||||
public bool ShouldRespectRateLimits { get; init; } = true;
|
||||
|
||||
private DiscordClient? _discordClient;
|
||||
[field: AllowNull, MaybeNull]
|
||||
protected DiscordClient Discord =>
|
||||
_discordClient ??= new DiscordClient(
|
||||
field ??= new DiscordClient(
|
||||
Token,
|
||||
ShouldRespectRateLimits ? RateLimitPreference.RespectAll : RateLimitPreference.IgnoreAll
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -16,7 +17,6 @@ using DiscordChatExporter.Core.Exceptions;
|
||||
using DiscordChatExporter.Core.Exporting;
|
||||
using DiscordChatExporter.Core.Exporting.Filtering;
|
||||
using DiscordChatExporter.Core.Exporting.Partitioning;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
using Gress;
|
||||
using Spectre.Console;
|
||||
|
||||
@ -24,8 +24,6 @@ namespace DiscordChatExporter.Cli.Commands.Base;
|
||||
|
||||
public abstract class ExportCommandBase : DiscordCommandBase
|
||||
{
|
||||
private readonly string _outputPath = Directory.GetCurrentDirectory();
|
||||
|
||||
[CommandOption(
|
||||
"output",
|
||||
'o',
|
||||
@ -36,11 +34,11 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
||||
)]
|
||||
public string OutputPath
|
||||
{
|
||||
get => _outputPath;
|
||||
get;
|
||||
// Handle ~/ in paths on Unix systems
|
||||
// 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.")]
|
||||
public ExportFormat ExportFormat { get; init; } = ExportFormat.HtmlDark;
|
||||
@ -103,8 +101,6 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
||||
)]
|
||||
public bool ShouldReuseAssets { get; init; } = false;
|
||||
|
||||
private readonly string? _assetsDirPath;
|
||||
|
||||
[CommandOption(
|
||||
"media-dir",
|
||||
Description = "Download assets to this directory. "
|
||||
@ -112,10 +108,10 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
||||
)]
|
||||
public string? AssetsDirPath
|
||||
{
|
||||
get => _assetsDirPath;
|
||||
get;
|
||||
// Handle ~/ in paths on Unix systems
|
||||
// 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.")]
|
||||
@ -144,8 +140,8 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
||||
)]
|
||||
public bool IsUkraineSupportMessageDisabled { get; init; } = false;
|
||||
|
||||
private ChannelExporter? _channelExporter;
|
||||
protected ChannelExporter Exporter => _channelExporter ??= new ChannelExporter(Discord);
|
||||
[field: AllowNull, MaybeNull]
|
||||
protected ChannelExporter Exporter => field ??= new ChannelExporter(Discord);
|
||||
|
||||
protected async ValueTask ExportAsync(IConsole console, IReadOnlyList<Channel> channels)
|
||||
{
|
||||
@ -353,44 +349,6 @@ public abstract class ExportCommandBase : DiscordCommandBase
|
||||
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)
|
||||
{
|
||||
// Support Ukraine callout
|
||||
|
@ -5,8 +5,6 @@ using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
using DiscordChatExporter.Cli.Commands.Base;
|
||||
using DiscordChatExporter.Cli.Commands.Converters;
|
||||
using DiscordChatExporter.Cli.Commands.Shared;
|
||||
using DiscordChatExporter.Cli.Utils.Extensions;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Discord.Dump;
|
||||
|
@ -1,16 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
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.Data;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace DiscordChatExporter.Cli.Commands;
|
||||
|
||||
|
@ -3,8 +3,6 @@ using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
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.Data;
|
||||
@ -28,7 +26,6 @@ public class ExportGuildCommand : ExportCommandBase
|
||||
var cancellationToken = console.RegisterCancellationHandler();
|
||||
var channels = new List<Channel>();
|
||||
|
||||
// Regular channels
|
||||
await console.Output.WriteLineAsync("Fetching channels...");
|
||||
|
||||
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(
|
||||
IEnumerable<Channel> channels,
|
||||
bool includeArchived = false,
|
||||
@ -329,7 +396,7 @@ public class DiscordClient(
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
Channel[] filteredChannels = channels
|
||||
var filteredChannels = channels
|
||||
// Categories cannot have threads
|
||||
.Where(c => !c.IsCategory)
|
||||
// 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(
|
||||
Snowflake channelId,
|
||||
Snowflake? before = null,
|
||||
|
Loading…
Reference in New Issue
Block a user