mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 11:42:24 -05:00 
			
		
		
		
	[feature] Add opt-in RSS feed for account's latest Public posts (#897)
* start adding rss functionality * add gorilla/feeds dependency * first bash at building rss feed still needs work, this is an interim commit * tidy up a bit * add publicOnly option to GetAccountLastPosted * implement rss endpoint * fix test * add initial user docs for rss * update rss logo * docs update * add rssFeed to frontend * feed -> feed.rss * enableRSS * increase rss logo size a lil bit * add rss toggle * move emojify to text package * fiddle with rss feed formatting * add Text field to test statuses * move status to rss item to typeconverter * update bun schema for enablerss * simplify 304 checking * assume account not rss * update tests * update swagger docs * allow more characters in title, trim nicer * update last posted to be more consistent
This commit is contained in:
		
					parent
					
						
							
								aa07750bdb
							
						
					
				
			
			
				commit
				
					
						80663061d8
					
				
			
		
					 58 changed files with 2282 additions and 211 deletions
				
			
		
							
								
								
									
										183
									
								
								vendor/github.com/gorilla/feeds/json.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								vendor/github.com/gorilla/feeds/json.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,183 @@ | |||
| package feeds | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const jsonFeedVersion = "https://jsonfeed.org/version/1" | ||||
| 
 | ||||
