hunter/hunter.go

121 lines
2.8 KiB
Go
Raw Normal View History

2024-07-05 09:14:10 +01:00
package main
import (
"fmt"
"log/slog"
"os"
"strconv"
"github.com/adrg/frontmatter"
"github.com/alecthomas/kong"
"github.com/gocolly/colly/v2"
)
// ToDo: emoji in text support
// ToDo: attachment support?
// ToDo: polls support?
type Status struct {
Username string
DisplayName string
ProfileURL string
AvatarURL string
Text string
Published string
ReplyCount int
FavouriteCount int
BoostCount int
}
func main() {
ctx := kong.Parse(&cli)
if cli.Debug {
slog.SetLogLoggerLevel(slog.LevelDebug)
}
if err := ctx.Run(); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
os.Exit(1)
}
}
func getStatusesForURL(url string) []*Status {
statuses := []*Status{}
c := colly.NewCollector()
c.OnHTML("article.status", func(e *colly.HTMLElement) {
replyCount, _ := strconv.Atoi(e.ChildText("aside.status-info div[title=Replies] dd"))
favouriteCount, _ := strconv.Atoi(e.ChildText("aside.status-info div[title=Faves] dd"))
boostCount, _ := strconv.Atoi(e.ChildText("aside.status-info div[title=Boosts] dd"))
status := &Status{
Username: e.ChildText("address div.author-strap span.username"),
DisplayName: e.ChildText("address div.author-strap span.displayname"),
ProfileURL: e.ChildAttr("address a", "href"),
AvatarURL: e.ChildAttr("address img.avatar", "src"),
Text: e.ChildText("div.status-body div.content"),
Published: e.ChildAttr("aside.status-info div.published-at time", "datetime"),
ReplyCount: replyCount,
FavouriteCount: favouriteCount,
BoostCount: boostCount,
}
slog.Debug("Status", "status", status)
statuses = append(statuses, status)
})
c.Visit(url)
return statuses
}
func getValForFile(filename, key string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", fmt.Errorf("cannot open file %q: %w", filename, err)
}
defer f.Close()
m := map[string]any{}
if _, err := frontmatter.Parse(f, &m); err != nil {
return "", fmt.Errorf("cannot parse frontmatter for %q: %w", filename, err)
}
slog.Debug("Frontmatter parsed", "filename", filename, "map", m)
if v, ok := m[key]; ok {
if vstr, ok := v.(string); ok {
return vstr, nil
}
}
return "", nil
}
func (u *statusesCmd) Run() error {
statuses := getStatusesForURL(u.URL)
for _, s := range statuses {
txt := s.Text
if len(txt) > 30 {
txt = txt[:27] + "..."
}
slog.Info("Status", "username", s.Username, "display name", s.DisplayName, "text", txt)
}
return nil
}
func (p *parseCmd) Run() error {
v, err := getValForFile(p.File, p.Key)
if err != nil {
return fmt.Errorf("cannot get value for key %q and file %q: %w", p.Key, p.File, err)
}
if v != "" {
fmt.Fprintf(os.Stdout, "Value for key %q is %q\n", p.Key, v)
}
return nil
}