Loading examples/blog/.vscode/settings.json +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ "halfyear", "httpsig", "logtape", "Misskey", "nodeinfo", "preact", "unfollow", Loading examples/blog/federation/mod.ts +7 −10 Original line number Diff line number Diff line Loading @@ -192,7 +192,11 @@ federation.setInboxListeners("/users/{handle}/inbox", "/inbox") await ctx.sendActivity( { handle: blog.handle }, recipient, new Accept({ actor: follow.objectId, object: follow }), new Accept({ id: new URL(`#accept/${handle}`, ctx.getActorUri(blog.handle)), actor: follow.objectId, object: follow, }), ); }) // The `Create` activity is handled by adding a comment to the post: Loading Loading @@ -238,21 +242,14 @@ federation.setInboxListeners("/users/{handle}/inbox", "/inbox") .on(Undo, async (ctx, undo) => { const activity = await undo.getObject(ctx); // An `Activity` to undo if (activity instanceof Follow) { if (activity.id == null) return; await removeFollower(activity.id.href); if (activity.id == null || activity.actorId == null) return; await removeFollower(activity.id.href, activity.actorId.href); } else { logger.getChild("inbox").warn( "Unsupported object type ({type}) for Undo activity: {object}", { type: activity?.constructor.name, object: activity }, ); } }) .onError((_ctx, error) => { logger.getChild("inbox").error( "An error occurred while processing an activity in the inbox handler:\n" + "{error}", { error }, ); }); // Since the blog does not follow anyone, the following dispatcher is Loading examples/blog/models/follower.ts +20 −2 Original line number Diff line number Diff line Loading @@ -23,14 +23,32 @@ export async function addFollower(follower: Follower): Promise<Follower> { return follower; } export async function removeFollower(activityId: string): Promise<void> { export async function removeFollower( activityId: string, actorId: string, ): Promise<void> { const kv = await openKv(); const follower = await kv.get<Follower>(["follower", activityId]); const followers = await kv.get<bigint>(["followers"]); if ( follower == null || follower.value == null || followers == null || followers.value == null ) return; ) { // Sometimes Follow.id and Undo<Follow>.object.id do not match... // (e.g., Misskey) for await (const entry of kv.list<Follower>({ prefix: ["follower"] })) { if (entry.value.id === actorId) { const followers = await kv.get<bigint>(["followers"]); if (followers == null || followers.value == null) continue; await kv.atomic() .check(follower) .check(followers) .delete(entry.key) .set(["followers"], followers.value - 1n) .commit(); } } } await kv.atomic() .check(follower) .check(followers) Loading Loading
examples/blog/.vscode/settings.json +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ "halfyear", "httpsig", "logtape", "Misskey", "nodeinfo", "preact", "unfollow", Loading
examples/blog/federation/mod.ts +7 −10 Original line number Diff line number Diff line Loading @@ -192,7 +192,11 @@ federation.setInboxListeners("/users/{handle}/inbox", "/inbox") await ctx.sendActivity( { handle: blog.handle }, recipient, new Accept({ actor: follow.objectId, object: follow }), new Accept({ id: new URL(`#accept/${handle}`, ctx.getActorUri(blog.handle)), actor: follow.objectId, object: follow, }), ); }) // The `Create` activity is handled by adding a comment to the post: Loading Loading @@ -238,21 +242,14 @@ federation.setInboxListeners("/users/{handle}/inbox", "/inbox") .on(Undo, async (ctx, undo) => { const activity = await undo.getObject(ctx); // An `Activity` to undo if (activity instanceof Follow) { if (activity.id == null) return; await removeFollower(activity.id.href); if (activity.id == null || activity.actorId == null) return; await removeFollower(activity.id.href, activity.actorId.href); } else { logger.getChild("inbox").warn( "Unsupported object type ({type}) for Undo activity: {object}", { type: activity?.constructor.name, object: activity }, ); } }) .onError((_ctx, error) => { logger.getChild("inbox").error( "An error occurred while processing an activity in the inbox handler:\n" + "{error}", { error }, ); }); // Since the blog does not follow anyone, the following dispatcher is Loading
examples/blog/models/follower.ts +20 −2 Original line number Diff line number Diff line Loading @@ -23,14 +23,32 @@ export async function addFollower(follower: Follower): Promise<Follower> { return follower; } export async function removeFollower(activityId: string): Promise<void> { export async function removeFollower( activityId: string, actorId: string, ): Promise<void> { const kv = await openKv(); const follower = await kv.get<Follower>(["follower", activityId]); const followers = await kv.get<bigint>(["followers"]); if ( follower == null || follower.value == null || followers == null || followers.value == null ) return; ) { // Sometimes Follow.id and Undo<Follow>.object.id do not match... // (e.g., Misskey) for await (const entry of kv.list<Follower>({ prefix: ["follower"] })) { if (entry.value.id === actorId) { const followers = await kv.get<bigint>(["followers"]); if (followers == null || followers.value == null) continue; await kv.atomic() .check(follower) .check(followers) .delete(entry.key) .set(["followers"], followers.value - 1n) .commit(); } } } await kv.atomic() .check(follower) .check(followers) Loading