Signed-off-by: jolheiser <john.olheiser@gmail.com>
main
jolheiser 2022-10-25 11:36:10 -05:00
parent 8ec7b3fb52
commit a54e766bd3
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
6 changed files with 170 additions and 70 deletions

2
.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
/trending
/trending.exe

65
config.go 100644
View File

@ -0,0 +1,65 @@
package main
import (
"errors"
"fmt"
"io/fs"
"os"
"github.com/adrg/xdg"
"gopkg.in/yaml.v3"
)
type config struct {
Languages []struct {
Name string `yaml:"name"`
Timeframes []string `yaml:"timeframes"`
} `yaml:"languages"`
}
func loadConfig() (*config, error) {
cfgPath, err := xdg.ConfigFile("trending/config.yaml")
if err != nil {
return nil, fmt.Errorf("could not get config file path: %w", err)
}
fi, err := os.Open(cfgPath)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("could not open config file: %w", err)
}
if err := createDefaultConfig(cfgPath); err != nil {
return nil, fmt.Errorf("could not create default config: %w", err)
}
return loadConfig()
}
defer fi.Close()
var cfg config
if err := yaml.NewDecoder(fi).Decode(&cfg); err != nil {
return nil, fmt.Errorf("could not decode config: %w", err)
}
return &cfg, nil
}
func createDefaultConfig(cfgPath string) error {
fi, err := os.Create(cfgPath)
if err != nil {
return err
}
defer fi.Close()
_, err = fi.Write(defaultConfig)
return err
}
var defaultConfig = []byte(`languages:
- name: go
timeframes:
- daily
- weekly
- monthly
- name: rust
timeframes:
- daily
- weekly
- monthly
`)

2
go.mod
View File

@ -4,8 +4,10 @@ go 1.19
require (
github.com/PuerkitoBio/goquery v1.8.0
github.com/adrg/xdg v0.4.0
github.com/caarlos0/log v0.1.10
github.com/charmbracelet/lipgloss v0.6.0
gopkg.in/yaml.v3 v3.0.1
)
require (

15
go.sum
View File

@ -1,5 +1,7 @@
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
@ -9,6 +11,8 @@ github.com/caarlos0/log v0.1.10 h1:kHKiXTKEeK019o7QQWXRbHVKFrYYljxuQ7vF2taEA3M=
github.com/caarlos0/log v0.1.10/go.mod h1:BLxpdZKXvWBjB6fshua4c8d7ApdYjypEDok6ibt+pXk=
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
@ -26,18 +30,29 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

82
main.go
View File

@ -1,11 +1,6 @@
package main
import (
"fmt"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/caarlos0/log"
"github.com/charmbracelet/lipgloss"
)
@ -13,80 +8,27 @@ import (
var Version = "develop"
func main() {
languages := []string{"go", "rust"}
cfg, err := loadConfig()
if err != nil {
log.WithError(err).Error("could not load config")
}
for _, lang := range languages {
l, err := trendingLang(lang, []string{"daily", "weekly", "monthly"})
for _, lang := range cfg.Languages {
l, err := trendingLang(lang.Name, lang.Timeframes)
if err != nil {
log.WithError(err).Fatal("could not get trending repositories")
}
log.Styles[log.InfoLevel] = lipgloss.NewStyle().Foreground(lipgloss.Color(l.color)).Bold(true)
log.Info(lang)
log.Info(lang.Name)
log.IncreasePadding()
for _, repo := range l.repositories {
log.WithField("link", repo.link()).Info(repo.name)
if repo.description != "" {
log.IncreasePadding()
log.Info(repo.description)
log.DecreasePadding()
}
}
log.ResetPadding()
}
}
func trendingLang(lang string, timeframes []string) (language, error) {
l := language{
repositories: make([]repository, 0),
}
repoMap := make(map[repository]struct{})
for _, timeframe := range timeframes {
la, err := trendingRepos(lang, timeframe)
if err != nil {
return la, err
}
l.color = la.color
for _, r := range la.repositories {
repoMap[r] = struct{}{}
}
}
for repo := range repoMap {
l.repositories = append(l.repositories, repo)
}
return l, nil
}
func trendingRepos(lang, timeframe string) (language, error) {
u := fmt.Sprintf("https://github.com/trending/%s?since=%s", lang, timeframe)
res, err := http.Get(u)
if err != nil {
return language{}, err
}
defer res.Body.Close()
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
return language{}, err
}
var l language
doc.Find(".Box-row").Each(func(_ int, s *goquery.Selection) {
if l.color == "" {
rlcStyle, _ := s.Find(".repo-language-color").Attr("style")
l.color = strings.Fields(rlcStyle)[1]
}
e := s.Find("h1 a")
repo := strings.Join(strings.Fields(e.Text()), "")
l.repositories = append(l.repositories, repository{name: repo})
})
return l, nil
}
type language struct {
color string
repositories []repository
}
type repository struct {
name string
}
func (r repository) link() string {
return fmt.Sprintf("https://github.com/%s", r.name)
}

74
trending.go 100644
View File

@ -0,0 +1,74 @@
package main
import (
"fmt"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
func trendingLang(lang string, timeframes []string) (language, error) {
l := language{
repositories: make([]repository, 0),
}
repoMap := make(map[repository]struct{})
for _, timeframe := range timeframes {
la, err := trendingRepos(lang, timeframe)
if err != nil {
return la, err
}
l.color = la.color
for _, r := range la.repositories {
repoMap[r] = struct{}{}
}
}
for repo := range repoMap {
l.repositories = append(l.repositories, repo)
}
return l, nil
}
func trendingRepos(lang, timeframe string) (language, error) {
u := fmt.Sprintf("https://github.com/trending/%s?since=%s", lang, timeframe)
res, err := http.Get(u)
if err != nil {
return language{}, err
}
defer res.Body.Close()
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
return language{}, err
}
var l language
doc.Find(".Box-row").Each(func(_ int, s *goquery.Selection) {
if l.color == "" {
rlcStyle, _ := s.Find(".repo-language-color").Attr("style")
l.color = strings.Fields(rlcStyle)[1]
}
e := s.Find("h1 a")
repo := strings.Join(strings.Fields(e.Text()), "")
d := strings.TrimSpace(s.Find("p").Text())
l.repositories = append(l.repositories, repository{name: repo, description: d})
})
return l, nil
}
type language struct {
color string
repositories []repository
}
type repository struct {
name string
description string
}
func (r repository) link() string {
return fmt.Sprintf("https://github.com/%s", r.name)
}