|
|
@ -0,0 +1,130 @@ |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"encoding/xml" |
|
|
|
"flag" |
|
|
|
"fmt" |
|
|
|
"io/ioutil" |
|
|
|
"net/http" |
|
|
|
"os" |
|
|
|
"sort" |
|
|
|
"time" |
|
|
|
) |
|
|
|
|
|
|
|
// This script downloads the OPML export from Miniflux, applies a filter and saves the result as a json document to use for a blogroll
|
|
|
|
|
|
|
|
type opml struct { |
|
|
|
Body struct { |
|
|
|
opmlOutline |
|
|
|
} `xml:"body" json:"body"` |
|
|
|
} |
|
|
|
|
|
|
|
type opmlOutline struct { |
|
|
|
Text string `xml:"text,attr" json:"_text,omitempty"` |
|
|
|
Title string `xml:"title,attr" json:"_title,omitempty"` |
|
|
|
XMLURL string `xml:"xmlUrl,attr" json:"_xmlUrl,omitempty"` |
|
|
|
HTMLURL string `xml:"htmlUrl,attr" json:"_htmlUrl,omitempty"` |
|
|
|
Outlines []opmlOutline `xml:"outline" json:"outline,omitempty"` |
|
|
|
} |
|
|
|
|
|
|
|
type flagArray []string |
|
|
|
|
|
|
|
func (i *flagArray) String() string { |
|
|
|
return fmt.Sprint(*i) |
|
|
|
} |
|
|
|
|
|
|
|
func (i *flagArray) Set(value string) error { |
|
|
|
*i = append(*i, value) |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func main() { |
|
|
|
|
|
|
|
var categories flagArray |
|
|
|
flag.Var(&categories, "c", "Allowed categories (specify multiple times for multiple categories)") |
|
|
|
|
|
|
|
var instance, token, output string |
|
|
|
flag.StringVar(&instance, "i", "", "Miniflux instance to use") |
|
|
|
flag.StringVar(&token, "t", "", "Token to use for the Miniflux API") |
|
|
|
flag.StringVar(&output, "o", "opml.json", "Output file to write to") |
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
if len(categories) == 0 || instance == "" || token == "" { |
|
|
|
fmt.Println("Not all required flags provided") |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Get OPML
|
|
|
|
client := http.Client{ |
|
|
|
Timeout: time.Second * 5, |
|
|
|
} |
|
|
|
req, err := http.NewRequest(http.MethodGet, instance+"/v1/export", nil) |
|
|
|
if err != nil { |
|
|
|
fmt.Println(err) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
req.Header.Set("Content-Type", "application/json") |
|
|
|
req.Header.Set("Accept", "application/json") |
|
|
|
req.Header.Set("X-Auth-Token", token) |
|
|
|
res, err := client.Do(req) |
|
|
|
if err != nil { |
|
|
|
fmt.Println(err) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
if res.StatusCode != http.StatusOK { |
|
|
|
fmt.Println("Status code: ", res.StatusCode) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
defer res.Body.Close() |
|
|
|
ob, err := ioutil.ReadAll(res.Body) |
|
|
|
if err != nil { |
|
|
|
fmt.Println(err) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Unmarshal
|
|
|
|
o := &opml{} |
|
|
|
err = xml.Unmarshal(ob, o) |
|
|
|
if err != nil { |
|
|
|
fmt.Println(err.Error()) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Filter
|
|
|
|
filtered := &opml{} |
|
|
|
for _, category := range categories { |
|
|
|
for _, outline := range o.Body.Outlines { |
|
|
|
if outline.Text == category { |
|
|
|
// Sort
|
|
|
|
sort.Slice(outline.Outlines, func(i, j int) bool { |
|
|
|
return outline.Outlines[i].Title < outline.Outlines[j].Title |
|
|
|
}) |
|
|
|
// Append
|
|
|
|
filtered.Body.Outlines = append(filtered.Body.Outlines, outline) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Marshal as JSON
|
|
|
|
jb, err := json.MarshalIndent(filtered.Body, "", " ") |
|
|
|
if err != nil { |
|
|
|
fmt.Println(err.Error()) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Write JSON to file
|
|
|
|
err = ioutil.WriteFile(output, jb, 0644) |
|
|
|
if err != nil { |
|
|
|
fmt.Println(err.Error()) |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
} |