package federation import ( "fmt" "os" "path" "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 CreateUnfollowActivity(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 } // find the original follow activity followActivityRecord, err := app.FindFirstRecordByFilter("activitypub_activities", "actor={:actor}&&object={:object}&&type={:type}", dbx.Params{"actor": followerActor.GetString("iri"), "object": followeeActor.GetString("iri"), "type": string(pub.FollowType)}) if err != nil { return err } followActivity := pub.FollowNew(pub.IRI(followActivityRecord.GetString("iri")), pub.IRI(followeeActor.GetString("iri"))) followActivity.Actor = pub.IRI(followActivityRecord.GetString("actor")) 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) record := core.NewRecord(collection) record.Set("id", recordId) record.Set("iri", id) record.Set("type", string(pub.UndoType)) record.Set("object", followActivity) record.Set("actor", followerActor.GetString("iri")) record.Set("published", time.Now()) err = app.Save(record) if err != nil { return err } activity := pub.UndoNew(pub.IRI(id), followActivity) activity.Actor = pub.IRI(followerActor.GetString("iri")) return PostActivity(app, followerActor, activity, []string{followeeActor.GetString("inbox")}) } // create outgoing unlike activity func CreateUnlikeActivity(app core.App, like *core.Record) error { origin := os.Getenv("ORIGIN") if origin == "" { return fmt.Errorf("ORIGIN not set") } actor, err := app.FindRecordById("activitypub_actors", like.GetString("actor")) if err != nil { return err } trail, err := app.FindRecordById("trails", like.GetString("trail")) if err != nil { return err } trailAuthor, err := app.FindRecordById("activitypub_actors", trail.GetString("author")) if err != nil { return err } object := trail.GetString("iri") if object == "" { // trail is local object = fmt.Sprintf("%s/api/v1/trail/%s", origin, trail.Id) } // find the original follow activity likeActivityRecord, err := app.FindFirstRecordByFilter("activitypub_activities", "actor={:actor}&&object={:object}&&type={:type}", dbx.Params{"actor": actor.GetString("iri"), "object": object, "type": string(pub.LikeType)}) if err != nil { return err } likeActivity := pub.LikeNew(pub.IRI(likeActivityRecord.GetString("iri")), pub.IRI(object)) likeActivity.Actor = pub.IRI(likeActivityRecord.GetString("actor")) 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) record := core.NewRecord(collection) record.Set("id", recordId) record.Set("iri", id) record.Set("type", string(pub.UndoType)) record.Set("object", likeActivity) record.Set("actor", actor.GetString("iri")) record.Set("published", time.Now()) err = app.Save(record) if err != nil { return err } activity := pub.UndoNew(pub.IRI(id), likeActivity) activity.Actor = pub.IRI(actor.GetString("iri")) return PostActivity(app, actor, activity, []string{trailAuthor.GetString("inbox")}) } func ProcessUndoActivity(app core.App, actor *core.Record, activity pub.Activity) error { if activity.Object.GetType() == pub.FollowType { return processUnfollowActivity(app, actor, activity) } else if activity.Object.GetType() == pub.LikeType { return processUnlikeActivity(app, actor, activity) } else { return fmt.Errorf("unknown undo activity object type") } } func processUnfollowActivity(app core.App, actor *core.Record, activity pub.Activity) error { // this was a local follow if actor.GetBool("isLocal") { return nil } followActivity := activity.Object.(*pub.Activity) followee, err := app.FindFirstRecordByData("activitypub_actors", "iri", followActivity.Object) if err != nil { return err } follow, err := app.FindFirstRecordByFilter("follows", "follower={:follower} && followee={:followee}", dbx.Params{"follower": actor.Id, "followee": followee.Id}) if err != nil { return err } err = app.Delete(follow) if err != nil { return err } return nil } func processUnlikeActivity(app core.App, actor *core.Record, activity pub.Activity) error { if actor.GetBool("isLocal") { return nil } likeActivity := activity.Object.(*pub.Activity) trailId := path.Base(likeActivity.Object.GetID().String()) trail, err := app.FindRecordById("trails", trailId) if err != nil { return err } like, err := app.FindFirstRecordByFilter("trail_like", "actor={:actor} && trail={:trail}", dbx.Params{"actor": actor.Id, "trail": trail.Id}) if err != nil { return err } err = app.Delete(like) if err != nil { return err } return nil }