Files
backtone/main.go
T
2026-01-04 05:37:43 -05:00

109 lines
3.9 KiB
Go

package main
import (
"encoding/base64"
"encoding/xml"
"fmt"
"os"
"github.com/gorilla/feeds"
"github.com/rwinkhart/backtone/atom"
"github.com/rwinkhart/backtone/bthtml"
"github.com/rwinkhart/go-boilerplate/other"
)
const cfgPath = "/etc/backtone.xml"
// feedFieldInfo provides the string value for a given field in the parent feed
// and specifies which index in the regex capture group to extract the same field
// from per individual news item.
// An index of -1 can be used to use the same value as the parent feed.
// An index of -2 can be used to use a default (placeholder) value.
type feedFieldInfoT struct {
Parent string `xml:"parent,omitempty"`
CaptureGroupConnector []string `xml:"captureGroupConnector,omitempty"`
CaptureGroupIndex []int8 `xml:"captureGroupIndex,omitempty"`
}
type feedT struct {
Title feedFieldInfoT `xml:"title"`
Link feedFieldInfoT `xml:"link"`
Description feedFieldInfoT `xml:"description"`
AuthorName feedFieldInfoT `xml:"authorName"`
AuthorEmail feedFieldInfoT `xml:"authorEmail"`
}
type inputT struct {
LoadSeconds float32 `xml:"loadSeconds"`
Regex string `xml:"regex"`
FlareSolverrURL string `xml:"flareSolverrURL"`
MaxFeedItems int `xml:"maxFeedItems"`
Feed feedT `xml:"feed"`
}
func main() {
// handle input argument (XML)
if len(os.Args) < 2 {
demoInputBytes, _ := xml.MarshalIndent(
inputT{
LoadSeconds: 2.5,
Regex: "PLACEHOLDER",
FlareSolverrURL: "http://127.0.0.1:8191",
MaxFeedItems: 10,
Feed: feedT{
Title: feedFieldInfoT{Parent: "Demo Title", CaptureGroupIndex: []int8{0}},
Link: feedFieldInfoT{Parent: "https://example.com/", CaptureGroupIndex: []int8{1}},
Description: feedFieldInfoT{Parent: "Demo description", CaptureGroupConnector: []string{"Prefix - ", " - Middle - ", " - Suffix"}, CaptureGroupIndex: []int8{1, 3}},
AuthorName: feedFieldInfoT{Parent: "John Doe", CaptureGroupIndex: []int8{3}},
AuthorEmail: feedFieldInfoT{Parent: "johndoe@example.com", CaptureGroupIndex: []int8{4}},
},
},
"",
" ",
)
other.PrintError(os.Args[0]+" must be called with valid base64-encoded XML input using the following schema:\n\n"+string(demoInputBytes), 1)
}
// decode base64 input XML
decodedXML, err := base64.StdEncoding.DecodeString(os.Args[1])
if err != nil {
other.PrintError("Failed to decode base64 input XML: "+err.Error(), 1)
}
var inputInfo inputT
if err := xml.Unmarshal(decodedXML, &inputInfo); err != nil {
other.PrintError("Failed to unmarshal input XML: "+err.Error(), 1)
}
rawHTML, err := bthtml.GetFromURL(inputInfo.FlareSolverrURL, inputInfo.Feed.Link.Parent, inputInfo.LoadSeconds)
if err != nil {
other.PrintError("Failed to get HTML content: "+err.Error(), 1)
}
atomXML, err := atom.GetFromHTML(
&feeds.Feed{
Title: inputInfo.Feed.Title.Parent,
Link: &feeds.Link{Href: inputInfo.Feed.Link.Parent},
Description: inputInfo.Feed.Description.Parent,
Author: &feeds.Author{Name: inputInfo.Feed.AuthorName.Parent, Email: inputInfo.Feed.AuthorEmail.Parent},
},
&rawHTML,
inputInfo.Regex,
atom.IndicesT{
TitleC: inputInfo.Feed.Title.CaptureGroupConnector,
TitleI: inputInfo.Feed.Title.CaptureGroupIndex,
LinkC: inputInfo.Feed.Link.CaptureGroupConnector,
LinkI: inputInfo.Feed.Link.CaptureGroupIndex,
DescriptionC: inputInfo.Feed.Description.CaptureGroupConnector,
DescriptionI: inputInfo.Feed.Description.CaptureGroupIndex,
AuthorNameC: inputInfo.Feed.AuthorName.CaptureGroupConnector,
AuthorNameI: inputInfo.Feed.AuthorName.CaptureGroupIndex,
AuthorEmailC: inputInfo.Feed.AuthorEmail.CaptureGroupConnector,
AuthorEmailI: inputInfo.Feed.AuthorEmail.CaptureGroupIndex,
},
inputInfo.MaxFeedItems,
)
if err != nil {
other.PrintError("Failed to generate Atom RSS feed from HTML:\n\n"+rawHTML+"\n\n"+err.Error(), 1)
}
fmt.Println(*atomXML)
}