Create and edit a subreddit, get its settings
Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
parent
a76dfa0a00
commit
e14a54f64e
5 changed files with 323 additions and 24 deletions
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// APIError is an error coming from Reddit.
|
||||
|
@ -44,14 +45,14 @@ type JSONErrorResponse struct {
|
|||
}
|
||||
|
||||
func (r *JSONErrorResponse) Error() string {
|
||||
var message string
|
||||
if len(r.JSON.Errors) > 0 {
|
||||
message = r.JSON.Errors[0].Error()
|
||||
errorMessages := make([]string, len(r.JSON.Errors))
|
||||
for i, err := range r.JSON.Errors {
|
||||
errorMessages[i] = err.Error()
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s %s: %d %s",
|
||||
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, message,
|
||||
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, strings.Join(errorMessages, ";"),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,103 @@ func (s *SubredditTrafficStats) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SubredditSettings are a subreddit's settings.
|
||||
type SubredditSettings struct {
|
||||
// The id of the subreddit.
|
||||
ID string `url:"-" json:"subreddit_id,omitempty"`
|
||||
|
||||
// One of: public, restricted, private, gold_restricted, archived, employees_only, gold_only, user.
|
||||
Type *string `url:"type,omitempty" json:"subreddit_type,omitempty"`
|
||||
|
||||
// A valid IETF language tag (underscore separated).
|
||||
Language *string `url:"lang,omitempty" json:"language,omitempty"`
|
||||
|
||||
// No longer than 100 characters.
|
||||
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
||||
// Raw markdown text. No longer than 500 characters.
|
||||
Description *string `url:"public_description,omitempty" json:"public_description,omitempty"`
|
||||
// Raw markdown text. No longer than 10240 characters.
|
||||
Sidebar *string `url:"description,omitempty" json:"description,omitempty"`
|
||||
// Raw markdown text. No longer than 1024 characters.
|
||||
SubmissionText *string `url:"submit_text,omitempty" json:"submit_text,omitempty"`
|
||||
// Raw markdown text. No longer than 5000 characters.
|
||||
WelcomeMessage *string `url:"welcome_message_text,omitempty" json:"welcome_message_text,omitempty"`
|
||||
WelcomeMessageEnabled *bool `url:"welcome_message_enabled,omitempty" json:"welcome_message_enabled,omitempty"`
|
||||
|
||||
AllowCrossposts *bool `url:"allow_post_crossposts,omitempty" json:"allow_post_crossposts,omitempty"`
|
||||
AllowChatPosts *bool `url:"allow_chat_post_creation,omitempty" json:"allow_chat_post_creation,omitempty"`
|
||||
AllowPollPosts *bool `url:"allow_polls,omitempty" json:"allow_polls,omitempty"`
|
||||
AllowFreeFormReports *bool `url:"free_form_reports,omitempty" json:"free_form_reports,omitempty"`
|
||||
AllowOriginalContent *bool `url:"original_content_tag_enabled,omitempty" json:"original_content_tag_enabled,omitempty"`
|
||||
// Allow image uploads and links to image hosting sites.
|
||||
AllowImages *bool `url:"allow_images,omitempty" json:"allow_images,omitempty"`
|
||||
AllowMultipleImagesPerPost *bool `url:"allow_galleries,omitempty" json:"allow_galleries,omitempty"`
|
||||
|
||||
ExcludeSitewideBannedUsersContent *bool `url:"exclude_banned_modqueue,omitempty" json:"exclude_banned_modqueue,omitempty"`
|
||||
|
||||
// An integer from 0 to 3.
|
||||
CrowdControlChalLevel *int `url:"crowd_control_chat_level,omitempty" json:"crowd_control_chat_level,omitempty"`
|
||||
|
||||
// Mark all posts in this subreddit as Original Content (OC) on the desktop redesign.
|
||||
AllOriginalContent *bool `url:"all_original_content,omitempty" json:"all_original_content,omitempty"`
|
||||
|
||||
// One of: none (recommended), confidence, top, new, controversial, old, random, qa, live.
|
||||
SuggestedCommentSort *string `url:"suggested_comment_sort,omitempty" json:"suggested_comment_sort,omitempty"`
|
||||
|
||||
// No longer than 60 characters.
|
||||
SubmitLinkPostLabel *string `url:"submit_link_label,omitempty" json:"submit_link_label,omitempty"`
|
||||
// No longer than 60 characters.
|
||||
SubmitTextPostLabel *string `url:"submit_text_label,omitempty" json:"submit_text_label,omitempty"`
|
||||
|
||||
// One of: any, link, self.
|
||||
PostType *string `url:"link_type,omitempty" json:"content_options,omitempty"`
|
||||
|
||||
// One of: low (disable most filtering), high (standard), all (filter everything, requiring mod approval).
|
||||
SpamFilterStrengthLinkPosts *string `url:"spam_links,omitempty" json:"spam_links,omitempty"`
|
||||
// One of: low (disable most filtering), high (standard), all (filter everything, requiring mod approval).
|
||||
SpamFilterStrengthTextPosts *string `url:"spam_selfposts,omitempty" json:"spam_selfposts,omitempty"`
|
||||
// One of: low (disable most filtering), high (standard), all (filter everything, requiring mod approval).
|
||||
SpamFilterStrengthComments *string `url:"spam_comments,omitempty" json:"spam_comments,omitempty"`
|
||||
|
||||
ShowContentThumbnails *bool `url:"show_media,omitempty" json:"show_media,omitempty"`
|
||||
ExpandMediaPreviewsOnCommentsPages *bool `url:"show_media_preview,omitempty" json:"show_media_preview,omitempty"`
|
||||
|
||||
CollapseDeletedComments *bool `url:"collapse_deleted_comments,omitempty" json:"collapse_deleted_comments,omitempty"`
|
||||
// An integer between 0 and 1440.
|
||||
MinutesToHideCommentScores *int `url:"comment_score_hide_mins,omitempty" json:"comment_score_hide_mins,omitempty"`
|
||||
|
||||
// Enable marking posts as containing spoilers.
|
||||
SpoilersEnabled *bool `url:"spoilers_enabled,omitempty" json:"spoilers_enabled,omitempty"`
|
||||
|
||||
// If there's an image header set, hovering the mouse over it will display this text.
|
||||
HeaderMouseoverText *string `url:"header-title,omitempty" json:"header_hover_text,omitempty"`
|
||||
|
||||
// 6-digit rgb hex colour, e.g. #AABBCC.
|
||||
// Thematic colour for the subreddit on mobile.
|
||||
MobileColour *string `url:"key_color,omitempty" json:"key_color,omitempty"`
|
||||
|
||||
// Can only be set to true if subreddit type is gold_only.
|
||||
HideAds *bool `url:"hide_ads,omitempty" json:"hide_ads,omitempty"`
|
||||
// Require viewers to be over 18 years old.
|
||||
NSFW *bool `url:"over_18,omitempty" json:"over_18,omitempty"`
|
||||
|
||||
// Show up in high-traffic feeds: Allow your community to be in r/all, r/popular, and trending lists where it can be
|
||||
// seen by the general Reddit population.
|
||||
AllowDiscoveryInHighTrafficFeeds *bool `url:"allow_top,omitempty" json:"default_set,omitempty"`
|
||||
// Get recommended to individual redditors. Let Reddit recommend your community to people who have similar interests.
|
||||
AllowDiscoveryByIndividualUsers *bool `url:"allow_discovery,omitempty" json:"allow_discovery,omitempty"`
|
||||
|
||||
// One of:
|
||||
// - disabled: wiki is disabled for everyone except mods.
|
||||
// - modonly: only mods, approved wiki contributors, or those on a page's edit list may edit.
|
||||
// - anyone: anyone who can submit to the subreddit may edit.
|
||||
WikiMode *string `url:"wikimode,omitempty" json:"wikimode,omitempty"`
|
||||
// Account age (in days) required to create and edit wiki pages.
|
||||
WikiMinimumAccountAge *int `url:"wiki_edit_age,omitempty" json:"wiki_edit_age,omitempty"`
|
||||
// Subreddit karma required to create and edit wiki pages.
|
||||
WikiMinimumKarma *int `url:"wiki_edit_karma,omitempty" json:"wiki_edit_karma,omitempty"`
|
||||
}
|
||||
|
||||
func (s *SubredditService) getPosts(ctx context.Context, sort string, subreddit string, opts interface{}) ([]*Post, *Response, error) {
|
||||
path := sort
|
||||
if subreddit != "" {
|
||||
|
@ -977,3 +1074,60 @@ func (s *SubredditService) UploadMobileHeader(ctx context.Context, subreddit, im
|
|||
func (s *SubredditService) UploadMobileIcon(ctx context.Context, subreddit, imagePath, imageName string) (string, *Response, error) {
|
||||
return s.uploadImage(ctx, subreddit, imagePath, "icon", imageName)
|
||||
}
|
||||
|
||||
// Create a subreddit.
|
||||
func (s *SubredditService) Create(ctx context.Context, name string, request *SubredditSettings) (*Response, error) {
|
||||
if request == nil {
|
||||
return nil, errors.New("*SubredditSettings: cannot be nil")
|
||||
}
|
||||
|
||||
form, err := query.Values(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
form.Set("name", name)
|
||||
form.Set("api_type", "json")
|
||||
|
||||
path := "api/site_admin"
|
||||
req, err := s.client.NewRequest(http.MethodPost, path, form)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(ctx, req, nil)
|
||||
}
|
||||
|
||||
// Edit a subreddit.
|
||||
// This endpoint expects all values of the request to be provided.
|
||||
// To make this easier, it might be useful to get the subreddit's current settings via GetSettings(), and use that as a starting point.
|
||||
func (s *SubredditService) Edit(ctx context.Context, subredditID string, request *SubredditSettings) (*Response, error) {
|
||||
if request == nil {
|
||||
return nil, errors.New("*SubredditSettings: cannot be nil")
|
||||
}
|
||||
|
||||
form, err := query.Values(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
form.Set("sr", subredditID)
|
||||
form.Set("api_type", "json")
|
||||
|
||||
path := "api/site_admin"
|
||||
req, err := s.client.NewRequest(http.MethodPost, path, form)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(ctx, req, nil)
|
||||
}
|
||||
|
||||
// GetSettings gets the settings of a subreddit.
|
||||
func (s *SubredditService) GetSettings(ctx context.Context, subreddit string) (*SubredditSettings, *Response, error) {
|
||||
path := fmt.Sprintf("r/%s/about/edit", subreddit)
|
||||
t, resp, err := s.client.getThing(ctx, path, nil)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
settings, _ := t.SubredditSettings()
|
||||
return settings, resp, nil
|
||||
}
|
||||
|
|
|
@ -338,6 +338,68 @@ var expectedStyleSheet = &SubredditStyleSheet{
|
|||
}`,
|
||||
}
|
||||
|
||||
var expectedSubredditSettings = &SubredditSettings{
|
||||
ID: "t5_test",
|
||||
|
||||
Type: String("private"),
|
||||
|
||||
Language: String("en"),
|
||||
|
||||
Title: String("hello!"),
|
||||
Description: String("description"),
|
||||
Sidebar: String("sidebar"),
|
||||
SubmissionText: String(""),
|
||||
WelcomeMessage: String(""),
|
||||
WelcomeMessageEnabled: Bool(false),
|
||||
|
||||
AllowCrossposts: Bool(false),
|
||||
AllowChatPosts: Bool(true),
|
||||
AllowPollPosts: Bool(false),
|
||||
AllowFreeFormReports: Bool(true),
|
||||
AllowOriginalContent: Bool(false),
|
||||
AllowImages: Bool(true),
|
||||
AllowMultipleImagesPerPost: Bool(true),
|
||||
|
||||
ExcludeSitewideBannedUsersContent: Bool(false),
|
||||
|
||||
CrowdControlChalLevel: Int(2),
|
||||
|
||||
AllOriginalContent: Bool(false),
|
||||
|
||||
SuggestedCommentSort: nil,
|
||||
|
||||
SubmitLinkPostLabel: String("submit a link!"),
|
||||
SubmitTextPostLabel: String("submit a post!"),
|
||||
|
||||
PostType: String("any"),
|
||||
|
||||
SpamFilterStrengthLinkPosts: String("low"),
|
||||
SpamFilterStrengthTextPosts: String("low"),
|
||||
SpamFilterStrengthComments: String("low"),
|
||||
|
||||
ShowContentThumbnails: Bool(false),
|
||||
ExpandMediaPreviewsOnCommentsPages: Bool(true),
|
||||
|
||||
CollapseDeletedComments: Bool(false),
|
||||
MinutesToHideCommentScores: Int(0),
|
||||
|
||||
SpoilersEnabled: Bool(true),
|
||||
|
||||
HeaderMouseoverText: String("hello!"),
|
||||
|
||||
MobileColour: String(""),
|
||||
|
||||
HideAds: Bool(false),
|
||||
NSFW: Bool(false),
|
||||
|
||||
AllowDiscoveryInHighTrafficFeeds: Bool(true),
|
||||
AllowDiscoveryByIndividualUsers: Bool(true),
|
||||
|
||||
WikiMode: String("modonly"),
|
||||
WikiMinimumAccountAge: Int(0),
|
||||
WikiMinimumKarma: Int(0),
|
||||
}
|
||||
|
||||
func TestSubredditService_HotPosts(t *testing.T) {
|
||||
client, mux, teardown := setup()
|
||||
defer teardown()
|
||||
|
@ -1523,3 +1585,20 @@ func TestSubredditService_UploadImage_Error(t *testing.T) {
|
|||
_, _, err = client.Subreddit.UploadImage(ctx, "testsubreddit", imageFile.Name(), "testname")
|
||||
require.EqualError(t, err, "could not upload image: error one; error two")
|
||||
}
|
||||
|
||||
func TestSubredditService_GetSettings(t *testing.T) {
|
||||
client, mux, teardown := setup()
|
||||
defer teardown()
|
||||
|
||||
blob, err := readFileContents("../testdata/subreddit/settings.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
mux.HandleFunc("/r/testsubreddit/about/edit", func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodGet, r.Method)
|
||||
fmt.Fprint(w, blob)
|
||||
})
|
||||
|
||||
subredditSettings, _, err := client.Subreddit.GetSettings(ctx, "testsubreddit")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedSubredditSettings, subredditSettings)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ const (
|
|||
kindSubreddit = "t5"
|
||||
kindTrophy = "t6"
|
||||
kindListing = "Listing"
|
||||
kindSubredditSettings = "subreddit_settings"
|
||||
kindKarmaList = "KarmaList"
|
||||
kindTrophyList = "TrophyList"
|
||||
kindUserList = "UserList"
|
||||
|
@ -91,6 +92,8 @@ func (t *thing) UnmarshalJSON(b []byte) error {
|
|||
v = new(Post)
|
||||
case kindSubreddit:
|
||||
v = new(Subreddit)
|
||||
case kindSubredditSettings:
|
||||
v = new(SubredditSettings)
|
||||
case kindLiveThread:
|
||||
v = new(LiveThread)
|
||||
case kindLiveThreadUpdate:
|
||||
|
@ -158,6 +161,11 @@ func (t *thing) Subreddit() (v *Subreddit, ok bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func (t *thing) SubredditSettings() (v *SubredditSettings, ok bool) {
|
||||
v, ok = t.Data.(*SubredditSettings)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *thing) LiveThread() (v *LiveThread, ok bool) {
|
||||
v, ok = t.Data.(*LiveThread)
|
||||
return
|
||||
|
|
57
testdata/subreddit/settings.json
vendored
Normal file
57
testdata/subreddit/settings.json
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"kind": "subreddit_settings",
|
||||
"data": {
|
||||
"default_set": true,
|
||||
"toxicity_threshold_chat_level": 1,
|
||||
"crowd_control_chat_level": 2,
|
||||
"disable_contributor_requests": false,
|
||||
"subreddit_id": "t5_test",
|
||||
"allow_images": true,
|
||||
"free_form_reports": true,
|
||||
"domain": null,
|
||||
"show_media": false,
|
||||
"wiki_edit_age": 0,
|
||||
"submit_text": "",
|
||||
"allow_polls": false,
|
||||
"title": "hello!",
|
||||
"collapse_deleted_comments": false,
|
||||
"wikimode": "modonly",
|
||||
"over_18": false,
|
||||
"allow_videos": true,
|
||||
"spoilers_enabled": true,
|
||||
"new_pinned_post_pns_enabled": true,
|
||||
"crowd_control_mode": false,
|
||||
"welcome_message_enabled": false,
|
||||
"welcome_message_text": "",
|
||||
"suggested_comment_sort": null,
|
||||
"restrict_posting": true,
|
||||
"original_content_tag_enabled": false,
|
||||
"description": "sidebar",
|
||||
"submit_link_label": "submit a link!",
|
||||
"allow_galleries": true,
|
||||
"allow_post_crossposts": false,
|
||||
"spam_comments": "low",
|
||||
"public_traffic": false,
|
||||
"restrict_commenting": false,
|
||||
"crowd_control_level": 0,
|
||||
"submit_text_label": "submit a post!",
|
||||
"all_original_content": false,
|
||||
"spam_selfposts": "low",
|
||||
"key_color": "",
|
||||
"language": "en",
|
||||
"wiki_edit_karma": 0,
|
||||
"hide_ads": false,
|
||||
"header_hover_text": "hello!",
|
||||
"allow_chat_post_creation": true,
|
||||
"allow_discovery": true,
|
||||
"exclude_banned_modqueue": false,
|
||||
"public_description": "description",
|
||||
"show_media_preview": true,
|
||||
"comment_score_hide_mins": 0,
|
||||
"subreddit_type": "private",
|
||||
"spam_links": "low",
|
||||
"allow_predictions": false,
|
||||
"user_flair_pns_enabled": true,
|
||||
"content_options": "any"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue