mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-04-01 22:35:21 +03:00
Merge branch 'autoloading' into master
This commit is contained in:
commit
4007afdcf5
19 changed files with 678 additions and 716 deletions
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('DanbooruBridge.php');
|
|
||||||
|
|
||||||
class BooruprojectBridge extends DanbooruBridge {
|
class BooruprojectBridge extends DanbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1963,677 +1963,3 @@ class DealabsBridge extends PepperBridgeAbstract {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PepperBridgeAbstract extends BridgeAbstract {
|
|
||||||
|
|
||||||
const CACHE_TIMEOUT = 3600;
|
|
||||||
|
|
||||||
public function collectData(){
|
|
||||||
switch($this->queriedContext) {
|
|
||||||
case $this->i8n('context-keyword'):
|
|
||||||
return $this->collectDataKeywords();
|
|
||||||
break;
|
|
||||||
case $this->i8n('context-group'):
|
|
||||||
return $this->collectDataGroup();
|
|
||||||
break;
|
|
||||||
case $this->i8n('context-talk'):
|
|
||||||
return $this->collectDataTalk();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Deal data from the choosen group in the choosed order
|
|
||||||
*/
|
|
||||||
protected function collectDataGroup()
|
|
||||||
{
|
|
||||||
$url = $this->getGroupURI();
|
|
||||||
$this->collectDeals($url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Deal data from the choosen keywords and parameters
|
|
||||||
*/
|
|
||||||
protected function collectDataKeywords()
|
|
||||||
{
|
|
||||||
/* Even if the original website uses POST with the search page, GET works too */
|
|
||||||
$url = $this->getSearchURI();
|
|
||||||
$this->collectDeals($url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Deal data using the given URL
|
|
||||||
*/
|
|
||||||
protected function collectDeals($url){
|
|
||||||
$html = getSimpleHTMLDOM($url);
|
|
||||||
$list = $html->find('article[id]');
|
|
||||||
|
|
||||||
// Deal Image Link CSS Selector
|
|
||||||
$selectorImageLink = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'cept-thread-image-link',
|
|
||||||
'imgFrame',
|
|
||||||
'imgFrame--noBorder',
|
|
||||||
'thread-listImgCell',
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deal Link CSS Selector
|
|
||||||
$selectorLink = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'cept-tt',
|
|
||||||
'thread-link',
|
|
||||||
'linkPlain',
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deal Hotness CSS Selector
|
|
||||||
$selectorHot = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'cept-vote-box',
|
|
||||||
'vote-box'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deal Description CSS Selector
|
|
||||||
$selectorDescription = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'overflow--wrap-break'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deal Date CSS Selector
|
|
||||||
$selectorDate = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'size--all-s',
|
|
||||||
'flex',
|
|
||||||
'boxAlign-jc--all-fe'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// If there is no results, we don't parse the content because it display some random deals
|
|
||||||
$noresult = $html->find('h3[class=size--all-l size--fromW2-xl size--fromW3-xxl]', 0);
|
|
||||||
if ($noresult != null && strpos($noresult->plaintext, $this->i8n('no-results')) !== false) {
|
|
||||||
$this->items = array();
|
|
||||||
} else {
|
|
||||||
foreach ($list as $deal) {
|
|
||||||
$item = array();
|
|
||||||
$item['uri'] = $this->getDealURI($deal);
|
|
||||||
$item['title'] = $this->getTitle($deal);
|
|
||||||
$item['author'] = $deal->find('span.thread-username', 0)->plaintext;
|
|
||||||
|
|
||||||
$item['content'] = '<table><tr><td><a href="'
|
|
||||||
. $item['uri']
|
|
||||||
. '"><img src="'
|
|
||||||
. $this->getImage($deal)
|
|
||||||
. '"/></td><td>'
|
|
||||||
. $this->getHTMLTitle($item)
|
|
||||||
. $this->getPrice($deal)
|
|
||||||
. $this->getDiscount($deal)
|
|
||||||
. $this->getShipsFrom($deal)
|
|
||||||
. $this->getShippingCost($deal)
|
|
||||||
. $this->getSource($deal)
|
|
||||||
. $deal->find('div[class*=' . $selectorDescription . ']', 0)->innertext
|
|
||||||
. '</td><td>'
|
|
||||||
. $deal->find('div[class*=' . $selectorHot . ']', 0)
|
|
||||||
->find('span', 1)->outertext
|
|
||||||
. '</td></table>';
|
|
||||||
$dealDateDiv = $deal->find('div[class*=' . $selectorDate . ']', 0)
|
|
||||||
->find('span[class=hide--toW3]');
|
|
||||||
$itemDate = end($dealDateDiv)->plaintext;
|
|
||||||
// In case of a Local deal, there is no date, but we can use
|
|
||||||
// this case for other reason (like date not in the last field)
|
|
||||||
if ($this->contains($itemDate, $this->i8n('localdeal'))) {
|
|
||||||
$item['timestamp'] = time();
|
|
||||||
} else if ($this->contains($itemDate, $this->i8n('relative-date-indicator'))) {
|
|
||||||
$item['timestamp'] = $this->relativeDateToTimestamp($itemDate);
|
|
||||||
} else {
|
|
||||||
$item['timestamp'] = $this->parseDate($itemDate);
|
|
||||||
}
|
|
||||||
$this->items[] = $item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Talk lastest comments
|
|
||||||
*/
|
|
||||||
protected function collectDataTalk(){
|
|
||||||
$threadURL = $this->getInput('url');
|
|
||||||
$onlyWithUrl = $this->getInput('only_with_url');
|
|
||||||
|
|
||||||
// Get Thread ID from url passed in parameter
|
|
||||||
$threadSearch = preg_match('/-([0-9]{1,20})$/', $threadURL, $matches);
|
|
||||||
|
|
||||||
// Show an error message if we can't find the thread ID in the URL sent by the user
|
|
||||||
if($threadSearch !== 1) {
|
|
||||||
returnClientError($this->i8n('thread-error'));
|
|
||||||
}
|
|
||||||
$threadID = $matches[1];
|
|
||||||
|
|
||||||
$url = $this->i8n('bridge-uri') . 'graphql';
|
|
||||||
|
|
||||||
// Get Cookies header to do the query
|
|
||||||
$cookies = $this->getCookies($url);
|
|
||||||
|
|
||||||
// GraphQL String
|
|
||||||
// This was extracted from https://www.dealabs.com/assets/js/modern/common_211b99.js
|
|
||||||
// This string was extracted during a Website visit, and minified using this neat tool :
|
|
||||||
// https://codepen.io/dangodev/pen/Baoqmoy
|
|
||||||
$graphqlString = <<<'HEREDOC'
|
|
||||||
query comments($filter:CommentFilter!,$limit:Int,$page:Int){comments(filter:$filter,limit:$limit,page:$page){
|
|
||||||
items{...commentFields}pagination{...paginationFields}}}fragment commentFields on Comment{commentId threadId url
|
|
||||||
preparedHtmlContent user{...userMediumAvatarFields...userNameFields...userPersonaFields bestBadge{...badgeFields}}
|
|
||||||
reactionCounts{type count}deletable currentUserReaction{type}reported reportable source status createdAt updatedAt
|
|
||||||
ignored popular deletedBy{username}notes{content createdAt user{username}}lastEdit{reason timeAgo userId}}fragment
|
|
||||||
userMediumAvatarFields on User{userId isDeletedOrPendingDeletion imageUrls(slot:"default",variations:
|
|
||||||
["user_small_avatar"])}fragment userNameFields on User{userId username isUserProfileHidden isDeletedOrPendingDeletion}
|
|
||||||
fragment userPersonaFields on User{persona{type text}}fragment badgeFields on Badge{badgeId level{...badgeLevelFields}}
|
|
||||||
fragment badgeLevelFields on BadgeLevel{key name description}fragment paginationFields on Pagination{count current last
|
|
||||||
next previous size order}
|
|
||||||
HEREDOC;
|
|
||||||
|
|
||||||
// Construct the JSON object to send to the Website
|
|
||||||
$queryArray = array (
|
|
||||||
'query' => $graphqlString,
|
|
||||||
'variables' => array (
|
|
||||||
'filter' => array (
|
|
||||||
'threadId' => array (
|
|
||||||
'eq' => $threadID,
|
|
||||||
),
|
|
||||||
'order' => array (
|
|
||||||
'direction' => 'Descending',
|
|
||||||
),
|
|
||||||
|
|
||||||
),
|
|
||||||
'page' => 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
$queryJSON = json_encode($queryArray);
|
|
||||||
|
|
||||||
// HTTP headers
|
|
||||||
$header = array(
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Accept: application/json, text/plain, */*',
|
|
||||||
'X-Pepper-Txn: threads.show',
|
|
||||||
'X-Request-Type: application/vnd.pepper.v1+json',
|
|
||||||
'X-Requested-With: XMLHttpRequest',
|
|
||||||
$cookies,
|
|
||||||
);
|
|
||||||
// CURL Options
|
|
||||||
$opts = array(
|
|
||||||
CURLOPT_POST => 1,
|
|
||||||
CURLOPT_POSTFIELDS => $queryJSON
|
|
||||||
);
|
|
||||||
$json = getContents($url, $header, $opts);
|
|
||||||
$objects = json_decode($json);
|
|
||||||
foreach($objects->data->comments->items as $comment) {
|
|
||||||
$item = array();
|
|
||||||
$item['uri'] = $comment->url;
|
|
||||||
$item['title'] = $comment->user->username . ' - ' . $comment->createdAt;
|
|
||||||
$item['author'] = $comment->user->username;
|
|
||||||
$item['content'] = $comment->preparedHtmlContent;
|
|
||||||
$item['uid'] = $comment->commentId;
|
|
||||||
// Timestamp handling needs a new parsing function
|
|
||||||
if($onlyWithUrl == true) {
|
|
||||||
// Count Links and Quote Links
|
|
||||||
$content = str_get_html($item['content']);
|
|
||||||
$countLinks = count($content->find('a[href]'));
|
|
||||||
$countQuoteLinks = count($content->find('a[href][class=userHtml-quote-source]'));
|
|
||||||
// Only add element if there are Links ans more links tant Quote links
|
|
||||||
if($countLinks > 0 && $countLinks > $countQuoteLinks) {
|
|
||||||
$this->items[] = $item;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->items[] = $item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract the cookies obtained from the URL
|
|
||||||
* @return array the array containing the cookies set by the URL
|
|
||||||
*/
|
|
||||||
private function getCookies($url)
|
|
||||||
{
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
// get headers too with this line
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
// get cookie
|
|
||||||
// multi-cookie variant contributed by @Combuster in comments
|
|
||||||
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches);
|
|
||||||
$cookies = array();
|
|
||||||
foreach($matches[1] as $item) {
|
|
||||||
parse_str($item, $cookie);
|
|
||||||
$cookies = array_merge($cookies, $cookie);
|
|
||||||
}
|
|
||||||
$header = 'Cookie: ';
|
|
||||||
foreach($cookies as $name => $content) {
|
|
||||||
$header .= $name . '=' . $content . '; ';
|
|
||||||
}
|
|
||||||
return $header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the string $str contains any of the string of the array $arr
|
|
||||||
* @return boolean true if the string matched anything otherwise false
|
|
||||||
*/
|
|
||||||
private function contains($str, array $arr)
|
|
||||||
{
|
|
||||||
foreach ($arr as $a) {
|
|
||||||
if (stripos($str, $a) !== false) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Price from a Deal if it exists
|
|
||||||
* @return string String of the deal price
|
|
||||||
*/
|
|
||||||
private function getPrice($deal)
|
|
||||||
{
|
|
||||||
if ($deal->find(
|
|
||||||
'span[class*=thread-price]', 0) != null) {
|
|
||||||
return '<div>' . $this->i8n('price') . ' : '
|
|
||||||
. $deal->find(
|
|
||||||
'span[class*=thread-price]', 0
|
|
||||||
)->plaintext
|
|
||||||
. '</div>';
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Title from a Deal if it exists
|
|
||||||
* @return string String of the deal title
|
|
||||||
*/
|
|
||||||
private function getTitle($deal)
|
|
||||||
{
|
|
||||||
|
|
||||||
$titleRoot = $deal->find('div[class*=threadGrid-title]', 0);
|
|
||||||
$titleA = $titleRoot->find('a[class*=thread-link]', 0);
|
|
||||||
$titleFirstChild = $titleRoot->first_child();
|
|
||||||
if($titleA !== null) {
|
|
||||||
$title = $titleA->plaintext;
|
|
||||||
} else {
|
|
||||||
// In some case, expired deals have a different format
|
|
||||||
$title = $titleRoot->find('span', 0)->plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $title;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Title from a Talk if it exists
|
|
||||||
* @return string String of the Talk title
|
|
||||||
*/
|
|
||||||
private function getTalkTitle()
|
|
||||||
{
|
|
||||||
$html = getSimpleHTMLDOMCached($this->getInput('url'));
|
|
||||||
$title = $html->find('h1[class=thread-title]', 0)->plaintext;
|
|
||||||
return $title;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the HTML Title code from an item
|
|
||||||
* @return string String of the deal title
|
|
||||||
*/
|
|
||||||
private function getHTMLTitle($item)
|
|
||||||
{
|
|
||||||
if($item['uri'] == '') {
|
|
||||||
$html = '<h2>' . $item['title'] . '</h2>';
|
|
||||||
} else {
|
|
||||||
$html = '<h2><a href="' . $item['uri'] . '">'
|
|
||||||
. $item['title'] . '</a></h2>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $html;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the URI from a Deal if it exists
|
|
||||||
* @return string String of the deal URI
|
|
||||||
*/
|
|
||||||
private function getDealURI($deal)
|
|
||||||
{
|
|
||||||
|
|
||||||
$uriA = $deal->find('div[class*=threadGrid-title]', 0)->find('a[class*=thread-link]', 0);
|
|
||||||
if($uriA === null) {
|
|
||||||
$uri = '';
|
|
||||||
} else {
|
|
||||||
$uri = $uriA->href;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $uri;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Shipping costs from a Deal if it exists
|
|
||||||
* @return string String of the deal shipping Cost
|
|
||||||
*/
|
|
||||||
private function getShippingCost($deal)
|
|
||||||
{
|
|
||||||
if ($deal->find('span[class*=space--ml-2 size--all-s overflow--wrap-off]', 0) != null) {
|
|
||||||
if ($deal->find('span[class*=space--ml-2 size--all-s overflow--wrap-off]', 0)->children(1) != null) {
|
|
||||||
return '<div>' . $this->i8n('shipping') . ' : '
|
|
||||||
. $deal->find('span[class*=space--ml-2 size--all-s overflow--wrap-off]', 0)->children(1)->innertext
|
|
||||||
. '</div>';
|
|
||||||
} else {
|
|
||||||
return '<div>' . $this->i8n('shipping') . ' : '
|
|
||||||
. $deal->find('span[class*=text--color-greyShade flex--inline]', 0)->innertext
|
|
||||||
. '</div>';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the source of a Deal if it exists
|
|
||||||
* @return string String of the deal source
|
|
||||||
*/
|
|
||||||
private function getSource($deal)
|
|
||||||
{
|
|
||||||
if ($deal->find('a[class*=text--color-greyShade]', 0) != null) {
|
|
||||||
return '<div>' . $this->i8n('origin') . ' : '
|
|
||||||
. $deal->find('a[class*=text--color-greyShade]', 0)->outertext
|
|
||||||
. '</div>';
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the original Price and discout from a Deal if it exists
|
|
||||||
* @return string String of the deal original price and discount
|
|
||||||
*/
|
|
||||||
private function getDiscount($deal)
|
|
||||||
{
|
|
||||||
if ($deal->find('span[class*=mute--text text--lineThrough]', 0) != null) {
|
|
||||||
$discountHtml = $deal->find('span[class=space--ml-1 size--all-l size--fromW3-xl]', 0);
|
|
||||||
if ($discountHtml != null) {
|
|
||||||
$discount = $discountHtml->plaintext;
|
|
||||||
} else {
|
|
||||||
$discount = '';
|
|
||||||
}
|
|
||||||
return '<div>' . $this->i8n('discount') . ' : <span style="text-decoration: line-through;">'
|
|
||||||
. $deal->find(
|
|
||||||
'span[class*=mute--text text--lineThrough]', 0
|
|
||||||
)->plaintext
|
|
||||||
. '</span> '
|
|
||||||
. $discount
|
|
||||||
. '</div>';
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Picture URL from a Deal if it exists
|
|
||||||
* @return string String of the deal Picture URL
|
|
||||||
*/
|
|
||||||
private function getImage($deal)
|
|
||||||
{
|
|
||||||
$selectorLazy = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'thread-image',
|
|
||||||
'width--all-auto',
|
|
||||||
'height--all-auto',
|
|
||||||
'imgFrame-img',
|
|
||||||
'img--dummy',
|
|
||||||
'js-lazy-img'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$selectorPlain = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'thread-image',
|
|
||||||
'width--all-auto',
|
|
||||||
'height--all-auto',
|
|
||||||
'imgFrame-img',
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if ($deal->find('img[class=' . $selectorLazy . ']', 0) != null) {
|
|
||||||
return json_decode(
|
|
||||||
html_entity_decode(
|
|
||||||
$deal->find('img[class=' . $selectorLazy . ']', 0)
|
|
||||||
->getAttribute('data-lazy-img')))->{'src'};
|
|
||||||
} else {
|
|
||||||
return $deal->find('img[class*=' . $selectorPlain . ']', 0 )->src;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the originating country from a Deal if it exists
|
|
||||||
* @return string String of the deal originating country
|
|
||||||
*/
|
|
||||||
private function getShipsFrom($deal)
|
|
||||||
{
|
|
||||||
$selector = implode(
|
|
||||||
' ', /* Notice this is a space! */
|
|
||||||
array(
|
|
||||||
'hide--toW2',
|
|
||||||
'metaRibbon',
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if ($deal->find('span[class*=' . $selector . ']', 0) != null) {
|
|
||||||
return '<div>'
|
|
||||||
. $deal->find('span[class*=' . $selector . ']', 0)->children(2)->plaintext
|
|
||||||
. '</div>';
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms a local date into a timestamp
|
|
||||||
* @return int timestamp of the input date
|
|
||||||
*/
|
|
||||||
private function parseDate($string)
|
|
||||||
{
|
|
||||||
$month_local = $this->i8n('local-months');
|
|
||||||
$month_en = array(
|
|
||||||
'January',
|
|
||||||
'February',
|
|
||||||
'March',
|
|
||||||
'April',
|
|
||||||
'May',
|
|
||||||
'June',
|
|
||||||
'July',
|
|
||||||
'August',
|
|
||||||
'September',
|
|
||||||
'October',
|
|
||||||
'November',
|
|
||||||
'December'
|
|
||||||
);
|
|
||||||
|
|
||||||
// A date can be prfixed with some words, we remove theme
|
|
||||||
$string = $this->removeDatePrefixes($string);
|
|
||||||
// We translate the local months name in the english one
|
|
||||||
$date_str = trim(str_replace($month_local, $month_en, $string));
|
|
||||||
|
|
||||||
// If the date does not contain any year, we add the current year
|
|
||||||
if (!preg_match('/[0-9]{4}/', $string)) {
|
|
||||||
$date_str .= ' ' . date('Y');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the Hour and minutes
|
|
||||||
$date_str .= ' 00:00';
|
|
||||||
$date = DateTime::createFromFormat('j F Y H:i', $date_str);
|
|
||||||
// In some case, the date is not recognized : as a workaround the actual date is taken
|
|
||||||
if($date === false) {
|
|
||||||
$date = new DateTime();
|
|
||||||
}
|
|
||||||
return $date->getTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the prefix of a date if it has one
|
|
||||||
* @return the date without prefiux
|
|
||||||
*/
|
|
||||||
private function removeDatePrefixes($string)
|
|
||||||
{
|
|
||||||
$string = str_replace($this->i8n('date-prefixes'), array(), $string);
|
|
||||||
return $string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the suffix of a relative date if it has one
|
|
||||||
* @return the relative date without suffixes
|
|
||||||
*/
|
|
||||||
private function removeRelativeDateSuffixes($string)
|
|
||||||
{
|
|
||||||
if (count($this->i8n('relative-date-ignore-suffix')) > 0) {
|
|
||||||
$string = preg_replace($this->i8n('relative-date-ignore-suffix'), '', $string);
|
|
||||||
}
|
|
||||||
return $string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms a relative local date into a timestamp
|
|
||||||
* @return int timestamp of the input date
|
|
||||||
*/
|
|
||||||
private function relativeDateToTimestamp($str) {
|
|
||||||
$date = new DateTime();
|
|
||||||
|
|
||||||
// In case of update date, replace it by the regular relative date first word
|
|
||||||
$str = str_replace($this->i8n('relative-date-alt-prefixes'), $this->i8n('local-time-relative')[0], $str);
|
|
||||||
|
|
||||||
$str = $this->removeRelativeDateSuffixes($str);
|
|
||||||
|
|
||||||
$search = $this->i8n('local-time-relative');
|
|
||||||
|
|
||||||
$replace = array(
|
|
||||||
'-',
|
|
||||||
'minute',
|
|
||||||
'hour',
|
|
||||||
'day',
|
|
||||||
'month',
|
|
||||||
'year',
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
$date->modify(str_replace($search, $replace, $str));
|
|
||||||
return $date->getTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RSS Feed title according to the parameters
|
|
||||||
* @return string the RSS feed Tiyle
|
|
||||||
*/
|
|
||||||
public function getName(){
|
|
||||||
switch($this->queriedContext) {
|
|
||||||
case $this->i8n('context-keyword'):
|
|
||||||
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-keyword') . ' : ' . $this->getInput('q');
|
|
||||||
break;
|
|
||||||
case $this->i8n('context-group'):
|
|
||||||
$values = $this->getParameters()[$this->i8n('context-group')]['group']['values'];
|
|
||||||
$group = array_search($this->getInput('group'), $values);
|
|
||||||
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-group') . ' : ' . $group;
|
|
||||||
break;
|
|
||||||
case $this->i8n('context-talk'):
|
|
||||||
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-talk') . ' : ' . $this->getTalkTitle();
|
|
||||||
break;
|
|
||||||
default: // Return default value
|
|
||||||
return static::NAME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RSS Feed URI according to the parameters
|
|
||||||
* @return string the RSS feed Title
|
|
||||||
*/
|
|
||||||
public function getURI(){
|
|
||||||
switch($this->queriedContext) {
|
|
||||||
case $this->i8n('context-keyword'):
|
|
||||||
return $this->getSearchURI();
|
|
||||||
break;
|
|
||||||
case $this->i8n('context-group'):
|
|
||||||
return $this->getGroupURI();
|
|
||||||
break;
|
|
||||||
case $this->i8n('context-talk'):
|
|
||||||
return $this->getTalkURI();
|
|
||||||
break;
|
|
||||||
default: // Return default value
|
|
||||||
return static::URI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RSS Feed URI for a keyword Feed
|
|
||||||
* @return string the RSS feed URI
|
|
||||||
*/
|
|
||||||
private function getSearchURI(){
|
|
||||||
$q = $this->getInput('q');
|
|
||||||
$hide_expired = $this->getInput('hide_expired');
|
|
||||||
$hide_local = $this->getInput('hide_local');
|
|
||||||
$priceFrom = $this->getInput('priceFrom');
|
|
||||||
$priceTo = $this->getInput('priceTo');
|
|
||||||
$url = $this->i8n('bridge-uri')
|
|
||||||
. 'search/advanced?q='
|
|
||||||
. urlencode($q)
|
|
||||||
. '&hide_expired=' . $hide_expired
|
|
||||||
. '&hide_local=' . $hide_local
|
|
||||||
. '&priceFrom=' . $priceFrom
|
|
||||||
. '&priceTo=' . $priceTo
|
|
||||||
/* Some default parameters
|
|
||||||
* search_fields : Search in Titres & Descriptions & Codes
|
|
||||||
* sort_by : Sort the search by new deals
|
|
||||||
* time_frame : Search will not be on a limited timeframe
|
|
||||||
*/
|
|
||||||
. '&search_fields[]=1&search_fields[]=2&search_fields[]=3&sort_by=new&time_frame=0';
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RSS Feed URI for a group Feed
|
|
||||||
* @return string the RSS feed URI
|
|
||||||
*/
|
|
||||||
private function getGroupURI(){
|
|
||||||
$group = $this->getInput('group');
|
|
||||||
$order = $this->getInput('order');
|
|
||||||
|
|
||||||
$url = $this->i8n('bridge-uri')
|
|
||||||
. $this->i8n('uri-group') . $group . $order;
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RSS Feed URI for a Talk Feed
|
|
||||||
* @return string the RSS feed URI
|
|
||||||
*/
|
|
||||||
private function getTalkURI(){
|
|
||||||
$url = $this->getInput('url');
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is some "localisation" function that returns the needed content using
|
|
||||||
* the "$lang" class variable in the local class
|
|
||||||
* @return various the local content needed
|
|
||||||
*/
|
|
||||||
protected function i8n($key)
|
|
||||||
{
|
|
||||||
if (array_key_exists($key, $this->lang)) {
|
|
||||||
return $this->lang[$key];
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('GithubIssueBridge.php');
|
|
||||||
class GitHubPullRequestBridge extends GithubIssueBridge {
|
class GitHubPullRequestBridge extends GithubIssueBridge {
|
||||||
const MAINTAINER = 'Yaman Qalieh';
|
const MAINTAINER = 'Yaman Qalieh';
|
||||||
const NAME = 'GitHub Pull Request';
|
const NAME = 'GitHub Pull Request';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once(__DIR__ . '/DealabsBridge.php');
|
|
||||||
class HotUKDealsBridge extends PepperBridgeAbstract {
|
class HotUKDealsBridge extends PepperBridgeAbstract {
|
||||||
|
|
||||||
const NAME = 'HotUKDeals bridge';
|
const NAME = 'HotUKDeals bridge';
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('MoebooruBridge.php');
|
|
||||||
|
|
||||||
class KonachanBridge extends MoebooruBridge {
|
class KonachanBridge extends MoebooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('MoebooruBridge.php');
|
|
||||||
|
|
||||||
class LolibooruBridge extends MoebooruBridge {
|
class LolibooruBridge extends MoebooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('Shimmie2Bridge.php');
|
|
||||||
|
|
||||||
class MilbooruBridge extends Shimmie2Bridge {
|
class MilbooruBridge extends Shimmie2Bridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('GelbooruBridge.php');
|
|
||||||
|
|
||||||
class MspabooruBridge extends GelbooruBridge {
|
class MspabooruBridge extends GelbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once(__DIR__ . '/DealabsBridge.php');
|
|
||||||
class MydealsBridge extends PepperBridgeAbstract {
|
class MydealsBridge extends PepperBridgeAbstract {
|
||||||
|
|
||||||
const NAME = 'Mydeals bridge';
|
const NAME = 'Mydeals bridge';
|
||||||
|
|
675
bridges/PepperBridgeAbstract.php
Normal file
675
bridges/PepperBridgeAbstract.php
Normal file
|
@ -0,0 +1,675 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class PepperBridgeAbstract extends BridgeAbstract {
|
||||||
|
|
||||||
|
const CACHE_TIMEOUT = 3600;
|
||||||
|
|
||||||
|
public function collectData(){
|
||||||
|
switch($this->queriedContext) {
|
||||||
|
case $this->i8n('context-keyword'):
|
||||||
|
return $this->collectDataKeywords();
|
||||||
|
break;
|
||||||
|
case $this->i8n('context-group'):
|
||||||
|
return $this->collectDataGroup();
|
||||||
|
break;
|
||||||
|
case $this->i8n('context-talk'):
|
||||||
|
return $this->collectDataTalk();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Deal data from the choosen group in the choosed order
|
||||||
|
*/
|
||||||
|
protected function collectDataGroup()
|
||||||
|
{
|
||||||
|
$url = $this->getGroupURI();
|
||||||
|
$this->collectDeals($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Deal data from the choosen keywords and parameters
|
||||||
|
*/
|
||||||
|
protected function collectDataKeywords()
|
||||||
|
{
|
||||||
|
/* Even if the original website uses POST with the search page, GET works too */
|
||||||
|
$url = $this->getSearchURI();
|
||||||
|
$this->collectDeals($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Deal data using the given URL
|
||||||
|
*/
|
||||||
|
protected function collectDeals($url){
|
||||||
|
$html = getSimpleHTMLDOM($url);
|
||||||
|
$list = $html->find('article[id]');
|
||||||
|
|
||||||
|
// Deal Image Link CSS Selector
|
||||||
|
$selectorImageLink = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'cept-thread-image-link',
|
||||||
|
'imgFrame',
|
||||||
|
'imgFrame--noBorder',
|
||||||
|
'thread-listImgCell',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deal Link CSS Selector
|
||||||
|
$selectorLink = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'cept-tt',
|
||||||
|
'thread-link',
|
||||||
|
'linkPlain',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deal Hotness CSS Selector
|
||||||
|
$selectorHot = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'cept-vote-box',
|
||||||
|
'vote-box'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deal Description CSS Selector
|
||||||
|
$selectorDescription = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'overflow--wrap-break'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deal Date CSS Selector
|
||||||
|
$selectorDate = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'size--all-s',
|
||||||
|
'flex',
|
||||||
|
'boxAlign-jc--all-fe'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// If there is no results, we don't parse the content because it display some random deals
|
||||||
|
$noresult = $html->find('h3[class=size--all-l size--fromW2-xl size--fromW3-xxl]', 0);
|
||||||
|
if ($noresult != null && strpos($noresult->plaintext, $this->i8n('no-results')) !== false) {
|
||||||
|
$this->items = array();
|
||||||
|
} else {
|
||||||
|
foreach ($list as $deal) {
|
||||||
|
$item = array();
|
||||||
|
$item['uri'] = $this->getDealURI($deal);
|
||||||
|
$item['title'] = $this->getTitle($deal);
|
||||||
|
$item['author'] = $deal->find('span.thread-username', 0)->plaintext;
|
||||||
|
|
||||||
|
$item['content'] = '<table><tr><td><a href="'
|
||||||
|
. $item['uri']
|
||||||
|
. '"><img src="'
|
||||||
|
. $this->getImage($deal)
|
||||||
|
. '"/></td><td>'
|
||||||
|
. $this->getHTMLTitle($item)
|
||||||
|
. $this->getPrice($deal)
|
||||||
|
. $this->getDiscount($deal)
|
||||||
|
. $this->getShipsFrom($deal)
|
||||||
|
. $this->getShippingCost($deal)
|
||||||
|
. $this->getSource($deal)
|
||||||
|
. $deal->find('div[class*=' . $selectorDescription . ']', 0)->innertext
|
||||||
|
. '</td><td>'
|
||||||
|
. $deal->find('div[class*=' . $selectorHot . ']', 0)
|
||||||
|
->find('span', 1)->outertext
|
||||||
|
. '</td></table>';
|
||||||
|
$dealDateDiv = $deal->find('div[class*=' . $selectorDate . ']', 0)
|
||||||
|
->find('span[class=hide--toW3]');
|
||||||
|
$itemDate = end($dealDateDiv)->plaintext;
|
||||||
|
// In case of a Local deal, there is no date, but we can use
|
||||||
|
// this case for other reason (like date not in the last field)
|
||||||
|
if ($this->contains($itemDate, $this->i8n('localdeal'))) {
|
||||||
|
$item['timestamp'] = time();
|
||||||
|
} else if ($this->contains($itemDate, $this->i8n('relative-date-indicator'))) {
|
||||||
|
$item['timestamp'] = $this->relativeDateToTimestamp($itemDate);
|
||||||
|
} else {
|
||||||
|
$item['timestamp'] = $this->parseDate($itemDate);
|
||||||
|
}
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Talk lastest comments
|
||||||
|
*/
|
||||||
|
protected function collectDataTalk(){
|
||||||
|
$threadURL = $this->getInput('url');
|
||||||
|
$onlyWithUrl = $this->getInput('only_with_url');
|
||||||
|
|
||||||
|
// Get Thread ID from url passed in parameter
|
||||||
|
$threadSearch = preg_match('/-([0-9]{1,20})$/', $threadURL, $matches);
|
||||||
|
|
||||||
|
// Show an error message if we can't find the thread ID in the URL sent by the user
|
||||||
|
if($threadSearch !== 1) {
|
||||||
|
returnClientError($this->i8n('thread-error'));
|
||||||
|
}
|
||||||
|
$threadID = $matches[1];
|
||||||
|
|
||||||
|
$url = $this->i8n('bridge-uri') . 'graphql';
|
||||||
|
|
||||||
|
// Get Cookies header to do the query
|
||||||
|
$cookies = $this->getCookies($url);
|
||||||
|
|
||||||
|
// GraphQL String
|
||||||
|
// This was extracted from https://www.dealabs.com/assets/js/modern/common_211b99.js
|
||||||
|
// This string was extracted during a Website visit, and minified using this neat tool :
|
||||||
|
// https://codepen.io/dangodev/pen/Baoqmoy
|
||||||
|
$graphqlString = <<<'HEREDOC'
|
||||||
|
query comments($filter:CommentFilter!,$limit:Int,$page:Int){comments(filter:$filter,limit:$limit,page:$page){
|
||||||
|
items{...commentFields}pagination{...paginationFields}}}fragment commentFields on Comment{commentId threadId url
|
||||||
|
preparedHtmlContent user{...userMediumAvatarFields...userNameFields...userPersonaFields bestBadge{...badgeFields}}
|
||||||
|
reactionCounts{type count}deletable currentUserReaction{type}reported reportable source status createdAt updatedAt
|
||||||
|
ignored popular deletedBy{username}notes{content createdAt user{username}}lastEdit{reason timeAgo userId}}fragment
|
||||||
|
userMediumAvatarFields on User{userId isDeletedOrPendingDeletion imageUrls(slot:"default",variations:
|
||||||
|
["user_small_avatar"])}fragment userNameFields on User{userId username isUserProfileHidden isDeletedOrPendingDeletion}
|
||||||
|
fragment userPersonaFields on User{persona{type text}}fragment badgeFields on Badge{badgeId level{...badgeLevelFields}}
|
||||||
|
fragment badgeLevelFields on BadgeLevel{key name description}fragment paginationFields on Pagination{count current last
|
||||||
|
next previous size order}
|
||||||
|
HEREDOC;
|
||||||
|
|
||||||
|
// Construct the JSON object to send to the Website
|
||||||
|
$queryArray = array (
|
||||||
|
'query' => $graphqlString,
|
||||||
|
'variables' => array (
|
||||||
|
'filter' => array (
|
||||||
|
'threadId' => array (
|
||||||
|
'eq' => $threadID,
|
||||||
|
),
|
||||||
|
'order' => array (
|
||||||
|
'direction' => 'Descending',
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
'page' => 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$queryJSON = json_encode($queryArray);
|
||||||
|
|
||||||
|
// HTTP headers
|
||||||
|
$header = array(
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Accept: application/json, text/plain, */*',
|
||||||
|
'X-Pepper-Txn: threads.show',
|
||||||
|
'X-Request-Type: application/vnd.pepper.v1+json',
|
||||||
|
'X-Requested-With: XMLHttpRequest',
|
||||||
|
$cookies,
|
||||||
|
);
|
||||||
|
// CURL Options
|
||||||
|
$opts = array(
|
||||||
|
CURLOPT_POST => 1,
|
||||||
|
CURLOPT_POSTFIELDS => $queryJSON
|
||||||
|
);
|
||||||
|
$json = getContents($url, $header, $opts);
|
||||||
|
$objects = json_decode($json);
|
||||||
|
foreach($objects->data->comments->items as $comment) {
|
||||||
|
$item = array();
|
||||||
|
$item['uri'] = $comment->url;
|
||||||
|
$item['title'] = $comment->user->username . ' - ' . $comment->createdAt;
|
||||||
|
$item['author'] = $comment->user->username;
|
||||||
|
$item['content'] = $comment->preparedHtmlContent;
|
||||||
|
$item['uid'] = $comment->commentId;
|
||||||
|
// Timestamp handling needs a new parsing function
|
||||||
|
if($onlyWithUrl == true) {
|
||||||
|
// Count Links and Quote Links
|
||||||
|
$content = str_get_html($item['content']);
|
||||||
|
$countLinks = count($content->find('a[href]'));
|
||||||
|
$countQuoteLinks = count($content->find('a[href][class=userHtml-quote-source]'));
|
||||||
|
// Only add element if there are Links ans more links tant Quote links
|
||||||
|
if($countLinks > 0 && $countLinks > $countQuoteLinks) {
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the cookies obtained from the URL
|
||||||
|
* @return array the array containing the cookies set by the URL
|
||||||
|
*/
|
||||||
|
private function getCookies($url)
|
||||||
|
{
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
// get headers too with this line
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||||
|
$result = curl_exec($ch);
|
||||||
|
// get cookie
|
||||||
|
// multi-cookie variant contributed by @Combuster in comments
|
||||||
|
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches);
|
||||||
|
$cookies = array();
|
||||||
|
foreach($matches[1] as $item) {
|
||||||
|
parse_str($item, $cookie);
|
||||||
|
$cookies = array_merge($cookies, $cookie);
|
||||||
|
}
|
||||||
|
$header = 'Cookie: ';
|
||||||
|
foreach($cookies as $name => $content) {
|
||||||
|
$header .= $name . '=' . $content . '; ';
|
||||||
|
}
|
||||||
|
return $header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the string $str contains any of the string of the array $arr
|
||||||
|
* @return boolean true if the string matched anything otherwise false
|
||||||
|
*/
|
||||||
|
private function contains($str, array $arr)
|
||||||
|
{
|
||||||
|
foreach ($arr as $a) {
|
||||||
|
if (stripos($str, $a) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Price from a Deal if it exists
|
||||||
|
* @return string String of the deal price
|
||||||
|
*/
|
||||||
|
private function getPrice($deal)
|
||||||
|
{
|
||||||
|
if ($deal->find(
|
||||||
|
'span[class*=thread-price]', 0) != null) {
|
||||||
|
return '<div>' . $this->i8n('price') . ' : '
|
||||||
|
. $deal->find(
|
||||||
|
'span[class*=thread-price]', 0
|
||||||
|
)->plaintext
|
||||||
|
. '</div>';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Title from a Deal if it exists
|
||||||
|
* @return string String of the deal title
|
||||||
|
*/
|
||||||
|
private function getTitle($deal)
|
||||||
|
{
|
||||||
|
|
||||||
|
$titleRoot = $deal->find('div[class*=threadGrid-title]', 0);
|
||||||
|
$titleA = $titleRoot->find('a[class*=thread-link]', 0);
|
||||||
|
$titleFirstChild = $titleRoot->first_child();
|
||||||
|
if($titleA !== null) {
|
||||||
|
$title = $titleA->plaintext;
|
||||||
|
} else {
|
||||||
|
// In some case, expired deals have a different format
|
||||||
|
$title = $titleRoot->find('span', 0)->plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $title;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Title from a Talk if it exists
|
||||||
|
* @return string String of the Talk title
|
||||||
|
*/
|
||||||
|
private function getTalkTitle()
|
||||||
|
{
|
||||||
|
$html = getSimpleHTMLDOMCached($this->getInput('url'));
|
||||||
|
$title = $html->find('h1[class=thread-title]', 0)->plaintext;
|
||||||
|
return $title;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HTML Title code from an item
|
||||||
|
* @return string String of the deal title
|
||||||
|
*/
|
||||||
|
private function getHTMLTitle($item)
|
||||||
|
{
|
||||||
|
if($item['uri'] == '') {
|
||||||
|
$html = '<h2>' . $item['title'] . '</h2>';
|
||||||
|
} else {
|
||||||
|
$html = '<h2><a href="' . $item['uri'] . '">'
|
||||||
|
. $item['title'] . '</a></h2>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URI from a Deal if it exists
|
||||||
|
* @return string String of the deal URI
|
||||||
|
*/
|
||||||
|
private function getDealURI($deal)
|
||||||
|
{
|
||||||
|
|
||||||
|
$uriA = $deal->find('div[class*=threadGrid-title]', 0)->find('a[class*=thread-link]', 0);
|
||||||
|
if($uriA === null) {
|
||||||
|
$uri = '';
|
||||||
|
} else {
|
||||||
|
$uri = $uriA->href;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Shipping costs from a Deal if it exists
|
||||||
|
* @return string String of the deal shipping Cost
|
||||||
|
*/
|
||||||
|
private function getShippingCost($deal)
|
||||||
|
{
|
||||||
|
if ($deal->find('span[class*=space--ml-2 size--all-s overflow--wrap-off]', 0) != null) {
|
||||||
|
if ($deal->find('span[class*=space--ml-2 size--all-s overflow--wrap-off]', 0)->children(1) != null) {
|
||||||
|
return '<div>' . $this->i8n('shipping') . ' : '
|
||||||
|
. $deal->find('span[class*=space--ml-2 size--all-s overflow--wrap-off]', 0)->children(1)->innertext
|
||||||
|
. '</div>';
|
||||||
|
} else {
|
||||||
|
return '<div>' . $this->i8n('shipping') . ' : '
|
||||||
|
. $deal->find('span[class*=text--color-greyShade flex--inline]', 0)->innertext
|
||||||
|
. '</div>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the source of a Deal if it exists
|
||||||
|
* @return string String of the deal source
|
||||||
|
*/
|
||||||
|
private function getSource($deal)
|
||||||
|
{
|
||||||
|
if ($deal->find('a[class*=text--color-greyShade]', 0) != null) {
|
||||||
|
return '<div>' . $this->i8n('origin') . ' : '
|
||||||
|
. $deal->find('a[class*=text--color-greyShade]', 0)->outertext
|
||||||
|
. '</div>';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the original Price and discout from a Deal if it exists
|
||||||
|
* @return string String of the deal original price and discount
|
||||||
|
*/
|
||||||
|
private function getDiscount($deal)
|
||||||
|
{
|
||||||
|
if ($deal->find('span[class*=mute--text text--lineThrough]', 0) != null) {
|
||||||
|
$discountHtml = $deal->find('span[class=space--ml-1 size--all-l size--fromW3-xl]', 0);
|
||||||
|
if ($discountHtml != null) {
|
||||||
|
$discount = $discountHtml->plaintext;
|
||||||
|
} else {
|
||||||
|
$discount = '';
|
||||||
|
}
|
||||||
|
return '<div>' . $this->i8n('discount') . ' : <span style="text-decoration: line-through;">'
|
||||||
|
. $deal->find(
|
||||||
|
'span[class*=mute--text text--lineThrough]', 0
|
||||||
|
)->plaintext
|
||||||
|
. '</span> '
|
||||||
|
. $discount
|
||||||
|
. '</div>';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Picture URL from a Deal if it exists
|
||||||
|
* @return string String of the deal Picture URL
|
||||||
|
*/
|
||||||
|
private function getImage($deal)
|
||||||
|
{
|
||||||
|
$selectorLazy = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'thread-image',
|
||||||
|
'width--all-auto',
|
||||||
|
'height--all-auto',
|
||||||
|
'imgFrame-img',
|
||||||
|
'img--dummy',
|
||||||
|
'js-lazy-img'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$selectorPlain = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'thread-image',
|
||||||
|
'width--all-auto',
|
||||||
|
'height--all-auto',
|
||||||
|
'imgFrame-img',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ($deal->find('img[class=' . $selectorLazy . ']', 0) != null) {
|
||||||
|
return json_decode(
|
||||||
|
html_entity_decode(
|
||||||
|
$deal->find('img[class=' . $selectorLazy . ']', 0)
|
||||||
|
->getAttribute('data-lazy-img')))->{'src'};
|
||||||
|
} else {
|
||||||
|
return $deal->find('img[class*=' . $selectorPlain . ']', 0 )->src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the originating country from a Deal if it exists
|
||||||
|
* @return string String of the deal originating country
|
||||||
|
*/
|
||||||
|
private function getShipsFrom($deal)
|
||||||
|
{
|
||||||
|
$selector = implode(
|
||||||
|
' ', /* Notice this is a space! */
|
||||||
|
array(
|
||||||
|
'hide--toW2',
|
||||||
|
'metaRibbon',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ($deal->find('span[class*=' . $selector . ']', 0) != null) {
|
||||||
|
return '<div>'
|
||||||
|
. $deal->find('span[class*=' . $selector . ']', 0)->children(2)->plaintext
|
||||||
|
. '</div>';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a local date into a timestamp
|
||||||
|
* @return int timestamp of the input date
|
||||||
|
*/
|
||||||
|
private function parseDate($string)
|
||||||
|
{
|
||||||
|
$month_local = $this->i8n('local-months');
|
||||||
|
$month_en = array(
|
||||||
|
'January',
|
||||||
|
'February',
|
||||||
|
'March',
|
||||||
|
'April',
|
||||||
|
'May',
|
||||||
|
'June',
|
||||||
|
'July',
|
||||||
|
'August',
|
||||||
|
'September',
|
||||||
|
'October',
|
||||||
|
'November',
|
||||||
|
'December'
|
||||||
|
);
|
||||||
|
|
||||||
|
// A date can be prfixed with some words, we remove theme
|
||||||
|
$string = $this->removeDatePrefixes($string);
|
||||||
|
// We translate the local months name in the english one
|
||||||
|
$date_str = trim(str_replace($month_local, $month_en, $string));
|
||||||
|
|
||||||
|
// If the date does not contain any year, we add the current year
|
||||||
|
if (!preg_match('/[0-9]{4}/', $string)) {
|
||||||
|
$date_str .= ' ' . date('Y');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the Hour and minutes
|
||||||
|
$date_str .= ' 00:00';
|
||||||
|
$date = DateTime::createFromFormat('j F Y H:i', $date_str);
|
||||||
|
// In some case, the date is not recognized : as a workaround the actual date is taken
|
||||||
|
if($date === false) {
|
||||||
|
$date = new DateTime();
|
||||||
|
}
|
||||||
|
return $date->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the prefix of a date if it has one
|
||||||
|
* @return the date without prefiux
|
||||||
|
*/
|
||||||
|
private function removeDatePrefixes($string)
|
||||||
|
{
|
||||||
|
$string = str_replace($this->i8n('date-prefixes'), array(), $string);
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the suffix of a relative date if it has one
|
||||||
|
* @return the relative date without suffixes
|
||||||
|
*/
|
||||||
|
private function removeRelativeDateSuffixes($string)
|
||||||
|
{
|
||||||
|
if (count($this->i8n('relative-date-ignore-suffix')) > 0) {
|
||||||
|
$string = preg_replace($this->i8n('relative-date-ignore-suffix'), '', $string);
|
||||||
|
}
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a relative local date into a timestamp
|
||||||
|
* @return int timestamp of the input date
|
||||||
|
*/
|
||||||
|
private function relativeDateToTimestamp($str) {
|
||||||
|
$date = new DateTime();
|
||||||
|
|
||||||
|
// In case of update date, replace it by the regular relative date first word
|
||||||
|
$str = str_replace($this->i8n('relative-date-alt-prefixes'), $this->i8n('local-time-relative')[0], $str);
|
||||||
|
|
||||||
|
$str = $this->removeRelativeDateSuffixes($str);
|
||||||
|
|
||||||
|
$search = $this->i8n('local-time-relative');
|
||||||
|
|
||||||
|
$replace = array(
|
||||||
|
'-',
|
||||||
|
'minute',
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'month',
|
||||||
|
'year',
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
$date->modify(str_replace($search, $replace, $str));
|
||||||
|
return $date->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the RSS Feed title according to the parameters
|
||||||
|
* @return string the RSS feed Tiyle
|
||||||
|
*/
|
||||||
|
public function getName(){
|
||||||
|
switch($this->queriedContext) {
|
||||||
|
case $this->i8n('context-keyword'):
|
||||||
|
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-keyword') . ' : ' . $this->getInput('q');
|
||||||
|
break;
|
||||||
|
case $this->i8n('context-group'):
|
||||||
|
$values = $this->getParameters()[$this->i8n('context-group')]['group']['values'];
|
||||||
|
$group = array_search($this->getInput('group'), $values);
|
||||||
|
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-group') . ' : ' . $group;
|
||||||
|
break;
|
||||||
|
case $this->i8n('context-talk'):
|
||||||
|
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-talk') . ' : ' . $this->getTalkTitle();
|
||||||
|
break;
|
||||||
|
default: // Return default value
|
||||||
|
return static::NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the RSS Feed URI according to the parameters
|
||||||
|
* @return string the RSS feed Title
|
||||||
|
*/
|
||||||
|
public function getURI(){
|
||||||
|
switch($this->queriedContext) {
|
||||||
|
case $this->i8n('context-keyword'):
|
||||||
|
return $this->getSearchURI();
|
||||||
|
break;
|
||||||
|
case $this->i8n('context-group'):
|
||||||
|
return $this->getGroupURI();
|
||||||
|
break;
|
||||||
|
case $this->i8n('context-talk'):
|
||||||
|
return $this->getTalkURI();
|
||||||
|
break;
|
||||||
|
default: // Return default value
|
||||||
|
return static::URI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the RSS Feed URI for a keyword Feed
|
||||||
|
* @return string the RSS feed URI
|
||||||
|
*/
|
||||||
|
private function getSearchURI(){
|
||||||
|
$q = $this->getInput('q');
|
||||||
|
$hide_expired = $this->getInput('hide_expired');
|
||||||
|
$hide_local = $this->getInput('hide_local');
|
||||||
|
$priceFrom = $this->getInput('priceFrom');
|
||||||
|
$priceTo = $this->getInput('priceTo');
|
||||||
|
$url = $this->i8n('bridge-uri')
|
||||||
|
. 'search/advanced?q='
|
||||||
|
. urlencode($q)
|
||||||
|
. '&hide_expired=' . $hide_expired
|
||||||
|
. '&hide_local=' . $hide_local
|
||||||
|
. '&priceFrom=' . $priceFrom
|
||||||
|
. '&priceTo=' . $priceTo
|
||||||
|
/* Some default parameters
|
||||||
|
* search_fields : Search in Titres & Descriptions & Codes
|
||||||
|
* sort_by : Sort the search by new deals
|
||||||
|
* time_frame : Search will not be on a limited timeframe
|
||||||
|
*/
|
||||||
|
. '&search_fields[]=1&search_fields[]=2&search_fields[]=3&sort_by=new&time_frame=0';
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the RSS Feed URI for a group Feed
|
||||||
|
* @return string the RSS feed URI
|
||||||
|
*/
|
||||||
|
private function getGroupURI(){
|
||||||
|
$group = $this->getInput('group');
|
||||||
|
$order = $this->getInput('order');
|
||||||
|
|
||||||
|
$url = $this->i8n('bridge-uri')
|
||||||
|
. $this->i8n('uri-group') . $group . $order;
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the RSS Feed URI for a Talk Feed
|
||||||
|
* @return string the RSS feed URI
|
||||||
|
*/
|
||||||
|
private function getTalkURI(){
|
||||||
|
$url = $this->getInput('url');
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is some "localisation" function that returns the needed content using
|
||||||
|
* the "$lang" class variable in the local class
|
||||||
|
* @return various the local content needed
|
||||||
|
*/
|
||||||
|
protected function i8n($key)
|
||||||
|
{
|
||||||
|
if (array_key_exists($key, $this->lang)) {
|
||||||
|
return $this->lang[$key];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('GelbooruBridge.php');
|
|
||||||
|
|
||||||
class Rule34Bridge extends GelbooruBridge {
|
class Rule34Bridge extends GelbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('Shimmie2Bridge.php');
|
|
||||||
|
|
||||||
class Rule34pahealBridge extends Shimmie2Bridge {
|
class Rule34pahealBridge extends Shimmie2Bridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('GelbooruBridge.php');
|
|
||||||
|
|
||||||
class SafebooruBridge extends GelbooruBridge {
|
class SafebooruBridge extends GelbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('DanbooruBridge.php');
|
|
||||||
|
|
||||||
class Shimmie2Bridge extends DanbooruBridge {
|
class Shimmie2Bridge extends DanbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('GelbooruBridge.php');
|
|
||||||
|
|
||||||
class TbibBridge extends GelbooruBridge {
|
class TbibBridge extends GelbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('GelbooruBridge.php');
|
|
||||||
|
|
||||||
class XbooruBridge extends GelbooruBridge {
|
class XbooruBridge extends GelbooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('MoebooruBridge.php');
|
|
||||||
|
|
||||||
class YandereBridge extends MoebooruBridge {
|
class YandereBridge extends MoebooruBridge {
|
||||||
|
|
||||||
|
|
|
@ -50,33 +50,8 @@ define('FILE_CONFIG_DEFAULT', PATH_ROOT . 'config.default.ini.php');
|
||||||
/** URL to the RSS-Bridge repository */
|
/** URL to the RSS-Bridge repository */
|
||||||
define('REPOSITORY', 'https://github.com/RSS-Bridge/rss-bridge/');
|
define('REPOSITORY', 'https://github.com/RSS-Bridge/rss-bridge/');
|
||||||
|
|
||||||
// Interfaces
|
|
||||||
require_once PATH_LIB . 'ActionInterface.php';
|
|
||||||
require_once PATH_LIB . 'BridgeInterface.php';
|
|
||||||
require_once PATH_LIB . 'CacheInterface.php';
|
|
||||||
require_once PATH_LIB . 'FormatInterface.php';
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
require_once PATH_LIB . 'FactoryAbstract.php';
|
|
||||||
require_once PATH_LIB . 'FeedItem.php';
|
|
||||||
require_once PATH_LIB . 'Debug.php';
|
|
||||||
require_once PATH_LIB . 'Exceptions.php';
|
|
||||||
require_once PATH_LIB . 'FormatFactory.php';
|
|
||||||
require_once PATH_LIB . 'FormatAbstract.php';
|
|
||||||
require_once PATH_LIB . 'BridgeFactory.php';
|
|
||||||
require_once PATH_LIB . 'BridgeAbstract.php';
|
|
||||||
require_once PATH_LIB . 'FeedExpander.php';
|
|
||||||
require_once PATH_LIB . 'CacheFactory.php';
|
|
||||||
require_once PATH_LIB . 'Authentication.php';
|
|
||||||
require_once PATH_LIB . 'Configuration.php';
|
|
||||||
require_once PATH_LIB . 'BridgeCard.php';
|
|
||||||
require_once PATH_LIB . 'BridgeList.php';
|
|
||||||
require_once PATH_LIB . 'ParameterValidator.php';
|
|
||||||
require_once PATH_LIB . 'ActionFactory.php';
|
|
||||||
require_once PATH_LIB . 'ActionAbstract.php';
|
|
||||||
require_once PATH_LIB . 'XPathAbstract.php';
|
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
require_once PATH_LIB . 'Exceptions.php';
|
||||||
require_once PATH_LIB . 'html.php';
|
require_once PATH_LIB . 'html.php';
|
||||||
require_once PATH_LIB . 'error.php';
|
require_once PATH_LIB . 'error.php';
|
||||||
require_once PATH_LIB . 'contents.php';
|
require_once PATH_LIB . 'contents.php';
|
||||||
|
|
|
@ -209,7 +209,7 @@ class BridgeImplementationTest extends TestCase {
|
||||||
|
|
||||||
public function dataBridgesProvider() {
|
public function dataBridgesProvider() {
|
||||||
$bridges = array();
|
$bridges = array();
|
||||||
foreach (glob(PATH_LIB_BRIDGES . '*.php') as $path) {
|
foreach (glob(PATH_LIB_BRIDGES . '*Bridge.php') as $path) {
|
||||||
$bridges[basename($path, '.php')] = array($path);
|
$bridges[basename($path, '.php')] = array($path);
|
||||||
}
|
}
|
||||||
return $bridges;
|
return $bridges;
|
||||||
|
|
Loading…
Add table
Reference in a new issue