X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=app%2FTwitchBot%2FTwitchBot.php;h=63f4236f685e92bf659610257e93177a78cf7a11;hb=8645b77ea2dc402f0265e1c8022ba18302506ca1;hp=fd283789b9878187907ef60baed3cc30bc3d350b;hpb=c4835dcd53401c4e618ba077726d655d476a82c8;p=alttp.git diff --git a/app/TwitchBot/TwitchBot.php b/app/TwitchBot/TwitchBot.php index fd28378..63f4236 100644 --- a/app/TwitchBot/TwitchBot.php +++ b/app/TwitchBot/TwitchBot.php @@ -1,7 +1,10 @@ nick = $nick; $this->logger = new Logger('TwitchBot'); $this->logger->pushHandler(new StreamHandler('php://stdout', Logger::INFO)); - $this->fetchToken(); + $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() { @@ -29,31 +40,23 @@ class TwitchBot { return Loop::get(); } + public function isReady() { + return $this->ready; + } + public function run() { + $this->shutting_down = false; $this->getLoop()->run(); } public function stop() { + $this->logger->info('shutting down'); + $this->shutting_down = true; $this->disconnect(); $this->getLoop()->stop(); } - public function fetchToken() { - $this->logger->info('acquiring token'); - $rsp = Http::post('https://id.twitch.tv/oauth2/token', [ - 'client_id' => config('twitch.client_id'), - 'client_secret' => config('twitch.client_secret'), - 'code' => config('twitch.code'), - 'grant_type' => 'authorization_code', - 'redirect_uri' => config('twitch.redirect_uri'), - ]); - var_dump($rsp); - var_dump($rsp->body()); - $this->token = $rsp->json(); - } - - public function connect() { ($this->connector)('wss://irc-ws.chat.twitch.tv:443')->done( [$this, 'handleWsConnect'], @@ -66,14 +69,13 @@ class TwitchBot { } public function handleWsConnect(WebSocket $ws) { - $this->logger->info('websocket connection estblished'); + $this->logger->info('websocket connection established'); $this->ws = $ws; - $ws->on('message', [$this, 'handleWsMessage']); - $ws->on('close', [$this, 'handleWsClose']); - $ws->on('error', [$this, 'handleWsError']); + $ws->on('message', [$this, 'handleWsMessage']); + $ws->on('close', [$this, 'handleWsClose']); + $ws->on('error', [$this, 'handleWsError']); $ws->send('CAP REQ :twitch.tv/tags twitch.tv/commands'); - $ws->send('PASS oauth:'.$this->token->access_token); - $ws->send('NICK localhorsttv'); + $this->login(); } public function handleWsConnectError(WebSocket $ws) { @@ -81,26 +83,121 @@ class TwitchBot { } public function handleWsMessage(Message $message, WebSocket $ws) { - $this->logger->info('websocket message received'); - var_dump($message->getPayload()); + $irc_messages = explode("\r\n", rtrim($message->getPayload(), "\r\n")); + foreach ($irc_messages as $irc_message) { + $this->logger->info('received IRC message '.$irc_message); + $this->handleIRCMessage(IRCMessage::fromString($irc_message)); + } } - public function handleWsClose(int $op, string $reason) { + 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 5 seconds'); + Loop::addTimer(5, [$this, 'connect']); + } } - public function handleWsError(\Exception $e, WebSocket $ws) { + public function handleWsError(\Exception $e, WebSocket $ws) { $this->logger->error('websocket error '.$e->getMessage()); } + 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->isNotice() && $msg->getText() == 'Login authentication failed') { + $this->logger->notice('login failed, refreshing access token'); + $this->token->refresh(); + $this->login(); + return; + } + if ($msg->command == '001') { + // successful login + $this->joinChannels(); + $this->ready = true; + return; + } + } + + public function getMessageChannel(IRCMessage $msg) { + $target = $msg->getPrivMsgTarget(); + 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 login() { + $this->ws->send('PASS oauth:'.$this->token->access); + $this->ws->send('NICK '.$this->nick); + } + + public function joinChannels() { + } + + 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->info('sending IRC message '.$irc_message); + $this->ws->send($irc_message); + $this->last_contact = time(); + } + + + 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 $loop; + private $nick; private $token; private $connector; private $ws; + private $ready = false; + private $shutting_down = false; + + private $last_contact; }