diff --git a/DiscordChatExporter.Core/Discord/Data/Channel.cs b/DiscordChatExporter.Core/Discord/Data/Channel.cs index 02ceb4e..9121622 100644 --- a/DiscordChatExporter.Core/Discord/Data/Channel.cs +++ b/DiscordChatExporter.Core/Discord/Data/Channel.cs @@ -42,7 +42,7 @@ public partial record Channel json.GetPropertyOrNull("recipients")? .EnumerateArrayOrNull()? .Select(User.Parse) - .Select(u => u.Name) + .Select(u => u.DisplayName) .Pipe(s => string.Join(", ", s)) ?? // Fallback diff --git a/DiscordChatExporter.Core/Discord/Data/Member.cs b/DiscordChatExporter.Core/Discord/Data/Member.cs index b84547f..5ebaf1d 100644 --- a/DiscordChatExporter.Core/Discord/Data/Member.cs +++ b/DiscordChatExporter.Core/Discord/Data/Member.cs @@ -11,7 +11,7 @@ namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/guild#guild-member-object public partial record Member( User User, - string? Nick, + string? DisplayName, string? AvatarUrl, IReadOnlyList RoleIds) : IHasId { @@ -25,7 +25,7 @@ public partial record Member public static Member Parse(JsonElement json, Snowflake? guildId = null) { var user = json.GetProperty("user").Pipe(User.Parse); - var nick = json.GetPropertyOrNull("nick")?.GetNonWhiteSpaceStringOrNull(); + var displayName = json.GetPropertyOrNull("nick")?.GetNonWhiteSpaceStringOrNull(); var roleIds = json .GetPropertyOrNull("roles")? @@ -43,7 +43,7 @@ public partial record Member return new Member( user, - nick, + displayName, avatarUrl, roleIds ); diff --git a/DiscordChatExporter.Core/Discord/Data/User.cs b/DiscordChatExporter.Core/Discord/Data/User.cs index 4720080..2171802 100644 --- a/DiscordChatExporter.Core/Discord/Data/User.cs +++ b/DiscordChatExporter.Core/Discord/Data/User.cs @@ -9,13 +9,24 @@ namespace DiscordChatExporter.Core.Discord.Data; public partial record User( Snowflake Id, bool IsBot, - int Discriminator, + // Remove after Discord migrates all accounts to the new system. + // With that, also remove the DiscriminatorFormatted and FullName properties. + // Replace existing calls to FullName with Name (not DisplayName). + int? Discriminator, string Name, + string DisplayName, string AvatarUrl) : IHasId { - public string DiscriminatorFormatted => $"{Discriminator:0000}"; + public string DiscriminatorFormatted => Discriminator is not null + ? $"{Discriminator:0000}" + : "0000"; - public string FullName => $"{Name}#{DiscriminatorFormatted}"; + // This effectively represents the user's true identity. + // In the old system, this is formed from the username and discriminator. + // In the new system, the username is already the user's unique identifier. + public string FullName => Discriminator is not null + ? $"{Name}#{DiscriminatorFormatted}" + : Name; } public partial record User @@ -24,16 +35,23 @@ public partial record User { var id = json.GetProperty("id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse); var isBot = json.GetPropertyOrNull("bot")?.GetBooleanOrNull() ?? false; - var discriminator = json.GetProperty("discriminator").GetNonWhiteSpaceString().Pipe(int.Parse); + + var discriminator = json + .GetPropertyOrNull("discriminator")? + .GetNonWhiteSpaceStringOrNull()? + .Pipe(int.Parse) + .NullIfDefault(); + var name = json.GetProperty("username").GetNonNullString(); + var displayName = json.GetPropertyOrNull("global_name")?.GetNonWhiteSpaceStringOrNull() ?? name; var avatarUrl = json .GetPropertyOrNull("avatar")? .GetNonWhiteSpaceStringOrNull()? .Pipe(h => ImageCdn.GetUserAvatarUrl(id, h)) ?? - ImageCdn.GetFallbackUserAvatarUrl(discriminator); + ImageCdn.GetFallbackUserAvatarUrl(discriminator ?? 0); - return new User(id, isBot, discriminator, name, avatarUrl); + return new User(id, isBot, discriminator, name, displayName, avatarUrl); } } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs index 3ea91ee..3bec86e 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs @@ -11,6 +11,7 @@ internal class FromMessageFilter : MessageFilter public override bool IsMatch(Message message) => string.Equals(_value, message.Author.Name, StringComparison.OrdinalIgnoreCase) || + string.Equals(_value, message.Author.DisplayName, StringComparison.OrdinalIgnoreCase) || string.Equals(_value, message.Author.FullName, StringComparison.OrdinalIgnoreCase) || string.Equals(_value, message.Author.Id.ToString(), StringComparison.OrdinalIgnoreCase); } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs index 5cccd52..a5d9418 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs @@ -12,6 +12,7 @@ internal class MentionsMessageFilter : MessageFilter public override bool IsMatch(Message message) => message.MentionedUsers.Any(user => string.Equals(_value, user.Name, StringComparison.OrdinalIgnoreCase) || + string.Equals(_value, user.DisplayName, StringComparison.OrdinalIgnoreCase) || string.Equals(_value, user.FullName, StringComparison.OrdinalIgnoreCase) || string.Equals(_value, user.Id.ToString(), StringComparison.OrdinalIgnoreCase) ); diff --git a/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs b/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs index 7946e68..437cfdf 100644 --- a/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs @@ -249,12 +249,12 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor var member = mention.TargetId?.Pipe(_context.TryGetMember); var fullName = member?.User.FullName ?? "Unknown"; - var nick = member?.Nick ?? member?.User.Name ?? "Unknown"; + var displayName = member?.DisplayName ?? member?.User.DisplayName ?? "Unknown"; _buffer.Append( // lang=html $""" - @{HtmlEncode(nick)} + @{HtmlEncode(displayName)} """ ); } diff --git a/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs b/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs index c3df597..88d36d7 100644 --- a/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs @@ -44,7 +44,7 @@ internal class JsonMessageWriter : MessageWriter _writer.WriteString("id", user.Id.ToString()); _writer.WriteString("name", user.Name); _writer.WriteString("discriminator", user.DiscriminatorFormatted); - _writer.WriteString("nickname", Context.TryGetMember(user.Id)?.Nick ?? user.Name); + _writer.WriteString("nickname", Context.TryGetMember(user.Id)?.DisplayName ?? user.DisplayName); _writer.WriteString("color", Context.TryGetUserColor(user.Id)?.ToHex()); _writer.WriteBoolean("isBot", user.IsBot); diff --git a/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml b/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml index 8404860..947c1ef 100644 --- a/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml @@ -40,9 +40,9 @@ var authorMember = Context.TryGetMember(message.Author.Id); var authorColor = Context.TryGetUserColor(message.Author.Id); - var authorNick = message.Author.IsBot - ? message.Author.Name - : authorMember?.Nick ?? message.Author.Name; + var authorDisplayName = message.Author.IsBot + ? message.Author.DisplayName + : authorMember?.DisplayName ?? message.Author.DisplayName;
@@ -71,7 +71,7 @@
@* Author name *@ - @authorNick + @authorDisplayName @* Space out the content *@ @@ -81,7 +81,7 @@ @if (message.Kind == MessageKind.RecipientAdd && message.MentionedUsers.Any()) { added - @message.MentionedUsers.First().Name + @message.MentionedUsers.First().DisplayName to the group. } else if (message.Kind == MessageKind.RecipientRemove && message.MentionedUsers.Any()) @@ -93,7 +93,7 @@ else { removed - @message.MentionedUsers.First().Name + @message.MentionedUsers.First().DisplayName from the group. } } @@ -168,12 +168,12 @@ { var referencedUserMember = Context.TryGetMember(message.ReferencedMessage.Author.Id); var referencedUserColor = Context.TryGetUserColor(message.ReferencedMessage.Author.Id); - var referencedUserNick = message.ReferencedMessage.Author.IsBot - ? message.ReferencedMessage.Author.Name - : referencedUserMember?.Nick ?? message.ReferencedMessage.Author.Name; + var referencedUserDisplayName = message.ReferencedMessage.Author.IsBot + ? message.ReferencedMessage.Author.DisplayName + : referencedUserMember?.DisplayName ?? message.ReferencedMessage.Author.DisplayName; Avatar -
@referencedUserNick
+
@referencedUserDisplayName
@if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden()) @@ -201,12 +201,12 @@ { var interactionUserMember = Context.TryGetMember(message.Interaction.User.Id); var interactionUserColor = Context.TryGetUserColor(message.Interaction.User.Id); - var interactionUserNick = message.Interaction.User.IsBot - ? message.Interaction.User.Name - : interactionUserMember?.Nick ?? message.Interaction.User.Name; + var interactionUserDisplayName = message.Interaction.User.IsBot + ? message.Interaction.User.DisplayName + : interactionUserMember?.DisplayName ?? message.Interaction.User.DisplayName; Avatar -
@interactionUserNick
+
@interactionUserDisplayName
used /@message.Interaction.Name
@@ -223,7 +223,7 @@ // Header
@* Author name *@ - @authorNick + @authorDisplayName @* Bot tag *@ @if (message.Author.IsBot) diff --git a/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs b/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs index 3d5eb07..f357a29 100644 --- a/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs @@ -59,9 +59,9 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor await _context.PopulateMemberAsync(mention.TargetId.Value, cancellationToken); var member = mention.TargetId?.Pipe(_context.TryGetMember); - var name = member?.User.Name ?? "Unknown"; + var displayName = member?.DisplayName ?? member?.User.DisplayName ?? "Unknown"; - _buffer.Append($"@{name}"); + _buffer.Append($"@{displayName}"); } else if (mention.Kind == MentionKind.Channel) { diff --git a/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs b/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs index 625dc1b..2f17245 100644 --- a/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs +++ b/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs @@ -9,13 +9,13 @@ internal static class PlainTextMessageExtensions public static string GetFallbackContent(this Message message) => message.Kind switch { MessageKind.RecipientAdd => message.MentionedUsers.Any() - ? $"Added {message.MentionedUsers.First().Name} to the group." + ? $"Added {message.MentionedUsers.First().DisplayName} to the group." : "Added a recipient.", MessageKind.RecipientRemove => message.MentionedUsers.Any() ? message.Author.Id == message.MentionedUsers.First().Id ? "Left the group." - : $"Removed {message.MentionedUsers.First().Name} from the group." + : $"Removed {message.MentionedUsers.First().DisplayName} from the group." : "Removed a recipient.", MessageKind.Call => diff --git a/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs index 3997987..304c695 100644 --- a/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs +++ b/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Utils.Extensions; @@ -10,4 +11,7 @@ public static class GenericExtensions !predicate(value) ? value : null; + + public static T? NullIfDefault(this T value) where T : struct => + value.NullIf(v => EqualityComparer.Default.Equals(v, default)); } \ No newline at end of file