Save uncategorized actor data in metadata

This commit is contained in:
2025-12-29 00:51:19 +08:00
parent 44a791db1f
commit 7cb471e978
5 changed files with 2747 additions and 64 deletions

View File

@@ -11,67 +11,33 @@ public class SnFediverseActor : ModelBase
{ {
public Guid Id { get; set; } = Guid.NewGuid(); public Guid Id { get; set; } = Guid.NewGuid();
[MaxLength(2048)] [MaxLength(2048)] public string Type { get; set; } = "Person";
public string Uri { get; set; } = null!; [MaxLength(2048)] public string Uri { get; set; } = null!;
[MaxLength(256)] public string Username { get; set; } = null!;
[MaxLength(256)] [MaxLength(2048)] public string? DisplayName { get; set; }
public string Username { get; set; } = null!; [MaxLength(4096)] public string? Bio { get; set; }
[MaxLength(2048)] public string? InboxUri { get; set; }
[MaxLength(2048)] [MaxLength(2048)] public string? OutboxUri { get; set; }
public string? DisplayName { get; set; } [MaxLength(2048)] public string? FollowersUri { get; set; }
[MaxLength(2048)] public string? FollowingUri { get; set; }
[MaxLength(4096)] [MaxLength(2048)] public string? FeaturedUri { get; set; }
public string? Bio { get; set; } [MaxLength(2048)] public string? PublicKeyId { get; set; }
[MaxLength(8192)] public string? PublicKey { get; set; }
[MaxLength(2048)] [Column(TypeName = "jsonb")] public Dictionary<string, object>? Metadata { get; set; }
public string? InboxUri { get; set; } [MaxLength(2048)] public string? AvatarUrl { get; set; }
[MaxLength(2048)] public string? HeaderUrl { get; set; }
[MaxLength(2048)]
public string? OutboxUri { get; set; }
[MaxLength(2048)]
public string? FollowersUri { get; set; }
[MaxLength(2048)]
public string? FollowingUri { get; set; }
[MaxLength(2048)]
public string? FeaturedUri { get; set; }
[MaxLength(2048)]
public string? PublicKeyId { get; set; }
[MaxLength(8192)]
public string? PublicKey { get; set; }
[Column(TypeName = "jsonb")]
public Dictionary<string, object>? Metadata { get; set; }
[MaxLength(2048)]
public string? AvatarUrl { get; set; }
[MaxLength(2048)]
public string? HeaderUrl { get; set; }
public bool IsBot { get; set; } = false; public bool IsBot { get; set; } = false;
public bool IsLocked { get; set; } = false; public bool IsLocked { get; set; } = false;
public bool IsDiscoverable { get; set; } = true; public bool IsDiscoverable { get; set; } = true;
public Guid InstanceId { get; set; } public Guid InstanceId { get; set; }
[JsonIgnore]
public SnFediverseInstance Instance { get; set; } = null!;
[JsonIgnore] [JsonIgnore] public SnFediverseInstance Instance { get; set; } = null!;
public ICollection<SnFediverseContent> Contents { get; set; } = []; [JsonIgnore] public ICollection<SnFediverseContent> Contents { get; set; } = [];
[JsonIgnore] public ICollection<SnFediverseActivity> Activities { get; set; } = [];
[JsonIgnore] [JsonIgnore] public ICollection<SnFediverseRelationship> FollowingRelationships { get; set; } = [];
public ICollection<SnFediverseActivity> Activities { get; set; } = []; [JsonIgnore] public ICollection<SnFediverseRelationship> FollowerRelationships { get; set; } = [];
[JsonIgnore]
public ICollection<SnFediverseRelationship> FollowingRelationships { get; set; } = [];
[JsonIgnore]
public ICollection<SnFediverseRelationship> FollowerRelationships { get; set; } = [];
public Instant? LastFetchedAt { get; set; } public Instant? LastFetchedAt { get; set; }
public Instant? LastActivityAt { get; set; } public Instant? LastActivityAt { get; set; }

View File

@@ -1,5 +1,6 @@
using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
@@ -66,7 +67,8 @@ public partial class ActivityPubDiscoveryService(
return !match.Success ? (null, null) : (match.Groups[1].Value, match.Groups[2].Value); return !match.Success ? (null, null) : (match.Groups[1].Value, match.Groups[2].Value);
} }
private async Task<(string? actorUri, string? avatarUrl)> GetActorUriFromWebfingerAsync(string username, string domain) private async Task<(string? actorUri, string? avatarUrl)> GetActorUriFromWebfingerAsync(string username,
string domain)
{ {
if (domain == Domain) if (domain == Domain)
return (null, null); return (null, null);
@@ -101,7 +103,8 @@ public partial class ActivityPubDiscoveryService(
} }
else else
{ {
logger.LogWarning("Unknown Content-Type from {Url}: {ContentType}, trying JSON parsing", webfingerUrl, contentType); logger.LogWarning("Unknown Content-Type from {Url}: {ContentType}, trying JSON parsing", webfingerUrl,
contentType);
result = ParseJsonWebfingerResponse(content, webfingerUrl); result = ParseJsonWebfingerResponse(content, webfingerUrl);
} }
@@ -111,7 +114,8 @@ public partial class ActivityPubDiscoveryService(
return (null, null); return (null, null);
} }
logger.LogInformation("Found actor URI via Webfinger: {ActorUri}, Avatar: {AvatarUrl}", result.actorUri, result.avatarUrl); logger.LogInformation("Found actor URI via Webfinger: {ActorUri}, Avatar: {AvatarUrl}", result.actorUri,
result.avatarUrl);
return result; return result;
} }
catch (Exception ex) catch (Exception ex)
@@ -206,7 +210,8 @@ public partial class ActivityPubDiscoveryService(
} }
} }
private async Task<SnFediverseActor?> StoreActorAsync(string actorUri, string username, string domain, string? webfingerAvatarUrl) private async Task<SnFediverseActor?> StoreActorAsync(string actorUri, string username, string domain,
string? webfingerAvatarUrl)
{ {
var existingActor = await db.FediverseActors var existingActor = await db.FediverseActors
.FirstOrDefaultAsync(a => a.Uri == actorUri); .FirstOrDefaultAsync(a => a.Uri == actorUri);
@@ -246,7 +251,7 @@ public partial class ActivityPubDiscoveryService(
logger.LogInformation("Successfully stored actor from Webfinger: {Username}@{Domain}", username, domain); logger.LogInformation("Successfully stored actor from Webfinger: {Username}@{Domain}", username, domain);
await FetchAdditionalActorDataAsync(actor); await FetchActorDataAsync(actor);
return actor; return actor;
} }
@@ -257,19 +262,21 @@ public partial class ActivityPubDiscoveryService(
} }
} }
private async Task FetchAdditionalActorDataAsync(SnFediverseActor actor) private async Task FetchActorDataAsync(SnFediverseActor actor)
{ {
try try
{ {
logger.LogInformation("Attempting to fetch additional actor data from: {ActorUri}", actor.Uri); logger.LogInformation("Attempting to fetch additional actor data from: {ActorUri}", actor.Uri);
var request = new HttpRequestMessage(HttpMethod.Get, actor.Uri); var request = new HttpRequestMessage(HttpMethod.Get, actor.Uri);
request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/activity+json")); request.Headers.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/activity+json"));
var response = await HttpClient.SendAsync(request); var response = await HttpClient.SendAsync(request);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{ {
logger.LogWarning("Failed to fetch actor data: {Url} - {StatusCode}, using Webfinger data only", actor.Uri, response.StatusCode); logger.LogWarning("Failed to fetch actor data: {Url} - {StatusCode}, using Webfinger data only",
actor.Uri, response.StatusCode);
return; return;
} }
@@ -282,6 +289,7 @@ public partial class ActivityPubDiscoveryService(
return; return;
} }
actor.Type = actorData.GetValueOrDefault("type")?.ToString();
actor.DisplayName = actorData.GetValueOrDefault("name")?.ToString(); actor.DisplayName = actorData.GetValueOrDefault("name")?.ToString();
actor.Bio = actorData.GetValueOrDefault("summary")?.ToString(); actor.Bio = actorData.GetValueOrDefault("summary")?.ToString();
actor.InboxUri = actorData.GetValueOrDefault("inbox")?.ToString(); actor.InboxUri = actorData.GetValueOrDefault("inbox")?.ToString();
@@ -297,13 +305,23 @@ public partial class ActivityPubDiscoveryService(
actor.IsLocked = actorData.GetValueOrDefault("manuallyApprovesFollowers")?.ToString() == "true"; actor.IsLocked = actorData.GetValueOrDefault("manuallyApprovesFollowers")?.ToString() == "true";
actor.IsDiscoverable = actorData.GetValueOrDefault("discoverable")?.ToString() != "false"; actor.IsDiscoverable = actorData.GetValueOrDefault("discoverable")?.ToString() != "false";
// Store additional fields in Metadata
var excludedKeys = new HashSet<string>
{
"id", "name", "summary", "preferredUsername", "inbox", "outbox", "followers", "following", "featured",
"icon", "image", "publicKey", "type", "manuallyApprovesFollowers", "discoverable", "@context"
};
actor.Metadata = actorData.Where(kvp => !excludedKeys.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
logger.LogInformation("Successfully fetched additional actor data for: {Username}", actor.Username); logger.LogInformation("Successfully fetched additional actor data for: {Username}", actor.Username);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogWarning(ex, "Failed to fetch additional actor data for {Uri}, using Webfinger data only", actor.Uri); logger.LogWarning(ex, "Failed to fetch additional actor data for {Uri}, using Webfinger data only",
actor.Uri);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DysonNetwork.Sphere.Migrations
{
/// <inheritdoc />
public partial class AddSeprateActorType : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "type",
table: "fediverse_actors",
type: "character varying(2048)",
maxLength: 2048,
nullable: false,
defaultValue: "Person");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "type",
table: "fediverse_actors");
}
}
}

View File

@@ -495,6 +495,12 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("character varying(2048)") .HasColumnType("character varying(2048)")
.HasColumnName("public_key_id"); .HasColumnName("public_key_id");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(2048)
.HasColumnType("character varying(2048)")
.HasColumnName("type");
b.Property<Instant>("UpdatedAt") b.Property<Instant>("UpdatedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");