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 }