[backend] Handle incoming ASAccept activities
This commit is contained in:
parent
8c9e6ef56c
commit
97e4a25742
4 changed files with 57 additions and 6 deletions
|
@ -1,3 +1,4 @@
|
|||
using Iceshrimp.Backend.Core.Configuration;
|
||||
using Iceshrimp.Backend.Core.Database;
|
||||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
using Iceshrimp.Backend.Core.Federation.ActivityStreams;
|
||||
|
@ -7,6 +8,7 @@ using Iceshrimp.Backend.Core.Middleware;
|
|||
using Iceshrimp.Backend.Core.Queues;
|
||||
using Iceshrimp.Backend.Core.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Iceshrimp.Backend.Core.Federation.ActivityPub;
|
||||
|
||||
|
@ -16,7 +18,8 @@ public class ActivityHandlerService(
|
|||
UserResolver userResolver,
|
||||
DatabaseContext db,
|
||||
QueueService queueService,
|
||||
ActivityRenderer activityRenderer
|
||||
ActivityRenderer activityRenderer,
|
||||
IOptions<Config.InstanceSection> config
|
||||
) {
|
||||
public Task PerformActivityAsync(ASActivity activity, string? inboxUserId) {
|
||||
logger.LogDebug("Processing activity: {activity}", activity.Id);
|
||||
|
@ -38,6 +41,10 @@ public class ActivityHandlerService(
|
|||
if (activity.Object is { } obj) return UnfollowAsync(obj, activity.Actor);
|
||||
throw GracefulException.UnprocessableEntity("Unfollow activity object is invalid");
|
||||
}
|
||||
case ASActivity.Types.Accept: {
|
||||
if (activity.Object is { } obj) return AcceptAsync(obj, activity.Actor);
|
||||
throw GracefulException.UnprocessableEntity("Follow activity object is invalid");
|
||||
}
|
||||
case ASActivity.Types.Undo: {
|
||||
//TODO: implement the rest
|
||||
//TODO: test if this actually works
|
||||
|
@ -118,4 +125,40 @@ public class ActivityHandlerService(
|
|||
await db.FollowRequests.Where(p => p.Follower == follower && p.Followee == followee).ExecuteDeleteAsync();
|
||||
await db.Followings.Where(p => p.Follower == follower && p.Followee == followee).ExecuteDeleteAsync();
|
||||
}
|
||||
|
||||
private async Task AcceptAsync(ASObject obj, ASObject actor) {
|
||||
var prefix = $"https://{config.Value.WebDomain}/follows/";
|
||||
if (!obj.Id.StartsWith(prefix))
|
||||
throw GracefulException.UnprocessableEntity($"Object id '{obj.Id}' not a valid follow request");
|
||||
|
||||
var resolvedActor = await userResolver.ResolveAsync(actor.Id);
|
||||
var ids = obj.Id[prefix.Length..].TrimEnd('/').Split("/");
|
||||
if (ids.Length != 2 || ids[1] != resolvedActor.Id)
|
||||
throw GracefulException
|
||||
.UnprocessableEntity($"Actor id '{resolvedActor.Id}' doesn't match followee id '{ids[1]}'");
|
||||
|
||||
var request = await db.FollowRequests
|
||||
.Include(p => p.Follower)
|
||||
.FirstOrDefaultAsync(p => p.Followee == resolvedActor && p.FollowerId == ids[0]);
|
||||
|
||||
if (request == null)
|
||||
throw GracefulException
|
||||
.UnprocessableEntity($"No follow request matching follower '{ids[0]}' and followee '{resolvedActor.Id}' found");
|
||||
|
||||
var following = new Following {
|
||||
Id = IdHelpers.GenerateSlowflakeId(),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Follower = request.Follower,
|
||||
Followee = resolvedActor,
|
||||
FollowerHost = request.FollowerHost,
|
||||
FolloweeHost = request.FolloweeHost,
|
||||
FollowerInbox = request.FollowerInbox,
|
||||
FolloweeInbox = request.FolloweeInbox,
|
||||
FollowerSharedInbox = request.FollowerSharedInbox,
|
||||
FolloweeSharedInbox = request.FolloweeSharedInbox
|
||||
};
|
||||
db.Remove(request);
|
||||
await db.AddAsync(following);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ public class ActivityRenderer(IOptions<Config.InstanceSection> config) {
|
|||
public static ASActivity RenderCreate(ASObject obj, ASObject actor) {
|
||||
return new ASActivity {
|
||||
Id = $"{obj.Id}#Create",
|
||||
Type = "https://www.w3.org/ns/activitystreams#Create",
|
||||
Type = ASActivity.Types.Create,
|
||||
Actor = new ASActor { Id = actor.Id },
|
||||
Object = obj
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ public class ActivityRenderer(IOptions<Config.InstanceSection> config) {
|
|||
public ASActivity RenderAccept(ASObject actor, ASObject obj) {
|
||||
return new ASActivity {
|
||||
Id = $"https://{config.Value.WebDomain}/activities/{Guid.NewGuid().ToString().ToLowerInvariant()}",
|
||||
Type = "https://www.w3.org/ns/activitystreams#Accept",
|
||||
Type = ASActivity.Types.Accept,
|
||||
Actor = new ASActor {
|
||||
Id = actor.Id
|
||||
},
|
||||
|
|
|
@ -30,6 +30,14 @@ public class ASFollow : ASActivity {
|
|||
public ASFollow() => Type = Types.Follow;
|
||||
}
|
||||
|
||||
public class ASUnfollow : ASActivity {
|
||||
public ASUnfollow() => Type = Types.Unfollow;
|
||||
}
|
||||
|
||||
public class ASAccept : ASActivity {
|
||||
public ASAccept() => Type = Types.Accept;
|
||||
}
|
||||
|
||||
//TODO: add the rest
|
||||
|
||||
public sealed class ASActivityConverter : ASSerializer.ListSingleObjectConverter<ASActivity>;
|
|
@ -25,9 +25,9 @@ public class ASObject {
|
|||
ASNote.Types.Note => token.ToObject<ASNote>(),
|
||||
ASActivity.Types.Create => token.ToObject<ASActivity>(),
|
||||
ASActivity.Types.Delete => token.ToObject<ASActivity>(),
|
||||
ASActivity.Types.Follow => token.ToObject<ASActivity>(),
|
||||
ASActivity.Types.Unfollow => token.ToObject<ASActivity>(),
|
||||
ASActivity.Types.Accept => token.ToObject<ASActivity>(),
|
||||
ASActivity.Types.Follow => token.ToObject<ASFollow>(),
|
||||
ASActivity.Types.Unfollow => token.ToObject<ASUnfollow>(),
|
||||
ASActivity.Types.Accept => token.ToObject<ASAccept>(),
|
||||
ASActivity.Types.Undo => token.ToObject<ASActivity>(),
|
||||
ASActivity.Types.Like => token.ToObject<ASActivity>(),
|
||||
_ => token.ToObject<ASObject>()
|
||||
|
|
Loading…
Add table
Reference in a new issue