Reformat codebase v4 (#2872)

Reformat code base to PSR12

Co-authored-by: rssbridge <noreply@github.com>
This commit is contained in:
Dag 2022-07-01 15:10:30 +02:00 committed by GitHub
parent 66568e3a39
commit 4f75591060
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
398 changed files with 58607 additions and 56442 deletions

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
* Atom feeds for websites that don't have one. * Atom feeds for websites that don't have one.
@ -6,9 +7,9 @@
* For the full license information, please view the UNLICENSE file distributed * For the full license information, please view the UNLICENSE file distributed
* with this source code. * with this source code.
* *
* @package Core * @package Core
* @license http://unlicense.org/ UNLICENSE * @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge * @link https://github.com/rss-bridge/rss-bridge
*/ */
/** /**
@ -23,85 +24,84 @@
*/ */
class ConnectivityAction implements ActionInterface class ConnectivityAction implements ActionInterface
{ {
public $userData = []; public $userData = [];
public function execute() { public function execute()
{
if (!Debug::isEnabled()) {
returnError('This action is only available in debug mode!', 400);
}
if(!Debug::isEnabled()) { if (!isset($this->userData['bridge'])) {
returnError('This action is only available in debug mode!', 400); $this->returnEntryPage();
} return;
}
if(!isset($this->userData['bridge'])) { $bridgeName = $this->userData['bridge'];
$this->returnEntryPage();
return;
}
$bridgeName = $this->userData['bridge']; $this->reportBridgeConnectivity($bridgeName);
}
$this->reportBridgeConnectivity($bridgeName); /**
* Generates a report about the bridge connectivity status and sends it back
* to the user.
*
* The report is generated as Json-formatted string in the format
* {
* "bridge": "<bridge-name>",
* "successful": true/false
* }
*
* @param string $bridgeName Name of the bridge to generate the report for
* @return void
*/
private function reportBridgeConnectivity($bridgeName)
{
$bridgeFac = new \BridgeFactory();
} if (!$bridgeFac->isWhitelisted($bridgeName)) {
header('Content-Type: text/html');
returnServerError('Bridge is not whitelisted!');
}
/** header('Content-Type: text/json');
* Generates a report about the bridge connectivity status and sends it back
* to the user.
*
* The report is generated as Json-formatted string in the format
* {
* "bridge": "<bridge-name>",
* "successful": true/false
* }
*
* @param string $bridgeName Name of the bridge to generate the report for
* @return void
*/
private function reportBridgeConnectivity($bridgeName) {
$bridgeFac = new \BridgeFactory(); $retVal = [
'bridge' => $bridgeName,
'successful' => false,
'http_code' => 200,
];
if(!$bridgeFac->isWhitelisted($bridgeName)) { $bridge = $bridgeFac->create($bridgeName);
header('Content-Type: text/html');
returnServerError('Bridge is not whitelisted!');
}
header('Content-Type: text/json'); if ($bridge === false) {
echo json_encode($retVal);
return;
}
$retVal = array( $curl_opts = [
'bridge' => $bridgeName, CURLOPT_CONNECTTIMEOUT => 5
'successful' => false, ];
'http_code' => 200,
);
$bridge = $bridgeFac->create($bridgeName); try {
$reply = getContents($bridge::URI, [], $curl_opts, true);
if($bridge === false) { if ($reply['code'] === 200) {
echo json_encode($retVal); $retVal['successful'] = true;
return; if (strpos(implode('', $reply['status_lines']), '301 Moved Permanently')) {
} $retVal['http_code'] = 301;
}
}
} catch (Exception $e) {
$retVal['successful'] = false;
}
$curl_opts = array( echo json_encode($retVal);
CURLOPT_CONNECTTIMEOUT => 5 }
);
try { private function returnEntryPage()
$reply = getContents($bridge::URI, array(), $curl_opts, true); {
echo <<<EOD
if($reply['code'] === 200) {
$retVal['successful'] = true;
if (strpos(implode('', $reply['status_lines']), '301 Moved Permanently')) {
$retVal['http_code'] = 301;
}
}
} catch(Exception $e) {
$retVal['successful'] = false;
}
echo json_encode($retVal);
}
private function returnEntryPage() {
echo <<<EOD
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -132,5 +132,5 @@ class ConnectivityAction implements ActionInterface
</body> </body>
</html> </html>
EOD; EOD;
} }
} }

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
* Atom feeds for websites that don't have one. * Atom feeds for websites that don't have one.
@ -6,50 +7,49 @@
* For the full license information, please view the UNLICENSE file distributed * For the full license information, please view the UNLICENSE file distributed
* with this source code. * with this source code.
* *
* @package Core * @package Core
* @license http://unlicense.org/ UNLICENSE * @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge * @link https://github.com/rss-bridge/rss-bridge
*/ */
class DetectAction implements ActionInterface class DetectAction implements ActionInterface
{ {
public $userData = []; public $userData = [];
public function execute() { public function execute()
$targetURL = $this->userData['url'] {
or returnClientError('You must specify a url!'); $targetURL = $this->userData['url']
or returnClientError('You must specify a url!');
$format = $this->userData['format'] $format = $this->userData['format']
or returnClientError('You must specify a format!'); or returnClientError('You must specify a format!');
$bridgeFac = new \BridgeFactory(); $bridgeFac = new \BridgeFactory();
foreach($bridgeFac->getBridgeNames() as $bridgeName) { foreach ($bridgeFac->getBridgeNames() as $bridgeName) {
if (!$bridgeFac->isWhitelisted($bridgeName)) {
continue;
}
if(!$bridgeFac->isWhitelisted($bridgeName)) { $bridge = $bridgeFac->create($bridgeName);
continue;
}
$bridge = $bridgeFac->create($bridgeName); if ($bridge === false) {
continue;
}
if($bridge === false) { $bridgeParams = $bridge->detectParameters($targetURL);
continue;
}
$bridgeParams = $bridge->detectParameters($targetURL); if (is_null($bridgeParams)) {
continue;
}
if(is_null($bridgeParams)) { $bridgeParams['bridge'] = $bridgeName;
continue; $bridgeParams['format'] = $format;
}
$bridgeParams['bridge'] = $bridgeName; header('Location: ?action=display&' . http_build_query($bridgeParams), true, 301);
$bridgeParams['format'] = $format; die();
}
header('Location: ?action=display&' . http_build_query($bridgeParams), true, 301); returnClientError('No bridge found for given URL: ' . $targetURL);
die(); }
}
returnClientError('No bridge found for given URL: ' . $targetURL);
}
} }

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
* Atom feeds for websites that don't have one. * Atom feeds for websites that don't have one.
@ -6,216 +7,220 @@
* For the full license information, please view the UNLICENSE file distributed * For the full license information, please view the UNLICENSE file distributed
* with this source code. * with this source code.
* *
* @package Core * @package Core
* @license http://unlicense.org/ UNLICENSE * @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge * @link https://github.com/rss-bridge/rss-bridge
*/ */
class DisplayAction implements ActionInterface class DisplayAction implements ActionInterface
{ {
public $userData = []; public $userData = [];
private function getReturnCode($error) { private function getReturnCode($error)
$returnCode = $error->getCode(); {
if ($returnCode === 301 || $returnCode === 302) { $returnCode = $error->getCode();
# Don't pass redirect codes to the exterior if ($returnCode === 301 || $returnCode === 302) {
$returnCode = 508; # Don't pass redirect codes to the exterior
} $returnCode = 508;
return $returnCode; }
} return $returnCode;
}
public function execute() { public function execute()
$bridge = array_key_exists('bridge', $this->userData) ? $this->userData['bridge'] : null; {
$bridge = array_key_exists('bridge', $this->userData) ? $this->userData['bridge'] : null;
$format = $this->userData['format'] $format = $this->userData['format']
or returnClientError('You must specify a format!'); or returnClientError('You must specify a format!');
$bridgeFac = new \BridgeFactory(); $bridgeFac = new \BridgeFactory();
// whitelist control // whitelist control
if(!$bridgeFac->isWhitelisted($bridge)) { if (!$bridgeFac->isWhitelisted($bridge)) {
throw new \Exception('This bridge is not whitelisted', 401); throw new \Exception('This bridge is not whitelisted', 401);
die; die;
} }
// Data retrieval // Data retrieval
$bridge = $bridgeFac->create($bridge); $bridge = $bridgeFac->create($bridge);
$bridge->loadConfiguration(); $bridge->loadConfiguration();
$noproxy = array_key_exists('_noproxy', $this->userData) $noproxy = array_key_exists('_noproxy', $this->userData)
&& filter_var($this->userData['_noproxy'], FILTER_VALIDATE_BOOLEAN); && filter_var($this->userData['_noproxy'], FILTER_VALIDATE_BOOLEAN);
if(defined('PROXY_URL') && PROXY_BYBRIDGE && $noproxy) { if (defined('PROXY_URL') && PROXY_BYBRIDGE && $noproxy) {
define('NOPROXY', true); define('NOPROXY', true);
} }
// Cache timeout // Cache timeout
$cache_timeout = -1; $cache_timeout = -1;
if(array_key_exists('_cache_timeout', $this->userData)) { if (array_key_exists('_cache_timeout', $this->userData)) {
if (!CUSTOM_CACHE_TIMEOUT) {
unset($this->userData['_cache_timeout']);
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '?' . http_build_query($this->userData);
header('Location: ' . $uri, true, 301);
die();
}
if(!CUSTOM_CACHE_TIMEOUT) { $cache_timeout = filter_var($this->userData['_cache_timeout'], FILTER_VALIDATE_INT);
unset($this->userData['_cache_timeout']); } else {
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '?' . http_build_query($this->userData); $cache_timeout = $bridge->getCacheTimeout();
header('Location: ' . $uri, true, 301); }
die();
}
$cache_timeout = filter_var($this->userData['_cache_timeout'], FILTER_VALIDATE_INT); // Remove parameters that don't concern bridges
$bridge_params = array_diff_key(
$this->userData,
array_fill_keys(
[
'action',
'bridge',
'format',
'_noproxy',
'_cache_timeout',
'_error_time'
],
''
)
);
} else { // Remove parameters that don't concern caches
$cache_timeout = $bridge->getCacheTimeout(); $cache_params = array_diff_key(
} $this->userData,
array_fill_keys(
[
'action',
'format',
'_noproxy',
'_cache_timeout',
'_error_time'
],
''
)
);
// Remove parameters that don't concern bridges // Initialize cache
$bridge_params = array_diff_key( $cacheFac = new CacheFactory();
$this->userData,
array_fill_keys(
array(
'action',
'bridge',
'format',
'_noproxy',
'_cache_timeout',
'_error_time'
), '')
);
// Remove parameters that don't concern caches $cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
$cache_params = array_diff_key( $cache->setScope('');
$this->userData, $cache->purgeCache(86400); // 24 hours
array_fill_keys( $cache->setKey($cache_params);
array(
'action',
'format',
'_noproxy',
'_cache_timeout',
'_error_time'
), '')
);
// Initialize cache $items = [];
$cacheFac = new CacheFactory(); $infos = [];
$mtime = $cache->getTime();
$cache = $cacheFac->create(Configuration::getConfig('cache', 'type')); if (
$cache->setScope(''); $mtime !== false
$cache->purgeCache(86400); // 24 hours && (time() - $cache_timeout < $mtime)
$cache->setKey($cache_params); && !Debug::isEnabled()
) { // Load cached data
// Send "Not Modified" response if client supports it
// Implementation based on https://stackoverflow.com/a/10847262
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$stime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
$items = array(); if ($mtime <= $stime) { // Cached data is older or same
$infos = array(); header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $mtime) . 'GMT', true, 304);
$mtime = $cache->getTime(); die();
}
}
if($mtime !== false $cached = $cache->loadData();
&& (time() - $cache_timeout < $mtime)
&& !Debug::isEnabled()) { // Load cached data
// Send "Not Modified" response if client supports it if (isset($cached['items']) && isset($cached['extraInfos'])) {
// Implementation based on https://stackoverflow.com/a/10847262 foreach ($cached['items'] as $item) {
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $items[] = new \FeedItem($item);
$stime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); }
if($mtime <= $stime) { // Cached data is older or same $infos = $cached['extraInfos'];
header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $mtime) . 'GMT', true, 304); }
die(); } else { // Collect new data
} try {
} $bridge->setDatas($bridge_params);
$bridge->collectData();
$cached = $cache->loadData(); $items = $bridge->getItems();
if(isset($cached['items']) && isset($cached['extraInfos'])) { // Transform "legacy" items to FeedItems if necessary.
foreach($cached['items'] as $item) { // Remove this code when support for "legacy" items ends!
$items[] = new \FeedItem($item); if (isset($items[0]) && is_array($items[0])) {
} $feedItems = [];
$infos = $cached['extraInfos']; foreach ($items as $item) {
} $feedItems[] = new \FeedItem($item);
}
} else { // Collect new data $items = $feedItems;
}
try { $infos = [
$bridge->setDatas($bridge_params); 'name' => $bridge->getName(),
$bridge->collectData(); 'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'icon' => $bridge->getIcon()
];
} catch (\Throwable $e) {
error_log($e);
$items = $bridge->getItems(); if (logBridgeError($bridge::NAME, $e->getCode()) >= Configuration::getConfig('error', 'report_limit')) {
if (Configuration::getConfig('error', 'output') === 'feed') {
$item = new \FeedItem();
// Transform "legacy" items to FeedItems if necessary. // Create "new" error message every 24 hours
// Remove this code when support for "legacy" items ends! $this->userData['_error_time'] = urlencode((int)(time() / 86400));
if(isset($items[0]) && is_array($items[0])) {
$feedItems = array();
foreach($items as $item) { $message = sprintf(
$feedItems[] = new \FeedItem($item); 'Bridge returned error %s! (%s)',
} $e->getCode(),
$this->userData['_error_time']
);
$item->setTitle($message);
$items = $feedItems; $item->setURI(
} (isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
. '?'
. http_build_query($this->userData)
);
$infos = array( $item->setTimestamp(time());
'name' => $bridge->getName(), $item->setContent(buildBridgeException($e, $bridge));
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'icon' => $bridge->getIcon()
);
} catch(\Throwable $e) {
error_log($e);
if(logBridgeError($bridge::NAME, $e->getCode()) >= Configuration::getConfig('error', 'report_limit')) { $items[] = $item;
if(Configuration::getConfig('error', 'output') === 'feed') { } elseif (Configuration::getConfig('error', 'output') === 'http') {
$item = new \FeedItem(); header('Content-Type: text/html', true, $this->getReturnCode($e));
die(buildTransformException($e, $bridge));
}
}
}
// Create "new" error message every 24 hours // Store data in cache
$this->userData['_error_time'] = urlencode((int)(time() / 86400)); $cache->saveData([
'items' => array_map(function ($i) {
return $i->toArray();
}, $items),
'extraInfos' => $infos
]);
}
$message = sprintf( // Data transformation
'Bridge returned error %s! (%s)', try {
$e->getCode(), $formatFac = new FormatFactory();
$this->userData['_error_time'] $format = $formatFac->create($format);
); $format->setItems($items);
$item->setTitle($message); $format->setExtraInfos($infos);
$lastModified = $cache->getTime();
$format->setLastModified($lastModified);
if ($lastModified) {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT');
}
header('Content-Type: ' . $format->getMimeType() . '; charset=' . $format->getCharset());
$item->setURI( echo $format->stringify();
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '') } catch (\Throwable $e) {
. '?' error_log($e);
. http_build_query($this->userData) header('Content-Type: text/html', true, $e->getCode());
); die(buildTransformException($e, $bridge));
}
$item->setTimestamp(time()); }
$item->setContent(buildBridgeException($e, $bridge));
$items[] = $item;
} elseif(Configuration::getConfig('error', 'output') === 'http') {
header('Content-Type: text/html', true, $this->getReturnCode($e));
die(buildTransformException($e, $bridge));
}
}
}
// Store data in cache
$cache->saveData(array(
'items' => array_map(function($i){ return $i->toArray(); }, $items),
'extraInfos' => $infos
));
}
// Data transformation
try {
$formatFac = new FormatFactory();
$format = $formatFac->create($format);
$format->setItems($items);
$format->setExtraInfos($infos);
$lastModified = $cache->getTime();
$format->setLastModified($lastModified);
if ($lastModified) {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT');
}
header('Content-Type: ' . $format->getMimeType() . '; charset=' . $format->getCharset());
echo $format->stringify();
} catch(\Throwable $e) {
error_log($e);
header('Content-Type: text/html', true, $e->getCode());
die(buildTransformException($e, $bridge));
}
}
} }

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
* Atom feeds for websites that don't have one. * Atom feeds for websites that don't have one.
@ -6,52 +7,49 @@
* For the full license information, please view the UNLICENSE file distributed * For the full license information, please view the UNLICENSE file distributed
* with this source code. * with this source code.
* *
* @package Core * @package Core
* @license http://unlicense.org/ UNLICENSE * @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge * @link https://github.com/rss-bridge/rss-bridge
*/ */
class ListAction implements ActionInterface class ListAction implements ActionInterface
{ {
public function execute() { public function execute()
$list = new StdClass(); {
$list->bridges = array(); $list = new StdClass();
$list->total = 0; $list->bridges = [];
$list->total = 0;
$bridgeFac = new \BridgeFactory(); $bridgeFac = new \BridgeFactory();
foreach($bridgeFac->getBridgeNames() as $bridgeName) { foreach ($bridgeFac->getBridgeNames() as $bridgeName) {
$bridge = $bridgeFac->create($bridgeName);
$bridge = $bridgeFac->create($bridgeName); if ($bridge === false) { // Broken bridge, show as inactive
$list->bridges[$bridgeName] = [
'status' => 'inactive'
];
if($bridge === false) { // Broken bridge, show as inactive continue;
}
$list->bridges[$bridgeName] = array( $status = $bridgeFac->isWhitelisted($bridgeName) ? 'active' : 'inactive';
'status' => 'inactive'
);
continue; $list->bridges[$bridgeName] = [
'status' => $status,
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'name' => $bridge->getName(),
'icon' => $bridge->getIcon(),
'parameters' => $bridge->getParameters(),
'maintainer' => $bridge->getMaintainer(),
'description' => $bridge->getDescription()
];
}
} $list->total = count($list->bridges);
$status = $bridgeFac->isWhitelisted($bridgeName) ? 'active' : 'inactive'; header('Content-Type: application/json');
echo json_encode($list, JSON_PRETTY_PRINT);
$list->bridges[$bridgeName] = array( }
'status' => $status,
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'name' => $bridge->getName(),
'icon' => $bridge->getIcon(),
'parameters' => $bridge->getParameters(),
'maintainer' => $bridge->getMaintainer(),
'description' => $bridge->getDescription()
);
}
$list->total = count($list->bridges);
header('Content-Type: application/json');
echo json_encode($list, JSON_PRETTY_PRINT);
}
} }

View file

@ -1,45 +1,48 @@
<?php <?php
class ABCNewsBridge extends BridgeAbstract {
const NAME = 'ABC News Bridge';
const URI = 'https://www.abc.net.au';
const DESCRIPTION = 'Topics of the Australian Broadcasting Corporation';
const MAINTAINER = 'yue-dongchen';
const PARAMETERS = array( class ABCNewsBridge extends BridgeAbstract
array( {
'topic' => array( const NAME = 'ABC News Bridge';
'type' => 'list', const URI = 'https://www.abc.net.au';
'name' => 'Region', const DESCRIPTION = 'Topics of the Australian Broadcasting Corporation';
'title' => 'Choose state', const MAINTAINER = 'yue-dongchen';
'values' => array(
'ACT' => 'act',
'NSW' => 'nsw',
'NT' => 'nt',
'QLD' => 'qld',
'SA' => 'sa',
'TAS' => 'tas',
'VIC' => 'vic',
'WA' => 'wa'
),
)
)
);
public function collectData() { const PARAMETERS = [
$url = 'https://www.abc.net.au/news/' . $this->getInput('topic'); [
$html = getSimpleHTMLDOM($url)->find('.YAJzu._2FvRw.ZWhbj._3BZxh', 0); 'topic' => [
$html = defaultLinkTo($html, $this->getURI()); 'type' => 'list',
'name' => 'Region',
'title' => 'Choose state',
'values' => [
'ACT' => 'act',
'NSW' => 'nsw',
'NT' => 'nt',
'QLD' => 'qld',
'SA' => 'sa',
'TAS' => 'tas',
'VIC' => 'vic',
'WA' => 'wa'
],
]
]
];
foreach($html->find('._2H7Su') as $article) { public function collectData()
$item = array(); {
$url = 'https://www.abc.net.au/news/' . $this->getInput('topic');
$html = getSimpleHTMLDOM($url)->find('.YAJzu._2FvRw.ZWhbj._3BZxh', 0);
$html = defaultLinkTo($html, $this->getURI());
$title = $article->find('._3T9Id.fmhNa.nsZdE._2c2Zy._1tOey._3EOTW', 0); foreach ($html->find('._2H7Su') as $article) {
$item['title'] = $title->plaintext; $item = [];
$item['uri'] = $title->href;
$item['content'] = $article->find('.rMkro._1cBaI._3PhF6._10YQT._1yL-m', 0)->plaintext;
$item['timestamp'] = strtotime($article->find('time', 0)->datetime);
$this->items[] = $item; $title = $article->find('._3T9Id.fmhNa.nsZdE._2c2Zy._1tOey._3EOTW', 0);
} $item['title'] = $title->plaintext;
} $item['uri'] = $title->href;
$item['content'] = $article->find('.rMkro._1cBaI._3PhF6._10YQT._1yL-m', 0)->plaintext;
$item['timestamp'] = strtotime($article->find('time', 0)->datetime);
$this->items[] = $item;
}
}
} }

View file

@ -1,118 +1,130 @@
<?php <?php
class AO3Bridge extends BridgeAbstract { class AO3Bridge extends BridgeAbstract
const NAME = 'AO3'; {
const URI = 'https://archiveofourown.org/'; const NAME = 'AO3';
const CACHE_TIMEOUT = 1800; const URI = 'https://archiveofourown.org/';
const DESCRIPTION = 'Returns works or chapters from Archive of Our Own'; const CACHE_TIMEOUT = 1800;
const MAINTAINER = 'Obsidienne'; const DESCRIPTION = 'Returns works or chapters from Archive of Our Own';
const PARAMETERS = array( const MAINTAINER = 'Obsidienne';
'List' => array( const PARAMETERS = [
'url' => array( 'List' => [
'name' => 'url', 'url' => [
'required' => true, 'name' => 'url',
// Example: F/F tag, complete works only 'required' => true,
'exampleValue' => 'https://archiveofourown.org/works?work_search[complete]=T&tag_id=F*s*F', // Example: F/F tag, complete works only
), 'exampleValue' => 'https://archiveofourown.org/works?work_search[complete]=T&tag_id=F*s*F',
), ],
'Bookmarks' => array( ],
'user' => array( 'Bookmarks' => [
'name' => 'user', 'user' => [
'required' => true, 'name' => 'user',
// Example: Nyaaru's bookmarks 'required' => true,
'exampleValue' => 'Nyaaru', // Example: Nyaaru's bookmarks
), 'exampleValue' => 'Nyaaru',
), ],
'Work' => array( ],
'id' => array( 'Work' => [
'name' => 'id', 'id' => [
'required' => true, 'name' => 'id',
// Example: latest chapters from A Better Past by LysSerris 'required' => true,
'exampleValue' => '18181853', // Example: latest chapters from A Better Past by LysSerris
), 'exampleValue' => '18181853',
) ],
); ]
];
// Feed for lists of works (e.g. recent works, search results, filtered tags, // Feed for lists of works (e.g. recent works, search results, filtered tags,
// bookmarks, series, collections). // bookmarks, series, collections).
private function collectList($url) { private function collectList($url)
$html = getSimpleHTMLDOM($url); {
$html = defaultLinkTo($html, self::URI); $html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, self::URI);
foreach($html->find('.index.group > li') as $element) { foreach ($html->find('.index.group > li') as $element) {
$item = array(); $item = [];
$title = $element->find('div h4 a', 0); $title = $element->find('div h4 a', 0);
if (!isset($title)) continue; // discard deleted works if (!isset($title)) {
$item['title'] = $title->plaintext; continue; // discard deleted works
$item['content'] = $element; }
$item['uri'] = $title->href; $item['title'] = $title->plaintext;
$item['content'] = $element;
$item['uri'] = $title->href;
$strdate = $element->find('div p.datetime', 0)->plaintext; $strdate = $element->find('div p.datetime', 0)->plaintext;
$item['timestamp'] = strtotime($strdate); $item['timestamp'] = strtotime($strdate);
$chapters = $element->find('dl dd.chapters', 0); $chapters = $element->find('dl dd.chapters', 0);
// bookmarked series and external works do not have a chapters count // bookmarked series and external works do not have a chapters count
$chapters = (isset($chapters) ? $chapters->plaintext : 0); $chapters = (isset($chapters) ? $chapters->plaintext : 0);
$item['uid'] = $item['uri'] . "/$strdate/$chapters"; $item['uid'] = $item['uri'] . "/$strdate/$chapters";
$this->items[] = $item; $this->items[] = $item;
} }
} }
// Feed for recent chapters of a specific work. // Feed for recent chapters of a specific work.
private function collectWork($id) { private function collectWork($id)
$url = self::URI . "/works/$id/navigate"; {
$html = getSimpleHTMLDOM($url); $url = self::URI . "/works/$id/navigate";
$html = defaultLinkTo($html, self::URI); $html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, self::URI);
$this->title = $html->find('h2 a', 0)->plaintext; $this->title = $html->find('h2 a', 0)->plaintext;
foreach($html->find('ol.index.group > li') as $element) { foreach ($html->find('ol.index.group > li') as $element) {
$item = array(); $item = [];
$item['title'] = $element->find('a', 0)->plaintext; $item['title'] = $element->find('a', 0)->plaintext;
$item['content'] = $element; $item['content'] = $element;
$item['uri'] = $element->find('a', 0)->href; $item['uri'] = $element->find('a', 0)->href;
$strdate = $element->find('span.datetime', 0)->plaintext; $strdate = $element->find('span.datetime', 0)->plaintext;
$strdate = str_replace('(', '', $strdate); $strdate = str_replace('(', '', $strdate);
$strdate = str_replace(')', '', $strdate); $strdate = str_replace(')', '', $strdate);
$item['timestamp'] = strtotime($strdate); $item['timestamp'] = strtotime($strdate);
$item['uid'] = $item['uri'] . "/$strdate"; $item['uid'] = $item['uri'] . "/$strdate";
$this->items[] = $item; $this->items[] = $item;
} }
$this->items = array_reverse($this->items); $this->items = array_reverse($this->items);
} }
public function collectData() { public function collectData()
switch($this->queriedContext) { {
case 'Bookmarks': switch ($this->queriedContext) {
$user = $this->getInput('user'); case 'Bookmarks':
$this->title = $user; $user = $this->getInput('user');
$url = self::URI $this->title = $user;
. '/users/' . $user $url = self::URI
. '/bookmarks?bookmark_search[sort_column]=bookmarkable_date'; . '/users/' . $user
return $this->collectList($url); . '/bookmarks?bookmark_search[sort_column]=bookmarkable_date';
case 'List': return $this->collectList( return $this->collectList($url);
$this->getInput('url') case 'List':
); return $this->collectList(
case 'Work': return $this->collectWork( $this->getInput('url')
$this->getInput('id') );
); case 'Work':
} return $this->collectWork(
} $this->getInput('id')
);
}
}
public function getName() { public function getName()
$name = parent::getName() . " $this->queriedContext"; {
if (isset($this->title)) $name .= " - $this->title"; $name = parent::getName() . " $this->queriedContext";
return $name; if (isset($this->title)) {
} $name .= " - $this->title";
}
return $name;
}
public function getIcon() { public function getIcon()
return self::URI . '/favicon.ico'; {
} return self::URI . '/favicon.ico';
}
} }

View file

@ -1,95 +1,98 @@
<?php <?php
class ARDMediathekBridge extends BridgeAbstract {
const NAME = 'ARD-Mediathek Bridge';
const URI = 'https://www.ardmediathek.de';
const DESCRIPTION = 'Feed of any series in the ARD-Mediathek, specified by its path';
const MAINTAINER = 'yue-dongchen';
/*
* Number of Items to be requested from ARDmediathek API
* 12 has been observed on the wild
* 29 is the highest successfully tested value
* More Items could be fetched via pagination
* The JSON-field pagination holds more information on that
* @const PAGESIZE number of requested items
*/
const PAGESIZE = 29;
/*
* The URL Prefix of the (Webapp-)API
* @const APIENDPOINT https-URL of the used endpoint
*/
const APIENDPOINT = 'https://api.ardmediathek.de/page-gateway/widgets/ard/asset/';
/*
* The URL prefix of the video link
* URLs from the webapp include a slug containing titles of show, episode, and tv station.
* It seems to work without that.
* @const VIDEOLINKPREFIX https-URL prefix of video links
*/
const VIDEOLINKPREFIX = 'https://www.ardmediathek.de/video/';
/*
* The requested width of the preview image
* 432 has been observed on the wild
* The webapp seems to also compute and add the height value
* It seems to works without that.
* @const IMAGEWIDTH width in px of the preview image
*/
const IMAGEWIDTH = 432;
/*
* Placeholder that will be replace by IMAGEWIDTH in the preview image URL
* @const IMAGEWIDTHPLACEHOLDER
*/
const IMAGEWIDTHPLACEHOLDER = '{width}';
const PARAMETERS = array( class ARDMediathekBridge extends BridgeAbstract
array( {
'path' => array( const NAME = 'ARD-Mediathek Bridge';
'name' => 'Show Link or ID', const URI = 'https://www.ardmediathek.de';
'required' => true, const DESCRIPTION = 'Feed of any series in the ARD-Mediathek, specified by its path';
'title' => 'Link to the show page or just its alphanumeric suffix', const MAINTAINER = 'yue-dongchen';
'defaultValue' => 'https://www.ardmediathek.de/sendung/45-min/Y3JpZDovL25kci5kZS8xMzkx/' /*
) * Number of Items to be requested from ARDmediathek API
) * 12 has been observed on the wild
); * 29 is the highest successfully tested value
* More Items could be fetched via pagination
* The JSON-field pagination holds more information on that
* @const PAGESIZE number of requested items
*/
const PAGESIZE = 29;
/*
* The URL Prefix of the (Webapp-)API
* @const APIENDPOINT https-URL of the used endpoint
*/
const APIENDPOINT = 'https://api.ardmediathek.de/page-gateway/widgets/ard/asset/';
/*
* The URL prefix of the video link
* URLs from the webapp include a slug containing titles of show, episode, and tv station.
* It seems to work without that.
* @const VIDEOLINKPREFIX https-URL prefix of video links
*/
const VIDEOLINKPREFIX = 'https://www.ardmediathek.de/video/';
/*
* The requested width of the preview image
* 432 has been observed on the wild
* The webapp seems to also compute and add the height value
* It seems to works without that.
* @const IMAGEWIDTH width in px of the preview image
*/
const IMAGEWIDTH = 432;
/*
* Placeholder that will be replace by IMAGEWIDTH in the preview image URL
* @const IMAGEWIDTHPLACEHOLDER
*/
const IMAGEWIDTHPLACEHOLDER = '{width}';
public function collectData() { const PARAMETERS = [
$oldTz = date_default_timezone_get(); [
'path' => [
'name' => 'Show Link or ID',
'required' => true,
'title' => 'Link to the show page or just its alphanumeric suffix',
'defaultValue' => 'https://www.ardmediathek.de/sendung/45-min/Y3JpZDovL25kci5kZS8xMzkx/'
]
]
];
date_default_timezone_set('Europe/Berlin'); public function collectData()
{
$oldTz = date_default_timezone_get();
$pathComponents = explode('/', $this->getInput('path')); date_default_timezone_set('Europe/Berlin');
if (empty($pathComponents)) {
returnClientError('Path may not be empty');
}
if (count($pathComponents) < 2) {
$showID = $pathComponents[0];
} else {
$lastKey = count($pathComponents) - 1;
$showID = $pathComponents[$lastKey];
if (strlen($showID) === 0) {
$showID = $pathComponents[$lastKey - 1];
}
}
$url = SELF::APIENDPOINT . $showID . '/?pageSize=' . SELF::PAGESIZE; $pathComponents = explode('/', $this->getInput('path'));
$rawJSON = getContents($url); if (empty($pathComponents)) {
$processedJSON = json_decode($rawJSON); returnClientError('Path may not be empty');
}
if (count($pathComponents) < 2) {
$showID = $pathComponents[0];
} else {
$lastKey = count($pathComponents) - 1;
$showID = $pathComponents[$lastKey];
if (strlen($showID) === 0) {
$showID = $pathComponents[$lastKey - 1];
}
}
foreach($processedJSON->teasers as $video) { $url = self::APIENDPOINT . $showID . '/?pageSize=' . self::PAGESIZE;
$item = array(); $rawJSON = getContents($url);
// there is also ->links->self->id, ->links->self->urlId, ->links->target->id, ->links->target->urlId $processedJSON = json_decode($rawJSON);
$item['uri'] = SELF::VIDEOLINKPREFIX . $video->id . '/';
// there is also ->mediumTitle and ->shortTitle
$item['title'] = $video->longTitle;
// in the test, aspect16x9 was the only child of images, not sure whether that is always true
$item['enclosures'] = array(
str_replace(SELF::IMAGEWIDTHPLACEHOLDER, SELF::IMAGEWIDTH, $video->images->aspect16x9->src)
);
$item['content'] = '<img src="' . $item['enclosures'][0] . '" /><p>';
$item['timestamp'] = $video->broadcastedOn;
$item['uid'] = $video->id;
$item['author'] = $video->publicationService->name;
$this->items[] = $item;
}
date_default_timezone_set($oldTz); foreach ($processedJSON->teasers as $video) {
} $item = [];
// there is also ->links->self->id, ->links->self->urlId, ->links->target->id, ->links->target->urlId
$item['uri'] = self::VIDEOLINKPREFIX . $video->id . '/';
// there is also ->mediumTitle and ->shortTitle
$item['title'] = $video->longTitle;
// in the test, aspect16x9 was the only child of images, not sure whether that is always true
$item['enclosures'] = [
str_replace(self::IMAGEWIDTHPLACEHOLDER, self::IMAGEWIDTH, $video->images->aspect16x9->src)
];
$item['content'] = '<img src="' . $item['enclosures'][0] . '" /><p>';
$item['timestamp'] = $video->broadcastedOn;
$item['uid'] = $video->id;
$item['author'] = $video->publicationService->name;
$this->items[] = $item;
}
date_default_timezone_set($oldTz);
}
} }

View file

@ -1,55 +1,58 @@
<?php <?php
class ASRockNewsBridge extends BridgeAbstract {
const NAME = 'ASRock News Bridge';
const URI = 'https://www.asrock.com';
const DESCRIPTION = 'Returns latest news articles';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array();
const CACHE_TIMEOUT = 3600; // 1 hour class ASRockNewsBridge extends BridgeAbstract
{
const NAME = 'ASRock News Bridge';
const URI = 'https://www.asrock.com';
const DESCRIPTION = 'Returns latest news articles';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [];
public function collectData() { const CACHE_TIMEOUT = 3600; // 1 hour
$html = getSimpleHTMLDOM(self::URI . '/news/index.asp'); public function collectData()
{
$html = getSimpleHTMLDOM(self::URI . '/news/index.asp');
$html = defaultLinkTo($html, self::URI . '/news/'); $html = defaultLinkTo($html, self::URI . '/news/');
foreach($html->find('div.inner > a') as $index => $a) { foreach ($html->find('div.inner > a') as $index => $a) {
$item = array(); $item = [];
$articlePath = $a->href; $articlePath = $a->href;
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT); $articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT);
$articlePageHtml = defaultLinkTo($articlePageHtml, self::URI); $articlePageHtml = defaultLinkTo($articlePageHtml, self::URI);
$contents = $articlePageHtml->find('div.Contents', 0); $contents = $articlePageHtml->find('div.Contents', 0);
$item['uri'] = $articlePath; $item['uri'] = $articlePath;
$item['title'] = $contents->find('h3', 0)->innertext; $item['title'] = $contents->find('h3', 0)->innertext;
$contents->find('h3', 0)->outertext = ''; $contents->find('h3', 0)->outertext = '';
$item['content'] = $contents->innertext; $item['content'] = $contents->innertext;
$item['timestamp'] = $this->extractDate($a->plaintext); $item['timestamp'] = $this->extractDate($a->plaintext);
$item['enclosures'][] = $a->find('img', 0)->src; $item['enclosures'][] = $a->find('img', 0)->src;
$this->items[] = $item; $this->items[] = $item;
if (count($this->items) >= 10) { if (count($this->items) >= 10) {
break; break;
} }
} }
} }
private function extractDate($text) { private function extractDate($text)
$dateRegex = '/^([0-9]{4}\/[0-9]{1,2}\/[0-9]{1,2})/'; {
$dateRegex = '/^([0-9]{4}\/[0-9]{1,2}\/[0-9]{1,2})/';
$text = trim($text); $text = trim($text);
if (preg_match($dateRegex, $text, $matches)) { if (preg_match($dateRegex, $text, $matches)) {
return $matches[1]; return $matches[1];
} }
return ''; return '';
} }
} }

View file

@ -1,37 +1,40 @@
<?php <?php
class AcrimedBridge extends FeedExpander {
const MAINTAINER = 'qwertygc'; class AcrimedBridge extends FeedExpander
const NAME = 'Acrimed Bridge'; {
const URI = 'https://www.acrimed.org/'; const MAINTAINER = 'qwertygc';
const CACHE_TIMEOUT = 4800; //2hours const NAME = 'Acrimed Bridge';
const DESCRIPTION = 'Returns the newest articles'; const URI = 'https://www.acrimed.org/';
const CACHE_TIMEOUT = 4800; //2hours
const DESCRIPTION = 'Returns the newest articles';
const PARAMETERS = [ const PARAMETERS = [
[ [
'limit' => [ 'limit' => [
'name' => 'limit', 'name' => 'limit',
'type' => 'number', 'type' => 'number',
'defaultValue' => -1, 'defaultValue' => -1,
] ]
] ]
]; ];
public function collectData(){ public function collectData()
$this->collectExpandableDatas( {
static::URI . 'spip.php?page=backend', $this->collectExpandableDatas(
$this->getInput('limit') static::URI . 'spip.php?page=backend',
); $this->getInput('limit')
} );
}
protected function parseItem($newsItem){ protected function parseItem($newsItem)
$item = parent::parseItem($newsItem); {
$item = parent::parseItem($newsItem);
$articlePage = getSimpleHTMLDOM($newsItem->link); $articlePage = getSimpleHTMLDOM($newsItem->link);
$article = sanitize($articlePage->find('article.article1', 0)->innertext); $article = sanitize($articlePage->find('article.article1', 0)->innertext);
$article = defaultLinkTo($article, static::URI); $article = defaultLinkTo($article, static::URI);
$item['content'] = $article; $item['content'] = $article;
return $item; return $item;
} }
} }

View file

@ -1,54 +1,57 @@
<?php <?php
class AirBreizhBridge extends BridgeAbstract {
const MAINTAINER = 'fanch317'; class AirBreizhBridge extends BridgeAbstract
const NAME = 'Air Breizh'; {
const URI = 'https://www.airbreizh.asso.fr/'; const MAINTAINER = 'fanch317';
const DESCRIPTION = 'Returns newests publications on Air Breizh'; const NAME = 'Air Breizh';
const PARAMETERS = array( const URI = 'https://www.airbreizh.asso.fr/';
'Publications' => array( const DESCRIPTION = 'Returns newests publications on Air Breizh';
'theme' => array( const PARAMETERS = [
'name' => 'Thematique', 'Publications' => [
'type' => 'list', 'theme' => [
'values' => array( 'name' => 'Thematique',
'Tout' => '', 'type' => 'list',
'Rapport d\'activite' => 'rapport-dactivite', 'values' => [
'Etude' => 'etudes', 'Tout' => '',
'Information' => 'information', 'Rapport d\'activite' => 'rapport-dactivite',
'Autres documents' => 'autres-documents', 'Etude' => 'etudes',
'Plan Régional de Surveillance de la qualité de lair' => 'prsqa', 'Information' => 'information',
'Transport' => 'transport' 'Autres documents' => 'autres-documents',
) 'Plan Régional de Surveillance de la qualité de lair' => 'prsqa',
) 'Transport' => 'transport'
) ]
); ]
]
];
public function getIcon() { public function getIcon()
return 'https://www.airbreizh.asso.fr/voy_content/uploads/2017/11/favicon.png'; {
} return 'https://www.airbreizh.asso.fr/voy_content/uploads/2017/11/favicon.png';
}
public function collectData(){ public function collectData()
$html = ''; {
$html = getSimpleHTMLDOM(static::URI . 'publications/?fwp_publications_thematiques=' . $this->getInput('theme')) $html = '';
or returnClientError('No results for this query.'); $html = getSimpleHTMLDOM(static::URI . 'publications/?fwp_publications_thematiques=' . $this->getInput('theme'))
or returnClientError('No results for this query.');
foreach ($html->find('article') as $article) { foreach ($html->find('article') as $article) {
$item = array(); $item = [];
// Title // Title
$item['title'] = $article->find('h2', 0)->plaintext; $item['title'] = $article->find('h2', 0)->plaintext;
// Author // Author
$item['author'] = 'Air Breizh'; $item['author'] = 'Air Breizh';
// Image // Image
$imagelink = $article->find('.card__image', 0)->find('img', 0)->getAttribute('src'); $imagelink = $article->find('.card__image', 0)->find('img', 0)->getAttribute('src');
// Content preview // Content preview
$item['content'] = '<img src="' . $imagelink . '" /> $item['content'] = '<img src="' . $imagelink . '" />
<br/>' <br/>'
. $article->find('.card__text', 0)->plaintext; . $article->find('.card__text', 0)->plaintext;
// URL // URL
$item['uri'] = $article->find('.publi__buttons', 0)->find('a', 0)->getAttribute('href'); $item['uri'] = $article->find('.publi__buttons', 0)->find('a', 0)->getAttribute('href');
// ID // ID
$item['id'] = $article->find('.publi__buttons', 0)->find('a', 0)->getAttribute('href'); $item['id'] = $article->find('.publi__buttons', 0)->find('a', 0)->getAttribute('href');
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }

View file

@ -1,73 +1,76 @@
<?php <?php
class AlbionOnlineBridge extends BridgeAbstract {
const NAME = 'Albion Online Changelog'; class AlbionOnlineBridge extends BridgeAbstract
const MAINTAINER = 'otakuf'; {
const URI = 'https://albiononline.com'; const NAME = 'Albion Online Changelog';
const DESCRIPTION = 'Returns the changes made to the Albion Online'; const MAINTAINER = 'otakuf';
const CACHE_TIMEOUT = 3600; // 60min const URI = 'https://albiononline.com';
const DESCRIPTION = 'Returns the changes made to the Albion Online';
const CACHE_TIMEOUT = 3600; // 60min
const PARAMETERS = array( array( const PARAMETERS = [ [
'postcount' => array( 'postcount' => [
'name' => 'Limit', 'name' => 'Limit',
'type' => 'number', 'type' => 'number',
'required' => true, 'required' => true,
'title' => 'Maximum number of items to return', 'title' => 'Maximum number of items to return',
'defaultValue' => 5, 'defaultValue' => 5,
), ],
'language' => array( 'language' => [
'name' => 'Language', 'name' => 'Language',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'English' => 'en', 'English' => 'en',
'Deutsch' => 'de', 'Deutsch' => 'de',
'Polski' => 'pl', 'Polski' => 'pl',
'Français' => 'fr', 'Français' => 'fr',
'Русский' => 'ru', 'Русский' => 'ru',
'Português' => 'pt', 'Português' => 'pt',
'Español' => 'es', 'Español' => 'es',
), ],
'title' => 'Language of changelog posts', 'title' => 'Language of changelog posts',
'defaultValue' => 'en', 'defaultValue' => 'en',
), ],
'full' => array( 'full' => [
'name' => 'Full changelog', 'name' => 'Full changelog',
'type' => 'checkbox', 'type' => 'checkbox',
'required' => false, 'required' => false,
'title' => 'Enable to receive the full changelog post for each item' 'title' => 'Enable to receive the full changelog post for each item'
), ],
)); ]];
public function collectData() { public function collectData()
$api = 'https://albiononline.com/'; {
// Example: https://albiononline.com/en/changelog/1/5 $api = 'https://albiononline.com/';
$url = $api . $this->getInput('language') . '/changelog/1/' . $this->getInput('postcount'); // Example: https://albiononline.com/en/changelog/1/5
$url = $api . $this->getInput('language') . '/changelog/1/' . $this->getInput('postcount');
$html = getSimpleHTMLDOM($url); $html = getSimpleHTMLDOM($url);
foreach ($html->find('li') as $data) { foreach ($html->find('li') as $data) {
$item = array(); $item = [];
$item['uri'] = self::URI . $data->find('a', 0)->getAttribute('href'); $item['uri'] = self::URI . $data->find('a', 0)->getAttribute('href');
$item['title'] = trim(explode('|', $data->find('span', 0)->plaintext)[0]); $item['title'] = trim(explode('|', $data->find('span', 0)->plaintext)[0]);
// Time below work only with en lang. Need to think about solution. May be separate request like getFullChangelog, but to english list for all language // Time below work only with en lang. Need to think about solution. May be separate request like getFullChangelog, but to english list for all language
//print_r( date_parse_from_format( 'M j, Y' , 'Sep 9, 2020') ); //print_r( date_parse_from_format( 'M j, Y' , 'Sep 9, 2020') );
//$item['timestamp'] = $this->extractDate($a->plaintext); //$item['timestamp'] = $this->extractDate($a->plaintext);
$item['author'] = 'albiononline.com'; $item['author'] = 'albiononline.com';
if($this->getInput('full')) { if ($this->getInput('full')) {
$item['content'] = $this->getFullChangelog($item['uri']); $item['content'] = $this->getFullChangelog($item['uri']);
} else { } else {
//$item['content'] = trim(preg_replace('/\s+/', ' ', $data->find('span', 0)->plaintext)); //$item['content'] = trim(preg_replace('/\s+/', ' ', $data->find('span', 0)->plaintext));
// Just use title, no info at all or use title and date, see above // Just use title, no info at all or use title and date, see above
$item['content'] = $item['title']; $item['content'] = $item['title'];
} }
$item['uid'] = hash('sha256', $item['title']); $item['uid'] = hash('sha256', $item['title']);
$this->items[] = $item; $this->items[] = $item;
} }
} }
private function getFullChangelog($url) { private function getFullChangelog($url)
$html = getSimpleHTMLDOMCached($url); {
$html = defaultLinkTo($html, self::URI); $html = getSimpleHTMLDOMCached($url);
return $html->find('div.small-12.columns', 1)->innertext; $html = defaultLinkTo($html, self::URI);
} return $html->find('div.small-12.columns', 1)->innertext;
}
} }

View file

@ -1,83 +1,87 @@
<?php <?php
class AlfaBankByBridge extends BridgeAbstract {
const MAINTAINER = 'lassana'; class AlfaBankByBridge extends BridgeAbstract
const NAME = 'AlfaBank.by Новости'; {
const URI = 'https://www.alfabank.by'; const MAINTAINER = 'lassana';
const DESCRIPTION = 'Уведомления Alfa-Now — новости от Альфа-Банка'; const NAME = 'AlfaBank.by Новости';
const CACHE_TIMEOUT = 3600; // 1 hour const URI = 'https://www.alfabank.by';
const PARAMETERS = array( const DESCRIPTION = 'Уведомления Alfa-Now — новости от Альфа-Банка';
'News' => array( const CACHE_TIMEOUT = 3600; // 1 hour
'business' => array( const PARAMETERS = [
'name' => 'Альфа Бизнес', 'News' => [
'type' => 'list', 'business' => [
'title' => 'В зависимости от выбора, возращает уведомления для" . 'name' => 'Альфа Бизнес',
'type' => 'list',
'title' => 'В зависимости от выбора, возращает уведомления для" .
" клиентов физ. лиц либо для клиентов-юридических лиц и ИП', " клиентов физ. лиц либо для клиентов-юридических лиц и ИП',
'values' => array( 'values' => [
'Новости' => 'news', 'Новости' => 'news',
'Новости бизнеса' => 'newsBusiness' 'Новости бизнеса' => 'newsBusiness'
), ],
'defaultValue' => 'news' 'defaultValue' => 'news'
), ],
'fullContent' => array( 'fullContent' => [
'name' => 'Включать содержимое', 'name' => 'Включать содержимое',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Если выбрано, содержимое уведомлений вставляется в поток (работает медленно)' 'title' => 'Если выбрано, содержимое уведомлений вставляется в поток (работает медленно)'
) ]
) ]
); ];
public function collectData() { public function collectData()
$business = $this->getInput('business') == 'newsBusiness'; {
$fullContent = $this->getInput('fullContent') == 'on'; $business = $this->getInput('business') == 'newsBusiness';
$fullContent = $this->getInput('fullContent') == 'on';
$mainPageUrl = self::URI . '/about/articles/uvedomleniya/'; $mainPageUrl = self::URI . '/about/articles/uvedomleniya/';
if($business) { if ($business) {
$mainPageUrl .= '?business=true'; $mainPageUrl .= '?business=true';
} }
$html = getSimpleHTMLDOM($mainPageUrl); $html = getSimpleHTMLDOM($mainPageUrl);
$limit = 0; $limit = 0;
foreach($html->find('a.notifications__item') as $element) { foreach ($html->find('a.notifications__item') as $element) {
if($limit < 10) { if ($limit < 10) {
$item = array(); $item = [];
$item['uid'] = 'urn:sha1:' . hash('sha1', $element->getAttribute('data-notification-id')); $item['uid'] = 'urn:sha1:' . hash('sha1', $element->getAttribute('data-notification-id'));
$item['title'] = $element->find('div.item-title', 0)->innertext; $item['title'] = $element->find('div.item-title', 0)->innertext;
$item['timestamp'] = DateTime::createFromFormat( $item['timestamp'] = DateTime::createFromFormat(
'd M Y', 'd M Y',
$this->ruMonthsToEn($element->find('div.item-date', 0)->innertext) $this->ruMonthsToEn($element->find('div.item-date', 0)->innertext)
)->getTimestamp(); )->getTimestamp();
$itemUrl = self::URI . $element->href; $itemUrl = self::URI . $element->href;
if($business) { if ($business) {
$itemUrl = str_replace('?business=true', '', $itemUrl); $itemUrl = str_replace('?business=true', '', $itemUrl);
} }
$item['uri'] = $itemUrl; $item['uri'] = $itemUrl;
if($fullContent) { if ($fullContent) {
$itemHtml = getSimpleHTMLDOM($itemUrl); $itemHtml = getSimpleHTMLDOM($itemUrl);
if($itemHtml) { if ($itemHtml) {
$item['content'] = $itemHtml->find('div.now-p__content-text', 0)->innertext; $item['content'] = $itemHtml->find('div.now-p__content-text', 0)->innertext;
} }
} }
$this->items[] = $item; $this->items[] = $item;
$limit++; $limit++;
} }
} }
} }
public function getIcon() { public function getIcon()
return static::URI . '/local/images/favicon.ico'; {
} return static::URI . '/local/images/favicon.ico';
}
private function ruMonthsToEn($date) { private function ruMonthsToEn($date)
$ruMonths = array( {
'Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня', $ruMonths = [
'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря' ); 'Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня',
$enMonths = array( 'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря' ];
'January', 'February', 'March', 'April', 'May', 'June', $enMonths = [
'July', 'August', 'September', 'October', 'November', 'December' ); 'January', 'February', 'March', 'April', 'May', 'June',
return str_replace($ruMonths, $enMonths, $date); 'July', 'August', 'September', 'October', 'November', 'December' ];
} return str_replace($ruMonths, $enMonths, $date);
}
} }

View file

@ -1,113 +1,115 @@
<?php <?php
class AllocineFRBridge extends BridgeAbstract {
const MAINTAINER = 'superbaillot.net'; class AllocineFRBridge extends BridgeAbstract
const NAME = 'Allo Cine Bridge'; {
const CACHE_TIMEOUT = 25200; // 7h const MAINTAINER = 'superbaillot.net';
const URI = 'https://www.allocine.fr'; const NAME = 'Allo Cine Bridge';
const DESCRIPTION = 'Bridge for allocine.fr'; const CACHE_TIMEOUT = 25200; // 7h
const PARAMETERS = array( array( const URI = 'https://www.allocine.fr';
'category' => array( const DESCRIPTION = 'Bridge for allocine.fr';
'name' => 'Emission', const PARAMETERS = [ [
'type' => 'list', 'category' => [
'title' => 'Sélectionner l\'emission', 'name' => 'Emission',
'values' => array( 'type' => 'list',
'Faux Raccord' => 'faux-raccord', 'title' => 'Sélectionner l\'emission',
'Fanzone' => 'fanzone', 'values' => [
'Game In Ciné' => 'game-in-cine', 'Faux Raccord' => 'faux-raccord',
'Pour la faire courte' => 'pour-la-faire-courte', 'Fanzone' => 'fanzone',
'Home Cinéma' => 'home-cinema', 'Game In Ciné' => 'game-in-cine',
'PILS - Par Ici Les Sorties' => 'pils-par-ici-les-sorties', 'Pour la faire courte' => 'pour-la-faire-courte',
'AlloCiné : l\'émission, sur LeStream' => 'allocine-lemission-sur-lestream', 'Home Cinéma' => 'home-cinema',
'Give Me Five' => 'give-me-five', 'PILS - Par Ici Les Sorties' => 'pils-par-ici-les-sorties',
'Aviez-vous remarqué ?' => 'aviez-vous-remarque', 'AlloCiné : l\'émission, sur LeStream' => 'allocine-lemission-sur-lestream',
'Et paf, il est mort' => 'et-paf-il-est-mort', 'Give Me Five' => 'give-me-five',
'The Big Fan Theory' => 'the-big-fan-theory', 'Aviez-vous remarqué ?' => 'aviez-vous-remarque',
'Clichés' => 'cliches', 'Et paf, il est mort' => 'et-paf-il-est-mort',
'Complètement...' => 'completement', 'The Big Fan Theory' => 'the-big-fan-theory',
'#Fun Facts' => 'fun-facts', 'Clichés' => 'cliches',
'Origin Story' => 'origin-story', 'Complètement...' => 'completement',
) '#Fun Facts' => 'fun-facts',
) 'Origin Story' => 'origin-story',
)); ]
]
]];
public function getURI(){ public function getURI()
if(!is_null($this->getInput('category'))) { {
if (!is_null($this->getInput('category'))) {
$categories = [
'faux-raccord' => '/video/programme-12284/',
'fanzone' => '/video/programme-12298/',
'game-in-cine' => '/video/programme-12288/',
'pour-la-faire-courte' => '/video/programme-20960/',
'home-cinema' => '/video/programme-12287/',
'pils-par-ici-les-sorties' => '/video/programme-25789/',
'allocine-lemission-sur-lestream' => '/video/programme-25123/',
'give-me-five' => '/video/programme-21919/saison-34518/',
'aviez-vous-remarque' => '/video/programme-19518/',
'et-paf-il-est-mort' => '/video/programme-25113/',
'the-big-fan-theory' => '/video/programme-20403/',
'cliches' => '/video/programme-24834/',
'completement' => '/video/programme-23859/',
'fun-facts' => '/video/programme-23040/',
'origin-story' => '/video/programme-25667/'
];
$categories = array( $category = $this->getInput('category');
'faux-raccord' => '/video/programme-12284/', if (array_key_exists($category, $categories)) {
'fanzone' => '/video/programme-12298/', return static::URI . $this->getLastSeasonURI($categories[$category]);
'game-in-cine' => '/video/programme-12288/', } else {
'pour-la-faire-courte' => '/video/programme-20960/', returnClientError('Emission inconnue');
'home-cinema' => '/video/programme-12287/', }
'pils-par-ici-les-sorties' => '/video/programme-25789/', }
'allocine-lemission-sur-lestream' => '/video/programme-25123/',
'give-me-five' => '/video/programme-21919/saison-34518/',
'aviez-vous-remarque' => '/video/programme-19518/',
'et-paf-il-est-mort' => '/video/programme-25113/',
'the-big-fan-theory' => '/video/programme-20403/',
'cliches' => '/video/programme-24834/',
'completement' => '/video/programme-23859/',
'fun-facts' => '/video/programme-23040/',
'origin-story' => '/video/programme-25667/'
);
$category = $this->getInput('category'); return parent::getURI();
if(array_key_exists($category, $categories)) { }
return static::URI . $this->getLastSeasonURI($categories[$category]);
} else {
returnClientError('Emission inconnue');
}
}
return parent::getURI(); private function getLastSeasonURI($category)
} {
$html = getSimpleHTMLDOMCached(static::URI . $category, 86400);
$seasonLink = $html->find('section[class=section-wrap section]', 0)->find('div[class=cf]', 0)->find('a', 0);
$URI = $seasonLink->href;
return $URI;
}
private function getLastSeasonURI($category) public function getName()
{ {
$html = getSimpleHTMLDOMCached(static::URI . $category, 86400); if (!is_null($this->getInput('category'))) {
$seasonLink = $html->find('section[class=section-wrap section]', 0)->find('div[class=cf]', 0)->find('a', 0); return self::NAME . ' : '
$URI = $seasonLink->href; . array_search(
return $URI; $this->getInput('category'),
} self::PARAMETERS[$this->queriedContext]['category']['values']
);
}
public function getName(){ return parent::getName();
if(!is_null($this->getInput('category'))) { }
return self::NAME . ' : '
. array_search(
$this->getInput('category'),
self::PARAMETERS[$this->queriedContext]['category']['values']
);
}
return parent::getName(); public function collectData()
} {
$html = getSimpleHTMLDOM($this->getURI());
public function collectData(){ $category = array_search(
$this->getInput('category'),
self::PARAMETERS[$this->queriedContext]['category']['values']
);
foreach ($html->find('div[class=gd-col-left]', 0)->find('div[class*=video-card]') as $element) {
$item = [];
$html = getSimpleHTMLDOM($this->getURI()); $title = $element->find('a[class*=meta-title-link]', 0);
$content = trim(defaultLinkTo($element->outertext, static::URI));
$category = array_search( // Replace image 'src' with the one in 'data-src'
$this->getInput('category'), $content = preg_replace('@src="data:image/gif;base64,[A-Za-z0-9+\/]*"@', '', $content);
self::PARAMETERS[$this->queriedContext]['category']['values'] $content = preg_replace('@data-src=@', 'src=', $content);
);
foreach($html->find('div[class=gd-col-left]', 0)->find('div[class*=video-card]') as $element) {
$item = array();
$title = $element->find('a[class*=meta-title-link]', 0); // Remove date in the content to prevent content update while the video is getting older
$content = trim(defaultLinkTo($element->outertext, static::URI)); $content = preg_replace('@<div class="meta-sub light">.*<span>[^<]*</span>[^<]*</div>@', '', $content);
// Replace image 'src' with the one in 'data-src' $item['content'] = $content;
$content = preg_replace('@src="data:image/gif;base64,[A-Za-z0-9+\/]*"@', '', $content); $item['title'] = trim($title->innertext);
$content = preg_replace('@data-src=@', 'src=', $content); $item['uri'] = static::URI . '/' . substr($title->href, 1);
$this->items[] = $item;
// Remove date in the content to prevent content update while the video is getting older }
$content = preg_replace('@<div class="meta-sub light">.*<span>[^<]*</span>[^<]*</div>@', '', $content); }
$item['content'] = $content;
$item['title'] = trim($title->innertext);
$item['uri'] = static::URI . '/' . substr($title->href, 1);
$this->items[] = $item;
}
}
} }

View file

@ -1,103 +1,104 @@
<?php <?php
class AmazonBridge extends BridgeAbstract { class AmazonBridge extends BridgeAbstract
{
const MAINTAINER = 'Alexis CHEMEL';
const NAME = 'Amazon';
const URI = 'https://www.amazon.com/';
const CACHE_TIMEOUT = 3600; // 1h
const DESCRIPTION = 'Returns products from Amazon search';
const MAINTAINER = 'Alexis CHEMEL'; const PARAMETERS = [[
const NAME = 'Amazon'; 'q' => [
const URI = 'https://www.amazon.com/'; 'name' => 'Keyword',
const CACHE_TIMEOUT = 3600; // 1h 'required' => true,
const DESCRIPTION = 'Returns products from Amazon search'; 'exampleValue' => 'watch',
],
'sort' => [
'name' => 'Sort by',
'type' => 'list',
'values' => [
'Relevance' => 'relevanceblender',
'Price: Low to High' => 'price-asc-rank',
'Price: High to Low' => 'price-desc-rank',
'Average Customer Review' => 'review-rank',
'Newest Arrivals' => 'date-desc-rank',
],
'defaultValue' => 'relevanceblender',
],
'tld' => [
'name' => 'Country',
'type' => 'list',
'values' => [
'Australia' => 'com.au',
'Brazil' => 'com.br',
'Canada' => 'ca',
'China' => 'cn',
'France' => 'fr',
'Germany' => 'de',
'India' => 'in',
'Italy' => 'it',
'Japan' => 'co.jp',
'Mexico' => 'com.mx',
'Netherlands' => 'nl',
'Spain' => 'es',
'Sweden' => 'se',
'Turkey' => 'com.tr',
'United Kingdom' => 'co.uk',
'United States' => 'com',
],
'defaultValue' => 'com',
],
]];
const PARAMETERS = array(array( public function collectData()
'q' => array( {
'name' => 'Keyword', $baseUrl = sprintf('https://www.amazon.%s', $this->getInput('tld'));
'required' => true,
'exampleValue' => 'watch',
),
'sort' => array(
'name' => 'Sort by',
'type' => 'list',
'values' => array(
'Relevance' => 'relevanceblender',
'Price: Low to High' => 'price-asc-rank',
'Price: High to Low' => 'price-desc-rank',
'Average Customer Review' => 'review-rank',
'Newest Arrivals' => 'date-desc-rank',
),
'defaultValue' => 'relevanceblender',
),
'tld' => array(
'name' => 'Country',
'type' => 'list',
'values' => array(
'Australia' => 'com.au',
'Brazil' => 'com.br',
'Canada' => 'ca',
'China' => 'cn',
'France' => 'fr',
'Germany' => 'de',
'India' => 'in',
'Italy' => 'it',
'Japan' => 'co.jp',
'Mexico' => 'com.mx',
'Netherlands' => 'nl',
'Spain' => 'es',
'Sweden' => 'se',
'Turkey' => 'com.tr',
'United Kingdom' => 'co.uk',
'United States' => 'com',
),
'defaultValue' => 'com',
),
));
public function collectData() { $url = sprintf(
'%s/s/?field-keywords=%s&sort=%s',
$baseUrl,
urlencode($this->getInput('q')),
$this->getInput('sort')
);
$baseUrl = sprintf('https://www.amazon.%s', $this->getInput('tld')); $dom = getSimpleHTMLDOM($url);
$url = sprintf( $elements = $dom->find('div.s-result-item');
'%s/s/?field-keywords=%s&sort=%s',
$baseUrl,
urlencode($this->getInput('q')),
$this->getInput('sort')
);
$dom = getSimpleHTMLDOM($url); foreach ($elements as $element) {
$item = [];
$elements = $dom->find('div.s-result-item'); $title = $element->find('h2', 0);
if (!$title) {
continue;
}
foreach($elements as $element) { $item['title'] = $title->innertext;
$item = [];
$title = $element->find('h2', 0); $itemUrl = $element->find('a', 0)->href;
if (!$title) { $item['uri'] = urljoin($baseUrl, $itemUrl);
continue;
}
$item['title'] = $title->innertext; $image = $element->find('img', 0);
if ($image) {
$item['content'] = '<img src="' . $image->getAttribute('src') . '" /><br />';
}
$itemUrl = $element->find('a', 0)->href; $price = $element->find('span.a-price > .a-offscreen', 0);
$item['uri'] = urljoin($baseUrl, $itemUrl); if ($price) {
$item['content'] .= $price->innertext;
}
$image = $element->find('img', 0); $this->items[] = $item;
if ($image) { }
$item['content'] = '<img src="' . $image->getAttribute('src') . '" /><br />'; }
}
$price = $element->find('span.a-price > .a-offscreen', 0); public function getName()
if ($price) { {
$item['content'] .= $price->innertext; if (!is_null($this->getInput('tld')) && !is_null($this->getInput('q'))) {
} return 'Amazon.' . $this->getInput('tld') . ': ' . $this->getInput('q');
}
$this->items[] = $item; return parent::getName();
} }
}
public function getName(){
if(!is_null($this->getInput('tld')) && !is_null($this->getInput('q'))) {
return 'Amazon.' . $this->getInput('tld') . ': ' . $this->getInput('q');
}
return parent::getName();
}
} }

View file

@ -1,243 +1,257 @@
<?php <?php
class AmazonPriceTrackerBridge extends BridgeAbstract { class AmazonPriceTrackerBridge extends BridgeAbstract
const MAINTAINER = 'captn3m0, sal0max'; {
const NAME = 'Amazon Price Tracker'; const MAINTAINER = 'captn3m0, sal0max';
const URI = 'https://www.amazon.com/'; const NAME = 'Amazon Price Tracker';
const CACHE_TIMEOUT = 3600; // 1h const URI = 'https://www.amazon.com/';
const DESCRIPTION = 'Tracks price for a single product on Amazon'; const CACHE_TIMEOUT = 3600; // 1h
const DESCRIPTION = 'Tracks price for a single product on Amazon';
const PARAMETERS = array( const PARAMETERS = [
array( [
'asin' => array( 'asin' => [
'name' => 'ASIN', 'name' => 'ASIN',
'required' => true, 'required' => true,
'exampleValue' => 'B071GB1VMQ', 'exampleValue' => 'B071GB1VMQ',
// https://stackoverflow.com/a/12827734 // https://stackoverflow.com/a/12827734
'pattern' => 'B[\dA-Z]{9}|\d{9}(X|\d)', 'pattern' => 'B[\dA-Z]{9}|\d{9}(X|\d)',
), ],
'tld' => array( 'tld' => [
'name' => 'Country', 'name' => 'Country',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'Australia' => 'com.au', 'Australia' => 'com.au',
'Brazil' => 'com.br', 'Brazil' => 'com.br',
'Canada' => 'ca', 'Canada' => 'ca',
'China' => 'cn', 'China' => 'cn',
'France' => 'fr', 'France' => 'fr',
'Germany' => 'de', 'Germany' => 'de',
'India' => 'in', 'India' => 'in',
'Italy' => 'it', 'Italy' => 'it',
'Japan' => 'co.jp', 'Japan' => 'co.jp',
'Mexico' => 'com.mx', 'Mexico' => 'com.mx',
'Netherlands' => 'nl', 'Netherlands' => 'nl',
'Spain' => 'es', 'Spain' => 'es',
'Sweden' => 'se', 'Sweden' => 'se',
'Turkey' => 'com.tr', 'Turkey' => 'com.tr',
'United Kingdom' => 'co.uk', 'United Kingdom' => 'co.uk',
'United States' => 'com', 'United States' => 'com',
), ],
'defaultValue' => 'com', 'defaultValue' => 'com',
), ],
)); ]];
const PRICE_SELECTORS = array( const PRICE_SELECTORS = [
'#priceblock_ourprice', '#priceblock_ourprice',
'.priceBlockBuyingPriceString', '.priceBlockBuyingPriceString',
'#newBuyBoxPrice', '#newBuyBoxPrice',
'#tp_price_block_total_price_ww', '#tp_price_block_total_price_ww',
'span.offer-price', 'span.offer-price',
'.a-color-price', '.a-color-price',
); ];
const WHITESPACE = " \t\n\r\0\x0B\xC2\xA0"; const WHITESPACE = " \t\n\r\0\x0B\xC2\xA0";
protected $title; protected $title;
/** /**
* Generates domain name given a amazon TLD * Generates domain name given a amazon TLD
*/ */
private function getDomainName() { private function getDomainName()
return 'https://www.amazon.' . $this->getInput('tld'); {
} return 'https://www.amazon.' . $this->getInput('tld');
}
/** /**
* Generates URI for a Amazon product page * Generates URI for a Amazon product page
*/ */
public function getURI() { public function getURI()
if (!is_null($this->getInput('asin'))) { {
return $this->getDomainName() . '/dp/' . $this->getInput('asin'); if (!is_null($this->getInput('asin'))) {
} return $this->getDomainName() . '/dp/' . $this->getInput('asin');
return parent::getURI(); }
} return parent::getURI();
}
/** /**
* Scrapes the product title from the html page * Scrapes the product title from the html page
* returns the default title if scraping fails * returns the default title if scraping fails
*/ */
private function getTitle($html) { private function getTitle($html)
$titleTag = $html->find('#productTitle', 0); {
$titleTag = $html->find('#productTitle', 0);
if (!$titleTag) { if (!$titleTag) {
return $this->getDefaultTitle(); return $this->getDefaultTitle();
} else { } else {
return trim(html_entity_decode($titleTag->innertext, ENT_QUOTES)); return trim(html_entity_decode($titleTag->innertext, ENT_QUOTES));
} }
} }
/** /**
* Title used by the feed if none could be found * Title used by the feed if none could be found
*/ */
private function getDefaultTitle() { private function getDefaultTitle()
return 'Amazon.' . $this->getInput('tld') . ': ' . $this->getInput('asin'); {
} return 'Amazon.' . $this->getInput('tld') . ': ' . $this->getInput('asin');
}
/** /**
* Returns name for the feed * Returns name for the feed
* Uses title (already scraped) if it has one * Uses title (already scraped) if it has one
*/ */
public function getName() { public function getName()
if (isset($this->title)) { {
return $this->title; if (isset($this->title)) {
} else { return $this->title;
return parent::getName(); } else {
} return parent::getName();
} }
}
private function parseDynamicImage($attribute) { private function parseDynamicImage($attribute)
$json = json_decode(html_entity_decode($attribute), true); {
$json = json_decode(html_entity_decode($attribute), true);
if ($json and count($json) > 0) { if ($json and count($json) > 0) {
return array_keys($json)[0]; return array_keys($json)[0];
} }
} }
/** /**
* Returns a generated image tag for the product * Returns a generated image tag for the product
*/ */
private function getImage($html) { private function getImage($html)
$imageSrc = $html->find('#main-image-container img', 0); {
$imageSrc = $html->find('#main-image-container img', 0);
if ($imageSrc) { if ($imageSrc) {
$hiresImage = $imageSrc->getAttribute('data-old-hires'); $hiresImage = $imageSrc->getAttribute('data-old-hires');
$dynamicImageAttribute = $imageSrc->getAttribute('data-a-dynamic-image'); $dynamicImageAttribute = $imageSrc->getAttribute('data-a-dynamic-image');
$image = $hiresImage ?: $this->parseDynamicImage($dynamicImageAttribute); $image = $hiresImage ?: $this->parseDynamicImage($dynamicImageAttribute);
} }
$image = $image ?: 'https://placekitten.com/200/300'; $image = $image ?: 'https://placekitten.com/200/300';
return <<<EOT return <<<EOT
<img width="300" style="max-width:300;max-height:300" src="$image" alt="{$this->title}" /> <img width="300" style="max-width:300;max-height:300" src="$image" alt="{$this->title}" />
EOT; EOT;
} }
/** /**
* Return \simple_html_dom object * Return \simple_html_dom object
* for the entire html of the product page * for the entire html of the product page
*/ */
private function getHtml() { private function getHtml()
$uri = $this->getURI(); {
$uri = $this->getURI();
return getSimpleHTMLDOM($uri) ?: returnServerError('Could not request Amazon.'); return getSimpleHTMLDOM($uri) ?: returnServerError('Could not request Amazon.');
} }
private function scrapePriceFromMetrics($html) { private function scrapePriceFromMetrics($html)
$asinData = $html->find('#cerberus-data-metrics', 0); {
$asinData = $html->find('#cerberus-data-metrics', 0);
// <div id="cerberus-data-metrics" style="display: none;" // <div id="cerberus-data-metrics" style="display: none;"
// data-asin="B00WTHJ5SU" data-asin-price="14.99" data-asin-shipping="0" // data-asin="B00WTHJ5SU" data-asin-price="14.99" data-asin-shipping="0"
// data-asin-currency-code="USD" data-substitute-count="-1" ... /> // data-asin-currency-code="USD" data-substitute-count="-1" ... />
if ($asinData) { if ($asinData) {
return array( return [
'price' => $asinData->getAttribute('data-asin-price'), 'price' => $asinData->getAttribute('data-asin-price'),
'currency' => $asinData->getAttribute('data-asin-currency-code'), 'currency' => $asinData->getAttribute('data-asin-currency-code'),
'shipping' => $asinData->getAttribute('data-asin-shipping') 'shipping' => $asinData->getAttribute('data-asin-shipping')
); ];
} }
return false; return false;
} }
private function scrapePriceTwister($html) { private function scrapePriceTwister($html)
$str = $html->find('.twister-plus-buying-options-price-data', 0); {
$str = $html->find('.twister-plus-buying-options-price-data', 0);
$data = json_decode($str->innertext, true); $data = json_decode($str->innertext, true);
if(count($data) === 1) { if (count($data) === 1) {
$data = $data[0]; $data = $data[0];
return array( return [
'displayPrice' => $data['displayPrice'], 'displayPrice' => $data['displayPrice'],
'currency' => $data['currency'], 'currency' => $data['currency'],
'shipping' => '0', 'shipping' => '0',
); ];
} }
return false; return false;
} }
private function scrapePriceGeneric($html) { private function scrapePriceGeneric($html)
$priceDiv = null; {
$priceDiv = null;
foreach(self::PRICE_SELECTORS as $sel) { foreach (self::PRICE_SELECTORS as $sel) {
$priceDiv = $html->find($sel, 0); $priceDiv = $html->find($sel, 0);
if ($priceDiv) { if ($priceDiv) {
break; break;
} }
} }
if (!$priceDiv) { if (!$priceDiv) {
return false; return false;
} }
$priceString = str_replace(str_split(self::WHITESPACE), '', $priceDiv->plaintext); $priceString = str_replace(str_split(self::WHITESPACE), '', $priceDiv->plaintext);
preg_match('/(\d+\.\d{0,2})/', $priceString, $matches); preg_match('/(\d+\.\d{0,2})/', $priceString, $matches);
$price = $matches[0]; $price = $matches[0];
$currency = str_replace($price, '', $priceString); $currency = str_replace($price, '', $priceString);
if ($price != null && $currency != null) { if ($price != null && $currency != null) {
return array( return [
'price' => $price, 'price' => $price,
'currency' => $currency, 'currency' => $currency,
'shipping' => '0' 'shipping' => '0'
); ];
} }
return false; return false;
} }
private function renderContent($image, $data) { private function renderContent($image, $data)
$price = $data['displayPrice']; {
if (!$price) { $price = $data['displayPrice'];
$price = "{$data['price']} {$data['currency']}"; if (!$price) {
} $price = "{$data['price']} {$data['currency']}";
}
$html = "$image<br>Price: $price"; $html = "$image<br>Price: $price";
if ($data['shipping'] !== '0') { if ($data['shipping'] !== '0') {
$html .= "<br>Shipping: {$data['shipping']} {$data['currency']}</br>"; $html .= "<br>Shipping: {$data['shipping']} {$data['currency']}</br>";
} }
return $html; return $html;
} }
/** /**
* Scrape method for Amazon product page * Scrape method for Amazon product page
* @return [type] [description] * @return [type] [description]
*/ */
public function collectData() { public function collectData()
$html = $this->getHtml(); {
$this->title = $this->getTitle($html); $html = $this->getHtml();
$imageTag = $this->getImage($html); $this->title = $this->getTitle($html);
$imageTag = $this->getImage($html);
$data = $this->scrapePriceGeneric($html); $data = $this->scrapePriceGeneric($html);
$item = array( $item = [
'title' => $this->title, 'title' => $this->title,
'uri' => $this->getURI(), 'uri' => $this->getURI(),
'content' => $this->renderContent($imageTag, $data), 'content' => $this->renderContent($imageTag, $data),
// This is to ensure that feed readers notice the price change // This is to ensure that feed readers notice the price change
'uid' => md5($data['price']) 'uid' => md5($data['price'])
); ];
$this->items[] = $item; $this->items[] = $item;
} }
} }

View file

@ -1,217 +1,218 @@
<?php <?php
class AnidexBridge extends BridgeAbstract {
const MAINTAINER = 'ORelio'; class AnidexBridge extends BridgeAbstract
const NAME = 'Anidex'; {
const URI = 'http://anidex.info/'; // anidex.info has ddos-guard so we need to use anidex.moe const MAINTAINER = 'ORelio';
const ALTERNATE_URI = 'https://anidex.moe/'; // anidex.moe returns 301 unless Host is set to anidex.info const NAME = 'Anidex';
const ALTERNATE_HOST = 'anidex.info'; // Correct host for requesting anidex.moe without 301 redirect const URI = 'http://anidex.info/'; // anidex.info has ddos-guard so we need to use anidex.moe
const DESCRIPTION = 'Returns the newest torrents, with optional search criteria.'; const ALTERNATE_URI = 'https://anidex.moe/'; // anidex.moe returns 301 unless Host is set to anidex.info
const PARAMETERS = array( const ALTERNATE_HOST = 'anidex.info'; // Correct host for requesting anidex.moe without 301 redirect
array( const DESCRIPTION = 'Returns the newest torrents, with optional search criteria.';
'id' => array( const PARAMETERS = [
'name' => 'Category', [
'type' => 'list', 'id' => [
'values' => array( 'name' => 'Category',
'All categories' => '0', 'type' => 'list',
'Anime' => '1,2,3', 'values' => [
'Anime - Sub' => '1', 'All categories' => '0',
'Anime - Raw' => '2', 'Anime' => '1,2,3',
'Anime - Dub' => '3', 'Anime - Sub' => '1',
'Live Action' => '4,5', 'Anime - Raw' => '2',
'Live Action - Sub' => '4', 'Anime - Dub' => '3',
'Live Action - Raw' => '5', 'Live Action' => '4,5',
'Light Novel' => '6', 'Live Action - Sub' => '4',
'Manga' => '7,8', 'Live Action - Raw' => '5',
'Manga - Translated' => '7', 'Light Novel' => '6',
'Manga - Raw' => '8', 'Manga' => '7,8',
'Music' => '9,10,11', 'Manga - Translated' => '7',
'Music - Lossy' => '9', 'Manga - Raw' => '8',
'Music - Lossless' => '10', 'Music' => '9,10,11',
'Music - Video' => '11', 'Music - Lossy' => '9',
'Games' => '12', 'Music - Lossless' => '10',
'Applications' => '13', 'Music - Video' => '11',
'Pictures' => '14', 'Games' => '12',
'Adult Video' => '15', 'Applications' => '13',
'Other' => '16' 'Pictures' => '14',
) 'Adult Video' => '15',
), 'Other' => '16'
'lang_id' => array( ]
'name' => 'Language', ],
'type' => 'list', 'lang_id' => [
'values' => array( 'name' => 'Language',
'All languages' => '0', 'type' => 'list',
'English' => '1', 'values' => [
'Japanese' => '2', 'All languages' => '0',
'Polish' => '3', 'English' => '1',
'Serbo-Croatian' => '4', 'Japanese' => '2',
'Dutch' => '5', 'Polish' => '3',
'Italian' => '6', 'Serbo-Croatian' => '4',
'Russian' => '7', 'Dutch' => '5',
'German' => '8', 'Italian' => '6',
'Hungarian' => '9', 'Russian' => '7',
'French' => '10', 'German' => '8',
'Finnish' => '11', 'Hungarian' => '9',
'Vietnamese' => '12', 'French' => '10',
'Greek' => '13', 'Finnish' => '11',
'Bulgarian' => '14', 'Vietnamese' => '12',
'Spanish (Spain)' => '15', 'Greek' => '13',
'Portuguese (Brazil)' => '16', 'Bulgarian' => '14',
'Portuguese (Portugal)' => '17', 'Spanish (Spain)' => '15',
'Swedish' => '18', 'Portuguese (Brazil)' => '16',
'Arabic' => '19', 'Portuguese (Portugal)' => '17',
'Danish' => '20', 'Swedish' => '18',
'Chinese (Simplified)' => '21', 'Arabic' => '19',
'Bengali' => '22', 'Danish' => '20',
'Romanian' => '23', 'Chinese (Simplified)' => '21',
'Czech' => '24', 'Bengali' => '22',
'Mongolian' => '25', 'Romanian' => '23',
'Turkish' => '26', 'Czech' => '24',
'Indonesian' => '27', 'Mongolian' => '25',
'Korean' => '28', 'Turkish' => '26',
'Spanish (LATAM)' => '29', 'Indonesian' => '27',
'Persian' => '30', 'Korean' => '28',
'Malaysian' => '31' 'Spanish (LATAM)' => '29',
) 'Persian' => '30',
), 'Malaysian' => '31'
'group_id' => array( ]
'name' => 'Group ID', ],
'type' => 'number' 'group_id' => [
), 'name' => 'Group ID',
'r' => array( 'type' => 'number'
'name' => 'Hide Remakes', ],
'type' => 'checkbox' 'r' => [
), 'name' => 'Hide Remakes',
'b' => array( 'type' => 'checkbox'
'name' => 'Only Batches', ],
'type' => 'checkbox' 'b' => [
), 'name' => 'Only Batches',
'a' => array( 'type' => 'checkbox'
'name' => 'Only Authorized', ],
'type' => 'checkbox' 'a' => [
), 'name' => 'Only Authorized',
'q' => array( 'type' => 'checkbox'
'name' => 'Keyword', ],
'description' => 'Keyword(s)', 'q' => [
'type' => 'text' 'name' => 'Keyword',
), 'description' => 'Keyword(s)',
'h' => array( 'type' => 'text'
'name' => 'Adult content', ],
'type' => 'list', 'h' => [
'values' => array( 'name' => 'Adult content',
'No filter' => '0', 'type' => 'list',
'Hide +18' => '1', 'values' => [
'Only +18' => '2' 'No filter' => '0',
) 'Hide +18' => '1',
) 'Only +18' => '2'
) ]
); ]
]
];
public function collectData() { public function collectData()
{
// Build Search URL from user-provided parameters
$search_url = self::ALTERNATE_URI . '?s=upload_timestamp&o=desc';
foreach (['id', 'lang_id', 'group_id'] as $param_name) {
$param = $this->getInput($param_name);
if (!empty($param) && intval($param) != 0 && ctype_digit(str_replace(',', '', $param))) {
$search_url .= '&' . $param_name . '=' . $param;
}
}
foreach (['r', 'b', 'a'] as $param_name) {
$param = $this->getInput($param_name);
if (!empty($param) && boolval($param)) {
$search_url .= '&' . $param_name . '=1';
}
}
$query = $this->getInput('q');
if (!empty($query)) {
$search_url .= '&q=' . urlencode($query);
}
$opt = [];
$h = $this->getInput('h');
if (!empty($h) && intval($h) != 0 && ctype_digit($h)) {
$opt[CURLOPT_COOKIE] = 'anidex_h_toggle=' . $h;
}
// Build Search URL from user-provided parameters // We need to use a different Host HTTP header to reach the correct page on ALTERNATE_URI
$search_url = self::ALTERNATE_URI . '?s=upload_timestamp&o=desc'; $headers = ['Host: ' . self::ALTERNATE_HOST];
foreach (array('id', 'lang_id', 'group_id') as $param_name) {
$param = $this->getInput($param_name);
if (!empty($param) && intval($param) != 0 && ctype_digit(str_replace(',', '', $param))) {
$search_url .= '&' . $param_name . '=' . $param;
}
}
foreach (array('r', 'b', 'a') as $param_name) {
$param = $this->getInput($param_name);
if (!empty($param) && boolval($param)) {
$search_url .= '&' . $param_name . '=1';
}
}
$query = $this->getInput('q');
if (!empty($query)) {
$search_url .= '&q=' . urlencode($query);
}
$opt = array();
$h = $this->getInput('h');
if (!empty($h) && intval($h) != 0 && ctype_digit($h)) {
$opt[CURLOPT_COOKIE] = 'anidex_h_toggle=' . $h;
}
// We need to use a different Host HTTP header to reach the correct page on ALTERNATE_URI // The HTTPS certificate presented by anidex.moe is for anidex.info. We need to ignore this.
$headers = array('Host: ' . self::ALTERNATE_HOST); // As a consequence, the bridge is intentionally marked as insecure by setting self::URI to http://
$opt[CURLOPT_SSL_VERIFYHOST] = 0;
$opt[CURLOPT_SSL_VERIFYPEER] = 0;
// The HTTPS certificate presented by anidex.moe is for anidex.info. We need to ignore this. // Retrieve torrent listing from search results, which does not contain torrent description
// As a consequence, the bridge is intentionally marked as insecure by setting self::URI to http:// $html = getSimpleHTMLDOM($search_url, $headers, $opt);
$opt[CURLOPT_SSL_VERIFYHOST] = 0; $links = $html->find('a');
$opt[CURLOPT_SSL_VERIFYPEER] = 0; $results = [];
foreach ($links as $link) {
if (strpos($link->href, '/torrent/') === 0 && !in_array($link->href, $results)) {
$results[] = $link->href;
}
}
if (empty($results) && empty($this->getInput('q'))) {
returnServerError('No results from Anidex: ' . $search_url);
}
// Retrieve torrent listing from search results, which does not contain torrent description //Process each item individually
$html = getSimpleHTMLDOM($search_url, $headers, $opt); foreach ($results as $element) {
$links = $html->find('a'); //Limit total amount of requests
$results = array(); if (count($this->items) >= 20) {
foreach ($links as $link) break;
if (strpos($link->href, '/torrent/') === 0 && !in_array($link->href, $results)) }
$results[] = $link->href;
if (empty($results) && empty($this->getInput('q')))
returnServerError('No results from Anidex: ' . $search_url);
//Process each item individually $torrent_id = str_replace('/torrent/', '', $element);
foreach ($results as $element) {
//Limit total amount of requests //Ignore entries without valid torrent ID
if(count($this->items) >= 20) { if ($torrent_id != 0 && ctype_digit($torrent_id)) {
break; //Retrieve data for this torrent ID
} $item_browse_uri = self::URI . 'torrent/' . $torrent_id;
$item_fetch_uri = self::ALTERNATE_URI . 'torrent/' . $torrent_id;
$torrent_id = str_replace('/torrent/', '', $element); //Retrieve full description from torrent page (cached for 24 hours: 86400 seconds)
if ($item_html = getSimpleHTMLDOMCached($item_fetch_uri, 86400, $headers, $opt)) {
//Retrieve data from page contents
$item_title = str_replace(' (Torrent) - AniDex ', '', $item_html->find('title', 0)->plaintext);
$item_desc = $item_html->find('div.panel-body', 0);
$item_author = trim($item_html->find('span.fa-user', 0)->parent()->plaintext);
$item_date = strtotime(trim($item_html->find('span.fa-clock', 0)->parent()->plaintext));
$item_image = $this->getURI() . 'images/user_logos/default.png';
//Ignore entries without valid torrent ID //Check for description-less torrent andn optionally extract image
if ($torrent_id != 0 && ctype_digit($torrent_id)) { $desc_title_found = false;
foreach ($item_html->find('h3.panel-title') as $h3) {
if (strpos($h3, 'Description') !== false) {
$desc_title_found = true;
break;
}
}
if ($desc_title_found) {
//Retrieve image for thumbnail or generic logo fallback
foreach ($item_desc->find('img') as $img) {
if (strpos($img->src, 'prez') === false) {
$item_image = $img->src;
break;
}
}
$item_desc = trim($item_desc->innertext);
} else {
$item_desc = '<em>No description.</em>';
}
//Retrieve data for this torrent ID //Build and add final item
$item_browse_uri = self::URI . 'torrent/' . $torrent_id; $item = [];
$item_fetch_uri = self::ALTERNATE_URI . 'torrent/' . $torrent_id; $item['uri'] = $item_browse_uri;
$item['title'] = $item_title;
//Retrieve full description from torrent page (cached for 24 hours: 86400 seconds) $item['author'] = $item_author;
if ($item_html = getSimpleHTMLDOMCached($item_fetch_uri, 86400, $headers, $opt)) { $item['timestamp'] = $item_date;
$item['enclosures'] = [$item_image];
//Retrieve data from page contents $item['content'] = $item_desc;
$item_title = str_replace(' (Torrent) - AniDex ', '', $item_html->find('title', 0)->plaintext); $this->items[] = $item;
$item_desc = $item_html->find('div.panel-body', 0); }
$item_author = trim($item_html->find('span.fa-user', 0)->parent()->plaintext); }
$item_date = strtotime(trim($item_html->find('span.fa-clock', 0)->parent()->plaintext)); $element = null;
$item_image = $this->getURI() . 'images/user_logos/default.png'; }
$results = null;
//Check for description-less torrent andn optionally extract image }
$desc_title_found = false;
foreach ($item_html->find('h3.panel-title') as $h3) {
if (strpos($h3, 'Description') !== false) {
$desc_title_found = true;
break;
}
}
if ($desc_title_found) {
//Retrieve image for thumbnail or generic logo fallback
foreach ($item_desc->find('img') as $img) {
if (strpos($img->src, 'prez') === false) {
$item_image = $img->src;
break;
}
}
$item_desc = trim($item_desc->innertext);
} else {
$item_desc = '<em>No description.</em>';
}
//Build and add final item
$item = array();
$item['uri'] = $item_browse_uri;
$item['title'] = $item_title;
$item['author'] = $item_author;
$item['timestamp'] = $item_date;
$item['enclosures'] = array($item_image);
$item['content'] = $item_desc;
$this->items[] = $item;
}
}
$element = null;
}
$results = null;
}
} }

View file

@ -1,141 +1,141 @@
<?php <?php
class AnimeUltimeBridge extends BridgeAbstract {
const MAINTAINER = 'ORelio'; class AnimeUltimeBridge extends BridgeAbstract
const NAME = 'Anime-Ultime'; {
const URI = 'http://www.anime-ultime.net/'; const MAINTAINER = 'ORelio';
const CACHE_TIMEOUT = 10800; // 3h const NAME = 'Anime-Ultime';
const DESCRIPTION = 'Returns the newest releases posted on Anime-Ultime.'; const URI = 'http://www.anime-ultime.net/';
const PARAMETERS = array( array( const CACHE_TIMEOUT = 10800; // 3h
'type' => array( const DESCRIPTION = 'Returns the newest releases posted on Anime-Ultime.';
'name' => 'Type', const PARAMETERS = [ [
'type' => 'list', 'type' => [
'values' => array( 'name' => 'Type',
'Everything' => '', 'type' => 'list',
'Anime' => 'A', 'values' => [
'Drama' => 'D', 'Everything' => '',
'Tokusatsu' => 'T' 'Anime' => 'A',
) 'Drama' => 'D',
) 'Tokusatsu' => 'T'
)); ]
]
]];
private $filter = 'Releases'; private $filter = 'Releases';
public function collectData(){ public function collectData()
{
//Add type filter if provided
$typeFilter = array_search(
$this->getInput('type'),
self::PARAMETERS[$this->queriedContext]['type']['values']
);
//Add type filter if provided //Build date and filters for making requests
$typeFilter = array_search( $thismonth = date('mY') . $typeFilter;
$this->getInput('type'), $lastmonth = date('mY', mktime(0, 0, 0, date('n') - 1, 1, date('Y'))) . $typeFilter;
self::PARAMETERS[$this->queriedContext]['type']['values']
);
//Build date and filters for making requests //Process each HTML page until having 10 releases
$thismonth = date('mY') . $typeFilter; $processedOK = 0;
$lastmonth = date('mY', mktime(0, 0, 0, date('n') - 1, 1, date('Y'))) . $typeFilter; foreach ([$thismonth, $lastmonth] as $requestFilter) {
$url = self::URI . 'history-0-1/' . $requestFilter;
$html = getContents($url);
// Convert html from iso-8859-1 => utf8
$html = utf8_encode($html);
$html = str_get_html($html);
//Process each HTML page until having 10 releases //Relases are sorted by day : process each day individually
$processedOK = 0; foreach ($html->find('div.history', 0)->find('h3') as $daySection) {
foreach (array($thismonth, $lastmonth) as $requestFilter) { //Retrieve day and build date information
$dateString = $daySection->plaintext;
$day = intval(substr($dateString, strpos($dateString, ' ') + 1, 2));
$item_date = strtotime(str_pad($day, 2, '0', STR_PAD_LEFT)
. '-'
. substr($requestFilter, 0, 2)
. '-'
. substr($requestFilter, 2, 4));
$url = self::URI . 'history-0-1/' . $requestFilter; //<h3>day</h3><br /><table><tr> <-- useful data in table rows
$html = getContents($url); $release = $daySection->next_sibling()->next_sibling()->first_child();
// Convert html from iso-8859-1 => utf8
$html = utf8_encode($html);
$html = str_get_html($html);
//Relases are sorted by day : process each day individually //Process each release of that day, ignoring first table row: contains table headers
foreach($html->find('div.history', 0)->find('h3') as $daySection) { while (!is_null($release = $release->next_sibling())) {
if (count($release->find('td')) > 0) {
//Retrieve metadata from table columns
$item_link_element = $release->find('td', 0)->find('a', 0);
$item_uri = self::URI . $item_link_element->href;
$item_name = html_entity_decode($item_link_element->plaintext);
//Retrieve day and build date information $item_image = self::URI . substr(
$dateString = $daySection->plaintext; $item_link_element->onmouseover,
$day = intval(substr($dateString, strpos($dateString, ' ') + 1, 2)); 37,
$item_date = strtotime(str_pad($day, 2, '0', STR_PAD_LEFT) strpos($item_link_element->onmouseover, ' ', 37) - 37
. '-' );
. substr($requestFilter, 0, 2)
. '-'
. substr($requestFilter, 2, 4));
//<h3>day</h3><br /><table><tr> <-- useful data in table rows $item_episode = html_entity_decode(
$release = $daySection->next_sibling()->next_sibling()->first_child(); str_pad(
$release->find('td', 1)->plaintext,
2,
'0',
STR_PAD_LEFT
)
);
//Process each release of that day, ignoring first table row: contains table headers $item_fansub = $release->find('td', 2)->plaintext;
while(!is_null($release = $release->next_sibling())) { $item_type = $release->find('td', 4)->plaintext;
if(count($release->find('td')) > 0) {
//Retrieve metadata from table columns if (!empty($item_uri)) {
$item_link_element = $release->find('td', 0)->find('a', 0); // Retrieve description from description page
$item_uri = self::URI . $item_link_element->href; $html_item = getContents($item_uri);
$item_name = html_entity_decode($item_link_element->plaintext); // Convert html from iso-8859-1 => utf8
$html_item = utf8_encode($html_item);
$item_description = substr(
$html_item,
strpos($html_item, 'class="principal_contain" align="center">') + 41
);
$item_description = substr(
$item_description,
0,
strpos($item_description, '<div id="table">')
);
$item_image = self::URI . substr( // Convert relative image src into absolute image src, remove line breaks
$item_link_element->onmouseover, $item_description = defaultLinkTo($item_description, self::URI);
37, $item_description = str_replace("\r", '', $item_description);
strpos($item_link_element->onmouseover, ' ', 37) - 37 $item_description = str_replace("\n", '', $item_description);
);
$item_episode = html_entity_decode( //Build and add final item
str_pad( $item = [];
$release->find('td', 1)->plaintext, $item['uri'] = $item_uri;
2, $item['title'] = $item_name . ' ' . $item_type . ' ' . $item_episode;
'0', $item['author'] = $item_fansub;
STR_PAD_LEFT $item['timestamp'] = $item_date;
) $item['enclosures'] = [$item_image];
); $item['content'] = $item_description;
$this->items[] = $item;
$processedOK++;
$item_fansub = $release->find('td', 2)->plaintext; //Stop processing once limit is reached
$item_type = $release->find('td', 4)->plaintext; if ($processedOK >= 10) {
return;
}
}
}
}
}
}
}
if(!empty($item_uri)) { public function getName()
{
if (!is_null($this->getInput('type'))) {
$typeFilter = array_search(
$this->getInput('type'),
self::PARAMETERS[$this->queriedContext]['type']['values']
);
// Retrieve description from description page return 'Latest ' . $typeFilter . ' - Anime-Ultime Bridge';
$html_item = getContents($item_uri); }
// Convert html from iso-8859-1 => utf8
$html_item = utf8_encode($html_item);
$item_description = substr(
$html_item,
strpos($html_item, 'class="principal_contain" align="center">') + 41
);
$item_description = substr($item_description,
0,
strpos($item_description, '<div id="table">')
);
// Convert relative image src into absolute image src, remove line breaks return parent::getName();
$item_description = defaultLinkTo($item_description, self::URI); }
$item_description = str_replace("\r", '', $item_description);
$item_description = str_replace("\n", '', $item_description);
//Build and add final item
$item = array();
$item['uri'] = $item_uri;
$item['title'] = $item_name . ' ' . $item_type . ' ' . $item_episode;
$item['author'] = $item_fansub;
$item['timestamp'] = $item_date;
$item['enclosures'] = array($item_image);
$item['content'] = $item_description;
$this->items[] = $item;
$processedOK++;
//Stop processing once limit is reached
if ($processedOK >= 10)
return;
}
}
}
}
}
}
public function getName() {
if(!is_null($this->getInput('type'))) {
$typeFilter = array_search(
$this->getInput('type'),
self::PARAMETERS[$this->queriedContext]['type']['values']
);
return 'Latest ' . $typeFilter . ' - Anime-Ultime Bridge';
}
return parent::getName();
}
} }

View file

@ -1,151 +1,159 @@
<?php <?php
class AppleAppStoreBridge extends BridgeAbstract { class AppleAppStoreBridge extends BridgeAbstract
{
const MAINTAINER = 'captn3m0';
const NAME = 'Apple App Store';
const URI = 'https://apps.apple.com/';
const CACHE_TIMEOUT = 3600; // 1h
const DESCRIPTION = 'Returns version updates for a specific application';
const MAINTAINER = 'captn3m0'; const PARAMETERS = [[
const NAME = 'Apple App Store'; 'id' => [
const URI = 'https://apps.apple.com/'; 'name' => 'Application ID',
const CACHE_TIMEOUT = 3600; // 1h 'required' => true,
const DESCRIPTION = 'Returns version updates for a specific application'; 'exampleValue' => '310633997'
],
'p' => [
'name' => 'Platform',
'type' => 'list',
'values' => [
'iPad' => 'ipad',
'iPhone' => 'iphone',
'Mac' => 'mac',
const PARAMETERS = array(array( // The following 2 are present in responses
'id' => array( // but not yet tested
'name' => 'Application ID', 'Web' => 'web',
'required' => true, 'Apple TV' => 'appletv',
'exampleValue' => '310633997' ],
), 'defaultValue' => 'iphone',
'p' => array( ],
'name' => 'Platform', 'country' => [
'type' => 'list', 'name' => 'Store Country',
'values' => array( 'type' => 'list',
'iPad' => 'ipad', 'values' => [
'iPhone' => 'iphone', 'US' => 'US',
'Mac' => 'mac', 'India' => 'IN',
'Canada' => 'CA',
'Germany' => 'DE',
],
'defaultValue' => 'US',
],
]];
// The following 2 are present in responses const PLATFORM_MAPPING = [
// but not yet tested 'iphone' => 'ios',
'Web' => 'web', 'ipad' => 'ios',
'Apple TV' => 'appletv', ];
),
'defaultValue' => 'iphone',
),
'country' => array(
'name' => 'Store Country',
'type' => 'list',
'values' => array(
'US' => 'US',
'India' => 'IN',
'Canada' => 'CA',
'Germany' => 'DE',
),
'defaultValue' => 'US',
),
));
const PLATFORM_MAPPING = array( private function makeHtmlUrl($id, $country)
'iphone' => 'ios', {
'ipad' => 'ios', return 'https://apps.apple.com/' . $country . '/app/id' . $id;
); }
private function makeHtmlUrl($id, $country){ private function makeJsonUrl($id, $platform, $country)
return 'https://apps.apple.com/' . $country . '/app/id' . $id; {
} return "https://amp-api.apps.apple.com/v1/catalog/$country/apps/$id?platform=$platform&extend=versionHistory";
}
private function makeJsonUrl($id, $platform, $country){ public function getName()
return "https://amp-api.apps.apple.com/v1/catalog/$country/apps/$id?platform=$platform&extend=versionHistory"; {
} if (isset($this->name)) {
return $this->name . ' - AppStore Updates';
}
public function getName(){ return parent::getName();
if (isset($this->name)) { }
return $this->name . ' - AppStore Updates';
}
return parent::getName(); /**
} * In case of some platforms, the data is present in the initial response
*/
private function getDataFromShoebox($id, $platform, $country)
{
$uri = $this->makeHtmlUrl($id, $country);
$html = getSimpleHTMLDOMCached($uri, 3600);
$script = $html->find('script[id="shoebox-ember-data-store"]', 0);
/** $json = json_decode($script->innertext, true);
* In case of some platforms, the data is present in the initial response return $json['data'];
*/ }
private function getDataFromShoebox($id, $platform, $country){
$uri = $this->makeHtmlUrl($id, $country);
$html = getSimpleHTMLDOMCached($uri, 3600);
$script = $html->find('script[id="shoebox-ember-data-store"]', 0);
$json = json_decode($script->innertext, true); private function getJWTToken($id, $platform, $country)
return $json['data']; {
} $uri = $this->makeHtmlUrl($id, $country);
private function getJWTToken($id, $platform, $country){ $html = getSimpleHTMLDOMCached($uri, 3600);
$uri = $this->makeHtmlUrl($id, $country);
$html = getSimpleHTMLDOMCached($uri, 3600); $meta = $html->find('meta[name="web-experience-app/config/environment"]', 0);
$meta = $html->find('meta[name="web-experience-app/config/environment"]', 0); $json = urldecode($meta->content);
$json = urldecode($meta->content); $json = json_decode($json);
$json = json_decode($json); return $json->MEDIA_API->token;
}
return $json->MEDIA_API->token; private function getAppData($id, $platform, $country, $token)
} {
$uri = $this->makeJsonUrl($id, $platform, $country);
private function getAppData($id, $platform, $country, $token){ $headers = [
$uri = $this->makeJsonUrl($id, $platform, $country); "Authorization: Bearer $token",
'Origin: https://apps.apple.com',
];
$headers = array( $json = json_decode(getContents($uri, $headers), true);
"Authorization: Bearer $token",
'Origin: https://apps.apple.com',
);
$json = json_decode(getContents($uri, $headers), true); return $json['data'][0];
}
return $json['data'][0]; /**
} * Parses the version history from the data received
* @return array list of versions with details on each element
*/
private function getVersionHistory($data, $platform)
{
switch ($platform) {
case 'mac':
return $data['relationships']['platforms']['data'][0]['attributes']['versionHistory'];
default:
$os = self::PLATFORM_MAPPING[$platform];
return $data['attributes']['platformAttributes'][$os]['versionHistory'];
}
}
/** public function collectData()
* Parses the version history from the data received {
* @return array list of versions with details on each element $id = $this->getInput('id');
*/ $country = $this->getInput('country');
private function getVersionHistory($data, $platform){ $platform = $this->getInput('p');
switch($platform) {
case 'mac':
return $data['relationships']['platforms']['data'][0]['attributes']['versionHistory'];
default:
$os = self::PLATFORM_MAPPING[$platform];
return $data['attributes']['platformAttributes'][$os]['versionHistory'];
}
}
public function collectData() { switch ($platform) {
$id = $this->getInput('id'); case 'mac':
$country = $this->getInput('country'); $data = $this->getDataFromShoebox($id, $platform, $country);
$platform = $this->getInput('p'); break;
switch ($platform) { default:
case 'mac': $token = $this->getJWTToken($id, $platform, $country);
$data = $this->getDataFromShoebox($id, $platform, $country); $data = $this->getAppData($id, $platform, $country, $token);
break; }
default: $versionHistory = $this->getVersionHistory($data, $platform);
$token = $this->getJWTToken($id, $platform, $country); $name = $this->name = $data['attributes']['name'];
$data = $this->getAppData($id, $platform, $country, $token); $author = $data['attributes']['artistName'];
}
$versionHistory = $this->getVersionHistory($data, $platform); foreach ($versionHistory as $row) {
$name = $this->name = $data['attributes']['name']; $item = [];
$author = $data['attributes']['artistName'];
foreach ($versionHistory as $row) { $item['content'] = nl2br($row['releaseNotes']);
$item = array(); $item['title'] = $name . ' - ' . $row['versionDisplay'];
$item['timestamp'] = $row['releaseDate'];
$item['author'] = $author;
$item['content'] = nl2br($row['releaseNotes']); $item['uri'] = $this->makeHtmlUrl($id, $country);
$item['title'] = $name . ' - ' . $row['versionDisplay'];
$item['timestamp'] = $row['releaseDate'];
$item['author'] = $author;
$item['uri'] = $this->makeHtmlUrl($id, $country); $this->items[] = $item;
}
$this->items[] = $item; }
}
}
} }

View file

@ -1,55 +1,57 @@
<?php <?php
class AppleMusicBridge extends BridgeAbstract { class AppleMusicBridge extends BridgeAbstract
const NAME = 'Apple Music'; {
const URI = 'https://www.apple.com'; const NAME = 'Apple Music';
const DESCRIPTION = 'Fetches the latest releases from an artist'; const URI = 'https://www.apple.com';
const MAINTAINER = 'bockiii'; const DESCRIPTION = 'Fetches the latest releases from an artist';
const PARAMETERS = array(array( const MAINTAINER = 'bockiii';
'artist' => array( const PARAMETERS = [[
'name' => 'Artist ID', 'artist' => [
'exampleValue' => '909253', 'name' => 'Artist ID',
'required' => true, 'exampleValue' => '909253',
), 'required' => true,
'limit' => array( ],
'name' => 'Latest X Releases (max 50)', 'limit' => [
'defaultValue' => '10', 'name' => 'Latest X Releases (max 50)',
'required' => true, 'defaultValue' => '10',
), 'required' => true,
)); ],
const CACHE_TIMEOUT = 21600; // 6 hours ]];
const CACHE_TIMEOUT = 21600; // 6 hours
public function collectData() { public function collectData()
# Limit the amount of releases to 50 {
if ($this->getInput('limit') > 50) { # Limit the amount of releases to 50
$limit = 50; if ($this->getInput('limit') > 50) {
} else { $limit = 50;
$limit = $this->getInput('limit'); } else {
} $limit = $this->getInput('limit');
}
$url = 'https://itunes.apple.com/lookup?id=' $url = 'https://itunes.apple.com/lookup?id='
. $this->getInput('artist') . $this->getInput('artist')
. '&entity=album&limit=' . '&entity=album&limit='
. $limit . . $limit .
'&sort=recent'; '&sort=recent';
$html = getSimpleHTMLDOM($url); $html = getSimpleHTMLDOM($url);
$json = json_decode($html); $json = json_decode($html);
foreach ($json->results as $obj) { foreach ($json->results as $obj) {
if ($obj->wrapperType === 'collection') { if ($obj->wrapperType === 'collection') {
$this->items[] = array( $this->items[] = [
'title' => $obj->artistName . ' - ' . $obj->collectionName, 'title' => $obj->artistName . ' - ' . $obj->collectionName,
'uri' => $obj->collectionViewUrl, 'uri' => $obj->collectionViewUrl,
'timestamp' => $obj->releaseDate, 'timestamp' => $obj->releaseDate,
'enclosures' => $obj->artworkUrl100, 'enclosures' => $obj->artworkUrl100,
'content' => '<a href=' . $obj->collectionViewUrl 'content' => '<a href=' . $obj->collectionViewUrl
. '><img src="' . $obj->artworkUrl100 . '" /></a><br><br>' . '><img src="' . $obj->artworkUrl100 . '" /></a><br><br>'
. $obj->artistName . ' - ' . $obj->collectionName . $obj->artistName . ' - ' . $obj->collectionName
. '<br>' . '<br>'
. $obj->copyright, . $obj->copyright,
); ];
} }
} }
} }
} }

View file

@ -1,92 +1,101 @@
<?php <?php
class ArtStationBridge extends BridgeAbstract {
const NAME = 'ArtStation';
const URI = 'https://www.artstation.com';
const DESCRIPTION = 'Fetches the latest ten artworks from a search query on ArtStation.';
const MAINTAINER = 'thefranke';
const CACHE_TIMEOUT = 3600; // 1h
const PARAMETERS = array( class ArtStationBridge extends BridgeAbstract
'Search Query' => array( {
'q' => array( const NAME = 'ArtStation';
'name' => 'Search term', const URI = 'https://www.artstation.com';
'required' => true, const DESCRIPTION = 'Fetches the latest ten artworks from a search query on ArtStation.';
'exampleValue' => 'bird' const MAINTAINER = 'thefranke';
) const CACHE_TIMEOUT = 3600; // 1h
)
);
public function getIcon() { const PARAMETERS = [
return 'https://www.artstation.com/assets/favicon-58653022bc38c1905ac7aa1b10bffa6b.ico'; 'Search Query' => [
} 'q' => [
'name' => 'Search term',
'required' => true,
'exampleValue' => 'bird'
]
]
];
public function getName() { public function getIcon()
return self::NAME . ': ' . $this->getInput('q'); {
} return 'https://www.artstation.com/assets/favicon-58653022bc38c1905ac7aa1b10bffa6b.ico';
}
private function fetchSearch($searchQuery) { public function getName()
$data = '{"query":"' . $searchQuery . '","page":1,"per_page":50,"sorting":"date",'; {
$data .= '"pro_first":"1","filters":[],"additional_fields":[]}'; return self::NAME . ': ' . $this->getInput('q');
}
$header = array( private function fetchSearch($searchQuery)
'Content-Type: application/json', {
'Accept: application/json' $data = '{"query":"' . $searchQuery . '","page":1,"per_page":50,"sorting":"date",';
); $data .= '"pro_first":"1","filters":[],"additional_fields":[]}';
$opts = array( $header = [
CURLOPT_POST => true, 'Content-Type: application/json',
CURLOPT_POSTFIELDS => $data, 'Accept: application/json'
CURLOPT_RETURNTRANSFER => true ];
);
$jsonSearchURL = self::URI . '/api/v2/search/projects.json'; $opts = [
$jsonSearchStr = getContents($jsonSearchURL, $header, $opts); CURLOPT_POST => true,
return json_decode($jsonSearchStr); CURLOPT_POSTFIELDS => $data,
} CURLOPT_RETURNTRANSFER => true
];
private function fetchProject($hashID) { $jsonSearchURL = self::URI . '/api/v2/search/projects.json';
$jsonProjectURL = self::URI . '/projects/' . $hashID . '.json'; $jsonSearchStr = getContents($jsonSearchURL, $header, $opts);
$jsonProjectStr = getContents($jsonProjectURL); return json_decode($jsonSearchStr);
return json_decode($jsonProjectStr); }
}
public function collectData() { private function fetchProject($hashID)
$searchTerm = $this->getInput('q'); {
$jsonQuery = $this->fetchSearch($searchTerm); $jsonProjectURL = self::URI . '/projects/' . $hashID . '.json';
$jsonProjectStr = getContents($jsonProjectURL);
return json_decode($jsonProjectStr);
}
foreach($jsonQuery->data as $media) { public function collectData()
// get detailed info about media item {
$jsonProject = $this->fetchProject($media->hash_id); $searchTerm = $this->getInput('q');
$jsonQuery = $this->fetchSearch($searchTerm);
// create item foreach ($jsonQuery->data as $media) {
$item = array(); // get detailed info about media item
$item['title'] = $media->title; $jsonProject = $this->fetchProject($media->hash_id);
$item['uri'] = $media->url;
$item['timestamp'] = strtotime($jsonProject->published_at);
$item['author'] = $media->user->full_name;
$item['categories'] = implode(',', $jsonProject->tags);
$item['content'] = '<a href="' // create item
. $media->url $item = [];
. '"><img style="max-width: 100%" src="' $item['title'] = $media->title;
. $jsonProject->cover_url $item['uri'] = $media->url;
. '"></a><p>' $item['timestamp'] = strtotime($jsonProject->published_at);
. $jsonProject->description $item['author'] = $media->user->full_name;
. '</p>'; $item['categories'] = implode(',', $jsonProject->tags);
$numAssets = count($jsonProject->assets); $item['content'] = '<a href="'
. $media->url
. '"><img style="max-width: 100%" src="'
. $jsonProject->cover_url
. '"></a><p>'
. $jsonProject->description
. '</p>';
if ($numAssets > 1) $numAssets = count($jsonProject->assets);
$item['content'] .= '<p><a href="'
. $media->url
. '">Project contains '
. ($numAssets - 1)
. ' more item(s).</a></p>';
$this->items[] = $item; if ($numAssets > 1) {
$item['content'] .= '<p><a href="'
. $media->url
. '">Project contains '
. ($numAssets - 1)
. ' more item(s).</a></p>';
}
if (count($this->items) >= 10) $this->items[] = $item;
break;
} if (count($this->items) >= 10) {
} break;
}
}
}
} }

View file

@ -1,160 +1,163 @@
<?php <?php
class Arte7Bridge extends BridgeAbstract {
const NAME = 'Arte +7'; class Arte7Bridge extends BridgeAbstract
const URI = 'https://www.arte.tv/'; {
const MAINTAINER = 'imagoiq'; const NAME = 'Arte +7';
const CACHE_TIMEOUT = 1800; // 30min const URI = 'https://www.arte.tv/';
const DESCRIPTION = 'Returns newest videos from ARTE +7'; const MAINTAINER = 'imagoiq';
const CACHE_TIMEOUT = 1800; // 30min
const DESCRIPTION = 'Returns newest videos from ARTE +7';
const API_TOKEN = 'Nzc1Yjc1ZjJkYjk1NWFhN2I2MWEwMmRlMzAzNjI5NmU3NWU3ODg4ODJjOWMxNTMxYzEzZGRjYjg2ZGE4MmIwOA'; const API_TOKEN = 'Nzc1Yjc1ZjJkYjk1NWFhN2I2MWEwMmRlMzAzNjI5NmU3NWU3ODg4ODJjOWMxNTMxYzEzZGRjYjg2ZGE4MmIwOA';
const PARAMETERS = array( const PARAMETERS = [
'global' => [ 'global' => [
'sort_by' => array( 'sort_by' => [
'type' => 'list', 'type' => 'list',
'name' => 'Sort by', 'name' => 'Sort by',
'required' => false, 'required' => false,
'defaultValue' => null, 'defaultValue' => null,
'values' => array( 'values' => [
'Default' => null, 'Default' => null,
'Video rights start date' => 'videoRightsBegin', 'Video rights start date' => 'videoRightsBegin',
'Video rights end date' => 'videoRightsEnd', 'Video rights end date' => 'videoRightsEnd',
'Brodcast date' => 'broadcastBegin', 'Brodcast date' => 'broadcastBegin',
'Creation date' => 'creationDate', 'Creation date' => 'creationDate',
'Last modified' => 'lastModified', 'Last modified' => 'lastModified',
'Number of views' => 'views', 'Number of views' => 'views',
'Number of views per period' => 'viewsPeriod', 'Number of views per period' => 'viewsPeriod',
'Available screens' => 'availableScreens', 'Available screens' => 'availableScreens',
'Episode' => 'episode' 'Episode' => 'episode'
), ],
), ],
'sort_direction' => array( 'sort_direction' => [
'type' => 'list', 'type' => 'list',
'name' => 'Sort direction', 'name' => 'Sort direction',
'required' => false, 'required' => false,
'defaultValue' => 'DESC', 'defaultValue' => 'DESC',
'values' => array( 'values' => [
'Ascending' => 'ASC', 'Ascending' => 'ASC',
'Descending' => 'DESC' 'Descending' => 'DESC'
), ],
), ],
'exclude_trailers' => [ 'exclude_trailers' => [
'name' => 'Exclude trailers', 'name' => 'Exclude trailers',
'type' => 'checkbox', 'type' => 'checkbox',
'required' => false, 'required' => false,
'defaultValue' => false 'defaultValue' => false
], ],
], ],
'Category' => array( 'Category' => [
'lang' => array( 'lang' => [
'type' => 'list', 'type' => 'list',
'name' => 'Language', 'name' => 'Language',
'values' => array( 'values' => [
'Français' => 'fr', 'Français' => 'fr',
'Deutsch' => 'de', 'Deutsch' => 'de',
'English' => 'en', 'English' => 'en',
'Español' => 'es', 'Español' => 'es',
'Polski' => 'pl', 'Polski' => 'pl',
'Italiano' => 'it' 'Italiano' => 'it'
), ],
), ],
'cat' => array( 'cat' => [
'type' => 'list', 'type' => 'list',
'name' => 'Category', 'name' => 'Category',
'values' => array( 'values' => [
'All videos' => null, 'All videos' => null,
'News & society' => 'ACT', 'News & society' => 'ACT',
'Series & fiction' => 'SER', 'Series & fiction' => 'SER',
'Cinema' => 'CIN', 'Cinema' => 'CIN',
'Culture' => 'ARS', 'Culture' => 'ARS',
'Culture pop' => 'CPO', 'Culture pop' => 'CPO',
'Discovery' => 'DEC', 'Discovery' => 'DEC',
'History' => 'HIST', 'History' => 'HIST',
'Science' => 'SCI', 'Science' => 'SCI',
'Other' => 'AUT' 'Other' => 'AUT'
) ]
), ],
), ],
'Collection' => array( 'Collection' => [
'lang' => array( 'lang' => [
'type' => 'list', 'type' => 'list',
'name' => 'Language', 'name' => 'Language',
'values' => array( 'values' => [
'Français' => 'fr', 'Français' => 'fr',
'Deutsch' => 'de', 'Deutsch' => 'de',
'English' => 'en', 'English' => 'en',
'Español' => 'es', 'Español' => 'es',
'Polski' => 'pl', 'Polski' => 'pl',
'Italiano' => 'it' 'Italiano' => 'it'
) ]
), ],
'col' => array( 'col' => [
'name' => 'Collection id', 'name' => 'Collection id',
'required' => true, 'required' => true,
'title' => 'ex. RC-014095 pour https://www.arte.tv/de/videos/RC-014095/blow-up/', 'title' => 'ex. RC-014095 pour https://www.arte.tv/de/videos/RC-014095/blow-up/',
'exampleValue' => 'RC-014095' 'exampleValue' => 'RC-014095'
) ]
) ]
); ];
public function collectData(){ public function collectData()
switch($this->queriedContext) { {
case 'Category': switch ($this->queriedContext) {
$category = $this->getInput('cat'); case 'Category':
$collectionId = null; $category = $this->getInput('cat');
break; $collectionId = null;
case 'Collection': break;
$collectionId = $this->getInput('col'); case 'Collection':
$category = null; $collectionId = $this->getInput('col');
break; $category = null;
} break;
}
$lang = $this->getInput('lang'); $lang = $this->getInput('lang');
$sort_by = $this->getInput('sort_by'); $sort_by = $this->getInput('sort_by');
$sort_direction = $this->getInput('sort_direction') == 'ASC' ? '' : '-'; $sort_direction = $this->getInput('sort_direction') == 'ASC' ? '' : '-';
$url = 'https://api.arte.tv/api/opa/v3/videos?limit=15&language=' $url = 'https://api.arte.tv/api/opa/v3/videos?limit=15&language='
. $lang . $lang
. ($sort_by != null ? '&sort=' . $sort_direction . $sort_by : '') . ($sort_by != null ? '&sort=' . $sort_direction . $sort_by : '')
. ($category != null ? '&category.code=' . $category : '') . ($category != null ? '&category.code=' . $category : '')
. ($collectionId != null ? '&collections.collectionId=' . $collectionId : ''); . ($collectionId != null ? '&collections.collectionId=' . $collectionId : '');
$header = array( $header = [
'Authorization: Bearer ' . self::API_TOKEN 'Authorization: Bearer ' . self::API_TOKEN
); ];
$input = getContents($url, $header); $input = getContents($url, $header);
$input_json = json_decode($input, true); $input_json = json_decode($input, true);
foreach($input_json['videos'] as $element) { foreach ($input_json['videos'] as $element) {
if($this->getInput('exclude_trailers') && $element['platform'] == 'EXTRAIT') { if ($this->getInput('exclude_trailers') && $element['platform'] == 'EXTRAIT') {
continue; continue;
} }
$durationSeconds = $element['durationSeconds']; $durationSeconds = $element['durationSeconds'];
$item = array(); $item = [];
$item['uri'] = $element['url']; $item['uri'] = $element['url'];
$item['id'] = $element['id']; $item['id'] = $element['id'];
$item['timestamp'] = strtotime($element['videoRightsBegin']); $item['timestamp'] = strtotime($element['videoRightsBegin']);
$item['title'] = $element['title']; $item['title'] = $element['title'];
if(!empty($element['subtitle'])) if (!empty($element['subtitle'])) {
$item['title'] = $element['title'] . ' | ' . $element['subtitle']; $item['title'] = $element['title'] . ' | ' . $element['subtitle'];
}
$durationMinutes = round((int)$durationSeconds / 60); $durationMinutes = round((int)$durationSeconds / 60);
$item['content'] = $element['teaserText'] $item['content'] = $element['teaserText']
. '<br><br>' . '<br><br>'
. $durationMinutes . $durationMinutes
. 'min<br><a href="' . 'min<br><a href="'
. $item['uri'] . $item['uri']
. '"><img src="' . '"><img src="'
. $element['mainImage']['url'] . $element['mainImage']['url']
. '" /></a>'; . '" /></a>';
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }

View file

@ -1,72 +1,76 @@
<?php <?php
class AsahiShimbunAJWBridge extends BridgeAbstract {
const NAME = 'Asahi Shimbun AJW';
const BASE_URI = 'http://www.asahi.com';
const URI = self::BASE_URI . '/ajw/';
const DESCRIPTION = 'Asahi Shimbun - Asia & Japan Watch';
const MAINTAINER = 'somini';
const PARAMETERS = array(
array(
'section' => array(
'type' => 'list',
'name' => 'Section',
'values' => array(
'Japan » Social Affairs' => 'japan/social',
'Japan » People' => 'japan/people',
'Japan » 3/11 Disaster' => 'japan/0311disaster',
'Japan » Sci & Tech' => 'japan/sci_tech',
'Politics' => 'politics',
'Business' => 'business',
'Culture » Style' => 'culture/style',
'Culture » Movies' => 'culture/movies',
'Culture » Manga & Anime' => 'culture/manga_anime',
'Asia » China' => 'asia_world/china',
'Asia » Korean Peninsula' => 'asia_world/korean_peninsula',
'Asia » Around Asia' => 'asia_world/around_asia',
'Asia » World' => 'asia_world/world',
'Opinion » Editorial' => 'opinion/editorial',
'Opinion » Vox Populi' => 'opinion/vox',
),
'defaultValue' => 'politics',
)
)
);
private function getSectionURI($section) { class AsahiShimbunAJWBridge extends BridgeAbstract
return self::getURI() . $section . '/'; {
} const NAME = 'Asahi Shimbun AJW';
const BASE_URI = 'http://www.asahi.com';
const URI = self::BASE_URI . '/ajw/';
const DESCRIPTION = 'Asahi Shimbun - Asia & Japan Watch';
const MAINTAINER = 'somini';
const PARAMETERS = [
[
'section' => [
'type' => 'list',
'name' => 'Section',
'values' => [
'Japan » Social Affairs' => 'japan/social',
'Japan » People' => 'japan/people',
'Japan » 3/11 Disaster' => 'japan/0311disaster',
'Japan » Sci & Tech' => 'japan/sci_tech',
'Politics' => 'politics',
'Business' => 'business',
'Culture » Style' => 'culture/style',
'Culture » Movies' => 'culture/movies',
'Culture » Manga & Anime' => 'culture/manga_anime',
'Asia » China' => 'asia_world/china',
'Asia » Korean Peninsula' => 'asia_world/korean_peninsula',
'Asia » Around Asia' => 'asia_world/around_asia',
'Asia » World' => 'asia_world/world',
'Opinion » Editorial' => 'opinion/editorial',
'Opinion » Vox Populi' => 'opinion/vox',
],
'defaultValue' => 'politics',
]
]
];
public function collectData() { private function getSectionURI($section)
$html = getSimpleHTMLDOM($this->getSectionURI($this->getInput('section'))); {
return self::getURI() . $section . '/';
}
foreach($html->find('#MainInner li a') as $element) { public function collectData()
if ($element->parent()->class == 'HeadlineTopImage-S') { {
Debug::log('Skip Headline, it is repeated below'); $html = getSimpleHTMLDOM($this->getSectionURI($this->getInput('section')));
continue;
}
$item = array();
$item['uri'] = self::BASE_URI . $element->href; foreach ($html->find('#MainInner li a') as $element) {
$e_lead = $element->find('span.Lead', 0); if ($element->parent()->class == 'HeadlineTopImage-S') {
if ($e_lead) { Debug::log('Skip Headline, it is repeated below');
$item['content'] = $e_lead->innertext; continue;
$e_lead->outertext = ''; }
} else { $item = [];
$item['content'] = $element->innertext;
}
$e_date = $element->find('span.EnDate', 0);
if ($e_date) {
$item['timestamp'] = strtotime($e_date->innertext);
$e_date->outertext = '';
}
$e_video = $element->find('span.EnVideo', 0);
if ($e_video) {
$e_video->outertext = '';
$element->innertext = "VIDEO: $element->innertext";
}
$item['title'] = $element->innertext;
$this->items[] = $item; $item['uri'] = self::BASE_URI . $element->href;
} $e_lead = $element->find('span.Lead', 0);
} if ($e_lead) {
$item['content'] = $e_lead->innertext;
$e_lead->outertext = '';
} else {
$item['content'] = $element->innertext;
}
$e_date = $element->find('span.EnDate', 0);
if ($e_date) {
$item['timestamp'] = strtotime($e_date->innertext);
$e_date->outertext = '';
}
$e_video = $element->find('span.EnVideo', 0);
if ($e_video) {
$e_video->outertext = '';
$element->innertext = "VIDEO: $element->innertext";
}
$item['title'] = $element->innertext;
$this->items[] = $item;
}
}
} }

View file

@ -1,74 +1,79 @@
<?php <?php
class AskfmBridge extends BridgeAbstract {
const MAINTAINER = 'az5he6ch, logmanoriginal'; class AskfmBridge extends BridgeAbstract
const NAME = 'Ask.fm Answers'; {
const URI = 'https://ask.fm/'; const MAINTAINER = 'az5he6ch, logmanoriginal';
const CACHE_TIMEOUT = 300; //5 min const NAME = 'Ask.fm Answers';
const DESCRIPTION = 'Returns answers from an Ask.fm user'; const URI = 'https://ask.fm/';
const PARAMETERS = array( const CACHE_TIMEOUT = 300; //5 min
'Ask.fm username' => array( const DESCRIPTION = 'Returns answers from an Ask.fm user';
'u' => array( const PARAMETERS = [
'name' => 'Username', 'Ask.fm username' => [
'required' => true, 'u' => [
'exampleValue' => 'ApprovedAndReal' 'name' => 'Username',
) 'required' => true,
) 'exampleValue' => 'ApprovedAndReal'
); ]
]
];
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM($this->getURI()); {
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, self::URI); $html = defaultLinkTo($html, self::URI);
foreach($html->find('article.streamItem-answer') as $element) { foreach ($html->find('article.streamItem-answer') as $element) {
$item = array(); $item = [];
$item['uri'] = $element->find('a.streamItem_meta', 0)->href; $item['uri'] = $element->find('a.streamItem_meta', 0)->href;
$question = trim($element->find('header.streamItem_header', 0)->innertext); $question = trim($element->find('header.streamItem_header', 0)->innertext);
$item['title'] = trim( $item['title'] = trim(
htmlspecialchars_decode($element->find('header.streamItem_header', 0)->plaintext, htmlspecialchars_decode(
ENT_QUOTES $element->find('header.streamItem_header', 0)->plaintext,
) ENT_QUOTES
); )
);
$item['timestamp'] = strtotime($element->find('time', 0)->datetime); $item['timestamp'] = strtotime($element->find('time', 0)->datetime);
$answer = trim($element->find('div.streamItem_content', 0)->innertext); $answer = trim($element->find('div.streamItem_content', 0)->innertext);
// This probably should be cleaned up, especially for YouTube embeds // This probably should be cleaned up, especially for YouTube embeds
if($visual = $element->find('div.streamItem_visual', 0)) { if ($visual = $element->find('div.streamItem_visual', 0)) {
$visual = $visual->innertext; $visual = $visual->innertext;
} }
// Fix tracking links, also doesn't work // Fix tracking links, also doesn't work
foreach($element->find('a') as $link) { foreach ($element->find('a') as $link) {
if(strpos($link->href, 'l.ask.fm') !== false) { if (strpos($link->href, 'l.ask.fm') !== false) {
$link->href = $link->plaintext; $link->href = $link->plaintext;
} }
} }
$item['content'] = '<p>' . $question $item['content'] = '<p>' . $question
. '</p><p>' . $answer . '</p><p>' . $answer
. '</p><p>' . $visual . '</p>'; . '</p><p>' . $visual . '</p>';
$this->items[] = $item; $this->items[] = $item;
} }
} }
public function getName(){ public function getName()
if(!is_null($this->getInput('u'))) { {
return self::NAME . ' : ' . $this->getInput('u'); if (!is_null($this->getInput('u'))) {
} return self::NAME . ' : ' . $this->getInput('u');
}
return parent::getName(); return parent::getName();
} }
public function getURI(){ public function getURI()
if(!is_null($this->getInput('u'))) { {
return self::URI . urlencode($this->getInput('u')); if (!is_null($this->getInput('u'))) {
} return self::URI . urlencode($this->getInput('u'));
}
return parent::getURI(); return parent::getURI();
} }
} }

View file

@ -1,270 +1,278 @@
<?php <?php
class AssociatedPressNewsBridge extends BridgeAbstract {
const NAME = 'Associated Press News Bridge';
const URI = 'https://apnews.com/';
const DESCRIPTION = 'Returns newest articles by topic';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(
'Standard Topics' => array(
'topic' => array(
'name' => 'Topic',
'type' => 'list',
'values' => array(
'AP Top News' => 'apf-topnews',
'Sports' => 'apf-sports',
'Entertainment' => 'apf-entertainment',
'Oddities' => 'apf-oddities',
'Travel' => 'apf-Travel',
'Technology' => 'apf-technology',
'Lifestyle' => 'apf-lifestyle',
'Business' => 'apf-business',
'U.S. News' => 'apf-usnews',
'Health' => 'apf-Health',
'Science' => 'apf-science',
'World News' => 'apf-WorldNews',
'Politics' => 'apf-politics',
'Religion' => 'apf-religion',
'Photo Galleries' => 'PhotoGalleries',
'Fact Checks' => 'APFactCheck',
'Videos' => 'apf-videos',
),
'defaultValue' => 'apf-topnews',
),
),
'Custom Topic' => array(
'topic' => array(
'name' => 'Topic',
'type' => 'text',
'required' => true,
'exampleValue' => 'europe'
),
)
);
const CACHE_TIMEOUT = 900; // 15 mins class AssociatedPressNewsBridge extends BridgeAbstract
{
const NAME = 'Associated Press News Bridge';
const URI = 'https://apnews.com/';
const DESCRIPTION = 'Returns newest articles by topic';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [
'Standard Topics' => [
'topic' => [
'name' => 'Topic',
'type' => 'list',
'values' => [
'AP Top News' => 'apf-topnews',
'Sports' => 'apf-sports',
'Entertainment' => 'apf-entertainment',
'Oddities' => 'apf-oddities',
'Travel' => 'apf-Travel',
'Technology' => 'apf-technology',
'Lifestyle' => 'apf-lifestyle',
'Business' => 'apf-business',
'U.S. News' => 'apf-usnews',
'Health' => 'apf-Health',
'Science' => 'apf-science',
'World News' => 'apf-WorldNews',
'Politics' => 'apf-politics',
'Religion' => 'apf-religion',
'Photo Galleries' => 'PhotoGalleries',
'Fact Checks' => 'APFactCheck',
'Videos' => 'apf-videos',
],
'defaultValue' => 'apf-topnews',
],
],
'Custom Topic' => [
'topic' => [
'name' => 'Topic',
'type' => 'text',
'required' => true,
'exampleValue' => 'europe'
],
]
];
private $detectParamRegex = '/^https?:\/\/(?:www\.)?apnews\.com\/(?:[tag|hub]+\/)?([\w-]+)$/'; const CACHE_TIMEOUT = 900; // 15 mins
private $tagEndpoint = 'https://afs-prod.appspot.com/api/v2/feed/tag?tags=';
private $feedName = '';
public function detectParameters($url) { private $detectParamRegex = '/^https?:\/\/(?:www\.)?apnews\.com\/(?:[tag|hub]+\/)?([\w-]+)$/';
$params = array(); private $tagEndpoint = 'https://afs-prod.appspot.com/api/v2/feed/tag?tags=';
private $feedName = '';
if(preg_match($this->detectParamRegex, $url, $matches) > 0) { public function detectParameters($url)
$params['topic'] = $matches[1]; {
$params['context'] = 'Custom Topic'; $params = [];
return $params;
}
return null; if (preg_match($this->detectParamRegex, $url, $matches) > 0) {
} $params['topic'] = $matches[1];
$params['context'] = 'Custom Topic';
return $params;
}
public function collectData() { return null;
switch($this->getInput('topic')) { }
case 'Podcasts':
returnClientError('Podcasts topic feed is not supported');
break;
case 'PressReleases':
returnClientError('PressReleases topic feed is not supported');
break;
default:
$this->collectCardData();
}
}
public function getURI() { public function collectData()
if (!is_null($this->getInput('topic'))) { {
return self::URI . $this->getInput('topic'); switch ($this->getInput('topic')) {
} case 'Podcasts':
returnClientError('Podcasts topic feed is not supported');
break;
case 'PressReleases':
returnClientError('PressReleases topic feed is not supported');
break;
default:
$this->collectCardData();
}
}
return parent::getURI(); public function getURI()
} {
if (!is_null($this->getInput('topic'))) {
return self::URI . $this->getInput('topic');
}
public function getName() { return parent::getURI();
if (!empty($this->feedName)) { }
return $this->feedName . ' - Associated Press';
}
return parent::getName(); public function getName()
} {
if (!empty($this->feedName)) {
return $this->feedName . ' - Associated Press';
}
private function getTagURI() { return parent::getName();
if (!is_null($this->getInput('topic'))) { }
return $this->tagEndpoint . $this->getInput('topic');
}
return parent::getURI(); private function getTagURI()
} {
if (!is_null($this->getInput('topic'))) {
return $this->tagEndpoint . $this->getInput('topic');
}
private function collectCardData() { return parent::getURI();
$json = getContents($this->getTagURI()) }
or returnServerError('Could not request: ' . $this->getTagURI());
$tagContents = json_decode($json, true); private function collectCardData()
{
$json = getContents($this->getTagURI())
or returnServerError('Could not request: ' . $this->getTagURI());
if (empty($tagContents['tagObjs'])) { $tagContents = json_decode($json, true);
returnClientError('Topic not found: ' . $this->getInput('topic'));
}
$this->feedName = $tagContents['tagObjs'][0]['name']; if (empty($tagContents['tagObjs'])) {
returnClientError('Topic not found: ' . $this->getInput('topic'));
}
foreach ($tagContents['cards'] as $card) { $this->feedName = $tagContents['tagObjs'][0]['name'];
$item = array();
// skip hub peeks & Notifications foreach ($tagContents['cards'] as $card) {
if ($card['cardType'] == 'Hub Peek' || $card['cardType'] == 'Notification') { $item = [];
continue;
}
$storyContent = $card['contents'][0]; // skip hub peeks & Notifications
if ($card['cardType'] == 'Hub Peek' || $card['cardType'] == 'Notification') {
continue;
}
switch($storyContent['contentType']) { $storyContent = $card['contents'][0];
case 'web': // Skip link only content
continue 2;
case 'video': switch ($storyContent['contentType']) {
$html = $this->processVideo($storyContent); case 'web': // Skip link only content
continue 2;
$item['enclosures'][] = 'https://storage.googleapis.com/afs-prod/media/' case 'video':
. $storyContent['media'][0]['id'] . '/800.jpeg'; $html = $this->processVideo($storyContent);
break;
default:
if (empty($storyContent['storyHTML'])) { // Skip if no storyHTML
continue 2;
}
$html = defaultLinkTo($storyContent['storyHTML'], self::URI); $item['enclosures'][] = 'https://storage.googleapis.com/afs-prod/media/'
$html = str_get_html($html); . $storyContent['media'][0]['id'] . '/800.jpeg';
break;
default:
if (empty($storyContent['storyHTML'])) { // Skip if no storyHTML
continue 2;
}
$this->processMediaPlaceholders($html, $storyContent['id']); $html = defaultLinkTo($storyContent['storyHTML'], self::URI);
$this->processHubLinks($html, $storyContent); $html = str_get_html($html);
$this->processIframes($html);
if (!is_null($storyContent['leadPhotoId'])) { $this->processMediaPlaceholders($html, $storyContent['id']);
$item['enclosures'][] = 'https://storage.googleapis.com/afs-prod/media/' $this->processHubLinks($html, $storyContent);
. $storyContent['leadPhotoId'] . '/800.jpeg'; $this->processIframes($html);
}
}
$item['title'] = $card['contents'][0]['headline']; if (!is_null($storyContent['leadPhotoId'])) {
$item['uri'] = self::URI . $card['shortId']; $item['enclosures'][] = 'https://storage.googleapis.com/afs-prod/media/'
. $storyContent['leadPhotoId'] . '/800.jpeg';
}
}
if ($card['contents'][0]['localLinkUrl']) { $item['title'] = $card['contents'][0]['headline'];
$item['uri'] = $card['contents'][0]['localLinkUrl']; $item['uri'] = self::URI . $card['shortId'];
}
$item['timestamp'] = $storyContent['published']; if ($card['contents'][0]['localLinkUrl']) {
$item['uri'] = $card['contents'][0]['localLinkUrl'];
}
if (is_null($storyContent['bylines']) === false) { $item['timestamp'] = $storyContent['published'];
// Remove 'By' from the bylines
if (substr($storyContent['bylines'], 0, 2) == 'By') {
$item['author'] = ltrim($storyContent['bylines'], 'By ');
} else {
$item['author'] = $storyContent['bylines'];
}
}
$item['content'] = $html; if (is_null($storyContent['bylines']) === false) {
// Remove 'By' from the bylines
if (substr($storyContent['bylines'], 0, 2) == 'By') {
$item['author'] = ltrim($storyContent['bylines'], 'By ');
} else {
$item['author'] = $storyContent['bylines'];
}
}
foreach ($storyContent['tagObjs'] as $tag) { $item['content'] = $html;
$item['categories'][] = $tag['name'];
}
$this->items[] = $item; foreach ($storyContent['tagObjs'] as $tag) {
$item['categories'][] = $tag['name'];
}
if (count($this->items) >= 15) { $this->items[] = $item;
break;
}
}
}
private function processMediaPlaceholders($html, $id) { if (count($this->items) >= 15) {
break;
}
}
}
if ($html->find('div.media-placeholder', 0)) { private function processMediaPlaceholders($html, $id)
// Fetch page content {
$json = getContents('https://afs-prod.appspot.com/api/v2/content/' . $id); if ($html->find('div.media-placeholder', 0)) {
$storyContent = json_decode($json, true); // Fetch page content
$json = getContents('https://afs-prod.appspot.com/api/v2/content/' . $id);
$storyContent = json_decode($json, true);
foreach ($html->find('div.media-placeholder') as $div) { foreach ($html->find('div.media-placeholder') as $div) {
$key = array_search($div->id, $storyContent['mediumIds']); $key = array_search($div->id, $storyContent['mediumIds']);
if (!isset($storyContent['media'][$key])) { if (!isset($storyContent['media'][$key])) {
continue; continue;
} }
$media = $storyContent['media'][$key]; $media = $storyContent['media'][$key];
if ($media['type'] === 'Photo') { if ($media['type'] === 'Photo') {
$mediaUrl = $media['gcsBaseUrl'] . $media['imageRenderedSizes'][0] . $media['imageFileExtension']; $mediaUrl = $media['gcsBaseUrl'] . $media['imageRenderedSizes'][0] . $media['imageFileExtension'];
$mediaCaption = $media['caption']; $mediaCaption = $media['caption'];
$div->outertext = <<<EOD $div->outertext = <<<EOD
<figure><img loading="lazy" src="{$mediaUrl}"/><figcaption>{$mediaCaption}</figcaption></figure> <figure><img loading="lazy" src="{$mediaUrl}"/><figcaption>{$mediaCaption}</figcaption></figure>
EOD; EOD;
} }
if ($media['type'] === 'YouTube') { if ($media['type'] === 'YouTube') {
$div->outertext = <<<EOD $div->outertext = <<<EOD
<iframe src="https://www.youtube.com/embed/{$media['externalId']}" width="560" height="315"> <iframe src="https://www.youtube.com/embed/{$media['externalId']}" width="560" height="315">
</iframe> </iframe>
EOD; EOD;
} }
} }
} }
} }
/* /*
Create full coverage links (HubLinks) Create full coverage links (HubLinks)
*/ */
private function processHubLinks($html, $storyContent) { private function processHubLinks($html, $storyContent)
{
if (!empty($storyContent['richEmbeds'])) {
foreach ($storyContent['richEmbeds'] as $embed) {
if ($embed['type'] === 'Hub Link') {
$url = self::URI . $embed['tag']['id'];
$div = $html->find('div[id=' . $embed['id'] . ']', 0);
if (!empty($storyContent['richEmbeds'])) { if ($div) {
foreach ($storyContent['richEmbeds'] as $embed) { $div->outertext = <<<EOD
if ($embed['type'] === 'Hub Link') {
$url = self::URI . $embed['tag']['id'];
$div = $html->find('div[id=' . $embed['id'] . ']', 0);
if ($div) {
$div->outertext = <<<EOD
<p><a href="{$url}">{$embed['calloutText']} {$embed['displayName']}</a></p> <p><a href="{$url}">{$embed['calloutText']} {$embed['displayName']}</a></p>
EOD; EOD;
} }
} }
} }
} }
} }
private function processVideo($storyContent) { private function processVideo($storyContent)
$video = $storyContent['media'][0]; {
$video = $storyContent['media'][0];
if ($video['type'] === 'YouTube') { if ($video['type'] === 'YouTube') {
$url = 'https://www.youtube.com/embed/' . $video['externalId']; $url = 'https://www.youtube.com/embed/' . $video['externalId'];
$html = <<<EOD $html = <<<EOD
<iframe width="560" height="315" src="{$url}" frameborder="0" allowfullscreen></iframe> <iframe width="560" height="315" src="{$url}" frameborder="0" allowfullscreen></iframe>
EOD; EOD;
} else { } else {
$html = <<<EOD $html = <<<EOD
<video controls poster="https://storage.googleapis.com/afs-prod/media/{$video['id']}/800.jpeg" preload="none"> <video controls poster="https://storage.googleapis.com/afs-prod/media/{$video['id']}/800.jpeg" preload="none">
<source src="{$video['gcsBaseUrl']} {$video['videoRenderedSizes'][0]} {$video['videoFileExtension']}" type="video/mp4"> <source src="{$video['gcsBaseUrl']} {$video['videoRenderedSizes'][0]} {$video['videoFileExtension']}" type="video/mp4">
</video> </video>
EOD; EOD;
} }
return $html; return $html;
} }
// Remove datawrapper.dwcdn.net iframes and related javaScript // Remove datawrapper.dwcdn.net iframes and related javaScript
private function processIframes($html) { private function processIframes($html)
{
foreach ($html->find('iframe') as $index => $iframe) {
if (preg_match('/datawrapper\.dwcdn\.net/', $iframe->src)) {
$iframe->outertext = '';
foreach ($html->find('iframe') as $index => $iframe) { if ($html->find('script', $index)) {
if (preg_match('/datawrapper\.dwcdn\.net/', $iframe->src)) { $html->find('script', $index)->outertext = '';
$iframe->outertext = ''; }
}
if ($html->find('script', $index)) { }
$html->find('script', $index)->outertext = ''; }
}
}
}
}
} }

View file

@ -1,48 +1,53 @@
<?php <?php
class AstrophysicsDataSystemBridge extends BridgeAbstract {
const NAME = 'SAO/NASA Astrophysics Data System';
const DESCRIPTION = 'Returns the latest publications from a query';
const URI = 'https://ui.adsabs.harvard.edu';
const PARAMETERS = array(
'Publications' => array(
'query' => array(
'name' => 'query',
'title' => 'Same format as the search bar on the website',
'exampleValue' => 'author:"huchra, john"',
'required' => true
)
));
private $feedTitle; class AstrophysicsDataSystemBridge extends BridgeAbstract
{
const NAME = 'SAO/NASA Astrophysics Data System';
const DESCRIPTION = 'Returns the latest publications from a query';
const URI = 'https://ui.adsabs.harvard.edu';
const PARAMETERS = [
'Publications' => [
'query' => [
'name' => 'query',
'title' => 'Same format as the search bar on the website',
'exampleValue' => 'author:"huchra, john"',
'required' => true
]
]];
public function getName() { private $feedTitle;
if ($this->queriedContext === 'Publications') {
return $this->feedTitle;
}
return parent::getName();
}
public function getURI() { public function getName()
if ($this->queriedContext === 'Publications') { {
return self::URI . '/search/?q=' . urlencode($this->getInput('query')); if ($this->queriedContext === 'Publications') {
} return $this->feedTitle;
return parent::getURI(); }
} return parent::getName();
}
public function collectData() { public function getURI()
$headers = array ( {
'Cookie: core=always;' if ($this->queriedContext === 'Publications') {
); return self::URI . '/search/?q=' . urlencode($this->getInput('query'));
$html = str_get_html(defaultLinkTo(getContents($this->getURI(), $headers), self::URI)); }
$this->feedTitle = html_entity_decode($html->find('title', 0)->plaintext); return parent::getURI();
foreach($html->find('div.row > ul > li') as $pub) { }
$item = array();
$item['title'] = $pub->find('h3.s-results-title', 0)->plaintext; public function collectData()
$item['content'] = $pub->find('div.s-results-links', 0); {
$item['uri'] = $pub->find('a.abs-redirect-link', 0)->href; $headers = [
$item['author'] = rtrim($pub->find('li.article-author', 0)->plaintext, ' ;'); 'Cookie: core=always;'
$item['timestamp'] = $pub->find('div[aria-label="date published"]', 0)->plaintext; ];
$this->items[] = $item; $html = str_get_html(defaultLinkTo(getContents($this->getURI(), $headers), self::URI));
} $this->feedTitle = html_entity_decode($html->find('title', 0)->plaintext);
} foreach ($html->find('div.row > ul > li') as $pub) {
$item = [];
$item['title'] = $pub->find('h3.s-results-title', 0)->plaintext;
$item['content'] = $pub->find('div.s-results-links', 0);
$item['uri'] = $pub->find('a.abs-redirect-link', 0)->href;
$item['author'] = rtrim($pub->find('li.article-author', 0)->plaintext, ' ;');
$item['timestamp'] = $pub->find('div[aria-label="date published"]', 0)->plaintext;
$this->items[] = $item;
}
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,58 +1,60 @@
<?php <?php
class AtmoOccitanieBridge extends BridgeAbstract {
const NAME = 'Atmo Occitanie'; class AtmoOccitanieBridge extends BridgeAbstract
const URI = 'https://www.atmo-occitanie.org/'; {
const DESCRIPTION = 'Fetches the latest air polution of cities in Occitanie from Atmo'; const NAME = 'Atmo Occitanie';
const MAINTAINER = 'floviolleau'; const URI = 'https://www.atmo-occitanie.org/';
const PARAMETERS = array(array( const DESCRIPTION = 'Fetches the latest air polution of cities in Occitanie from Atmo';
'city' => array( const MAINTAINER = 'floviolleau';
'name' => 'Ville', const PARAMETERS = [[
'required' => true, 'city' => [
'exampleValue' => 'cahors' 'name' => 'Ville',
) 'required' => true,
)); 'exampleValue' => 'cahors'
const CACHE_TIMEOUT = 7200; ]
]];
const CACHE_TIMEOUT = 7200;
public function collectData() { public function collectData()
$uri = self::URI . $this->getInput('city'); {
$uri = self::URI . $this->getInput('city');
$html = getSimpleHTMLDOM($uri); $html = getSimpleHTMLDOM($uri);
$generalMessage = $html->find('.landing-ville .city-banner .iqa-avertissement', 0)->innertext; $generalMessage = $html->find('.landing-ville .city-banner .iqa-avertissement', 0)->innertext;
$recommendationsDom = $html->find('.landing-ville .recommandations', 0); $recommendationsDom = $html->find('.landing-ville .recommandations', 0);
$recommendationsItemDom = $recommendationsDom->find('.recommandation-item .label'); $recommendationsItemDom = $recommendationsDom->find('.recommandation-item .label');
$recommendationsMessage = ''; $recommendationsMessage = '';
$i = 0; $i = 0;
$len = count($recommendationsItemDom); $len = count($recommendationsItemDom);
foreach ($recommendationsItemDom as $key => $value) { foreach ($recommendationsItemDom as $key => $value) {
if ($i == 0) { if ($i == 0) {
$recommendationsMessage .= trim($value->innertext) . '.'; $recommendationsMessage .= trim($value->innertext) . '.';
} else { } else {
$recommendationsMessage .= ' ' . trim($value->innertext) . '.'; $recommendationsMessage .= ' ' . trim($value->innertext) . '.';
} }
$i++; $i++;
} }
$lastRecommendationsDom = $recommendationsDom->find('.col-md-6', -1); $lastRecommendationsDom = $recommendationsDom->find('.col-md-6', -1);
$informationHeaderMessage = $lastRecommendationsDom->find('.heading', 0)->innertext; $informationHeaderMessage = $lastRecommendationsDom->find('.heading', 0)->innertext;
$indice = $lastRecommendationsDom->find('.current-indice .indice div', 0)->innertext; $indice = $lastRecommendationsDom->find('.current-indice .indice div', 0)->innertext;
$informationDescriptionMessage = $lastRecommendationsDom->find('.current-indice .description p', 0)->innertext; $informationDescriptionMessage = $lastRecommendationsDom->find('.current-indice .description p', 0)->innertext;
$message = "$generalMessage L'indice est de $indice/10. $informationDescriptionMessage. $recommendationsMessage"; $message = "$generalMessage L'indice est de $indice/10. $informationDescriptionMessage. $recommendationsMessage";
$city = $this->getInput('city'); $city = $this->getInput('city');
$item['uri'] = $uri; $item['uri'] = $uri;
$today = date('d/m/Y'); $today = date('d/m/Y');
$item['title'] = "Bulletin de l'air du $today pour la ville : $city."; $item['title'] = "Bulletin de l'air du $today pour la ville : $city.";
//$item['title'] .= ' Retrouvez plus d\'informations en allant sur atmo-occitanie.org #QualiteAir. ' . $message; //$item['title'] .= ' Retrouvez plus d\'informations en allant sur atmo-occitanie.org #QualiteAir. ' . $message;
$item['title'] .= ' #QualiteAir. ' . $message; $item['title'] .= ' #QualiteAir. ' . $message;
$item['author'] = 'floviolleau'; $item['author'] = 'floviolleau';
$item['content'] = $message; $item['content'] = $message;
$item['uid'] = hash('sha256', $item['title']); $item['uid'] = hash('sha256', $item['title']);
$this->items[] = $item; $this->items[] = $item;
} }
} }

View file

@ -1,135 +1,135 @@
<?php <?php
class AutoJMBridge extends BridgeAbstract { class AutoJMBridge extends BridgeAbstract
{
const NAME = 'AutoJM';
const URI = 'https://www.autojm.fr/';
const DESCRIPTION = 'Suivre les offres de véhicules proposés par AutoJM en fonction des critères de filtrages';
const MAINTAINER = 'sysadminstory';
const PARAMETERS = [
'Afficher les offres de véhicules disponible sur la recheche AutoJM' => [
'url' => [
'name' => 'URL de la page de recherche',
'type' => 'text',
'required' => true,
'title' => 'URL d\'une recherche avec filtre de véhicules sans le http://www.autojm.fr/',
'exampleValue' => 'recherche?brands[]=peugeot&ranges[]=peugeot-nouvelle-308-2021-5p'
],
]
];
const CACHE_TIMEOUT = 3600;
const NAME = 'AutoJM'; public function getIcon()
const URI = 'https://www.autojm.fr/'; {
const DESCRIPTION = 'Suivre les offres de véhicules proposés par AutoJM en fonction des critères de filtrages'; return self::URI . 'favicon.ico';
const MAINTAINER = 'sysadminstory'; }
const PARAMETERS = array(
'Afficher les offres de véhicules disponible sur la recheche AutoJM' => array(
'url' => array(
'name' => 'URL de la page de recherche',
'type' => 'text',
'required' => true,
'title' => 'URL d\'une recherche avec filtre de véhicules sans le http://www.autojm.fr/',
'exampleValue' => 'recherche?brands[]=peugeot&ranges[]=peugeot-nouvelle-308-2021-5p'
),
)
);
const CACHE_TIMEOUT = 3600;
public function getIcon() { public function getName()
return self::URI . 'favicon.ico'; {
} switch ($this->queriedContext) {
case 'Afficher les offres de véhicules disponible sur la recheche AutoJM':
return 'AutoJM | Recherche de véhicules';
break;
default:
return parent::getName();
}
}
public function getName() { public function collectData()
switch($this->queriedContext) { {
case 'Afficher les offres de véhicules disponible sur la recheche AutoJM': // Get the number of result for this search
return 'AutoJM | Recherche de véhicules'; $search_url = self::URI . $this->getInput('url') . '&open=energy&onlyFilters=false';
break;
default:
return parent::getName();
}
} // Set the header 'X-Requested-With' like the website does it
$header = [
'X-Requested-With: XMLHttpRequest'
];
public function collectData() { // Get the JSON content of the form
$json = getContents($search_url, $header);
// Get the number of result for this search // Extract the HTML content from the JSON result
$search_url = self::URI . $this->getInput('url') . '&open=energy&onlyFilters=false'; $data = json_decode($json);
// Set the header 'X-Requested-With' like the website does it $nb_results = $data->nbResults;
$header = array( $total_pages = ceil($nb_results / 15);
'X-Requested-With: XMLHttpRequest'
);
// Get the JSON content of the form // Limit the number of page to analyse to 10
$json = getContents($search_url, $header); for ($page = 1; $page <= $total_pages && $page <= 10; $page++) {
// Get the result the next page
$html = $this->getResults($page);
// Extract the HTML content from the JSON result // Go through every car of the search
$data = json_decode($json); $list = $html->find('div[class*=card-car card-car--listing]');
foreach ($list as $car) {
// Get the info about the car offer
$image = $car->find('div[class=card-car__header__img]', 0)->find('img', 0)->src;
// Decode HTML attribute JSON data
$car_data = json_decode(html_entity_decode($car->{'data-layer'}));
$car_model = $car->{'data-title'} . ' ' . $car->{'data-suptitle'};
$availability = $car->find('div[class=card-car__modalites]', 0)->find('div[class=col]', 0)->plaintext;
$warranty = $car->find('div[data-type=WarrantyCard]', 0)->plaintext;
$discount_html = $car->find('div[class=subtext vehicle_reference_element]', 0);
// Check if there is any discount info displayed
if ($discount_html != null) {
$reference_price_value = $discount_html->find('span[data-cfg=vehicle__reference_price]', 0)->plaintext;
$discount_percent_value = $discount_html->find('span[data-cfg=vehicle__discount_percent]', 0)->plaintext;
$reference_price = '<li>Prix de référence : <s>' . $reference_price_value . '</s></li>';
$discount_percent = '<li>Réduction : ' . $discount_percent_value . ' %</li>';
} else {
$reference_price = '';
$discount_percent = '';
}
$price = $car_data->price;
$kilometer = $car->find('span[data-cfg=vehicle__kilometer]', 0)->plaintext;
$energy = $car->find('span[data-cfg=vehicle__energy__label]', 0)->plaintext;
$power = $car->find('span[data-cfg=vehicle__tax_horse_power]', 0)->plaintext;
$seats = $car->find('span[data-cfg=vehicle__seats]', 0)->plaintext;
$doors = $car->find('span[data-cfg=vehicle__door__label]', 0)->plaintext;
$transmission = $car->find('span[data-cfg=vehicle__transmission]', 0)->plaintext;
$loa_html = $car->find('span[data-cfg=vehicle__loa]', 0);
// Check if any LOA price is displayed
if ($loa_html != null) {
$loa_value = $car->find('span[data-cfg=vehicle__loa]', 0)->plaintext;
$loa = '<li>LOA : à partir de ' . $loa_value . ' / mois </li>';
} else {
$loa = '';
}
$nb_results = $data->nbResults; // Construct the new item
$total_pages = ceil($nb_results / 15); $item = [];
$item['title'] = $car_model;
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />'
. $car_model . '</p>';
$item['content'] .= '<ul><li>Disponibilité : ' . $availability . '</li>';
$item['content'] .= '<li>Prix : ' . $price . ' €</li>';
$item['content'] .= $reference_price;
$item['content'] .= $loa;
$item['content'] .= $discount_percent;
$item['content'] .= '<li>Garantie : ' . $warranty . '</li>';
$item['content'] .= '<li>Kilométrage : ' . $kilometer . ' km</li>';
$item['content'] .= '<li>Energie : ' . $energy . '</li>';
$item['content'] .= '<li>Puissance: ' . $power . ' CV Fiscaux</li>';
$item['content'] .= '<li>Nombre de Places : ' . $seats . ' place(s)</li>';
$item['content'] .= '<li>Nombre de portes : ' . $doors . '</li>';
$item['content'] .= '<li>Boite de vitesse : ' . $transmission . '</li></ul>';
$item['uri'] = $car_data->{'uri'};
$item['uid'] = hash('md5', $item['content']);
$this->items[] = $item;
}
}
}
// Limit the number of page to analyse to 10 private function getResults(int $page)
for($page = 1; $page <= $total_pages && $page <= 10; $page++) { {
// Get the result the next page $user_input = $this->getInput('url');
$html = $this->getResults($page); $search_data = preg_replace('#(recherche|recherche/[0-9]{1,10})\?#', 'recherche/' . $page . '?', $user_input);
// Go through every car of the search $search_url = self::URI . $search_data . '&open=energy&onlyFilters=false';
$list = $html->find('div[class*=card-car card-car--listing]');
foreach ($list as $car) {
// Get the info about the car offer // Get the HTML content of the page
$image = $car->find('div[class=card-car__header__img]', 0)->find('img', 0)->src; $html = getSimpleHTMLDOMCached($search_url);
// Decode HTML attribute JSON data
$car_data = json_decode(html_entity_decode($car->{'data-layer'}));
$car_model = $car->{'data-title'} . ' ' . $car->{'data-suptitle'};
$availability = $car->find('div[class=card-car__modalites]', 0)->find('div[class=col]', 0)->plaintext;
$warranty = $car->find('div[data-type=WarrantyCard]', 0)->plaintext;
$discount_html = $car->find('div[class=subtext vehicle_reference_element]', 0);
// Check if there is any discount info displayed
if ($discount_html != null) {
$reference_price_value = $discount_html->find('span[data-cfg=vehicle__reference_price]', 0)->plaintext;
$discount_percent_value = $discount_html->find('span[data-cfg=vehicle__discount_percent]', 0)->plaintext;
$reference_price = '<li>Prix de référence : <s>' . $reference_price_value . '</s></li>';
$discount_percent = '<li>Réduction : ' . $discount_percent_value . ' %</li>';
} else {
$reference_price = '';
$discount_percent = '';
}
$price = $car_data->price;
$kilometer = $car->find('span[data-cfg=vehicle__kilometer]', 0)->plaintext;
$energy = $car->find('span[data-cfg=vehicle__energy__label]', 0)->plaintext;
$power = $car->find('span[data-cfg=vehicle__tax_horse_power]', 0)->plaintext;
$seats = $car->find('span[data-cfg=vehicle__seats]', 0)->plaintext;
$doors = $car->find('span[data-cfg=vehicle__door__label]', 0)->plaintext;
$transmission = $car->find('span[data-cfg=vehicle__transmission]', 0)->plaintext;
$loa_html = $car->find('span[data-cfg=vehicle__loa]', 0);
// Check if any LOA price is displayed
if($loa_html != null) {
$loa_value = $car->find('span[data-cfg=vehicle__loa]', 0)->plaintext;
$loa = '<li>LOA : à partir de ' . $loa_value . ' / mois </li>';
} else {
$loa = '';
}
// Construct the new item return $html;
$item = array(); }
$item['title'] = $car_model;
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />'
. $car_model . '</p>';
$item['content'] .= '<ul><li>Disponibilité : ' . $availability . '</li>';
$item['content'] .= '<li>Prix : ' . $price . ' €</li>';
$item['content'] .= $reference_price;
$item['content'] .= $loa;
$item['content'] .= $discount_percent;
$item['content'] .= '<li>Garantie : ' . $warranty . '</li>';
$item['content'] .= '<li>Kilométrage : ' . $kilometer . ' km</li>';
$item['content'] .= '<li>Energie : ' . $energy . '</li>';
$item['content'] .= '<li>Puissance: ' . $power . ' CV Fiscaux</li>';
$item['content'] .= '<li>Nombre de Places : ' . $seats . ' place(s)</li>';
$item['content'] .= '<li>Nombre de portes : ' . $doors . '</li>';
$item['content'] .= '<li>Boite de vitesse : ' . $transmission . '</li></ul>';
$item['uri'] = $car_data->{'uri'};
$item['uid'] = hash('md5', $item['content']);
$this->items[] = $item;
}
}
}
private function getResults(int $page)
{
$user_input = $this->getInput('url');
$search_data = preg_replace('#(recherche|recherche/[0-9]{1,10})\?#', 'recherche/' . $page . '?', $user_input);
$search_url = self::URI . $search_data . '&open=energy&onlyFilters=false';
// Get the HTML content of the page
$html = getSimpleHTMLDOMCached($search_url);
return $html;
}
} }

View file

@ -1,54 +1,63 @@
<?php <?php
class AwwwardsBridge extends BridgeAbstract {
const NAME = 'Awwwards';
const URI = 'https://www.awwwards.com/';
const DESCRIPTION = 'Fetches the latest ten sites of the day from Awwwards';
const MAINTAINER = 'Paroleen';
const CACHE_TIMEOUT = 3600;
const SITESURI = 'https://www.awwwards.com/websites/sites_of_the_day/'; class AwwwardsBridge extends BridgeAbstract
const SITEURI = 'https://www.awwwards.com/sites/'; {
const ASSETSURI = 'https://assets.awwwards.com/awards/media/cache/thumb_417_299/'; const NAME = 'Awwwards';
const URI = 'https://www.awwwards.com/';
const DESCRIPTION = 'Fetches the latest ten sites of the day from Awwwards';
const MAINTAINER = 'Paroleen';
const CACHE_TIMEOUT = 3600;
private $sites = array(); const SITESURI = 'https://www.awwwards.com/websites/sites_of_the_day/';
const SITEURI = 'https://www.awwwards.com/sites/';
const ASSETSURI = 'https://assets.awwwards.com/awards/media/cache/thumb_417_299/';
public function getIcon() { private $sites = [];
return 'https://www.awwwards.com/favicon.ico';
}
private function fetchSites() { public function getIcon()
Debug::log('Fetching all sites'); {
$sites = getSimpleHTMLDOM(self::SITESURI); return 'https://www.awwwards.com/favicon.ico';
}
Debug::log('Parsing all JSON data'); private function fetchSites()
foreach($sites->find('li[data-model]') as $site) { {
$decode = html_entity_decode($site->attr['data-model'], Debug::log('Fetching all sites');
ENT_QUOTES, 'utf-8'); $sites = getSimpleHTMLDOM(self::SITESURI);
$decode = json_decode($decode, true);
$this->sites[] = $decode;
}
}
public function collectData() { Debug::log('Parsing all JSON data');
$this->fetchSites(); foreach ($sites->find('li[data-model]') as $site) {
$decode = html_entity_decode(
$site->attr['data-model'],
ENT_QUOTES,
'utf-8'
);
$decode = json_decode($decode, true);
$this->sites[] = $decode;
}
}
Debug::log('Building RSS feed'); public function collectData()
foreach($this->sites as $site) { {
$item = array(); $this->fetchSites();
$item['title'] = $site['title'];
$item['timestamp'] = $site['createdAt'];
$item['categories'] = $site['tags'];
$item['content'] = '<img src="' Debug::log('Building RSS feed');
. self::ASSETSURI foreach ($this->sites as $site) {
. $site['images']['thumbnail'] $item = [];
. '">'; $item['title'] = $site['title'];
$item['uri'] = self::SITEURI . $site['slug']; $item['timestamp'] = $site['createdAt'];
$item['categories'] = $site['tags'];
$this->items[] = $item; $item['content'] = '<img src="'
. self::ASSETSURI
. $site['images']['thumbnail']
. '">';
$item['uri'] = self::SITEURI . $site['slug'];
if(count($this->items) >= 10) $this->items[] = $item;
break;
} if (count($this->items) >= 10) {
} break;
}
}
}
} }

View file

@ -1,263 +1,269 @@
<?php <?php
class BAEBridge extends BridgeAbstract {
const MAINTAINER = 'couraudt';
const NAME = 'Bourse Aux Equipiers Bridge';
const URI = 'https://www.bourse-aux-equipiers.com';
const DESCRIPTION = 'Returns the newest sailing offers.';
const PARAMETERS = array(
array(
'keyword' => array(
'name' => 'Filtrer par mots clés',
'title' => 'Entrez le mot clé à filtrer ici'
),
'type' => array(
'name' => 'Type de recherche',
'title' => 'Afficher seuleument un certain type d\'annonce',
'type' => 'list',
'values' => array(
'Toutes les annonces' => false,
'Les embarquements' => 'boat',
'Les skippers' => 'skipper',
'Les équipiers' => 'crew'
)
)
)
);
public function collectData() { class BAEBridge extends BridgeAbstract
$url = $this->getURI(); {
$html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.'); const MAINTAINER = 'couraudt';
const NAME = 'Bourse Aux Equipiers Bridge';
const URI = 'https://www.bourse-aux-equipiers.com';
const DESCRIPTION = 'Returns the newest sailing offers.';
const PARAMETERS = [
[
'keyword' => [
'name' => 'Filtrer par mots clés',
'title' => 'Entrez le mot clé à filtrer ici'
],
'type' => [
'name' => 'Type de recherche',
'title' => 'Afficher seuleument un certain type d\'annonce',
'type' => 'list',
'values' => [
'Toutes les annonces' => false,
'Les embarquements' => 'boat',
'Les skippers' => 'skipper',
'Les équipiers' => 'crew'
]
]
]
];
$annonces = $html->find('main article'); public function collectData()
foreach ($annonces as $annonce) { {
$detail = $annonce->find('footer a', 0); $url = $this->getURI();
$html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.');
$htmlDetail = getSimpleHTMLDOMCached(parent::getURI() . $detail->href); $annonces = $html->find('main article');
if (!$htmlDetail) foreach ($annonces as $annonce) {
continue; $detail = $annonce->find('footer a', 0);
$item = array(); $htmlDetail = getSimpleHTMLDOMCached(parent::getURI() . $detail->href);
if (!$htmlDetail) {
continue;
}
$item['title'] = $annonce->find('header h2', 0)->plaintext; $item = [];
$item['uri'] = parent::getURI() . $detail->href;
$content = $htmlDetail->find('article p', 0)->innertext; $item['title'] = $annonce->find('header h2', 0)->plaintext;
if (!empty($this->getInput('keyword'))) { $item['uri'] = parent::getURI() . $detail->href;
$keyword = $this->removeAccents(strtolower($this->getInput('keyword')));
$cleanTitle = $this->removeAccents(strtolower($item['title']));
if (strpos($cleanTitle, $keyword) === false) {
$cleanContent = $this->removeAccents(strtolower($content));
if (strpos($cleanContent, $keyword) === false) {
continue;
}
}
}
$content .= '<hr>'; $content = $htmlDetail->find('article p', 0)->innertext;
$content .= $htmlDetail->find('section', 0)->innertext; if (!empty($this->getInput('keyword'))) {
$item['content'] = defaultLinkTo($content, parent::getURI()); $keyword = $this->removeAccents(strtolower($this->getInput('keyword')));
$image = $htmlDetail->find('#zoom', 0); $cleanTitle = $this->removeAccents(strtolower($item['title']));
if ($image) { if (strpos($cleanTitle, $keyword) === false) {
$item['enclosures'] = array(parent::getURI() . $image->getAttribute('src')); $cleanContent = $this->removeAccents(strtolower($content));
} if (strpos($cleanContent, $keyword) === false) {
$this->items[] = $item; continue;
} }
} }
}
public function getURI() { $content .= '<hr>';
$uri = parent::getURI(); $content .= $htmlDetail->find('section', 0)->innertext;
if (!empty($this->getInput('type'))) { $item['content'] = defaultLinkTo($content, parent::getURI());
if ($this->getInput('type') == 'boat') { $image = $htmlDetail->find('#zoom', 0);
$uri .= '/embarquements.html'; if ($image) {
} elseif ($this->getInput('type') == 'skipper') { $item['enclosures'] = [parent::getURI() . $image->getAttribute('src')];
$uri .= '/skippers.html'; }
} else { $this->items[] = $item;
$uri .= '/equipiers.html'; }
} }
}
return $uri; public function getURI()
} {
$uri = parent::getURI();
if (!empty($this->getInput('type'))) {
if ($this->getInput('type') == 'boat') {
$uri .= '/embarquements.html';
} elseif ($this->getInput('type') == 'skipper') {
$uri .= '/skippers.html';
} else {
$uri .= '/equipiers.html';
}
}
private function removeAccents($string) { return $uri;
$chars = array( }
// Decompositions for Latin-1 Supplement
'ª' => 'a', 'º' => 'o',
'À' => 'A', 'Á' => 'A',
'Â' => 'A', 'Ã' => 'A',
'Ä' => 'A', 'Å' => 'A',
'Æ' => 'AE', 'Ç' => 'C',
'È' => 'E', 'É' => 'E',
'Ê' => 'E', 'Ë' => 'E',
'Ì' => 'I', 'Í' => 'I',
'Î' => 'I', 'Ï' => 'I',
'Ð' => 'D', 'Ñ' => 'N',
'Ò' => 'O', 'Ó' => 'O',
'Ô' => 'O', 'Õ' => 'O',
'Ö' => 'O', 'Ù' => 'U',
'Ú' => 'U', 'Û' => 'U',
'Ü' => 'U', 'Ý' => 'Y',
'Þ' => 'TH', 'ß' => 's',
'à' => 'a', 'á' => 'a',
'â' => 'a', 'ã' => 'a',
'ä' => 'a', 'å' => 'a',
'æ' => 'ae', 'ç' => 'c',
'è' => 'e', 'é' => 'e',
'ê' => 'e', 'ë' => 'e',
'ì' => 'i', 'í' => 'i',
'î' => 'i', 'ï' => 'i',
'ð' => 'd', 'ñ' => 'n',
'ò' => 'o', 'ó' => 'o',
'ô' => 'o', 'õ' => 'o',
'ö' => 'o', 'ø' => 'o',
'ù' => 'u', 'ú' => 'u',
'û' => 'u', 'ü' => 'u',
'ý' => 'y', 'þ' => 'th',
'ÿ' => 'y', 'Ø' => 'O',
// Decompositions for Latin Extended-A
'Ā' => 'A', 'ā' => 'a',
'Ă' => 'A', 'ă' => 'a',
'Ą' => 'A', 'ą' => 'a',
'Ć' => 'C', 'ć' => 'c',
'Ĉ' => 'C', 'ĉ' => 'c',
'Ċ' => 'C', 'ċ' => 'c',
'Č' => 'C', 'č' => 'c',
'Ď' => 'D', 'ď' => 'd',
'Đ' => 'D', 'đ' => 'd',
'Ē' => 'E', 'ē' => 'e',
'Ĕ' => 'E', 'ĕ' => 'e',
'Ė' => 'E', 'ė' => 'e',
'Ę' => 'E', 'ę' => 'e',
'Ě' => 'E', 'ě' => 'e',
'Ĝ' => 'G', 'ĝ' => 'g',
'Ğ' => 'G', 'ğ' => 'g',
'Ġ' => 'G', 'ġ' => 'g',
'Ģ' => 'G', 'ģ' => 'g',
'Ĥ' => 'H', 'ĥ' => 'h',
'Ħ' => 'H', 'ħ' => 'h',
'Ĩ' => 'I', 'ĩ' => 'i',
'Ī' => 'I', 'ī' => 'i',
'Ĭ' => 'I', 'ĭ' => 'i',
'Į' => 'I', 'į' => 'i',
'İ' => 'I', 'ı' => 'i',
'IJ' => 'IJ', 'ij' => 'ij',
'Ĵ' => 'J', 'ĵ' => 'j',
'Ķ' => 'K', 'ķ' => 'k',
'ĸ' => 'k', 'Ĺ' => 'L',
'ĺ' => 'l', 'Ļ' => 'L',
'ļ' => 'l', 'Ľ' => 'L',
'ľ' => 'l', 'Ŀ' => 'L',
'ŀ' => 'l', 'Ł' => 'L',
'ł' => 'l', 'Ń' => 'N',
'ń' => 'n', 'Ņ' => 'N',
'ņ' => 'n', 'Ň' => 'N',
'ň' => 'n', 'ʼn' => 'n',
'Ŋ' => 'N', 'ŋ' => 'n',
'Ō' => 'O', 'ō' => 'o',
'Ŏ' => 'O', 'ŏ' => 'o',
'Ő' => 'O', 'ő' => 'o',
'Œ' => 'OE', 'œ' => 'oe',
'Ŕ' => 'R', 'ŕ' => 'r',
'Ŗ' => 'R', 'ŗ' => 'r',
'Ř' => 'R', 'ř' => 'r',
'Ś' => 'S', 'ś' => 's',
'Ŝ' => 'S', 'ŝ' => 's',
'Ş' => 'S', 'ş' => 's',
'Š' => 'S', 'š' => 's',
'Ţ' => 'T', 'ţ' => 't',
'Ť' => 'T', 'ť' => 't',
'Ŧ' => 'T', 'ŧ' => 't',
'Ũ' => 'U', 'ũ' => 'u',
'Ū' => 'U', 'ū' => 'u',
'Ŭ' => 'U', 'ŭ' => 'u',
'Ů' => 'U', 'ů' => 'u',
'Ű' => 'U', 'ű' => 'u',
'Ų' => 'U', 'ų' => 'u',
'Ŵ' => 'W', 'ŵ' => 'w',
'Ŷ' => 'Y', 'ŷ' => 'y',
'Ÿ' => 'Y', 'Ź' => 'Z',
'ź' => 'z', 'Ż' => 'Z',
'ż' => 'z', 'Ž' => 'Z',
'ž' => 'z', 'ſ' => 's',
// Decompositions for Latin Extended-B
'Ș' => 'S', 'ș' => 's',
'Ț' => 'T', 'ț' => 't',
// Euro Sign
'€' => 'E',
// GBP (Pound) Sign
'£' => '',
// Vowels with diacritic (Vietnamese)
// unmarked
'Ơ' => 'O', 'ơ' => 'o',
'Ư' => 'U', 'ư' => 'u',
// grave accent
'Ầ' => 'A', 'ầ' => 'a',
'Ằ' => 'A', 'ằ' => 'a',
'Ề' => 'E', 'ề' => 'e',
'Ồ' => 'O', 'ồ' => 'o',
'Ờ' => 'O', 'ờ' => 'o',
'Ừ' => 'U', 'ừ' => 'u',
'Ỳ' => 'Y', 'ỳ' => 'y',
// hook
'Ả' => 'A', 'ả' => 'a',
'Ẩ' => 'A', 'ẩ' => 'a',
'Ẳ' => 'A', 'ẳ' => 'a',
'Ẻ' => 'E', 'ẻ' => 'e',
'Ể' => 'E', 'ể' => 'e',
'Ỉ' => 'I', 'ỉ' => 'i',
'Ỏ' => 'O', 'ỏ' => 'o',
'Ổ' => 'O', 'ổ' => 'o',
'Ở' => 'O', 'ở' => 'o',
'Ủ' => 'U', 'ủ' => 'u',
'Ử' => 'U', 'ử' => 'u',
'Ỷ' => 'Y', 'ỷ' => 'y',
// tilde
'Ẫ' => 'A', 'ẫ' => 'a',
'Ẵ' => 'A', 'ẵ' => 'a',
'Ẽ' => 'E', 'ẽ' => 'e',
'Ễ' => 'E', 'ễ' => 'e',
'Ỗ' => 'O', 'ỗ' => 'o',
'Ỡ' => 'O', 'ỡ' => 'o',
'Ữ' => 'U', 'ữ' => 'u',
'Ỹ' => 'Y', 'ỹ' => 'y',
// acute accent
'Ấ' => 'A', 'ấ' => 'a',
'Ắ' => 'A', 'ắ' => 'a',
'Ế' => 'E', 'ế' => 'e',
'Ố' => 'O', 'ố' => 'o',
'Ớ' => 'O', 'ớ' => 'o',
'Ứ' => 'U', 'ứ' => 'u',
// dot below
'Ạ' => 'A', 'ạ' => 'a',
'Ậ' => 'A', 'ậ' => 'a',
'Ặ' => 'A', 'ặ' => 'a',
'Ẹ' => 'E', 'ẹ' => 'e',
'Ệ' => 'E', 'ệ' => 'e',
'Ị' => 'I', 'ị' => 'i',
'Ọ' => 'O', 'ọ' => 'o',
'Ộ' => 'O', 'ộ' => 'o',
'Ợ' => 'O', 'ợ' => 'o',
'Ụ' => 'U', 'ụ' => 'u',
'Ự' => 'U', 'ự' => 'u',
'Ỵ' => 'Y', 'ỵ' => 'y',
// Vowels with diacritic (Chinese, Hanyu Pinyin)
'ɑ' => 'a',
// macron
'Ǖ' => 'U', 'ǖ' => 'u',
// acute accent
'Ǘ' => 'U', 'ǘ' => 'u',
// caron
'Ǎ' => 'A', 'ǎ' => 'a',
'Ǐ' => 'I', 'ǐ' => 'i',
'Ǒ' => 'O', 'ǒ' => 'o',
'Ǔ' => 'U', 'ǔ' => 'u',
'Ǚ' => 'U', 'ǚ' => 'u',
// grave accent
'Ǜ' => 'U', 'ǜ' => 'u',
);
$string = strtr($string, $chars); private function removeAccents($string)
{
$chars = [
// Decompositions for Latin-1 Supplement
'ª' => 'a', 'º' => 'o',
'À' => 'A', 'Á' => 'A',
'Â' => 'A', 'Ã' => 'A',
'Ä' => 'A', 'Å' => 'A',
'Æ' => 'AE', 'Ç' => 'C',
'È' => 'E', 'É' => 'E',
'Ê' => 'E', 'Ë' => 'E',
'Ì' => 'I', 'Í' => 'I',
'Î' => 'I', 'Ï' => 'I',
'Ð' => 'D', 'Ñ' => 'N',
'Ò' => 'O', 'Ó' => 'O',
'Ô' => 'O', 'Õ' => 'O',
'Ö' => 'O', 'Ù' => 'U',
'Ú' => 'U', 'Û' => 'U',
'Ü' => 'U', 'Ý' => 'Y',
'Þ' => 'TH', 'ß' => 's',
'à' => 'a', 'á' => 'a',
'â' => 'a', 'ã' => 'a',
'ä' => 'a', 'å' => 'a',
'æ' => 'ae', 'ç' => 'c',
'è' => 'e', 'é' => 'e',
'ê' => 'e', 'ë' => 'e',
'ì' => 'i', 'í' => 'i',
'î' => 'i', 'ï' => 'i',
'ð' => 'd', 'ñ' => 'n',
'ò' => 'o', 'ó' => 'o',
'ô' => 'o', 'õ' => 'o',
'ö' => 'o', 'ø' => 'o',
'ù' => 'u', 'ú' => 'u',
'û' => 'u', 'ü' => 'u',
'ý' => 'y', 'þ' => 'th',
'ÿ' => 'y', 'Ø' => 'O',
// Decompositions for Latin Extended-A
'Ā' => 'A', 'ā' => 'a',
'Ă' => 'A', 'ă' => 'a',
'Ą' => 'A', 'ą' => 'a',
'Ć' => 'C', 'ć' => 'c',
'Ĉ' => 'C', 'ĉ' => 'c',
'Ċ' => 'C', 'ċ' => 'c',
'Č' => 'C', 'č' => 'c',
'Ď' => 'D', 'ď' => 'd',
'Đ' => 'D', 'đ' => 'd',
'Ē' => 'E', 'ē' => 'e',
'Ĕ' => 'E', 'ĕ' => 'e',
'Ė' => 'E', 'ė' => 'e',
'Ę' => 'E', 'ę' => 'e',
'Ě' => 'E', 'ě' => 'e',
'Ĝ' => 'G', 'ĝ' => 'g',
'Ğ' => 'G', 'ğ' => 'g',
'Ġ' => 'G', 'ġ' => 'g',
'Ģ' => 'G', 'ģ' => 'g',
'Ĥ' => 'H', 'ĥ' => 'h',
'Ħ' => 'H', 'ħ' => 'h',
'Ĩ' => 'I', 'ĩ' => 'i',
'Ī' => 'I', 'ī' => 'i',
'Ĭ' => 'I', 'ĭ' => 'i',
'Į' => 'I', 'į' => 'i',
'İ' => 'I', 'ı' => 'i',
'IJ' => 'IJ', 'ij' => 'ij',
'Ĵ' => 'J', 'ĵ' => 'j',
'Ķ' => 'K', 'ķ' => 'k',
'ĸ' => 'k', 'Ĺ' => 'L',
'ĺ' => 'l', 'Ļ' => 'L',
'ļ' => 'l', 'Ľ' => 'L',
'ľ' => 'l', 'Ŀ' => 'L',
'ŀ' => 'l', 'Ł' => 'L',
'ł' => 'l', 'Ń' => 'N',
'ń' => 'n', 'Ņ' => 'N',
'ņ' => 'n', 'Ň' => 'N',
'ň' => 'n', 'ʼn' => 'n',
'Ŋ' => 'N', 'ŋ' => 'n',
'Ō' => 'O', 'ō' => 'o',
'Ŏ' => 'O', 'ŏ' => 'o',
'Ő' => 'O', 'ő' => 'o',
'Œ' => 'OE', 'œ' => 'oe',
'Ŕ' => 'R', 'ŕ' => 'r',
'Ŗ' => 'R', 'ŗ' => 'r',
'Ř' => 'R', 'ř' => 'r',
'Ś' => 'S', 'ś' => 's',
'Ŝ' => 'S', 'ŝ' => 's',
'Ş' => 'S', 'ş' => 's',
'Š' => 'S', 'š' => 's',
'Ţ' => 'T', 'ţ' => 't',
'Ť' => 'T', 'ť' => 't',
'Ŧ' => 'T', 'ŧ' => 't',
'Ũ' => 'U', 'ũ' => 'u',
'Ū' => 'U', 'ū' => 'u',
'Ŭ' => 'U', 'ŭ' => 'u',
'Ů' => 'U', 'ů' => 'u',
'Ű' => 'U', 'ű' => 'u',
'Ų' => 'U', 'ų' => 'u',
'Ŵ' => 'W', 'ŵ' => 'w',
'Ŷ' => 'Y', 'ŷ' => 'y',
'Ÿ' => 'Y', 'Ź' => 'Z',
'ź' => 'z', 'Ż' => 'Z',
'ż' => 'z', 'Ž' => 'Z',
'ž' => 'z', 'ſ' => 's',
// Decompositions for Latin Extended-B
'Ș' => 'S', 'ș' => 's',
'Ț' => 'T', 'ț' => 't',
// Euro Sign
'€' => 'E',
// GBP (Pound) Sign
'£' => '',
// Vowels with diacritic (Vietnamese)
// unmarked
'Ơ' => 'O', 'ơ' => 'o',
'Ư' => 'U', 'ư' => 'u',
// grave accent
'Ầ' => 'A', 'ầ' => 'a',
'Ằ' => 'A', 'ằ' => 'a',
'Ề' => 'E', 'ề' => 'e',
'Ồ' => 'O', 'ồ' => 'o',
'Ờ' => 'O', 'ờ' => 'o',
'Ừ' => 'U', 'ừ' => 'u',
'Ỳ' => 'Y', 'ỳ' => 'y',
// hook
'Ả' => 'A', 'ả' => 'a',
'Ẩ' => 'A', 'ẩ' => 'a',
'Ẳ' => 'A', 'ẳ' => 'a',
'Ẻ' => 'E', 'ẻ' => 'e',
'Ể' => 'E', 'ể' => 'e',
'Ỉ' => 'I', 'ỉ' => 'i',
'Ỏ' => 'O', 'ỏ' => 'o',
'Ổ' => 'O', 'ổ' => 'o',
'Ở' => 'O', 'ở' => 'o',
'Ủ' => 'U', 'ủ' => 'u',
'Ử' => 'U', 'ử' => 'u',
'Ỷ' => 'Y', 'ỷ' => 'y',
// tilde
'Ẫ' => 'A', 'ẫ' => 'a',
'Ẵ' => 'A', 'ẵ' => 'a',
'Ẽ' => 'E', 'ẽ' => 'e',
'Ễ' => 'E', 'ễ' => 'e',
'Ỗ' => 'O', 'ỗ' => 'o',
'Ỡ' => 'O', 'ỡ' => 'o',
'Ữ' => 'U', 'ữ' => 'u',
'Ỹ' => 'Y', 'ỹ' => 'y',
// acute accent
'Ấ' => 'A', 'ấ' => 'a',
'Ắ' => 'A', 'ắ' => 'a',
'Ế' => 'E', 'ế' => 'e',
'Ố' => 'O', 'ố' => 'o',
'Ớ' => 'O', 'ớ' => 'o',
'Ứ' => 'U', 'ứ' => 'u',
// dot below
'Ạ' => 'A', 'ạ' => 'a',
'Ậ' => 'A', 'ậ' => 'a',
'Ặ' => 'A', 'ặ' => 'a',
'Ẹ' => 'E', 'ẹ' => 'e',
'Ệ' => 'E', 'ệ' => 'e',
'Ị' => 'I', 'ị' => 'i',
'Ọ' => 'O', 'ọ' => 'o',
'Ộ' => 'O', 'ộ' => 'o',
'Ợ' => 'O', 'ợ' => 'o',
'Ụ' => 'U', 'ụ' => 'u',
'Ự' => 'U', 'ự' => 'u',
'Ỵ' => 'Y', 'ỵ' => 'y',
// Vowels with diacritic (Chinese, Hanyu Pinyin)
'ɑ' => 'a',
// macron
'Ǖ' => 'U', 'ǖ' => 'u',
// acute accent
'Ǘ' => 'U', 'ǘ' => 'u',
// caron
'Ǎ' => 'A', 'ǎ' => 'a',
'Ǐ' => 'I', 'ǐ' => 'i',
'Ǒ' => 'O', 'ǒ' => 'o',
'Ǔ' => 'U', 'ǔ' => 'u',
'Ǚ' => 'U', 'ǚ' => 'u',
// grave accent
'Ǜ' => 'U', 'ǜ' => 'u',
];
return $string; $string = strtr($string, $chars);
}
return $string;
}
} }

View file

@ -1,432 +1,440 @@
<?php <?php
class BadDragonBridge extends BridgeAbstract {
const NAME = 'Bad Dragon Bridge';
const URI = 'https://bad-dragon.com/';
const CACHE_TIMEOUT = 300; // 5min
const DESCRIPTION = 'Returns sales or new clearance items';
const MAINTAINER = 'Roliga';
const PARAMETERS = array(
'Sales' => array(
),
'Clearance' => array(
'ready_made' => array(
'name' => 'Ready Made',
'type' => 'checkbox'
),
'flop' => array(
'name' => 'Flops',
'type' => 'checkbox'
),
'skus' => array(
'name' => 'Products',
'exampleValue' => 'chanceflared, crackers',
'title' => 'Comma separated list of product SKUs'
),
'onesize' => array(
'name' => 'One-Size',
'type' => 'checkbox'
),
'mini' => array(
'name' => 'Mini',
'type' => 'checkbox'
),
'small' => array(
'name' => 'Small',
'type' => 'checkbox'
),
'medium' => array(
'name' => 'Medium',
'type' => 'checkbox'
),
'large' => array(
'name' => 'Large',
'type' => 'checkbox'
),
'extralarge' => array(
'name' => 'Extra Large',
'type' => 'checkbox'
),
'category' => array(
'name' => 'Category',
'type' => 'list',
'values' => array(
'All' => 'all',
'Accessories' => 'accessories',
'Merchandise' => 'merchandise',
'Dildos' => 'insertable',
'Masturbators' => 'penetrable',
'Packers' => 'packer',
'Lil\' Squirts' => 'shooter',
'Lil\' Vibes' => 'vibrator',
'Wearables' => 'wearable'
),
'defaultValue' => 'all',
),
'soft' => array(
'name' => 'Soft Firmness',
'type' => 'checkbox'
),
'med_firm' => array(
'name' => 'Medium Firmness',
'type' => 'checkbox'
),
'firm' => array(
'name' => 'Firm',
'type' => 'checkbox'
),
'split' => array(
'name' => 'Split Firmness',
'type' => 'checkbox'
),
'maxprice' => array(
'name' => 'Max Price',
'type' => 'number',
'required' => true,
'defaultValue' => 300
),
'minprice' => array(
'name' => 'Min Price',
'type' => 'number',
'defaultValue' => 0
),
'cumtube' => array(
'name' => 'Cumtube',
'type' => 'checkbox'
),
'suctionCup' => array(
'name' => 'Suction Cup',
'type' => 'checkbox'
),
'noAccessories' => array(
'name' => 'No Accessories',
'type' => 'checkbox'
)
)
);
/* class BadDragonBridge extends BridgeAbstract
* This sets index $strFrom (or $strTo if set) in $outArr to 'on' if {
* $inArr[$param] contains $strFrom. const NAME = 'Bad Dragon Bridge';
* It is used for translating BD's shop filter URLs into something we can use. const URI = 'https://bad-dragon.com/';
* const CACHE_TIMEOUT = 300; // 5min
* For the query '?type[]=ready_made&type[]=flop' we would have an array like: const DESCRIPTION = 'Returns sales or new clearance items';
* Array ( const MAINTAINER = 'Roliga';
* [type] => Array ( const PARAMETERS = [
* [0] => ready_made 'Sales' => [
* [1] => flop ],
* ) 'Clearance' => [
* ) 'ready_made' => [
* which could be translated into: 'name' => 'Ready Made',
* Array ( 'type' => 'checkbox'
* [ready_made] => on ],
* [flop] => on 'flop' => [
* ) 'name' => 'Flops',
* */ 'type' => 'checkbox'
private function setParam($inArr, &$outArr, $param, $strFrom, $strTo = null) { ],
if(isset($inArr[$param]) && in_array($strFrom, $inArr[$param])) { 'skus' => [
$outArr[($strTo ?: $strFrom)] = 'on'; 'name' => 'Products',
} 'exampleValue' => 'chanceflared, crackers',
} 'title' => 'Comma separated list of product SKUs'
],
'onesize' => [
'name' => 'One-Size',
'type' => 'checkbox'
],
'mini' => [
'name' => 'Mini',
'type' => 'checkbox'
],
'small' => [
'name' => 'Small',
'type' => 'checkbox'
],
'medium' => [
'name' => 'Medium',
'type' => 'checkbox'
],
'large' => [
'name' => 'Large',
'type' => 'checkbox'
],
'extralarge' => [
'name' => 'Extra Large',
'type' => 'checkbox'
],
'category' => [
'name' => 'Category',
'type' => 'list',
'values' => [
'All' => 'all',
'Accessories' => 'accessories',
'Merchandise' => 'merchandise',
'Dildos' => 'insertable',
'Masturbators' => 'penetrable',
'Packers' => 'packer',
'Lil\' Squirts' => 'shooter',
'Lil\' Vibes' => 'vibrator',
'Wearables' => 'wearable'
],
'defaultValue' => 'all',
],
'soft' => [
'name' => 'Soft Firmness',
'type' => 'checkbox'
],
'med_firm' => [
'name' => 'Medium Firmness',
'type' => 'checkbox'
],
'firm' => [
'name' => 'Firm',
'type' => 'checkbox'
],
'split' => [
'name' => 'Split Firmness',
'type' => 'checkbox'
],
'maxprice' => [
'name' => 'Max Price',
'type' => 'number',
'required' => true,
'defaultValue' => 300
],
'minprice' => [
'name' => 'Min Price',
'type' => 'number',
'defaultValue' => 0
],
'cumtube' => [
'name' => 'Cumtube',
'type' => 'checkbox'
],
'suctionCup' => [
'name' => 'Suction Cup',
'type' => 'checkbox'
],
'noAccessories' => [
'name' => 'No Accessories',
'type' => 'checkbox'
]
]
];
public function detectParameters($url) { /*
$params = array(); * This sets index $strFrom (or $strTo if set) in $outArr to 'on' if
* $inArr[$param] contains $strFrom.
* It is used for translating BD's shop filter URLs into something we can use.
*
* For the query '?type[]=ready_made&type[]=flop' we would have an array like:
* Array (
* [type] => Array (
* [0] => ready_made
* [1] => flop
* )
* )
* which could be translated into:
* Array (
* [ready_made] => on
* [flop] => on
* )
* */
private function setParam($inArr, &$outArr, $param, $strFrom, $strTo = null)
{
if (isset($inArr[$param]) && in_array($strFrom, $inArr[$param])) {
$outArr[($strTo ?: $strFrom)] = 'on';
}
}
// Sale public function detectParameters($url)
$regex = '/^(https?:\/\/)?bad-dragon\.com\/sales/'; {
if(preg_match($regex, $url, $matches) > 0) { $params = [];
return $params;
}
// Clearance // Sale
$regex = '/^(https?:\/\/)?bad-dragon\.com\/shop\/clearance/'; $regex = '/^(https?:\/\/)?bad-dragon\.com\/sales/';
if(preg_match($regex, $url, $matches) > 0) { if (preg_match($regex, $url, $matches) > 0) {
parse_str(parse_url($url, PHP_URL_QUERY), $urlParams); return $params;
}
$this->setParam($urlParams, $params, 'type', 'ready_made'); // Clearance
$this->setParam($urlParams, $params, 'type', 'flop'); $regex = '/^(https?:\/\/)?bad-dragon\.com\/shop\/clearance/';
if (preg_match($regex, $url, $matches) > 0) {
parse_str(parse_url($url, PHP_URL_QUERY), $urlParams);
if(isset($urlParams['skus'])) { $this->setParam($urlParams, $params, 'type', 'ready_made');
$skus = array(); $this->setParam($urlParams, $params, 'type', 'flop');
foreach($urlParams['skus'] as $sku) {
is_string($sku) && $skus[] = $sku;
is_array($sku) && $skus[] = $sku[0];
}
$params['skus'] = implode(',', $skus);
}
$this->setParam($urlParams, $params, 'sizes', 'onesize'); if (isset($urlParams['skus'])) {
$this->setParam($urlParams, $params, 'sizes', 'mini'); $skus = [];
$this->setParam($urlParams, $params, 'sizes', 'small'); foreach ($urlParams['skus'] as $sku) {
$this->setParam($urlParams, $params, 'sizes', 'medium'); is_string($sku) && $skus[] = $sku;
$this->setParam($urlParams, $params, 'sizes', 'large'); is_array($sku) && $skus[] = $sku[0];
$this->setParam($urlParams, $params, 'sizes', 'extralarge'); }
$params['skus'] = implode(',', $skus);
}
if(isset($urlParams['category'])) { $this->setParam($urlParams, $params, 'sizes', 'onesize');
$params['category'] = strtolower($urlParams['category']); $this->setParam($urlParams, $params, 'sizes', 'mini');
} else{ $this->setParam($urlParams, $params, 'sizes', 'small');
$params['category'] = 'all'; $this->setParam($urlParams, $params, 'sizes', 'medium');
} $this->setParam($urlParams, $params, 'sizes', 'large');
$this->setParam($urlParams, $params, 'sizes', 'extralarge');
$this->setParam($urlParams, $params, 'firmnessValues', 'soft'); if (isset($urlParams['category'])) {
$this->setParam($urlParams, $params, 'firmnessValues', 'medium', 'med_firm'); $params['category'] = strtolower($urlParams['category']);
$this->setParam($urlParams, $params, 'firmnessValues', 'firm'); } else {
$this->setParam($urlParams, $params, 'firmnessValues', 'split'); $params['category'] = 'all';
}
if(isset($urlParams['price'])) { $this->setParam($urlParams, $params, 'firmnessValues', 'soft');
isset($urlParams['price']['max']) $this->setParam($urlParams, $params, 'firmnessValues', 'medium', 'med_firm');
&& $params['maxprice'] = $urlParams['price']['max']; $this->setParam($urlParams, $params, 'firmnessValues', 'firm');
isset($urlParams['price']['min']) $this->setParam($urlParams, $params, 'firmnessValues', 'split');
&& $params['minprice'] = $urlParams['price']['min'];
}
isset($urlParams['cumtube']) if (isset($urlParams['price'])) {
&& $urlParams['cumtube'] === '1' isset($urlParams['price']['max'])
&& $params['cumtube'] = 'on'; && $params['maxprice'] = $urlParams['price']['max'];
isset($urlParams['suctionCup']) isset($urlParams['price']['min'])
&& $urlParams['suctionCup'] === '1' && $params['minprice'] = $urlParams['price']['min'];
&& $params['suctionCup'] = 'on'; }
isset($urlParams['noAccessories'])
&& $urlParams['noAccessories'] === '1'
&& $params['noAccessories'] = 'on';
return $params; isset($urlParams['cumtube'])
} && $urlParams['cumtube'] === '1'
&& $params['cumtube'] = 'on';
isset($urlParams['suctionCup'])
&& $urlParams['suctionCup'] === '1'
&& $params['suctionCup'] = 'on';
isset($urlParams['noAccessories'])
&& $urlParams['noAccessories'] === '1'
&& $params['noAccessories'] = 'on';
return null; return $params;
} }
public function getName() { return null;
switch($this->queriedContext) { }
case 'Sales':
return 'Bad Dragon Sales';
case 'Clearance':
return 'Bad Dragon Clearance Search';
default:
return parent::getName();
}
}
public function getURI() { public function getName()
switch($this->queriedContext) { {
case 'Sales': switch ($this->queriedContext) {
return self::URI . 'sales'; case 'Sales':
case 'Clearance': return 'Bad Dragon Sales';
return $this->inputToURL(); case 'Clearance':
default: return 'Bad Dragon Clearance Search';
return parent::getURI(); default:
} return parent::getName();
} }
}
public function collectData() { public function getURI()
switch($this->queriedContext) { {
case 'Sales': switch ($this->queriedContext) {
$sales = json_decode(getContents(self::URI . 'api/sales')); case 'Sales':
return self::URI . 'sales';
case 'Clearance':
return $this->inputToURL();
default:
return parent::getURI();
}
}
foreach($sales as $sale) { public function collectData()
$item = array(); {
switch ($this->queriedContext) {
case 'Sales':
$sales = json_decode(getContents(self::URI . 'api/sales'));
$item['title'] = $sale->title; foreach ($sales as $sale) {
$item['timestamp'] = strtotime($sale->startDate); $item = [];
$item['uri'] = $this->getURI() . '/' . $sale->slug; $item['title'] = $sale->title;
$item['timestamp'] = strtotime($sale->startDate);
$contentHTML = '<p><img src="' . $sale->image->url . '"></p>'; $item['uri'] = $this->getURI() . '/' . $sale->slug;
if(isset($sale->endDate)) {
$contentHTML .= '<p><b>This promotion ends on '
. gmdate('M j, Y \a\t g:i A T', strtotime($sale->endDate))
. '</b></p>';
} else {
$contentHTML .= '<p><b>This promotion never ends</b></p>';
}
$ul = false;
$content = json_decode($sale->content);
foreach($content->blocks as $block) {
switch($block->type) {
case 'header-one':
$contentHTML .= '<h1>' . $block->text . '</h1>';
break;
case 'header-two':
$contentHTML .= '<h2>' . $block->text . '</h2>';
break;
case 'header-three':
$contentHTML .= '<h3>' . $block->text . '</h3>';
break;
case 'unordered-list-item':
if(!$ul) {
$contentHTML .= '<ul>';
$ul = true;
}
$contentHTML .= '<li>' . $block->text . '</li>';
break;
default:
if($ul) {
$contentHTML .= '</ul>';
$ul = false;
}
$contentHTML .= '<p>' . $block->text . '</p>';
break;
}
}
$item['content'] = $contentHTML;
$this->items[] = $item; $contentHTML = '<p><img src="' . $sale->image->url . '"></p>';
} if (isset($sale->endDate)) {
break; $contentHTML .= '<p><b>This promotion ends on '
case 'Clearance': . gmdate('M j, Y \a\t g:i A T', strtotime($sale->endDate))
$toyData = json_decode(getContents($this->inputToURL(true))); . '</b></p>';
} else {
$contentHTML .= '<p><b>This promotion never ends</b></p>';
}
$ul = false;
$content = json_decode($sale->content);
foreach ($content->blocks as $block) {
switch ($block->type) {
case 'header-one':
$contentHTML .= '<h1>' . $block->text . '</h1>';
break;
case 'header-two':
$contentHTML .= '<h2>' . $block->text . '</h2>';
break;
case 'header-three':
$contentHTML .= '<h3>' . $block->text . '</h3>';
break;
case 'unordered-list-item':
if (!$ul) {
$contentHTML .= '<ul>';
$ul = true;
}
$contentHTML .= '<li>' . $block->text . '</li>';
break;
default:
if ($ul) {
$contentHTML .= '</ul>';
$ul = false;
}
$contentHTML .= '<p>' . $block->text . '</p>';
break;
}
}
$item['content'] = $contentHTML;
$productList = json_decode(getContents(self::URI $this->items[] = $item;
. 'api/inventory-toy/product-list')); }
break;
case 'Clearance':
$toyData = json_decode(getContents($this->inputToURL(true)));
foreach($toyData->toys as $toy) { $productList = json_decode(getContents(self::URI
$item = array(); . 'api/inventory-toy/product-list'));
$item['uri'] = $this->getURI() foreach ($toyData->toys as $toy) {
. '#' $item = [];
. $toy->id;
$item['timestamp'] = strtotime($toy->created);
foreach($productList as $product) { $item['uri'] = $this->getURI()
if($product->sku == $toy->sku) { . '#'
$item['title'] = $product->name; . $toy->id;
break; $item['timestamp'] = strtotime($toy->created);
}
}
// images foreach ($productList as $product) {
$content = '<p>'; if ($product->sku == $toy->sku) {
foreach($toy->images as $image) { $item['title'] = $product->name;
$content .= '<a href="' break;
. $image->fullFilename }
. '"><img src="' }
. $image->thumbFilename
. '" /></a>';
}
// price
$content .= '</p><p><b>Price:</b> $'
. $toy->price
// size
. '<br /><b>Size:</b> '
. $toy->size
// color
. '<br /><b>Color:</b> '
. $toy->color
// features
. '<br /><b>Features:</b> '
. ($toy->suction_cup ? 'Suction cup' : '')
. ($toy->suction_cup && $toy->cumtube ? ', ' : '')
. ($toy->cumtube ? 'Cumtube' : '')
. ($toy->suction_cup || $toy->cumtube ? '' : 'None');
// firmness
$firmnessTexts = array(
'2' => 'Extra soft',
'3' => 'Soft',
'5' => 'Medium',
'8' => 'Firm'
);
$firmnesses = explode('/', $toy->firmness);
if(count($firmnesses) === 2) {
$content .= '<br /><b>Firmness:</b> '
. $firmnessTexts[$firmnesses[0]]
. ', '
. $firmnessTexts[$firmnesses[1]];
} else{
$content .= '<br /><b>Firmness:</b> '
. $firmnessTexts[$firmnesses[0]];
}
// flop
if($toy->type === 'flop') {
$content .= '<br /><b>Flop reason:</b> '
. $toy->flop_reason;
}
$content .= '</p>';
$item['content'] = $content;
$enclosures = array(); // images
foreach($toy->images as $image) { $content = '<p>';
$enclosures[] = $image->fullFilename; foreach ($toy->images as $image) {
} $content .= '<a href="'
$item['enclosures'] = $enclosures; . $image->fullFilename
. '"><img src="'
. $image->thumbFilename
. '" /></a>';
}
// price
$content .= '</p><p><b>Price:</b> $'
. $toy->price
// size
. '<br /><b>Size:</b> '
. $toy->size
// color
. '<br /><b>Color:</b> '
. $toy->color
// features
. '<br /><b>Features:</b> '
. ($toy->suction_cup ? 'Suction cup' : '')
. ($toy->suction_cup && $toy->cumtube ? ', ' : '')
. ($toy->cumtube ? 'Cumtube' : '')
. ($toy->suction_cup || $toy->cumtube ? '' : 'None');
// firmness
$firmnessTexts = [
'2' => 'Extra soft',
'3' => 'Soft',
'5' => 'Medium',
'8' => 'Firm'
];
$firmnesses = explode('/', $toy->firmness);
if (count($firmnesses) === 2) {
$content .= '<br /><b>Firmness:</b> '
. $firmnessTexts[$firmnesses[0]]
. ', '
. $firmnessTexts[$firmnesses[1]];
} else {
$content .= '<br /><b>Firmness:</b> '
. $firmnessTexts[$firmnesses[0]];
}
// flop
if ($toy->type === 'flop') {
$content .= '<br /><b>Flop reason:</b> '
. $toy->flop_reason;
}
$content .= '</p>';
$item['content'] = $content;
$categories = array(); $enclosures = [];
$categories[] = $toy->sku; foreach ($toy->images as $image) {
$categories[] = $toy->type; $enclosures[] = $image->fullFilename;
$categories[] = $toy->size; }
if($toy->cumtube) { $item['enclosures'] = $enclosures;
$categories[] = 'cumtube';
}
if($toy->suction_cup) {
$categories[] = 'suction_cup';
}
$item['categories'] = $categories;
$this->items[] = $item; $categories = [];
} $categories[] = $toy->sku;
break; $categories[] = $toy->type;
} $categories[] = $toy->size;
} if ($toy->cumtube) {
$categories[] = 'cumtube';
}
if ($toy->suction_cup) {
$categories[] = 'suction_cup';
}
$item['categories'] = $categories;
private function inputToURL($api = false) { $this->items[] = $item;
$url = self::URI; }
$url .= ($api ? 'api/inventory-toys?' : 'shop/clearance?'); break;
}
}
// Default parameters private function inputToURL($api = false)
$url .= 'limit=60'; {
$url .= '&page=1'; $url = self::URI;
$url .= '&sort[field]=created'; $url .= ($api ? 'api/inventory-toys?' : 'shop/clearance?');
$url .= '&sort[direction]=desc';
// Product types // Default parameters
$url .= ($this->getInput('ready_made') ? '&type[]=ready_made' : ''); $url .= 'limit=60';
$url .= ($this->getInput('flop') ? '&type[]=flop' : ''); $url .= '&page=1';
$url .= '&sort[field]=created';
$url .= '&sort[direction]=desc';
// Product names // Product types
foreach(array_filter(explode(',', $this->getInput('skus'))) as $sku) { $url .= ($this->getInput('ready_made') ? '&type[]=ready_made' : '');
$url .= '&skus[]=' . urlencode(trim($sku)); $url .= ($this->getInput('flop') ? '&type[]=flop' : '');
}
// Size // Product names
$url .= ($this->getInput('onesize') ? '&sizes[]=onesize' : ''); foreach (array_filter(explode(',', $this->getInput('skus'))) as $sku) {
$url .= ($this->getInput('mini') ? '&sizes[]=mini' : ''); $url .= '&skus[]=' . urlencode(trim($sku));
$url .= ($this->getInput('small') ? '&sizes[]=small' : ''); }
$url .= ($this->getInput('medium') ? '&sizes[]=medium' : '');
$url .= ($this->getInput('large') ? '&sizes[]=large' : '');
$url .= ($this->getInput('extralarge') ? '&sizes[]=extralarge' : '');
// Category // Size
$url .= ($this->getInput('category') ? '&category=' $url .= ($this->getInput('onesize') ? '&sizes[]=onesize' : '');
. urlencode($this->getInput('category')) : ''); $url .= ($this->getInput('mini') ? '&sizes[]=mini' : '');
$url .= ($this->getInput('small') ? '&sizes[]=small' : '');
$url .= ($this->getInput('medium') ? '&sizes[]=medium' : '');
$url .= ($this->getInput('large') ? '&sizes[]=large' : '');
$url .= ($this->getInput('extralarge') ? '&sizes[]=extralarge' : '');
// Firmness // Category
if($api) { $url .= ($this->getInput('category') ? '&category='
$url .= ($this->getInput('soft') ? '&firmnessValues[]=3' : ''); . urlencode($this->getInput('category')) : '');
$url .= ($this->getInput('med_firm') ? '&firmnessValues[]=5' : '');
$url .= ($this->getInput('firm') ? '&firmnessValues[]=8' : '');
if($this->getInput('split')) {
$url .= '&firmnessValues[]=3/5';
$url .= '&firmnessValues[]=3/8';
$url .= '&firmnessValues[]=8/3';
$url .= '&firmnessValues[]=5/8';
$url .= '&firmnessValues[]=8/5';
}
} else{
$url .= ($this->getInput('soft') ? '&firmnessValues[]=soft' : '');
$url .= ($this->getInput('med_firm') ? '&firmnessValues[]=medium' : '');
$url .= ($this->getInput('firm') ? '&firmnessValues[]=firm' : '');
$url .= ($this->getInput('split') ? '&firmnessValues[]=split' : '');
}
// Price // Firmness
$url .= ($this->getInput('maxprice') ? '&price[max]=' if ($api) {
. $this->getInput('maxprice') : '&price[max]=300'); $url .= ($this->getInput('soft') ? '&firmnessValues[]=3' : '');
$url .= ($this->getInput('minprice') ? '&price[min]=' $url .= ($this->getInput('med_firm') ? '&firmnessValues[]=5' : '');
. $this->getInput('minprice') : '&price[min]=0'); $url .= ($this->getInput('firm') ? '&firmnessValues[]=8' : '');
if ($this->getInput('split')) {
$url .= '&firmnessValues[]=3/5';
$url .= '&firmnessValues[]=3/8';
$url .= '&firmnessValues[]=8/3';
$url .= '&firmnessValues[]=5/8';
$url .= '&firmnessValues[]=8/5';
}
} else {
$url .= ($this->getInput('soft') ? '&firmnessValues[]=soft' : '');
$url .= ($this->getInput('med_firm') ? '&firmnessValues[]=medium' : '');
$url .= ($this->getInput('firm') ? '&firmnessValues[]=firm' : '');
$url .= ($this->getInput('split') ? '&firmnessValues[]=split' : '');
}
// Features // Price
$url .= ($this->getInput('cumtube') ? '&cumtube=1' : ''); $url .= ($this->getInput('maxprice') ? '&price[max]='
$url .= ($this->getInput('suctionCup') ? '&suctionCup=1' : ''); . $this->getInput('maxprice') : '&price[max]=300');
$url .= ($this->getInput('noAccessories') ? '&noAccessories=1' : ''); $url .= ($this->getInput('minprice') ? '&price[min]='
. $this->getInput('minprice') : '&price[min]=0');
return $url; // Features
} $url .= ($this->getInput('cumtube') ? '&cumtube=1' : '');
$url .= ($this->getInput('suctionCup') ? '&suctionCup=1' : '');
$url .= ($this->getInput('noAccessories') ? '&noAccessories=1' : '');
return $url;
}
} }

View file

@ -1,186 +1,211 @@
<?php <?php
class BakaUpdatesMangaReleasesBridge extends BridgeAbstract {
const NAME = 'Baka Updates Manga Releases';
const URI = 'https://www.mangaupdates.com/';
const DESCRIPTION = 'Get the latest series releases';
const MAINTAINER = 'fulmeek, KamaleiZestri';
const PARAMETERS = array(
'By series' => array(
'series_id' => array(
'name' => 'Series ID',
'type' => 'number',
'required' => true,
'exampleValue' => '188066'
)
),
'By list' => array(
'list_id' => array(
'name' => 'List ID and Type',
'type' => 'text',
'required' => true,
'exampleValue' => '4395&list=read'
)
)
);
const LIMIT_COLS = 5;
const LIMIT_ITEMS = 10;
const RELEASES_URL = 'https://www.mangaupdates.com/releases.html';
private $feedName = ''; class BakaUpdatesMangaReleasesBridge extends BridgeAbstract
{
const NAME = 'Baka Updates Manga Releases';
const URI = 'https://www.mangaupdates.com/';
const DESCRIPTION = 'Get the latest series releases';
const MAINTAINER = 'fulmeek, KamaleiZestri';
const PARAMETERS = [
'By series' => [
'series_id' => [
'name' => 'Series ID',
'type' => 'number',
'required' => true,
'exampleValue' => '188066'
]
],
'By list' => [
'list_id' => [
'name' => 'List ID and Type',
'type' => 'text',
'required' => true,
'exampleValue' => '4395&list=read'
]
]
];
const LIMIT_COLS = 5;
const LIMIT_ITEMS = 10;
const RELEASES_URL = 'https://www.mangaupdates.com/releases.html';
public function collectData() { private $feedName = '';
if($this -> queriedContext == 'By series')
$this -> collectDataBySeries();
else //queriedContext == 'By list'
$this -> collectDataByList();
}
public function getURI(){ public function collectData()
if($this -> queriedContext == 'By series') { {
$series_id = $this->getInput('series_id'); if ($this -> queriedContext == 'By series') {
if (!empty($series_id)) { $this -> collectDataBySeries();
return self::URI . 'releases.html?search=' . $series_id . '&stype=series'; } else { //queriedContext == 'By list'
} $this -> collectDataByList();
} else //queriedContext == 'By list' }
return self::RELEASES_URL; }
return self::URI; public function getURI()
} {
if ($this -> queriedContext == 'By series') {
$series_id = $this->getInput('series_id');
if (!empty($series_id)) {
return self::URI . 'releases.html?search=' . $series_id . '&stype=series';
}
} else { //queriedContext == 'By list'
return self::RELEASES_URL;
}
public function getName(){ return self::URI;
if(!empty($this->feedName)) { }
return $this->feedName . ' - ' . self::NAME;
}
return parent::getName();
}
private function getSanitizedHash($string) { public function getName()
return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string)))); {
} if (!empty($this->feedName)) {
return $this->feedName . ' - ' . self::NAME;
}
return parent::getName();
}
private function filterText($text) { private function getSanitizedHash($string)
return rtrim($text, '* '); {
} return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string))));
}
private function filterHTML($text) { private function filterText($text)
return $this->filterText(html_entity_decode($text)); {
} return rtrim($text, '* ');
}
private function findID($manga) { private function filterHTML($text)
// sometimes new series are on the release list that have no ID. just drop them. {
if(@$this -> filterHTML($manga -> find('a', 0) -> href) != null) { return $this->filterText(html_entity_decode($text));
preg_match('/id=([0-9]*)/', $this -> filterHTML($manga -> find('a', 0) -> href), $match); }
return $match[1];
} else
return 0;
}
private function collectDataBySeries() { private function findID($manga)
$html = getSimpleHTMLDOM($this->getURI()); {
// sometimes new series are on the release list that have no ID. just drop them.
if (@$this -> filterHTML($manga -> find('a', 0) -> href) != null) {
preg_match('/id=([0-9]*)/', $this -> filterHTML($manga -> find('a', 0) -> href), $match);
return $match[1];
} else {
return 0;
}
}
// content is an unstructured pile of divs, ugly to parse private function collectDataBySeries()
$cols = $html->find('div#main_content div.row > div.text'); {
if (!$cols) $html = getSimpleHTMLDOM($this->getURI());
returnServerError('No releases');
$rows = array_slice( // content is an unstructured pile of divs, ugly to parse
array_chunk($cols, self::LIMIT_COLS), 0, self::LIMIT_ITEMS $cols = $html->find('div#main_content div.row > div.text');
); if (!$cols) {
returnServerError('No releases');
}
if (isset($rows[0][1])) { $rows = array_slice(
$this->feedName = $this->filterHTML($rows[0][1]->plaintext); array_chunk($cols, self::LIMIT_COLS),
} 0,
self::LIMIT_ITEMS
);
foreach($rows as $cols) { if (isset($rows[0][1])) {
if (count($cols) < self::LIMIT_COLS) continue; $this->feedName = $this->filterHTML($rows[0][1]->plaintext);
}
$item = array(); foreach ($rows as $cols) {
$title = array(); if (count($cols) < self::LIMIT_COLS) {
continue;
}
$item['content'] = ''; $item = [];
$title = [];
$objDate = $cols[0]; $item['content'] = '';
if ($objDate)
$item['timestamp'] = strtotime($objDate->plaintext);
$objTitle = $cols[1]; $objDate = $cols[0];
if ($objTitle) { if ($objDate) {
$title[] = $this->filterHTML($objTitle->plaintext); $item['timestamp'] = strtotime($objDate->plaintext);
$item['content'] .= '<p>Series: ' . $this->filterText($objTitle->innertext) . '</p>'; }
}
$objVolume = $cols[2]; $objTitle = $cols[1];
if ($objVolume && !empty($objVolume->plaintext)) if ($objTitle) {
$title[] = 'Vol.' . $objVolume->plaintext; $title[] = $this->filterHTML($objTitle->plaintext);
$item['content'] .= '<p>Series: ' . $this->filterText($objTitle->innertext) . '</p>';
}
$objChapter = $cols[3]; $objVolume = $cols[2];
if ($objChapter && !empty($objChapter->plaintext)) if ($objVolume && !empty($objVolume->plaintext)) {
$title[] = 'Chp.' . $objChapter->plaintext; $title[] = 'Vol.' . $objVolume->plaintext;
}
$objAuthor = $cols[4]; $objChapter = $cols[3];
if ($objAuthor && !empty($objAuthor->plaintext)) { if ($objChapter && !empty($objChapter->plaintext)) {
$item['author'] = $this->filterHTML($objAuthor->plaintext); $title[] = 'Chp.' . $objChapter->plaintext;
$item['content'] .= '<p>Groups: ' . $this->filterText($objAuthor->innertext) . '</p>'; }
}
$item['title'] = implode(' ', $title); $objAuthor = $cols[4];
$item['uri'] = $this->getURI(); if ($objAuthor && !empty($objAuthor->plaintext)) {
$item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']); $item['author'] = $this->filterHTML($objAuthor->plaintext);
$item['content'] .= '<p>Groups: ' . $this->filterText($objAuthor->innertext) . '</p>';
}
$this->items[] = $item; $item['title'] = implode(' ', $title);
} $item['uri'] = $this->getURI();
} $item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']);
private function collectDataByList() { $this->items[] = $item;
$this -> feedName = 'Releases'; }
$list = array(); }
$releasesHTML = getSimpleHTMLDOM(self::RELEASES_URL); private function collectDataByList()
{
$this -> feedName = 'Releases';
$list = [];
$list_id = $this -> getInput('list_id'); $releasesHTML = getSimpleHTMLDOM(self::RELEASES_URL);
$listHTML = getSimpleHTMLDOM('https://www.mangaupdates.com/mylist.html?id=' . $list_id);
//get ids of the manga that the user follows, $list_id = $this -> getInput('list_id');
$parts = $listHTML -> find('table#ptable tr > td.pl'); $listHTML = getSimpleHTMLDOM('https://www.mangaupdates.com/mylist.html?id=' . $list_id);
foreach($parts as $part) {
$list[] = $this -> findID($part);
}
//similar to above, but the divs are in groups of 3. //get ids of the manga that the user follows,
$cols = $releasesHTML -> find('div#main_content div.row > div.pbreak'); $parts = $listHTML -> find('table#ptable tr > td.pl');
$rows = array_slice(array_chunk($cols, 3), 0); foreach ($parts as $part) {
$list[] = $this -> findID($part);
}
foreach($rows as $cols) { //similar to above, but the divs are in groups of 3.
//check if current manga is in user's list. $cols = $releasesHTML -> find('div#main_content div.row > div.pbreak');
$id = $this -> findId($cols[0]); $rows = array_slice(array_chunk($cols, 3), 0);
if(!array_search($id, $list)) continue;
$item = array(); foreach ($rows as $cols) {
$title = array(); //check if current manga is in user's list.
$id = $this -> findId($cols[0]);
if (!array_search($id, $list)) {
continue;
}
$item['content'] = ''; $item = [];
$title = [];
$objTitle = $cols[0]; $item['content'] = '';
if ($objTitle) {
$title[] = $this->filterHTML($objTitle->plaintext);
$item['content'] .= '<p>Series: ' . $this->filterHTML($objTitle -> innertext) . '</p>';
}
$objVolChap = $cols[1]; $objTitle = $cols[0];
if ($objVolChap && !empty($objVolChap->plaintext)) if ($objTitle) {
$title[] = $this -> filterHTML($objVolChap -> innertext); $title[] = $this->filterHTML($objTitle->plaintext);
$item['content'] .= '<p>Series: ' . $this->filterHTML($objTitle -> innertext) . '</p>';
}
$objAuthor = $cols[2]; $objVolChap = $cols[1];
if ($objAuthor && !empty($objAuthor->plaintext)) { if ($objVolChap && !empty($objVolChap->plaintext)) {
$item['author'] = $this->filterHTML($objAuthor -> plaintext); $title[] = $this -> filterHTML($objVolChap -> innertext);
$item['content'] .= '<p>Groups: ' . $this->filterHTML($objAuthor -> innertext) . '</p>'; }
}
$item['title'] = implode(' ', $title); $objAuthor = $cols[2];
$item['uri'] = self::URI . 'releases.html?search=' . $id . '&stype=series'; if ($objAuthor && !empty($objAuthor->plaintext)) {
$item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']); $item['author'] = $this->filterHTML($objAuthor -> plaintext);
$item['content'] .= '<p>Groups: ' . $this->filterHTML($objAuthor -> innertext) . '</p>';
}
$this->items[] = $item; $item['title'] = implode(' ', $title);
} $item['uri'] = self::URI . 'releases.html?search=' . $id . '&stype=series';
} $item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']);
$this->items[] = $item;
}
}
} }

View file

@ -1,408 +1,420 @@
<?php <?php
class BandcampBridge extends BridgeAbstract {
const MAINTAINER = 'sebsauvage, Roliga'; class BandcampBridge extends BridgeAbstract
const NAME = 'Bandcamp Bridge'; {
const URI = 'https://bandcamp.com/'; const MAINTAINER = 'sebsauvage, Roliga';
const CACHE_TIMEOUT = 600; // 10min const NAME = 'Bandcamp Bridge';
const DESCRIPTION = 'New bandcamp releases by tag, band or album'; const URI = 'https://bandcamp.com/';
const PARAMETERS = array( const CACHE_TIMEOUT = 600; // 10min
'By tag' => array( const DESCRIPTION = 'New bandcamp releases by tag, band or album';
'tag' => array( const PARAMETERS = [
'name' => 'tag', 'By tag' => [
'type' => 'text', 'tag' => [
'required' => true, 'name' => 'tag',
'exampleValue' => 'hip-hop-rap' 'type' => 'text',
) 'required' => true,
), 'exampleValue' => 'hip-hop-rap'
'By band' => array( ]
'band' => array( ],
'name' => 'band', 'By band' => [
'type' => 'text', 'band' => [
'title' => 'Band name as seen in the band page URL', 'name' => 'band',
'required' => true, 'type' => 'text',
'exampleValue' => 'aesoprock' 'title' => 'Band name as seen in the band page URL',
), 'required' => true,
'type' => array( 'exampleValue' => 'aesoprock'
'name' => 'Articles are', ],
'type' => 'list', 'type' => [
'values' => array( 'name' => 'Articles are',
'Releases' => 'releases', 'type' => 'list',
'Releases, new one when track list changes' => 'changes', 'values' => [
'Individual tracks' => 'tracks' 'Releases' => 'releases',
), 'Releases, new one when track list changes' => 'changes',
'defaultValue' => 'changes' 'Individual tracks' => 'tracks'
), ],
'limit' => array( 'defaultValue' => 'changes'
'name' => 'limit', ],
'type' => 'number', 'limit' => [
'required' => true, 'name' => 'limit',
'title' => 'Number of releases to return', 'type' => 'number',
'defaultValue' => 5 'required' => true,
) 'title' => 'Number of releases to return',
), 'defaultValue' => 5
'By label' => array( ]
'label' => array( ],
'name' => 'label', 'By label' => [
'type' => 'text', 'label' => [
'title' => 'label name as seen in the label page URL', 'name' => 'label',
'required' => true 'type' => 'text',
), 'title' => 'label name as seen in the label page URL',
'type' => array( 'required' => true
'name' => 'Articles are', ],
'type' => 'list', 'type' => [
'values' => array( 'name' => 'Articles are',
'Releases' => 'releases', 'type' => 'list',
'Releases, new one when track list changes' => 'changes', 'values' => [
'Individual tracks' => 'tracks' 'Releases' => 'releases',
), 'Releases, new one when track list changes' => 'changes',
'defaultValue' => 'changes' 'Individual tracks' => 'tracks'
), ],
'limit' => array( 'defaultValue' => 'changes'
'name' => 'limit', ],
'type' => 'number', 'limit' => [
'title' => 'Number of releases to return', 'name' => 'limit',
'defaultValue' => 5 'type' => 'number',
) 'title' => 'Number of releases to return',
), 'defaultValue' => 5
'By album' => array( ]
'band' => array( ],
'name' => 'band', 'By album' => [
'type' => 'text', 'band' => [
'title' => 'Band name as seen in the album page URL', 'name' => 'band',
'required' => true, 'type' => 'text',
'exampleValue' => 'aesoprock' 'title' => 'Band name as seen in the album page URL',
), 'required' => true,
'album' => array( 'exampleValue' => 'aesoprock'
'name' => 'album', ],
'type' => 'text', 'album' => [
'title' => 'Album name as seen in the album page URL', 'name' => 'album',
'required' => true, 'type' => 'text',
'exampleValue' => 'appleseed' 'title' => 'Album name as seen in the album page URL',
), 'required' => true,
'type' => array( 'exampleValue' => 'appleseed'
'name' => 'Articles are', ],
'type' => 'list', 'type' => [
'values' => array( 'name' => 'Articles are',
'Releases' => 'releases', 'type' => 'list',
'Releases, new one when track list changes' => 'changes', 'values' => [
'Individual tracks' => 'tracks' 'Releases' => 'releases',
), 'Releases, new one when track list changes' => 'changes',
'defaultValue' => 'tracks' 'Individual tracks' => 'tracks'
) ],
) 'defaultValue' => 'tracks'
); ]
const IMGURI = 'https://f4.bcbits.com/'; ]
const IMGSIZE_300PX = 23; ];
const IMGSIZE_700PX = 16; const IMGURI = 'https://f4.bcbits.com/';
const IMGSIZE_300PX = 23;
const IMGSIZE_700PX = 16;
private $feedName; private $feedName;
public function getIcon() { public function getIcon()
return 'https://s4.bcbits.com/img/bc_favicon.ico'; {
} return 'https://s4.bcbits.com/img/bc_favicon.ico';
}
public function collectData(){ public function collectData()
switch($this->queriedContext) { {
case 'By tag': switch ($this->queriedContext) {
$url = self::URI . 'api/hub/1/dig_deeper'; case 'By tag':
$data = $this->buildRequestJson(); $url = self::URI . 'api/hub/1/dig_deeper';
$header = array( $data = $this->buildRequestJson();
'Content-Type: application/json', $header = [
'Content-Length: ' . strlen($data) 'Content-Type: application/json',
); 'Content-Length: ' . strlen($data)
$opts = array( ];
CURLOPT_CUSTOMREQUEST => 'POST', $opts = [
CURLOPT_POSTFIELDS => $data CURLOPT_CUSTOMREQUEST => 'POST',
); CURLOPT_POSTFIELDS => $data
$content = getContents($url, $header, $opts); ];
$content = getContents($url, $header, $opts);
$json = json_decode($content); $json = json_decode($content);
if ($json->ok !== true) { if ($json->ok !== true) {
returnServerError('Invalid response'); returnServerError('Invalid response');
} }
foreach ($json->items as $entry) { foreach ($json->items as $entry) {
$url = $entry->tralbum_url; $url = $entry->tralbum_url;
$artist = $entry->artist; $artist = $entry->artist;
$title = $entry->title; $title = $entry->title;
// e.g. record label is the releaser, but not the artist // e.g. record label is the releaser, but not the artist
$releaser = $entry->band_name !== $entry->artist ? $entry->band_name : null; $releaser = $entry->band_name !== $entry->artist ? $entry->band_name : null;
$full_title = $artist . ' - ' . $title; $full_title = $artist . ' - ' . $title;
$full_artist = $artist; $full_artist = $artist;
if (isset($releaser)) { if (isset($releaser)) {
$full_title .= ' (' . $releaser . ')'; $full_title .= ' (' . $releaser . ')';
$full_artist .= ' (' . $releaser . ')'; $full_artist .= ' (' . $releaser . ')';
} }
$small_img = $this->getImageUrl($entry->art_id, self::IMGSIZE_300PX); $small_img = $this->getImageUrl($entry->art_id, self::IMGSIZE_300PX);
$img = $this->getImageUrl($entry->art_id, self::IMGSIZE_700PX); $img = $this->getImageUrl($entry->art_id, self::IMGSIZE_700PX);
$item = array( $item = [
'uri' => $url, 'uri' => $url,
'author' => $full_artist, 'author' => $full_artist,
'title' => $full_title 'title' => $full_title
); ];
$item['content'] = "<img src='$small_img' /><br/>$full_title"; $item['content'] = "<img src='$small_img' /><br/>$full_title";
$item['enclosures'] = array($img); $item['enclosures'] = [$img];
$this->items[] = $item; $this->items[] = $item;
} }
break; break;
case 'By band': case 'By band':
case 'By label': case 'By label':
case 'By album': case 'By album':
$html = getSimpleHTMLDOMCached($this->getURI(), 86400); $html = getSimpleHTMLDOMCached($this->getURI(), 86400);
if ($html->find('meta[name=title]', 0)) { if ($html->find('meta[name=title]', 0)) {
$this->feedName = $html->find('meta[name=title]', 0)->content; $this->feedName = $html->find('meta[name=title]', 0)->content;
} else { } else {
$this->feedName = str_replace('Music | ', '', $html->find('title', 0)->plaintext); $this->feedName = str_replace('Music | ', '', $html->find('title', 0)->plaintext);
} }
$regex = '/band_id=(\d+)/'; $regex = '/band_id=(\d+)/';
if(preg_match($regex, $html, $matches) == false) if (preg_match($regex, $html, $matches) == false) {
returnServerError('Unable to find band ID on: ' . $this->getURI()); returnServerError('Unable to find band ID on: ' . $this->getURI());
$band_id = $matches[1]; }
$band_id = $matches[1];
$tralbums = array(); $tralbums = [];
switch($this->queriedContext) { switch ($this->queriedContext) {
case 'By band': case 'By band':
case 'By label': case 'By label':
$query_data = array( $query_data = [
'band_id' => $band_id 'band_id' => $band_id
); ];
$band_data = $this->apiGet('mobile/22/band_details', $query_data); $band_data = $this->apiGet('mobile/22/band_details', $query_data);
$num_albums = min(count($band_data->discography), $this->getInput('limit')); $num_albums = min(count($band_data->discography), $this->getInput('limit'));
for($i = 0; $i < $num_albums; $i++) { for ($i = 0; $i < $num_albums; $i++) {
$album_basic_data = $band_data->discography[$i]; $album_basic_data = $band_data->discography[$i];
// 'a' or 't' for albums and individual tracks respectively // 'a' or 't' for albums and individual tracks respectively
$tralbum_type = substr($album_basic_data->item_type, 0, 1); $tralbum_type = substr($album_basic_data->item_type, 0, 1);
$query_data = array( $query_data = [
'band_id' => $band_id, 'band_id' => $band_id,
'tralbum_type' => $tralbum_type, 'tralbum_type' => $tralbum_type,
'tralbum_id' => $album_basic_data->item_id 'tralbum_id' => $album_basic_data->item_id
); ];
$tralbums[] = $this->apiGet('mobile/22/tralbum_details', $query_data); $tralbums[] = $this->apiGet('mobile/22/tralbum_details', $query_data);
} }
break; break;
case 'By album': case 'By album':
$regex = '/album=(\d+)/'; $regex = '/album=(\d+)/';
if(preg_match($regex, $html, $matches) == false) if (preg_match($regex, $html, $matches) == false) {
returnServerError('Unable to find album ID on: ' . $this->getURI()); returnServerError('Unable to find album ID on: ' . $this->getURI());
$album_id = $matches[1]; }
$album_id = $matches[1];
$query_data = array( $query_data = [
'band_id' => $band_id, 'band_id' => $band_id,
'tralbum_type' => 'a', 'tralbum_type' => 'a',
'tralbum_id' => $album_id 'tralbum_id' => $album_id
); ];
$tralbums[] = $this->apiGet('mobile/22/tralbum_details', $query_data); $tralbums[] = $this->apiGet('mobile/22/tralbum_details', $query_data);
break; break;
} }
foreach ($tralbums as $tralbum_data) { foreach ($tralbums as $tralbum_data) {
if ($tralbum_data->type === 'a' && $this->getInput('type') === 'tracks') { if ($tralbum_data->type === 'a' && $this->getInput('type') === 'tracks') {
foreach ($tralbum_data->tracks as $track) { foreach ($tralbum_data->tracks as $track) {
$query_data = array( $query_data = [
'band_id' => $band_id, 'band_id' => $band_id,
'tralbum_type' => 't', 'tralbum_type' => 't',
'tralbum_id' => $track->track_id 'tralbum_id' => $track->track_id
); ];
$track_data = $this->apiGet('mobile/22/tralbum_details', $query_data); $track_data = $this->apiGet('mobile/22/tralbum_details', $query_data);
$this->items[] = $this->buildTralbumItem($track_data); $this->items[] = $this->buildTralbumItem($track_data);
} }
} else { } else {
$this->items[] = $this->buildTralbumItem($tralbum_data); $this->items[] = $this->buildTralbumItem($tralbum_data);
} }
} }
break; break;
} }
} }
private function buildTralbumItem($tralbum_data){ private function buildTralbumItem($tralbum_data)
$band_data = $tralbum_data->band; {
$band_data = $tralbum_data->band;
// Format title like: ARTIST - ALBUM/TRACK (OPTIONAL RELEASER) // Format title like: ARTIST - ALBUM/TRACK (OPTIONAL RELEASER)
// Format artist/author like: ARTIST (OPTIONAL RELEASER) // Format artist/author like: ARTIST (OPTIONAL RELEASER)
// //
// If the album/track is released under a label/a band other than the artist // If the album/track is released under a label/a band other than the artist
// themselves, append that releaser name to the title and artist/author. // themselves, append that releaser name to the title and artist/author.
// //
// This sadly doesn't always work right for individual tracks as the artist // This sadly doesn't always work right for individual tracks as the artist
// of the track is always set to the releaser. // of the track is always set to the releaser.
$artist = $tralbum_data->tralbum_artist; $artist = $tralbum_data->tralbum_artist;
$full_title = $artist . ' - ' . $tralbum_data->title; $full_title = $artist . ' - ' . $tralbum_data->title;
$full_artist = $artist; $full_artist = $artist;
if (isset($tralbum_data->label)) { if (isset($tralbum_data->label)) {
$full_title .= ' (' . $tralbum_data->label . ')'; $full_title .= ' (' . $tralbum_data->label . ')';
$full_artist .= ' (' . $tralbum_data->label . ')'; $full_artist .= ' (' . $tralbum_data->label . ')';
} elseif ($band_data->name !== $artist) { } elseif ($band_data->name !== $artist) {
$full_title .= ' (' . $band_data->name . ')'; $full_title .= ' (' . $band_data->name . ')';
$full_artist .= ' (' . $band_data->name . ')'; $full_artist .= ' (' . $band_data->name . ')';
} }
$small_img = $this->getImageUrl($tralbum_data->art_id, self::IMGSIZE_300PX); $small_img = $this->getImageUrl($tralbum_data->art_id, self::IMGSIZE_300PX);
$img = $this->getImageUrl($tralbum_data->art_id, self::IMGSIZE_700PX); $img = $this->getImageUrl($tralbum_data->art_id, self::IMGSIZE_700PX);
$item = array( $item = [
'uri' => $tralbum_data->bandcamp_url, 'uri' => $tralbum_data->bandcamp_url,
'author' => $full_artist, 'author' => $full_artist,
'title' => $full_title, 'title' => $full_title,
'enclosures' => array($img), 'enclosures' => [$img],
'timestamp' => $tralbum_data->release_date 'timestamp' => $tralbum_data->release_date
); ];
$item['categories'] = array(); $item['categories'] = [];
foreach ($tralbum_data->tags as $tag) { foreach ($tralbum_data->tags as $tag) {
$item['categories'][] = $tag->norm_name; $item['categories'][] = $tag->norm_name;
} }
// Give articles a unique UID depending on its track list // Give articles a unique UID depending on its track list
// Releases should then show up as new articles when tracks are added // Releases should then show up as new articles when tracks are added
if ($this->getInput('type') === 'changes') { if ($this->getInput('type') === 'changes') {
$item['uid'] = "bandcamp/$band_data->band_id/$tralbum_data->id/"; $item['uid'] = "bandcamp/$band_data->band_id/$tralbum_data->id/";
foreach ($tralbum_data->tracks as $track) { foreach ($tralbum_data->tracks as $track) {
$item['uid'] .= $track->track_id; $item['uid'] .= $track->track_id;
} }
} }
$item['content'] = "<img src='$small_img' /><br/>$full_title<br/>"; $item['content'] = "<img src='$small_img' /><br/>$full_title<br/>";
if ($tralbum_data->type === 'a') { if ($tralbum_data->type === 'a') {
$item['content'] .= '<ol>'; $item['content'] .= '<ol>';
foreach ($tralbum_data->tracks as $track) { foreach ($tralbum_data->tracks as $track) {
$item['content'] .= "<li>$track->title</li>"; $item['content'] .= "<li>$track->title</li>";
} }
$item['content'] .= '</ol>'; $item['content'] .= '</ol>';
} }
if (!empty($tralbum_data->about)) { if (!empty($tralbum_data->about)) {
$item['content'] .= '<p>' $item['content'] .= '<p>'
. nl2br($tralbum_data->about) . nl2br($tralbum_data->about)
. '</p>'; . '</p>';
} }
return $item; return $item;
} }
private function buildRequestJson(){ private function buildRequestJson()
$requestJson = array( {
'tag' => $this->getInput('tag'), $requestJson = [
'page' => 1, 'tag' => $this->getInput('tag'),
'sort' => 'date' 'page' => 1,
); 'sort' => 'date'
return json_encode($requestJson); ];
} return json_encode($requestJson);
}
private function getImageUrl($id, $size){ private function getImageUrl($id, $size)
return self::IMGURI . 'img/a' . $id . '_' . $size . '.jpg'; {
} return self::IMGURI . 'img/a' . $id . '_' . $size . '.jpg';
}
private function apiGet($endpoint, $query_data) { private function apiGet($endpoint, $query_data)
$url = self::URI . 'api/' . $endpoint . '?' . http_build_query($query_data); {
$data = json_decode(getContents($url)); $url = self::URI . 'api/' . $endpoint . '?' . http_build_query($query_data);
return $data; $data = json_decode(getContents($url));
} return $data;
}
public function getURI(){ public function getURI()
switch($this->queriedContext) { {
case 'By tag': switch ($this->queriedContext) {
if(!is_null($this->getInput('tag'))) { case 'By tag':
return self::URI if (!is_null($this->getInput('tag'))) {
. 'tag/' return self::URI
. urlencode($this->getInput('tag')) . 'tag/'
. '?sort_field=date'; . urlencode($this->getInput('tag'))
} . '?sort_field=date';
break; }
case 'By label': break;
if(!is_null($this->getInput('label'))) { case 'By label':
return 'https://' if (!is_null($this->getInput('label'))) {
. $this->getInput('label') return 'https://'
. '.bandcamp.com/music'; . $this->getInput('label')
} . '.bandcamp.com/music';
break; }
case 'By band': break;
if(!is_null($this->getInput('band'))) { case 'By band':
return 'https://' if (!is_null($this->getInput('band'))) {
. $this->getInput('band') return 'https://'
. '.bandcamp.com/music'; . $this->getInput('band')
} . '.bandcamp.com/music';
break; }
case 'By album': break;
if(!is_null($this->getInput('band')) && !is_null($this->getInput('album'))) { case 'By album':
return 'https://' if (!is_null($this->getInput('band')) && !is_null($this->getInput('album'))) {
. $this->getInput('band') return 'https://'
. '.bandcamp.com/album/' . $this->getInput('band')
. $this->getInput('album'); . '.bandcamp.com/album/'
} . $this->getInput('album');
break; }
} break;
}
return parent::getURI(); return parent::getURI();
} }
public function getName(){ public function getName()
switch($this->queriedContext) { {
case 'By tag': switch ($this->queriedContext) {
if(!is_null($this->getInput('tag'))) { case 'By tag':
return $this->getInput('tag') . ' - Bandcamp Tag'; if (!is_null($this->getInput('tag'))) {
} return $this->getInput('tag') . ' - Bandcamp Tag';
break; }
case 'By band': break;
if(isset($this->feedName)) { case 'By band':
return $this->feedName . ' - Bandcamp Band'; if (isset($this->feedName)) {
} elseif(!is_null($this->getInput('band'))) { return $this->feedName . ' - Bandcamp Band';
return $this->getInput('band') . ' - Bandcamp Band'; } elseif (!is_null($this->getInput('band'))) {
} return $this->getInput('band') . ' - Bandcamp Band';
break; }
case 'By label': break;
if(isset($this->feedName)) { case 'By label':
return $this->feedName . ' - Bandcamp Label'; if (isset($this->feedName)) {
} elseif(!is_null($this->getInput('label'))) { return $this->feedName . ' - Bandcamp Label';
return $this->getInput('label') . ' - Bandcamp Label'; } elseif (!is_null($this->getInput('label'))) {
} return $this->getInput('label') . ' - Bandcamp Label';
break; }
case 'By album': break;
if(isset($this->feedName)) { case 'By album':
return $this->feedName . ' - Bandcamp Album'; if (isset($this->feedName)) {
} elseif(!is_null($this->getInput('album'))) { return $this->feedName . ' - Bandcamp Album';
return $this->getInput('album') . ' - Bandcamp Album'; } elseif (!is_null($this->getInput('album'))) {
} return $this->getInput('album') . ' - Bandcamp Album';
break; }
} break;
}
return parent::getName(); return parent::getName();
} }
public function detectParameters($url) { public function detectParameters($url)
$params = array(); {
$params = [];
// By tag // By tag
$regex = '/^(https?:\/\/)?bandcamp\.com\/tag\/([^\/.&?\n]+)/'; $regex = '/^(https?:\/\/)?bandcamp\.com\/tag\/([^\/.&?\n]+)/';
if(preg_match($regex, $url, $matches) > 0) { if (preg_match($regex, $url, $matches) > 0) {
$params['tag'] = urldecode($matches[2]); $params['tag'] = urldecode($matches[2]);
return $params; return $params;
} }
// By band // By band
$regex = '/^(https?:\/\/)?([^\/.&?\n]+?)\.bandcamp\.com/'; $regex = '/^(https?:\/\/)?([^\/.&?\n]+?)\.bandcamp\.com/';
if(preg_match($regex, $url, $matches) > 0) { if (preg_match($regex, $url, $matches) > 0) {
$params['band'] = urldecode($matches[2]); $params['band'] = urldecode($matches[2]);
return $params; return $params;
} }
// By album // By album
$regex = '/^(https?:\/\/)?([^\/.&?\n]+?)\.bandcamp\.com\/album\/([^\/.&?\n]+)/'; $regex = '/^(https?:\/\/)?([^\/.&?\n]+?)\.bandcamp\.com\/album\/([^\/.&?\n]+)/';
if(preg_match($regex, $url, $matches) > 0) { if (preg_match($regex, $url, $matches) > 0) {
$params['band'] = urldecode($matches[2]); $params['band'] = urldecode($matches[2]);
$params['album'] = urldecode($matches[3]); $params['album'] = urldecode($matches[3]);
return $params; return $params;
} }
return null; return null;
} }
} }

View file

@ -1,159 +1,164 @@
<?php <?php
class BandcampDailyBridge extends BridgeAbstract {
const NAME = 'Bandcamp Daily Bridge';
const URI = 'https://daily.bandcamp.com';
const DESCRIPTION = 'Returns newest articles';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(
'Latest articles' => array(),
'Best of' => array(
'best-content' => array(
'name' => 'content',
'type' => 'list',
'values' => array(
'Best Ambient' => 'best-ambient',
'Best Beat Tapes' => 'best-beat-tapes',
'Best Dance 12\'s' => 'best-dance-12s',
'Best Contemporary Classical' => 'best-contemporary-classical',
'Best Electronic' => 'best-electronic',
'Best Experimental' => 'best-experimental',
'Best Hip-Hop' => 'best-hip-hop',
'Best Jazz' => 'best-jazz',
'Best Metal' => 'best-metal',
'Best Punk' => 'best-punk',
'Best Reissues' => 'best-reissues',
'Best Soul' => 'best-soul',
),
'defaultValue' => 'best-ambient',
),
),
'Genres' => array(
'genres-content' => array(
'name' => 'content',
'type' => 'list',
'values' => array(
'Acoustic' => 'genres/acoustic',
'Alternative' => 'genres/alternative',
'Ambient' => 'genres/ambient',
'Blues' => 'genres/blues',
'Classical' => 'genres/classical',
'Comedy' => 'genres/comedy',
'Country' => 'genres/country',
'Devotional' => 'genres/devotional',
'Electronic' => 'genres/electronic',
'Experimental' => 'genres/experimental',
'Folk' => 'genres/folk',
'Funk' => 'genres/funk',
'Hip-Hop/Rap' => 'genres/hip-hop-rap',
'Jazz' => 'genres/jazz',
'Kids' => 'genres/kids',
'Latin' => 'genres/latin',
'Metal' => 'genres/metal',
'Pop' => 'genres/pop',
'Punk' => 'genres/punk',
'R&B/Soul' => 'genres/r-b-soul',
'Reggae' => 'genres/reggae',
'Rock' => 'genres/rock',
'Soundtrack' => 'genres/soundtrack',
'Spoken Word' => 'genres/spoken-word',
'World' => 'genres/world',
),
'defaultValue' => 'genres/acoustic',
),
),
'Franchises' => array(
'franchises-content' => array(
'name' => 'content',
'type' => 'list',
'values' => array(
'Lists' => 'lists',
'Features' => 'features',
'Album of the Day' => 'album-of-the-day',
'Acid Test' => 'acid-test',
'Bandcamp Navigator' => 'bandcamp-navigator',
'Big Ups' => 'big-ups',
'Certified' => 'certified',
'Gallery' => 'gallery',
'Hidden Gems' => 'hidden-gems',
'High Scores' => 'high-scores',
'Label Profile' => 'label-profile',
'Lifetime Achievement' => 'lifetime-achievement',
'Scene Report' => 'scene-report',
'Seven Essential Releases' => 'seven-essential-releases',
'The Merch Table' => 'the-merch-table',
),
'defaultValue' => 'lists',
),
)
);
const CACHE_TIMEOUT = 3600; // 1 hour class BandcampDailyBridge extends BridgeAbstract
{
const NAME = 'Bandcamp Daily Bridge';
const URI = 'https://daily.bandcamp.com';
const DESCRIPTION = 'Returns newest articles';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [
'Latest articles' => [],
'Best of' => [
'best-content' => [
'name' => 'content',
'type' => 'list',
'values' => [
'Best Ambient' => 'best-ambient',
'Best Beat Tapes' => 'best-beat-tapes',
'Best Dance 12\'s' => 'best-dance-12s',
'Best Contemporary Classical' => 'best-contemporary-classical',
'Best Electronic' => 'best-electronic',
'Best Experimental' => 'best-experimental',
'Best Hip-Hop' => 'best-hip-hop',
'Best Jazz' => 'best-jazz',
'Best Metal' => 'best-metal',
'Best Punk' => 'best-punk',
'Best Reissues' => 'best-reissues',
'Best Soul' => 'best-soul',
],
'defaultValue' => 'best-ambient',
],
],
'Genres' => [
'genres-content' => [
'name' => 'content',
'type' => 'list',
'values' => [
'Acoustic' => 'genres/acoustic',
'Alternative' => 'genres/alternative',
'Ambient' => 'genres/ambient',
'Blues' => 'genres/blues',
'Classical' => 'genres/classical',
'Comedy' => 'genres/comedy',
'Country' => 'genres/country',
'Devotional' => 'genres/devotional',
'Electronic' => 'genres/electronic',
'Experimental' => 'genres/experimental',
'Folk' => 'genres/folk',
'Funk' => 'genres/funk',
'Hip-Hop/Rap' => 'genres/hip-hop-rap',
'Jazz' => 'genres/jazz',
'Kids' => 'genres/kids',
'Latin' => 'genres/latin',
'Metal' => 'genres/metal',
'Pop' => 'genres/pop',
'Punk' => 'genres/punk',
'R&B/Soul' => 'genres/r-b-soul',
'Reggae' => 'genres/reggae',
'Rock' => 'genres/rock',
'Soundtrack' => 'genres/soundtrack',
'Spoken Word' => 'genres/spoken-word',
'World' => 'genres/world',
],
'defaultValue' => 'genres/acoustic',
],
],
'Franchises' => [
'franchises-content' => [
'name' => 'content',
'type' => 'list',
'values' => [
'Lists' => 'lists',
'Features' => 'features',
'Album of the Day' => 'album-of-the-day',
'Acid Test' => 'acid-test',
'Bandcamp Navigator' => 'bandcamp-navigator',
'Big Ups' => 'big-ups',
'Certified' => 'certified',
'Gallery' => 'gallery',
'Hidden Gems' => 'hidden-gems',
'High Scores' => 'high-scores',
'Label Profile' => 'label-profile',
'Lifetime Achievement' => 'lifetime-achievement',
'Scene Report' => 'scene-report',
'Seven Essential Releases' => 'seven-essential-releases',
'The Merch Table' => 'the-merch-table',
],
'defaultValue' => 'lists',
],
]
];
public function collectData() { const CACHE_TIMEOUT = 3600; // 1 hour
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());
$html = defaultLinkTo($html, self::URI); public function collectData()
{
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());
$articles = $html->find('articles-list', 0); $html = defaultLinkTo($html, self::URI);
foreach($articles->find('div.list-article') as $index => $article) { $articles = $html->find('articles-list', 0);
$item = array();
$articlePath = $article->find('a.title', 0)->href; foreach ($articles->find('div.list-article') as $index => $article) {
$item = [];
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, 3600) $articlePath = $article->find('a.title', 0)->href;
or returnServerError('Could not request: ' . $articlePath);
$item['uri'] = $articlePath; $articlePageHtml = getSimpleHTMLDOMCached($articlePath, 3600)
$item['title'] = $articlePageHtml->find('article-title', 0)->innertext; or returnServerError('Could not request: ' . $articlePath);
$item['author'] = $articlePageHtml->find('article-credits > a', 0)->innertext;
$item['content'] = html_entity_decode($articlePageHtml->find('meta[name="description"]', 0)->content, ENT_QUOTES);
$item['timestamp'] = $articlePageHtml->find('meta[property="article:published_time"]', 0)->content;
$item['categories'][] = $articlePageHtml->find('meta[property="article:section"]', 0)->content;
if ($articlePageHtml->find('meta[property="article:tag"]', 0)) { $item['uri'] = $articlePath;
$item['categories'][] = $articlePageHtml->find('meta[property="article:tag"]', 0)->content; $item['title'] = $articlePageHtml->find('article-title', 0)->innertext;
} $item['author'] = $articlePageHtml->find('article-credits > a', 0)->innertext;
$item['content'] = html_entity_decode($articlePageHtml->find('meta[name="description"]', 0)->content, ENT_QUOTES);
$item['timestamp'] = $articlePageHtml->find('meta[property="article:published_time"]', 0)->content;
$item['categories'][] = $articlePageHtml->find('meta[property="article:section"]', 0)->content;
$item['enclosures'][] = $articlePageHtml->find('meta[name="twitter:image"]', 0)->content; if ($articlePageHtml->find('meta[property="article:tag"]', 0)) {
$item['categories'][] = $articlePageHtml->find('meta[property="article:tag"]', 0)->content;
}
$this->items[] = $item; $item['enclosures'][] = $articlePageHtml->find('meta[name="twitter:image"]', 0)->content;
if (count($this->items) >= 10) { $this->items[] = $item;
break;
}
}
}
public function getURI() { if (count($this->items) >= 10) {
switch($this->queriedContext) { break;
case 'Latest articles': }
return self::URI . '/latest'; }
case 'Best of': }
case 'Genres':
case 'Franchises':
// TODO Switch to array_key_first once php >= 7.3
$contentKey = key(self::PARAMETERS[$this->queriedContext]);
return self::URI . '/' . $this->getInput($contentKey);
default:
return parent::getURI();
}
}
public function getName() { public function getURI()
switch($this->queriedContext) { {
case 'Latest articles': switch ($this->queriedContext) {
return $this->queriedContext . ' - Bandcamp Daily'; case 'Latest articles':
case 'Best of': return self::URI . '/latest';
case 'Genres': case 'Best of':
case 'Franchises': case 'Genres':
$contentKey = array_key_first(self::PARAMETERS[$this->queriedContext]); case 'Franchises':
$contentValues = array_flip(self::PARAMETERS[$this->queriedContext][$contentKey]['values']); // TODO Switch to array_key_first once php >= 7.3
$contentKey = key(self::PARAMETERS[$this->queriedContext]);
return self::URI . '/' . $this->getInput($contentKey);
default:
return parent::getURI();
}
}
return $contentValues[$this->getInput($contentKey)] . ' - Bandcamp Daily'; public function getName()
default: {
return parent::getName(); switch ($this->queriedContext) {
} case 'Latest articles':
} return $this->queriedContext . ' - Bandcamp Daily';
case 'Best of':
case 'Genres':
case 'Franchises':
$contentKey = array_key_first(self::PARAMETERS[$this->queriedContext]);
$contentValues = array_flip(self::PARAMETERS[$this->queriedContext][$contentKey]['values']);
return $contentValues[$this->getInput($contentKey)] . ' - Bandcamp Daily';
default:
return parent::getName();
}
}
} }

View file

@ -1,31 +1,33 @@
<?php <?php
class BastaBridge extends BridgeAbstract {
const MAINTAINER = 'qwertygc'; class BastaBridge extends BridgeAbstract
const NAME = 'Bastamag Bridge'; {
const URI = 'https://www.bastamag.net/'; const MAINTAINER = 'qwertygc';
const CACHE_TIMEOUT = 7200; // 2h const NAME = 'Bastamag Bridge';
const DESCRIPTION = 'Returns the newest articles.'; const URI = 'https://www.bastamag.net/';
const CACHE_TIMEOUT = 7200; // 2h
const DESCRIPTION = 'Returns the newest articles.';
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend'); {
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend');
$limit = 0; $limit = 0;
foreach($html->find('item') as $element) { foreach ($html->find('item') as $element) {
if($limit < 10) { if ($limit < 10) {
$item = array(); $item = [];
$item['title'] = $element->find('title', 0)->innertext; $item['title'] = $element->find('title', 0)->innertext;
$item['uri'] = $element->find('guid', 0)->plaintext; $item['uri'] = $element->find('guid', 0)->plaintext;
$item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext); $item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext);
$html = getSimpleHTMLDOM($item['uri']); $html = getSimpleHTMLDOM($item['uri']);
$html = defaultLinkTo($html, self::URI); $html = defaultLinkTo($html, self::URI);
$item['content'] = $html->find('div.texte', 0)->innertext; $item['content'] = $html->find('div.texte', 0)->innertext;
$this->items[] = $item; $this->items[] = $item;
$limit++; $limit++;
} }
} }
} }
} }

View file

@ -1,41 +1,45 @@
<?php <?php
class BinanceBridge extends BridgeAbstract {
const NAME = 'Binance Blog';
const URI = 'https://www.binance.com/en/blog';
const DESCRIPTION = 'Subscribe to the Binance blog.';
const MAINTAINER = 'thefranke';
const CACHE_TIMEOUT = 3600; // 1h
public function getIcon() { class BinanceBridge extends BridgeAbstract
return 'https://bin.bnbstatic.com/static/images/common/favicon.ico'; {
} const NAME = 'Binance Blog';
const URI = 'https://www.binance.com/en/blog';
const DESCRIPTION = 'Subscribe to the Binance blog.';
const MAINTAINER = 'thefranke';
const CACHE_TIMEOUT = 3600; // 1h
public function collectData() { public function getIcon()
$html = getSimpleHTMLDOM(self::URI) {
or returnServerError('Could not fetch Binance blog data.'); return 'https://bin.bnbstatic.com/static/images/common/favicon.ico';
}
$appData = $html->find('script[id="__APP_DATA"]'); public function collectData()
$appDataJson = json_decode($appData[0]->innertext); {
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not fetch Binance blog data.');
foreach($appDataJson->pageData->redux->blogList->blogList as $element) { $appData = $html->find('script[id="__APP_DATA"]');
$appDataJson = json_decode($appData[0]->innertext);
$date = $element->postTime; foreach ($appDataJson->pageData->redux->blogList->blogList as $element) {
$abstract = $element->brief; $date = $element->postTime;
$uri = self::URI . '/' . $element->lang . '/blog/' . $element->idStr; $abstract = $element->brief;
$title = $element->title; $uri = self::URI . '/' . $element->lang . '/blog/' . $element->idStr;
$content = $element->content; $title = $element->title;
$content = $element->content;
$item = array(); $item = [];
$item['title'] = $title; $item['title'] = $title;
$item['uri'] = $uri; $item['uri'] = $uri;
$item['timestamp'] = substr($date, 0, -3); $item['timestamp'] = substr($date, 0, -3);
$item['author'] = 'Binance'; $item['author'] = 'Binance';
$item['content'] = $content; $item['content'] = $content;
$this->items[] = $item; $this->items[] = $item;
if (count($this->items) >= 10) if (count($this->items) >= 10) {
break; break;
} }
} }
}
} }

View file

@ -1,45 +1,44 @@
<?php <?php
class BlaguesDeMerdeBridge extends BridgeAbstract {
const MAINTAINER = 'superbaillot.net, logmanoriginal'; class BlaguesDeMerdeBridge extends BridgeAbstract
const NAME = 'Blagues De Merde'; {
const URI = 'http://www.blaguesdemerde.fr/'; const MAINTAINER = 'superbaillot.net, logmanoriginal';
const CACHE_TIMEOUT = 7200; // 2h const NAME = 'Blagues De Merde';
const DESCRIPTION = 'Blagues De Merde'; const URI = 'http://www.blaguesdemerde.fr/';
const CACHE_TIMEOUT = 7200; // 2h
const DESCRIPTION = 'Blagues De Merde';
public function getIcon() { public function getIcon()
return self::URI . 'assets/img/favicon.ico'; {
} return self::URI . 'assets/img/favicon.ico';
}
public function collectData(){ public function collectData()
{
$html = getSimpleHTMLDOM(self::URI);
$html = getSimpleHTMLDOM(self::URI); foreach ($html->find('div.blague') as $element) {
$item = [];
foreach($html->find('div.blague') as $element) { $item['uri'] = static::URI . '#' . $element->id;
$item['author'] = $element->find('div[class="blague-footer"] p strong', 0)->plaintext;
$item = array(); // Let the title be everything up to the first <br>
$item['title'] = trim(explode("\n", $element->find('div.text', 0)->plaintext)[0]);
$item['uri'] = static::URI . '#' . $element->id; $item['content'] = strip_tags($element->find('div.text', 0));
$item['author'] = $element->find('div[class="blague-footer"] p strong', 0)->plaintext;
// Let the title be everything up to the first <br> // timestamp is part of:
$item['title'] = trim(explode("\n", $element->find('div.text', 0)->plaintext)[0]); // <p>Par <strong>{author}</strong> le {date} dans <strong>{category}</strong></p>
preg_match(
'/.+le(.+)dans.*/',
$element->find('div[class="blague-footer"]', 0)->plaintext,
$matches
);
$item['content'] = strip_tags($element->find('div.text', 0)); $item['timestamp'] = strtotime($matches[1]);
// timestamp is part of: $this->items[] = $item;
// <p>Par <strong>{author}</strong> le {date} dans <strong>{category}</strong></p> }
preg_match( }
'/.+le(.+)dans.*/',
$element->find('div[class="blague-footer"]', 0)->plaintext,
$matches
);
$item['timestamp'] = strtotime($matches[1]);
$this->items[] = $item;
}
}
} }

View file

@ -1,29 +1,32 @@
<?php <?php
class BleepingComputerBridge extends FeedExpander {
const MAINTAINER = 'csisoap'; class BleepingComputerBridge extends FeedExpander
const NAME = 'Bleeping Computer'; {
const URI = 'https://www.bleepingcomputer.com/'; const MAINTAINER = 'csisoap';
const DESCRIPTION = 'Returns the newest articles.'; const NAME = 'Bleeping Computer';
const URI = 'https://www.bleepingcomputer.com/';
const DESCRIPTION = 'Returns the newest articles.';
protected function parseItem($item){ protected function parseItem($item)
$item = parent::parseItem($item); {
$item = parent::parseItem($item);
$article_html = getSimpleHTMLDOMCached($item['uri']); $article_html = getSimpleHTMLDOMCached($item['uri']);
if(!$article_html) { if (!$article_html) {
$item['content'] .= '<p><em>Could not request ' . $this->getName() . ': ' . $item['uri'] . '</em></p>'; $item['content'] .= '<p><em>Could not request ' . $this->getName() . ': ' . $item['uri'] . '</em></p>';
return $item; return $item;
} }
$article_content = $article_html->find('div.articleBody', 0)->innertext; $article_content = $article_html->find('div.articleBody', 0)->innertext;
$article_content = stripRecursiveHTMLSection($article_content, 'div', '<div class="cz-related-article-wrapp'); $article_content = stripRecursiveHTMLSection($article_content, 'div', '<div class="cz-related-article-wrapp');
$item['content'] = trim($article_content); $item['content'] = trim($article_content);
return $item; return $item;
} }
public function collectData(){ public function collectData()
$feed = static::URI . 'feed/'; {
$this->collectExpandableDatas($feed); $feed = static::URI . 'feed/';
} $this->collectExpandableDatas($feed);
}
} }

View file

@ -1,60 +1,60 @@
<?php <?php
class BlizzardNewsBridge extends XPathAbstract { class BlizzardNewsBridge extends XPathAbstract
{
const NAME = 'Blizzard News';
const URI = 'https://news.blizzard.com';
const DESCRIPTION = 'Blizzard (game company) newsfeed';
const MAINTAINER = 'Niehztog';
const PARAMETERS = [
'' => [
'locale' => [
'name' => 'Language',
'type' => 'list',
'values' => [
'Deutsch' => 'de-de',
'English (EU)' => 'en-gb',
'English (US)' => 'en-us',
'Español (EU)' => 'es-es',
'Español (AL)' => 'es-mx',
'Français' => 'fr-fr',
'Italiano' => 'it-it',
'日本語' => 'ja-jp',
'한국어' => 'ko-kr',
'Polski' => 'pl-pl',
'Português (AL)' => 'pt-br',
'Русский' => 'ru-ru',
'ภาษาไทย' => 'th-th',
'简体中文' => 'zh-cn',
'繁體中文' => 'zh-tw'
],
'defaultValue' => 'en-us',
'title' => 'Select your language'
]
]
];
const CACHE_TIMEOUT = 3600;
const NAME = 'Blizzard News'; const XPATH_EXPRESSION_ITEM = '/html/body/div/div[4]/div[2]/div[2]/div/div/section/ol/li/article';
const URI = 'https://news.blizzard.com'; const XPATH_EXPRESSION_ITEM_TITLE = './/div/div[2]/h2';
const DESCRIPTION = 'Blizzard (game company) newsfeed'; const XPATH_EXPRESSION_ITEM_CONTENT = './/div[@class="ArticleListItem-description"]/div[@class="h6"]';
const MAINTAINER = 'Niehztog'; const XPATH_EXPRESSION_ITEM_URI = './/a[@class="ArticleLink ArticleLink"]/@href';
const PARAMETERS = array( const XPATH_EXPRESSION_ITEM_AUTHOR = '';
'' => array( const XPATH_EXPRESSION_ITEM_TIMESTAMP = './/time[@class="ArticleListItem-footerTimestamp"]/@timestamp';
'locale' => array( const XPATH_EXPRESSION_ITEM_ENCLOSURES = './/div[@class="ArticleListItem-image"]/@style';
'name' => 'Language', const XPATH_EXPRESSION_ITEM_CATEGORIES = './/div[@class="ArticleListItem-label"]';
'type' => 'list', const SETTING_FIX_ENCODING = true;
'values' => array(
'Deutsch' => 'de-de',
'English (EU)' => 'en-gb',
'English (US)' => 'en-us',
'Español (EU)' => 'es-es',
'Español (AL)' => 'es-mx',
'Français' => 'fr-fr',
'Italiano' => 'it-it',
'日本語' => 'ja-jp',
'한국어' => 'ko-kr',
'Polski' => 'pl-pl',
'Português (AL)' => 'pt-br',
'Русский' => 'ru-ru',
'ภาษาไทย' => 'th-th',
'简体中文' => 'zh-cn',
'繁體中文' => 'zh-tw'
),
'defaultValue' => 'en-us',
'title' => 'Select your language'
)
)
);
const CACHE_TIMEOUT = 3600;
const XPATH_EXPRESSION_ITEM = '/html/body/div/div[4]/div[2]/div[2]/div/div/section/ol/li/article'; /**
const XPATH_EXPRESSION_ITEM_TITLE = './/div/div[2]/h2'; * Source Web page URL (should provide either HTML or XML content)
const XPATH_EXPRESSION_ITEM_CONTENT = './/div[@class="ArticleListItem-description"]/div[@class="h6"]'; * @return string
const XPATH_EXPRESSION_ITEM_URI = './/a[@class="ArticleLink ArticleLink"]/@href'; */
const XPATH_EXPRESSION_ITEM_AUTHOR = ''; protected function getSourceUrl()
const XPATH_EXPRESSION_ITEM_TIMESTAMP = './/time[@class="ArticleListItem-footerTimestamp"]/@timestamp'; {
const XPATH_EXPRESSION_ITEM_ENCLOSURES = './/div[@class="ArticleListItem-image"]/@style'; $locale = $this->getInput('locale');
const XPATH_EXPRESSION_ITEM_CATEGORIES = './/div[@class="ArticleListItem-label"]'; if ('zh-cn' === $locale) {
const SETTING_FIX_ENCODING = true; return 'https://cn.news.blizzard.com';
}
/** return 'https://news.blizzard.com/' . $locale;
* Source Web page URL (should provide either HTML or XML content) }
* @return string
*/
protected function getSourceUrl(){
$locale = $this->getInput('locale');
if('zh-cn' === $locale) {
return 'https://cn.news.blizzard.com';
}
return 'https://news.blizzard.com/' . $locale;
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,71 +1,77 @@
<?php <?php
class BooruprojectBridge extends DanbooruBridge { class BooruprojectBridge extends DanbooruBridge
{
const MAINTAINER = 'mitsukarenai';
const NAME = 'Booruproject';
const URI = 'https://booru.org/';
const DESCRIPTION = 'Returns images from given page of booruproject';
const PARAMETERS = [
'global' => [
'p' => [
'name' => 'page',
'defaultValue' => 0,
'type' => 'number'
],
't' => [
'name' => 'tags',
'required' => true,
'exampleValue' => 'tagme',
'title' => 'Use "all" to get all posts'
]
],
'Booru subdomain (subdomain.booru.org)' => [
'i' => [
'name' => 'Subdomain',
'required' => true,
'exampleValue' => 'rm'
]
]
];
const MAINTAINER = 'mitsukarenai'; const PATHTODATA = '.thumb';
const NAME = 'Booruproject'; const IDATTRIBUTE = 'id';
const URI = 'https://booru.org/'; const TAGATTRIBUTE = 'title';
const DESCRIPTION = 'Returns images from given page of booruproject'; const PIDBYPAGE = 20;
const PARAMETERS = array(
'global' => array(
'p' => array(
'name' => 'page',
'defaultValue' => 0,
'type' => 'number'
),
't' => array(
'name' => 'tags',
'required' => true,
'exampleValue' => 'tagme',
'title' => 'Use "all" to get all posts'
)
),
'Booru subdomain (subdomain.booru.org)' => array(
'i' => array(
'name' => 'Subdomain',
'required' => true,
'exampleValue' => 'rm'
)
)
);
const PATHTODATA = '.thumb'; protected function getFullURI()
const IDATTRIBUTE = 'id'; {
const TAGATTRIBUTE = 'title'; return $this->getURI()
const PIDBYPAGE = 20; . 'index.php?page=post&s=list&pid='
. ($this->getInput('p') ? ($this->getInput('p') - 1) * static::PIDBYPAGE : '')
. '&tags=' . urlencode($this->getInput('t'));
}
protected function getFullURI(){ protected function getTags($element)
return $this->getURI() {
. 'index.php?page=post&s=list&pid=' $tags = parent::getTags($element);
. ($this->getInput('p') ? ($this->getInput('p') - 1) * static::PIDBYPAGE : '') $tags = explode(' ', $tags);
. '&tags=' . urlencode($this->getInput('t'));
}
protected function getTags($element){ // Remove statistics from the tags list (identified by colon)
$tags = parent::getTags($element); foreach ($tags as $key => $tag) {
$tags = explode(' ', $tags); if (strpos($tag, ':') !== false) {
unset($tags[$key]);
}
}
// Remove statistics from the tags list (identified by colon) return implode(' ', $tags);
foreach($tags as $key => $tag) { }
if(strpos($tag, ':') !== false) unset($tags[$key]);
}
return implode(' ', $tags); public function getURI()
} {
if (!is_null($this->getInput('i'))) {
return 'https://' . $this->getInput('i') . '.booru.org/';
}
public function getURI(){ return parent::getURI();
if(!is_null($this->getInput('i'))) { }
return 'https://' . $this->getInput('i') . '.booru.org/';
}
return parent::getURI(); public function getName()
} {
if (!is_null($this->getInput('i'))) {
return static::NAME . ' ' . $this->getInput('i');
}
public function getName(){ return parent::getName();
if(!is_null($this->getInput('i'))) { }
return static::NAME . ' ' . $this->getInput('i');
}
return parent::getName();
}
} }

View file

@ -1,73 +1,75 @@
<?php <?php
class BrutBridge extends BridgeAbstract {
const NAME = 'Brut Bridge';
const URI = 'https://www.brut.media';
const DESCRIPTION = 'Returns 10 newest videos by category and edition';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(array(
'category' => array(
'name' => 'Category',
'type' => 'list',
'values' => array(
'News' => 'news',
'International' => 'international',
'Economy' => 'economy',
'Science and Technology' => 'science-and-technology',
'Entertainment' => 'entertainment',
'Sports' => 'sport',
'Nature' => 'nature',
'Health' => 'health',
),
'defaultValue' => 'news',
),
'edition' => array(
'name' => ' Edition',
'type' => 'list',
'values' => array(
'United States' => 'us',
'United Kingdom' => 'uk',
'France' => 'fr',
'Spain' => 'es',
'India' => 'in',
'Mexico' => 'mx',
),
'defaultValue' => 'us',
)
)
);
const CACHE_TIMEOUT = 1800; // 30 mins class BrutBridge extends BridgeAbstract
{
const NAME = 'Brut Bridge';
const URI = 'https://www.brut.media';
const DESCRIPTION = 'Returns 10 newest videos by category and edition';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [[
'category' => [
'name' => 'Category',
'type' => 'list',
'values' => [
'News' => 'news',
'International' => 'international',
'Economy' => 'economy',
'Science and Technology' => 'science-and-technology',
'Entertainment' => 'entertainment',
'Sports' => 'sport',
'Nature' => 'nature',
'Health' => 'health',
],
'defaultValue' => 'news',
],
'edition' => [
'name' => ' Edition',
'type' => 'list',
'values' => [
'United States' => 'us',
'United Kingdom' => 'uk',
'France' => 'fr',
'Spain' => 'es',
'India' => 'in',
'Mexico' => 'mx',
],
'defaultValue' => 'us',
]
]
];
private $jsonRegex = '/window\.__PRELOADED_STATE__ = ((?:.*)});/'; const CACHE_TIMEOUT = 1800; // 30 mins
public function collectData() { private $jsonRegex = '/window\.__PRELOADED_STATE__ = ((?:.*)});/';
$html = getSimpleHTMLDOM($this->getURI()); public function collectData()
{
$html = getSimpleHTMLDOM($this->getURI());
$results = $html->find('div.results', 0); $results = $html->find('div.results', 0);
foreach($results->find('li.col-6.col-sm-4.col-md-3.col-lg-2.px-2.pb-4') as $li) { foreach ($results->find('li.col-6.col-sm-4.col-md-3.col-lg-2.px-2.pb-4') as $li) {
$item = array(); $item = [];
$videoPath = self::URI . $li->children(0)->href; $videoPath = self::URI . $li->children(0)->href;
$videoPageHtml = getSimpleHTMLDOMCached($videoPath, 3600); $videoPageHtml = getSimpleHTMLDOMCached($videoPath, 3600);
$json = $this->extractJson($videoPageHtml); $json = $this->extractJson($videoPageHtml);
$id = array_keys((array) $json->media->index)[0]; $id = array_keys((array) $json->media->index)[0];
$item['uri'] = $videoPath; $item['uri'] = $videoPath;
$item['title'] = $json->media->index->$id->title; $item['title'] = $json->media->index->$id->title;
$item['timestamp'] = $json->media->index->$id->published_at; $item['timestamp'] = $json->media->index->$id->published_at;
$item['enclosures'][] = $json->media->index->$id->media->thumbnail; $item['enclosures'][] = $json->media->index->$id->media->thumbnail;
$description = $json->media->index->$id->description; $description = $json->media->index->$id->description;
$article = ''; $article = '';
if (is_null($json->media->index->$id->media->seo_article) === false) { if (is_null($json->media->index->$id->media->seo_article) === false) {
$article = markdownToHtml($json->media->index->$id->media->seo_article); $article = markdownToHtml($json->media->index->$id->media->seo_article);
} }
$item['content'] = <<<EOD $item['content'] = <<<EOD
<video controls poster="{$json->media->index->$id->media->thumbnail}" preload="none"> <video controls poster="{$json->media->index->$id->media->thumbnail}" preload="none">
<source src="{$json->media->index->$id->media->mp4_url}" type="video/mp4"> <source src="{$json->media->index->$id->media->mp4_url}" type="video/mp4">
</video> </video>
@ -75,53 +77,53 @@ class BrutBridge extends BridgeAbstract {
{$article} {$article}
EOD; EOD;
$this->items[] = $item; $this->items[] = $item;
if (count($this->items) >= 10) { if (count($this->items) >= 10) {
break; break;
} }
} }
} }
public function getURI() { public function getURI()
{
if (!is_null($this->getInput('edition')) && !is_null($this->getInput('category'))) {
return self::URI . '/' . $this->getInput('edition') . '/' . $this->getInput('category');
}
if (!is_null($this->getInput('edition')) && !is_null($this->getInput('category'))) { return parent::getURI();
return self::URI . '/' . $this->getInput('edition') . '/' . $this->getInput('category'); }
}
return parent::getURI(); public function getName()
} {
if (!is_null($this->getInput('edition')) && !is_null($this->getInput('category'))) {
$parameters = $this->getParameters();
public function getName() { $editionValues = array_flip($parameters[0]['edition']['values']);
$categoryValues = array_flip($parameters[0]['category']['values']);
if (!is_null($this->getInput('edition')) && !is_null($this->getInput('category'))) { return $categoryValues[$this->getInput('category')] . ' - ' .
$parameters = $this->getParameters(); $editionValues[$this->getInput('edition')] . ' - Brut.';
}
$editionValues = array_flip($parameters[0]['edition']['values']); return parent::getName();
$categoryValues = array_flip($parameters[0]['category']['values']); }
return $categoryValues[$this->getInput('category')] . ' - ' . /**
$editionValues[$this->getInput('edition')] . ' - Brut.'; * Extract JSON from page
} */
private function extractJson($html)
{
if (!preg_match($this->jsonRegex, $html, $parts)) {
returnServerError('Failed to extract data from page');
}
return parent::getName(); $data = json_decode($parts[1]);
}
/** if ($data === false) {
* Extract JSON from page returnServerError('Failed to decode extracted data');
*/ }
private function extractJson($html) {
if (!preg_match($this->jsonRegex, $html, $parts)) { return $data;
returnServerError('Failed to extract data from page'); }
}
$data = json_decode($parts[1]);
if ($data === false) {
returnServerError('Failed to decode extracted data');
}
return $data;
}
} }

View file

@ -1,180 +1,190 @@
<?php <?php
class BugzillaBridge extends BridgeAbstract { class BugzillaBridge extends BridgeAbstract
const NAME = 'Bugzilla Bridge'; {
const URI = 'https://www.bugzilla.org/'; const NAME = 'Bugzilla Bridge';
const DESCRIPTION = 'Bridge for any Bugzilla instance'; const URI = 'https://www.bugzilla.org/';
const MAINTAINER = 'Yaman Qalieh'; const DESCRIPTION = 'Bridge for any Bugzilla instance';
const PARAMETERS = array( const MAINTAINER = 'Yaman Qalieh';
'global' => array( const PARAMETERS = [
'instance' => array( 'global' => [
'name' => 'Instance URL', 'instance' => [
'required' => true, 'name' => 'Instance URL',
'exampleValue' => 'https://bugzilla.mozilla.org' 'required' => true,
) 'exampleValue' => 'https://bugzilla.mozilla.org'
), ]
'Bug comments' => array( ],
'id' => array( 'Bug comments' => [
'name' => 'Bug tracking ID', 'id' => [
'type' => 'number', 'name' => 'Bug tracking ID',
'required' => true, 'type' => 'number',
'title' => 'Insert bug tracking ID', 'required' => true,
'exampleValue' => 121241 'title' => 'Insert bug tracking ID',
), 'exampleValue' => 121241
'limit' => array( ],
'name' => 'Number of comments to return', 'limit' => [
'type' => 'number', 'name' => 'Number of comments to return',
'required' => false, 'type' => 'number',
'title' => 'Specify number of comments to return', 'required' => false,
'defaultValue' => -1 'title' => 'Specify number of comments to return',
), 'defaultValue' => -1
'skiptags' => array( ],
'name' => 'Skip offtopic comments', 'skiptags' => [
'type' => 'checkbox', 'name' => 'Skip offtopic comments',
'title' => 'Excludes comments tagged as advocacy, metoo, or offtopic from the feed' 'type' => 'checkbox',
) 'title' => 'Excludes comments tagged as advocacy, metoo, or offtopic from the feed'
) ]
); ]
];
const SKIPPED_ACTIVITY = array( const SKIPPED_ACTIVITY = [
'cc' => true, 'cc' => true,
'comment_tag' => true 'comment_tag' => true
); ];
const SKIPPED_TAGS = array('advocacy', 'metoo', 'offtopic'); const SKIPPED_TAGS = ['advocacy', 'metoo', 'offtopic'];
private $instance; private $instance;
private $bugid; private $bugid;
private $buguri; private $buguri;
private $title; private $title;
public function getName() { public function getName()
if (!is_null($this->title)) { {
return $this->title; if (!is_null($this->title)) {
} return $this->title;
return parent::getName(); }
} return parent::getName();
}
public function getURI() { public function getURI()
return $this->buguri ?? parent::getURI(); {
} return $this->buguri ?? parent::getURI();
}
public function collectData() { public function collectData()
$this->instance = rtrim($this->getInput('instance'), '/'); {
$this->bugid = $this->getInput('id'); $this->instance = rtrim($this->getInput('instance'), '/');
$this->buguri = $this->instance . '/show_bug.cgi?id=' . $this->bugid; $this->bugid = $this->getInput('id');
$this->buguri = $this->instance . '/show_bug.cgi?id=' . $this->bugid;
$url = $this->instance . '/rest/bug/' . $this->bugid; $url = $this->instance . '/rest/bug/' . $this->bugid;
$this->getTitle($url); $this->getTitle($url);
$this->collectComments($url . '/comment'); $this->collectComments($url . '/comment');
$this->collectUpdates($url . '/history'); $this->collectUpdates($url . '/history');
usort($this->items, function($a, $b) { usort($this->items, function ($a, $b) {
return $b['timestamp'] <=> $a['timestamp']; return $b['timestamp'] <=> $a['timestamp'];
}); });
if ($this->getInput('limit') > 0) { if ($this->getInput('limit') > 0) {
$this->items = array_slice($this->items, 0, $this->getInput('limit')); $this->items = array_slice($this->items, 0, $this->getInput('limit'));
} }
} }
protected function getTitle($url) { protected function getTitle($url)
// Only request the summary for a faster request {
$json = json_decode(getContents($url . '?include_fields=summary'), true); // Only request the summary for a faster request
$this->title = 'Bug ' . $this->bugid . ' - ' . $json = json_decode(getContents($url . '?include_fields=summary'), true);
$json['bugs'][0]['summary'] . ' - ' . $this->title = 'Bug ' . $this->bugid . ' - ' .
// Remove https:// $json['bugs'][0]['summary'] . ' - ' .
substr($this->instance, 8); // Remove https://
} substr($this->instance, 8);
}
protected function collectComments($url) { protected function collectComments($url)
$json = json_decode(getContents($url), true); {
$json = json_decode(getContents($url), true);
// Array of comments is here // Array of comments is here
if (!isset($json['bugs'][$this->bugid]['comments'])) { if (!isset($json['bugs'][$this->bugid]['comments'])) {
returnClientError('Cannot find REST endpoint'); returnClientError('Cannot find REST endpoint');
} }
foreach($json['bugs'][$this->bugid]['comments'] as $comment) { foreach ($json['bugs'][$this->bugid]['comments'] as $comment) {
$item = array(); $item = [];
if ($this->getInput('skiptags') and if (
array_intersect(self::SKIPPED_TAGS, $comment['tags'])) { $this->getInput('skiptags') and
continue; array_intersect(self::SKIPPED_TAGS, $comment['tags'])
} ) {
$item['categories'] = $comment['tags']; continue;
$item['uri'] = $this->buguri . '#c' . $comment['count']; }
$item['title'] = 'Comment ' . $comment['count']; $item['categories'] = $comment['tags'];
$item['timestamp'] = $comment['creation_time']; $item['uri'] = $this->buguri . '#c' . $comment['count'];
$item['author'] = $this->getUser($comment['creator']); $item['title'] = 'Comment ' . $comment['count'];
$item['content'] = $comment['text']; $item['timestamp'] = $comment['creation_time'];
if (isset($comment['is_markdown']) and $comment['is_markdown']) { $item['author'] = $this->getUser($comment['creator']);
$item['content'] = markdownToHtml($item['content']); $item['content'] = $comment['text'];
} if (isset($comment['is_markdown']) and $comment['is_markdown']) {
if (!is_null($comment['attachment_id'])) { $item['content'] = markdownToHtml($item['content']);
$item['enclosures'] = array($this->instance . '/attachment.cgi?id=' . $comment['attachment_id']); }
} if (!is_null($comment['attachment_id'])) {
$this->items[] = $item; $item['enclosures'] = [$this->instance . '/attachment.cgi?id=' . $comment['attachment_id']];
} }
} $this->items[] = $item;
}
}
protected function collectUpdates($url) { protected function collectUpdates($url)
$json = json_decode(getContents($url), true); {
$json = json_decode(getContents($url), true);
// Array of changesets which contain an array of changes // Array of changesets which contain an array of changes
if (!isset($json['bugs']['0']['history'])) { if (!isset($json['bugs']['0']['history'])) {
returnClientError('Cannot find REST endpoint'); returnClientError('Cannot find REST endpoint');
} }
foreach($json['bugs']['0']['history'] as $changeset) { foreach ($json['bugs']['0']['history'] as $changeset) {
$author = $this->getUser($changeset['who']); $author = $this->getUser($changeset['who']);
$timestamp = $changeset['when']; $timestamp = $changeset['when'];
foreach($changeset['changes'] as $change) { foreach ($changeset['changes'] as $change) {
// Skip updates to the cc list and comment tagging // Skip updates to the cc list and comment tagging
if (isset(self::SKIPPED_ACTIVITY[$change['field_name']])) { if (isset(self::SKIPPED_ACTIVITY[$change['field_name']])) {
continue; continue;
} }
$item = array(); $item = [];
$item['uri'] = $this->buguri; $item['uri'] = $this->buguri;
$item['title'] = 'Updated'; $item['title'] = 'Updated';
$item['timestamp'] = $timestamp; $item['timestamp'] = $timestamp;
$item['author'] = $author; $item['author'] = $author;
$item['content'] = ucfirst($change['field_name']) . ': ' . $item['content'] = ucfirst($change['field_name']) . ': ' .
($change['removed'] === '' ? '[nothing]' : $change['removed']) . ' -> ' . ($change['removed'] === '' ? '[nothing]' : $change['removed']) . ' -> ' .
($change['added'] === '' ? '[nothing]' : $change['added']); ($change['added'] === '' ? '[nothing]' : $change['added']);
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }
protected function getUser($user) { protected function getUser($user)
// Check if the user endpoint is available {
if ($this->loadCacheValue($this->instance . 'userEndpointClosed')) { // Check if the user endpoint is available
return $user; if ($this->loadCacheValue($this->instance . 'userEndpointClosed')) {
} return $user;
}
$cache = $this->loadCacheValue($this->instance . $user); $cache = $this->loadCacheValue($this->instance . $user);
if (!is_null($cache)) { if (!is_null($cache)) {
return $cache; return $cache;
} }
$url = $this->instance . '/rest/user/' . $user . '?include_fields=real_name'; $url = $this->instance . '/rest/user/' . $user . '?include_fields=real_name';
try { try {
$json = json_decode(getContents($url), true); $json = json_decode(getContents($url), true);
if (isset($json['error']) and $json['error']) { if (isset($json['error']) and $json['error']) {
throw new Exception; throw new Exception();
} }
} catch (Exception $e) { } catch (Exception $e) {
$this->saveCacheValue($this->instance . 'userEndpointClosed', true); $this->saveCacheValue($this->instance . 'userEndpointClosed', true);
return $user; return $user;
} }
$username = $json['users']['0']['real_name']; $username = $json['users']['0']['real_name'];
if (empty($username)) { if (empty($username)) {
$username = $user; $username = $user;
} }
$this->saveCacheValue($this->instance . $user, $username); $this->saveCacheValue($this->instance . $user, $username);
return $username; return $username;
} }
} }

View file

@ -2,217 +2,219 @@
class BukowskisBridge extends BridgeAbstract class BukowskisBridge extends BridgeAbstract
{ {
const NAME = 'Bukowskis'; const NAME = 'Bukowskis';
const URI = 'https://www.bukowskis.com'; const URI = 'https://www.bukowskis.com';
const DESCRIPTION = 'Fetches info about auction objects from Bukowskis auction house'; const DESCRIPTION = 'Fetches info about auction objects from Bukowskis auction house';
const MAINTAINER = 'Qluxzz'; const MAINTAINER = 'Qluxzz';
const PARAMETERS = array(array( const PARAMETERS = [[
'category' => array( 'category' => [
'name' => 'Category', 'name' => 'Category',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'All categories' => '', 'All categories' => '',
'Art' => array( 'Art' => [
'All' => 'art', 'All' => 'art',
'Classic Art' => 'art.classic-art', 'Classic Art' => 'art.classic-art',
'Classic Finnish Art' => 'art.classic-finnish-art', 'Classic Finnish Art' => 'art.classic-finnish-art',
'Classic Swedish Art' => 'art.classic-swedish-art', 'Classic Swedish Art' => 'art.classic-swedish-art',
'Contemporary' => 'art.contemporary', 'Contemporary' => 'art.contemporary',
'Modern Finnish Art' => 'art.modern-finnish-art', 'Modern Finnish Art' => 'art.modern-finnish-art',
'Modern International Art' => 'art.modern-international-art', 'Modern International Art' => 'art.modern-international-art',
'Modern Swedish Art' => 'art.modern-swedish-art', 'Modern Swedish Art' => 'art.modern-swedish-art',
'Old Masters' => 'art.old-masters', 'Old Masters' => 'art.old-masters',
'Other' => 'art.other', 'Other' => 'art.other',
'Photographs' => 'art.photographs', 'Photographs' => 'art.photographs',
'Prints' => 'art.prints', 'Prints' => 'art.prints',
'Sculpture' => 'art.sculpture', 'Sculpture' => 'art.sculpture',
'Swedish Old Masters' => 'art.swedish-old-masters', 'Swedish Old Masters' => 'art.swedish-old-masters',
), ],
'Asian Ceramics & Works of Art' => array( 'Asian Ceramics & Works of Art' => [
'All' => 'asian-ceramics-works-of-art', 'All' => 'asian-ceramics-works-of-art',
'Other' => 'asian-ceramics-works-of-art.other', 'Other' => 'asian-ceramics-works-of-art.other',
'Porcelain' => 'asian-ceramics-works-of-art.porcelain', 'Porcelain' => 'asian-ceramics-works-of-art.porcelain',
), ],
'Books & Manuscripts' => array( 'Books & Manuscripts' => [
'All' => 'books-manuscripts', 'All' => 'books-manuscripts',
'Books' => 'books-manuscripts.books', 'Books' => 'books-manuscripts.books',
), ],
'Carpets, rugs & textiles' => array( 'Carpets, rugs & textiles' => [
'All' => 'carpets-rugs-textiles', 'All' => 'carpets-rugs-textiles',
'European' => 'carpets-rugs-textiles.european', 'European' => 'carpets-rugs-textiles.european',
'Oriental' => 'carpets-rugs-textiles.oriental', 'Oriental' => 'carpets-rugs-textiles.oriental',
'Rest of the world' => 'carpets-rugs-textiles.rest-of-the-world', 'Rest of the world' => 'carpets-rugs-textiles.rest-of-the-world',
'Scandinavian' => 'carpets-rugs-textiles.scandinavian', 'Scandinavian' => 'carpets-rugs-textiles.scandinavian',
), ],
'Ceramics & porcelain' => array( 'Ceramics & porcelain' => [
'All' => 'ceramics-porcelain', 'All' => 'ceramics-porcelain',
'Ceramic ware' => 'ceramics-porcelain.ceramic-ware', 'Ceramic ware' => 'ceramics-porcelain.ceramic-ware',
'European' => 'ceramics-porcelain.european', 'European' => 'ceramics-porcelain.european',
'Rest of the world' => 'ceramics-porcelain.rest-of-the-world', 'Rest of the world' => 'ceramics-porcelain.rest-of-the-world',
'Scandinavian' => 'ceramics-porcelain.scandinavian', 'Scandinavian' => 'ceramics-porcelain.scandinavian',
), ],
'Collectibles' => array( 'Collectibles' => [
'All' => 'collectibles', 'All' => 'collectibles',
'Advertising & Retail' => 'collectibles.advertising-retail', 'Advertising & Retail' => 'collectibles.advertising-retail',
'Memorabilia' => 'collectibles.memorabilia', 'Memorabilia' => 'collectibles.memorabilia',
'Movies & music' => 'collectibles.movies-music', 'Movies & music' => 'collectibles.movies-music',
'Other' => 'collectibles.other', 'Other' => 'collectibles.other',
'Retro & Popular Culture' => 'collectibles.retro-popular-culture', 'Retro & Popular Culture' => 'collectibles.retro-popular-culture',
'Technica & Nautica' => 'collectibles.technica-nautica', 'Technica & Nautica' => 'collectibles.technica-nautica',
'Toys' => 'collectibles.toys', 'Toys' => 'collectibles.toys',
), ],
'Design' => array( 'Design' => [
'All' => 'design', 'All' => 'design',
'Art glass' => 'design.art-glass', 'Art glass' => 'design.art-glass',
'Furniture' => 'design.furniture', 'Furniture' => 'design.furniture',
'Other' => 'design.other', 'Other' => 'design.other',
), ],
'Folk art' => array( 'Folk art' => [
'All' => 'folk-art', 'All' => 'folk-art',
'All categories' => 'lots', 'All categories' => 'lots',
), ],
'Furniture' => array( 'Furniture' => [
'All' => 'furniture', 'All' => 'furniture',
'Armchairs & Sofas' => 'furniture.armchairs-sofas', 'Armchairs & Sofas' => 'furniture.armchairs-sofas',
'Cabinets & Bureaus' => 'furniture.cabinets-bureaus', 'Cabinets & Bureaus' => 'furniture.cabinets-bureaus',
'Chairs' => 'furniture.chairs', 'Chairs' => 'furniture.chairs',
'Garden furniture' => 'furniture.garden-furniture', 'Garden furniture' => 'furniture.garden-furniture',
'Mirrors' => 'furniture.mirrors', 'Mirrors' => 'furniture.mirrors',
'Other' => 'furniture.other', 'Other' => 'furniture.other',
'Shelves & Book cases' => 'furniture.shelves-book-cases', 'Shelves & Book cases' => 'furniture.shelves-book-cases',
'Tables' => 'furniture.tables', 'Tables' => 'furniture.tables',
), ],
'Glassware' => array( 'Glassware' => [
'All' => 'glassware', 'All' => 'glassware',
'Glassware' => 'glassware.glassware', 'Glassware' => 'glassware.glassware',
'Other' => 'glassware.other', 'Other' => 'glassware.other',
), ],
'Jewellery' => array( 'Jewellery' => [
'All' => 'jewellery', 'All' => 'jewellery',
'Bracelets' => 'jewellery.bracelets', 'Bracelets' => 'jewellery.bracelets',
'Brooches' => 'jewellery.brooches', 'Brooches' => 'jewellery.brooches',
'Earrings' => 'jewellery.earrings', 'Earrings' => 'jewellery.earrings',
'Necklaces & Pendants' => 'jewellery.necklaces-pendants', 'Necklaces & Pendants' => 'jewellery.necklaces-pendants',
'Other' => 'jewellery.other', 'Other' => 'jewellery.other',
'Rings' => 'jewellery.rings', 'Rings' => 'jewellery.rings',
), ],
'Lighting' => array( 'Lighting' => [
'All' => 'lighting', 'All' => 'lighting',
'Candle sticks & Candelabras' => 'lighting.candle-sticks-candelabras', 'Candle sticks & Candelabras' => 'lighting.candle-sticks-candelabras',
'Ceiling lights' => 'lighting.ceiling-lights', 'Ceiling lights' => 'lighting.ceiling-lights',
'Chandeliers' => 'lighting.chandeliers', 'Chandeliers' => 'lighting.chandeliers',
'Floor lights' => 'lighting.floor-lights', 'Floor lights' => 'lighting.floor-lights',
'Other' => 'lighting.other', 'Other' => 'lighting.other',
'Table lights' => 'lighting.table-lights', 'Table lights' => 'lighting.table-lights',
'Wall lights' => 'lighting.wall-lights', 'Wall lights' => 'lighting.wall-lights',
), ],
'Militaria' => array( 'Militaria' => [
'All' => 'militaria', 'All' => 'militaria',
'Honors & Medals' => 'militaria.honors-medals', 'Honors & Medals' => 'militaria.honors-medals',
'Other militaria' => 'militaria.other-militaria', 'Other militaria' => 'militaria.other-militaria',
'Weaponry' => 'militaria.weaponry', 'Weaponry' => 'militaria.weaponry',
), ],
'Miscellaneous' => array( 'Miscellaneous' => [
'All' => 'miscellaneous', 'All' => 'miscellaneous',
'Brass, Copper & Pewter' => 'miscellaneous.brass-copper-pewter', 'Brass, Copper & Pewter' => 'miscellaneous.brass-copper-pewter',
'Nickel silver' => 'miscellaneous.nickel-silver', 'Nickel silver' => 'miscellaneous.nickel-silver',
'Oriental' => 'miscellaneous.oriental', 'Oriental' => 'miscellaneous.oriental',
'Other' => 'miscellaneous.other', 'Other' => 'miscellaneous.other',
), ],
'Silver' => array( 'Silver' => [
'All' => 'silver', 'All' => 'silver',
'Candle sticks' => 'silver.candle-sticks', 'Candle sticks' => 'silver.candle-sticks',
'Cups & Bowls' => 'silver.cups-bowls', 'Cups & Bowls' => 'silver.cups-bowls',
'Cutlery' => 'silver.cutlery', 'Cutlery' => 'silver.cutlery',
'Other' => 'silver.other', 'Other' => 'silver.other',
), ],
'Timepieces' => array( 'Timepieces' => [
'All' => 'timepieces', 'All' => 'timepieces',
'Other' => 'timepieces.other', 'Other' => 'timepieces.other',
'Pocket watches' => 'timepieces.pocket-watches', 'Pocket watches' => 'timepieces.pocket-watches',
'Table clocks' => 'timepieces.table-clocks', 'Table clocks' => 'timepieces.table-clocks',
'Wrist watches' => 'timepieces.wrist-watches', 'Wrist watches' => 'timepieces.wrist-watches',
), ],
'Vintage & Fashion' => array( 'Vintage & Fashion' => [
'All' => 'vintage-fashion', 'All' => 'vintage-fashion',
'Accessories' => 'vintage-fashion.accessories', 'Accessories' => 'vintage-fashion.accessories',
'Bags & Trunks' => 'vintage-fashion.bags-trunks', 'Bags & Trunks' => 'vintage-fashion.bags-trunks',
'Clothes' => 'vintage-fashion.clothes', 'Clothes' => 'vintage-fashion.clothes',
), ],
) ]
), ],
'sort_order' => array( 'sort_order' => [
'name' => 'Sort order', 'name' => 'Sort order',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'Ending soon' => 'ending', 'Ending soon' => 'ending',
'Most recent' => 'recent', 'Most recent' => 'recent',
'Most bids' => 'most', 'Most bids' => 'most',
'Fewest bids' => 'fewest', 'Fewest bids' => 'fewest',
'Lowest price' => 'lowest', 'Lowest price' => 'lowest',
'Highest price' => 'highest', 'Highest price' => 'highest',
'Lowest estimate' => 'low', 'Lowest estimate' => 'low',
'Highest estimate' => 'high', 'Highest estimate' => 'high',
'Alphabetical' => 'alphabetical', 'Alphabetical' => 'alphabetical',
), ],
), ],
'language' => array( 'language' => [
'name' => 'Language', 'name' => 'Language',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'English' => 'en', 'English' => 'en',
'Swedish' => 'sv', 'Swedish' => 'sv',
'Finnish' => 'fi' 'Finnish' => 'fi'
), ],
), ],
)); ]];
const CACHE_TIMEOUT = 3600; // 1 hour const CACHE_TIMEOUT = 3600; // 1 hour
private $title; private $title;
public function collectData() public function collectData()
{ {
$baseUrl = 'https://www.bukowskis.com'; $baseUrl = 'https://www.bukowskis.com';
$category = $this->getInput('category'); $category = $this->getInput('category');
$language = $this->getInput('language'); $language = $this->getInput('language');
$sort_order = $this->getInput('sort_order'); $sort_order = $this->getInput('sort_order');
$url = $baseUrl . '/' . $language . '/lots'; $url = $baseUrl . '/' . $language . '/lots';
if ($category) if ($category) {
$url = $url . '/category/' . $category; $url = $url . '/category/' . $category;
}
if ($sort_order) if ($sort_order) {
$url = $url . '/sort/' . $sort_order; $url = $url . '/sort/' . $sort_order;
}
$html = getSimpleHTMLDOM($url); $html = getSimpleHTMLDOM($url);
$this->title = htmlspecialchars_decode($html->find('title', 0)->innertext); $this->title = htmlspecialchars_decode($html->find('title', 0)->innertext);
foreach ($html->find('div.c-lot-index-lot') as $lot) { foreach ($html->find('div.c-lot-index-lot') as $lot) {
$title = $lot->find('a.c-lot-index-lot__title', 0)->plaintext; $title = $lot->find('a.c-lot-index-lot__title', 0)->plaintext;
$relative_url = $lot->find('a.c-lot-index-lot__link', 0)->href; $relative_url = $lot->find('a.c-lot-index-lot__link', 0)->href;
$images = json_decode( $images = json_decode(
htmlspecialchars_decode( htmlspecialchars_decode(
$lot $lot
->find('img.o-aspect-ratio__image', 0) ->find('img.o-aspect-ratio__image', 0)
->getAttribute('data-thumbnails') ->getAttribute('data-thumbnails')
) )
); );
$this->items[] = array( $this->items[] = [
'title' => $title, 'title' => $title,
'uri' => $baseUrl . $relative_url, 'uri' => $baseUrl . $relative_url,
'uid' => $lot->getAttribute('data-lot-id'), 'uid' => $lot->getAttribute('data-lot-id'),
'content' => count($images) > 0 ? "<img src='$images[0]'/><br/>$title" : $title, 'content' => count($images) > 0 ? "<img src='$images[0]'/><br/>$title" : $title,
'enclosures' => array_slice($images, 1), 'enclosures' => array_slice($images, 1),
); ];
} }
} }
public function getName() public function getName()
{ {
return $this->title ?: parent::getName(); return $this->title ?: parent::getName();
} }
} }

View file

@ -1,84 +1,88 @@
<?php <?php
class BundesbankBridge extends BridgeAbstract {
const PARAM_LANG = 'lang'; class BundesbankBridge extends BridgeAbstract
{
const PARAM_LANG = 'lang';
const LANG_EN = 'en'; const LANG_EN = 'en';
const LANG_DE = 'de'; const LANG_DE = 'de';
const NAME = 'Bundesbank Bridge'; const NAME = 'Bundesbank Bridge';
const URI = 'https://www.bundesbank.de/'; const URI = 'https://www.bundesbank.de/';
const DESCRIPTION = 'Returns the latest studies of the Bundesbank (Germany)'; const DESCRIPTION = 'Returns the latest studies of the Bundesbank (Germany)';
const MAINTAINER = 'logmanoriginal'; const MAINTAINER = 'logmanoriginal';
const CACHE_TIMEOUT = 86400; // 24 hours const CACHE_TIMEOUT = 86400; // 24 hours
const PARAMETERS = array( const PARAMETERS = [
array( [
self::PARAM_LANG => array( self::PARAM_LANG => [
'name' => 'Language', 'name' => 'Language',
'type' => 'list', 'type' => 'list',
'defaultValue' => self::LANG_DE, 'defaultValue' => self::LANG_DE,
'values' => array( 'values' => [
'English' => self::LANG_EN, 'English' => self::LANG_EN,
'Deutsch' => self::LANG_DE 'Deutsch' => self::LANG_DE
) ]
) ]
) ]
); ];
public function getIcon() { public function getIcon()
return self::URI . 'resource/crblob/1890/a7f48ee0ae35348748121770ba3ca009/mL/favicon-ico-data.ico'; {
} return self::URI . 'resource/crblob/1890/a7f48ee0ae35348748121770ba3ca009/mL/favicon-ico-data.ico';
}
public function getURI() { public function getURI()
switch($this->getInput(self::PARAM_LANG)) { {
case self::LANG_EN: return self::URI . 'en/publications/reports/studies'; switch ($this->getInput(self::PARAM_LANG)) {
case self::LANG_DE: return self::URI . 'de/publikationen/berichte/studien'; case self::LANG_EN:
} return self::URI . 'en/publications/reports/studies';
case self::LANG_DE:
return self::URI . 'de/publikationen/berichte/studien';
}
return parent::getURI(); return parent::getURI();
} }
public function collectData() { public function collectData()
{
$html = getSimpleHTMLDOM($this->getURI());
$html = getSimpleHTMLDOM($this->getURI()); $html = defaultLinkTo($html, $this->getURI());
$html = defaultLinkTo($html, $this->getURI()); foreach ($html->find('ul.resultlist li') as $study) {
$item = [];
foreach($html->find('ul.resultlist li') as $study) { $item['uri'] = $study->find('.teasable__link', 0)->href;
$item = array();
$item['uri'] = $study->find('.teasable__link', 0)->href; // Get title without child elements (i.e. subtitle)
$title = $study->find('.teasable__title div.h2', 0);
// Get title without child elements (i.e. subtitle) foreach ($title->children as &$child) {
$title = $study->find('.teasable__title div.h2', 0); $child->outertext = '';
}
foreach($title->children as &$child) { $item['title'] = $title->innertext;
$child->outertext = '';
}
$item['title'] = $title->innertext; // Add subtitle to the content if it exists
$item['content'] = '';
// Add subtitle to the content if it exists if ($subtitle = $study->find('.teasable__subtitle', 0)) {
$item['content'] = ''; $item['content'] .= '<strong>' . $study->find('.teasable__subtitle', 0)->plaintext . '</strong>';
}
if($subtitle = $study->find('.teasable__subtitle', 0)) { $item['content'] .= '<p>' . $study->find('.teasable__text', 0)->plaintext . '</p>';
$item['content'] .= '<strong>' . $study->find('.teasable__subtitle', 0)->plaintext . '</strong>';
}
$item['content'] .= '<p>' . $study->find('.teasable__text', 0)->plaintext . '</p>'; $item['timestamp'] = strtotime($study->find('.teasable__date', 0)->plaintext);
$item['timestamp'] = strtotime($study->find('.teasable__date', 0)->plaintext); // Downloads and older studies don't have images
if ($study->find('.teasable__image', 0)) {
$item['enclosures'] = [
$study->find('.teasable__image img', 0)->src
];
}
// Downloads and older studies don't have images $this->items[] = $item;
if($study->find('.teasable__image', 0)) { }
$item['enclosures'] = array( }
$study->find('.teasable__image img', 0)->src
);
}
$this->items[] = $item;
}
}
} }

View file

@ -1,89 +1,94 @@
<?php <?php
class BundestagParteispendenBridge extends BridgeAbstract {
const MAINTAINER = 'mibe';
const NAME = 'Deutscher Bundestag - Parteispenden';
const URI = 'https://www.bundestag.de/parlament/praesidium/parteienfinanzierung/fundstellen50000';
const CACHE_TIMEOUT = 86400; // 24h class BundestagParteispendenBridge extends BridgeAbstract
const DESCRIPTION = 'Returns the latest "soft money" donations to parties represented in the German Bundestag.'; {
const CONTENT_TEMPLATE = <<<TMPL const MAINTAINER = 'mibe';
const NAME = 'Deutscher Bundestag - Parteispenden';
const URI = 'https://www.bundestag.de/parlament/praesidium/parteienfinanzierung/fundstellen50000';
const CACHE_TIMEOUT = 86400; // 24h
const DESCRIPTION = 'Returns the latest "soft money" donations to parties represented in the German Bundestag.';
const CONTENT_TEMPLATE = <<<TMPL
<p><b>Partei:</b><br>%s</p> <p><b>Partei:</b><br>%s</p>
<p><b>Spendenbetrag:</b><br>%s</p> <p><b>Spendenbetrag:</b><br>%s</p>
<p><b>Spender:</b><br>%s</p> <p><b>Spender:</b><br>%s</p>
<p><b>Eingang der Spende:</b><br>%s</p> <p><b>Eingang der Spende:</b><br>%s</p>
TMPL; TMPL;
public function getIcon() public function getIcon()
{ {
return 'https://www.bundestag.de/static/appdata/includes/images/layout/favicon.ico'; return 'https://www.bundestag.de/static/appdata/includes/images/layout/favicon.ico';
} }
public function collectData() public function collectData()
{ {
$ajaxUri = <<<URI $ajaxUri = <<<URI
https://www.bundestag.de/ajax/filterlist/de/parlament/praesidium/parteienfinanzierung/fundstellen50000/462002-462002 https://www.bundestag.de/ajax/filterlist/de/parlament/praesidium/parteienfinanzierung/fundstellen50000/462002-462002
URI; URI;
// Get the main page // Get the main page
$html = getSimpleHTMLDOMCached($ajaxUri, self::CACHE_TIMEOUT) $html = getSimpleHTMLDOMCached($ajaxUri, self::CACHE_TIMEOUT)
or returnServerError('Could not request AJAX list.'); or returnServerError('Could not request AJAX list.');
// Build the URL from the first anchor element. The list is sorted by year, descending, so the first element is the current year. // Build the URL from the first anchor element. The list is sorted by year, descending, so the first element is the current year.
$firstAnchor = $html->find('a', 0) $firstAnchor = $html->find('a', 0)
or returnServerError('Could not find the proper HTML element.'); or returnServerError('Could not find the proper HTML element.');
$url = 'https://www.bundestag.de' . $firstAnchor->href; $url = 'https://www.bundestag.de' . $firstAnchor->href;
// Get the actual page with the soft money donations // Get the actual page with the soft money donations
$html = getSimpleHTMLDOMCached($url, self::CACHE_TIMEOUT) $html = getSimpleHTMLDOMCached($url, self::CACHE_TIMEOUT)
or returnServerError('Could not request ' . $url); or returnServerError('Could not request ' . $url);
$rows = $html->find('table.table > tbody > tr') $rows = $html->find('table.table > tbody > tr')
or returnServerError('Could not find the proper HTML elements.'); or returnServerError('Could not find the proper HTML elements.');
foreach($rows as $row) { foreach ($rows as $row) {
$item = $this->generateItemFromRow($row); $item = $this->generateItemFromRow($row);
if (is_array($item)) { if (is_array($item)) {
$item['uri'] = $url; $item['uri'] = $url;
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }
private function generateItemFromRow(simple_html_dom_node $row) private function generateItemFromRow(simple_html_dom_node $row)
{ {
// The row must have 5 columns. There are monthly header rows, which are ignored here. // The row must have 5 columns. There are monthly header rows, which are ignored here.
if(count($row->children) != 5) if (count($row->children) != 5) {
return null; return null;
}
$item = array(); $item = [];
// | column | paragraph inside column // | column | paragraph inside column
$party = $row->children[0]->children[0]->innertext; $party = $row->children[0]->children[0]->innertext;
$amount = $row->children[1]->children[0]->innertext . ' €'; $amount = $row->children[1]->children[0]->innertext . ' €';
$donor = $row->children[2]->children[0]->innertext; $donor = $row->children[2]->children[0]->innertext;
$date = $row->children[3]->children[0]->innertext; $date = $row->children[3]->children[0]->innertext;
$dip = $row->children[4]->children[0]->find('a.dipLink', 0); $dip = $row->children[4]->children[0]->find('a.dipLink', 0);
// Strip whitespace from date string. // Strip whitespace from date string.
$date = str_replace(' ', '', $date); $date = str_replace(' ', '', $date);
$content = sprintf(self::CONTENT_TEMPLATE, $party, $amount, $donor, $date); $content = sprintf(self::CONTENT_TEMPLATE, $party, $amount, $donor, $date);
$item = array( $item = [
'title' => $party . ': ' . $amount, 'title' => $party . ': ' . $amount,
'content' => $content, 'content' => $content,
'uid' => sha1($content), 'uid' => sha1($content),
); ];
// Try to get the link to the official document // Try to get the link to the official document
if ($dip != null) if ($dip != null) {
$item['enclosures'] = array($dip->href); $item['enclosures'] = [$dip->href];
}
// Try to parse the date // Try to parse the date
$dateTime = DateTime::createFromFormat('d.m.Y', $date); $dateTime = DateTime::createFromFormat('d.m.Y', $date);
if ($dateTime !== false) if ($dateTime !== false) {
$item['timestamp'] = $dateTime->getTimestamp(); $item['timestamp'] = $dateTime->getTimestamp();
}
return $item; return $item;
} }
} }

View file

@ -1,36 +1,38 @@
<?php <?php
class CBCEditorsBlogBridge extends BridgeAbstract {
const MAINTAINER = 'quickwick'; class CBCEditorsBlogBridge extends BridgeAbstract
const NAME = 'CBC Editors Blog'; {
const URI = 'https://www.cbc.ca/news/editorsblog'; const MAINTAINER = 'quickwick';
const DESCRIPTION = 'Recent CBC Editor\'s Blog posts'; const NAME = 'CBC Editors Blog';
const URI = 'https://www.cbc.ca/news/editorsblog';
const DESCRIPTION = 'Recent CBC Editor\'s Blog posts';
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM(self::URI); {
$html = getSimpleHTMLDOM(self::URI);
// Loop on each blog post entry // Loop on each blog post entry
foreach($html->find('div.contentListCards', 0)->find('a[data-test=type-story]') as $element) { foreach ($html->find('div.contentListCards', 0)->find('a[data-test=type-story]') as $element) {
$headline = ($element->find('.headline', 0))->innertext; $headline = ($element->find('.headline', 0))->innertext;
$timestamp = ($element->find('time', 0))->datetime; $timestamp = ($element->find('time', 0))->datetime;
$articleUri = 'https://www.cbc.ca' . $element->href; $articleUri = 'https://www.cbc.ca' . $element->href;
$summary = ($element->find('div.description', 0))->innertext; $summary = ($element->find('div.description', 0))->innertext;
$thumbnailUris = ($element->find('img[loading=lazy]', 0))->srcset; $thumbnailUris = ($element->find('img[loading=lazy]', 0))->srcset;
$thumbnailUri = rtrim(explode(',', $thumbnailUris)[0], ' 300w'); $thumbnailUri = rtrim(explode(',', $thumbnailUris)[0], ' 300w');
// Fill item // Fill item
$item = array(); $item = [];
$item['uri'] = $articleUri; $item['uri'] = $articleUri;
$item['id'] = $item['uri']; $item['id'] = $item['uri'];
$item['timestamp'] = $timestamp; $item['timestamp'] = $timestamp;
$item['title'] = $headline; $item['title'] = $headline;
$item['content'] = '<img src="' $item['content'] = '<img src="'
. $thumbnailUri . '" /><br>' . $summary; . $thumbnailUri . '" /><br>' . $summary;
$item['author'] = 'Editor\'s Blog'; $item['author'] = 'Editor\'s Blog';
if(isset($item['title'])) { if (isset($item['title'])) {
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }
} }

View file

@ -1,108 +1,114 @@
<?php <?php
class CNETBridge extends BridgeAbstract {
const MAINTAINER = 'ORelio'; class CNETBridge extends BridgeAbstract
const NAME = 'CNET News'; {
const URI = 'https://www.cnet.com/'; const MAINTAINER = 'ORelio';
const CACHE_TIMEOUT = 3600; // 1h const NAME = 'CNET News';
const DESCRIPTION = 'Returns the newest articles.'; const URI = 'https://www.cnet.com/';
const PARAMETERS = array( const CACHE_TIMEOUT = 3600; // 1h
array( const DESCRIPTION = 'Returns the newest articles.';
'topic' => array( const PARAMETERS = [
'name' => 'Topic', [
'type' => 'list', 'topic' => [
'values' => array( 'name' => 'Topic',
'All articles' => '', 'type' => 'list',
'Apple' => 'apple', 'values' => [
'Google' => 'google', 'All articles' => '',
'Microsoft' => 'tags-microsoft', 'Apple' => 'apple',
'Computers' => 'topics-computers', 'Google' => 'google',
'Mobile' => 'topics-mobile', 'Microsoft' => 'tags-microsoft',
'Sci-Tech' => 'topics-sci-tech', 'Computers' => 'topics-computers',
'Security' => 'topics-security', 'Mobile' => 'topics-mobile',
'Internet' => 'topics-internet', 'Sci-Tech' => 'topics-sci-tech',
'Tech Industry' => 'topics-tech-industry' 'Security' => 'topics-security',
) 'Internet' => 'topics-internet',
) 'Tech Industry' => 'topics-tech-industry'
) ]
); ]
]
];
private function cleanArticle($article_html) { private function cleanArticle($article_html)
$offset_p = strpos($article_html, '<p>'); {
$offset_figure = strpos($article_html, '<figure'); $offset_p = strpos($article_html, '<p>');
$offset = ($offset_figure < $offset_p ? $offset_figure : $offset_p); $offset_figure = strpos($article_html, '<figure');
$article_html = substr($article_html, $offset); $offset = ($offset_figure < $offset_p ? $offset_figure : $offset_p);
$article_html = str_replace('href="/', 'href="' . self::URI, $article_html); $article_html = substr($article_html, $offset);
$article_html = str_replace(' height="0"', '', $article_html); $article_html = str_replace('href="/', 'href="' . self::URI, $article_html);
$article_html = str_replace('<noscript>', '', $article_html); $article_html = str_replace(' height="0"', '', $article_html);
$article_html = str_replace('</noscript>', '', $article_html); $article_html = str_replace('<noscript>', '', $article_html);
$article_html = StripWithDelimiters($article_html, '<a class="clickToEnlarge', '</a>'); $article_html = str_replace('</noscript>', '', $article_html);
$article_html = stripWithDelimiters($article_html, '<span class="nowPlaying', '</span>'); $article_html = StripWithDelimiters($article_html, '<a class="clickToEnlarge', '</a>');
$article_html = stripWithDelimiters($article_html, '<span class="duration', '</span>'); $article_html = stripWithDelimiters($article_html, '<span class="nowPlaying', '</span>');
$article_html = stripWithDelimiters($article_html, '<script', '</script>'); $article_html = stripWithDelimiters($article_html, '<span class="duration', '</span>');
$article_html = stripWithDelimiters($article_html, '<svg', '</svg>'); $article_html = stripWithDelimiters($article_html, '<script', '</script>');
return $article_html; $article_html = stripWithDelimiters($article_html, '<svg', '</svg>');
} return $article_html;
}
public function collectData() { public function collectData()
{
// Retrieve and check user input
$topic = str_replace('-', '/', $this->getInput('topic'));
if (!empty($topic) && (substr_count($topic, '/') > 1 || !ctype_alpha(str_replace('/', '', $topic)))) {
returnClientError('Invalid topic: ' . $topic);
}
// Retrieve and check user input // Retrieve webpage
$topic = str_replace('-', '/', $this->getInput('topic')); $pageUrl = self::URI . (empty($topic) ? 'news/' : $topic . '/');
if (!empty($topic) && (substr_count($topic, '/') > 1 || !ctype_alpha(str_replace('/', '', $topic)))) $html = getSimpleHTMLDOM($pageUrl);
returnClientError('Invalid topic: ' . $topic);
// Retrieve webpage // Process articles
$pageUrl = self::URI . (empty($topic) ? 'news/' : $topic . '/'); foreach ($html->find('div.assetBody, div.riverPost') as $element) {
$html = getSimpleHTMLDOM($pageUrl); if (count($this->items) >= 10) {
break;
}
// Process articles $article_title = trim($element->find('h2, h3', 0)->plaintext);
foreach($html->find('div.assetBody, div.riverPost') as $element) { $article_uri = self::URI . substr($element->find('a', 0)->href, 1);
$article_thumbnail = $element->parent()->find('img[src]', 0)->src;
$article_timestamp = strtotime($element->find('time.assetTime, div.timeAgo', 0)->plaintext);
$article_author = trim($element->find('a[rel=author], a.name', 0)->plaintext);
$article_content = '<p><b>' . trim($element->find('p.dek', 0)->plaintext) . '</b></p>';
if(count($this->items) >= 10) { if (is_null($article_thumbnail)) {
break; $article_thumbnail = extractFromDelimiters($element->innertext, '<img src="', '"');
} }
$article_title = trim($element->find('h2, h3', 0)->plaintext); if (!empty($article_title) && !empty($article_uri) && strpos($article_uri, self::URI . 'news/') !== false) {
$article_uri = self::URI . substr($element->find('a', 0)->href, 1); $article_html = getSimpleHTMLDOMCached($article_uri) or $article_html = null;
$article_thumbnail = $element->parent()->find('img[src]', 0)->src;
$article_timestamp = strtotime($element->find('time.assetTime, div.timeAgo', 0)->plaintext);
$article_author = trim($element->find('a[rel=author], a.name', 0)->plaintext);
$article_content = '<p><b>' . trim($element->find('p.dek', 0)->plaintext) . '</b></p>';
if (is_null($article_thumbnail)) if (!is_null($article_html)) {
$article_thumbnail = extractFromDelimiters($element->innertext, '<img src="', '"'); if (empty($article_thumbnail)) {
$article_thumbnail = $article_html->find('div.originalImage', 0);
}
if (empty($article_thumbnail)) {
$article_thumbnail = $article_html->find('span.imageContainer', 0);
}
if (is_object($article_thumbnail)) {
$article_thumbnail = $article_thumbnail->find('img', 0)->src;
}
if (!empty($article_title) && !empty($article_uri) && strpos($article_uri, self::URI . 'news/') !== false) { $article_content .= trim(
$this->cleanArticle(
extractFromDelimiters(
$article_html,
'<article',
'<footer'
)
)
);
}
$article_html = getSimpleHTMLDOMCached($article_uri) or $article_html = null; $item = [];
$item['uri'] = $article_uri;
if (!is_null($article_html)) { $item['title'] = $article_title;
$item['author'] = $article_author;
if (empty($article_thumbnail)) $item['timestamp'] = $article_timestamp;
$article_thumbnail = $article_html->find('div.originalImage', 0); $item['enclosures'] = [$article_thumbnail];
if (empty($article_thumbnail)) $item['content'] = $article_content;
$article_thumbnail = $article_html->find('span.imageContainer', 0); $this->items[] = $item;
if (is_object($article_thumbnail)) }
$article_thumbnail = $article_thumbnail->find('img', 0)->src; }
}
$article_content .= trim(
$this->cleanArticle(
extractFromDelimiters(
$article_html, '<article', '<footer'
)
)
);
}
$item = array();
$item['uri'] = $article_uri;
$item['title'] = $article_title;
$item['author'] = $article_author;
$item['timestamp'] = $article_timestamp;
$item['enclosures'] = array($article_thumbnail);
$item['content'] = $article_content;
$this->items[] = $item;
}
}
}
} }

View file

@ -1,63 +1,64 @@
<?php <?php
class CNETFranceBridge extends FeedExpander class CNETFranceBridge extends FeedExpander
{ {
const MAINTAINER = 'leomaradan'; const MAINTAINER = 'leomaradan';
const NAME = 'CNET France'; const NAME = 'CNET France';
const URI = 'https://www.cnetfrance.fr/'; const URI = 'https://www.cnetfrance.fr/';
const CACHE_TIMEOUT = 3600; // 1h const CACHE_TIMEOUT = 3600; // 1h
const DESCRIPTION = 'CNET France RSS with filters'; const DESCRIPTION = 'CNET France RSS with filters';
const PARAMETERS = array( const PARAMETERS = [
'filters' => array( 'filters' => [
'title' => array( 'title' => [
'name' => 'Exclude by title', 'name' => 'Exclude by title',
'required' => false, 'required' => false,
'title' => 'Title term, separated by semicolon (;)', 'title' => 'Title term, separated by semicolon (;)',
'exampleValue' => 'bon plan;bons plans;au meilleur prix;des meilleures offres;Amazon Prime Day;RED by SFR ou B&You' 'exampleValue' => 'bon plan;bons plans;au meilleur prix;des meilleures offres;Amazon Prime Day;RED by SFR ou B&You'
), ],
'url' => array( 'url' => [
'name' => 'Exclude by url', 'name' => 'Exclude by url',
'required' => false, 'required' => false,
'title' => 'URL term, separated by semicolon (;)', 'title' => 'URL term, separated by semicolon (;)',
'exampleValue' => 'bon-plan;bons-plans' 'exampleValue' => 'bon-plan;bons-plans'
) ]
) ]
); ];
private $bannedTitle = array(); private $bannedTitle = [];
private $bannedURL = array(); private $bannedURL = [];
public function collectData() public function collectData()
{ {
$title = $this->getInput('title'); $title = $this->getInput('title');
$url = $this->getInput('url'); $url = $this->getInput('url');
if ($title !== null) { if ($title !== null) {
$this->bannedTitle = explode(';', $title); $this->bannedTitle = explode(';', $title);
} }
if ($url !== null) { if ($url !== null) {
$this->bannedURL = explode(';', $url); $this->bannedURL = explode(';', $url);
} }
$this->collectExpandableDatas('https://www.cnetfrance.fr/feeds/rss/news/'); $this->collectExpandableDatas('https://www.cnetfrance.fr/feeds/rss/news/');
} }
protected function parseItem($feedItem) protected function parseItem($feedItem)
{ {
$item = parent::parseItem($feedItem); $item = parent::parseItem($feedItem);
foreach ($this->bannedTitle as $term) { foreach ($this->bannedTitle as $term) {
if (preg_match('/' . $term . '/mi', $item['title']) === 1) { if (preg_match('/' . $term . '/mi', $item['title']) === 1) {
return null; return null;
} }
} }
foreach ($this->bannedURL as $term) { foreach ($this->bannedURL as $term) {
if (preg_match('/' . $term . '/mi', $item['uri']) === 1) { if (preg_match('/' . $term . '/mi', $item['uri']) === 1) {
return null; return null;
} }
} }
return $item; return $item;
} }
} }

View file

@ -7,134 +7,139 @@
// it is not reliable and contain no useful information. This bridge create a // it is not reliable and contain no useful information. This bridge create a
// sane feed with additional information like tags and a link to the CWE // sane feed with additional information like tags and a link to the CWE
// a description of the vulnerability. // a description of the vulnerability.
class CVEDetailsBridge extends BridgeAbstract { class CVEDetailsBridge extends BridgeAbstract
const MAINTAINER = 'Aaron Fischer'; {
const NAME = 'CVE Details'; const MAINTAINER = 'Aaron Fischer';
const CACHE_TIMEOUT = 60 * 60 * 6; // 6 hours const NAME = 'CVE Details';
const DESCRIPTION = 'Report new CVE vulnerabilities for a given vendor (and product)'; const CACHE_TIMEOUT = 60 * 60 * 6; // 6 hours
const URI = 'https://www.cvedetails.com'; const DESCRIPTION = 'Report new CVE vulnerabilities for a given vendor (and product)';
const URI = 'https://www.cvedetails.com';
const PARAMETERS = array(array( const PARAMETERS = [[
// The Vendor ID can be taken from the URL // The Vendor ID can be taken from the URL
'vendor_id' => array( 'vendor_id' => [
'name' => 'Vendor ID', 'name' => 'Vendor ID',
'type' => 'number', 'type' => 'number',
'required' => true, 'required' => true,
'exampleValue' => 74, // PHP 'exampleValue' => 74, // PHP
), ],
// The optional Product ID can be taken from the URL as well // The optional Product ID can be taken from the URL as well
'product_id' => array( 'product_id' => [
'name' => 'Product ID', 'name' => 'Product ID',
'type' => 'number', 'type' => 'number',
'required' => false, 'required' => false,
'exampleValue' => 128, // PHP 'exampleValue' => 128, // PHP
), ],
)); ]];
private $html = null; private $html = null;
private $vendor = ''; private $vendor = '';
private $product = ''; private $product = '';
// Return the URL to query. // Return the URL to query.
// Because of the optional product ID, we need to attach it if it is // Because of the optional product ID, we need to attach it if it is
// set. The search result page has the exact same structure (with and // set. The search result page has the exact same structure (with and
// without the product ID). // without the product ID).
private function buildUrl() { private function buildUrl()
$url = self::URI . '/vulnerability-list/vendor_id-' . $this->getInput('vendor_id'); {
if ($this->getInput('product_id') !== '') { $url = self::URI . '/vulnerability-list/vendor_id-' . $this->getInput('vendor_id');
$url .= '/product_id-' . $this->getInput('product_id'); if ($this->getInput('product_id') !== '') {
} $url .= '/product_id-' . $this->getInput('product_id');
// Sadly, there is no way (prove me wrong please) to sort the search }
// result by publish date. So the nearest alternative is the CVE // Sadly, there is no way (prove me wrong please) to sort the search
// number, which should be mostly accurate. // result by publish date. So the nearest alternative is the CVE
$url .= '?order=1'; // Order by CVE number DESC // number, which should be mostly accurate.
$url .= '?order=1'; // Order by CVE number DESC
return $url; return $url;
} }
// Make the actual request to cvedetails.com and stores the response // Make the actual request to cvedetails.com and stores the response
// (HTML) for later use and extract vendor and product from it. // (HTML) for later use and extract vendor and product from it.
private function fetchContent() { private function fetchContent()
$html = getSimpleHTMLDOM($this->buildUrl()); {
$this->html = defaultLinkTo($html, self::URI); $html = getSimpleHTMLDOM($this->buildUrl());
$this->html = defaultLinkTo($html, self::URI);
$vendor = $html->find('#contentdiv > h1 > a', 0); $vendor = $html->find('#contentdiv > h1 > a', 0);
if ($vendor == null) { if ($vendor == null) {
returnServerError('Invalid Vendor ID ' . returnServerError('Invalid Vendor ID ' .
$this->getInput('vendor_id') . $this->getInput('vendor_id') .
' or Product ID ' . ' or Product ID ' .
$this->getInput('product_id')); $this->getInput('product_id'));
} }
$this->vendor = $vendor->innertext; $this->vendor = $vendor->innertext;
$product = $html->find('#contentdiv > h1 > a', 1); $product = $html->find('#contentdiv > h1 > a', 1);
if ($product != null) { if ($product != null) {
$this->product = $product->innertext; $this->product = $product->innertext;
} }
} }
// Build the name of the feed. // Build the name of the feed.
public function getName() { public function getName()
if ($this->getInput('vendor_id') == '') { {
return self::NAME; if ($this->getInput('vendor_id') == '') {
} return self::NAME;
}
if ($this->html == null) { if ($this->html == null) {
$this->fetchContent(); $this->fetchContent();
} }
$name = 'CVE Vulnerabilities for ' . $this->vendor; $name = 'CVE Vulnerabilities for ' . $this->vendor;
if ($this->product != '') { if ($this->product != '') {
$name .= '/' . $this->product; $name .= '/' . $this->product;
} }
return $name; return $name;
} }
// Pull the data from the HTML response and fill the items.. // Pull the data from the HTML response and fill the items..
public function collectData() { public function collectData()
if ($this->html == null) { {
$this->fetchContent(); if ($this->html == null) {
} $this->fetchContent();
}
foreach ($this->html->find('#vulnslisttable .srrowns') as $i => $tr) { foreach ($this->html->find('#vulnslisttable .srrowns') as $i => $tr) {
// There are some optional vulnerability types, which will be // There are some optional vulnerability types, which will be
// added to the categories as well as the CWE number -- which is // added to the categories as well as the CWE number -- which is
// always given. // always given.
$categories = array($this->vendor); $categories = [$this->vendor];
$enclosures = array(); $enclosures = [];
$cwe = $tr->find('td', 2)->find('a', 0); $cwe = $tr->find('td', 2)->find('a', 0);
if ($cwe != null) { if ($cwe != null) {
$cwe = $cwe->innertext; $cwe = $cwe->innertext;
$categories[] = 'CWE-' . $cwe; $categories[] = 'CWE-' . $cwe;
$enclosures[] = 'https://cwe.mitre.org/data/definitions/' . $cwe . '.html'; $enclosures[] = 'https://cwe.mitre.org/data/definitions/' . $cwe . '.html';
} }
$c = $tr->find('td', 4)->innertext; $c = $tr->find('td', 4)->innertext;
if (trim($c) != '') { if (trim($c) != '') {
$categories[] = $c; $categories[] = $c;
} }
if ($this->product != '') { if ($this->product != '') {
$categories[] = $this->product; $categories[] = $this->product;
} }
// The CVE number itself // The CVE number itself
$title = $tr->find('td', 1)->find('a', 0)->innertext; $title = $tr->find('td', 1)->find('a', 0)->innertext;
$this->items[] = array( $this->items[] = [
'uri' => $tr->find('td', 1)->find('a', 0)->href, 'uri' => $tr->find('td', 1)->find('a', 0)->href,
'title' => $title, 'title' => $title,
'timestamp' => $tr->find('td', 5)->innertext, 'timestamp' => $tr->find('td', 5)->innertext,
'content' => $tr->next_sibling()->innertext, 'content' => $tr->next_sibling()->innertext,
'categories' => $categories, 'categories' => $categories,
'enclosures' => $enclosures, 'enclosures' => $enclosures,
'uid' => $tr->find('td', 1)->find('a', 0)->innertext, 'uid' => $tr->find('td', 1)->find('a', 0)->innertext,
); ];
// We only want to fetch the latest 10 CVEs // We only want to fetch the latest 10 CVEs
if (count($this->items) >= 10) { if (count($this->items) >= 10) {
break; break;
} }
} }
} }
} }

View file

@ -1,134 +1,138 @@
<?php <?php
class CachetBridge extends BridgeAbstract { class CachetBridge extends BridgeAbstract
const NAME = 'Cachet Bridge'; {
const URI = 'https://cachethq.io/'; const NAME = 'Cachet Bridge';
const DESCRIPTION = 'Returns status updates from any Cachet installation'; const URI = 'https://cachethq.io/';
const MAINTAINER = 'klimplant'; const DESCRIPTION = 'Returns status updates from any Cachet installation';
const PARAMETERS = array( const MAINTAINER = 'klimplant';
array( const PARAMETERS = [
'host' => array( [
'name' => 'Cachet installation', 'host' => [
'type' => 'text', 'name' => 'Cachet installation',
'required' => true, 'type' => 'text',
'title' => 'The URL of the Cachet installation', 'required' => true,
'exampleValue' => 'https://demo.cachethq.io/', 'title' => 'The URL of the Cachet installation',
), 'additional_info' => array( 'exampleValue' => 'https://demo.cachethq.io/',
'name' => 'Additional Timestamps', ], 'additional_info' => [
'type' => 'checkbox', 'name' => 'Additional Timestamps',
'title' => 'Whether to include the given timestamps' 'type' => 'checkbox',
) 'title' => 'Whether to include the given timestamps'
) ]
); ]
const CACHE_TIMEOUT = 300; ];
const CACHE_TIMEOUT = 300;
private $componentCache = array(); private $componentCache = [];
public function getURI() { public function getURI()
return $this->getInput('host') === null ? 'https://cachethq.io/' : $this->getInput('host'); {
} return $this->getInput('host') === null ? 'https://cachethq.io/' : $this->getInput('host');
}
/** /**
* Validates the ping request to the cache API * Validates the ping request to the cache API
* *
* @param string $ping * @param string $ping
* @return boolean * @return boolean
*/ */
private function validatePing($ping) { private function validatePing($ping)
$ping = json_decode($ping); {
if ($ping === null) { $ping = json_decode($ping);
return false; if ($ping === null) {
} return false;
return $ping->data === 'Pong!'; }
} return $ping->data === 'Pong!';
}
/** /**
* Returns the component name of a cachat component * Returns the component name of a cachat component
* *
* @param integer $id * @param integer $id
* @return string * @return string
*/ */
private function getComponentName($id) { private function getComponentName($id)
if ($id === 0) { {
return ''; if ($id === 0) {
} return '';
if (array_key_exists($id, $this->componentCache)) { }
return $this->componentCache[$id]; if (array_key_exists($id, $this->componentCache)) {
} return $this->componentCache[$id];
}
$component = getContents($this->getURI() . '/api/v1/components/' . $id); $component = getContents($this->getURI() . '/api/v1/components/' . $id);
$component = json_decode($component); $component = json_decode($component);
if ($component === null) { if ($component === null) {
return ''; return '';
} }
return $component->data->name; return $component->data->name;
} }
public function collectData() { public function collectData()
$ping = getContents(urljoin($this->getURI(), '/api/v1/ping')); {
if (!$this->validatePing($ping)) { $ping = getContents(urljoin($this->getURI(), '/api/v1/ping'));
returnClientError('Provided URI is invalid!'); if (!$this->validatePing($ping)) {
} returnClientError('Provided URI is invalid!');
}
$url = urljoin($this->getURI(), '/api/v1/incidents?sort=id&order=desc'); $url = urljoin($this->getURI(), '/api/v1/incidents?sort=id&order=desc');
$incidents = getContents($url); $incidents = getContents($url);
$incidents = json_decode($incidents); $incidents = json_decode($incidents);
if ($incidents === null) { if ($incidents === null) {
returnClientError('/api/v1/incidents returned no valid json'); returnClientError('/api/v1/incidents returned no valid json');
} }
usort($incidents->data, function ($a, $b) { usort($incidents->data, function ($a, $b) {
$timeA = strtotime($a->updated_at); $timeA = strtotime($a->updated_at);
$timeB = strtotime($b->updated_at); $timeB = strtotime($b->updated_at);
return $timeA > $timeB ? -1 : 1; return $timeA > $timeB ? -1 : 1;
}); });
foreach ($incidents->data as $incident) { foreach ($incidents->data as $incident) {
if (isset($incident->permalink)) {
$permalink = $incident->permalink;
} else {
$permalink = urljoin($this->getURI(), '/incident/' . $incident->id);
}
if (isset($incident->permalink)) { $title = $incident->human_status . ': ' . $incident->name;
$permalink = $incident->permalink; $message = '';
} else { if ($this->getInput('additional_info')) {
$permalink = urljoin($this->getURI(), '/incident/' . $incident->id); if (isset($incident->occurred_at)) {
} $message .= 'Occurred at: ' . $incident->occurred_at . "\r\n";
}
if (isset($incident->scheduled_at)) {
$message .= 'Scheduled at: ' . $incident->scheduled_at . "\r\n";
}
if (isset($incident->created_at)) {
$message .= 'Created at: ' . $incident->created_at . "\r\n";
}
if (isset($incident->updated_at)) {
$message .= 'Updated at: ' . $incident->updated_at . "\r\n\r\n";
}
}
$title = $incident->human_status . ': ' . $incident->name; $message .= $incident->message;
$message = ''; $content = nl2br($message);
if ($this->getInput('additional_info')) { $componentName = $this->getComponentName($incident->component_id);
if (isset($incident->occurred_at)) { $uidOrig = $permalink . $incident->created_at;
$message .= 'Occurred at: ' . $incident->occurred_at . "\r\n"; $uid = hash('sha512', $uidOrig);
} $timestamp = strtotime($incident->created_at);
if (isset($incident->scheduled_at)) { $categories = [];
$message .= 'Scheduled at: ' . $incident->scheduled_at . "\r\n"; $categories[] = $incident->human_status;
} if ($componentName !== '') {
if (isset($incident->created_at)) { $categories[] = $componentName;
$message .= 'Created at: ' . $incident->created_at . "\r\n"; }
}
if (isset($incident->updated_at)) {
$message .= 'Updated at: ' . $incident->updated_at . "\r\n\r\n";
}
}
$message .= $incident->message; $item = [];
$content = nl2br($message); $item['uri'] = $permalink;
$componentName = $this->getComponentName($incident->component_id); $item['title'] = $title;
$uidOrig = $permalink . $incident->created_at; $item['timestamp'] = $timestamp;
$uid = hash('sha512', $uidOrig); $item['content'] = $content;
$timestamp = strtotime($incident->created_at); $item['uid'] = $uid;
$categories = array(); $item['categories'] = $categories;
$categories[] = $incident->human_status;
if ($componentName !== '') {
$categories[] = $componentName;
}
$item = array(); $this->items[] = $item;
$item['uri'] = $permalink; }
$item['title'] = $title; }
$item['timestamp'] = $timestamp;
$item['content'] = $content;
$item['uid'] = $uid;
$item['categories'] = $categories;
$this->items[] = $item;
}
}
} }

View file

@ -1,41 +1,44 @@
<?php <?php
class CarThrottleBridge extends FeedExpander {
const NAME = 'Car Throttle ';
const URI = 'https://www.carthrottle.com';
const DESCRIPTION = 'Get the latest car-related news from Car Throttle.';
const MAINTAINER = 't0stiman';
public function collectData() { class CarThrottleBridge extends FeedExpander
$this->collectExpandableDatas('https://www.carthrottle.com/rss', 10); {
} const NAME = 'Car Throttle ';
const URI = 'https://www.carthrottle.com';
const DESCRIPTION = 'Get the latest car-related news from Car Throttle.';
const MAINTAINER = 't0stiman';
protected function parseItem($feedItem) { public function collectData()
$item = parent::parseItem($feedItem); {
$this->collectExpandableDatas('https://www.carthrottle.com/rss', 10);
}
//fetch page protected function parseItem($feedItem)
$articlePage = getSimpleHTMLDOMCached($feedItem->link) {
or returnServerError('Could not retrieve ' . $feedItem->link); $item = parent::parseItem($feedItem);
$subtitle = $articlePage->find('p.standfirst', 0); //fetch page
$article = $articlePage->find('div.content_field', 0); $articlePage = getSimpleHTMLDOMCached($feedItem->link)
or returnServerError('Could not retrieve ' . $feedItem->link);
$item['content'] = str_get_html($subtitle . $article); $subtitle = $articlePage->find('p.standfirst', 0);
$article = $articlePage->find('div.content_field', 0);
//convert <iframe>s to <a>s. meant for embedded videos. $item['content'] = str_get_html($subtitle . $article);
foreach($item['content']->find('iframe') as $found) {
$iframeUrl = $found->getAttribute('src'); //convert <iframe>s to <a>s. meant for embedded videos.
foreach ($item['content']->find('iframe') as $found) {
$iframeUrl = $found->getAttribute('src');
if ($iframeUrl) { if ($iframeUrl) {
$found->outertext = '<a href="' . $iframeUrl . '">' . $iframeUrl . '</a>'; $found->outertext = '<a href="' . $iframeUrl . '">' . $iframeUrl . '</a>';
} }
} }
//remove scripts from the text //remove scripts from the text
foreach ($item['content']->find('script') as $remove) { foreach ($item['content']->find('script') as $remove) {
$remove->outertext = ''; $remove->outertext = '';
} }
return $item; return $item;
} }
} }

View file

@ -1,118 +1,135 @@
<?php <?php
class CastorusBridge extends BridgeAbstract {
const MAINTAINER = 'logmanoriginal';
const NAME = 'Castorus Bridge';
const URI = 'https://www.castorus.com';
const CACHE_TIMEOUT = 600; // 10min
const DESCRIPTION = 'Returns the latest changes';
const PARAMETERS = array( class CastorusBridge extends BridgeAbstract
'Get latest changes' => array(), {
'Get latest changes via ZIP code' => array( const MAINTAINER = 'logmanoriginal';
'zip' => array( const NAME = 'Castorus Bridge';
'name' => 'ZIP code', const URI = 'https://www.castorus.com';
'type' => 'text', const CACHE_TIMEOUT = 600; // 10min
'required' => true, const DESCRIPTION = 'Returns the latest changes';
'exampleValue' => '7',
'title' => 'Insert ZIP code (complete or partial). e.g: 78125 OR 781 OR 7'
)
),
'Get latest changes via city name' => array(
'city' => array(
'name' => 'City name',
'type' => 'text',
'required' => true,
'exampleValue' => 'Paris',
'title' => 'Insert city name (complete or partial). e.g: Paris OR Par OR P'
)
)
);
// Extracts the title from an actitiy const PARAMETERS = [
private function extractActivityTitle($activity){ 'Get latest changes' => [],
$title = $activity->find('a', 0); 'Get latest changes via ZIP code' => [
'zip' => [
'name' => 'ZIP code',
'type' => 'text',
'required' => true,
'exampleValue' => '7',
'title' => 'Insert ZIP code (complete or partial). e.g: 78125 OR 781 OR 7'
]
],
'Get latest changes via city name' => [
'city' => [
'name' => 'City name',
'type' => 'text',
'required' => true,
'exampleValue' => 'Paris',
'title' => 'Insert city name (complete or partial). e.g: Paris OR Par OR P'
]
]
];
if(!$title) // Extracts the title from an actitiy
returnServerError('Cannot find title!'); private function extractActivityTitle($activity)
{
$title = $activity->find('a', 0);
return trim($title->plaintext); if (!$title) {
} returnServerError('Cannot find title!');
}
// Extracts the url from an actitiy return trim($title->plaintext);
private function extractActivityUrl($activity){ }
$url = $activity->find('a', 0);
if(!$url) // Extracts the url from an actitiy
returnServerError('Cannot find url!'); private function extractActivityUrl($activity)
{
$url = $activity->find('a', 0);
return self::URI . $url->href; if (!$url) {
} returnServerError('Cannot find url!');
}
// Extracts the time from an activity return self::URI . $url->href;
private function extractActivityTime($activity){ }
// Unfortunately the time is part of the parent node,
// so we have to clear all child nodes first
$nodes = $activity->find('*');
if(!$nodes) // Extracts the time from an activity
returnServerError('Cannot find nodes!'); private function extractActivityTime($activity)
{
// Unfortunately the time is part of the parent node,
// so we have to clear all child nodes first
$nodes = $activity->find('*');
foreach($nodes as $node) { if (!$nodes) {
$node->outertext = ''; returnServerError('Cannot find nodes!');
} }
return strtotime($activity->innertext); foreach ($nodes as $node) {
} $node->outertext = '';
}
// Extracts the price change return strtotime($activity->innertext);
private function extractActivityPrice($activity){ }
$price = $activity->find('span', 1);
if(!$price) // Extracts the price change
returnServerError('Cannot find price!'); private function extractActivityPrice($activity)
{
$price = $activity->find('span', 1);
return $price->innertext; if (!$price) {
} returnServerError('Cannot find price!');
}
public function collectData(){ return $price->innertext;
$zip_filter = trim($this->getInput('zip')); }
$city_filter = trim($this->getInput('city'));
$html = getSimpleHTMLDOM(self::URI); public function collectData()
{
$zip_filter = trim($this->getInput('zip'));
$city_filter = trim($this->getInput('city'));
if(!$html) $html = getSimpleHTMLDOM(self::URI);
returnServerError('Could not load data from ' . self::URI . '!');
$activities = $html->find('div#activite > li'); if (!$html) {
returnServerError('Could not load data from ' . self::URI . '!');
}
if(!$activities) $activities = $html->find('div#activite > li');
returnServerError('Failed to find activities!');
foreach($activities as $activity) { if (!$activities) {
$item = array(); returnServerError('Failed to find activities!');
}
$item['title'] = $this->extractActivityTitle($activity); foreach ($activities as $activity) {
$item['uri'] = $this->extractActivityUrl($activity); $item = [];
$item['timestamp'] = $this->extractActivityTime($activity);
$item['content'] = '<a href="'
. $item['uri']
. '">'
. $item['title']
. '</a><br><p>'
. $this->extractActivityPrice($activity)
. '</p>';
if(isset($zip_filter) $item['title'] = $this->extractActivityTitle($activity);
&& !(substr($item['title'], 0, strlen($zip_filter)) === $zip_filter)) { $item['uri'] = $this->extractActivityUrl($activity);
continue; // Skip this item $item['timestamp'] = $this->extractActivityTime($activity);
} $item['content'] = '<a href="'
. $item['uri']
. '">'
. $item['title']
. '</a><br><p>'
. $this->extractActivityPrice($activity)
. '</p>';
if(isset($city_filter) if (
&& !(substr($item['title'], strpos($item['title'], ' ') + 1, strlen($city_filter)) === $city_filter)) { isset($zip_filter)
continue; // Skip this item && !(substr($item['title'], 0, strlen($zip_filter)) === $zip_filter)
} ) {
continue; // Skip this item
}
$this->items[] = $item; if (
} isset($city_filter)
} && !(substr($item['title'], strpos($item['title'], ' ') + 1, strlen($city_filter)) === $city_filter)
) {
continue; // Skip this item
}
$this->items[] = $item;
}
}
} }

View file

@ -1,60 +1,62 @@
<?php <?php
class CdactionBridge extends BridgeAbstract { class CdactionBridge extends BridgeAbstract
const NAME = 'CD-ACTION bridge'; {
const URI = 'https://cdaction.pl'; const NAME = 'CD-ACTION bridge';
const DESCRIPTION = 'Fetches the latest posts from given category.'; const URI = 'https://cdaction.pl';
const MAINTAINER = 'tomaszkane'; const DESCRIPTION = 'Fetches the latest posts from given category.';
const PARAMETERS = array( array( const MAINTAINER = 'tomaszkane';
'category' => array( const PARAMETERS = [ [
'name' => 'Kategoria', 'category' => [
'type' => 'list', 'name' => 'Kategoria',
'values' => array( 'type' => 'list',
'Najnowsze (wszystkie)' => 'najnowsze', 'values' => [
'Newsy' => 'newsy', 'Najnowsze (wszystkie)' => 'najnowsze',
'Recenzje' => 'recenzje', 'Newsy' => 'newsy',
'Teksty' => array( 'Recenzje' => 'recenzje',
'Publicystyka' => 'publicystyka', 'Teksty' => [
'Zapowiedzi' => 'zapowiedzi', 'Publicystyka' => 'publicystyka',
'Już graliśmy' => 'juz-gralismy', 'Zapowiedzi' => 'zapowiedzi',
'Poradniki' => 'poradniki', 'Już graliśmy' => 'juz-gralismy',
), 'Poradniki' => 'poradniki',
'Kultura' => 'kultura', ],
'Wideo' => 'wideo', 'Kultura' => 'kultura',
'Czasopismo' => 'czasopismo', 'Wideo' => 'wideo',
'Technologie' => array( 'Czasopismo' => 'czasopismo',
'Artykuły' => 'artykuly', 'Technologie' => [
'Testy' => 'testy', 'Artykuły' => 'artykuly',
), 'Testy' => 'testy',
'Na luzie' => array( ],
'Konkursy' => 'konkursy', 'Na luzie' => [
'Nadgodziny' => 'nadgodziny', 'Konkursy' => 'konkursy',
) 'Nadgodziny' => 'nadgodziny',
) ]
)) ]
); ]]
];
public function collectData() { public function collectData()
$html = getSimpleHTMLDOM($this->getURI() . '/' . $this->getInput('category')); {
$html = getSimpleHTMLDOM($this->getURI() . '/' . $this->getInput('category'));
$newsJson = $html->find('script#__NEXT_DATA__', 0)->innertext; $newsJson = $html->find('script#__NEXT_DATA__', 0)->innertext;
if (!$newsJson = json_decode($newsJson)) { if (!$newsJson = json_decode($newsJson)) {
return; return;
} }
$queriesIndex = $this->getInput('category') === 'najnowsze' ? 0 : 1; $queriesIndex = $this->getInput('category') === 'najnowsze' ? 0 : 1;
foreach ($newsJson->props->pageProps->dehydratedState->queries[$queriesIndex]->state->data->results as $news) { foreach ($newsJson->props->pageProps->dehydratedState->queries[$queriesIndex]->state->data->results as $news) {
$item = array(); $item = [];
$item['uri'] = $this->getURI() . '/' . $news->category->slug . '/' . $news->slug; $item['uri'] = $this->getURI() . '/' . $news->category->slug . '/' . $news->slug;
$item['title'] = $news->title; $item['title'] = $news->title;
$item['timestamp'] = $news->publishedAt; $item['timestamp'] = $news->publishedAt;
$item['author'] = $news->editor->fullName; $item['author'] = $news->editor->fullName;
$item['content'] = $news->lead; $item['content'] = $news->lead;
$item['enclosures'][] = $news->bannerUrl; $item['enclosures'][] = $news->bannerUrl;
$item['categories'] = array_column($news->tags, 'name'); $item['categories'] = array_column($news->tags, 'name');
$item['uid'] = $news->id; $item['uid'] = $news->id;
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }

View file

@ -1,83 +1,88 @@
<?php <?php
class CeskaTelevizeBridge extends BridgeAbstract { class CeskaTelevizeBridge extends BridgeAbstract
{
const NAME = 'Česká televize Bridge';
const URI = 'https://www.ceskatelevize.cz';
const CACHE_TIMEOUT = 3600;
const DESCRIPTION = 'Return newest videos';
const MAINTAINER = 'kolarcz';
const NAME = 'Česká televize Bridge'; const PARAMETERS = [
const URI = 'https://www.ceskatelevize.cz'; [
const CACHE_TIMEOUT = 3600; 'url' => [
const DESCRIPTION = 'Return newest videos'; 'name' => 'url to the show',
const MAINTAINER = 'kolarcz'; 'required' => true,
'exampleValue' => 'https://www.ceskatelevize.cz/porady/1097181328-udalosti/'
]
]
];
const PARAMETERS = array( private function fixChars($text)
array( {
'url' => array( return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
'name' => 'url to the show', }
'required' => true,
'exampleValue' => 'https://www.ceskatelevize.cz/porady/1097181328-udalosti/'
)
)
);
private function fixChars($text) { private function getUploadTimeFromString($string)
return html_entity_decode($text, ENT_QUOTES, 'UTF-8'); {
} if (strpos($string, 'dnes') !== false) {
return strtotime('today');
} elseif (strpos($string, 'včera') !== false) {
return strtotime('yesterday');
} elseif (!preg_match('/(\d+).\s(\d+).(\s(\d+))?/', $string, $match)) {
returnServerError('Could not get date from Česká televize string');
}
private function getUploadTimeFromString($string) { $date = sprintf('%04d-%02d-%02d', isset($match[3]) ? $match[3] : date('Y'), $match[2], $match[1]);
if (strpos($string, 'dnes') !== false) { return strtotime($date);
return strtotime('today'); }
} elseif (strpos($string, 'včera') !== false) {
return strtotime('yesterday');
} elseif (!preg_match('/(\d+).\s(\d+).(\s(\d+))?/', $string, $match)) {
returnServerError('Could not get date from Česká televize string');
}
$date = sprintf('%04d-%02d-%02d', isset($match[3]) ? $match[3] : date('Y'), $match[2], $match[1]); public function collectData()
return strtotime($date); {
} $url = $this->getInput('url');
public function collectData() { $validUrl = '/^(https:\/\/www\.ceskatelevize\.cz\/porady\/\d+-[a-z0-9-]+\/)(bonus\/)?$/';
$url = $this->getInput('url'); if (!preg_match($validUrl, $url, $match)) {
returnServerError('Invalid url');
}
$validUrl = '/^(https:\/\/www\.ceskatelevize\.cz\/porady\/\d+-[a-z0-9-]+\/)(bonus\/)?$/'; $category = isset($match[4]) ? $match[4] : 'nove';
if (!preg_match($validUrl, $url, $match)) { $fixedUrl = "{$match[1]}dily/{$category}/";
returnServerError('Invalid url');
}
$category = isset($match[4]) ? $match[4] : 'nove'; $html = getSimpleHTMLDOM($fixedUrl);
$fixedUrl = "{$match[1]}dily/{$category}/";
$html = getSimpleHTMLDOM($fixedUrl); $this->feedUri = $fixedUrl;
$this->feedName = str_replace('Přehled dílů — ', '', $this->fixChars($html->find('title', 0)->plaintext));
if ($category !== 'nove') {
$this->feedName .= " ({$category})";
}
$this->feedUri = $fixedUrl; foreach ($html->find('#episodeListSection a[data-testid=next-link]') as $element) {
$this->feedName = str_replace('Přehled dílů — ', '', $this->fixChars($html->find('title', 0)->plaintext)); $itemTitle = $element->find('h3', 0);
if ($category !== 'nove') { $itemContent = $element->find('div[class^=content-]', 0);
$this->feedName .= " ({$category})"; $itemDate = $element->find('div[class^=playTime-] span', 0);
} $itemThumbnail = $element->find('img', 0);
$itemUri = self::URI . $element->getAttribute('href');
foreach ($html->find('#episodeListSection a[data-testid=next-link]') as $element) { $item = [
$itemTitle = $element->find('h3', 0); 'title' => $this->fixChars($itemTitle->plaintext),
$itemContent = $element->find('div[class^=content-]', 0); 'uri' => $itemUri,
$itemDate = $element->find('div[class^=playTime-] span', 0); 'content' => '<img src="' . $itemThumbnail->getAttribute('src') . '" /><br />'
$itemThumbnail = $element->find('img', 0); . $this->fixChars($itemContent->plaintext),
$itemUri = self::URI . $element->getAttribute('href'); 'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext)
];
$item = array( $this->items[] = $item;
'title' => $this->fixChars($itemTitle->plaintext), }
'uri' => $itemUri, }
'content' => '<img src="' . $itemThumbnail->getAttribute('src') . '" /><br />'
. $this->fixChars($itemContent->plaintext),
'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext)
);
$this->items[] = $item; public function getURI()
} {
} return isset($this->feedUri) ? $this->feedUri : parent::getURI();
}
public function getURI() { public function getName()
return isset($this->feedUri) ? $this->feedUri : parent::getURI(); {
} return isset($this->feedName) ? $this->feedName : parent::getName();
}
public function getName() {
return isset($this->feedName) ? $this->feedName : parent::getName();
}
} }

View file

@ -1,346 +1,359 @@
<?php <?php
class CodebergBridge extends BridgeAbstract {
const NAME = 'Codeberg Bridge';
const URI = 'https://codeberg.org/';
const DESCRIPTION = 'Returns commits, issues, pull requests or releases for a repository.';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(
'Commits' => array(
'branch' => array(
'name' => 'branch',
'type' => 'text',
'exampleValue' => 'main',
'required' => false,
'title' => 'Optional, main branch is used by default.',
),
),
'Issues' => array(),
'Issue Comments' => array(
'issueId' => array(
'name' => 'Issue ID',
'type' => 'text',
'required' => true,
'exampleValue' => '513',
)
),
'Pull Requests' => array(),
'Releases' => array(),
'global' => array(
'username' => array(
'name' => 'Username',
'type' => 'text',
'exampleValue' => 'Codeberg',
'title' => 'Username of account that the repository belongs to.',
'required' => true,
),
'repo' => array(
'name' => 'Repository',
'type' => 'text',
'exampleValue' => 'Community',
'required' => true,
)
)
);
const CACHE_TIMEOUT = 1800; class CodebergBridge extends BridgeAbstract
{
const NAME = 'Codeberg Bridge';
const URI = 'https://codeberg.org/';
const DESCRIPTION = 'Returns commits, issues, pull requests or releases for a repository.';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [
'Commits' => [
'branch' => [
'name' => 'branch',
'type' => 'text',
'exampleValue' => 'main',
'required' => false,
'title' => 'Optional, main branch is used by default.',
],
],
'Issues' => [],
'Issue Comments' => [
'issueId' => [
'name' => 'Issue ID',
'type' => 'text',
'required' => true,
'exampleValue' => '513',
]
],
'Pull Requests' => [],
'Releases' => [],
'global' => [
'username' => [
'name' => 'Username',
'type' => 'text',
'exampleValue' => 'Codeberg',
'title' => 'Username of account that the repository belongs to.',
'required' => true,
],
'repo' => [
'name' => 'Repository',
'type' => 'text',
'exampleValue' => 'Community',
'required' => true,
]
]
];
const TEST_DETECT_PARAMETERS = array( const CACHE_TIMEOUT = 1800;
'https://codeberg.org/Codeberg/Community/issues/507' => array(
'context' => 'Issue Comments', 'username' => 'Codeberg', 'repo' => 'Community', 'issueId' => '507'
),
'https://codeberg.org/Codeberg/Community/issues' => array(
'context' => 'Issues', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/pulls' => array(
'context' => 'Pull Requests', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/releases' => array(
'context' => 'Releases', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/commits/branch/master' => array(
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community', 'branch' => 'master'
),
'https://codeberg.org/Codeberg/Community/commits' => array(
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community'
)
);
private $defaultBranch = 'main'; const TEST_DETECT_PARAMETERS = [
private $issueTitle = ''; 'https://codeberg.org/Codeberg/Community/issues/507' => [
'context' => 'Issue Comments', 'username' => 'Codeberg', 'repo' => 'Community', 'issueId' => '507'
],
'https://codeberg.org/Codeberg/Community/issues' => [
'context' => 'Issues', 'username' => 'Codeberg', 'repo' => 'Community'
],
'https://codeberg.org/Codeberg/Community/pulls' => [
'context' => 'Pull Requests', 'username' => 'Codeberg', 'repo' => 'Community'
],
'https://codeberg.org/Codeberg/Community/releases' => [
'context' => 'Releases', 'username' => 'Codeberg', 'repo' => 'Community'
],
'https://codeberg.org/Codeberg/Community/commits/branch/master' => [
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community', 'branch' => 'master'
],
'https://codeberg.org/Codeberg/Community/commits' => [
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community'
]
];
private $urlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)(?:\/commits\/branch\/([\w]+))?/'; private $defaultBranch = 'main';
private $issuesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues/'; private $issueTitle = '';
private $pullsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/pulls/';
private $releasesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/releases/';
private $issueCommentsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues\/([0-9]+)/';
public function detectParameters($url) { private $urlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)(?:\/commits\/branch\/([\w]+))?/';
$params = array(); private $issuesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues/';
private $pullsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/pulls/';
private $releasesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/releases/';
private $issueCommentsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues\/([0-9]+)/';
// Issue Comments public function detectParameters($url)
if(preg_match($this->issueCommentsUrlRegex, $url, $matches)) { {
$params['context'] = 'Issue Comments'; $params = [];
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
$params['issueId'] = $matches[3];
return $params; // Issue Comments
} if (preg_match($this->issueCommentsUrlRegex, $url, $matches)) {
$params['context'] = 'Issue Comments';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
$params['issueId'] = $matches[3];
// Issues return $params;
if(preg_match($this->issuesUrlRegex, $url, $matches)) { }
$params['context'] = 'Issues';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params; // Issues
} if (preg_match($this->issuesUrlRegex, $url, $matches)) {
$params['context'] = 'Issues';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
// Pull Requests return $params;
if(preg_match($this->pullsUrlRegex, $url, $matches)) { }
$params['context'] = 'Pull Requests';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params; // Pull Requests
} if (preg_match($this->pullsUrlRegex, $url, $matches)) {
$params['context'] = 'Pull Requests';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
// Releases return $params;
if(preg_match($this->releasesUrlRegex, $url, $matches)) { }
$params['context'] = 'Releases';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params; // Releases
} if (preg_match($this->releasesUrlRegex, $url, $matches)) {
$params['context'] = 'Releases';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
// Commits return $params;
if(preg_match($this->urlRegex, $url, $matches)) { }
$params['context'] = 'Commits';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
if (isset($matches[3])) { // Commits
$params['branch'] = $matches[3]; if (preg_match($this->urlRegex, $url, $matches)) {
} $params['context'] = 'Commits';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params; if (isset($matches[3])) {
} $params['branch'] = $matches[3];
}
return null; return $params;
} }
public function collectData() { return null;
$html = getSimpleHTMLDOM($this->getURI()); }
$html = defaultLinkTo($html, $this->getURI()); public function collectData()
{
$html = getSimpleHTMLDOM($this->getURI());
switch($this->queriedContext) { $html = defaultLinkTo($html, $this->getURI());
case 'Commits':
$this->extractCommits($html);
break;
case 'Issues':
$this->extractIssues($html);
break;
case 'Issue Comments':
$this->extractIssueComments($html);
break;
case 'Pull Requests':
$this->extractPulls($html);
break;
case 'Releases':
$this->extractReleases($html);
break;
default:
returnClientError('Invalid context: ' . $this->queriedContext);
}
}
public function getName() { switch ($this->queriedContext) {
switch($this->queriedContext) { case 'Commits':
case 'Commits': $this->extractCommits($html);
if ($this->getBranch() === $this->defaultBranch) { break;
return $this->getRepo() . ' Commits'; case 'Issues':
} $this->extractIssues($html);
break;
case 'Issue Comments':
$this->extractIssueComments($html);
break;
case 'Pull Requests':
$this->extractPulls($html);
break;
case 'Releases':
$this->extractReleases($html);
break;
default:
returnClientError('Invalid context: ' . $this->queriedContext);
}
}
return $this->getRepo() . ' Commits (' . $this->getBranch() . ' branch) - ' . self::NAME; public function getName()
case 'Issues': {
return $this->getRepo() . ' Issues - ' . self::NAME; switch ($this->queriedContext) {
case 'Issue Comments': case 'Commits':
return $this->issueTitle . ' - Issue Comments - ' . self::NAME; if ($this->getBranch() === $this->defaultBranch) {
case 'Pull Requests': return $this->getRepo() . ' Commits';
return $this->getRepo() . ' Pull Requests - ' . self::NAME; }
case 'Releases':
return $this->getRepo() . ' Releases - ' . self::NAME;
default:
return parent::getName();
}
}
public function getURI() { return $this->getRepo() . ' Commits (' . $this->getBranch() . ' branch) - ' . self::NAME;
switch($this->queriedContext) { case 'Issues':
case 'Commits': return $this->getRepo() . ' Issues - ' . self::NAME;
return self::URI . $this->getRepo() . '/commits/branch/' . $this->getBranch(); case 'Issue Comments':
case 'Issues': return $this->issueTitle . ' - Issue Comments - ' . self::NAME;
return self::URI . $this->getRepo() . '/issues/'; case 'Pull Requests':
case 'Issue Comments': return $this->getRepo() . ' Pull Requests - ' . self::NAME;
return self::URI . $this->getRepo() . '/issues/' . $this->getInput('issueId'); case 'Releases':
case 'Pull Requests': return $this->getRepo() . ' Releases - ' . self::NAME;
return self::URI . $this->getRepo() . '/pulls'; default:
case 'Releases': return parent::getName();
return self::URI . $this->getRepo() . '/releases'; }
default: }
return parent::getURI();
}
}
private function getBranch() { public function getURI()
if ($this->getInput('branch')) { {
return $this->getInput('branch'); switch ($this->queriedContext) {
} case 'Commits':
return self::URI . $this->getRepo() . '/commits/branch/' . $this->getBranch();
case 'Issues':
return self::URI . $this->getRepo() . '/issues/';
case 'Issue Comments':
return self::URI . $this->getRepo() . '/issues/' . $this->getInput('issueId');
case 'Pull Requests':
return self::URI . $this->getRepo() . '/pulls';
case 'Releases':
return self::URI . $this->getRepo() . '/releases';
default:
return parent::getURI();
}
}
return $this->defaultBranch; private function getBranch()
} {
if ($this->getInput('branch')) {
return $this->getInput('branch');
}
private function getRepo() { return $this->defaultBranch;
return $this->getInput('username') . '/' . $this->getInput('repo'); }
}
/** private function getRepo()
* Extract commits {
*/ return $this->getInput('username') . '/' . $this->getInput('repo');
private function extractCommits($html) { }
$table = $html->find('table#commits-table', 0);
$tbody = $table->find('tbody.commit-list', 0);
foreach ($tbody->find('tr') as $tr) { /**
$item = array(); * Extract commits
*/
private function extractCommits($html)
{
$table = $html->find('table#commits-table', 0);
$tbody = $table->find('tbody.commit-list', 0);
$message = $tr->find('td.message', 0); foreach ($tbody->find('tr') as $tr) {
$item = [];
$item['title'] = $message->find('span.message-wrapper', 0)->plaintext; $message = $tr->find('td.message', 0);
$item['uri'] = $tr->find('td.sha', 0)->find('a', 0)->href;
$item['author'] = $tr->find('td.author', 0)->plaintext;
$item['timestamp'] = $tr->find('td', 3)->find('span', 0)->title;
if ($message->find('pre.commit-body', 0)) { $item['title'] = $message->find('span.message-wrapper', 0)->plaintext;
$message->find('pre.commit-body', 0)->style = ''; $item['uri'] = $tr->find('td.sha', 0)->find('a', 0)->href;
$item['author'] = $tr->find('td.author', 0)->plaintext;
$item['timestamp'] = $tr->find('td', 3)->find('span', 0)->title;
$item['content'] = $message->find('pre.commit-body', 0); if ($message->find('pre.commit-body', 0)) {
} else { $message->find('pre.commit-body', 0)->style = '';
$item['content'] = '<blockquote>' . $item['title'] . '</blockquote>';
}
$this->items[] = $item; $item['content'] = $message->find('pre.commit-body', 0);
} } else {
} $item['content'] = '<blockquote>' . $item['title'] . '</blockquote>';
}
/** $this->items[] = $item;
* Extract issues }
*/ }
private function extractIssues($html) {
$div = $html->find('div.issue.list', 0);
foreach ($div->find('li.item') as $li) { /**
$item = array(); * Extract issues
*/
private function extractIssues($html)
{
$div = $html->find('div.issue.list', 0);
$number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext); foreach ($div->find('li.item') as $li) {
$item = [];
$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')'; $number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext);
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('span.time-since', 0)->title;
$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
// Fetch issue page $item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$issuePage = getSimpleHTMLDOMCached($item['uri'], 3600); $item['uri'] = $li->find('a.title', 0)->href;
$issuePage = defaultLinkTo($issuePage, self::URI); $item['timestamp'] = $li->find('span.time-since', 0)->title;
$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
$item['content'] = $issuePage->find('div.timeline-item.comment.first', 0)->find('div.render-content.markup', 0); // Fetch issue page
$issuePage = getSimpleHTMLDOMCached($item['uri'], 3600);
$issuePage = defaultLinkTo($issuePage, self::URI);
foreach ($li->find('a.ui.label') as $label) { $item['content'] = $issuePage->find('div.timeline-item.comment.first', 0)->find('div.render-content.markup', 0);
$item['categories'][] = $label->plaintext;
}
$this->items[] = $item; foreach ($li->find('a.ui.label') as $label) {
} $item['categories'][] = $label->plaintext;
} }
/** $this->items[] = $item;
* Extract issue comments }
*/ }
private function extractIssueComments($html) {
$this->issueTitle = $html->find('span#issue-title', 0)->plaintext
. ' (' . $html->find('span.index', 0)->plaintext . ')';
foreach ($html->find('div.timeline-item.comment') as $div) { /**
$item = array(); * Extract issue comments
*/
private function extractIssueComments($html)
{
$this->issueTitle = $html->find('span#issue-title', 0)->plaintext
. ' (' . $html->find('span.index', 0)->plaintext . ')';
if ($div->class === 'timeline-item comment merge box') { foreach ($html->find('div.timeline-item.comment') as $div) {
continue; $item = [];
}
$item['title'] = $this->ellipsisTitle($div->find('div.render-content.markup', 0)->plaintext); if ($div->class === 'timeline-item comment merge box') {
$item['uri'] = $div->find('span.text.grey', 0)->find('a', 1)->href; continue;
$item['content'] = $div->find('div.render-content.markup', 0); }
if ($div->find('div.dropzone-attachments', 0)) { $item['title'] = $this->ellipsisTitle($div->find('div.render-content.markup', 0)->plaintext);
$item['content'] .= $div->find('div.dropzone-attachments', 0); $item['uri'] = $div->find('span.text.grey', 0)->find('a', 1)->href;
} $item['content'] = $div->find('div.render-content.markup', 0);
$item['author'] = $div->find('a.author', 0)->innertext; if ($div->find('div.dropzone-attachments', 0)) {
$item['timestamp'] = $div->find('span.time-since', 0)->title; $item['content'] .= $div->find('div.dropzone-attachments', 0);
}
$this->items[] = $item; $item['author'] = $div->find('a.author', 0)->innertext;
} $item['timestamp'] = $div->find('span.time-since', 0)->title;
}
/** $this->items[] = $item;
* Extract pulls }
*/ }
private function extractPulls($html) {
$div = $html->find('div.issue.list', 0);
foreach ($div->find('li.item') as $li) { /**
$item = array(); * Extract pulls
*/
private function extractPulls($html)
{
$div = $html->find('div.issue.list', 0);
$number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext); foreach ($div->find('li.item') as $li) {
$item = [];
$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')'; $number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext);
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('span.time-since', 0)->title;
$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
// Fetch pull request page $item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$pullRequestPage = getSimpleHTMLDOMCached($item['uri'], 3600); $item['uri'] = $li->find('a.title', 0)->href;
$pullRequestPage = defaultLinkTo($pullRequestPage, self::URI); $item['timestamp'] = $li->find('span.time-since', 0)->title;
$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
$item['content'] = $pullRequestPage->find('ui.timeline', 0)->find('div.render-content.markup', 0); // Fetch pull request page
$pullRequestPage = getSimpleHTMLDOMCached($item['uri'], 3600);
$pullRequestPage = defaultLinkTo($pullRequestPage, self::URI);
foreach ($li->find('a.ui.label') as $label) { $item['content'] = $pullRequestPage->find('ui.timeline', 0)->find('div.render-content.markup', 0);
$item['categories'][] = $label->plaintext;
}
$this->items[] = $item; foreach ($li->find('a.ui.label') as $label) {
} $item['categories'][] = $label->plaintext;
} }
/** $this->items[] = $item;
* Extract releases }
*/ }
private function extractReleases($html) {
$ul = $html->find('ul#release-list', 0);
foreach ($ul->find('li.ui.grid') as $li) { /**
$item = array(); * Extract releases
$item['title'] = $li->find('h4', 0)->plaintext; */
$item['uri'] = $li->find('h4', 0)->find('a', 0)->href; private function extractReleases($html)
{
$ul = $html->find('ul#release-list', 0);
$tag = $this->stripSvg($li->find('span.tag', 0)); foreach ($ul->find('li.ui.grid') as $li) {
$commit = $this->stripSvg($li->find('span.commit', 0)); $item = [];
$downloads = $this->extractDownloads($li->find('details.download', 0)); $item['title'] = $li->find('h4', 0)->plaintext;
$item['uri'] = $li->find('h4', 0)->find('a', 0)->href;
$item['content'] = $li->find('div.markup.desc', 0); $tag = $this->stripSvg($li->find('span.tag', 0));
$item['content'] .= <<<HTML $commit = $this->stripSvg($li->find('span.commit', 0));
$downloads = $this->extractDownloads($li->find('details.download', 0));
$item['content'] = $li->find('div.markup.desc', 0);
$item['content'] .= <<<HTML
<strong>Tag</strong> <strong>Tag</strong>
<p>{$tag}</p> <p>{$tag}</p>
<strong>Commit</strong> <strong>Commit</strong>
@ -348,56 +361,59 @@ class CodebergBridge extends BridgeAbstract {
{$downloads} {$downloads}
HTML; HTML;
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title; $item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
$item['author'] = $li->find('span.author', 0)->find('a', 0)->plaintext; $item['author'] = $li->find('span.author', 0)->find('a', 0)->plaintext;
$this->items[] = $item; $this->items[] = $item;
} }
} }
/** /**
* Extract downloads for a releases * Extract downloads for a releases
*/ */
private function extractDownloads($html, $skipFirst = false) { private function extractDownloads($html, $skipFirst = false)
$downloads = ''; {
$downloads = '';
foreach ($html->find('a') as $index => $a) { foreach ($html->find('a') as $index => $a) {
if ($skipFirst === true && $index === 0) { if ($skipFirst === true && $index === 0) {
continue; continue;
} }
$downloads .= <<<HTML $downloads .= <<<HTML
<a href="{$a->herf}">{$a->plaintext}</a><br> <a href="{$a->herf}">{$a->plaintext}</a><br>
HTML; HTML;
} }
return <<<EOD return <<<EOD
<strong>Downloads</strong> <strong>Downloads</strong>
<p>{$downloads}</p> <p>{$downloads}</p>
EOD; EOD;
} }
/** /**
* Ellipsis title to first 100 characters * Ellipsis title to first 100 characters
*/ */
private function ellipsisTitle($text) { private function ellipsisTitle($text)
$length = 100; {
$length = 100;
if (strlen($text) > $length) { if (strlen($text) > $length) {
$text = explode('<br>', wordwrap($text, $length, '<br>')); $text = explode('<br>', wordwrap($text, $length, '<br>'));
return $text[0] . '...'; return $text[0] . '...';
} }
return $text; return $text;
} }
/** /**
* Strip SVG tag * Strip SVG tag
*/ */
private function stripSvg($html) { private function stripSvg($html)
if ($html->find('svg', 0)) { {
$html->find('svg', 0)->outertext = ''; if ($html->find('svg', 0)) {
} $html->find('svg', 0)->outertext = '';
}
return $html; return $html;
} }
} }

View file

@ -1,83 +1,85 @@
<?php <?php
class CollegeDeFranceBridge extends BridgeAbstract {
const MAINTAINER = 'pit-fgfjiudghdf'; class CollegeDeFranceBridge extends BridgeAbstract
const NAME = 'CollegeDeFrance'; {
const URI = 'https://www.college-de-france.fr/'; const MAINTAINER = 'pit-fgfjiudghdf';
const CACHE_TIMEOUT = 10800; // 3h const NAME = 'CollegeDeFrance';
const DESCRIPTION = 'Returns the latest audio and video from CollegeDeFrance'; const URI = 'https://www.college-de-france.fr/';
const CACHE_TIMEOUT = 10800; // 3h
const DESCRIPTION = 'Returns the latest audio and video from CollegeDeFrance';
public function collectData(){ public function collectData()
$months = array( {
'01' => 'janv.', $months = [
'02' => 'févr.', '01' => 'janv.',
'03' => 'mars', '02' => 'févr.',
'04' => 'avr.', '03' => 'mars',
'05' => 'mai', '04' => 'avr.',
'06' => 'juin', '05' => 'mai',
'07' => 'juil.', '06' => 'juin',
'08' => 'août', '07' => 'juil.',
'09' => 'sept.', '08' => 'août',
'10' => 'oct.', '09' => 'sept.',
'11' => 'nov.', '10' => 'oct.',
'12' => 'déc.' '11' => 'nov.',
); '12' => 'déc.'
];
// The "API" used by the site returns a list of partial HTML in this form // The "API" used by the site returns a list of partial HTML in this form
/* <li> /* <li>
* <a href="/site/thomas-romer/guestlecturer-2016-04-15-14h30.htm" data-target="after"> * <a href="/site/thomas-romer/guestlecturer-2016-04-15-14h30.htm" data-target="after">
* <span class="date"><span class="list-icon list-icon-video"></span> * <span class="date"><span class="list-icon list-icon-video"></span>
* <span class="list-icon list-icon-audio"></span>15 avr. 2016</span> * <span class="list-icon list-icon-audio"></span>15 avr. 2016</span>
* <span class="lecturer">Christopher Hays</span> * <span class="lecturer">Christopher Hays</span>
* <span class='title'>Imagery of Divine Suckling in the Hebrew Bible and the Ancient Near East</span> * <span class='title'>Imagery of Divine Suckling in the Hebrew Bible and the Ancient Near East</span>
* </a> * </a>
* </li> * </li>
*/ */
$html = getSimpleHTMLDOM(self::URI $html = getSimpleHTMLDOM(self::URI
. 'components/search-audiovideo.jsp?fulltext=&siteid=1156951719600&lang=FR&type=all'); . 'components/search-audiovideo.jsp?fulltext=&siteid=1156951719600&lang=FR&type=all');
foreach($html->find('a[data-target]') as $element) { foreach ($html->find('a[data-target]') as $element) {
$item = array(); $item = [];
$item['title'] = $element->find('.title', 0)->plaintext; $item['title'] = $element->find('.title', 0)->plaintext;
// Most relative URLs contains an hour in addition to the date, so let's use it // Most relative URLs contains an hour in addition to the date, so let's use it
// <a href="/site/yann-lecun/course-2016-04-08-11h00.htm" data-target="after"> // <a href="/site/yann-lecun/course-2016-04-08-11h00.htm" data-target="after">
// //
// Sometimes there's an __1, perhaps it signifies an update // Sometimes there's an __1, perhaps it signifies an update
// "/site/patrick-boucheron/seminar-2016-05-03-18h00__1.htm" // "/site/patrick-boucheron/seminar-2016-05-03-18h00__1.htm"
// //
// But unfortunately some don't have any hours info // But unfortunately some don't have any hours info
// <a href="/site/institut-physique/ // <a href="/site/institut-physique/
// The-Mysteries-of-Decoherence-Sebastien-Gleyzes-[Video-3-35].htm" data-target="after"> // The-Mysteries-of-Decoherence-Sebastien-Gleyzes-[Video-3-35].htm" data-target="after">
$timezone = new DateTimeZone('Europe/Paris'); $timezone = new DateTimeZone('Europe/Paris');
// strpos($element->href, '201') will break in 2020 but it'll // strpos($element->href, '201') will break in 2020 but it'll
// probably break prior to then due to site changes anyway // probably break prior to then due to site changes anyway
$d = DateTime::createFromFormat( $d = DateTime::createFromFormat(
'!Y-m-d-H\hi', '!Y-m-d-H\hi',
substr($element->href, strpos($element->href, '201'), 16), substr($element->href, strpos($element->href, '201'), 16),
$timezone $timezone
); );
if(!$d) { if (!$d) {
$d = DateTime::createFromFormat( $d = DateTime::createFromFormat(
'!d m Y', '!d m Y',
trim(str_replace( trim(str_replace(
array_values($months), array_values($months),
array_keys($months), array_keys($months),
$element->find('.date', 0)->plaintext $element->find('.date', 0)->plaintext
)), )),
$timezone $timezone
); );
} }
$item['timestamp'] = $d->format('U'); $item['timestamp'] = $d->format('U');
$item['content'] = $element->find('.lecturer', 0)->innertext $item['content'] = $element->find('.lecturer', 0)->innertext
. ' - ' . ' - '
. $element->find('.title', 0)->innertext; . $element->find('.title', 0)->innertext;
$item['uri'] = self::URI . $element->href; $item['uri'] = self::URI . $element->href;
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }

View file

@ -1,25 +1,28 @@
<?php <?php
class ComboiosDePortugalBridge extends BridgeAbstract {
const NAME = 'CP | Avisos';
const BASE_URI = 'https://www.cp.pt';
const URI = self::BASE_URI . '/passageiros/pt';
const DESCRIPTION = 'Comboios de Portugal | Avisos';
const MAINTAINER = 'somini';
public function collectData() { class ComboiosDePortugalBridge extends BridgeAbstract
# Do not verify SSL certificate (the server doesn't send the intermediate) {
# https://github.com/RSS-Bridge/rss-bridge/issues/2397 const NAME = 'CP | Avisos';
$html = getSimpleHTMLDOM($this->getURI() . '/consultar-horarios/avisos', array(), array( const BASE_URI = 'https://www.cp.pt';
CURLOPT_SSL_VERIFYPEER => 0, const URI = self::BASE_URI . '/passageiros/pt';
)); const DESCRIPTION = 'Comboios de Portugal | Avisos';
const MAINTAINER = 'somini';
foreach($html->find('.warnings-table a') as $element) { public function collectData()
$item = array(); {
# Do not verify SSL certificate (the server doesn't send the intermediate)
# https://github.com/RSS-Bridge/rss-bridge/issues/2397
$html = getSimpleHTMLDOM($this->getURI() . '/consultar-horarios/avisos', [], [
CURLOPT_SSL_VERIFYPEER => 0,
]);
$item['title'] = $element->innertext; foreach ($html->find('.warnings-table a') as $element) {
$item['uri'] = self::BASE_URI . implode('/', array_map('urlencode', explode('/', $element->href))); $item = [];
$this->items[] = $item; $item['title'] = $element->innertext;
} $item['uri'] = self::BASE_URI . implode('/', array_map('urlencode', explode('/', $element->href)));
}
$this->items[] = $item;
}
}
} }

View file

@ -1,64 +1,71 @@
<?php <?php
class ComicsKingdomBridge extends BridgeAbstract {
const MAINTAINER = 'stjohnjohnson'; class ComicsKingdomBridge extends BridgeAbstract
const NAME = 'Comics Kingdom Unofficial RSS'; {
const URI = 'https://comicskingdom.com/'; const MAINTAINER = 'stjohnjohnson';
const CACHE_TIMEOUT = 21600; // 6h const NAME = 'Comics Kingdom Unofficial RSS';
const DESCRIPTION = 'Comics Kingdom Unofficial RSS'; const URI = 'https://comicskingdom.com/';
const PARAMETERS = array( array( const CACHE_TIMEOUT = 21600; // 6h
'comicname' => array( const DESCRIPTION = 'Comics Kingdom Unofficial RSS';
'name' => 'comicname', const PARAMETERS = [ [
'type' => 'text', 'comicname' => [
'exampleValue' => 'mutts', 'name' => 'comicname',
'title' => 'The name of the comic in the URL after https://comicskingdom.com/', 'type' => 'text',
'required' => true 'exampleValue' => 'mutts',
) 'title' => 'The name of the comic in the URL after https://comicskingdom.com/',
)); 'required' => true
]
]];
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM($this->getURI(), array(), array(), true, false); {
$html = getSimpleHTMLDOM($this->getURI(), [], [], true, false);
// Get author from first page // Get author from first page
$author = $html->find('div.author p', 0);; $author = $html->find('div.author p', 0);
;
// Get current date/link // Get current date/link
$link = $html->find('meta[property=og:url]', -1)->content; $link = $html->find('meta[property=og:url]', -1)->content;
for($i = 0; $i < 3; $i++) { for ($i = 0; $i < 3; $i++) {
$item = array(); $item = [];
$page = getSimpleHTMLDOM($link); $page = getSimpleHTMLDOM($link);
$imagelink = $page->find('meta[property=og:image]', 0)->content; $imagelink = $page->find('meta[property=og:image]', 0)->content;
$date = explode('/', $link); $date = explode('/', $link);
$item['id'] = $imagelink; $item['id'] = $imagelink;
$item['uri'] = $link; $item['uri'] = $link;
$item['author'] = $author; $item['author'] = $author;
$item['title'] = 'Comics Kingdom ' . $this->getInput('comicname'); $item['title'] = 'Comics Kingdom ' . $this->getInput('comicname');
$item['timestamp'] = DateTime::createFromFormat('Y-m-d', $date[count($date) - 1])->getTimestamp(); $item['timestamp'] = DateTime::createFromFormat('Y-m-d', $date[count($date) - 1])->getTimestamp();
$item['content'] = '<img src="' . $imagelink . '" />'; $item['content'] = '<img src="' . $imagelink . '" />';
$this->items[] = $item; $this->items[] = $item;
$link = $page->find('div.comic-viewer-inline a', 0)->href; $link = $page->find('div.comic-viewer-inline a', 0)->href;
if (empty($link)) break; // allow bridge to continue if there's less than 3 comics if (empty($link)) {
} break; // allow bridge to continue if there's less than 3 comics
} }
}
}
public function getURI(){ public function getURI()
if(!is_null($this->getInput('comicname'))) { {
return self::URI . urlencode($this->getInput('comicname')); if (!is_null($this->getInput('comicname'))) {
} return self::URI . urlencode($this->getInput('comicname'));
}
return parent::getURI(); return parent::getURI();
} }
public function getName(){ public function getName()
if(!is_null($this->getInput('comicname'))) { {
return $this->getInput('comicname') . ' - Comics Kingdom'; if (!is_null($this->getInput('comicname'))) {
} return $this->getInput('comicname') . ' - Comics Kingdom';
}
return parent::getName(); return parent::getName();
} }
} }

View file

@ -1,26 +1,30 @@
<?php <?php
class CommonDreamsBridge extends FeedExpander {
const MAINTAINER = 'nyutag'; class CommonDreamsBridge extends FeedExpander
const NAME = 'CommonDreams Bridge'; {
const URI = 'https://www.commondreams.org/'; const MAINTAINER = 'nyutag';
const DESCRIPTION = 'Returns the newest articles.'; const NAME = 'CommonDreams Bridge';
const URI = 'https://www.commondreams.org/';
const DESCRIPTION = 'Returns the newest articles.';
public function collectData(){ public function collectData()
$this->collectExpandableDatas('http://www.commondreams.org/rss.xml', 10); {
} $this->collectExpandableDatas('http://www.commondreams.org/rss.xml', 10);
}
protected function parseItem($newsItem){ protected function parseItem($newsItem)
$item = parent::parseItem($newsItem); {
$item['content'] = $this->extractContent($item['uri']); $item = parent::parseItem($newsItem);
return $item; $item['content'] = $this->extractContent($item['uri']);
} return $item;
}
private function extractContent($url){ private function extractContent($url)
$html3 = getSimpleHTMLDOMCached($url); {
$text = $html3->find('div[class=field--type-text-with-summary]', 0)->innertext; $html3 = getSimpleHTMLDOMCached($url);
$html3->clear(); $text = $html3->find('div[class=field--type-text-with-summary]', 0)->innertext;
unset ($html3); $html3->clear();
return $text; unset($html3);
} return $text;
}
} }

View file

@ -1,34 +1,36 @@
<?php <?php
class CopieDoubleBridge extends BridgeAbstract {
const MAINTAINER = 'superbaillot.net'; class CopieDoubleBridge extends BridgeAbstract
const NAME = 'CopieDouble'; {
const URI = 'http://www.copie-double.com/'; const MAINTAINER = 'superbaillot.net';
const CACHE_TIMEOUT = 14400; // 4h const NAME = 'CopieDouble';
const DESCRIPTION = 'CopieDouble'; const URI = 'http://www.copie-double.com/';
const CACHE_TIMEOUT = 14400; // 4h
const DESCRIPTION = 'CopieDouble';
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM(self::URI); {
$html = getSimpleHTMLDOM(self::URI);
$table = $html->find('table table', 2); $table = $html->find('table table', 2);
foreach($table->find('tr') as $element) { foreach ($table->find('tr') as $element) {
$td = $element->find('td', 0); $td = $element->find('td', 0);
if($td->class === 'couleur_1') { if ($td->class === 'couleur_1') {
$item = array(); $item = [];
$title = $td->innertext; $title = $td->innertext;
$pos = strpos($title, '<a'); $pos = strpos($title, '<a');
$title = substr($title, 0, $pos); $title = substr($title, 0, $pos);
$item['title'] = $title; $item['title'] = $title;
} elseif(strpos($element->innertext, '/images/suivant.gif') === false) { } elseif (strpos($element->innertext, '/images/suivant.gif') === false) {
$a = $element->find('a', 0); $a = $element->find('a', 0);
$item['uri'] = self::URI . $a->href; $item['uri'] = self::URI . $a->href;
$content = str_replace('src="/', 'src="/' . self::URI, $element->find('td', 0)->innertext); $content = str_replace('src="/', 'src="/' . self::URI, $element->find('td', 0)->innertext);
$content = str_replace('href="/', 'href="' . self::URI, $content); $content = str_replace('href="/', 'href="' . self::URI, $content);
$item['content'] = $content; $item['content'] = $content;
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }
} }

View file

@ -1,26 +1,29 @@
<?php <?php
class CourrierInternationalBridge extends FeedExpander {
const MAINTAINER = 'teromene'; class CourrierInternationalBridge extends FeedExpander
const NAME = 'Courrier International Bridge'; {
const URI = 'https://www.courrierinternational.com/'; const MAINTAINER = 'teromene';
const CACHE_TIMEOUT = 300; // 5 min const NAME = 'Courrier International Bridge';
const DESCRIPTION = 'Returns the newest articles'; const URI = 'https://www.courrierinternational.com/';
const CACHE_TIMEOUT = 300; // 5 min
const DESCRIPTION = 'Returns the newest articles';
public function collectData(){ public function collectData()
$this->collectExpandableDatas(static::URI . 'feed/all/rss.xml', 20); {
} $this->collectExpandableDatas(static::URI . 'feed/all/rss.xml', 20);
}
protected function parseItem($feedItem){ protected function parseItem($feedItem)
$item = parent::parseItem($feedItem); {
$item = parent::parseItem($feedItem);
$articlePage = getSimpleHTMLDOMCached($feedItem->link); $articlePage = getSimpleHTMLDOMCached($feedItem->link);
$content = $articlePage->find('.article-text, depeche-text', 0); $content = $articlePage->find('.article-text, depeche-text', 0);
if (!$content) { if (!$content) {
return $item; return $item;
} }
$item['content'] = sanitize($content); $item['content'] = sanitize($content);
return $item; return $item;
} }
} }

View file

@ -1,106 +1,110 @@
<?php <?php
class CraigslistBridge extends BridgeAbstract {
const NAME = 'Craigslist Bridge';
const URI = 'https://craigslist.org/';
const DESCRIPTION = 'Returns craigslist search results';
const PARAMETERS = array( array( class CraigslistBridge extends BridgeAbstract
'region' => array( {
'name' => 'Region', const NAME = 'Craigslist Bridge';
'title' => 'The subdomain before craigslist.org in the URL', const URI = 'https://craigslist.org/';
'exampleValue' => 'sfbay', const DESCRIPTION = 'Returns craigslist search results';
'required' => true
),
'search' => array(
'name' => 'Search Query',
'title' => 'Everything in the URL after /search/',
'exampleValue' => 'sya?query=laptop',
'required' => true
),
'limit' => array(
'name' => 'Number of Posts',
'type' => 'number',
'title' => 'The maximum number of posts is 120. Use 0 for unlimited posts.',
'defaultValue' => '25'
)
));
const TEST_DETECT_PARAMETERS = array( const PARAMETERS = [ [
'https://sfbay.craigslist.org/search/sya?query=laptop' => array( 'region' => [
'region' => 'sfbay', 'search' => 'sya?query=laptop' 'name' => 'Region',
), 'title' => 'The subdomain before craigslist.org in the URL',
'https://newyork.craigslist.org/search/sss?query=32gb+flash+drive&bundleDuplicates=1&max_price=20' => array( 'exampleValue' => 'sfbay',
'region' => 'newyork', 'search' => 'sss?query=32gb+flash+drive&bundleDuplicates=1&max_price=20' 'required' => true
), ],
); 'search' => [
'name' => 'Search Query',
'title' => 'Everything in the URL after /search/',
'exampleValue' => 'sya?query=laptop',
'required' => true
],
'limit' => [
'name' => 'Number of Posts',
'type' => 'number',
'title' => 'The maximum number of posts is 120. Use 0 for unlimited posts.',
'defaultValue' => '25'
]
]];
const URL_REGEX = '/^https:\/\/(?<region>\w+).craigslist.org\/search\/(?<search>.+)/'; const TEST_DETECT_PARAMETERS = [
'https://sfbay.craigslist.org/search/sya?query=laptop' => [
'region' => 'sfbay', 'search' => 'sya?query=laptop'
],
'https://newyork.craigslist.org/search/sss?query=32gb+flash+drive&bundleDuplicates=1&max_price=20' => [
'region' => 'newyork', 'search' => 'sss?query=32gb+flash+drive&bundleDuplicates=1&max_price=20'
],
];
public function detectParameters($url) { const URL_REGEX = '/^https:\/\/(?<region>\w+).craigslist.org\/search\/(?<search>.+)/';
if(preg_match(self::URL_REGEX, $url, $matches)) {
$params = array();
$params['region'] = $matches['region'];
$params['search'] = $matches['search'];
return $params;
}
}
public function getURI() { public function detectParameters($url)
if (!is_null($this->getInput('region'))) { {
$domain = 'https://' . $this->getInput('region') . '.craigslist.org/search/'; if (preg_match(self::URL_REGEX, $url, $matches)) {
return urljoin($domain, $this->getInput('search')); $params = [];
} $params['region'] = $matches['region'];
return parent::getURI(); $params['search'] = $matches['search'];
} return $params;
}
}
public function collectData() { public function getURI()
$uri = $this->getURI(); {
$html = getSimpleHTMLDOM($uri); if (!is_null($this->getInput('region'))) {
$domain = 'https://' . $this->getInput('region') . '.craigslist.org/search/';
return urljoin($domain, $this->getInput('search'));
}
return parent::getURI();
}
// Check if no results page is shown (nearby results) public function collectData()
if ($html->find('.displaycountShow', 0)->plaintext == '0') { {
return; $uri = $this->getURI();
} $html = getSimpleHTMLDOM($uri);
// Search for "more from nearby areas" banner in order to skip those results // Check if no results page is shown (nearby results)
$results = $html->find('.result-row, h4.nearby'); if ($html->find('.displaycountShow', 0)->plaintext == '0') {
return;
}
// Limit the number of posts // Search for "more from nearby areas" banner in order to skip those results
if ($this->getInput('limit') > 0) { $results = $html->find('.result-row, h4.nearby');
$results = array_slice($results, 0, $this->getInput('limit'));
}
foreach($results as $post) { // Limit the number of posts
if ($this->getInput('limit') > 0) {
$results = array_slice($results, 0, $this->getInput('limit'));
}
// Skip "nearby results" banner and results foreach ($results as $post) {
// This only appears when searchNearby is not specified // Skip "nearby results" banner and results
if ($post->tag == 'h4') { // This only appears when searchNearby is not specified
break; if ($post->tag == 'h4') {
} break;
}
$item = array(); $item = [];
$heading = $post->find('.result-heading a', 0); $heading = $post->find('.result-heading a', 0);
$item['uri'] = $heading->href; $item['uri'] = $heading->href;
$item['title'] = $heading->plaintext; $item['title'] = $heading->plaintext;
$item['timestamp'] = $post->find('.result-date', 0)->datetime; $item['timestamp'] = $post->find('.result-date', 0)->datetime;
$item['uid'] = $heading->id; $item['uid'] = $heading->id;
$item['content'] = $post->find('.result-price', 0)->plaintext . ' ' $item['content'] = $post->find('.result-price', 0)->plaintext . ' '
// Find the location (local and nearby results if searchNearby=1) // Find the location (local and nearby results if searchNearby=1)
. $post->find('.result-hood, span.nearby', 0)->plaintext; . $post->find('.result-hood, span.nearby', 0)->plaintext;
$images = $post->find('.result-image[data-ids]', 0); $images = $post->find('.result-image[data-ids]', 0);
if (!is_null($images)) { if (!is_null($images)) {
$item['content'] .= '<br>'; $item['content'] .= '<br>';
foreach(explode(',', $images->getAttribute('data-ids')) as $image) { foreach (explode(',', $images->getAttribute('data-ids')) as $image) {
// Remove leading 3: from each image id // Remove leading 3: from each image id
$id = substr($image, 2); $id = substr($image, 2);
$image_uri = 'https://images.craigslist.org/' . $id . '_300x300.jpg'; $image_uri = 'https://images.craigslist.org/' . $id . '_300x300.jpg';
$item['content'] .= '<img src="' . $image_uri . '">'; $item['content'] .= '<img src="' . $image_uri . '">';
$item['enclosures'][] = $image_uri; $item['enclosures'][] = $image_uri;
} }
} }
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }

View file

@ -1,227 +1,233 @@
<?php <?php
class CrewbayBridge extends BridgeAbstract {
const MAINTAINER = 'couraudt';
const NAME = 'Crewbay Bridge';
const URI = 'https://www.crewbay.com';
const DESCRIPTION = 'Returns the newest sailing offers.';
const PARAMETERS = array(
array(
'keyword' => array(
'name' => 'Filter by keyword',
'title' => 'Enter the keyword to filter here'
),
'type' => array(
'name' => 'Type of search',
'title' => 'Choose between finding a boat or a crew',
'type' => 'list',
'values' => array(
'Find a boat' => 'boats',
'Find a crew' => 'crew'
)
),
'status' => array(
'name' => 'Status on the boat',
'title' => 'Choose between recreational or professional classified ads',
'type' => 'list',
'values' => array(
'Recreational' => 'recreational',
'Professional' => 'professional'
)
),
'recreational_position' => array(
'name' => 'Recreational position wanted',
'title' => 'Filter by recreational position you wanted aboard',
'required' => false,
'type' => 'list',
'values' => array(
'' => '',
'Amateur Crew' => 'Amateur Crew',
'Friendship' => 'Friendship',
'Competent Crew' => 'Competent Crew',
'Racing' => 'Racing',
'Voluntary work' => 'Voluntary work',
'Mile building' => 'Mile building'
)
),
'professional_position' => array(
'name' => 'Professional position wanted',
'title' => 'Filter by professional position you wanted aboard',
'required' => false,
'type' => 'list',
'values' => array(
'' => '',
'1st Engineer' => '1st Engineer',
'1st Mate' => '1st Mate',
'Beautician' => 'Beautician',
'Bosun' => 'Bosun',
'Captain' => 'Captain',
'Chef' => 'Chef',
'Steward(ess)' => 'Steward(ess)',
'Deckhand' => 'Deckhand',
'Delivery Crew' => 'Delivery Crew',
'Dive Instructor' => 'Dive Instructor',
'Masseur' => 'Masseur',
'Medical Staff' => 'Medical Staff',
'Nanny' => 'Nanny',
'Navigator' => 'Navigator',
'Racing Crew' => 'Racing Crew',
'Teacher' => 'Teacher',
'Electrical Engineer' => 'Electrical Engineer',
'Fitter' => 'Fitter',
'2nd Engineer' => '2nd Engineer',
'3rd Engineer' => '3rd Engineer',
'Lead Deckhand' => 'Lead Deckhand',
'Security Officer' => 'Security Officer',
'O.O.W' => 'O.O.W',
'1st Officer' => '1st Officer',
'2nd Officer' => '2nd Officer',
'3rd Officer' => '3rd Officer',
'Captain/Engineer' => 'Captain/Engineer',
'Hairdresser' => 'Hairdresser',
'Fitness Trainer' => 'Fitness Trainer',
'Laundry' => 'Laundry',
'Solo Steward/ess' => 'Solo Steward/ess',
'Stew/Deck' => 'Stew/Deck',
'2nd Steward/ess' => '2nd Steward/ess',
'3rd Steward/ess' => '3rd Steward/ess',
'Chief Steward/ess' => 'Chief Steward/ess',
'Head Housekeeper' => 'Head Housekeeper',
'Purser' => 'Purser',
'Cook' => 'Cook',
'Cook/Stew' => 'Cook/Stew',
'2nd Chef' => '2nd Chef',
'Head Chef' => 'Head Chef',
'Administrator' => 'Administrator',
'P.A' => 'P.A',
'Villa staff' => 'Villa staff',
'Housekeeping/Stew' => 'Housekeeping/Stew',
'Stew/Beautician' => 'Stew/Beautician',
'Stew/Masseuse' => 'Stew/Masseuse',
'Manager' => 'Manager',
'Sailing instructor' => 'Sailing instructor'
)
)
)
);
public function collectData() { class CrewbayBridge extends BridgeAbstract
$url = $this->getURI(); {
$html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.'); const MAINTAINER = 'couraudt';
const NAME = 'Crewbay Bridge';
const URI = 'https://www.crewbay.com';
const DESCRIPTION = 'Returns the newest sailing offers.';
const PARAMETERS = [
[
'keyword' => [
'name' => 'Filter by keyword',
'title' => 'Enter the keyword to filter here'
],
'type' => [
'name' => 'Type of search',
'title' => 'Choose between finding a boat or a crew',
'type' => 'list',
'values' => [
'Find a boat' => 'boats',
'Find a crew' => 'crew'
]
],
'status' => [
'name' => 'Status on the boat',
'title' => 'Choose between recreational or professional classified ads',
'type' => 'list',
'values' => [
'Recreational' => 'recreational',
'Professional' => 'professional'
]
],
'recreational_position' => [
'name' => 'Recreational position wanted',
'title' => 'Filter by recreational position you wanted aboard',
'required' => false,
'type' => 'list',
'values' => [
'' => '',
'Amateur Crew' => 'Amateur Crew',
'Friendship' => 'Friendship',
'Competent Crew' => 'Competent Crew',
'Racing' => 'Racing',
'Voluntary work' => 'Voluntary work',
'Mile building' => 'Mile building'
]
],
'professional_position' => [
'name' => 'Professional position wanted',
'title' => 'Filter by professional position you wanted aboard',
'required' => false,
'type' => 'list',
'values' => [
'' => '',
'1st Engineer' => '1st Engineer',
'1st Mate' => '1st Mate',
'Beautician' => 'Beautician',
'Bosun' => 'Bosun',
'Captain' => 'Captain',
'Chef' => 'Chef',
'Steward(ess)' => 'Steward(ess)',
'Deckhand' => 'Deckhand',
'Delivery Crew' => 'Delivery Crew',
'Dive Instructor' => 'Dive Instructor',
'Masseur' => 'Masseur',
'Medical Staff' => 'Medical Staff',
'Nanny' => 'Nanny',
'Navigator' => 'Navigator',
'Racing Crew' => 'Racing Crew',
'Teacher' => 'Teacher',
'Electrical Engineer' => 'Electrical Engineer',
'Fitter' => 'Fitter',
'2nd Engineer' => '2nd Engineer',
'3rd Engineer' => '3rd Engineer',
'Lead Deckhand' => 'Lead Deckhand',
'Security Officer' => 'Security Officer',
'O.O.W' => 'O.O.W',
'1st Officer' => '1st Officer',
'2nd Officer' => '2nd Officer',
'3rd Officer' => '3rd Officer',
'Captain/Engineer' => 'Captain/Engineer',
'Hairdresser' => 'Hairdresser',
'Fitness Trainer' => 'Fitness Trainer',
'Laundry' => 'Laundry',
'Solo Steward/ess' => 'Solo Steward/ess',
'Stew/Deck' => 'Stew/Deck',
'2nd Steward/ess' => '2nd Steward/ess',
'3rd Steward/ess' => '3rd Steward/ess',
'Chief Steward/ess' => 'Chief Steward/ess',
'Head Housekeeper' => 'Head Housekeeper',
'Purser' => 'Purser',
'Cook' => 'Cook',
'Cook/Stew' => 'Cook/Stew',
'2nd Chef' => '2nd Chef',
'Head Chef' => 'Head Chef',
'Administrator' => 'Administrator',
'P.A' => 'P.A',
'Villa staff' => 'Villa staff',
'Housekeeping/Stew' => 'Housekeeping/Stew',
'Stew/Beautician' => 'Stew/Beautician',
'Stew/Masseuse' => 'Stew/Masseuse',
'Manager' => 'Manager',
'Sailing instructor' => 'Sailing instructor'
]
]
]
];
$annonces = $html->find('#SearchResults div.result'); public function collectData()
$limit = 0; {
$url = $this->getURI();
$html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.');
foreach ($annonces as $annonce) { $annonces = $html->find('#SearchResults div.result');
$detail = $annonce->find('.btn--profile', 0); $limit = 0;
$htmlDetail = getSimpleHTMLDOMCached($detail->href);
if (!empty($this->getInput('recreational_position')) || !empty($this->getInput('professional_position'))) { foreach ($annonces as $annonce) {
if ($this->getInput('type') == 'boats') { $detail = $annonce->find('.btn--profile', 0);
if ($this->getInput('status') == 'professional') { $htmlDetail = getSimpleHTMLDOMCached($detail->href);
$positions = array($annonce->find('.title .position', 0)->plaintext);
} else {
$positions = array(str_replace('Wanted:', '', $annonce->find('.content li', 0)->plaintext));
}
} else {
$list = $htmlDetail->find('.viewer-details .viewer-list');
$positions = explode("\r\n", end($list)->find('span.value', 0)->plaintext);
}
$found = false; if (!empty($this->getInput('recreational_position')) || !empty($this->getInput('professional_position'))) {
$keyword = $this->getInput('status') == 'professional' ? 'professional_position' : 'recreational_position'; if ($this->getInput('type') == 'boats') {
foreach ($positions as $position) { if ($this->getInput('status') == 'professional') {
if (strpos(trim($position), $this->getInput($keyword)) !== false) { $positions = [$annonce->find('.title .position', 0)->plaintext];
$found = true; } else {
break; $positions = [str_replace('Wanted:', '', $annonce->find('.content li', 0)->plaintext)];
} }
} } else {
$list = $htmlDetail->find('.viewer-details .viewer-list');
$positions = explode("\r\n", end($list)->find('span.value', 0)->plaintext);
}
if (!$found) { $found = false;
continue; $keyword = $this->getInput('status') == 'professional' ? 'professional_position' : 'recreational_position';
} foreach ($positions as $position) {
} if (strpos(trim($position), $this->getInput($keyword)) !== false) {
$found = true;
break;
}
}
$item = array(); if (!$found) {
continue;
}
}
if ($this->getInput('type') == 'boats') { $item = [];
$titleSelector = '.title h2';
} else {
$titleSelector = '.layout__item h2';
}
$userName = $annonce->find('.result--description a', 0)->plaintext;
$annonceTitle = trim($annonce->find($titleSelector, 0)->plaintext);
if (empty($annonceTitle)) {
$item['title'] = $userName;
} else {
$item['title'] = $userName . ' - ' . $annonceTitle;
}
$item['uri'] = $detail->href; if ($this->getInput('type') == 'boats') {
$images = $annonce->find('.avatar img'); $titleSelector = '.title h2';
$item['enclosures'] = array(end($images)->getAttribute('src')); } else {
$titleSelector = '.layout__item h2';
}
$userName = $annonce->find('.result--description a', 0)->plaintext;
$annonceTitle = trim($annonce->find($titleSelector, 0)->plaintext);
if (empty($annonceTitle)) {
$item['title'] = $userName;
} else {
$item['title'] = $userName . ' - ' . $annonceTitle;
}
$content = $htmlDetail->find('.viewer-intro--info', 0)->innertext; $item['uri'] = $detail->href;
$images = $annonce->find('.avatar img');
$item['enclosures'] = [end($images)->getAttribute('src')];
$sections = $htmlDetail->find('.viewer-container .viewer-section'); $content = $htmlDetail->find('.viewer-intro--info', 0)->innertext;
foreach ($sections as $section) {
if ($section->find('.viewer-section-title', 0)) {
$class = str_replace('viewer-', '', explode(' ', $section->getAttribute('class'))[0]);
if (!in_array($class, array('apply', 'photos', 'reviews', 'contact', 'experience', 'qa'))) {
// Basic sections
$content .= $section->find('.viewer-section-title h3', 0)->outertext;
$content .= $section->find('.viewer-section-content', 0)->innertext;
}
} else {
// Info section
$content .= $section->find('.viewer-section-content h3', 0)->outertext;
$content .= $section->find('.viewer-section-content p', 0)->outertext;
}
}
if (!empty($this->getInput('keyword'))) { $sections = $htmlDetail->find('.viewer-container .viewer-section');
$keyword = strtolower($this->getInput('keyword')); foreach ($sections as $section) {
if (strpos(strtolower($item['title']), $keyword) === false) { if ($section->find('.viewer-section-title', 0)) {
if (strpos(strtolower($content), $keyword) === false) { $class = str_replace('viewer-', '', explode(' ', $section->getAttribute('class'))[0]);
continue; if (!in_array($class, ['apply', 'photos', 'reviews', 'contact', 'experience', 'qa'])) {
} // Basic sections
} $content .= $section->find('.viewer-section-title h3', 0)->outertext;
} $content .= $section->find('.viewer-section-content', 0)->innertext;
}
} else {
// Info section
$content .= $section->find('.viewer-section-content h3', 0)->outertext;
$content .= $section->find('.viewer-section-content p', 0)->outertext;
}
}
$item['content'] = $content; if (!empty($this->getInput('keyword'))) {
$keyword = strtolower($this->getInput('keyword'));
if (strpos(strtolower($item['title']), $keyword) === false) {
if (strpos(strtolower($content), $keyword) === false) {
continue;
}
}
}
$tags = $htmlDetail->find('li.viewer-tags--tag'); $item['content'] = $content;
foreach ($tags as $tag) {
if (!isset($item['categories'])) {
$item['categories'] = array();
}
$text = trim($tag->plaintext);
if (!in_array($text, $item['categories'])) {
$item['categories'][] = $text;
}
}
$this->items[] = $item; $tags = $htmlDetail->find('li.viewer-tags--tag');
$limit += 1; foreach ($tags as $tag) {
if (!isset($item['categories'])) {
$item['categories'] = [];
}
$text = trim($tag->plaintext);
if (!in_array($text, $item['categories'])) {
$item['categories'][] = $text;
}
}
if ($limit == 10) break; $this->items[] = $item;
} $limit += 1;
}
public function getURI() { if ($limit == 10) {
$uri = parent::getURI(); break;
}
}
}
if ($this->getInput('type') == 'boats') { public function getURI()
$uri .= '/boats'; {
} else { $uri = parent::getURI();
$uri .= '/crew';
}
if ($this->getInput('status') == 'professional') { if ($this->getInput('type') == 'boats') {
$uri .= '/professional'; $uri .= '/boats';
} else { } else {
$uri .= '/recreational'; $uri .= '/crew';
} }
return $uri; if ($this->getInput('status') == 'professional') {
} $uri .= '/professional';
} else {
$uri .= '/recreational';
}
return $uri;
}
} }

View file

@ -1,44 +1,47 @@
<?php <?php
class CryptomeBridge extends BridgeAbstract {
const MAINTAINER = 'BoboTiG'; class CryptomeBridge extends BridgeAbstract
const NAME = 'Cryptome'; {
const URI = 'https://cryptome.org/'; const MAINTAINER = 'BoboTiG';
const CACHE_TIMEOUT = 21600; // 6h const NAME = 'Cryptome';
const DESCRIPTION = 'Returns the N most recent documents.'; const URI = 'https://cryptome.org/';
const PARAMETERS = array( array( const CACHE_TIMEOUT = 21600; // 6h
'n' => array( const DESCRIPTION = 'Returns the N most recent documents.';
'name' => 'number of elements', const PARAMETERS = [ [
'type' => 'number', 'n' => [
'required' => true, 'name' => 'number of elements',
'exampleValue' => 10 'type' => 'number',
) 'required' => true,
)); 'exampleValue' => 10
]
]];
public function getIcon() { public function getIcon()
return self::URI . '/favicon.ico'; {
} return self::URI . '/favicon.ico';
}
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM(self::URI); {
$html = getSimpleHTMLDOM(self::URI);
$number = $this->getInput('n'); $number = $this->getInput('n');
if(!empty($number)) { if (!empty($number)) {
$num = min($number, 20); $num = min($number, 20);
} }
$i = 0; $i = 0;
foreach($html->find('pre', 1)->find('b') as $element) { foreach ($html->find('pre', 1)->find('b') as $element) {
foreach($element->find('a') as $element1) { foreach ($element->find('a') as $element1) {
$item = array(); $item = [];
$item['uri'] = $element1->href; $item['uri'] = $element1->href;
$item['title'] = $element->plaintext; $item['title'] = $element->plaintext;
$this->items[] = $item; $this->items[] = $item;
if ($i > $num) { if ($i > $num) {
break 2; break 2;
} }
$i++; $i++;
} }
} }
} }
} }

View file

@ -1,98 +1,102 @@
<?php <?php
class CubariBridge extends BridgeAbstract class CubariBridge extends BridgeAbstract
{ {
const NAME = 'Cubari'; const NAME = 'Cubari';
const URI = 'https://cubari.moe'; const URI = 'https://cubari.moe';
const DESCRIPTION = 'Parses given cubari-formatted JSON file for updates.'; const DESCRIPTION = 'Parses given cubari-formatted JSON file for updates.';
const MAINTAINER = 'KamaleiZestri'; const MAINTAINER = 'KamaleiZestri';
const PARAMETERS = array(array( const PARAMETERS = [[
'gist' => array( 'gist' => [
'name' => 'Gist/Raw Url', 'name' => 'Gist/Raw Url',
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'exampleValue' => 'https://raw.githubusercontent.com/kurisumx/baka/main/ikedan' 'exampleValue' => 'https://raw.githubusercontent.com/kurisumx/baka/main/ikedan'
) ]
)); ]];
private $mangaTitle = ''; private $mangaTitle = '';
public function getName() public function getName()
{ {
if (!empty($this->mangaTitle)) if (!empty($this->mangaTitle)) {
return $this->mangaTitle . ' - ' . self::NAME; return $this->mangaTitle . ' - ' . self::NAME;
else } else {
return self::NAME; return self::NAME;
} }
}
public function getURI() public function getURI()
{ {
if ($this->getInput('gist') != '') if ($this->getInput('gist') != '') {
return self::URI . '/read/gist/' . $this->getEncodedGist(); return self::URI . '/read/gist/' . $this->getEncodedGist();
else } else {
return self::URI; return self::URI;
} }
}
/** /**
* The Cubari bridge. * The Cubari bridge.
* *
* Cubari urls are base64 encodes of a given github raw or gist link described as below: * Cubari urls are base64 encodes of a given github raw or gist link described as below:
* https://cubari.moe/read/gist/${bаse64.url_encode(raw/<rest of the url...>)}/ * https://cubari.moe/read/gist/${bаse64.url_encode(raw/<rest of the url...>)}/
* https://cubari.moe/read/gist/${bаse64.url_encode(gist/<rest of the url...>)}/ * https://cubari.moe/read/gist/${bаse64.url_encode(gist/<rest of the url...>)}/
* https://cubari.moe/read/gist/${gitio shortcode} * https://cubari.moe/read/gist/${gitio shortcode}
* *
* This bridge uses just the raw/gist and generates matching cubari urls. * This bridge uses just the raw/gist and generates matching cubari urls.
*/ */
public function collectData() public function collectData()
{ {
$jsonSite = getContents($this->getInput('gist')); $jsonSite = getContents($this->getInput('gist'));
$jsonFile = json_decode($jsonSite, true); $jsonFile = json_decode($jsonSite, true);
$this->mangaTitle = $jsonFile['title']; $this->mangaTitle = $jsonFile['title'];
$chapters = $jsonFile['chapters']; $chapters = $jsonFile['chapters'];
foreach ($chapters as $chapnum => $chapter) { foreach ($chapters as $chapnum => $chapter) {
$item = $this->getItemFromChapter($chapnum, $chapter); $item = $this->getItemFromChapter($chapnum, $chapter);
$this->items[] = $item; $this->items[] = $item;
} }
array_multisort(array_column($this->items, 'timestamp'), SORT_DESC, $this->items); array_multisort(array_column($this->items, 'timestamp'), SORT_DESC, $this->items);
} }
protected function getEncodedGist() protected function getEncodedGist()
{ {
$url = $this->getInput('gist'); $url = $this->getInput('gist');
preg_match('/\/([a-z]*)\.githubusercontent.com(.*)/', $url, $matches); preg_match('/\/([a-z]*)\.githubusercontent.com(.*)/', $url, $matches);
// raw or gist is first match. // raw or gist is first match.
$unencoded = $matches[1] . $matches[2]; $unencoded = $matches[1] . $matches[2];
return base64_encode($unencoded); return base64_encode($unencoded);
} }
private function getSanitizedHash($string) private function getSanitizedHash($string)
{ {
return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string)))); return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string))));
} }
protected function getItemFromChapter($chapnum, $chapter) protected function getItemFromChapter($chapnum, $chapter)
{ {
$item = array(); $item = [];
$item['uri'] = $this->getURI() . '/' . $chapnum; $item['uri'] = $this->getURI() . '/' . $chapnum;
$item['title'] = 'Chapter ' . $chapnum . ' - ' . $chapter['title'] . ' - ' . $this->mangaTitle; $item['title'] = 'Chapter ' . $chapnum . ' - ' . $chapter['title'] . ' - ' . $this->mangaTitle;
foreach ($chapter['groups'] as $key => $value) foreach ($chapter['groups'] as $key => $value) {
$item['author'] = $key; $item['author'] = $key;
$item['timestamp'] = $chapter['last_updated']; }
$item['timestamp'] = $chapter['last_updated'];
$item['content'] = '<p>Manga: <a href=' . $this->getURI() . '>' . $this->mangaTitle . '</a> </p> $item['content'] = '<p>Manga: <a href=' . $this->getURI() . '>' . $this->mangaTitle . '</a> </p>
<p>Chapter Number: ' . $chapnum . '</p> <p>Chapter Number: ' . $chapnum . '</p>
<p>Chapter Title: <a href=' . $item['uri'] . '>' . $chapter['title'] . '</a></p> <p>Chapter Title: <a href=' . $item['uri'] . '>' . $chapter['title'] . '</a></p>
<p>Group: ' . $item['author'] . '</p>'; <p>Group: ' . $item['author'] . '</p>';
$item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']); $item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']);
return $item; return $item;
} }
} }

View file

@ -1,108 +1,110 @@
<?php <?php
class CuriousCatBridge extends BridgeAbstract {
const NAME = 'Curious Cat Bridge';
const URI = 'https://curiouscat.me';
const DESCRIPTION = 'Returns list of newest questions and answers for a user profile';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(array(
'username' => array(
'name' => 'Username',
'type' => 'text',
'required' => true,
'exampleValue' => 'koethekoethe',
)
));
const CACHE_TIMEOUT = 3600; class CuriousCatBridge extends BridgeAbstract
{
const NAME = 'Curious Cat Bridge';
const URI = 'https://curiouscat.me';
const DESCRIPTION = 'Returns list of newest questions and answers for a user profile';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [[
'username' => [
'name' => 'Username',
'type' => 'text',
'required' => true,
'exampleValue' => 'koethekoethe',
]
]];
public function collectData() { const CACHE_TIMEOUT = 3600;
$url = self::URI . '/api/v2/profile?username=' . urlencode($this->getInput('username')); public function collectData()
{
$url = self::URI . '/api/v2/profile?username=' . urlencode($this->getInput('username'));
$apiJson = getContents($url); $apiJson = getContents($url);
$apiData = json_decode($apiJson, true); $apiData = json_decode($apiJson, true);
foreach($apiData['posts'] as $post) { foreach ($apiData['posts'] as $post) {
$item = array(); $item = [];
$item['author'] = 'Anonymous'; $item['author'] = 'Anonymous';
if ($post['senderData']['id'] !== false) { if ($post['senderData']['id'] !== false) {
$item['author'] = $post['senderData']['username']; $item['author'] = $post['senderData']['username'];
} }
$item['uri'] = $this->getURI() . '/post/' . $post['id']; $item['uri'] = $this->getURI() . '/post/' . $post['id'];
$item['title'] = $this->ellipsisTitle($post['comment']); $item['title'] = $this->ellipsisTitle($post['comment']);
$item['content'] = $this->processContent($post); $item['content'] = $this->processContent($post);
$item['timestamp'] = $post['timestamp']; $item['timestamp'] = $post['timestamp'];
$this->items[] = $item; $this->items[] = $item;
} }
} }
public function getURI() { public function getURI()
{
if (!is_null($this->getInput('username'))) {
return self::URI . '/' . $this->getInput('username');
}
if (!is_null($this->getInput('username'))) { return parent::getURI();
return self::URI . '/' . $this->getInput('username'); }
}
return parent::getURI(); public function getName()
} {
if (!is_null($this->getInput('username'))) {
return $this->getInput('username') . ' - Curious Cat';
}
public function getName() { return parent::getName();
}
if (!is_null($this->getInput('username'))) { private function processContent($post)
return $this->getInput('username') . ' - Curious Cat'; {
} $author = 'Anonymous';
return parent::getName(); if ($post['senderData']['id'] !== false) {
} $authorUrl = self::URI . '/' . $post['senderData']['username'];
private function processContent($post) { $author = <<<EOD
$author = 'Anonymous';
if ($post['senderData']['id'] !== false) {
$authorUrl = self::URI . '/' . $post['senderData']['username'];
$author = <<<EOD
<a href="{$authorUrl}">{$post['senderData']['username']}</a> <a href="{$authorUrl}">{$post['senderData']['username']}</a>
EOD; EOD;
} }
$question = $this->formatUrls($post['comment']); $question = $this->formatUrls($post['comment']);
$answer = $this->formatUrls($post['reply']); $answer = $this->formatUrls($post['reply']);
$content = <<<EOD $content = <<<EOD
<p>{$author} asked:</p> <p>{$author} asked:</p>
<blockquote>{$question}</blockquote><br/> <blockquote>{$question}</blockquote><br/>
<p>{$post['addresseeData']['username']} answered:</p> <p>{$post['addresseeData']['username']} answered:</p>
<blockquote>{$answer}</blockquote> <blockquote>{$answer}</blockquote>
EOD; EOD;
return $content; return $content;
} }
private function ellipsisTitle($text) { private function ellipsisTitle($text)
$length = 150; {
$length = 150;
if (strlen($text) > $length) { if (strlen($text) > $length) {
$text = explode('<br>', wordwrap($text, $length, '<br>')); $text = explode('<br>', wordwrap($text, $length, '<br>'));
return $text[0] . '...'; return $text[0] . '...';
} }
return $text; return $text;
} }
private function formatUrls($content) { private function formatUrls($content)
{
return preg_replace( return preg_replace(
'/(http[s]{0,1}\:\/\/[a-zA-Z0-9.\/\?\&=\-_]{4,})/ims', '/(http[s]{0,1}\:\/\/[a-zA-Z0-9.\/\?\&=\-_]{4,})/ims',
'<a target="_blank" href="$1" target="_blank">$1</a> ', '<a target="_blank" href="$1" target="_blank">$1</a> ',
$content $content
); );
}
}
} }

View file

@ -1,37 +1,42 @@
<?php <?php
class CyanideAndHappinessBridge extends BridgeAbstract {
const NAME = 'Cyanide & Happiness';
const URI = 'https://explosm.net/';
const DESCRIPTION = 'The Webcomic from Explosm.';
const MAINTAINER = 'sal0max';
const CACHE_TIMEOUT = 60 * 60 * 2; // 2 hours
public function getIcon() { class CyanideAndHappinessBridge extends BridgeAbstract
return self::URI . 'favicon-32x32.png'; {
} const NAME = 'Cyanide & Happiness';
const URI = 'https://explosm.net/';
const DESCRIPTION = 'The Webcomic from Explosm.';
const MAINTAINER = 'sal0max';
const CACHE_TIMEOUT = 60 * 60 * 2; // 2 hours
public function getURI(){ public function getIcon()
return self::URI . 'comics/latest#comic'; {
} return self::URI . 'favicon-32x32.png';
}
public function collectData() { public function getURI()
$html = getSimpleHTMLDOM($this->getUri()); {
return self::URI . 'comics/latest#comic';
}
foreach ($html->find('[class*=ComicImage]') as $element) { public function collectData()
$date = $element->find('[class^=Author__Right] p', 0)->plaintext; {
$author = str_replace('by ', '', $element->find('[class^=Author__Right] p', 1)->plaintext); $html = getSimpleHTMLDOM($this->getUri());
$image = $element->find('img', 0)->src;
$link = $html->find('[rel=canonical]', 0)->href;
$item = array( foreach ($html->find('[class*=ComicImage]') as $element) {
'uid' => $link, $date = $element->find('[class^=Author__Right] p', 0)->plaintext;
'author' => $author, $author = str_replace('by ', '', $element->find('[class^=Author__Right] p', 1)->plaintext);
'title' => $date, $image = $element->find('img', 0)->src;
'uri' => $link . '#comic', $link = $html->find('[rel=canonical]', 0)->href;
'timestamp' => str_replace('.', '-', $date) . 'T00:00:00Z',
'content' => "<img src=\"$image\" />" $item = [
); 'uid' => $link,
$this->items[] = $item; 'author' => $author,
} 'title' => $date,
} 'uri' => $link . '#comic',
'timestamp' => str_replace('.', '-', $date) . 'T00:00:00Z',
'content' => "<img src=\"$image\" />"
];
$this->items[] = $item;
}
}
} }

View file

@ -1,203 +1,209 @@
<?php <?php
class DailymotionBridge extends BridgeAbstract {
const MAINTAINER = 'mitsukarenai'; class DailymotionBridge extends BridgeAbstract
const NAME = 'Dailymotion Bridge'; {
const URI = 'https://www.dailymotion.com/'; const MAINTAINER = 'mitsukarenai';
const CACHE_TIMEOUT = 3600; // 1h const NAME = 'Dailymotion Bridge';
const DESCRIPTION = 'Returns the 5 newest videos by username/playlist or search'; const URI = 'https://www.dailymotion.com/';
const CACHE_TIMEOUT = 3600; // 1h
const DESCRIPTION = 'Returns the 5 newest videos by username/playlist or search';
const PARAMETERS = array ( const PARAMETERS = [
'By username' => array( 'By username' => [
'u' => array( 'u' => [
'name' => 'username', 'name' => 'username',
'required' => true, 'required' => true,
'exampleValue' => 'moviepilot', 'exampleValue' => 'moviepilot',
) ]
), ],
'By playlist id' => array( 'By playlist id' => [
'p' => array( 'p' => [
'name' => 'playlist id', 'name' => 'playlist id',
'required' => true, 'required' => true,
'exampleValue' => 'x6xyc6', 'exampleValue' => 'x6xyc6',
) ]
), ],
'From search results' => array( 'From search results' => [
's' => array( 's' => [
'name' => 'Search keyword', 'name' => 'Search keyword',
'required' => true, 'required' => true,
'exampleValue' => 'matrix', 'exampleValue' => 'matrix',
), ],
'pa' => array( 'pa' => [
'name' => 'Page', 'name' => 'Page',
'type' => 'number', 'type' => 'number',
'defaultValue' => 1, 'defaultValue' => 1,
) ]
) ]
); ];
private $feedName = ''; private $feedName = '';
private $apiUrl = 'https://api.dailymotion.com'; private $apiUrl = 'https://api.dailymotion.com';
private $apiFields = 'created_time,description,id,owner.screenname,tags,thumbnail_url,title,url'; private $apiFields = 'created_time,description,id,owner.screenname,tags,thumbnail_url,title,url';
public function getIcon() { public function getIcon()
return 'https://static1-ssl.dmcdn.net/images/neon/favicons/android-icon-36x36.png.vf806ca4ed0deed812'; {
} return 'https://static1-ssl.dmcdn.net/images/neon/favicons/android-icon-36x36.png.vf806ca4ed0deed812';
}
public function collectData() { public function collectData()
{
if ($this->queriedContext === 'By username' || $this->queriedContext === 'By playlist id') {
$apiJson = getContents($this->getApiUrl());
if ($this->queriedContext === 'By username' || $this->queriedContext === 'By playlist id') { $apiData = json_decode($apiJson, true);
$apiJson = getContents($this->getApiUrl()); $this->feedName = $this->getPlaylistTitle($this->getInput('p'));
$apiData = json_decode($apiJson, true); foreach ($apiData['list'] as $apiItem) {
$item = [];
$this->feedName = $this->getPlaylistTitle($this->getInput('p')); $item['uri'] = $apiItem['url'];
$item['uid'] = $apiItem['id'];
foreach ($apiData['list'] as $apiItem) { $item['title'] = $apiItem['title'];
$item = array(); $item['timestamp'] = $apiItem['created_time'];
$item['author'] = $apiItem['owner.screenname'];
$item['uri'] = $apiItem['url']; $item['content'] = '<p><a href="' . $apiItem['url'] . '">
$item['uid'] = $apiItem['id'];
$item['title'] = $apiItem['title'];
$item['timestamp'] = $apiItem['created_time'];
$item['author'] = $apiItem['owner.screenname'];
$item['content'] = '<p><a href="' . $apiItem['url'] . '">
<img src="' . $apiItem['thumbnail_url'] . '"></a></p><p>' . $apiItem['description'] . '</p>'; <img src="' . $apiItem['thumbnail_url'] . '"></a></p><p>' . $apiItem['description'] . '</p>';
$item['categories'] = $apiItem['tags']; $item['categories'] = $apiItem['tags'];
$item['enclosures'][] = $apiItem['thumbnail_url']; $item['enclosures'][] = $apiItem['thumbnail_url'];
$this->items[] = $item; $this->items[] = $item;
} }
} }
if ($this->queriedContext === 'From search results') { if ($this->queriedContext === 'From search results') {
$html = getSimpleHTMLDOM($this->getURI());
$html = getSimpleHTMLDOM($this->getURI()); foreach ($html->find('div.media a.preview_link') as $element) {
$item = [];
foreach($html->find('div.media a.preview_link') as $element) { $item['id'] = str_replace('/video/', '', strtok($element->href, '_'));
$item = array(); $metadata = $this->getMetadata($item['id']);
$item['id'] = str_replace('/video/', '', strtok($element->href, '_')); if (empty($metadata)) {
$metadata = $this->getMetadata($item['id']); continue;
}
if(empty($metadata)) { $item['uri'] = $metadata['uri'];
continue; $item['title'] = $metadata['title'];
} $item['timestamp'] = $metadata['timestamp'];
$item['uri'] = $metadata['uri']; $item['content'] = '<a href="'
$item['title'] = $metadata['title']; . $item['uri']
$item['timestamp'] = $metadata['timestamp']; . '"><img src="'
. $metadata['thumbnailUri']
. '" /></a><br><a href="'
. $item['uri']
. '">'
. $item['title']
. '</a>';
$item['content'] = '<a href="' $this->items[] = $item;
. $item['uri']
. '"><img src="'
. $metadata['thumbnailUri']
. '" /></a><br><a href="'
. $item['uri']
. '">'
. $item['title']
. '</a>';
$this->items[] = $item; if (count($this->items) >= 5) {
break;
}
}
}
}
if (count($this->items) >= 5) { public function getName()
break; {
} switch ($this->queriedContext) {
} case 'By username':
} $specific = $this->getInput('u');
} break;
case 'By playlist id':
$specific = strtok($this->getInput('p'), '_');
public function getName() { if ($this->feedName) {
switch($this->queriedContext) { $specific = $this->feedName;
case 'By username': }
$specific = $this->getInput('u');
break;
case 'By playlist id':
$specific = strtok($this->getInput('p'), '_');
if ($this->feedName) { break;
$specific = $this->feedName; case 'From search results':
} $specific = $this->getInput('s');
break;
default:
return parent::getName();
}
break; return $specific . ' : Dailymotion';
case 'From search results': }
$specific = $this->getInput('s');
break;
default: return parent::getName();
}
return $specific . ' : Dailymotion'; public function getURI()
} {
$uri = self::URI;
switch ($this->queriedContext) {
case 'By username':
$uri .= 'user/' . urlencode($this->getInput('u'));
break;
case 'By playlist id':
$uri .= 'playlist/' . urlencode(strtok($this->getInput('p'), '_'));
break;
case 'From search results':
$uri .= 'search/' . urlencode($this->getInput('s'));
public function getURI(){ if (!is_null($this->getInput('pa'))) {
$uri = self::URI; $pa = $this->getInput('pa');
switch($this->queriedContext) {
case 'By username':
$uri .= 'user/' . urlencode($this->getInput('u'));
break;
case 'By playlist id':
$uri .= 'playlist/' . urlencode(strtok($this->getInput('p'), '_'));
break;
case 'From search results':
$uri .= 'search/' . urlencode($this->getInput('s'));
if(!is_null($this->getInput('pa'))) { if ($this->getInput('pa') < 1) {
$pa = $this->getInput('pa'); $pa = 1;
}
if ($this->getInput('pa') < 1) { $uri .= '/' . $pa;
$pa = 1; }
} break;
default:
return parent::getURI();
}
return $uri;
}
$uri .= '/' . $pa; private function getMetadata($id)
} {
break; $metadata = [];
default: return parent::getURI();
}
return $uri;
}
private function getMetadata($id) { $html = getSimpleHTMLDOM(self::URI . 'video/' . $id);
$metadata = array();
$html = getSimpleHTMLDOM(self::URI . 'video/' . $id); if (!$html) {
return $metadata;
}
if(!$html) { $metadata['title'] = $html->find('meta[property=og:title]', 0)->getAttribute('content');
return $metadata; $metadata['timestamp'] = strtotime(
} $html->find('meta[property=video:release_date]', 0)->getAttribute('content')
);
$metadata['thumbnailUri'] = $html->find('meta[property=og:image]', 0)->getAttribute('content');
$metadata['uri'] = $html->find('meta[property=og:url]', 0)->getAttribute('content');
return $metadata;
}
$metadata['title'] = $html->find('meta[property=og:title]', 0)->getAttribute('content'); private function getPlaylistTitle($id)
$metadata['timestamp'] = strtotime( {
$html->find('meta[property=video:release_date]', 0)->getAttribute('content') $title = '';
);
$metadata['thumbnailUri'] = $html->find('meta[property=og:image]', 0)->getAttribute('content');
$metadata['uri'] = $html->find('meta[property=og:url]', 0)->getAttribute('content');
return $metadata;
}
private function getPlaylistTitle($id) { $url = self::URI . 'playlist/' . $id;
$title = '';
$url = self::URI . 'playlist/' . $id; $html = getSimpleHTMLDOM($url);
$html = getSimpleHTMLDOM($url); $title = $html->find('meta[property=og:title]', 0)->getAttribute('content');
return $title;
}
$title = $html->find('meta[property=og:title]', 0)->getAttribute('content'); private function getApiUrl()
return $title; {
} switch ($this->queriedContext) {
case 'By username':
private function getApiUrl() { return $this->apiUrl . '/user/' . $this->getInput('u')
. '/videos?fields=' . urlencode($this->apiFields) . '&availability=1&sort=recent&limit=5';
switch($this->queriedContext) { break;
case 'By username': case 'By playlist id':
return $this->apiUrl . '/user/' . $this->getInput('u') return $this->apiUrl . '/playlist/' . $this->getInput('p')
. '/videos?fields=' . urlencode($this->apiFields) . '&availability=1&sort=recent&limit=5'; . '/videos?fields=' . urlencode($this->apiFields) . '&limit=5';
break; break;
case 'By playlist id': }
return $this->apiUrl . '/playlist/' . $this->getInput('p') }
. '/videos?fields=' . urlencode($this->apiFields) . '&limit=5';
break;
}
}
} }

View file

@ -1,68 +1,73 @@
<?php <?php
class DanbooruBridge extends BridgeAbstract {
const MAINTAINER = 'mitsukarenai, logmanoriginal'; class DanbooruBridge extends BridgeAbstract
const NAME = 'Danbooru'; {
const URI = 'http://donmai.us/'; const MAINTAINER = 'mitsukarenai, logmanoriginal';
const CACHE_TIMEOUT = 1800; // 30min const NAME = 'Danbooru';
const DESCRIPTION = 'Returns images from given page'; const URI = 'http://donmai.us/';
const CACHE_TIMEOUT = 1800; // 30min
const DESCRIPTION = 'Returns images from given page';
const PARAMETERS = array( const PARAMETERS = [
'global' => array( 'global' => [
'p' => array( 'p' => [
'name' => 'page', 'name' => 'page',
'defaultValue' => 1, 'defaultValue' => 1,
'type' => 'number' 'type' => 'number'
), ],
't' => array( 't' => [
'type' => 'text', 'type' => 'text',
'name' => 'tags', 'name' => 'tags',
'exampleValue' => 'cosplay', 'exampleValue' => 'cosplay',
) ]
), ],
0 => array() 0 => []
); ];
const PATHTODATA = 'article'; const PATHTODATA = 'article';
const IDATTRIBUTE = 'data-id'; const IDATTRIBUTE = 'data-id';
const TAGATTRIBUTE = 'alt'; const TAGATTRIBUTE = 'alt';
protected function getFullURI(){ protected function getFullURI()
return $this->getURI() {
. 'posts?&page=' . $this->getInput('p') return $this->getURI()
. '&tags=' . urlencode($this->getInput('t')); . 'posts?&page=' . $this->getInput('p')
} . '&tags=' . urlencode($this->getInput('t'));
}
protected function getTags($element){ protected function getTags($element)
return $element->find('img', 0)->getAttribute(static::TAGATTRIBUTE); {
} return $element->find('img', 0)->getAttribute(static::TAGATTRIBUTE);
}
protected function getItemFromElement($element){ protected function getItemFromElement($element)
// Fix links {
defaultLinkTo($element, $this->getURI()); // Fix links
defaultLinkTo($element, $this->getURI());
$item = array(); $item = [];
$item['uri'] = html_entity_decode($element->find('a', 0)->href); $item['uri'] = html_entity_decode($element->find('a', 0)->href);
$item['postid'] = (int)preg_replace('/[^0-9]/', '', $element->getAttribute(static::IDATTRIBUTE)); $item['postid'] = (int)preg_replace('/[^0-9]/', '', $element->getAttribute(static::IDATTRIBUTE));
$item['timestamp'] = time(); $item['timestamp'] = time();
$thumbnailUri = $element->find('img', 0)->src; $thumbnailUri = $element->find('img', 0)->src;
$item['categories'] = array_filter(explode(' ', $this->getTags($element))); $item['categories'] = array_filter(explode(' ', $this->getTags($element)));
$item['title'] = $this->getName() . ' | ' . $item['postid']; $item['title'] = $this->getName() . ' | ' . $item['postid'];
$item['content'] = '<a href="' $item['content'] = '<a href="'
. $item['uri'] . $item['uri']
. '"><img src="' . '"><img src="'
. $thumbnailUri . $thumbnailUri
. '" /></a><br>Tags: ' . '" /></a><br>Tags: '
. $this->getTags($element); . $this->getTags($element);
return $item; return $item;
} }
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOMCached($this->getFullURI()); {
$html = getSimpleHTMLDOMCached($this->getFullURI());
foreach($html->find(static::PATHTODATA) as $element) { foreach ($html->find(static::PATHTODATA) as $element) {
$this->items[] = $this->getItemFromElement($element); $this->items[] = $this->getItemFromElement($element);
} }
} }
} }

View file

@ -1,27 +1,28 @@
<?php <?php
class DansTonChatBridge extends BridgeAbstract {
const MAINTAINER = 'Astalaseven'; class DansTonChatBridge extends BridgeAbstract
const NAME = 'DansTonChat Bridge'; {
const URI = 'https://danstonchat.com/'; const MAINTAINER = 'Astalaseven';
const CACHE_TIMEOUT = 21600; //6h const NAME = 'DansTonChat Bridge';
const DESCRIPTION = 'Returns latest quotes from DansTonChat.'; const URI = 'https://danstonchat.com/';
const CACHE_TIMEOUT = 21600; //6h
const DESCRIPTION = 'Returns latest quotes from DansTonChat.';
public function collectData(){ public function collectData()
{
$html = getSimpleHTMLDOM(self::URI . 'latest.html');
$html = getSimpleHTMLDOM(self::URI . 'latest.html'); foreach ($html->find('div.item') as $element) {
$item = [];
foreach($html->find('div.item') as $element) { $item['uri'] = $element->find('a', 0)->href;
$item = array(); $titleContent = $element->find('h3 a', 0);
$item['uri'] = $element->find('a', 0)->href; if ($titleContent) {
$titleContent = $element->find('h3 a', 0); $item['title'] = 'DansTonChat ' . html_entity_decode($titleContent->plaintext, ENT_QUOTES);
if($titleContent) { } else {
$item['title'] = 'DansTonChat ' . html_entity_decode($titleContent->plaintext, ENT_QUOTES); $item['title'] = 'DansTonChat';
} else { }
$item['title'] = 'DansTonChat'; $item['content'] = $element->find('div.item-content a', 0)->innertext;
} $this->items[] = $item;
$item['content'] = $element->find('div.item-content a', 0)->innertext; }
$this->items[] = $item; }
}
}
} }

View file

@ -1,83 +1,90 @@
<?php <?php
class DarkReadingBridge extends FeedExpander {
const MAINTAINER = 'ORelio';
const NAME = 'Dark Reading Bridge';
const URI = 'https://www.darkreading.com/';
const DESCRIPTION = 'Returns the newest articles from Dark Reading';
const PARAMETERS = array( array( class DarkReadingBridge extends FeedExpander
'feed' => array( {
'name' => 'Feed', const MAINTAINER = 'ORelio';
'type' => 'list', const NAME = 'Dark Reading Bridge';
'values' => array( const URI = 'https://www.darkreading.com/';
'All Dark Reading Stories' => '000_AllArticles', const DESCRIPTION = 'Returns the newest articles from Dark Reading';
'Attacks/Breaches' => '644_Attacks/Breaches',
'Application Security' => '645_Application%20Security',
'Database Security' => '646_Database%20Security',
'Cloud' => '647_Cloud',
'Endpoint' => '648_Endpoint',
'Authentication' => '649_Authentication',
'Privacy' => '650_Privacy',
'Mobile' => '651_Mobile',
'Perimeter' => '652_Perimeter',
'Risk' => '653_Risk',
'Compliance' => '654_Compliance',
'Operations' => '655_Operations',
'Careers and People' => '656_Careers%20and%20People',
'Identity and Access Management' => '657_Identity%20and%20Access%20Management',
'Analytics' => '658_Analytics',
'Threat Intelligence' => '659_Threat%20Intelligence',
'Security Monitoring' => '660_Security%20Monitoring',
'Vulnerabilities / Threats' => '661_Vulnerabilities%20/%20Threats',
'Advanced Threats' => '662_Advanced%20Threats',
'Insider Threats' => '663_Insider%20Threats',
'Vulnerability Management' => '664_Vulnerability%20Management',
)
),
'limit' => self::LIMIT,
));
public function collectData(){ const PARAMETERS = [ [
$feed = $this->getInput('feed'); 'feed' => [
$feed_splitted = explode('_', $feed); 'name' => 'Feed',
$feed_id = $feed_splitted[0]; 'type' => 'list',
$feed_name = $feed_splitted[1]; 'values' => [
if(empty($feed) || !ctype_digit($feed_id) || !preg_match('/[A-Za-z%20\/]/', $feed_name)) { 'All Dark Reading Stories' => '000_AllArticles',
returnClientError('Invalid feed, please check the "feed" parameter.'); 'Attacks/Breaches' => '644_Attacks/Breaches',
} 'Application Security' => '645_Application%20Security',
$feed_url = $this->getURI() . 'rss_simple.asp'; 'Database Security' => '646_Database%20Security',
if ($feed_id != '000') { 'Cloud' => '647_Cloud',
$feed_url .= '?f_n=' . $feed_id . '&f_ln=' . $feed_name; 'Endpoint' => '648_Endpoint',
} 'Authentication' => '649_Authentication',
$limit = $this->getInput('limit') ?? 10; 'Privacy' => '650_Privacy',
$this->collectExpandableDatas($feed_url, $limit); 'Mobile' => '651_Mobile',
} 'Perimeter' => '652_Perimeter',
'Risk' => '653_Risk',
'Compliance' => '654_Compliance',
'Operations' => '655_Operations',
'Careers and People' => '656_Careers%20and%20People',
'Identity and Access Management' => '657_Identity%20and%20Access%20Management',
'Analytics' => '658_Analytics',
'Threat Intelligence' => '659_Threat%20Intelligence',
'Security Monitoring' => '660_Security%20Monitoring',
'Vulnerabilities / Threats' => '661_Vulnerabilities%20/%20Threats',
'Advanced Threats' => '662_Advanced%20Threats',
'Insider Threats' => '663_Insider%20Threats',
'Vulnerability Management' => '664_Vulnerability%20Management',
]
],
'limit' => self::LIMIT,
]];
protected function parseItem($newsItem){ public function collectData()
$item = parent::parseItem($newsItem); {
$article = getSimpleHTMLDOMCached($item['uri']); $feed = $this->getInput('feed');
$item['content'] = $this->extractArticleContent($article); $feed_splitted = explode('_', $feed);
$item['enclosures'] = array(); //remove author profile picture $feed_id = $feed_splitted[0];
$image = $article->find('meta[property="og:image"]', 0); $feed_name = $feed_splitted[1];
if (is_object($image)) { if (empty($feed) || !ctype_digit($feed_id) || !preg_match('/[A-Za-z%20\/]/', $feed_name)) {
$image = $image->content; returnClientError('Invalid feed, please check the "feed" parameter.');
$item['enclosures'] = array($image); }
} $feed_url = $this->getURI() . 'rss_simple.asp';
return $item; if ($feed_id != '000') {
} $feed_url .= '?f_n=' . $feed_id . '&f_ln=' . $feed_name;
}
$limit = $this->getInput('limit') ?? 10;
$this->collectExpandableDatas($feed_url, $limit);
}
private function extractArticleContent($article){ protected function parseItem($newsItem)
$content = $article->find('div.article-content', 0)->innertext; {
$item = parent::parseItem($newsItem);
$article = getSimpleHTMLDOMCached($item['uri']);
$item['content'] = $this->extractArticleContent($article);
$item['enclosures'] = []; //remove author profile picture
$image = $article->find('meta[property="og:image"]', 0);
if (is_object($image)) {
$image = $image->content;
$item['enclosures'] = [$image];
}
return $item;
}
foreach (array( private function extractArticleContent($article)
'<div class="divsplitter', {
'<div style="float: left; margin-right: 2px;', $content = $article->find('div.article-content', 0)->innertext;
'<div class="more-insights',
'<div id="more-insights',
) as $div_start) {
$content = stripRecursiveHTMLSection($content, 'div', $div_start);
}
return $content; foreach (
} [
'<div class="divsplitter',
'<div style="float: left; margin-right: 2px;',
'<div class="more-insights',
'<div id="more-insights',
] as $div_start
) {
$content = stripRecursiveHTMLSection($content, 'div', $div_start);
}
return $content;
}
} }

View file

@ -1,57 +1,61 @@
<?php <?php
class DauphineLibereBridge extends FeedExpander {
const MAINTAINER = 'qwertygc'; class DauphineLibereBridge extends FeedExpander
const NAME = 'Dauphine Bridge'; {
const URI = 'https://www.ledauphine.com/'; const MAINTAINER = 'qwertygc';
const CACHE_TIMEOUT = 7200; // 2h const NAME = 'Dauphine Bridge';
const DESCRIPTION = 'Returns the newest articles.'; const URI = 'https://www.ledauphine.com/';
const CACHE_TIMEOUT = 7200; // 2h
const DESCRIPTION = 'Returns the newest articles.';
const PARAMETERS = array( array( const PARAMETERS = [ [
'u' => array( 'u' => [
'name' => 'Catégorie de l\'article', 'name' => 'Catégorie de l\'article',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'À la une' => '', 'À la une' => '',
'France Monde' => 'france-monde', 'France Monde' => 'france-monde',
'Faits Divers' => 'faits-divers', 'Faits Divers' => 'faits-divers',
'Économie et Finance' => 'economie-et-finance', 'Économie et Finance' => 'economie-et-finance',
'Politique' => 'politique', 'Politique' => 'politique',
'Sport' => 'sport', 'Sport' => 'sport',
'Ain' => 'ain', 'Ain' => 'ain',
'Alpes-de-Haute-Provence' => 'haute-provence', 'Alpes-de-Haute-Provence' => 'haute-provence',
'Hautes-Alpes' => 'hautes-alpes', 'Hautes-Alpes' => 'hautes-alpes',
'Ardèche' => 'ardeche', 'Ardèche' => 'ardeche',
'Drôme' => 'drome', 'Drôme' => 'drome',
'Isère Sud' => 'isere-sud', 'Isère Sud' => 'isere-sud',
'Savoie' => 'savoie', 'Savoie' => 'savoie',
'Haute-Savoie' => 'haute-savoie', 'Haute-Savoie' => 'haute-savoie',
'Vaucluse' => 'vaucluse' 'Vaucluse' => 'vaucluse'
) ]
) ]
)); ]];
public function collectData(){ public function collectData()
$url = self::URI . 'rss'; {
$url = self::URI . 'rss';
if(empty($this->getInput('u'))) { if (empty($this->getInput('u'))) {
$url = self::URI . $this->getInput('u') . '/rss'; $url = self::URI . $this->getInput('u') . '/rss';
} }
$this->collectExpandableDatas($url, 10); $this->collectExpandableDatas($url, 10);
} }
protected function parseItem($newsItem){ protected function parseItem($newsItem)
$item = parent::parseItem($newsItem); {
$item['content'] = $this->extractContent($item['uri']); $item = parent::parseItem($newsItem);
return $item; $item['content'] = $this->extractContent($item['uri']);
} return $item;
}
private function extractContent($url){ private function extractContent($url)
$html2 = getSimpleHTMLDOMCached($url); {
foreach ($html2->find('.noprint, link, script, iframe, .shareTool, .contentInfo') as $remove) { $html2 = getSimpleHTMLDOMCached($url);
$remove->outertext = ''; foreach ($html2->find('.noprint, link, script, iframe, .shareTool, .contentInfo') as $remove) {
} $remove->outertext = '';
return $html2->find('div.content', 0)->innertext; }
} return $html2->find('div.content', 0)->innertext;
}
} }

View file

@ -1,37 +1,40 @@
<?php <?php
class DavesTrailerPageBridge extends BridgeAbstract {
const MAINTAINER = 'johnnygroovy';
const NAME = 'Daves Trailer Page Bridge';
const URI = 'https://www.davestrailerpage.co.uk/';
const DESCRIPTION = 'Last trailers in HD thanks to Dave.';
public function collectData(){ class DavesTrailerPageBridge extends BridgeAbstract
$html = getSimpleHTMLDOM(static::URI) {
or returnClientError('No results for this query.'); const MAINTAINER = 'johnnygroovy';
const NAME = 'Daves Trailer Page Bridge';
const URI = 'https://www.davestrailerpage.co.uk/';
const DESCRIPTION = 'Last trailers in HD thanks to Dave.';
$curr_date = null; public function collectData()
foreach ($html->find('tr') as $tr) { {
// If it's a date row, update the current date $html = getSimpleHTMLDOM(static::URI)
if ($tr->align == 'center') { or returnClientError('No results for this query.');
$curr_date = $tr->plaintext;
continue;
}
$item = array(); $curr_date = null;
foreach ($html->find('tr') as $tr) {
// If it's a date row, update the current date
if ($tr->align == 'center') {
$curr_date = $tr->plaintext;
continue;
}
// title $item = [];
$item['title'] = $tr->find('td', 0)->find('b', 0)->plaintext;
// content // title
$item['content'] = $tr->find('ul', 1); $item['title'] = $tr->find('td', 0)->find('b', 0)->plaintext;
// uri // content
$item['uri'] = $tr->find('a', 3)->getAttribute('href'); $item['content'] = $tr->find('ul', 1);
// date: parsed by FeedItem using strtotime // uri
$item['timestamp'] = $curr_date; $item['uri'] = $tr->find('a', 3)->getAttribute('href');
$this->items[] = $item; // date: parsed by FeedItem using strtotime
} $item['timestamp'] = $curr_date;
}
$this->items[] = $item;
}
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,46 +1,47 @@
<?php <?php
class DemoBridge extends BridgeAbstract {
const MAINTAINER = 'teromene'; class DemoBridge extends BridgeAbstract
const NAME = 'DemoBridge'; {
const URI = 'http://github.com/rss-bridge/rss-bridge'; const MAINTAINER = 'teromene';
const DESCRIPTION = 'Bridge used for demos'; const NAME = 'DemoBridge';
const URI = 'http://github.com/rss-bridge/rss-bridge';
const DESCRIPTION = 'Bridge used for demos';
const PARAMETERS = array( const PARAMETERS = [
'testCheckbox' => array( 'testCheckbox' => [
'testCheckbox' => array( 'testCheckbox' => [
'type' => 'checkbox', 'type' => 'checkbox',
'name' => 'test des checkbox' 'name' => 'test des checkbox'
) ]
), ],
'testList' => array( 'testList' => [
'testList' => array( 'testList' => [
'type' => 'list', 'type' => 'list',
'name' => 'test des listes', 'name' => 'test des listes',
'values' => array( 'values' => [
'Test' => 'test', 'Test' => 'test',
'Test 2' => 'test2' 'Test 2' => 'test2'
) ]
) ]
), ],
'testNumber' => array( 'testNumber' => [
'testNumber' => array( 'testNumber' => [
'type' => 'number', 'type' => 'number',
'name' => 'test des numéros', 'name' => 'test des numéros',
'exampleValue' => '1515632' 'exampleValue' => '1515632'
) ]
) ]
); ];
public function collectData(){ public function collectData()
{
$item = [];
$item['author'] = 'Me!';
$item['title'] = 'Test';
$item['content'] = 'Awesome content !';
$item['id'] = 'Lalala';
$item['uri'] = 'http://example.com/test';
$item = array(); $this->items[] = $item;
$item['author'] = 'Me!'; }
$item['title'] = 'Test';
$item['content'] = 'Awesome content !';
$item['id'] = 'Lalala';
$item['uri'] = 'http://example.com/test';
$this->items[] = $item;
}
} }

View file

@ -1,116 +1,122 @@
<?php <?php
class DerpibooruBridge extends BridgeAbstract {
const NAME = 'Derpibooru Bridge';
const URI = 'https://derpibooru.org/';
const DESCRIPTION = 'Returns newest images from a Derpibooru search';
const CACHE_TIMEOUT = 300; // 5min
const MAINTAINER = 'Roliga';
const PARAMETERS = array( class DerpibooruBridge extends BridgeAbstract
array( {
'f' => array( const NAME = 'Derpibooru Bridge';
'name' => 'Filter', const URI = 'https://derpibooru.org/';
'type' => 'list', const DESCRIPTION = 'Returns newest images from a Derpibooru search';
'values' => array( const CACHE_TIMEOUT = 300; // 5min
'Everything' => 56027, const MAINTAINER = 'Roliga';
'18+ R34' => 37432,
'Legacy Default' => 37431,
'18+ Dark' => 37429,
'Maximum Spoilers' => 37430,
'Default' => 100073
),
'defaultValue' => 56027
), const PARAMETERS = [
'q' => array( [
'name' => 'Query', 'f' => [
'required' => true, 'name' => 'Filter',
'exampleValue' => 'dog', 'type' => 'list',
) 'values' => [
) 'Everything' => 56027,
); '18+ R34' => 37432,
'Legacy Default' => 37431,
'18+ Dark' => 37429,
'Maximum Spoilers' => 37430,
'Default' => 100073
],
'defaultValue' => 56027
public function detectParameters($url){ ],
$params = array(); 'q' => [
'name' => 'Query',
'required' => true,
'exampleValue' => 'dog',
]
]
];
// Search page e.g. https://derpibooru.org/search?q=cute public function detectParameters($url)
$regex = '/^(https?:\/\/)?(www\.)?derpibooru.org\/search.+q=([^\/&?\n]+)/'; {
if(preg_match($regex, $url, $matches) > 0) { $params = [];
$params['q'] = urldecode($matches[3]);
return $params;
}
// Tag page, e.g. https://derpibooru.org/tags/artist-colon-devinian // Search page e.g. https://derpibooru.org/search?q=cute
$regex = '/^(https?:\/\/)?(www\.)?derpibooru.org\/tags\/([^\/&?\n]+)/'; $regex = '/^(https?:\/\/)?(www\.)?derpibooru.org\/search.+q=([^\/&?\n]+)/';
if(preg_match($regex, $url, $matches) > 0) { if (preg_match($regex, $url, $matches) > 0) {
$params['q'] = str_replace('-colon-', ':', urldecode($matches[3])); $params['q'] = urldecode($matches[3]);
return $params; return $params;
} }
return null; // Tag page, e.g. https://derpibooru.org/tags/artist-colon-devinian
} $regex = '/^(https?:\/\/)?(www\.)?derpibooru.org\/tags\/([^\/&?\n]+)/';
if (preg_match($regex, $url, $matches) > 0) {
$params['q'] = str_replace('-colon-', ':', urldecode($matches[3]));
return $params;
}
public function getName(){ return null;
if(!is_null($this->getInput('q'))) { }
return 'Derpibooru search for: '
. $this->getInput('q');
} else {
return parent::getName();
}
}
public function getURI(){ public function getName()
if(!is_null($this->getInput('f')) && !is_null($this->getInput('q'))) { {
return self::URI if (!is_null($this->getInput('q'))) {
. 'search?filter_id=' return 'Derpibooru search for: '
. urlencode($this->getInput('f')) . $this->getInput('q');
. '&q=' } else {
. urlencode($this->getInput('q')); return parent::getName();
} else { }
return parent::getURI(); }
}
}
public function collectData(){ public function getURI()
$queryJson = json_decode(getContents( {
self::URI if (!is_null($this->getInput('f')) && !is_null($this->getInput('q'))) {
. 'api/v1/json/search/images?filter_id=' return self::URI
. urlencode($this->getInput('f')) . 'search?filter_id='
. '&q=' . urlencode($this->getInput('f'))
. urlencode($this->getInput('q')) . '&q='
)); . urlencode($this->getInput('q'));
} else {
return parent::getURI();
}
}
foreach($queryJson->images as $post) { public function collectData()
$item = array(); {
$queryJson = json_decode(getContents(
self::URI
. 'api/v1/json/search/images?filter_id='
. urlencode($this->getInput('f'))
. '&q='
. urlencode($this->getInput('q'))
));
$postUri = self::URI . $post->id; foreach ($queryJson->images as $post) {
$item = [];
$item['uri'] = $postUri; $postUri = self::URI . $post->id;
$item['title'] = $post->name;
$item['timestamp'] = strtotime($post->created_at);
$item['author'] = $post->uploader;
$item['enclosures'] = array($post->view_url);
$item['categories'] = $post->tags;
$item['content'] = '<p><a href="' // image preview $item['uri'] = $postUri;
. $postUri $item['title'] = $post->name;
. '"><img src="' $item['timestamp'] = strtotime($post->created_at);
. $post->representations->medium $item['author'] = $post->uploader;
. '"></a></p><p>' // description $item['enclosures'] = [$post->view_url];
. $post->description $item['categories'] = $post->tags;
. '</p><p><b>Size:</b> ' // image size
. $post->width $item['content'] = '<p><a href="' // image preview
. 'x' . $postUri
. $post->height; . '"><img src="'
// source link . $post->representations->medium
if ($post->source_url != null) { . '"></a></p><p>' // description
$item['content'] .= '<br><b>Source:</b> <a href="' . $post->description
. $post->source_url . '</p><p><b>Size:</b> ' // image size
. '">' . $post->width
. $post->source_url . 'x'
. '</a></p>'; . $post->height;
}; // source link
$this->items[] = $item; if ($post->source_url != null) {
} $item['content'] .= '<br><b>Source:</b> <a href="'
} . $post->source_url
. '">'
. $post->source_url
. '</a></p>';
};
$this->items[] = $item;
}
}
} }

View file

@ -1,243 +1,250 @@
<?php <?php
class DesoutterBridge extends BridgeAbstract {
const CATEGORY_NEWS = 'News & Events'; class DesoutterBridge extends BridgeAbstract
const CATEGORY_INDUSTRY = 'Industry 4.0 News'; {
const CATEGORY_NEWS = 'News & Events';
const CATEGORY_INDUSTRY = 'Industry 4.0 News';
const NAME = 'Desoutter Bridge'; const NAME = 'Desoutter Bridge';
const URI = 'https://www.desouttertools.com'; const URI = 'https://www.desouttertools.com';
const DESCRIPTION = 'Returns feeds for news from Desoutter'; const DESCRIPTION = 'Returns feeds for news from Desoutter';
const MAINTAINER = 'logmanoriginal'; const MAINTAINER = 'logmanoriginal';
const CACHE_TIMEOUT = 86400; // 24 hours const CACHE_TIMEOUT = 86400; // 24 hours
const PARAMETERS = array( const PARAMETERS = [
self::CATEGORY_NEWS => array( self::CATEGORY_NEWS => [
'news_lang' => array( 'news_lang' => [
'name' => 'Language', 'name' => 'Language',
'type' => 'list', 'type' => 'list',
'title' => 'Select your language', 'title' => 'Select your language',
'defaultValue' => 'https://www.desouttertools.com/about-desoutter/news-events', 'defaultValue' => 'https://www.desouttertools.com/about-desoutter/news-events',
'values' => array( 'values' => [
'Corporate' 'Corporate'
=> 'https://www.desouttertools.com/about-desoutter/news-events', => 'https://www.desouttertools.com/about-desoutter/news-events',
'Česko' 'Česko'
=> 'https://www.desouttertools.cz/o-desoutter/aktuality-udalsoti', => 'https://www.desouttertools.cz/o-desoutter/aktuality-udalsoti',
'Deutschland' 'Deutschland'
=> 'https://www.desoutter.de/ueber-desoutter/news-events', => 'https://www.desoutter.de/ueber-desoutter/news-events',
'España' 'España'
=> 'https://www.desouttertools.es/sobre-desoutter/noticias-eventos', => 'https://www.desouttertools.es/sobre-desoutter/noticias-eventos',
'México' 'México'
=> 'https://www.desouttertools.mx/acerca-desoutter/noticias-eventos', => 'https://www.desouttertools.mx/acerca-desoutter/noticias-eventos',
'France' 'France'
=> 'https://www.desouttertools.fr/a-propos-de-desoutter/actualites-evenements', => 'https://www.desouttertools.fr/a-propos-de-desoutter/actualites-evenements',
'Magyarország' 'Magyarország'
=> 'https://www.desouttertools.hu/a-desoutter-vallalatrol/hirek-esemenyek', => 'https://www.desouttertools.hu/a-desoutter-vallalatrol/hirek-esemenyek',
'Italia' 'Italia'
=> 'https://www.desouttertools.it/su-desoutter/news-eventi', => 'https://www.desouttertools.it/su-desoutter/news-eventi',
'日本' '日本'
=> 'https://www.desouttertools.jp/desotanituite/niyusu-ibento', => 'https://www.desouttertools.jp/desotanituite/niyusu-ibento',
'대한민국' '대한민국'
=> 'https://www.desouttertools.co.kr/desoteoe-daehaeseo/nyuseu-mic-ibenteu', => 'https://www.desouttertools.co.kr/desoteoe-daehaeseo/nyuseu-mic-ibenteu',
'Polska' 'Polska'
=> 'https://www.desouttertools.pl/o-desoutter/aktualnosci-wydarzenia', => 'https://www.desouttertools.pl/o-desoutter/aktualnosci-wydarzenia',
'Brasil' 'Brasil'
=> 'https://www.desouttertools.com.br/sobre-desoutter/noti%C2%ADcias-eventos', => 'https://www.desouttertools.com.br/sobre-desoutter/noti%C2%ADcias-eventos',
'Portugal' 'Portugal'
=> 'https://www.desouttertools.pt/sobre-desoutter/notIcias-eventos', => 'https://www.desouttertools.pt/sobre-desoutter/notIcias-eventos',
'România' 'România'
=> 'https://www.desouttertools.ro/despre-desoutter/noutati-evenimente', => 'https://www.desouttertools.ro/despre-desoutter/noutati-evenimente',
'Российская Федерация' 'Российская Федерация'
=> 'https://www.desouttertools.com.ru/o-desoutter/novosti-mieropriiatiia', => 'https://www.desouttertools.com.ru/o-desoutter/novosti-mieropriiatiia',
'Slovensko' 'Slovensko'
=> 'https://www.desouttertools.sk/o-spolocnosti-desoutter/novinky-udalosti', => 'https://www.desouttertools.sk/o-spolocnosti-desoutter/novinky-udalosti',
'Slovenija' 'Slovenija'
=> 'https://www.desouttertools.si/o-druzbi-desoutter/novice-dogodki', => 'https://www.desouttertools.si/o-druzbi-desoutter/novice-dogodki',
'Sverige' 'Sverige'
=> 'https://www.desouttertools.se/om-desoutter/nyheter-evenemang', => 'https://www.desouttertools.se/om-desoutter/nyheter-evenemang',
'Türkiye' 'Türkiye'
=> 'https://www.desoutter.com.tr/desoutter-hakkinda/haberler-etkinlikler', => 'https://www.desoutter.com.tr/desoutter-hakkinda/haberler-etkinlikler',
'中国' '中国'
=> 'https://www.desouttertools.com.cn/guan-yu-ma-tou/xin-wen-he-huo-dong', => 'https://www.desouttertools.com.cn/guan-yu-ma-tou/xin-wen-he-huo-dong',
) ]
), ],
), ],
self::CATEGORY_INDUSTRY => array( self::CATEGORY_INDUSTRY => [
'industry_lang' => array( 'industry_lang' => [
'name' => 'Language', 'name' => 'Language',
'type' => 'list', 'type' => 'list',
'title' => 'Select your language', 'title' => 'Select your language',
'defaultValue' => 'Corporate', 'defaultValue' => 'Corporate',
'values' => array( 'values' => [
'Corporate' 'Corporate'
=> 'https://www.desouttertools.com/industry-4-0/news', => 'https://www.desouttertools.com/industry-4-0/news',
'Česko' 'Česko'
=> 'https://www.desouttertools.cz/prumysl-4-0/novinky', => 'https://www.desouttertools.cz/prumysl-4-0/novinky',
'Deutschland' 'Deutschland'
=> 'https://www.desoutter.de/industrie-4-0/news', => 'https://www.desoutter.de/industrie-4-0/news',
'España' 'España'
=> 'https://www.desouttertools.es/industria-4-0/noticias', => 'https://www.desouttertools.es/industria-4-0/noticias',
'México' 'México'
=> 'https://www.desouttertools.mx/industria-4-0/noticias', => 'https://www.desouttertools.mx/industria-4-0/noticias',
'France' 'France'
=> 'https://www.desouttertools.fr/industrie-4-0/actualites', => 'https://www.desouttertools.fr/industrie-4-0/actualites',
'Magyarország' 'Magyarország'
=> 'https://www.desouttertools.hu/industry-4-0/hirek', => 'https://www.desouttertools.hu/industry-4-0/hirek',
'Italia' 'Italia'
=> 'https://www.desouttertools.it/industry-4-0/news', => 'https://www.desouttertools.it/industry-4-0/news',
'日本' '日本'
=> 'https://www.desouttertools.jp/industry-4-0/news', => 'https://www.desouttertools.jp/industry-4-0/news',
'대한민국' '대한민국'
=> 'https://www.desouttertools.co.kr/industry-4-0/news', => 'https://www.desouttertools.co.kr/industry-4-0/news',
'Polska' 'Polska'
=> 'https://www.desouttertools.pl/przemysl-4-0/wiadomosci', => 'https://www.desouttertools.pl/przemysl-4-0/wiadomosci',
'Brasil' 'Brasil'
=> 'https://www.desouttertools.com.br/industria-4-0/noticias', => 'https://www.desouttertools.com.br/industria-4-0/noticias',
'Portugal' 'Portugal'
=> 'https://www.desouttertools.pt/industria-4-0/noticias', => 'https://www.desouttertools.pt/industria-4-0/noticias',
'România' 'România'
=> 'https://www.desouttertools.ro/industry-4-0/noutati', => 'https://www.desouttertools.ro/industry-4-0/noutati',
'Российская Федерация' 'Российская Федерация'
=> 'https://www.desouttertools.com.ru/industry-4-0/news', => 'https://www.desouttertools.com.ru/industry-4-0/news',
'Slovensko' 'Slovensko'
=> 'https://www.desouttertools.sk/priemysel-4-0/novinky', => 'https://www.desouttertools.sk/priemysel-4-0/novinky',
'Slovenija' 'Slovenija'
=> 'https://www.desouttertools.si/industrija-4-0/novice', => 'https://www.desouttertools.si/industrija-4-0/novice',
'Sverige' 'Sverige'
=> 'https://www.desouttertools.se/industri-4-0/nyheter', => 'https://www.desouttertools.se/industri-4-0/nyheter',
'Türkiye' 'Türkiye'
=> 'https://www.desoutter.com.tr/endustri-4-0/haberler', => 'https://www.desoutter.com.tr/endustri-4-0/haberler',
'中国' '中国'
=> 'https://www.desouttertools.com.cn/industry-4-0/news', => 'https://www.desouttertools.com.cn/industry-4-0/news',
) ]
), ],
), ],
'global' => array( 'global' => [
'full' => array( 'full' => [
'name' => 'Load full articles', 'name' => 'Load full articles',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Enable to load the full article for each item' 'title' => 'Enable to load the full article for each item'
), ],
'limit' => array( 'limit' => [
'name' => 'Limit', 'name' => 'Limit',
'type' => 'number', 'type' => 'number',
'required' => true, 'required' => true,
'defaultValue' => 3, 'defaultValue' => 3,
'title' => "Maximum number of items to return in the feed.\n0 = unlimited" 'title' => "Maximum number of items to return in the feed.\n0 = unlimited"
) ]
) ]
); ];
private $title; private $title;
public function getURI() { public function getURI()
switch($this->queriedContext) { {
case self::CATEGORY_NEWS: switch ($this->queriedContext) {
return $this->getInput('news_lang') ?: parent::getURI(); case self::CATEGORY_NEWS:
case self::CATEGORY_INDUSTRY: return $this->getInput('news_lang') ?: parent::getURI();
return $this->getInput('industry_lang') ?: parent::getURI(); case self::CATEGORY_INDUSTRY:
} return $this->getInput('industry_lang') ?: parent::getURI();
}
return parent::getURI(); return parent::getURI();
} }
public function getName() { public function getName()
return isset($this->title) ? $this->title . ' - ' . parent::getName() : parent::getName(); {
} return isset($this->title) ? $this->title . ' - ' . parent::getName() : parent::getName();
}
public function collectData() { public function collectData()
{
// Uncomment to generate list of languages automtically (dev mode)
/*
switch($this->queriedContext) {
case self::CATEGORY_NEWS:
$this->extractNewsLanguages(); die;
case self::CATEGORY_INDUSTRY:
$this->extractIndustryLanguages(); die;
}
*/
// Uncomment to generate list of languages automtically (dev mode) $html = getSimpleHTMLDOM($this->getURI());
/*
switch($this->queriedContext) {
case self::CATEGORY_NEWS:
$this->extractNewsLanguages(); die;
case self::CATEGORY_INDUSTRY:
$this->extractIndustryLanguages(); die;
}
*/
$html = getSimpleHTMLDOM($this->getURI()); $html = defaultLinkTo($html, $this->getURI());
$html = defaultLinkTo($html, $this->getURI()); $this->title = html_entity_decode($html->find('title', 0)->plaintext, ENT_QUOTES);
$this->title = html_entity_decode($html->find('title', 0)->plaintext, ENT_QUOTES); $limit = $this->getInput('limit') ?: 0;
$limit = $this->getInput('limit') ?: 0; foreach ($html->find('article') as $article) {
$item = [];
foreach($html->find('article') as $article) { $item['uri'] = $article->find('a', 0)->href;
$item = array(); $item['title'] = $article->find('a[title]', 0)->title;
$item['uri'] = $article->find('a', 0)->href; if ($this->getInput('full')) {
$item['title'] = $article->find('a[title]', 0)->title; $item['content'] = $this->getFullNewsArticle($item['uri']);
} else {
$item['content'] = $article->find('div.tile-body p', 0)->plaintext;
}
if($this->getInput('full')) { $this->items[] = $item;
$item['content'] = $this->getFullNewsArticle($item['uri']);
} else {
$item['content'] = $article->find('div.tile-body p', 0)->plaintext;
}
$this->items[] = $item; if ($limit > 0 && count($this->items) >= $limit) {
break;
}
}
}
if ($limit > 0 && count($this->items) >= $limit) break; private function getFullNewsArticle($uri)
} {
$html = getSimpleHTMLDOMCached($uri);
} $html = defaultLinkTo($html, $this->getURI());
private function getFullNewsArticle($uri) { return $html->find('section.article', 0);
$html = getSimpleHTMLDOMCached($uri); }
$html = defaultLinkTo($html, $this->getURI()); /**
* Generates a HTML page with a PHP formatted array of languages,
* pointing to the corresponding news pages. Implementation is based
* on the 'Corporate' site.
* @return void
*/
private function extractNewsLanguages()
{
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events');
return $html->find('section.article', 0); $html = defaultLinkTo($html, static::URI);
}
/** $items = $html->find('ul[class="dropdown-menu"] li');
* Generates a HTML page with a PHP formatted array of languages,
* pointing to the corresponding news pages. Implementation is based
* on the 'Corporate' site.
* @return void
*/
private function extractNewsLanguages() {
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events');
$html = defaultLinkTo($html, static::URI); $list = "\t'Corporate'\n\t=> 'https://www.desouttertools.com/about-desoutter/news-events',\n";
$items = $html->find('ul[class="dropdown-menu"] li'); foreach ($items as $item) {
$lang = trim($item->plaintext);
$uri = $item->find('a', 0)->href;
$list = "\t'Corporate'\n\t=> 'https://www.desouttertools.com/about-desoutter/news-events',\n"; $list .= "\t'{$lang}'\n\t=> '{$uri}',\n";
}
foreach($items as $item) { echo $list;
$lang = trim($item->plaintext); }
$uri = $item->find('a', 0)->href;
$list .= "\t'{$lang}'\n\t=> '{$uri}',\n"; /**
} * Generates a HTML page with a PHP formatted array of languages,
* pointing to the corresponding news pages. Implementation is based
* on the 'Corporate' site.
* @return void
*/
private function extractIndustryLanguages()
{
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news');
echo $list; $html = defaultLinkTo($html, static::URI);
}
/** $items = $html->find('ul[class="dropdown-menu"] li');
* Generates a HTML page with a PHP formatted array of languages,
* pointing to the corresponding news pages. Implementation is based
* on the 'Corporate' site.
* @return void
*/
private function extractIndustryLanguages() {
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news');
$html = defaultLinkTo($html, static::URI); $list = "\t'Corporate'\n\t=> 'https://www.desouttertools.com/industry-4-0/news',\n";
$items = $html->find('ul[class="dropdown-menu"] li'); foreach ($items as $item) {
$lang = trim($item->plaintext);
$uri = $item->find('a', 0)->href;
$list = "\t'Corporate'\n\t=> 'https://www.desouttertools.com/industry-4-0/news',\n"; $list .= "\t'{$lang}'\n\t=> '{$uri}',\n";
}
foreach($items as $item) { echo $list;
$lang = trim($item->plaintext); }
$uri = $item->find('a', 0)->href;
$list .= "\t'{$lang}'\n\t=> '{$uri}',\n";
}
echo $list;
}
} }

View file

@ -1,107 +1,113 @@
<?php <?php
class DevToBridge extends BridgeAbstract {
const CONTEXT_BY_TAG = 'By tag'; class DevToBridge extends BridgeAbstract
{
const CONTEXT_BY_TAG = 'By tag';
const NAME = 'dev.to Bridge'; const NAME = 'dev.to Bridge';
const URI = 'https://dev.to'; const URI = 'https://dev.to';
const DESCRIPTION = 'Returns feeds for tags'; const DESCRIPTION = 'Returns feeds for tags';
const MAINTAINER = 'logmanoriginal'; const MAINTAINER = 'logmanoriginal';
const CACHE_TIMEOUT = 10800; // 15 min. const CACHE_TIMEOUT = 10800; // 15 min.
const PARAMETERS = array( const PARAMETERS = [
self::CONTEXT_BY_TAG => array( self::CONTEXT_BY_TAG => [
'tag' => array( 'tag' => [
'name' => 'Tag', 'name' => 'Tag',
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'title' => 'Insert your tag', 'title' => 'Insert your tag',
'exampleValue' => 'python' 'exampleValue' => 'python'
), ],
'full' => array( 'full' => [
'name' => 'Full article', 'name' => 'Full article',
'type' => 'checkbox', 'type' => 'checkbox',
'required' => false, 'required' => false,
'title' => 'Enable to receive the full article for each item' 'title' => 'Enable to receive the full article for each item'
) ]
) ]
); ];
public function getURI() { public function getURI()
switch($this->queriedContext) { {
case self::CONTEXT_BY_TAG: switch ($this->queriedContext) {
if($tag = $this->getInput('tag')) { case self::CONTEXT_BY_TAG:
return static::URI . '/t/' . urlencode($tag); if ($tag = $this->getInput('tag')) {
} return static::URI . '/t/' . urlencode($tag);
break; }
} break;
}
return parent::getURI(); return parent::getURI();
} }
public function getIcon() { public function getIcon()
return 'https://practicaldev-herokuapp-com.freetls.fastly.net/assets/ {
return 'https://practicaldev-herokuapp-com.freetls.fastly.net/assets/
apple-icon-5c6fa9f2bce280428589c6195b7f1924206a53b782b371cfe2d02da932c8c173.png'; apple-icon-5c6fa9f2bce280428589c6195b7f1924206a53b782b371cfe2d02da932c8c173.png';
} }
public function collectData() { public function collectData()
$html = getSimpleHTMLDOMCached($this->getURI()); {
$html = getSimpleHTMLDOMCached($this->getURI());
$html = defaultLinkTo($html, static::URI); $html = defaultLinkTo($html, static::URI);
$articles = $html->find('div.crayons-story') $articles = $html->find('div.crayons-story')
or returnServerError('Could not find articles!'); or returnServerError('Could not find articles!');
foreach($articles as $article) { foreach ($articles as $article) {
$item = array(); $item = [];
$item['uri'] = $article->find('a[id*=article-link]', 0)->href; $item['uri'] = $article->find('a[id*=article-link]', 0)->href;
$item['title'] = $article->find('h2 > a', 0)->plaintext; $item['title'] = $article->find('h2 > a', 0)->plaintext;
$item['timestamp'] = $article->find('time', 0)->datetime; $item['timestamp'] = $article->find('time', 0)->datetime;
$item['author'] = $article->find('a.crayons-story__secondary.fw-medium', 0)->plaintext; $item['author'] = $article->find('a.crayons-story__secondary.fw-medium', 0)->plaintext;
// Profile image // Profile image
$item['enclosures'] = array($article->find('img', 0)->src); $item['enclosures'] = [$article->find('img', 0)->src];
if($this->getInput('full')) { if ($this->getInput('full')) {
$fullArticle = $this->getFullArticle($item['uri']); $fullArticle = $this->getFullArticle($item['uri']);
$item['content'] = <<<EOD $item['content'] = <<<EOD
<p>{$fullArticle}</p> <p>{$fullArticle}</p>
EOD; EOD;
} else { } else {
$item['content'] = <<<EOD $item['content'] = <<<EOD
<img src="{$item['enclosures'][0]}" alt="{$item['author']}"> <img src="{$item['enclosures'][0]}" alt="{$item['author']}">
<p>{$item['title']}</p> <p>{$item['title']}</p>
EOD; EOD;
} }
// categories // categories
foreach ($article->find('a.crayons-tag') as $tag) { foreach ($article->find('a.crayons-tag') as $tag) {
$item['categories'][] = str_replace('#', '', $tag->plaintext); $item['categories'][] = str_replace('#', '', $tag->plaintext);
} }
$this->items[] = $item; $this->items[] = $item;
} }
} }
public function getName() { public function getName()
if (!is_null($this->getInput('tag'))) { {
return ucfirst($this->getInput('tag')) . ' - dev.to'; if (!is_null($this->getInput('tag'))) {
} return ucfirst($this->getInput('tag')) . ' - dev.to';
}
return parent::getName(); return parent::getName();
} }
private function getFullArticle($url) { private function getFullArticle($url)
$html = getSimpleHTMLDOMCached($url); {
$html = getSimpleHTMLDOMCached($url);
$html = defaultLinkTo($html, static::URI); $html = defaultLinkTo($html, static::URI);
if ($html->find('div.crayons-article__cover', 0)) { if ($html->find('div.crayons-article__cover', 0)) {
return $html->find('div.crayons-article__cover', 0) . $html->find('[id="article-body"]', 0); return $html->find('div.crayons-article__cover', 0) . $html->find('[id="article-body"]', 0);
} }
return $html->find('[id="article-body"]', 0); return $html->find('[id="article-body"]', 0);
} }
} }

View file

@ -2,406 +2,405 @@
class DeveloppezDotComBridge extends FeedExpander class DeveloppezDotComBridge extends FeedExpander
{ {
const MAINTAINER = 'Binnette';
const NAME = 'Developpez.com Actus (FR)';
const URI = 'https://www.developpez.com/';
const DOMAIN = '.developpez.com/';
const RSS_URL = 'index/rss';
const CACHE_TIMEOUT = 1800; // 30min
const DESCRIPTION = 'Returns complete posts from developpez.com';
// Encodings used by Developpez.com in their articles body
const ENCONDINGS = ['Windows-1252', 'UTF-8'];
const PARAMETERS = [
[
'limit' => [
'name' => 'Max items',
'type' => 'number',
'defaultValue' => 5,
],
// list of the differents RSS availables
'domain' => [
'type' => 'list',
'name' => 'Domaine',
'title' => 'Chosissez un sous-domaine',
'values' => [
'= Domaine principal =' => 'www',
'4d' => '4d',
'abbyy' => 'abbyy',
'access' => 'access',
'agile' => 'agile',
'ajax' => 'ajax',
'algo' => 'algo',
'alm' => 'alm',
'android' => 'android',
'apache' => 'apache',
'applications' => 'applications',
'arduino' => 'arduino',
'asm' => 'asm',
'asp' => 'asp',
'aspose' => 'aspose',
'bacasable' => 'bacasable',
'big-data' => 'big-data',
'bpm' => 'bpm',
'bsd' => 'bsd',
'business-intelligence' => 'business-intelligence',
'c' => 'c',
'cloud-computing' => 'cloud-computing',
'club' => 'club',
'cms' => 'cms',
'cpp' => 'cpp',
'crm' => 'crm',
'css' => 'css',
'd' => 'd',
'dart' => 'dart',
'data-science' => 'data-science',
'db2' => 'db2',
'delphi' => 'delphi',
'dotnet' => 'dotnet',
'droit' => 'droit',
'eclipse' => 'eclipse',
'edi' => 'edi',
'embarque' => 'embarque',
'emploi' => 'emploi',
'etudes' => 'etudes',
'excel' => 'excel',
'firebird' => 'firebird',
'flash' => 'flash',
'go' => 'go',
'green-it' => 'green-it',
'gtk' => 'gtk',
'hardware' => 'hardware',
'hpc' => 'hpc',
'humour' => 'humour',
'ibmcloud' => 'ibmcloud',
'intelligence-artificielle' => 'intelligence-artificielle',
'interbase' => 'interbase',
'ios' => 'ios',
'java' => 'java',
'javascript' => 'javascript',
'javaweb' => 'javaweb',
'jetbrains' => 'jetbrains',
'jeux' => 'jeux',
'kotlin' => 'kotlin',
'labview' => 'labview',
'laravel' => 'laravel',
'latex' => 'latex',
'lazarus' => 'lazarus',
'linux' => 'linux',
'mac' => 'mac',
'matlab' => 'matlab',
'megaoffice' => 'megaoffice',
'merise' => 'merise',
'microsoft' => 'microsoft',
'mobiles' => 'mobiles',
'mongodb' => 'mongodb',
'mysql' => 'mysql',
'netbeans' => 'netbeans',
'nodejs' => 'nodejs',
'nosql' => 'nosql',
'objective-c' => 'objective-c',
'office' => 'office',
'open-source' => 'open-source',
'openoffice-libreoffice' => 'openoffice-libreoffice',
'oracle' => 'oracle',
'outlook' => 'outlook',
'pascal' => 'pascal',
'perl' => 'perl',
'php' => 'php',
'portail-emploi' => 'portail-emploi',
'portail-projets' => 'portail-projets',
'postgresql' => 'postgresql',
'powerpoint' => 'powerpoint',
'preprod-emploi' => 'preprod-emploi',
'programmation' => 'programmation',
'project' => 'project',
'purebasic' => 'purebasic',
'pyqt' => 'pyqt',
'python' => 'python',
'qt-creator' => 'qt-creator',
'qt' => 'qt',
'r' => 'r',
'raspberry-pi' => 'raspberry-pi',
'reseau' => 'reseau',
'ruby' => 'ruby',
'rust' => 'rust',
'sap' => 'sap',
'sas' => 'sas',
'scilab' => 'scilab',
'securite' => 'securite',
'sgbd' => 'sgbd',
'sharepoint' => 'sharepoint',
'solutions-entreprise' => 'solutions-entreprise',
'spring' => 'spring',
'sqlserver' => 'sqlserver',
'stages' => 'stages',
'supervision' => 'supervision',
'swift' => 'swift',
'sybase' => 'sybase',
'symfony' => 'symfony',
'systeme' => 'systeme',
'talend' => 'talend',
'typescript' => 'typescript',
'uml' => 'uml',
'unix' => 'unix',
'vb' => 'vb',
'vba' => 'vba',
'virtualisation' => 'virtualisation',
'visualstudio' => 'visualstudio',
'web-semantique' => 'web-semantique',
'web' => 'web',
'webmarketing' => 'webmarketing',
'wind' => 'wind',
'windows-azure' => 'windows-azure',
'windows' => 'windows',
'windowsphone' => 'windowsphone',
'word' => 'word',
'xhtml' => 'xhtml',
'xml' => 'xml',
'zend-framework' => 'zend-framework'
],
]
]
];
const MAINTAINER = 'Binnette'; /**
const NAME = 'Developpez.com Actus (FR)'; * Return the RSS url for selected domain
const URI = 'https://www.developpez.com/'; */
const DOMAIN = '.developpez.com/'; private function getRssUrl()
const RSS_URL = 'index/rss'; {
const CACHE_TIMEOUT = 1800; // 30min $domain = $this->getInput('domain');
const DESCRIPTION = 'Returns complete posts from developpez.com'; if (!empty($domain)) {
// Encodings used by Developpez.com in their articles body return 'https://' . $domain . self::DOMAIN . self::RSS_URL;
const ENCONDINGS = array('Windows-1252', 'UTF-8'); }
const PARAMETERS = array(
array(
'limit' => array(
'name' => 'Max items',
'type' => 'number',
'defaultValue' => 5,
),
// list of the differents RSS availables
'domain' => array(
'type' => 'list',
'name' => 'Domaine',
'title' => 'Chosissez un sous-domaine',
'values' => array(
'= Domaine principal =' => 'www',
'4d' => '4d',
'abbyy' => 'abbyy',
'access' => 'access',
'agile' => 'agile',
'ajax' => 'ajax',
'algo' => 'algo',
'alm' => 'alm',
'android' => 'android',
'apache' => 'apache',
'applications' => 'applications',
'arduino' => 'arduino',
'asm' => 'asm',
'asp' => 'asp',
'aspose' => 'aspose',
'bacasable' => 'bacasable',
'big-data' => 'big-data',
'bpm' => 'bpm',
'bsd' => 'bsd',
'business-intelligence' => 'business-intelligence',
'c' => 'c',
'cloud-computing' => 'cloud-computing',
'club' => 'club',
'cms' => 'cms',
'cpp' => 'cpp',
'crm' => 'crm',
'css' => 'css',
'd' => 'd',
'dart' => 'dart',
'data-science' => 'data-science',
'db2' => 'db2',
'delphi' => 'delphi',
'dotnet' => 'dotnet',
'droit' => 'droit',
'eclipse' => 'eclipse',
'edi' => 'edi',
'embarque' => 'embarque',
'emploi' => 'emploi',
'etudes' => 'etudes',
'excel' => 'excel',
'firebird' => 'firebird',
'flash' => 'flash',
'go' => 'go',
'green-it' => 'green-it',
'gtk' => 'gtk',
'hardware' => 'hardware',
'hpc' => 'hpc',
'humour' => 'humour',
'ibmcloud' => 'ibmcloud',
'intelligence-artificielle' => 'intelligence-artificielle',
'interbase' => 'interbase',
'ios' => 'ios',
'java' => 'java',
'javascript' => 'javascript',
'javaweb' => 'javaweb',
'jetbrains' => 'jetbrains',
'jeux' => 'jeux',
'kotlin' => 'kotlin',
'labview' => 'labview',
'laravel' => 'laravel',
'latex' => 'latex',
'lazarus' => 'lazarus',
'linux' => 'linux',
'mac' => 'mac',
'matlab' => 'matlab',
'megaoffice' => 'megaoffice',
'merise' => 'merise',
'microsoft' => 'microsoft',
'mobiles' => 'mobiles',
'mongodb' => 'mongodb',
'mysql' => 'mysql',
'netbeans' => 'netbeans',
'nodejs' => 'nodejs',
'nosql' => 'nosql',
'objective-c' => 'objective-c',
'office' => 'office',
'open-source' => 'open-source',
'openoffice-libreoffice' => 'openoffice-libreoffice',
'oracle' => 'oracle',
'outlook' => 'outlook',
'pascal' => 'pascal',
'perl' => 'perl',
'php' => 'php',
'portail-emploi' => 'portail-emploi',
'portail-projets' => 'portail-projets',
'postgresql' => 'postgresql',
'powerpoint' => 'powerpoint',
'preprod-emploi' => 'preprod-emploi',
'programmation' => 'programmation',
'project' => 'project',
'purebasic' => 'purebasic',
'pyqt' => 'pyqt',
'python' => 'python',
'qt-creator' => 'qt-creator',
'qt' => 'qt',
'r' => 'r',
'raspberry-pi' => 'raspberry-pi',
'reseau' => 'reseau',
'ruby' => 'ruby',
'rust' => 'rust',
'sap' => 'sap',
'sas' => 'sas',
'scilab' => 'scilab',
'securite' => 'securite',
'sgbd' => 'sgbd',
'sharepoint' => 'sharepoint',
'solutions-entreprise' => 'solutions-entreprise',
'spring' => 'spring',
'sqlserver' => 'sqlserver',
'stages' => 'stages',
'supervision' => 'supervision',
'swift' => 'swift',
'sybase' => 'sybase',
'symfony' => 'symfony',
'systeme' => 'systeme',
'talend' => 'talend',
'typescript' => 'typescript',
'uml' => 'uml',
'unix' => 'unix',
'vb' => 'vb',
'vba' => 'vba',
'virtualisation' => 'virtualisation',
'visualstudio' => 'visualstudio',
'web-semantique' => 'web-semantique',
'web' => 'web',
'webmarketing' => 'webmarketing',
'wind' => 'wind',
'windows-azure' => 'windows-azure',
'windows' => 'windows',
'windowsphone' => 'windowsphone',
'word' => 'word',
'xhtml' => 'xhtml',
'xml' => 'xml',
'zend-framework' => 'zend-framework'
),
)
)
);
/** return self::URI . self::RSS_URL;
* Return the RSS url for selected domain }
*/
private function getRssUrl()
{
$domain = $this->getInput('domain');
if (!empty($domain)) {
return 'https://' . $domain . self::DOMAIN . self::RSS_URL;
}
return self::URI . self::RSS_URL; /**
} * Grabs the RSS item from Developpez.com
*/
public function collectData()
{
$url = $this->getRssUrl();
$this->collectExpandableDatas($url, 20);
}
/** /**
* Grabs the RSS item from Developpez.com * Parse the content of every RSS item. And will try to get the full article
*/ * pointed by the item URL intead of the default abstract.
public function collectData() */
{ protected function parseItem($newsItem)
$url = $this->getRssUrl(); {
$this->collectExpandableDatas($url, 20); if (count($this->items) >= $this->getInput('limit')) {
} return null;
}
/** // This function parse each entry in the RSS with the default parse
* Parse the content of every RSS item. And will try to get the full article $item = parent::parseItem($newsItem);
* pointed by the item URL intead of the default abstract.
*/
protected function parseItem($newsItem)
{
if (count($this->items) >= $this->getInput('limit')) {
return null;
}
// This function parse each entry in the RSS with the default parse // There is a bug in Developpez RSS, coma are writtent as '~?' in the
$item = parent::parseItem($newsItem); // title, so I have to fix it manually
$item['title'] = $this->fixComaInTitle($item['title']);
// There is a bug in Developpez RSS, coma are writtent as '~?' in the // We get the content of the full article behind the RSS item URL
// title, so I have to fix it manually $articleHTMLContent = getSimpleHTMLDOMCached($item['uri']);
$item['title'] = $this->fixComaInTitle($item['title']);
// We get the content of the full article behind the RSS item URL // Here we call our custom parser
$articleHTMLContent = getSimpleHTMLDOMCached($item['uri']); $fullText = $this->extractFullText($articleHTMLContent);
if (!is_null($fullText)) {
// if we manage to parse the page behind the url of the RSS item
// then we set it as the new content. Otherwise we keep the default
// content to avoid RSS Bridge to return an empty item
$item['content'] = $fullText;
}
// Here we call our custom parser // Now we will attach video url in item
$fullText = $this->extractFullText($articleHTMLContent); $videosUrl = $this->getAllVideoUrl($articleHTMLContent);
if (!is_null($fullText)) { if (!empty($videosUrl)) {
// if we manage to parse the page behind the url of the RSS item $item['enclosures'] = array_merge($item['enclosures'], $videosUrl);
// then we set it as the new content. Otherwise we keep the default }
// content to avoid RSS Bridge to return an empty item
$item['content'] = $fullText;
}
// Now we will attach video url in item // Now we can look for the blog writer/creator
$videosUrl = $this->getAllVideoUrl($articleHTMLContent); $author = $articleHTMLContent->find('[itemprop="creator"]', 0);
if (!empty($videosUrl)) { if (!empty($author)) {
$item['enclosures'] = array_merge($item['enclosures'], $videosUrl); $item['author'] = $author->outertext;
} }
// Now we can look for the blog writer/creator return $item;
$author = $articleHTMLContent->find('[itemprop="creator"]', 0); }
if (!empty($author)) {
$item['author'] = $author->outertext;
}
return $item; /**
} * Replace '~?' by a proper coma ','
*/
private function fixComaInTitle($txt)
{
return str_replace('~?', ',', $txt);
}
/** /**
* Replace '~?' by a proper coma ',' * Return the full article pointed by the url in the RSS item
*/ * Since Developpez.com only provides a short abstract of the article, we
private function fixComaInTitle($txt) * use the url to retrieve the complete article and return it as the content
{ */
return str_replace('~?', ',', $txt); private function extractFullText($articleHTMLContent)
} {
// All blog entry contains a div with the class 'content'. This div
// contains the complete blog article. But the RSS can also return
// announcement and not a blog article. So the next if, should take
// care of the "non blog" entry
$divArticleEntry = $articleHTMLContent->find('div.content', 0);
if (is_null($divArticleEntry)) {
// Didn't find the div with class content. It is probably not a blog
// entry. It is probably just an announcement for an ebook, a PDF,
// etc. So we can use the default RSS item content.
return null;
}
/** // The following code is a bit hacky, but I really manage to get the
* Return the full article pointed by the url in the RSS item // full content of articles without any encoding issues. What is very
* Since Developpez.com only provides a short abstract of the article, we // weird and ugly in Developpez.com is the fact the some paragraphs of
* use the url to retrieve the complete article and return it as the content // the article will be encoded as UTF-8 and some other paragraphs will
*/ // be encoded as Windows-1252. So we can NOT decode the full article
private function extractFullText($articleHTMLContent) // with only one encoding. We have to check every paragraph and
{ // determine its encoding
// All blog entry contains a div with the class 'content'. This div
// contains the complete blog article. But the RSS can also return
// announcement and not a blog article. So the next if, should take
// care of the "non blog" entry
$divArticleEntry = $articleHTMLContent->find('div.content', 0);
if (is_null($divArticleEntry)) {
// Didn't find the div with class content. It is probably not a blog
// entry. It is probably just an announcement for an ebook, a PDF,
// etc. So we can use the default RSS item content.
return null;
}
// The following code is a bit hacky, but I really manage to get the // This contains all the 'paragraphs' of the article. It includes the
// full content of articles without any encoding issues. What is very // pictures, the text and the links at the bottom of the article
// weird and ugly in Developpez.com is the fact the some paragraphs of $paragraphs = $divArticleEntry->nodes;
// the article will be encoded as UTF-8 and some other paragraphs will // This will store the complete decoded content
// be encoded as Windows-1252. So we can NOT decode the full article $fullText = '';
// with only one encoding. We have to check every paragraph and
// determine its encoding
// This contains all the 'paragraphs' of the article. It includes the // For each paragraph, we will identify the encoding, then decode it
// pictures, the text and the links at the bottom of the article // and finally store the decoded content in $text
$paragraphs = $divArticleEntry->nodes; foreach ($paragraphs as $paragraph) {
// This will store the complete decoded content // We have to recreate a new DOM document from the current node
$fullText = ''; // otherwise the find function will look in the complet article and
// not only in the current paragraph. This is an ugly behavior of
// the library Simple HTML DOM Parser...
$html = str_get_html($paragraph->outertext);
$fullText .= $this->decodeParagraph($html);
}
// For each paragraph, we will identify the encoding, then decode it // Finally we return the full 'well' enconded content of the article
// and finally store the decoded content in $text return $fullText;
foreach ($paragraphs as $paragraph) { }
// We have to recreate a new DOM document from the current node
// otherwise the find function will look in the complet article and
// not only in the current paragraph. This is an ugly behavior of
// the library Simple HTML DOM Parser...
$html = str_get_html($paragraph->outertext);
$fullText .= $this->decodeParagraph($html);
}
// Finally we return the full 'well' enconded content of the article /**
return $fullText; *
} */
private function decodeParagraph($p)
/** {
* // First we check if this paragraph is a video
*/ $videoUrl = $this->getVideoUrl($p);
private function decodeParagraph($p) if (!empty($videoUrl)) {
{ // If this is a video, we just return a link to the video
// First we check if this paragraph is a video // &#128250; => 🎞️
$videoUrl = $this->getVideoUrl($p); return '<p>
if (!empty($videoUrl)) {
// If this is a video, we just return a link to the video
// &#128250; => 🎞️
return '<p>
<b>&#128250; <a href="' . $videoUrl . '">Voir la vidéo</a></b> <b>&#128250; <a href="' . $videoUrl . '">Voir la vidéo</a></b>
</p>'; </p>';
} }
// We take outertext to get the complete paragraph not only the text // We take outertext to get the complete paragraph not only the text
// inside it. That way we still graph block <img> and so on. // inside it. That way we still graph block <img> and so on.
$pTxt = $p->outertext; $pTxt = $p->outertext;
// This will store the decoded text if we manage to decode it // This will store the decoded text if we manage to decode it
$decodedTxt = ''; $decodedTxt = '';
// This is the only way to properly decode each paragraph. I tried // This is the only way to properly decode each paragraph. I tried
// many stuffs but this is the only working way I found. // many stuffs but this is the only working way I found.
foreach (self::ENCONDINGS as $enc) { foreach (self::ENCONDINGS as $enc) {
// We check the encoding of the current paragraph // We check the encoding of the current paragraph
if (mb_check_encoding($pTxt, $enc)) { if (mb_check_encoding($pTxt, $enc)) {
// If the encoding is well recognized, we can convert from // If the encoding is well recognized, we can convert from
// this encoding to UTF-8 // this encoding to UTF-8
$decodedTxt = iconv($enc, 'UTF-8', $pTxt); $decodedTxt = iconv($enc, 'UTF-8', $pTxt);
} }
} }
// We should not trim the strings to avoid the <a> to be glued to the // We should not trim the strings to avoid the <a> to be glued to the
// text like: the software<a href="...">started</a>to... // text like: the software<a href="...">started</a>to...
if (!empty($decodedTxt)) { if (!empty($decodedTxt)) {
// We manage to decode the text, so we take the decoded version // We manage to decode the text, so we take the decoded version
return $this->formatParagraph($decodedTxt); return $this->formatParagraph($decodedTxt);
} else { } else {
// Otherwise we take the non decoded version and hope it will // Otherwise we take the non decoded version and hope it will
// be displayed not too ugly in the fulltext content // be displayed not too ugly in the fulltext content
return $this->formatParagraph($pTxt); return $this->formatParagraph($pTxt);
} }
} }
/** /**
* Return true in $txt is a HTML tag and not plain text * Return true in $txt is a HTML tag and not plain text
*/ */
private function isHtmlTagNotTxt($txt) private function isHtmlTagNotTxt($txt)
{ {
$html = str_get_html($txt); $html = str_get_html($txt);
return $html && $html->root && count($html->root->children) > 0; return $html && $html->root && count($html->root->children) > 0;
} }
/** /**
* Will add a space before paragraph when needed * Will add a space before paragraph when needed
*/ */
private function formatParagraph($txt) private function formatParagraph($txt)
{ {
// If the paragraph is an html tag, we add a space before // If the paragraph is an html tag, we add a space before
if ($this->isHtmlTagNotTxt($txt)) { if ($this->isHtmlTagNotTxt($txt)) {
// the first element is an html tag and not a text, so we can add a // the first element is an html tag and not a text, so we can add a
// space before it // space before it
return ' ' . $txt; return ' ' . $txt;
} }
// If the text start with word (not punctation), we had a space // If the text start with word (not punctation), we had a space
$pattern = '/^\w/'; $pattern = '/^\w/';
if (preg_match($pattern, $txt)) { if (preg_match($pattern, $txt)) {
return ' ' . $txt; return ' ' . $txt;
} }
return $txt; return $txt;
} }
/** /**
* Retrieve all video url in the article * Retrieve all video url in the article
*/ */
private function getAllVideoUrl($item) private function getAllVideoUrl($item)
{ {
// Array of video url // Array of video url
$url = array(); $url = [];
// Developpez use a div with the class video-container // Developpez use a div with the class video-container
$divsVideo = $item->find('div.video-container'); $divsVideo = $item->find('div.video-container');
if (empty($divsVideo)) { if (empty($divsVideo)) {
return $url; return $url;
} }
// get the url of the video // get the url of the video
foreach ($divsVideo as $div) { foreach ($divsVideo as $div) {
$html = str_get_html($div->outertext); $html = str_get_html($div->outertext);
$url[] = $this->getVideoUrl($html); $url[] = $this->getVideoUrl($html);
} }
return $url; return $url;
} }
/** /**
* Retrieve URL video. We have to check for the src of an iframe * Retrieve URL video. We have to check for the src of an iframe
* Work for Youtube. Will have to test for other video platform * Work for Youtube. Will have to test for other video platform
*/ */
private function getVideoUrl($p) private function getVideoUrl($p)
{ {
$divVideo = $p->find('div.video-container', 0); $divVideo = $p->find('div.video-container', 0);
if (empty($divVideo)) { if (empty($divVideo)) {
return null; return null;
} }
$iframe = $divVideo->find('iframe', 0); $iframe = $divVideo->find('iframe', 0);
if (empty($iframe)) { if (empty($iframe)) {
return null; return null;
} }
$src = trim($iframe->getAttribute('src')); $src = trim($iframe->getAttribute('src'));
if (empty($src)) { if (empty($src)) {
return null; return null;
} }
if (str_starts_with($src, '//')) { if (str_starts_with($src, '//')) {
$src = 'https:' . $src; $src = 'https:' . $src;
} }
return $src; return $src;
} }
} }

View file

@ -1,84 +1,89 @@
<?php <?php
class DiarioDeNoticiasBridge extends BridgeAbstract {
const NAME = 'Diário de Notícias (PT)';
const URI = 'https://dn.pt';
const DESCRIPTION = 'Diário de Notícias (DN.PT)';
const MAINTAINER = 'somini';
const PARAMETERS = array(
'Tag' => array(
'n' => array(
'name' => 'Tag Name',
'required' => true,
'exampleValue' => 'rogerio-casanova',
)
)
);
const MONPT = array( class DiarioDeNoticiasBridge extends BridgeAbstract
'jan', {
'fev', const NAME = 'Diário de Notícias (PT)';
'mar', const URI = 'https://dn.pt';
'abr', const DESCRIPTION = 'Diário de Notícias (DN.PT)';
'mai', const MAINTAINER = 'somini';
'jun', const PARAMETERS = [
'jul', 'Tag' => [
'ago', 'n' => [
'set', 'name' => 'Tag Name',
'out', 'required' => true,
'nov', 'exampleValue' => 'rogerio-casanova',
'dez', ]
); ]
];
public function getIcon() { const MONPT = [
return 'https://static.globalnoticias.pt/dn/common/images/favicons/favicon-128.png'; 'jan',
} 'fev',
'mar',
'abr',
'mai',
'jun',
'jul',
'ago',
'set',
'out',
'nov',
'dez',
];
public function getName() { public function getIcon()
switch($this->queriedContext) { {
case 'Tag': return 'https://static.globalnoticias.pt/dn/common/images/favicons/favicon-128.png';
$name = self::NAME . ' | Tag | ' . $this->getInput('n'); }
break;
default:
$name = self::NAME;
}
return $name;
}
public function getURI() { public function getName()
switch($this->queriedContext) { {
case 'Tag': switch ($this->queriedContext) {
$url = self::URI . '/tag/' . $this->getInput('n') . '.html'; case 'Tag':
break; $name = self::NAME . ' | Tag | ' . $this->getInput('n');
default: break;
$url = self::URI; default:
} $name = self::NAME;
return $url; }
} return $name;
}
public function collectData() { public function getURI()
$archives = self::getURI(); {
$html = getSimpleHTMLDOMCached($archives); switch ($this->queriedContext) {
case 'Tag':
$url = self::URI . '/tag/' . $this->getInput('n') . '.html';
break;
default:
$url = self::URI;
}
return $url;
}
foreach($html->find('article') as $element) { public function collectData()
$item = array(); {
$archives = self::getURI();
$html = getSimpleHTMLDOMCached($archives);
$title = $element->find('.t-am-title', 0); foreach ($html->find('article') as $element) {
$link = $element->find('a.t-am-text', 0); $item = [];
$item['title'] = $title->plaintext; $title = $element->find('.t-am-title', 0);
$item['uri'] = self::URI . $link->href; $link = $element->find('a.t-am-text', 0);
$snippet = $element->find('.t-am-lead', 0); $item['title'] = $title->plaintext;
if ($snippet) { $item['uri'] = self::URI . $link->href;
$item['content'] = $snippet->plaintext;
}
preg_match('|edicao-do-dia\\/(?P<day>\d\d)-(?P<monpt>\w\w\w)-(?P<year>\d\d\d\d)|', $link->href, $d);
if ($d) {
$item['timestamp'] = sprintf('%s-%s-%s', $d['year'], array_search($d['monpt'], self::MONPT) + 1, $d['day']);
}
$this->items[] = $item; $snippet = $element->find('.t-am-lead', 0);
} if ($snippet) {
$item['content'] = $snippet->plaintext;
}
preg_match('|edicao-do-dia\\/(?P<day>\d\d)-(?P<monpt>\w\w\w)-(?P<year>\d\d\d\d)|', $link->href, $d);
if ($d) {
$item['timestamp'] = sprintf('%s-%s-%s', $d['year'], array_search($d['monpt'], self::MONPT) + 1, $d['day']);
}
} $this->items[] = $item;
}
}
} }

View file

@ -1,59 +1,68 @@
<?php <?php
class DiarioDoAlentejoBridge extends BridgeAbstract {
const MAINTAINER = 'somini';
const NAME = 'Diário do Alentejo';
const URI = 'https://www.diariodoalentejo.pt';
const DESCRIPTION = 'Semanário Regionalista Independente';
const CACHE_TIMEOUT = 28800; // 8h
/* This is used to hack around obtaining a timestamp. It's just a list of Month names in Portuguese ... */ class DiarioDoAlentejoBridge extends BridgeAbstract
const PT_MONTH_NAMES = array( {
'janeiro', const MAINTAINER = 'somini';
'fevereiro', const NAME = 'Diário do Alentejo';
'março', const URI = 'https://www.diariodoalentejo.pt';
'abril', const DESCRIPTION = 'Semanário Regionalista Independente';
'maio', const CACHE_TIMEOUT = 28800; // 8h
'junho',
'julho',
'agosto',
'setembro',
'outubro',
'novembro',
'dezembro');
public function getIcon() { /* This is used to hack around obtaining a timestamp. It's just a list of Month names in Portuguese ... */
return 'https://www.diariodoalentejo.pt/images/favicon/apple-touch-icon.png'; const PT_MONTH_NAMES = [
} 'janeiro',
'fevereiro',
'março',
'abril',
'maio',
'junho',
'julho',
'agosto',
'setembro',
'outubro',
'novembro',
'dezembro'];
public function collectData(){ public function getIcon()
/* This is slow as molasses (>30s!), keep the cache timeout high to avoid killing the host */ {
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx'); return 'https://www.diariodoalentejo.pt/images/favicon/apple-touch-icon.png';
}
foreach($html->find('.list_news .item') as $element) { public function collectData()
$item = array(); {
/* This is slow as molasses (>30s!), keep the cache timeout high to avoid killing the host */
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx');
$item_link = $element->find('.body h2.title a', 0); foreach ($html->find('.list_news .item') as $element) {
/* Another broken URL, see also `bridges/ComboiosDePortugalBridge.php` */ $item = [];
$item['uri'] = self::URI . implode('/', array_map('urlencode', explode('/', $item_link->href)));
$item['title'] = $item_link->innertext;
$item['timestamp'] = str_ireplace( $item_link = $element->find('.body h2.title a', 0);
array_map(function($name) { return ' ' . $name . ' '; }, self::PT_MONTH_NAMES), /* Another broken URL, see also `bridges/ComboiosDePortugalBridge.php` */
array_map(function($num) { return sprintf('-%02d-', $num); }, range(1, sizeof(self::PT_MONTH_NAMES))), $item['uri'] = self::URI . implode('/', array_map('urlencode', explode('/', $item_link->href)));
$element->find('span.date', 0)->innertext); $item['title'] = $item_link->innertext;
/* Fix the Image URL */ $item['timestamp'] = str_ireplace(
$item_image = $element->find('img.thumb', 0); array_map(function ($name) {
$item_image->src = preg_replace('/.*&img=([^&]+).*/', '\1', $item_image->getAttribute('data-src')); return ' ' . $name . ' ';
}, self::PT_MONTH_NAMES),
array_map(function ($num) {
return sprintf('-%02d-', $num);
}, range(1, sizeof(self::PT_MONTH_NAMES))),
$element->find('span.date', 0)->innertext
);
/* Content: */ /* Fix the Image URL */
/* - Image */ $item_image = $element->find('img.thumb', 0);
/* - Category */ $item_image->src = preg_replace('/.*&img=([^&]+).*/', '\1', $item_image->getAttribute('data-src'));
$content = $item_image .
'<center>' . $element->find('a.category', 0) . '</center>';
$item['content'] = defaultLinkTo($content, self::URI);
$this->items[] = $item; /* Content: */
} /* - Image */
} /* - Category */
$content = $item_image .
'<center>' . $element->find('a.category', 0) . '</center>';
$item['content'] = defaultLinkTo($content, self::URI);
$this->items[] = $item;
}
}
} }

View file

@ -1,123 +1,127 @@
<?php <?php
class DiceBridge extends BridgeAbstract {
const MAINTAINER = 'rogerdc'; class DiceBridge extends BridgeAbstract
const NAME = 'Dice Unofficial RSS'; {
const URI = 'https://www.dice.com/'; const MAINTAINER = 'rogerdc';
const DESCRIPTION = 'The Unofficial Dice RSS'; const NAME = 'Dice Unofficial RSS';
// const CACHE_TIMEOUT = 86400; // 1 day const URI = 'https://www.dice.com/';
const DESCRIPTION = 'The Unofficial Dice RSS';
// const CACHE_TIMEOUT = 86400; // 1 day
const PARAMETERS = array(array( const PARAMETERS = [[
'for_one' => array( 'for_one' => [
'name' => 'With at least one of the words', 'name' => 'With at least one of the words',
'required' => false, 'required' => false,
), ],
'for_all' => array( 'for_all' => [
'name' => 'With all of the words', 'name' => 'With all of the words',
'required' => false, 'required' => false,
), ],
'for_exact' => array( 'for_exact' => [
'name' => 'With the exact phrase', 'name' => 'With the exact phrase',
'required' => false, 'required' => false,
), ],
'for_none' => array( 'for_none' => [
'name' => 'With none of these words', 'name' => 'With none of these words',
'required' => false, 'required' => false,
), ],
'for_jt' => array( 'for_jt' => [
'name' => 'Within job title', 'name' => 'Within job title',
'required' => false, 'required' => false,
), ],
'for_com' => array( 'for_com' => [
'name' => 'Within company name', 'name' => 'Within company name',
'required' => false, 'required' => false,
), ],
'for_loc' => array( 'for_loc' => [
'name' => 'City, State, or ZIP code', 'name' => 'City, State, or ZIP code',
'required' => false, 'required' => false,
), ],
'radius' => array( 'radius' => [
'name' => 'Radius in miles', 'name' => 'Radius in miles',
'type' => 'list', 'type' => 'list',
'required' => false, 'required' => false,
'values' => array( 'values' => [
'Exact Location' => 'El', 'Exact Location' => 'El',
'Within 5 miles' => '5', 'Within 5 miles' => '5',
'Within 10 miles' => '10', 'Within 10 miles' => '10',
'Within 20 miles' => '20', 'Within 20 miles' => '20',
'Within 30 miles' => '0', 'Within 30 miles' => '0',
'Within 40 miles' => '40', 'Within 40 miles' => '40',
'Within 50 miles' => '50', 'Within 50 miles' => '50',
'Within 75 miles' => '75', 'Within 75 miles' => '75',
'Within 100 miles' => '100', 'Within 100 miles' => '100',
), ],
'defaultValue' => '0', 'defaultValue' => '0',
), ],
'jtype' => array( 'jtype' => [
'name' => 'Job type', 'name' => 'Job type',
'type' => 'list', 'type' => 'list',
'required' => false, 'required' => false,
'values' => array( 'values' => [
'Full-Time' => 'Full Time', 'Full-Time' => 'Full Time',
'Part-Time' => 'Part Time', 'Part-Time' => 'Part Time',
'Contract - Independent' => 'Contract Independent', 'Contract - Independent' => 'Contract Independent',
'Contract - W2' => 'Contract W2', 'Contract - W2' => 'Contract W2',
'Contract to Hire - Independent' => 'C2H Independent', 'Contract to Hire - Independent' => 'C2H Independent',
'Contract to Hire - W2' => 'C2H W2', 'Contract to Hire - W2' => 'C2H W2',
'Third Party - Contract - Corp-to-Corp' => 'Contract Corp-To-Corp', 'Third Party - Contract - Corp-to-Corp' => 'Contract Corp-To-Corp',
'Third Party - Contract to Hire - Corp-to-Corp' => 'C2H Corp-To-Corp', 'Third Party - Contract to Hire - Corp-to-Corp' => 'C2H Corp-To-Corp',
), ],
'defaultValue' => 'Full Time', 'defaultValue' => 'Full Time',
), ],
'telecommute' => array( 'telecommute' => [
'name' => 'Telecommute', 'name' => 'Telecommute',
'type' => 'checkbox', 'type' => 'checkbox',
), ],
)); ]];
public function getIcon() { public function getIcon()
return 'https://assets.dice.com/techpro/img/favicons/favicon.ico'; {
} return 'https://assets.dice.com/techpro/img/favicons/favicon.ico';
}
public function collectData() { public function collectData()
$uri = 'https://www.dice.com/jobs/advancedResult.html'; {
$uri .= '?for_one=' . urlencode($this->getInput('for_one')); $uri = 'https://www.dice.com/jobs/advancedResult.html';
$uri .= '&for_all=' . urlencode($this->getInput('for_all')); $uri .= '?for_one=' . urlencode($this->getInput('for_one'));
$uri .= '&for_exact=' . urlencode($this->getInput('for_exact')); $uri .= '&for_all=' . urlencode($this->getInput('for_all'));
$uri .= '&for_none=' . urlencode($this->getInput('for_none')); $uri .= '&for_exact=' . urlencode($this->getInput('for_exact'));
$uri .= '&for_jt=' . urlencode($this->getInput('for_jt')); $uri .= '&for_none=' . urlencode($this->getInput('for_none'));
$uri .= '&for_com=' . urlencode($this->getInput('for_com')); $uri .= '&for_jt=' . urlencode($this->getInput('for_jt'));
$uri .= '&for_loc=' . urlencode($this->getInput('for_loc')); $uri .= '&for_com=' . urlencode($this->getInput('for_com'));
if ($this->getInput('jtype')) { $uri .= '&for_loc=' . urlencode($this->getInput('for_loc'));
$uri .= '&jtype=' . urlencode($this->getInput('jtype')); if ($this->getInput('jtype')) {
} $uri .= '&jtype=' . urlencode($this->getInput('jtype'));
$uri .= '&sort=date&limit=100'; }
$uri .= '&radius=' . urlencode($this->getInput('radius')); $uri .= '&sort=date&limit=100';
if ($this->getInput('telecommute')) { $uri .= '&radius=' . urlencode($this->getInput('radius'));
$uri .= '&telecommute=true'; if ($this->getInput('telecommute')) {
} $uri .= '&telecommute=true';
}
$html = getSimpleHTMLDOM($uri); $html = getSimpleHTMLDOM($uri);
foreach($html->find('div.complete-serp-result-div') as $element) { foreach ($html->find('div.complete-serp-result-div') as $element) {
$item = array(); $item = [];
// Title // Title
$masterLink = $element->find('a[id^=position]', 0); $masterLink = $element->find('a[id^=position]', 0);
$item['title'] = $masterLink->title; $item['title'] = $masterLink->title;
// URL // URL
$uri = $masterLink->href; $uri = $masterLink->href;
// $uri = substr($uri, 0, strrpos($uri, '?')); // $uri = substr($uri, 0, strrpos($uri, '?'));
$item['uri'] = substr($uri, 0, strrpos($uri, '?')); $item['uri'] = substr($uri, 0, strrpos($uri, '?'));
// ID // ID
$item['id'] = $masterLink->value; $item['id'] = $masterLink->value;
// Image // Image
$image = $element->find('img', 0); $image = $element->find('img', 0);
if ($image) if ($image) {
$item['image'] = $image->getAttribute('src'); $item['image'] = $image->getAttribute('src');
// Content }
$shortdesc = $element->find('.shortdesc', '0'); // Content
$shortdesc = ($shortdesc) ? $shortdesc->innertext : ''; $shortdesc = $element->find('.shortdesc', '0');
$item['content'] = $shortdesc; $shortdesc = ($shortdesc) ? $shortdesc->innertext : '';
$this->items[] = $item; $item['content'] = $shortdesc;
} $this->items[] = $item;
} }
}
} }

View file

@ -1,35 +1,36 @@
<?php <?php
class DilbertBridge extends BridgeAbstract {
const MAINTAINER = 'kranack'; class DilbertBridge extends BridgeAbstract
const NAME = 'Dilbert Daily Strip'; {
const URI = 'https://dilbert.com'; const MAINTAINER = 'kranack';
const CACHE_TIMEOUT = 21600; // 6h const NAME = 'Dilbert Daily Strip';
const DESCRIPTION = 'The Unofficial Dilbert Daily Comic Strip'; const URI = 'https://dilbert.com';
const CACHE_TIMEOUT = 21600; // 6h
const DESCRIPTION = 'The Unofficial Dilbert Daily Comic Strip';
public function collectData(){ public function collectData()
{
$html = getSimpleHTMLDOM(self::URI);
$html = getSimpleHTMLDOM(self::URI); foreach ($html->find('section.comic-item') as $element) {
$img = $element->find('img', 0);
$link = $element->find('a', 0);
$comic = $img->src;
$title = $img->alt;
$url = $link->href;
$date = substr(strrchr($url, '/'), 1);
if (empty($title)) {
$title = 'Dilbert Comic Strip on ' . $date;
}
$date = strtotime($date);
foreach($html->find('section.comic-item') as $element) { $item = [];
$item['uri'] = $url;
$img = $element->find('img', 0); $item['title'] = $title;
$link = $element->find('a', 0); $item['author'] = 'Scott Adams';
$comic = $img->src; $item['timestamp'] = $date;
$title = $img->alt; $item['content'] = '<img src="' . $comic . '" alt="' . $img->alt . '" />';
$url = $link->href; $this->items[] = $item;
$date = substr(strrchr($url, '/'), 1); }
if (empty($title)) }
$title = 'Dilbert Comic Strip on ' . $date;
$date = strtotime($date);
$item = array();
$item['uri'] = $url;
$item['title'] = $title;
$item['author'] = 'Scott Adams';
$item['timestamp'] = $date;
$item['content'] = '<img src="' . $comic . '" alt="' . $img->alt . '" />';
$this->items[] = $item;
}
}
} }

View file

@ -1,120 +1,114 @@
<?php <?php
class DiscogsBridge extends BridgeAbstract { class DiscogsBridge extends BridgeAbstract
{
const MAINTAINER = 'teromene';
const NAME = 'DiscogsBridge';
const URI = 'https://www.discogs.com/';
const DESCRIPTION = 'Returns releases from discogs';
const PARAMETERS = [
'Artist Releases' => [
'artistid' => [
'name' => 'Artist ID',
'type' => 'number',
'required' => true,
'exampleValue' => '28104',
'title' => 'Only the ID from an artist page. EG /artist/28104-Aesop-Rock is 28104'
]
],
'Label Releases' => [
'labelid' => [
'name' => 'Label ID',
'type' => 'number',
'required' => true,
'exampleValue' => '8201',
'title' => 'Only the ID from a label page. EG /label/8201-Rhymesayers-Entertainment is 8201'
]
],
'User Wantlist' => [
'username_wantlist' => [
'name' => 'Username',
'type' => 'text',
'required' => true,
'exampleValue' => 'TheBlindMaster',
]
],
'User Folder' => [
'username_folder' => [
'name' => 'Username',
'type' => 'text',
],
'folderid' => [
'name' => 'Folder ID',
'type' => 'number',
]
]
];
const MAINTAINER = 'teromene'; public function collectData()
const NAME = 'DiscogsBridge'; {
const URI = 'https://www.discogs.com/'; if (!empty($this->getInput('artistid')) || !empty($this->getInput('labelid'))) {
const DESCRIPTION = 'Returns releases from discogs'; if (!empty($this->getInput('artistid'))) {
const PARAMETERS = array( $data = getContents('https://api.discogs.com/artists/'
'Artist Releases' => array( . $this->getInput('artistid')
'artistid' => array( . '/releases?sort=year&sort_order=desc');
'name' => 'Artist ID', } elseif (!empty($this->getInput('labelid'))) {
'type' => 'number', $data = getContents('https://api.discogs.com/labels/'
'required' => true, . $this->getInput('labelid')
'exampleValue' => '28104', . '/releases?sort=year&sort_order=desc');
'title' => 'Only the ID from an artist page. EG /artist/28104-Aesop-Rock is 28104' }
)
),
'Label Releases' => array(
'labelid' => array(
'name' => 'Label ID',
'type' => 'number',
'required' => true,
'exampleValue' => '8201',
'title' => 'Only the ID from a label page. EG /label/8201-Rhymesayers-Entertainment is 8201'
)
),
'User Wantlist' => array(
'username_wantlist' => array(
'name' => 'Username',
'type' => 'text',
'required' => true,
'exampleValue' => 'TheBlindMaster',
)
),
'User Folder' => array(
'username_folder' => array(
'name' => 'Username',
'type' => 'text',
),
'folderid' => array(
'name' => 'Folder ID',
'type' => 'number',
)
)
);
public function collectData() { $jsonData = json_decode($data, true);
foreach ($jsonData['releases'] as $release) {
$item = [];
$item['author'] = $release['artist'];
$item['title'] = $release['title'];
$item['id'] = $release['id'];
$resId = array_key_exists('main_release', $release) ? $release['main_release'] : $release['id'];
$item['uri'] = self::URI . $this->getInput('artistid') . '/release/' . $resId;
if(!empty($this->getInput('artistid')) || !empty($this->getInput('labelid'))) { if (isset($release['year'])) {
$item['timestamp'] = DateTime::createFromFormat('Y', $release['year'])->getTimestamp();
}
if(!empty($this->getInput('artistid'))) { $item['content'] = $item['author'] . ' - ' . $item['title'];
$data = getContents('https://api.discogs.com/artists/' $this->items[] = $item;
. $this->getInput('artistid') }
. '/releases?sort=year&sort_order=desc'); } elseif (!empty($this->getInput('username_wantlist')) || !empty($this->getInput('username_folder'))) {
} elseif(!empty($this->getInput('labelid'))) { if (!empty($this->getInput('username_wantlist'))) {
$data = getContents('https://api.discogs.com/labels/' $data = getContents('https://api.discogs.com/users/'
. $this->getInput('labelid') . $this->getInput('username_wantlist')
. '/releases?sort=year&sort_order=desc'); . '/wants?sort=added&sort_order=desc');
} $jsonData = json_decode($data, true)['wants'];
} elseif (!empty($this->getInput('username_folder'))) {
$data = getContents('https://api.discogs.com/users/'
. $this->getInput('username_folder')
. '/collection/folders/'
. $this->getInput('folderid')
. '/releases?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['releases'];
}
foreach ($jsonData as $element) {
$infos = $element['basic_information'];
$item = [];
$item['title'] = $infos['title'];
$item['author'] = $infos['artists'][0]['name'];
$item['id'] = $infos['artists'][0]['id'];
$item['uri'] = self::URI . $infos['artists'][0]['id'] . '/release/' . $infos['id'];
$item['timestamp'] = strtotime($element['date_added']);
$item['content'] = $item['author'] . ' - ' . $item['title'];
$this->items[] = $item;
}
}
}
$jsonData = json_decode($data, true); public function getURI()
foreach($jsonData['releases'] as $release) { {
return self::URI;
}
$item = array(); public function getName()
$item['author'] = $release['artist']; {
$item['title'] = $release['title']; return static::NAME;
$item['id'] = $release['id']; }
$resId = array_key_exists('main_release', $release) ? $release['main_release'] : $release['id'];
$item['uri'] = self::URI . $this->getInput('artistid') . '/release/' . $resId;
if(isset($release['year'])) {
$item['timestamp'] = DateTime::createFromFormat('Y', $release['year'])->getTimestamp();
}
$item['content'] = $item['author'] . ' - ' . $item['title'];
$this->items[] = $item;
}
} elseif(!empty($this->getInput('username_wantlist')) || !empty($this->getInput('username_folder'))) {
if(!empty($this->getInput('username_wantlist'))) {
$data = getContents('https://api.discogs.com/users/'
. $this->getInput('username_wantlist')
. '/wants?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['wants'];
} elseif(!empty($this->getInput('username_folder'))) {
$data = getContents('https://api.discogs.com/users/'
. $this->getInput('username_folder')
. '/collection/folders/'
. $this->getInput('folderid')
. '/releases?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['releases'];
}
foreach($jsonData as $element) {
$infos = $element['basic_information'];
$item = array();
$item['title'] = $infos['title'];
$item['author'] = $infos['artists'][0]['name'];
$item['id'] = $infos['artists'][0]['id'];
$item['uri'] = self::URI . $infos['artists'][0]['id'] . '/release/' . $infos['id'];
$item['timestamp'] = strtotime($element['date_added']);
$item['content'] = $item['author'] . ' - ' . $item['title'];
$this->items[] = $item;
}
}
}
public function getURI() {
return self::URI;
}
public function getName() {
return static::NAME;
}
} }

View file

@ -1,77 +1,81 @@
<?php <?php
class DockerHubBridge extends BridgeAbstract {
const NAME = 'Docker Hub Bridge';
const URI = 'https://hub.docker.com';
const DESCRIPTION = 'Returns new images for a container';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(
'User Submitted Image' => array(
'user' => array(
'name' => 'User',
'type' => 'text',
'required' => true,
'exampleValue' => 'rssbridge',
),
'repo' => array(
'name' => 'Repository',
'type' => 'text',
'required' => true,
'exampleValue' => 'rss-bridge',
)
),
'Official Image' => array(
'repo' => array(
'name' => 'Repository',
'type' => 'text',
'required' => true,
'exampleValue' => 'postgres',
)
),
);
const CACHE_TIMEOUT = 3600; // 1 hour class DockerHubBridge extends BridgeAbstract
{
const NAME = 'Docker Hub Bridge';
const URI = 'https://hub.docker.com';
const DESCRIPTION = 'Returns new images for a container';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = [
'User Submitted Image' => [
'user' => [
'name' => 'User',
'type' => 'text',
'required' => true,
'exampleValue' => 'rssbridge',
],
'repo' => [
'name' => 'Repository',
'type' => 'text',
'required' => true,
'exampleValue' => 'rss-bridge',
]
],
'Official Image' => [
'repo' => [
'name' => 'Repository',
'type' => 'text',
'required' => true,
'exampleValue' => 'postgres',
]
],
];
private $apiURL = 'https://hub.docker.com/v2/repositories/'; const CACHE_TIMEOUT = 3600; // 1 hour
private $imageUrlRegex = '/hub\.docker\.com\/r\/([\w]+)\/([\w-]+)\/?/';
private $officialImageUrlRegex = '/hub\.docker\.com\/_\/([\w-]+)\/?/';
public function detectParameters($url) { private $apiURL = 'https://hub.docker.com/v2/repositories/';
$params = array(); private $imageUrlRegex = '/hub\.docker\.com\/r\/([\w]+)\/([\w-]+)\/?/';
private $officialImageUrlRegex = '/hub\.docker\.com\/_\/([\w-]+)\/?/';
// user submitted image public function detectParameters($url)
if(preg_match($this->imageUrlRegex, $url, $matches)) { {
$params['context'] = 'User Submitted Image'; $params = [];
$params['user'] = $matches[1];
$params['repo'] = $matches[2];
return $params;
}
// official image // user submitted image
if(preg_match($this->officialImageUrlRegex, $url, $matches)) { if (preg_match($this->imageUrlRegex, $url, $matches)) {
$params['context'] = 'Official Image'; $params['context'] = 'User Submitted Image';
$params['repo'] = $matches[1]; $params['user'] = $matches[1];
return $params; $params['repo'] = $matches[2];
} return $params;
}
return null; // official image
} if (preg_match($this->officialImageUrlRegex, $url, $matches)) {
$params['context'] = 'Official Image';
$params['repo'] = $matches[1];
return $params;
}
public function collectData() { return null;
$json = getContents($this->getApiUrl()); }
$data = json_decode($json, false); public function collectData()
{
$json = getContents($this->getApiUrl());
foreach ($data->results as $result) { $data = json_decode($json, false);
$item = array();
$lastPushed = date('Y-m-d H:i:s', strtotime($result->tag_last_pushed)); foreach ($data->results as $result) {
$item = [];
$item['title'] = $result->name; $lastPushed = date('Y-m-d H:i:s', strtotime($result->tag_last_pushed));
$item['uid'] = $result->id;
$item['uri'] = $this->getTagUrl($result->name); $item['title'] = $result->name;
$item['author'] = $result->last_updater_username; $item['uid'] = $result->id;
$item['timestamp'] = $result->tag_last_pushed; $item['uri'] = $this->getTagUrl($result->name);
$item['content'] = <<<EOD $item['author'] = $result->last_updater_username;
$item['timestamp'] = $result->tag_last_pushed;
$item['content'] = <<<EOD
<Strong>Tag</strong><br> <Strong>Tag</strong><br>
<p>{$result->name}</p> <p>{$result->name}</p>
<Strong>Last pushed</strong><br> <Strong>Last pushed</strong><br>
@ -80,86 +84,93 @@ class DockerHubBridge extends BridgeAbstract {
{$this->getImages($result)} {$this->getImages($result)}
EOD; EOD;
$this->items[] = $item; $this->items[] = $item;
} }
}
} public function getURI()
{
if ($this->queriedContext === 'Official Image') {
return self::URI . '/_/' . $this->getRepo();
}
public function getURI() { if ($this->getInput('repo')) {
if ($this->queriedContext === 'Official Image') { return self::URI . '/r/' . $this->getRepo();
return self::URI . '/_/' . $this->getRepo(); }
}
if ($this->getInput('repo')) { return parent::getURI();
return self::URI . '/r/' . $this->getRepo(); }
}
return parent::getURI(); public function getName()
} {
if ($this->getInput('repo')) {
return $this->getRepo() . ' - Docker Hub';
}
public function getName() { return parent::getName();
if ($this->getInput('repo')) { }
return $this->getRepo() . ' - Docker Hub';
}
return parent::getName(); private function getRepo()
} {
if ($this->queriedContext === 'Official Image') {
return $this->getInput('repo');
}
private function getRepo() { return $this->getInput('user') . '/' . $this->getInput('repo');
if ($this->queriedContext === 'Official Image') { }
return $this->getInput('repo');
}
return $this->getInput('user') . '/' . $this->getInput('repo'); private function getApiUrl()
} {
if ($this->queriedContext === 'Official Image') {
return $this->apiURL . 'library/' . $this->getRepo() . '/tags/?page_size=25&page=1';
}
private function getApiUrl() { return $this->apiURL . $this->getRepo() . '/tags/?page_size=25&page=1';
if ($this->queriedContext === 'Official Image') { }
return $this->apiURL . 'library/' . $this->getRepo() . '/tags/?page_size=25&page=1';
}
return $this->apiURL . $this->getRepo() . '/tags/?page_size=25&page=1'; private function getLayerUrl($name, $digest)
} {
if ($this->queriedContext === 'Official Image') {
return self::URI . '/layers/' . $this->getRepo() . '/library/' .
$this->getRepo() . '/' . $name . '/images/' . $digest;
}
private function getLayerUrl($name, $digest) { return self::URI . '/layers/' . $this->getRepo() . '/' . $name . '/images/' . $digest;
if ($this->queriedContext === 'Official Image') { }
return self::URI . '/layers/' . $this->getRepo() . '/library/' .
$this->getRepo() . '/' . $name . '/images/' . $digest;
}
return self::URI . '/layers/' . $this->getRepo() . '/' . $name . '/images/' . $digest; private function getTagUrl($name)
} {
if ($this->queriedContext === 'Official Image') {
return self::URI . '/_/' . $this->getRepo() . '?tab=tags&name=' . $name;
}
private function getTagUrl($name) { return self::URI . '/r/' . $this->getRepo() . '/tags?name=' . $name;
if ($this->queriedContext === 'Official Image') { }
return self::URI . '/_/' . $this->getRepo() . '?tab=tags&name=' . $name;
}
return self::URI . '/r/' . $this->getRepo() . '/tags?name=' . $name; private function getImages($result)
} {
$html = <<<EOD
private function getImages($result) {
$html = <<<EOD
<table style="width:300px;"><thead><tr><th>Digest</th><th>OS/architecture</th></tr></thead></tbody> <table style="width:300px;"><thead><tr><th>Digest</th><th>OS/architecture</th></tr></thead></tbody>
EOD; EOD;
foreach ($result->images as $image) { foreach ($result->images as $image) {
$layersUrl = $this->getLayerUrl($result->name, $image->digest); $layersUrl = $this->getLayerUrl($result->name, $image->digest);
$id = $this->getShortDigestId($image->digest); $id = $this->getShortDigestId($image->digest);
$html .= <<<EOD $html .= <<<EOD
<tr> <tr>
<td><a href="{$layersUrl}">{$id}</a></td> <td><a href="{$layersUrl}">{$id}</a></td>
<td>{$image->os}/{$image->architecture}</td> <td>{$image->os}/{$image->architecture}</td>
</tr> </tr>
EOD; EOD;
} }
return $html . '</tbody></table>'; return $html . '</tbody></table>';
} }
private function getShortDigestId($digest) { private function getShortDigestId($digest)
$parts = explode(':', $digest); {
return substr($parts[1], 0, 12); $parts = explode(':', $digest);
} return substr($parts[1], 0, 12);
}
} }

View file

@ -1,79 +1,82 @@
<?php <?php
/** /**
* Retourne les dons d'une recherche filtrée sur le site Donnons.org * Retourne les dons d'une recherche filtrée sur le site Donnons.org
* Example: https://donnons.org/Sport/Ile-de-France * Example: https://donnons.org/Sport/Ile-de-France
*/ */
class DonnonsBridge extends BridgeAbstract { class DonnonsBridge extends BridgeAbstract
{
const MAINTAINER = 'Binnette';
const NAME = 'Donnons.org';
const URI = 'https://donnons.org';
const CACHE_TIMEOUT = 1800; // 30min
const DESCRIPTION = 'Retourne les dons depuis le site Donnons.org.';
const MAINTAINER = 'Binnette'; const PARAMETERS = [
const NAME = 'Donnons.org'; [
const URI = 'https://donnons.org'; 'q' => [
const CACHE_TIMEOUT = 1800; // 30min 'name' => 'Url de recherche',
const DESCRIPTION = 'Retourne les dons depuis le site Donnons.org.'; 'required' => true,
'exampleValue' => '/Sport/Ile-de-France',
'pattern' => '\/.*',
'title' => 'Faites une recherche sur le site. Puis copiez ici la fin de lurl. Doit commencer par /',
],
'p' => [
'name' => 'Nombre de pages à scanner',
'type' => 'number',
'required' => true,
'defaultValue' => 5,
'title' => 'Indique le nombre de pages de donnons.org qui seront scannées'
]
]
];
const PARAMETERS = array( public function collectData()
array( {
'q' => array( $pages = $this->getInput('p');
'name' => 'Url de recherche',
'required' => true,
'exampleValue' => '/Sport/Ile-de-France',
'pattern' => '\/.*',
'title' => 'Faites une recherche sur le site. Puis copiez ici la fin de lurl. Doit commencer par /',
),
'p' => array(
'name' => 'Nombre de pages à scanner',
'type' => 'number',
'required' => true,
'defaultValue' => 5,
'title' => 'Indique le nombre de pages de donnons.org qui seront scannées'
)
)
);
public function collectData() { for ($i = 1; $i <= $pages; $i++) {
$pages = $this->getInput('p'); $this->collectDataByPage($i);
}
}
for($i = 1; $i <= $pages; $i++) { private function collectDataByPage($page)
$this->collectDataByPage($i); {
} $uri = $this->getPageURI($page);
}
private function collectDataByPage($page) { $html = getSimpleHTMLDOM($uri);
$uri = $this->getPageURI($page);
$html = getSimpleHTMLDOM($uri); $searchDiv = $html->find('div[id=search]', 0);
$searchDiv = $html->find('div[id=search]', 0); if (!is_null($searchDiv)) {
$elements = $searchDiv->find('a.lst-annonce');
foreach ($elements as $element) {
$item = [];
if(!is_null($searchDiv)) { // Lien vers le don
$elements = $searchDiv->find('a.lst-annonce'); $item['uri'] = self::URI . $element->href;
foreach($elements as $element) { // Id de l'objet
$item = array(); $item['uid'] = $element->getAttribute('data-id');
// Lien vers le don // Grab info from json
$item['uri'] = self::URI . $element->href; $jsonString = $element->find('script', 0)->innertext;
// Id de l'objet $json = json_decode($jsonString, true);
$item['uid'] = $element->getAttribute('data-id');
// Grab info from json $name = $json['name'];
$jsonString = $element->find('script', 0)->innertext; $category = $json['category'];
$json = json_decode($jsonString, true); $date = $json['availabilityStarts'];
$description = $json['description'];
$city = $json['availableAtOrFrom']['address']['addressLocality'];
$region = $json['availableAtOrFrom']['address']['addressRegion'];
$name = $json['name']; // Grab info from HTML
$category = $json['category']; $imageSrc = $element->find('img.ima-center', 0)->getAttribute('src');
$date = $json['availabilityStarts']; // Use large image instead of small one
$description = $json['description']; $imageSrc = str_replace('/xs/', '/lg/', $imageSrc);
$city = $json['availableAtOrFrom']['address']['addressLocality']; $image = self::URI . $imageSrc;
$region = $json['availableAtOrFrom']['address']['addressRegion']; $author = $element->find('div.avatar-holder', 0)->plaintext;
// Grab info from HTML $content = '
$imageSrc = $element->find('img.ima-center', 0)->getAttribute('src');
// Use large image instead of small one
$imageSrc = str_replace('/xs/', '/lg/', $imageSrc);
$image = self::URI . $imageSrc;
$author = $element->find('div.avatar-holder', 0)->plaintext;
$content = '
<img style="margin-right:1em;" src="' . $image . '"> <img style="margin-right:1em;" src="' . $image . '">
<div> <div>
<h1>' . $name . '</h1> <h1>' . $name . '</h1>
@ -84,42 +87,45 @@ class DonnonsBridge extends BridgeAbstract {
</div> </div>
'; ';
// Titre du don // Titre du don
$item['title'] = '[' . $category . '] ' . $name; $item['title'] = '[' . $category . '] ' . $name;
$item['timestamp'] = $date; $item['timestamp'] = $date;
$item['author'] = $author; $item['author'] = $author;
$item['content'] = $content; $item['content'] = $content;
$item['enclosures'] = array($image); $item['enclosures'] = [$image];
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }
private function getPageURI($page) { private function getPageURI($page)
$uri = $this->getURI(); {
$haveQueryParams = strpos($uri, '?') !== false; $uri = $this->getURI();
$haveQueryParams = strpos($uri, '?') !== false;
if($haveQueryParams) { if ($haveQueryParams) {
return $uri . '&page=' . $page; return $uri . '&page=' . $page;
} else { } else {
return $uri . '?page=' . $page; return $uri . '?page=' . $page;
} }
} }
public function getURI() { public function getURI()
if(!is_null($this->getInput('q'))) { {
return self::URI . $this->getInput('q'); if (!is_null($this->getInput('q'))) {
} return self::URI . $this->getInput('q');
}
return parent::getURI(); return parent::getURI();
} }
public function getName() { public function getName()
if(!is_null($this->getInput('q'))) { {
return 'Donnons.org - ' . $this->getInput('q'); if (!is_null($this->getInput('q'))) {
} return 'Donnons.org - ' . $this->getInput('q');
}
return parent::getName(); return parent::getName();
} }
} }

View file

@ -1,103 +1,110 @@
<?php <?php
class DribbbleBridge extends BridgeAbstract {
const MAINTAINER = 'quentinus95'; class DribbbleBridge extends BridgeAbstract
const NAME = 'Dribbble popular shots'; {
const URI = 'https://dribbble.com'; const MAINTAINER = 'quentinus95';
const CACHE_TIMEOUT = 1800; const NAME = 'Dribbble popular shots';
const DESCRIPTION = 'Returns the newest popular shots from Dribbble.'; const URI = 'https://dribbble.com';
const CACHE_TIMEOUT = 1800;
const DESCRIPTION = 'Returns the newest popular shots from Dribbble.';
public function getIcon() { public function getIcon()
return 'https://cdn.dribbble.com/assets/ {
return 'https://cdn.dribbble.com/assets/
favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico'; favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
} }
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM(self::URI); {
$html = getSimpleHTMLDOM(self::URI);
$json = $this->loadEmbeddedJsonData($html); $json = $this->loadEmbeddedJsonData($html);
foreach($html->find('li[id^="screenshot-"]') as $shot) { foreach ($html->find('li[id^="screenshot-"]') as $shot) {
$item = array(); $item = [];
$additional_data = $this->findJsonForShot($shot, $json); $additional_data = $this->findJsonForShot($shot, $json);
if ($additional_data === null) { if ($additional_data === null) {
$item['uri'] = self::URI . $shot->find('a', 0)->href; $item['uri'] = self::URI . $shot->find('a', 0)->href;
$item['title'] = $shot->find('.shot-title', 0)->plaintext; $item['title'] = $shot->find('.shot-title', 0)->plaintext;
} else { } else {
$item['timestamp'] = strtotime($additional_data['published_at']); $item['timestamp'] = strtotime($additional_data['published_at']);
$item['uri'] = self::URI . $additional_data['path']; $item['uri'] = self::URI . $additional_data['path'];
$item['title'] = $additional_data['title']; $item['title'] = $additional_data['title'];
} }
$item['author'] = trim($shot->find('.user-information .display-name', 0)->plaintext); $item['author'] = trim($shot->find('.user-information .display-name', 0)->plaintext);
$description = $shot->find('.comment', 0); $description = $shot->find('.comment', 0);
$item['content'] = $description === null ? '' : $description->plaintext; $item['content'] = $description === null ? '' : $description->plaintext;
$preview_path = $shot->find('figure img', 1)->attr['data-srcset']; $preview_path = $shot->find('figure img', 1)->attr['data-srcset'];
$item['content'] .= $this->getImageTag($preview_path, $item['title']); $item['content'] .= $this->getImageTag($preview_path, $item['title']);
$item['enclosures'] = array($this->getFullSizeImagePath($preview_path)); $item['enclosures'] = [$this->getFullSizeImagePath($preview_path)];
$this->items[] = $item; $this->items[] = $item;
} }
} }
private function loadEmbeddedJsonData($html){ private function loadEmbeddedJsonData($html)
$json = array(); {
$scripts = $html->find('script'); $json = [];
$scripts = $html->find('script');
foreach($scripts as $script) { foreach ($scripts as $script) {
if(strpos($script->innertext, 'newestShots') !== false) { if (strpos($script->innertext, 'newestShots') !== false) {
// fix single quotes // fix single quotes
$script->innertext = preg_replace('/\'(.*)\'(,?)$/im', '"\1"\2', $script->innertext); $script->innertext = preg_replace('/\'(.*)\'(,?)$/im', '"\1"\2', $script->innertext);
// fix JavaScript JSON (why do they not adhere to the standard?) // fix JavaScript JSON (why do they not adhere to the standard?)
$script->innertext = preg_replace('/^(\s*)(\w+):/im', '\1"\2":', $script->innertext); $script->innertext = preg_replace('/^(\s*)(\w+):/im', '\1"\2":', $script->innertext);
// fix relative dates, so they are recognized by strtotime // fix relative dates, so they are recognized by strtotime
$script->innertext = preg_replace('/"about ([0-9]+ hours? ago)"(,?)$/im', '"\1"\2', $script->innertext); $script->innertext = preg_replace('/"about ([0-9]+ hours? ago)"(,?)$/im', '"\1"\2', $script->innertext);
// find beginning of JSON array // find beginning of JSON array
$start = strpos($script->innertext, '['); $start = strpos($script->innertext, '[');
// find end of JSON array, compensate for missing character! // find end of JSON array, compensate for missing character!
$end = strpos($script->innertext, '];') + 1; $end = strpos($script->innertext, '];') + 1;
// convert JSON to PHP array // convert JSON to PHP array
$json = json_decode(substr($script->innertext, $start, $end - $start), true); $json = json_decode(substr($script->innertext, $start, $end - $start), true);
break; break;
} }
} }
return $json; return $json;
} }
private function findJsonForShot($shot, $json){ private function findJsonForShot($shot, $json)
foreach($json as $element) { {
if(strpos($shot->getAttribute('id'), (string)$element['id']) !== false) { foreach ($json as $element) {
return $element; if (strpos($shot->getAttribute('id'), (string)$element['id']) !== false) {
} return $element;
} }
}
return null; return null;
} }
private function getImageTag($preview_path, $title){ private function getImageTag($preview_path, $title)
return sprintf( {
'<br /> <a href="%s"><img srcset="%s" alt="%s" /></a>', return sprintf(
$this->getFullSizeImagePath($preview_path), '<br /> <a href="%s"><img srcset="%s" alt="%s" /></a>',
$preview_path, $this->getFullSizeImagePath($preview_path),
$title $preview_path,
); $title
} );
}
private function getFullSizeImagePath($preview_path){ private function getFullSizeImagePath($preview_path)
// Get last image from srcset {
$src_set_urls = explode(',', $preview_path); // Get last image from srcset
$url = end($src_set_urls); $src_set_urls = explode(',', $preview_path);
$url = explode(' ', $url)[1]; $url = end($src_set_urls);
$url = explode(' ', $url)[1];
return htmlspecialchars_decode($url); return htmlspecialchars_decode($url);
} }
} }

View file

@ -1,205 +1,232 @@
<?php <?php
class Drive2ruBridge extends BridgeAbstract {
const MAINTAINER = 'dotter-ak'; class Drive2ruBridge extends BridgeAbstract
const NAME = 'Drive2.ru'; {
const URI = 'https://drive2.ru/'; const MAINTAINER = 'dotter-ak';
const DESCRIPTION = 'Лента новостей и тестдрайвов, бортжурналов по выбранной марке или модели const NAME = 'Drive2.ru';
const URI = 'https://drive2.ru/';
const DESCRIPTION = 'Лента новостей и тестдрайвов, бортжурналов по выбранной марке или модели
(также работает с фильтром по категориям), блогов пользователей и публикаций по темам.'; (также работает с фильтром по категориям), блогов пользователей и публикаций по темам.';
const PARAMETERS = array( const PARAMETERS = [
'Новости и тест-драйвы' => array(), 'Новости и тест-драйвы' => [],
'Бортжурналы (По модели или марке)' => array( 'Бортжурналы (По модели или марке)' => [
'url' => array( 'url' => [
'name' => 'Ссылка на страницу с бортжурналом', 'name' => 'Ссылка на страницу с бортжурналом',
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'title' => 'Например: https://www.drive2.ru/experience/suzuki/g4895/', 'title' => 'Например: https://www.drive2.ru/experience/suzuki/g4895/',
'exampleValue' => 'https://www.drive2.ru/experience/suzuki/g4895/' 'exampleValue' => 'https://www.drive2.ru/experience/suzuki/g4895/'
), ],
), ],
'Личные блоги' => array( 'Личные блоги' => [
'username' => array( 'username' => [
'name' => 'Никнейм пользователя на сайте', 'name' => 'Никнейм пользователя на сайте',
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'title' => 'Например: Mickey', 'title' => 'Например: Mickey',
'exampleValue' => 'Mickey' 'exampleValue' => 'Mickey'
) ]
), ],
'Публикации по темам (Стоит почитать)' => array( 'Публикации по темам (Стоит почитать)' => [
'topic' => array( 'topic' => [
'name' => 'Темы', 'name' => 'Темы',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'Автозвук' => '16', 'Автозвук' => '16',
'Автомобильный дизайн' => '10', 'Автомобильный дизайн' => '10',
'Автоспорт' => '11', 'Автоспорт' => '11',
'Автошоу, музеи, выставки' => '12', 'Автошоу, музеи, выставки' => '12',
'Безопасность' => '18', 'Безопасность' => '18',
'Беспилотные автомобили' => '15', 'Беспилотные автомобили' => '15',
'Видеосюжеты' => '20', 'Видеосюжеты' => '20',
'Вне дорог' => '21', 'Вне дорог' => '21',
'Встречи' => '22', 'Встречи' => '22',
'Выбор и покупка машины' => '23', 'Выбор и покупка машины' => '23',
'Гаджеты' => '30', 'Гаджеты' => '30',
'Гибридные машины' => '32', 'Гибридные машины' => '32',
'Грузовики, автобусы, спецтехника' => '31', 'Грузовики, автобусы, спецтехника' => '31',
'Доработка интерьера' => '35', 'Доработка интерьера' => '35',
'Законодательство' => '40', 'Законодательство' => '40',
'История автомобилестроения' => '50', 'История автомобилестроения' => '50',
'Мототехника' => '60', 'Мототехника' => '60',
'Новые модели и концепты' => '85', 'Новые модели и концепты' => '85',
'Обучение вождению' => '70', 'Обучение вождению' => '70',
'Путешествия' => '80', 'Путешествия' => '80',
'Ремонт и обслуживание' => '90', 'Ремонт и обслуживание' => '90',
'Реставрация ретро-авто' => '91', 'Реставрация ретро-авто' => '91',
'Сделай сам' => '104', 'Сделай сам' => '104',
'Смешное' => '103', 'Смешное' => '103',
'Спорткары' => '102', 'Спорткары' => '102',
'Стайлинг' => '101', 'Стайлинг' => '101',
'Тест-драйвы' => '110', 'Тест-драйвы' => '110',
'Тюнинг' => '111', 'Тюнинг' => '111',
'Фотосессии' => '120', 'Фотосессии' => '120',
'Шины и диски' => '140', 'Шины и диски' => '140',
'Электрика' => '130', 'Электрика' => '130',
'Электромобили' => '131' 'Электромобили' => '131'
), ],
'defaultValue' => '16', 'defaultValue' => '16',
) ]
), ],
'global' => array( 'global' => [
'full_articles' => array( 'full_articles' => [
'name' => 'Загружать в ленту полный текст', 'name' => 'Загружать в ленту полный текст',
'type' => 'checkbox' 'type' => 'checkbox'
) ]
) ]
); ];
private $title; private $title;
private function getUserContent($url) { private function getUserContent($url)
$html = getSimpleHTMLDOM($url); {
$this->title = $html->find('title', 0)->innertext; $html = getSimpleHTMLDOM($url);
$articles = $html->find('div.js-entity'); $this->title = $html->find('title', 0)->innertext;
foreach ($articles as $article) { $articles = $html->find('div.js-entity');
$item = array(); foreach ($articles as $article) {
$item['title'] = $article->find('a.c-link--text', 0)->plaintext; $item = [];
$item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 0)->href); $item['title'] = $article->find('a.c-link--text', 0)->plaintext;
if($this->getInput('full_articles')) { $item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 0)->href);
$item['content'] = $this->addCommentsLink( if ($this->getInput('full_articles')) {
$this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.c-post__body', 0))->innertext, $item['content'] = $this->addCommentsLink(
$item['uri'] $this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.c-post__body', 0))->innertext,
); $item['uri']
} else { );
$item['content'] = $this->addReadMoreLink($article->find('div.c-post-preview__lead', 0), $item['uri']); } else {
} $item['content'] = $this->addReadMoreLink($article->find('div.c-post-preview__lead', 0), $item['uri']);
$item['author'] = $article->find('a.c-username--wrap', 0)->plaintext; }
if (!is_null($article->find('img', 1))) $item['enclosures'][] = $article->find('img', 1)->src; $item['author'] = $article->find('a.c-username--wrap', 0)->plaintext;
$this->items[] = $item; if (!is_null($article->find('img', 1))) {
} $item['enclosures'][] = $article->find('img', 1)->src;
} }
$this->items[] = $item;
}
}
private function getLogbooksContent($url) { private function getLogbooksContent($url)
$html = getSimpleHTMLDOM($url); {
$this->title = $html->find('title', 0)->innertext; $html = getSimpleHTMLDOM($url);
$articles = $html->find('div.js-entity'); $this->title = $html->find('title', 0)->innertext;
foreach ($articles as $article) { $articles = $html->find('div.js-entity');
$item = array(); foreach ($articles as $article) {
$item['title'] = $article->find('a.c-link--text', 1)->plaintext; $item = [];
$item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 1)->href); $item['title'] = $article->find('a.c-link--text', 1)->plaintext;
if($this->getInput('full_articles')) { $item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 1)->href);
$item['content'] = $this->addCommentsLink( if ($this->getInput('full_articles')) {
$this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.c-post__body', 0))->innertext, $item['content'] = $this->addCommentsLink(
$item['uri'] $this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.c-post__body', 0))->innertext,
); $item['uri']
} else { );
$item['content'] = $this->addReadMoreLink($article->find('div.c-post-preview__lead', 0), $item['uri']); } else {
} $item['content'] = $this->addReadMoreLink($article->find('div.c-post-preview__lead', 0), $item['uri']);
$item['author'] = $article->find('a.c-username--wrap', 0)->plaintext; }
if (!is_null($article->find('img', 1))) $item['enclosures'][] = $article->find('img', 1)->src; $item['author'] = $article->find('a.c-username--wrap', 0)->plaintext;
$this->items[] = $item; if (!is_null($article->find('img', 1))) {
} $item['enclosures'][] = $article->find('img', 1)->src;
} }
$this->items[] = $item;
}
}
private function getNews() { private function getNews()
$html = getSimpleHTMLDOM('https://www.drive2.ru/editorial/'); {
$this->title = $html->find('title', 0)->innertext; $html = getSimpleHTMLDOM('https://www.drive2.ru/editorial/');
$articles = $html->find('div.c-article-card'); $this->title = $html->find('title', 0)->innertext;
foreach ($articles as $article) { $articles = $html->find('div.c-article-card');
$item = array(); foreach ($articles as $article) {
$item['title'] = $article->find('a.c-link--text', 0)->plaintext; $item = [];
$item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 0)->href); $item['title'] = $article->find('a.c-link--text', 0)->plaintext;
if($this->getInput('full_articles')) { $item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 0)->href);
$item['content'] = $this->addCommentsLink( if ($this->getInput('full_articles')) {
$this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.article', 0))->innertext, $item['content'] = $this->addCommentsLink(
$item['uri'] $this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.article', 0))->innertext,
); $item['uri']
} else { );
$item['content'] = $this->addReadMoreLink($article->find('div.c-article-card__lead', 0), $item['uri']); } else {
} $item['content'] = $this->addReadMoreLink($article->find('div.c-article-card__lead', 0), $item['uri']);
$item['author'] = 'Новости и тест-драйвы на Drive2.ru'; }
if (!is_null($article->find('img', 0))) $item['enclosures'][] = $article->find('img', 0)->src; $item['author'] = 'Новости и тест-драйвы на Drive2.ru';
$this->items[] = $item; if (!is_null($article->find('img', 0))) {
} $item['enclosures'][] = $article->find('img', 0)->src;
} }
$this->items[] = $item;
}
}
private function adjustContent($content) { private function adjustContent($content)
foreach ($content->find('div.o-group') as $node) {
$node->outertext = ''; foreach ($content->find('div.o-group') as $node) {
foreach($content->find('div, span') as $attrs) $node->outertext = '';
foreach ($attrs->getAllAttributes() as $attr => $val) }
$attrs->removeAttribute($attr); foreach ($content->find('div, span') as $attrs) {
foreach ($content->getElementsByTagName('figcaption') as $attrs) foreach ($attrs->getAllAttributes() as $attr => $val) {
$attrs->setAttribute( $attrs->removeAttribute($attr);
'style', }
'font-style: italic; font-size: small; margin: 0 100px 75px;'); }
foreach ($content->find('script') as $node) foreach ($content->getElementsByTagName('figcaption') as $attrs) {
$node->outertext = ''; $attrs->setAttribute(
foreach ($content->find('iframe') as $node) { 'style',
preg_match('/embed\/(.*?)\?/', $node->src, $match); 'font-style: italic; font-size: small; margin: 0 100px 75px;'
$node->outertext = '<a href="https://www.youtube.com/watch?v=' . $match[1] . );
'">https://www.youtube.com/watch?v=' . $match[1] . '</a>'; }
} foreach ($content->find('script') as $node) {
return $content; $node->outertext = '';
} }
foreach ($content->find('iframe') as $node) {
preg_match('/embed\/(.*?)\?/', $node->src, $match);
$node->outertext = '<a href="https://www.youtube.com/watch?v=' . $match[1] .
'">https://www.youtube.com/watch?v=' . $match[1] . '</a>';
}
return $content;
}
private function addCommentsLink ($content, $url) { private function addCommentsLink($content, $url)
return $content . '<br><a href="' . $url . '#comments">Перейти к комментариям</a>'; {
} return $content . '<br><a href="' . $url . '#comments">Перейти к комментариям</a>';
}
private function addReadMoreLink ($content, $url) { private function addReadMoreLink($content, $url)
if (!is_null($content)) {
return preg_replace('!\s+!', ' ', str_replace('Читать дальше', '', $content->plaintext)) . if (!is_null($content)) {
'<br><a href="' . $url . '">Читать далее</a>'; return preg_replace('!\s+!', ' ', str_replace('Читать дальше', '', $content->plaintext)) .
else return ''; '<br><a href="' . $url . '">Читать далее</a>';
} } else {
return '';
}
}
public function collectData() { public function collectData()
switch($this->queriedContext) { {
default: switch ($this->queriedContext) {
case 'Новости и тест-драйвы': default:
$this->getNews(); case 'Новости и тест-драйвы':
break; $this->getNews();
case 'Бортжурналы (По модели или марке)': break;
if (!preg_match('/^https:\/\/www.drive2.ru\/experience/', $this->getInput('url'))) case 'Бортжурналы (По модели или марке)':
returnServerError('Invalid url'); if (!preg_match('/^https:\/\/www.drive2.ru\/experience/', $this->getInput('url'))) {
$this->getLogbooksContent($this->getInput('url')); returnServerError('Invalid url');
break; }
case 'Личные блоги': $this->getLogbooksContent($this->getInput('url'));
if (!preg_match('/^[a-zA-Z0-9-]{3,16}$/', $this->getInput('username'))) break;
returnServerError('Invalid username'); case 'Личные блоги':
$this->getUserContent('https://www.drive2.ru/users/' . $this->getInput('username')); if (!preg_match('/^[a-zA-Z0-9-]{3,16}$/', $this->getInput('username'))) {
break; returnServerError('Invalid username');
case 'Публикации по темам (Стоит почитать)': }
$this->getUserContent('https://www.drive2.ru/topics/' . $this->getInput('topic')); $this->getUserContent('https://www.drive2.ru/users/' . $this->getInput('username'));
break; break;
} case 'Публикации по темам (Стоит почитать)':
} $this->getUserContent('https://www.drive2.ru/topics/' . $this->getInput('topic'));
break;
}
}
public function getName() { public function getName()
return $this->title ?: parent::getName(); {
} return $this->title ?: parent::getName();
}
public function getIcon() { public function getIcon()
return 'https://www.drive2.ru/favicon.ico'; {
} return 'https://www.drive2.ru/favicon.ico';
}
} }

View file

@ -1,42 +1,44 @@
<?php <?php
class DuckDuckGoBridge extends BridgeAbstract {
const MAINTAINER = 'Astalaseven'; class DuckDuckGoBridge extends BridgeAbstract
const NAME = 'DuckDuckGo'; {
const URI = 'https://duckduckgo.com/'; const MAINTAINER = 'Astalaseven';
const CACHE_TIMEOUT = 21600; // 6h const NAME = 'DuckDuckGo';
const DESCRIPTION = 'Returns results from DuckDuckGo.'; const URI = 'https://duckduckgo.com/';
const CACHE_TIMEOUT = 21600; // 6h
const DESCRIPTION = 'Returns results from DuckDuckGo.';
const SORT_DATE = '+sort:date'; const SORT_DATE = '+sort:date';
const SORT_RELEVANCE = ''; const SORT_RELEVANCE = '';
const PARAMETERS = array( array( const PARAMETERS = [ [
'u' => array( 'u' => [
'name' => 'keyword', 'name' => 'keyword',
'exampleValue' => 'duck', 'exampleValue' => 'duck',
'required' => true 'required' => true
), ],
'sort' => array( 'sort' => [
'name' => 'sort by', 'name' => 'sort by',
'type' => 'list', 'type' => 'list',
'required' => false, 'required' => false,
'values' => array( 'values' => [
'date' => self::SORT_DATE, 'date' => self::SORT_DATE,
'relevance' => self::SORT_RELEVANCE 'relevance' => self::SORT_RELEVANCE
), ],
'defaultValue' => self::SORT_DATE 'defaultValue' => self::SORT_DATE
) ]
)); ]];
public function collectData(){ public function collectData()
$html = getSimpleHTMLDOM(self::URI . 'html/?kd=-1&q=' . $this->getInput('u') . $this->getInput('sort')); {
$html = getSimpleHTMLDOM(self::URI . 'html/?kd=-1&q=' . $this->getInput('u') . $this->getInput('sort'));
foreach($html->find('div.result') as $element) { foreach ($html->find('div.result') as $element) {
$item = array(); $item = [];
$item['uri'] = $element->find('a.result__a', 0)->href; $item['uri'] = $element->find('a.result__a', 0)->href;
$item['title'] = $element->find('h2.result__title', 0)->plaintext; $item['title'] = $element->find('h2.result__title', 0)->plaintext;
$item['content'] = $element->find('a.result__snippet', 0)->plaintext; $item['content'] = $element->find('a.result__snippet', 0)->plaintext;
$this->items[] = $item; $this->items[] = $item;
} }
} }
} }

View file

@ -1,111 +1,118 @@
<?php <?php
class EZTVBridge extends BridgeAbstract {
const MAINTAINER = 'alexAubin'; class EZTVBridge extends BridgeAbstract
const NAME = 'EZTV'; {
const URI = 'https://eztv.re/'; const MAINTAINER = 'alexAubin';
const DESCRIPTION = 'Returns list of torrents for specific show(s) const NAME = 'EZTV';
const URI = 'https://eztv.re/';
const DESCRIPTION = 'Returns list of torrents for specific show(s)
on EZTV. Get IMDB IDs from IMDB.'; on EZTV. Get IMDB IDs from IMDB.';
const PARAMETERS = array( const PARAMETERS = [
array( [
'ids' => array( 'ids' => [
'name' => 'Show IMDB IDs', 'name' => 'Show IMDB IDs',
'exampleValue' => '8740790,1733785', 'exampleValue' => '8740790,1733785',
'required' => true, 'required' => true,
'title' => 'One or more IMDB show IDs (can be found in the IMDB show URL)' 'title' => 'One or more IMDB show IDs (can be found in the IMDB show URL)'
), ],
'no480' => array( 'no480' => [
'name' => 'No 480p', 'name' => 'No 480p',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Activate to exclude 480p torrents' 'title' => 'Activate to exclude 480p torrents'
), ],
'no720' => array( 'no720' => [
'name' => 'No 720p', 'name' => 'No 720p',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Activate to exclude 720p torrents' 'title' => 'Activate to exclude 720p torrents'
), ],
'no1080' => array( 'no1080' => [
'name' => 'No 1080p', 'name' => 'No 1080p',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Activate to exclude 1080p torrents' 'title' => 'Activate to exclude 1080p torrents'
), ],
'no2160' => array( 'no2160' => [
'name' => 'No 2160p', 'name' => 'No 2160p',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Activate to exclude 2160p torrents' 'title' => 'Activate to exclude 2160p torrents'
), ],
'noUnknownRes' => array( 'noUnknownRes' => [
'name' => 'No Unknown resolution', 'name' => 'No Unknown resolution',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Activate to exclude unknown resolution torrents' 'title' => 'Activate to exclude unknown resolution torrents'
), ],
) ]
); ];
// Shamelessly lifted from https://stackoverflow.com/a/2510459 // Shamelessly lifted from https://stackoverflow.com/a/2510459
protected function formatBytes($bytes, $precision = 2) { protected function formatBytes($bytes, $precision = 2)
$units = array('B', 'KB', 'MB', 'GB', 'TB'); {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0); $bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1); $pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow); $bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow]; return round($bytes, $precision) . ' ' . $units[$pow];
} }
protected function getItemFromTorrent($torrent){ protected function getItemFromTorrent($torrent)
$item = array(); {
$item['uri'] = $torrent->episode_url; $item = [];
$item['author'] = $torrent->imdb_id; $item['uri'] = $torrent->episode_url;
$item['timestamp'] = date('d F Y H:i:s', $torrent->date_released_unix); $item['author'] = $torrent->imdb_id;
$item['title'] = $torrent->title; $item['timestamp'] = date('d F Y H:i:s', $torrent->date_released_unix);
$item['enclosures'][] = $torrent->torrent_url; $item['title'] = $torrent->title;
$item['enclosures'][] = $torrent->torrent_url;
$thumbnailUri = 'https:' . $torrent->small_screenshot; $thumbnailUri = 'https:' . $torrent->small_screenshot;
$torrentSize = $this->formatBytes($torrent->size_bytes); $torrentSize = $this->formatBytes($torrent->size_bytes);
$item['content'] = $torrent->filename . '<br>File size: ' $item['content'] = $torrent->filename . '<br>File size: '
. $torrentSize . '<br><a href="' . $torrent->magnet_url . $torrentSize . '<br><a href="' . $torrent->magnet_url
. '">magnet link</a><br><a href="' . $torrent->torrent_url . '">magnet link</a><br><a href="' . $torrent->torrent_url
. '">torrent link</a><br><img src="' . $thumbnailUri . '" />'; . '">torrent link</a><br><img src="' . $thumbnailUri . '" />';
return $item; return $item;
} }
private static function compareDate($torrent1, $torrent2) { private static function compareDate($torrent1, $torrent2)
return (strtotime($torrent1['timestamp']) < strtotime($torrent2['timestamp']) ? 1 : -1); {
} return (strtotime($torrent1['timestamp']) < strtotime($torrent2['timestamp']) ? 1 : -1);
}
public function collectData(){ public function collectData()
$showIds = explode(',', $this->getInput('ids')); {
$showIds = explode(',', $this->getInput('ids'));
foreach($showIds as $showId) { foreach ($showIds as $showId) {
$eztvUri = $this->getURI() . 'api/get-torrents?imdb_id=' . $showId; $eztvUri = $this->getURI() . 'api/get-torrents?imdb_id=' . $showId;
$content = getContents($eztvUri); $content = getContents($eztvUri);
$torrents = json_decode($content)->torrents; $torrents = json_decode($content)->torrents;
foreach($torrents as $torrent) { foreach ($torrents as $torrent) {
$title = $torrent->title; $title = $torrent->title;
$regex480 = '/480p/'; $regex480 = '/480p/';
$regex720 = '/720p/'; $regex720 = '/720p/';
$regex1080 = '/1080p/'; $regex1080 = '/1080p/';
$regex2160 = '/2160p/'; $regex2160 = '/2160p/';
$regexUnknown = '/(480p|720p|1080p|2160p)/'; $regexUnknown = '/(480p|720p|1080p|2160p)/';
// Skip unwanted resolution torrents // Skip unwanted resolution torrents
if ((preg_match($regex480, $title) === 1 && $this->getInput('no480')) if (
|| (preg_match($regex720, $title) === 1 && $this->getInput('no720')) (preg_match($regex480, $title) === 1 && $this->getInput('no480'))
|| (preg_match($regex1080, $title) === 1 && $this->getInput('no1080')) || (preg_match($regex720, $title) === 1 && $this->getInput('no720'))
|| (preg_match($regex2160, $title) === 1 && $this->getInput('no2160')) || (preg_match($regex1080, $title) === 1 && $this->getInput('no1080'))
|| (preg_match($regexUnknown, $title) !== 1 && $this->getInput('noUnknownRes'))) { || (preg_match($regex2160, $title) === 1 && $this->getInput('no2160'))
continue; || (preg_match($regexUnknown, $title) !== 1 && $this->getInput('noUnknownRes'))
} ) {
continue;
}
$this->items[] = $this->getItemFromTorrent($torrent); $this->items[] = $this->getItemFromTorrent($torrent);
} }
} }
// Sort all torrents in array by date // Sort all torrents in array by date
usort($this->items, array('EZTVBridge', 'compareDate')); usort($this->items, ['EZTVBridge', 'compareDate']);
} }
} }

View file

@ -1,143 +1,149 @@
<?php <?php
class EconomistBridge extends FeedExpander {
const MAINTAINER = 'bockiii'; class EconomistBridge extends FeedExpander
const NAME = 'Economist Bridge'; {
const URI = 'https://www.economist.com/'; const MAINTAINER = 'bockiii';
const CACHE_TIMEOUT = 3600; //1hour const NAME = 'Economist Bridge';
const DESCRIPTION = 'Returns the latest articles for the selected category'; const URI = 'https://www.economist.com/';
const CACHE_TIMEOUT = 3600; //1hour
const DESCRIPTION = 'Returns the latest articles for the selected category';
const PARAMETERS = array( const PARAMETERS = [
'global' => array( 'global' => [
'limit' => array( 'limit' => [
'name' => 'Feed Item Limit', 'name' => 'Feed Item Limit',
'required' => true, 'required' => true,
'type' => 'number', 'type' => 'number',
'defaultValue' => 10, 'defaultValue' => 10,
'title' => 'Maximum number of returned feed items. Maximum 30, default 10' 'title' => 'Maximum number of returned feed items. Maximum 30, default 10'
) ]
), ],
'Topics' => array( 'Topics' => [
'topic' => array( 'topic' => [
'name' => 'Topics', 'name' => 'Topics',
'type' => 'list', 'type' => 'list',
'title' => 'Select a Topic', 'title' => 'Select a Topic',
'defaultValue' => 'latest', 'defaultValue' => 'latest',
'values' => array( 'values' => [
'Latest' => 'latest', 'Latest' => 'latest',
'The world this week' => 'the-world-this-week', 'The world this week' => 'the-world-this-week',
'Letters' => 'letters', 'Letters' => 'letters',
'Leaders' => 'leaders', 'Leaders' => 'leaders',
'Briefings' => 'briefing', 'Briefings' => 'briefing',
'Special reports' => 'special-report', 'Special reports' => 'special-report',
'Britain' => 'britain', 'Britain' => 'britain',
'Europe' => 'europe', 'Europe' => 'europe',
'United States' => 'united-states', 'United States' => 'united-states',
'The Americas' => 'the-americas', 'The Americas' => 'the-americas',
'Middle East and Africa' => 'middle-east-and-africa', 'Middle East and Africa' => 'middle-east-and-africa',
'Asia' => 'asia', 'Asia' => 'asia',
'China' => 'china', 'China' => 'china',
'International' => 'international', 'International' => 'international',
'Business' => 'business', 'Business' => 'business',
'Finance and economics' => 'finance-and-economics', 'Finance and economics' => 'finance-and-economics',
'Science and technology' => 'science-and-technology', 'Science and technology' => 'science-and-technology',
'Books and arts' => 'books-and-arts', 'Books and arts' => 'books-and-arts',
'Obituaries' => 'obituary', 'Obituaries' => 'obituary',
'Graphic detail' => 'graphic-detail', 'Graphic detail' => 'graphic-detail',
'Indicators' => 'economic-and-financial-indicators', 'Indicators' => 'economic-and-financial-indicators',
) ]
) ]
), ],
'Blogs' => array( 'Blogs' => [
'blog' => array( 'blog' => [
'name' => 'Blogs', 'name' => 'Blogs',
'type' => 'list', 'type' => 'list',
'title' => 'Select a Blog', 'title' => 'Select a Blog',
'values' => array( 'values' => [
'Bagehots notebook' => 'bagehots-notebook', 'Bagehots notebook' => 'bagehots-notebook',
'Bartleby' => 'bartleby', 'Bartleby' => 'bartleby',
'Buttonwoods notebook' => 'buttonwoods-notebook', 'Buttonwoods notebook' => 'buttonwoods-notebook',
'Charlemagnes notebook' => 'charlemagnes-notebook', 'Charlemagnes notebook' => 'charlemagnes-notebook',
'Democracy in America' => 'democracy-in-america', 'Democracy in America' => 'democracy-in-america',
'Erasmus' => 'erasmus', 'Erasmus' => 'erasmus',
'Free exchange' => 'free-exchange', 'Free exchange' => 'free-exchange',
'Game theory' => 'game-theory', 'Game theory' => 'game-theory',
'Gulliver' => 'gulliver', 'Gulliver' => 'gulliver',
'Kaffeeklatsch' => 'kaffeeklatsch', 'Kaffeeklatsch' => 'kaffeeklatsch',
'Prospero' => 'prospero', 'Prospero' => 'prospero',
'The Economist Explains' => 'the-economist-explains', 'The Economist Explains' => 'the-economist-explains',
) ]
) ]
) ]
); ];
public function collectData(){ public function collectData()
// get if topics or blogs were selected and store the selected category {
switch ($this->queriedContext) { // get if topics or blogs were selected and store the selected category
case 'Topics': switch ($this->queriedContext) {
$category = $this->getInput('topic'); case 'Topics':
break; $category = $this->getInput('topic');
case 'Blogs': break;
$category = $this->getInput('blog'); case 'Blogs':
break; $category = $this->getInput('blog');
default: break;
$category = 'latest'; default:
} $category = 'latest';
// limit the returned articles to 30 at max }
if ((int)$this->getInput('limit') <= 30) { // limit the returned articles to 30 at max
$limit = (int)$this->getInput('limit'); if ((int)$this->getInput('limit') <= 30) {
} else { $limit = (int)$this->getInput('limit');
$limit = 30; } else {
} $limit = 30;
}
$this->collectExpandableDatas('https://www.economist.com/' . $category . '/rss.xml', $limit); $this->collectExpandableDatas('https://www.economist.com/' . $category . '/rss.xml', $limit);
} }
protected function parseItem($feedItem){ protected function parseItem($feedItem)
$item = parent::parseItem($feedItem); {
$article = getSimpleHTMLDOM($item['uri']); $item = parent::parseItem($feedItem);
// before the article can be added, it needs to be cleaned up, thus, the extra function $article = getSimpleHTMLDOM($item['uri']);
// We also need to distinguish between old style and new style articles // before the article can be added, it needs to be cleaned up, thus, the extra function
if ($article->find('article', 0)->getAttribute('data-test-id') == 'Article') { // We also need to distinguish between old style and new style articles
$contentNode = 'div.layout-article-body'; if ($article->find('article', 0)->getAttribute('data-test-id') == 'Article') {
$imgNode = 'div.article__lead-image'; $contentNode = 'div.layout-article-body';
$categoryNode = 'span.article__subheadline'; $imgNode = 'div.article__lead-image';
} elseif ($article->find('article', 0)->getAttribute('data-test-id') === 'NewArticle') { $categoryNode = 'span.article__subheadline';
$contentNode = 'section'; } elseif ($article->find('article', 0)->getAttribute('data-test-id') === 'NewArticle') {
$imgNode = 'figure.css-12eysrk.e3y6nua0'; $contentNode = 'section';
$categoryNode = 'span.ern1uyf0'; $imgNode = 'figure.css-12eysrk.e3y6nua0';
} else { $categoryNode = 'span.ern1uyf0';
return; } else {
} return;
}
$item['content'] = $this->cleanContent($article, $contentNode); $item['content'] = $this->cleanContent($article, $contentNode);
// only the article lead image is retained if it's there // only the article lead image is retained if it's there
if (!is_null($article->find($imgNode, 0))) { if (!is_null($article->find($imgNode, 0))) {
$item['enclosures'][] = $article->find($imgNode, 0)->find('img', 0)->getAttribute('src'); $item['enclosures'][] = $article->find($imgNode, 0)->find('img', 0)->getAttribute('src');
} else { } else {
$item['enclosures'][] = ''; $item['enclosures'][] = '';
} }
// add the subheadline as category. This will create a link in new articles // add the subheadline as category. This will create a link in new articles
// and a text in old articles // and a text in old articles
$item['categories'][] = $article->find($categoryNode, 0)->innertext; $item['categories'][] = $article->find($categoryNode, 0)->innertext;
return $item; return $item;
} }
private function cleanContent($article, $contentNode){ private function cleanContent($article, $contentNode)
// the actual article is in this div {
$content = $article->find($contentNode, 0)->innertext; // the actual article is in this div
// clean the article content. Remove all div's since the text is in paragraph elements $content = $article->find($contentNode, 0)->innertext;
foreach (array( // clean the article content. Remove all div's since the text is in paragraph elements
'<div ' foreach (
) as $tag_start) { [
$content = stripRecursiveHTMLSection($content, 'div', $tag_start); '<div '
} ] as $tag_start
// now remove embedded iframes. The podcast postings contain these for example ) {
$content = preg_replace('/<iframe.*?\/iframe>/i', '', $content); $content = stripRecursiveHTMLSection($content, 'div', $tag_start);
// fix the relative links }
$content = defaultLinkTo($content, $this->getURI()); // now remove embedded iframes. The podcast postings contain these for example
$content = preg_replace('/<iframe.*?\/iframe>/i', '', $content);
// fix the relative links
$content = defaultLinkTo($content, $this->getURI());
return $content; return $content;
} }
} }

View file

@ -1,141 +1,143 @@
<?php <?php
class EconomistWorldInBriefBridge extends BridgeAbstract class EconomistWorldInBriefBridge extends BridgeAbstract
{ {
const MAINTAINER = 'sqrtminusone'; const MAINTAINER = 'sqrtminusone';
const NAME = 'Economist the World in Brief Bridge'; const NAME = 'Economist the World in Brief Bridge';
const URI = 'https://www.economist.com/the-world-in-brief'; const URI = 'https://www.economist.com/the-world-in-brief';
const CACHE_TIMEOUT = 3600; // 1 hour const CACHE_TIMEOUT = 3600; // 1 hour
const DESCRIPTION = 'Returns stories from the World in Brief section'; const DESCRIPTION = 'Returns stories from the World in Brief section';
const PARAMETERS = array( const PARAMETERS = [
'' => array( '' => [
'splitGobbets' => array( 'splitGobbets' => [
'name' => 'Split the short stories', 'name' => 'Split the short stories',
'type' => 'checkbox', 'type' => 'checkbox',
'defaultValue' => false, 'defaultValue' => false,
'title' => 'Whether to split the short stories into separate entries' 'title' => 'Whether to split the short stories into separate entries'
), ],
'limit' => array( 'limit' => [
'name' => 'Truncate headers for the short stories', 'name' => 'Truncate headers for the short stories',
'type' => 'number', 'type' => 'number',
'defaultValue' => 100 'defaultValue' => 100
), ],
'agenda' => array( 'agenda' => [
'name' => 'Add agenda for the day', 'name' => 'Add agenda for the day',
'type' => 'checkbox', 'type' => 'checkbox',
'defaultValue' => 'checked' 'defaultValue' => 'checked'
), ],
'agendaPictures' => array( 'agendaPictures' => [
'name' => 'Include pictures to the agenda', 'name' => 'Include pictures to the agenda',
'type' => 'checkbox', 'type' => 'checkbox',
'defaultValue' => 'checked' 'defaultValue' => 'checked'
), ],
'quote' => array( 'quote' => [
'name' => 'Include the quote of the day', 'name' => 'Include the quote of the day',
'type' => 'checkbox' 'type' => 'checkbox'
) ]
) ]
); ];
public function collectData() public function collectData()
{ {
$html = getSimpleHTMLDOM(self::URI); $html = getSimpleHTMLDOM(self::URI);
$gobbets = $html->find('._gobbets', 0); $gobbets = $html->find('._gobbets', 0);
if ($this->getInput('splitGobbets') == 1) { if ($this->getInput('splitGobbets') == 1) {
$this->splitGobbets($gobbets); $this->splitGobbets($gobbets);
} else { } else {
$this->mergeGobbets($gobbets); $this->mergeGobbets($gobbets);
}; };
if ($this->getInput('agenda') == 1) { if ($this->getInput('agenda') == 1) {
$articles = $html->find('._articles', 0); $articles = $html->find('._articles', 0);
$this->collectArticles($articles); $this->collectArticles($articles);
} }
if ($this->getInput('quote') == 1) { if ($this->getInput('quote') == 1) {
$quote = $html->find('._quote-container', 0); $quote = $html->find('._quote-container', 0);
$this->addQuote($quote); $this->addQuote($quote);
} }
} }
private function splitGobbets($gobbets) private function splitGobbets($gobbets)
{ {
$today = new Datetime(); $today = new Datetime();
$today->setTime(0, 0, 0, 0); $today->setTime(0, 0, 0, 0);
$limit = $this->getInput('limit'); $limit = $this->getInput('limit');
foreach ($gobbets->find('._gobbet') as $gobbet) { foreach ($gobbets->find('._gobbet') as $gobbet) {
$title = $gobbet->plaintext; $title = $gobbet->plaintext;
$match = preg_match('/[\.,]/', $title, $matches, PREG_OFFSET_CAPTURE); $match = preg_match('/[\.,]/', $title, $matches, PREG_OFFSET_CAPTURE);
if ($match > 0) { if ($match > 0) {
$point = $matches[0][1]; $point = $matches[0][1];
$title = mb_substr($title, 0, $point); $title = mb_substr($title, 0, $point);
} }
if ($limit && mb_strlen($title) > $limit) { if ($limit && mb_strlen($title) > $limit) {
$title = mb_substr($title, 0, $limit) . '...'; $title = mb_substr($title, 0, $limit) . '...';
} }
$item = array( $item = [
'uri' => self::URI, 'uri' => self::URI,
'title' => $title, 'title' => $title,
'content' => $gobbet->innertext, 'content' => $gobbet->innertext,
'timestamp' => $today->format('U'), 'timestamp' => $today->format('U'),
'uid' => md5($gobbet->plaintext) 'uid' => md5($gobbet->plaintext)
); ];
$this->items[] = $item; $this->items[] = $item;
} }
} }
private function mergeGobbets($gobbets) private function mergeGobbets($gobbets)
{ {
$today = new Datetime(); $today = new Datetime();
$today->setTime(0, 0, 0, 0); $today->setTime(0, 0, 0, 0);
$contents = ''; $contents = '';
foreach ($gobbets->find('._gobbet') as $gobbet) { foreach ($gobbets->find('._gobbet') as $gobbet) {
$contents .= "<p>{$gobbet->innertext}"; $contents .= "<p>{$gobbet->innertext}";
} }
$this->items[] = array( $this->items[] = [
'uri' => self::URI, 'uri' => self::URI,
'title' => 'World in brief at ' . $today->format('Y.m.d'), 'title' => 'World in brief at ' . $today->format('Y.m.d'),
'content' => $contents, 'content' => $contents,
'timestamp' => $today->format('U'), 'timestamp' => $today->format('U'),
'uid' => 'world-in-brief-' . $today->format('U') 'uid' => 'world-in-brief-' . $today->format('U')
); ];
} }
private function collectArticles($articles) private function collectArticles($articles)
{ {
$i = 0; $i = 0;
$today = new Datetime(); $today = new Datetime();
$today->setTime(0, 0, 0, 0); $today->setTime(0, 0, 0, 0);
foreach ($articles->find('._article') as $article) { foreach ($articles->find('._article') as $article) {
$title = $article->find('._headline', 0)->plaintext; $title = $article->find('._headline', 0)->plaintext;
$image = $article->find('._main-image', 0); $image = $article->find('._main-image', 0);
$content = $article->find('._content', 0); $content = $article->find('._content', 0);
$res_content = ''; $res_content = '';
if ($image != null && $this->getInput('agendaPictures') == 1) { if ($image != null && $this->getInput('agendaPictures') == 1) {
$img = $image->find('img', 0); $img = $image->find('img', 0);
$res_content .= '<img src="' . $img->src . '" />'; $res_content .= '<img src="' . $img->src . '" />';
} }
$res_content .= $content->innertext; $res_content .= $content->innertext;
$this->items[] = array( $this->items[] = [
'uri' => self::URI, 'uri' => self::URI,
'title' => $title, 'title' => $title,
'content' => $res_content, 'content' => $res_content,
'timestamp' => $today->format('U'), 'timestamp' => $today->format('U'),
'uid' => 'story-' . $today->format('U') . "{$i}", 'uid' => 'story-' . $today->format('U') . "{$i}",
); ];
$i++; $i++;
} }
} }
private function addQuote($quote) { private function addQuote($quote)
$today = new Datetime(); {
$today->setTime(0, 0, 0, 0); $today = new Datetime();
$this->items[] = array( $today->setTime(0, 0, 0, 0);
'uri' => self::URI, $this->items[] = [
'title' => 'Quote of the day ' . $today->format('Y.m.d'), 'uri' => self::URI,
'content' => $quote->innertext, 'title' => 'Quote of the day ' . $today->format('Y.m.d'),
'timestamp' => $today->format('U'), 'content' => $quote->innertext,
'uid' => 'quote-' . $today->format('U') 'timestamp' => $today->format('U'),
); 'uid' => 'quote-' . $today->format('U')
} ];
}
} }

View file

@ -1,53 +1,55 @@
<?php <?php
class EliteDangerousGalnetBridge extends BridgeAbstract {
const MAINTAINER = 'corenting'; class EliteDangerousGalnetBridge extends BridgeAbstract
const NAME = 'Elite: Dangerous Galnet'; {
const URI = 'https://community.elitedangerous.com/galnet/'; const MAINTAINER = 'corenting';
const CACHE_TIMEOUT = 7200; // 2h const NAME = 'Elite: Dangerous Galnet';
const DESCRIPTION = 'Returns the latest page of news from Galnet'; const URI = 'https://community.elitedangerous.com/galnet/';
const PARAMETERS = array( const CACHE_TIMEOUT = 7200; // 2h
array( const DESCRIPTION = 'Returns the latest page of news from Galnet';
'language' => array( const PARAMETERS = [
'name' => 'Language', [
'type' => 'list', 'language' => [
'values' => array( 'name' => 'Language',
'English' => 'en', 'type' => 'list',
'French' => 'fr', 'values' => [
'German' => 'de' 'English' => 'en',
), 'French' => 'fr',
'defaultValue' => 'en' 'German' => 'de'
) ],
) 'defaultValue' => 'en'
); ]
]
];
public function collectData(){ public function collectData()
$language = $this->getInput('language'); {
$url = 'https://community.elitedangerous.com/'; $language = $this->getInput('language');
$url = $url . $language . '/galnet'; $url = 'https://community.elitedangerous.com/';
$html = getSimpleHTMLDOM($url); $url = $url . $language . '/galnet';
$html = getSimpleHTMLDOM($url);
foreach($html->find('div.article') as $element) { foreach ($html->find('div.article') as $element) {
$item = array(); $item = [];
$uri = $element->find('h3 a', 0)->href; $uri = $element->find('h3 a', 0)->href;
$uri = 'https://community.elitedangerous.com/' . $language . $uri; $uri = 'https://community.elitedangerous.com/' . $language . $uri;
$item['uri'] = $uri; $item['uri'] = $uri;
$item['title'] = $element->find('h3 a', 0)->plaintext; $item['title'] = $element->find('h3 a', 0)->plaintext;
$content = $element->find('p', -1)->innertext; $content = $element->find('p', -1)->innertext;
$item['content'] = $content; $item['content'] = $content;
$date = $element->find('p.small', 0)->innertext; $date = $element->find('p.small', 0)->innertext;
$article_year = substr($date, -4) - 1286; //Convert E:D date to actual date $article_year = substr($date, -4) - 1286; //Convert E:D date to actual date
$date = substr($date, 0, -4) . $article_year; $date = substr($date, 0, -4) . $article_year;
$item['timestamp'] = strtotime($date); $item['timestamp'] = strtotime($date);
$this->items[] = $item; $this->items[] = $item;
} }
//Remove duplicates that sometimes show up on the website //Remove duplicates that sometimes show up on the website
$this->items = array_unique($this->items, SORT_REGULAR); $this->items = array_unique($this->items, SORT_REGULAR);
} }
} }

View file

@ -1,150 +1,141 @@
<?php <?php
class ElloBridge extends BridgeAbstract {
const MAINTAINER = 'teromene'; class ElloBridge extends BridgeAbstract
const NAME = 'Ello Bridge'; {
const URI = 'https://ello.co/'; const MAINTAINER = 'teromene';
const CACHE_TIMEOUT = 4800; //2hours const NAME = 'Ello Bridge';
const DESCRIPTION = 'Returns the newest posts for Ello'; const URI = 'https://ello.co/';
const CACHE_TIMEOUT = 4800; //2hours
const DESCRIPTION = 'Returns the newest posts for Ello';
const PARAMETERS = array( const PARAMETERS = [
'By User' => array( 'By User' => [
'u' => array( 'u' => [
'name' => 'Username', 'name' => 'Username',
'required' => true, 'required' => true,
'exampleValue' => 'zteph', 'exampleValue' => 'zteph',
'title' => 'Username' 'title' => 'Username'
) ]
), ],
'Search' => array( 'Search' => [
's' => array( 's' => [
'name' => 'Search', 'name' => 'Search',
'required' => true, 'required' => true,
'exampleValue' => 'bird', 'exampleValue' => 'bird',
'title' => 'Search' 'title' => 'Search'
) ]
) ]
); ];
public function collectData() { public function collectData()
{
$header = [
'Authorization: Bearer ' . $this->getAPIKey()
];
$header = array( if (!empty($this->getInput('u'))) {
'Authorization: Bearer ' . $this->getAPIKey() $postData = getContents(self::URI . 'api/v2/users/~' . urlencode($this->getInput('u')) . '/posts', $header) or
); returnServerError('Unable to query Ello API.');
} else {
$postData = getContents(self::URI . 'api/v2/posts?terms=' . urlencode($this->getInput('s')), $header) or
returnServerError('Unable to query Ello API.');
}
if(!empty($this->getInput('u'))) { $postData = json_decode($postData);
$postData = getContents(self::URI . 'api/v2/users/~' . urlencode($this->getInput('u')) . '/posts', $header) or $count = 0;
returnServerError('Unable to query Ello API.'); foreach ($postData->posts as $post) {
} else { $item = [];
$postData = getContents(self::URI . 'api/v2/posts?terms=' . urlencode($this->getInput('s')), $header) or $item['author'] = $this->getUsername($post, $postData);
returnServerError('Unable to query Ello API.'); $item['timestamp'] = strtotime($post->created_at);
} $item['title'] = strip_tags($this->findText($post->summary));
$item['content'] = $this->getPostContent($post->body);
$item['enclosures'] = $this->getEnclosures($post, $postData);
$item['uri'] = self::URI . $item['author'] . '/post/' . $post->token;
$content = $post->body;
$postData = json_decode($postData); $this->items[] = $item;
$count = 0; $count += 1;
foreach($postData->posts as $post) { }
}
$item = array(); private function findText($path)
$item['author'] = $this->getUsername($post, $postData); {
$item['timestamp'] = strtotime($post->created_at); foreach ($path as $summaryElement) {
$item['title'] = strip_tags($this->findText($post->summary)); if ($summaryElement->kind == 'text') {
$item['content'] = $this->getPostContent($post->body); return $summaryElement->data;
$item['enclosures'] = $this->getEnclosures($post, $postData); }
$item['uri'] = self::URI . $item['author'] . '/post/' . $post->token; }
$content = $post->body;
$this->items[] = $item; return '';
$count += 1; }
} private function getPostContent($path)
{
$content = '';
foreach ($path as $summaryElement) {
if ($summaryElement->kind == 'text') {
$content .= $summaryElement->data;
} elseif ($summaryElement->kind == 'image') {
$alt = '';
if (property_exists($summaryElement->data, 'alt')) {
$alt = $summaryElement->data->alt;
}
$content .= '<img src="' . $summaryElement->data->url . '" alt="' . $alt . '" />';
}
}
} return $content;
}
private function findText($path) { private function getEnclosures($post, $postData)
{
$assets = [];
foreach ($post->links->assets as $asset) {
foreach ($postData->linked->assets as $assetLink) {
if ($asset == $assetLink->id) {
$assets[] = $assetLink->attachment->original->url;
break;
}
}
}
foreach($path as $summaryElement) { return $assets;
}
if($summaryElement->kind == 'text') { private function getUsername($post, $postData)
return $summaryElement->data; {
} foreach ($postData->linked->users as $user) {
if ($user->id == $post->links->author->id) {
return $user->username;
}
}
}
} private function getAPIKey()
{
$cacheFac = new CacheFactory();
return ''; $cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
$cache->setScope(get_called_class());
$cache->setKey(['key']);
$key = $cache->loadData();
} if ($key == null) {
$keyInfo = getContents(self::URI . 'api/webapp-token') or
returnServerError('Unable to get token.');
$key = json_decode($keyInfo)->token->access_token;
$cache->saveData($key);
}
private function getPostContent($path) { return $key;
}
$content = ''; public function getName()
foreach($path as $summaryElement) { {
if (!is_null($this->getInput('u'))) {
return $this->getInput('u') . ' - Ello Bridge';
}
if($summaryElement->kind == 'text') { return parent::getName();
$content .= $summaryElement->data; }
} elseif ($summaryElement->kind == 'image') {
$alt = '';
if(property_exists($summaryElement->data, 'alt')) {
$alt = $summaryElement->data->alt;
}
$content .= '<img src="' . $summaryElement->data->url . '" alt="' . $alt . '" />';
}
}
return $content;
}
private function getEnclosures($post, $postData) {
$assets = array();
foreach($post->links->assets as $asset) {
foreach($postData->linked->assets as $assetLink) {
if($asset == $assetLink->id) {
$assets[] = $assetLink->attachment->original->url;
break;
}
}
}
return $assets;
}
private function getUsername($post, $postData) {
foreach($postData->linked->users as $user) {
if($user->id == $post->links->author->id) {
return $user->username;
}
}
}
private function getAPIKey() {
$cacheFac = new CacheFactory();
$cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
$cache->setScope(get_called_class());
$cache->setKey(array('key'));
$key = $cache->loadData();
if($key == null) {
$keyInfo = getContents(self::URI . 'api/webapp-token') or
returnServerError('Unable to get token.');
$key = json_decode($keyInfo)->token->access_token;
$cache->saveData($key);
}
return $key;
}
public function getName(){
if(!is_null($this->getInput('u'))) {
return $this->getInput('u') . ' - Ello Bridge';
}
return parent::getName();
}
} }

View file

@ -1,41 +1,44 @@
<?php <?php
class ElsevierBridge extends BridgeAbstract {
const MAINTAINER = 'dvikan'; class ElsevierBridge extends BridgeAbstract
const NAME = 'Elsevier journals recent articles'; {
const URI = 'https://www.journals.elsevier.com/'; const MAINTAINER = 'dvikan';
const CACHE_TIMEOUT = 43200; //12h const NAME = 'Elsevier journals recent articles';
const DESCRIPTION = 'Returns the recent articles published in Elsevier journals'; const URI = 'https://www.journals.elsevier.com/';
const CACHE_TIMEOUT = 43200; //12h
const DESCRIPTION = 'Returns the recent articles published in Elsevier journals';
const PARAMETERS = array( array( const PARAMETERS = [ [
'j' => array( 'j' => [
'name' => 'Journal name', 'name' => 'Journal name',
'required' => true, 'required' => true,
'exampleValue' => 'academic-pediatrics', 'exampleValue' => 'academic-pediatrics',
'title' => 'Insert html-part of your journal' 'title' => 'Insert html-part of your journal'
) ]
)); ]];
public function collectData(){ public function collectData()
// Not all journals have the /recent-articles page {
$url = sprintf('https://www.journals.elsevier.com/%s/recent-articles/', $this->getInput('j')); // Not all journals have the /recent-articles page
$html = getSimpleHTMLDOM($url); $url = sprintf('https://www.journals.elsevier.com/%s/recent-articles/', $this->getInput('j'));
$html = getSimpleHTMLDOM($url);
foreach($html->find('article') as $recentArticle) { foreach ($html->find('article') as $recentArticle) {
$item = []; $item = [];
$item['uri'] = $recentArticle->find('a', 0)->getAttribute('href'); $item['uri'] = $recentArticle->find('a', 0)->getAttribute('href');
$item['title'] = $recentArticle->find('h2', 0)->plaintext; $item['title'] = $recentArticle->find('h2', 0)->plaintext;
$item['author'] = $recentArticle->find('p > span', 0)->plaintext; $item['author'] = $recentArticle->find('p > span', 0)->plaintext;
$publicationDateString = trim($recentArticle->find('p > span', 1)->plaintext); $publicationDateString = trim($recentArticle->find('p > span', 1)->plaintext);
$publicationDate = DateTimeImmutable::createFromFormat('F d, Y', $publicationDateString); $publicationDate = DateTimeImmutable::createFromFormat('F d, Y', $publicationDateString);
if ($publicationDate) { if ($publicationDate) {
$item['timestamp'] = $publicationDate->getTimestamp(); $item['timestamp'] = $publicationDate->getTimestamp();
} }
$this->items[] = $item; $this->items[] = $item;
} }
} }
public function getIcon(): string { public function getIcon(): string
return 'https://cdn.elsevier.io/verona/includes/favicons/favicon-32x32.png'; {
} return 'https://cdn.elsevier.io/verona/includes/favicons/favicon-32x32.png';
}
} }

View file

@ -1,26 +1,30 @@
<?php <?php
class EngadgetBridge extends FeedExpander {
const MAINTAINER = 'IceWreck'; class EngadgetBridge extends FeedExpander
const NAME = 'Engadget Bridge'; {
const URI = 'https://www.engadget.com/'; const MAINTAINER = 'IceWreck';
const CACHE_TIMEOUT = 3600; const NAME = 'Engadget Bridge';
const DESCRIPTION = 'Article content for Engadget.'; const URI = 'https://www.engadget.com/';
const CACHE_TIMEOUT = 3600;
const DESCRIPTION = 'Article content for Engadget.';
public function collectData(){ public function collectData()
$this->collectExpandableDatas(static::URI . 'rss.xml', 15); {
} $this->collectExpandableDatas(static::URI . 'rss.xml', 15);
}
protected function parseItem($newsItem){ protected function parseItem($newsItem)
$item = parent::parseItem($newsItem); {
// $articlePage gets the entire page's contents $item = parent::parseItem($newsItem);
$articlePage = getSimpleHTMLDOM($newsItem->link); // $articlePage gets the entire page's contents
// figure contain's the main article image $articlePage = getSimpleHTMLDOM($newsItem->link);
$article = $articlePage->find('figure', 0); // figure contain's the main article image
// .article-text has the actual article $article = $articlePage->find('figure', 0);
foreach($articlePage->find('.article-text') as $element) // .article-text has the actual article
$article = $article . $element; foreach ($articlePage->find('.article-text') as $element) {
$item['content'] = $article; $article = $article . $element;
return $item; }
} $item['content'] = $article;
return $item;
}
} }

View file

@ -1,92 +1,94 @@
<?php <?php
class EpicgamesBridge extends BridgeAbstract {
const NAME = 'Epic Games Store News'; class EpicgamesBridge extends BridgeAbstract
const MAINTAINER = 'otakuf'; {
const URI = 'https://www.epicgames.com'; const NAME = 'Epic Games Store News';
const DESCRIPTION = 'Returns the latest posts from epicgames.com'; const MAINTAINER = 'otakuf';
const CACHE_TIMEOUT = 3600; // 60min const URI = 'https://www.epicgames.com';
const DESCRIPTION = 'Returns the latest posts from epicgames.com';
const CACHE_TIMEOUT = 3600; // 60min
const PARAMETERS = array( array( const PARAMETERS = [ [
'postcount' => array( 'postcount' => [
'name' => 'Limit', 'name' => 'Limit',
'type' => 'number', 'type' => 'number',
'required' => true, 'required' => true,
'title' => 'Maximum number of items to return', 'title' => 'Maximum number of items to return',
'defaultValue' => 10, 'defaultValue' => 10,
), ],
'language' => array( 'language' => [
'name' => 'Language', 'name' => 'Language',
'type' => 'list', 'type' => 'list',
'values' => array( 'values' => [
'English' => 'en', 'English' => 'en',
'العربية' => 'ar', 'العربية' => 'ar',
'Deutsch' => 'de', 'Deutsch' => 'de',
'Español (Spain)' => 'es-ES', 'Español (Spain)' => 'es-ES',
'Español (LA)' => 'es-MX', 'Español (LA)' => 'es-MX',
'Français' => 'fr', 'Français' => 'fr',
'Italiano' => 'it', 'Italiano' => 'it',
'日本語' => 'ja', '日本語' => 'ja',
'한국어' => 'ko', '한국어' => 'ko',
'Polski' => 'pl', 'Polski' => 'pl',
'Português (Brasil)' => 'pt-BR', 'Português (Brasil)' => 'pt-BR',
'Русский' => 'ru', 'Русский' => 'ru',
'ไทย' => 'th', 'ไทย' => 'th',
'Türkçe' => 'tr', 'Türkçe' => 'tr',
'简体中文' => 'zh-CN', '简体中文' => 'zh-CN',
'繁體中文' => 'zh-Hant', '繁體中文' => 'zh-Hant',
), ],
'title' => 'Language of blog posts', 'title' => 'Language of blog posts',
'defaultValue' => 'en', 'defaultValue' => 'en',
), ],
)); ]];
public function collectData() { public function collectData()
$api = 'https://store-content.ak.epicgames.com/api/'; {
$api = 'https://store-content.ak.epicgames.com/api/';
// Get sticky posts first // Get sticky posts first
// Example: https://store-content.ak.epicgames.com/api/ru/content/blog/sticky?locale=ru // Example: https://store-content.ak.epicgames.com/api/ru/content/blog/sticky?locale=ru
$urlSticky = $api . $this->getInput('language') . '/content/blog/sticky'; $urlSticky = $api . $this->getInput('language') . '/content/blog/sticky';
// Then get posts // Then get posts
// Example: https://store-content.ak.epicgames.com/api/ru/content/blog?limit=25 // Example: https://store-content.ak.epicgames.com/api/ru/content/blog?limit=25
$urlBlog = $api . $this->getInput('language') . '/content/blog?limit=' . $this->getInput('postcount'); $urlBlog = $api . $this->getInput('language') . '/content/blog?limit=' . $this->getInput('postcount');
$dataSticky = getContents($urlSticky); $dataSticky = getContents($urlSticky);
$dataBlog = getContents($urlBlog); $dataBlog = getContents($urlBlog);
// Merge data // Merge data
$decodedData = array_merge(json_decode($dataSticky), json_decode($dataBlog)); $decodedData = array_merge(json_decode($dataSticky), json_decode($dataBlog));
foreach($decodedData as $key => $value) { foreach ($decodedData as $key => $value) {
$item = array(); $item = [];
$item['uri'] = self::URI . $value->url; $item['uri'] = self::URI . $value->url;
$item['title'] = $value->title; $item['title'] = $value->title;
$item['timestamp'] = $value->date; $item['timestamp'] = $value->date;
$item['author'] = 'Epic Games Store'; $item['author'] = 'Epic Games Store';
if(!empty($value->author)) { if (!empty($value->author)) {
$item['author'] = $value->author; $item['author'] = $value->author;
} }
if(!empty($value->content)) { if (!empty($value->content)) {
$item['content'] = defaultLinkTo($value->content, self::URI); $item['content'] = defaultLinkTo($value->content, self::URI);
} }
if(!empty($value->image)) { if (!empty($value->image)) {
$item['enclosures'][] = $value->image; $item['enclosures'][] = $value->image;
} }
$item['uid'] = $value->_id; $item['uid'] = $value->_id;
$item['id'] = $value->_id; $item['id'] = $value->_id;
$this->items[] = $item; $this->items[] = $item;
} }
// Sort data // Sort data
usort($this->items, function ($item1, $item2) { usort($this->items, function ($item1, $item2) {
if ($item2['timestamp'] == $item1['timestamp']) { if ($item2['timestamp'] == $item1['timestamp']) {
return 0; return 0;
} }
return ($item2['timestamp'] < $item1['timestamp']) ? -1 : 1; return ($item2['timestamp'] < $item1['timestamp']) ? -1 : 1;
}); });
// Limit data // Limit data
$this->items = array_slice($this->items, 0, $this->getInput('postcount')); $this->items = array_slice($this->items, 0, $this->getInput('postcount'));
} }
} }

View file

@ -1,69 +1,75 @@
<?php <?php
class EsquerdaNetBridge extends FeedExpander {
const MAINTAINER = 'somini';
const NAME = 'Esquerda.net';
const URI = 'https://www.esquerda.net';
const DESCRIPTION = 'Esquerda.net';
const PARAMETERS = array(
array(
'feed' => array(
'name' => 'Feed',
'type' => 'list',
'defaultValue' => 'Geral',
'values' => array(
'Geral' => 'geral',
'Dossier' => 'artigos-dossier',
'Vídeo' => 'video',
'Opinião' => 'opinioes',
'Rádio' => 'radio',
)
)
)
);
public function getURI() { class EsquerdaNetBridge extends FeedExpander
$type = $this->getInput('feed'); {
return self::URI . '/rss/' . $type; const MAINTAINER = 'somini';
} const NAME = 'Esquerda.net';
const URI = 'https://www.esquerda.net';
const DESCRIPTION = 'Esquerda.net';
const PARAMETERS = [
[
'feed' => [
'name' => 'Feed',
'type' => 'list',
'defaultValue' => 'Geral',
'values' => [
'Geral' => 'geral',
'Dossier' => 'artigos-dossier',
'Vídeo' => 'video',
'Opinião' => 'opinioes',
'Rádio' => 'radio',
]
]
]
];
public function getIcon() { public function getURI()
return 'https://www.esquerda.net/sites/default/files/favicon_0.ico'; {
} $type = $this->getInput('feed');
return self::URI . '/rss/' . $type;
}
public function collectData(){ public function getIcon()
parent::collectExpandableDatas($this->getURI()); {
} return 'https://www.esquerda.net/sites/default/files/favicon_0.ico';
}
protected function parseItem($newsItem){ public function collectData()
# Fix Publish date {
$badDate = $newsItem->pubDate; parent::collectExpandableDatas($this->getURI());
preg_match('|(?P<day>\d\d)/(?P<month>\d\d)/(?P<year>\d\d\d\d) - (?P<hour>\d\d):(?P<minute>\d\d)|', $badDate, $d); }
$newsItem->pubDate = sprintf('%s-%s-%sT%s:%s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['minute']);
$item = parent::parseItem($newsItem); protected function parseItem($newsItem)
# Include all the content {
$uri = $item['uri']; # Fix Publish date
$html = getSimpleHTMLDOMCached($uri); $badDate = $newsItem->pubDate;
$content = $html->find('div#content div.content', 0); preg_match('|(?P<day>\d\d)/(?P<month>\d\d)/(?P<year>\d\d\d\d) - (?P<hour>\d\d):(?P<minute>\d\d)|', $badDate, $d);
## Fix author $newsItem->pubDate = sprintf('%s-%s-%sT%s:%s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['minute']);
$authorHTML = $html->find('.field-name-field-op-author a', 0); $item = parent::parseItem($newsItem);
if ($authorHTML) { # Include all the content
$item['author'] = $authorHTML->innertext; $uri = $item['uri'];
$authorHTML->remove(); $html = getSimpleHTMLDOMCached($uri);
} $content = $html->find('div#content div.content', 0);
## Remove crap ## Fix author
$content->find('.field-name-addtoany', 0)->remove(); $authorHTML = $html->find('.field-name-field-op-author a', 0);
## Fix links if ($authorHTML) {
$content = defaultLinkTo($content, self::URI); $item['author'] = $authorHTML->innertext;
## Fix Images $authorHTML->remove();
foreach($content->find('img') as $img) { }
$altSrc = $img->getAttribute('data-src'); ## Remove crap
if ($altSrc) { $content->find('.field-name-addtoany', 0)->remove();
$img->setAttribute('src', $altSrc); ## Fix links
} $content = defaultLinkTo($content, self::URI);
$img->width = null; ## Fix Images
$img->height = null; foreach ($content->find('img') as $img) {
} $altSrc = $img->getAttribute('data-src');
$item['content'] = $content; if ($altSrc) {
return $item; $img->setAttribute('src', $altSrc);
} }
$img->width = null;
$img->height = null;
}
$item['content'] = $content;
return $item;
}
} }

View file

@ -1,26 +1,28 @@
<?php <?php
class EstCeQuonMetEnProdBridge extends BridgeAbstract {
const MAINTAINER = 'ORelio'; class EstCeQuonMetEnProdBridge extends BridgeAbstract
const NAME = 'Est-ce qu\'on met en prod aujourd\'hui ?'; {
const URI = 'https://www.estcequonmetenprodaujourdhui.info/'; const MAINTAINER = 'ORelio';
const CACHE_TIMEOUT = 21600; // 6h const NAME = 'Est-ce qu\'on met en prod aujourd\'hui ?';
const DESCRIPTION = 'Should we put a website in production today? (French)'; const URI = 'https://www.estcequonmetenprodaujourdhui.info/';
const CACHE_TIMEOUT = 21600; // 6h
const DESCRIPTION = 'Should we put a website in production today? (French)';
public function collectData() { public function collectData()
$html = getSimpleHTMLDOM(self::URI); {
$html = getSimpleHTMLDOM(self::URI);
$item = array(); $item = [];
$item['uri'] = $this->getURI() . '#' . date('Y-m-d'); $item['uri'] = $this->getURI() . '#' . date('Y-m-d');
$item['title'] = $this->getName(); $item['title'] = $this->getName();
$item['author'] = 'Nicolas Hoffmann'; $item['author'] = 'Nicolas Hoffmann';
$item['timestamp'] = strtotime('today midnight'); $item['timestamp'] = strtotime('today midnight');
$item['content'] = str_replace( $item['content'] = str_replace(
'src="/', 'src="/',
'src="' . self::URI, 'src="' . self::URI,
trim(extractFromDelimiters($html->outertext, '<body role="document">', '<div id="share')) trim(extractFromDelimiters($html->outertext, '<body role="document">', '<div id="share'))
); );
$this->items[] = $item; $this->items[] = $item;
} }
} }

Some files were not shown because too many files have changed in this diff Show more