Repository: nicklaw5/helix
Pull request #158 (2022-12-07 09:10:00)

feat: add send whisper endpoint
- implement new send whisper endpoint
- add unit test
- https://dev.twitch.tv/docs/api/reference#send-whisper


Patch:
diff --git a/whispers.go b/whispers.go
new file mode 100644
index 0000000..3ce544e
--- /dev/null
+++ b/whispers.go
@@ -0,0 +1,33 @@
+package helix
+
+type SendUserWhisperParams struct {
+	FromUserID string `query:"from_user_id"`
+	ToUserID   string `query:"to_user_id"`
+	Message    string `json:"message"`
+}
+
+type SendUserWhisperResponse struct {
+	ResponseCommon
+}
+
+// SendUserWhisper
+// Requires user access token with user:manage:whispers scope.
+// The user sending the whisper must have a verified phone number.
+// The API may silently drop whispers that it suspects of violating Twitch policies 204 still returned.
+// You may whisper to a maximum of 40 unique recipients per day. Within the per day limit.
+// You may whisper a maximum of 3 whispers per second and a maximum of 100 whispers per minute.
+// Message character limit:
+//   - Max 500 chars to new recipient
+//   - Max 10,000 if recurring recipient,
+//   - > 10,000 chars are truncated.
+func (c *Client) SendUserWhisper(params *SendUserWhisperParams) (*SendUserWhisperResponse, error) {
+	resp, err := c.postAsJSON("/whispers", nil, params)
+	if err != nil {
+		return nil, err
+	}
+
+	whisperResp := &SendUserWhisperResponse{}
+	resp.HydrateResponseCommon(&whisperResp.ResponseCommon)
+
+	return whisperResp, nil
+}


Test patch:
diff --git a/whispers_test.go b/whispers_test.go
new file mode 100644
index 0000000..d6a4526
--- /dev/null
+++ b/whispers_test.go
@@ -0,0 +1,75 @@
+package helix
+
+import (
+	"net/http"
+	"testing"
+)
+
+func TestSendUserWhisper(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode            int
+		options               *Options
+		SendUserWhisperParams *SendUserWhisperParams
+		respBody              string
+	}{
+		{
+			http.StatusBadRequest,
+			&Options{ClientID: "my-client-id", UserAccessToken: "moderator-access-token"},
+			&SendUserWhisperParams{ToUserID: "100249558", FromUserID: "100249559", Message: ""},
+			`{"error":"Bad Request","status":400,"message":"The parameter \"Color\" was malformed: the value must be a valid color"}`,
+		},
+		{
+			http.StatusNoContent,
+			&Options{ClientID: "my-client-id", UserAccessToken: "moderator-access-token"},
+			&SendUserWhisperParams{ToUserID: "100249558", FromUserID: "100249559", Message: "hello twitch chat"},
+			``,
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		resp, err := c.SendUserWhisper(testCase.SendUserWhisperParams)
+		if err != nil {
+			t.Error(err)
+		}
+
+		if resp.StatusCode != testCase.statusCode {
+			t.Errorf("expected status code to be %d, got %d", testCase.statusCode, resp.StatusCode)
+		}
+
+		if resp.StatusCode == http.StatusBadRequest {
+			if resp.Error != "Bad Request" {
+				t.Errorf("expected error to be %s, got %s", "Bad Request", resp.Error)
+			}
+
+			if resp.ErrorStatus != http.StatusBadRequest {
+				t.Errorf("expected error status to be %d, got %d", http.StatusBadRequest, resp.ErrorStatus)
+			}
+
+			continue
+		}
+	}
+
+	// Test with HTTP Failure
+	options := &Options{
+		ClientID: "my-client-id",
+		HTTPClient: &badMockHTTPClient{
+			newMockHandler(0, "", nil),
+		},
+	}
+	c := &Client{
+		opts: options,
+	}
+
+	_, err := c.SendUserWhisper(&SendUserWhisperParams{})
+	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")
+	}
+}