2020-07-11 13:49:07 -04:00
|
|
|
package reddit
|
2020-07-06 22:10:47 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2020-08-07 01:11:58 -04:00
|
|
|
|
|
|
|
"github.com/google/go-querystring/query"
|
2020-07-06 22:10:47 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// MultiService handles communication with the multireddit
|
|
|
|
// related methods of the Reddit API.
|
|
|
|
//
|
|
|
|
// Reddit API docs: https://www.reddit.com/dev/api#section_multis
|
2020-07-21 23:05:24 -04:00
|
|
|
type MultiService struct {
|
|
|
|
client *Client
|
|
|
|
}
|
2020-07-06 22:10:47 -04:00
|
|
|
|
|
|
|
// Multi is a multireddit, i.e. a customizable group of subreddits.
|
|
|
|
// Users can create multis for custom navigation, instead of browsing
|
|
|
|
// one subreddit or all subreddits at a time.
|
|
|
|
type Multi struct {
|
2020-07-11 01:37:19 -04:00
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
DisplayName string `json:"display_name,omitempty"`
|
|
|
|
// Format: user/{username}/m/{multiname}
|
2020-07-06 22:10:47 -04:00
|
|
|
Path string `json:"path,omitempty"`
|
|
|
|
Description string `json:"description_md,omitempty"`
|
|
|
|
Subreddits SubredditNames `json:"subreddits"`
|
2020-07-11 01:37:19 -04:00
|
|
|
CopiedFrom *string `json:"copied_from"`
|
2020-07-06 22:10:47 -04:00
|
|
|
|
|
|
|
Owner string `json:"owner,omitempty"`
|
2020-07-11 01:37:19 -04:00
|
|
|
OwnerID string `json:"owner_id,omitempty"`
|
2020-07-06 22:10:47 -04:00
|
|
|
Created *Timestamp `json:"created_utc,omitempty"`
|
|
|
|
|
|
|
|
NumberOfSubscribers int `json:"num_subscribers"`
|
|
|
|
Visibility string `json:"visibility,omitempty"`
|
|
|
|
Subscribed bool `json:"is_subscriber"`
|
|
|
|
Favorite bool `json:"is_favorited"`
|
|
|
|
CanEdit bool `json:"can_edit"`
|
|
|
|
NSFW bool `json:"over_18"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubredditNames is a list of subreddit names.
|
|
|
|
type SubredditNames []string
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
|
|
func (n *SubredditNames) UnmarshalJSON(data []byte) error {
|
2020-07-30 13:01:18 -04:00
|
|
|
type subreddit struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
}
|
|
|
|
var subreddits []subreddit
|
2020-07-06 22:10:47 -04:00
|
|
|
|
|
|
|
err := json.Unmarshal(data, &subreddits)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-29 13:52:12 -04:00
|
|
|
*n = make(SubredditNames, len(subreddits))
|
|
|
|
for i, sr := range subreddits {
|
|
|
|
(*n)[i] = sr.Name
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-11 01:37:19 -04:00
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
|
|
|
func (n *SubredditNames) MarshalJSON() ([]byte, error) {
|
2020-07-30 13:01:18 -04:00
|
|
|
type subreddit struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
}
|
2020-07-11 01:37:19 -04:00
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
subreddits := make([]subreddit, 0)
|
2020-07-30 13:01:18 -04:00
|
|
|
for _, name := range *n {
|
|
|
|
subreddits = append(subreddits, subreddit{name})
|
2020-07-11 01:37:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return json.Marshal(subreddits)
|
|
|
|
}
|
|
|
|
|
2020-07-06 22:10:47 -04:00
|
|
|
// MultiCopyRequest represents a request to copy a multireddit.
|
|
|
|
type MultiCopyRequest struct {
|
2020-08-07 01:11:58 -04:00
|
|
|
FromPath string `url:"from"`
|
|
|
|
ToPath string `url:"to"`
|
2020-07-06 22:10:47 -04:00
|
|
|
// Raw markdown text.
|
2020-08-07 01:11:58 -04:00
|
|
|
Description string `url:"description_md,omitempty"`
|
2020-07-06 22:10:47 -04:00
|
|
|
// No longer than 50 characters.
|
2020-08-07 01:11:58 -04:00
|
|
|
DisplayName string `url:"display_name,omitempty"`
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-07-11 01:37:19 -04:00
|
|
|
// MultiCreateOrUpdateRequest represents a request to create/update a multireddit.
|
|
|
|
type MultiCreateOrUpdateRequest struct {
|
|
|
|
// For updates, this is the display name, i.e. the header of the multi.
|
|
|
|
// Not part of the path necessarily.
|
|
|
|
Name string `json:"display_name,omitempty"`
|
|
|
|
Description string `json:"description_md,omitempty"`
|
|
|
|
Subreddits SubredditNames `json:"subreddits,omitempty"`
|
2020-08-16 22:22:13 -04:00
|
|
|
// One of: private, public, hidden.
|
2020-07-11 01:37:19 -04:00
|
|
|
Visibility string `json:"visibility,omitempty"`
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-07-11 01:37:19 -04:00
|
|
|
type rootMultiDescription struct {
|
2020-09-09 23:02:06 -04:00
|
|
|
Body string `json:"body_md"`
|
2020-07-11 01:37:19 -04:00
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Get the multireddit from its url path.
|
2020-07-06 22:10:47 -04:00
|
|
|
func (s *MultiService) Get(ctx context.Context, multiPath string) (*Multi, *Response, error) {
|
|
|
|
path := fmt.Sprintf("api/multi/%s", multiPath)
|
2020-09-18 11:27:42 -04:00
|
|
|
t, resp, err := s.client.getThing(ctx, path, nil)
|
2020-07-06 22:10:47 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
2020-09-18 11:27:42 -04:00
|
|
|
multi, _ := t.Multi()
|
2020-09-01 19:30:05 -04:00
|
|
|
return multi, resp, nil
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mine returns your multireddits.
|
2020-08-31 12:51:45 -04:00
|
|
|
func (s *MultiService) Mine(ctx context.Context) ([]*Multi, *Response, error) {
|
2020-07-06 22:10:47 -04:00
|
|
|
path := "api/multi/mine"
|
|
|
|
req, err := s.client.NewRequest(http.MethodGet, path, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
root := new(things)
|
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
2020-07-06 22:10:47 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
return root.Multis, resp, nil
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Of returns the user's public multireddits.
|
2020-07-11 01:37:19 -04:00
|
|
|
// Or, if the user is you, all of your multireddits.
|
2020-08-31 12:51:45 -04:00
|
|
|
func (s *MultiService) Of(ctx context.Context, username string) ([]*Multi, *Response, error) {
|
2020-07-06 22:10:47 -04:00
|
|
|
path := fmt.Sprintf("api/multi/user/%s", username)
|
|
|
|
req, err := s.client.NewRequest(http.MethodGet, path, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
root := new(things)
|
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
2020-07-06 22:10:47 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
return root.Multis, resp, nil
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Copy a multireddit.
|
2020-07-06 22:10:47 -04:00
|
|
|
func (s *MultiService) Copy(ctx context.Context, copyRequest *MultiCopyRequest) (*Multi, *Response, error) {
|
|
|
|
if copyRequest == nil {
|
2020-09-29 13:40:34 -04:00
|
|
|
return nil, nil, errors.New("*MultiCopyRequest: cannot be nil")
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-07-11 01:37:19 -04:00
|
|
|
path := "api/multi/copy"
|
2020-08-07 01:11:58 -04:00
|
|
|
form, err := query.Values(copyRequest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2020-07-06 22:10:47 -04:00
|
|
|
|
2020-09-09 23:02:06 -04:00
|
|
|
req, err := s.client.NewRequest(http.MethodPost, path, form)
|
2020-07-06 22:10:47 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
root := new(thing)
|
2020-07-06 22:10:47 -04:00
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
multi, _ := root.Multi()
|
|
|
|
return multi, resp, nil
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Create a multireddit.
|
2020-07-11 01:37:19 -04:00
|
|
|
func (s *MultiService) Create(ctx context.Context, createRequest *MultiCreateOrUpdateRequest) (*Multi, *Response, error) {
|
2020-07-06 22:10:47 -04:00
|
|
|
if createRequest == nil {
|
2020-09-29 13:40:34 -04:00
|
|
|
return nil, nil, errors.New("*MultiCreateOrUpdateRequest: cannot be nil")
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-09-18 11:27:42 -04:00
|
|
|
byteValue, err := json.Marshal(createRequest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("model", string(byteValue))
|
|
|
|
|
2020-07-11 01:37:19 -04:00
|
|
|
path := "api/multi"
|
2020-09-18 11:27:42 -04:00
|
|
|
req, err := s.client.NewRequest(http.MethodPost, path, form)
|
2020-07-11 01:37:19 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
root := new(thing)
|
2020-07-11 01:37:19 -04:00
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
multi, _ := root.Multi()
|
|
|
|
return multi, resp, nil
|
2020-07-11 01:37:19 -04:00
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Update a multireddit.
|
2020-07-11 01:37:19 -04:00
|
|
|
// If the multireddit does not exist, it will be created.
|
|
|
|
func (s *MultiService) Update(ctx context.Context, multiPath string, updateRequest *MultiCreateOrUpdateRequest) (*Multi, *Response, error) {
|
|
|
|
if updateRequest == nil {
|
2020-09-29 13:40:34 -04:00
|
|
|
return nil, nil, errors.New("*MultiCreateOrUpdateRequest: cannot be nil")
|
2020-07-11 01:37:19 -04:00
|
|
|
}
|
|
|
|
|
2020-09-18 11:27:42 -04:00
|
|
|
byteValue, err := json.Marshal(updateRequest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("model", string(byteValue))
|
|
|
|
|
2020-07-11 01:37:19 -04:00
|
|
|
path := fmt.Sprintf("api/multi/%s", multiPath)
|
2020-09-18 11:27:42 -04:00
|
|
|
req, err := s.client.NewRequest(http.MethodPut, path, form)
|
2020-07-06 22:10:47 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:51:45 -04:00
|
|
|
root := new(thing)
|
2020-07-06 22:10:47 -04:00
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 19:30:05 -04:00
|
|
|
multi, _ := root.Multi()
|
|
|
|
return multi, resp, nil
|
2020-07-06 22:10:47 -04:00
|
|
|
}
|
|
|
|
|
2020-08-27 18:49:30 -04:00
|
|
|
// Delete a multireddit.
|
2020-07-06 22:10:47 -04:00
|
|
|
func (s *MultiService) Delete(ctx context.Context, multiPath string) (*Response, error) {
|
|
|
|
path := fmt.Sprintf("api/multi/%s", multiPath)
|
|
|
|
req, err := s.client.NewRequest(http.MethodDelete, path, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
2020-07-11 01:37:19 -04:00
|
|
|
|
2020-09-09 23:02:06 -04:00
|
|
|
// Description gets a multireddit's description.
|
|
|
|
func (s *MultiService) Description(ctx context.Context, multiPath string) (string, *Response, error) {
|
2020-07-11 01:37:19 -04:00
|
|
|
path := fmt.Sprintf("api/multi/%s/description", multiPath)
|
2020-09-18 11:27:42 -04:00
|
|
|
t, resp, err := s.client.getThing(ctx, path, nil)
|
2020-07-11 01:37:19 -04:00
|
|
|
if err != nil {
|
|
|
|
return "", resp, err
|
|
|
|
}
|
2020-09-18 11:27:42 -04:00
|
|
|
multiDescription, _ := t.MultiDescription()
|
2020-09-09 23:02:06 -04:00
|
|
|
return multiDescription, resp, nil
|
2020-07-11 01:37:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateDescription updates a multireddit's description.
|
|
|
|
func (s *MultiService) UpdateDescription(ctx context.Context, multiPath string, description string) (string, *Response, error) {
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("model", fmt.Sprintf(`{"body_md":"%s"}`, description))
|
|
|
|
|
2020-09-18 11:27:42 -04:00
|
|
|
path := fmt.Sprintf("api/multi/%s/description", multiPath)
|
2020-09-09 23:02:06 -04:00
|
|
|
req, err := s.client.NewRequest(http.MethodPut, path, form)
|
2020-07-11 01:37:19 -04:00
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-09 23:02:06 -04:00
|
|
|
root := new(thing)
|
2020-07-11 01:37:19 -04:00
|
|
|
resp, err := s.client.Do(ctx, req, root)
|
|
|
|
if err != nil {
|
|
|
|
return "", resp, err
|
|
|
|
}
|
|
|
|
|
2020-09-09 23:02:06 -04:00
|
|
|
multiDescription, _ := root.MultiDescription()
|
|
|
|
return multiDescription, resp, nil
|
2020-07-11 01:37:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddSubreddit adds a subreddit to a multireddit.
|
|
|
|
func (s *MultiService) AddSubreddit(ctx context.Context, multiPath string, subreddit string) (*Response, error) {
|
|
|
|
path := fmt.Sprintf("api/multi/%s/r/%s", multiPath, subreddit)
|
|
|
|
|
|
|
|
form := url.Values{}
|
|
|
|
form.Set("model", fmt.Sprintf(`{"name":"%s"}`, subreddit))
|
|
|
|
|
2020-09-09 23:02:06 -04:00
|
|
|
req, err := s.client.NewRequest(http.MethodPut, path, form)
|
2020-07-11 01:37:19 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteSubreddit removes a subreddit from a multireddit.
|
|
|
|
func (s *MultiService) DeleteSubreddit(ctx context.Context, multiPath string, subreddit string) (*Response, error) {
|
|
|
|
path := fmt.Sprintf("api/multi/%s/r/%s", multiPath, subreddit)
|
|
|
|
req, err := s.client.NewRequest(http.MethodDelete, path, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return s.client.Do(ctx, req, nil)
|
|
|
|
}
|