]> Sergey Matveev's repositories - linksexp.git/blob - main.go
gemini:// support
[linksexp.git] / main.go
1 /*
2 linksexp -- Texinfo/XBEL/OPML/urls autogeneration from recfile bookmark
3 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package main
19
20 import (
21         "flag"
22         "fmt"
23         "io"
24         "os"
25         "sort"
26         "strings"
27         "time"
28
29         "go.cypherpunks.ru/recfile"
30 )
31
32 type ByTitle []map[string][]string
33
34 func (a ByTitle) Len() int      { return len(a) }
35 func (a ByTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
36 func (a ByTitle) Less(i, j int) bool {
37         return strings.Compare(a[i]["Title"][0], a[j]["Title"][0]) < 0
38 }
39
40 func main() {
41         doXBEL := flag.Bool("xbel", false, "Make XBEL")
42         doOPML := flag.Bool("opml", false, "Make OPML")
43         doURLS := flag.Bool("urls", false, "Make newsboat urls")
44         flag.Parse()
45
46         r := recfile.NewReader(os.Stdin)
47
48         if *doURLS {
49                 for {
50                         m, err := r.NextMapWithSlice()
51                         if err == io.EOF {
52                                 break
53                         }
54                         if err != nil {
55                                 panic(err)
56                         }
57                         if m["%rec"] != nil {
58                                 continue
59                         }
60                         sort.Strings(m["Category"])
61                         cats := strings.Join(m["Category"], " ")
62                         for _, f := range m["Feed"] {
63                                 if strings.HasPrefix(f, "gemini://") {
64                                         f = "https://gemini/" + f
65                                 }
66                                 fmt.Println(f, cats)
67                         }
68                 }
69                 os.Exit(0)
70         }
71
72         if *doOPML {
73                 data := make([]map[string][]string, 0)
74                 for {
75                         m, err := r.NextMapWithSlice()
76                         if err == io.EOF {
77                                 break
78                         }
79                         if err != nil {
80                                 panic(err)
81                         }
82                         if m["%rec"] != nil {
83                                 continue
84                         }
85                         sort.Strings(m["Category"])
86                         data = append(data, m)
87                 }
88                 sort.Sort(ByTitle(data))
89                 opml(data)
90                 os.Exit(0)
91         }
92
93         data := make(map[string][]map[string][]string)
94         for {
95                 m, err := r.NextMapWithSlice()
96                 if err == io.EOF {
97                         break
98                 }
99                 if err != nil {
100                         panic(err)
101                 }
102                 if m["%rec"] != nil {
103                         continue
104                 }
105                 sort.Strings(m["Category"])
106                 if cs := m["Category"]; len(cs) == 0 {
107                         data["Uncategorized"] = append(data["Uncategorized"], m)
108                 } else {
109                         for _, cat := range cs {
110                                 data[cat] = append(data[cat], m)
111                         }
112                 }
113         }
114         cats := make([]string, 0, len(data))
115         for c := range data {
116                 cats = append(cats, c)
117                 sort.Sort(ByTitle(data[c]))
118         }
119         sort.Strings(cats)
120
121         if *doXBEL {
122                 xbel(cats, data)
123                 os.Exit(0)
124         }
125
126         fmt.Println("Updated:", time.Now().Format(time.RFC3339))
127
128         fmt.Println("@menu")
129         fmt.Println("Categories:")
130         for _, cat := range cats {
131                 fmt.Printf("* %s (%d items): LinksCat%s\n", cat, len(data[cat]), cat)
132         }
133         fmt.Println("@end menu")
134
135         for _, cat := range cats {
136                 fmt.Println("@node", "LinksCat"+cat)
137                 fmt.Println("@section Links category:", cat)
138                 fmt.Println("@multitable @columnfractions .05 .8 .1 .05")
139                 fmt.Println("@headitem @tab @tab Other categories @tab Feed URLs")
140                 ents := data[cat]
141                 for n, ent := range ents {
142                         catsOther := make([]string, 0)
143                         for _, c := range ent["Category"] {
144                                 if c != cat {
145                                         catsOther = append(catsOther, c)
146                                 }
147                         }
148                         var note string
149                         if len(ent["Note"]) > 0 {
150                                 note = "(" + strings.Trim(ent["Note"][0], " \n") + ")"
151                         }
152                         fmt.Printf(
153                                 "  @item %d @tab @url{%s,, %s} %s @tab %s @tab\n",
154                                 n,
155                                 strings.ReplaceAll(ent["URL"][0], "@", "@@"),
156                                 strings.ReplaceAll(ent["Title"][0], "@", "@@"),
157                                 note,
158                                 strings.Join(catsOther, ", "),
159                         )
160                         switch feeds := ent["Feed"]; len(feeds) {
161                         case 0:
162                                 fmt.Printf("    @emph{STATIC}\n")
163                         case 1:
164                                 fmt.Printf(
165                                         "    @url{%s, feed}\n",
166                                         strings.ReplaceAll(feeds[0], "@", "@@"),
167                                 )
168                         default:
169                                 for i, feed := range feeds {
170                                         fmt.Printf(
171                                                 "    @url{%s, feed%d}\n",
172                                                 strings.ReplaceAll(feed, "@", "@@"), i,
173                                         )
174                                 }
175                         }
176                 }
177                 fmt.Println("@end multitable")
178         }
179 }