Add ModerationService, tweak structs, delete unneeded ones
Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
parent
10a5d5ac86
commit
a0b06ed651
9 changed files with 273 additions and 69 deletions
|
@ -303,10 +303,11 @@ func (s *AccountService) Trophies(ctx context.Context) ([]Trophy, *Response, err
|
|||
return nil, resp, err
|
||||
}
|
||||
|
||||
// todo: use Things struct
|
||||
var trophies []Trophy
|
||||
for _, trophy := range root.Data.Trophies {
|
||||
trophies = append(trophies, trophy.Data)
|
||||
if trophy.Data != nil {
|
||||
trophies = append(trophies, *trophy.Data)
|
||||
}
|
||||
}
|
||||
|
||||
return trophies, resp, nil
|
||||
|
|
|
@ -98,6 +98,7 @@ type Client struct {
|
|||
Comment *CommentService
|
||||
Flair *FlairService
|
||||
Listings *ListingsService
|
||||
Moderation *ModerationService
|
||||
Multi *MultiService
|
||||
Post *PostService
|
||||
Search *SearchService
|
||||
|
@ -134,6 +135,7 @@ func newClient(httpClient *http.Client) *Client {
|
|||
c.Comment = (*CommentService)(&c.common)
|
||||
c.Flair = (*FlairService)(&c.common)
|
||||
c.Listings = (*ListingsService)(&c.common)
|
||||
c.Moderation = (*ModerationService)(&c.common)
|
||||
c.Multi = (*MultiService)(&c.common)
|
||||
c.Post = (*PostService)(&c.common)
|
||||
c.Search = (*SearchService)(&c.common)
|
||||
|
|
67
moderation.go
Normal file
67
moderation.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package reddit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ModerationService handles communication with the moderation
|
||||
// related methods of the Reddit API.
|
||||
//
|
||||
// Reddit API docs: https://www.reddit.com/dev/api/#section_moderation
|
||||
type ModerationService service
|
||||
|
||||
// ModAction is an action executed by a moderator of a subreddit, such
|
||||
// as inviting another user to be a mod, or setting permissions.
|
||||
type ModAction struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Created *Timestamp `json:"created_utc,omitempty"`
|
||||
|
||||
Moderator string `json:"mod,omitempty"`
|
||||
// Not the full ID, just the ID36.
|
||||
ModeratorID string `json:"mod_id36,omitempty"`
|
||||
|
||||
// The author of whatever the action was produced on, e.g. a user, post, comment, etc.
|
||||
TargetAuthor string `json:"target_author,omitempty"`
|
||||
// This is the full ID of whatever the target was.
|
||||
TargetID string `json:"target_fullname,omitempty"`
|
||||
TargetTitle string `json:"target_title,omitempty"`
|
||||
TargetPermalink string `json:"target_permalink,omitempty"`
|
||||
TargetBody string `json:"target_body,omitempty"`
|
||||
|
||||
Subreddit string `json:"subreddit,omitempty"`
|
||||
// Not the full ID, just the ID36.
|
||||
SubredditID string `json:"sr_id36,omitempty"`
|
||||
}
|
||||
|
||||
// GetActions gets a list of moderator actions on a subreddit.
|
||||
func (s *ModerationService) GetActions(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*ModActions, *Response, error) {
|
||||
form := newSearchOptions(opts...)
|
||||
|
||||
path := fmt.Sprintf("r/%s/about/log", subreddit)
|
||||
path = addQuery(path, form)
|
||||
|
||||
req, err := s.client.NewRequest(http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(rootListing)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.getModeratorActions(), resp, nil
|
||||
}
|
||||
|
||||
/*
|
||||
type rootTrophyListing struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data struct {
|
||||
Trophies []rootTrophy `json:"trophies"`
|
||||
} `json:"data"`
|
||||
}
|
||||
*/
|
65
moderation_test.go
Normal file
65
moderation_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package reddit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var expectedModActionsResult = &ModActions{
|
||||
ModActions: []ModAction{
|
||||
{
|
||||
ID: "ModAction_b4e7979a-c4ad-11ea-8440-0ea1b7c2b8f9",
|
||||
Action: "spamcomment",
|
||||
Created: &Timestamp{time.Date(2020, 7, 13, 2, 8, 14, 0, time.UTC)},
|
||||
|
||||
Moderator: "v_95",
|
||||
ModeratorID: "164ab8",
|
||||
|
||||
TargetAuthor: "testuser",
|
||||
TargetID: "t1_fxw10aa",
|
||||
TargetPermalink: "/r/helloworldtestt/comments/hq6r3t/yo/fxw10aa/",
|
||||
TargetBody: "hi",
|
||||
|
||||
Subreddit: "helloworldtestt",
|
||||
SubredditID: "2uquw1",
|
||||
},
|
||||
{
|
||||
ID: "ModAction_a0408162-c4ad-11ea-8239-0e3b48262e8b",
|
||||
Action: "sticky",
|
||||
Created: &Timestamp{time.Date(2020, 7, 13, 2, 7, 38, 0, time.UTC)},
|
||||
|
||||
Moderator: "v_95",
|
||||
ModeratorID: "164ab8",
|
||||
|
||||
TargetAuthor: "testuser",
|
||||
TargetID: "t3_hq6r3t",
|
||||
TargetTitle: "yo",
|
||||
TargetPermalink: "/r/helloworldtestt/comments/hq6r3t/yo/",
|
||||
|
||||
Subreddit: "helloworldtestt",
|
||||
SubredditID: "2uquw1",
|
||||
},
|
||||
},
|
||||
After: "ModAction_a0408162-c4ad-11ea-8239-0e3b48262e8b",
|
||||
Before: "",
|
||||
}
|
||||
|
||||
func TestModerationService_GetActions(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
blob := readFileContents(t, "testdata/moderation/actions.json")
|
||||
|
||||
mux.HandleFunc("/r/testsubreddit/about/log", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
fmt.Fprint(w, blob)
|
||||
})
|
||||
|
||||
result, _, err := client.Moderation.GetActions(ctx, "testsubreddit")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedModActionsResult, result)
|
||||
}
|
25
search.go
25
search.go
|
@ -20,11 +20,8 @@ import (
|
|||
// Reddit API docs: https://www.reddit.com/dev/api/#section_search
|
||||
type SearchService service
|
||||
|
||||
// SearchOptions define options used in search queries.
|
||||
type SearchOptions = url.Values
|
||||
|
||||
func newSearchOptions(opts ...SearchOptionSetter) SearchOptions {
|
||||
searchOptions := make(SearchOptions)
|
||||
func newSearchOptions(opts ...SearchOptionSetter) url.Values {
|
||||
searchOptions := make(url.Values)
|
||||
for _, opt := range opts {
|
||||
opt(searchOptions)
|
||||
}
|
||||
|
@ -32,18 +29,18 @@ func newSearchOptions(opts ...SearchOptionSetter) SearchOptions {
|
|||
}
|
||||
|
||||
// SearchOptionSetter sets values for the options.
|
||||
type SearchOptionSetter func(opts SearchOptions)
|
||||
type SearchOptionSetter func(opts url.Values)
|
||||
|
||||
// SetAfter sets the after option.
|
||||
func SetAfter(v string) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("after", v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetBefore sets the before option.
|
||||
func SetBefore(v string) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("before", v)
|
||||
}
|
||||
}
|
||||
|
@ -51,41 +48,41 @@ func SetBefore(v string) SearchOptionSetter {
|
|||
// SetLimit sets the limit option.
|
||||
// Warning: It seems like setting the limit to 1 sometimes returns 0 results.
|
||||
func SetLimit(v int) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("limit", fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
|
||||
// SetSort sets the sort option.
|
||||
func SetSort(v Sort) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("sort", v.String())
|
||||
}
|
||||
}
|
||||
|
||||
// SetTimespan sets the timespan option.
|
||||
func SetTimespan(v Timespan) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("timespan", v.String())
|
||||
}
|
||||
}
|
||||
|
||||
// setType sets the type option.
|
||||
func setType(v string) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("type", v)
|
||||
}
|
||||
}
|
||||
|
||||
// setQuery sets the q option.
|
||||
func setQuery(v string) SearchOptionSetter {
|
||||
return func(opts SearchOptions) {
|
||||
return func(opts url.Values) {
|
||||
opts.Set("q", v)
|
||||
}
|
||||
}
|
||||
|
||||
// setRestrict sets the restrict_sr option.
|
||||
func setRestrict(opts SearchOptions) {
|
||||
func setRestrict(opts url.Values) {
|
||||
opts.Set("restrict_sr", "true")
|
||||
}
|
||||
|
||||
|
|
15
subreddit.go
15
subreddit.go
|
@ -15,11 +15,16 @@ import (
|
|||
// Reddit API docs: https://www.reddit.com/dev/api/#section_subreddits
|
||||
type SubredditService service
|
||||
|
||||
type subredditNamesRoot struct {
|
||||
type rootSubreddit struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data *Subreddit `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type rootSubredditNames struct {
|
||||
Names []string `json:"names,omitempty"`
|
||||
}
|
||||
|
||||
type subredditShortsRoot struct {
|
||||
type rootSubredditShorts struct {
|
||||
Subreddits []SubredditShort `json:"subreddits,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -66,7 +71,7 @@ func (s *SubredditService) GetByName(ctx context.Context, subreddit string) (*Su
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(subredditRoot)
|
||||
root := new(rootSubreddit)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
@ -161,7 +166,7 @@ func (s *SubredditService) SearchSubredditNames(ctx context.Context, query strin
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(subredditNamesRoot)
|
||||
root := new(rootSubredditNames)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
@ -180,7 +185,7 @@ func (s *SubredditService) SearchSubredditInfo(ctx context.Context, query string
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(subredditShortsRoot)
|
||||
root := new(rootSubredditShorts)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
|
51
testdata/moderation/actions.json
vendored
Normal file
51
testdata/moderation/actions.json
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"kind": "Listing",
|
||||
"data": {
|
||||
"modhash": null,
|
||||
"dist": null,
|
||||
"children": [
|
||||
{
|
||||
"kind": "modaction",
|
||||
"data": {
|
||||
"description": null,
|
||||
"target_body": "hi",
|
||||
"mod_id36": "164ab8",
|
||||
"created_utc": 1594606094.0,
|
||||
"subreddit": "helloworldtestt",
|
||||
"target_title": null,
|
||||
"target_permalink": "/r/helloworldtestt/comments/hq6r3t/yo/fxw10aa/",
|
||||
"subreddit_name_prefixed": "r/helloworldtestt",
|
||||
"details": "spam",
|
||||
"action": "spamcomment",
|
||||
"target_author": "testuser",
|
||||
"target_fullname": "t1_fxw10aa",
|
||||
"sr_id36": "2uquw1",
|
||||
"id": "ModAction_b4e7979a-c4ad-11ea-8440-0ea1b7c2b8f9",
|
||||
"mod": "v_95"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "modaction",
|
||||
"data": {
|
||||
"description": null,
|
||||
"target_body": null,
|
||||
"mod_id36": "164ab8",
|
||||
"created_utc": 1594606058.0,
|
||||
"subreddit": "helloworldtestt",
|
||||
"target_title": "yo",
|
||||
"target_permalink": "/r/helloworldtestt/comments/hq6r3t/yo/",
|
||||
"subreddit_name_prefixed": "r/helloworldtestt",
|
||||
"details": null,
|
||||
"action": "sticky",
|
||||
"target_author": "testuser",
|
||||
"target_fullname": "t3_hq6r3t",
|
||||
"sr_id36": "2uquw1",
|
||||
"id": "ModAction_a0408162-c4ad-11ea-8239-0e3b48262e8b",
|
||||
"mod": "v_95"
|
||||
}
|
||||
}
|
||||
],
|
||||
"after": "ModAction_a0408162-c4ad-11ea-8239-0e3b48262e8b",
|
||||
"before": null
|
||||
}
|
||||
}
|
77
things.go
77
things.go
|
@ -18,7 +18,8 @@ const (
|
|||
kindKarmaList = "KarmaList"
|
||||
kindTrophyList = "TrophyList"
|
||||
kindUserList = "UserList"
|
||||
kindMode = "more"
|
||||
kindMore = "more"
|
||||
kindModAction = "modaction"
|
||||
)
|
||||
|
||||
// Sort is a sorting option.
|
||||
|
@ -123,29 +124,10 @@ type Things struct {
|
|||
Users []User `json:"users,omitempty"`
|
||||
Posts []Post `json:"posts,omitempty"`
|
||||
Subreddits []Subreddit `json:"subreddits,omitempty"`
|
||||
ModActions []ModAction `json:"moderationActions,omitempty"`
|
||||
// todo: add the other kinds of things
|
||||
}
|
||||
|
||||
type commentRoot struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data *Comment `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type userRoot struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data *User `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type postRoot struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data *Post `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type subredditRoot struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data *Subreddit `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Things) init() {
|
||||
if t.Comments == nil {
|
||||
t.Comments = make([]Comment, 0)
|
||||
|
@ -159,6 +141,9 @@ func (t *Things) init() {
|
|||
if t.Subreddits == nil {
|
||||
t.Subreddits = make([]Subreddit, 0)
|
||||
}
|
||||
if t.ModActions == nil {
|
||||
t.ModActions = make([]ModAction, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
|
@ -171,31 +156,38 @@ func (t *Things) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
for _, child := range children {
|
||||
byteValue, _ := json.Marshal(child)
|
||||
data := child["data"]
|
||||
byteValue, _ := json.Marshal(data)
|
||||
|
||||
switch child["kind"] {
|
||||
// todo: kindMore
|
||||
case kindComment:
|
||||
root := new(commentRoot)
|
||||
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
|
||||
t.Comments = append(t.Comments, *root.Data)
|
||||
v := new(Comment)
|
||||
if err := json.Unmarshal(byteValue, v); err == nil && v != nil {
|
||||
t.Comments = append(t.Comments, *v)
|
||||
}
|
||||
case kindAccount:
|
||||
root := new(userRoot)
|
||||
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
|
||||
t.Users = append(t.Users, *root.Data)
|
||||
v := new(User)
|
||||
if err := json.Unmarshal(byteValue, v); err == nil && v != nil {
|
||||
t.Users = append(t.Users, *v)
|
||||
}
|
||||
case kindLink:
|
||||
root := new(postRoot)
|
||||
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
|
||||
t.Posts = append(t.Posts, *root.Data)
|
||||
v := new(Post)
|
||||
if err := json.Unmarshal(byteValue, v); err == nil && v != nil {
|
||||
t.Posts = append(t.Posts, *v)
|
||||
}
|
||||
case kindMessage:
|
||||
case kindSubreddit:
|
||||
root := new(subredditRoot)
|
||||
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
|
||||
t.Subreddits = append(t.Subreddits, *root.Data)
|
||||
v := new(Subreddit)
|
||||
if err := json.Unmarshal(byteValue, v); err == nil && v != nil {
|
||||
t.Subreddits = append(t.Subreddits, *v)
|
||||
}
|
||||
case kindAward:
|
||||
case kindModAction:
|
||||
v := new(ModAction)
|
||||
if err := json.Unmarshal(byteValue, v); err == nil && v != nil {
|
||||
t.ModActions = append(t.ModActions, *v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,6 +386,16 @@ func (rl *rootListing) getSubreddits() *Subreddits {
|
|||
return v
|
||||
}
|
||||
|
||||
func (rl *rootListing) getModeratorActions() *ModActions {
|
||||
v := new(ModActions)
|
||||
if rl != nil && rl.Data != nil {
|
||||
v.ModActions = rl.Data.Things.ModActions
|
||||
v.After = rl.Data.After
|
||||
v.Before = rl.Data.Before
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Comments is a list of comments
|
||||
type Comments struct {
|
||||
Comments []Comment `json:"comments"`
|
||||
|
@ -422,6 +424,13 @@ type Posts struct {
|
|||
Before string `json:"before"`
|
||||
}
|
||||
|
||||
// ModActions is a list of moderator action.
|
||||
type ModActions struct {
|
||||
ModActions []ModAction `json:"moderator_actions"`
|
||||
After string `json:"after"`
|
||||
Before string `json:"before"`
|
||||
}
|
||||
|
||||
// PostAndComments is a post and its comments
|
||||
type PostAndComments struct {
|
||||
Post Post `json:"post"`
|
||||
|
|
13
user.go
13
user.go
|
@ -13,6 +13,11 @@ import (
|
|||
// Reddit API docs: https://www.reddit.com/dev/api/#section_users
|
||||
type UserService service
|
||||
|
||||
type rootUser struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data *User `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// User represents a Reddit user.
|
||||
type User struct {
|
||||
// this is not the full ID, watch out.
|
||||
|
@ -66,7 +71,7 @@ type rootTrophyListing struct {
|
|||
|
||||
type rootTrophy struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data Trophy `json:"data"`
|
||||
Data *Trophy `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// Trophy is a Reddit award.
|
||||
|
@ -84,7 +89,7 @@ func (s *UserService) Get(ctx context.Context, username string) (*User, *Respons
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(userRoot)
|
||||
root := new(rootUser)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
@ -501,7 +506,9 @@ func (s *UserService) TrophiesOf(ctx context.Context, username string) ([]Trophy
|
|||
|
||||
var trophies []Trophy
|
||||
for _, trophy := range root.Data.Trophies {
|
||||
trophies = append(trophies, trophy.Data)
|
||||
if trophy.Data != nil {
|
||||
trophies = append(trophies, *trophy.Data)
|
||||
}
|
||||
}
|
||||
|
||||
return trophies, resp, nil
|
||||
|
|
Loading…
Reference in a new issue