Skip to content

Commit

Permalink
Merge branch 'akireee:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
zspri committed Aug 28, 2022
2 parents d92779c + a9a2a01 commit 6c1dc0d
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 4 deletions.
9 changes: 5 additions & 4 deletions authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,11 @@ type ValidateTokenResponse struct {
}

type validateTokenDetails struct {
ClientID string `json:"client_id"`
Login string `json:"login"`
Scopes []string `json:"scopes"`
UserID string `json:"user_id"`
ClientID string `json:"client_id"`
Login string `json:"login"`
Scopes []string `json:"scopes"`
UserID string `json:"user_id"`
ExpiresIn int `json:"expires_in"`
}

// ValidateToken - Validate access token
Expand Down
26 changes: 26 additions & 0 deletions chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ type GetEmoteSetsParams struct {
EmoteSetIDs []string `query:"emote_set_id"` // Minimum: 1. Maximum: 25.
}

type SendChatAnnouncementParams struct {
BroadcasterID string `query:"broadcaster_id"` // required
ModeratorID string `query:"moderator_id"` // required
Message string `json:"message"` // upto 500 chars, thereafter str is truncated
// blue || green || orange || purple are valid, default 'primary' or empty str result in channel accent color.
Color string `json:"color"`
}

type SendChatAnnouncementResponse struct {
ResponseCommon
}

type GetChannelEmotesResponse struct {
ResponseCommon
Data ManyEmotes
Expand Down Expand Up @@ -136,3 +148,17 @@ func (c *Client) GetEmoteSets(params *GetEmoteSetsParams) (*GetEmoteSetsResponse

return emotes, nil
}

// SendChatAnnouncement sends an announcement to the broadcaster’s chat room.
// Required scope: moderator:manage:announcements
func (c *Client) SendChatAnnouncement(params *SendChatAnnouncementParams) (*SendChatAnnouncementResponse, error) {
resp, err := c.postAsJSON("/chat/announcements", nil, params)
if err != nil {
return nil, err
}

chatResp := &SendChatAnnouncementResponse{}
resp.HydrateResponseCommon(&chatResp.ResponseCommon)

return chatResp, nil
}
74 changes: 74 additions & 0 deletions chat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,77 @@ func TestGetEmoteSets(t *testing.T) {
t.Error("expected error does match return error")
}
}

func TestSendChatAnnouncement(t *testing.T) {
t.Parallel()

testCases := []struct {
statusCode int
options *Options
SendChatAnnouncementParams *SendChatAnnouncementParams
respBody string
}{
{
http.StatusBadRequest,
&Options{ClientID: "my-client-id", UserAccessToken: "moderator-access-token"},
&SendChatAnnouncementParams{BroadcasterID: "100249558", ModeratorID: "100249558", Message: "hello world", Color: "blue"},
`{"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"},
&SendChatAnnouncementParams{BroadcasterID: "100249558", ModeratorID: "100249558", Message: "hello twitch chat", Color: "blue"},
``,
},
}

for _, testCase := range testCases {
c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))

resp, err := c.SendChatAnnouncement(testCase.SendChatAnnouncementParams)
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)
}

expectedErrMsg := "The parameter \"Color\" was malformed: the value must be a valid color"
if resp.ErrorMessage != expectedErrMsg {
t.Errorf("expected error message to be %s, got %s", expectedErrMsg, resp.ErrorMessage)
}

continue
}
}

// Test with HTTP Failure
options := &Options{
ClientID: "my-client-id",
HTTPClient: &badMockHTTPClient{
newMockHandler(0, "", nil),
},
}
c := &Client{
opts: options,
}

_, err := c.SendChatAnnouncement(&SendChatAnnouncementParams{})
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")
}
}
1 change: 1 addition & 0 deletions eventsub.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type EventSubSubscriptionsResponse struct {
type EventSubSubscriptionsParams struct {
Status string `query:"status"`
Type string `query:"type"`
UserID string `query:"user_id"`
After string `query:"after"`
}

Expand Down
31 changes: 31 additions & 0 deletions streams.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ type StreamsParams struct {
UserLogins []string `query:"user_login"` // limit 100
}

type ManyStreamKeys struct {
Data []struct {
StreamKey string `json:"stream_key"`
} `json:"data"`
}

type StreamKeysResponse struct {
ResponseCommon
Data ManyStreamKeys
}

type StreamKeyParams struct {
BroadcasterID string `query:"broadcaster_id"`
}

// GetStreams returns a list of live channels based on the search parameters.
// To query offline channels, use SearchChannels.
func (c *Client) GetStreams(params *StreamsParams) (*StreamsResponse, error) {
Expand Down Expand Up @@ -82,3 +97,19 @@ func (c *Client) GetFollowedStream(params *FollowedStreamsParams) (*StreamsRespo

return streams, nil
}

// GetStreamKey : Returns the secret stream key of the broadcaster
//
// Required scope: channel:read:stream_key
func (c *Client) GetStreamKey(params *StreamKeyParams) (*StreamKeysResponse, error) {
resp, err := c.get("/streams/key", &ManyStreamKeys{}, params)
if err != nil {
return nil, err
}

streams := &StreamKeysResponse{}
resp.HydrateResponseCommon(&streams.ResponseCommon)
streams.Data.Data = resp.Data.(*ManyStreamKeys).Data

return streams, nil
}
76 changes: 76 additions & 0 deletions streams_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,79 @@ func TestGetFollowedStreams(t *testing.T) {
t.Error("expected error does match return error")
}
}

func TestGetStreamKeys(t *testing.T) {
t.Parallel()

testCases := []struct {
statusCode int
options *Options
broadcasterID string
Length int
respBody string
}{
{
http.StatusOK,
&Options{ClientID: "my-client-id"},
"kivutar",
1,
`{"data":[{"stream_key":"live_695820277_TF1dAMbU4cQvGKyrk2Q88SvWNCw6Rs"}]}`,
},
{
http.StatusUnauthorized,
&Options{ClientID: "my-client-id"},
"kivutar",
0,
`{"error":"Unauthorized","status":401,"message":"Invalid OAuth token"}`,
},
}

for _, testCase := range testCases {
c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))

resp, err := c.GetStreamKey(&StreamKeyParams{
BroadcasterID: testCase.broadcasterID,
})
if err != nil {
t.Error(err)
}

// Test Bad Request Responses
if resp.StatusCode == http.StatusBadRequest {
firstErrStr := "Invalid OAuth token"
if resp.ErrorMessage != firstErrStr {
t.Errorf("expected error message to be \"%s\", got \"%s\"", firstErrStr, resp.ErrorMessage)
}
continue
}

if resp.StatusCode != testCase.statusCode {
t.Errorf("expected status code to be \"%d\", got \"%d\"", testCase.statusCode, resp.StatusCode)
}

if len(resp.Data.Data) != testCase.Length {
t.Errorf("expected \"%d\" streams, got \"%d\"", testCase.Length, len(resp.Data.Data))
}
}

// Test with HTTP Failure
options := &Options{
ClientID: "my-client-id",
HTTPClient: &badMockHTTPClient{
newMockHandler(0, "", nil),
},
}

c := &Client{
opts: options,
}

_, err := c.GetStreamKey(&StreamKeyParams{})
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")
}
}

0 comments on commit 6c1dc0d

Please sign in to comment.