package federation import ( "fmt" "os" "pocketbase/util" "time" pub "github.com/go-ap/activitypub" "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/security" ) // create outgoing follow activity func CreateFollowActivity(app core.App, follow *core.Record) error { origin := os.Getenv("ORIGIN") if origin == "" { return fmt.Errorf("ORIGIN not set") } follower := follow.GetString("follower") followee := follow.GetString("followee") followerActor, err := app.FindRecordById("activitypub_actors", follower) if err != nil { return err } followeeActor, err := app.FindRecordById("activitypub_actors", followee) if err != nil { return err } collection, err := app.FindCollectionByNameOrId("activitypub_activities") if err != nil { return err } recordId := security.RandomStringWithAlphabet(core.DefaultIdLength, core.DefaultIdAlphabet) id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, recordId) activity := pub.FollowNew(pub.IRI(id), pub.IRI(followeeActor.GetString("iri"))) activity.Actor = pub.IRI(followerActor.GetString("iri")) err = PostActivity(app, followerActor, activity, []string{followeeActor.GetString("inbox")}) if err != nil { return err } record := core.NewRecord(collection) record.Set("id", recordId) record.Set("iri", id) record.Set("type", string(pub.FollowType)) record.Set("object", followeeActor.GetString("iri")) record.Set("actor", followerActor.GetString("iri")) record.Set("published", time.Now()) return app.Save(record) } // process incoming follow activity func ProcessFollowActivity(app core.App, actor *core.Record, activity pub.Activity) error { origin := os.Getenv("ORIGIN") if origin == "" { return fmt.Errorf("ORIGIN not set") } // find the followee in our db object, err := app.FindFirstRecordByData("activitypub_actors", "iri", activity.Object) if err != nil { return err } // a remote actor has requested the follow // this means we have not yet created a follow entry in our db // we accept it immediately if !actor.GetBool("isLocal") { followCollection, err := app.FindCollectionByNameOrId("follows") if err != nil { return err } followRecord := core.NewRecord(followCollection) followRecord.Set("follower", actor.Id) followRecord.Set("followee", object.Id) followRecord.Set("status", "accepted") err = app.Save(followRecord) if err != nil { return err } } recordId := security.RandomStringWithAlphabet(core.DefaultIdLength, core.DefaultIdAlphabet) id := fmt.Sprintf("%s/api/v1/activitypub/activity/%s", origin, recordId) // send the accept activity back to the actor's inbox acceptActivity := pub.AcceptNew(pub.IRI(id), activity) acceptActivity.Actor = activity.Object err = PostActivity(app, object, acceptActivity, []string{actor.GetString("inbox")}) if err != nil { return err } // create record of the accept activity in our db collection, err := app.FindCollectionByNameOrId("activitypub_activities") if err != nil { return err } record := core.NewRecord(collection) record.Set("id", recordId) record.Set("iri", id) record.Set("type", string(pub.AcceptType)) record.Set("object", activity) record.Set("actor", object.GetString("iri")) record.Set("published", time.Now()) err = app.Save(record) if err != nil { return err } // send a notification to the followee notification := util.Notification{ Type: util.NewFollower, Metadata: map[string]string{ "follower": fmt.Sprintf("@%s@%s", actor.GetString("username"), actor.GetString("domain")), }, Seen: false, Author: actor.Id, } return util.SendNotification(app, notification, object) } func ProcessAcceptActivity(app core.App, actor *core.Record, activity pub.Activity) error { followActivity := activity.Object.(*pub.Activity) follower, err := app.FindFirstRecordByData("activitypub_actors", "iri", followActivity.Actor) if err != nil { return err } follow, err := app.FindFirstRecordByFilter("follows", "follower={:follower} && followee={:followee}", dbx.Params{"follower": follower.Id, "followee": actor.Id}) if err != nil { return err } follow.Set("status", "accepted") err = app.Save(follow) if err != nil { return err } // err = util.SyncOutbox(app, actor) // if err != nil { // return err // } return nil }