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