bring back yahoo pipes!
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 167 lines 4.8 kB view raw
1package outputs 2 3import ( 4 "context" 5 "encoding/xml" 6 "fmt" 7 "time" 8 9 "github.com/kierank/pipes/nodes" 10) 11 12type RSSOutputNode struct{} 13 14func (n *RSSOutputNode) Type() string { return "rss-output" } 15func (n *RSSOutputNode) Label() string { return "RSS Output" } 16func (n *RSSOutputNode) Description() string { return "Output data as RSS feed" } 17func (n *RSSOutputNode) Category() string { return "output" } 18func (n *RSSOutputNode) Inputs() int { return 1 } 19func (n *RSSOutputNode) Outputs() int { return 0 } 20 21func (n *RSSOutputNode) Execute(ctx context.Context, config map[string]interface{}, inputs [][]interface{}, execCtx *nodes.Context) ([]interface{}, error) { 22 if len(inputs) == 0 || len(inputs[0]) == 0 { 23 execCtx.Log("rss-output", "info", "No input data") 24 return nil, nil 25 } 26 27 data := inputs[0] 28 29 // Get feed metadata from config 30 feedTitle := getStringConfig(config, "title", "Pipes Feed") 31 feedDescription := getStringConfig(config, "description", "Feed generated by Pipes") 32 feedLink := getStringConfig(config, "link", "http://localhost:3001") 33 34 // Build RSS feed 35 type RSSItem struct { 36 Title string `xml:"title"` 37 Description string `xml:"description"` 38 Link string `xml:"link"` 39 PubDate string `xml:"pubDate,omitempty"` 40 GUID string `xml:"guid,omitempty"` 41 Author string `xml:"author,omitempty"` 42 Categories []string `xml:"category,omitempty"` 43 } 44 45 type RSSChannel struct { 46 Title string `xml:"title"` 47 Description string `xml:"description"` 48 Link string `xml:"link"` 49 Items []RSSItem `xml:"item"` 50 } 51 52 type RSS struct { 53 XMLName xml.Name `xml:"rss"` 54 Version string `xml:"version,attr"` 55 Channel RSSChannel `xml:"channel"` 56 } 57 58 var items []RSSItem 59 for _, item := range data { 60 itemMap, ok := item.(map[string]interface{}) 61 if !ok { 62 continue 63 } 64 65 rssItem := RSSItem{ 66 Title: getStringFromMap(itemMap, "title", "Untitled"), 67 Description: getStringFromMap(itemMap, "description", ""), 68 Link: getStringFromMap(itemMap, "link", ""), 69 GUID: getStringFromMap(itemMap, "guid", ""), 70 Author: getStringFromMap(itemMap, "author", ""), 71 } 72 73 // Try to get published date - check "published" first, then fall back to "published_at" timestamp 74 if pubDate, ok := itemMap["published"].(string); ok && pubDate != "" { 75 rssItem.PubDate = pubDate 76 } else if timestamp, ok := itemMap["published_at"].(int64); ok && timestamp > 0 { 77 // Convert Unix timestamp to RFC1123 format for RSS 78 rssItem.PubDate = time.Unix(timestamp, 0).UTC().Format(time.RFC1123Z) 79 } 80 81 // Extract categories if present 82 if categories, ok := itemMap["categories"].([]interface{}); ok { 83 for _, cat := range categories { 84 if catStr, ok := cat.(string); ok { 85 rssItem.Categories = append(rssItem.Categories, catStr) 86 } 87 } 88 } 89 90 items = append(items, rssItem) 91 } 92 93 feed := RSS{ 94 Version: "2.0", 95 Channel: RSSChannel{ 96 Title: feedTitle, 97 Description: feedDescription, 98 Link: feedLink, 99 Items: items, 100 }, 101 } 102 103 xmlData, err := xml.MarshalIndent(feed, "", " ") 104 if err != nil { 105 return nil, fmt.Errorf("marshal RSS: %w", err) 106 } 107 108 rssOutput := fmt.Sprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n%s", string(xmlData)) 109 110 // Save output to database for public access 111 if err := execCtx.SaveOutput("rss", rssOutput, "application/rss+xml"); err != nil { 112 execCtx.Log("rss-output", "error", "Failed to save output: "+err.Error()) 113 } 114 115 execCtx.Log("rss-output", "info", rssOutput) 116 117 return data, nil 118} 119 120func (n *RSSOutputNode) ValidateConfig(config map[string]interface{}) error { 121 return nil 122} 123 124func (n *RSSOutputNode) GetConfigSchema() *nodes.ConfigSchema { 125 return &nodes.ConfigSchema{ 126 Fields: []nodes.ConfigField{ 127 { 128 Name: "title", 129 Label: "Feed Title", 130 Type: "text", 131 Required: false, 132 DefaultValue: "Pipes Feed", 133 HelpText: "Title of the RSS feed", 134 }, 135 { 136 Name: "description", 137 Label: "Feed Description", 138 Type: "textarea", 139 Required: false, 140 DefaultValue: "Feed generated by Pipes", 141 HelpText: "Description of the RSS feed", 142 }, 143 { 144 Name: "link", 145 Label: "Feed Link", 146 Type: "url", 147 Required: false, 148 DefaultValue: "http://localhost:3001", 149 HelpText: "URL of the feed", 150 }, 151 }, 152 } 153} 154 155func getStringConfig(config map[string]interface{}, key, defaultValue string) string { 156 if val, ok := config[key].(string); ok && val != "" { 157 return val 158 } 159 return defaultValue 160} 161 162func getStringFromMap(m map[string]interface{}, key, defaultValue string) string { 163 if val, ok := m[key].(string); ok { 164 return val 165 } 166 return defaultValue 167}