diff options
author | 2021-08-10 13:32:39 +0200 | |
---|---|---|
committer | 2021-08-10 13:32:39 +0200 | |
commit | 0f2de6394a1c52d47e326bb7d7d129a217ae4f6f (patch) | |
tree | e2709bdbbbbcf5e12d6da62b653b67f1789ab1c5 /internal/ap | |
parent | Frodo swaggins (#126) (diff) | |
download | gotosocial-0f2de6394a1c52d47e326bb7d7d129a217ae4f6f.tar.xz |
Dereference remote replies (#132)
* decided where to put reply dereferencing
* fiddling with dereferencing threads
* further adventures
* tidy up some stuff
* move dereferencing functionality
* a bunch of refactoring
* go fmt
* more refactoring
* bleep bloop
* docs and linting
* start implementing replies collection on gts side
* fiddling around
* allow dereferencing our replies
* lint, fmt
Diffstat (limited to 'internal/ap')
-rw-r--r-- | internal/ap/extract.go | 614 | ||||
-rw-r--r-- | internal/ap/interfaces.go | 321 |
2 files changed, 935 insertions, 0 deletions
diff --git a/internal/ap/extract.go b/internal/ap/extract.go new file mode 100644 index 000000000..baffd4bf2 --- /dev/null +++ b/internal/ap/extract.go @@ -0,0 +1,614 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +// Package ap contains models and utilities for working with activitypub/activitystreams representations. +// +// It is built on top of go-fed/activity. +package ap + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "net/url" + "strings" + "time" + + "github.com/go-fed/activity/pub" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/util" +) + +// ExtractPreferredUsername returns a string representation of an interface's preferredUsername property. +func ExtractPreferredUsername(i WithPreferredUsername) (string, error) { + u := i.GetActivityStreamsPreferredUsername() + if u == nil || !u.IsXMLSchemaString() { + return "", errors.New("preferredUsername was not a string") + } + if u.GetXMLSchemaString() == "" { + return "", errors.New("preferredUsername was empty") + } + return u.GetXMLSchemaString(), nil +} + +// ExtractName returns a string representation of an interface's name property. +func ExtractName(i WithName) (string, error) { + nameProp := i.GetActivityStreamsName() + if nameProp == nil { + return "", errors.New("activityStreamsName not found") + } + + // take the first name string we can find + for iter := nameProp.Begin(); iter != nameProp.End(); iter = iter.Next() { + if iter.IsXMLSchemaString() && iter.GetXMLSchemaString() != "" { + return iter.GetXMLSchemaString(), nil + } + } + + return "", errors.New("activityStreamsName not found") +} + +// ExtractInReplyToURI extracts the inReplyToURI property (if present) from an interface. +func ExtractInReplyToURI(i WithInReplyTo) *url.URL { + inReplyToProp := i.GetActivityStreamsInReplyTo() + if inReplyToProp == nil { + // the property just wasn't set + return nil + } + for iter := inReplyToProp.Begin(); iter != inReplyToProp.End(); iter = iter.Next() { + if iter.IsIRI() { + if iter.GetIRI() != nil { + return iter.GetIRI() + } + } + } + // couldn't find a URI + return nil +} + +// ExtractURLItems extracts a slice of URLs from a property that has withItems. +func ExtractURLItems(i WithItems) []*url.URL { + urls := []*url.URL{} + items := i.GetActivityStreamsItems() + if items == nil || items.Len() == 0 { + return urls + } + + for iter := items.Begin(); iter != items.End(); iter = iter.Next() { + if iter.IsIRI() { + urls = append(urls, iter.GetIRI()) + } + } + return urls +} + +// ExtractTos returns a list of URIs that the activity addresses as To. +func ExtractTos(i WithTo) ([]*url.URL, error) { + to := []*url.URL{} + toProp := i.GetActivityStreamsTo() + if toProp == nil { + return nil, errors.New("toProp was nil") + } + for iter := toProp.Begin(); iter != toProp.End(); iter = iter.Next() { + if iter.IsIRI() { + if iter.GetIRI() != nil { + to = append(to, iter.GetIRI()) + } + } + } + return to, nil +} + +// ExtractCCs returns a list of URIs that the activity addresses as CC. +func ExtractCCs(i WithCC) ([]*url.URL, error) { + cc := []*url.URL{} + ccProp := i.GetActivityStreamsCc() + if ccProp == nil { + return cc, nil + } + for iter := ccProp.Begin(); iter != ccProp.End(); iter = iter.Next() { + if iter.IsIRI() { + if iter.GetIRI() != nil { + cc = append(cc, iter.GetIRI()) + } + } + } + return cc, nil +} + +// ExtractAttributedTo returns the URL of the actor that the withAttributedTo is attributed to. +func ExtractAttributedTo(i WithAttributedTo) (*url.URL, error) { + attributedToProp := i.GetActivityStreamsAttributedTo() + if attributedToProp == nil { + return nil, errors.New("attributedToProp was nil") + } + for iter := attributedToProp.Begin(); iter != attributedToProp.End(); iter = iter.Next() { + if iter.IsIRI() { + if iter.GetIRI() != nil { + return iter.GetIRI(), nil + } + } + } + return nil, errors.New("couldn't find iri for attributed to") +} + +// ExtractPublished extracts the publication time of an activity. +func ExtractPublished(i WithPublished) (time.Time, error) { + publishedProp := i.GetActivityStreamsPublished() + if publishedProp == nil { + return time.Time{}, errors.New("published prop was nil") + } + + if !publishedProp.IsXMLSchemaDateTime() { + return time.Time{}, errors.New("published prop was not date time") + } + + t := publishedProp.Get() + if t.IsZero() { + return time.Time{}, errors.New("published time was zero") + } + return t, nil +} + +// ExtractIconURL extracts a URL to a supported image file from something like: +// "icon": { +// "mediaType": "image/jpeg", +// "type": "Image", +// "url": "http://example.org/path/to/some/file.jpeg" +// }, +func ExtractIconURL(i WithIcon) (*url.URL, error) { + iconProp := i.GetActivityStreamsIcon() + if iconProp == nil { + return nil, errors.New("icon property was nil") + } + + // icon can potentially contain multiple entries, so we iterate through all of them + // here in order to find the first one that meets these criteria: + // 1. is an image + // 2. has a URL so we can grab it + for iter := iconProp.Begin(); iter != iconProp.End(); iter = iter.Next() { + // 1. is an image + if !iter.IsActivityStreamsImage() { + continue + } + imageValue := iter.GetActivityStreamsImage() + if imageValue == nil { + continue + } + + // 2. has a URL so we can grab it + url, err := ExtractURL(imageValue) + if err == nil && url != nil { + return url, nil + } + } + // if we get to this point we didn't find an icon meeting our criteria :'( + return nil, errors.New("could not extract valid image from icon") +} + +// ExtractImageURL extracts a URL to a supported image file from something like: +// "image": { +// "mediaType": "image/jpeg", +// "type": "Image", +// "url": "http://example.org/path/to/some/file.jpeg" +// }, +func ExtractImageURL(i WithImage) (*url.URL, error) { + imageProp := i.GetActivityStreamsImage() + if imageProp == nil { + return nil, errors.New("icon property was nil") + } + + // icon can potentially contain multiple entries, so we iterate through all of them + // here in order to find the first one that meets these criteria: + // 1. is an image + // 2. has a URL so we can grab it + for iter := imageProp.Begin(); iter != imageProp.End(); iter = iter.Next() { + // 1. is an image + if !iter.IsActivityStreamsImage() { + continue + } + imageValue := iter.GetActivityStreamsImage() + if imageValue == nil { + continue + } + + // 2. has a URL so we can grab it + url, err := ExtractURL(imageValue) + if err == nil && url != nil { + return url, nil + } + } + // if we get to this point we didn't find an image meeting our criteria :'( + return nil, errors.New("could not extract valid image from image property") +} + +// ExtractSummary extracts the summary/content warning of an interface. +func ExtractSummary(i WithSummary) (string, error) { + summaryProp := i.GetActivityStreamsSummary() + if summaryProp == nil { + return "", errors.New("summary property was nil") + } + + for iter := summaryProp.Begin(); iter != summaryProp.End(); iter = iter.Next() { + if iter.IsXMLSchemaString() && iter.GetXMLSchemaString() != "" { + return iter.GetXMLSchemaString(), nil + } + } + + return "", errors.New("could not extract summary") +} + +// ExtractDiscoverable extracts the Discoverable boolean of an interface. +func ExtractDiscoverable(i WithDiscoverable) (bool, error) { + if i.GetTootDiscoverable() == nil { + return false, errors.New("discoverable was nil") + } + return i.GetTootDiscoverable().Get(), nil +} + +// ExtractURL extracts the URL property of an interface. +func ExtractURL(i WithURL) (*url.URL, error) { + urlProp := i.GetActivityStreamsUrl() + if urlProp == nil { + return nil, errors.New("url property was nil") + } + + for iter := urlProp.Begin(); iter != urlProp.End(); iter = iter.Next() { + if iter.IsIRI() && iter.GetIRI() != nil { + return iter.GetIRI(), nil + } + } + + return nil, errors.New("could not extract url") +} + +// ExtractPublicKeyForOwner extracts the public key from an interface, as long as it belongs to the specified owner. +// It will return the public key itself, the id/URL of the public key, or an error if something goes wrong. +func ExtractPublicKeyForOwner(i WithPublicKey, forOwner *url.URL) (*rsa.PublicKey, *url.URL, error) { + publicKeyProp := i.GetW3IDSecurityV1PublicKey() + if publicKeyProp == nil { + return nil, nil, errors.New("public key property was nil") + } + + for iter := publicKeyProp.Begin(); iter != publicKeyProp.End(); iter = iter.Next() { + pkey := iter.Get() + if pkey == nil { + continue + } + + pkeyID, err := pub.GetId(pkey) + if err != nil || pkeyID == nil { + continue + } + + if pkey.GetW3IDSecurityV1Owner() == nil || pkey.GetW3IDSecurityV1Owner().Get() == nil || pkey.GetW3IDSecurityV1Owner().Get().String() != forOwner.String() { + continue + } + + if pkey.GetW3IDSecurityV1PublicKeyPem() == nil { + continue + } + + pkeyPem := pkey.GetW3IDSecurityV1PublicKeyPem().Get() + if pkeyPem == "" { + continue + } + + block, _ := pem.Decode([]byte(pkeyPem)) + if block == nil || block.Type != "PUBLIC KEY" { + return nil, nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type") + } + + p, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf("could not parse public key from block bytes: %s", err) + } + if p == nil { + return nil, nil, errors.New("returned public key was empty") + } + + if publicKey, ok := p.(*rsa.PublicKey); ok { + return publicKey, pkeyID, nil + } + } + return nil, nil, errors.New("couldn't find public key") +} + +// ExtractContent returns a string representation of the interface's Content property. +func ExtractContent(i WithContent) (string, error) { + contentProperty := i.GetActivityStreamsContent() + if contentProperty == nil { + return "", errors.New("content property was nil") + } + for iter := contentProperty.Begin(); iter != contentProperty.End(); iter = iter.Next() { + if iter.IsXMLSchemaString() && iter.GetXMLSchemaString() != "" { + return iter.GetXMLSchemaString(), nil + } + } + return "", errors.New("no content found") +} + +// ExtractAttachments returns a slice of attachments on the interface. +func ExtractAttachments(i WithAttachment) ([]*gtsmodel.MediaAttachment, error) { + attachments := []*gtsmodel.MediaAttachment{} + attachmentProp := i.GetActivityStreamsAttachment() + if attachmentProp == nil { + return attachments, nil + } + for iter := attachmentProp.Begin(); iter != attachmentProp.End(); iter = iter.Next() { + t := iter.GetType() + if t == nil { + continue + } + attachmentable, ok := t.(Attachmentable) + if !ok { + continue + } + attachment, err := ExtractAttachment(attachmentable) + if err != nil { + continue + } + attachments = append(attachments, attachment) + } + return attachments, nil +} + +// ExtractAttachment returns a gts model of an attachment from an attachmentable interface. +func ExtractAttachment(i Attachmentable) (*gtsmodel.MediaAttachment, error) { + attachment := >smodel.MediaAttachment{ + File: gtsmodel.File{}, + } + + attachmentURL, err := ExtractURL(i) + if err != nil { + return nil, err + } + attachment.RemoteURL = attachmentURL.String() + + mediaType := i.GetActivityStreamsMediaType() + if mediaType == nil { + return nil, errors.New("no media type") + } + if mediaType.Get() == "" { + return nil, errors.New("no media type") + } + attachment.File.ContentType = mediaType.Get() + attachment.Type = gtsmodel.FileTypeImage + + name, err := ExtractName(i) + if err == nil { + attachment.Description = name + } + + attachment.Processing = gtsmodel.ProcessingStatusReceived + + return attachment, nil +} + +// func extractBlurhash(i withBlurhash) (string, error) { +// if i.GetTootBlurhashProperty() == nil { +// return "", errors.New("blurhash property was nil") +// } +// if i.GetTootBlurhashProperty().Get() == "" { +// return "", errors.New("empty blurhash string") +// } +// return i.GetTootBlurhashProperty().Get(), nil +// } + +// ExtractHashtags returns a slice of tags on the interface. +func ExtractHashtags(i WithTag) ([]*gtsmodel.Tag, error) { + tags := []*gtsmodel.Tag{} + tagsProp := i.GetActivityStreamsTag() + if tagsProp == nil { + return tags, nil + } + for iter := tagsProp.Begin(); iter != tagsProp.End(); iter = iter.Next() { + t := iter.GetType() + if t == nil { + continue + } + + if t.GetTypeName() != "Hashtag" { + continue + } + + hashtaggable, ok := t.(Hashtaggable) + if !ok { + continue + } + + tag, err := ExtractHashtag(hashtaggable) + if err != nil { + continue + } + + tags = append(tags, tag) + } + return tags, nil +} + +// ExtractHashtag returns a gtsmodel tag from a hashtaggable. +func ExtractHashtag(i Hashtaggable) (*gtsmodel.Tag, error) { + tag := >smodel.Tag{} + + hrefProp := i.GetActivityStreamsHref() + if hrefProp == nil || !hrefProp.IsIRI() { + return nil, errors.New("no href prop") + } + tag.URL = hrefProp.GetIRI().String() + + name, err := ExtractName(i) + if err != nil { + return nil, err + } + tag.Name = strings.TrimPrefix(name, "#") + + return tag, nil +} + +// ExtractEmojis returns a slice of emojis on the interface. +func ExtractEmojis(i WithTag) ([]*gtsmodel.Emoji, error) { + emojis := []*gtsmodel.Emoji{} + tagsProp := i.GetActivityStreamsTag() + if tagsProp == nil { + return emojis, nil + } + for iter := tagsProp.Begin(); iter != tagsProp.End(); iter = iter.Next() { + t := iter.GetType() + if t == nil { + continue + } + + if t.GetTypeName() != "Emoji" { + continue + } + + emojiable, ok := t.(Emojiable) + if !ok { + continue + } + + emoji, err := ExtractEmoji(emojiable) + if err != nil { + continue + } + + emojis = append(emojis, emoji) + } + return emojis, nil +} + +// ExtractEmoji ... +func ExtractEmoji(i Emojiable) (*gtsmodel.Emoji, error) { + emoji := >smodel.Emoji{} + + idProp := i.GetJSONLDId() + if idProp == nil || !idProp.IsIRI() { + return nil, errors.New("no id for emoji") + } + uri := idProp.GetIRI() + emoji.URI = uri.String() + emoji.Domain = uri.Host + + name, err := ExtractName(i) + if err != nil { + return nil, err + } + emoji.Shortcode = strings.Trim(name, ":") + + if i.GetActivityStreamsIcon() == nil { + return nil, errors.New("no icon for emoji") + } + imageURL, err := ExtractIconURL(i) + if err != nil { + return nil, errors.New("no url for emoji image") + } + emoji.ImageRemoteURL = imageURL.String() + + return emoji, nil +} + +// ExtractMentions extracts a slice of gtsmodel Mentions from a WithTag interface. +func ExtractMentions(i WithTag) ([]*gtsmodel.Mention, error) { + mentions := []*gtsmodel.Mention{} + tagsProp := i.GetActivityStreamsTag() + if tagsProp == nil { + return mentions, nil + } + for iter := tagsProp.Begin(); iter != tagsProp.End(); iter = iter.Next() { + t := iter.GetType() + if t == nil { + continue + } + + if t.GetTypeName() != "Mention" { + continue + } + + mentionable, ok := t.(Mentionable) + if !ok { + continue + } + + mention, err := ExtractMention(mentionable) + if err != nil { + continue + } + + mentions = append(mentions, mention) + } + return mentions, nil +} + +// ExtractMention extracts a gts model mention from a Mentionable. +func ExtractMention(i Mentionable) (*gtsmodel.Mention, error) { + mention := >smodel.Mention{} + + mentionString, err := ExtractName(i) + if err != nil { + return nil, err + } + + // just make sure the mention string is valid so we can handle it properly later on... + username, domain, err := util.ExtractMentionParts(mentionString) + if err != nil { + return nil, err + } + if username == "" || domain == "" { + return nil, errors.New("username or domain was empty") + } + mention.NameString = mentionString + + // the href prop should be the AP URI of a user we know, eg https://example.org/users/whatever_user + hrefProp := i.GetActivityStreamsHref() + if hrefProp == nil || !hrefProp.IsIRI() { + return nil, errors.New("no href prop") + } + mention.MentionedAccountURI = hrefProp.GetIRI().String() + return mention, nil +} + +// ExtractActor extracts the actor ID/IRI from an interface WithActor. +func ExtractActor(i WithActor) (*url.URL, error) { + actorProp := i.GetActivityStreamsActor() + if actorProp == nil { + return nil, errors.New("actor property was nil") + } + for iter := actorProp.Begin(); iter != actorProp.End(); iter = iter.Next() { + if iter.IsIRI() && iter.GetIRI() != nil { + return iter.GetIRI(), nil + } + } + return nil, errors.New("no iri found for actor prop") +} + +// ExtractObject extracts a URL object from a WithObject interface. +func ExtractObject(i WithObject) (*url.URL, error) { + objectProp := i.GetActivityStreamsObject() + if objectProp == nil { + return nil, errors.New("object property was nil") + } + for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() { + if iter.IsIRI() && iter.GetIRI() != nil { + return iter.GetIRI(), nil + } + } + return nil, errors.New("no iri found for object prop") +} diff --git a/internal/ap/interfaces.go b/internal/ap/interfaces.go new file mode 100644 index 000000000..43dd149d5 --- /dev/null +++ b/internal/ap/interfaces.go @@ -0,0 +1,321 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package ap + +import "github.com/go-fed/activity/streams/vocab" + +// Accountable represents the minimum activitypub interface for representing an 'account'. +// This interface is fulfilled by: Person, Application, Organization, Service, and Group +type Accountable interface { + WithJSONLDId + WithTypeName + + WithPreferredUsername + WithIcon + WithName + WithImage + WithSummary + WithDiscoverable + WithURL + WithPublicKey + WithInbox + WithOutbox + WithFollowing + WithFollowers + WithFeatured +} + +// Statusable represents the minimum activitypub interface for representing a 'status'. +// This interface is fulfilled by: Article, Document, Image, Video, Note, Page, Event, Place, Mention, Profile +type Statusable interface { + WithJSONLDId + WithTypeName + + WithSummary + WithInReplyTo + WithPublished + WithURL + WithAttributedTo + WithTo + WithCC + WithSensitive + WithConversation + WithContent + WithAttachment + WithTag + WithReplies +} + +// Attachmentable represents the minimum activitypub interface for representing a 'mediaAttachment'. +// This interface is fulfilled by: Audio, Document, Image, Video +type Attachmentable interface { + WithTypeName + WithMediaType + WithURL + WithName +} + +// Hashtaggable represents the minimum activitypub interface for representing a 'hashtag' tag. +type Hashtaggable interface { + WithTypeName + WithHref + WithName +} + +// Emojiable represents the minimum interface for an 'emoji' tag. +type Emojiable interface { + WithJSONLDId + WithTypeName + WithName + WithUpdated + WithIcon +} + +// Mentionable represents the minimum interface for a 'mention' tag. +type Mentionable interface { + WithName + WithHref +} + +// Followable represents the minimum interface for an activitystreams 'follow' activity. +type Followable interface { + WithJSONLDId + WithTypeName + + WithActor + WithObject +} + +// Likeable represents the minimum interface for an activitystreams 'like' activity. +type Likeable interface { + WithJSONLDId + WithTypeName + + WithActor + WithObject +} + +// Blockable represents the minimum interface for an activitystreams 'block' activity. +type Blockable interface { + WithJSONLDId + WithTypeName + + WithActor + WithObject +} + +// Announceable represents the minimum interface for an activitystreams 'announce' activity. +type Announceable interface { + WithJSONLDId + WithTypeName + + WithActor + WithObject + WithPublished + WithTo + WithCC +} + +// CollectionPageable represents the minimum interface for an activitystreams 'CollectionPage' object. +type CollectionPageable interface { + WithJSONLDId + WithTypeName + + WithNext + WithPartOf + WithItems +} + +// WithJSONLDId represents an activity with JSONLDIdProperty +type WithJSONLDId interface { + GetJSONLDId() vocab.JSONLDIdProperty +} + +// WithTypeName represents an activity with a type name +type WithTypeName interface { + GetTypeName() string +} + +// WithPreferredUsername represents an activity with ActivityStreamsPreferredUsernameProperty +type WithPreferredUsername interface { + GetActivityStreamsPreferredUsername() vocab.ActivityStreamsPreferredUsernameProperty +} + +// WithIcon represents an activity with ActivityStreamsIconProperty +type WithIcon interface { + GetActivityStreamsIcon() vocab.ActivityStreamsIconProperty +} + +// WithName represents an activity with ActivityStreamsNameProperty +type WithName interface { + GetActivityStreamsName() vocab.ActivityStreamsNameProperty +} + +// WithImage represents an activity with ActivityStreamsImageProperty +type WithImage interface { + GetActivityStreamsImage() vocab.ActivityStreamsImageProperty +} + +// WithSummary represents an activity with ActivityStreamsSummaryProperty +type WithSummary interface { + GetActivityStreamsSummary() vocab.ActivityStreamsSummaryProperty +} + +// WithDiscoverable represents an activity with TootDiscoverableProperty +type WithDiscoverable interface { + GetTootDiscoverable() vocab.TootDiscoverableProperty +} + +// WithURL represents an activity with ActivityStreamsUrlProperty +type WithURL interface { + GetActivityStreamsUrl() vocab.ActivityStreamsUrlProperty +} + +// WithPublicKey represents an activity with W3IDSecurityV1PublicKeyProperty +type WithPublicKey interface { + GetW3IDSecurityV1PublicKey() vocab.W3IDSecurityV1PublicKeyProperty +} + +// WithInbox represents an activity with ActivityStreamsInboxProperty +type WithInbox interface { + GetActivityStreamsInbox() vocab.ActivityStreamsInboxProperty +} + +// WithOutbox represents an activity with ActivityStreamsOutboxProperty +type WithOutbox interface { + GetActivityStreamsOutbox() vocab.ActivityStreamsOutboxProperty +} + +// WithFollowing represents an activity with ActivityStreamsFollowingProperty +type WithFollowing interface { + GetActivityStreamsFollowing() vocab.ActivityStreamsFollowingProperty +} + +// WithFollowers represents an activity with ActivityStreamsFollowersProperty +type WithFollowers interface { + GetActivityStreamsFollowers() vocab.ActivityStreamsFollowersProperty +} + +// WithFeatured represents an activity with TootFeaturedProperty +type WithFeatured interface { + GetTootFeatured() vocab.TootFeaturedProperty +} + +// WithAttributedTo represents an activity with ActivityStreamsAttributedToProperty +type WithAttributedTo interface { + GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty +} + +// WithAttachment represents an activity with ActivityStreamsAttachmentProperty +type WithAttachment interface { + GetActivityStreamsAttachment() vocab.ActivityStreamsAttachmentProperty +} + +// WithTo represents an activity with ActivityStreamsToProperty +type WithTo interface { + GetActivityStreamsTo() vocab.ActivityStreamsToProperty +} + +// WithInReplyTo represents an activity with ActivityStreamsInReplyToProperty +type WithInReplyTo interface { + GetActivityStreamsInReplyTo() vocab.ActivityStreamsInReplyToProperty +} + +// WithCC represents an activity with ActivityStreamsCcProperty +type WithCC interface { + GetActivityStreamsCc() vocab.ActivityStreamsCcProperty +} + +// WithSensitive ... +type WithSensitive interface { + // TODO +} + +// WithConversation ... +type WithConversation interface { + // TODO +} + +// WithContent represents an activity with ActivityStreamsContentProperty +type WithContent interface { + GetActivityStreamsContent() vocab.ActivityStreamsContentProperty +} + +// WithPublished represents an activity with ActivityStreamsPublishedProperty +type WithPublished interface { + GetActivityStreamsPublished() vocab.ActivityStreamsPublishedProperty +} + +// WithTag represents an activity with ActivityStreamsTagProperty +type WithTag interface { + GetActivityStreamsTag() vocab.ActivityStreamsTagProperty +} + +// WithReplies represents an activity with ActivityStreamsRepliesProperty +type WithReplies interface { + GetActivityStreamsReplies() vocab.ActivityStreamsRepliesProperty +} + +// WithMediaType represents an activity with ActivityStreamsMediaTypeProperty +type WithMediaType interface { + GetActivityStreamsMediaType() vocab.ActivityStreamsMediaTypeProperty +} + +// type withBlurhash interface { +// GetTootBlurhashProperty() vocab.TootBlurhashProperty +// } + +// type withFocalPoint interface { +// // TODO +// } + +// WithHref represents an activity with ActivityStreamsHrefProperty +type WithHref interface { + GetActivityStreamsHref() vocab.ActivityStreamsHrefProperty +} + +// WithUpdated represents an activity with ActivityStreamsUpdatedProperty +type WithUpdated interface { + GetActivityStreamsUpdated() vocab.ActivityStreamsUpdatedProperty +} + +// WithActor represents an activity with ActivityStreamsActorProperty +type WithActor interface { + GetActivityStreamsActor() vocab.ActivityStreamsActorProperty +} + +// WithObject represents an activity with ActivityStreamsObjectProperty +type WithObject interface { + GetActivityStreamsObject() vocab.ActivityStreamsObjectProperty +} + +// WithNext represents an activity with ActivityStreamsNextProperty +type WithNext interface { + GetActivityStreamsNext() vocab.ActivityStreamsNextProperty +} + +// WithPartOf represents an activity with ActivityStreamsPartOfProperty +type WithPartOf interface { + GetActivityStreamsPartOf() vocab.ActivityStreamsPartOfProperty +} + +// WithItems represents an activity with ActivityStreamsItemsProperty +type WithItems interface { + GetActivityStreamsItems() vocab.ActivityStreamsItemsProperty +} |