✨ Better ap object builder in post
This commit is contained in:
@@ -8,7 +8,7 @@ using Swashbuckle.AspNetCore.Annotations;
|
|||||||
namespace DysonNetwork.Sphere.ActivityPub;
|
namespace DysonNetwork.Sphere.ActivityPub;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("activitypub")]
|
[Route("activitypub/actors/{username}")]
|
||||||
public class ActivityPubController(
|
public class ActivityPubController(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
@@ -20,7 +20,7 @@ public class ActivityPubController(
|
|||||||
{
|
{
|
||||||
private string Domain => configuration["ActivityPub:Domain"] ?? "localhost";
|
private string Domain => configuration["ActivityPub:Domain"] ?? "localhost";
|
||||||
|
|
||||||
[HttpGet("actors/{username}")]
|
[HttpGet("")]
|
||||||
[Produces("application/activity+json")]
|
[Produces("application/activity+json")]
|
||||||
[ProducesResponseType(typeof(ActivityPubActor), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubActor), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
@@ -88,7 +88,7 @@ public class ActivityPubController(
|
|||||||
return Ok(actor);
|
return Ok(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("actors/{username}/inbox")]
|
[HttpPost("inbox")]
|
||||||
[Consumes("application/activity+json")]
|
[Consumes("application/activity+json")]
|
||||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
@@ -120,7 +120,7 @@ public class ActivityPubController(
|
|||||||
return Accepted();
|
return Accepted();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("actors/{username}/outbox")]
|
[HttpGet("outbox")]
|
||||||
[Produces("application/activity+json")]
|
[Produces("application/activity+json")]
|
||||||
[ProducesResponseType(typeof(ActivityPubCollection), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubCollection), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ActivityPubCollectionPage), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubCollectionPage), StatusCodes.Status200OK)]
|
||||||
@@ -157,32 +157,21 @@ public class ActivityPubController(
|
|||||||
.Take(pageSize)
|
.Take(pageSize)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var items = posts.Select(post => new
|
var items = posts.Select(post =>
|
||||||
{
|
{
|
||||||
id = $"https://{Domain}/activitypub/objects/{post.Id}/activity",
|
var postObject = ActivityPubObjectFactory.CreatePostObject(configuration, post, actorUrl);
|
||||||
type = "Create",
|
postObject["url"] = $"https://{Domain}/posts/{post.Id}";
|
||||||
actor = actorUrl,
|
return new Dictionary<string, object>
|
||||||
published = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
|
||||||
to = new[] { "https://www.w3.org/ns/activitystreams#Public" },
|
|
||||||
cc = new[] { $"{actorUrl}/followers" },
|
|
||||||
@object = new
|
|
||||||
{
|
{
|
||||||
id = $"https://{Domain}/activitypub/objects/{post.Id}",
|
["id"] = $"https://{Domain}/activitypub/objects/{post.Id}/activity",
|
||||||
type = post.Type == PostType.Article ? "Article" : "Note",
|
["type"] = "Create",
|
||||||
published = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
["actor"] = actorUrl,
|
||||||
attributedTo = actorUrl,
|
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
||||||
content = post.Content ?? "",
|
["to"] = ActivityPubObjectFactory.PublicTo,
|
||||||
url = $"https://{Domain}/posts/{post.Id}",
|
["cc"] = new[] { $"{actorUrl}/followers" },
|
||||||
to = new[] { "https://www.w3.org/ns/activitystreams#Public" },
|
["@object"] = postObject
|
||||||
cc = new[] { $"{actorUrl}/followers" },
|
};
|
||||||
attachment = post.Attachments.Select(a => new
|
}).Cast<object>().ToList();
|
||||||
{
|
|
||||||
type = "Document",
|
|
||||||
mediaType = a.MimeType,
|
|
||||||
url = $"{configuration["ActivityPub:FileBaseUrl"] ?? $"https://{Domain}/files"}/{a.Id}"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}).ToList<object>();
|
|
||||||
|
|
||||||
var collectionPage = new ActivityPubCollectionPage
|
var collectionPage = new ActivityPubCollectionPage
|
||||||
{
|
{
|
||||||
@@ -213,7 +202,7 @@ public class ActivityPubController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("actors/{username}/followers")]
|
[HttpGet("followers")]
|
||||||
[Produces("application/activity+json")]
|
[Produces("application/activity+json")]
|
||||||
[ProducesResponseType(typeof(ActivityPubCollection), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubCollection), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ActivityPubCollectionPage), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubCollectionPage), StatusCodes.Status200OK)]
|
||||||
@@ -279,7 +268,7 @@ public class ActivityPubController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("actors/{username}/following")]
|
[HttpGet("following")]
|
||||||
[Produces("application/activity+json")]
|
[Produces("application/activity+json")]
|
||||||
[ProducesResponseType(typeof(ActivityPubCollection), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubCollection), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ActivityPubCollectionPage), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ActivityPubCollectionPage), StatusCodes.Status200OK)]
|
||||||
|
|||||||
@@ -166,24 +166,9 @@ public class ActivityPubDeliveryService(
|
|||||||
["type"] = "Create",
|
["type"] = "Create",
|
||||||
["actor"] = actorUrl,
|
["actor"] = actorUrl,
|
||||||
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
||||||
["to"] = new[] { "https://www.w3.org/ns/activitystreams#Public" },
|
["to"] = ActivityPubObjectFactory.PublicTo,
|
||||||
["cc"] = new[] { $"{actorUrl}/followers" },
|
["cc"] = new[] { $"{actorUrl}/followers" },
|
||||||
["object"] = new Dictionary<string, object>
|
["object"] = ActivityPubObjectFactory.CreatePostObject(configuration, post, actorUrl)
|
||||||
{
|
|
||||||
["id"] = postUrl,
|
|
||||||
["type"] = post.Type == PostType.Article ? "Article" : "Note",
|
|
||||||
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
|
||||||
["attributedTo"] = actorUrl,
|
|
||||||
["content"] = post.Content ?? "",
|
|
||||||
["to"] = new[] { "https://www.w3.org/ns/activitystreams#Public" },
|
|
||||||
["cc"] = new[] { $"{actorUrl}/followers" },
|
|
||||||
["attachment"] = post.Attachments.Select(a => new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
["type"] = "Document",
|
|
||||||
["mediaType"] = "image/jpeg",
|
|
||||||
["url"] = $"{AssetsBaseUrl}/{a.Id}"
|
|
||||||
}).ToList<object>()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var followers = await GetRemoteFollowersAsync();
|
var followers = await GetRemoteFollowersAsync();
|
||||||
@@ -217,25 +202,9 @@ public class ActivityPubDeliveryService(
|
|||||||
["type"] = "Update",
|
["type"] = "Update",
|
||||||
["actor"] = actorUrl,
|
["actor"] = actorUrl,
|
||||||
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
||||||
["to"] = new[] { "https://www.w3.org/ns/activitystreams#Public" },
|
["to"] = ActivityPubObjectFactory.PublicTo,
|
||||||
["cc"] = new[] { $"{actorUrl}/followers" },
|
["cc"] = new[] { $"{actorUrl}/followers" },
|
||||||
["object"] = new Dictionary<string, object>
|
["object"] = ActivityPubObjectFactory.CreatePostObject(configuration, post, actorUrl)
|
||||||
{
|
|
||||||
["id"] = postUrl,
|
|
||||||
["type"] = post.Type == PostType.Article ? "Article" : "Note",
|
|
||||||
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
|
||||||
["updated"] = post.EditedAt?.ToDateTimeOffset() ?? new DateTimeOffset(),
|
|
||||||
["attributedTo"] = actorUrl,
|
|
||||||
["content"] = post.Content ?? "",
|
|
||||||
["to"] = new[] { "https://www.w3.org/ns/activitystreams#Public" },
|
|
||||||
["cc"] = new[] { $"{actorUrl}/followers" },
|
|
||||||
["attachment"] = post.Attachments.Select(a => new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
["type"] = "Document",
|
|
||||||
["mediaType"] = "image/jpeg",
|
|
||||||
["url"] = $"{AssetsBaseUrl}/{a.Id}"
|
|
||||||
}).ToList<object>()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var followers = await GetRemoteFollowersAsync();
|
var followers = await GetRemoteFollowersAsync();
|
||||||
|
|||||||
69
DysonNetwork.Sphere/ActivityPub/ActivityPubObjectFactory.cs
Normal file
69
DysonNetwork.Sphere/ActivityPub/ActivityPubObjectFactory.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System.Text;
|
||||||
|
using DysonNetwork.Shared.Models;
|
||||||
|
using Markdig;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.ActivityPub;
|
||||||
|
|
||||||
|
public static class ActivityPubObjectFactory
|
||||||
|
{
|
||||||
|
public static readonly string[] PublicTo = ["https://www.w3.org/ns/activitystreams#Public"];
|
||||||
|
|
||||||
|
public static Dictionary<string, object> CreatePostObject(
|
||||||
|
IConfiguration configuration,
|
||||||
|
SnPost post,
|
||||||
|
string actorUrl
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var baseDomain = configuration["ActivityPub:Domain"] ?? "localhost";
|
||||||
|
var assetsBaseUrl = configuration["ActivityPub:FileBaseUrl"] ?? $"https://{baseDomain}/files";
|
||||||
|
var postUrl = $"https://{baseDomain}/posts/{post.Id}";
|
||||||
|
|
||||||
|
// Build content by combining title, description, and main content
|
||||||
|
var contentBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(post.Title))
|
||||||
|
contentBuilder.Append($"# {post.Title}\n\n");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(post.Description))
|
||||||
|
contentBuilder.Append($"{post.Description}\n\n");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(post.Content))
|
||||||
|
contentBuilder.Append(post.Content);
|
||||||
|
|
||||||
|
// Ensure content is not empty for ActivityPub compatibility
|
||||||
|
if (contentBuilder.Length == 0)
|
||||||
|
contentBuilder.Append("Shared media");
|
||||||
|
|
||||||
|
if (post.Tags.Count > 0)
|
||||||
|
{
|
||||||
|
contentBuilder.Append("\n\n");
|
||||||
|
contentBuilder.Append(
|
||||||
|
string.Join(' ', post.Tags.Select(x => $"#{x.Slug}"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var finalContent = contentBuilder.ToString();
|
||||||
|
|
||||||
|
var postObject = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["id"] = postUrl,
|
||||||
|
["type"] = post.Type == PostType.Article ? "Article" : "Note",
|
||||||
|
["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(),
|
||||||
|
["attributedTo"] = actorUrl,
|
||||||
|
["content"] = Markdown.ToHtml(finalContent),
|
||||||
|
["to"] = PublicTo,
|
||||||
|
["cc"] = new[] { $"{actorUrl}/followers" },
|
||||||
|
["attachment"] = post.Attachments.Select(a => new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["type"] = "Document",
|
||||||
|
["mediaType"] = a.MimeType ?? "media/jpeg",
|
||||||
|
["url"] = $"{assetsBaseUrl}/{a.Id}"
|
||||||
|
}).ToList<object>()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (post.EditedAt.HasValue)
|
||||||
|
postObject["updated"] = post.EditedAt.Value.ToDateTimeOffset();
|
||||||
|
|
||||||
|
return postObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user