mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-03-17 20:32:34 +00:00
Remove Channel.ParentNameWithFallback
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
@@ -20,32 +21,36 @@ public partial record Channel(
|
||||
Snowflake? LastMessageId
|
||||
) : IHasId
|
||||
{
|
||||
// Used for visual backwards-compatibility with old exports, where
|
||||
// channels without a parent (i.e. mostly DM channels) or channels
|
||||
// with an inaccessible parent (i.e. inside private categories) had
|
||||
// a fallback category created for them.
|
||||
public string ParentNameWithFallback =>
|
||||
Parent?.Name
|
||||
?? Kind switch
|
||||
{
|
||||
ChannelKind.GuildCategory => "Category",
|
||||
ChannelKind.GuildTextChat => "Text",
|
||||
ChannelKind.DirectTextChat => "Private",
|
||||
ChannelKind.DirectGroupTextChat => "Group",
|
||||
ChannelKind.GuildPrivateThread => "Private Thread",
|
||||
ChannelKind.GuildPublicThread => "Public Thread",
|
||||
ChannelKind.GuildNews => "News",
|
||||
ChannelKind.GuildNewsThread => "News Thread",
|
||||
_ => "Default"
|
||||
};
|
||||
public bool IsDirect => Kind is ChannelKind.DirectTextChat or ChannelKind.DirectGroupTextChat;
|
||||
|
||||
public bool IsGuild => !IsDirect;
|
||||
|
||||
public bool IsCategory => Kind == ChannelKind.GuildCategory;
|
||||
|
||||
public bool IsVoice => Kind is ChannelKind.GuildVoiceChat or ChannelKind.GuildStageVoice;
|
||||
|
||||
public bool IsThread =>
|
||||
Kind
|
||||
is ChannelKind.GuildNewsThread
|
||||
or ChannelKind.GuildPublicThread
|
||||
or ChannelKind.GuildPrivateThread;
|
||||
|
||||
public bool IsEmpty => LastMessageId is null;
|
||||
|
||||
// Only needed for WPF data binding. Don't use anywhere else.
|
||||
public bool IsVoice => Kind.IsVoice();
|
||||
public IEnumerable<Channel> GetParents()
|
||||
{
|
||||
var current = Parent;
|
||||
while (current is not null)
|
||||
{
|
||||
yield return current;
|
||||
current = current.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
// Only needed for WPF data binding. Don't use anywhere else.
|
||||
public bool IsThread => Kind.IsThread();
|
||||
public Channel? TryGetRootParent() => GetParents().LastOrDefault();
|
||||
|
||||
public string GetHierarchicalName() =>
|
||||
string.Join(" / ", GetParents().Reverse().Select(c => c.Name).Append(Name));
|
||||
|
||||
public bool MayHaveMessagesAfter(Snowflake messageId) => !IsEmpty && messageId < LastMessageId;
|
||||
|
||||
|
||||
@@ -16,20 +16,3 @@ public enum ChannelKind
|
||||
GuildDirectory = 14,
|
||||
GuildForum = 15
|
||||
}
|
||||
|
||||
public static class ChannelKindExtensions
|
||||
{
|
||||
public static bool IsDirect(this ChannelKind kind) =>
|
||||
kind is ChannelKind.DirectTextChat or ChannelKind.DirectGroupTextChat;
|
||||
|
||||
public static bool IsGuild(this ChannelKind kind) => !kind.IsDirect();
|
||||
|
||||
public static bool IsVoice(this ChannelKind kind) =>
|
||||
kind is ChannelKind.GuildVoiceChat or ChannelKind.GuildStageVoice;
|
||||
|
||||
public static bool IsThread(this ChannelKind kind) =>
|
||||
kind
|
||||
is ChannelKind.GuildNewsThread
|
||||
or ChannelKind.GuildPublicThread
|
||||
or ChannelKind.GuildPrivateThread;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,12 @@ using JsonExtensions.Reading;
|
||||
namespace DiscordChatExporter.Core.Discord.Data;
|
||||
|
||||
// https://discord.com/developers/docs/resources/guild#guild-object
|
||||
public record Guild(Snowflake Id, string Name, string IconUrl) : IHasId
|
||||
public partial record Guild(Snowflake Id, string Name, string IconUrl) : IHasId
|
||||
{
|
||||
public bool IsDirect => Id == DirectMessages.Id;
|
||||
}
|
||||
|
||||
public partial record Guild
|
||||
{
|
||||
// Direct messages are encapsulated within a special pseudo-guild for consistency
|
||||
public static Guild DirectMessages { get; } =
|
||||
|
||||
@@ -30,7 +30,13 @@ public partial record Message(
|
||||
Interaction? Interaction
|
||||
) : IHasId
|
||||
{
|
||||
public bool IsReplyLike => Kind == MessageKind.Reply || Interaction is not null;
|
||||
public bool IsSystemNotification =>
|
||||
Kind is >= MessageKind.RecipientAdd and <= MessageKind.ThreadCreated;
|
||||
|
||||
public bool IsReply => Kind == MessageKind.Reply;
|
||||
|
||||
// App interactions are rendered as replies in the Discord client, but they are not actually replies
|
||||
public bool IsReplyLike => IsReply || Interaction is not null;
|
||||
|
||||
public IEnumerable<User> GetReferencedUsers()
|
||||
{
|
||||
|
||||
@@ -14,8 +14,3 @@ public enum MessageKind
|
||||
ThreadCreated = 18,
|
||||
Reply = 19
|
||||
}
|
||||
|
||||
public static class MessageKindExtensions
|
||||
{
|
||||
public static bool IsSystemNotification(this MessageKind kind) => (int)kind is >= 1 and <= 18;
|
||||
}
|
||||
|
||||
@@ -286,11 +286,12 @@ public class DiscordClient
|
||||
yield break;
|
||||
|
||||
var tokenKind = _resolvedTokenKind ??= await GetTokenKindAsync(cancellationToken);
|
||||
|
||||
var channels = (await GetGuildChannelsAsync(guildId, cancellationToken))
|
||||
// Categories cannot have threads
|
||||
.Where(c => c.Kind != ChannelKind.GuildCategory)
|
||||
.Where(c => !c.IsCategory)
|
||||
// Voice channels cannot have threads
|
||||
.Where(c => !c.Kind.IsVoice())
|
||||
.Where(c => !c.IsVoice)
|
||||
// Empty channels cannot have threads
|
||||
.Where(c => !c.IsEmpty)
|
||||
// If the 'before' boundary is specified, skip channels that don't have messages
|
||||
|
||||
@@ -92,7 +92,7 @@ internal partial class CsvMessageWriter : MessageWriter
|
||||
await _writer.WriteAsync(',');
|
||||
|
||||
// Message content
|
||||
if (message.Kind.IsSystemNotification())
|
||||
if (message.IsSystemNotification)
|
||||
{
|
||||
await _writer.WriteAsync(CsvEncode(message.GetFallbackContent()));
|
||||
}
|
||||
|
||||
@@ -99,10 +99,21 @@ public partial class ExportRequest
|
||||
{
|
||||
var buffer = new StringBuilder();
|
||||
|
||||
// Guild and channel names
|
||||
buffer.Append(
|
||||
$"{guild.Name} - {channel.ParentNameWithFallback} - {channel.Name} [{channel.Id}]"
|
||||
);
|
||||
// Guild name
|
||||
buffer.Append(guild.Name);
|
||||
|
||||
// Parent name
|
||||
if (channel.Parent is not null)
|
||||
buffer.Append(" - ").Append(channel.Parent.Name);
|
||||
|
||||
// Channel name and ID
|
||||
buffer
|
||||
.Append(" - ")
|
||||
.Append(channel.Name)
|
||||
.Append(' ')
|
||||
.Append('[')
|
||||
.Append(channel.Id)
|
||||
.Append(']');
|
||||
|
||||
// Date range
|
||||
if (after is not null || before is not null)
|
||||
@@ -142,9 +153,8 @@ public partial class ExportRequest
|
||||
Channel channel,
|
||||
Snowflake? after,
|
||||
Snowflake? before
|
||||
)
|
||||
{
|
||||
return Regex.Replace(
|
||||
) =>
|
||||
Regex.Replace(
|
||||
path,
|
||||
"%.",
|
||||
m =>
|
||||
@@ -153,14 +163,18 @@ public partial class ExportRequest
|
||||
{
|
||||
"%g" => guild.Id.ToString(),
|
||||
"%G" => guild.Name,
|
||||
|
||||
"%t" => channel.Parent?.Id.ToString() ?? "",
|
||||
"%T" => channel.Parent?.Name ?? "",
|
||||
|
||||
"%c" => channel.Id.ToString(),
|
||||
"%C" => channel.Name,
|
||||
|
||||
"%p" => channel.Position?.ToString(CultureInfo.InvariantCulture) ?? "0",
|
||||
"%P"
|
||||
=> channel.Parent?.Position?.ToString(CultureInfo.InvariantCulture)
|
||||
?? "0",
|
||||
|
||||
"%a"
|
||||
=> after?.ToDate().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
|
||||
?? "",
|
||||
@@ -172,12 +186,12 @@ public partial class ExportRequest
|
||||
"yyyy-MM-dd",
|
||||
CultureInfo.InvariantCulture
|
||||
),
|
||||
|
||||
"%%" => "%",
|
||||
_ => m.Value
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static string GetOutputBaseFilePath(
|
||||
Guild guild,
|
||||
|
||||
@@ -280,7 +280,7 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||
else if (mention.Kind == MentionKind.Channel)
|
||||
{
|
||||
var channel = mention.TargetId?.Pipe(_context.TryGetChannel);
|
||||
var symbol = channel?.Kind.IsVoice() == true ? "🔊" : "#";
|
||||
var symbol = channel?.IsVoice == true ? "🔊" : "#";
|
||||
var name = channel?.Name ?? "deleted-channel";
|
||||
|
||||
_buffer.Append(
|
||||
|
||||
@@ -30,22 +30,22 @@ internal class HtmlMessageWriter : MessageWriter
|
||||
if (_messageGroup.LastOrDefault() is not { } lastMessage)
|
||||
return true;
|
||||
|
||||
// Reply messages cannot join existing groups because they need to appear first
|
||||
if (message.Kind == MessageKind.Reply)
|
||||
// Reply-like messages cannot join existing groups because they need to appear first
|
||||
if (message.IsReplyLike)
|
||||
return false;
|
||||
|
||||
// Grouping for system notifications
|
||||
if (message.Kind.IsSystemNotification())
|
||||
if (message.IsSystemNotification)
|
||||
{
|
||||
// Can only be grouped with other system notifications
|
||||
if (!lastMessage.Kind.IsSystemNotification())
|
||||
if (!lastMessage.IsSystemNotification)
|
||||
return false;
|
||||
}
|
||||
// Grouping for normal messages
|
||||
else
|
||||
{
|
||||
// Can only be grouped with other normal messages
|
||||
if (lastMessage.Kind.IsSystemNotification())
|
||||
if (lastMessage.IsSystemNotification)
|
||||
return false;
|
||||
|
||||
// Messages must be within 7 minutes of each other
|
||||
|
||||
@@ -273,8 +273,11 @@ internal class JsonMessageWriter : MessageWriter
|
||||
_writer.WriteStartObject("channel");
|
||||
_writer.WriteString("id", Context.Request.Channel.Id.ToString());
|
||||
_writer.WriteString("type", Context.Request.Channel.Kind.ToString());
|
||||
|
||||
// Original schema did not account for threads, so 'category' actually refers to the parent channel
|
||||
_writer.WriteString("categoryId", Context.Request.Channel.Parent?.Id.ToString());
|
||||
_writer.WriteString("category", Context.Request.Channel.ParentNameWithFallback);
|
||||
_writer.WriteString("category", Context.Request.Channel.Parent?.Name);
|
||||
|
||||
_writer.WriteString("name", Context.Request.Channel.Name);
|
||||
_writer.WriteString("topic", Context.Request.Channel.Topic);
|
||||
|
||||
@@ -329,7 +332,7 @@ internal class JsonMessageWriter : MessageWriter
|
||||
_writer.WriteBoolean("isPinned", message.IsPinned);
|
||||
|
||||
// Content
|
||||
if (message.Kind.IsSystemNotification())
|
||||
if (message.IsSystemNotification)
|
||||
{
|
||||
_writer.WriteString("content", message.GetFallbackContent());
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<div id="chatlog__message-container-@message.Id" class="chatlog__message-container @(message.IsPinned ? "chatlog__message-container--pinned" : null)" data-message-id="@message.Id">
|
||||
<div class="chatlog__message">
|
||||
@* System notification *@
|
||||
@if (message.Kind.IsSystemNotification())
|
||||
@if (message.IsSystemNotification)
|
||||
{
|
||||
<div class="chatlog__message-aside">
|
||||
<svg class="chatlog__system-notification-icon">
|
||||
@@ -329,7 +329,7 @@
|
||||
|
||||
<div class="chatlog__embed">
|
||||
<div class="chatlog__embed-invite-container">
|
||||
<div class="chatlog__embed-invite-title">@(invite.Channel?.Kind.IsDirect() == true ? "Invite to join a group DM" : "Invite to join a server")</div>
|
||||
<div class="chatlog__embed-invite-title">@(invite.Channel?.IsDirect == true ? "Invite to join a group DM" : "Invite to join a server")</div>
|
||||
<div class="chatlog__embed-invite">
|
||||
<div class="chatlog__embed-invite-guild-icon-container">
|
||||
<img class="chatlog__embed-invite-guild-icon" src="@await ResolveAssetUrlAsync(invite.Channel?.IconUrl ?? invite.Guild.IconUrl)" alt="Guild icon" loading="lazy">
|
||||
|
||||
@@ -72,7 +72,7 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
|
||||
_buffer.Append($"#{name}");
|
||||
|
||||
// Voice channel marker
|
||||
if (channel?.Kind.IsVoice() == true)
|
||||
if (channel?.IsVoice == true)
|
||||
_buffer.Append(" [voice]");
|
||||
}
|
||||
else if (mention.Kind == MentionKind.Role)
|
||||
|
||||
@@ -200,9 +200,7 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||
{
|
||||
await _writer.WriteLineAsync(new string('=', 62));
|
||||
await _writer.WriteLineAsync($"Guild: {Context.Request.Guild.Name}");
|
||||
await _writer.WriteLineAsync(
|
||||
$"Channel: {Context.Request.Channel.ParentNameWithFallback} / {Context.Request.Channel.Name}"
|
||||
);
|
||||
await _writer.WriteLineAsync($"Channel: {Context.Request.Channel.GetHierarchicalName()}");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic))
|
||||
{
|
||||
@@ -238,7 +236,7 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||
await WriteMessageHeaderAsync(message);
|
||||
|
||||
// Content
|
||||
if (message.Kind.IsSystemNotification())
|
||||
if (message.IsSystemNotification)
|
||||
{
|
||||
await _writer.WriteLineAsync(message.GetFallbackContent());
|
||||
}
|
||||
|
||||
@@ -1004,7 +1004,7 @@
|
||||
</div>
|
||||
<div class="preamble__entries-container">
|
||||
<div class="preamble__entry">@Context.Request.Guild.Name</div>
|
||||
<div class="preamble__entry">@Context.Request.Channel.ParentNameWithFallback / @Context.Request.Channel.Name</div>
|
||||
<div class="preamble__entry">@Context.Request.Channel.GetHierarchicalName()</div>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user