summaryrefslogtreecommitdiff
path: root/internal/typeutils
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2025-01-23 17:18:23 +0000
committerLibravatar GitHub <noreply@github.com>2025-01-23 18:18:23 +0100
commit9333bbc4d0d5ae46c72fca1f5b1aacb3c0a7653e (patch)
treee7ccd1dc3abff5386e0b86e6baf659c9aa3b55a5 /internal/typeutils
parent[feature] Add warning about `trusted-proxies` to make config easier (#3675) (diff)
downloadgotosocial-9333bbc4d0d5ae46c72fca1f5b1aacb3c0a7653e.tar.xz
[feature] Serve bot accounts over AP as Service instead of Person (#3672)
* pepis * oopsie doopsie * bollocks
Diffstat (limited to 'internal/typeutils')
-rw-r--r--internal/typeutils/internaltoas.go94
-rw-r--r--internal/typeutils/internaltoas_test.go91
-rw-r--r--internal/typeutils/wrap.go71
-rw-r--r--internal/typeutils/wrap_test.go80
4 files changed, 242 insertions, 94 deletions
diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go
index de1badb5c..ce5187bde 100644
--- a/internal/typeutils/internaltoas.go
+++ b/internal/typeutils/internaltoas.go
@@ -36,12 +36,24 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/uris"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
)
-// AccountToAS converts a gts model account into an activity streams person, suitable for federation
-func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) {
- person := streams.NewActivityStreamsPerson()
+// AccountToAS converts a gts model account
+// into an activity streams person or service.
+func (c *Converter) AccountToAS(
+ ctx context.Context,
+ a *gtsmodel.Account,
+) (ap.Accountable, error) {
+ // accountable is a service if this
+ // is a bot account, otherwise a person.
+ var accountable ap.Accountable
+ if util.PtrOrZero(a.Bot) {
+ accountable = streams.NewActivityStreamsService()
+ } else {
+ accountable = streams.NewActivityStreamsPerson()
+ }
// id should be the activitypub URI of this user
// something like https://example.org/users/example_user
@@ -51,13 +63,13 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
idProp := streams.NewJSONLDIdProperty()
idProp.SetIRI(profileIDURI)
- person.SetJSONLDId(idProp)
+ accountable.SetJSONLDId(idProp)
// published
// The moment when the account was created.
publishedProp := streams.NewActivityStreamsPublishedProperty()
publishedProp.Set(a.CreatedAt)
- person.SetActivityStreamsPublished(publishedProp)
+ accountable.SetActivityStreamsPublished(publishedProp)
// following
// The URI for retrieving a list of accounts this user is following
@@ -67,7 +79,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
followingProp := streams.NewActivityStreamsFollowingProperty()
followingProp.SetIRI(followingURI)
- person.SetActivityStreamsFollowing(followingProp)
+ accountable.SetActivityStreamsFollowing(followingProp)
// followers
// The URI for retrieving a list of this user's followers
@@ -77,7 +89,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
followersProp := streams.NewActivityStreamsFollowersProperty()
followersProp.SetIRI(followersURI)
- person.SetActivityStreamsFollowers(followersProp)
+ accountable.SetActivityStreamsFollowers(followersProp)
// inbox
// the activitypub inbox of this user for accepting messages
@@ -87,7 +99,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
inboxProp := streams.NewActivityStreamsInboxProperty()
inboxProp.SetIRI(inboxURI)
- person.SetActivityStreamsInbox(inboxProp)
+ accountable.SetActivityStreamsInbox(inboxProp)
// shared inbox -- only add this if we know for sure it has one
if a.SharedInboxURI != nil && *a.SharedInboxURI != "" {
@@ -101,7 +113,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
sharedInboxProp.SetIRI(sharedInboxURI)
endpoints.SetActivityStreamsSharedInbox(sharedInboxProp)
endpointsProp.AppendActivityStreamsEndpoints(endpoints)
- person.SetActivityStreamsEndpoints(endpointsProp)
+ accountable.SetActivityStreamsEndpoints(endpointsProp)
}
// outbox
@@ -112,7 +124,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
outboxProp := streams.NewActivityStreamsOutboxProperty()
outboxProp.SetIRI(outboxURI)
- person.SetActivityStreamsOutbox(outboxProp)
+ accountable.SetActivityStreamsOutbox(outboxProp)
// featured posts
// Pinned posts.
@@ -122,7 +134,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
featuredProp := streams.NewTootFeaturedProperty()
featuredProp.SetIRI(featuredURI)
- person.SetTootFeatured(featuredProp)
+ accountable.SetTootFeatured(featuredProp)
// featuredTags
// NOT IMPLEMENTED
@@ -131,7 +143,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
preferredUsernameProp.SetXMLSchemaString(a.Username)
- person.SetActivityStreamsPreferredUsername(preferredUsernameProp)
+ accountable.SetActivityStreamsPreferredUsername(preferredUsernameProp)
// name
// Used as profile display name.
@@ -141,14 +153,14 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
} else {
nameProp.AppendXMLSchemaString(a.Username)
}
- person.SetActivityStreamsName(nameProp)
+ accountable.SetActivityStreamsName(nameProp)
// summary
// Used as profile bio.
if a.Note != "" {
summaryProp := streams.NewActivityStreamsSummaryProperty()
summaryProp.AppendXMLSchemaString(a.Note)
- person.SetActivityStreamsSummary(summaryProp)
+ accountable.SetActivityStreamsSummary(summaryProp)
}
// url
@@ -159,19 +171,19 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
urlProp := streams.NewActivityStreamsUrlProperty()
urlProp.AppendIRI(profileURL)
- person.SetActivityStreamsUrl(urlProp)
+ accountable.SetActivityStreamsUrl(urlProp)
// manuallyApprovesFollowers
// Will be shown as a locked account.
manuallyApprovesFollowersProp := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
manuallyApprovesFollowersProp.Set(*a.Locked)
- person.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp)
+ accountable.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp)
// discoverable
// Will be shown in the profile directory.
discoverableProp := streams.NewTootDiscoverableProperty()
discoverableProp.Set(*a.Discoverable)
- person.SetTootDiscoverable(discoverableProp)
+ accountable.SetTootDiscoverable(discoverableProp)
// devices
// NOT IMPLEMENTED, probably won't implement
@@ -189,7 +201,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
alsoKnownAsURIs[i] = uri
}
- ap.SetAlsoKnownAs(person, alsoKnownAsURIs)
+ ap.SetAlsoKnownAs(accountable, alsoKnownAsURIs)
}
// movedTo
@@ -200,7 +212,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
return nil, err
}
- ap.SetMovedTo(person, movedTo)
+ ap.SetMovedTo(accountable, movedTo)
}
// publicKey
@@ -241,7 +253,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
// set the public key property on the Person
- person.SetW3IDSecurityV1PublicKey(publicKeyProp)
+ accountable.SetW3IDSecurityV1PublicKey(publicKeyProp)
// tags
tagProp := streams.NewActivityStreamsTagProperty()
@@ -269,7 +281,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
// tag -- hashtags
// TODO
- person.SetActivityStreamsTag(tagProp)
+ accountable.SetActivityStreamsTag(tagProp)
// attachment
// Used for profile fields.
@@ -290,7 +302,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
attachmentProp.AppendSchemaPropertyValue(propertyValue)
}
- person.SetActivityStreamsAttachment(attachmentProp)
+ accountable.SetActivityStreamsAttachment(attachmentProp)
}
// endpoints
@@ -326,7 +338,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
iconImage.SetActivityStreamsUrl(avatarURLProperty)
iconProperty.AppendActivityStreamsImage(iconImage)
- person.SetActivityStreamsIcon(iconProperty)
+ accountable.SetActivityStreamsIcon(iconProperty)
}
}
@@ -360,20 +372,32 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
headerImage.SetActivityStreamsUrl(headerURLProperty)
headerProperty.AppendActivityStreamsImage(headerImage)
- person.SetActivityStreamsImage(headerProperty)
+ accountable.SetActivityStreamsImage(headerProperty)
}
}
- return person, nil
+ return accountable, nil
}
-// AccountToASMinimal converts a gts model account into an activity streams person, suitable for federation.
+// AccountToASMinimal converts a gts model account
+// into an activity streams person or service.
//
-// The returned account will just have the Type, Username, PublicKey, and ID properties set. This is
-// suitable for serving to requesters to whom we want to give as little information as possible because
-// we don't trust them (yet).
-func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) {
- person := streams.NewActivityStreamsPerson()
+// The returned account will just have the Type, Username,
+// PublicKey, and ID properties set. This is suitable for
+// serving to requesters to whom we want to give as little
+// information as possible because we don't trust them (yet).
+func (c *Converter) AccountToASMinimal(
+ ctx context.Context,
+ a *gtsmodel.Account,
+) (ap.Accountable, error) {
+ // accountable is a service if this
+ // is a bot account, otherwise a person.
+ var accountable ap.Accountable
+ if util.PtrOrZero(a.Bot) {
+ accountable = streams.NewActivityStreamsService()
+ } else {
+ accountable = streams.NewActivityStreamsPerson()
+ }
// id should be the activitypub URI of this user
// something like https://example.org/users/example_user
@@ -383,13 +407,13 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account)
}
idProp := streams.NewJSONLDIdProperty()
idProp.SetIRI(profileIDURI)
- person.SetJSONLDId(idProp)
+ accountable.SetJSONLDId(idProp)
// preferredUsername
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
preferredUsernameProp.SetXMLSchemaString(a.Username)
- person.SetActivityStreamsPreferredUsername(preferredUsernameProp)
+ accountable.SetActivityStreamsPreferredUsername(preferredUsernameProp)
// publicKey
// Required for signatures.
@@ -429,9 +453,9 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account)
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
// set the public key property on the Person
- person.SetW3IDSecurityV1PublicKey(publicKeyProp)
+ accountable.SetW3IDSecurityV1PublicKey(publicKeyProp)
- return person, nil
+ return accountable, nil
}
// StatusToAS converts a gts model status into an ActivityStreams Statusable implementation, suitable for federation
diff --git a/internal/typeutils/internaltoas_test.go b/internal/typeutils/internaltoas_test.go
index 344a42798..4d0d95641 100644
--- a/internal/typeutils/internaltoas_test.go
+++ b/internal/typeutils/internaltoas_test.go
@@ -27,6 +27,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -38,10 +39,10 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
testAccount := &gtsmodel.Account{}
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
- asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
- ser, err := ap.Serialize(asPerson)
+ ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -94,14 +95,80 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
}`, string(bytes))
}
+func (suite *InternalToASTestSuite) TestAccountToASBot() {
+ testAccount := &gtsmodel.Account{}
+ *testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
+
+ // Update zork to be a bot.
+ testAccount.Bot = util.Ptr(true)
+ if err := suite.state.DB.UpdateAccount(context.Background(), testAccount); err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ suite.NoError(err)
+
+ ser, err := ap.Serialize(accountable)
+ suite.NoError(err)
+
+ bytes, err := json.MarshalIndent(ser, "", " ")
+ suite.NoError(err)
+
+ suite.Equal(`{
+ "@context": [
+ "https://w3id.org/security/v1",
+ "https://www.w3.org/ns/activitystreams",
+ {
+ "discoverable": "toot:discoverable",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "toot": "http://joinmastodon.org/ns#"
+ }
+ ],
+ "discoverable": true,
+ "featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
+ "followers": "http://localhost:8080/users/the_mighty_zork/followers",
+ "following": "http://localhost:8080/users/the_mighty_zork/following",
+ "icon": {
+ "mediaType": "image/jpeg",
+ "type": "Image",
+ "url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpg"
+ },
+ "id": "http://localhost:8080/users/the_mighty_zork",
+ "image": {
+ "mediaType": "image/jpeg",
+ "type": "Image",
+ "url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg"
+ },
+ "inbox": "http://localhost:8080/users/the_mighty_zork/inbox",
+ "manuallyApprovesFollowers": false,
+ "name": "original zork (he/they)",
+ "outbox": "http://localhost:8080/users/the_mighty_zork/outbox",
+ "preferredUsername": "the_mighty_zork",
+ "publicKey": {
+ "id": "http://localhost:8080/users/the_mighty_zork/main-key",
+ "owner": "http://localhost:8080/users/the_mighty_zork",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "published": "2022-05-20T11:09:18Z",
+ "summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
+ "tag": [],
+ "type": "Service",
+ "url": "http://localhost:8080/@the_mighty_zork"
+}`, string(bytes))
+}
+
func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
testAccount := &gtsmodel.Account{}
*testAccount = *suite.testAccounts["local_account_2"]
- asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
- ser, err := ap.Serialize(asPerson)
+ ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -176,10 +243,10 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
suite.FailNow(err.Error())
}
- asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
- ser, err := ap.Serialize(asPerson)
+ ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -246,10 +313,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
*testAccount = *suite.testAccounts["local_account_2"]
testAccount.Fields = testAccount.Fields[0:1] // Take only one field.
- asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
- ser, err := ap.Serialize(asPerson)
+ ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -308,10 +375,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
testAccount.Emojis = []*gtsmodel.Emoji{suite.testEmojis["rainbow"]}
- asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
- ser, err := ap.Serialize(asPerson)
+ ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@@ -381,10 +448,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
sharedInbox := "http://localhost:8080/sharedInbox"
testAccount.SharedInboxURI = &sharedInbox
- asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
- ser, err := ap.Serialize(asPerson)
+ ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
diff --git a/internal/typeutils/wrap.go b/internal/typeutils/wrap.go
index 89bcdfc09..1230981d4 100644
--- a/internal/typeutils/wrap.go
+++ b/internal/typeutils/wrap.go
@@ -18,68 +18,45 @@
package typeutils
import (
- "net/url"
-
- "github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
"github.com/superseriousbusiness/gotosocial/internal/ap"
- "github.com/superseriousbusiness/gotosocial/internal/gtserror"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
+ "github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
-// WrapPersonInUpdate ...
-func (c *Converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, originAccount *gtsmodel.Account) (vocab.ActivityStreamsUpdate, error) {
+// WrapAccountableInUpdate wraps the given accountable
+// in an Update activity with the accountable as the object.
+//
+// The Update will be addressed to Public and bcc followers.
+func (c *Converter) WrapAccountableInUpdate(accountable ap.Accountable) (vocab.ActivityStreamsUpdate, error) {
update := streams.NewActivityStreamsUpdate()
- // set the actor
- actorURI, err := url.Parse(originAccount.URI)
- if err != nil {
- return nil, gtserror.Newf("error parsing url %s: %w", originAccount.URI, err)
- }
- actorProp := streams.NewActivityStreamsActorProperty()
- actorProp.AppendIRI(actorURI)
- update.SetActivityStreamsActor(actorProp)
+ // Set actor IRI to this accountable's IRI.
+ ap.AppendActorIRIs(update, ap.GetJSONLDId(accountable))
- // set the ID
- newID, err := id.NewRandomULID()
- if err != nil {
- return nil, err
- }
+ // Set the update ID
+ updateURI := uris.GenerateURIForUpdate(ap.ExtractPreferredUsername(accountable), id.NewULID())
+ ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(update), updateURI)
- idString := uris.GenerateURIForUpdate(originAccount.Username, newID)
- idURI, err := url.Parse(idString)
- if err != nil {
- return nil, gtserror.Newf("error parsing url %s: %w", idString, err)
- }
- idProp := streams.NewJSONLDIdProperty()
- idProp.SetIRI(idURI)
- update.SetJSONLDId(idProp)
-
- // set the person as the object here
+ // Set the accountable as the object of the update.
objectProp := streams.NewActivityStreamsObjectProperty()
- objectProp.AppendActivityStreamsPerson(person)
+ switch t := accountable.(type) {
+ case vocab.ActivityStreamsPerson:
+ objectProp.AppendActivityStreamsPerson(t)
+ case vocab.ActivityStreamsService:
+ objectProp.AppendActivityStreamsService(t)
+ default:
+ log.Panicf(nil, "%T was neither person nor service", t)
+ }
update.SetActivityStreamsObject(objectProp)
- // to should be public
- toURI, err := url.Parse(pub.PublicActivityPubIRI)
- if err != nil {
- return nil, gtserror.Newf("error parsing url %s: %w", pub.PublicActivityPubIRI, err)
- }
- toProp := streams.NewActivityStreamsToProperty()
- toProp.AppendIRI(toURI)
- update.SetActivityStreamsTo(toProp)
+ // to should be public.
+ ap.AppendTo(update, ap.PublicURI())
- // bcc followers
- followersURI, err := url.Parse(originAccount.FollowersURI)
- if err != nil {
- return nil, gtserror.Newf("error parsing url %s: %w", originAccount.FollowersURI, err)
- }
- bccProp := streams.NewActivityStreamsBccProperty()
- bccProp.AppendIRI(followersURI)
- update.SetActivityStreamsBcc(bccProp)
+ // bcc should be followers.
+ ap.AppendBcc(update, ap.GetFollowers(accountable))
return update, nil
}
diff --git a/internal/typeutils/wrap_test.go b/internal/typeutils/wrap_test.go
index 1085c8c66..8c8af7506 100644
--- a/internal/typeutils/wrap_test.go
+++ b/internal/typeutils/wrap_test.go
@@ -139,6 +139,86 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() {
}`, string(bytes))
}
+func (suite *WrapTestSuite) TestWrapAccountableInUpdate() {
+ testAccount := suite.testAccounts["local_account_1"]
+
+ accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ create, err := suite.typeconverter.WrapAccountableInUpdate(accountable)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ createI, err := ap.Serialize(create)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ // Get the ID as it's not determinate.
+ createID := ap.GetJSONLDId(create)
+
+ bytes, err := json.MarshalIndent(createI, "", " ")
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ suite.Equal(`{
+ "@context": [
+ "https://w3id.org/security/v1",
+ "https://www.w3.org/ns/activitystreams",
+ {
+ "discoverable": "toot:discoverable",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "toot": "http://joinmastodon.org/ns#"
+ }
+ ],
+ "actor": "http://localhost:8080/users/the_mighty_zork",
+ "bcc": "http://localhost:8080/users/the_mighty_zork/followers",
+ "id": "`+createID.String()+`",
+ "object": {
+ "discoverable": true,
+ "featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
+ "followers": "http://localhost:8080/users/the_mighty_zork/followers",
+ "following": "http://localhost:8080/users/the_mighty_zork/following",
+ "icon": {
+ "mediaType": "image/jpeg",
+ "type": "Image",
+ "url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpg"
+ },
+ "id": "http://localhost:8080/users/the_mighty_zork",
+ "image": {
+ "mediaType": "image/jpeg",
+ "type": "Image",
+ "url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg"
+ },
+ "inbox": "http://localhost:8080/users/the_mighty_zork/inbox",
+ "manuallyApprovesFollowers": false,
+ "name": "original zork (he/they)",
+ "outbox": "http://localhost:8080/users/the_mighty_zork/outbox",
+ "preferredUsername": "the_mighty_zork",
+ "publicKey": {
+ "id": "http://localhost:8080/users/the_mighty_zork/main-key",
+ "owner": "http://localhost:8080/users/the_mighty_zork",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "published": "2022-05-20T11:09:18Z",
+ "summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
+ "tag": [],
+ "type": "Person",
+ "url": "http://localhost:8080/@the_mighty_zork"
+ },
+ "to": "https://www.w3.org/ns/activitystreams#Public",
+ "type": "Update"
+}`, string(bytes))
+}
+
func TestWrapTestSuite(t *testing.T) {
suite.Run(t, new(WrapTestSuite))
}