clientId = (string) config('rig.twitch.client_id'); $this->token = (string) config('rig.twitch.token'); $this->broadcasterId = (string) config('rig.twitch.broadcaster_id'); $this->http = new Client([ 'base_uri' => 'https://api.twitch.tv/helix/', 'timeout' => 8.0, ]); } public function enabled(): bool { return $this->clientId !== '' && $this->token !== ''; } /** * Returns [['id' => '12345', 'name' => 'Half-Life 2'], …] for an exact-name match, * or null if Twitch returned no matches. */ public function searchCategory(string $query): ?array { try { $res = $this->http->get('search/categories', [ 'headers' => $this->headers(), 'query' => ['query' => $query, 'first' => 20], ]); } catch (GuzzleException $e) { throw new RuntimeException('Twitch search failed: ' . $e->getMessage(), 0, $e); } $body = json_decode((string) $res->getBody(), true); $hits = $body['data'] ?? []; if (empty($hits)) return null; // Prefer exact (case-insensitive) match; otherwise the first hit Twitch returned. foreach ($hits as $row) { if (strcasecmp((string) ($row['name'] ?? ''), $query) === 0) { return ['id' => (string) $row['id'], 'name' => (string) $row['name']]; } } return ['id' => (string) $hits[0]['id'], 'name' => (string) $hits[0]['name']]; } public function setChannel(string $title, ?string $gameId = null): void { if ($this->broadcasterId === '') { throw new RuntimeException('TWITCH_BROADCASTER_ID not set in .env'); } $body = ['title' => $title]; if ($gameId !== null && $gameId !== '') $body['game_id'] = $gameId; try { $this->http->patch('channels', [ 'headers' => $this->headers() + ['Content-Type' => 'application/json'], 'query' => ['broadcaster_id' => $this->broadcasterId], 'json' => $body, ]); } catch (GuzzleException $e) { throw new RuntimeException('Twitch PATCH /helix/channels failed: ' . $e->getMessage(), 0, $e); } } private function headers(): array { return [ 'Authorization' => 'Bearer ' . $this->token, 'Client-Id' => $this->clientId, ]; } }