From 9152959ac019cf168f28ad6fa46ea93cb9b0da80 Mon Sep 17 00:00:00 2001 From: Vartan Benohanian Date: Mon, 24 Aug 2020 23:52:18 -0400 Subject: [PATCH] Un/invite users to become mods, un/ban users Signed-off-by: Vartan Benohanian --- reddit/moderation.go | 172 ++++++++++++++++++++++++++++++++++++++ reddit/moderation_test.go | 116 +++++++++++++++++++++++++ 2 files changed, 288 insertions(+) diff --git a/reddit/moderation.go b/reddit/moderation.go index cf5fe7e..76fd286 100644 --- a/reddit/moderation.go +++ b/reddit/moderation.go @@ -5,6 +5,8 @@ import ( "fmt" "net/http" "net/url" + + "github.com/google/go-querystring/query" ) // ModerationService handles communication with the moderation @@ -210,3 +212,173 @@ func (s *ModerationService) UnignoreReports(ctx context.Context, id string) (*Re return s.client.Do(ctx, req, nil) } + +// ModPermissions are the different permissions moderators have or don't have on a subreddit. +// Read about them here: https://mods.reddithelp.com/hc/en-us/articles/360009381491-User-Management-moderators-and-permissions +type ModPermissions struct { + All bool + Access bool + ChatConfig bool + ChatOperator bool + Config bool + Flair bool + Mail bool + Posts bool + Wiki bool +} + +func (p *ModPermissions) String() (s string) { + if p == nil { + return "+all" + } + + if p.All { + s += "+" + } else { + s += "-" + } + s += "all," + + if p.Access { + s += "+" + } else { + s += "-" + } + s += "access," + + if p.ChatConfig { + s += "+" + } else { + s += "-" + } + s += "chat_config," + + if p.ChatOperator { + s += "+" + } else { + s += "-" + } + s += "chat_operator," + + if p.Config { + s += "+" + } else { + s += "-" + } + s += "config," + + if p.Flair { + s += "+" + } else { + s += "-" + } + s += "flair," + + if p.Mail { + s += "+" + } else { + s += "-" + } + s += "mail," + + if p.Posts { + s += "+" + } else { + s += "-" + } + s += "posts," + + if p.Wiki { + s += "+" + } else { + s += "-" + } + s += "wiki" + + return +} + +// Invite a user to become a moderator of the subreddit. +// If permissions is nil, all permissions will be granted. +func (s *ModerationService) Invite(ctx context.Context, subreddit string, username string, permissions *ModPermissions) (*Response, error) { + path := fmt.Sprintf("r/%s/api/friend", subreddit) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", username) + form.Set("type", "moderator_invite") + form.Set("permissions", permissions.String()) + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Uninvite a user from becoming a moderator of the subreddit. +func (s *ModerationService) Uninvite(ctx context.Context, subreddit string, username string) (*Response, error) { + path := fmt.Sprintf("r/%s/api/unfriend", subreddit) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", username) + form.Set("type", "moderator_invite") + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// BanConfig configures the ban of the user being banned. +type BanConfig struct { + Reason string `url:"reason,omitempty"` + // Not visible to the user being banned. + ModNote string `url:"note,omitempty"` + // How long the ban will last. 0-999. Leave nil for permanent. + Days *int `url:"duration,omitempty"` + // Note to include in the ban message to the user. + Message string `url:"ban_message,omitempty"` +} + +// Ban a user from the subreddit. +func (s *ModerationService) Ban(ctx context.Context, subreddit string, username string, config *BanConfig) (*Response, error) { + path := fmt.Sprintf("r/%s/api/friend", subreddit) + + form, err := query.Values(config) + if err != nil { + return nil, err + } + + form.Set("api_type", "json") + form.Set("name", username) + form.Set("type", "banned") + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Unban a user from the subreddit. +func (s *ModerationService) Unban(ctx context.Context, subreddit string, username string) (*Response, error) { + path := fmt.Sprintf("r/%s/api/unfriend", subreddit) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", username) + form.Set("type", "banned") + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/reddit/moderation_test.go b/reddit/moderation_test.go index d9c272d..38b1dd5 100644 --- a/reddit/moderation_test.go +++ b/reddit/moderation_test.go @@ -260,3 +260,119 @@ func TestModerationService_UnignoreReports(t *testing.T) { _, err := client.Moderation.UnignoreReports(ctx, "t3_test") require.NoError(t, err) } + +func TestModerationService_Invite(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/r/testsubreddit/api/friend", func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodPost, r.Method) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", "testuser") + form.Set("type", "moderator_invite") + form.Set("permissions", "+all") + + err := r.ParseForm() + require.NoError(t, err) + require.Equal(t, form, r.PostForm) + }) + + _, err := client.Moderation.Invite(ctx, "testsubreddit", "testuser", nil) + require.NoError(t, err) +} + +func TestModerationService_Invite_Permissions(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/r/testsubreddit/api/friend", func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodPost, r.Method) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", "testuser") + form.Set("type", "moderator_invite") + form.Set("permissions", "-all,-access,-chat_config,+chat_operator,+config,-flair,-mail,-posts,+wiki") + + err := r.ParseForm() + require.NoError(t, err) + require.Equal(t, form, r.PostForm) + }) + + _, err := client.Moderation.Invite(ctx, "testsubreddit", "testuser", &ModPermissions{ChatOperator: true, Config: true, Wiki: true}) + require.NoError(t, err) +} + +func TestModerationService_Uninvite(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/r/testsubreddit/api/unfriend", func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodPost, r.Method) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", "testuser") + form.Set("type", "moderator_invite") + + err := r.ParseForm() + require.NoError(t, err) + require.Equal(t, form, r.PostForm) + }) + + _, err := client.Moderation.Uninvite(ctx, "testsubreddit", "testuser") + require.NoError(t, err) +} + +func TestModerationService_Ban(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/r/testsubreddit/api/friend", func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodPost, r.Method) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", "testuser") + form.Set("type", "banned") + form.Set("reason", "test reason") + form.Set("note", "test mod note") + form.Set("duration", "5") + form.Set("ban_message", "test message") + + err := r.ParseForm() + require.NoError(t, err) + require.Equal(t, form, r.PostForm) + }) + + _, err := client.Moderation.Ban(ctx, "testsubreddit", "testuser", &BanConfig{ + Reason: "test reason", + ModNote: "test mod note", + Days: Int(5), + Message: "test message", + }) + require.NoError(t, err) +} + +func TestModerationService_Unban(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + mux.HandleFunc("/r/testsubreddit/api/unfriend", func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodPost, r.Method) + + form := url.Values{} + form.Set("api_type", "json") + form.Set("name", "testuser") + form.Set("type", "banned") + + err := r.ParseForm() + require.NoError(t, err) + require.Equal(t, form, r.PostForm) + }) + + _, err := client.Moderation.Unban(ctx, "testsubreddit", "testuser") + require.NoError(t, err) +}