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