]> git.localhorst.tv Git - alttp.git/commitdiff
racetime sync
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 22 Aug 2023 09:05:22 +0000 (11:05 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 22 Aug 2023 09:05:22 +0000 (11:05 +0200)
app/Console/Commands/SyncRacetime.php [new file with mode: 0644]
app/Console/Kernel.php
database/migrations/2023_08_21_073535_add_event_racetime.php [new file with mode: 0644]
database/migrations/2023_08_22_075105_add_episode_raceroom.php [new file with mode: 0644]
resources/js/components/common/Icon.js
resources/js/components/episodes/Item.js
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/_variables.scss

diff --git a/app/Console/Commands/SyncRacetime.php b/app/Console/Commands/SyncRacetime.php
new file mode 100644 (file)
index 0000000..34e41c4
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\Event;
+use App\Models\Episode;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Support\Facades\Http;
+
+class SyncRacetime extends Command {
+
+       /**
+        * The name and signature of the console command.
+        *
+        * @var string
+        */
+       protected $signature = 'sync:racetime';
+
+       /**
+        * The console command description.
+        *
+        * @var string
+        */
+       protected $description = 'Link racetime rooms with episodes';
+
+       /**
+        * Execute the console command.
+        *
+        * @return int
+        */
+       public function handle() {
+               $events = Event::whereNotNull('racetime_category')
+                       ->where(function (Builder $query) {
+                               $query->whereNull('end');
+                               $query->orWhere('end', '>', now());
+                       })
+                       ->get();
+
+               foreach ($events as $event) {
+                       try {
+                               $this->line('syncing '.$event->name);
+                               $this->syncEvent($event);
+                       } catch (\Exception $e) {
+                               $this->error('error syncing event '.$event->name.': '.$e->getMessage());
+                       }
+               }
+
+               return Command::SUCCESS;
+       }
+
+       private function syncEvent(Event $event) {
+               $episodes = $event->episodes()->whereBetween('start', [now()->sub(1, 'hour'), now()->add(30, 'minute')])->get();
+               if ($episodes->isEmpty()) return;
+               $racerooms = $this->getRacetime($event->racetime_category);
+               foreach ($episodes as $episode) {
+                       $rooms = $this->filterID($episode, $racerooms);
+                       if (count($rooms) > 1) {
+                               $rooms = $this->filterStartTime($episode, $rooms);
+                       }
+                       if (count($rooms) > 1) {
+                               $rooms = $this->filterPlayerNames($episode, $rooms);
+                       }
+                       if (count($rooms) == 1) {
+                               $this->line(' - linking episode '.$episode->id.' with room '.$rooms[0]['name']);
+                               $episode->raceroom = 'https://racetime.gg'.$rooms[0]['url'];
+                               $episode->save();
+                       }
+               }
+       }
+
+       private function getRacetime($category) {
+               if (isset($this->cat_cache[$category])) {
+                       return $this->cat_cache[$category];
+               }
+               $category_data = HTTP::get('https://racetime.gg/'.$category.'/data');
+               $races_data = HTTP::get('https://racetime.gg/'.$category.'/races/data');
+               $this->cat_cache[$category] = array_merge(array_values($category_data['current_races']), array_values($races_data['races']));
+               return $this->cat_cache[$category];
+       }
+
+       private function filterID(Episode $episode, $racerooms) {
+               if (empty($episode->ext_id) || substr($episode->ext_id, 0, 3) != 'sg:') {
+                       return $racerooms;
+               }
+               $rooms = [];
+               $id = substr($episode->ext_id, 3);
+               foreach ($racerooms as $room) {
+                       if (strpos($room['info'], $id) !== false) {
+                               $rooms[] = $room;
+                       }
+               }
+               return empty($rooms) ? $racerooms : $rooms;
+       }
+
+       private function filterStartTime(Episode $episode, $racerooms) {
+               if (empty($episode->start)) {
+                       return $racerooms;
+               }
+               $rooms = [];
+               $from = Carbon::createFromFormat('Y-m-d H:i:s', $episode->start, 'UTC')->sub(1, 'hour');
+               $till = Carbon::createFromFormat('Y-m-d H:i:s', $episode->start, 'UTC')->add(1, 'hour');
+               foreach ($racerooms as $room) {
+                       $created = Carbon::createFromFormat('Y-m-d\TH:i:s.uP', $room['opened_at']);
+                       if ($created->isAfter($from) && $created->isBefore($till)) {
+                               $rooms[] = $room;
+                       }
+               }
+               return empty($rooms) ? $racerooms : $rooms;
+       }
+
+       private function filterPlayerNames(Episode $episode, $racerooms) {
+               $pnames = [];
+               foreach ($episode->players as $player) {
+                       $pname = trim($player->getName());
+                       if (!empty($pname)) {
+                               $pnames[] = $pname;
+                       }
+               }
+               if (empty($pnames)) {
+                       return $racerooms;
+               }
+               $rooms = [];
+               foreach ($racerooms as $room) {
+                       $missing = false;
+                       foreach ($pnames as $pname) {
+                               if (stripos($room['info'], $pname) === false) {
+                                       $missing = true;
+                                       break;
+                               }
+                       }
+                       if (!$missing) {
+                               $rooms[] = $room;
+                       }
+               }
+               return empty($rooms) ? $racerooms : $rooms;
+       }
+
+       private $cat_cache = [];
+
+}
index 904bfd19db2522e245aeab8bfa5c6799a38bbb69..c3d9affa9cc5431706a005f38806bfca64ae72e2 100644 (file)
@@ -15,9 +15,10 @@ class Kernel extends ConsoleKernel
      */
     protected function schedule(Schedule $schedule)
     {
-               $schedule->command('sync:avatars')->everyFiveMinutes();
                $schedule->command('sync:speedgaming')->everyFiveMinutes();
                $schedule->command('sync:sra')->everyFifteenMinutes();
+               $schedule->command('sync:avatars')->everyFiveMinutes();
+               $schedule->command('sync:racetime')->everyFiveMinutes();
     }
 
     /**
diff --git a/database/migrations/2023_08_21_073535_add_event_racetime.php b/database/migrations/2023_08_21_073535_add_event_racetime.php
new file mode 100644 (file)
index 0000000..21b05fb
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+       /**
+        * Run the migrations.
+        *
+        * @return void
+        */
+       public function up()
+       {
+               Schema::table('events', function(Blueprint $table) {
+                       $table->string('racetime_category')->nullable()->default(null);
+               });
+       }
+
+       /**
+        * Reverse the migrations.
+        *
+        * @return void
+        */
+       public function down()
+       {
+               Schema::table('events', function(Blueprint $table) {
+                       $table->dropColumn('racetime_category');
+               });
+       }
+};
diff --git a/database/migrations/2023_08_22_075105_add_episode_raceroom.php b/database/migrations/2023_08_22_075105_add_episode_raceroom.php
new file mode 100644 (file)
index 0000000..7eb5006
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+       /**
+        * Run the migrations.
+        *
+        * @return void
+        */
+       public function up()
+       {
+               Schema::table('episodes', function(Blueprint $table) {
+                       $table->string('raceroom')->nullable()->default(null);
+               });
+       }
+
+       /**
+        * Reverse the migrations.
+        *
+        * @return void
+        */
+       public function down()
+       {
+               Schema::table('episodes', function(Blueprint $table) {
+                       $table->dropColumn('raceroom');
+               });
+       }
+};
index e1c4d46016104dbaa259540fdde56558348097e3..641577057418d55f3ea5494be029a19d83a8bdaa 100644 (file)
@@ -85,6 +85,7 @@ Icon.PENDING = makePreset('PendingIcon', 'clock');
 Icon.PIN = makePreset('PinIcon', 'location-pin');
 Icon.PLAY = makePreset('PlayIcon', 'play');
 Icon.PROTOCOL = makePreset('ProtocolIcon', 'file-alt');
