diff --git a/bridges/AmazonPriceTrackerBridge.php b/bridges/AmazonPriceTrackerBridge.php index 6de451f1..b07bdb7c 100644 --- a/bridges/AmazonPriceTrackerBridge.php +++ b/bridges/AmazonPriceTrackerBridge.php @@ -125,14 +125,13 @@ class AmazonPriceTrackerBridge extends BridgeAbstract */ private function getImage($html) { + $image = 'https://placekitten.com/200/300'; $imageSrc = $html->find('#main-image-container img', 0); - if ($imageSrc) { $hiresImage = $imageSrc->getAttribute('data-old-hires'); $dynamicImageAttribute = $imageSrc->getAttribute('data-a-dynamic-image'); $image = $hiresImage ?: $this->parseDynamicImage($dynamicImageAttribute); } - $image = $image ?: 'https://placekitten.com/200/300'; return << diff --git a/bridges/EBayBridge.php b/bridges/EBayBridge.php index a867a179..66fad10c 100644 --- a/bridges/EBayBridge.php +++ b/bridges/EBayBridge.php @@ -66,16 +66,27 @@ class EBayBridge extends BridgeAbstract $new_listing_label->remove(); } - $item['title'] = $listing->find('.s-item__title', 0)->plaintext; + $listingTitle = $listing->find('.s-item__title', 0); + if ($listingTitle) { + $item['title'] = $listingTitle->plaintext; + } $subtitle = implode('', $listing->find('.s-item__subtitle')); - $item['uri'] = $listing->find('.s-item__link', 0)->href; + $listingUrl = $listing->find('.s-item__link', 0); + if ($listingUrl) { + $item['uri'] = $listingUrl->href; + } else { + $item['uri'] = null; + } - preg_match('/.*\/itm\/(\d+).*/i', $item['uri'], $matches); - $item['uid'] = $matches[1]; + if (preg_match('/.*\/itm\/(\d+).*/i', $item['uri'], $matches)) { + $item['uid'] = $matches[1]; + } + + $priceDom = $listing->find('.s-item__details > .s-item__detail > .s-item__price', 0); + $price = $priceDom->plaintext ?? 'N/A'; - $price = $listing->find('.s-item__details > .s-item__detail > .s-item__price', 0)->plaintext; $shippingFree = $listing->find('.s-item__details > .s-item__detail > .s-item__freeXDays', 0)->plaintext ?? ''; $localDelivery = $listing->find('.s-item__details > .s-item__detail > .s-item__localDelivery', 0)->plaintext ?? ''; $logisticsCost = $listing->find('.s-item__details > .s-item__detail > .s-item__logisticsCost', 0)->plaintext ?? ''; diff --git a/bridges/FB2Bridge.php b/bridges/FB2Bridge.php index 19030dd2..141ea59b 100644 --- a/bridges/FB2Bridge.php +++ b/bridges/FB2Bridge.php @@ -304,7 +304,11 @@ EOD $regex = '/"pageID":"([0-9]*)"/'; preg_match($regex, $pageContent, $matches); - return ['userId' => $matches[1], 'username' => $username]; + $arr = [ + 'userId' => $matches[1] ?? null, + 'username' => $username, + ]; + return $arr; } public function getName() diff --git a/bridges/FilterBridge.php b/bridges/FilterBridge.php index 61ce6d78..992fe0c3 100644 --- a/bridges/FilterBridge.php +++ b/bridges/FilterBridge.php @@ -80,10 +80,15 @@ class FilterBridge extends FeedExpander // Generate title from first 50 characters of content? if ($this->getInput('title_from_content') && array_key_exists('content', $item)) { $content = str_get_html($item['content']); - $pos = strpos($item['content'], ' ', 50); - $item['title'] = substr($content->plaintext, 0, $pos); - if (strlen($content->plaintext) >= $pos) { - $item['title'] .= '...'; + $plaintext = $content->plaintext; + if (mb_strlen($plaintext) < 51) { + $item['title'] = $plaintext; + } else { + $pos = strpos($item['content'], ' ', 50); + $item['title'] = substr($plaintext, 0, $pos); + if (strlen($plaintext) >= $pos) { + $item['title'] .= '...'; + } } } diff --git a/bridges/GithubIssueBridge.php b/bridges/GithubIssueBridge.php index a75aa252..e4e995e3 100644 --- a/bridges/GithubIssueBridge.php +++ b/bridges/GithubIssueBridge.php @@ -137,7 +137,8 @@ class GithubIssueBridge extends BridgeAbstract { $uri = $this->buildGitHubIssueCommentUri($issueNbr, $comment->id); - $author = $comment->find('.author', 0)->plaintext; + $authorDom = $comment->find('.author', 0); + $author = $authorDom->plaintext ?? null; $header = $comment->find('.timeline-comment-header > h3', 0); $title .= ' / ' . ($header ? $header->plaintext : 'Activity'); diff --git a/bridges/ImgsedBridge.php b/bridges/ImgsedBridge.php index 6c49facb..1555c578 100644 --- a/bridges/ImgsedBridge.php +++ b/bridges/ImgsedBridge.php @@ -212,9 +212,12 @@ HTML, private function parseDate($content) { $date = date_create(); - $relativeDate = date_interval_create_from_date_string(str_replace(' ago', '', $content)); + $dateString = str_replace(' ago', '', $content); + $relativeDate = date_interval_create_from_date_string($dateString); if ($relativeDate) { date_sub($date, $relativeDate); + } else { + Logger::info(sprintf('Unable to parse date string: %s', $dateString)); } return date_format($date, 'r'); } diff --git a/bridges/ReutersBridge.php b/bridges/ReutersBridge.php index 63c838c2..2acadfc3 100644 --- a/bridges/ReutersBridge.php +++ b/bridges/ReutersBridge.php @@ -143,18 +143,6 @@ class ReutersBridge extends BridgeAbstract 'wire' ]; - /** - * Performs an HTTP request to the Reuters API and returns decoded JSON - * in the form of an associative array - * @param string $feed_uri Full API URL to fetch data - * @return array - */ - private function getJson($uri) - { - $returned_data = getContents($uri); - return json_decode($returned_data, true); - } - /** * Takes in data from Reuters Wire API and * creates structured data in the form of a list @@ -295,8 +283,19 @@ class ReutersBridge extends BridgeAbstract { // This will make another request to API to get full detail of article and author's name. $url = $this->getAPIURL($feed_uri, 'article', $is_article_uid); - $rawData = $this->getJson($url); + try { + $json = getContents($url); + $rawData = Json::decode($json); + } catch (\JsonException $e) { + return [ + 'content' => '', + 'author' => '', + 'category' => '', + 'images' => '', + 'published_at' => '' + ]; + } $article_content = ''; $authorlist = ''; $category = []; @@ -494,7 +493,8 @@ EOD; { $endpoint = $this->getSectionEndpoint(); $url = $this->getAPIURL($endpoint, 'section'); - $data = $this->getJson($url); + $json = getContents($url); + $data = Json::decode($json); $stories = []; $section_name = ''; diff --git a/bridges/RoadAndTrackBridge.php b/bridges/RoadAndTrackBridge.php index 31bd7ec8..c236036c 100644 --- a/bridges/RoadAndTrackBridge.php +++ b/bridges/RoadAndTrackBridge.php @@ -50,7 +50,12 @@ class RoadAndTrackBridge extends BridgeAbstract } $item['author'] = $article->find('.byline-name', 0)->innertext ?? ''; - $item['timestamp'] = strtotime($article->find('.content-info-date', 0)->getAttribute('datetime')); + + $contentInfoDate = $article->find('.content-info-date', 0); + if ($contentInfoDate) { + $datetime = $contentInfoDate->getAttribute('datetime'); + $item['timestamp'] = strtotime($datetime); + } $content = $article->find('.content-container', 0); if ($content->find('.content-rail', 0) !== null) { diff --git a/bridges/TikTokBridge.php b/bridges/TikTokBridge.php index e7cac825..556e5ffc 100644 --- a/bridges/TikTokBridge.php +++ b/bridges/TikTokBridge.php @@ -33,8 +33,9 @@ class TikTokBridge extends BridgeAbstract $title = $html->find('h1', 0)->plaintext ?? self::NAME; $this->feedName = htmlspecialchars_decode($title); - $SIGI_STATE_RAW = $html->find('script[id=SIGI_STATE]', 0)->innertext; - $SIGI_STATE = json_decode($SIGI_STATE_RAW); + $var = $html->find('script[id=SIGI_STATE]', 0); + $SIGI_STATE_RAW = $var->innertext; + $SIGI_STATE = Json::decode($SIGI_STATE_RAW, false); foreach ($SIGI_STATE->ItemModule as $key => $value) { $item = []; diff --git a/bridges/VkBridge.php b/bridges/VkBridge.php index c5a9d4cc..967734ef 100644 --- a/bridges/VkBridge.php +++ b/bridges/VkBridge.php @@ -490,18 +490,22 @@ class VkBridge extends BridgeAbstract private function getContents() { - $header = ['Accept-language: en', 'Cookie: remixlang=3']; + $httpHeaders = [ + 'Accept-language: en', + 'Cookie: remixlang=3', + ]; $redirects = 0; $uri = $this->getURI(); while ($redirects < 2) { - $response = getContents($uri, $header, [CURLOPT_FOLLOWLOCATION => false], true); + $response = getContents($uri, $httpHeaders, [CURLOPT_FOLLOWLOCATION => false], true); if (in_array($response['code'], [200, 304])) { return $response['content']; } - $uri = urljoin(self::URI, $response['header']['location'][0]); + $headers = $response['headers']; + $uri = urljoin(self::URI, $headers['location'][0]); if (str_contains($uri, '/429.html')) { returnServerError('VK responded "Too many requests"'); diff --git a/bridges/YouTubeCommunityTabBridge.php b/bridges/YouTubeCommunityTabBridge.php index 32502f61..20822828 100644 --- a/bridges/YouTubeCommunityTabBridge.php +++ b/bridges/YouTubeCommunityTabBridge.php @@ -78,19 +78,27 @@ class YouTubeCommunityTabBridge extends BridgeAbstract returnServerError('Channel does not have a community tab'); } - foreach ($this->getCommunityPosts($json) as $key => $post) { + $posts = $this->getCommunityPosts($json); + foreach ($posts as $key => $post) { $this->itemTitle = ''; if (!isset($post->backstagePostThreadRenderer)) { continue; } - $details = $post->backstagePostThreadRenderer->post->backstagePostRenderer; + if (isset($post->backstagePostThreadRenderer->post->backstagePostRenderer)) { + $details = $post->backstagePostThreadRenderer->post->backstagePostRenderer; + } elseif (isset($post->backstagePostThreadRenderer->post->sharedPostRenderer)) { + // todo: properly extract data from this shared post + $details = $post->backstagePostThreadRenderer->post->sharedPostRenderer; + } else { + continue; + } $item = []; $item['uri'] = self::URI . '/post/' . $details->postId; - $item['author'] = $details->authorText->runs[0]->text; - $item['content'] = ''; + $item['author'] = $details->authorText->runs[0]->text ?? null; + $item['content'] = $item['uri']; if (isset($details->contentText->runs)) { $text = $this->getText($details->contentText->runs); diff --git a/lib/Logger.php b/lib/Logger.php index 91b8e3c1..5423f62c 100644 --- a/lib/Logger.php +++ b/lib/Logger.php @@ -55,6 +55,8 @@ final class Logger 'Unable to find channel. The channel is non-existing or non-public', // fb 'This group is not public! RSS-Bridge only supports public groups!', + 'You must be logged in to view this page', + 'Unable to get the page id. You should consider getting the ID by hand', // tiktok 404 'https://www.tiktok.com/@', ]; diff --git a/lib/TwitterClient.php b/lib/TwitterClient.php index 92c797d6..0c6b9535 100644 --- a/lib/TwitterClient.php +++ b/lib/TwitterClient.php @@ -155,7 +155,7 @@ class TwitterClient throw $e; } } - } else if ($operation == 'By list ID') { + } elseif ($operation === 'By list ID') { $id = $query['listId']; } else { throw new \Exception('Unknown operation to make list tweets'); @@ -348,6 +348,11 @@ class TwitterClient // Grab the first error message throw new \Exception(sprintf('From twitter api: "%s"', $response->errors[0]->message)); } + if (!isset($response->data->user_by_screen_name->list)) { + throw new \Exception( + sprintf('Unable to find list in twitter response for %s, %s', $screenName, $listSlug) + ); + } $listInfo = $response->data->user_by_screen_name->list; $this->data[$screenName . '-' . $listSlug] = $listInfo;