-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
165 lines (137 loc) · 4.16 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Main logic
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
// All types are just clones of the JSON returned by a Tweet request call
type Media struct {
Type string `json:"type"`
MediaURLHttps string `json:"media_url_https"`
ExpandedURL string `json:"expanded_url"`
}
type Entities struct {
Media []Media `json:"media"`
}
type User struct {
ScreenName string `json:"screen_name"`
}
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
type Tweet struct {
CreatedAt string `json:"created_at"`
DisplayTextRange []int `json:"display_text_range"`
FullText string `json:"full_text"`
Entities Entities `json:"extended_entities"`
User User `json:"user"`
Errors []Error `json:"errors"`
}
func (t *Tweet) CheckMedia() {
// The HTML display template only writes Media.MediaURLHttps, which is ideal
// for directly linking to images, but links to the thumbnail for any other
// media type. This procedure assigns Media.ExpandedURL to Media.MediaURLHTTPS
// for non-image media, so that it may be accessed properly.
for i, _ := range t.Entities.Media {
if t.Entities.Media[i].Type != "photo" {
t.Entities.Media[i].MediaURLHttps = t.Entities.Media[i].ExpandedURL
}
}
}
func (t *Tweet) Print(w http.ResponseWriter) {
// Displays a tweet in plaintext format.
x, y := t.DisplayTextRange[0], t.DisplayTextRange[1]
fmt.Fprintln(w, t.User.ScreenName)
fmt.Fprintln(w, t.CreatedAt)
fmt.Fprintln(w, t.FullText[x:y])
fmt.Fprintln(w, "--")
for _, m := range t.Entities.Media {
fmt.Fprintln(w, m.Type, m.MediaURLHttps)
}
}
func (t *Tweet) PrintError(w http.ResponseWriter) {
// Print out all error messages
for _, e := range t.Errors {
fmt.Fprintln(w, e.Message)
}
}
func (t *Tweet) PrintJSON(w http.ResponseWriter) {
// Displays a tweet in JSON format.
s, err := json.Marshal(t)
if err != nil {
log.Println(err)
return
}
fmt.Fprintln(w, string(s))
}
func (t *Tweet) PrintHTML(w http.ResponseWriter) {
// Displays a tweet as HTML.
x, y := t.DisplayTextRange[0], t.DisplayTextRange[1]
t.FullText = t.FullText[x:y]
TweetTemplate.Execute(w, t)
}
func serve(w http.ResponseWriter, r *http.Request) {
// This is the solitary HTTP listener function. When provided no arguments,
// it prints the form HTMLSearch as a web interface. It prints the tweet
// pointed to by url= as format= otherwise. The default format is plaintext,
// for simple command-line parsing, but the default format provided by the
// form is HTML.
u, format := "", ""
t := Tweet{}
k, _ := r.URL.Query()["url"]
if len(k) > 0 {
u = k[0]
}
k, _ = r.URL.Query()["format"]
if len(k) > 0 {
format = strings.ToLower(k[0])
}
if u == "" {
fmt.Fprintf(w, SearchHTML)
return
}
// Only tweets as "https://twitter.com/user/status/id" are honored at the
// moment. I'm not sure if there are more exotic arrangements that should
// be accounted for.
fields := strings.Split(u, "/")
if len(fields) < 6 {
fmt.Fprintln(w, "Invalid URL")
return
}
id := fields[5]
client := &http.Client{}
req, _ := http.NewRequest("GET", T_URL + id, nil)
req.Header.Add("Authorization", TOKEN)
res, _ := client.Do(req)
body, _ := ioutil.ReadAll(res.Body)
json.Unmarshal(body, &t)
if len(t.Errors) != 0 {
t.PrintError(w)
return
}
t.CheckMedia()
if format == "json" {
t.PrintJSON(w)
} else if format == "html" {
t.PrintHTML(w)
} else {
t.Print(w)
}
}
func main() {
// Check for the solitary port argument, then listen on it. Nothing fancy.
if len(os.Args) != 2 {
log.Fatal("Usage: bitter port")
}
http.HandleFunc("/", serve)
err := http.ListenAndServe(":" + os.Args[1], nil)
if err != nil {
log.Fatal(err)
}
}