X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=app%2FTwitchBot%2FTwitchBot.php;h=63f4236f685e92bf659610257e93177a78cf7a11;hb=HEAD;hp=3ca2db9283e6651e521fa2fe4aeaa596d94f9e2e;hpb=898d01d4ac5ccaa23621abda0761a893ff8c1074;p=alttp.git diff --git a/app/TwitchBot/TwitchBot.php b/app/TwitchBot/TwitchBot.php index 3ca2db9..242ac92 100644 --- a/app/TwitchBot/TwitchBot.php +++ b/app/TwitchBot/TwitchBot.php @@ -3,6 +3,7 @@ namespace App\TwitchBot; use App\Models\Channel; +use App\Models\TwitchBotCommand; use App\Models\TwitchToken; use Monolog\Handler\StreamHandler; use Monolog\Logger; @@ -13,17 +14,22 @@ use React\EventLoop\Loop; class TwitchBot { - public function __construct() { + public function __construct($nick) { + $this->nick = $nick; $this->logger = new Logger('TwitchBot'); - $this->logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG)); + $this->logger->pushHandler(new StreamHandler('php://stdout', Logger::INFO)); - $this->token = TwitchToken::firstWhere('nick', 'localhorsttv'); + $this->token = TwitchToken::firstWhere('nick', $nick); if (!$this->token) { throw new \Exception('unable to find access token'); } + if ($this->token->hasExpired()) { + $this->token->refresh(); + } $this->connector = new Connector(); $this->connect(); + $this->startPinger(); } public function getLogger() { @@ -34,6 +40,10 @@ class TwitchBot { return Loop::get(); } + public function isReady() { + return $this->ready; + } + public function run() { $this->shutting_down = false; $this->getLoop()->run(); @@ -75,16 +85,17 @@ class TwitchBot { public function handleWsMessage(Message $message, WebSocket $ws) { $irc_messages = explode("\r\n", rtrim($message->getPayload(), "\r\n")); foreach ($irc_messages as $irc_message) { - $this->logger->debug('received IRC message '.$irc_message); + $this->logger->info('received IRC message '.$irc_message); $this->handleIRCMessage(IRCMessage::fromString($irc_message)); } } public function handleWsClose(int $op, string $reason) { + $this->ready = false; $this->logger->info('websocket connection closed: '.$reason.' ['.$op.']'); if (!$this->shutting_down) { - $this->logger->info('reconnecting in 10 seconds'); - Loop::addTimer(10, [$this, 'connect']); + $this->logger->info('reconnecting in 5 seconds'); + Loop::addTimer(5, [$this, 'connect']); } } @@ -94,12 +105,21 @@ class TwitchBot { public function handleIRCMessage(IRCMessage $msg) { + $this->last_contact = time(); + if ($msg->isPing()) { + $this->sendIRCMessage($msg->makePong()); + return; + } + if ($msg->isPong()) { + return; + } + $this->logMessage($msg); if ($msg->isPrivMsg()) { $this->handlePrivMsg($msg); return; } - if ($msg->isPing()) { - $this->sendIRCMessage($msg->makePong()); + if ($msg->isWhisper()) { + $this->handleWhisper($msg); return; } if ($msg->isNotice() && $msg->getText() == 'Login authentication failed') { @@ -111,53 +131,107 @@ class TwitchBot { if ($msg->command == '001') { // successful login $this->joinChannels(); + $this->ready = true; + return; + } + if ($msg->command == 'GLOBALUSERSTATE') { + // receive own user metadata + $this->handleUserState($msg); return; } } - public function handlePrivMsg(IRCMessage $msg) { + public function getMessageChannel(IRCMessage $msg) { $target = $msg->getPrivMsgTarget(); - if ($target[0] != '#') return; - $text = $msg->getText(); - if ($text[0] != '!') return; - $channel = Channel::firstWhere('twitch_chat', '=', $target); - if (!$channel) return; - $this->logger->info('got command '.$text.' on channel '.$channel->title); + if (substr($target, 0, 1) !== '#') { + $target = '#'.$target; + } + return Channel::firstWhere('twitch_chat', '=', $target); + } + + public function logMessage(IRCMessage $msg) { + } + + public function handlePrivMsg(IRCMessage $msg) { + } + + public function handleUserState(IRCMessage $msg) { + if (isset($msg->tags['user-id'])) { + $this->user_id = $msg->tags['user-id']; + } + } + + public function handleWhisper(IRCMessage $msg) { } public function login() { $this->ws->send('PASS oauth:'.$this->token->access); - $this->ws->send('NICK localhorsttv'); + $this->ws->send('NICK '.$this->nick); } public function joinChannels() { - $this->logger->info('joining channels'); - $channels = Channel::where('twitch_chat', '!=', '')->get(); - $names = []; - foreach ($channels as $channel) { - $names[] = $channel->twitch_chat; - } - $chunks = array_chunk($names, 10); - foreach ($chunks as $chunk) { - $this->sendIRCMessage(IRCMessage::join($chunk)); - } + } + + private function startPinger() { + $this->getLoop()->addPeriodicTimer(15, function () { + if (!$this->ready) return; + if (time() - $this->last_contact < 60) return; + try { + $this->sendIRCMessage(IRCMessage::ping($this->nick)); + } catch (\Exception $e) { + } + }); } public function sendIRCMessage(IRCMessage $msg) { $irc_message = $msg->encode(); - $this->logger->debug('sending IRC message '.$irc_message); + $this->logger->info('sending IRC message '.$irc_message); $this->ws->send($irc_message); + $this->last_contact = time(); + } + + public function sendWhisper($to, $msg) { + $this->logger->info('sending whisper to '.$to.': '.$msg); + try { + $response = $this->token->request()->post('/whispers?from_user_id='.$this->user_id.'&to_user_id='.$to, [ + 'message' => $msg, + ]); + if (!$response->successful()) { + $this->logger->error('sending whisper to '.$to.': '.$response->status()); + } + } catch (\Exception $e) { + $this->logger->error('sending whisper to '.$to.': '.$e->getMessage()); + } + } + + + protected function listenCommands() { + $this->getLoop()->addPeriodicTimer(1, function () { + if (!$this->isReady()) return; + $command = TwitchBotCommand::where('bot_nick', '=', $this->nick)->where('status', '=', 'pending')->oldest()->first(); + if ($command) { + try { + $command->execute($this); + } catch (\Exception $e) { + } + } + }); } private $logger; + private $nick; private $token; + private $user_id = ''; private $connector; private $ws; + private $ready = false; private $shutting_down = false; + private $last_contact; + } ?>