Repository: nicklaw5/helix
Pull request #166 (2023-01-03 18:55:59)

Implements GetModerators method
Add `GetModerators` method to use endpoint [`/moderation/moderators`](https://dev.twitch.tv/docs/api/reference#get-moderators)


Patch:
diff --git a/SUPPORTED_ENDPOINTS.md b/SUPPORTED_ENDPOINTS.md
index 18f2f41..8e0c680 100644
--- a/SUPPORTED_ENDPOINTS.md
+++ b/SUPPORTED_ENDPOINTS.md
@@ -76,7 +76,7 @@
 - [x] Add Blocked Term
 - [x] Remove Blocked Term
 - [x] Delete Chat Messages
-- [ ] Get Moderators
+- [x] Get Moderators
 - [ ] Get Moderator Events
 - [ ] Remove Channel Moderator
 - [ ] Get VIPs
diff --git a/docs/moderation_docs.md b/docs/moderation_docs.md
index faee2af..86fce21 100644
--- a/docs/moderation_docs.md
+++ b/docs/moderation_docs.md
@@ -261,3 +261,30 @@ if err != nil {
 
 fmt.Printf("%+v\n", resp)
 ```
+
+## Get Moderators
+
+To use this function you need a user access token with the `moderation:read` scope.
+`BroadcasterID` is required and need to be the same as the user id of the user access token.
+
+This is an example of how to get moderators of a channel.
+
+```go
+client, err := helix.NewClient(&helix.Options{
+    ClientID: "your-client-id",
+    UserAccessToken: "your-user-access-token",
+})
+if err != nil {
+    // handle error
+}
+
+resp, err := client.GetModerators(&helix.GetModeratorsParams{
+    BroadcasterID: "145328278",
+    First: 10
+})
+if err != nil {
+    // handle error
+}
+
+fmt.Printf("%+v\n", resp)
+```
diff --git a/moderation.go b/moderation.go
index 5226940..b841abb 100644
--- a/moderation.go
+++ b/moderation.go
@@ -291,3 +291,49 @@ func (c *Client) DeleteAllChatMessages(params *DeleteAllChatMessagesParams) (*De
 
 	return deletedMessagesResp, nil
 }
+
+type GetModeratorsParams struct {
+	// Required
+	BroadcasterID string `query:"broadcaster_id"`
+
+	// Optional
+	UserIDs []string `query:"user_id"` // Limit 100
+	After   string   `query:"after"`
+	First   int      `query:"first"`
+}
+
+type Moderator struct {
+	UserID    string `json:"user_id"`
+	UserLogin string `json:"user_login"`
+	UserName  string `json:"user_name"`
+}
+
+type ManyModerators struct {
+	Moderators []Moderator `json:"data"`
+	Pagination Pagination  `json:"pagination"`
+}
+
+type ModeratorsResponse struct {
+	ResponseCommon
+	Data ManyModerators
+}
+
+// GetModerators Gets all users allowed to moderate the broadcaster’s chat room.
+// Required scope: moderation:read
+func (c *Client) GetModerators(params *GetModeratorsParams) (*ModeratorsResponse, error) {
+	if params.BroadcasterID == "" {
+		return nil, errors.New("broadcaster id must be provided")
+	}
+
+	resp, err := c.get("/moderation/moderators", &ManyModerators{}, params)
+	if err != nil {
+		return nil, err
+	}
+  
+	moderators := &ModeratorsResponse{}
+	resp.HydrateResponseCommon(&moderators.ResponseCommon)
+	moderators.Data.Moderators = resp.Data.(*ManyModerators).Moderators
+	moderators.Data.Pagination = resp.Data.(*ManyModerators).Pagination
+
+	return moderators, nil
+}


Test patch:
diff --git a/moderation_test.go b/moderation_test.go
index d0f0500..0d7def5 100644
--- a/moderation_test.go
+++ b/moderation_test.go
@@ -622,6 +622,7 @@ func TestDeleteChatMessage(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RemoveBlockedTerm(&RemoveBlockedTermParams{BroadcasterID: "1234", ModeratorID: "1234", ID: "test"})
@@ -696,6 +697,7 @@ func TestDeleteAllChatMessages(t *testing.T) {
 	}
 	c := &Client{
 		opts: options,
+		ctx:  context.Background(),
 	}
 
 	_, err := c.RemoveBlockedTerm(&RemoveBlockedTermParams{BroadcasterID: "1234", ModeratorID: "1234", ID: "test"})
@@ -707,3 +709,103 @@ func TestDeleteAllChatMessages(t *testing.T) {
 		t.Error("expected error does match return error")
 	}
 }
+
+func TestGetModerators(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode int
+		options    *Options
+		params     *GetModeratorsParams
+		respBody   string
+		parsed     []Moderator
+		errorMsg   string
+	}{
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id", UserAccessToken: "moderator-access-token"},
+			&GetModeratorsParams{BroadcasterID: "424596340", First: 2},
+			`{
+				"data": [
+					{
+						"user_id": "424596340",
+						"user_login": "quotrok",
+						"user_name": "quotrok"
+					}
+				],
+				"pagination": {
+				  "cursor": "eyJiIjpudWxsLCJhIjp7IkN1cnNvciI6I..."
+				}
+			}`,
+			[]Moderator{
+				{
+					UserID:    "424596340",
+					UserLogin: "quotrok",
+					UserName:  "quotrok",
+				},
+			},
+			"",
+		},
+		{
+			http.StatusBadRequest,
+			&Options{ClientID: "my-client-id", UserAccessToken: "moderator-access-token"},
+			&GetModeratorsParams{BroadcasterID: ""},
+			``,
+			[]Moderator{},
+			"broadcaster id must be provided",
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		resp, err := c.GetModerators(testCase.params)
+
+		if err != nil {
+			if err.Error() != testCase.errorMsg {
+				t.Errorf("expected error message to be %s, got %s", testCase.errorMsg, err.Error())
+			}
+			continue
+		}
+
+		if resp.StatusCode != testCase.statusCode {
+			t.Errorf("expected status code to be %d, got %d", testCase.statusCode, resp.StatusCode)
+		}
+
+		for i, moderator := range resp.Data.Moderators {
+			if moderator.UserID != testCase.parsed[i].UserID {
+				t.Errorf("Expected struct field UserID = %s, was %s", testCase.parsed[i].UserID, moderator.UserID)
+			}
+
+			if moderator.UserLogin != testCase.parsed[i].UserLogin {
+				t.Errorf("Expected struct field BroadcasterName = %s, was %s", testCase.parsed[i].UserLogin, moderator.UserLogin)
+			}
+
+			if moderator.UserName != testCase.parsed[i].UserName {
+				t.Errorf("Expected struct field BroadcasterLanguage = %s, was %s", testCase.parsed[i].UserName, moderator.UserName)
+			}
+		}
+
+	}
+
+	// Test with HTTP Failure
+	options := &Options{
+		ClientID: "my-client-id",
+		HTTPClient: &badMockHTTPClient{
+			newMockHandler(0, "", nil),
+		},
+	}
+	c := &Client{
+		opts: options,
+		ctx:  context.Background(),
+	}
+
+	_, err := c.GetModerators(&GetModeratorsParams{BroadcasterID: "424596340", First: 2})
+	if err == nil {
+		t.Error("expected error but got nil")
+	}
+
+	if err.Error() != "Failed to execute API request: Oops, that's bad :(" {
+		t.Error("expected error does match return error")
+	}
+}