diff options
Diffstat (limited to 'internal/typeutils')
-rw-r--r-- | internal/typeutils/astointernal.go | 50 | ||||
-rw-r--r-- | internal/typeutils/internaltoas.go | 24 | ||||
-rw-r--r-- | internal/typeutils/wrap.go | 83 | ||||
-rw-r--r-- | internal/typeutils/wrap_test.go | 4 |
4 files changed, 100 insertions, 61 deletions
diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go index 81dbc6f40..92465c790 100644 --- a/internal/typeutils/astointernal.go +++ b/internal/typeutils/astointernal.go @@ -216,40 +216,10 @@ func (c *Converter) ASRepresentationToAccount(ctx context.Context, accountable a return acct, nil } -func (c *Converter) extractAttachments(i ap.WithAttachment) []*gtsmodel.MediaAttachment { - attachmentProp := i.GetActivityStreamsAttachment() - if attachmentProp == nil { - return nil - } - - attachments := make([]*gtsmodel.MediaAttachment, 0, attachmentProp.Len()) - - for iter := attachmentProp.Begin(); iter != attachmentProp.End(); iter = iter.Next() { - t := iter.GetType() - if t == nil { - continue - } - - attachmentable, ok := t.(ap.Attachmentable) - if !ok { - log.Error(nil, "ap attachment was not attachmentable") - continue - } - - attachment, err := ap.ExtractAttachment(attachmentable) - if err != nil { - log.Errorf(nil, "error extracting attachment: %s", err) - continue - } - - attachments = append(attachments, attachment) - } - - return attachments -} - // ASStatus converts a remote activitystreams 'status' representation into a gts model status. func (c *Converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusable) (*gtsmodel.Status, error) { + var err error + status := new(gtsmodel.Status) // status.URI @@ -281,7 +251,19 @@ func (c *Converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab // status.Attachments // // Media attachments for later dereferencing. - status.Attachments = c.extractAttachments(statusable) + status.Attachments, err = ap.ExtractAttachments(statusable) + if err != nil { + l.Warnf("error(s) extracting attachments: %v", err) + } + + // status.Poll + // + // Attached poll information (the statusable will actually + // be a Pollable, as a Question is a subset of our Status). + if pollable, ok := ap.ToPollable(statusable); ok { + // TODO: handle decoding poll data + _ = pollable + } // status.Hashtags // @@ -341,7 +323,7 @@ func (c *Converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab // error if we don't. attributedTo, err := ap.ExtractAttributedToURI(statusable) if err != nil { - return nil, gtserror.Newf("%w", err) + return nil, gtserror.Newf("error extracting attributed to uri: %w", err) } accountURI := attributedTo.String() diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index d82fe8e04..b920d9a0e 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -29,6 +29,7 @@ import ( "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/config" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" @@ -403,21 +404,15 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) return person, nil } -// StatusToAS converts a gts model status into an activity streams note, suitable for federation -func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (vocab.ActivityStreamsNote, error) { - // ensure prerequisites here before we get stuck in - - // check if author account is already attached to status and attach it if not - // if we can't retrieve this, bail here already because we can't attribute the status to anyone - if s.Account == nil { - a, err := c.state.DB.GetAccountByID(ctx, s.AccountID) - if err != nil { - return nil, gtserror.Newf("error retrieving author account from db: %w", err) - } - s.Account = a +// StatusToAS converts a gts model status into an ActivityStreams Statusable implementation, suitable for federation +func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (ap.Statusable, error) { + // Ensure the status model is fully populated. + // The status and poll models are REQUIRED so nothing to do if this fails. + if err := c.state.DB.PopulateStatus(ctx, s); err != nil { + return nil, gtserror.Newf("error populating status: %w", err) } - // create the Note! + // We convert it as an AS Note. status := streams.NewActivityStreamsNote() // id @@ -529,7 +524,6 @@ func (c *Converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (vocab.A } tagProp.AppendTootHashtag(asHashtag) } - status.SetActivityStreamsTag(tagProp) // parse out some URIs we need here @@ -1419,7 +1413,7 @@ func (c *Converter) StatusesToASOutboxPage(ctx context.Context, outboxID string, return nil, err } - create, err := c.WrapNoteInCreate(note, true) + create, err := c.WrapStatusableInCreate(note, true) if err != nil { return nil, err } diff --git a/internal/typeutils/wrap.go b/internal/typeutils/wrap.go index 67d3c2b5c..128c4ef15 100644 --- a/internal/typeutils/wrap.go +++ b/internal/typeutils/wrap.go @@ -44,7 +44,6 @@ func (c *Converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, origi update.SetActivityStreamsActor(actorProp) // set the ID - newID, err := id.NewRandomULID() if err != nil { return nil, err @@ -85,26 +84,29 @@ func (c *Converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, origi return update, nil } -// WrapNoteInCreate wraps a Note with a Create activity. +// WrapNoteInCreate wraps a Statusable with a Create activity. // // If objectIRIOnly is set to true, then the function won't put the *entire* note in the Object field of the Create, // but just the AP URI of the note. This is useful in cases where you want to give a remote server something to dereference, // and still have control over whether or not they're allowed to actually see the contents. -func (c *Converter) WrapNoteInCreate(note vocab.ActivityStreamsNote, objectIRIOnly bool) (vocab.ActivityStreamsCreate, error) { +func (c *Converter) WrapStatusableInCreate(status ap.Statusable, objectIRIOnly bool) (vocab.ActivityStreamsCreate, error) { create := streams.NewActivityStreamsCreate() // Object property objectProp := streams.NewActivityStreamsObjectProperty() if objectIRIOnly { - objectProp.AppendIRI(note.GetJSONLDId().GetIRI()) + // Only append the object IRI to objectProp. + objectProp.AppendIRI(status.GetJSONLDId().GetIRI()) } else { - objectProp.AppendActivityStreamsNote(note) + // Our statusable's are always note types. + asNote := status.(vocab.ActivityStreamsNote) + objectProp.AppendActivityStreamsNote(asNote) } create.SetActivityStreamsObject(objectProp) // ID property idProp := streams.NewJSONLDIdProperty() - createID := note.GetJSONLDId().GetIRI().String() + "/activity" + createID := status.GetJSONLDId().GetIRI().String() + "/activity" createIDIRI, err := url.Parse(createID) if err != nil { return nil, err @@ -114,7 +116,7 @@ func (c *Converter) WrapNoteInCreate(note vocab.ActivityStreamsNote, objectIRIOn // Actor Property actorProp := streams.NewActivityStreamsActorProperty() - actorIRI, err := ap.ExtractAttributedToURI(note) + actorIRI, err := ap.ExtractAttributedToURI(status) if err != nil { return nil, gtserror.Newf("couldn't extract AttributedTo: %w", err) } @@ -123,7 +125,7 @@ func (c *Converter) WrapNoteInCreate(note vocab.ActivityStreamsNote, objectIRIOn // Published Property publishedProp := streams.NewActivityStreamsPublishedProperty() - published, err := ap.ExtractPublished(note) + published, err := ap.ExtractPublished(status) if err != nil { return nil, gtserror.Newf("couldn't extract Published: %w", err) } @@ -132,7 +134,7 @@ func (c *Converter) WrapNoteInCreate(note vocab.ActivityStreamsNote, objectIRIOn // To Property toProp := streams.NewActivityStreamsToProperty() - if toURIs := ap.ExtractToURIs(note); len(toURIs) != 0 { + if toURIs := ap.ExtractToURIs(status); len(toURIs) != 0 { for _, toURI := range toURIs { toProp.AppendIRI(toURI) } @@ -141,7 +143,7 @@ func (c *Converter) WrapNoteInCreate(note vocab.ActivityStreamsNote, objectIRIOn // Cc Property ccProp := streams.NewActivityStreamsCcProperty() - if ccURIs := ap.ExtractCcURIs(note); len(ccURIs) != 0 { + if ccURIs := ap.ExtractCcURIs(status); len(ccURIs) != 0 { for _, ccURI := range ccURIs { ccProp.AppendIRI(ccURI) } @@ -150,3 +152,64 @@ func (c *Converter) WrapNoteInCreate(note vocab.ActivityStreamsNote, objectIRIOn return create, nil } + +// WrapStatusableInUpdate wraps a Statusable with an Update activity. +// +// If objectIRIOnly is set to true, then the function won't put the *entire* note in the Object field of the Create, +// but just the AP URI of the note. This is useful in cases where you want to give a remote server something to dereference, +// and still have control over whether or not they're allowed to actually see the contents. +func (c *Converter) WrapStatusableInUpdate(status ap.Statusable, objectIRIOnly bool) (vocab.ActivityStreamsUpdate, error) { + update := streams.NewActivityStreamsUpdate() + + // Object property + objectProp := streams.NewActivityStreamsObjectProperty() + if objectIRIOnly { + objectProp.AppendIRI(status.GetJSONLDId().GetIRI()) + } else if _, ok := status.(ap.Pollable); ok { + asQuestion := status.(vocab.ActivityStreamsQuestion) + objectProp.AppendActivityStreamsQuestion(asQuestion) + } else { + asNote := status.(vocab.ActivityStreamsNote) + objectProp.AppendActivityStreamsNote(asNote) + } + update.SetActivityStreamsObject(objectProp) + + // ID property + idProp := streams.NewJSONLDIdProperty() + createID := status.GetJSONLDId().GetIRI().String() + "/activity" + createIDIRI, err := url.Parse(createID) + if err != nil { + return nil, err + } + idProp.SetIRI(createIDIRI) + update.SetJSONLDId(idProp) + + // Actor Property + actorProp := streams.NewActivityStreamsActorProperty() + actorIRI, err := ap.ExtractAttributedToURI(status) + if err != nil { + return nil, gtserror.Newf("couldn't extract AttributedTo: %w", err) + } + actorProp.AppendIRI(actorIRI) + update.SetActivityStreamsActor(actorProp) + + // To Property + toProp := streams.NewActivityStreamsToProperty() + if toURIs := ap.ExtractToURIs(status); len(toURIs) != 0 { + for _, toURI := range toURIs { + toProp.AppendIRI(toURI) + } + update.SetActivityStreamsTo(toProp) + } + + // Cc Property + ccProp := streams.NewActivityStreamsCcProperty() + if ccURIs := ap.ExtractCcURIs(status); len(ccURIs) != 0 { + for _, ccURI := range ccURIs { + ccProp.AppendIRI(ccURI) + } + update.SetActivityStreamsCc(ccProp) + } + + return update, nil +} diff --git a/internal/typeutils/wrap_test.go b/internal/typeutils/wrap_test.go index 46d28f5c4..51f67f455 100644 --- a/internal/typeutils/wrap_test.go +++ b/internal/typeutils/wrap_test.go @@ -36,7 +36,7 @@ func (suite *WrapTestSuite) TestWrapNoteInCreateIRIOnly() { note, err := suite.typeconverter.StatusToAS(context.Background(), testStatus) suite.NoError(err) - create, err := suite.typeconverter.WrapNoteInCreate(note, true) + create, err := suite.typeconverter.WrapStatusableInCreate(note, true) suite.NoError(err) suite.NotNil(create) @@ -64,7 +64,7 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() { note, err := suite.typeconverter.StatusToAS(context.Background(), testStatus) suite.NoError(err) - create, err := suite.typeconverter.WrapNoteInCreate(note, false) + create, err := suite.typeconverter.WrapStatusableInCreate(note, false) suite.NoError(err) suite.NotNil(create) |