package lasa
import (
"context"
"embed"
"html"
"io"
"reflect"
"text/template"
"time"
site "tangled.org/anhgelus.world/goat-site"
"tangled.org/anhgelus.world/xrpc"
"tangled.org/anhgelus.world/xrpc/atproto"
)
//go:embed rss.xml
var rssTemplate embed.FS
type FeedItem struct {
Title string
Link string
Description string
PubDate string
UpdatedDate string
Author string
Categories []string
}
type FeedData struct {
// required
Title string
Link string
Description string
// optional
LastBuildDate string
Items []FeedItem
Author string
}
type ErrCannotGenerateFeed struct {
v string
}
func (err ErrCannotGenerateFeed) Error() string {
return "cannot generate RSS: " + err.v
}
func GenerateRSS(
ctx context.Context,
client xrpc.Client,
w io.Writer,
author *atproto.DID,
pub xrpc.RecordStored[*site.Publication],
) error {
if pub.Value.Description == nil {
return ErrCannotGenerateFeed{"description is not set"}
}
data, err := genFeedData(ctx, client, author, pub)
if err != nil {
return err
}
return template.Must(template.New("rss").Funcs(map[string]any{
"isSet": IsSet,
}).ParseFS(rssTemplate, "rss.xml")).ExecuteTemplate(w, "rss.xml", data)
}
func genFeedData(
ctx context.Context,
client xrpc.Client,
author *atproto.DID,
pub xrpc.RecordStored[*site.Publication],
) (FeedData, error) {
data := FeedData{
Title: html.EscapeString(pub.Value.Name),
Link: pub.Value.URL.String(),
}
if pub.Value.Description != nil {
data.Description = *pub.Value.Description
}
items, err := ListDocuments(ctx, client, author, pub.URI)
if err != nil {
return data, err
}
doc, err := client.Directory().ResolveDID(ctx, author)
if err != nil {
return data, err
}
handle, ok := doc.Handle()
if ok {
data.Author = handle.String()
}
data.Items = make([]FeedItem, len(items))
for i, item := range items {
url := pub.Value.URL
if item.Value.Path == nil {
return data, ErrCannotGenerateFeed{"path is not set for " + item.Value.Title}
}
url.Path = *item.Value.Path
for i, v := range item.Value.Tags {
item.Value.Tags[i] = html.EscapeString(v)
}
d := FeedItem{
Link: url.String(),
Title: html.EscapeString(item.Value.Title),
PubDate: item.Value.PublishedAt.Format(time.RFC1123),
Categories: item.Value.Tags,
}
if ok {
d.Author = "@" + handle.String()
}
if item.Value.Description != nil {
d.Description = html.EscapeString(*item.Value.Description)
}
if item.Value.UpdatedAt != nil {
d.UpdatedDate = item.Value.UpdatedAt.Format(time.RFC1123)
} else {
d.UpdatedDate = d.PubDate
}
if i == 0 {
data.LastBuildDate = d.UpdatedDate
}
data.Items[i] = d
}
return data, nil
}
func IsSet(v any) bool {
return !reflect.ValueOf(v).IsZero()
}