]> git.localhorst.tv Git - alttp.git/commitdiff
sync HTH crew
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 24 Sep 2025 12:02:46 +0000 (14:02 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 24 Sep 2025 12:02:46 +0000 (14:02 +0200)
app/Console/Commands/SyncHTH.php
app/Console/Commands/SyncNmgLeague.php
database/migrations/2025_09_24_112139_default_channel_short_name_to_empty_string.php [new file with mode: 0644]

index deab332b8c31a1852b418c66fd8a9dd77ab93467..a75a697c0f74d8356410c31c8ac062efffeafaeb 100644 (file)
@@ -2,8 +2,10 @@
 
 namespace App\Console\Commands;
 
+use App\Models\Channel;
 use App\Models\DiscordBotCommand;
 use App\Models\Episode;
+use App\Models\EpisodeCrew;
 use App\Models\EpisodePlayer;
 use App\Models\Event;
 use App\Models\User;
@@ -11,6 +13,7 @@ use Carbon\Carbon;
 use Illuminate\Console\Command;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Str;
 
 class SyncHTH extends Command {
 
@@ -34,7 +37,7 @@ class SyncHTH extends Command {
         * @return int
         */
        public function handle() {
-               $events = Event::where('external_schedule', 'LIKE', 'hth:%')
+               $events = Event::query()->where('external_schedule', 'LIKE', 'hth:%')
                        ->where(function (Builder $query) {
                                $query->whereNull('end');
                                $query->orWhere('end', '>', now());
@@ -60,7 +63,7 @@ class SyncHTH extends Command {
                $hthEvent = $parts[1];
                $this->line('series: "'.$hthSeries.'", event: "'.$hthEvent.'"');
                $hthSchedule = Http::withHeaders([
-                       'X-API-Key', config('hth.api_key')
+                       'X-API-Key' => config('hth.api_key')
                ])->post('https://hth.zeldaspeedruns.com/api/v1/graphql', [
                        'operation_name' => 'my_schedule',
                        'variables' => [
@@ -71,29 +74,40 @@ class SyncHTH extends Command {
                                query my_schedule(\$my_series: String, \$my_event: String) {
                                        series(name: \$my_series) {
                                                event(name: \$my_event) {
-                                               races {
-                                                       id
-                                                       start
-                                                       room
-                                                       phase
-                                                       round
-                                                       game
-                                                       teams {
-                                                       name
-                                                       members {
-                                                               role
-                                                               user {
+                                                       races {
                                                                id
-                                                               displayName
-                                                               racetimeId
-                                                               discordId
+                                                               start
+                                                               room
+                                                               phase
+                                                               round
+                                                               game
+                                                               teams {
+                                                                       name
+                                                                       members {
+                                                                               role
+                                                                               user {
+                                                                                       id
+                                                                                       displayName
+                                                                                       discordId
+                                                                               }
+                                                                       }
+                                                               }
+                                                               restreamUrls {
+                                                                       url
+                                                                       language
+                                                               }
+                                                               confirmedVolunteers {
+                                                                       roleTypeName
+                                                                       user {
+                                                                               id
+                                                                               displayName
+                                                                               discordId
+                                                                       }
                                                                }
-                                                       }
                                                        }
                                                }
                                        }
                                }
-                       }
                        GRAPHQL,
                ])->throw()->json();
                foreach ($hthSchedule['data']['series']['event']['races'] as $hthEntry) {
@@ -107,7 +121,7 @@ class SyncHTH extends Command {
 
        private function syncSchedule(Event $event, $hthEntry): void {
                $ext_id = 'hth:'.$hthEntry['id'];
-               $episode = Episode::firstWhere('ext_id', '=', $ext_id);
+               $episode = Episode::query()->firstWhere('ext_id', '=', $ext_id);
                if (!$hthEntry['start']) {
                        if ($episode) {
                                $episode->callOff();
@@ -115,6 +129,10 @@ class SyncHTH extends Command {
                        }
                        return;
                }
+               if ($hthEntry['start'] < now()->sub(7, 'day')) {
+                       // skip if too old
+                       return;
+               }
                if (!$episode) {
                        $episode = new Episode();
                        $episode->ext_id = $ext_id;
@@ -145,6 +163,23 @@ class SyncHTH extends Command {
                                }
                        }
                }
+
+               $first_channel = null;
+               if (!empty($hthEntry['restreamUrls'])) {
+                       foreach ($hthEntry['restreamUrls'] as $restream) {
+                               $channel = $this->syncChannel($episode, $restream);
+                               $episode->channels()->syncWithoutDetaching([$channel->id]);
+                               if (is_null($first_channel)) {
+                                       $first_channel = $channel;
+                               }
+                       }
+               }
+               $this->purgeCrew($episode, $hthEntry['confirmedVolunteers']);
+               if (!empty($hthEntry['confirmedVolunteers'])) {
+                       foreach ($hthEntry['confirmedVolunteers'] as $hthCrew) {
+                               $this->syncCrew($episode, $hthCrew, $first_channel);
+                       }
+               }
        }
 
        private function purgePlayers(Episode $episode, $hthEntry): void {
@@ -177,24 +212,79 @@ class SyncHTH extends Command {
                $player->save();
        }
 
+       private function syncChannel(Episode $episode, $hthChannel): Channel {
+               $normalized_url = Str::rtrim(Str::lower($hthChannel['url']), '/');
+               $normalized_url = str_replace('http://', 'https://', $normalized_url);
+               $normalized_url = str_replace('www.twitch.tv', 'twitch.tv', $normalized_url);
+               $channel = Channel::query()->firstWhere('stream_link', 'LIKE', $normalized_url);
+               if (!$channel) {
+                       $channel = new Channel();
+                       $channel->stream_link = $normalized_url;
+                       $channel->title = Str::afterLast($normalized_url, '/');
+                       $channel->languages = [static::$LANGUAGE_MAP[$hthChannel['language']]];
+                       $channel->twitch_chat = '#'.Str::afterLast($normalized_url, '/');
+                       $channel->access_key = Str::uuid();
+                       $channel->save();
+               }
+               return $channel;
+       }
+
+       private function purgeCrew(Episode $episode, $hthCrews) {
+               $ext_ids = [];
+               foreach ($hthCrews as $hthCrew) {
+                       $ext_ids[] = 'hth:'.$hthCrew['roleTypeName'].':'.$hthCrew['user']['id'];
+               }
+               $episode->crew()->where('ext_id', 'LIKE', 'hth:%')->whereNotIn('ext_id', $ext_ids)->delete();
+       }
+
+       private function syncCrew(Episode $episode, $hthCrew, Channel|null $channel): void {
+               $ext_id = 'hth:'.$hthCrew['roleTypeName'].':'.$hthCrew['user']['id'];
+               $crew = $episode->crew()->firstWhere('ext_id', '=', $ext_id);
+               if (!$crew) {
+                       $crew = new EpisodeCrew();
+                       $crew->ext_id = $ext_id;
+                       $crew->episode()->associate($episode);
+               }
+               $user = $this->getUserByHTHPlayer($hthCrew);
+               if ($user) {
+                       $crew->user()->associate($user);
+               } else {
+                       $crew->user()->disassociate();
+               }
+               $role = static::$ROLE_MAP[$hthCrew['roleTypeName']];
+               if ($role == 'commentary') {
+                       if ($channel) {
+                               $crew->channel()->associate($channel);
+                       } else {
+                               $crew->channel()->disassociate();
+                       }
+               }
+               $crew->role = $role;
+               $crew->confirmed = true;
+               if (!empty($hthCrew['user']['displayName'])) {
+                       $crew->name_override = $hthCrew['user']['displayName'];
+               }
+               $crew->save();
+       }
+
        private function getUserByHTHPlayer($player): User|null {
                if (!empty($player['user']['discordId'])) {
-                       $user = User::find($player['user']['discordId']);
+                       $user = User::query()->find($player['user']['discordId']);
                        if ($user) {
                                return $user;
                        }
                        DiscordBotCommand::syncUser($player['user']['discordId']);
                }
                if (!empty($player['user']['displayName'])) {
-                       $user = User::firstWhere('discord_nickname', 'LIKE', $player['user']['displayName']);
+                       $user = User::query()->firstWhere('discord_nickname', 'LIKE', $player['user']['displayName']);
                        if ($user) {
                                return $user;
                        }
-                       $user = User::firstWhere('global_name', 'LIKE', $player['user']['displayName']);
+                       $user = User::query()->firstWhere('global_name', 'LIKE', $player['user']['displayName']);
                        if ($user) {
                                return $user;
                        }
-                       $user = User::firstWhere('username', 'LIKE', $player['user']['displayName']);
+                       $user = User::query()->firstWhere('username', 'LIKE', $player['user']['displayName']);
                        if ($user) {
                                return $user;
                        }
@@ -202,4 +292,17 @@ class SyncHTH extends Command {
                return null;
        }
 
+       private static $LANGUAGE_MAP = [
+               'ENGLISH' => 'en',
+               'FRENCH' => 'fr',
+               'GERMAN' => 'de',
+               'PORTUGUESE' => 'pt',
+       ];
+
+       private static $ROLE_MAP = [
+               'Commentary' => 'commentary',
+               'Race Monitoring' => 'setup',
+               'Tracking' => 'tracking',
+       ];
+
 }
index cbf66530e7ce93aa4a1022c913e2929395239c25..7c109c2e904815de598312c1d144b47683652c24 100644 (file)
@@ -164,14 +164,17 @@ class SyncNmgLeague extends Command {
        }
 
        private function syncChannel(Episode $episode, string $channel_url): Channel {
-               $normalized_url = str_replace('http://', 'https://', strtolower($channel_url));
+               $normalized_url = Str::rtrim(Str::lower($channel_url), '/');
+               $normalized_url = str_replace('http://', 'https://', $normalized_url);
                $normalized_url = str_replace('www.twitch.tv', 'twitch.tv', $normalized_url);
-               $channel = Channel::firstWhere('stream_link', 'LIKE', $normalized_url);
+               $channel = Channel::query()->firstWhere('stream_link', 'LIKE', $normalized_url);
                if (!$channel) {
                        $channel = new Channel();
                        $channel->stream_link = $normalized_url;
-                       $channel->title = Str::afterLast($channel_url, '/');
+                       $channel->title = Str::afterLast($normalized_url, '/');
                        $channel->languages = ['en'];
+                       $channel->twitch_chat = '#'.Str::afterLast($normalized_url, '/');
+                       $channel->access_key = Str::uuid();
                        $channel->save();
                }
                return $channel;
diff --git a/database/migrations/2025_09_24_112139_default_channel_short_name_to_empty_string.php b/database/migrations/2025_09_24_112139_default_channel_short_name_to_empty_string.php
new file mode 100644 (file)
index 0000000..d991f3a
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+               Schema::table('channels', function (Blueprint $table) {
+                       $table->string('short_name')->default('')->change();
+               });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+    }
+};