+Icon.RACETIME = makePreset('RacetimeIcon', 'stopwatch');
 Icon.REJECT = makePreset('RejectIcon', 'square-xmark');
 Icon.REMOVE = makePreset('RemoveIcon', 'square-xmark');
 Icon.RESULT = makePreset('ResultIcon', 'clock');
index adb710916a088ba8730f89912ba3e643918a06d5..4fb466669dfdc2591b90fab9778f263cff4a4ab4 100644 (file)
@@ -67,6 +67,20 @@ const Item = ({ episode, onAddRestream, onApply, onEditRestream, user }) => {
                                {!hasChannels && hasPlayers ?
                                        <MultiLink players={episode.players} />
                                : null}
+                               {episode.raceroom ?
+                                       <div>
+                                               <Button
+                                                       href={episode.raceroom}
+                                                       target="_blank"
+                                                       title={t('episodes.raceroom')}
+                                                       variant="outline-secondary"
+                                               >
+                                                       <Icon.RACETIME title="" />
+                                                       {' '}
+                                                       {t('episodes.raceroom')}
+                                               </Button>
+                                       </div>
+                               : null}
                                {onAddRestream && canRestreamEpisode(user, episode) ?
                                        <div>
                                                <Button
@@ -123,6 +137,7 @@ Item.propTypes = {
                }),
                players: PropTypes.arrayOf(PropTypes.shape({
                })),
+               raceroom: PropTypes.string,
                start: PropTypes.string,
                title: PropTypes.string,
        }),
index e55885ff11dea4d448c95cc72938a7d1a6d8d21a..a83971caa33e51f025c589e61ecb8e689c9e9fb0 100644 (file)
@@ -108,6 +108,7 @@ export default {
                        channel: 'Kanal',
                        commentary: 'Kommentar',
                        empty: 'Keine anstehenden Termine.',
+                       raceroom: 'Raceroom',
                        restreamDialog: {
                                acceptComms: 'Suche Kommentatoren',
                                acceptTracker: 'Suche Tracker',
index 52c9031d8362b02659a4a6193ed20d0474fed7db..8ea7fa803c4c8854280639a7823ca424d3ee2b6e 100644 (file)
@@ -108,6 +108,7 @@ export default {
                        channel: 'Channel',
                        commentary: 'Commentary',
                        empty: 'No dates coming up.',
+                       raceroom: 'Race room',
                        restreamDialog: {
                                acceptComms: 'Open commentary application',
                                acceptTracker: 'Open tracker application',
index 3c0d0e7460a876c1df890bea895db559d19918b3..c6b8ae5fa2d86c6c2f1b524fde25d8d7fb69449b 100644 (file)
@@ -10,6 +10,7 @@ $bronze: #ad8a56;
 $challonge: #ff7324;
 $discord: #5865f2;
 $gold: #c9b037;
+$racetime: #eeeeee;
 $silver: #b4b4b4;
 $twitch: #6441a5;
 $youtube: #ff0000;
@@ -18,6 +19,7 @@ $youtube: #ff0000;
 $custom-colors: (
        "challonge": $challonge,
        "discord": $discord,
+       "racetime": $racetime,
        "twitch": $twitch,
        "youtube": $youtube
 );