Activity Pub Implementation #58

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

View File

@ -63,6 +63,7 @@ type ActivityPubService struct {
interactionRepository repository.InteractionRepository interactionRepository repository.InteractionRepository
entryService *EntryService entryService *EntryService
siteConfigServcie *SiteConfigService siteConfigServcie *SiteConfigService
binService *BinaryService
} }
func NewActivityPubService( func NewActivityPubService(
@ -71,6 +72,7 @@ func NewActivityPubService(
interactionRepository repository.InteractionRepository, interactionRepository repository.InteractionRepository,
entryService *EntryService, entryService *EntryService,
siteConfigServcie *SiteConfigService, siteConfigServcie *SiteConfigService,
binService *BinaryService,
bus *EventBus, bus *EventBus,
) *ActivityPubService { ) *ActivityPubService {
service := &ActivityPubService{ service := &ActivityPubService{
@ -78,6 +80,7 @@ func NewActivityPubService(
configRepo: configRepo, configRepo: configRepo,
interactionRepository: interactionRepository, interactionRepository: interactionRepository,
entryService: entryService, entryService: entryService,
binService: binService,
siteConfigServcie: siteConfigServcie, siteConfigServcie: siteConfigServcie,
} }
@ -458,50 +461,20 @@ func (s *ActivityPubService) sendObject(to vocab.Actor, data []byte) error {
func (svc *ActivityPubService) NotifyEntryCreated(entry model.Entry) { func (svc *ActivityPubService) NotifyEntryCreated(entry model.Entry) {
slog.Info("Processing Entry Create for ActivityPub") slog.Info("Processing Entry Create for ActivityPub")
// limit to notes for now
noteEntry, ok := entry.(*entrytypes.Note)
if !ok {
slog.Info("not a note")
return
}
siteCfg, _ := svc.siteConfigServcie.GetSiteConfig()
followers, err := svc.AllFollowers() followers, err := svc.AllFollowers()
if err != nil { if err != nil {
slog.Error("Cannot retrieve followers") slog.Error("Cannot retrieve followers")
} }
content := noteEntry.Content() object, err := svc.entryToObject(entry)
if err != nil {
r := regexp.MustCompile("#[a-z0-9_]+") slog.Error("Cannot convert object", "err", err)
matches := r.FindAllString(string(content), -1)
tags := vocab.ItemCollection{}
for _, hashtag := range matches {
tags.Append(vocab.Object{
ID: vocab.ID(svc.HashtagId(hashtag)),
Name: vocab.NaturalLanguageValues{{Value: vocab.Content(hashtag)}},
})
} }
note := vocab.Note{ create := vocab.CreateNew(object.ID, object)
ID: vocab.ID(noteEntry.FullUrl(siteCfg)), create.Actor = object.AttributedTo
Type: "Note", create.To = object.To
To: vocab.ItemCollection{ create.Published = object.Published
vocab.PublicNS,
vocab.IRI(svc.FollowersUrl()),
},
Published: *noteEntry.PublishedAt(),
AttributedTo: vocab.ID(svc.ActorUrl()),
Content: vocab.NaturalLanguageValues{
{Value: vocab.Content(content)},
},
Tag: tags,
}
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( data, err := jsonld.WithContext(
jsonld.IRI(vocab.ActivityBaseURI), jsonld.IRI(vocab.ActivityBaseURI),
jsonld.Context{ jsonld.Context{
@ -529,28 +502,20 @@ func (svc *ActivityPubService) NotifyEntryUpdated(entry model.Entry) {
} }
func (svc *ActivityPubService) NotifyEntryDeleted(entry model.Entry) { func (svc *ActivityPubService) NotifyEntryDeleted(entry model.Entry) {
slog.Info("Processing Entry Delete for ActivityPub") obj, err := svc.entryToObject(entry)
// limit to notes for now if err != nil {
noteEntry, ok := entry.(*entrytypes.Note) slog.Error("error converting to object", "err", err)
if !ok {
slog.Info("not a note")
return return
} }
siteCfg, _ := svc.siteConfigServcie.GetSiteConfig()
followers, err := svc.AllFollowers() followers, err := svc.AllFollowers()
if err != nil { if err != nil {
slog.Error("Cannot retrieve followers") slog.Error("Cannot retrieve followers")
} }
note := vocab.Note{ delete := vocab.DeleteNew(obj.ID, obj)
ID: vocab.ID(noteEntry.FullUrl(siteCfg)), delete.Actor = obj.AttributedTo
Type: "Note", delete.To = obj.To
}
delete := vocab.DeleteNew(vocab.IRI(noteEntry.FullUrl(siteCfg)), note)
delete.Actor = note.AttributedTo
delete.To = note.To
delete.Published = time.Now() delete.Published = time.Now()
data, err := jsonld.WithContext( data, err := jsonld.WithContext(
jsonld.IRI(vocab.ActivityBaseURI), jsonld.IRI(vocab.ActivityBaseURI),
@ -568,3 +533,88 @@ func (svc *ActivityPubService) NotifyEntryDeleted(entry model.Entry) {
} }
} }
func (svc *ActivityPubService) entryToObject(entry model.Entry) (vocab.Object, error) {
// limit to notes for now
if noteEntry, ok := entry.(*entrytypes.Note); ok {
return svc.noteToObject(noteEntry), nil
}
if imageEntry, ok := entry.(*entrytypes.Image); ok {
return svc.imageToObject(imageEntry), nil
}
slog.Warn("entry type not yet supported for activity pub")
return vocab.Object{}, errors.New("entry type not supported")
}
func (svc *ActivityPubService) noteToObject(noteEntry *entrytypes.Note) vocab.Object {
siteCfg, _ := svc.siteConfigServcie.GetSiteConfig()
content := noteEntry.Content()
r := regexp.MustCompile("#[a-z0-9_]+")
matches := r.FindAllString(string(content), -1)
tags := vocab.ItemCollection{}
for _, hashtag := range matches {
tags.Append(vocab.Object{
ID: vocab.ID(svc.HashtagId(hashtag)),
Name: vocab.NaturalLanguageValues{{Value: vocab.Content(hashtag)}},
})
}
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(content)},
},
Tag: tags,
}
return note
}
func (svc *ActivityPubService) imageToObject(imageEntry *entrytypes.Image) vocab.Object {
siteCfg, _ := svc.siteConfigServcie.GetSiteConfig()
content := imageEntry.Content()
imgPath := imageEntry.ImageUrl()
fullImageUrl, _ := url.JoinPath(siteCfg.FullUrl, imgPath)
binaryFile, err := svc.binService.FindById(imageEntry.MetaData().(*entrytypes.ImageMetaData).ImageId)
if err != nil {
slog.Error("cannot get image file")
}
attachments := vocab.ItemCollection{}
attachments = append(attachments, vocab.Document{
Type: vocab.DocumentType,
MediaType: vocab.MimeType(binaryFile.Mime()),
URL: vocab.ID(fullImageUrl),
})
image := vocab.Note{
ID: vocab.ID(imageEntry.FullUrl(siteCfg)),
Type: "Note",
To: vocab.ItemCollection{
vocab.PublicNS,
vocab.IRI(svc.FollowersUrl()),
},
Published: *imageEntry.PublishedAt(),
AttributedTo: vocab.ID(svc.ActorUrl()),
Name: vocab.NaturalLanguageValues{
{Value: vocab.Content(imageEntry.Title())},
},
Content: vocab.NaturalLanguageValues{
{Value: vocab.Content(content)},
},
Attachment: attachments,
// Tag: tags,
}
return image
}

View File

@ -70,7 +70,7 @@ func App(db infra.Database) *web.WebApp {
) )
apService := app.NewActivityPubService( apService := app.NewActivityPubService(
followersRepo, configRepo, interactionRepo, followersRepo, configRepo, interactionRepo,
entryService, siteConfigService, entryService, siteConfigService, binaryService,
eventBus, eventBus,
) )