6 "strconv" |
6 "strconv" |
7 |
7 |
8 "github.com/sendgrid/rest" |
8 "github.com/sendgrid/rest" |
9 ) |
9 ) |
10 |
10 |
|
11 // updateStatusOptions contains option field for POST and DELETE API calls |
|
12 type updateStatusOptions struct { |
|
13 // The ID is used for most commands |
|
14 ID int |
|
15 |
|
16 // The following fields are used for posting a new status |
|
17 Status string |
|
18 InReplyToID int |
|
19 MediaIDs []int |
|
20 Sensitive bool |
|
21 SpoilerText string |
|
22 Visibility string // "direct", "private", "unlisted" or "public" |
|
23 } |
|
24 |
11 // queryStatusData queries the statuses API |
25 // queryStatusData queries the statuses API |
12 // The subquery can be empty (the status itself), "context", "card", |
26 // The subquery can be empty or "status" (the status itself), "context", |
13 // "reblogged_by", "favourited_by". |
27 // "card", "reblogged_by", "favourited_by". |
|
28 // The data argument will receive the object(s) returned by the API server. |
14 func (g *Client) queryStatusData(statusID int, subquery string, data interface{}) error { |
29 func (g *Client) queryStatusData(statusID int, subquery string, data interface{}) error { |
15 endPoint := "statuses/" + strconv.Itoa(statusID) |
30 endPoint := "statuses/" + strconv.Itoa(statusID) |
16 |
31 |
17 if statusID < 1 { |
32 if statusID < 1 { |
18 return ErrInvalidID |
33 return ErrInvalidID |
19 } |
34 } |
20 |
35 |
21 if subquery != "" { |
36 if subquery != "" && subquery != "status" { |
22 // TODO: check subquery values? |
37 switch subquery { |
|
38 case "context", "card", "reblogged_by", "favourited_by": |
|
39 default: |
|
40 return ErrInvalidParameter |
|
41 } |
|
42 |
23 endPoint += "/" + subquery |
43 endPoint += "/" + subquery |
24 } |
44 } |
25 req := g.prepareRequest(endPoint) |
45 req := g.prepareRequest(endPoint) |
26 r, err := rest.API(req) |
46 r, err := rest.API(req) |
27 if err != nil { |
47 if err != nil { |
39 } |
59 } |
40 |
60 |
41 return nil |
61 return nil |
42 } |
62 } |
43 |
63 |
|
64 // updateStatusData updates the statuses |
|
65 // The subquery can be empty or "status" (to post a status), "delete" (for |
|
66 // deleting a status), "reblog", "unreblog", "favourite", "unfavourite". |
|
67 // The data argument will receive the object(s) returned by the API server. |
|
68 func (g *Client) updateStatusData(subquery string, opts updateStatusOptions, data interface{}) error { |
|
69 method := rest.Post |
|
70 endPoint := "statuses" |
|
71 |
|
72 switch subquery { |
|
73 case "", "status": |
|
74 subquery = "status" |
|
75 if opts.Status == "" { |
|
76 return ErrInvalidParameter |
|
77 } |
|
78 switch opts.Visibility { |
|
79 case "", "direct", "private", "unlisted", "public": |
|
80 // Okay |
|
81 default: |
|
82 return ErrInvalidParameter |
|
83 } |
|
84 if len(opts.MediaIDs) > 4 { |
|
85 return fmt.Errorf("too many (>4) media IDs") |
|
86 } |
|
87 case "delete": |
|
88 method = rest.Delete |
|
89 if opts.ID < 1 { |
|
90 return ErrInvalidID |
|
91 } |
|
92 endPoint += "/" + strconv.Itoa(opts.ID) |
|
93 case "reblog", "unreblog", "favourite", "unfavourite": |
|
94 if opts.ID < 1 { |
|
95 return ErrInvalidID |
|
96 } |
|
97 endPoint += "/" + strconv.Itoa(opts.ID) + "/" + subquery |
|
98 default: |
|
99 return ErrInvalidParameter |
|
100 } |
|
101 |
|
102 req := g.prepareRequest(endPoint) |
|
103 req.Method = method |
|
104 |
|
105 // Form items for a new toot |
|
106 if subquery == "status" { |
|
107 req.QueryParams["status"] = opts.Status |
|
108 if opts.InReplyToID > 0 { |
|
109 req.QueryParams["in_reply_to_id"] = strconv.Itoa(opts.InReplyToID) |
|
110 } |
|
111 for i, id := range opts.MediaIDs { |
|
112 qID := fmt.Sprintf("media_ids[%d]", i+1) |
|
113 req.QueryParams[qID] = strconv.Itoa(id) |
|
114 } |
|
115 if opts.Sensitive { |
|
116 req.QueryParams["sensitive"] = "true" |
|
117 } |
|
118 if opts.SpoilerText != "" { |
|
119 req.QueryParams["spoiler_text"] = opts.SpoilerText |
|
120 } |
|
121 if opts.Visibility != "" { |
|
122 req.QueryParams["visibility"] = opts.Visibility |
|
123 } |
|
124 } |
|
125 |
|
126 r, err := rest.API(req) |
|
127 if err != nil { |
|
128 return fmt.Errorf("status/%s API query: %s", subquery, err.Error()) |
|
129 } |
|
130 |
|
131 // Check for error reply |
|
132 var errorResult Error |
|
133 if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { |
|
134 return fmt.Errorf("%s", errorResult.Text) |
|
135 } |
|
136 |
|
137 // Not an error reply; let's unmarshall the data |
|
138 err = json.Unmarshal([]byte(r.Body), &data) |
|
139 if err != nil { |
|
140 return fmt.Errorf("status/%s API: %s", subquery, err.Error()) |
|
141 } |
|
142 return nil |
|
143 } |
|
144 |
44 // GetStatus returns a status |
145 // GetStatus returns a status |
45 // The returned status can be nil if there is an error or if the |
146 // The returned status can be nil if there is an error or if the |
46 // requested ID does not exist. |
147 // requested ID does not exist. |
47 func (g *Client) GetStatus(id int) (*Status, error) { |
148 func (g *Client) GetStatus(id int) (*Status, error) { |
48 var status Status |
149 var status Status |
49 |
150 |
50 if err := g.queryStatusData(id, "", &status); err != nil { |
151 if err := g.queryStatusData(id, "status", &status); err != nil { |
51 return nil, err |
152 return nil, err |
52 } |
153 } |
53 |
154 |
54 if status.ID == 0 { |
155 if status.ID == 0 { |
55 return nil, ErrEntityNotFound |
156 return nil, ErrEntityNotFound |
91 func (g *Client) GetStatusFavouritedBy(id int) ([]Account, error) { |
192 func (g *Client) GetStatusFavouritedBy(id int) ([]Account, error) { |
92 var accounts []Account |
193 var accounts []Account |
93 err := g.queryStatusData(id, "favourited_by", &accounts) |
194 err := g.queryStatusData(id, "favourited_by", &accounts) |
94 return accounts, err |
195 return accounts, err |
95 } |
196 } |
|
197 |
|
198 // PostStatus posts a new "toot" |
|
199 // All parameters but "text" can be empty. |
|
200 // Visibility must be empty, or one of "direct", "private", "unlisted" and "public". |
|
201 func (g *Client) PostStatus(text string, inReplyTo int, mediaIDs []int, sensitive bool, spoilerText string, visibility string) (*Status, error) { |
|
202 var status Status |
|
203 o := updateStatusOptions{ |
|
204 Status: text, |
|
205 InReplyToID: inReplyTo, |
|
206 MediaIDs: mediaIDs, |
|
207 Sensitive: sensitive, |
|
208 SpoilerText: spoilerText, |
|
209 Visibility: visibility, |
|
210 } |
|
211 |
|
212 err := g.updateStatusData("status", o, &status) |
|
213 if err != nil { |
|
214 return nil, err |
|
215 } |
|
216 if status.ID == 0 { |
|
217 return nil, ErrEntityNotFound // TODO Change error message |
|
218 } |
|
219 return &status, err |
|
220 } |
|
221 |
|
222 // DeleteStatus deletes a status |
|
223 func (g *Client) DeleteStatus(id int) error { |
|
224 var status Status |
|
225 o := updateStatusOptions{ID: id} |
|
226 err := g.updateStatusData("delete", o, &status) |
|
227 return err |
|
228 } |
|
229 |
|
230 // ReblogStatus reblogs a status |
|
231 func (g *Client) ReblogStatus(id int) error { |
|
232 var status Status |
|
233 o := updateStatusOptions{ID: id} |
|
234 err := g.updateStatusData("reblog", o, &status) |
|
235 return err |
|
236 } |
|
237 |
|
238 // UnreblogStatus unreblogs a status |
|
239 func (g *Client) UnreblogStatus(id int) error { |
|
240 var status Status |
|
241 o := updateStatusOptions{ID: id} |
|
242 err := g.updateStatusData("unreblog", o, &status) |
|
243 return err |
|
244 } |
|
245 |
|
246 // FavouriteStatus favourites a status |
|
247 func (g *Client) FavouriteStatus(id int) error { |
|
248 var status Status |
|
249 o := updateStatusOptions{ID: id} |
|
250 err := g.updateStatusData("favourite", o, &status) |
|
251 return err |
|
252 } |
|
253 |
|
254 // UnfavouriteStatus unfavourites a status |
|
255 func (g *Client) UnfavouriteStatus(id int) error { |
|
256 var status Status |
|
257 o := updateStatusOptions{ID: id} |
|
258 err := g.updateStatusData("unfavourite", o, &status) |
|
259 return err |
|
260 } |