]> git.localhorst.tv Git - alttp.git/blob - app/Console/Commands/SyncSpeedGaming.php
queue user sync on discovery
[alttp.git] / app / Console / Commands / SyncSpeedGaming.php
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Models\Channel;
6 use App\Models\DiscordBotCommand;
7 use App\Models\Episode;
8 use App\Models\EpisodePlayer;
9 use App\Models\Event;
10 use App\Models\Organization;
11 use App\Models\User;
12 use Carbon\Carbon;
13 use Illuminate\Console\Command;
14 use Illuminate\Database\Eloquent\Builder;
15 use Illuminate\Support\Facades\Http;
16
17 class SyncSpeedGaming extends Command {
18
19         /**
20          * The name and signature of the console command.
21          *
22          * @var string
23          */
24         protected $signature = 'sync:speedgaming';
25
26         /**
27          * The console command description.
28          *
29          * @var string
30          */
31         protected $description = 'Synchronize SpeedGaming schedule';
32
33         /**
34          * Execute the console command.
35          *
36          * @return int
37          */
38         public function handle() {
39                 $this->org = Organization::where('name', '=', 'sg')->firstOrFail();
40
41                 $events = Event::where('external_schedule', 'LIKE', 'sg:%')
42                         ->where(function (Builder $query) {
43                                 $query->whereNull('end');
44                                 $query->orWhere('end', '<', now());
45                         })
46                         ->get();
47
48                 foreach ($events as $event) {
49                         try {
50                                 $this->line('syncing '.$event->name);
51                                 $this->syncEvent($event);
52                         } catch (\Exception $e) {
53                                 $this->error('error syncing event '.$event->name.': '.$e->getMessage());
54                         }
55                 }
56
57                 return 0;
58         }
59
60         private function syncEvent(Event $event) {
61                 $sgHandle = substr($event->external_schedule, 3);
62                 $from = now()->sub(1, 'day');
63                 $to = now()->add(6, 'day');
64                 $sgSchedule = HTTP::get('https://speedgaming.org/api/schedule/', [
65                         'event' => $sgHandle,
66                         'from' => $from->toIso8601String(),
67                         'to' => $to->toIso8601String(),
68                 ])->json();
69                 $this->purgeSchedule($event, $from, $to, $sgSchedule);
70                 foreach ($sgSchedule as $sgEntry) {
71                         try {
72                                 $this->syncSchedule($event, $sgEntry);
73                         } catch (Exception $e) {
74                                 $this->error('error syncing episode '.$sgEntry['id'].': '.$e->getMessage());
75                         }
76                 }
77         }
78
79         private function purgeSchedule(Event $event, $from, $to, $sgSchedule) {
80                 $ext_ids = [];
81                 foreach ($sgSchedule as $sgEntry) {
82                         $ext_ids[] = 'sg:'.$sgEntry['id'];
83                 }
84                 $to_purge = $event->episodes()
85                         ->where('start', '>=', $from)
86                         ->where('start', '<=', $to)
87                         ->whereNotIn('ext_id', $ext_ids);
88                 foreach ($to_purge->get() as $episode) {
89                         $episode->players()->delete();
90                 }
91                 $to_purge->delete();
92         }
93
94         private function syncSchedule(Event $event, $sgEntry) {
95                 $ext_id = 'sg:'.$sgEntry['id'];
96                 $episode = Episode::firstWhere('ext_id', '=', $ext_id);
97                 if (!$episode) {
98                         $episode = new Episode();
99                         $episode->ext_id = $ext_id;
100                 }
101                 $episode->event()->associate($event);
102                 $episode->title = $sgEntry['match1']['title'];
103                 $start = Carbon::createFromFormat('Y-m-d\TH:i:sP', $sgEntry['when']);
104                 if ($start->ne($episode->start)) {
105                         $episode->start = $start;
106                 }
107                 $episode->estimate = $sgEntry['length'] * 60;
108                 $episode->confirmed = $sgEntry['approved'];
109                 $episode->comment = $sgEntry['match1']['note'];
110                 $episode->save();
111
112                 $this->purgeChannels($episode, $sgEntry);
113                 $channelIds = [];
114                 foreach ($sgEntry['channels'] as $sgChannel) {
115                         if ($sgChannel['initials'] == 'NONE') continue;
116                         try {
117                                 $channel = $this->syncChannel($episode, $sgChannel);
118                                 $channelIds[] = $channel->id;
119                         } catch (Exception $e) {
120                                 $this->error('error syncing channel '.$sgChannel['id'].': '.$e->getMessage());
121                         }
122                 }
123                 if (!empty($channelIds)) {
124                         $episode->channels()->syncWithoutDetaching($channelIds);
125                 }
126
127                 $this->purgePlayers($episode, $sgEntry);
128                 foreach ($sgEntry['match1']['players'] as $sgPlayer) {
129                         try {
130                                 $this->syncPlayer($episode, $sgPlayer);
131                         } catch (Exception $e) {
132                                 $this->error('error syncing player '.$sgPlayer['id'].': '.$e->getMessage());
133                         }
134                 }
135         }
136
137         private function purgeChannels(Episode $episode, $sgEntry) {
138                 $ext_ids = [];
139                 foreach ($sgEntry['channels'] as $sgChannel) {
140                         $ext_ids[] = 'sg:'.$sgChannel['id'];
141                 }
142                 $episode->channels()
143                   ->where('ext_id', 'LIKE', 'sg:%')
144                   ->whereNotIn('ext_id', $ext_ids)
145                   ->detach();
146         }
147
148         private function syncChannel(Episode $episode, $sgChannel) {
149                 $ext_id = 'sg:'.$sgChannel['id'];
150                 $channel = $this->org->channels()->firstWhere('ext_id', '=', $ext_id);
151                 if (!$channel) {
152                         $channel = new Channel();
153                         $channel->ext_id = $ext_id;
154                         $channel->organization()->associate($this->org);
155                 }
156                 $channel->short_name = $sgChannel['initials'];
157                 $channel->title = $sgChannel['name'];
158                 $channel->stream_link = 'https://twitch.tv/'.strtolower($sgChannel['name']);
159                 $channel->languages = [$sgChannel['language']];
160                 $channel->save();
161                 return $channel;
162         }
163
164         private function purgePlayers(Episode $episode, $sgEntry) {
165                 $ext_ids = [];
166                 foreach ($sgEntry['match1']['players'] as $sgPlayer) {
167                         $ext_ids[] = 'sg:'.$sgPlayer['id'];
168                 }
169                 $episode->players()->whereNotIn('ext_id', $ext_ids)->delete();
170         }
171
172         private function syncPlayer(Episode $episode, $sgPlayer) {
173                 $ext_id = 'sg:'.$sgPlayer['id'];
174                 $player = $episode->players()->firstWhere('ext_id', '=', $ext_id);
175                 if (!$player) {
176                         $player = new EpisodePlayer();
177                         $player->ext_id = $ext_id;
178                         $player->episode()->associate($episode);
179                 }
180                 $user = $this->getUserBySGPlayer($sgPlayer);
181                 if ($user) {
182                         $player->user()->associate($user);
183                         if (empty($user->stream_link)) {
184                                 if (!empty($sgPlayer['publicStream'])) {
185                                         $user->stream_link = 'https://twitch.tv/'.strtolower($sgPlayer['publicStream']);
186                                         $user->save();
187                                 } else if (!empty($sgPlayer['streamingFrom'])) {
188                                         $user->stream_link = 'https://twitch.tv/'.strtolower($sgPlayer['streamingFrom']);
189                                         $user->save();
190                                 }
191                         }
192                 } else {
193                         $player->user()->disassociate();
194                 }
195                 if (!empty($sgPlayer['displayName'])) {
196                         $player->name_override = $sgPlayer['displayName'];
197                 }
198                 if (!empty($sgPlayer['streamingFrom'])) {
199                         $player->stream_override = strtolower($sgPlayer['streamingFrom']);
200                 }
201                 $player->save();
202         }
203
204         private function getUserBySGPlayer($player) {
205                 if (!empty($player['discordId'])) {
206                         $user = User::find($player['discordId']);
207                         if ($user) return $user;
208                         DiscordBotCommand::syncUser($player['discordId']);
209                 }
210                 if (!empty($player['discordTag'])) {
211                         $tag = explode('#', $player['discordTag']);
212                         $user = User::firstWhere([
213                                 ['username', 'LIKE', $tag[0]],
214                                 ['username', 'LIKE', $tag[1]],
215                         ]);
216                         if ($user) return $user;
217                 }
218                 return null;
219         }
220
221         private $org;
222
223 }