Files
wanderer/db/federation/announce.go
2025-06-24 17:55:59 +02:00

277 lines
6.7 KiB
Go

package federation
import (
"database/sql"
"fmt"
"net/url"
"os"
"path"
"pocketbase/util"
"strings"
"time"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tools/security"
pub "github.com/go-ap/activitypub"
)
type AnnounceType string
const (
TrailAnnounceType AnnounceType = "trail"
ListAnnounceType AnnounceType = "list"
)
func CreateAnnounceActivity(app core.App, record *core.Record, typ AnnounceType) error {
origin := os.Getenv("ORIGIN")
if origin == "" {
return fmt.Errorf("ORIGIN not set")
}
var subject *core.Record
var object pub.Item
var err error
if typ == TrailAnnounceType {
subject, err = app.FindRecordById("trails", record.GetString("trail"))
if err != nil {
return err
}
object, err = util.ObjectFromTrail(app, subject, nil)
if err != nil {
return err
}
} else if typ == ListAnnounceType {
subject, err = app.FindRecordById("lists", record.GetString("list"))
if err != nil {
return err
}
object, err = util.ObjectFromList(app, subject)
if err != nil {
return err
}
} else {
return fmt.Errorf("unknown announce type")
}
subjectActor, err := app.FindRecordById("activitypub_actors", subject.GetString("author"))
if err != nil {
return err
}
objectActor, err := app.FindRecordById("activitypub_actors", record.GetString("actor"))
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)
to := objectActor.GetString("iri")
actor := subjectActor.GetString("iri")
activity := pub.AnnounceNew(pub.IRI(id), object)
activity.To = pub.ItemCollection{pub.IRI(to)}
activity.Actor = pub.IRI(actor)
activity.Tag = pub.ItemCollection{
pub.Object{
Type: pub.NoteType,
Name: pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, "permission")),
Content: pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, record.GetString("permission"))),
},
}
err = PostActivity(app, subjectActor, activity, []string{objectActor.GetString("inbox")})
if err != nil {
return err
}
activityRecord := core.NewRecord(collection)
activityRecord.Set("id", recordId)
activityRecord.Set("iri", id)
activityRecord.Set("to", []string{to})
activityRecord.Set("type", string(pub.AnnounceType))
activityRecord.Set("object", object)
activityRecord.Set("actor", actor)
activityRecord.Set("published", time.Now())
return app.Save(activityRecord)
}
// process incoming announce activity
func ProcessAnnounceActivity(app core.App, actor *core.Record, activity pub.Activity) error {
origin := os.Getenv("ORIGIN")
if origin == "" {
return fmt.Errorf("ORIGIN not set")
}
object := activity.Object.GetID().String()
if strings.Contains(object, "/api/v1/trail") {
processTrailAnnounceActivity(app, actor, activity)
} else if strings.Contains(object, "/api/v1/list") {
processListAnnounceActivity(app, actor, activity)
} else {
return fmt.Errorf("unknown announce type")
}
return nil
}
func processTrailAnnounceActivity(app core.App, actor *core.Record, activity pub.Activity) error {
objectActor, err := app.FindFirstRecordByData("activitypub_actors", "iri", activity.To[0].GetID().String())
if err != nil {
return err
}
var trail *core.Record
if !actor.GetBool("isLocal") {
trail, err = util.TrailFromActivity(activity, app, actor)
if err != nil {
return err
}
permission := "view"
// tags, err := pub.ToItemCollection(activity.Tag)
// if err != nil {
// return err
// }
// for _, tag := range tags.Collection() {
// tagObj, err := pub.ToObject(tag)
// if err != nil {
// continue
// }
// name := tagObj.Name.First().Value.String()
// content := tagObj.Content.First().Value.String()
// if name == "permission" {
// permission = content
// }
// }
record, err := app.FindFirstRecordByFilter("trail_share", "trail={:trailId}&&actor={:actorId}", dbx.Params{"trailId": trail.Id, "actorId": objectActor.Id})
if err != nil {
if err == sql.ErrNoRows {
collection, err := app.FindCollectionByNameOrId("trail_share")
if err != nil {
return err
}
record = core.NewRecord(collection)
record.Set("trail", trail.Id)
record.Set("actor", objectActor.Id)
} else {
return err
}
}
record.Set("permission", permission)
err = app.Save(record)
if err != nil {
return err
}
} else {
trailUrl, err := url.Parse(activity.Object.GetID().String())
if err != nil {
return err
}
trailId := path.Base(trailUrl.Path)
trail, err = app.FindRecordById("trails", trailId)
if err != nil {
return err
}
}
notification := util.Notification{
Type: util.TrailShare,
Metadata: map[string]string{
"id": trail.Id,
"trail": trail.GetString("name"),
"author": fmt.Sprintf("@%s@%s", actor.GetString("preferred_username"), actor.GetString("domain")),
},
Seen: false,
Author: actor.Id,
}
err = util.SendNotification(app, notification, objectActor)
if err != nil {
return err
}
return nil
}
func processListAnnounceActivity(app core.App, actor *core.Record, activity pub.Activity) error {
objectActor, err := app.FindFirstRecordByData("activitypub_actors", "iri", activity.To[0].GetID().String())
if err != nil {
return err
}
var list *core.Record
if !actor.GetBool("isLocal") {
list, err = util.ListFromActivity(activity, app, actor)
if err != nil {
return err
}
record, err := app.FindFirstRecordByFilter("list_share", "list={:listId}&&actor={:actorId}", dbx.Params{"listId": list.Id, "actorId": objectActor.Id})
if err != nil {
if err == sql.ErrNoRows {
collection, err := app.FindCollectionByNameOrId("list_share")
if err != nil {
return err
}
record = core.NewRecord(collection)
record.Set("list", list.Id)
record.Set("actor", objectActor.Id)
record.Set("permission", "view")
} else {
return err
}
}
err = app.Save(record)
if err != nil {
return err
}
} else {
listUrl, err := url.Parse(activity.Object.GetID().String())
if err != nil {
return err
}
listId := path.Base(listUrl.Path)
list, err = app.FindRecordById("trails", listId)
if err != nil {
return err
}
}
notification := util.Notification{
Type: util.ListShare,
Metadata: map[string]string{
"id": list.Id,
"list": list.GetString("name"),
"author": fmt.Sprintf("@%s@%s", actor.GetString("preferred_username"), actor.GetString("domain")),
},
Seen: false,
Author: actor.Id,
}
err = util.SendNotification(app, notification, objectActor)
if err != nil {
return err
}
return nil
}