diff options
Diffstat (limited to 'internal/ap')
| -rw-r--r-- | internal/ap/activitystreams.go | 29 | ||||
| -rw-r--r-- | internal/ap/ap_test.go | 10 | ||||
| -rw-r--r-- | internal/ap/extract.go | 28 | ||||
| -rw-r--r-- | internal/ap/interfaces.go | 115 | ||||
| -rw-r--r-- | internal/ap/properties.go | 179 | ||||
| -rw-r--r-- | internal/ap/serialize.go | 4 |
6 files changed, 320 insertions, 45 deletions
diff --git a/internal/ap/activitystreams.go b/internal/ap/activitystreams.go index 56ebc4909..9c2464410 100644 --- a/internal/ap/activitystreams.go +++ b/internal/ap/activitystreams.go @@ -23,15 +23,18 @@ import ( "code.superseriousbusiness.org/activity/pub" ) -// PublicURI returns a fresh copy of the *url.URL version of the -// magic ActivityPub URI https://www.w3.org/ns/activitystreams#Public -func PublicURI() *url.URL { - publicURI, err := url.Parse(pub.PublicActivityPubIRI) +// publicIRI is a pre-parsed global public IRI instance. +var publicIRI = func() *url.URL { + url, err := url.Parse(pub.PublicActivityPubIRI) if err != nil { panic(err) } - return publicURI -} + return url +}() + +// PublicIRI returns a fresh copy of the *url.URL version of the +// magic ActivityPub URI https://www.w3.org/ns/activitystreams#Public +func PublicIRI() *url.URL { var u url.URL; u = *publicIRI; return &u } // https://www.w3.org/TR/activitystreams-vocabulary const ( @@ -102,9 +105,12 @@ const ( /* GtS stuff */ - ObjectLikeApproval = "LikeApproval" - ObjectReplyApproval = "ReplyApproval" - ObjectAnnounceApproval = "AnnounceApproval" + ActivityLikeRequest = "LikeRequest" + ActivityReplyRequest = "ReplyRequest" + ActivityAnnounceRequest = "AnnounceRequest" + ObjectLikeAuthorization = "LikeAuthorization" + ObjectReplyAuthorization = "ReplyAuthorization" + ObjectAnnounceAuthorization = "AnnounceAuthorization" /* Funkwhale stuff */ @@ -138,7 +144,10 @@ func isActivity(typeName string) bool { ActivityAnnounce, ActivityBlock, ActivityFlag, - ActivityDislike: + ActivityDislike, + ActivityLikeRequest, + ActivityReplyRequest, + ActivityAnnounceRequest: return true default: return false diff --git a/internal/ap/ap_test.go b/internal/ap/ap_test.go index 0956fee66..9f275434d 100644 --- a/internal/ap/ap_test.go +++ b/internal/ap/ap_test.go @@ -110,7 +110,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote { // Anyone can like. canLikeAlwaysProp := streams.NewGoToSocialAlwaysProperty() - canLikeAlwaysProp.AppendIRI(ap.PublicURI()) + canLikeAlwaysProp.AppendIRI(ap.PublicIRI()) canLike.SetGoToSocialAlways(canLikeAlwaysProp) // Empty approvalRequired. @@ -127,7 +127,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote { // Anyone can reply. canReplyAlwaysProp := streams.NewGoToSocialAlwaysProperty() - canReplyAlwaysProp.AppendIRI(ap.PublicURI()) + canReplyAlwaysProp.AppendIRI(ap.PublicIRI()) canReply.SetGoToSocialAlways(canReplyAlwaysProp) // Set empty approvalRequired. @@ -150,7 +150,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote { // Public requires approval to announce. canAnnounceApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty() - canAnnounceApprovalRequiredProp.AppendIRI(ap.PublicURI()) + canAnnounceApprovalRequiredProp.AppendIRI(ap.PublicIRI()) canAnnounce.SetGoToSocialApprovalRequired(canAnnounceApprovalRequiredProp) // Set canAnnounce on the policy. @@ -265,7 +265,7 @@ func addressable1() ap.Addressable { note := streams.NewActivityStreamsNote() toProp := streams.NewActivityStreamsToProperty() - toProp.AppendIRI(ap.PublicURI()) + toProp.AppendIRI(ap.PublicIRI()) note.SetActivityStreamsTo(toProp) @@ -287,7 +287,7 @@ func addressable2() ap.Addressable { note.SetActivityStreamsTo(toProp) ccProp := streams.NewActivityStreamsCcProperty() - ccProp.AppendIRI(ap.PublicURI()) + ccProp.AppendIRI(ap.PublicIRI()) note.SetActivityStreamsCc(ccProp) diff --git a/internal/ap/extract.go b/internal/ap/extract.go index 7a727b005..f26b02a82 100644 --- a/internal/ap/extract.go +++ b/internal/ap/extract.go @@ -36,7 +36,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/util" ) -// ExtractObjects will extract object vocab.Types from given implementing interface. +// ExtractObjects will extract object TypeOrIRIs from given implementing interface. func ExtractObjects(with WithObject) []TypeOrIRI { // Extract the attached object (if any). objProp := with.GetActivityStreamsObject() @@ -58,6 +58,28 @@ func ExtractObjects(with WithObject) []TypeOrIRI { return objs } +// ExtractInstrument will extract instrument TypeOrIRIs from given implementing interface. +func ExtractInstruments(with WithInstrument) []TypeOrIRI { + // Extract the attached instrument (if any). + instrProp := with.GetActivityStreamsInstrument() + if instrProp == nil { + return nil + } + + // Check for invalid len. + if instrProp.Len() == 0 { + return nil + } + + // Accumulate all of the instruments into a slice. + instrs := make([]TypeOrIRI, instrProp.Len()) + for i := range instrProp.Len() { + instrs[i] = instrProp.At(i) + } + + return instrs +} + // ExtractActivityData will extract the usable data type (e.g. Note, Question, etc) and corresponding JSON, from activity. func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) ([]TypeOrIRI, []any, bool) { switch typeName := activity.GetTypeName(); { @@ -1222,14 +1244,14 @@ func ExtractInteractionPolicy( statusable Statusable, owner *gtsmodel.Account, ) *gtsmodel.InteractionPolicy { - ipa, ok := statusable.(InteractionPolicyAware) + wip, ok := statusable.(WithInteractionPolicy) if !ok { // Not a type with interaction // policy properties settable. return nil } - policyProp := ipa.GetGoToSocialInteractionPolicy() + policyProp := wip.GetGoToSocialInteractionPolicy() if policyProp == nil || policyProp.Len() != 1 { return nil } diff --git a/internal/ap/interfaces.go b/internal/ap/interfaces.go index ec961f80b..3224cecc0 100644 --- a/internal/ap/interfaces.go +++ b/internal/ap/interfaces.go @@ -143,13 +143,13 @@ func ToAcceptable(t vocab.Type) (Acceptable, bool) { return acceptable, true } -// IsApprovable returns whether AS vocab type name -// is something that can be cast to Approvable. -func IsApprovable(typeName string) bool { +// IsAuthorizationable returns whether AS vocab type name +// is something that can be cast to Authorizationable. +func IsAuthorizationable(typeName string) bool { switch typeName { - case ObjectLikeApproval, - ObjectReplyApproval, - ObjectAnnounceApproval: + case ObjectLikeAuthorization, + ObjectReplyAuthorization, + ObjectAnnounceAuthorization: return true default: return false @@ -157,12 +157,12 @@ func IsApprovable(typeName string) bool { } // ToAcceptable safely tries to cast vocab.Type as Approvable. -func ToApprovable(t vocab.Type) (Approvable, bool) { - approvable, ok := t.(Approvable) - if !ok || !IsApprovable(t.GetTypeName()) { +func ToAuthorizationable(t vocab.Type) (Authorizationable, bool) { + authable, ok := t.(Authorizationable) + if !ok || !IsAuthorizationable(t.GetTypeName()) { return nil, false } - return approvable, true + return authable, true } // IsAttachmentable returns whether AS vocab type name @@ -188,6 +188,36 @@ func ToAttachmentable(t vocab.Type) (Attachmentable, bool) { return attachmentable, true } +// IsAnnounceable returns whether AS vocab type name +// is something that can be cast to vocab.ActivityStreamsAnnounce. +func IsAnnounceable(typeName string) bool { + return typeName == ActivityAnnounce +} + +// ToAnnounceable safely tries to cast vocab.Type as vocab.ActivityStreamsAnnounce. +func ToAnnounceable(t vocab.Type) (vocab.ActivityStreamsAnnounce, bool) { + announceable, ok := t.(vocab.ActivityStreamsAnnounce) + if !ok || t.GetTypeName() != ActivityAnnounce { + return nil, false + } + return announceable, true +} + +// IsLikeable returns whether AS vocab type name +// is something that can be cast to vocab.ActivityStreamsLike. +func IsLikeable(typeName string) bool { + return typeName == ActivityLike +} + +// ToAnnouncToLikeableeable safely tries to cast vocab.Type as vocab.ActivityStreamsLike. +func ToLikeable(t vocab.Type) (vocab.ActivityStreamsLike, bool) { + likeable, ok := t.(vocab.ActivityStreamsLike) + if !ok || t.GetTypeName() != ActivityLike { + return nil, false + } + return likeable, true +} + // Activityable represents the minimum activitypub interface for representing an 'activity'. // (see: IsActivityable() for types implementing this, though you MUST make sure to check // the typeName as this bare interface may be implementable by non-Activityable types). @@ -258,11 +288,6 @@ type Statusable interface { WithReplies } -type InteractionPolicyAware interface { - WithInteractionPolicy - WithApprovedBy -} - // Pollable represents the minimum activitypub interface for representing a 'poll' (it's a subset of a status). // (see: IsPollable() for types implementing this, though you MUST make sure to check // the typeName as this bare interface may be implementable by non-Pollable types). @@ -299,14 +324,14 @@ type Acceptable interface { WithResult } -// Approvable represents the minimum activitypub interface -// for a LikeApproval, ReplyApproval, or AnnounceApproval. -type Approvable interface { +// Authorizationable represents the minimum interface for a +// LikeAuthorization, ReplyAuthorization, AnnounceAuthorization. +type Authorizationable interface { vocab.Type WithAttributedTo - WithObject - WithTarget + WithInteractingObject + WithInteractionTarget } // Attachmentable represents the minimum activitypub interface for representing a 'mediaAttachment'. (see: IsAttachmentable). @@ -392,6 +417,16 @@ type ReplyToable interface { WithInReplyTo } +// InteractionRequestable represents the minimum interface for an interaction request +// activity, eg., LikeRequest, ReplyRequest, AnnounceRequest, QuoteRequest, etc.. +type InteractionRequestable interface { + vocab.Type + + WithActor + WithObject + WithInstrument +} + // CollectionIterator represents the minimum interface for interacting with a // wrapped Collection or OrderedCollection in order to access next / prev items. type CollectionIterator interface { @@ -683,6 +718,12 @@ type WithObject interface { SetActivityStreamsObject(vocab.ActivityStreamsObjectProperty) } +// WithInstrument represents an activity with ActivityStreamsInstrumentProperty +type WithInstrument interface { + GetActivityStreamsInstrument() vocab.ActivityStreamsInstrumentProperty + SetActivityStreamsInstrument(vocab.ActivityStreamsInstrumentProperty) +} + // WithTarget represents an activity with ActivityStreamsTargetProperty type WithTarget interface { GetActivityStreamsTarget() vocab.ActivityStreamsTargetProperty @@ -775,14 +816,44 @@ type WithPolicyRules interface { GetGoToSocialApprovalRequired() vocab.GoToSocialApprovalRequiredProperty // Deprecated } -// WithApprovedBy represents a Statusable with the approvedBy property. +// WithApprovedBy represents an object with the approvedBy property. type WithApprovedBy interface { GetGoToSocialApprovedBy() vocab.GoToSocialApprovedByProperty SetGoToSocialApprovedBy(vocab.GoToSocialApprovedByProperty) } -// WithVotersCount represents an activity or object the result property. +// WithLikeAuthorization represents a Likeable with the likeAuthorization property. +type WithLikeAuthorization interface { + GetGoToSocialLikeAuthorization() vocab.GoToSocialLikeAuthorizationProperty + SetGoToSocialLikeAuthorization(vocab.GoToSocialLikeAuthorizationProperty) +} + +// WithReplyAuthorization represents a statusable with the replyAuthorization property. +type WithReplyAuthorization interface { + GetGoToSocialReplyAuthorization() vocab.GoToSocialReplyAuthorizationProperty + SetGoToSocialReplyAuthorization(vocab.GoToSocialReplyAuthorizationProperty) +} + +// WithAnnounceAuthorization represents an Announceable with the announceAuthorization property. +type WithAnnounceAuthorization interface { + GetGoToSocialAnnounceAuthorization() vocab.GoToSocialAnnounceAuthorizationProperty + SetGoToSocialAnnounceAuthorization(vocab.GoToSocialAnnounceAuthorizationProperty) +} + +// WithResult represents an activity or object with the result property. type WithResult interface { GetActivityStreamsResult() vocab.ActivityStreamsResultProperty SetActivityStreamsResult(vocab.ActivityStreamsResultProperty) } + +// WithInteractingObject represents an activity or object with the InteractingObject property. +type WithInteractingObject interface { + GetGoToSocialInteractingObject() vocab.GoToSocialInteractingObjectProperty + SetGoToSocialInteractingObject(vocab.GoToSocialInteractingObjectProperty) +} + +// WithInteractionTarget represents an activity or object with the InteractionTarget property. +type WithInteractionTarget interface { + GetGoToSocialInteractionTarget() vocab.GoToSocialInteractionTargetProperty + SetGoToSocialInteractionTarget(vocab.GoToSocialInteractionTargetProperty) +} diff --git a/internal/ap/properties.go b/internal/ap/properties.go index 3e064bae0..51e4ab1c0 100644 --- a/internal/ap/properties.go +++ b/internal/ap/properties.go @@ -226,6 +226,36 @@ func AppendObjectIRIs(with WithObject, object ...*url.URL) { }, object...) } +// AppendInstrumentIRIs appends the given IRIs to the Instrument property of 'with'. +func AppendInstrumentIRIs(with WithInstrument, instrument ...*url.URL) { + appendIRIs(func() Property[vocab.ActivityStreamsInstrumentPropertyIterator] { + instrumentProp := with.GetActivityStreamsInstrument() + if instrumentProp == nil { + instrumentProp = streams.NewActivityStreamsInstrumentProperty() + with.SetActivityStreamsInstrument(instrumentProp) + } + return instrumentProp + }, instrument...) +} + +// GetResultIRIs returns the IRIs contained in the `result` property of 'with'. +func GetResultIRIs(with WithResult) []*url.URL { + resultProp := with.GetActivityStreamsResult() + return extractIRIs(resultProp) +} + +// AppendResultIRIs appends the given IRIs to the Result property of 'with'. +func AppendResultIRIs(with WithResult, result ...*url.URL) { + appendIRIs(func() Property[vocab.ActivityStreamsResultPropertyIterator] { + resultProp := with.GetActivityStreamsResult() + if resultProp == nil { + resultProp = streams.NewActivityStreamsResultProperty() + with.SetActivityStreamsResult(resultProp) + } + return resultProp + }, result...) +} + // GetTargetIRIs returns the IRIs contained in the Target property of 'with'. func GetTargetIRIs(with WithTarget) []*url.URL { targetProp := with.GetActivityStreamsTarget() @@ -262,6 +292,42 @@ func AppendAttributedTo(with WithAttributedTo, attribTo ...*url.URL) { }, attribTo...) } +// GetInteractingObject returns IRIs contained in the interactingObject property of 'with'. +func GetInteractingObject(with WithInteractingObject) []*url.URL { + intObjProp := with.GetGoToSocialInteractingObject() + return getIRIs(intObjProp) +} + +// AppendInteractingObject appends the given IRIs to the interactingObject property of 'with'. +func AppendInteractingObject(with WithInteractingObject, interactingObject ...*url.URL) { + appendIRIs(func() Property[vocab.GoToSocialInteractingObjectPropertyIterator] { + intObjProp := with.GetGoToSocialInteractingObject() + if intObjProp == nil { + intObjProp = streams.NewGoToSocialInteractingObjectProperty() + with.SetGoToSocialInteractingObject(intObjProp) + } + return intObjProp + }, interactingObject...) +} + +// GetInteractionTarget returns IRIs contained in the interactionTarget property of 'with'. +func GetInteractionTarget(with WithInteractionTarget) []*url.URL { + intTargetProp := with.GetGoToSocialInteractionTarget() + return getIRIs(intTargetProp) +} + +// AppendInteractionTarget appends the given IRIs to the interactionTarget property of 'with'. +func AppendInteractionTarget(with WithInteractionTarget, interactionTarget ...*url.URL) { + appendIRIs(func() Property[vocab.GoToSocialInteractionTargetPropertyIterator] { + intTargetProp := with.GetGoToSocialInteractionTarget() + if intTargetProp == nil { + intTargetProp = streams.NewGoToSocialInteractionTargetProperty() + with.SetGoToSocialInteractionTarget(intTargetProp) + } + return intTargetProp + }, interactionTarget...) +} + // GetInReplyTo returns the IRIs contained in the InReplyTo property of 'with'. func GetInReplyTo(with WithInReplyTo) []*url.URL { replyProp := with.GetActivityStreamsInReplyTo() @@ -607,11 +673,11 @@ func SetHidesCcPublicFromUnauthedWeb(with WithHidesCcPublicFromUnauthedWeb, hide // GetApprovedBy returns the URL contained in // the ApprovedBy property of 'with', if set. func GetApprovedBy(with WithApprovedBy) *url.URL { - mafProp := with.GetGoToSocialApprovedBy() - if mafProp == nil || !mafProp.IsIRI() { + abProp := with.GetGoToSocialApprovedBy() + if abProp == nil || !abProp.IsIRI() { return nil } - return mafProp.Get() + return abProp.Get() } // SetApprovedBy sets the given url @@ -625,6 +691,69 @@ func SetApprovedBy(with WithApprovedBy, approvedBy *url.URL) { abProp.Set(approvedBy) } +// GetLikeAuthorization returns the URL contained in +// the likeAuthorization property of 'with', if set. +func GetLikeAuthorization(with WithLikeAuthorization) *url.URL { + laProp := with.GetGoToSocialLikeAuthorization() + if laProp == nil || !laProp.IsIRI() { + return nil + } + return laProp.Get() +} + +// SetLikeAuthorization sets the given url on +// the 'likeAuthorization' property of 'with'. +func SetLikeAuthorization(with WithLikeAuthorization, likeAuthorization *url.URL) { + laProp := with.GetGoToSocialLikeAuthorization() + if laProp == nil { + laProp = streams.NewGoToSocialLikeAuthorizationProperty() + with.SetGoToSocialLikeAuthorization(laProp) + } + laProp.Set(likeAuthorization) +} + +// GetReplyAuthorization returns the URL contained in +// the replyAuthorization property of 'with', if set. +func GetReplyAuthorization(with WithReplyAuthorization) *url.URL { + raProp := with.GetGoToSocialReplyAuthorization() + if raProp == nil || !raProp.IsIRI() { + return nil + } + return raProp.Get() +} + +// SetReplyAuthorization sets the given url on +// the 'replyAuthorization' property of 'with'. +func SetReplyAuthorization(with WithReplyAuthorization, replyAuthorization *url.URL) { + raProp := with.GetGoToSocialReplyAuthorization() + if raProp == nil { + raProp = streams.NewGoToSocialReplyAuthorizationProperty() + with.SetGoToSocialReplyAuthorization(raProp) + } + raProp.Set(replyAuthorization) +} + +// GetAnnounceAuthorization returns the URL contained in +// the announceAuthorization property of 'with', if set. +func GetAnnounceAuthorization(with WithAnnounceAuthorization) *url.URL { + aaProp := with.GetGoToSocialAnnounceAuthorization() + if aaProp == nil || !aaProp.IsIRI() { + return nil + } + return aaProp.Get() +} + +// SetAnnounceAuthorization sets the given url on +// the 'announceAuthorization' property of 'with'. +func SetAnnounceAuthorization(with WithAnnounceAuthorization, announceAuthorization *url.URL) { + aaProp := with.GetGoToSocialAnnounceAuthorization() + if aaProp == nil { + aaProp = streams.NewGoToSocialAnnounceAuthorizationProperty() + with.SetGoToSocialAnnounceAuthorization(aaProp) + } + aaProp.Set(announceAuthorization) +} + // GetMediaType returns the string contained in // the MediaType property of 'with', if set. func GetMediaType(with WithMediaType) string { @@ -689,6 +818,50 @@ func SetBlurhash(with WithBlurhash, mediaType string) { bProp.Set(mediaType) } +// AppendSensitive appends the given sensitive +// boolean to the `sensitive` property of 'with'. +func AppendSensitive(with WithSensitive, sensitive bool) { + sProp := with.GetActivityStreamsSensitive() + if sProp == nil { + sProp = streams.NewActivityStreamsSensitiveProperty() + with.SetActivityStreamsSensitive(sProp) + } + sProp.AppendXMLSchemaBoolean(sensitive) +} + +// AppendContent appends the given content +// string to the `content` property of 'with'. +func AppendContent(with WithContent, content string) { + cProp := with.GetActivityStreamsContent() + if cProp == nil { + cProp = streams.NewActivityStreamsContentProperty() + with.SetActivityStreamsContent(cProp) + } + cProp.AppendXMLSchemaString(content) +} + +// AppendContentMap appends the given content +// language map to the `content` property of 'with'. +func AppendContentMap(with WithContent, contentMap map[string]string) { + cProp := with.GetActivityStreamsContent() + if cProp == nil { + cProp = streams.NewActivityStreamsContentProperty() + with.SetActivityStreamsContent(cProp) + } + cProp.AppendRDFLangString(contentMap) +} + +// SetReplies sets the given replies collection +// to the `replies` property of 'with'. +func SetReplies(with WithReplies, replies vocab.ActivityStreamsCollection) { + rProp := with.GetActivityStreamsReplies() + if rProp == nil { + rProp = streams.NewActivityStreamsRepliesProperty() + with.SetActivityStreamsReplies(rProp) + } + rProp.SetActivityStreamsCollection(replies) +} + // extractIRIs extracts just the AP IRIs from an iterable // property that may contain types (with IRIs) or just IRIs. // diff --git a/internal/ap/serialize.go b/internal/ap/serialize.go index c64c14d75..10af16ea4 100644 --- a/internal/ap/serialize.go +++ b/internal/ap/serialize.go @@ -153,8 +153,8 @@ func serializeStatusable(t vocab.Type, includeContext bool) (map[string]interfac NormalizeOutgoingAttachmentProp(statusable, data) NormalizeOutgoingContentProp(statusable, data) - if ipa, ok := statusable.(InteractionPolicyAware); ok { - NormalizeOutgoingInteractionPolicyProp(ipa, data) + if wip, ok := statusable.(WithInteractionPolicy); ok { + NormalizeOutgoingInteractionPolicyProp(wip, data) } return data, nil |
