From 67cd813e70fe044ecad1c7720d8400605a605cce Mon Sep 17 00:00:00 2001 From: Vartan Benohanian Date: Tue, 11 Aug 2020 14:51:47 -0400 Subject: [PATCH] Get wikibanned, contributors, wikicontributors, etc. Signed-off-by: Vartan Benohanian --- subreddit.go | 206 +++++++++++++----- subreddit_test.go | 192 +++++++++++----- .../{muted-users.json => relationships.json} | 4 +- user.go | 8 - 4 files changed, 296 insertions(+), 114 deletions(-) rename testdata/subreddit/{muted-users.json => relationships.json} (85%) diff --git a/subreddit.go b/subreddit.go index 9b5395e..82f078e 100644 --- a/subreddit.go +++ b/subreddit.go @@ -26,28 +26,31 @@ type rootSubredditNames struct { Names []string `json:"names,omitempty"` } -type rootModeratorList struct { - Kind string `json:"kind,omitempty"` - Data struct { - Moderators []Moderator `json:"children"` - } `json:"data"` +// Relationship holds information about a relationship (friend/blocked). +// todo: there's also banned, wikibanned, etc. +type Relationship struct { + ID string `json:"rel_id,omitempty"` + User string `json:"name,omitempty"` + UserID string `json:"id,omitempty"` + Created *Timestamp `json:"date,omitempty"` +} + +// Relationships is a listing of relationships. +type Relationships struct { + Relationships []*Relationship `json:"relationships"` + After string `json:"after"` + Before string `json:"before"` } // Moderator is a user who moderates a subreddit. type Moderator struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` + *Relationship Permissions []string `json:"mod_permissions"` } -// Ban represents a banned user and the ban information. +// Ban represents a banned relationship. type Ban struct { - ID string `json:"rel_id,omitempty"` - Created *Timestamp `json:"date,omitempty"` - - User string `json:"name,omitempty"` - UserID string `json:"id,omitempty"` - + *Relationship // nil means the ban is permanent DaysLeft *int `json:"days_left"` Note string `json:"note,omitempty"` @@ -60,22 +63,6 @@ type Bans struct { Before string `json:"before"` } -// Mute represents a muted user and the mute information. -type Mute struct { - ID string `json:"rel_id,omitempty"` - Created *Timestamp `json:"date,omitempty"` - - User string `json:"name,omitempty"` - UserID string `json:"id,omitempty"` -} - -// Mutes is a listing of Mute. -type Mutes struct { - Mutes []*Mute `json:"mutes"` - After string `json:"after"` - Before string `json:"before"` -} - func (s *SubredditService) getPosts(ctx context.Context, sort string, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { path := sort if subreddit != "" { @@ -416,24 +403,6 @@ func (s *SubredditService) getSticky(ctx context.Context, subreddit string, num return root, resp, nil } -// Moderators returns the moderators of a subreddit. -func (s *SubredditService) Moderators(ctx context.Context, subreddit string) (interface{}, *Response, error) { - path := fmt.Sprintf("r/%s/about/moderators", subreddit) - - req, err := s.client.NewRequest(http.MethodGet, path, nil) - if err != nil { - return nil, nil, err - } - - root := new(rootModeratorList) - resp, err := s.client.Do(ctx, req, root) - if err != nil { - return nil, resp, err - } - - return root.Data.Moderators, resp, nil -} - // todo: sr_detail's NSFW indicator is over_18 instead of over18 func (s *SubredditService) random(ctx context.Context, nsfw bool) (*Subreddit, *Response, error) { path := "r/random" @@ -551,7 +520,7 @@ func (s *SubredditService) Banned(ctx context.Context, subreddit string, opts *L } // Muted gets muted users from the subreddit. -func (s *SubredditService) Muted(ctx context.Context, subreddit string, opts *ListOptions) (*Mutes, *Response, error) { +func (s *SubredditService) Muted(ctx context.Context, subreddit string, opts *ListOptions) (*Relationships, *Response, error) { path := fmt.Sprintf("r/%s/about/muted", subreddit) path, err := addOptions(path, opts) @@ -564,11 +533,46 @@ func (s *SubredditService) Muted(ctx context.Context, subreddit string, opts *Li return nil, nil, err } + root := new(struct { + Data struct { + Relationships []*Relationship `json:"children"` + After string `json:"after"` + Before string `json:"before"` + } `json:"data"` + }) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + relationships := &Relationships{ + Relationships: root.Data.Relationships, + After: root.Data.After, + Before: root.Data.Before, + } + + return relationships, resp, nil +} + +// WikiBanned gets banned users from the subreddit. +func (s *SubredditService) WikiBanned(ctx context.Context, subreddit string, opts *ListOptions) (*Bans, *Response, error) { + path := fmt.Sprintf("r/%s/about/wikibanned", subreddit) + + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + var root struct { Data struct { - Mutes []*Mute `json:"children"` - After string `json:"after"` - Before string `json:"before"` + Bans []*Ban `json:"children"` + After string `json:"after"` + Before string `json:"before"` } `json:"data"` } resp, err := s.client.Do(ctx, req, &root) @@ -576,11 +580,103 @@ func (s *SubredditService) Muted(ctx context.Context, subreddit string, opts *Li return nil, resp, err } - mutes := &Mutes{ - Mutes: root.Data.Mutes, + bans := &Bans{ + Bans: root.Data.Bans, After: root.Data.After, Before: root.Data.Before, } - return mutes, resp, nil + return bans, resp, nil +} + +// Contributors gets contributors (also known as approved users) from the subreddit. +func (s *SubredditService) Contributors(ctx context.Context, subreddit string, opts *ListOptions) (*Relationships, *Response, error) { + path := fmt.Sprintf("r/%s/about/contributors", subreddit) + + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(struct { + Data struct { + Relationships []*Relationship `json:"children"` + After string `json:"after"` + Before string `json:"before"` + } `json:"data"` + }) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + relationships := &Relationships{ + Relationships: root.Data.Relationships, + After: root.Data.After, + Before: root.Data.Before, + } + + return relationships, resp, nil +} + +// WikiContributors gets contributors of the wiki from the subreddit. +func (s *SubredditService) WikiContributors(ctx context.Context, subreddit string, opts *ListOptions) (*Relationships, *Response, error) { + path := fmt.Sprintf("r/%s/about/wikicontributors", subreddit) + + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(struct { + Data struct { + Relationships []*Relationship `json:"children"` + After string `json:"after"` + Before string `json:"before"` + } `json:"data"` + }) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + relationships := &Relationships{ + Relationships: root.Data.Relationships, + After: root.Data.After, + Before: root.Data.Before, + } + + return relationships, resp, nil +} + +// Moderators gets the moderators of the subreddit. +func (s *SubredditService) Moderators(ctx context.Context, subreddit string) ([]*Moderator, *Response, error) { + path := fmt.Sprintf("r/%s/about/moderators", subreddit) + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(struct { + Data struct { + Moderators []*Moderator `json:"children"` + } `json:"data"` + }) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root.Data.Moderators, resp, nil } diff --git a/subreddit_test.go b/subreddit_test.go index b5e49f4..44b8f4b 100644 --- a/subreddit_test.go +++ b/subreddit_test.go @@ -200,11 +200,6 @@ var expectedSearchPosts = &Posts{ After: "t3_hmwhd7", } -var expectedModerators = []Moderator{ - {ID: "t2_test1", Name: "testuser1", Permissions: []string{"all"}}, - {ID: "t2_test2", Name: "testuser2", Permissions: []string{"all"}}, -} - var expectedRandomSubreddit = &Subreddit{ FullID: "t5_2wi4l", Created: &Timestamp{time.Date(2013, 3, 1, 4, 4, 18, 0, time.UTC)}, @@ -219,24 +214,47 @@ var expectedRandomSubreddit = &Subreddit{ Subscribers: 52357, } +var expectedRelationships3 = &Relationships{ + Relationships: []*Relationship{ + { + ID: "rel_id1", + Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 2, 0, time.UTC)}, + User: "testuser1", + UserID: "t2_user1", + }, + { + ID: "rel_id2", + Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 0, 0, time.UTC)}, + User: "testuser2", + UserID: "t2_user2", + }, + }, + After: "", + Before: "", +} + var expectedBans = &Bans{ Bans: []*Ban{ { - ID: "rb_123", - Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 2, 0, time.UTC)}, + Relationship: &Relationship{ + ID: "rb_123", + Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 2, 0, time.UTC)}, - User: "testuser1", - UserID: "t2_user1", + User: "testuser1", + UserID: "t2_user1", + }, DaysLeft: Int(43), Note: "Spam", }, { - ID: "rb_456", - Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 0, 0, time.UTC)}, + Relationship: &Relationship{ + ID: "rb_456", + Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 0, 0, time.UTC)}, - User: "testuser2", - UserID: "t2_user2", + User: "testuser2", + UserID: "t2_user2", + }, DaysLeft: nil, Note: "Spam", @@ -246,25 +264,25 @@ var expectedBans = &Bans{ Before: "", } -var expectedMutes = &Mutes{ - Mutes: []*Mute{ - { - ID: "Mute_123", - Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 2, 0, time.UTC)}, - - User: "testuser1", - UserID: "t2_user1", - }, - { - ID: "Mute_456", - Created: &Timestamp{time.Date(2020, 8, 11, 2, 35, 0, 0, time.UTC)}, - - User: "testuser2", - UserID: "t2_user2", +var expectedModerators = []*Moderator{ + { + Relationship: &Relationship{ + ID: "rb_tmatb9", + User: "testuser1", + UserID: "t2_test1", + Created: &Timestamp{time.Date(2013, 7, 29, 20, 44, 27, 0, time.UTC)}, }, + Permissions: []string{"all"}, + }, + { + Relationship: &Relationship{ + ID: "rb_5c9s4d", + User: "testuser2", + UserID: "t2_test2", + Created: &Timestamp{time.Date(2014, 3, 1, 18, 13, 53, 0, time.UTC)}, + }, + Permissions: []string{"all"}, }, - After: "", - Before: "", } func TestSubredditService_HotPosts(t *testing.T) { @@ -791,23 +809,6 @@ func TestSubredditService_SearchPosts_InSubreddits(t *testing.T) { assert.Equal(t, expectedSearchPosts, posts) } -func TestSubredditService_Moderators(t *testing.T) { - setup() - defer teardown() - - blob, err := readFileContents("testdata/subreddit/moderators.json") - assert.NoError(t, err) - - mux.HandleFunc("/r/test/about/moderators", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, http.MethodGet, r.Method) - fmt.Fprint(w, blob) - }) - - moderators, _, err := client.Subreddit.Moderators(ctx, "test") - assert.NoError(t, err) - assert.Equal(t, expectedModerators, moderators) -} - func TestSubredditService_Random(t *testing.T) { setup() defer teardown() @@ -901,7 +902,7 @@ func TestSubredditService_Muted(t *testing.T) { setup() defer teardown() - blob, err := readFileContents("testdata/subreddit/muted-users.json") + blob, err := readFileContents("testdata/subreddit/relationships.json") assert.NoError(t, err) mux.HandleFunc("/r/test/about/muted", func(w http.ResponseWriter, r *http.Request) { @@ -920,5 +921,98 @@ func TestSubredditService_Muted(t *testing.T) { mutes, _, err := client.Subreddit.Muted(ctx, "test", &ListOptions{Before: "testbefore", Limit: 50}) assert.NoError(t, err) - assert.Equal(t, expectedMutes, mutes) + assert.Equal(t, expectedRelationships3, mutes) +} + +func TestSubredditService_WikiBanned(t *testing.T) { + setup() + defer teardown() + + blob, err := readFileContents("testdata/subreddit/banned-users.json") + assert.NoError(t, err) + + mux.HandleFunc("/r/test/about/wikibanned", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("after", "testafter") + form.Set("limit", "15") + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + bans, _, err := client.Subreddit.WikiBanned(ctx, "test", &ListOptions{After: "testafter", Limit: 15}) + assert.NoError(t, err) + assert.Equal(t, expectedBans, bans) +} + +func TestSubredditService_Contributors(t *testing.T) { + setup() + defer teardown() + + blob, err := readFileContents("testdata/subreddit/relationships.json") + assert.NoError(t, err) + + mux.HandleFunc("/r/test/about/contributors", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "5") + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + contributors, _, err := client.Subreddit.Contributors(ctx, "test", &ListOptions{Limit: 5}) + assert.NoError(t, err) + assert.Equal(t, expectedRelationships3, contributors) +} + +func TestSubredditService_WikiContributors(t *testing.T) { + setup() + defer teardown() + + blob, err := readFileContents("testdata/subreddit/relationships.json") + assert.NoError(t, err) + + mux.HandleFunc("/r/test/about/wikicontributors", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "99") + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + contributors, _, err := client.Subreddit.WikiContributors(ctx, "test", &ListOptions{Limit: 99}) + assert.NoError(t, err) + assert.Equal(t, expectedRelationships3, contributors) +} + +func TestSubredditService_Moderators(t *testing.T) { + setup() + defer teardown() + + blob, err := readFileContents("testdata/subreddit/moderators.json") + assert.NoError(t, err) + + mux.HandleFunc("/r/test/about/moderators", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + moderators, _, err := client.Subreddit.Moderators(ctx, "test") + assert.NoError(t, err) + assert.Equal(t, expectedModerators, moderators) } diff --git a/testdata/subreddit/muted-users.json b/testdata/subreddit/relationships.json similarity index 85% rename from testdata/subreddit/muted-users.json rename to testdata/subreddit/relationships.json index a362d09..298aaba 100644 --- a/testdata/subreddit/muted-users.json +++ b/testdata/subreddit/relationships.json @@ -6,13 +6,13 @@ "children": [ { "date": 1597113302.0, - "rel_id": "Mute_123", + "rel_id": "rel_id1", "name": "testuser1", "id": "t2_user1" }, { "date": 1597113300.0, - "rel_id": "Mute_456", + "rel_id": "rel_id2", "name": "testuser2", "id": "t2_user2" } diff --git a/user.go b/user.go index cfd669a..92358b0 100644 --- a/user.go +++ b/user.go @@ -49,14 +49,6 @@ type UserSummary struct { NSFW bool `json:"profile_over_18"` } -// Relationship holds information about a relationship (friend/blocked). -type Relationship struct { - ID string `json:"rel_id,omitempty"` - User string `json:"name,omitempty"` - UserID string `json:"id,omitempty"` - Created *Timestamp `json:"date,omitempty"` -} - // Blocked represents a blocked relationship. type Blocked struct { Blocked string `json:"name,omitempty"`