[backend/federation] Handle user profile fields (ISH-34)
This commit is contained in:
parent
90eb93cfb2
commit
2f3ca1e477
8 changed files with 51 additions and 9 deletions
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -6,7 +6,8 @@ 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 MastodonNs = "http://joinmastodon.org/ns";
|
public const string SchemaNs = "http://schema.org";
|
||||||
|
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"];
|
||||||
|
|
||||||
public static readonly string[] BrowserSafeMimeTypes =
|
public static readonly string[] BrowserSafeMimeTypes =
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue