diff --git a/accounts.go b/accounts.go index a52cf9a..59b8ec8 100644 --- a/accounts.go +++ b/accounts.go @@ -84,9 +84,9 @@ 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) { +func (c *Client) GetAccountStatuses(ctx context.Context, id int64, pg *Pagination) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, nil) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, pg) if err != nil { return nil, err } @@ -94,29 +94,19 @@ 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) { - 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 - } - total = append(total, accounts...) - if !next { - break - } - time.Sleep(c.interval) +func (c *Client) GetAccountFollowers(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { + var accounts []*Account + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), nil, &accounts, pg) + if err != nil { + return nil, err } - return total, nil + return accounts, nil } // GetAccountFollowing return following list. -func (c *Client) GetAccountFollowing(ctx context.Context, id int64) ([]*Account, error) { +func (c *Client) GetAccountFollowing(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, nil) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, pg) if err != nil { return nil, err } @@ -124,9 +114,9 @@ func (c *Client) GetAccountFollowing(ctx context.Context, id int64) ([]*Account, } // GetBlocks return block list. -func (c *Client) GetBlocks(ctx context.Context) ([]*Account, error) { +func (c *Client) GetBlocks(ctx context.Context, pg *Pagination) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, pg) if err != nil { return nil, err } @@ -246,9 +236,9 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er } // GetFollowRequests return follow-requests. -func (c *Client) GetFollowRequests(ctx context.Context) ([]*Account, error) { +func (c *Client) GetFollowRequests(ctx context.Context, pg *Pagination) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, pg) if err != nil { return nil, err } @@ -266,9 +256,9 @@ func (c *Client) FollowRequestReject(ctx context.Context, id int64) error { } // GetMutes returns the list of users muted by the current user. -func (c *Client) GetMutes(ctx context.Context) ([]*Account, error) { +func (c *Client) GetMutes(ctx context.Context, pg *Pagination) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, pg) if err != nil { return nil, err } diff --git a/accounts_test.go b/accounts_test.go index cc05d1b..7628425 100644 --- a/accounts_test.go +++ b/accounts_test.go @@ -25,11 +25,11 @@ func TestGetAccount(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - a, err := client.GetAccount(context.Background(), 1) + _, err := client.GetAccount(context.Background(), 1) if err == nil { t.Fatalf("should be fail: %v", err) } - a, err = client.GetAccount(context.Background(), 1234567) + a, err := client.GetAccount(context.Background(), 1234567) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -124,11 +124,11 @@ func TestGetAccountStatuses(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetAccountStatuses(context.Background(), 123) + _, err := client.GetAccountStatuses(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - ss, err := client.GetAccountStatuses(context.Background(), 1234567) + ss, err := client.GetAccountStatuses(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -157,11 +157,11 @@ func TestGetAccountFollowers(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetAccountFollowers(context.Background(), 123) + _, err := client.GetAccountFollowers(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fl, err := client.GetAccountFollowers(context.Background(), 1234567) + fl, err := client.GetAccountFollowers(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -193,11 +193,11 @@ func TestGetAccountFollowing(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetAccountFollowing(context.Background(), 123) + _, err := client.GetAccountFollowing(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fl, err := client.GetAccountFollowing(context.Background(), 1234567) + fl, err := client.GetAccountFollowing(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -231,11 +231,11 @@ func TestGetBlocks(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetBlocks(context.Background()) + _, err := client.GetBlocks(context.Background(), nil) if err == nil { t.Fatalf("should be fail: %v", err) } - bl, err := client.GetBlocks(context.Background()) + bl, err := client.GetBlocks(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -566,11 +566,11 @@ func TestGetFollowRequests(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetFollowRequests(context.Background()) + _, err := client.GetFollowRequests(context.Background(), nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fReqs, err := client.GetFollowRequests(context.Background()) + fReqs, err := client.GetFollowRequests(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -652,11 +652,11 @@ func TestGetMutes(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetMutes(context.Background()) + _, err := client.GetMutes(context.Background(), nil) if err == nil { t.Fatalf("should be fail: %v", err) } - mutes, err := client.GetMutes(context.Background()) + mutes, err := client.GetMutes(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go index 8824332..ecb4a79 100644 --- a/cmd/mstdn/cmd_followers.go +++ b/cmd/mstdn/cmd_followers.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "time" "github.com/mattn/go-mastodon" "github.com/urfave/cli" @@ -16,9 +17,18 @@ func cmdFollowers(c *cli.Context) error { if err != nil { return err } - followers, err := client.GetAccountFollowers(context.Background(), account.ID) - if err != nil { - return err + var followers []*mastodon.Account + var pg mastodon.Pagination + for { + fs, err := client.GetAccountFollowers(context.Background(), account.ID, &pg) + if err != nil { + return err + } + followers = append(followers, fs...) + if pg.MaxID == 0 { + break + } + time.Sleep(10 * time.Second) } s := newScreen(config) for _, follower := range followers { diff --git a/cmd/mstdn/cmd_followers_test.go b/cmd/mstdn/cmd_followers_test.go index 442a068..f8c5080 100644 --- a/cmd/mstdn/cmd_followers_test.go +++ b/cmd/mstdn/cmd_followers_test.go @@ -17,6 +17,7 @@ func TestCmdFollowers(t *testing.T) { fmt.Fprintln(w, `{"id": 123}`) return case "/api/v1/accounts/123/followers": + w.Header().Set("Link", `; rel="prev"`) fmt.Fprintln(w, `[{"id": 234, "username": "ZZZ", "acct": "zzz"}]`) return } diff --git a/cmd/mstdn/cmd_notification.go b/cmd/mstdn/cmd_notification.go index 6d4192c..b32ba4e 100644 --- a/cmd/mstdn/cmd_notification.go +++ b/cmd/mstdn/cmd_notification.go @@ -11,7 +11,7 @@ import ( func cmdNotification(c *cli.Context) error { client := c.App.Metadata["client"].(*mastodon.Client) - notifications, err := client.GetNotifications(context.Background()) + notifications, err := client.GetNotifications(context.Background(), nil) if err != nil { return err } diff --git a/cmd/mstdn/cmd_timeline.go b/cmd/mstdn/cmd_timeline.go index d634ebf..d9a0024 100644 --- a/cmd/mstdn/cmd_timeline.go +++ b/cmd/mstdn/cmd_timeline.go @@ -10,7 +10,7 @@ import ( func cmdTimeline(c *cli.Context) error { client := c.App.Metadata["client"].(*mastodon.Client) config := c.App.Metadata["config"].(*mastodon.Config) - timeline, err := client.GetTimelineHome(context.Background()) + timeline, err := client.GetTimelineHome(context.Background(), nil) if err != nil { return err } diff --git a/example_test.go b/example_test.go index 307b6d5..9b7c879 100644 --- a/example_test.go +++ b/example_test.go @@ -32,7 +32,7 @@ func ExampleClient() { if err != nil { log.Fatal(err) } - timeline, err := c.GetTimelineHome(context.Background()) + timeline, err := c.GetTimelineHome(context.Background(), nil) if err != nil { log.Fatal(err) } diff --git a/mastodon.go b/mastodon.go index 16ccb08..240833d 100644 --- a/mastodon.go +++ b/mastodon.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "encoding/json" + "errors" + "fmt" "io" "mime/multipart" "net/http" @@ -11,8 +13,10 @@ import ( "os" "path" "path/filepath" + "strconv" "strings" - "time" + + "github.com/tomnomnom/linkheader" ) // Config is a setting for access mastodon APIs. @@ -26,61 +30,10 @@ type Config struct { // Client is a API client for mastodon. type Client struct { http.Client - config *Config - interval time.Duration + config *Config } -type page struct { - next string -} - -func linkHeader(h http.Header, rel string) []string { - var links []string - for _, v := range h["Link"] { - var p string - for len(v) > 0 { - i := strings.Index(v, ";") - if i < 0 { - break - } - e := i - i++ - for i < len(v) { - if v[i] != ' ' { - break - } - i++ - } - p = strings.TrimSpace(v[i:]) - if !strings.HasPrefix(p, "rel=") { - break - } - i += 4 - pos := strings.Index(p[4:], `,`) - if pos > 0 { - p = p[4 : 4+pos] - i += pos - } else { - p = p[4:] - i = len(v) - 1 - } - if k := strings.Trim(p, `"`); k == rel { - links = append(links, strings.Trim(v[:e], "<>")) - } - i++ - for i < len(v) { - if v[i] != ' ' { - break - } - i++ - } - v = v[i:] - } - } - return links -} - -func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, next *bool) error { +func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, pg *Pagination) error { u, err := url.Parse(c.config.Server) if err != nil { return err @@ -92,6 +45,9 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in if values, ok := params.(url.Values); ok { var body io.Reader if method == http.MethodGet { + if pg != nil { + values = pg.setValues(values) + } u.RawQuery = values.Encode() } else { body = strings.NewReader(values.Encode()) @@ -127,6 +83,9 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in } ct = mw.FormDataContentType() } else { + if method == http.MethodGet && pg != nil { + u.RawQuery = pg.toValues().Encode() + } req, err = http.NewRequest(method, u.String(), nil) if err != nil { return err @@ -144,23 +103,18 @@ 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 parseAPIError("bad request", resp) } else if res == nil { return nil + } else if pg != nil { + if lh := resp.Header.Get("Link"); lh != "" { + pg2, err := newPagination(lh) + if err != nil { + return err + } + *pg = *pg2 + } } return json.NewDecoder(resp.Body).Decode(&res) } @@ -168,9 +122,8 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in // NewClient return new mastodon API client. func NewClient(config *Config) *Client { return &Client{ - Client: *http.DefaultClient, - config: config, - interval: 10 * time.Second, + Client: *http.DefaultClient, + config: config, } } @@ -257,3 +210,67 @@ type Results struct { Statuses []*Status `json:"statuses"` Hashtags []string `json:"hashtags"` } + +// Pagination is a struct for specifying the get range. +type Pagination struct { + MaxID int64 + SinceID int64 + Limit int64 +} + +func newPagination(rawlink string) (*Pagination, error) { + if rawlink == "" { + return nil, errors.New("empty link header") + } + + p := &Pagination{} + for _, link := range linkheader.Parse(rawlink) { + switch link.Rel { + case "next": + maxID, err := getPaginationID(link.URL, "max_id") + if err != nil { + return nil, err + } + p.MaxID = maxID + case "prev": + sinceID, err := getPaginationID(link.URL, "since_id") + if err != nil { + return nil, err + } + p.SinceID = sinceID + } + } + + return p, nil +} + +func getPaginationID(rawurl, key string) (int64, error) { + u, err := url.Parse(rawurl) + if err != nil { + return 0, err + } + + id, err := strconv.ParseInt(u.Query().Get(key), 10, 64) + if err != nil { + return 0, err + } + + return id, nil +} + +func (p *Pagination) toValues() url.Values { + return p.setValues(url.Values{}) +} + +func (p *Pagination) setValues(params url.Values) url.Values { + if p.MaxID > 0 { + params.Set("max_id", fmt.Sprint(p.MaxID)) + } else if p.SinceID > 0 { + params.Set("since_id", fmt.Sprint(p.SinceID)) + } + if p.Limit > 0 { + params.Set("limit", fmt.Sprint(p.Limit)) + } + + return params +} diff --git a/mastodon_test.go b/mastodon_test.go index e0bf960..eb6c476 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -6,11 +6,88 @@ import ( "io" "net/http" "net/http/httptest" - "reflect" + "net/url" "testing" "time" ) +func TestDoAPI(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Query().Get("max_id") == "999" { + w.Header().Set("Link", `<:>; rel="next"`) + } else { + w.Header().Set("Link", `; rel="next", ; rel="prev"`) + } + fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`) + })) + defer ts.Close() + + c := NewClient(&Config{Server: ts.URL}) + var accounts []Account + err := c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, &Pagination{ + MaxID: 999, + }) + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + pg := &Pagination{ + MaxID: 123, + SinceID: 789, + Limit: 10, + } + err = c.doAPI(context.Background(), http.MethodGet, "/", url.Values{}, &accounts, pg) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if pg.MaxID != 234 { + t.Fatalf("want %d but %d", 234, pg.MaxID) + } + if pg.SinceID != 890 { + t.Fatalf("want %d but %d", 890, pg.SinceID) + } + if accounts[0].Username != "foo" { + t.Fatalf("want %q but %q", "foo", accounts[0].Username) + } + if accounts[1].Username != "bar" { + t.Fatalf("want %q but %q", "bar", accounts[1].Username) + } + + pg = &Pagination{ + MaxID: 123, + SinceID: 789, + Limit: 10, + } + err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, pg) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if pg.MaxID != 234 { + t.Fatalf("want %d but %d", 234, pg.MaxID) + } + if pg.SinceID != 890 { + t.Fatalf("want %d but %d", 890, pg.SinceID) + } + if accounts[0].Username != "foo" { + t.Fatalf("want %q but %q", "foo", accounts[0].Username) + } + if accounts[1].Username != "bar" { + t.Fatalf("want %q but %q", "bar", accounts[1].Username) + } + + // *Pagination is nil + err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, nil) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if accounts[0].Username != "foo" { + t.Fatalf("want %q but %q", "foo", accounts[0].Username) + } + if accounts[1].Username != "bar" { + t.Fatalf("want %q but %q", "bar", accounts[1].Username) + } +} + func TestAuthenticate(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.FormValue("username") != "valid" || r.FormValue("password") != "user" { @@ -153,7 +230,7 @@ func TestGetTimelineHome(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - tl, err := client.GetTimelineHome(context.Background()) + tl, err := client.GetTimelineHome(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -183,7 +260,7 @@ func TestGetTimelineHomeWithCancel(t *testing.T) { }) ctx, cancel := context.WithCancel(context.Background()) cancel() - _, err := client.GetTimelineHome(ctx) + _, err := client.GetTimelineHome(ctx, nil) if err == nil { t.Fatalf("should be fail: %v", err) } @@ -200,47 +277,85 @@ func TestForTheCoverages(t *testing.T) { _ = (&ErrorEvent{io.EOF}).Error() } -func TestLinkHeader(t *testing.T) { - tests := []struct { - header []string - rel string - want []string - }{ - { - header: []string{`; rel="foo"`}, - rel: "boo", - want: nil, - }, - { - header: []string{`; rel="foo"`}, - rel: "foo", - want: []string{"http://example.com/?max_id=3"}, - }, - { - header: []string{`; rel="foo1"`}, - rel: "foo", - want: nil, - }, - { - header: []string{`; rel="foo", ; rel="bar"`}, - rel: "foo", - want: []string{"http://example.com/?max_id=3"}, - }, - { - header: []string{`; rel="foo", ; rel="bar"`}, - rel: "bar", - want: []string{"http://example.com/?max_id=4"}, - }, +func TestNewPagination(t *testing.T) { + _, err := newPagination("") + if err == nil { + t.Fatalf("should be fail: %v", err) } - for _, test := range tests { - h := make(http.Header) - for _, he := range test.header { - h.Add("Link", he) - } - got := linkHeader(h, test.rel) - if !reflect.DeepEqual(got, test.want) { - t.Fatalf("want %v but %v", test.want, got) - } + _, err = newPagination(`<:>; rel="next"`) + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + _, err = newPagination(`<:>; rel="prev"`) + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + pg, err := newPagination(`; rel="next", ; rel="prev"`) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if pg.MaxID != 123 { + t.Fatalf("want %d but %d", 123, pg.MaxID) + } + if pg.SinceID != 789 { + t.Fatalf("want %d but %d", 789, pg.SinceID) + } +} + +func TestGetPaginationID(t *testing.T) { + _, err := getPaginationID(":", "max_id") + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + _, err = getPaginationID("http://example.com?max_id=abc", "max_id") + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + id, err := getPaginationID("http://example.com?max_id=123", "max_id") + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if id != 123 { + t.Fatalf("want %d but %d", 123, id) + } +} + +func TestPaginationSetValues(t *testing.T) { + p := &Pagination{ + MaxID: 123, + SinceID: 789, + Limit: 10, + } + before := url.Values{"key": {"value"}} + after := p.setValues(before) + if after.Get("key") != "value" { + t.Fatalf("want %q but %q", "value", after.Get("key")) + } + if after.Get("max_id") != "123" { + t.Fatalf("want %q but %q", "123", after.Get("max_id")) + } + if after.Get("since_id") != "" { + t.Fatalf("result should be empty string: %q", after.Get("since_id")) + } + if after.Get("limit") != "10" { + t.Fatalf("want %q but %q", "10", after.Get("limit")) + } + + p = &Pagination{ + MaxID: 0, + SinceID: 789, + } + before = url.Values{} + after = p.setValues(before) + if after.Get("max_id") != "" { + t.Fatalf("result should be empty string: %q", after.Get("max_id")) + } + if after.Get("since_id") != "789" { + t.Fatalf("want %q but %q", "789", after.Get("since_id")) } } diff --git a/notification.go b/notification.go index a25f3f0..15e6cf7 100644 --- a/notification.go +++ b/notification.go @@ -17,9 +17,9 @@ type Notification struct { } // GetNotifications return notifications. -func (c *Client) GetNotifications(ctx context.Context) ([]*Notification, error) { +func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notification, error) { var notifications []*Notification - err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, pg) if err != nil { return nil, err } diff --git a/notification_test.go b/notification_test.go index 87f0e32..740dec9 100644 --- a/notification_test.go +++ b/notification_test.go @@ -32,7 +32,7 @@ func TestGetNotifications(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - ns, err := client.GetNotifications(context.Background()) + ns, err := client.GetNotifications(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } diff --git a/status.go b/status.go index dbbed03..5191481 100644 --- a/status.go +++ b/status.go @@ -48,9 +48,9 @@ type Card struct { } // GetFavourites return the favorite list of the current user. -func (c *Client) GetFavourites(ctx context.Context) ([]*Status, error) { +func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, pg) if err != nil { return nil, err } @@ -88,9 +88,9 @@ 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) { +func (c *Client) GetRebloggedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, nil) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, pg) if err != nil { return nil, err } @@ -98,9 +98,9 @@ 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) { +func (c *Client) GetFavouritedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, nil) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, pg) if err != nil { return nil, err } @@ -148,9 +148,9 @@ 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) { +func (c *Client) GetTimelineHome(ctx context.Context, pg *Pagination) ([]*Status, error) { var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg) if err != nil { return nil, err } @@ -158,14 +158,14 @@ func (c *Client) GetTimelineHome(ctx context.Context) ([]*Status, error) { } // GetTimelinePublic return statuses from public timeline. -func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool) ([]*Status, error) { +func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) { params := url.Values{} if isLocal { params.Set("local", "t") } var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg) if err != nil { return nil, err } @@ -173,14 +173,14 @@ func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool) ([]*Status } // GetTimelineHashtag return statuses from tagged timeline. -func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool) ([]*Status, error) { +func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool, pg *Pagination) ([]*Status, error) { params := url.Values{} if isLocal { params.Set("local", "t") } var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, nil) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, pg) if err != nil { return nil, err } @@ -189,7 +189,7 @@ func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal boo // GetTimelineMedia return statuses from media timeline. // NOTE: This is an experimental feature of pawoo.net. -func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool) ([]*Status, error) { +func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) { params := url.Values{} params.Set("media", "t") if isLocal { @@ -197,7 +197,7 @@ func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool) ([]*Status, } var statuses []*Status - err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg) if err != nil { return nil, err } diff --git a/status_test.go b/status_test.go index bc4eed6..1359bb9 100644 --- a/status_test.go +++ b/status_test.go @@ -21,7 +21,7 @@ func TestGetFavourites(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - favs, err := client.GetFavourites(context.Background()) + favs, err := client.GetFavourites(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -152,11 +152,11 @@ func TestGetRebloggedBy(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetRebloggedBy(context.Background(), 123) + _, err := client.GetRebloggedBy(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - rbs, err := client.GetRebloggedBy(context.Background(), 1234567) + rbs, err := client.GetRebloggedBy(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -188,11 +188,11 @@ func TestGetFavouritedBy(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetFavouritedBy(context.Background(), 123) + _, err := client.GetFavouritedBy(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fbs, err := client.GetFavouritedBy(context.Background(), 1234567) + fbs, err := client.GetFavouritedBy(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -338,11 +338,11 @@ func TestGetTimelinePublic(t *testing.T) { defer ts.Close() client := NewClient(&Config{Server: ts.URL}) - _, err := client.GetTimelinePublic(context.Background(), false) + _, err := client.GetTimelinePublic(context.Background(), false, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - tl, err := client.GetTimelinePublic(context.Background(), true) + tl, err := client.GetTimelinePublic(context.Background(), true, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -374,11 +374,11 @@ func TestGetTimelineHashtag(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetTimelineHashtag(context.Background(), "notfound", false) + _, err := client.GetTimelineHashtag(context.Background(), "notfound", false, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - tags, err := client.GetTimelineHashtag(context.Background(), "zzz", true) + tags, err := client.GetTimelineHashtag(context.Background(), "zzz", true, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -410,11 +410,11 @@ func TestGetTimelineMedia(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, err := client.GetTimelineMedia(context.Background(), false) + _, err := client.GetTimelineMedia(context.Background(), false, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - tags, err := client.GetTimelineMedia(context.Background(), true) + tags, err := client.GetTimelineMedia(context.Background(), true, nil) if err != nil { t.Fatalf("should not be fail: %v", err) }