Add feed generating capabilities
`feed.tmpl.xml` should be RSS2.0-conforming. Ideally each news item would have a date of publication, but RSS doesn't require it. I found it easier to copy `genblog.go` to `genfeed.go` than to shoehorn in feed logic to the blog generator. As such, `genfeed.go` might have some unnecessary stuff in it (though the go compiler didn't complain, so who knows?!). I also added the necessary machinery in `generate_homepage`. Of course, I did very minimal testing.pull/1/head
parent
3feec1a252
commit
eca0447a35
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>tilde.town blog</title>
|
||||||
|
<description>web log of tilde town</description>
|
||||||
|
<link>https://tilde.town/blog.html</link>
|
||||||
|
<atom:link rel="self" type="application/rss+xml" href="https://tilde.town/blog.xml"/>
|
||||||
|
{{ range .News }}
|
||||||
|
<item>
|
||||||
|
<title>{{.Title}}</title>
|
||||||
|
<pubDate>{{.Pubdate}}</pubDate>
|
||||||
|
<description>
|
||||||
|
<![CDATA[{{.Content}}]]>
|
||||||
|
</description>
|
||||||
|
<guid isPermalink="false">{{.Pubdate}}-{{.Title}}</guid>
|
||||||
|
</item>
|
||||||
|
{{ end }}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
|
@ -6,5 +6,6 @@ set -e
|
||||||
|
|
||||||
cd /town/src/tilde.town
|
cd /town/src/tilde.town
|
||||||
/usr/bin/go run genblog.go > blog.html
|
/usr/bin/go run genblog.go > blog.html
|
||||||
|
/usr/bin/go run genfeed.go > blog.xml
|
||||||
/usr/bin/go run genusers.go > users.html
|
/usr/bin/go run genusers.go > users.html
|
||||||
/bin/cp index.html blog.html users.html blog.css style.css /var/www/tilde.town/
|
/bin/cp index.html blog.html blog.xml users.html blog.css style.css /var/www/tilde.town/
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"sort"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
const statsPath = "/usr/local/bin/stats"
|
||||||
|
|
||||||
|
//go:embed feed.tmpl.xml
|
||||||
|
var feedTmpl string
|
||||||
|
|
||||||
|
type newsEntry struct {
|
||||||
|
Title string // Title of entry
|
||||||
|
Pubdate string // Human readable date
|
||||||
|
Content string // HTML of entry
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Username string
|
||||||
|
Default bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type tildeData struct {
|
||||||
|
News []newsEntry
|
||||||
|
Users []User
|
||||||
|
ActiveUsers []string `json:"active_users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ByName []User
|
||||||
|
|
||||||
|
func (n ByName) Len() int { return len(n) }
|
||||||
|
func (n ByName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||||
|
func (n ByName) Less(i, j int) bool { return n[i].Username < n[j].Username }
|
||||||
|
|
||||||
|
func _main() error {
|
||||||
|
data, err := stats()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type tmplData struct {
|
||||||
|
News []newsEntry
|
||||||
|
Lights string
|
||||||
|
}
|
||||||
|
|
||||||
|
td := &tmplData{
|
||||||
|
News: data.News,
|
||||||
|
Lights: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(ByName(data.Users))
|
||||||
|
|
||||||
|
isActive := func(username string) bool {
|
||||||
|
for _, u := range data.ActiveUsers {
|
||||||
|
if u == username {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range data.Users {
|
||||||
|
if isActive(u.Username) {
|
||||||
|
td.Lights += fmt.Sprintf("<a href=\"/~%s\">*</a>", u.Username)
|
||||||
|
} else if !u.Default {
|
||||||
|
td.Lights += fmt.Sprintf("<a href=\"/~%s\">+</a>", u.Username)
|
||||||
|
} else {
|
||||||
|
td.Lights += "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := template.New("feed").Parse(feedTmpl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse the feed template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := bytes.Buffer{}
|
||||||
|
if err = t.Execute(&out, td); err != nil {
|
||||||
|
return fmt.Errorf("failed to render feed template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(out.String())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stats() (*tildeData, error) {
|
||||||
|
sout := bytes.Buffer{}
|
||||||
|
cmd := exec.Command(statsPath)
|
||||||
|
cmd.Stdout = &sout
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data tildeData
|
||||||
|
|
||||||
|
err = json.Unmarshal(sout.Bytes(), &data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := _main()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue