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;
12 class Channel extends Model {
17 public function broadcastOn($event) {
19 new PrivateChannel('Channel.'.$this->id),
21 if (!empty($this->access_key)) {
22 $channels[] = new PublicChannel('ChannelKey.'.$this->access_key);
27 public function getCurrentEpisode() {
28 return $this->episodes()
29 ->where('start', '<', now()->subMinutes(10))
30 ->orderBy('start', 'DESC')
34 public function randomOfClass($class) {
35 if (is_array($class)) {
36 return $this->queryChatlog()
37 ->whereIn('classification', $class)
40 return $this->queryChatlog()
41 ->where('classification', '=', $class)
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());
55 $source = $this->getChatSetting('source', 'any');
56 if (in_array($source, ['catchan', 'chan'])) {
57 $query->where('channel_id', $this->id);
59 if (in_array($source, ['cat', 'catchan'])) {
60 $query->where('twitch_category', $this->twitch_category);
65 public function getPreferredLanguages() {
66 $setting = $this->getChatSetting('language');
70 if (!empty($this->languages)) {
71 return $this->languages;
76 public function getChatSetting($name, $default = null) {
77 if (array_key_exists($name, $this->chat_settings)) {
78 return $this->chat_settings[$name];
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)
88 ->orderBy('score', 'desc')
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'));
101 public function hasActiveGuessing() {
102 return !is_null($this->guessing_start);
105 public function isAcceptingGuesses() {
106 return !is_null($this->guessing_start) && is_null($this->guessing_end);
109 public function startGuessing($type) {
110 $this->guessing_type = $type;
111 $this->guessing_start = now();
115 public function stopGuessing() {
116 $this->guessing_end = now();
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])
128 return $this->guessing_settings[$this->guessing_type][$name];
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;
140 foreach ($unique_guesses as $guess) {
141 if ($guess->guess == $solution) {
142 $candidates[] = $guess;
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;
154 } else if ($distance < $min_distance) {
155 $candidates = [$guess];
156 $min_distance = $distance;
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;
173 $winners[] = $winner;
179 public function clearGuessing() {
180 $this->guessing_start = null;
181 $this->guessing_end = null;
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);
194 public function registerGuess($uid, $uname, $guess) {
195 $model = new GuessingGuess();
196 $model->channel()->associate($this);
198 $model->uname = $uname;
199 $model->guess = $this->transformGuess($guess);
203 public function scoreGuessing($solution, $guess, $first) {
204 $transformed = $this->transformGuess($guess);
205 if ($transformed == $solution) {
207 return $this->getGuessingSetting('points_exact_first', 1);
209 return $this->getGuessingSetting('points_exact_other', 1);
211 $distance = abs(intval($transformed) - intval($solution));
212 if ($distance <= $this->getGuessingSetting('points_close_max', 3)) {
214 return $this->getGuessingSetting('points_close_first', 1);
216 return $this->getGuessingSetting('points_close_other', 1);
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;
230 public function listGuessingWinners($winners) {
233 foreach ($winners as $winner) {
234 if ($winner->score > 0) {
235 $names[] = $winner->uname;
236 $distance = abs(intval($winner->guess) - intval($winner->solution));
241 $msg = $this->getGuessingSetting('no_winners_message');
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);
249 public function listAnd($entries) {
250 $lang = empty($this->languages) ? 'en' : $this->languages[0];
252 return Arr::join($entries, ', ', ' und ');
254 return Arr::join($entries, ', ', ' and ');
257 public function chat_bot_logs() {
258 return $this->hasMany(ChatBotLog::class);
261 public function crews() {
262 return $this->hasMany(ChannelCrew::class);
265 public function episodes() {
266 return $this->belongsToMany(Episode::class)
267 ->using(Restream::class)
268 ->withPivot('accept_comms', 'accept_tracker');
271 public function guesses() {
272 return $this->hasMany(GuessingGuess::class);
275 public function organization() {
276 return $this->belongsTo(Organization::class);
279 public function winners() {
280 return $this->hasMany(GuessingWinner::class);
285 'chat_commands' => 'array',
286 'chat_settings' => 'array',
287 'guessing_end' => 'datetime',
288 'guessing_settings' => 'array',
289 'guessing_start' => 'datetime',
290 'languages' => 'array',
294 protected $hidden = [