namespace App\Models;
+use Illuminate\Broadcasting\Channel as PublicChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Database\Eloquent\BroadcastsEvents;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Arr;
-class Channel extends Model
-{
+class Channel extends Model {
+
+ use BroadcastsEvents;
use HasFactory;
+ public function broadcastOn($event) {
+ $channels = [
+ new PrivateChannel('Channel.'.$this->id),
+ ];
+ if (!empty($this->access_key)) {
+ $channels[] = new PublicChannel('ChannelKey.'.$this->access_key);
+ }
+ return $channels;
+ }
+
public function getCurrentEpisode() {
return $this->episodes()
->where('start', '<', now()->subMinutes(10))
->first();
}
+ public function randomOfClass($class) {
+ if (is_array($class)) {
+ return $this->queryChatlog()
+ ->whereIn('classification', $class)
+ ->first();
+ }
+ return $this->queryChatlog()
+ ->where('classification', '=', $class)
+ ->first();
+ }
+
+ public function queryChatlog() {
+ $min_age = $this->getChatSetting('min_age', 1);
+ $query = ChatLog::where('type', '=', 'chat')
+ ->where('banned', '=', false)
+ ->where('created_at', '<', now()->sub($min_age, 'day'))
+ ->where(function ($query) {
+ $query->whereNull('detected_language');
+ $query->orWhereIn('detected_language', $this->getPreferredLanguages());
+ })
+ ->inRandomOrder();
+ $source = $this->getChatSetting('source', 'any');
+ if (in_array($source, ['catchan', 'chan'])) {
+ $query->where('channel_id', $this->id);
+ }
+ if (in_array($source, ['cat', 'catchan'])) {
+ $query->where('twitch_category', $this->twitch_category);
+ }
+ return $query;
+ }
+
+ public function getPreferredLanguages() {
+ $setting = $this->getChatSetting('language');
+ if ($setting) {
+ return [$setting];
+ }
+ if (!empty($this->languages)) {
+ return $this->languages;
+ }
+ return ['de'];
+ }
+
+ public function getChatSetting($name, $default = null) {
+ if (array_key_exists($name, $this->chat_settings)) {
+ return $this->chat_settings[$name];
+ }
+ return $default;
+ }
+
+ public function getGuessingLeaderboard() {
+ $query = $this->winners()
+ ->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')
+ ->where('score', '!=', 0)
+ ->groupBy('uid')
+ ->orderBy('score', 'desc')
+ ->limit(10);
+ $type = $this->getGuessingSetting('leaderboard_type', 'all');
+ if ($type == 'month') {
+ $query->where('created_at', '>=', now()->startOfMonth());
+ } else if ($type == 'year') {
+ $query->where('created_at', '>=', now()->startOfYear());
+ } else if (is_numeric($type)) {
+ $query->where('created_at', '>=', now()->sub($type, 'days'));
+ }
+ return $query->get();
+ }
+
+ public function hasActiveGuessing() {
+ return !is_null($this->guessing_start);
+ }
+
+ public function isAcceptingGuesses() {
+ return !is_null($this->guessing_start) && is_null($this->guessing_end);
+ }
+
+ public function startGuessing($type) {
+ $this->guessing_type = $type;
+ $this->guessing_start = now();
+ $this->save();
+ }
+
+ public function stopGuessing() {
+ $this->guessing_end = now();
+ $this->save();
+ }
+
+ public function getGuessingSetting($name, $default = null) {
+ if (empty($this->guessing_settings) ||
+ empty($this->guessing_type) ||
+ !array_key_exists($this->guessing_type, $this->guessing_settings) ||
+ !array_key_exists($name, $this->guessing_settings[$this->guessing_type])
+ ) {
+ return $default;
+ }
+ return $this->guessing_settings[$this->guessing_type][$name];
+ }
+
+ public function solveGuessing($solution) {
+ $start = $this->guessing_start;
+ $end = is_null($this->guessing_end) ? now() : $this->guessing_end;
+ $guesses = $this->guesses()->whereBetween('created_at', [$start, $end])->orderBy('created_at', 'ASC')->get();
+ $unique_guesses = [];
+ foreach ($guesses as $guess) {
+ $unique_guesses[$guess->uid] = $guess;
+ }
+ $candidates = [];
+ foreach ($unique_guesses as $guess) {
+ if ($guess->guess == $solution) {
+ $candidates[] = $guess;
+ }
+ }
+ if (empty($candidates) && is_numeric($solution)) {
+ $min_distance = null;
+ foreach ($unique_guesses as $guess) {
+ $distance = abs(intval($guess->guess) - intval($solution));
+ if (is_null($min_distance) || $distance == $min_distance) {
+ $candidates[] = $guess;
+ if (is_null($min_distance)) {
+ $min_distance = $distance;
+ }
+ } else if ($distance < $min_distance) {
+ $candidates = [$guess];
+ $min_distance = $distance;
+ }
+ }
+ }
+ $winners = [];
+ $first = true;
+ foreach ($candidates as $candidate) {
+ $score = $this->scoreGuessing($solution, $candidate->guess, $first);
+ $winner = new GuessingWinner();
+ $winner->channel()->associate($this);
+ $winner->pod = $start;
+ $winner->uid = $candidate->uid;
+ $winner->uname = $candidate->uname;
+ $winner->guess = $candidate->guess;
+ $winner->solution = $solution;
+ $winner->score = $score;
+ $winner->save();
+ $winners[] = $winner;
+ $first = false;
+ }
+ return $winners;
+ }
+
+ public function clearGuessing() {
+ $this->guessing_start = null;
+ $this->guessing_end = null;
+ $this->save();
+ }
+
+ public function transformGuess($original) {
+ $transformed = trim($original);
+ if ($this->guessing_type == 'gtbk') {
+ $transformed = str_replace(['roodyo1Gtbigkey'], ['2'], $transformed);
+ $transformed = str_ireplace(['vier 4Head'], ['4'], $transformed);
+ }
+ return $transformed;
+ }
+
+ public function registerGuess($uid, $uname, $guess) {
+ $model = new GuessingGuess();
+ $model->channel()->associate($this);
+ $model->uid = $uid;
+ $model->uname = $uname;
+ $model->guess = $this->transformGuess($guess);
+ $model->save();
+ }
+
+ public function scoreGuessing($solution, $guess, $first) {
+ $transformed = $this->transformGuess($guess);
+ if ($transformed == $solution) {
+ if ($first) {
+ return $this->getGuessingSetting('points_exact_first', 1);
+ }
+ return $this->getGuessingSetting('points_exact_other', 1);
+ }
+ $distance = abs(intval($transformed) - intval($solution));
+ if ($distance <= $this->getGuessingSetting('points_close_max', 3)) {
+ if ($first) {
+ return $this->getGuessingSetting('points_close_first', 1);
+ }
+ return $this->getGuessingSetting('points_close_other', 1);
+ }
+ return 0;
+ }
+
+ public function isValidGuess($solution) {
+ $transformed = $this->transformGuess($solution);
+ if ($this->guessing_type == 'gtbk') {
+ $int_solution = intval($transformed);
+ return is_numeric($transformed) && $int_solution >= 0 && $int_solution <= 23;
+ }
+ return false;
+ }
+
+ public function listGuessingWinners($winners) {
+ $names = [];
+ $distance = 0;
+ foreach ($winners as $winner) {
+ if ($winner->score > 0) {
+ $names[] = $winner->uname;
+ $distance = abs(intval($winner->guess) - intval($winner->solution));
+ }
+ }
+ $msg = '';
+ if (empty($names)) {
+ $msg = $this->getGuessingSetting('no_winners_message');
+ } else {
+ $msg = $this->getGuessingSetting($distance ? 'close_winners_message' : 'winners_message', $this->getGuessingSetting('winners_message'));
+ $msg = str_replace(['{distance}', '{names}'], [$distance, $this->listAnd($names)], $msg);
+ }
+ return $msg;
+ }
+
+ public function listAnd($entries) {
+ $lang = empty($this->languages) ? 'en' : $this->languages[0];
+ if ($lang == 'de') {
+ return Arr::join($entries, ', ', ' und ');
+ }
+ return Arr::join($entries, ', ', ' and ');
+ }
+
+ public function chat_bot_logs() {
+ return $this->hasMany(ChatBotLog::class);
+ }
+
public function crews() {
return $this->hasMany(ChannelCrew::class);
}
->withPivot('accept_comms', 'accept_tracker');
}
+ public function guesses() {
+ return $this->hasMany(GuessingGuess::class);
+ }
+
public function organization() {
return $this->belongsTo(Organization::class);
}
+ public function winners() {
+ return $this->hasMany(GuessingWinner::class);
+ }
+
protected $casts = [
'chat' => 'boolean',
'chat_commands' => 'array',
'chat_settings' => 'array',
+ 'guessing_end' => 'datetime',
+ 'guessing_settings' => 'array',
+ 'guessing_start' => 'datetime',
'languages' => 'array',
'join' => 'boolean',
];
protected $hidden = [
+ 'access_key',
+ 'chat',
+ 'chat_commands',
+ 'chat_settings',
'created_at',
'ext_id',
+ 'guessing_settings',
+ 'join',
+ 'twitch_chat',
'updated_at',
];