From a1e4c4e415352b815911f22dba7e6cab349880b8 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Tue, 19 Nov 2024 21:25:44 +0100 Subject: [PATCH] Add support for fediverse:creator meta tag and add new config option (attributionDomains) --- activityPub.go | 2 ++ activityStreams.go | 62 ++++++++++++++++++++++++++++++++++++++++++++-- app.go | 1 + config.go | 5 ++-- example-config.yml | 2 ++ go.mod | 11 ++++---- go.sum | 22 ++++++++-------- uiComponents.go | 5 ++++ 8 files changed, 91 insertions(+), 19 deletions(-) diff --git a/activityPub.go b/activityPub.go index 6551267..8197e8e 100644 --- a/activityPub.go +++ b/activityPub.go @@ -107,9 +107,11 @@ func (a *goBlog) apEnabled() bool { } func (a *goBlog) prepareWebfinger() { + a.apUserHandle = map[string]string{} a.webfingerResources = map[string]*configBlog{} a.webfingerAccts = map[string]string{} for name, blog := range a.cfg.Blogs { + a.apUserHandle[name] = "@" + name + "@" + a.cfg.Server.publicHostname acct := "acct:" + name + "@" + a.cfg.Server.publicHostname a.webfingerResources[acct] = blog a.webfingerResources[a.apIri(blog)] = blog diff --git a/activityStreams.go b/activityStreams.go index 9c5292f..d9311b2 100644 --- a/activityStreams.go +++ b/activityStreams.go @@ -125,12 +125,15 @@ func (a *goBlog) activityPubId(p *post) ap.IRI { return ap.IRI(fu) } -func (a *goBlog) toApPerson(blog string) *ap.Person { +func (a *goBlog) toApPerson(blog string) *goBlogPerson { b := a.cfg.Blogs[blog] apIri := a.apAPIri(b) - apBlog := ap.PersonNew(apIri) + apBlog := &goBlogPerson{ + Person: *ap.PersonNew(apIri), + AlsoKnownAs: nil, + } apBlog.URL = apIri apBlog.Name.Set(ap.DefaultLang, ap.Content(a.renderMdTitle(b.Title))) @@ -156,6 +159,12 @@ func (a *goBlog) toApPerson(blog string) *ap.Person { apBlog.Icon = icon } + var attributionDomains ap.ItemCollection + for _, ad := range a.cfg.ActivityPub.AttributionDomains { + attributionDomains = append(attributionDomains, ap.IRI(ad)) + } + apBlog.AttributionDomains = attributionDomains + return apBlog } @@ -184,3 +193,52 @@ func apUsername(person *ap.Person) string { } return fmt.Sprintf("@%s@%s", preferredUsername, u.Host) } + +// Modified types + +type goBlogPerson struct { + ap.Person + AlsoKnownAs ap.ItemCollection `jsonld:"alsoKnownAs,omitempty"` + AttributionDomains ap.ItemCollection `jsonld:"attributionDomains,omitempty"` +} + +func (a goBlogPerson) MarshalJSON() ([]byte, error) { + // Taken from AP library, Person.MarshalJSON + + b := make([]byte, 0) + notEmpty := false + ap.JSONWrite(&b, '{') + + ap.OnObject(a.Person, func(o *ap.Object) error { + notEmpty = ap.JSONWriteObjectValue(&b, *o) + return nil + }) + if a.Inbox != nil { + notEmpty = ap.JSONWriteItemProp(&b, "inbox", a.Inbox) || notEmpty + } + if a.Following != nil { + notEmpty = ap.JSONWriteItemProp(&b, "following", a.Following) || notEmpty + } + if a.Followers != nil { + notEmpty = ap.JSONWriteItemProp(&b, "followers", a.Followers) || notEmpty + } + if a.PreferredUsername != nil { + notEmpty = ap.JSONWriteNaturalLanguageProp(&b, "preferredUsername", a.PreferredUsername) || notEmpty + } + if len(a.PublicKey.PublicKeyPem)+len(a.PublicKey.ID) > 0 { + if v, err := a.PublicKey.MarshalJSON(); err == nil && len(v) > 0 { + notEmpty = ap.JSONWriteProp(&b, "publicKey", v) || notEmpty + } + } + + // Custom + if len(a.AttributionDomains) > 0 { + notEmpty = ap.JSONWriteItemCollectionProp(&b, "attributionDomains", a.AttributionDomains, false) + } + + if notEmpty { + ap.JSONWrite(&b, '}') + return b, nil + } + return nil, nil +} diff --git a/app.go b/app.go index 5ea8f74..afa4539 100644 --- a/app.go +++ b/app.go @@ -33,6 +33,7 @@ type goBlog struct { apHttpClients map[string]*apc.C webfingerResources map[string]*configBlog webfingerAccts map[string]string + apUserHandle map[string]string // ActivityStreams asCheckMediaTypes []ct.MediaType // Assets diff --git a/config.go b/config.go index 5f79199..433829a 100644 --- a/config.go +++ b/config.go @@ -274,8 +274,9 @@ type configRegexRedirect struct { } type configActivityPub struct { - Enabled bool `mapstructure:"enabled"` - TagsTaxonomies []string `mapstructure:"tagsTaxonomies"` + Enabled bool `mapstructure:"enabled"` + TagsTaxonomies []string `mapstructure:"tagsTaxonomies"` + AttributionDomains []string `mapstructure:"attributionDomains"` } type configNotifications struct { diff --git a/example-config.yml b/example-config.yml index 10814d6..f3aea21 100644 --- a/example-config.yml +++ b/example-config.yml @@ -90,6 +90,8 @@ activityPub: enabled: true # Enable ActivityPub tagsTaxonomies: # Post taxonomies to use as "Hashtags" - tags + attributionDomains: # Domains allowed to use fediverse:creator for your ActivityPub actors in published articles + - example.com # Add your blog at least # Webmention webmention: diff --git a/go.mod b/go.mod index f809f3b..91e973b 100644 --- a/go.mod +++ b/go.mod @@ -18,8 +18,8 @@ require ( github.com/elnormous/contenttype v1.0.4 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 github.com/emersion/go-smtp v0.21.3 - github.com/go-ap/activitypub v0.0.0-20241104140659-be99a725f9d5 - github.com/go-ap/client v0.0.0-20241104140838-929b90e84e8a + github.com/go-ap/activitypub v0.0.0-20241114170014-e897df079e3d + github.com/go-ap/client v0.0.0-20241114180623-d0658a04422c github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-chi/chi/v5 v5.1.0 github.com/go-fed/httpsig v1.1.0 @@ -66,7 +66,7 @@ require ( golang.org/x/sync v0.9.0 golang.org/x/text v0.20.0 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.21.1 + maunium.net/go/mautrix v0.22.0 willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e ) @@ -74,7 +74,8 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect git.sr.ht/~mariusor/cache v0.0.0-20241026131931-1ae5432a2760 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect - git.sr.ht/~mariusor/lw v0.0.0-20240906100438-00d2184b2120 // indirect + git.sr.ht/~mariusor/lw v0.0.0-20241117105956-4b4009e28502 // indirect + git.sr.ht/~mariusor/mask v0.0.0-20240327084502-ef2a9438457e // indirect github.com/andybalholm/cascadia v1.3.2 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/boombuler/barcode v1.0.2 // indirect @@ -121,7 +122,7 @@ require ( github.com/toorop/go-dkim v0.0.0-20240103092955-90b7d1423f92 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/x448/float16 v0.8.4 // indirect - go.mau.fi/util v0.8.1 // indirect + go.mau.fi/util v0.8.2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/image v0.22.0 // indirect diff --git a/go.sum b/go.sum index 5ad21bb..bd964ca 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,10 @@ git.sr.ht/~mariusor/cache v0.0.0-20241026131931-1ae5432a2760 h1:lW2/5Fb0wXBqThID git.sr.ht/~mariusor/cache v0.0.0-20241026131931-1ae5432a2760/go.mod h1:IIDpTy8PpvCIEsyAtLHU+l5KCwpGJ4qLYNwalGg0AVk= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -git.sr.ht/~mariusor/lw v0.0.0-20240906100438-00d2184b2120 h1:OLxL9lel79BV3EHu/AMU+DtcuOnHnb3OjrLyF7NbE1w= -git.sr.ht/~mariusor/lw v0.0.0-20240906100438-00d2184b2120/go.mod h1:kXJ4JsgGBu7IVBKlrVvGjSLJmpsAGqZwq/JU/kTUaLw= +git.sr.ht/~mariusor/lw v0.0.0-20241117105956-4b4009e28502 h1:vHrLbbAzwxsIqPiIuYD30Vebx6lSacNzrmOm43p+JqU= +git.sr.ht/~mariusor/lw v0.0.0-20241117105956-4b4009e28502/go.mod h1:kXJ4JsgGBu7IVBKlrVvGjSLJmpsAGqZwq/JU/kTUaLw= +git.sr.ht/~mariusor/mask v0.0.0-20240327084502-ef2a9438457e h1:IrPLQI6txX0tCcdTBNjvF16UZk6SFb9MqyVE0TKVFJ4= +git.sr.ht/~mariusor/mask v0.0.0-20240327084502-ef2a9438457e/go.mod h1:Mw0HVQc45uMVOiZNDngXg6zQiO2h/yTsNhI5cm0uk3A= github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4= github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= @@ -65,10 +67,10 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-ap/activitypub v0.0.0-20241104140659-be99a725f9d5 h1:TuHgI3hG3XQ78xHgFvCjlApZV32doA25F0sLwJNlcjU= -github.com/go-ap/activitypub v0.0.0-20241104140659-be99a725f9d5/go.mod h1:rpIPGre4qtTgSpVT0zz3hycAMuLtUt7BNngVNpyXhL8= -github.com/go-ap/client v0.0.0-20241104140838-929b90e84e8a h1:YROPlcBtCu/39itdbvbI5IuoIZvXcD1iBdZUuLcC9aI= -github.com/go-ap/client v0.0.0-20241104140838-929b90e84e8a/go.mod h1:Saq7b0oVyIeUCfdfNQ7g26FuMvmFN8yEFfdgDEWiwTU= +github.com/go-ap/activitypub v0.0.0-20241114170014-e897df079e3d h1:dIKWJ4tlGOE5Su/B7wcK9tAVbln4w/l88G47RVj3T2w= +github.com/go-ap/activitypub v0.0.0-20241114170014-e897df079e3d/go.mod h1:rpIPGre4qtTgSpVT0zz3hycAMuLtUt7BNngVNpyXhL8= +github.com/go-ap/client v0.0.0-20241114180623-d0658a04422c h1:+QbdY6Kcm2/9P2j+R1KT8ZyL/M9ClZDEHtOI5ilvSTU= +github.com/go-ap/client v0.0.0-20241114180623-d0658a04422c/go.mod h1:pmwMHroEma9F9+MM/4W5Xi9lbX1LsCiEAWIFydN2RgA= github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 h1:eQEXAzWEijFbwtm/Hr2EtFbM0LvATRd1ltpDb+mfjQk= github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568/go.mod h1:Vkh+Z3f24K8nMsJKXo1FHn5ebPsXvB/WDH5JRtYqdNo= github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw= @@ -296,8 +298,8 @@ github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxA github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= go.hacdias.com/indielib v0.4.0 h1:jB9G459Ne2WjwNwilCpfm5lyK4Q7U8aSd/jN98pSKhY= go.hacdias.com/indielib v0.4.0/go.mod h1:2s7VsHwiaFeVldmreFOg0WXM32W1bytVPYnosAoQiyI= -go.mau.fi/util v0.8.1 h1:Ga43cz6esQBYqcjZ/onRoVnYWoUwjWbsxVeJg2jOTSo= -go.mau.fi/util v0.8.1/go.mod h1:T1u/rD2rzidVrBLyaUdPpZiJdP/rsyi+aTzn0D+Q6wc= +go.mau.fi/util v0.8.2 h1:zWbVHwdRKwI6U9AusmZ8bwgcLosikwbb4GGqLrNr1YE= +go.mau.fi/util v0.8.2/go.mod h1:BHHC9R2WLMJd1bwTZfTcFxUgRFmUgUmiWcT4RbzUgiA= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -376,8 +378,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c= gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -maunium.net/go/mautrix v0.21.1 h1:Z+e448jtlY977iC1kokNJTH5kg2WmDpcQCqn+v9oZOA= -maunium.net/go/mautrix v0.21.1/go.mod h1:7F/S6XAdyc/6DW+Q7xyFXRSPb6IjfqMb1OMepQ8C8OE= +maunium.net/go/mautrix v0.22.0 h1:nLrnLYiMyFV6qZPqpkNogkOPgm2dQTYiQXlu9Nc3rz8= +maunium.net/go/mautrix v0.22.0/go.mod h1:oqwf9WYC/brqucM+heYk4gX11O59nP+ljvyxVhndFIM= willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e h1:TRIOwo0NxN4KVSgYlYmiQktd9I96YgZ3942/JVzhwTM= willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e/go.mod h1:zzo0hFA/E/nl1ZAjXiXA7KCKwCTdgBU+7HXltGgHeGA= willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 h1:V5+O+YZHchEwu6ZmPcqT1dQ+mHgE356Q+w9SVOQ+QZg= diff --git a/uiComponents.go b/uiComponents.go index 98ca0b8..77d48c8 100644 --- a/uiComponents.go +++ b/uiComponents.go @@ -416,6 +416,11 @@ func (a *goBlog) renderPostHeadMeta(hb *htmlbuilder.HtmlBuilder, p *post) { for _, img := range a.photoLinks(p) { hb.WriteElementOpen("meta", "itemprop", "image", "content", img) } + if a.apEnabled() { + if userHandle, ok := a.apUserHandle[p.Blog]; ok { + hb.WriteElementOpen("meta", "name", "fediverse:creator", "property", "fediverse:creator", "content", userHandle) + } + } } // TOR notice in the footer