package notes import ( "time" ) type Media struct { URL string `json:"url"` Alt string `json:"alt"` Width int `json:"width,omitempty"` Height int `json:"height,omitempty"` } type Note struct { URI string `json:"uri"` AuthorDID string `json:"authorDID"` AuthorHandle string `json:"authorHandle"` AuthorName string `json:"authorName"` AuthorNoUnauth bool `json:"authorNoUnauth"` // author has !no-unauthenticated label AvatarURL string `json:"avatarURL"` Text string `json:"text"` CreatedAt time.Time `json:"createdAt"` ReplyTo *string `json:"replyTo,omitempty"` Facets []Facet `json:"facets,omitempty"` Parents []Note `json:"parents,omitempty"` Media []Media `json:"media,omitempty"` } type Facet struct { ByteStart int `json:"byteStart"` ByteEnd int `json:"byteEnd"` Type string `json:"type"` Value string `json:"value"` // the link destination, whether url, profile or tag } func MissingNote(uri string) *Note { return &Note{ URI: uri, AuthorDID: "MISSING", Text: "This post is unavailable. It may have been deleted.", CreatedAt: time.Time{}, } } func isChainContained(smallChainPost, bigChainPost Note) bool { if len(smallChainPost.Parents) > len(bigChainPost.Parents) { return false } // if the thread is going to differ at any post, it's going to be at the end // thus if the posts match their parents MUST match too // (not that I expect to ever compare a post to itself but this way it won't break if I do. And technically a thread contains itself) if len(smallChainPost.Parents) == len(bigChainPost.Parents) { return smallChainPost.URI == bigChainPost.URI } // check if the last post of the smaller chain is the (n+1)-th post of the bigger chain // where n is the number of parents in the smaller reply chain offset := len(bigChainPost.Parents) - len(smallChainPost.Parents) return smallChainPost.URI == bigChainPost.Parents[offset-1].URI } func DeduplicateThreads(posts []Note) []Note { filtered := []Note{} for _, post := range posts { isRedundant := false for _, kept := range filtered { // check if this post's chain is contained in any kept post if isChainContained(post, kept) { isRedundant = true break } } if !isRedundant { filtered = append(filtered, post) } } return filtered }