2020-07-05 02:01:08 +02:00
|
|
|
package news
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-05-16 22:41:33 +02:00
|
|
|
"context"
|
2020-07-05 02:01:08 +02:00
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
|
|
|
"html"
|
2021-08-26 09:31:14 +02:00
|
|
|
"io"
|
2020-07-05 02:01:08 +02:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2021-09-08 22:28:08 +02:00
|
|
|
"github.com/Jguer/yay/v11/pkg/text"
|
2020-07-05 02:01:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type item struct {
|
|
|
|
Title string `xml:"title"`
|
|
|
|
Link string `xml:"link"`
|
|
|
|
Description string `xml:"description"`
|
|
|
|
PubDate string `xml:"pubDate"`
|
|
|
|
Creator string `xml:"dc:creator"`
|
|
|
|
}
|
|
|
|
|
2020-07-27 00:11:24 +02:00
|
|
|
func (item *item) print(buildTime time.Time, all, quiet bool) {
|
2020-07-05 02:01:08 +02:00
|
|
|
var fd string
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
date, err := time.Parse(time.RFC1123Z, item.PubDate)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
} else {
|
|
|
|
fd = text.FormatTime(int(date.Unix()))
|
2020-07-27 00:11:24 +02:00
|
|
|
if !all && !buildTime.IsZero() {
|
2020-07-05 02:01:08 +02:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2021-11-07 00:15:50 +01:00
|
|
|
func PrintNewsFeed(ctx context.Context, client *http.Client, cutOffDate time.Time, bottomUp, all, quiet bool) error {
|
2022-08-14 19:41:54 +02:00
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://archlinux.org/feeds/news", http.NoBody)
|
2021-05-16 22:41:33 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
2020-07-05 02:01:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer resp.Body.Close()
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2021-08-26 09:31:14 +02:00
|
|
|
body, err := io.ReadAll(resp.Body)
|
2020-07-05 02:01:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rssGot := rss{}
|
|
|
|
|
|
|
|
d := xml.NewDecoder(bytes.NewReader(body))
|
2021-08-11 20:13:28 +02:00
|
|
|
if err := d.Decode(&rssGot); err != nil {
|
2020-07-05 02:01:08 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-11-07 00:15:50 +01:00
|
|
|
if bottomUp {
|
2020-07-05 02:01:08 +02:00
|
|
|
for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- {
|
2020-07-27 00:44:31 +02:00
|
|
|
rssGot.Channel.Items[i].print(cutOffDate, all, quiet)
|
2020-07-05 02:01:08 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for i := 0; i < len(rssGot.Channel.Items); i++ {
|
2020-07-27 00:44:31 +02:00
|
|
|
rssGot.Channel.Items[i].print(cutOffDate, all, quiet)
|
2020-07-05 02:01:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Crude html parsing, good enough for the arch news
|
|
|
|
// This is only displayed in the terminal so there should be no security
|
2021-08-11 20:13:28 +02:00
|
|
|
// concerns.
|
2020-07-05 02:01:08 +02:00
|
|
|
func parseNews(str string) string {
|
2021-08-11 20:13:28 +02:00
|
|
|
var (
|
|
|
|
buffer bytes.Buffer
|
|
|
|
tagBuffer bytes.Buffer
|
|
|
|
escapeBuffer bytes.Buffer
|
|
|
|
inTag = false
|
|
|
|
inEscape = false
|
|
|
|
)
|
2020-07-05 02:01:08 +02:00
|
|
|
|
|
|
|
for _, char := range str {
|
|
|
|
if inTag {
|
|
|
|
if char == '>' {
|
|
|
|
inTag = false
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
switch tagBuffer.String() {
|
|
|
|
case "code":
|
|
|
|
buffer.WriteString(text.CyanCode)
|
|
|
|
case "/code":
|
|
|
|
buffer.WriteString(text.ResetCode)
|
|
|
|
case "/p":
|
|
|
|
buffer.WriteRune('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
tagBuffer.WriteRune(char)
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if inEscape {
|
|
|
|
if char == ';' {
|
|
|
|
inEscape = false
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
escapeBuffer.WriteRune(char)
|
|
|
|
s := html.UnescapeString(escapeBuffer.String())
|
|
|
|
buffer.WriteString(s)
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
escapeBuffer.WriteRune(char)
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if char == '<' {
|
|
|
|
inTag = true
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
tagBuffer.Reset()
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if char == '&' {
|
|
|
|
inEscape = true
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
escapeBuffer.Reset()
|
|
|
|
escapeBuffer.WriteRune(char)
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer.WriteRune(char)
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer.WriteString(text.ResetCode)
|
2021-08-11 20:13:28 +02:00
|
|
|
|
2020-07-05 02:01:08 +02:00
|
|
|
return buffer.String()
|
|
|
|
}
|