X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=app%2FTwitchBot%2FTwitchChatBot.php;h=90a718d25b36875591e44b7ea9baf8d33fba69f6;hb=ebdf8e5f6761de2abd85b01096a67dee62d7d4aa;hp=576bf04a9dfdcf8b938598b3d129f8b3f2869c8b;hpb=cce68689529251915af11ade10699ffa74cb6a3b;p=alttp.git diff --git a/app/TwitchBot/TwitchChatBot.php b/app/TwitchBot/TwitchChatBot.php index 576bf04..90a718d 100644 --- a/app/TwitchBot/TwitchChatBot.php +++ b/app/TwitchBot/TwitchChatBot.php @@ -3,23 +3,18 @@ namespace App\TwitchBot; use App\Models\Channel; +use App\Models\ChatBotLog; use App\Models\ChatLog; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; class TwitchChatBot extends TwitchBot { public function __construct() { parent::__construct('horstiebot'); - $this->channels = Channel::where('twitch_chat', '!=', '')->where('chat', '=', true)->get(); - foreach ($this->channels as $channel) { - $this->notes[$channel->id] = [ - 'last_read' => 0, - 'last_write' => time(), - 'read_since_last_write' => 0, - 'wait_msgs' => $this->randomWaitMsgs($channel), - 'wait_time' => $this->randomWaitTime($channel), - ]; - } + $this->updateChannels(); $this->startTimer(); + $this->listenCommands(); } public function joinChannels() { @@ -45,7 +40,7 @@ class TwitchChatBot extends TwitchBot { if ($msg->nick == 'horstiebot') return; $channel = $this->getMessageChannel($msg); if (!$channel) return; - $this->tagChannelRead($channel); + $this->tagChannelRead($channel, $msg); } @@ -56,10 +51,17 @@ class TwitchChatBot extends TwitchBot { $this->decideSend($channel); } }); + $this->getLoop()->addPeriodicTimer(60, function () { + $this->updateChannels(); + }); + } + + private function updateChannels() { + $this->channels = Channel::where('twitch_chat', '!=', '')->where('chat', '=', true)->get(); } private function decideSend(Channel $channel) { - $notes = $this->notes[$channel->id]; + $notes = $this->getNotes($channel); if ($notes['read_since_last_write'] < $notes['wait_msgs']) { return; } @@ -70,45 +72,232 @@ class TwitchChatBot extends TwitchBot { // don't immediately respond if we crossed the msg threshold last return; } - $text = $this->randomMsg($channel); + $text = $this->contextualMsg($channel); + if (!$text) $text = $this->randomChat($channel); if (!$text) return; + $actual_text = is_object($text) ? $text->text_content : $text; $this->tagChannelWrite($channel); - $this->sendIRCMessage(IRCMessage::privmsg($channel->twitch_chat, $text)); + $this->sendIRCMessage(IRCMessage::privmsg($channel->twitch_chat, $actual_text)); + $log = new ChatBotLog(); + $log->channel()->associate($channel); + if (is_object($text)) { + $log->origin()->associate($text); + } + $log->text = $actual_text; + $log->save(); } - private function randomMsg(Channel $channel) { - $line = ChatLog::where('type', '=', 'chat') - ->where('banned', '=', false) - ->where('created_at', '<', now()->sub(1, 'day')) - ->where(function ($query) use ($channel) { - $query->whereNull('detected_language'); - $query->orWhereIn('detected_language', $channel->languages); - }) - ->inRandomOrder() + private function getNotes(Channel $channel) { + if (!isset($this->notes[$channel->id])) { + $this->notes[$channel->id] = [ + 'last_read' => 0, + 'last_special' => [], + 'last_write' => time(), + 'latest_msgs' => [], + 'read_since_last_write' => 0, + 'wait_msgs' => $this->randomWaitMsgs($channel), + 'wait_time' => $this->randomWaitTime($channel), + ]; + } + return $this->notes[$channel->id]; + } + + private function getNote(Channel $channel, $name, $default = null) { + $notes = $this->getNotes($channel); + if (array_key_exists($name, $notes)) { + return $notes[$name]; + } + return $default; + } + + private function setNote(Channel $channel, $name, $value) { + $this->getNotes($channel); + $this->notes[$channel->id][$name] = $value; + } + + private function collectClassifications(Channel $channel) { + $classifications = []; + $notes = $this->getNotes($channel); + foreach ($notes['latest_msgs'] as $msg) { + $classification = $msg->classify(); + if ($classification == 'unclassified') continue; + if (isset($classifications[$classification])) { + ++$classifications[$classification]; + } else { + $classifications[$classification] = 1; + } + } + arsort($classifications); + return $classifications; + } + + private function contextualMsg(Channel $channel) { + $last = $this->getNote($channel, 'last_special'); + $classifications = $this->collectClassifications($channel); + $count_quotas = [ + 'gg' => 2, + 'gl' => 2, + 'hi' => 2, + 'hype' => 2, + 'lol' => 2, + 'number' => 2, + 'pog' => 2, + 'o7' => 2, + ]; + $time_quotas = [ + 'gg' => 300, + 'gl' => 900, + 'hi' => 60, + 'hype' => 60, + 'lol' => 60, + 'number' => 300, + 'pog' => 60, + 'o7' => 300, + ]; + foreach ($classifications as $classification => $count) { + if ($classification == $last) continue; + if (!isset($count_quotas[$classification]) || $count < $count_quotas[$classification]) continue; + if (!isset($time_quotas[$classification]) || $this->getTimeSinceSpecial($channel, $classification) < $time_quotas[$classification]) continue; + $this->tagChannelSpecialSent($channel, $classification); + if ($classification == 'number') { + return $this->randomContextualNumber($channel); + } + if ($classification == 'lol') { + return $this->randomLaughter($channel); + } + return $channel->randomOfClass($classification); + } + return false; + } + + private function randomChat(Channel $channel) { + return $channel->queryChatlog() + ->whereIn('classification', ['hi', 'hype', 'lol', 'pog', 'unclassified']) ->first(); - return $line->text_content; + } + + private function randomContextualNumber(Channel $channel) { + $notes = $this->getNotes($channel); + $min = 100000; + $max = 0; + foreach ($notes['latest_msgs'] as $msg) { + if ($msg->classify() == 'number') { + $number = $msg->getNumericValue(); + $min = min($min, $number); + $max = max($max, $number); + } + } + return random_int($min, $max); + } + + private function randomLaughter(Channel $channel) { + return Arr::random([ + ':tf:', + '4Head', + 'CarlSmile', + 'CruW', + 'DendiFace', + 'EleGiggle', + 'GunRun', + 'heh', + 'Hhhehehe', + 'HypeLUL', + 'Jebaited', + 'Jebasted', + 'KEKW', + 'KEKHeim', + 'KKona', + 'KomodoHype', + 'MaxLOL', + 'MingLee', + 'lol', + 'LOL!', + 'LUL', + 'OneHand', + 'SeemsGood', + 'ShadyLulu', + 'SoonerLater', + 'SUBprise', + 'xD', + 'YouDontSay', + $channel->randomOfClass('lol'), + ]); + } + + private function randomMsg(Channel $channel) { + return $channel->queryChatlog()->first(); } private function randomWaitMsgs(Channel $channel) { - return random_int(1, 10); + $min = $channel->getChatSetting('wait_msgs_min', 1); + $max = $channel->getChatSetting('wait_msgs_max', 10); + return random_int($min, $max); } private function randomWaitTime(Channel $channel) { - return random_int(1, 900); + $min = $channel->getChatSetting('wait_time_min', 1); + $max = $channel->getChatSetting('wait_time_max', 900); + return random_int($min, $max); } - private function tagChannelRead(Channel $channel) { + private function tagChannelRead(Channel $channel, IRCMessage $msg) { + $this->getNotes($channel); $this->notes[$channel->id]['last_read'] = time(); ++$this->notes[$channel->id]['read_since_last_write']; + + $tokenized = $msg->tokenize(); + if (!ChatLog::isKnownBot($msg->nick) && !$tokenized->isSpammy()) { + $this->notes[$channel->id]['latest_msgs'][] = $tokenized; + if (count($this->notes[$channel->id]['latest_msgs']) > 10) { + array_shift($this->notes[$channel->id]['latest_msgs']); + } + } + if ($this->isDirectedAtMe($msg->getText()) && $this->shouldRespond($channel)) { + $this->notes[$channel->id]['wait_msgs'] = 0; + $this->notes[$channel->id]['wait_time'] = 0; + } } private function tagChannelWrite(Channel $channel) { + $this->getNotes($channel); $this->notes[$channel->id]['last_write'] = time(); $this->notes[$channel->id]['read_since_last_write'] = 0; $this->notes[$channel->id]['wait_msgs'] = $this->randomWaitMsgs($channel); $this->notes[$channel->id]['wait_time'] = $this->randomWaitTime($channel); } + private function tagChannelSpecialSent(Channel $channel, $classification) { + $this->getNotes($channel); + $this->notes[$channel->id]['last_special'][$classification] = time(); + } + + private function getTimeSinceSpecial(Channel $channel, $classification) { + $notes = $this->getNotes($channel); + if (isset($notes['last_special'][$classification])) { + return time() - $notes['last_special'][$classification]; + } + return 999999; + } + + private function isDirectedAtMe($raw_text) { + $text = strtolower($raw_text); + if (strpos($text, 'horsti') !== false) { + return true; + } + return false; + } + + private function shouldRespond(Channel $channel) { + $setting = $channel->getChatSetting('respond', 'yes'); + if ($setting == 'yes') { + return true; + } + if ($setting == '50') { + return random_int(0, 1); + } + return false; + } + private $channels; private $notes = [];