X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=app%2FTwitchBot%2FTwitchBot.php;h=3cba5d958bac4975fd2e3fdf93fd901208ffcc18;hb=cde5d79cf2f09d61fa7b181cd3a1a19050a4aeb3;hp=fd283789b9878187907ef60baed3cc30bc3d350b;hpb=c4835dcd53401c4e618ba077726d655d476a82c8;p=alttp.git diff --git a/app/TwitchBot/TwitchBot.php b/app/TwitchBot/TwitchBot.php index fd28378..3cba5d9 100644 --- a/app/TwitchBot/TwitchBot.php +++ b/app/TwitchBot/TwitchBot.php @@ -1,7 +1,9 @@ logger = new Logger('TwitchBot'); $this->logger->pushHandler(new StreamHandler('php://stdout', Logger::INFO)); - $this->fetchToken(); + $this->token = TwitchToken::firstWhere('nick', 'localhorsttv'); + if (!$this->token) { + throw new \Exception('unable to find access token'); + } $this->connector = new Connector(); $this->connect(); @@ -30,30 +35,18 @@ class TwitchBot { } 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 +59,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 +73,103 @@ 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->debug('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->logger->info('websocket connection closed: '.$reason.' ['.$op.']'); + if (!$this->shutting_down) { + $this->logger->info('reconnecting in 10 seconds'); + Loop::addTimer(10, [$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) { + if ($msg->isPrivMsg()) { + $this->handlePrivMsg($msg); + return; + } + if ($msg->isPing()) { + $this->sendIRCMessage($msg->makePong()); + 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(); + return; + } + } + + public function handlePrivMsg(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->handleChatCommand($channel, $msg); + } + + public function handleChatCommand(Channel $channel, IRCMessage $msg) { + $cmd = explode(' ', ltrim($msg->getText(), '!'), 2); + if (!isset($channel->chat_commands[$cmd[0]])) return; + $config = $channel->chat_commands[$cmd[0]]; + $this->logger->info('got command '.$cmd[0].' on channel '.$channel->title); + try { + $command = ChatCommand::create($this, $channel, $config); + $command->execute($cmd[1] ?? ''); + } catch (\Exception $e) { + $this->logger->warning('error executing command '.$cmd[0].' on channel '.$channel->title.': '.$e->getMessage()); + } + } + + public function login() { + $this->ws->send('PASS oauth:'.$this->token->access); + $this->ws->send('NICK localhorsttv'); + } + + 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)); + } + } + + public function sendIRCMessage(IRCMessage $msg) { + $irc_message = $msg->encode(); + $this->logger->debug('sending IRC message '.$irc_message); + $this->ws->send($irc_message); + } + + private $logger; - private $loop; private $token; private $connector; private $ws; + private $shutting_down = false; }