11 "encoding/json" |
11 "encoding/json" |
12 "fmt" |
12 "fmt" |
13 "mime/multipart" |
13 "mime/multipart" |
14 "os" |
14 "os" |
15 "path/filepath" |
15 "path/filepath" |
16 "strconv" |
|
17 |
16 |
18 "github.com/pkg/errors" |
17 "github.com/pkg/errors" |
19 "github.com/sendgrid/rest" |
18 "github.com/sendgrid/rest" |
20 ) |
19 ) |
21 |
20 |
22 // getAccountsOptions contains option fields for POST and DELETE API calls |
21 // getAccountsOptions contains option fields for POST and DELETE API calls |
23 type getAccountsOptions struct { |
22 type getAccountsOptions struct { |
24 // The ID is used for most commands |
23 // The ID is used for most commands |
25 ID int64 |
24 ID ActivityID |
26 |
25 |
27 // Following can be set to true to limit a search to "following" accounts |
26 // Following can be set to true to limit a search to "following" accounts |
28 Following bool |
27 Following bool |
29 |
28 |
30 // The Q field (query) is used when searching for accounts |
29 // The Q field (query) is used when searching for accounts |
47 |
46 |
48 // updateRelationship returns a Relationship entity |
47 // updateRelationship returns a Relationship entity |
49 // The operation 'op' can be "follow", "unfollow", "block", "unblock", |
48 // The operation 'op' can be "follow", "unfollow", "block", "unblock", |
50 // "mute", "unmute". |
49 // "mute", "unmute". |
51 // The id is optional and depends on the operation. |
50 // The id is optional and depends on the operation. |
52 func (mc *Client) updateRelationship(op string, id int64, params apiCallParams) (*Relationship, error) { |
51 func (mc *Client) updateRelationship(op string, id ActivityID, params apiCallParams) (*Relationship, error) { |
53 var endPoint string |
52 var endPoint string |
54 method := rest.Post |
53 method := rest.Post |
55 strID := strconv.FormatInt(id, 10) |
|
56 |
54 |
57 switch op { |
55 switch op { |
58 case "follow", "unfollow", "block", "unblock", "mute", "unmute", "pin", "unpin": |
56 case "follow", "unfollow", "block", "unblock", "mute", "unmute", "pin", "unpin": |
59 endPoint = "accounts/" + strID + "/" + op |
57 endPoint = "accounts/" + id + "/" + op |
60 default: |
58 default: |
61 return nil, ErrInvalidParameter |
59 return nil, ErrInvalidParameter |
62 } |
60 } |
63 |
61 |
64 var rel Relationship |
62 var rel Relationship |
70 |
68 |
71 // getSingleAccount returns an account entity |
69 // getSingleAccount returns an account entity |
72 // The operation 'op' can be "account", "verify_credentials", |
70 // The operation 'op' can be "account", "verify_credentials", |
73 // "follow_requests/authorize" or // "follow_requests/reject". |
71 // "follow_requests/authorize" or // "follow_requests/reject". |
74 // The id is optional and depends on the operation. |
72 // The id is optional and depends on the operation. |
75 func (mc *Client) getSingleAccount(op string, id int64) (*Account, error) { |
73 func (mc *Client) getSingleAccount(op string, id ActivityID) (*Account, error) { |
76 var endPoint string |
74 var endPoint string |
77 method := rest.Get |
75 method := rest.Get |
78 strID := strconv.FormatInt(id, 10) |
|
79 |
76 |
80 switch op { |
77 switch op { |
81 case "account": |
78 case "account": |
82 endPoint = "accounts/" + strID |
79 endPoint = "accounts/" + id |
83 case "verify_credentials": |
80 case "verify_credentials": |
84 endPoint = "accounts/verify_credentials" |
81 endPoint = "accounts/verify_credentials" |
85 case "follow_requests/authorize", "follow_requests/reject": |
82 case "follow_requests/authorize", "follow_requests/reject": |
86 // The documentation is incorrect, the endpoint actually |
83 // The documentation is incorrect, the endpoint actually |
87 // is "follow_requests/:id/{authorize|reject}" |
84 // is "follow_requests/:id/{authorize|reject}" |
88 endPoint = op[:16] + strID + "/" + op[16:] |
85 endPoint = op[:16] + id + "/" + op[16:] |
89 method = rest.Post |
86 method = rest.Post |
90 default: |
87 default: |
91 return nil, ErrInvalidParameter |
88 return nil, ErrInvalidParameter |
92 } |
89 } |
93 |
90 |
136 lopt = opts.Limit |
133 lopt = opts.Limit |
137 } |
134 } |
138 |
135 |
139 switch op { |
136 switch op { |
140 case "followers", "following": |
137 case "followers", "following": |
141 if opts == nil || opts.ID < 1 { |
138 if opts == nil || opts.ID == "" { |
142 return []Account{}, ErrInvalidID |
139 return []Account{}, ErrInvalidID |
143 } |
140 } |
144 endPoint = "accounts/" + strconv.FormatInt(opts.ID, 10) + "/" + op |
141 endPoint = "accounts/" + opts.ID + "/" + op |
145 case "follow_requests", "blocks", "mutes": |
142 case "follow_requests", "blocks", "mutes": |
146 endPoint = op |
143 endPoint = op |
147 case "search": |
144 case "search": |
148 if opts == nil || opts.Q == "" { |
145 if opts == nil || opts.Q == "" { |
149 return []Account{}, ErrInvalidParameter |
146 return []Account{}, ErrInvalidParameter |
150 } |
147 } |
151 endPoint = "accounts/" + op |
148 endPoint = "accounts/" + op |
152 case "reblogged_by", "favourited_by": |
149 case "reblogged_by", "favourited_by": |
153 if opts == nil || opts.ID < 1 { |
150 if opts == nil || opts.ID == "" { |
154 return []Account{}, ErrInvalidID |
151 return []Account{}, ErrInvalidID |
155 } |
152 } |
156 endPoint = "statuses/" + strconv.FormatInt(opts.ID, 10) + "/" + op |
153 endPoint = "statuses/" + opts.ID + "/" + op |
157 default: |
154 default: |
158 return nil, ErrInvalidParameter |
155 return nil, ErrInvalidParameter |
159 } |
156 } |
160 |
157 |
161 // Handle target-specific query parameters |
158 // Handle target-specific query parameters |
171 } |
168 } |
172 |
169 |
173 // GetAccount returns an account entity |
170 // GetAccount returns an account entity |
174 // The returned value can be nil if there is an error or if the |
171 // The returned value can be nil if there is an error or if the |
175 // requested ID does not exist. |
172 // requested ID does not exist. |
176 func (mc *Client) GetAccount(accountID int64) (*Account, error) { |
173 func (mc *Client) GetAccount(accountID ActivityID) (*Account, error) { |
177 account, err := mc.getSingleAccount("account", accountID) |
174 account, err := mc.getSingleAccount("account", accountID) |
178 if err != nil { |
175 if err != nil { |
179 return nil, err |
176 return nil, err |
180 } |
177 } |
181 if account != nil && account.ID == 0 { |
178 if account != nil && account.ID == "" { |
182 return nil, ErrEntityNotFound |
179 return nil, ErrEntityNotFound |
183 } |
180 } |
184 return account, nil |
181 return account, nil |
185 } |
182 } |
186 |
183 |
187 // GetCurrentAccount returns the current user account |
184 // GetCurrentAccount returns the current user account |
188 func (mc *Client) GetCurrentAccount() (*Account, error) { |
185 func (mc *Client) GetCurrentAccount() (*Account, error) { |
189 account, err := mc.getSingleAccount("verify_credentials", 0) |
186 account, err := mc.getSingleAccount("verify_credentials", "") |
190 if err != nil { |
187 if err != nil { |
191 return nil, err |
188 return nil, err |
192 } |
189 } |
193 if account != nil && account.ID == 0 { |
190 if account != nil && account.ID == "" { |
194 return nil, ErrEntityNotFound |
191 return nil, ErrEntityNotFound |
195 } |
192 } |
196 return account, nil |
193 return account, nil |
197 } |
194 } |
198 |
195 |
199 // GetAccountFollowers returns the list of accounts following a given account |
196 // GetAccountFollowers returns the list of accounts following a given account |
200 func (mc *Client) GetAccountFollowers(accountID int64, lopt *LimitParams) ([]Account, error) { |
197 func (mc *Client) GetAccountFollowers(accountID ActivityID, lopt *LimitParams) ([]Account, error) { |
201 o := &getAccountsOptions{ID: accountID, Limit: lopt} |
198 o := &getAccountsOptions{ID: accountID, Limit: lopt} |
202 return mc.getMultipleAccountsHelper("followers", o) |
199 return mc.getMultipleAccountsHelper("followers", o) |
203 } |
200 } |
204 |
201 |
205 // GetAccountFollowing returns the list of accounts a given account is following |
202 // GetAccountFollowing returns the list of accounts a given account is following |
206 func (mc *Client) GetAccountFollowing(accountID int64, lopt *LimitParams) ([]Account, error) { |
203 func (mc *Client) GetAccountFollowing(accountID ActivityID, lopt *LimitParams) ([]Account, error) { |
207 o := &getAccountsOptions{ID: accountID, Limit: lopt} |
204 o := &getAccountsOptions{ID: accountID, Limit: lopt} |
208 return mc.getMultipleAccountsHelper("following", o) |
205 return mc.getMultipleAccountsHelper("following", o) |
209 } |
206 } |
210 |
207 |
211 // FollowAccount follows an account |
208 // FollowAccount follows an account |
212 // 'reblogs' can be used to specify if boots should be displayed or hidden. |
209 // 'reblogs' can be used to specify if boots should be displayed or hidden. |
213 func (mc *Client) FollowAccount(accountID int64, reblogs *bool) (*Relationship, error) { |
210 func (mc *Client) FollowAccount(accountID ActivityID, reblogs *bool) (*Relationship, error) { |
214 var params apiCallParams |
211 var params apiCallParams |
215 if reblogs != nil { |
212 if reblogs != nil { |
216 params = make(apiCallParams) |
213 params = make(apiCallParams) |
217 if *reblogs { |
214 if *reblogs { |
218 params["reblogs"] = "true" |
215 params["reblogs"] = "true" |
254 |
251 |
255 var account Account |
252 var account Account |
256 if err := mc.apiCall("v1/follows", rest.Post, params, nil, nil, &account); err != nil { |
253 if err := mc.apiCall("v1/follows", rest.Post, params, nil, nil, &account); err != nil { |
257 return nil, err |
254 return nil, err |
258 } |
255 } |
259 if account.ID == 0 { |
256 if account.ID == "" { |
260 return nil, ErrEntityNotFound |
257 return nil, ErrEntityNotFound |
261 } |
258 } |
262 return &account, nil |
259 return &account, nil |
263 } |
260 } |
264 |
261 |
265 // BlockAccount blocks an account |
262 // BlockAccount blocks an account |
266 func (mc *Client) BlockAccount(accountID int64) (*Relationship, error) { |
263 func (mc *Client) BlockAccount(accountID ActivityID) (*Relationship, error) { |
267 rel, err := mc.updateRelationship("block", accountID, nil) |
264 rel, err := mc.updateRelationship("block", accountID, nil) |
268 if err != nil { |
265 if err != nil { |
269 return nil, err |
266 return nil, err |
270 } |
267 } |
271 if rel == nil { |
268 if rel == nil { |
287 } |
284 } |
288 |
285 |
289 // MuteAccount mutes an account |
286 // MuteAccount mutes an account |
290 // Note that with current Mastodon API, muteNotifications defaults to true |
287 // Note that with current Mastodon API, muteNotifications defaults to true |
291 // when it is not provided. |
288 // when it is not provided. |
292 func (mc *Client) MuteAccount(accountID int64, muteNotifications *bool) (*Relationship, error) { |
289 func (mc *Client) MuteAccount(accountID ActivityID, muteNotifications *bool) (*Relationship, error) { |
293 var params apiCallParams |
290 var params apiCallParams |
294 |
291 |
295 if muteNotifications != nil { |
292 if muteNotifications != nil { |
296 params = make(apiCallParams) |
293 params = make(apiCallParams) |
297 if *muteNotifications { |
294 if *muteNotifications { |
350 o := &getAccountsOptions{Limit: lopt} |
347 o := &getAccountsOptions{Limit: lopt} |
351 return mc.getMultipleAccountsHelper("follow_requests", o) |
348 return mc.getMultipleAccountsHelper("follow_requests", o) |
352 } |
349 } |
353 |
350 |
354 // GetAccountRelationships returns a list of relationship entities for the given accounts |
351 // GetAccountRelationships returns a list of relationship entities for the given accounts |
355 func (mc *Client) GetAccountRelationships(accountIDs []int64) ([]Relationship, error) { |
352 func (mc *Client) GetAccountRelationships(accountIDs []ActivityID) ([]Relationship, error) { |
356 if len(accountIDs) < 1 { |
353 if len(accountIDs) < 1 { |
357 return nil, ErrInvalidID |
354 return nil, ErrInvalidID |
358 } |
355 } |
359 |
356 |
360 params := make(apiCallParams) |
357 params := make(apiCallParams) |
361 for i, id := range accountIDs { |
358 for i, id := range accountIDs { |
362 if id < 1 { |
359 if id == "" { |
363 return nil, ErrInvalidID |
360 return nil, ErrInvalidID |
364 } |
361 } |
365 qID := fmt.Sprintf("[%d]id", i) |
362 qID := fmt.Sprintf("[%d]id", i) |
366 params[qID] = strconv.FormatInt(id, 10) |
363 params[qID] = id |
367 } |
364 } |
368 |
365 |
369 var rl []Relationship |
366 var rl []Relationship |
370 if err := mc.apiCall("v1/accounts/relationships", rest.Get, params, nil, nil, &rl); err != nil { |
367 if err := mc.apiCall("v1/accounts/relationships", rest.Get, params, nil, nil, &rl); err != nil { |
371 return nil, err |
368 return nil, err |
379 // If excludeReplies is true, skip statuses that reply to other statuses. |
376 // If excludeReplies is true, skip statuses that reply to other statuses. |
380 // If lopt.All is true, several requests will be made until the API server |
377 // If lopt.All is true, several requests will be made until the API server |
381 // has nothing to return. |
378 // has nothing to return. |
382 // If lopt.Limit is set (and not All), several queries can be made until the |
379 // If lopt.Limit is set (and not All), several queries can be made until the |
383 // limit is reached. |
380 // limit is reached. |
384 func (mc *Client) GetAccountStatuses(accountID int64, onlyPinned, onlyMedia, excludeReplies bool, lopt *LimitParams) ([]Status, error) { |
381 func (mc *Client) GetAccountStatuses(accountID ActivityID, onlyPinned, onlyMedia, excludeReplies bool, lopt *LimitParams) ([]Status, error) { |
385 if accountID < 1 { |
382 if accountID == "" { |
386 return nil, ErrInvalidID |
383 return nil, ErrInvalidID |
387 } |
384 } |
388 |
385 |
389 endPoint := "accounts/" + strconv.FormatInt(accountID, 10) + "/" + "statuses" |
386 endPoint := "accounts/" + accountID + "/" + "statuses" |
390 params := make(apiCallParams) |
387 params := make(apiCallParams) |
391 if onlyMedia { |
388 if onlyMedia { |
392 params["only_media"] = "true" |
389 params["only_media"] = "true" |
393 } |
390 } |
394 if onlyPinned { |
391 if onlyPinned { |
400 |
397 |
401 return mc.getMultipleStatuses(endPoint, params, lopt) |
398 return mc.getMultipleStatuses(endPoint, params, lopt) |
402 } |
399 } |
403 |
400 |
404 // FollowRequestAuthorize authorizes or rejects an account follow-request |
401 // FollowRequestAuthorize authorizes or rejects an account follow-request |
405 func (mc *Client) FollowRequestAuthorize(accountID int64, authorize bool) error { |
402 func (mc *Client) FollowRequestAuthorize(accountID ActivityID, authorize bool) error { |
406 endPoint := "follow_requests/reject" |
403 endPoint := "follow_requests/reject" |
407 if authorize { |
404 if authorize { |
408 endPoint = "follow_requests/authorize" |
405 endPoint = "follow_requests/authorize" |
409 } |
406 } |
410 _, err := mc.getSingleAccount(endPoint, accountID) |
407 _, err := mc.getSingleAccount(endPoint, accountID) |