Introduce the foundation for the new username system

pull/1098/head
Tyrrrz 1 year ago
parent bf0d8ab31e
commit 3d2d197904

@ -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

@ -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<Snowflake> 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
);

@ -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);
}
}

@ -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);
}

@ -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)
);

@ -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
$"""
<span class="chatlog__markdown-mention" title="{HtmlEncode(fullName)}">@{HtmlEncode(nick)}</span>
<span class="chatlog__markdown-mention" title="{HtmlEncode(fullName)}">@{HtmlEncode(displayName)}</span>
"""
);
}

@ -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);

@ -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;
<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">
@ -71,7 +71,7 @@
<div class="chatlog__message-primary">
@* Author name *@
<span class="chatlog__system-notification-author" style="@(authorColor is not null ? $"color: rgb({authorColor.Value.R}, {authorColor.Value.G}, {authorColor.Value.B})" : null)" title="@message.Author.FullName" data-user-id="@message.Author.Id">@authorNick</span>
<span class="chatlog__system-notification-author" style="@(authorColor is not null ? $"color: rgb({authorColor.Value.R}, {authorColor.Value.G}, {authorColor.Value.B})" : null)" title="@message.Author.FullName" data-user-id="@message.Author.Id">@authorDisplayName</span>
@* Space out the content *@
<span> </span>
@ -81,7 +81,7 @@
@if (message.Kind == MessageKind.RecipientAdd && message.MentionedUsers.Any())
{
<span>added </span>
<a class="chatlog__system-notification-link" title="@message.MentionedUsers.First().FullName">@message.MentionedUsers.First().Name</a>
<a class="chatlog__system-notification-link" title="@message.MentionedUsers.First().FullName">@message.MentionedUsers.First().DisplayName</a>
<span> to the group.</span>
}
else if (message.Kind == MessageKind.RecipientRemove && message.MentionedUsers.Any())
@ -93,7 +93,7 @@
else
{
<span>removed </span>
<a class="chatlog__system-notification-link" title="@message.MentionedUsers.First().FullName">@message.MentionedUsers.First().Name</a>
<a class="chatlog__system-notification-link" title="@message.MentionedUsers.First().FullName">@message.MentionedUsers.First().DisplayName</a>
<span> from the group.</span>
}
}
@ -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;
<img class="chatlog__reply-avatar" src="@await ResolveAssetUrlAsync(referencedUserMember?.AvatarUrl ?? message.ReferencedMessage.Author.AvatarUrl)" alt="Avatar" loading="lazy">
<div class="chatlog__reply-author" style="@(referencedUserColor is not null ? $"color: rgb({referencedUserColor.Value.R}, {referencedUserColor.Value.G}, {referencedUserColor.Value.B})" : null)" title="@message.ReferencedMessage.Author.FullName">@referencedUserNick</div>
<div class="chatlog__reply-author" style="@(referencedUserColor is not null ? $"color: rgb({referencedUserColor.Value.R}, {referencedUserColor.Value.G}, {referencedUserColor.Value.B})" : null)" title="@message.ReferencedMessage.Author.FullName">@referencedUserDisplayName</div>
<div class="chatlog__reply-content">
<span class="chatlog__reply-link" onclick="scrollToMessage(event, '@message.ReferencedMessage.Id')">
@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;
<img class="chatlog__reply-avatar" src="@await ResolveAssetUrlAsync(interactionUserMember?.AvatarUrl ?? message.Interaction.User.AvatarUrl)" alt="Avatar" loading="lazy">
<div class="chatlog__reply-author" style="@(interactionUserColor is not null ? $"color: rgb({interactionUserColor.Value.R}, {interactionUserColor.Value.G}, {interactionUserColor.Value.B})" : null)" title="@message.Interaction.User.FullName">@interactionUserNick</div>
<div class="chatlog__reply-author" style="@(interactionUserColor is not null ? $"color: rgb({interactionUserColor.Value.R}, {interactionUserColor.Value.G}, {interactionUserColor.Value.B})" : null)" title="@message.Interaction.User.FullName">@interactionUserDisplayName</div>
<div class="chatlog__reply-content">
used /@message.Interaction.Name
</div>
@ -223,7 +223,7 @@
// Header
<div class="chatlog__header">
@* Author name *@
<span class="chatlog__author" style="@(authorColor is not null ? $"color: rgb({authorColor.Value.R}, {authorColor.Value.G}, {authorColor.Value.B})" : null)" title="@message.Author.FullName" data-user-id="@message.Author.Id">@authorNick</span>
<span class="chatlog__author" style="@(authorColor is not null ? $"color: rgb({authorColor.Value.R}, {authorColor.Value.G}, {authorColor.Value.B})" : null)" title="@message.Author.FullName" data-user-id="@message.Author.Id">@authorDisplayName</span>
@* Bot tag *@
@if (message.Author.IsBot)

@ -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)
{

@ -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 =>

@ -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<T>(this T value) where T : struct =>
value.NullIf(v => EqualityComparer<T>.Default.Equals(v, default));
}
Loading…
Cancel
Save