Get/set/delete trusted users. Tweak friends list struct
Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
parent
12e801f83c
commit
6ffcf70090
5 changed files with 227 additions and 63 deletions
153
account.go
153
account.go
|
@ -2,9 +2,8 @@ package geddit
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// AccountService handles communication with the account
|
||||
|
@ -28,9 +27,10 @@ type SubredditKarma struct {
|
|||
// Settings are the user's account settings.
|
||||
// Some of the fields' descriptions are taken from:
|
||||
// https://praw.readthedocs.io/en/latest/code_overview/other/preferences.html#praw.models.Preferences.update
|
||||
// todo: these should probably be pointers with omitempty
|
||||
type Settings struct {
|
||||
// Control whose private messages you see: "everyone" or "whitelisted".
|
||||
// Control whose private messages you see.
|
||||
// - "everyone": everyone except blocked users
|
||||
// - "whitelisted": only trusted users
|
||||
AcceptPrivateMessages *string `json:"accept_pms,omitempty"`
|
||||
// Allow Reddit to use your activity on Reddit to show you more relevant advertisements.
|
||||
ActivityRelevantAds *bool `json:"activity_relevant_ads,omitempty"`
|
||||
|
@ -209,6 +209,13 @@ type Settings struct {
|
|||
EnableVideoAutoplay *bool `json:"video_autoplay,omitempty"`
|
||||
}
|
||||
|
||||
type rootRelationshipList struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data struct {
|
||||
Relationships []Relationship `json:"children"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Info returns some general information about your account.
|
||||
func (s *AccountService) Info(ctx context.Context) (*User, *Response, error) {
|
||||
path := "api/v1/me"
|
||||
|
@ -296,6 +303,7 @@ 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)
|
||||
|
@ -304,52 +312,6 @@ func (s *AccountService) Trophies(ctx context.Context) ([]Trophy, *Response, err
|
|||
return trophies, resp, nil
|
||||
}
|
||||
|
||||
type rootFriendList struct {
|
||||
Friends []Relationship
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (l *rootFriendList) UnmarshalJSON(b []byte) error {
|
||||
var resBody []interface{}
|
||||
err := json.Unmarshal(b, &resBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resBody) == 0 {
|
||||
return errors.New("unexpected data length received")
|
||||
}
|
||||
|
||||
data, ok := resBody[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("unexpected data type received")
|
||||
}
|
||||
|
||||
dataMap, ok := data["data"].(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("data does not contain expected field")
|
||||
}
|
||||
|
||||
children, ok := dataMap["children"].([]interface{})
|
||||
if !ok {
|
||||
return errors.New("data does not contain expected field")
|
||||
}
|
||||
|
||||
byteValue, err := json.Marshal(children)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var friends []Relationship
|
||||
err = json.Unmarshal(byteValue, &friends)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Friends = friends
|
||||
return nil
|
||||
}
|
||||
|
||||
// Friends returns a list of your friends.
|
||||
func (s *AccountService) Friends(ctx context.Context) ([]Relationship, *Response, error) {
|
||||
path := "prefs/friends"
|
||||
|
@ -359,20 +321,13 @@ func (s *AccountService) Friends(ctx context.Context) ([]Relationship, *Response
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(rootFriendList)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
root := make([]rootRelationshipList, 2)
|
||||
resp, err := s.client.Do(ctx, req, &root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Friends, resp, nil
|
||||
}
|
||||
|
||||
type rootBlockedListing struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Data struct {
|
||||
Blocked []Relationship `json:"children"`
|
||||
} `json:"data"`
|
||||
return root[0].Data.Relationships, resp, nil
|
||||
}
|
||||
|
||||
// Blocked returns a list of your blocked users.
|
||||
|
@ -384,11 +339,85 @@ func (s *AccountService) Blocked(ctx context.Context) ([]Relationship, *Response
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(rootBlockedListing)
|
||||
root := new(rootRelationshipList)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Data.Blocked, resp, nil
|
||||
return root.Data.Relationships, resp, nil
|
||||
}
|
||||
|
||||
// Messaging returns blocked users and trusted users, respectively.
|
||||
func (s *AccountService) Messaging(ctx context.Context) ([]Relationship, []Relationship, *Response, error) {
|
||||
path := "prefs/messaging"
|
||||
|
||||
req, err := s.client.NewRequest(http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
root := make([]rootRelationshipList, 2)
|
||||
resp, err := s.client.Do(ctx, req, &root)
|
||||
if err != nil {
|
||||
return nil, nil, resp, err
|
||||
}
|
||||
|
||||
blocked := root[0].Data.Relationships
|
||||
trusted := root[1].Data.Relationships
|
||||
|
||||
return blocked, trusted, resp, nil
|
||||
}
|
||||
|
||||
// Trusted returns a list of your trusted users.
|
||||
func (s *AccountService) Trusted(ctx context.Context) ([]Relationship, *Response, error) {
|
||||
path := "prefs/trusted"
|
||||
|
||||
req, err := s.client.NewRequest(http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(rootRelationshipList)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Data.Relationships, resp, nil
|
||||
}
|
||||
|
||||
// AddTrusted adds a user to your trusted users.
|
||||
// This is not visible in the Reddit API docs.
|
||||
func (s *AccountService) AddTrusted(ctx context.Context, username string) (*Response, error) {
|
||||
path := "api/add_whitelisted"
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("api_type", "json")
|
||||
form.Set("name", username)
|
||||
// todo: you can also do this with the user id. form.Set("id", id). should we? or is this enough?
|
||||
|
||||
req, err := s.client.NewPostForm(path, form)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(ctx, req, nil)
|
||||
}
|
||||
|
||||
// RemoveTrusted removes a user from your trusted users.
|
||||
// This is not visible in the Reddit API docs.
|
||||
func (s *AccountService) RemoveTrusted(ctx context.Context, username string) (*Response, error) {
|
||||
path := "api/remove_whitelisted"
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("name", username)
|
||||
// todo: you can also do this with the user id. form.Set("id", id). should we? or is this enough?
|
||||
|
||||
req, err := s.client.NewPostForm(path, form)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(ctx, req, nil)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -112,6 +113,15 @@ var expectedRelationships = []Relationship{
|
|||
},
|
||||
}
|
||||
|
||||
var expectedRelationships2 = []Relationship{
|
||||
{
|
||||
ID: "r9_1re60i",
|
||||
User: "test3",
|
||||
UserID: "t2_test3",
|
||||
Created: &Timestamp{time.Date(2020, 3, 6, 2, 27, 0, 0, time.UTC)},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAccountService_Info(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
@ -230,3 +240,75 @@ func TestAccountService_Blocked(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedRelationships, relationships)
|
||||
}
|
||||
|
||||
func TestAccountService_Messaging(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
blob := readFileContents(t, "testdata/account/messaging.json")
|
||||
|
||||
mux.HandleFunc("/prefs/messaging", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
fmt.Fprint(w, blob)
|
||||
})
|
||||
|
||||
blocked, trusted, _, err := client.Account.Messaging(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedRelationships, blocked)
|
||||
assert.Equal(t, expectedRelationships2, trusted)
|
||||
}
|
||||
|
||||
func TestAccountService_Trusted(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
blob := readFileContents(t, "testdata/account/trusted.json")
|
||||
|
||||
mux.HandleFunc("/prefs/trusted", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
fmt.Fprint(w, blob)
|
||||
})
|
||||
|
||||
relationships, _, err := client.Account.Trusted(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedRelationships, relationships)
|
||||
}
|
||||
|
||||
func TestAccountService_AddTrusted(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/api/add_whitelisted", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("api_type", "json")
|
||||
form.Set("name", "test123")
|
||||
|
||||
err := r.ParseForm()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, form, r.Form)
|
||||
})
|
||||
|
||||
_, err := client.Account.AddTrusted(ctx, "test123")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAccountService_RemoveTrusted(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/api/remove_whitelisted", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("name", "test123")
|
||||
|
||||
err := r.ParseForm()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, form, r.Form)
|
||||
})
|
||||
|
||||
_, err := client.Account.RemoveTrusted(ctx, "test123")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ type RedditError struct {
|
|||
}
|
||||
|
||||
func (e *RedditError) Error() string {
|
||||
return fmt.Sprintf("%s: %s because of field %q", e.Label, e.Reason, e.Field)
|
||||
return fmt.Sprintf("field %q caused %s: %s", e.Field, e.Label, e.Reason)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
|
|
34
testdata/account/messaging.json
vendored
Normal file
34
testdata/account/messaging.json
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
[
|
||||
{
|
||||
"kind": "UserList",
|
||||
"data": {
|
||||
"children": [
|
||||
{
|
||||
"date": 1593362635,
|
||||
"rel_id": "r9_1r4879",
|
||||
"name": "test1",
|
||||
"id": "t2_test1"
|
||||
},
|
||||
{
|
||||
"date": 1593362642,
|
||||
"rel_id": "r9_1re930",
|
||||
"name": "test2",
|
||||
"id": "t2_test2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "UserList",
|
||||
"data": {
|
||||
"children": [
|
||||
{
|
||||
"date": 1583461620,
|
||||
"rel_id": "r9_1re60i",
|
||||
"name": "test3",
|
||||
"id": "t2_test3"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
19
testdata/account/trusted.json
vendored
Normal file
19
testdata/account/trusted.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"kind": "UserList",
|
||||
"data": {
|
||||
"children": [
|
||||
{
|
||||
"date": 1593362635,
|
||||
"rel_id": "r9_1r4879",
|
||||
"name": "test1",
|
||||
"id": "t2_test1"
|
||||
},
|
||||
{
|
||||
"date": 1593362642,
|
||||
"rel_id": "r9_1re930",
|
||||
"name": "test2",
|
||||
"id": "t2_test2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue