diff --git a/accounts.go b/accounts.go index 9e0b17d..9161a1f 100644 --- a/accounts.go +++ b/accounts.go @@ -30,7 +30,7 @@ type Account struct { // GetAccount return Account. func (c *Client) GetAccount(ctx context.Context, id int) (*Account, error) { var account Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d", id), nil, &account) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d", id), nil, &account, nil) if err != nil { return nil, err } @@ -40,7 +40,7 @@ func (c *Client) GetAccount(ctx context.Context, id int) (*Account, error) { // GetAccountCurrentUser return Account of current user. func (c *Client) GetAccountCurrentUser(ctx context.Context) (*Account, error) { var account Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account, nil) if err != nil { return nil, err } @@ -76,7 +76,7 @@ func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account, } var account Account - err := c.doAPI(ctx, http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account) + err := c.doAPI(ctx, http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account, nil) if err != nil { return nil, err } @@ -86,7 +86,7 @@ func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account, // GetAccountStatuses return statuses by specified accuont. func (c *Client) GetAccountStatuses(ctx context.Context, id int64) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, nil) if err != nil { return nil, err } @@ -95,18 +95,31 @@ func (c *Client) GetAccountStatuses(ctx context.Context, id int64) ([]*Status, e // GetAccountFollowers return followers list. func (c *Client) GetAccountFollowers(ctx context.Context, id int64) ([]*Account, error) { - var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), nil, &accounts) - if err != nil { - return nil, err + params := url.Values{} + var total []*Account + for { + var accounts []*Account + var next bool + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), params, &accounts, &next) + if err != nil { + return nil, err + } + for _, a := range accounts { + println(a.Username) + } + total = append(total, accounts...) + if !next { + break + } + time.Sleep(10 * time.Second) } - return accounts, nil + return total, nil } // GetAccountFollowing return following list. func (c *Client) GetAccountFollowing(ctx context.Context, id int64) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, nil) if err != nil { return nil, err } @@ -116,7 +129,7 @@ func (c *Client) GetAccountFollowing(ctx context.Context, id int64) ([]*Account, // GetBlocks return block list. func (c *Client) GetBlocks(ctx context.Context) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, nil) if err != nil { return nil, err } @@ -136,7 +149,7 @@ type Relationship struct { // AccountFollow follow the account. func (c *Client) AccountFollow(ctx context.Context, id int64) (*Relationship, error) { var relationship Relationship - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/follow", id), nil, &relationship) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/follow", id), nil, &relationship, nil) if err != nil { return nil, err } @@ -146,7 +159,7 @@ func (c *Client) AccountFollow(ctx context.Context, id int64) (*Relationship, er // AccountUnfollow unfollow the account. func (c *Client) AccountUnfollow(ctx context.Context, id int64) (*Relationship, error) { var relationship Relationship - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unfollow", id), nil, &relationship) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unfollow", id), nil, &relationship, nil) if err != nil { return nil, err } @@ -156,7 +169,7 @@ func (c *Client) AccountUnfollow(ctx context.Context, id int64) (*Relationship, // AccountBlock block the account. func (c *Client) AccountBlock(ctx context.Context, id int64) (*Relationship, error) { var relationship Relationship - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/block", id), nil, &relationship) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/block", id), nil, &relationship, nil) if err != nil { return nil, err } @@ -166,7 +179,7 @@ func (c *Client) AccountBlock(ctx context.Context, id int64) (*Relationship, err // AccountUnblock unblock the account. func (c *Client) AccountUnblock(ctx context.Context, id int64) (*Relationship, error) { var relationship Relationship - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unblock", id), nil, &relationship) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unblock", id), nil, &relationship, nil) if err != nil { return nil, err } @@ -176,7 +189,7 @@ func (c *Client) AccountUnblock(ctx context.Context, id int64) (*Relationship, e // AccountMute mute the account. func (c *Client) AccountMute(ctx context.Context, id int64) (*Relationship, error) { var relationship Relationship - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/mute", id), nil, &relationship) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/mute", id), nil, &relationship, nil) if err != nil { return nil, err } @@ -186,7 +199,7 @@ func (c *Client) AccountMute(ctx context.Context, id int64) (*Relationship, erro // AccountUnmute unmute the account. func (c *Client) AccountUnmute(ctx context.Context, id int64) (*Relationship, error) { var relationship Relationship - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unmute", id), nil, &relationship) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unmute", id), nil, &relationship, nil) if err != nil { return nil, err } @@ -199,7 +212,7 @@ func (c *Client) GetAccountRelationship(ctx context.Context, id int64) ([]*Relat params.Set("id", fmt.Sprint(id)) var relationships []*Relationship - err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/relationship", params, &relationships) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/relationship", params, &relationships, nil) if err != nil { return nil, err } @@ -213,7 +226,7 @@ func (c *Client) AccountsSearch(ctx context.Context, q string, limit int64) ([]* params.Set("limit", fmt.Sprint(limit)) var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/search", params, &accounts) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/search", params, &accounts, nil) if err != nil { return nil, err } @@ -226,7 +239,7 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er params.Set("uri", uri) var account Account - err := c.doAPI(ctx, http.MethodPost, "/api/v1/follows", params, &account) + err := c.doAPI(ctx, http.MethodPost, "/api/v1/follows", params, &account, nil) if err != nil { return nil, err } @@ -236,7 +249,7 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er // GetFollowRequests return follow-requests. func (c *Client) GetFollowRequests(ctx context.Context) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, nil) if err != nil { return nil, err } @@ -245,18 +258,18 @@ func (c *Client) GetFollowRequests(ctx context.Context) ([]*Account, error) { // FollowRequestAuthorize is authorize the follow request of user with id. func (c *Client) FollowRequestAuthorize(ctx context.Context, id int64) error { - return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/authorize", id), nil, nil) + return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/authorize", id), nil, nil, nil) } // FollowRequestReject is rejects the follow request of user with id. func (c *Client) FollowRequestReject(ctx context.Context, id int64) error { - return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/reject", id), nil, nil) + return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/reject", id), nil, nil, nil) } // GetMutes returns the list of users muted by the current user. func (c *Client) GetMutes(ctx context.Context) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, nil) if err != nil { return nil, err } diff --git a/instance.go b/instance.go index b84b77b..9b45c25 100644 --- a/instance.go +++ b/instance.go @@ -16,7 +16,7 @@ type Instance struct { // GetInstance return Instance. func (c *Client) GetInstance(ctx context.Context) (*Instance, error) { var instance Instance - err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance", nil, &instance) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance", nil, &instance, nil) if err != nil { return nil, err } diff --git a/mastodon.go b/mastodon.go index 399eac4..14c8e8e 100644 --- a/mastodon.go +++ b/mastodon.go @@ -29,7 +29,32 @@ type Client struct { config *Config } -func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}) error { +type page struct { + next string +} + +func linkHeader(h http.Header, rel string) []string { + var links []string + for _, v := range h["Link"] { + parts := strings.Split(v, ";") + for _, p := range parts { + p = strings.TrimSpace(p) + if !strings.HasPrefix(p, "rel=") { + continue + } + pos := strings.Index(p[4:], `,`) + if pos > 0 { + p = p[4 : 4+pos] + } + if v := strings.Trim(p, `"`); v == rel { + links = append(links, strings.Trim(parts[0], "<>")) + } + } + } + return links +} + +func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, next *bool) error { u, err := url.Parse(c.config.Server) if err != nil { return err @@ -84,6 +109,19 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in } defer resp.Body.Close() + if next != nil && params != nil { + nl := linkHeader(resp.Header, "next") + *next = false + if len(nl) > 0 { + u, err = url.Parse(nl[0]) + if err == nil { + for k, v := range u.Query() { + params.(url.Values)[k] = v + } + } + *next = true + } + } if resp.StatusCode != http.StatusOK { return fmt.Errorf("bad request: %v", resp.Status) } else if res == nil { diff --git a/notification.go b/notification.go index 5664adc..475b1b4 100644 --- a/notification.go +++ b/notification.go @@ -19,7 +19,7 @@ type Notification struct { // GetNotifications return notifications. func (c *Client) GetNotifications(ctx context.Context) ([]*Notification, error) { var notifications []*Notification - err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, nil) if err != nil { return nil, err } @@ -29,7 +29,7 @@ func (c *Client) GetNotifications(ctx context.Context) ([]*Notification, error) // GetNotifications return notification. func (c *Client) GetNotification(ctx context.Context, id int64) (*Notification, error) { var notification Notification - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, ¬ification) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, ¬ification, nil) if err != nil { return nil, err } @@ -38,5 +38,5 @@ func (c *Client) GetNotification(ctx context.Context, id int64) (*Notification, // ClearNotifications clear notifications. func (c *Client) ClearNotifications(ctx context.Context) error { - return c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil) + return c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil, nil) } diff --git a/report.go b/report.go index 73fc452..f58d063 100644 --- a/report.go +++ b/report.go @@ -14,7 +14,7 @@ type Report struct { // GetReports return report of the current user. func (c *Client) GetReports(ctx context.Context) ([]*Report, error) { var reports []*Report - err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports, nil) if err != nil { return nil, err } @@ -24,7 +24,7 @@ func (c *Client) GetReports(ctx context.Context) ([]*Report, error) { // Report reports the report func (c *Client) Report(ctx context.Context, id int64) (*Report, error) { var report Report - err := c.doAPI(ctx, http.MethodPost, "/api/v1/reports", nil, &report) + err := c.doAPI(ctx, http.MethodPost, "/api/v1/reports", nil, &report, nil) if err != nil { return nil, err } diff --git a/status.go b/status.go index 09dffac..0b7c8a0 100644 --- a/status.go +++ b/status.go @@ -49,7 +49,7 @@ type Card struct { // GetFavourites return the favorite list of the current user. func (c *Client) GetFavourites(ctx context.Context) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, nil) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func (c *Client) GetFavourites(ctx context.Context) ([]*Status, error) { // GetStatus return status specified by id. func (c *Client) GetStatus(ctx context.Context, id int64) (*Status, error) { var status Status - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d", id), nil, &status) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d", id), nil, &status, nil) if err != nil { return nil, err } @@ -69,7 +69,7 @@ func (c *Client) GetStatus(ctx context.Context, id int64) (*Status, error) { // GetStatusContext return status specified by id. func (c *Client) GetStatusContext(ctx context.Context, id int64) (*Context, error) { var context Context - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/context", id), nil, &context) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/context", id), nil, &context, nil) if err != nil { return nil, err } @@ -79,7 +79,7 @@ func (c *Client) GetStatusContext(ctx context.Context, id int64) (*Context, erro // GetStatusCard return status specified by id. func (c *Client) GetStatusCard(ctx context.Context, id int64) (*Card, error) { var card Card - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/card", id), nil, &card) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/card", id), nil, &card, nil) if err != nil { return nil, err } @@ -89,7 +89,7 @@ func (c *Client) GetStatusCard(ctx context.Context, id int64) (*Card, error) { // GetRebloggedBy returns the account list of the user who reblogged the toot of id. func (c *Client) GetRebloggedBy(ctx context.Context, id int64) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, nil) if err != nil { return nil, err } @@ -99,7 +99,7 @@ func (c *Client) GetRebloggedBy(ctx context.Context, id int64) ([]*Account, erro // GetFavouritedBy returns the account list of the user who liked the toot of id. func (c *Client) GetFavouritedBy(ctx context.Context, id int64) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, nil) if err != nil { return nil, err } @@ -109,7 +109,7 @@ func (c *Client) GetFavouritedBy(ctx context.Context, id int64) ([]*Account, err // Reblog is reblog the toot of id and return status of reblog. func (c *Client) Reblog(ctx context.Context, id int64) (*Status, error) { var status Status - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/reblog", id), nil, &status) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/reblog", id), nil, &status, nil) if err != nil { return nil, err } @@ -119,7 +119,7 @@ func (c *Client) Reblog(ctx context.Context, id int64) (*Status, error) { // Unreblog is unreblog the toot of id and return status of the original toot. func (c *Client) Unreblog(ctx context.Context, id int64) (*Status, error) { var status Status - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unreblog", id), nil, &status) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unreblog", id), nil, &status, nil) if err != nil { return nil, err } @@ -129,7 +129,7 @@ func (c *Client) Unreblog(ctx context.Context, id int64) (*Status, error) { // Favourite is favourite the toot of id and return status of the favourite toot. func (c *Client) Favourite(ctx context.Context, id int64) (*Status, error) { var status Status - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/favourite", id), nil, &status) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/favourite", id), nil, &status, nil) if err != nil { return nil, err } @@ -139,7 +139,7 @@ func (c *Client) Favourite(ctx context.Context, id int64) (*Status, error) { // Unfavourite is unfavourite the toot of id and return status of the unfavourite toot. func (c *Client) Unfavourite(ctx context.Context, id int64) (*Status, error) { var status Status - err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unfavourite", id), nil, &status) + err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unfavourite", id), nil, &status, nil) if err != nil { return nil, err } @@ -149,7 +149,7 @@ func (c *Client) Unfavourite(ctx context.Context, id int64) (*Status, error) { // GetTimelineHome return statuses from home timeline. func (c *Client) GetTimelineHome(ctx context.Context) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, nil) if err != nil { return nil, err } @@ -159,7 +159,7 @@ func (c *Client) GetTimelineHome(ctx context.Context) ([]*Status, error) { // GetTimelineHashtag return statuses from tagged timeline. func (c *Client) GetTimelineHashtag(ctx context.Context, tag string) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", (&url.URL{Path: tag}).EscapedPath()), nil, &statuses) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", (&url.URL{Path: tag}).EscapedPath()), nil, &statuses, nil) if err != nil { return nil, err } @@ -177,7 +177,7 @@ func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) { //params.Set("visibility", "public") var status Status - err := c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status) + err := c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status, nil) if err != nil { return nil, err } @@ -186,7 +186,7 @@ func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) { // DeleteStatus delete the toot. func (c *Client) DeleteStatus(ctx context.Context, id int64) error { - return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil) + return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil, nil) } // Search search content with query. @@ -195,7 +195,7 @@ func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results, params.Set("q", q) params.Set("resolve", fmt.Sprint(resolve)) var results Results - err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results, nil) if err != nil { return nil, err } @@ -205,7 +205,7 @@ func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results, // PostMedia upload a media attachment. func (c *Client) UploadMedia(ctx context.Context, file string) (*Attachment, error) { var attachment Attachment - err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", file, &attachment) + err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", file, &attachment, nil) if err != nil { return nil, err }