From 19c4e0fda2a3202215ed02a9b100d5b6aed52bfe Mon Sep 17 00:00:00 2001 From: Brandin Arsenault Date: Tue, 15 Jun 2021 19:49:14 -0300 Subject: [PATCH 1/4] Allow optional bearer --- src/NewTwitchApi/RequestGenerator.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/NewTwitchApi/RequestGenerator.php b/src/NewTwitchApi/RequestGenerator.php index d73aeaa..96c8ab5 100644 --- a/src/NewTwitchApi/RequestGenerator.php +++ b/src/NewTwitchApi/RequestGenerator.php @@ -7,8 +7,16 @@ class RequestGenerator { - public function generate(string $httpMethod, string $uriEndpoint, string $bearer, array $queryParamsMap = [], array $bodyParams = []): RequestInterface + public function generate(string $httpMethod, string $uriEndpoint, string $bearer = null, array $queryParamsMap = [], array $bodyParams = []): RequestInterface { + $headers = [ + 'Accept' => 'application/json', + ]; + + if ($bearer) { + $headers['Authorization'] = sprintf('Bearer %s', $bearer); + } + if (count($bodyParams) > 0) { $request = new Request( $httpMethod, @@ -17,7 +25,7 @@ public function generate(string $httpMethod, string $uriEndpoint, string $bearer $uriEndpoint, $this->generateQueryParams($queryParamsMap) ), - ['Authorization' => sprintf('Bearer %s', $bearer), 'Accept' => 'application/json'], + $headers, $this->generateBodyParams($bodyParams) ); } else { @@ -28,7 +36,7 @@ public function generate(string $httpMethod, string $uriEndpoint, string $bearer $uriEndpoint, $this->generateQueryParams($queryParamsMap) ), - ['Authorization' => sprintf('Bearer %s', $bearer)] + $headers ); } From 3cf8270c949efbc2e63479b72b204a6368374751 Mon Sep 17 00:00:00 2001 From: Brandin Arsenault Date: Tue, 15 Jun 2021 19:49:29 -0300 Subject: [PATCH 2/4] Add an explicit Optional Auth GET function getApiWithOptionalAuth --- src/NewTwitchApi/Resources/AbstractResource.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/NewTwitchApi/Resources/AbstractResource.php b/src/NewTwitchApi/Resources/AbstractResource.php index 6db7eb0..5c7079b 100644 --- a/src/NewTwitchApi/Resources/AbstractResource.php +++ b/src/NewTwitchApi/Resources/AbstractResource.php @@ -28,6 +28,14 @@ protected function getApi(string $uriEndpoint, string $bearer, array $queryParam return $this->sendToApi('GET', $uriEndpoint, $bearer, $queryParamsMap, $bodyParams); } + /** + * @throws GuzzleException + */ + protected function getApiWithOptionalAuth(string $uriEndpoint, string $bearer = null, array $queryParamsMap = [], array $bodyParams = []): ResponseInterface + { + return $this->sendToApi('GET', $uriEndpoint, $bearer, $queryParamsMap, $bodyParams); + } + /** * @throws GuzzleException */ @@ -60,7 +68,7 @@ protected function putApi(string $uriEndpoint, string $bearer, array $queryParam return $this->sendToApi('PUT', $uriEndpoint, $bearer, $queryParamsMap, $bodyParams); } - private function sendToApi(string $httpMethod, string $uriEndpoint, string $bearer, array $queryParamsMap = [], array $bodyParams = []): ResponseInterface + private function sendToApi(string $httpMethod, string $uriEndpoint, string $bearer = null, array $queryParamsMap = [], array $bodyParams = []): ResponseInterface { return $this->guzzleClient->send($this->requestGenerator->generate($httpMethod, $uriEndpoint, $bearer, $queryParamsMap, $bodyParams)); } From 2cb075277f7e0ecbeaa35d335adcec3d4d1e551a Mon Sep 17 00:00:00 2001 From: Brandin Arsenault Date: Tue, 15 Jun 2021 22:04:32 -0300 Subject: [PATCH 3/4] Add SchedulesApi --- spec/NewTwitchApi/NewTwitchApiSpec.php | 6 + .../Resources/ScheduleApiSpec.php | 84 ++++++++++ src/NewTwitchApi/NewTwitchApi.php | 8 + src/NewTwitchApi/Resources/ScheduleApi.php | 145 ++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 spec/NewTwitchApi/Resources/ScheduleApiSpec.php create mode 100644 src/NewTwitchApi/Resources/ScheduleApi.php diff --git a/spec/NewTwitchApi/NewTwitchApiSpec.php b/spec/NewTwitchApi/NewTwitchApiSpec.php index 55b95b8..d97ce42 100644 --- a/spec/NewTwitchApi/NewTwitchApiSpec.php +++ b/spec/NewTwitchApi/NewTwitchApiSpec.php @@ -16,6 +16,7 @@ use NewTwitchApi\Resources\ModerationApi; use NewTwitchApi\Resources\PollsApi; use NewTwitchApi\Resources\PredictionsApi; +use NewTwitchApi\Resources\ScheduleApi; use NewTwitchApi\Resources\StreamsApi; use NewTwitchApi\Resources\SubscriptionsApi; use NewTwitchApi\Resources\TagsApi; @@ -88,6 +89,11 @@ function it_should_provide_predictions_api() $this->getPredictionsApi()->shouldBeAnInstanceOf(PredictionsApi::class); } + function it_should_provide_schedule_api() + { + $this->getScheduleApi()->shouldBeAnInstanceOf(ScheduleApi::class); + } + function it_should_provide_subscriptions_api() { $this->getSubscriptionsApi()->shouldBeAnInstanceOf(SubscriptionsApi::class); diff --git a/spec/NewTwitchApi/Resources/ScheduleApiSpec.php b/spec/NewTwitchApi/Resources/ScheduleApiSpec.php new file mode 100644 index 0000000..18064e5 --- /dev/null +++ b/spec/NewTwitchApi/Resources/ScheduleApiSpec.php @@ -0,0 +1,84 @@ +beConstructedWith($guzzleClient, $requestGenerator); + $guzzleClient->send($request)->willReturn($response); + } + + function it_should_get_channel_stream_schedule(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('GET', 'schedule', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123']], [])->willReturn($request); + $this->getChannelStreamSchedule('TEST_TOKEN', '123')->shouldBe($response); + } + + function it_should_get_channel_stream_schedule_with_opts(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('GET', 'schedule', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123'], ['key' => 'start_time', 'value' => '2021-06-15T23:08:20+00:00'], ['key' => 'utc_offset', 'value' => '240'], ['key' => 'first', 'value' => 25], ['key' => 'after', 'value' => 'abc']], [])->willReturn($request); + $this->getChannelStreamSchedule('TEST_TOKEN', '123', [], '2021-06-15T23:08:20+00:00', '240', 25, 'abc')->shouldBe($response); + } + + function it_should_get_a_channel_stream_schedule(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('GET', 'schedule', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123'], ['key' => 'id', 'value' => '456']], [])->willReturn($request); + $this->getChannelStreamSchedule('TEST_TOKEN', '123', ['456'])->shouldBe($response); + } + + function it_should_get_multiple_channel_stream_schedules(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('GET', 'schedule', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123'], ['key' => 'id', 'value' => '456'], ['key' => 'id', 'value' => '789']], [])->willReturn($request); + $this->getChannelStreamSchedule('TEST_TOKEN', '123', ['456', '789'])->shouldBe($response); + } + + function it_should_get_channel_icalendar_with_no_auth(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('GET', 'schedule/icalendar', null, [['key' => 'broadcaster_id', 'value' => '123']], [])->willReturn($request); + $this->getChanneliCalendar(null, '123')->shouldBe($response); + } + + function it_should_get_channel_icalendar_with_auth(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('GET', 'schedule/icalendar', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123']], [])->willReturn($request); + $this->getChanneliCalendar('TEST_TOKEN', '123')->shouldBe($response); + } + + function it_should_update_channel_stream_schedule(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('PATCH', 'schedule/settings', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123'], ['key' => 'is_vacation_enabled', 'value' => true], ['key' => 'vacation_start_time', 'value' => '2021-06-15T23:08:20+00:00'], ['key' => 'vacation_end_time', 'value' => '2021-06-22T23:08:20+00:00'], ['key' => 'timezone', 'value' => 'America/New_York']], [])->willReturn($request); + $this->updateChannelStreamSchedule('TEST_TOKEN', '123', true, '2021-06-15T23:08:20+00:00', '2021-06-22T23:08:20+00:00', 'America/New_York')->shouldBe($response); + } + + function it_should_create_channel_stream_schedule_segment(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('POST', 'schedule/segment', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123']], [['key' => 'start_time', 'value' => '2021-06-15T23:08:20+00:00'], ['key' => 'timezone', 'value' => 'America/New_York'], ['key' => 'is_recurring', 'value' => true]])->willReturn($request); + $this->createChannelStreamScheduleSegment('TEST_TOKEN', '123', '2021-06-15T23:08:20+00:00', 'America/New_York', true)->shouldBe($response); + } + + function it_should_create_channel_stream_schedule_segment_with_opts(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('POST', 'schedule/segment', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123']], [['key' => 'start_time', 'value' => '2021-06-15T23:08:20+00:00'], ['key' => 'timezone', 'value' => 'America/New_York'], ['key' => 'is_recurring', 'value' => true], ['key' => 'duration', 'value' => '240']])->willReturn($request); + $this->createChannelStreamScheduleSegment('TEST_TOKEN', '123', '2021-06-15T23:08:20+00:00', 'America/New_York', true, ['duration' => '240'])->shouldBe($response); + } + + function it_should_update_channel_stream_schedule_segment(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('PATCH', 'schedule/segment', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123'], ['key' => 'id', 'value' => '456']], [['key' => 'start_time', 'value' => '2021-06-15T23:08:20+00:00'], ['key' => 'timezone', 'value' => 'America/New_York'], ['key' => 'is_canceled', 'value' => true], ['key' => 'duration', 'value' => '240']])->willReturn($request); + $this->updateChannelStreamScheduleSegment('TEST_TOKEN', '123', '456', ['start_time' => '2021-06-15T23:08:20+00:00', 'timezone' => 'America/New_York', 'is_canceled' => true, 'duration' => '240'])->shouldBe($response); + } + + function it_should_delete_channel_stream_schedule_segment(RequestGenerator $requestGenerator, Request $request, Response $response) + { + $requestGenerator->generate('DELETE', 'schedule/segment', 'TEST_TOKEN', [['key' => 'broadcaster_id', 'value' => '123'], ['key' => 'id', 'value' => '456']], [])->willReturn($request); + $this->deleteChannelStreamScheduleSegment('TEST_TOKEN', '123', '456')->shouldBe($response); + } +} diff --git a/src/NewTwitchApi/NewTwitchApi.php b/src/NewTwitchApi/NewTwitchApi.php index 9d04eda..bea409b 100644 --- a/src/NewTwitchApi/NewTwitchApi.php +++ b/src/NewTwitchApi/NewTwitchApi.php @@ -20,6 +20,7 @@ use NewTwitchApi\Resources\ModerationApi; use NewTwitchApi\Resources\PollsApi; use NewTwitchApi\Resources\PredictionsApi; +use NewTwitchApi\Resources\ScheduleApi; use NewTwitchApi\Resources\SearchApi; use NewTwitchApi\Resources\StreamsApi; use NewTwitchApi\Resources\SubscriptionsApi; @@ -47,6 +48,7 @@ class NewTwitchApi private $moderationApi; private $pollsApi; private $predictionsApi; + private $scheduleApi; private $searchApi; private $streamsApi; private $subscriptionsApi; @@ -75,6 +77,7 @@ public function __construct(HelixGuzzleClient $helixGuzzleClient, string $client $this->moderationApi = new ModerationApi($helixGuzzleClient, $requestGenerator); $this->pollsApi = new PollsApi($helixGuzzleClient, $requestGenerator); $this->predictionsApi = new PredictionsApi($helixGuzzleClient, $requestGenerator); + $this->scheduleApi = new ScheduleApi($helixGuzzleClient, $requestGenerator); $this->searchApi = new SearchApi($helixGuzzleClient, $requestGenerator); $this->streamsApi = new StreamsApi($helixGuzzleClient, $requestGenerator); $this->subscriptionsApi = new SubscriptionsApi($helixGuzzleClient, $requestGenerator); @@ -161,6 +164,11 @@ public function getPredictionsApi(): PredictionsApi return $this->predictionsApi; } + public function getScheduleApi(): ScheduleApi + { + return $this->scheduleApi; + } + public function getSearchApi(): SearchApi { return $this->searchApi; diff --git a/src/NewTwitchApi/Resources/ScheduleApi.php b/src/NewTwitchApi/Resources/ScheduleApi.php new file mode 100644 index 0000000..8c10d4c --- /dev/null +++ b/src/NewTwitchApi/Resources/ScheduleApi.php @@ -0,0 +1,145 @@ + 'broadcaster_id', 'value' => $broadcasterId]; + + foreach ($ids as $id) { + $queryParamsMap[] = ['key' => 'id', 'value' => $id]; + } + + if ($startTime) { + $queryParamsMap[] = ['key' => 'start_time', 'value' => $startTime]; + } + + if ($utcOffset) { + $queryParamsMap[] = ['key' => 'utc_offset', 'value' => $utcOffset]; + } + + if ($first) { + $queryParamsMap[] = ['key' => 'first', 'value' => $first]; + } + + if ($after) { + $queryParamsMap[] = ['key' => 'after', 'value' => $after]; + } + + return $this->getApi('schedule', $bearer, $queryParamsMap); + } + + /** + * @throws GuzzleException + * @link https://dev.twitch.tv/docs/api/reference/#get-channel-icalendar + */ + public function getChanneliCalendar(string $bearer = null, string $broadcasterId): ResponseInterface + { + // This endpoint at the time of addition does not require any authorization, so the bearer is null. + // However, to prevent a breaking update in the future, it will remain the first function parameter. + // You may simple pass NULL to this to bypass authentication. + + $queryParamsMap = []; + + $queryParamsMap[] = ['key' => 'broadcaster_id', 'value' => $broadcasterId]; + + return $this->getApiWithOptionalAuth('schedule/icalendar', $bearer, $queryParamsMap); + } + + /** + * @throws GuzzleException + * @link https://dev.twitch.tv/docs/api/reference/#update-channel-stream-schedule + */ + public function updateChannelStreamSchedule(string $bearer, string $broadcasterId, bool $isVacationEnabled = null, $vacationStartTime = null, $vacationEndTime = null, $timezone = null): ResponseInterface + { + $queryParamsMap = []; + + $queryParamsMap[] = ['key' => 'broadcaster_id', 'value' => $broadcasterId]; + + if ($isVacationEnabled) { + $queryParamsMap[] = ['key' => 'is_vacation_enabled', 'value' => $isVacationEnabled]; + } + + if ($vacationStartTime) { + $queryParamsMap[] = ['key' => 'vacation_start_time', 'value' => $vacationStartTime]; + } + + if ($vacationEndTime) { + $queryParamsMap[] = ['key' => 'vacation_end_time', 'value' => $vacationEndTime]; + } + + if ($timezone) { + $queryParamsMap[] = ['key' => 'timezone', 'value' => $timezone]; + } + + return $this->patchApi('schedule/settings', $bearer, $queryParamsMap); + } + + /** + * @throws GuzzleException + * @link https://dev.twitch.tv/docs/api/reference/#create-channel-stream-schedule-segment + */ + public function createChannelStreamScheduleSegment(string $bearer, string $broadcasterId, string $startTime, string $timezone, bool $isRecurring, array $additionalBodyParams = []): ResponseInterface + { + // $additionalBodyParams should be a standard key => value format, eg. ['duration' => '240']; + $queryParamsMap = $bodyParamsMap = []; + + $queryParamsMap[] = ['key' => 'broadcaster_id', 'value' => $broadcasterId]; + + $bodyParamsMap[] = ['key' => 'start_time', 'value' => $startTime]; + $bodyParamsMap[] = ['key' => 'timezone', 'value' => $timezone]; + $bodyParamsMap[] = ['key' => 'is_recurring', 'value' => $isRecurring]; + + foreach ($additionalBodyParams as $key => $value) { + $bodyParamsMap[] = ['key' => $key, 'value' => $value]; + } + + return $this->postApi('schedule/segment', $bearer, $queryParamsMap, $bodyParamsMap); + } + + /** + * @throws GuzzleException + * @link https://dev.twitch.tv/docs/api/reference/#update-channel-stream-schedule-segment + */ + public function updateChannelStreamScheduleSegment(string $bearer, string $broadcasterId, string $segmentId, array $updateValues = []): ResponseInterface + { + // $updateValues should be a standard key => value format based on the values available on the documentation, eg. ['duration' => '240']; + $queryParamsMap = $bodyParamsMap = []; + + $queryParamsMap[] = ['key' => 'broadcaster_id', 'value' => $broadcasterId]; + $queryParamsMap[] = ['key' => 'id', 'value' => $segmentId]; + + foreach ($updateValues as $key => $value) { + $bodyParamsMap[] = ['key' => $key, 'value' => $value]; + } + + return $this->patchApi('schedule/segment', $bearer, $queryParamsMap, $bodyParamsMap); + } + + /** + * @throws GuzzleException + * @link https://dev.twitch.tv/docs/api/reference/#delete-channel-stream-schedule-segment + */ + public function deleteChannelStreamScheduleSegment(string $bearer, string $broadcasterId, string $segmentId): ResponseInterface + { + $queryParamsMap = []; + + $queryParamsMap[] = ['key' => 'broadcaster_id', 'value' => $broadcasterId]; + $queryParamsMap[] = ['key' => 'id', 'value' => $segmentId]; + + return $this->deleteApi('schedule/segment', $bearer, $queryParamsMap); + } +} From b0a047a7f360e05eb18518aa8df4ee49b50837ad Mon Sep 17 00:00:00 2001 From: Brandin Arsenault Date: Tue, 15 Jun 2021 22:05:16 -0300 Subject: [PATCH 4/4] Promote channel.subscription.message to v1 --- spec/NewTwitchApi/Resources/EventSubApiSpec.php | 2 +- src/NewTwitchApi/Resources/EventSubApi.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/NewTwitchApi/Resources/EventSubApiSpec.php b/spec/NewTwitchApi/Resources/EventSubApiSpec.php index edf21d6..3649624 100644 --- a/spec/NewTwitchApi/Resources/EventSubApiSpec.php +++ b/spec/NewTwitchApi/Resources/EventSubApiSpec.php @@ -93,7 +93,7 @@ function it_should_subscribe_to_channel_subscription_gift(RequestGenerator $requ function it_should_subscribe_to_channel_subscription_message(RequestGenerator $requestGenerator, Request $request, Response $response) { - $this->createEventSubSubscription('channel.subscription.message', 'beta', ['broadcaster_user_id' => '12345'], $requestGenerator)->willReturn($request); + $this->createEventSubSubscription('channel.subscription.message', '1', ['broadcaster_user_id' => '12345'], $requestGenerator)->willReturn($request); $this->subscribeToChannelSubscriptionMessage($this->bearer, $this->secret, $this->callback, '12345')->shouldBe($response); } diff --git a/src/NewTwitchApi/Resources/EventSubApi.php b/src/NewTwitchApi/Resources/EventSubApi.php index e911fd2..65a21ac 100644 --- a/src/NewTwitchApi/Resources/EventSubApi.php +++ b/src/NewTwitchApi/Resources/EventSubApi.php @@ -148,7 +148,7 @@ public function subscribeToChannelSubscriptionMessage(string $bearer, string $se $secret, $callback, 'channel.subscription.message', - 'beta', + '1', ['broadcaster_user_id' => $twitchId], ); }