GoBlog/activityStreams.go

186 lines
5.7 KiB
Go
Raw Normal View History

package main
import (
"bytes"
2021-02-24 12:16:33 +00:00
"context"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"github.com/araddon/dateparse"
ct "github.com/elnormous/contenttype"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
"go.goblog.app/app/pkgs/contenttype"
)
const asRequestKey contextKey = "asRequest"
2021-02-24 12:16:33 +00:00
func (a *goBlog) checkActivityStreamsRequest(next http.Handler) http.Handler {
2021-06-19 06:37:16 +00:00
if len(a.asCheckMediaTypes) == 0 {
a.asCheckMediaTypes = []ct.MediaType{
ct.NewMediaType(contenttype.HTML),
ct.NewMediaType(contenttype.AS),
ct.NewMediaType(contenttype.LDJSON),
2022-12-27 07:13:13 +00:00
ct.NewMediaType(contenttype.LDJSON + "; profile=\"https://www.w3.org/ns/activitystreams\""),
2021-06-19 06:37:16 +00:00
}
}
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if ap := a.cfg.ActivityPub; ap != nil && ap.Enabled && !a.isPrivate() {
// Check if accepted media type is not HTML
2021-06-19 06:37:16 +00:00
if mt, _, err := ct.GetAcceptableMediaType(r, a.asCheckMediaTypes); err == nil && mt.String() != a.asCheckMediaTypes[0].String() {
2021-02-24 12:16:33 +00:00
next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), asRequestKey, true)))
return
}
}
next.ServeHTTP(rw, r)
})
}
func (a *goBlog) serveActivityStreamsPost(p *post, w http.ResponseWriter, r *http.Request) {
a.serveAPItem(a.toAPNote(p), w, r)
}
func (a *goBlog) toAPNote(p *post) *ap.Note {
// Create a Note object
note := ap.ObjectNew(ap.NoteType)
note.ID = a.activityPubId(p)
note.URL = ap.IRI(a.fullPostURL(p))
2022-12-14 15:03:54 +00:00
note.AttributedTo = a.apAPIri(a.getBlogFromPost(p))
2022-12-26 18:52:06 +00:00
// Audience
switch p.Visibility {
case visibilityPublic:
note.To.Append(ap.PublicNS, a.apGetFollowersCollectionId(p.Blog, a.getBlogFromPost(p)))
case visibilityUnlisted:
note.To.Append(a.apGetFollowersCollectionId(p.Blog, a.getBlogFromPost(p)))
note.CC.Append(ap.PublicNS)
}
for _, m := range p.Parameters[activityPubMentionsParameter] {
note.CC.Append(ap.IRI(m))
}
// Name and Type
2021-08-05 06:09:34 +00:00
if title := p.RenderedTitle; title != "" {
note.Type = ap.ArticleType
note.Name.Add(ap.DefaultLangRef(title))
}
// Content
2022-12-26 18:52:06 +00:00
note.MediaType = ap.MimeType(contenttype.HTML)
note.Content.Add(ap.DefaultLangRef(a.postHtml(&postHtmlOptions{p: p, absolute: true, activityPub: true})))
// Attachments
if images := p.Parameters[a.cfg.Micropub.PhotoParam]; len(images) > 0 {
var attachments ap.ItemCollection
for _, image := range images {
apImage := ap.ObjectNew(ap.ImageType)
apImage.URL = ap.IRI(image)
attachments.Append(apImage)
}
note.Attachment = attachments
}
// Tags
for _, tagTax := range a.cfg.ActivityPub.TagsTaxonomies {
for _, tag := range p.Parameters[tagTax] {
apTag := &ap.Object{Type: "Hashtag"}
apTag.Name.Add(ap.DefaultLangRef(tag))
apTag.URL = ap.IRI(a.getFullAddress(a.getRelativePath(p.Blog, fmt.Sprintf("/%s/%s", tagTax, urlize(tag)))))
note.Tag.Append(apTag)
}
}
2022-12-26 18:52:06 +00:00
// Mentions
for _, mention := range p.Parameters[activityPubMentionsParameter] {
apMention := ap.MentionNew(ap.IRI(mention))
apMention.Href = ap.IRI(mention)
note.Tag.Append(apMention)
}
if replyLinkActor := p.firstParameter(activityPubReplyActorParameter); replyLinkActor != "" {
apMention := ap.MentionNew(ap.IRI(replyLinkActor))
apMention.Href = ap.IRI(replyLinkActor)
note.Tag.Append(apMention)
}
// Dates
if p.Published != "" {
2020-12-16 19:21:35 +00:00
if t, err := dateparse.ParseLocal(p.Published); err == nil {
note.Published = t
}
}
if p.Updated != "" {
2020-12-16 19:21:35 +00:00
if t, err := dateparse.ParseLocal(p.Updated); err == nil {
2022-12-21 19:36:48 +00:00
note.Updated = t
}
}
// Reply
if replyLink := p.firstParameter(a.cfg.Micropub.ReplyParam); replyLink != "" {
note.InReplyTo = ap.IRI(replyLink)
}
return note
}
const activityPubVersionParam = "activitypubversion"
func (a *goBlog) activityPubId(p *post) ap.IRI {
fu := a.fullPostURL(p)
if version := p.firstParameter(activityPubVersionParam); version != "" {
return ap.IRI(fu + "?activitypubversion=" + version)
}
return ap.IRI(fu)
}
func (a *goBlog) toApPerson(blog string) *ap.Person {
b := a.cfg.Blogs[blog]
apIri := a.apAPIri(b)
apBlog := ap.PersonNew(apIri)
apBlog.URL = apIri
apBlog.Name.Set(ap.DefaultLang, ap.Content(a.renderMdTitle(b.Title)))
apBlog.Summary.Set(ap.DefaultLang, ap.Content(b.Description))
apBlog.PreferredUsername.Set(ap.DefaultLang, ap.Content(blog))
apBlog.Inbox = ap.IRI(a.getFullAddress("/activitypub/inbox/" + blog))
apBlog.Followers = ap.IRI(a.getFullAddress("/activitypub/followers/" + blog))
apBlog.PublicKey.Owner = apIri
apBlog.PublicKey.ID = ap.IRI(a.apIri(b) + "#main-key")
apBlog.PublicKey.PublicKeyPem = string(pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Headers: nil,
Bytes: a.apPubKeyBytes,
}))
2022-11-27 14:06:43 +00:00
if a.hasProfileImage() {
icon := &ap.Image{}
icon.Type = ap.ImageType
2022-11-27 14:06:43 +00:00
icon.MediaType = ap.MimeType(contenttype.JPEG)
2022-11-27 14:26:00 +00:00
icon.URL = ap.IRI(a.getFullAddress(a.profileImagePath(profileImageFormatJPEG, 0, 0)))
apBlog.Icon = icon
}
return apBlog
2022-04-21 16:18:39 +00:00
}
func (a *goBlog) serveActivityStreams(blog string, w http.ResponseWriter, r *http.Request) {
a.serveAPItem(a.toApPerson(blog), w, r)
}
func (a *goBlog) serveAPItem(item any, w http.ResponseWriter, r *http.Request) {
2022-04-10 09:46:35 +00:00
// Encode
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(item)
if err != nil {
2022-04-10 09:46:35 +00:00
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
return
}
// Send response
w.Header().Set(contentType, contenttype.ASUTF8)
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
}
func apUsername(person *ap.Person) string {
preferredUsername := person.PreferredUsername.First().Value.String()
u, err := url.Parse(person.GetLink().String())
if err != nil || u == nil || u.Host == "" || preferredUsername == "" {
return person.GetLink().String()
}
return fmt.Sprintf("@%s@%s", preferredUsername, u.Host)
}