]> git.localhorst.tv Git - alttp.git/blob - app/Models/Channel.php
8045a4194fa529561ba6a091c0aa7770beb18dce
[alttp.git] / app / Models / Channel.php
1 <?php
2
3 namespace App\Models;
4
5 use Illuminate\Broadcasting\Channel as PublicChannel;
6 use Illuminate\Broadcasting\PrivateChannel;
7 use Illuminate\Database\Eloquent\BroadcastsEvents;
8 use Illuminate\Database\Eloquent\Factories\HasFactory;
9 use Illuminate\Database\Eloquent\Model;
10 use Illuminate\Support\Arr;
11
12 class Channel extends Model {
13
14         use BroadcastsEvents;
15         use HasFactory;
16
17         public function broadcastOn($event) {
18                 $channels = [
19                         new PrivateChannel('Channel.'.$this->id),
20                 ];
21                 if (!empty($this->access_key)) {
22                         $channels[] = new PublicChannel('ChannelKey.'.$this->access_key);
23                 }
24                 return $channels;
25         }
26
27         public function getCurrentEpisode() {
28                 return $this->episodes()
29                         ->where('start', '<', now()->subMinutes(10))
30                         ->orderBy('start', 'DESC')
31                         ->first();
32         }
33
34         public function randomOfClass($class) {
35                 if (is_array($class)) {
36                         return $this->queryChatlog()
37                                 ->whereIn('classification', $class)
38                                 ->first();
39                 }
40                 return $this->queryChatlog()
41                         ->where('classification', '=', $class)
42                         ->first();
43         }
44
45         public function queryChatlog() {
46                 return ChatLog::where('type', '=', 'chat')
47                         ->where('banned', '=', false)
48                         ->where('created_at', '<', now()->sub(1, 'day'))
49                         ->where(function ($query) {
50                                 $query->whereNull('detected_language');
51                                 $query->orWhereIn('detected_language', $this->getPreferredLanguages());
52                         })
53                         ->inRandomOrder();
54         }
55
56         public function getPreferredLanguages() {
57                 $setting = $this->getChatSetting('language');
58                 if ($setting) {
59                         return [$setting];
60                 }
61                 if (!empty($this->languages)) {
62                         return $this->languages;
63                 }
64                 return ['de'];
65         }
66
67         public function getChatSetting($name, $default = null) {
68                 if (array_key_exists($name, $this->chat_settings)) {
69                         return $this->chat_settings[$name];
70                 }
71                 return $default;
72         }
73
74         public function getGuessingLeaderboard() {
75                 $query = $this->winners()
76                         ->selectRaw('(select t2.uname from guessing_winners t2 where t2.uid = guessing_winners.uid order by created_at desc limit 1) as name, sum(score) as score')
77                         ->where('score', '!=', 0)
78                         ->groupBy('uid')
79                         ->orderBy('score', 'desc')
80                         ->limit(10);
81                 $type = $this->getGuessingSetting('leaderboard_type', 'all');
82                 if ($type == 'month') {
83                         $query->where('created_at', '>=', now()->startOfMonth());
84                 } else if ($type == 'year') {
85                         $query->where('created_at', '>=', now()->startOfYear());
86                 } else if (is_numeric($type)) {
87                         $query->where('created_at', '>=', now()->sub($type, 'days'));
88                 }
89                 return $query->get();
90         }
91
92         public function hasActiveGuessing() {
93                 return !is_null($this->guessing_start);
94         }
95
96         public function isAcceptingGuesses() {
97                 return !is_null($this->guessing_start) && is_null($this->guessing_end);
98         }
99
100         public function startGuessing($type) {
101                 $this->guessing_type = $type;
102                 $this->guessing_start = now();
103                 $this->save();
104         }
105
106         public function stopGuessing() {
107                 $this->guessing_end = now();
108                 $this->save();
109         }
110
111         public function getGuessingSetting($name, $default = null) {
112                 if (empty($this->guessing_settings) ||
113                         empty($this->guessing_type) ||
114                         !array_key_exists($this->guessing_type, $this->guessing_settings) ||
115                         !array_key_exists($name, $this->guessing_settings[$this->guessing_type])
116                 ) {
117                         return $default;
118                 }
119                 return $this->guessing_settings[$this->guessing_type][$name];
120         }
121
122         public function solveGuessing($solution) {
123                 $start = $this->guessing_start;
124                 $end = is_null($this->guessing_end) ? now() : $this->guessing_end;
125                 $guesses = $this->guesses()->whereBetween('created_at', [$start, $end])->orderBy('created_at', 'ASC')->get();
126                 $unique_guesses = [];
127                 foreach ($guesses as $guess) {
128                         $unique_guesses[$guess->uid] = $guess;
129                 }
130                 $candidates = [];
131                 foreach ($unique_guesses as $guess) {
132                         if ($guess->guess == $solution) {
133                                 $candidates[] = $guess;
134                         }
135                 }
136                 if (empty($candidates) && is_numeric($solution)) {
137                         $min_distance = null;
138                         foreach ($unique_guesses as $guess) {
139                                 $distance = abs(intval($guess->guess) - intval($solution));
140                                 if (is_null($min_distance) || $distance == $min_distance) {
141                                         $candidates[] = $guess;
142                                         if (is_null($min_distance)) {
143                                                 $min_distance = $distance;
144                                         }
145                                 } else if ($distance < $min_distance) {
146                                         $candidates = [$guess];
147                                         $min_distance = $distance;
148                                 }
149                         }
150                 }
151                 $winners = [];
152                 $first = true;
153                 foreach ($candidates as $candidate) {
154                         $score = $this->scoreGuessing($solution, $candidate->guess, $first);
155                         $winner = new GuessingWinner();
156                         $winner->channel()->associate($this);
157                         $winner->pod = $start;
158                         $winner->uid = $candidate->uid;
159                         $winner->uname = $candidate->uname;
160                         $winner->guess = $candidate->guess;
161                         $winner->solution = $solution;
162                         $winner->score = $score;
163                         $winner->save();
164                         $winners[] = $winner;
165                         $first = false;
166                 }
167                 return $winners;
168         }
169
170         public function clearGuessing() {
171                 $this->guessing_start = null;
172                 $this->guessing_end = null;
173                 $this->save();
174         }
175
176         public function transformGuess($original) {
177                 $transformed = trim($original);
178                 if ($this->guessing_type == 'gtbk') {
179                         $transformed = str_replace(['roodyo1Gtbigkey'], ['2'], $transformed);
180                         $transformed = str_ireplace(['vier 4Head'], ['4'], $transformed);
181                 }
182                 return $transformed;
183         }
184
185         public function registerGuess($uid, $uname, $guess) {
186                 $model = new GuessingGuess();
187                 $model->channel()->associate($this);
188                 $model->uid = $uid;
189                 $model->uname = $uname;
190                 $model->guess = $this->transformGuess($guess);
191                 $model->save();
192         }
193
194         public function scoreGuessing($solution, $guess, $first) {
195                 $transformed = $this->transformGuess($guess);
196                 if ($transformed == $solution) {
197                         if ($first) {
198                                 return $this->getGuessingSetting('points_exact_first', 1);
199                         }
200                         return $this->getGuessingSetting('points_exact_other', 1);
201                 }
202                 $distance = abs(intval($transformed) - intval($solution));
203                 if ($distance <= $this->getGuessingSetting('points_close_max', 3)) {
204                         if ($first) {
205                                 return $this->getGuessingSetting('points_close_first', 1);
206                         }
207                         return $this->getGuessingSetting('points_close_other', 1);
208                 }
209                 return 0;
210         }
211
212         public function isValidGuess($solution) {
213                 $transformed = $this->transformGuess($solution);
214                 if ($this->guessing_type == 'gtbk') {
215                         $int_solution = intval($transformed);
216                         return is_numeric($transformed) && $int_solution > 0 && $int_solution < 23;
217                 }
218                 return false;
219         }
220
221         public function listGuessingWinners($winners) {
222                 $names = [];
223                 $distance = 0;
224                 foreach ($winners as $winner) {
225                         if ($winner->score > 0) {
226                                 $names[] = $winner->uname;
227                                 $distance = abs(intval($winner->guess) - intval($winner->solution));
228                         }
229                 }
230                 $msg = '';
231                 if (empty($names)) {
232                         $msg = $this->getGuessingSetting('no_winners_message');
233                 } else {
234                         $msg = $this->getGuessingSetting($distance ? 'close_winners_message' : 'winners_message', $this->getGuessingSetting('winners_message'));
235                         $msg = str_replace(['{distance}', '{names}'], [$distance, $this->listAnd($names)], $msg);
236                 }
237                 return $msg;
238         }
239
240         public function listAnd($entries) {
241                 $lang = empty($this->languages) ? 'en' : $this->languages[0];
242                 if ($lang == 'de') {
243                         return Arr::join($entries, ', ', ' und ');
244                 }
245                 return Arr::join($entries, ', ', ' and ');
246         }
247
248         public function chat_bot_logs() {
249                 return $this->hasMany(ChatBotLog::class);
250         }
251
252         public function crews() {
253                 return $this->hasMany(ChannelCrew::class);
254         }
255
256         public function episodes() {
257                 return $this->belongsToMany(Episode::class)
258                         ->using(Restream::class)
259                         ->withPivot('accept_comms', 'accept_tracker');
260         }
261
262         public function guesses() {
263                 return $this->hasMany(GuessingGuess::class);
264         }
265
266         public function organization() {
267                 return $this->belongsTo(Organization::class);
268         }
269
270         public function winners() {
271                 return $this->hasMany(GuessingWinner::class);
272         }
273
274         protected $casts = [
275                 'chat' => 'boolean',
276                 'chat_commands' => 'array',
277                 'chat_settings' => 'array',
278                 'guessing_end' => 'datetime',
279                 'guessing_settings' => 'array',
280                 'guessing_start' => 'datetime',
281                 'languages' => 'array',
282                 'join' => 'boolean',
283         ];
284
285         protected $hidden = [
286                 'access_key',
287                 'chat',
288                 'chat_commands',
289                 'chat_settings',
290                 'created_at',
291                 'ext_id',
292                 'guessing_settings',
293                 'join',
294                 'twitch_chat',
295                 'updated_at',
296         ];
297
298 }