Activity Pub Implementation #58
|
@ -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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue