Skip to content

Commit 1a697b8

Browse files
committed
Major refactoring
1 parent f18969b commit 1a697b8

File tree

3 files changed

+146
-106
lines changed

3 files changed

+146
-106
lines changed

cmd/unsavory/main.go

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,26 @@ package main
22

33
import (
44
"flag"
5-
"fmt"
6-
"os"
5+
"log"
76

8-
pb "github.com/citizen428/unsavory/internal/pinboard"
7+
un "github.com/citizen428/unsavory/internal/unsavory"
98
)
109

11-
var token = flag.String("token", "", "Pinboard API token")
10+
var (
11+
dryRun = flag.Bool("dry-run", false, "Enables dry run mode")
12+
token = flag.String("token", "", "Pinboard API token")
13+
)
14+
15+
func init() {
16+
log.SetFlags(0)
17+
}
1218

1319
func main() {
1420
flag.Parse()
1521
if *token == "" {
16-
fmt.Fprintf(os.Stderr, "missing required API token\n")
17-
os.Exit(1)
22+
log.Fatalln("Missing required API token")
1823
}
1924

20-
client := pb.NewClient(*token)
21-
urls := client.GetAllURLs()
22-
results := make(chan pb.CheckResponse)
23-
fmt.Printf("Retrieved %d URLS\n", len(urls))
24-
25-
for _, url := range urls {
26-
go client.CheckURL(url, results)
27-
}
28-
29-
var result pb.CheckResponse
30-
for range urls {
31-
result = <-results
32-
if result.StatusCode == 404 {
33-
fmt.Printf("Deleting %s\n", result.URL)
34-
client.DeleteURL(result.URL)
35-
}
36-
}
25+
un := un.NewClient(*token, *dryRun)
26+
un.Run()
3727
}

internal/pinboard/pinboard.go

Lines changed: 0 additions & 83 deletions
This file was deleted.

internal/unsavory/unsavory.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Package unsavory provides the types, functions and methods for interacting
2+
// with the Pinboard API and checking URLs for 404 status codes.
3+
package unsavory
4+
5+
import (
6+
"encoding/json"
7+
"fmt"
8+
"log"
9+
"net/http"
10+
"strings"
11+
"time"
12+
)
13+
14+
const (
15+
baseURL = "https://api.pinboard.in/v1"
16+
userAgent = "UnsavoryNG"
17+
)
18+
19+
// Client bundles all values necessary for API requests.
20+
type Client struct {
21+
baseQuery string
22+
client *http.Client
23+
24+
DryRun bool
25+
Token string
26+
}
27+
28+
type checkResponse struct {
29+
URL string
30+
Status string
31+
StatusCode int
32+
}
33+
34+
// NewClient returns a configured unsavory.Client.
35+
func NewClient(token string, dryRun bool) *Client {
36+
client := &http.Client{
37+
Timeout: time.Second * 10,
38+
}
39+
40+
return &Client{
41+
baseQuery: fmt.Sprintf("format=json&auth_token=%s", token),
42+
Token: token,
43+
DryRun: dryRun,
44+
client: client}
45+
}
46+
47+
// Run fetches all URLs, checks them individually and removes saved links return
48+
// a 404 status code.
49+
func (c *Client) Run() {
50+
if c.DryRun {
51+
log.Printf("You are using dry run mode. No links will be deleted!\n\n")
52+
}
53+
54+
log.Println("Retrieving URLs")
55+
urls := c.getAllURLs()
56+
log.Printf("Retrieved %d URLS\n", len(urls))
57+
58+
results := make(chan checkResponse)
59+
for _, url := range urls {
60+
go c.checkURL(url, results)
61+
}
62+
63+
var result checkResponse
64+
for range urls {
65+
result = <-results
66+
switch result.StatusCode {
67+
case 200:
68+
continue
69+
case 404:
70+
log.Printf("Deleting %s\n", result.URL)
71+
c.deleteURL(result.URL)
72+
default:
73+
log.Printf("%s:%s\n", result.Status, result.URL)
74+
}
75+
}
76+
}
77+
78+
func (c *Client) getAllURLs() []string {
79+
var posts []struct {
80+
URL string `json:"href"`
81+
}
82+
83+
resp := c.request("/posts/all")
84+
defer resp.Body.Close()
85+
86+
if resp.StatusCode != 200 {
87+
log.Fatalln("Could not retrieve URLs!\nPlease check your API token.")
88+
}
89+
90+
json.NewDecoder(resp.Body).Decode(&posts)
91+
92+
urls := make([]string, len(posts))
93+
94+
for i, post := range posts {
95+
urls[i] = post.URL
96+
}
97+
return urls
98+
}
99+
100+
func (c *Client) checkURL(url string, results chan<- checkResponse) {
101+
resp, err := c.client.Head(url)
102+
if err != nil {
103+
results <- checkResponse{url, err.Error(), 0}
104+
return
105+
}
106+
results <- checkResponse{url, resp.Status, resp.StatusCode}
107+
}
108+
109+
func (c *Client) deleteURL(url string) {
110+
if !c.DryRun {
111+
c.request("/posts/delete", fmt.Sprintf("url=%s", url))
112+
}
113+
}
114+
115+
func (c *Client) request(path string, query ...string) *http.Response {
116+
url := fmt.Sprintf("%s%s?%s", baseURL, path, c.baseQuery)
117+
if len(query) > 0 {
118+
url = url + "&" + strings.Join(query, "&")
119+
}
120+
121+
req, err := http.NewRequest("GET", url, nil)
122+
if err != nil {
123+
log.Fatalln(err)
124+
}
125+
req.Header.Set("User-Agent", userAgent)
126+
127+
resp, err := c.client.Do(req)
128+
if err != nil {
129+
log.Fatalln(err)
130+
}
131+
132+
return resp
133+
}

0 commit comments

Comments
 (0)