summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2025-03-24 11:56:42 +0100
committerLibravatar GitHub <noreply@github.com>2025-03-24 10:56:42 +0000
commit27171a78ded38a7142aecfcaa75ede86f73b7a5b (patch)
tree6468899981ba4f6fd78cd95544ed1ded34120437 /internal
parent[chore]: Bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2 (#3927) (diff)
downloadgotosocial-27171a78ded38a7142aecfcaa75ede86f73b7a5b.tar.xz
[feature] Parse funkwhale `Album` as Statusable to allow barebones interacting with bandwagon (#3931)
* bump activity version * parse funkwhale / bandwagon album as statusable
Diffstat (limited to 'internal')
-rw-r--r--internal/ap/activitystreams.go9
-rw-r--r--internal/ap/extract.go9
-rw-r--r--internal/ap/interfaces.go11
-rw-r--r--internal/ap/resolve_test.go50
-rw-r--r--internal/ap/serialize.go4
-rw-r--r--internal/typeutils/astointernal.go13
-rw-r--r--internal/typeutils/astointernal_test.go63
-rw-r--r--internal/typeutils/internaltoas.go55
8 files changed, 171 insertions, 43 deletions
diff --git a/internal/ap/activitystreams.go b/internal/ap/activitystreams.go
index a185edabe..0d1bc62c9 100644
--- a/internal/ap/activitystreams.go
+++ b/internal/ap/activitystreams.go
@@ -98,10 +98,17 @@ const (
// that we don't *yet* know what type of Object something is.
ObjectUnknown = "Unknown"
- // Extensions and unofficial additions.
+ /* Extensions and unofficial additions */
+
+ /* GtS stuff */
+
ObjectLikeApproval = "LikeApproval"
ObjectReplyApproval = "ReplyApproval"
ObjectAnnounceApproval = "AnnounceApproval"
+
+ /* Funkwhale stuff */
+
+ ObjectAlbum = "Album"
)
// isActivity returns whether AS type name is of an Activity (NOT IntransitiveActivity).
diff --git a/internal/ap/extract.go b/internal/ap/extract.go
index 20f8eb98a..d454d69f6 100644
--- a/internal/ap/extract.go
+++ b/internal/ap/extract.go
@@ -1078,7 +1078,14 @@ func ExtractInteractionPolicy(
statusable Statusable,
owner *gtsmodel.Account,
) *gtsmodel.InteractionPolicy {
- policyProp := statusable.GetGoToSocialInteractionPolicy()
+ ipa, ok := statusable.(InteractionPolicyAware)
+ if !ok {
+ // Not a type with interaction
+ // policy properties settable.
+ return nil
+ }
+
+ policyProp := ipa.GetGoToSocialInteractionPolicy()
if policyProp == nil || policyProp.Len() != 1 {
return nil
}
diff --git a/internal/ap/interfaces.go b/internal/ap/interfaces.go
index 0fd982eb8..4c91c57f6 100644
--- a/internal/ap/interfaces.go
+++ b/internal/ap/interfaces.go
@@ -76,7 +76,8 @@ func IsStatusable(typeName string) bool {
ObjectEvent,
ObjectPlace,
ObjectProfile,
- ActivityQuestion:
+ ActivityQuestion,
+ ObjectAlbum:
return true
default:
return false
@@ -226,11 +227,13 @@ type Statusable interface {
WithTo
WithCc
WithSensitive
- WithConversation
WithContent
WithAttachment
WithTag
WithReplies
+}
+
+type InteractionPolicyAware interface {
WithInteractionPolicy
WithApprovedBy
}
@@ -589,10 +592,6 @@ type WithSensitive interface {
SetActivityStreamsSensitive(vocab.ActivityStreamsSensitiveProperty)
}
-// WithConversation ...
-type WithConversation interface { // TODO
-}
-
// WithContent represents an activity with ActivityStreamsContentProperty
type WithContent interface {
GetActivityStreamsContent() vocab.ActivityStreamsContentProperty
diff --git a/internal/ap/resolve_test.go b/internal/ap/resolve_test.go
index aaf90ab0a..b70dba77b 100644
--- a/internal/ap/resolve_test.go
+++ b/internal/ap/resolve_test.go
@@ -80,6 +80,56 @@ func (suite *ResolveTestSuite) TestResolveNonAPJSONAsAccountable() {
suite.Nil(accountable)
}
+func (suite *ResolveTestSuite) TestResolveBandwagonAlbumAsStatusable() {
+ b := []byte(`{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "discoverable": "toot:discoverable",
+ "indexable": "toot:indexable",
+ "toot": "https://joinmastodon.org/ns#"
+ },
+ "https://funkwhale.audio/ns"
+ ],
+ "artists": [
+ {
+ "id": "https://bandwagon.fm/@67a0a0808121f77ed3466870",
+ "name": "Luka Prinčič",
+ "type": "Artist"
+ }
+ ],
+ "attachment": [
+ {
+ "mediaType": "image/webp",
+ "name": "image",
+ "type": "Document",
+ "url": "https://bandwagon.fm/67a0a219f050061c8b4ce427/attachments/67a0a21bf050061c8b4ce429"
+ }
+ ],
+ "attributedTo": "https://bandwagon.fm/@67a0a0808121f77ed3466870",
+ "content": "... a transgenre mutation, a fluid entity, jagged pop, electro-funk, techno-cabaret, a schlager, and soft alternative, queer to the core, satire and tragedy, sharp and fun indulgence for the dance of bodies and brains, activism and hedonism, which would all like to steal your attention.\r\n\r\nDRAGX̶FUNK is pronounced /dɹæɡɑːfʌŋk/.\r\n\r\n---\r\n\r\n## Buy digital\r\n💳 [Stripe](https://buy.stripe.com/6oE8x52iG1Kq5pKeV3)\r\n\r\n---\r\n\r\n## Buy dl/merch\r\n🎵 [Bandcamp](https://lukaprincic.bandcamp.com/album/dragx-funk) \r\n\r\n---\r\n\r\n## More:\r\n🌐 [prin.lu](https://prin.lu/music/241205_dragx-funk/) \r\n👉 [kamizdat.si](https://kamizdat.si/releases/dragx-funk-2/)\r\n",
+ "context": "https://bandwagon.fm/67a0a219f050061c8b4ce427",
+ "id": "https://bandwagon.fm/67a0a219f050061c8b4ce427",
+ "library": "https://bandwagon.fm/67a0a219f050061c8b4ce427/pub/children",
+ "license": "CC-BY-NC-SA",
+ "name": "DRAGX̶FUNK",
+ "published": "2025-03-17T11:40:53Z",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "tracks": "https://bandwagon.fm/67a0a219f050061c8b4ce427/pub/children",
+ "type": "Album",
+ "url": "https://bandwagon.fm/67a0a219f050061c8b4ce427"
+}`)
+
+ statusable, err := ap.ResolveStatusable(
+ context.Background(), io.NopCloser(bytes.NewReader(b)),
+ )
+ suite.NoError(err)
+ suite.NotNil(statusable)
+}
+
func TestResolveTestSuite(t *testing.T) {
suite.Run(t, &ResolveTestSuite{})
}
diff --git a/internal/ap/serialize.go b/internal/ap/serialize.go
index f1dc095f6..65ba69ba1 100644
--- a/internal/ap/serialize.go
+++ b/internal/ap/serialize.go
@@ -153,7 +153,9 @@ func serializeStatusable(t vocab.Type, includeContext bool) (map[string]interfac
NormalizeOutgoingAttachmentProp(statusable, data)
NormalizeOutgoingContentProp(statusable, data)
- NormalizeOutgoingInteractionPolicyProp(statusable, data)
+ if ipa, ok := statusable.(InteractionPolicyAware); ok {
+ NormalizeOutgoingInteractionPolicyProp(ipa, data)
+ }
return data, nil
}
diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go
index 16aa430a3..741e1509e 100644
--- a/internal/typeutils/astointernal.go
+++ b/internal/typeutils/astointernal.go
@@ -434,9 +434,10 @@ func (c *Converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
return nil, gtserror.SetMalformed(err)
}
- // Status was sent to us or dereffed
- // by us so it must be federated.
+ // Status was sent to us or dereffed by
+ // us so it must be federated and not local.
status.Federated = util.Ptr(true)
+ status.Local = util.Ptr(false)
// Derive interaction policy for this status.
status.InteractionPolicy = ap.ExtractInteractionPolicy(
@@ -446,9 +447,11 @@ func (c *Converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
// Set approvedByURI if present,
// for later dereferencing.
- approvedByURI := ap.GetApprovedBy(statusable)
- if approvedByURI != nil {
- status.ApprovedByURI = approvedByURI.String()
+ if ipa, ok := statusable.(ap.InteractionPolicyAware); ok {
+ approvedByURI := ap.GetApprovedBy(ipa)
+ if approvedByURI != nil {
+ status.ApprovedByURI = approvedByURI.String()
+ }
}
// Assume not pending approval; this may
diff --git a/internal/typeutils/astointernal_test.go b/internal/typeutils/astointernal_test.go
index 458913f6f..67b7d75af 100644
--- a/internal/typeutils/astointernal_test.go
+++ b/internal/typeutils/astointernal_test.go
@@ -21,7 +21,6 @@ import (
"bytes"
"context"
"encoding/json"
- "fmt"
"io"
"testing"
@@ -224,8 +223,7 @@ func (suite *ASToInternalTestSuite) TestParseOwncastService() {
b, err := json.Marshal(apiAcct)
suite.NoError(err)
-
- fmt.Printf("\n\n\n%s\n\n\n", string(b))
+ suite.NotNil(b)
}
func (suite *ASToInternalTestSuite) TestParseBookwyrmStatus() {
@@ -282,6 +280,65 @@ func (suite *ASToInternalTestSuite) TestParseBookwyrmStatus() {
suite.Len(status.Attachments, 1)
}
+func (suite *ASToInternalTestSuite) TestParseBandwagonAlbum() {
+ authorAccount := suite.testAccounts["remote_account_1"]
+
+ raw := `{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "discoverable": "toot:discoverable",
+ "indexable": "toot:indexable",
+ "toot": "https://joinmastodon.org/ns#"
+ },
+ "https://funkwhale.audio/ns"
+ ],
+ "artists": [
+ {
+ "id": "https://bandwagon.fm/@67a0a0808121f77ed3466870",
+ "name": "Luka Prinčič",
+ "type": "Artist"
+ }
+ ],
+ "attachment": [
+ {
+ "mediaType": "image/webp",
+ "name": "image",
+ "type": "Document",
+ "url": "https://bandwagon.fm/67a0a219f050061c8b4ce427/attachments/67a0a21bf050061c8b4ce429"
+ }
+ ],
+ "attributedTo": "` + authorAccount.URI + `",
+ "content": "... a transgenre mutation, a fluid entity, jagged pop, electro-funk, techno-cabaret, a schlager, and soft alternative, queer to the core, satire and tragedy, sharp and fun indulgence for the dance of bodies and brains, activism and hedonism, which would all like to steal your attention.\r\n\r\nDRAGX̶FUNK is pronounced /dɹæɡɑːfʌŋk/.\r\n\r\n---\r\n\r\n## Buy digital\r\n💳 [Stripe](https://buy.stripe.com/6oE8x52iG1Kq5pKeV3)\r\n\r\n---\r\n\r\n## Buy dl/merch\r\n🎵 [Bandcamp](https://lukaprincic.bandcamp.com/album/dragx-funk) \r\n\r\n---\r\n\r\n## More:\r\n🌐 [prin.lu](https://prin.lu/music/241205_dragx-funk/) \r\n👉 [kamizdat.si](https://kamizdat.si/releases/dragx-funk-2/)\r\n",
+ "context": "https://bandwagon.fm/67a0a219f050061c8b4ce427",
+ "id": "https://bandwagon.fm/67a0a219f050061c8b4ce427",
+ "library": "https://bandwagon.fm/67a0a219f050061c8b4ce427/pub/children",
+ "license": "CC-BY-NC-SA",
+ "name": "DRAGX̶FUNK",
+ "published": "2025-03-17T11:40:53Z",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "tracks": "https://bandwagon.fm/67a0a219f050061c8b4ce427/pub/children",
+ "type": "Album",
+ "url": "https://bandwagon.fm/67a0a219f050061c8b4ce427"
+}`
+
+ t := suite.jsonToType(raw)
+ asArticle, ok := t.(ap.Statusable)
+ if !ok {
+ suite.FailNow("type not coercible")
+ }
+
+ s, err := suite.typeconverter.ASStatusToStatus(context.Background(), asArticle)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+ suite.NotNil(s)
+ suite.NoError(err)
+}
+
func (suite *ASToInternalTestSuite) TestParseFlag1() {
reportedAccount := suite.testAccounts["local_account_1"]
reportingAccount := suite.testAccounts["remote_account_1"]
diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go
index c5e055638..7d420de2c 100644
--- a/internal/typeutils/internaltoas.go
+++ b/internal/typeutils/internaltoas.go
@@ -705,35 +705,38 @@ func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (ap.Stat
status.SetActivityStreamsSensitive(sensitiveProp)
// interactionPolicy
- var p *gtsmodel.InteractionPolicy
- if s.InteractionPolicy != nil {
- // Use InteractionPolicy
- // set on the status.
- p = s.InteractionPolicy
- } else {
- // Fall back to default policy
- // for the status's visibility.
- p = gtsmodel.DefaultInteractionPolicyFor(s.Visibility)
- }
- policy, err := c.InteractionPolicyToASInteractionPolicy(ctx, p, s)
- if err != nil {
- return nil, fmt.Errorf("error creating interactionPolicy: %w", err)
- }
-
- policyProp := streams.NewGoToSocialInteractionPolicyProperty()
- policyProp.AppendGoToSocialInteractionPolicy(policy)
- status.SetGoToSocialInteractionPolicy(policyProp)
-
- // Parse + set approvedBy.
- if s.ApprovedByURI != "" {
- approvedBy, err := url.Parse(s.ApprovedByURI)
+ if ipa, ok := status.(ap.InteractionPolicyAware); ok {
+ var p *gtsmodel.InteractionPolicy
+ if s.InteractionPolicy != nil {
+ // Use InteractionPolicy
+ // set on the status.
+ p = s.InteractionPolicy
+ } else {
+ // Fall back to default policy
+ // for the status's visibility.
+ p = gtsmodel.DefaultInteractionPolicyFor(s.Visibility)
+ }
+ policy, err := c.InteractionPolicyToASInteractionPolicy(ctx, p, s)
if err != nil {
- return nil, fmt.Errorf("error parsing approvedBy: %w", err)
+ return nil, fmt.Errorf("error creating interactionPolicy: %w", err)
}
- approvedByProp := streams.NewGoToSocialApprovedByProperty()
- approvedByProp.Set(approvedBy)
- status.SetGoToSocialApprovedBy(approvedByProp)
+ // Set interaction policy.
+ policyProp := streams.NewGoToSocialInteractionPolicyProperty()
+ policyProp.AppendGoToSocialInteractionPolicy(policy)
+ ipa.SetGoToSocialInteractionPolicy(policyProp)
+
+ // Parse + set approvedBy.
+ if s.ApprovedByURI != "" {
+ approvedBy, err := url.Parse(s.ApprovedByURI)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing approvedBy: %w", err)
+ }
+
+ approvedByProp := streams.NewGoToSocialApprovedByProperty()
+ approvedByProp.Set(approvedBy)
+ ipa.SetGoToSocialApprovedBy(approvedByProp)
+ }
}
return status, nil