| // JSONAuthor represents the author of the feed or of an individual item | ||||
| // in the feed | ||||
| type JSONAuthor struct { | ||||
| 	Name   string `json:"name,omitempty"` | ||||
| 	Url    string `json:"url,omitempty"` | ||||
| 	Avatar string `json:"avatar,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JSONAttachment represents a related resource. Podcasts, for instance, would | ||||
| // include an attachment that’s an audio or video file. | ||||
| type JSONAttachment struct { | ||||
| 	Url      string        `json:"url,omitempty"` | ||||
| 	MIMEType string        `json:"mime_type,omitempty"` | ||||
| 	Title    string        `json:"title,omitempty"` | ||||
| 	Size     int32         `json:"size,omitempty"` | ||||
| 	Duration time.Duration `json:"duration_in_seconds,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON implements the json.Marshaler interface. | ||||
| // The Duration field is marshaled in seconds, all other fields are marshaled | ||||
| // based upon the definitions in struct tags. | ||||
| func (a *JSONAttachment) MarshalJSON() ([]byte, error) { | ||||
| 	type EmbeddedJSONAttachment JSONAttachment | ||||
| 	return json.Marshal(&struct { | ||||
| 		Duration float64 `json:"duration_in_seconds,omitempty"` | ||||
| 		*EmbeddedJSONAttachment | ||||
| 	}{ | ||||
| 		EmbeddedJSONAttachment: (*EmbeddedJSONAttachment)(a), | ||||
| 		Duration:               a.Duration.Seconds(), | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON implements the json.Unmarshaler interface. | ||||
| // The Duration field is expected to be in seconds, all other field types | ||||
| // match the struct definition. | ||||
| func (a *JSONAttachment) UnmarshalJSON(data []byte) error { | ||||
| 	type EmbeddedJSONAttachment JSONAttachment | ||||
| 	var raw struct { | ||||
| 		Duration float64 `json:"duration_in_seconds,omitempty"` | ||||
| 		*EmbeddedJSONAttachment | ||||
| 	} | ||||
| 	raw.EmbeddedJSONAttachment = (*EmbeddedJSONAttachment)(a) | ||||
| 
 | ||||
| 	err := json.Unmarshal(data, &raw) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if raw.Duration > 0 { | ||||
| 		nsec := int64(raw.Duration * float64(time.Second)) | ||||
| 		raw.EmbeddedJSONAttachment.Duration = time.Duration(nsec) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // JSONItem represents a single entry/post for the feed. | ||||
| type JSONItem struct { | ||||
| 	Id            string           `json:"id"` | ||||
| 	Url           string           `json:"url,omitempty"` | ||||
| 	ExternalUrl   string           `json:"external_url,omitempty"` | ||||
| 	Title         string           `json:"title,omitempty"` | ||||
| 	ContentHTML   string           `json:"content_html,omitempty"` | ||||
| 	ContentText   string           `json:"content_text,omitempty"` | ||||
| 	Summary       string           `json:"summary,omitempty"` | ||||
| 	Image         string           `json:"image,omitempty"` | ||||
| 	BannerImage   string           `json:"banner_,omitempty"` | ||||
| 	PublishedDate *time.Time       `json:"date_published,omitempty"` | ||||
| 	ModifiedDate  *time.Time       `json:"date_modified,omitempty"` | ||||
| 	Author        *JSONAuthor      `json:"author,omitempty"` | ||||
| 	Tags          []string         `json:"tags,omitempty"` | ||||
| 	Attachments   []JSONAttachment `json:"attachments,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JSONHub describes an endpoint that can be used to subscribe to real-time | ||||
| // notifications from the publisher of this feed. | ||||
| type JSONHub struct { | ||||
| 	Type string `json:"type"` | ||||
| 	Url  string `json:"url"` | ||||
| } | ||||
| 
 | ||||
| // JSONFeed represents a syndication feed in the JSON Feed Version 1 format. | ||||
| // Matching the specification found here: https://jsonfeed.org/version/1. | ||||
| type JSONFeed struct { | ||||
| 	Version     string      `json:"version"` | ||||
| 	Title       string      `json:"title"` | ||||
| 	HomePageUrl string      `json:"home_page_url,omitempty"` | ||||
| 	FeedUrl     string      `json:"feed_url,omitempty"` | ||||
| 	Description string      `json:"description,omitempty"` | ||||
| 	UserComment string      `json:"user_comment,omitempty"` | ||||
| 	NextUrl     string      `json:"next_url,omitempty"` | ||||
| 	Icon        string      `json:"icon,omitempty"` | ||||
| 	Favicon     string      `json:"favicon,omitempty"` | ||||
| 	Author      *JSONAuthor `json:"author,omitempty"` | ||||
| 	Expired     *bool       `json:"expired,omitempty"` | ||||
| 	Hubs        []*JSONItem `json:"hubs,omitempty"` | ||||
| 	Items       []*JSONItem `json:"items,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JSON is used to convert a generic Feed to a JSONFeed. | ||||
| type JSON struct { | ||||
| 	*Feed | ||||
| } | ||||
| 
 | ||||
| // ToJSON encodes f into a JSON string. Returns an error if marshalling fails. | ||||
| func (f *JSON) ToJSON() (string, error) { | ||||
| 	return f.JSONFeed().ToJSON() | ||||
| } | ||||
| 
 | ||||
| // ToJSON encodes f into a JSON string. Returns an error if marshalling fails. | ||||
| func (f *JSONFeed) ToJSON() (string, error) { | ||||
| 	data, err := json.MarshalIndent(f, "", "  ") | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return string(data), nil | ||||
| } | ||||
| 
 | ||||
| // JSONFeed creates a new JSONFeed with a generic Feed struct's data. | ||||
| func (f *JSON) JSONFeed() *JSONFeed { | ||||
| 	feed := &JSONFeed{ | ||||
| 		Version:     jsonFeedVersion, | ||||
| 		Title:       f.Title, | ||||
| 		Description: f.Description, | ||||
| 	} | ||||
| 
 | ||||
| 	if f.Link != nil { | ||||
| 		feed.HomePageUrl = f.Link.Href | ||||
| 	} | ||||
| 	if f.Author != nil { | ||||
| 		feed.Author = &JSONAuthor{ | ||||
| 			Name: f.Author.Name, | ||||
| 		} | ||||
| 	} | ||||
| 	for _, e := range f.Items { | ||||
| 		feed.Items = append(feed.Items, newJSONItem(e)) | ||||
| 	} | ||||
| 	return feed | ||||
| } | ||||
| 
 | ||||
| func newJSONItem(i *Item) *JSONItem { | ||||
| 	item := &JSONItem{ | ||||
| 		Id:      i.Id, | ||||
| 		Title:   i.Title, | ||||
| 		Summary: i.Description, | ||||
| 
 | ||||
| 		ContentHTML: i.Content, | ||||
| 	} | ||||
| 
 | ||||
| 	if i.Link != nil { | ||||
| 		item.Url = i.Link.Href | ||||
| 	} | ||||
| 	if i.Source != nil { | ||||
| 		item.ExternalUrl = i.Source.Href | ||||
| 	} | ||||
| 	if i.Author != nil { | ||||
| 		item.Author = &JSONAuthor{ | ||||
| 			Name: i.Author.Name, | ||||
| 		} | ||||
| 	} | ||||
| 	if !i.Created.IsZero() { | ||||
| 		item.PublishedDate = &i.Created | ||||
| 	} | ||||
| 	if !i.Updated.IsZero() { | ||||
| 		item.ModifiedDate = &i.Updated | ||||
| 	} | ||||
| 	if i.Enclosure != nil && strings.HasPrefix(i.Enclosure.Type, "image/") { | ||||
| 		item.Image = i.Enclosure.Url | ||||
| 	} | ||||
| 
 | ||||
| 	return item | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue