--- /dev/null
+<?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 = [];
+
+}