Skip to content

Commit ecc51e4

Browse files
committed
Add generator for Apple Security Research blog
1 parent 5b688a3 commit ecc51e4

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

cmd/feedgen/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ func main() {
5656
MaxLength: 50,
5757
}
5858
gen = &g
59+
case "asr":
60+
gen = &generators.AppleSecurityGenerator{}
5961
default:
6062
log.Println("Missing valid feed type")
6163
flag.Usage()
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package generators
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"github.com/gorilla/feeds"
7+
"golang.org/x/net/html"
8+
"net/http"
9+
"time"
10+
)
11+
12+
type AppleSecurityGenerator struct {
13+
filterFunc ItemFilterFunc
14+
itemModFunc ItemModifierFunc
15+
}
16+
type Page struct {
17+
Props struct {
18+
PageProps struct {
19+
Blogs []BlogEntry `json:"blogs"`
20+
} `json:"pageProps"`
21+
} `json:"props"`
22+
}
23+
24+
type BlogEntry struct {
25+
Id string `json:"id"`
26+
Slug string `json:"slug"`
27+
Title string `json:"title"`
28+
Description string `json:"description"`
29+
Date string `json:"date"`
30+
}
31+
32+
func (e BlogEntry) Time() time.Time {
33+
t, _ := time.Parse("2006-01-02", e.Date)
34+
return t
35+
}
36+
37+
func (e BlogEntry) Link() string {
38+
return fmt.Sprintf("https://security.apple.com/blog/%s", e.Slug)
39+
}
40+
41+
func (g *AppleSecurityGenerator) RegisterItemFilter(callback ItemFilterFunc) {
42+
g.filterFunc = callback
43+
}
44+
func (g *AppleSecurityGenerator) RegisterItemModifier(callback ItemModifierFunc) {
45+
g.itemModFunc = callback
46+
}
47+
48+
func (g *AppleSecurityGenerator) Feed() (*feeds.Feed, error) {
49+
page, err := g.getParsedPage()
50+
if err != nil {
51+
return nil, err
52+
}
53+
54+
feed := feeds.Feed{
55+
Title: "Apple Security Research",
56+
Link: &feeds.Link{Href: "https://security.apple.com/blog/"},
57+
Updated: time.Now(),
58+
}
59+
60+
for _, blog := range page.Props.PageProps.Blogs {
61+
item := &feeds.Item{
62+
Title: blog.Title,
63+
Link: &feeds.Link{Href: blog.Link()},
64+
Created: blog.Time(),
65+
Updated: blog.Time(),
66+
Content: blog.Description,
67+
}
68+
if g.filterFunc != nil && !g.filterFunc(item) {
69+
continue
70+
}
71+
if g.itemModFunc != nil {
72+
g.itemModFunc(item)
73+
}
74+
feed.Items = append(feed.Items, item)
75+
}
76+
return &feed, nil
77+
}
78+
79+
func getJsonNode(nodes *html.Node) *html.Node {
80+
if nodes.Type == html.ElementNode && nodes.Data == "script" {
81+
for _, attr := range nodes.Attr {
82+
if attr.Key == "type" && attr.Val == "application/json" {
83+
return nodes
84+
}
85+
}
86+
}
87+
for c := nodes.FirstChild; c != nil; c = c.NextSibling {
88+
if data := getJsonNode(c); data != nil {
89+
return data
90+
}
91+
}
92+
return nil
93+
}
94+
95+
func (g *AppleSecurityGenerator) getParsedPage() (*Page, error) {
96+
res, err := http.Get("https://security.apple.com/blog/")
97+
if err != nil {
98+
return nil, err
99+
}
100+
101+
if res.StatusCode != 200 {
102+
return nil, fmt.Errorf("failed to get Apple Security blog: %d", res.StatusCode)
103+
}
104+
105+
defer func() { _ = res.Body.Close() }()
106+
107+
nodes, err := html.Parse(res.Body)
108+
if err != nil {
109+
return nil, err
110+
}
111+
112+
jsonNode := getJsonNode(nodes)
113+
if jsonNode == nil {
114+
return nil, fmt.Errorf("failed to find JSON node")
115+
}
116+
117+
var page Page
118+
if err = json.Unmarshal([]byte(jsonNode.FirstChild.Data), &page); err != nil {
119+
return nil, err
120+
}
121+
return &page, nil
122+
}

0 commit comments

Comments
 (0)