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.
This commit is contained in:
		
							parent
							
								
									3feec1a252
								
							
						
					
					
						commit
						eca0447a35
					
				
							
								
								
									
										19
									
								
								feed.tmpl.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								feed.tmpl.xml
									
									
									
									
									
										Normal file
									
								
							| @ -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/ | ||||||
|  | |||||||
							
								
								
									
										121
									
								
								genfeed.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								genfeed.go
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user