getUpdate()->getCallbackQuery(); $callbackData = json_decode($callbackQuery->getData(), true); $response = match ($callbackData['action'] ?? null) { 'REFRESH' => $this->handleRefresh($callbackQuery, $callbackData), 'FXVX' => $this->handleSwapFxVx($callbackQuery, $callbackData), default => $this->handleDelete($callbackQuery, $callbackData), }; if ($response !== null) { return $response; } } catch (\Exception $e) { } return Request::emptyResponse(); } private function handleDelete(CallbackQuery $callbackQuery, array $callbackData): ?ServerResponse { $originalSenderId = $callbackData['sender_id']; $callbackSenderId = $callbackQuery->getFrom()?->getId(); if ( $originalSenderId === $callbackSenderId ) { return Request::deleteMessage([ 'chat_id' => $callbackQuery->getMessage()->getChat()->getId(), 'message_id' => $callbackQuery->getMessage()->getMessageId(), ]); } else { // Only possible if the user already has an DM channel open with the bot // but better than nothing return Request::sendMessage([ 'chat_id' => $callbackSenderId, 'text' => 'Only the person sending the original message can delete the reply.', ]); } return null; } private function handleSwapFxVx(CallbackQuery $callbackQuery, array $callbackData): ?ServerResponse { if (!$callbackQuery->getMessage()?->getText()) { return null; } $link = $callbackQuery->getMessage()->getText(); $count = 1; if (str_starts_with($link, 'https://vx')) { $link = str_replace('https://vx', 'https://fx', $link, $count); } elseif (str_starts_with($link, 'https://fx')) { $link = str_replace('https://fx', 'https://vx', $link, $count); } return Request::editMessageText([ 'chat_id' => $callbackQuery->getMessage()->getChat()->getId(), 'message_id' => $callbackQuery->getMessage()->getMessageId(), 'text'=> $link, 'reply_markup' => $callbackQuery->getMessage()->getReplyMarkup(), ]); } private function handleRefresh(CallbackQuery $callbackQuery, array $callbackData): ?ServerResponse { if (!$callbackQuery->getMessage()?->getText()) { return null; } $attemptLimit = 3; $query = '?tmfx'; preg_match('/status\/\d+(?:\/photo\/(\d))?(?:\?tmfx(\d))?/', $callbackQuery->getMessage()->getText(), $matches); $currentAttempt = empty($matches[2]) ? 1 : intval($matches[2])+1; $photoNo = $matches[1] ?? null; // The proper way to refresh to a URL is to talk to @WebpageBot // A workaround is to just add a query string // But VXTwitter check for /photo/1, /photo/2, ... by looking at URI (including query string) ending with /1, /2 // To maintain compatibility with both fx/vx, we will use https://vxtwitter.com/aaa/status/1234567890/photo/3?tmfx1/3 $newUrl = explode($query, $callbackQuery->getMessage()->getText())[0]; $newUrl = explode('/photo/', $newUrl)[0]; if ($photoNo) { $newUrl .= '/photo/' . $photoNo; } $newUrl .= $query . $currentAttempt; if ($photoNo) { $newUrl .= '/' . $photoNo; } $keyboard = $callbackQuery->getMessage()->getReplyMarkup(); if ($currentAttempt >= $attemptLimit) { $keyboard = new InlineKeyboard(array_filter( $callbackQuery->getMessage()->getReplyMarkup()->getProperty('inline_keyboard')[0], static fn ($x) => $x->getProperty('text') !== '🔄 Refresh', )); } return Request::editMessageText([ 'chat_id' => $callbackQuery->getMessage()->getChat()->getId(), 'message_id' => $callbackQuery->getMessage()->getMessageId(), 'text'=> $newUrl, 'reply_markup' => $keyboard, ]); } }