123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package news
- import (
- "bytes"
- "encoding/xml"
- "fmt"
- "html"
- "io/ioutil"
- "net/http"
- "os"
- "strings"
- "time"
- "github.com/Jguer/go-alpm"
- "github.com/Jguer/yay/v10/pkg/query"
- "github.com/Jguer/yay/v10/pkg/settings"
- "github.com/Jguer/yay/v10/pkg/text"
- )
- type item struct {
- Title string `xml:"title"`
- Link string `xml:"link"`
- Description string `xml:"description"`
- PubDate string `xml:"pubDate"`
- Creator string `xml:"dc:creator"`
- }
- func (item *item) print(buildTime time.Time, all, quiet bool) {
- var fd string
- date, err := time.Parse(time.RFC1123Z, item.PubDate)
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- } else {
- fd = text.FormatTime(int(date.Unix()))
- if !all && !buildTime.IsZero() {
- if buildTime.After(date) {
- return
- }
- }
- }
- fmt.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title)))
- if !quiet {
- desc := strings.TrimSpace(parseNews(item.Description))
- fmt.Println(desc)
- }
- }
- type channel struct {
- Title string `xml:"title"`
- Link string `xml:"link"`
- Description string `xml:"description"`
- Language string `xml:"language"`
- Lastbuilddate string `xml:"lastbuilddate"`
- Items []item `xml:"item"`
- }
- type rss struct {
- Channel channel `xml:"channel"`
- }
- func PrintNewsFeed(alpmHandle *alpm.Handle, sortMode int, double, quiet bool) error {
- resp, err := http.Get("https://archlinux.org/feeds/news")
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
- rssGot := rss{}
- d := xml.NewDecoder(bytes.NewReader(body))
- err = d.Decode(&rssGot)
- if err != nil {
- return err
- }
- buildTime, err := lastBuildTime(alpmHandle)
- if err != nil {
- return err
- }
- if sortMode == settings.BottomUp {
- for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- {
- rssGot.Channel.Items[i].print(buildTime, double, quiet)
- }
- } else {
- for i := 0; i < len(rssGot.Channel.Items); i++ {
- rssGot.Channel.Items[i].print(buildTime, double, quiet)
- }
- }
- return nil
- }
- func lastBuildTime(alpmHandle *alpm.Handle) (time.Time, error) {
- var lastTime time.Time
- pkgs, _, _, _, err := query.FilterPackages(alpmHandle)
- if err != nil {
- return lastTime, err
- }
- for _, pkg := range pkgs {
- thisTime := pkg.BuildDate()
- if thisTime.After(lastTime) {
- lastTime = thisTime
- }
- }
- return lastTime, nil
- }
- // Crude html parsing, good enough for the arch news
- // This is only displayed in the terminal so there should be no security
- // concerns
- func parseNews(str string) string {
- var buffer bytes.Buffer
- var tagBuffer bytes.Buffer
- var escapeBuffer bytes.Buffer
- inTag := false
- inEscape := false
- for _, char := range str {
- if inTag {
- if char == '>' {
- inTag = false
- switch tagBuffer.String() {
- case "code":
- buffer.WriteString(text.CyanCode)
- case "/code":
- buffer.WriteString(text.ResetCode)
- case "/p":
- buffer.WriteRune('\n')
- }
- continue
- }
- tagBuffer.WriteRune(char)
- continue
- }
- if inEscape {
- if char == ';' {
- inEscape = false
- escapeBuffer.WriteRune(char)
- s := html.UnescapeString(escapeBuffer.String())
- buffer.WriteString(s)
- continue
- }
- escapeBuffer.WriteRune(char)
- continue
- }
- if char == '<' {
- inTag = true
- tagBuffer.Reset()
- continue
- }
- if char == '&' {
- inEscape = true
- escapeBuffer.Reset()
- escapeBuffer.WriteRune(char)
- continue
- }
- buffer.WriteRune(char)
- }
- buffer.WriteString(text.ResetCode)
- return buffer.String()
- }
|