Activity Pub Implementation #58

Merged
h4kor merged 13 commits from activity_pub into main 2024-05-18 14:31:11 +00:00
5 changed files with 117 additions and 3 deletions
Showing only changes of commit cba57ba708 - Show all commits

View File

@ -14,6 +14,7 @@ import (
"owl-blogs/app/repository"
"owl-blogs/config"
"owl-blogs/domain/model"
entrytypes "owl-blogs/entry_types"
"owl-blogs/render"
"reflect"
"time"
@ -62,12 +63,17 @@ func NewActivityPubService(
followersRepo repository.FollowerRepository,
configRepo repository.ConfigRepository,
siteConfigServcie *SiteConfigService,
bus *EventBus,
) *ActivityPubService {
return &ActivityPubService{
service := &ActivityPubService{
followersRepo: followersRepo,
configRepo: configRepo,
siteConfigServcie: siteConfigServcie,
}
bus.Subscribe(service)
return service
}
func (svc *ActivityPubService) defaultConfig() ActivityPubConfig {
@ -313,3 +319,69 @@ func (s *ActivityPubService) sendObject(to vocab.Actor, data []byte) error {
slog.Info("Retrieved", "status", resp.Status, "body", string(body))
return nil
}
/*
* Notifiers
*/
func (svc *ActivityPubService) NotifyEntryCreated(entry model.Entry) {
// limit to notes for now
noteEntry, ok := entry.(*entrytypes.Note)
if !ok {
slog.Info("not an image")
return
}
siteCfg, _ := svc.siteConfigServcie.GetSiteConfig()
followers, err := svc.AllFollowers()
if err != nil {
slog.Error("Cannot retrieve followers")
}
note := vocab.Note{
ID: vocab.ID(noteEntry.FullUrl(siteCfg)),
Type: "Note",
To: vocab.ItemCollection{
vocab.PublicNS,
vocab.IRI(svc.FollowersUrl()),
},
Published: *noteEntry.PublishedAt(),
AttributedTo: vocab.ID(svc.ActorUrl()),
Content: vocab.NaturalLanguageValues{
{Value: vocab.Content(noteEntry.Content())},
},
}
create := vocab.CreateNew(vocab.IRI(noteEntry.FullUrl(siteCfg)), note)
create.Actor = note.AttributedTo
create.To = note.To
create.Published = note.Published
data, err := jsonld.WithContext(
jsonld.IRI(vocab.ActivityBaseURI),
jsonld.Context{
jsonld.ContextElement{
Term: "toot",
IRI: jsonld.IRI("http://joinmastodon.org/ns#"),
},
},
).Marshal(create)
if err != nil {
slog.Error("marshalling error", "err", err)
}
for _, follower := range followers {
actor, err := svc.GetActor(follower)
if err != nil {
slog.Error("Unable to retrieve follower actor", "err", err)
}
svc.sendObject(actor, data)
}
}
func (svc *ActivityPubService) NotifyEntryUpdated(entry model.Entry) {
}
func (svc *ActivityPubService) NotifyEntryDeleted(entry model.Entry) {
}

View File

@ -66,7 +66,7 @@ func App(db infra.Database) *web.WebApp {
webmentionService := app.NewWebmentionService(
siteConfigService, interactionRepo, entryRepo, httpClient, eventBus,
)
apService := app.NewActivityPubService(followersRepo, configRepo, siteConfigService)
apService := app.NewActivityPubService(followersRepo, configRepo, siteConfigService, eventBus)
// setup render functions
render.SiteConfigService = siteConfigService

View File

@ -1,6 +1,7 @@
package model
import (
"net/url"
"time"
)
@ -21,6 +22,8 @@ type Entry interface {
SetPublishedAt(publishedAt *time.Time)
SetMetaData(metaData EntryMetaData)
SetAuthorId(authorId string)
FullUrl(cfg SiteConfig) string
}
type EntryMetaData interface {
@ -60,3 +63,8 @@ func (e *EntryBase) AuthorId() string {
func (e *EntryBase) SetAuthorId(authorId string) {
e.authorId = authorId
}
func (e *EntryBase) FullUrl(cfg SiteConfig) string {
u, _ := url.JoinPath(cfg.FullUrl, "/posts/", e.ID(), "/")
return u
}

View File

@ -0,0 +1,34 @@
package model_test
import (
"owl-blogs/domain/model"
"testing"
"github.com/stretchr/testify/require"
)
func TestEntryFullUrl(t *testing.T) {
type testCase struct {
Id string
Url string
Want string
}
testCases := []testCase{
{Id: "foobar", Url: "https://example.com", Want: "https://example.com/posts/foobar/"},
{Id: "foobar", Url: "https://example.com/", Want: "https://example.com/posts/foobar/"},
{Id: "foobar", Url: "http://example.com", Want: "http://example.com/posts/foobar/"},
{Id: "foobar", Url: "http://example.com/", Want: "http://example.com/posts/foobar/"},
{Id: "bi-bar-buz", Url: "https://example.com", Want: "https://example.com/posts/bi-bar-buz/"},
{Id: "foobar", Url: "https://example.com/lol/", Want: "https://example.com/lol/posts/foobar/"},
}
for _, test := range testCases {
e := model.EntryBase{}
e.SetID(test.Id)
cfg := model.SiteConfig{FullUrl: test.Url}
require.Equal(t, e.FullUrl(cfg), test.Want)
}
}

View File

@ -171,7 +171,7 @@ func (s *ActivityPubServer) processUndo(r *http.Request, act *vocab.Activity) er
return err
}
// go acpub.Accept(gameName, act)
go s.apService.Accept(act)
return nil
}