121 lines
2.8 KiB
Go
121 lines
2.8 KiB
Go
|
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
|
||
|
}
|