' . $headline->plaintext . '
' . $content; + } + + $item = []; + $item['uri'] = $article_uri; + $item['title'] = $title; + $item['author'] = $author; + $item['content'] = $content; - $item = []; - $item['uri'] = $article_uri; - $item['title'] = $article_title; - $item['author'] = $article_author; - $item['timestamp'] = $article_timestamp; - $item['enclosures'] = [$article_thumbnail]; - $item['content'] = $article_content; - $this->items[] = $item; + if (!is_null($date)) { + $item['timestamp'] = $date; } + + if (!is_null($enclosure)) { + $item['enclosures'] = [$enclosure]; + } + + $this->items[] = $item; } } } diff --git a/bridges/CarThrottleBridge.php b/bridges/CarThrottleBridge.php index 913b686caec..70d7b54e140 100644 --- a/bridges/CarThrottleBridge.php +++ b/bridges/CarThrottleBridge.php @@ -9,8 +9,7 @@ class CarThrottleBridge extends BridgeAbstract public function collectData() { - $news = getSimpleHTMLDOMCached(self::URI . 'news') - or returnServerError('could not retrieve page'); + $news = getSimpleHTMLDOMCached(self::URI . 'news'); $this->items[] = []; @@ -22,8 +21,7 @@ public function collectData() $item['uri'] = self::URI . $titleElement->getAttribute('href'); $item['title'] = $titleElement->innertext; - $articlePage = getSimpleHTMLDOMCached($item['uri']) - or returnServerError('could not retrieve page'); + $articlePage = getSimpleHTMLDOMCached($item['uri']); $authorDiv = $articlePage->find('div.author div'); if ($authorDiv) { diff --git a/bridges/CeskaTelevizeBridge.php b/bridges/CeskaTelevizeBridge.php index 003cd4c76f0..be00d6640e7 100644 --- a/bridges/CeskaTelevizeBridge.php +++ b/bridges/CeskaTelevizeBridge.php @@ -57,9 +57,9 @@ public function collectData() $this->feedName .= " ({$category})"; } - foreach ($html->find('#episodeListSection a[data-testid=next-link]') as $element) { + foreach ($html->find('#episodeListSection a[data-testid=card]') as $element) { $itemTitle = $element->find('h3', 0); - $itemContent = $element->find('div[class^=content-]', 0); + $itemContent = $element->find('p[class^=content-]', 0); $itemDate = $element->find('div[class^=playTime-] span', 0); $itemThumbnail = $element->find('img', 0); $itemUri = self::URI . $element->getAttribute('href'); diff --git a/bridges/CodebergBridge.php b/bridges/CodebergBridge.php index 2a450477340..79dd706cdd9 100644 --- a/bridges/CodebergBridge.php +++ b/bridges/CodebergBridge.php @@ -79,9 +79,9 @@ class CodebergBridge extends BridgeAbstract public function collectData() { - $html = getSimpleHTMLDOM($this->getURI()); - - $html = defaultLinkTo($html, $this->getURI()); + $url = $this->getURI(); + $html = getSimpleHTMLDOM($url); + $html = defaultLinkTo($html, $url); switch ($this->queriedContext) { case 'Commits': @@ -205,22 +205,22 @@ private function extractCommits($html) */ private function extractIssues($html) { - $div = $html->find('div.issue.list', 0); + $issueList = $html->find('div#issue-list', 0); - foreach ($div->find('li.item') as $li) { + foreach ($issueList->find('div.flex-item') as $div) { $item = []; - $number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext); + $number = trim($div->find('a.index,ml-0.mr-2', 0)->plaintext); - $item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')'; - $item['uri'] = $li->find('a.title', 0)->href; + $item['title'] = $div->find('a.issue-title', 0)->plaintext . ' (' . $number . ')'; + $item['uri'] = $div->find('a.issue-title', 0)->href; - $time = $li->find('relative-time.time-since', 0); + $time = $div->find('relative-time.time-since', 0); if ($time) { $item['timestamp'] = $time->datetime; } - $item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext; + //$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext; // Fetch issue page $issuePage = getSimpleHTMLDOMCached($item['uri'], 3600); @@ -228,7 +228,7 @@ private function extractIssues($html) $item['content'] = $issuePage->find('div.timeline-item.comment.first', 0)->find('div.render-content.markup', 0); - foreach ($li->find('a.ui.label') as $label) { + foreach ($div->find('a.ui.label') as $label) { $item['categories'][] = $label->plaintext; } diff --git a/bridges/CssSelectorBridge.php b/bridges/CssSelectorBridge.php index f6ab8d15588..8fba52858ef 100644 --- a/bridges/CssSelectorBridge.php +++ b/bridges/CssSelectorBridge.php @@ -336,9 +336,11 @@ protected function entryHtmlRetrieveMetadata($entry_html) ], 'timestamp' => [ 'article:published_time', + 'og:article:published_time', 'releaseDate', 'releasedate', 'article:modified_time', + 'og:article:modified_time', 'lastModified', 'lastmodified' ], @@ -351,8 +353,9 @@ protected function entryHtmlRetrieveMetadata($entry_html) 'thumbnailimg' ], 'author' => [ - 'author', 'article:author', + 'og:article:author', + 'author', 'article:author:username', 'profile:first_name', 'profile:last_name', diff --git a/bridges/DagensNyheterDirektBridge.php b/bridges/DagensNyheterDirektBridge.php new file mode 100644 index 00000000000..4d1629fbbd5 --- /dev/null +++ b/bridges/DagensNyheterDirektBridge.php @@ -0,0 +1,62 @@ +find('article') as $element) { + $link = $element->find('button', 0)->getAttribute('data-link'); + $datetime = $element->getAttribute('data-publication-time'); + $url = self::BASEURL . $link; + $title = $element->find('h2', 0)->plaintext; + $author = $element->find('div.ds-byline__titles', 0)->plaintext; + // Debug::log($link); + // Debug::log($datetime); + // Debug::log($title); + // Debug::log($url); + // Debug::log($author); + + $article_content = $element->find('div.direkt-post__content', 0); + $article_html = ''; + + $figure = $element->find('figure', 0); + + if ($figure) { + $article_html = $figure->find('img', 0) . '
' . $figure->find('figcaption', 0) . '
'; + } + + foreach ($article_content->find('p') as $p) { + $article_html = $article_html . $p; + } + + $this->items[] = [ + 'uri' => $url, + 'title' => $title, + 'author' => trim($author), + 'timestamp' => $datetime, + 'content' => trim($article_html), + ]; + + if (count($this->items) > self::LIMIT) { + break; + } + } + } +} diff --git a/bridges/DealabsBridge.php b/bridges/DealabsBridge.php index a904c3ff495..4d39502ca9a 100644 --- a/bridges/DealabsBridge.php +++ b/bridges/DealabsBridge.php @@ -1910,6 +1910,7 @@ class DealabsBridge extends PepperBridgeAbstract 'context-talk' => 'Surveillance Discussion', 'uri-group' => 'groupe/', 'uri-deal' => 'bons-plans/', + 'uri-merchant' => 'search/bons-plans?merchant-id=', 'request-error' => 'Impossible de joindre Dealabs', 'thread-error' => 'Impossible de déterminer l\'ID de la discussion. Vérifiez l\'URL que vous avez entré', 'no-results' => 'Il n'y a rien à afficher pour le moment :(', diff --git a/bridges/DemosBerlinBridge.php b/bridges/DemosBerlinBridge.php new file mode 100644 index 00000000000..05fd2335d45 --- /dev/null +++ b/bridges/DemosBerlinBridge.php @@ -0,0 +1,62 @@ + [ + 'name' => 'Tage', + 'type' => 'number', + 'title' => 'Einträge für die nächsten Tage zurückgeben', + 'required' => true, + 'defaultValue' => 7, + ] + ]]; + + public function getIcon() + { + return 'https://www.berlin.de/i9f/r1/images/favicon/favicon.ico'; + } + + public function collectData() + { + $json = getContents('https://www.berlin.de/polizei/service/versammlungsbehoerde/versammlungen-aufzuege/index.php/index/all.json'); + $jsonFile = json_decode($json, true); + + $daysInterval = DateInterval::createFromDateString($this->getInput('days') . ' day'); + $maxTargetDate = date_add(new DateTime('now'), $daysInterval); + + foreach ($jsonFile['index'] as $entry) { + $entryDay = implode('-', array_reverse(explode('.', $entry['datum']))); // dd.mm.yyyy to yyyy-mm-dd + $ts = (new DateTime())->setTimestamp(strtotime($entryDay)); + if ($ts <= $maxTargetDate) { + $item = []; + $item['uri'] = 'https://www.berlin.de/polizei/service/versammlungsbehoerde/versammlungen-aufzuege/index.php/detail/' . $entry['id']; + $item['timestamp'] = $entryDay . ' ' . $entry['von']; + $item['title'] = $entry['thema']; + $location = $entry['strasse_nr'] . ' ' . $entry['plz']; + $locationQuery = http_build_query(['query' => $location]); + $item['content'] = <<{$entry['thema']} +📅
+ + 📍 {$location} + +{$entry['aufzugsstrecke']}
+ HTML; + $item['uid'] = $this->getSanitizedHash($entry['datum'] . '-' . $entry['von'] . '-' . $entry['bis'] . '-' . $entry['thema']); + + $this->items[] = $item; + } + } + } + + private function getSanitizedHash($string) + { + return hash('sha1', preg_replace('/[^a-zA-Z0-9]/', '', strtolower($string))); + } +} diff --git a/bridges/EZTVBridge.php b/bridges/EZTVBridge.php index 73318f0c713..25a88124266 100644 --- a/bridges/EZTVBridge.php +++ b/bridges/EZTVBridge.php @@ -96,7 +96,7 @@ protected function getEztvUri() protected function getItemFromTorrent($torrent) { $item = []; - $item['uri'] = $torrent->episode_url; + $item['uri'] = $torrent->episode_url ?? $torrent->torrent_url; $item['author'] = $torrent->imdb_id; $item['timestamp'] = $torrent->date_released_unix; $item['title'] = $torrent->title; diff --git a/bridges/EdfPricesBridge.php b/bridges/EdfPricesBridge.php new file mode 100644 index 00000000000..f67ed30b1c7 --- /dev/null +++ b/bridges/EdfPricesBridge.php @@ -0,0 +1,106 @@ + [ + 'name' => 'Choisir un contrat', + 'type' => 'list', + // we can add later HCHP, EJP, base + 'values' => ['Tempo' => '/energie/edf/tarifs/tempo'], + ] + ] + ]; + const CACHE_TIMEOUT = 7200; // 2h + + /** + * @param simple_html_dom $html + * @param string $contractUri + * @return void + */ + private function tempo(simple_html_dom $html, string $contractUri): void + { + // current color and next + $daysDom = $html->find('#calendrier', 0)->nextSibling()->find('.card--ejp'); + if ($daysDom && count($daysDom) === 2) { + foreach ($daysDom as $dayDom) { + $day = trim($dayDom->find('.card__title', 0)->innertext) . '/' . (new \DateTime('now'))->format(('Y')); + $dayColor = $dayDom->find('.card-ejp__icon span', 0)->innertext; + + $text = $day . ' - ' . $dayColor; + $item['uri'] = self::URI . $contractUri; + $item['title'] = $text; + $item['author'] = self::MAINTAINER; + $item['content'] = $text; + $item['uid'] = hash('sha256', $item['title']); + + $this->items[] = $item; + } + } + + // colors + $ulDom = $html->find('#tarif-de-l-offre-edf-tempo-current-date-html-year', 0)->nextSibling()->nextSibling()->nextSibling(); + $elementsDom = $ulDom->find('li'); + if ($elementsDom && count($elementsDom) === 3) { + foreach ($elementsDom as $elementDom) { + $item = []; + + $matches = []; + preg_match_all('/Jour (.*) : Heures (.*) : (.*) € \/ Heures (.*) : (.*) €/um', $elementDom->innertext, $matches, PREG_SET_ORDER, 0); + + if ($matches && count($matches[0]) === 6) { + for ($i = 0; $i < 2; $i++) { + $text = 'Jour ' . $matches[0][1] . ' - Heures ' . $matches[0][2 + 2 * $i] . ' : ' . $matches[0][3 + 2 * $i] . '€'; + $item['uri'] = self::URI . $contractUri; + $item['title'] = $text; + $item['author'] = self::MAINTAINER; + $item['content'] = $text; + $item['uid'] = hash('sha256', $item['title']); + + $this->items[] = $item; + } + } + } + } + + // powers + $ulPowerContract = $ulDom->nextSibling()->nextSibling(); + $elementsPowerContractDom = $ulPowerContract->find('li'); + if ($elementsPowerContractDom && count($elementsPowerContractDom) === 4) { + foreach ($elementsPowerContractDom as $elementPowerContractDom) { + $item = []; + + $matches = []; + preg_match_all('/(.*) kVA : (.*) €/um', $elementPowerContractDom->innertext, $matches, PREG_SET_ORDER, 0); + + if ($matches && count($matches[0]) === 3) { + $text = $matches[0][1] . ' kVA : ' . $matches[0][2] . '€'; + $item['uri'] = self::URI . $contractUri; + $item['title'] = $text; + $item['author'] = self::MAINTAINER; + $item['content'] = $text; + $item['uid'] = hash('sha256', $item['title']); + + $this->items[] = $item; + } + } + } + } + + public function collectData() + { + $contract = $this->getKey('contract'); + $contractUri = $this->getInput('contract'); + $html = getSimpleHTMLDOM(self::URI . $contractUri); + + if ($contract === 'Tempo') { + $this->tempo($html, $contractUri); + } + } +} diff --git a/bridges/FarsideNitterBridge.php b/bridges/FarsideNitterBridge.php new file mode 100644 index 00000000000..b167347acf8 --- /dev/null +++ b/bridges/FarsideNitterBridge.php @@ -0,0 +1,103 @@ + [ + 'name' => 'username', + 'required' => true, + 'exampleValue' => 'NASA' + ], + 'noreply' => [ + 'name' => 'Without replies', + 'type' => 'checkbox', + 'title' => 'Only return initial tweets' + ], + 'noretweet' => [ + 'name' => 'Without retweets', + 'required' => false, + 'type' => 'checkbox', + 'title' => 'Hide retweets' + ], + 'linkbacktotwitter' => [ + 'name' => 'Link back to twitter', + 'required' => false, + 'type' => 'checkbox', + 'title' => 'Rewrite links back to twitter.com' + ] + ], + ]; + + public function detectParameters($url) + { + if (preg_match('/^(https?:\/\/)?(www\.)?(nitter\.net|twitter\.com)\/([^\/?\n]+)/', $url, $matches) > 0) { + return [ + 'username' => $matches[4], + 'noreply' => true, + 'noretweet' => true, + 'linkbacktotwitter' => true + ]; + } + return null; + } + + public function collectData() + { + $this->getRSS(); + } + + private function getRSS($attempt = 0) + { + try { + $this->collectExpandableDatas(self::URI . $this->getInput('username') . '/rss'); + } catch (\Exception $e) { + if ($attempt >= self::MAX_RETRIES) { + throw $e; + } else { + $this->getRSS($attempt++); + } + } + } + + protected function parseItem(array $item) + { + if ($this->getInput('noreply') && substr($item['title'], 0, 5) == 'R to ') { + return; + } + if ($this->getInput('noretweet') && substr($item['title'], 0, 6) == 'RT by ') { + return; + } + $item['title'] = truncate($item['title']); + if (preg_match('/(\/status\/.+)/', $item['uri'], $matches) > 0) { + if ($this->getInput('linkbacktotwitter')) { + $item['uri'] = self::HOST . $this->getInput('username') . $matches[1]; + } else { + $item['uri'] = self::URI . $this->getInput('username') . $matches[1]; + } + } + return $item; + } + + public function getName() + { + if (preg_match('/(.+) \//', parent::getName(), $matches) > 0) { + return $matches[1]; + } + return parent::getName(); + } + + public function getURI() + { + if ($this->getInput('linkbacktotwitter')) { + return self::HOST . $this->getInput('username'); + } else { + return self::URI . $this->getInput('username'); + } + } +} diff --git a/bridges/FreeTelechargerBridge.php b/bridges/FreeTelechargerBridge.php index 8362b4ff74c..f0e5d35a5bb 100644 --- a/bridges/FreeTelechargerBridge.php +++ b/bridges/FreeTelechargerBridge.php @@ -3,7 +3,7 @@ class FreeTelechargerBridge extends BridgeAbstract { const NAME = 'Free-Telecharger'; - const URI = 'https://www.free-telecharger.live/'; + const URI = 'https://www.free-telecharger.art/'; const DESCRIPTION = 'Suivi de série sur Free-Telecharger'; const MAINTAINER = 'sysadminstory'; const PARAMETERS = [ @@ -12,43 +12,46 @@ class FreeTelechargerBridge extends BridgeAbstract 'name' => 'URL de la série', 'type' => 'text', 'required' => true, - 'title' => 'URL d\'une série sans le https://www.free-telecharger.live/', + 'title' => 'URL d\'une série sans le https://www.free-telecharger.art/', 'pattern' => 'series.*\.html', - 'exampleValue' => 'series-vf-hd/145458-the-last-of-us-saison-1-web-dl-720p.html' + 'exampleValue' => 'series-vf-hd/151432-wolf-saison-1-complete-web-dl-720p.html' ], ] ]; const CACHE_TIMEOUT = 3600; + private string $showTitle; + private string $showTechDetails; + public function collectData() { - $html = getSimpleHTMLDOM(self::URI . $this->getInput('url')); + $html = getSimpleHTMLDOM(self::URI . $this->getInput('url')); - // Find all block content of the page - $blocks = $html->find('div[class=block1]'); + // Find all block content of the page + $blocks = $html->find('div[class=block1]'); - // Global Infos block - $infosBlock = $blocks[0]; - // Links block - $linksBlock = $blocks[2]; + // Global Infos block + $infosBlock = $blocks[0]; + // Links block + $linksBlock = $blocks[2]; - // Extract Global Show infos - $this->showTitle = trim($infosBlock->find('div[class=titre1]', 0)->find('font', 0)->plaintext); - $this->showTechDetails = trim($infosBlock->find('div[align=center]', 0)->find('b', 0)->plaintext); + // Extract Global Show infos + $this->showTitle = trim($infosBlock->find('div[class=titre1]', 0)->find('font', 0)->plaintext); + $this->showTechDetails = trim($infosBlock->find('div[align=center]', 0)->find('b', 0)->plaintext); - // Get Episodes names and links - $episodes = $linksBlock->find('div[id=link]', 0)->find('font[color=#ff6600]'); - $links = $linksBlock->find('div[id=link]', 0)->find('a'); + // Get Episodes names and links + $episodes = $linksBlock->find('div[id=link]', 0)->find('font[color=#e93100]'); + $links = $linksBlock->find('div[id=link]', 0)->find('a'); foreach ($episodes as $index => $episode) { - $item = []; // Create an empty item - $item['title'] = $this->showTitle . ' ' . $this->showTechDetails . ' - ' . ltrim(trim($episode->plaintext), '-'); - $item['uri'] = $links[$index]->href; - $item['content'] = '' . $item['title'] . ''; - $item['uid'] = hash('md5', $item['uri']); + $item = []; // Create an empty item + $item['title'] = $this->showTitle . ' ' . $this->showTechDetails . ' - ' . ltrim(trim($episode->plaintext), '-'); + $item['uri'] = $links[$index]->href; + $item['content'] = '' . $item['title'] . ''; + $item['uid'] = hash('md5', $item['uri']); - $this->items[] = $item; // Add this item to the list + $this->items[] = $item; // Add this item to the list } } @@ -57,7 +60,7 @@ public function getName() switch ($this->queriedContext) { case 'Suivi de publication de série': return $this->showTitle . ' ' . $this->showTechDetails . ' - ' . self::NAME; - break; + break; default: return self::NAME; } @@ -68,7 +71,7 @@ public function getURI() switch ($this->queriedContext) { case 'Suivi de publication de série': return self::URI . $this->getInput('url'); - break; + break; default: return self::URI; } @@ -76,14 +79,14 @@ public function getURI() public function detectParameters($url) { - // Example: https://www.free-telecharger.live/series-vf-hd/145458-the-last-of-us-saison-1-web-dl-720p.html + // Example: https://www.free-telecharger.art/series-vf-hd/151432-wolf-saison-1-complete-web-dl-720p.html $params = []; - $regex = '/^https:\/\/www.*\.free-telecharger\.live\/(series.*\.html)/'; + $regex = '/^https:\/\/www.*\.free-telecharger\.art\/(series.*\.html)/'; if (preg_match($regex, $url, $matches) > 0) { - $params['context'] = 'Suivi de publication de série'; - $params['url'] = urldecode($matches[1]); - return $params; + $params['context'] = 'Suivi de publication de série'; + $params['url'] = urldecode($matches[1]); + return $params; } return null; diff --git a/bridges/GatesNotesBridge.php b/bridges/GatesNotesBridge.php index 24ba9b2ec17..0d9199680f2 100644 --- a/bridges/GatesNotesBridge.php +++ b/bridges/GatesNotesBridge.php @@ -23,12 +23,14 @@ public function collectData() $cleanedContent = str_replace([ 'Price New:
$PriceNew
Price Newbefore:
$OldPriceNew
Max Price Used:
%s,00 €
Price Used:
$PriceUsed
Price Used before:
$OldPriceUsed
Max Price Used:
%s,00 €
\n"; + $item['content'] .= "
\n"; } } else { - $item['content'] .= "
\n"; + $item['content'] .= "
\n"; } } if (!is_null($this->getInput('r'))) { if ($this->getInput('r')) { - $item['content'] .= "
\n
\n
\n" . json_encode($listing); + $item['content'] .= "
\n
\n
\n" . json_encode($listing) . "
$url"; } } $item['content'] .= "
\n
\nPrice: " . $listing->priceInfo->priceCents / 100; @@ -130,4 +189,80 @@ public function getName() } return parent::getName(); } + + /** + * Method can be used to scrape the subcategories from marktplaats + */ + private static function scrapeSubCategories() + { + $main = []; + $main['Select a category'] = ''; + $marktplaatsHTML = file_get_html('https://www.marktplaats.nl'); + foreach ($marktplaatsHTML->find('select[id=categoryId] option') as $opt) { + if (!str_contains($opt->innertext, 'categorie')) { + $main[$opt->innertext] = $opt->value; + $ids[] = $opt->value; + } + } + + $result = []; + foreach ($ids as $id) { + $url = 'https://www.marktplaats.nl/lrp/api/search?l1CategoryId=' . $id; + $jsonstring = getContents($url); + $jsondata = json_decode((string)$jsonstring); + if (isset($jsondata->searchCategoryOptions)) { + $categories = $jsondata->searchCategoryOptions; + if (isset($jsondata->categoriesById->$id)) { + $maincategory = $jsondata->categoriesById->$id; + $array = []; + foreach ($categories as $categorie) { + $array[$categorie->fullName] = $categorie->id; + } + $result[$maincategory->fullName] = $array; + } + } else { + print($jsonstring); + } + } + $combinedResult = [ + 'main' => $main, + 'sub' => $result + ]; + return $combinedResult; + } + + /** + * Helper method to construct the array that could be used for categories + * + * @param $array + * @param $indent + * @return void + */ + private static function printArrayAsCode($array, $indent = 0) + { + foreach ($array as $key => $value) { + if (is_array($value)) { + echo str_repeat(' ', $indent) . "'$key' => [" . PHP_EOL; + self::printArrayAsCode($value, $indent + 1); + echo str_repeat(' ', $indent) . '],' . PHP_EOL; + } else { + $value = str_replace('\'', '\\\'', $value); + $key = str_replace('\'', '\\\'', $key); + echo str_repeat(' ', $indent) . "'$key' => '$value'," . PHP_EOL; + } + } + } + + private static function printScrapeArray() + { + $array = (MarktplaatsBridge::scrapeSubCategories()); + + echo '$myArray = [' . PHP_EOL; + self::printArrayAsCode($array['main'], 1); + echo '];' . PHP_EOL; + + echo '$myArray = [' . PHP_EOL; + self::printArrayAsCode($array['sub'], 1); + echo '];' . PHP_EOL; + } } diff --git a/bridges/MotatosBridge.php b/bridges/MotatosBridge.php new file mode 100644 index 00000000000..6833521a794 --- /dev/null +++ b/bridges/MotatosBridge.php @@ -0,0 +1,102 @@ + [ + 'name' => 'Region', + 'type' => 'list', + 'title' => 'Choose country', + 'values' => [ + 'Austria' => 'at', + 'Denmark' => 'dk', + 'Finland' => 'fi', + 'Germany' => 'de', + 'Sweden' => 'se', + ], + ], + ]]; + + public function getName() + { + switch ($this->getInput('region')) { + case 'at': + return 'Motatos'; + case 'dk': + return 'Motatos'; + case 'de': + return 'Motatos'; + case 'fi': + return 'Matsmart'; + case 'se': + return 'Matsmart'; + default: + return self::NAME; + } + } + + public function getURI() + { + switch ($this->getInput('region')) { + case 'at': + return 'https://www.motatos.at/neu-im-shop'; + case 'dk': + return 'https://www.motatos.dk/nye-varer'; + case 'de': + return 'https://www.motatos.de/neu-im-shop'; + case 'fi': + return 'https://www.matsmart.fi/uusimmat'; + case 'se': + return 'https://www.matsmart.se/nyinkommet'; + default: + return self::URI; + } + } + + public function getIcon() + { + return 'https://www.motatos.de/favicon.ico'; + } + + private function getApiUrl() + { + switch ($this->getInput('region')) { + case 'at': + return 'https://api.findify.io/v4/4359f7b3-17e0-4f74-9fdb-e6606dfed25c/smart-collection/new-arrivals'; + case 'dk': + return 'https://api.findify.io/v4/3709426e-621a-49df-bd61-ac8543452022/smart-collection/new-arrivals'; + case 'de': + return 'https://api.findify.io/v4/2a044754-6cda-4541-b159-39133b75386c/smart-collection/new-arrivals'; + case 'fi': + return 'https://api.findify.io/v4/63946f89-2a82-4839-a412-883b79144f7b/smart-collection/new-arrivals'; + case 'se': + return 'https://api.findify.io/v4/3ae86b36-a1bd-4442-a3d9-2af6845908e6/smart-collection/new-arrivals'; + } + } + + public function collectData() + { + // motatos uses this api to dynamically load more items on page scroll + $json = getContents($this->getApiUrl() . '?t_client=0&user={%22uid%22:%220%22,%22sid%22:%220%22}'); + $jsonFile = json_decode($json, true); + + foreach ($jsonFile['items'] as $entry) { + $item = []; + $item['uid'] = $entry['custom_fields']['uuid'][0]; + $item['uri'] = $entry['product_url']; + $item['timestamp'] = $entry['created_at'] / 1000; + $item['title'] = $entry['title']; + $item['content'] = <<{$entry['title']} + +
{$entry['price'][0]}€
+ HTML; + $this->items[] = $item; + } + } +} diff --git a/bridges/MydealsBridge.php b/bridges/MydealsBridge.php index 0ef9c201701..d7e074a9aac 100644 --- a/bridges/MydealsBridge.php +++ b/bridges/MydealsBridge.php @@ -2,9 +2,9 @@ class MydealsBridge extends PepperBridgeAbstract { - const NAME = 'Mydeals bridge'; + const NAME = 'Mydealz bridge'; const URI = 'https://www.mydealz.de/'; - const DESCRIPTION = 'Zeigt die Deals von mydeals.de'; + const DESCRIPTION = 'Zeigt die Deals von mydealz.de'; const MAINTAINER = 'sysadminstory'; const PARAMETERS = [ 'Suche nach Stichworten' => [ @@ -2021,9 +2021,10 @@ class MydealsBridge extends PepperBridgeAbstract 'context-talk' => 'Überwachung Diskussion', 'uri-group' => 'gruppe/', 'uri-deal' => 'deals/', + 'uri-merchant' => 'search/gutscheine?merchant-id=', 'request-error' => 'Could not request mydeals', 'thread-error' => 'Die ID der Diskussion kann nicht ermittelt werden. Überprüfen Sie die eingegebene URL', - 'no-results' => 'Ups, wir konnten keine Deals zu', + 'no-results' => 'Ups, wir konnten nichts', 'relative-date-indicator' => [ 'vor', 'seit' @@ -2068,7 +2069,9 @@ class MydealsBridge extends PepperBridgeAbstract 'relative-date-alt-prefixes' => [ 'aktualisiert vor ', 'kommentiert vor ', - 'heiß seit ' + 'eingestellt vor ', + 'heiß seit ', + 'vor ' ], 'relative-date-ignore-suffix' => [ '/von.*$/' diff --git a/bridges/NintendoBridge.php b/bridges/NintendoBridge.php new file mode 100644 index 00000000000..1f463e91a00 --- /dev/null +++ b/bridges/NintendoBridge.php @@ -0,0 +1,486 @@ + [ + 'category' => [ + 'name' => 'Category', + 'type' => 'list', + 'values' => [ + 'All' => 'all', + 'Mario Kart 8 Deluxe' => 'mk8d', + 'Splatoon 2' => 's2', + 'Super Mario 3D All-Stars' => 'sm3as', + 'Super Mario 3D World + Bowser’s Fury' => 'sm3wbf', + 'Super Mario Bros. Wonder' => 'smbw', + 'Super Mario Maker 2' => 'smm2', + 'Super Mario Odyssey' => 'smo', + 'Super Smash Bros. Ultimate' => 'ssbu', + 'Switch Firmware' => 'sf', + 'The Legend of Zelda: Link’s Awakening' => 'tlozla', + 'The Legend of Zelda: Skyward Sword HD' => 'tlozss', + 'The Legend of Zelda: Tears of the Kingdom' => 'tloztotk', + 'Xenoblade Chronicles 2' => 'xc2', + ], + 'defaultValue' => 'mk8d', + 'title' => 'Select category' + ], + 'country' => [ + 'name' => 'Country', + 'type' => 'list', + 'values' => [ + 'België' => 'be/nl', + 'Belgique' => 'be/fr', + 'Deutschland' => 'de', + 'España' => 'es', + 'France' => 'fr', + 'Italia' => 'it', + 'Nederland' => 'nl', + 'Österreich' => 'at', + 'Portugal' => 'pt', + 'Schweiz' => 'ch/de', + 'Suisse' => 'ch/fr', + 'Svizzera' => 'ch/it', + 'UK & Ireland' => 'co.uk', + 'South Africa' => 'co.za' + ], + 'defaultValue' => 'co.uk', + 'title' => 'Select your country' + ] + ] + ]; + + const CACHE_TIMEOUT = 3600; + + const FEED_SOURCE_URL = [ + 'mk8d' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Mario-Kart-8-Deluxe-1482895.html', + 's2' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Splatoon-2-1482897.html', + 'sm3as' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-3D-All-Stars-1844226.html', + 'sm3wbf' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-3D-World-Bowser-s-Fury-1920668.html', + 'smbw' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-Bros-Wonder-2485410.html', + 'smm2' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-Maker-2-1586745.html', + 'smo' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-Odyssey-1482901.html', + 'ssbu' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Smash-Bros-Ultimate-1484130.html', + 'sf' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/System-Updates/Nintendo-Switch-System-Updates-and-Change-History-1445507.html', + 'tlozla' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-The-Legend-of-Zelda-Link-s-Awakening-1666739.html', + 'tlozss' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-The-Legend-of-Zelda-Skyward-Sword-HD-2022801.html', + 'tloztotk' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-The-Legend-of-Zelda-Tears-of-the-Kingdom-2388231.html', + 'xc2' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/Xenoblade-Chronicles-2-Update-History-1482911.html', + ]; + const XPATH_EXPRESSION_ITEM = '//div[@class="col-xs-12 content"]/div[starts-with(@id,"v") and @class="collapse"]'; + const XPATH_EXPRESSION_ITEM_FIRMWARE = '//div[@id="latest" and @class="collapse" and @rel="1"]'; + const XPATH_EXPRESSION_ITEM_TITLE = '(.//h2[1] | .//strong[1])[1]/node()'; + const XPATH_EXPRESSION_ITEM_CONTENT = '.'; + const XPATH_EXPRESSION_ITEM_URI = '//link[@rel="canonical"]/@href'; + + //const XPATH_EXPRESSION_ITEM_AUTHOR = ''; + const XPATH_EXPRESSION_ITEM_TIMESTAMP_PART = 'substring-after(//a[@class="collapse_link collapsed" and @data-target="#{{id_here}}"]/text(), "{{label_here}}")'; + const XPATH_EXPRESSION_ITEM_TIMESTAMP = 'substring(' . self::XPATH_EXPRESSION_ITEM_TIMESTAMP_PART . ', 1, string-length(' + . self::XPATH_EXPRESSION_ITEM_TIMESTAMP_PART . ') - 1)'; + + //const XPATH_EXPRESSION_ITEM_ENCLOSURES = ''; + //const XPATH_EXPRESSION_ITEM_CATEGORIES = ''; + const SETTING_FIX_ENCODING = false; + const SETTING_USE_RAW_ITEM_CONTENT = true; + + private const GAME_COUNTRY_DATE_SUBSTRING_PART = [ + 'mk8d' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'ubblicata il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada no dia ', + 'en' => 'eleased ', + ], + 's2' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'ubblicata il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + 'sm3as' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'ubliée le ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + 'sm3wbf' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada no dia ', + 'en' => 'eleased ', + ], + 'smbw' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + 'smm2' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'ubliée le ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada no dia ', + 'en' => 'eleased ', + ], + 'smo' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada no dia ', + 'en' => 'eleased ', + ], + 'ssbu' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada no dia ', + 'en' => 'eleased ', + ], + 'sf' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'ise en ligne le ', + 'it' => 'ubblicata il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada no dia ', + 'en' => 'istributed ', + ], + 'tlozla' => [ + 'de' => 'eröffentlicht ', + 'es' => 'ublicada el ', + 'fr' => 'atée du ', + 'it' => 'istribuita il ', + 'nl' => 'itgegeven op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + 'tlozss' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'ubblicata l\'', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + 'tloztotk' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'ubliée le ', + 'it' => 'ubblicata il ', + 'nl' => 'erschenen op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + 'xc2' => [ + 'de' => 'eröffentlicht am ', + 'es' => 'isponible desde el ', + 'fr' => 'atée du ', + 'it' => 'istribuita il ', + 'nl' => 'itgebracht op ', + 'pt' => 'ançada a ', + 'en' => 'eleased ', + ], + ]; + + private const GAME_COUNTRY_DATE_FORMAT = [ + 'mk8d' => [ + 'de' => 'd.m.y', + 'es' => 'd-m-y', + 'fr' => 'd/m/Y', + 'it' => 'd/m/y', + 'nl' => 'd m Y', + 'pt' => 'd/m/y', + 'en' => 'd/m/y', + ], + 's2' => [ + 'de' => 'd.m.Y', + 'es' => 'd-m-Y', + 'fr' => 'd/m/y', + 'it' => 'd/m/y', + 'nl' => 'd/m/y', + 'pt' => 'd/m/y', + 'en' => 'd F Y', + ], + 'sm3as' => [ + 'de' => 'j. m Y', + 'es' => 'j \d\e m \d\e Y', + 'fr' => 'j m Y', + 'it' => 'j m Y', + 'nl' => 'j m Y', + 'pt' => 'j \d\e m \d\e Y', + 'en' => 'j F Y', + ], + 'sm3wbf' => [ + 'de' => 'd.m.y', + 'es' => 'd-m-y', + 'fr' => 'd/m/y', + 'it' => 'd/m/y', + 'nl' => 'd m Y', + 'pt' => 'd/m/y', + 'en' => 'F j, Y', + ], + 'smbw' => [ + 'de' => 'd. m Y', + 'es' => 'j \d\e m \d\e Y', + 'fr' => 'd/m/Y', + 'it' => 'j m Y', + 'nl' => 'd m Y', + 'pt' => 'j \d\e m \d\e Y', + 'en' => 'j F Y', + ], + 'smm2' => [ + 'de' => 'd.m.Y', + 'es' => 'd-m-Y', + 'fr' => 'd/m/Y', + 'it' => 'd/m/Y', + 'nl' => 'd m Y', + 'pt' => 'd/m/y', + 'en' => 'd/m/y', + ], + 'smo' => [ + 'de' => 'd.m.Y', + 'es' => 'd-m-Y', + 'fr' => 'd/m/Y', + 'it' => 'd/m/y', + 'nl' => 'd m Y', + 'pt' => 'd/m/y', + 'en' => 'd/m/y', + ], + 'ssbu' => [ + 'de' => 'd. m Y', + 'es' => 'j \d\e m \d\e Y', + 'fr' => 'j m Y', + 'it' => 'j m Y', + 'nl' => 'd m Y', + 'pt' => 'd/m/Y', + 'en' => 'j F Y', + ], + 'sf' => [ + 'de' => 'd.m.Y', + 'es' => 'd-m-y', + 'fr' => 'd/m/Y', + 'it' => 'd/m/Y', + 'nl' => 'd m Y', + 'pt' => 'd/m/Y', + 'en' => 'd/m/Y', + ], + 'tlozla' => [ + 'de' => 'd. m Y', + 'es' => 'j m \d\e Y', + 'fr' => 'd/m/y', + 'it' => 'j m Y', + 'nl' => 'd m Y', + 'pt' => 'j \d\e m \d\e Y', + 'en' => 'j F y', + ], + 'tlozss' => [ + 'de' => 'd. m Y', + 'es' => 'j \d\e m \d\e Y', + 'fr' => 'd/m/y', + 'it' => 'j m Y', + 'nl' => 'd m Y', + 'pt' => 'j \d\e m \d\e Y', + 'en' => 'j F Y', + ], + 'tloztotk' => [ + 'de' => 'd. m Y', + 'es' => 'j \d\e m \d\e Y', + 'fr' => 'j m Y', + 'it' => 'j m Y', + 'nl' => 'd m Y', + 'pt' => 'j \d\e m \d\e Y', + 'en' => 'j F Y', + ], + 'xc2' => [ + 'de' => 'd.m.y', + 'es' => 'd-m-y', + 'fr' => 'd/m/Y', + 'it' => 'd/m/y', + 'nl' => 'd m Y', + 'pt' => 'd/m/y', + 'en' => 'd/m/y', + ], + ]; + + private const FOREIGN_MONTH_NAMES = [ + 'nl' => ['01' => 'januari', '02' => 'februari', '03' => 'maart', '04' => 'april', '05' => 'mei', '06' => 'juni', '07' => 'juli', '08' => 'augustus', + '09' => 'september', '10' => 'oktober', '11' => 'november', '12' => 'december'], + 'fr' => ['01' => 'janvier', '02' => 'février', '03' => 'mars', '04' => 'avril', '05' => 'mai', '06' => 'juin', '07' => 'juillet', '08' => 'août', + '09' => 'septembre', '10' => 'octobre', '11' => 'novembre', '12' => 'décembre'], + 'de' => ['01' => 'Januar', '02' => 'Februar', '03' => 'März', '04' => 'April', '05' => 'Mai', '06' => 'Juni', '07' => 'Juli', '08' => 'August', + '09' => 'September', '10' => 'Oktober', '11' => 'November', '12' => 'Dezember'], + 'es' => ['01' => 'enero', '02' => 'febrero', '03' => 'marzo', '04' => 'abril', '05' => 'mayo', '06' => 'junio', '07' => 'julio', '08' => 'agosto', + '09' => 'septiembre', '10' => 'octubre', '11' => 'noviembre', '12' => 'diciembre'], + 'it' => ['01' => 'gennaio', '02' => 'febbraio', '03' => 'marzo', '04' => 'aprile', '05' => 'maggio', '06' => 'giugno', '07' => 'luglio', '08' => 'agosto', + '09' => 'settembre', '10' => 'ottobre', '11' => 'novembre', '12' => 'dicembre'], + 'pt' => ['01' => 'janeiro', '02' => 'fevereiro', '03' => 'março', '04' => 'abril', '05' => 'maio', '06' => 'junho', '07' => 'julho', '08' => 'agosto', + '09' => 'setembro', '10' => 'outubro', '11' => 'novembro', '12' => 'dezembro'], + ]; + const LANGUAGE_REWRITE = ['co.uk' => 'en', 'co.za' => 'en', 'at' => 'de']; + + private string $lastId = ''; + private ?string $currentCategory = ''; + + private function getCurrentCategory() + { + if (empty($this->currentCategory)) { + $category = $this->getInput('category'); + $this->currentCategory = empty($category) ? self::PARAMETERS['']['category']['defaultValue'] : $category; + } + return $this->currentCategory; + } + + public function getIcon() + { + return 'https://www.nintendo.co.uk/favicon.ico'; + } + + public function getURI() + { + $category = $this->getInput('category'); + return 'all' === $category ? self::URI : $this->getSourceUrl(); + } + + protected function provideFeedTitle(\DOMXPath $xpath) + { + $category = $this->getInput('category'); + $categoryName = array_search($category, self::PARAMETERS['']['category']['values']); + return 'all' === $category ? self::NAME : $categoryName . ' Software-Updates'; + } + + protected function getSourceUrl() + { + $country = $this->getInput('country'); + $category = $this->getCurrentCategory(); + return str_replace(self::PARAMETERS['']['country']['defaultValue'], $country, self::FEED_SOURCE_URL[$category]); + } + + protected function getExpressionItem() + { + $category = $this->getCurrentCategory(); + return 'sf' === $category ? self::XPATH_EXPRESSION_ITEM_FIRMWARE : self::XPATH_EXPRESSION_ITEM; + } + + protected function getExpressionItemTimestamp() + { + if (empty($this->lastId)) { + return null; + } + $country = $this->getInput('country'); + $category = $this->getCurrentCategory(); + $language = $this->getLanguageFromCountry($country); + return str_replace( + ['{{id_here}}', '{{label_here}}'], + [$this->lastId, static::GAME_COUNTRY_DATE_SUBSTRING_PART[$category][$language]], + static::XPATH_EXPRESSION_ITEM_TIMESTAMP + ); + } + + protected function getExpressionItemCategories() + { + $category = $this->getCurrentCategory(); + $categoryName = array_search($category, self::PARAMETERS['']['category']['values']); + return 'string("' . $categoryName . '")'; + } + + public function collectData() + { + $category = $this->getCurrentCategory(); + if ('all' === $category) { + $allItems = []; + foreach (self::PARAMETERS['']['category']['values'] as $catKey) { + if ('all' === $catKey) { + continue; + } + $this->currentCategory = $catKey; + $this->items = []; + parent::collectData(); + $allItems = [...$allItems, ...$this->items]; + } + $this->currentCategory = 'all'; + $this->items = $allItems; + } else { + parent::collectData(); + } + } + + protected function formatItemTitle($value) + { + if (false !== strpos($value, ' (')) { + $value = substr($value, 0, strpos($value, ' (')); + } + if ('all' === $this->getInput('category')) { + $category = $this->getCurrentCategory(); + $categoryName = array_search($category, self::PARAMETERS['']['category']['values']); + return $categoryName . ' ' . $value; + } + return $value; + } + + protected function formatItemContent($value) + { + $result = preg_match('~