mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-08-28 13:58:53 +02:00
Merge branch 'master' into FabBridge
This commit is contained in:
commit
71396becd6
17 changed files with 204 additions and 416 deletions
|
@ -1,8 +1,21 @@
|
|||
FROM rssbridge/rss-bridge:latest
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --yes --no-install-recommends \
|
||||
git && \
|
||||
pecl install xdebug && \
|
||||
pear install PHP_CodeSniffer && \
|
||||
docker-php-ext-enable xdebug
|
||||
COPY --chmod=755 post-create-command.sh /usr/local/bin/post-create-command
|
||||
|
||||
ADD https://raw.githubusercontent.com/docker-library/php/master/docker-php-ext-enable /usr/local/bin/docker-php-ext-enable
|
||||
RUN chmod u+x /usr/local/bin/docker-php-ext-enable
|
||||
|
||||
ADD https://getcomposer.org/installer /usr/local/bin/composer-installer.php
|
||||
RUN chmod u+x /usr/local/bin/composer-installer.php
|
||||
RUN php /usr/local/bin/composer-installer.php --check && \
|
||||
php /usr/local/bin/composer-installer.php --filename=composer --install-dir=/usr/local/bin
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
git \
|
||||
php-dev \
|
||||
make \
|
||||
unzip
|
||||
|
||||
RUN pecl install xdebug && \
|
||||
PHP_INI_DIR=/etc/php/8.2/fpm docker-php-ext-enable xdebug
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
"vscode": {
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"php.validate.executablePath": "/usr/local/bin/php",
|
||||
"phpSniffer.executablesFolder": "/usr/local/bin/",
|
||||
"phpcs.executablePath": "/usr/local/bin/phpcs",
|
||||
"php.validate.executablePath": "/usr/bin/php",
|
||||
"phpSniffer.executablesFolder": "/root/.config/composer/vendor/bin",
|
||||
"phpcs.executablePath": "/root/.config/composer/vendor/bin/phpcs",
|
||||
"phpcs.lintOnType": false
|
||||
},
|
||||
|
||||
|
@ -22,6 +22,9 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"remoteEnv": {
|
||||
"PATH": "${containerEnv:PATH}:/root/.config/composer/vendor/bin",
|
||||
},
|
||||
"forwardPorts": [3100, 9000, 9003],
|
||||
"postCreateCommand": "cp .devcontainer/nginx.conf /etc/nginx/conf.d/default.conf && cp .devcontainer/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini && mkdir .vscode && cp .devcontainer/launch.json .vscode && echo '*' > whitelist.txt && chmod a+x \"$(pwd)\" && rm -rf /var/www/html && ln -s \"$(pwd)\" /var/www/html && nginx && php-fpm -D"
|
||||
"postCreateCommand": "/usr/local/bin/post-create-command"
|
||||
}
|
|
@ -9,7 +9,8 @@
|
|||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9003,
|
||||
"auto": true
|
||||
"auto": true,
|
||||
"log": true
|
||||
},
|
||||
{
|
||||
"name": "Launch currently open script",
|
||||
|
|
27
.devcontainer/post-create-command.sh
Executable file
27
.devcontainer/post-create-command.sh
Executable file
|
@ -0,0 +1,27 @@
|
|||
#/bin/sh
|
||||
|
||||
cp .devcontainer/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
cp .devcontainer/xdebug.ini /etc/php/8.2/fpm/conf.d/xdebug.ini
|
||||
|
||||
# This should download some dev-dependencies, like phpunit and the PHP code sniffers
|
||||
composer global require "phpunit/phpunit:^9"
|
||||
composer global require "squizlabs/php_codesniffer:^3.6"
|
||||
composer global require "phpcompatibility/php-compatibility:^9.3"
|
||||
|
||||
# We need to this manually for running the PHPCompatibility ruleset
|
||||
phpcs --config-set installed_paths /root/.config/composer/vendor/phpcompatibility/php-compatibility
|
||||
|
||||
mkdir -p .vscode
|
||||
cp .devcontainer/launch.json .vscode
|
||||
|
||||
echo '*' > whitelist.txt
|
||||
|
||||
chmod a+x $(pwd)
|
||||
rm -rf /var/www/html
|
||||
ln -s $(pwd) /var/www/html
|
||||
|
||||
# Solves possible issue of cache directory not being accessible
|
||||
chown www-data:www-data -R $(pwd)/cache
|
||||
|
||||
nginx
|
||||
php-fpm8.2 -D
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
|
@ -8,7 +8,7 @@ on:
|
|||
|
||||
jobs:
|
||||
phpcs:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.4']
|
||||
|
@ -21,7 +21,7 @@ jobs:
|
|||
- run: phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p
|
||||
|
||||
phpcompatibility:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.4']
|
||||
|
@ -36,7 +36,7 @@ jobs:
|
|||
- run: ~/.composer/vendor/bin/phpcs . --standard=phpcompatibility.xml --warning-severity=0 --extensions=php -p
|
||||
|
||||
executable_php_files_check:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
|
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -8,7 +8,7 @@ on:
|
|||
|
||||
jobs:
|
||||
phpunit8:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.4', '8.0', '8.1']
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
<?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 = [[
|
||||
'username' => [
|
||||
'name' => 'Username',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => 'koethekoethe',
|
||||
]
|
||||
]];
|
||||
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$url = self::URI . '/api/v2/profile?username=' . urlencode($this->getInput('username'));
|
||||
|
||||
$apiJson = getContents($url);
|
||||
|
||||
$apiData = Json::decode($apiJson);
|
||||
if (isset($apiData['error'])) {
|
||||
throw new \Exception($apiData['error_code']);
|
||||
}
|
||||
|
||||
foreach ($apiData['posts'] as $post) {
|
||||
$item = [];
|
||||
|
||||
$item['author'] = 'Anonymous';
|
||||
|
||||
if ($post['senderData']['id'] !== false) {
|
||||
$item['author'] = $post['senderData']['username'];
|
||||
}
|
||||
|
||||
$item['uri'] = $this->getURI() . '/post/' . $post['id'];
|
||||
$item['title'] = $this->ellipsisTitle($post['comment']);
|
||||
|
||||
$item['content'] = $this->processContent($post);
|
||||
$item['timestamp'] = $post['timestamp'];
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI()
|
||||
{
|
||||
if (!is_null($this->getInput('username'))) {
|
||||
return self::URI . '/' . $this->getInput('username');
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
if (!is_null($this->getInput('username'))) {
|
||||
return $this->getInput('username') . ' - Curious Cat';
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
private function processContent($post)
|
||||
{
|
||||
$author = 'Anonymous';
|
||||
|
||||
if ($post['senderData']['id'] !== false) {
|
||||
$authorUrl = self::URI . '/' . $post['senderData']['username'];
|
||||
|
||||
$author = <<<EOD
|
||||
<a href="{$authorUrl}">{$post['senderData']['username']}</a>
|
||||
EOD;
|
||||
}
|
||||
|
||||
$question = $this->formatUrls($post['comment']);
|
||||
$answer = $this->formatUrls($post['reply']);
|
||||
|
||||
$content = <<<EOD
|
||||
<p>{$author} asked:</p>
|
||||
<blockquote>{$question}</blockquote><br/>
|
||||
<p>{$post['addresseeData']['username']} answered:</p>
|
||||
<blockquote>{$answer}</blockquote>
|
||||
EOD;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function ellipsisTitle($text)
|
||||
{
|
||||
$length = 150;
|
||||
|
||||
if (strlen($text) > $length) {
|
||||
$text = explode('<br>', wordwrap($text, $length, '<br>'));
|
||||
return $text[0] . '...';
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function formatUrls($content)
|
||||
{
|
||||
return preg_replace(
|
||||
'/(http[s]{0,1}\:\/\/[a-zA-Z0-9.\/\?\&=\-_]{4,})/ims',
|
||||
'<a target="_blank" href="$1" target="_blank">$1</a> ',
|
||||
$content
|
||||
);
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ class EpicGamesFreeBridge extends BridgeAbstract
|
|||
'Türkçe' => 'tr',
|
||||
'简体中文' => 'zh-CN',
|
||||
'繁體中文' => 'zh-Hant',
|
||||
],
|
||||
],
|
||||
'title' => 'Language for game information',
|
||||
'defaultValue' => 'en-US',
|
||||
],
|
||||
|
@ -51,16 +51,21 @@ class EpicGamesFreeBridge extends BridgeAbstract
|
|||
|
||||
$data = $json['data']['Catalog']['searchStore']['elements'];
|
||||
foreach ($data as $element) {
|
||||
if (!isset($element['promotions']['promotionalOffers'][0])) {
|
||||
$promo = $element['promotions']['promotionalOffers'][0]['promotionalOffers'][0] ?? false;
|
||||
if (
|
||||
!$promo ||
|
||||
$promo['discountSetting']['discountType'] !== 'PERCENTAGE' ||
|
||||
$promo['discountSetting']['discountPercentage'] !== 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$item = [
|
||||
'author' => $element['seller']['name'],
|
||||
'content' => $element['description'],
|
||||
'enclosures' => array_map(fn($item) => $item['url'], $element['keyImages']),
|
||||
'timestamp' => strtotime($element['promotions']['promotionalOffers'][0]['promotionalOffers'][0]['startDate']),
|
||||
'timestamp' => strtotime($promo['startDate']),
|
||||
'title' => $element['title'],
|
||||
'url' => parent::getURI() . $this->getInput('locale') . '/p/' . $element['urlSlug'],
|
||||
'uri' => parent::getURI() . $this->getInput('locale') . '/p/' . $element['productSlug'],
|
||||
];
|
||||
$this->items[] = $item;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ class FabBridge extends BridgeAbstract
|
|||
|
||||
public function collectData()
|
||||
{
|
||||
|
||||
$responseheaders = get_headers(static::URI);
|
||||
|
||||
$csrf = "";
|
||||
|
@ -27,8 +28,8 @@ class FabBridge extends BridgeAbstract
|
|||
'Accept: application/json, text/plain, */*',
|
||||
'Accept-Language: en',
|
||||
'Accept-Encoding: gzip, deflate, br, zstd',
|
||||
'Referer: ' . static::URI,
|
||||
'Cookie: ' . $csrf,
|
||||
'Referer: ' . static::URI
|
||||
];
|
||||
|
||||
$json = getContents($url, $header);
|
||||
|
|
|
@ -31,25 +31,33 @@ class GoComicsBridge extends BridgeAbstract
|
|||
public function collectData()
|
||||
{
|
||||
$link = $this->getURI();
|
||||
$landingpage = getSimpleHTMLDOM($link);
|
||||
$element = $landingpage->find('div[data-post-url]', 0);
|
||||
if ($element) {
|
||||
$link = $element->getAttribute('data-post-url');
|
||||
} else { // fallback for comics without data-post-url (assumes daily comic)
|
||||
$nextcomiclink = $landingpage->find('a[class*="ComicNavigation_controls__button_previous__"]', 0)->href;
|
||||
preg_match('/(\d{4}\/\d{2}\/\d{2})/', $nextcomiclink, $nclmatches);
|
||||
if (!empty($nclmatches[1])) {
|
||||
$nextdate = new DateTime($nclmatches[1]);
|
||||
$nextdate = $nextdate->modify('+1 day')->format('Y/m/d');
|
||||
$link = $link . '/' . $nextdate;
|
||||
} else {
|
||||
throw new \Exception('Could not find the first comic URL. Please create a new GitHub issue.');
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $this->getInput('limit'); $i++) {
|
||||
$html = getSimpleHTMLDOM($link);
|
||||
// get json data from the first page
|
||||
$json = $html->find('div[class^="ShowComicViewer_showComicViewer__comic__"] script[type="application/ld+json"]', 0)->innertext;
|
||||
$data = json_decode($json, false);
|
||||
|
||||
$imagelink = $html->find('meta[property="og:image"]', 0)->content;
|
||||
$parts = explode('/', $link);
|
||||
$date = DateTime::createFromFormat('Y/m/d', implode('/', array_slice($parts, -3)));
|
||||
$title = $html->find('meta[property="og:title"]', 0)->content;
|
||||
preg_match('/by (.*?) for/', $title, $authormatches);
|
||||
$author = $authormatches[1] ?? 'GoComics';
|
||||
|
||||
$item = [];
|
||||
|
||||
$author = $data->author->name;
|
||||
$imagelink = $data->contentUrl;
|
||||
$date = $data->datePublished;
|
||||
$title = $data->name . ' - GoComics';
|
||||
|
||||
// get a permlink for this day's comic if there isn't one specified
|
||||
if ($link === $this->getURI()) {
|
||||
$link = $this->getURI() . '/' . DateTime::createFromFormat('F j, Y', $date)->format('Y/m/d');
|
||||
}
|
||||
|
||||
$item['id'] = $imagelink;
|
||||
$item['uri'] = $link;
|
||||
$item['author'] = $author;
|
||||
|
@ -57,7 +65,7 @@ class GoComicsBridge extends BridgeAbstract
|
|||
if ($this->getInput('date-in-title') === true) {
|
||||
$item['title'] = $title;
|
||||
}
|
||||
$item['timestamp'] = DateTime::createFromFormat('F j, Y', $date)->setTime(0, 0, 0)->getTimestamp();
|
||||
$item['timestamp'] = $date->setTime(0, 0, 0)->getTimestamp();
|
||||
$item['content'] = '<img src="' . $imagelink . '" />';
|
||||
|
||||
$link = rtrim(self::URI, '/') . $html->find('a[class*="ComicNavigation_controls__button_previous__"]', 0)->href;
|
||||
|
|
|
@ -152,7 +152,7 @@ class GolemBridge extends FeedExpander
|
|||
$img->src = $img->getAttribute('data-src-full');
|
||||
}
|
||||
|
||||
foreach ($content->find('p, h1, h2, h3, img[src*="."], iframe, video') as $element) {
|
||||
foreach ($content->find('p, h1, h2, h3, pre, img[src*="."], iframe, video') as $element) {
|
||||
$item .= $element;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,16 @@ class IdealoBridge extends BridgeAbstract
|
|||
]
|
||||
];
|
||||
|
||||
private $headers = [
|
||||
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0',
|
||||
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
|
||||
'Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.5,en;q=0.3'
|
||||
];
|
||||
private $options = [
|
||||
CURLOPT_TRANSFER_ENCODING => 1,
|
||||
CURLOPT_ACCEPT_ENCODING => 'gzip, deflate, br'
|
||||
];
|
||||
|
||||
public function getIcon()
|
||||
{
|
||||
return 'https://cdn.idealo.com/storage/ids-assets/ico/favicon.ico';
|
||||
|
@ -53,10 +63,7 @@ class IdealoBridge extends BridgeAbstract
|
|||
|
||||
// The cache does not contain the title of the bridge, we must get it and save it in the cache
|
||||
if ($product === null) {
|
||||
$header = [
|
||||
'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15'
|
||||
];
|
||||
$html = getSimpleHTMLDOM($link, $header);
|
||||
$html = getSimpleHTMLDOM($link, $this->headers, $this->options);
|
||||
$product = $html->find('.oopStage-title', 0)->find('span', 0)->plaintext;
|
||||
$this->saveCacheValue($keyTITLE, $product);
|
||||
}
|
||||
|
@ -123,13 +130,8 @@ class IdealoBridge extends BridgeAbstract
|
|||
}
|
||||
public function collectData()
|
||||
{
|
||||
// Needs header with user-agent to function properly.
|
||||
$header = [
|
||||
'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15'
|
||||
];
|
||||
|
||||
$link = $this->getInput('Link');
|
||||
$html = getSimpleHTMLDOM($link, $header);
|
||||
$html = getSimpleHTMLDOM($link, $this->headers, $this->options);
|
||||
|
||||
// Get Productname
|
||||
$titleobj = $html->find('.oopStage-title', 0);
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
<?php
|
||||
|
||||
class OpenlyBridge extends BridgeAbstract
|
||||
{
|
||||
const NAME = 'Openly Bridge';
|
||||
const URI = 'https://www.openlynews.com/';
|
||||
const DESCRIPTION = 'Returns news articles';
|
||||
const MAINTAINER = 'VerifiedJoseph';
|
||||
const PARAMETERS = [
|
||||
'All News' => [],
|
||||
'All Opinion' => [],
|
||||
'By Region' => [
|
||||
'region' => [
|
||||
'name' => 'Region',
|
||||
'type' => 'list',
|
||||
'values' => [
|
||||
'Africa' => 'africa',
|
||||
'Asia Pacific' => 'asia-pacific',
|
||||
'Europe' => 'europe',
|
||||
'Latin America' => 'latin-america',
|
||||
'Middle Easta' => 'middle-east',
|
||||
'North America' => 'north-america'
|
||||
]
|
||||
],
|
||||
'content' => [
|
||||
'name' => 'Content',
|
||||
'type' => 'list',
|
||||
'values' => [
|
||||
'News' => 'news',
|
||||
'Opinion' => 'people'
|
||||
],
|
||||
'defaultValue' => 'news'
|
||||
]
|
||||
],
|
||||
'By Tag' => [
|
||||
'tag' => [
|
||||
'name' => 'Tag',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => 'lgbt-law',
|
||||
],
|
||||
'content' => [
|
||||
'name' => 'Content',
|
||||
'type' => 'list',
|
||||
'values' => [
|
||||
'News' => 'news',
|
||||
'Opinion' => 'people'
|
||||
],
|
||||
'defaultValue' => 'news'
|
||||
]
|
||||
],
|
||||
'By Author' => [
|
||||
'profileId' => [
|
||||
'name' => 'Profile ID',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => '003D000002WZGYRIA5',
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
const TEST_DETECT_PARAMETERS = [
|
||||
'https://www.openlynews.com/profile/?id=0033z00002XUTepAAH' => [
|
||||
'context' => 'By Author', 'profileId' => '0033z00002XUTepAAH'
|
||||
],
|
||||
'https://www.openlynews.com/news/?page=1&theme=lgbt-law' => [
|
||||
'context' => 'By Tag', 'content' => 'news', 'tag' => 'lgbt-law'
|
||||
],
|
||||
'https://www.openlynews.com/news/?page=1®ion=north-america' => [
|
||||
'context' => 'By Region', 'content' => 'news', 'region' => 'north-america'
|
||||
],
|
||||
'https://www.openlynews.com/news/?theme=lgbt-law' => [
|
||||
'context' => 'By Tag', 'content' => 'news', 'tag' => 'lgbt-law'
|
||||
],
|
||||
'https://www.openlynews.com/news/?region=north-america' => [
|
||||
'context' => 'By Region', 'content' => 'news', 'region' => 'north-america'
|
||||
]
|
||||
];
|
||||
|
||||
const CACHE_TIMEOUT = 900; // 15 mins
|
||||
const ARTICLE_CACHE_TIMEOUT = 3600; // 1 hour
|
||||
|
||||
private $feedTitle = '';
|
||||
private $itemLimit = 10;
|
||||
|
||||
private $profileUrlRegex = '/openlynews\.com\/profile\/\?id=([a-zA-Z0-9]+)/';
|
||||
private $tagUrlRegex = '/openlynews\.com\/([a-z]+)\/\?(?:page=(?:[0-9]+)&)?theme=([\w-]+)/';
|
||||
private $regionUrlRegex = '/openlynews\.com\/([a-z]+)\/\?(?:page=(?:[0-9]+)&)?region=([\w-]+)/';
|
||||
|
||||
public function detectParameters($url)
|
||||
{
|
||||
$params = [];
|
||||
|
||||
if (preg_match($this->profileUrlRegex, $url, $matches) > 0) {
|
||||
$params['context'] = 'By Author';
|
||||
$params['profileId'] = $matches[1];
|
||||
return $params;
|
||||
}
|
||||
|
||||
if (preg_match($this->tagUrlRegex, $url, $matches) > 0) {
|
||||
$params['context'] = 'By Tag';
|
||||
$params['content'] = $matches[1];
|
||||
$params['tag'] = $matches[2];
|
||||
return $params;
|
||||
}
|
||||
|
||||
if (preg_match($this->regionUrlRegex, $url, $matches) > 0) {
|
||||
$params['context'] = 'By Region';
|
||||
$params['content'] = $matches[1];
|
||||
$params['region'] = $matches[2];
|
||||
return $params;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$url = $this->getAjaxURI();
|
||||
|
||||
if ($this->queriedContext === 'By Author') {
|
||||
$url = $this->getURI();
|
||||
}
|
||||
|
||||
$html = getSimpleHTMLDOM($url);
|
||||
$html = defaultLinkTo($html, $this->getURI());
|
||||
|
||||
if ($html->find('h1', 0)) {
|
||||
$this->feedTitle = $html->find('h1', 0)->plaintext;
|
||||
}
|
||||
|
||||
if ($html->find('h2.title-v4', 0)) {
|
||||
$html->find('span.tooltiptext', 0)->innertext = '';
|
||||
$this->feedTitle = $html->find('a.tooltipitem', 0)->plaintext;
|
||||
}
|
||||
|
||||
$items = $html->find('div.item');
|
||||
$limit = 5;
|
||||
foreach (array_slice($items, 0, $limit) as $div) {
|
||||
$this->items[] = $this->getArticle($div->find('a', 0)->href);
|
||||
|
||||
if (count($this->items) >= $this->itemLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI()
|
||||
{
|
||||
switch ($this->queriedContext) {
|
||||
case 'All News':
|
||||
return self::URI . 'news';
|
||||
break;
|
||||
case 'All Opinion':
|
||||
return self::URI . 'people';
|
||||
break;
|
||||
case 'By Tag':
|
||||
return self::URI . $this->getInput('content') . '/?theme=' . $this->getInput('tag');
|
||||
case 'By Region':
|
||||
return self::URI . $this->getInput('content') . '/?region=' . $this->getInput('region');
|
||||
break;
|
||||
case 'By Author':
|
||||
return self::URI . 'profile/?id=' . $this->getInput('profileId');
|
||||
break;
|
||||
default:
|
||||
return parent::getURI();
|
||||
}
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
switch ($this->queriedContext) {
|
||||
case 'All News':
|
||||
return 'News - Openly';
|
||||
break;
|
||||
case 'All Opinion':
|
||||
return 'Opinion - Openly';
|
||||
break;
|
||||
case 'By Tag':
|
||||
if (empty($this->feedTitle)) {
|
||||
$this->feedTitle = $this->getInput('tag');
|
||||
}
|
||||
|
||||
if ($this->getInput('content') === 'people') {
|
||||
return $this->feedTitle . ' - Opinion - Openly';
|
||||
}
|
||||
|
||||
return $this->feedTitle . ' - Openly';
|
||||
break;
|
||||
case 'By Region':
|
||||
if (empty($this->feedTitle)) {
|
||||
$this->feedTitle = $this->getInput('region');
|
||||
}
|
||||
|
||||
if ($this->getInput('content') === 'people') {
|
||||
return $this->feedTitle . ' - Opinion - Openly';
|
||||
}
|
||||
|
||||
return $this->feedTitle . ' - Openly';
|
||||
break;
|
||||
case 'By Author':
|
||||
if (empty($this->feedTitle)) {
|
||||
$this->feedTitle = $this->getInput('profileId');
|
||||
}
|
||||
|
||||
return $this->feedTitle . ' - Author - Openly';
|
||||
break;
|
||||
default:
|
||||
return parent::getName();
|
||||
}
|
||||
}
|
||||
|
||||
private function getAjaxURI()
|
||||
{
|
||||
$part = '/ajax.html?';
|
||||
|
||||
switch ($this->queriedContext) {
|
||||
case 'All News':
|
||||
return self::URI . 'news' . $part;
|
||||
break;
|
||||
case 'All Opinion':
|
||||
return self::URI . 'people' . $part;
|
||||
break;
|
||||
case 'By Tag':
|
||||
return self::URI . $this->getInput('content') . $part . 'theme=' . $this->getInput('tag');
|
||||
break;
|
||||
case 'By Region':
|
||||
return self::URI . $this->getInput('content') . $part . 'region=' . $this->getInput('region');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function getArticle($url)
|
||||
{
|
||||
$article = getSimpleHTMLDOMCached($url, self::ARTICLE_CACHE_TIMEOUT);
|
||||
$article = defaultLinkTo($article, $this->getURI());
|
||||
|
||||
$item = [];
|
||||
$item['title'] = $article->find('h1', 0)->plaintext;
|
||||
$item['uri'] = $url;
|
||||
$item['content'] = $article->find('div.body-text', 0);
|
||||
$item['enclosures'][] = $article->find('meta[name="twitter:image"]', 0)->content;
|
||||
$item['timestamp'] = $article->find('div.meta.small', 0)->plaintext;
|
||||
|
||||
if ($article->find('div.meta a', 0)) {
|
||||
$item['author'] = $article->find('div.meta a', 0)->plaintext;
|
||||
}
|
||||
|
||||
foreach ($article->find('div.themes li') as $li) {
|
||||
$item['categories'][] = trim(htmlspecialchars($li->plaintext, ENT_QUOTES));
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
}
|
95
bridges/PaulGrahamBridge.php
Normal file
95
bridges/PaulGrahamBridge.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
class PaulGrahamBridge extends BridgeAbstract
|
||||
{
|
||||
const NAME = 'Paul Graham Essays';
|
||||
const URI = 'https://www.paulgraham.com/articles.html';
|
||||
const DESCRIPTION = 'Returns the latest Paul Graham essays in display order';
|
||||
const MAINTAINER = 'Claire (for Stéphane)';
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$html = getSimpleHTMLDOM(self::URI);
|
||||
|
||||
// Navigate to the right TD
|
||||
// /html/body/table/tbody/tr/td[3]
|
||||
$tables = $html->find('body table');
|
||||
if (!isset($tables[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tds = $tables[0]->find('td');
|
||||
if (!isset($tds[2])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contentTd = $tds[2];
|
||||
|
||||
// Find all inner tables (each one holds a single essay link)
|
||||
$essayTables = $contentTd->find('table');
|
||||
if (!isset($essayTables[1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$essayTable = $essayTables[1];
|
||||
|
||||
// /html/body/table/tbody/tr/td[3]/table[2]/tbody/tr[2]/td/font/a
|
||||
|
||||
$links = $essayTable->find('font');
|
||||
|
||||
$essayLinks = [];
|
||||
foreach ($links as $t) {
|
||||
$link = $t->find('a', 0);
|
||||
if (!$link) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$href = trim($link->href);
|
||||
$title = trim($link->plaintext);
|
||||
|
||||
if (empty($href) || strpos($href, 'http') === 0 || !preg_match('/\.html$/', $href)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$essayLinks[] = [
|
||||
'title' => $title,
|
||||
'url' => 'https://www.paulgraham.com/' . $href,
|
||||
];
|
||||
}
|
||||
|
||||
// Only fetch the first 10 (in display order)
|
||||
$essayLinks = array_slice($essayLinks, 0, 10);
|
||||
|
||||
foreach ($essayLinks as $essay) {
|
||||
$item = [
|
||||
'uri' => $essay['url'],
|
||||
'title' => $essay['title'],
|
||||
'uid' => $essay['url'],
|
||||
'content' => '',
|
||||
];
|
||||
|
||||
$essayHtml = getSimpleHTMLDOMCached($essay['url']);
|
||||
if ($essayHtml) {
|
||||
$essayTables = $essayHtml->find('body table');
|
||||
if (isset($essayTables[0])) {
|
||||
$essayTds = $essayTables[0]->find('td');
|
||||
if (isset($essayTds[2])) {
|
||||
$mainContent = $essayTds[2]->innertext;
|
||||
$mainDom = str_get_html($mainContent);
|
||||
|
||||
// Strip unwanted layout elements
|
||||
foreach ($mainDom->find('map, img, script') as $el) {
|
||||
$el->outertext = '';
|
||||
}
|
||||
|
||||
$item['content'] = $mainDom->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ class TelegramBridge extends BridgeAbstract
|
|||
$notSupported = $messageDiv->find('div.message_media_not_supported_wrap', 0);
|
||||
if ($notSupported) {
|
||||
// For unknown reasons, the telegram preview page omits the content of this post
|
||||
$message = 'RSS-Bridge was unable to find the content of this post.<br><br>' . $notSupported->innertext;
|
||||
$message = (string) $notSupported->innertext;
|
||||
}
|
||||
|
||||
if ($messageDiv->find('div.tgme_widget_message_forwarded_from', 0)) {
|
||||
|
|
|
@ -92,6 +92,7 @@ class CacheFactory
|
|||
if (empty($port)) {
|
||||
throw new \Exception('"port" param is not set for ' . $section);
|
||||
}
|
||||
$port = (string) $port;
|
||||
if (!ctype_digit($port)) {
|
||||
throw new \Exception('"port" param is invalid for ' . $section);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
final class Configuration
|
||||
{
|
||||
private const VERSION = '2025-01-26';
|
||||
private const VERSION = '2025-06-03';
|
||||
|
||||
private static $config = [];
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue