[backend/federation] Handle user profile fields (ISH-34)

This commit is contained in:
Laura Hausmann 2024-02-25 00:55:35 +01:00
parent 90eb93cfb2
commit 2f3ca1e477
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
8 changed files with 51 additions and 9 deletions

View file

@ -22,6 +22,16 @@ public class UserRenderer(IOptions<Config.InstanceSection> config, MfmConverter
acct += $"@{user.Host}"; acct += $"@{user.Host}";
var profileEmoji = emoji?.Where(p => user.Emojis.Contains(p.Id)).ToList() ?? await GetEmoji([user]); var profileEmoji = emoji?.Where(p => user.Emojis.Contains(p.Id)).ToList() ?? await GetEmoji([user]);
var fields = profile?.Fields
.Select(p => new Field
{
Name = p.Name,
Value = p.Value,
VerifiedAt = p.IsVerified.HasValue && p.IsVerified.Value
? DateTime.Now.ToStringIso8601Like()
: null
})
.ToList();
var res = new AccountEntity var res = new AccountEntity
{ {
@ -44,7 +54,7 @@ public class UserRenderer(IOptions<Config.InstanceSection> config, MfmConverter
MovedToAccount = null, //TODO MovedToAccount = null, //TODO
IsBot = user.IsBot, IsBot = user.IsBot,
IsDiscoverable = user.IsExplorable, IsDiscoverable = user.IsExplorable,
Fields = [], //TODO Fields = fields ?? [],
Emoji = profileEmoji Emoji = profileEmoji
}; };

View file

@ -23,7 +23,7 @@ public class AccountEntity : IEntity
[J("moved")] public required AccountEntity? MovedToAccount { get; set; } [J("moved")] public required AccountEntity? MovedToAccount { get; set; }
[J("bot")] public required bool IsBot { get; set; } [J("bot")] public required bool IsBot { get; set; }
[J("discoverable")] public required bool IsDiscoverable { get; set; } [J("discoverable")] public required bool IsDiscoverable { get; set; }
[J("fields")] public required Field[] Fields { get; set; } [J("fields")] public required List<Field> Fields { get; set; }
[J("source")] public AccountSource? Source { get; set; } [J("source")] public AccountSource? Source { get; set; }
[J("emojis")] public required List<EmojiEntity> Emoji { get; set; } [J("emojis")] public required List<EmojiEntity> Emoji { get; set; }
[J("id")] public required string Id { get; set; } [J("id")] public required string Id { get; set; }

View file

@ -6,6 +6,7 @@ public static class Constants
public const string W3IdSecurityNs = "https://w3id.org/security"; public const string W3IdSecurityNs = "https://w3id.org/security";
public const string PurlDcNs = "http://purl.org/dc/terms"; public const string PurlDcNs = "http://purl.org/dc/terms";
public const string XsdNs = "http://www.w3.org/2001/XMLSchema"; public const string XsdNs = "http://www.w3.org/2001/XMLSchema";
public const string SchemaNs = "http://schema.org";
public const string MastodonNs = "http://joinmastodon.org/ns"; public const string MastodonNs = "http://joinmastodon.org/ns";
public static readonly string[] SystemUsers = ["instance.actor", "relay.actor"]; public static readonly string[] SystemUsers = ["instance.actor", "relay.actor"];

View file

@ -73,7 +73,6 @@ public class NoteRenderer(IOptions<Config.InstanceSection> config, MfmConverter
.Where(p => note.FileIds.Contains(p.Id) && p.UserHost == null) .Where(p => note.FileIds.Contains(p.Id) && p.UserHost == null)
.Select(p => new ASDocument .Select(p => new ASDocument
{ {
Type = $"{Constants.ActivityStreamsNs}#Document",
Sensitive = p.IsSensitive, Sensitive = p.IsSensitive,
Url = new ASObjectBase(p.WebpublicUrl ?? p.Url), Url = new ASObjectBase(p.WebpublicUrl ?? p.Url),
MediaType = p.Type, MediaType = p.Type,

View file

@ -121,6 +121,10 @@ public class ASActor : ASObject
[JC(typeof(ASTagConverter))] [JC(typeof(ASTagConverter))]
public List<ASTag>? Tags { get; set; } public List<ASTag>? Tags { get; set; }
[J($"{Constants.ActivityStreamsNs}#attachment")]
[JC(typeof(ASAttachmentConverter))]
public List<ASAttachment>? Attachments { get; set; }
public bool IsBot => Type == $"{Constants.ActivityStreamsNs}#Service"; public bool IsBot => Type == $"{Constants.ActivityStreamsNs}#Service";
public void Normalize(string uri, string acct) public void Normalize(string uri, string acct)

View file

@ -15,6 +15,8 @@ public class ASAttachment : ASObjectBase
public class ASDocument : ASAttachment public class ASDocument : ASAttachment
{ {
public ASDocument() => Type = $"{Constants.ActivityStreamsNs}#Document";
[J($"{Constants.ActivityStreamsNs}#url")] [J($"{Constants.ActivityStreamsNs}#url")]
[JC(typeof(ASObjectBaseConverter))] [JC(typeof(ASObjectBaseConverter))]
public ASObjectBase? Url { get; set; } public ASObjectBase? Url { get; set; }
@ -32,6 +34,17 @@ public class ASDocument : ASAttachment
public string? Description { get; set; } public string? Description { get; set; }
} }
public class ASField : ASAttachment
{
public ASField() => Type = $"{Constants.SchemaNs}#PropertyValue";
[J($"{Constants.ActivityStreamsNs}#name")] [JC(typeof(ValueObjectConverter))]
public string? Name;
[J($"{Constants.SchemaNs}#value")] [JC(typeof(ValueObjectConverter))]
public string? Value;
}
public sealed class ASAttachmentConverter : JsonConverter public sealed class ASAttachmentConverter : JsonConverter
{ {
public override bool CanWrite => false; public override bool CanWrite => false;
@ -74,9 +87,12 @@ public sealed class ASAttachmentConverter : JsonConverter
{ {
var attachment = obj.ToObject<ASAttachment?>(); var attachment = obj.ToObject<ASAttachment?>();
return attachment?.Type == $"{Constants.ActivityStreamsNs}#Document" return attachment?.Type switch
? obj.ToObject<ASDocument?>() {
: attachment; $"{Constants.ActivityStreamsNs}#Document" => obj.ToObject<ASDocument?>(),
$"{Constants.SchemaNs}#PropertyValue" => obj.ToObject<ASField?>(),
_ => attachment
};
} }
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)

View file

@ -583,6 +583,7 @@ public class NoteService(
if (attachments is not { Count: > 0 }) return []; if (attachments is not { Count: > 0 }) return [];
var result = await attachments var result = await attachments
.OfType<ASDocument>() .OfType<ASDocument>()
.Take(10)
.Select(p => driveSvc.StoreFile(p.Url?.Id, user, p.Sensitive ?? sensitive, p.Description, .Select(p => driveSvc.StoreFile(p.Url?.Id, user, p.Sensitive ?? sensitive, p.Description,
p.MediaType)) p.MediaType))
.AwaitAllNoConcurrencyAsync(); .AwaitAllNoConcurrencyAsync();

View file

@ -101,6 +101,11 @@ public class UserService(
.Where(p => p != null) .Where(p => p != null)
.Cast<string>() .Cast<string>()
.ToList(); .ToList();
var fields = actor.Attachments?
.OfType<ASField>()
.Where(p => p is { Name: not null, Value: not null })
.Select(p => new UserProfile.Field { Name = p.Name!, Value = p.Value! })
.ToArray();
user = new User user = new User
{ {
@ -134,7 +139,7 @@ public class UserService(
Description = actor.MkSummary ?? await mfmConverter.FromHtmlAsync(actor.Summary), Description = actor.MkSummary ?? await mfmConverter.FromHtmlAsync(actor.Summary),
//Birthday = TODO, //Birthday = TODO,
//Location = TODO, //Location = TODO,
//Fields = TODO, Fields = fields ?? [],
UserHost = user.Host, UserHost = user.Host,
Url = actor.Url?.Link Url = actor.Url?.Link
}; };
@ -226,6 +231,12 @@ public class UserService(
.Cast<string>() .Cast<string>()
.ToList(); .ToList();
var fields = actor.Attachments?
.OfType<ASField>()
.Where(p => p is { Name: not null, Value: not null })
.Select(p => new UserProfile.Field { Name = p.Name!, Value = p.Value! })
.ToArray();
user.Emojis = emoji.Select(p => p.Id).ToList(); user.Emojis = emoji.Select(p => p.Id).ToList();
user.Tags = tags ?? []; user.Tags = tags ?? [];
//TODO: FollowersCount //TODO: FollowersCount
@ -238,7 +249,7 @@ public class UserService(
user.UserProfile.Description = actor.MkSummary ?? await mfmConverter.FromHtmlAsync(actor.Summary); user.UserProfile.Description = actor.MkSummary ?? await mfmConverter.FromHtmlAsync(actor.Summary);
//user.UserProfile.Birthday = TODO; //user.UserProfile.Birthday = TODO;
//user.UserProfile.Location = TODO; //user.UserProfile.Location = TODO;
//user.UserProfile.Fields = TODO; user.UserProfile.Fields = fields ?? [];
user.UserProfile.UserHost = user.Host; user.UserProfile.UserHost = user.Host;
user.UserProfile.Url = actor.Url?.Link; user.UserProfile.Url = actor.Url?.Link;