Skip to content

Commit

Permalink
Merge pull request #183 from Mahcks/raids
Browse files Browse the repository at this point in the history
Add raid support
  • Loading branch information
nicklaw5 authored Feb 20, 2023
2 parents 0ea4c37 + 9bd578f commit 72caaed
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 2 deletions.
4 changes: 2 additions & 2 deletions SUPPORTED_ENDPOINTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@
- [x] Get Predictions
- [x] Create Prediction
- [x] End Prediction
- [ ] Start a raid
- [ ] Cancel a raid
- [x] Start a raid
- [x] Cancel a raid
- [ ] Get Channel Stream Schedule
- [ ] Get Channel iCalendar
- [ ] Update Channel Stream Schedule
Expand Down
46 changes: 46 additions & 0 deletions docs/raid_docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Raid Documentation

## Start Raid

This is an example of how to start a raid.

```go
client, err := helix.NewClient(&helix.Options{
ClientID: "your-client-id",
})
if err != nil {
// handle error
}

resp, err := client.StartRaid(&helix.StartRaidParams{
FromBroadcasterID: "22484632",
ToBroadcasterID: "71092938"
})
if err != nil {
// handle error
}

fmt.Printf("%+v\n", resp)
```

## Cancel Raid

This is an example of how to cancel a raid.

```go
client, err := helix.NewClient(&helix.Options{
ClientID: "your-client-id",
})
if err != nil {
// handle error
}

resp, err := resp, err := client.CancelRaid(&helix.CancelRaidParams{
BroadcasterID: "22484632",
})
if err != nil {
// handle error
}

fmt.Printf("%+v\n", resp)
```
66 changes: 66 additions & 0 deletions raids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package helix

// RaidResponse is the response from StartRaid
type RaidResponse struct {
ResponseCommon
Data StartRaidResponse
}

// StartRaidParams are the parameters for StartRaid
type StartRaidParams struct {
FromBroadcasterID string `query:"from_broadcaster_id"`
ToBroadcasterID string `query:"to_broadcaster_id"`
}

// StartRaidResponse is the response data in RaidResponse
type StartRaidResponse struct {
Data []RaidDetails `json:"data"`
}

// RaidDetails describes details of the ongoing raid
type RaidDetails struct {
CreatedAt Time `json:"created_at"`
IsMature bool `json:"is_mature"`
}

// StartRaid raids another channel by sending the broadcaster’s viewers to the targeted channel.
// When called, the Twitch UX pops up a window at the top of the chat room that identifies the number of viewers in the raid.
// The raid occurs when the broadcaster clicks Raid Now or after the 90-second countdown expires.
// Required scope: channel:manage:raids
// Rate limit: 10 requests within a 10-minute window.
func (c *Client) StartRaid(params *StartRaidParams) (*RaidResponse, error) {
resp, err := c.post("/raids", &StartRaidResponse{}, params)
if err != nil {
return nil, err
}

raid := &RaidResponse{}
resp.HydrateResponseCommon(&raid.ResponseCommon)

return raid, nil
}

// CancelRaidResponse is the response from StartRaid
type CancelRaidResponse struct {
ResponseCommon
}

// CancelRaidParams are the parameters for CancelRaid
type CancelRaidParams struct {
BroadcasterID string `query:"broadcaster_id"`
}

// CancelRaid cancels a pending raid.
// Required scope: channel:manage:raids
// Rate limit: 10 requests within a 10-minute window.
func (c *Client) CancelRaid(params *CancelRaidParams) (*CancelRaidResponse, error) {
resp, err := c.delete("/raids", nil, params)
if err != nil {
return nil, err
}

canceledRaid := &CancelRaidResponse{}
resp.HydrateResponseCommon(&canceledRaid.ResponseCommon)

return canceledRaid, nil
}
165 changes: 165 additions & 0 deletions raids_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package helix

import (
"context"
"net/http"
"testing"
)

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

testCases := []struct {
statusCode int
options *Options
FromBroadcasterID string
ToBroadcasterID string
respBody string
}{
{
http.StatusOK,
&Options{ClientID: "my-client-id"},
"237509153",
"237509153",
`{"data":[{"created_at": "2022-02-18T07:20:50.52Z","is_mature": false}]}`,
},
{
http.StatusBadRequest,
&Options{ClientID: "my-client-id"},
"237509153",
"237509153",
`{"error":"Bad Request","status":400,"message":"The IDs in \"from_broadcaster_id\" and \"to_broadcaster_id\" cannot be the same ID."}`,
},
}

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

resp, err := c.StartRaid(&StartRaidParams{
FromBroadcasterID: testCase.FromBroadcasterID,
ToBroadcasterID: testCase.ToBroadcasterID,
})
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 IDs in \"from_broadcaster_id\" and \"to_broadcaster_id\" cannot be the same ID."
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,
ctx: context.Background(),
}

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

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

testCases := []struct {
statusCode int
options *Options
BroadcasterID string
respBody string
}{
{
http.StatusNoContent,
&Options{ClientID: "my-client-id"},
"237509153",
"",
},
{
http.StatusBadRequest,
&Options{ClientID: "my-client-id"},
"237509153",
`{"error":"Bad Request","status":400,"message":"The ID in the \"broadcaster_id query\" parameter is not valid."}`,
},
}

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

resp, err := c.CancelRaid(&CancelRaidParams{
BroadcasterID: testCase.BroadcasterID,
})
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 ID in the \"broadcaster_id query\" parameter is not valid."
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,
ctx: context.Background(),
}

_, err := c.CancelRaid(&CancelRaidParams{})
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 72caaed

Please sign in to comment.