1
0
mirror of https://github.com/RSS-Bridge/rss-bridge.git synced 2025-08-17 22:02:09 +02:00

Compare commits

..

590 Commits

Author SHA1 Message Date
Yaman Qalieh
9503f9ad7f [Release] 2022-06-14 (#2818) 2022-06-14 09:47:12 -04:00
Yaman Qalieh
3e2423d86b [MsnMondeBridge] Fix bridge (#2813) 2022-06-14 09:45:46 -04:00
Yaman Qalieh
90e0504da5 [Shimmie2Bridge] Fix bridge (#2814) 2022-06-14 09:45:35 -04:00
Yaman Qalieh
4b3b1ca163 [KununuBridge] Fix bridge for default parameters (#2816) 2022-06-14 09:45:01 -04:00
Yaman Qalieh
5b93bba1a3 [ZenodoBridge] Fix bridge (#2815) 2022-06-14 09:44:26 -04:00
Dag
b8786da137 Update 06_Public_Hosts.md (#2812)
Modify url to use https.
2022-06-10 17:19:45 +02:00
Kristian Salonen
166ead902d [Configuration.php] Update the version name to dev.2022-06-10 (#2811) 2022-06-10 15:05:34 +02:00
sal0max
de279de762 [CyanideAndHappiness] Add bridge (#2807) 2022-06-10 07:29:01 +02:00
Jan Tojnar
347f9a3eda [contents] Add MIME type for mp3 (#2809)
Without this, format tests fail on systems without `/etc/mime.types`.
2022-06-09 22:41:10 -04:00
Jan Tojnar
1af6cbeb1e [XML formats] Ensure elements are connected to DOM before further manipulation (#2806)
We are setting xmlns attributes at the root element but PHP would
still attach redundant ones to the DOM elements created with `createElementNS`.
That was because PHP reconciles namespace attributes when appending elements to DOM
but since we previously only attached the elements after all children were attached,
the reconciliation algorithm was not able to see the root element’s attributes.

To fix this, let’s attach each element to its parent immediately after it is created.
2022-06-09 18:33:23 +02:00
Yaman Qalieh
37f211a37e Add Laravel framework license 2022-06-09 12:13:07 -04:00
Yaman Qalieh
bea0595e5c Add php-urljoin license 2022-06-09 12:13:07 -04:00
Dag
6df5a4bc14 feat: backport php 7.3 functions (#2803)
* feat: backport php 7.3 functions

* fix: add license

* fix: formatting

* fix: add note in README regarding license
2022-06-09 18:00:51 +02:00
Yaman Qalieh
3927ecd822 [UsenixBridge] Add bridge (#2800) 2022-06-09 16:56:52 +02:00
pubak42
1b0a6f2813 [VixenBridge] New bridge (#2763) 2022-06-09 16:53:26 +02:00
Yaman Qalieh
8f0d90f653 [PixivBridge] Fix tags (#2799) 2022-06-08 23:05:56 -04:00
Yaman Qalieh
037d5866ca [BandcampDailyBridge] Fix list duplicates (#2795) 2022-06-08 19:37:06 -04:00
Yaman Qalieh
75c4c9f256 Revert "[Usenix] Add new bridge for USENIX (usenix.org) publications (#2772)" (#2793)
This reverts commit baa4ea8338.
2022-06-08 00:44:37 -04:00
Dag
baa4ea8338 [Usenix] Add new bridge for USENIX (usenix.org) publications (#2772)
Currently only supporting the ;login: publication.
2022-06-08 05:43:56 +02:00
Jan Tojnar
12ddee4054 tests/Formats: Simplify by using a base class (#2779)
There is a lot of redundancy. Let’s not repeat ourselves.

Unfortunately, since we do not install PHPUnit as a project dependency on CI,
it does not use the composer’s PSR-4 autoloader and the tests are unable to find
the `BaseFormatTest` class.

Until we resolve that, let’s load the class explicitly.
2022-06-08 02:17:32 +02:00
Dag
6582a66a2d Revert "Update tests.yml (#2788)" (#2792)
This reverts commit aa32040bd4.
2022-06-08 02:16:06 +02:00
sysadminstory
a4785370fa [DealabsBridge-HotUKDealsBridge-MydealsBridge-PepperBridgeAbstract] Fix (#2789)
the date handling

The deal posting date logic was wrong, and leaded to warnings and
notice. Now, only the feed with the deal sorted by date contains date
(the feed sorted by hottest deal does not contain a date anymore,
because there are no deal date in this case).
2022-06-07 23:55:15 +02:00
Dag
aa32040bd4 Update tests.yml (#2788) 2022-06-07 23:33:16 +02:00
Jan Tojnar
44e8007d9c tests: Use PSR-4-style namespaces (#2778)
We cannot yet switch to namespaces for RSS-Bridge itself but for tests we are not limited by BC.
It does not actually do anything since PHPUnit will search for the test files without the help of the autoloader but it still makes the directory cleaner.
2022-06-07 23:22:33 +02:00
Jan Tojnar
90d22f0d80 [{Atom,Mrss}Format]: Generate using DomDocument (#2771)
* [AtomFormat]: Generate using DomDocument

This will escape the HTML content for us as needed.

* [MrssFormat]: Generate using DomDocument

This will escape the HTML content for us as needed.
2022-06-07 23:22:03 +02:00
Jan Tojnar
fb501652d5 Formats: Remove display & related method (#2776)
Format should not be responsible for sending HTTP response.
2022-06-07 18:05:33 +02:00
Joseph
e85932b1a5 [BrutBridge] Fix bridge (#2787) 2022-06-07 18:05:03 +02:00
Korytov Pavel
53f9970403 [EuronewsBridge] Add bridge (#2786) 2022-06-07 10:25:20 -04:00
Jan Tojnar
19ad2584da [NFLRUSBridge] Remove byte-order-mark (#2777)
With UTF-8 byte-order mark in the file, the `ListActionTest::testOutput`
would fail after converting tests to PSR-4 namespaces:

    invalid JSON output: Syntax error
    Failed asserting that null is not null.

This is because ListAction::execute tries to create the bridge objects
and, when the files containing the bridge classes are not loaded yet,
the autoloader starts including them. Since this happens after output
buffering has begun, any text in the PHP file before the `<?php` tag
such as the BOM will end up in the buffer to be parsed by `json_decode`.

Previously, it worked by chance thanks to some other test including the file
before `ListActionTest`. With the restructuring, `Actions\ListActionTest`
will run sooner and become responsible for triggering the autoloader.

To prevent this in the future, I also disallowed BOM in the coding style.
2022-06-07 04:59:22 +02:00
somini
190c95fa62 [PCGWNewsBridge]: New Bridge (#2785) 2022-06-06 01:02:15 +02:00
Dag
678e5d9866 [NeuviemeArt] Exterminate dead bridge (#2784)
They moved to https://www.bubblebd.com/9emeart

Fixes #2774
2022-06-05 22:56:54 +02:00
Yaman Qalieh
4787eb3799 [WordPressMadaraBridge] Add Bridge (#2782) 2022-06-05 14:40:43 -04:00
Yaman Qalieh
a863234474 [MangaDexBridge] add chapter search context (#2783) 2022-06-05 14:28:05 -04:00
Korytov Pavel
4260be26a2 [EconomistWorldInBriefBridge] indent with tabs instead of spaces (#2781) 2022-06-05 18:40:48 +02:00
Mynacol
713d06ba08 [GitlabIssueBridge] Code cleanup (#2780)
- Rename parseMRDescription() -> parseMergeRequestDescription()
- Move parseMergeRequestDescription() below parseIssueDescription()
- Inline getProjectURI()
2022-06-05 18:39:54 +02:00
Korytov Pavel
7256d1138b [EconomistWorldInBriefBridge] Add bridge (#2765) 2022-06-05 17:16:11 +02:00
Dag
71310d2c5a [OsmAndBlog] Remove bridge (#2775)
They revamped their page. The feed has been returning a single item
for some time.

Their blog can be followed at:
https://osmand.net/blog/atom.xml
2022-06-05 03:51:37 +02:00
Dag
92d813fbea [NotAlways] fix: broken url (#2773)
The /all url now actually points to a specific item.
I think we want the frontpage for this.

Fixes:
Fatal error: Uncaught Error: Call to a member function find() on null in NotAlwaysBridge.php:37
2022-06-05 02:56:51 +02:00
Mynacol
3f896f9465 [GitlabIssueBridge] Add bridge (#2760)
* [GitlabIssueBridge] new bridge

This tracks issue comments on arbitrary gitlab projects.

* [GitlabIssueBridge] Prepare for Merge Request support + fixes

- Proper UIDs
- Default bridge name fixed
- Fix cache identifiers
- Add TODOs

* [GitlabIssueBridge] creation timestamp preferred

And prefer original author over editor.

* [GitlabIssueBridge] Do not add date to item title

Prettier without it.

* [GitlabIssueBridge] Support Merge Requests

This bridge can now generate feeds for Merge Requests.

* [GitlabIssueBridge] typo

* [GitlabIssueBridge] Fix Img src attr in comments

* [GitlabIssueBridge] Fix function call

* [GitlabIssueBridge] Fix test

Use gitlab.com if no h parameter was given.
Fixes a phpunit test.

* [GitlabIssueBridge] linting

* [GitlabIssueBridge] Add MR support to description

* [GitlabIssueBridge] Move function collectData

* [GitlabIssueBridge] rm single-use class constants

* [GitlabIssueBridge] Remove manual caching

Just depend on rss-bridges built-in caching.
2022-06-04 23:59:10 +02:00
Shikiryu
b7e1dc1ab1 [KhinsiderBridge] fix RSS because of the new layout (#2767)
* [KhinsiderBridge] fix RSS because of the new layout

* [KhinsiderBridge] fix phpcs
2022-06-04 22:41:37 +02:00
sysadminstory
8e41887393 [DealabsBridge-HotUKDealsBridge-MydealsBridge] Fix example values (#2766)
Added real example values for discussion to allow automatic testing.

Updated keywords example value to be sure there will be some results.
2022-06-04 22:40:20 +02:00
Mynacol
8865521b3b [GolemBridge] Remove image galleries (#2761)
Do not add all images of the image gallery, but only the preselected one.

Often, the same gallery is used multiple times with different preselected
images. The previous implementation always added all images of the
gallery, cluttering the article. This patch only adds the preselected one.

The no-js link wrapping around the gallery leads to a 403 Forbidden
page, so linking that doesn't work to really support galleries.
2022-06-04 22:27:24 +02:00
Austin Huang
8172d10bb5 [Amazon & AmazonPriceTracker] Add Turkey, close #2665 (#2758)
* [AmazonPriceTracker] Add Turkey, close #2665

* [Amazon] Add Sweden & Turkey

consistent with price tracker
2022-06-04 21:59:52 +02:00
Park0
299ad87168 [Marktplaats] #2553 example values added (#2752)
For automation tests example values are needed
2022-06-04 21:06:38 +02:00
Dag
d60d8313d0 fix: type error in function call (#2769)
Fixes:
Argument 2 passed to getContents() must be of the type array, int given
2022-06-04 21:05:43 +02:00
Yaman Qalieh
1fd2f37bb4 [PixivBridge] Fix 404 for fullsize novel images (#2751) 2022-06-04 20:53:10 +02:00
Dag
04b1609ce0 docs: refactor table of public instances (#2749) 2022-06-04 20:52:10 +02:00
Christian Schabesberger
2fa24e780b Fix nordbayern (#2730) 2022-06-04 20:50:16 +02:00
quickwick
3b04e318ae [SlusheBridge] New bridge (#2700) 2022-06-04 20:10:07 +02:00
Alexandre Alapetite
05cd1c0b67 [Core] Add expose to dockerfile (#2762) 2022-05-30 20:05:42 +02:00
Dag
cb05cacd6a fix: add 429 to status codes (#2757) 2022-05-27 15:25:12 +02:00
Bocki
4d18312604 [Core] Prtester fix list fix (#2753) 2022-05-25 20:26:39 +02:00
Joseph
85e5ce2679 [UberNewsroomBridge] Add bridge (#2748) 2022-05-25 09:43:18 +02:00
Bocki
7afc577e97 [core] Fix nested selection lists (#2750) 2022-05-25 09:38:53 +02:00
KamaleiZestri
462319344b [CubariBridge] New Bridge (#2747) 2022-05-24 13:34:40 +02:00
Dag
5cc34b884a [core] Improve getContents docs (#2742) 2022-05-22 21:27:23 -04:00
KamaleiZestri
dd025894e9 [PillowfortBridge] Modify example value for Pillowfort Bridge (#2746) 2022-05-22 15:30:45 -04:00
Dag
1d0a0b927b fix: use accept header when fetching feed (#2737)
* fix: use accept header when fetching feed

* fix: include atom too, and reuse constants from format classes

* add a catch all accept header
2022-05-18 00:18:33 +02:00
Dag
4007afdcf5 Merge branch 'autoloading' into master 2022-05-17 23:59:18 +02:00
Dag
7d00b0c5df fix: include http code in exception (#2726) 2022-05-17 23:47:12 +02:00
Dag
0212c4790f fix: connectivityaction (#2725) 2022-05-17 23:46:37 +02:00
Kingsley Yung
7a87a09fc5 [YouTubeCommunityTab] Fix error occuring when YouTube returns non-English webpage. (#2739) 2022-05-17 09:35:16 +02:00
sysadminstory
1e3f5f3ad3 [PepperBridge] Update CSS selectors (#2740)
Updated some CSS selectors to follow the website change
2022-05-17 09:34:03 +02:00
Yaman Qalieh
f709778b28 [MydealsBridge] Fix Example value (#2728) 2022-05-14 08:04:21 -04:00
Yaman Qalieh
f4a0711b62 docker: fix find error (#2733) 2022-05-14 07:18:58 -04:00
Dag
4d069fcf99 remove unnecessary includes 2022-05-13 09:35:25 +02:00
Dag
f00f90328d refactor: extract class PepperBridgeAbstract 2022-05-13 09:29:56 +02:00
Yaman Qalieh
bb6d553dd5 Revert "refactor: remove unnecesary includes" (#2723)
This reverts commit fd449be4eb.
2022-05-12 16:28:03 -04:00
Joseph
97b513823d [HaveIBeenPwnedBridge] Fix item URIs (#2724) 2022-05-12 22:16:34 +02:00
Eric G
e01f0bcaf2 [GiteaBridge] Rewrite to decouple from Gogs and add contexts (#2718) 2022-05-12 22:15:03 +02:00
Yaman Qalieh
e5829d37b6 [HaveIBeenPwnedBridge] Use API to get Data (#2720) 2022-05-12 21:53:03 +02:00
Yaman Qalieh
73b1a6a7aa [FDroidRepoBridge] Add F-Droid Repo Bridge 2022-05-12 09:37:11 -04:00
Yaman Qalieh
e07fac777a core: Enable zip extension 2022-05-12 09:37:11 -04:00
Dag
fd449be4eb refactor: remove unnecesary includes 2022-05-12 15:15:09 +02:00
Dag
829fc6cca2 docker: Switch to nginx in docker image (#2721)
Co-authored-by: Yaman Qalieh <ybq987@gmail.com>
2022-05-11 20:19:25 -04:00
Dag
fcc3707210 refactor: swap the order of sprintf values 2022-05-11 22:37:59 +02:00
Dag
d5e9dbf47d refactor: restore some useful comments 2022-05-11 22:35:03 +02:00
User123698745
96a63a8e81 [PicukiBridge] fix image not displaying (#2717) 2022-05-10 19:53:29 +02:00
quickwick
9110b70f07 [TwitterV2Bridge] Properly include quoted tweets (#2713) 2022-05-10 09:41:12 +02:00
Yaman Qalieh
6547ed0c04 [docs] Add documentation for html.php functions (#2714) 2022-05-10 09:37:53 +02:00
Dag
8982995445 refactor: remove unused method 2022-05-09 23:32:45 +02:00
Dag
76084cdcca fix: logic bug in limiting 2022-05-09 21:02:21 +02:00
Dag
a28dca2c9d chore: upgrade phpunit 7 => 9
Upgraded with:

composer require -W --dev phpunit/phpunit:^9
2022-05-09 20:52:02 +02:00
sysadminstory
51f0d046d0 [AllocineFRBridge] Automatically find the last season for every show (#2709)
The bridge now finds the last season URI by itself, and don't rely on
static URL stored in the bridge itself.
2022-05-08 17:22:39 +02:00
Christian Schabesberger
fb2ed95368 Fix nordbayern (#2708)
* fix newspaper thumbnails are shown again

* show article teaser on top of title image for NN
2022-05-08 16:37:53 +02:00
Mynacol
36d11fd06e [XenForoBridge] Fix error if message is < 70 chars (#2707)
At the time of writing, this occurs on the following thread:
https://forum.xda-developers.com/t/optimized-lineageos19-1-v4-0-23apr.4426575/

Fixes the following error:
ValueError: strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack) in ./rss-bridge/bridges/XenForoBridge.php:272
Stack trace:
0 ./rss-bridge/bridges/XenForoBridge.php(272): strpos()
1 ./rss-bridge/bridges/XenForoBridge.php(146): XenForoBridge->extractThreadPostsV2()
2 ./rss-bridge/actions/DisplayAction.php(134): XenForoBridge->collectData()
3 ./rss-bridge/index.php(24): DisplayAction->execute()
4 {main}
2022-05-08 16:25:01 +02:00
quickwick
d107592094 Don't hide quoting tweets when 'hide retweets' is selected (#2706) 2022-05-08 16:22:31 +02:00
Yaman Qalieh
0ce71d561d [PixivBridge] [UnsplashBridge] Fix deprecated null (#2705) 2022-05-08 16:17:26 +02:00
Dag
f5a51038cc fix: error when passing null values
This bug was introduced by me when refactoring the http client.

Fixes:

Fatal error: Uncaught TypeError: Argument 2 passed to getContents() must be of the type array, null given
2022-05-08 04:42:24 +02:00
Yaman Qalieh
3476b06ee0 [MangaDexBridge] Exclude external chapters (#2703) 2022-05-08 04:22:33 +02:00
Yaman Qalieh
158ee41be4 [AtomFormat] Remove redundant fallback content (#2702) 2022-05-08 04:21:32 +02:00
sysadminstory
37843e8777 [RadioMelodieBridge] Fix date parsing (#2701)
The date is now correctly parsed for every month in the year (There are months mane in french that are 3 letters long
2022-05-08 04:19:06 +02:00
Mynacol
56e991122b [GolemBridge] Add golem.de bridge (#2696) 2022-05-08 04:08:55 +02:00
Dag
5d77d14f9d feat: add retry logic to the http client (#2692)
* refactor: extract http client

* feat: add retry logic to http client
2022-05-08 03:58:57 +02:00
Dag
0c7a7f320f refactor: BridgeFactory (#2691) 2022-05-08 03:58:42 +02:00
Dag
b2f1d051fc fix: don't bork upstream with http status code -1 (#2690) 2022-05-08 03:57:46 +02:00
Dag
641e2eedf5 test: exclude Pixiv for a particular test 2022-05-08 03:55:24 +02:00
Binnette
bc773a49f8 Full rewrite of bridge DeveloppezDotCom (#2689) 2022-05-08 03:38:33 +02:00
Yaman Qalieh
410daee1d5 [PixivBridge] Add User context (#2650) 2022-05-08 02:46:57 +02:00
Christian Schabesberger
adeaede930 [NordbayernBridge] Fix Bridge (#2675) 2022-05-02 19:06:30 +02:00
Dag
9b82ff352d fix: Fatal error: Uncaught ArgumentCountError 2022-05-01 21:35:52 +02:00
Nemo
31455b6838 [npci] Adds new NPCI Bridge (#2651) 2022-04-29 00:01:18 +02:00
Dag
63b08f7da9 Update app.json 2022-04-26 22:43:13 +02:00
Dag
61cfbe6c53 Update app.json 2022-04-26 22:41:34 +02:00
quickwick
4c26950b71 [TwitterV2Bridge] Fix empty object check (#2673) 2022-04-26 12:11:26 +02:00
pirnz
9dc31dfcfa [AsahiShimbunAJWBridge] Updated Asia section links (#2671) 2022-04-26 12:10:10 +02:00
Dag
db8462e6fa chore: add scripts section to composer.json (#2684) 2022-04-26 01:59:50 +02:00
Dag
19a8165fc6 docs: add host to public instances (#2685) 2022-04-26 01:41:04 +02:00
Dag
0ef298f9cc refactor: add php autoloader (#2655) 2022-04-26 00:57:59 +02:00
Corentin Garcia
b090b17bbf [RobinhoodSnacksBridge] fix bridge (#2676) 2022-04-26 00:53:18 +02:00
sysadminstory
ca749e7bad [ZoneTelechargement] Remove bridge (#2678)
Website announced the shutdown
2022-04-25 20:01:39 +02:00
Patrick Collins
e1c898848f [contents.php] Fix incorrect reference to UnexpectedResponseException's responseBody. (#2677) 2022-04-23 10:04:56 +05:00
Eric G
46a356b0b2 [GogsBridge] Add protocol to examplevalue (#2668) 2022-04-18 22:31:26 +02:00
Joseph
fe042305e4 [GoogleSearchBridge] Update bridge (#1869) 2022-04-16 23:16:38 +02:00
Loïc Fürhoff
669e92357a [Arte7Bridge] Exclude trailers and sort by v2 (#2664) 2022-04-16 23:08:27 +02:00
Nemo
1dec457b7b docu: Add back rss-bridge.bb8.fun (#2666) 2022-04-16 23:05:26 +02:00
TotalCaesar659
a38951b911 general: Update URLs to HTTPS (#2667) 2022-04-16 23:03:15 +02:00
Dag
b11f1368bf Update 06_Public_Hosts.md 2022-04-15 22:51:30 +02:00
Dag
1a698b3554 fix: remove dead public instances
https://rss-bridge.bb8.fun 404
https://myrss4fun.xyz not serving rss-bridge
https://rssbridge.fossdaily.xyz Curl failed for "https://rssbridge.fossdaily.xyz": Could not resolve host: rssbridge.fossdaily.xyz (6)
https://bridge.noisebridge.info 502
https://rss-bridge.esmailelbob.xyz 502
2022-04-15 22:51:12 +02:00
Dag
73ebdbf67a Revert "[Arte7Bridge] Exclude trailers and sort by (#2660)" (#2662)
This reverts commit 924eaf2011.

That commit broke the bridge.
2022-04-15 22:02:41 +02:00
Eugene Molotov
ac766aa47f [RutubeBridge] Add bridge (#2661) 2022-04-16 00:37:38 +05:00
Nemo
2be613e015 [BookMyShowBridge] Add new bridge (#1349) 2022-04-15 19:55:32 +02:00
Loïc Fürhoff
924eaf2011 [Arte7Bridge] Exclude trailers and sort by (#2660) 2022-04-14 23:20:09 +02:00
Alex Balgavy
d082bfca4a [SeznamZpravyBridge] fix: broken bridge (#2658) 2022-04-14 14:38:16 +02:00
Dag
91283f3a62 fix: deprecation notice (#2656) 2022-04-13 21:35:54 +02:00
Dag
d6beb713b5 chore: upgrade dependencies and improve package.json (#2648)
* chore: update composer dependencies

$ composer update
Loading composer repositories with package information
Updating dependencies
Info from https://repo.packagist.org: #StandWithUkraine
Lock file operations: 0 installs, 8 updates, 0 removals
  - Upgrading doctrine/instantiator (1.4.0 => 1.4.1)
  - Upgrading myclabs/deep-copy (1.10.2 => 1.11.0)
  - Upgrading phpdocumentor/reflection-docblock (5.2.2 => 5.3.0)
  - Upgrading phpdocumentor/type-resolver (1.4.0 => 1.6.1)
  - Upgrading phpspec/prophecy (1.13.0 => v1.15.0)
  - Upgrading phpunit/php-file-iterator (2.0.4 => 2.0.5)
  - Upgrading sebastian/exporter (3.1.3 => 3.1.4)
  - Upgrading symfony/polyfill-ctype (v1.23.0 => v1.25.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 8 updates, 0 removals
  - Upgrading symfony/polyfill-ctype (v1.23.0 => v1.25.0): Extracting archive
  - Upgrading phpdocumentor/type-resolver (1.4.0 => 1.6.1): Extracting archive
  - Upgrading phpdocumentor/reflection-docblock (5.2.2 => 5.3.0): Extracting archive
  - Upgrading sebastian/exporter (3.1.3 => 3.1.4): Extracting archive
  - Upgrading phpunit/php-file-iterator (2.0.4 => 2.0.5): Extracting archive
  - Upgrading doctrine/instantiator (1.4.0 => 1.4.1): Extracting archive
  - Upgrading phpspec/prophecy (1.13.0 => v1.15.0): Extracting archive
  - Upgrading myclabs/deep-copy (1.10.2 => 1.11.0): Extracting archive
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
16 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

* chore: add dev-dependency squizlabs/php_codesniffer (phpcs)

$ composer require --dev squizlabs/php_codesniffer
Info from https://repo.packagist.org: #StandWithUkraine
Using version ^3.6 for squizlabs/php_codesniffer
./composer.json has been updated
Running composer update squizlabs/php_codesniffer
Loading composer repositories with package information
Updating dependencies
Info from https://repo.packagist.org: #StandWithUkraine
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking squizlabs/php_codesniffer (3.6.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing squizlabs/php_codesniffer (3.6.2): Extracting archive
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
16 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

* chore: add package type => "project" in composer.json
2022-04-13 21:04:27 +02:00
Dag
d62b977394 refactor: ./tests (#2649)
* refactor: ./tests

* test: consolidate testsuites

* refactor: move config setup into rssbridge.php

Makes it easier to unit test.

* lint
2022-04-13 21:04:10 +02:00
Austin Huang
183004f954 Update 06_Public_Hosts.md (#2654)
1. nixnet.xyz => nixnet.services
2. Add my instance

Supersedes #2653
2022-04-13 20:36:28 +02:00
Shikiryu
ff8ece213f [PicalaBridge] Add new bridge (#2646) 2022-04-13 13:25:32 +02:00
Nemo
3e5675c256 [GoodreadsBridge] Add new bridge (#1559) 2022-04-13 13:18:05 +02:00
Dag
5a7d305e07 [Nordbayern] fix: problem with absolute and relative link (#2637)
* [Nordbayern] fix: problem with absolute and relative link

Fixes:

cURL error: Could not resolve host: www.nordbayern.dehttps
2022-04-12 23:40:37 +02:00
Dag
7379e2b3d5 [Parler] feat: add new bridge (#2634) 2022-04-12 23:39:32 +02:00
Dag
57c8806954 [ParuVenduImmo] fix: try to repair broken css selectors (#2641)
* [ParuVenduImmo] fix: try to repair broken css selectors

Needs more work.
2022-04-12 23:37:54 +02:00
Dag
b6e8350596 fix: a bunch of small changes in multiple bridges (#2644)
Mostly refactors.
2022-04-12 23:37:30 +02:00
Dag
5b7dd45b20 [UsbekEtRica] fix: broken css selectors (#2643) 2022-04-12 23:37:17 +02:00
Dag
f9801a5c58 [RoadAndTrack] fix: broken css selectors (#2642) 2022-04-12 23:37:05 +02:00
Dag
563c099d80 [NFLRUS] fix: broken css selectors (#2640)
This bridge needs more work.
2022-04-12 23:35:04 +02:00
Dag
b6e8e3ea6e [N26] fix: broken css selectors (#2639) 2022-04-12 23:34:52 +02:00
Dag
9e2e32a19d [Amazon] fix: broken css selectors (#2638) 2022-04-12 23:34:40 +02:00
Dag
df5c259375 [LaCentrale] fix: broken css selectors (#2636) 2022-04-12 23:34:23 +02:00
quickwick
6021d2ffa6 [GelbooruBridge] Change tag examplevalue to one valid for all inheriting bridges (#2645) 2022-04-12 16:52:34 +02:00
Dag
908da78113 Update phpcs.xml 2022-04-11 02:28:41 +02:00
Dag
a28481aaa8 [XenForo] fix: sort items by date in descending order (#2633) 2022-04-11 00:42:53 +02:00
Dag
bb81af086f [Castorus] fix: htmlentities bug (#2632) 2022-04-10 22:47:02 +02:00
Dag
60f1c46779 docs: move screenshots from wiki to repo (#2631) 2022-04-10 22:23:06 +02:00
Corentin Garcia
ae760e40cc replace wiki links with documentation links (#2630)
* docs: update composer support links

* docs: update link from wiki to docs for github issue template

* docs: update link pointing to wiki to point to new documentation

* docs: replace wiki links by documentation links in README and
CONTRIBUTING files
2022-04-10 21:58:29 +02:00
dag
5a733b3d82 feat: add limit options to the slowest bridges 2022-04-10 18:56:24 +02:00
dag
0b40f51c01 [Picuki] fix: item parsing (#2619)
Fixes a problem with the entire content being a link.

Also truncate title.

They have referrer checks on their images. So clicking the
enclosure doesnt work. Will fix later.
2022-04-10 18:54:48 +02:00
dag
dbee47f1d6 fix: give better error message when feed can't be parsed (#2618) 2022-04-10 18:54:32 +02:00
dag
c3a106892d fix: require curl extension (#2617) 2022-04-10 18:54:18 +02:00
quickwick
db28bedb23 [TwitterV2Bridge] Changes to output HTML/CSS, larger display image by default (#2626) 2022-04-10 18:53:35 +02:00
User123698745
aacf5812ff [GiphyBridge] include search text in feed name (#2628)
Co-authored-by: User123698745 <usr123698745+git@gmail.com>
2022-04-10 18:06:33 +02:00
User123698745
bf2f9a06f9 [Docker] force unix line ending on bash files to prevent docker run failing on windows (#2629) 2022-04-10 18:05:43 +02:00
User123698745
7833d0e6c3 [GiphyBridge] include bundle parameter in api calls to reduce bandwidth (#2627) 2022-04-10 15:21:43 +02:00
Corentin Garcia
c498749c2b [TwitterEngineeringBridge] add bridge #2385 (#2623) 2022-04-10 11:47:42 +02:00
User123698745
722f9ff0ce [GiphyBridge] use not rate limited public api key (#2625)
Co-authored-by: User123698745 <usr123698745+git@gmail.com>
2022-04-10 11:37:02 +02:00
Vít Kabele
5c08984714 InstagramBridge.php: Display usernames and hashtags as links. (#1582) 2022-04-08 23:47:10 +02:00
dag
dc01891634 fix: enclosure link privacy (#2620) 2022-04-08 23:14:43 +02:00
dag
cce11964a4 feat: add a timeout option for http client (#2600) 2022-04-08 21:22:13 +02:00
Corentin Garcia
8c18c02c65 [GatesNotesBridge] Add feedaxpander bridge for Bill Gate's blog (fix issue #2386) (#2611) 2022-04-08 21:21:13 +02:00
Antoine Turmel
51d27300be [FeedMergeBridge] Add new bridge (#1385)
* [FeedMergeBridge] Add new bridge

Here is a bridge that merges two or more feeds into one.

Co-authored-by: Bocki <henning@bocklage.com>
Co-authored-by: Dag <me@dvikan.no>
2022-04-08 21:13:05 +02:00
quickwick
c0e2a430ab [TwitterV2Bridge] Add parameter to include only media tweets (#2614) 2022-04-07 09:00:28 +02:00
quickwick
daae089299 [TwitterV2Bridge] Changes to parameters and output titles (#2612) 2022-04-06 20:56:56 +02:00
Joseph
d98add2cac [TelegramBridge] Fix issues & add support for location messages (#2133) 2022-04-06 10:15:21 +02:00
Tobias Alexander Franke
a3b0b91dee [BinanceBridge] Remove announcements because of Cloudflare issue (#2610) 2022-04-05 23:20:27 +02:00
langfingaz
6ffe531e4f [UnsplashBridge] extend functionality (#1813) 2022-04-05 15:00:10 +02:00
Bocki
fb28107cc4 [Core] Fix prtester context issue (#2609) 2022-04-05 14:46:42 +02:00
Bocki
2c50bbae95 [AssociatedPressNewsBridge] fix checks (#2608) 2022-04-05 14:37:15 +02:00
Joseph
8f9314947b [AssociatedPressNewsBridge] Add bridge (#1475) 2022-04-05 14:03:25 +02:00
Mikalai Daronin
b24cdd47f0 [AlfaBankByBridge] new bridge for alfabank.by (#2349) 2022-04-05 13:14:09 +02:00
Tomer Shvueli
233a3cb643 general: Added a button to install RSS Bridge on Cloudron (#2559) 2022-04-05 12:22:47 +02:00
Bocki
91c6645fc7 [core] fix testing changes (#2607) 2022-04-05 12:17:19 +02:00
quickwick
780581939a [TwitterV2Bridge] New Bridge for Twitter v2 API (#2471)
* New Bridge for Twitter using v2 API

* Top comment block, tweaks to match contributing guide

* [TwitterV2Bridge] new Bridge (sort of)

* Discovered the point of, and re-added, no image scaling option

* Fix the phpcs sniff violations (I hope)

* More linter fixes, I figured out how to use phpcs locally

* Removed unnecessary custom version of getContents function

* Limit query to 100 tweets, valid example query, improved error handling

* Added config doc (correctly, I hope) with link from DESCRIPTION

* little tweak to doc
2022-04-04 21:13:05 +02:00
arnd-s
0d305f1530 [TwitterBridge] Migration to API V1.1 (#2433) 2022-04-04 19:50:59 +02:00
Nemo
e1e9a12440 [AmazonPriceTrackerBridge] Minor fix for parser, and new strategy (#2603) 2022-04-04 19:41:40 +02:00
Bocki
d34b94848b [Core] Adapt list behavior (#2605) 2022-04-04 19:40:46 +02:00
Nemo
2eaf48de99 Fix AppleAppStoreBridge (#2604) 2022-04-04 19:05:52 +02:00
LogMANOriginal
d3bb00f754 docs: Explain loadCacheValue and saveCacheValue
This adds documentation for methods added via #1380.
2022-04-03 12:19:13 +02:00
dag
00a3f80ac4 [Mangareader] chore: remove dead bridge (#2597)
It's currently timing out.
2022-04-03 10:26:17 +02:00
dag
260fc41d72 [RTFB] chore: remove dead and unmaintained bridge (#2596) 2022-04-03 10:23:06 +02:00
quickwick
28f5066fc4 Delete broken, unneeded bridges (#2595) 2022-04-03 10:10:56 +02:00
Michael Bemmerl
aa83a990d1 [OtrkeyFinderBridge] Remove HTML in title (#2594)
* [OtrkeyFinderBridge] Provide a better example that actually returns results.

* [OtrkeyFinderBridge] Remove HTML in filename.
2022-04-03 10:09:42 +02:00
Yaman Qalieh
7dcf09a876 [GitHub] Allow custom search query (#2593) 2022-04-03 10:07:35 +02:00
somini
d123e6007e Fixup deprecations on PHP 8 (#2592)
* Fixup deprecations on PHP 8

Fix #2448

* Configure a default fallback for getInput function

* Appease phpcs

* Avoid changing getInput function

Revert "Configure a default fallback for getInput function"

This reverts commit 94004c5104.
2022-04-03 09:53:13 +02:00
quickwick
a5eb02d3c3 [MixcloudBridge] switch to using API (#2591)
* switch to using public API

* switch to different API endpoints

* fix: urlencode username

Co-authored-by: Dag <me@dvikan.no>
2022-04-03 09:51:41 +02:00
dag
7b168a29f0 [WordPressPluginUpdate] fix: broken bridge (#2572)
I think they removed the changelog html page. Or maybe it
was a redirect. Anyways, this change uses their json api
to fetch plugin data.
2022-04-03 09:38:34 +02:00
dag
bed20e9f28 feat: extract curl ua to config value (#2568)
* exclude config.default.ini.php from phpcs
2022-04-03 09:37:39 +02:00
Joseph
42788cd3ee [YahtzeeDevDiaryBridge] Remove bridge (#2580)
Website has rss feeds. https://www.escapistmagazine.com/category/yahtzees-dev-diary/feed/
2022-04-02 21:11:34 +02:00
Yaman Qalieh
fb0e7ede89 [ParksOnTheAirBridge] Fix links (#2590) 2022-04-02 12:53:10 +02:00
LogMANOriginal
f311fb8083 [BridgeAbstract] Add loadCacheValue() and saveCacheValue() (#1380)
* [BridgeAbstract] Add loadCacheValue() and saveCacheValue()

Bridges currently need to implement value caching manually, which
results in duplicate code and more complex bridges.

This commit adds two protected functions to BridgeAbstract that make
it possible for bridges to store and retrieve values from a temporary
cache by key.

Co-Authored-By: Roliga <roliga.here@gmail.com>

Co-authored-by: Roliga <roliga.here@gmail.com>
2022-04-02 08:15:28 +02:00
Jacob Zelek
40a4e7b7c2 [ParksOnTheAir] New bridge for amateur radio (#2086) 2022-04-01 20:17:00 +02:00
Yaman Qalieh
73cc791ce1 [MangaDexBridge] Add new bridge (#2583) 2022-04-01 20:15:47 +02:00
Yaman Qalieh
d4707fc119 [CraigslistBridge] Fix notice with nearby results (#2588)
If the search query includes searchNearby=1, nearby results do not have
.result-hood to indicate location, instead using .nearby.
2022-04-01 16:38:37 +02:00
Yaman Qalieh
8aa091beda [GithubIssueBridge] Fix notice with reviews (#2589)
Some timeline items, like review threads and the first comment on PRs,
have no header, so this handles the first comment and adds a generic
title if that doesn't work.
2022-04-01 16:38:07 +02:00
Foxocube
d6695c0e73 [FurAffinityUserBridge] Replate username/password with cookie login (#1641) 2022-03-31 20:28:46 +02:00
Bocki
b6798b9878 general: doc fix (#2586) 2022-03-31 20:27:36 +02:00
Bocki
6baf38f251 general: fix doc (#2585) 2022-03-31 20:17:40 +02:00
Bocki
e6ae91b4d0 [FuraffinityuserBridge] Add doc about login (#2584) 2022-03-31 20:13:19 +02:00
Joseph
e525b5b427 [OpenClassroomsBridge] Remove bridge (#2582) 2022-03-31 19:41:59 +02:00
dag
983df45264 [CourrierInternationalBridge] fix: don't break on unusual feed items #2570 (#2571)
* [CourrierInternationalBridge] fix: skip unusual feed items #2570

This skips feed items who don't have content.
The one I encountered was a horoscope.
This change makes sure the bridge dont errors out.
2022-03-31 17:01:11 +02:00
dag
8717c33646 [Glassdoor] fix: repair broken bridge (#2577) 2022-03-31 17:00:14 +02:00
Joseph
7280ed7df7 [ScribdBridge] Update example profile URL value (#2578) 2022-03-31 15:58:23 +02:00
Joseph
d6b431a34b [DownDetectorBridge] Remove bridge (#2579) 2022-03-31 15:33:33 +02:00
Teemu Ikonen
aa0aa727ad [Arte7Bridge] Support all languages (#2543) 2022-03-31 11:17:07 +02:00
dag
06ef3946cd [PokemonTV] fix: use exampleValue that returns items (#2573) 2022-03-31 09:55:55 +02:00
dag
e94d447727 [DaveRamseyBlogBridge] fix: remove dead bridge #2345 (#2574) 2022-03-31 09:52:28 +02:00
dag
25e9f69261 [ElsevierBridge] fix: broken bridge (#2575) 2022-03-31 09:49:30 +02:00
dag
3e363bbc20 [FootitoBridge] chore: remove bridge (#2576) 2022-03-31 09:46:04 +02:00
Matt DeMoss
cf2dad3ab8 Reducer (retrying after failed tests) (#2273) 2022-03-30 01:50:07 +02:00
floviolleau
d6a4f2fd5b [VieDeMerdeBridge] fix due to website changes (#2567) 2022-03-30 00:58:29 +02:00
Yaman Qalieh
d27c1a99c2 [YeggiBridge] Add model source and tags (#2566) 2022-03-30 00:57:25 +02:00
Yaman Qalieh
0d80f2d5c3 [YeggiBridge] Extend description for discovery (#2565) 2022-03-29 23:48:28 +02:00
Joseph
a485beadd7 [FlickrBridge] Add content option to By username (#1861) 2022-03-29 23:46:55 +02:00
dag
ec7d2a4afb [QPlayBridge] chore: remove dead bridge (#2564) 2022-03-29 23:33:46 +02:00
dag
427becf441 [ThingiverseBridge] chore: remove dead bridge (#2563) 2022-03-29 23:13:14 +02:00
dag
267fdb27fc chore: remove dead bridge (#2562) 2022-03-29 23:12:47 +02:00
Bocki
ac242609f4 [core] Update simplehtmldom to latest released (#2556) 2022-03-29 22:45:26 +02:00
dag
461269195b fix: ignore partial json_encode() errors in JsonFormat (#2554)
Without this change, JsonFormat simply returns
an empty array. #2283
2022-03-29 22:45:00 +02:00
dag
060b4c7d58 [AnimeUltimeBridge] fix: convert strings from iso-8859-1 to utf8 (#2552)
This fixes a bug with json_encode() being unable to produce output
because it expects utf8 strings.
2022-03-29 22:44:43 +02:00
dag
cd174c7e22 [DanbooruBridge] refactor: remove unnecessary fork of simplehtmldom (#2550) 2022-03-29 22:44:20 +02:00
quickwick
907d09f116 [GelbooruBridge] + inheriting Bridges. Switch to using Gelbooru API (#2472) 2022-03-29 22:42:09 +02:00
DRogueRonin
c6675ddeee [GroupBundNaturschutzBridge] Add bridge and adjust XPathAbstract (#2445) 2022-03-29 22:40:31 +02:00
Yaman Qalieh
98a0c2de55 [EtsyBridge] Repair bridge and flip checkbox (#2457) 2022-03-29 22:23:14 +02:00
KN4CK3R
a746987d7a [Webfail] Extract timestamp from element (#1852)
Works only for German language.
2022-03-29 20:46:55 +02:00
Michael Bemmerl
6d4155f995 [GithubTrendingBridge] Fix bridge: not all languages worked (#1615)
* [GithubTrendingBridge] Fix bridge: not all languages worked

Languages with more than one word (like "Common Lisp") were not working. Looks like GitHub changed the parameter format: white space is encoded with dashes.

This prompted me to update all languages while I was at it. This also fixed the bug that the C# & F# languages were not working, because the # has to be URL encoded, which is now done in the parameter value. The language "Ren'Py" was commented out. Probably because the single quote was not escaped? I also fixed that.

* [GithubTrendingBridge] Fix PHP notice.

A repo owner can leave the repo description empty, which means the HTML element isn't there. In this case the code produced a PHP notice. This is fixed by checking for null.

* Changed getName() to retrieve the language name directly from the PARAMETERS.

Co-authored-by: dag <me@dvikan.no>
2022-03-29 20:15:18 +02:00
Bocki
58f9e41e0b [core] Change comment behavior (#2558) 2022-03-28 23:04:38 +02:00
csisoap
e86ce338a2 [ReutersBridge] Updated 'Top News' feed, some fix (#2488) 2022-03-28 20:34:41 +02:00
Mickaël Schoentgen
626cc9119a Update CryptomeBridge.php (#2555) 2022-03-28 17:18:17 +02:00
Bocki
44af64d3aa [Docker] Debug addition fixed (#2551) 2022-03-28 01:30:24 +02:00
dag
90db8c4969 [WordpressBridge] fix: add css selector for article, #2173 (#2545)
* [Wordpress] fix: add css selector for article, #2173

* fix: resolve relative links in item content
2022-03-28 00:20:44 +02:00
Bocki
8e423277e0 [core] Update pr html generator (#2549) 2022-03-27 23:35:13 +02:00
Glandos
fe43537b45 [PhoronixBridge] support multipage and embed benchmarks (#2522) 2022-03-27 13:45:32 +02:00
Thibault Couraud
87533222c7 [FindACrewBridge] Fix bridge (#2541) 2022-03-26 19:10:48 +01:00
Stelfux
91b8e4196e [FeedExpander.php] Preserve original icon (#2145) 2022-03-26 19:09:27 +01:00
Dag
74ec1b5687 [LWNprevBridge] fix: broken bridge 2022-03-26 03:17:46 +01:00
Dag
94e6feced2 Merge branch 'master' of github.com:RSS-Bridge/rss-bridge 2022-03-26 02:31:23 +01:00
Dag
b144ab2bd7 [HeiseBrige] fix: broken bridge
This is a feed expander and heise sometimes includes
feed items which point to https://www.techstage.de
for which we dont have parsing for.
2022-03-26 02:30:21 +01:00
Yaman Qalieh
012ecf8e52 [GoogleGroupsBridge] Add new bridge for Google Groups (#2451) 2022-03-26 01:09:33 +01:00
Dag
4d4ce3f380 [Arte7Bridge] test: use legal default value for checkbox 2022-03-26 00:07:34 +01:00
Dag
2c00ecb923 [Arte7Bridge] feat: add duration filter #662
The feed item was given a "duration" key but that's not used
for anything.

refs https://github.com/RSS-Bridge/rss-bridge/issues/662
2022-03-26 00:03:38 +01:00
quickwick
02ba3adcc9 [EZTVBridge] Switch to using EZTV API (#2476) 2022-03-25 22:21:47 +01:00
quickwick
37e3d6f2f6 [CBCEditorsBlogBridge] New bridge (#2487) 2022-03-25 21:35:06 +01:00
Joseph
7f4a0fae0c [YouTubeCommunityTabBridge] Add Bridge (#1594) 2022-03-25 21:31:39 +01:00
Dag
33da1476c9 [AcrimedBridge] feat: add limit option
This change preserves the prior behavior of
fetching all items.

This particular feed always has exactly 10 items.
2022-03-25 20:59:23 +01:00
Eugene Molotov
364cc8d0b8 [docs] InstagramBridge: adapt bridge documentation to new documentation structure (#2538) 2022-03-26 00:07:18 +05:00
Bocki
4c947211d2 [core] prtester debug mode (#2537) 2022-03-25 19:56:17 +01:00
Bocki
c46ff51c51 [core] Adapt pr tester 2022-03-25 19:38:26 +01:00
Bocki
608723f95c [core] Adapt pr tester (#2536) 2022-03-25 19:31:11 +01:00
Eugene Molotov
25081eedba [InstagramBridge] Documentation for configuring this bridge (#2437) 2022-03-25 23:25:35 +05:00
Bocki
aff442de1b [core] Add pr-html-generator (#2525) 2022-03-25 16:56:38 +01:00
Yaman Qalieh
105fbe9dda [PlantUMLReleasesBridge] Bridge optimizations (#2459) 2022-03-25 16:44:42 +01:00
Dag
3187592dba fix: add a few example/default values 2022-03-25 15:33:34 +01:00
Dag
2bd3f22dd5 [IPBBridge] fix: bug in feed detection logic
The previous author forgot to also append ".xml"
when actually fetching the detected feed.
2022-03-25 14:49:57 +01:00
Eugene Molotov
35b905c074 [core] Re-enable phpunit tests (#2393) 2022-03-25 12:09:05 +01:00
Yaman Qalieh
197149d90b [CraigslistBridge] Add new bridge (#2479) 2022-03-25 10:58:54 +01:00
Bocki
f11e792f84 [maintenance] Fix tests (#2532) 2022-03-25 10:41:27 +01:00
Jacob Zelek
071412130b [SummitsOnTheAir] New bridge (#2096) 2022-03-25 09:48:55 +01:00
Dag
8b59772be3 [ElsevierBridge] fix: typo in exampleValue 2022-03-25 03:42:05 +01:00
Dag
6e0589f9a0 [EconomistBridge] fix: broken bridge
Fixes: Call to a member function find() on boo

The new-style articles had their DOM changed.
2022-03-25 03:07:35 +01:00
Dag
b57d19b29c [DesoutterBridge] fix: default value 2022-03-25 02:29:51 +01:00
Dag
dbd480e2c0 [AsahiShimbunAJWBridge] fix: use case sensitive list value
This bug happened not in web UI but when extracting the default
value in order to do automatic testing.
2022-03-25 02:04:55 +01:00
Dag
35afee6103 [ABCNews] fix: broken css selector 2022-03-25 01:41:40 +01:00
Yaman Qalieh
32a6348418 [NordbayernBridge] Fix linting issue (#2531) 2022-03-25 01:26:57 +01:00
quickwick
b5ab2ee676 Add stickers endpoint for search (#2483) 2022-03-25 01:25:53 +01:00
Yaman Qalieh
acef0ab5cc [WallpaperStopBridge] Delete bridge (#2458)
This website is no longer serving content
2022-03-25 01:05:05 +01:00
Yaman Qalieh
e0d99f2a84 [DavesTrailerPageBridge] Add timestamps to feed (#2456) 2022-03-25 01:02:16 +01:00
Niehztog
55acf661b9 add support for more media types as enclosures, handle result of /tex… (#2324) 2022-03-25 00:30:14 +01:00
eggwhalefrog
3a9e528301 [NordbayernBridge] add author & timestamp of article (#2309) 2022-03-25 00:28:06 +01:00
Florent Machen
297a6cf191 [WorldCosplayBridge] fix Cosplayer API response structure (#2307) 2022-03-25 00:27:23 +01:00
Florent Machen
9cd8e93bb9 [GQMagazineBridge] fix retrieve the content of an article at a given url (#2305) 2022-03-25 00:26:38 +01:00
Dag
943a5e3e8b [CryptomeBridge] Fix pageformat (#2239) 2022-03-25 00:11:28 +01:00
Yaman Qalieh
2ade568a84 [WikipediaBridge] Add Russian Version (#2529) 2022-03-25 00:02:38 +01:00
Dag
50bab079e1 feat: add new bridge HashnodeBridge (#2231)
https://github.com/RSS-Bridge/rss-bridge/pull/2231
2022-03-24 23:58:17 +01:00
Yaman Qalieh
bb06826680 [FolhaDeSaoPauloBridge] Fix Linting Issue (#2528) 2022-03-24 23:36:23 +01:00
Dag
534864f47b Revert "WikipediaBridge: Added russian version (#2184)"
Was buggy.

This reverts commit f7af2beb79.
2022-03-24 23:34:43 +01:00
NikNikYkt
f7af2beb79 WikipediaBridge: Added russian version (#2184) 2022-03-24 23:32:33 +01:00
Yaman Qalieh
76ade41543 [No Squash] Fix Linting (#2527) 2022-03-24 23:24:55 +01:00
somini
cb4bc57c72 [FolhaDeSaoPauloBridge]: Small improvements (#1724) 2022-03-24 23:16:02 +01:00
sysadminstory
5c69577253 [ZoneTelechargementBridge] Fix links (#2526) 2022-03-24 23:13:40 +01:00
dawidsowa
78a5136cc9 bridges: change 'tags' to 'categories' (#1942) 2022-03-24 23:04:12 +01:00
Michael Bemmerl
1f2b295bf3 [BundestagParteispendenBridge] Add bridge to get the latest donations (#1613) 2022-03-24 22:42:15 +01:00
Michael Bemmerl
e89b4287b8 [SchweinfurtBuergerinformationenBridge] Add new bridge (#1610) 2022-03-24 22:37:44 +01:00
µKöff
02ab11121b [LaTeX3ProjectNewslettersBridge] New Bridge (#1589) 2022-03-24 22:26:19 +01:00
Paroleen
3d570761e5 [SpotifyBridge] Add new bridge (#1535) 2022-03-24 21:58:53 +01:00
Joseph
1ae7cf6530 [ScribdBridge] Fix bridge (#1478) 2022-03-24 21:32:16 +01:00
Jakub Valenta
8e2b65556f [Config] Don't check PATH_CACHE for memcached (#1489) 2022-03-24 21:29:16 +01:00
Joseph
0d20e9a05c [BandcampDailyBridge] Add Bridge (#1485) 2022-03-24 21:21:57 +01:00
Antoine Turmel
6a72432f76 Proposition : Open new feeds in a new tab #1389 2022-03-24 20:37:34 +01:00
Joseph
296ff9c63a [KilledbyGoogleBridge] Add bridge (#1373) 2022-03-24 20:03:18 +01:00
Bocki
2bba89d0f5 [DonnonsBridge] Fix linting error (#2524) 2022-03-24 12:15:22 +01:00
Bocki
b1c36da14e [GiphyBridge] Add examplevalue (#2523) 2022-03-24 12:11:01 +01:00
Bocki
1a8d0babd1 [Multiple] Fix all exampleValues and required variables (#2296) 2022-03-24 11:59:34 +01:00
Dag
f766193106 [IndeedBridge] fix: broken bridge
The html was reworked.
2022-03-24 13:52:02 +05:00
Dag
b6d1c7a58f fix: php notice
Fixes:
Notice: Undefined variable: message in /home/rssbridge/public/lib/contents.php on line 39
2022-03-24 13:52:02 +05:00
Dag
f34e09e93b [GithubGist] fix: broken css selector for title 2022-03-24 13:52:02 +05:00
Dag
384790537b fix: bug in cloudflare response detection
The cloudflare server header was not recognized in
some cases such as when the server header is "server"
or when the header value is "Cloudflare".
2022-03-24 13:52:02 +05:00
Dag
7bdc53125c fix: properly verify the existence of the curl module
This fixes a bug where it didnt use curl from cli
even though it's installed.

I believe this preserves the original intention to
not require the curl module to be installed.

https://github.com/RSS-Bridge/rss-bridge/pull/979
2022-03-24 13:52:02 +05:00
Binnette
076c413d3e Fix DonnonsBridge image not showing (#2521) 2022-03-24 13:51:55 +05:00
mw80
26f0380aaa [InstagramBridge] Add detectParameters (#1476) 2022-03-23 08:09:59 +05:00
Dag
14a7516625 feat: add new bridge StandfordSIRbookreviewBridge (#1638) 2022-03-23 00:43:26 +01:00
Dag
c30c0200d5 [TheTVDBBridge] fix: remove dead bridge
https://github.com/RSS-Bridge/rss-bridge/pull/1482
2022-03-23 00:07:41 +01:00
Dag
e01d9d1700 Squashed commit of the following:
commit 81d7934ab9
Author: µKöff <muekoeff@muekev.de>
Date:   Thu May 28 14:55:00 2020 +0200

    [FunkBridge] New Bridge
2022-03-22 22:42:15 +01:00
Joseph
d41aa84b13 [BridgeCard] Use full bridge name in data-ref tag (#1560)
Updates the data-ref tag of each bridge card to use the bridge's full name (eg. Apple Music) instead of its filename (eg. AppleMusic). This fixes issues with the search not returned some bridges.
2022-03-22 22:08:26 +01:00
Dag
6211a2cd37 [TagBoardBridge] fix: remove dead bridge
https://github.com/RSS-Bridge/rss-bridge/pull/1474
2022-03-22 21:38:16 +01:00
Bocki
76f5de3d0f [Documentation] Move all wiki pages into the repo and make it pretty (#2494) 2022-03-22 21:33:29 +01:00
t0stiman
16470e8119 [CarThrottleBridge] add bridge for carthrottle.com (#2514) 2022-03-22 21:15:40 +01:00
Dag
1fd3b12512 [ExecuteProgramBridge] style: remove execution bits 2022-03-22 21:11:18 +01:00
Dag
5aa163e7d6 [GettrBridge] fix: don't use php7.2 feature
The constant "JSON_THROW_ON_ERROR" is not present in
PHP version 7.2 or earlier
2022-03-22 21:08:15 +01:00
Bocki
ec90bd905e Add debug case (#2292) 2022-03-22 20:47:40 +01:00
dag
b646afffff [ExecuteProgramBridge] Add new bridge for www.executeprogram.com (#2339) 2022-03-22 20:46:59 +01:00
dag
05c31f49ce [ETTVBridge] fix: remove bridge ETTVBridge (#2511)
They went dead in Feb 2022.

Piracy Icon ETTV Officially Shuts Down Due to a Lack of Funds.

https://torrentfreak.com/piracy-icon-ettv-officially-shuts-down-due-to-a-lack-of-funds-220206/
2022-03-22 20:43:55 +01:00
dag
0b123ef8be [GettrBridge] Add new bridge for gettr.com (#2495) (#2505) 2022-03-22 20:43:31 +01:00
dag
cd5c59b84c [ARDMediathekBridge] remove timezone modification (#2507) 2022-03-22 20:42:54 +01:00
dag
e8db2479b5 [GithubTrendingBridge] fix: the description selector was broken (#2513) 2022-03-22 20:41:59 +01:00
dag
c87f4631f2 [core] feat: improve date rendering in html formatter (#2516) 2022-03-22 20:41:13 +01:00
Bockiii
ac8e94ec56 [EconomistBridge] Fix for new layout (#2489) 2022-03-23 00:24:07 +05:00
dag
1a3419a2d4 [GiphyBridge] Lazy load images (#2512)
This change instructs browsers to gradually load images
as the user is scrolling down. This is good for performance
because browsers wont download all images right away.
2022-03-21 00:43:25 +05:00
sysadminstory
ad6549efec [ZoneTelechargementBridge] (#2503)
The website keeps activating the Cloudflare protection even on the "non"
standard URL.

To bypass the Cloudflare protection, the bridge tries to resolve a bucnh
of known subdomains, and check if the IP is in a Cloudflare IP range.

If one of the subdomains has a "non Cloudflare" IP, then we use the
CURL_RESOLVE Curl option to connect the unprotected URL using the real
IP instead of the Cloudflare IP.

I hope this will help the bridge to work without needing a fix every week !
2022-03-18 14:01:46 +05:00
dag
3638b5553a phpcs: allow short array syntax (#2506)
Short array syntax was added to PHP in PHP 5.4 (2012) and replaces array() with []
2022-03-18 13:58:47 +05:00
User123698745
a7e70926f9 [Docker file] Fix wrong version string in docker images (#2497)
do not fully ignore git directory when building docker images
".git/HEAD" and ".git/refs/heads/*" are required by "getVersion()" in "lib/Configuration.php" to build the version string
2022-03-14 05:48:40 +05:00
Paul Staroch
18504f2356 [lib/contents.php] Use variable name 'retVal' instead of 'retval' as variable names are case-sensitive (#2498) 2022-03-14 05:46:30 +05:00
Eugene Molotov
05273a9278 [core] Make getContents exceptions to be handled correctly and correct exception message (#2447)
This commit fixes following issues:
1. 'Unexpected response' error message was returned, even if upstream did not return anything
2. Inability to handle non-20x messages with checking response body
2022-03-12 01:18:01 +05:00
Tomasz Kane
2e88955648 [CdactionBridge] Add missing channels (#2477) 2022-03-10 02:03:21 +05:00
Mynacol
cbef3b3360 [HeiseBridge] Properly extract authors (#2466) 2022-03-05 23:51:03 +05:00
Jonathan Kay
9564e9291f [ComicsKingdomBridge] Grab the last meta og:url content instead of first (#2484)
There are now two og:url values on the page, the first no longer contains the necessary date URL for the bridge to work.  Finding the last og:url on the page restores the bridge to working order.
2022-03-05 23:47:04 +05:00
sysadminstory
ad1ef3425a [ZoneTelechargementBridge] Fix protected links URL (#2481)
Links URL have been changed: the rewriteProtectedLink function is now updated !
2022-03-03 10:57:12 +05:00
csisoap
3bd4b0d6ab [ReutersBridge] Fix unexpected behaviour with article (#2478)
Sometimes, there are some articles that redirected to another site,
cause the bridge to fail.

Also about disable UID, Reuters frequently updated their article and my
feed reader don't update, I think it's maybe its UID.
2022-03-02 09:50:02 +05:00
Yaman Qalieh
6585ebc89b [ActionFactory] Prevent leaking working directory (#2480) 2022-03-01 14:14:53 +05:00
Corentin Garcia
d94bb08259 [RainbowSixSiegeBridge] Fix bridge (#2475) 2022-02-27 23:33:46 +05:00
Mynacol
2811bdc054 [HeiseBridge] Consistently use seite=all parameter (#2465)
This also filters out the parameter wt_mc=rss.red.ho.ho.atom.beitrag.beitrag from the item uri.
2022-02-24 23:41:42 +05:00
Joseph
0cf9da927e [CodebergBridge] Fix bridge (#2464) 2022-02-24 00:31:08 +05:00
Eugene Molotov
73a5dd928a [TwitterBridge] Don't decode HTML entities for feed content (#2470) 2022-02-24 00:28:29 +05:00
Yaman Qalieh
680fa29668 [ContainerLinuxReleasesBridge] Delete bridge (#2455)
CoreOS has been discontinued and coreos.com has been taken down (it redirects to redhat.com)
2022-02-19 17:43:04 +05:00
Loïc Fürhoff
765af484bc [RtsBridge] Add new bridge for Radio Télévision Suisse (#2442) 2022-02-17 08:15:19 +05:00
Jonathan Kay
7252252e3c [ComicsKingdomBridge] Fixes to accomodate new layout and site changes (#2444) 2022-02-13 12:27:41 +05:00
sysadminstory
3c18784576 [ZoneTelechargementBridge] Follow site changes (#2426) 2022-02-12 12:59:54 +05:00
Bockiii
3cde07db10 [Dockerfile] Rebase on php:7-apache-buster (#2446) 2022-02-12 09:27:01 +05:00
Yaman Qalieh
8723647513 [GooglePlayStoreBridge] Add bridge (#2110) 2022-02-12 09:17:12 +05:00
Eugene Molotov
f54c996e0f [CI] Add check, if php files are marked as non-executable (#2439) 2022-01-30 14:20:47 +05:00
Tomasz Kane
09fac3aa35 [CdactionBridge] Add new bridge (#2431) 2022-01-30 13:52:00 +05:00
ORelio
c1c998dd13 [GBAtempBridge] Fix content extraction (#2314)
Bridge was broken since GBAtemp's Xenforo 2 upgrade on 2021-09-23
2022-01-29 10:29:01 +05:00
Eugene Molotov
fb19142a54 [InstagramBridge] Add options to reduce 429 errors
First option is session_id of existing Instagram account.
Second option is customizing cache timeout for InstagramBridge.

Those options can be combined.
2022-01-26 00:35:15 +05:00
Eugene Molotov
9be00ff84e [core] Load bridge configuration immediately after creating bridge object
Primary reason is allowing to load configuration
params, when executing getCacheTimeout
2022-01-26 00:32:37 +05:00
Mitsu
918041cc28 [FDroid] minor syntax fix for phpcs 2022-01-24 12:41:33 +01:00
Mitsu
e9f871ce68 [FDroid] cache up, add timestamp extraction
- increase caching from 2 to 4 hours
- using cURL, extract Last-Modified header of app icons and use as item timestamp
Test warning: F-Droid response time is quite slow even on static assets, the additional requests might impact bridge performance further
2022-01-24 12:36:49 +01:00
Sandro
018fd1c8f2 [GithubPullRequestBridge] Sort by newest PRs instead of latest updated (#2064) 2022-01-24 10:58:54 +05:00
Eugene Molotov
30553d8665 contrib: lint fetch_contributors.php 2022-01-20 10:22:10 +05:00
Eugene Molotov
5df8bf956a Bump version to dev.2022-01-20 2022-01-20 10:17:59 +05:00
Eugene Molotov
a49767e71b [README] Update list of contributors 2022-01-20 10:17:06 +05:00
Eugene Molotov
0584fdddde README: add buttons to irc and matrix 2022-01-20 10:12:08 +05:00
Eugene Molotov
0fdd281fb2 README: take out debian and guix buttons, since they are not officially maintained 2022-01-20 09:45:53 +05:00
Eugene Molotov
0e17282f60 contrib: add somehow working emacs script to help prepare new release 2022-01-20 09:43:01 +05:00
Eugene Molotov
0f1ec4a879 contrib: save template for creating new releases 2022-01-20 09:43:01 +05:00
Eugene Molotov
e9d3d7ba67 [contrib] Place script for fetching contributors from GitHub's gist to contrib directory
No need to depend on gist.github.com
2022-01-20 09:43:01 +05:00
岳东辰
3cef35a432 [ABCNewsBridge] New bridge (#2255) 2022-01-17 10:35:01 +05:00
Eugene Molotov
2f10d2345a [TwitterBridge] Meet the new maintainer: arnd-s 2022-01-16 23:42:28 +05:00
arnd-s
02a8ae4c62 [TwitterBridge] Use Twitter API V1.1 guest/activate for requesting new guest tokens (#2414)
Instead of searching inside base html page for the guest token, this patch instead uses the Twitter API V1.1 to acquire guest tokens
2022-01-16 07:59:02 +05:00
Štěpán Škorpil
084a1bcf19 [CeskaTelevizeBridge] Follow website changes (#2420) 2022-01-16 07:54:20 +05:00
Dag
418f951dd1 [ThePirateBayBridge] Repair broken bridge
This is more like a refactor because they dont serve data in plain
html anymore. Instead, the data is available from a json api
at apibay.org

Could possibly expand this bridge because their api has more to give.

I learned about this api by grokking https://thepiratebay.org/static/main.js
and by looking at browser ajax requests.

For some unknown reason they host some static assets at
https://torrindex.net/ which is used by the bridge to render
magnet image and user status image.

Signed-off-by: Dag <me@dvikan.no>
2022-01-15 11:18:57 +05:00
Bockiii
9dcce0ba1d [EconomistBridge] Fix if no article image present (#2328) 2022-01-15 10:29:18 +05:00
Joseph
607d9297ff [DuckDuckGoBridge] Fix bridge (#2335) 2022-01-15 10:24:13 +05:00
Joseph
c65feffb61 [FirefoxAddonsBridge] Fix add-on download links (#2338) 2022-01-15 10:16:43 +05:00
MarKoeh
f259fa7f9f [ARDMediathekBridge] Switch to JSON-API (#2380)
* Switch ARDMediathekBridge to JSON-API

The html screen scraping approach of ARDMediathekBridge did not work reliably. I could not find one show for which the item list was not empty using the html screen scraping approach.

The proposed change uses the JSON-API of the WebApp. Although there is still room for improvement (feed title, better understanding of the API, more accurate mimic of the webapp's behavior, de-pagination …), it does work with this change.

Indicate that now full URLs as well as just the ID are accepted.
2022-01-10 11:47:49 +01:00
sysadminstory
368a198321 [PepperBridge] fix and discussion (#2383)
* [PepperBridge] Fix some Notice for expired items

Some expired iteams are not identical to normal items for the title and
URI, so they got a special handling to remove PHP Notice.

The "most commented" sorting option was removed and show now some forum.
So it was removed.

With DealabsBridge, MydealsBridge and HotUKDealsBridge, you can monitor
a discussion for new comments, excluding or not post without URL
2022-01-10 11:46:26 +01:00
Albirew
590fdd9f9b [HentaiHavenBridge] domain and content retrieval update (#2402) 2022-01-10 11:44:41 +01:00
jNullj
799c93a3c6 [ExplosmBridge] Rewrite to work without feedburner (#2417)
* [ExplosmBridge] Rewrite to work without feedburner

re-wrote the bridge to scrap from the new explosm site as the old method of using feedburner is not working anymore, feedburner is stuck on dec/22 when the explosm site changed.
2022-01-10 11:44:18 +01:00
Quentin de Longraye
2f957b6870 [CI] Tag docker images with the commit sha (#2418)
We do not release stable tags often. To avoid using `latest`, for instance in Kubernetes manifests, this change allows to reference a repository commit. This way it is easy to lock image to a specific commit, and try and rollback if a change to a newer commit brings regressions.
2022-01-09 14:09:28 +05:00
arnd-s
12ff697ab0 [TwitterBridge] Continue using existing guesttoken from cache, when requesting a new one failed (#2396) 2022-01-06 10:59:37 +05:00
somini
8530aa54f2 [ComboiosDePortugalBridge] Temporarily ignore certificate checks (#2403) 2022-01-05 04:44:59 +05:00
Eugene Molotov
d0ef8aa71d [CI] Add support for php 8.0 and 8.1 (#2405) 2022-01-05 04:40:30 +05:00
Eugene Molotov
59e77a9e51 [README] Clarify problems with InstagramBridge and FacebookBridge (#2401) 2022-01-03 11:15:45 +05:00
Eugene Molotov
37cb4091d4 bridges: remove redundant "or returnServerError" after getContents/getSimpleHTMLDom/getSimpleHTMLDomCached (#2398)
When fetching website contents, exceptions already raise on fetching error
2022-01-02 14:36:09 +05:00
Christian Schabesberger
fc51c6753d [NordbayernBridge] Fix banner images (#2384) 2022-01-02 03:51:59 +05:00
sysadminstory
71cd15c35d [AutoJMBridge] Rework of the script to handle the new website (#2390)
The brand - model page does not have filters now, so the actual feed
using this bridge are broken.

Website has changed. The new website offers a new search function with
many more filter. So I switched to this new search page for this bridge.
2022-01-02 02:15:13 +05:00
Eugene Molotov
df408fb8bc [CI] Temporarly disable phpunit7 until it starts working again (#2392) 2021-12-26 09:25:36 +05:00
Shikiryu
e545f43a67 [KhinsiderBridge] Add bridge (#2302) 2021-12-20 12:36:44 +05:00
dag
ec55e99934 [HackerNewsUserThreadsBridge] Repair broken bridge (#2344) 2021-12-19 14:18:57 +05:00
dag
67e33186ce [GiphyBridge] Repair broken bridge (#2347) 2021-12-18 15:26:51 +05:00
sysadminstory
a0bbbd6978 [RadioMelodieBridge] Fix to use the new website layout (#2330) 2021-12-18 15:19:58 +05:00
Joseph
2ee091665a [ASRockNewsBridge] Fix bridge (#2373) 2021-12-18 15:17:16 +05:00
triatic
35930ee4e4 [TwitterBridge] Increase guest token expiry time (#2374) 2021-12-18 14:54:18 +05:00
Aaron F
e1290aa42c [CVEDetailsBridge] Add bridge (#2332)
CVE Details is a collection of CVEs, taken from the National Vulnerability
Database (NVD) and other sources like the Exploit DB and Metasploit. The
website categorizes it by vendor and product and attach the CWE category.
There is an Atom feed available, but only logged in users can use it,
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
a description of the vulnerability.
2021-12-18 09:44:05 +05:00
dag
814711e3af [ScmbBridge] Remove "read more" text only if it exists (#2368) 2021-12-16 10:06:52 +05:00
Eugene Molotov
28db707587 [OpenClassroomsBridge] sebsauvage does not maintain this bridge
Reference: https://github.com/RSS-Bridge/rss-bridge/issues/2333#issuecomment-989611085
2021-12-09 15:26:31 +05:00
dag
b48739d0ba [DerpibooruBridge] Fix parsing of title (#2346)
The previous value was an int and was not accepted
by rss-bridge as a title. This change uses the image
name instead which fixes the problem and is a better
title than the image id.
2021-12-08 17:17:28 +01:00
csisoap
b9d92150e1 [ReutersBridge] Migrate to new API (#2348)
* [ReutersBridge] Migrate to new API

- Add new API, feeds.
- Old feed name are perserved for backward compatibility.
- Remove 'Special Report' feed.
- Some feed continue to use old Wire API due to not available in new one.
- Add some new type of content, replace iframe with blockquote for twitter.
2021-12-08 17:16:40 +01:00
Bockiii
b395fe2641 [core] Implemented feature to read config from environment variables (#2100) 2021-12-03 11:15:08 +05:00
Eugene Molotov
4bc534c80f [FacebookBridge] teromene and logmanoriginal do not maintain this bridge defacto 2021-12-03 05:31:46 +05:00
Eugene Molotov
071fdef599 [core] Drop php 5.6 and php 7.0 support (#2224) 2021-12-03 04:12:16 +05:00
erik
ae6a3227b0 [FlashbackBridge] Add new bridge (#2343) 2021-12-03 03:57:21 +05:00
benzel
f469489b56 [AppleAppStoreBridge] Add Germany (#2350) 2021-12-03 03:56:06 +05:00
dag
490f556783 [TheCodingLoveBridge] Remove redundant bridge (#2342)
This is now a wordpress blog and their official feed is at https://thecodinglove.com/feed
2021-11-29 11:26:13 +05:00
somini
192f0278d2 [PanacheDigitalGamesBridge] Add bridge (#2321) 2021-11-06 23:58:30 +05:00
sysadminstory
2b634002f2 [DealabsBridge - HotUKDealsBridge - MydealsBridge] Set the Feed URL according to the parameters (#2320) 2021-11-06 23:43:44 +05:00
Christian Schabesberger
42379071e9 [NordbayernBridge] Fix banner URL (#2326)
* make banner images show for nordbayern again
* make author portrait not apear as article banner for nordbayern
2021-11-06 23:28:12 +05:00
Joseph
fd54042ef3 [BandcampBridge] Add support for labels (#2286) 2021-10-30 01:43:20 +05:00
Martin Leyrer
3764348b76 [core] Accept additional "successful" 2xx status codes (#2310) 2021-10-30 01:38:25 +05:00
dotter-ak
0ba0e2de4e [UrlebirdBridge] Fix for non ASCII characters in post URI (#2312) 2021-10-30 01:36:09 +05:00
Florent Machen
4187d8f4cf [GitHubGistBridge] fix use the css selector "contains" to find a class in the middle of the utility classes (#2306) 2021-10-30 01:32:31 +05:00
ORelio
1c6532a9d0 [NextgovBridge] Update categories, fix missing element (#2316) 2021-10-30 01:28:02 +05:00
ORelio
547829f971 [FuturaSciences] Improve content extraction (#2317)
- Fix tracking removal in URL
- Fix images broken due to new lazy loading mechanism
- Remove headline, articles do not have it anymore
- Improve article cleanup
2021-10-30 01:24:19 +05:00
ORelio
970bdd45f9 [DarkReadingBridge] Fix content extraction (#2315)
Also:
- Add article limit (main feed was broken due to too many articles)
- Add support for article thumbnail
2021-10-30 01:21:07 +05:00
Eugene Molotov
b86ed70376 [core] Backported str_starts_with, str_ends_with and str_contains from php 8 (#2318) 2021-10-30 01:06:04 +05:00
Eugene Molotov
9254d14f50 [VkBridge] Multiple fixes:
- Correct video title
- Do not add repost of deleted post to feed
2021-10-25 10:16:24 +05:00
Matt DeMoss
8f98e07979 [PcGamerBridge] Use meta tags to generate feed contents (#2271) 2021-10-19 11:53:26 +05:00
Bockiii
8d0fc54e4d [FSecureBlogBridge] Limit number of returned items (#2300) 2021-10-19 08:23:38 +05:00
csisoap
bdf15c3ce0 [UnogsBridge] Add fallback if not found any high-res image (#2301) 2021-10-19 08:22:17 +05:00
sal0max
927b08ed00 [LegoIdeasBridge] Add bridge (#2284)
* [LegoIdeasBridge] Add bridge
2021-10-14 22:52:42 +02:00
Niehztog
87b3aaa550 [XPathAbstract] Fix encoding of feed output (#2297) 2021-10-14 14:18:00 +05:00
Yaman Qalieh
c445ba6ebb [MozillaBugTrackerBridge] Fix incorrect newlines in feed title (#2298) 2021-10-14 14:13:18 +05:00
Bockiii
793c55f43d [Docker] Allow to define port via HTTP_PORT (#2285) 2021-10-09 22:02:38 +05:00
Bockiii
11be390e65 [MozillaSecurityBridge] Limit items to 20 (#2287) 2021-10-09 16:03:12 +05:00
somini
ba5baaf7c9 [JornalDeNoticiasBridge] Add bridge (#2293) 2021-10-07 16:15:21 +05:00
sysadminstory
f0ddd686e3 [ExtremeDownloadBridge] Update URL (#2290) 2021-10-05 12:52:29 +05:00
sysadminstory
f6b9864bdd [ZoneTelechargementBridge] Update website URL (#2289) 2021-10-05 12:51:26 +05:00
KamaleiZestri
64b7c54bc8 [BakaUpdatesMangaReleasesBridge] Added option to display releases based on a user list (#2276)
* [BakaUpdatesMangaReleasesBridge] Added option to display releases based on a user list
2021-10-03 22:35:31 +02:00
KamaleiZestri
d5a010adcd [PillowfortBridge] Added new bridge (#2275)
* [PillowfortBridge] Added new bridge
2021-10-03 21:59:33 +02:00
Bockiii
2a609b39bd [LinkedInBridge] Remove bridge (#2269) 2021-10-03 21:00:24 +05:00
Bockiii
dacc586dca New docker build mechanism (#2268)
* New docker build mechanism
2021-10-03 16:06:30 +02:00
csisoap
8bcf4ebfbf [NationalGeographicBridge] Rewrite bridge (#2177)
- All the option will be preserved.
- Add timestamp, author's name included with full article.
2021-10-01 18:39:36 +05:00
csisoap
cb111a3ebd [UnogsBridge] Better feed title for Country context (#2279) 2021-09-28 14:20:04 +05:00
Timendum
42e40e2823 [PicukiBridge] Fix image URLs in content (#2282)
URLs in Picuki are already absolute
2021-09-28 14:15:41 +05:00
Bockiii
1ddce120ae [EconomistBridge] Full rework (#2272) 2021-09-26 16:25:19 +05:00
Joseph
927cb17dbf [SoundcloudBridge] Add support for albums, reposts & likes (#2236) 2021-09-20 17:53:41 +05:00
D5k H3h
ccb2e64fd0 [WallpaperflareBridge] Add bridge (#2179) 2021-09-11 13:27:17 +05:00
csisoap
a26408594b [YoutubeBridge] New features, fixes and refactors (#2208)
New features:
- Add support for custom channel name.

Fixes:
- In playlist mode last uploaded videos were not in feed
- Search mode returned empty feed

And a lot of refactoring
2021-09-11 13:20:14 +05:00
Bockiii
324932642d [PokemonTVBridge] Add bridge (#2219) 2021-09-05 12:11:36 +05:00
Bockiii
455b5e09a1 [Dockerfile] Add custom config location (#2098) 2021-09-05 08:10:54 +05:00
Bockiii
bcc15228d8 [RedditBridge] Return back NSFW posts to feed (#2257)
As it was working before applying https://github.com/RSS-Bridge/rss-bridge/pull/2229
2021-09-04 18:00:02 +05:00
Eugene Molotov
68d9e2ff24 [NineGagBridge] Remove whitespace on wtf section
Don't know why github actions did not run on https://github.com/RSS-Bridge/rss-bridge/pull/2094
Certainly wtf
2021-08-25 18:16:00 +05:00
Bockiii
a5d33615f5 [RedditBridge] Add keyword search function (#2229) 2021-08-25 18:09:36 +05:00
Christian Schabesberger
8f634eb4a1 [NordBayernBridge] Fix election articles (#2253) 2021-08-25 18:08:23 +05:00
Bockiii
677e4974d1 [NineGagBridge] Updated sections and added video 2021-08-25 18:06:10 +05:00
Joseph
10c5259493 [OpenlyBridge] Add bridge (#2129) 2021-08-15 23:42:28 +05:00
Joseph
cb3c055df9 [InternetArchiveBridge] Add detectParameters (#2142) 2021-08-15 23:36:38 +05:00
Christian Schabesberger
036a3ad245 [NordbayernBridge] Add city of Erlangen (#2248) 2021-08-15 18:57:40 +05:00
Joseph
69ce8106ce [BingSearchBridge] Remove bridge (#2242)
Microsoft has removed the bing search discover functionality.

https://www.bing.com/discover/ pages now redirect to https://www.bing.com/images/trending
2021-08-13 08:54:37 +05:00
ORelio
8a30480a45 [Releases3DSBridge] Remove requests to IGN (#2246)
This part of the bridge was meant to find game info on IGN but rarely found useful results, and is harder to maintain than the rest of this bridge due to changes at IGN.
2021-08-13 08:52:57 +05:00
Joseph
f36832b66e [CodebergBridge] Add bridge (#1951) 2021-08-13 08:51:50 +05:00
t0stiman
f3f934ed8b [HardwareInfoBridge] Add bridge (#2232) 2021-08-10 23:00:32 +05:00
Joseph
7c46c64242 [FierPandaBridge] Remove bridge (#2238) 2021-08-10 22:55:24 +05:00
ORelio
bf1773ed8b [GBAtemp] Fix news extraction (#2241) 2021-08-10 22:49:10 +05:00
Joseph
4529e3699a [BridgeImplementationTest] Allow multiple contexts to have an empty parameters array (#1954) 2021-08-10 22:37:41 +05:00
sysadminstory
f5f0b77805 [RadioMelodieBridge] Replace JS Audio Player (#2233)
The Javascript Audio Player is replaced by the plain <audio> HTML tag
2021-08-04 09:04:45 +05:00
t0stiman
3637777070 [style] add dark mode (#2029) 2021-08-01 16:31:58 +05:00
Tobias Alexander Franke
c673917aca [BinanceBridge] Fix blog posts (#2226) 2021-07-29 21:55:36 +05:00
Eugene Molotov
877707f7b2 [contrib] Add directory for unorganized bin of various useful things contributed by the community around RSS-Bridge
Idea came after reading https://drewdevault.com/2020/06/06/Add-a-contrib-directory.html
2021-07-28 00:51:34 +05:00
sysadminstory
2689f5f7fa [DealabsBridge - HotUKDealsBridge - MydealsBridge] Update groups (#2083)
The bridges has been updated with the newest "groups" available on every website
2021-07-28 00:46:01 +05:00
Bockiii
bf1cb8fadc [IndiegogoBridge] New bridge (#2135) 2021-07-28 00:41:56 +05:00
Eugene Molotov
716f5ddc0e [PikabuBridge] Do not strip strikethrough tags in body content 2021-07-24 00:39:00 +05:00
Eugene Molotov
0ee549f468 [PikabuBridge] Remove whitespace from fake news marker 2021-07-24 00:39:00 +05:00
Eugene Molotov
4a1e26fd07 [WordPressBridge] aledeg is not maintainer of this bridge 2021-07-24 00:37:57 +05:00
sal0max
e14f647075 [SpottschauBridge] New bridge (#2193) 2021-07-20 13:16:43 +05:00
Marcus
34489431b4 [PicukiBridge] New bridge. Alternative to InstagramBridge (#2183) 2021-07-20 13:06:56 +05:00
dotter-ak
126cf1a7fa [Drive2ruBridge] Fixed incorrect titles and URLs in logbooks (#2215) 2021-07-20 13:03:17 +05:00
岳东辰
3050f0ae70 [ARDMediathekBridge] New bridge (#2158) 2021-07-20 12:58:50 +05:00
csisoap
cabf7a748a [ReutersBridge] Change timestamp, add new feed, add alt text to image (#2150) 2021-07-20 12:54:07 +05:00
dotter-ak
84450e7e8d [Drive2ruBridge] Bugfix (#2211) 2021-07-13 10:20:46 +05:00
Joseph
971cd9ba39 [FirefoxAddonsBridge] Remove duplicate code (#2209) 2021-07-12 22:56:51 +05:00
Nemo
9fa782105d [AmazonPriceTrackerBridge] Fixes for subscription items (#2205) 2021-07-12 22:49:29 +05:00
dotter-ak
cb7f5b057f [Drive2ruBridge] Add news, personal blogs and featured topics (#2156) 2021-07-09 00:00:16 +05:00
csisoap
a8d1acfdad [UnogsBridge] New bridge (#2198) 2021-07-07 15:25:13 +05:00
sal0max
b5d9742a21 [NikonDownloadCenterBridge] Add bridge (#2195) 2021-07-06 18:00:19 +05:00
sal0max
7dd1a7dccc [AmazonPriceTrackerBridge] Fix bridge (#2194) 2021-07-06 01:26:08 +05:00
csisoap
1f6ad000ce [RedditBridge] Add option to choose for New, Hot and Top submissions (#2189) 2021-07-02 00:41:56 +05:00
Christian Schabesberger
398e175fe0 [NordbayernBridge] Follow site updates (#2169) 2021-07-01 07:21:58 +05:00
Corentin Garcia
0de2db853f [NYTBridge] Fix article parsing (#2106)
Co-authored-by: podiki <podiki@users.noreply.github.com>
2021-06-30 15:14:25 +05:00
sysadminstory
9399ebb2c6 [RadioMelodieBridge] Add timestamp support and content fix (#2105) 2021-06-26 01:12:33 +05:00
dotter-ak
606972dd3c [UrlebirdBridge] Add bridge (#2163) 2021-06-26 00:55:13 +05:00
Bockiii
ecaae735d9 [core] Support for bridge maintainers' donation URLs (#2102) 2021-06-26 00:45:25 +05:00
岳东辰
5598fef3cf [WikipediaBridge] Update elements (#2167) 2021-06-20 15:23:29 +05:00
triatic
9c99a1a9c1 [FacebookBridge] Increase cache timeout (#2149)
Facebook aggressively throttles queries now
2021-06-12 23:15:56 +05:00
ORelio
75cc52a62c [FilterBridge] Various improvements (#2148)
- Add option for case-insensitive regex
- Allow matching item content or author in addition to item title
- Optionally attempt to convert encoding when applying matches
2021-06-07 23:11:12 +05:00
Eugene Molotov
973e49d93e [TelegramBridge] Add test cases for detectParameters 2021-06-05 09:34:16 +05:00
Eugene Molotov
c580219627 [BridgeImplementationTest] Implement feature for testing detectParameters method 2021-06-05 09:34:16 +05:00
Joseph
a18af3952d [KATBridge] Remove bridge (#2141) 2021-06-03 00:16:12 +05:00
Joseph
15907e2bbd [StoriesIGBridge] Remove bridge (#2139)
Website has been unavailable since at least October 2020

https://web.archive.org/web/20201016100555/https://storiesig.com/
2021-06-01 10:20:11 +05:00
Joseph
459adf4790 [ExplosmBridge] Fix website URI (#2140) 2021-06-01 10:18:14 +05:00
AxorPL
d38bc18232 [Formula1Bridge] Minor fixes (#2128)
- removed useless returnServerError according to your recommendation
- made use of the caption property if present
- fixed link URL
2021-05-30 23:33:15 +05:00
Jacques Heunis
eec1163fb9 [ItchioBridge] Remove reliance on in-page timestamps (#2127)
This significantly increases the possibility of missing updates (if
files are uploaded but no file names or post contents are changed) and
of showing an update when there is none (if the post text is changed
but no new files are uploaded). However with the on-page timestamps
removed I'm not sure if there is a good way to do this more accurately
so this is good as we can do at the moment.
2021-05-30 23:12:19 +05:00
Bockiii
b074abcc0d [AppleMusicBridge] Complete rebuild for new site (#2134) 2021-05-30 23:08:39 +05:00
Joseph
2ae9793f2c [CI] Update ubuntu version (16.04 => 20.04) (#2136)
Support for Ubuntu 16.04 (Xenial Xerus) runners is deprecated and will be removed on August 1, 2021
https://github.com/shivammathur/setup-php/issues/452
2021-05-30 23:04:57 +05:00
Joseph
f02d80e141 [FirefoxAddonsBridge] Fix download link extraction (#2120) 2021-05-25 00:46:07 +05:00
Yaman Qalieh
44e01a4282 [PixivBridge] Rewrite Bridge (#2111)
Also added options:
- Search for Illustrations, Manga or Novels
- Custom Post Limit
- Choose between thumbnails and full-sized image
2021-05-25 00:42:39 +05:00
Eugene Molotov
63d257d9d0 [PikabuBridge] Cut "script" element from post body (#2125)
Also correct description and remove useless returnServerError call
2021-05-24 00:50:24 +05:00
dotter-ak
37cd071453 [Drive2ruBridge] Add bridge (#2116) 2021-05-24 00:33:46 +05:00
Christian Schabesberger
17f9c44bfc [NordbayernBridge] Fix jpeg regex (#2118) 2021-05-19 13:44:17 +05:00
Christian Schabesberger
28aaf59007 [NordbayernBridge] Exclude slideshows when handling articles (#2117) 2021-05-19 13:43:10 +05:00
Corentin Garcia
e8d241e8c9 [CourrierInternationalBridge] Switch to FeedExpander (#2107) 2021-05-17 23:36:11 +05:00
t0stiman
2b793f04de [RaceDepartmentBridge] Follow site changes (#2087) 2021-05-17 23:18:51 +05:00
Eugene Molotov
655e02e3fe [VMwareSecurityBridge] Remove bridge
There is fulltext RSS feed: https://www.vmware.com/security/advisories.xml
2021-05-17 12:01:23 +05:00
Eugene Molotov
a4bd04310f [YoutubeBridge] Several fixes and switch maintainer (#2115)
- Fix incorrectly working method for querying video info
- Partially fix playlist mode.
At least it works, if playlist has more than 15 videos. But maximum 100 video items are parsed from playlist.

Reason of switching maintainer: https://github.com/RSS-Bridge/rss-bridge/issues/2113#issuecomment-841156902
2021-05-17 01:02:45 +05:00
Bockiii
e48617530d [ExplosmBridge] Add bridge (#2092) 2021-05-13 23:35:42 +05:00
Bockiii
3585575d68 [Docker] Add support for arm32/64 (#2104) 2021-05-13 22:57:10 +05:00
somini
e79a02ac2e [PresidenciaPTBridge]: Support multiple sections (#2082) 2021-05-10 00:22:10 +05:00
Bockiii
63ebf5ceec [BridgeCard] Make HTML default, remove other buttons (#2101) 2021-05-08 15:10:16 +05:00
AxorPL
378f78d6eb [Formula1Bridge] New bridge (#2085) 2021-05-06 23:16:22 +05:00
marius851000
d7ba7782f3 [DerpibooruBridge] Make it work again (#2079) 2021-04-26 23:07:42 +05:00
Eugene Molotov
b24b5ed3ee Bump version to dev.2021-04-25 2021-04-25 15:32:35 +05:00
Eugene Molotov
f06a8ae307 [README] Update list of contributors 2021-04-25 15:30:17 +05:00
dawidsowa
4f7ef212b7 [RedditBridge] Add detectParameters (#2070) 2021-04-19 22:17:36 +05:00
dawidsowa
13e9a96cf3 [RedditBridge]: Add score filter (#2045) 2021-04-19 22:14:35 +05:00
ORelio
00a24a98be [NyaaTorrents] Rewrite as Feed Expander (#2073)
NyaaTorrents allows search criteria as URL parameters in RSS feed so we just need to expand feed items
2021-04-19 21:59:51 +05:00
FiveFilters.org
76c38332ee [TwitterBridge] Improve timeline processing for username mode (#1946) 2021-04-12 23:08:38 +05:00
Joseph
65be209a47 [TwitScoopBridge] Remove less than (<) character from item title (#2034) 2021-04-12 23:01:46 +05:00
sysadminstory
146639ffc9 [ZoneTelechargement] Update unprotected URL and Feed URL (#2065)
The unproteced URL has changed again, and has been updated.

The RSS Feed URL is now a link to the Show page and not to the Homepage anymore.
2021-04-12 23:01:09 +05:00
sysadminstory
e1c19461ca [ExtremeDownloadBridge] Feed URL updated (#2066)
The Feed URL is now a link to the TV Show and not the Homepage !
2021-04-12 22:59:16 +05:00
Harvey Christian Pacleb
ff0c7a9013 [GenshinImpactBridge] Use Asia/Shanghai time zone for article dates (#2040) 2021-04-10 13:35:34 +05:00
ORelio
b754d14698 [FeedExpander] Handle Atom enclosures (#2039) 2021-04-04 15:21:15 +05:00
sysadminstory
3423b3bbe1 [ZoneTelechargement] Change URL load method (#2044) 2021-04-04 14:31:48 +05:00
Joseph
5966cc0a9c [TheFarSideBridge] Add bridge (#1484) 2021-04-03 23:31:49 +05:00
Joseph
579bfa669c [WallmineNewsBridge] Add bridge (#2035) 2021-04-02 18:01:51 +05:00
ORelio
d61871a45e [NyaaTorrents] Allow searching by username (#2033) 2021-03-31 10:59:31 +05:00
Eugene Molotov
0c8fabeb11 [PikabuBridge] Marking posts from "Как бы Новости" section, which are funny and deliberately fake (#2032) 2021-03-30 23:06:23 +05:00
Matthieu Rakotojaona
40c84b5dc3 [HackerNewsUserThreads] New bridge (#1902) 2021-03-30 21:56:17 +05:00
Yaman Qalieh
6f75d07456 [GitHubPullRequestBridge] Add new bridge inheriting GithubIssueBridge (#2001) 2021-03-29 22:15:56 +05:00
guigot
b4f809aa44 [MondeDiploBridge] Fix blog article uri (#1961) 2021-03-25 22:15:02 +05:00
Joseph
bcecd70df7 [DownDetectorBridge] Fix bridge (#1957) 2021-03-25 22:02:45 +05:00
Joseph
a6c0874b9a [TwitScoopBridge] Fix encoding of less than character (<) (#2023) 2021-03-21 11:39:01 +05:00
somini
9e6f063cfd [PresidenciaPT]: New Bridge (#2016) 2021-03-17 21:30:47 +05:00
Joseph
f904353fd2 [InternetArchiveBridge] Fix collection links (#1551) 2021-03-16 18:07:04 +05:00
Andrea Draghetti
3aafd44079 [TelegramBridge] Display the name of the attachments (#2003)
Sometimes attachments are posted in Telegram channels without any text. 

The script recognizes a new message but does not report any text, with this commit the file names will also be included in the RSS Feed.
2021-03-16 18:04:07 +05:00
Joseph
75b85f61e7 [BandcampBridge] Fix title extraction on empty band pages (#1966) 2021-03-16 18:00:26 +05:00
Joseph
07e1e8497c [DockerHubBridge] Add detectParameters() (#1996) 2021-03-15 21:54:26 +05:00
Joseph
700813e924 [FirefoxAddonsBridge] Add detectParameters() (#1997) 2021-03-15 21:27:53 +05:00
Joseph
5c011c8d90 [TwitScoopBridge] Add bridge (#2018) 2021-03-15 21:20:02 +05:00
Joseph
8d0d08a4d8 [YeggiBridge] Fix lint error (#2019) 2021-03-15 21:15:01 +05:00
Antoine Turmel
55548dcb5f [YeggiBridge] New bridge (#1910) 2021-03-13 21:57:30 +05:00
Justin Goette
0217b270a7 [README] Fix typo (#2004) 2021-03-10 22:13:41 +05:00
Joseph
2ed34f5ebe [FirefoxAddonsBridge] Set unique id for items (#2007)
Adds unique id for each item using item title.
2021-03-09 11:16:56 +05:00
csisoap
2448ed41c9 [ReutersBridge] Add new wireitem template type (#2006)
and retain the list of parameters
2021-03-09 11:15:48 +05:00
Joseph
b25674b3a0 [HtmlFormat] Use str_ireplace() when creating feed format URLs (#2008)
Fixes feed format URLs not being created with correct format value if html feed URL uses a lowercase format value.
2021-03-08 12:17:12 +05:00
Joseph
2ce1a6365b [README] Update build status badges (#1995) 2021-02-28 18:41:03 +05:00
Joseph
30aeeb2a0c [DockerHubBridge] Add support for official images (#1999) 2021-02-28 18:26:24 +05:00
Joseph
c294a652a3 [TelegramBridge] Add detectParameters() (#1998) 2021-02-28 18:20:44 +05:00
Park0
a5f2175531 [SymfonyCasts] Added new bridge (#2000) 2021-02-28 18:17:54 +05:00
dawidsowa
569276f4ef [RedditBridge]: Add user option (#1943) 2021-02-23 12:08:43 +05:00
Joseph
687eb728d4 [DockerHubBridge] Fix bridge name (#1994) 2021-02-22 15:03:04 +00:00
Monocularity
0521ba5873 [NordbayernBridge] Fixed typo of region "Hilpoltstein" (#1962) 2021-02-21 16:43:23 +00:00
Joseph
3d642971c0 [FirefoxAddonsBridge] Add bridge (#1952) 2021-02-21 17:30:01 +05:00
Shikiryu
8f086169cc [TheYeteeBridge] Fix HTML parsing (#1986) 2021-02-21 15:19:20 +05:00
Joseph
ce34e7eb89 [DockerhubBridge] Add bridge (#1990) 2021-02-21 15:17:07 +05:00
sysadminstory
ee5d190391 [RadioMelodieBridge] Fix header image (#1985)
Header Image is now using a direct link to the image, but without the
website base URL : the bridge now sends the right URL.
2021-02-17 10:07:35 +05:00
Eugene Molotov
98352845a1 [VkBridge] Remove non ascii chars from post date to correctly parse it (#1977) 2021-02-10 18:11:48 +05:00
Joseph
9e58735b01 [FormatFactory] Ignore case in format values (#1967) 2021-02-09 18:13:03 +05:00
Thomas
771b851b52 [contents.php] Fix logical error in getSimpleHTMLDOMCached function (#1974)
Previously content was only loaded from cache when debug mode was enabled (the opposite of the expected behavior)
2021-02-09 17:40:16 +05:00
Joseph
809343ed06 [SoundcloudBridge] Fix client ID extraction (#1973) 2021-02-09 17:33:14 +05:00
Lyra
e9424f6a08 Merge branch 'master' of github.com:RSS-Bridge/rss-bridge 2021-02-07 14:44:36 +01:00
Lyra
e5846c03ba Attempt to fix LeBonCoinBridge 2021-02-07 14:44:28 +01:00
Joseph
6224fbb6a2 [BridgeCard] Display configuration options (if enabled) when bridge has no parameters (#1968)
Updates displayBridgeCard() in BridgeCard to allow configuration options noproxy and cache_timeout to be displayed, if enabled, when a bridge has no parameters in its PARAMETERS array
2021-02-05 10:17:30 +05:00
sysadminstory
eab575dc9d [ZoneTelechargement] Update Direct Download Unprotected URL (#1963) 2021-01-30 18:02:36 +00:00
André Andersson
b56637c833 [BukowskisBridge] Add bridge (#1927) 2021-01-29 23:36:45 +05:00
Simon816
005b22701d [FSecureBlogBridge] Add bridge (#1932) 2021-01-29 23:27:35 +05:00
hollowleviathan
43b7621f45 [ReutersBridge] Add bridge (#1653) 2021-01-29 22:57:40 +05:00
Joseph
ea289a0cea [GithubIssueBridge] Fix issue id and comment id extraction (#1950) 2021-01-27 11:06:59 +05:00
Joseph
43acb555e0 [ChristianDailyReporterBridge] Remove bridge (#1948)
> The time has come for the Christian Daily Reporter to ride off into the sunset. Disrn is here now and it delivers everything CDR did, times ten.

https://christiandailyreporter.com/
2021-01-22 10:20:17 +05:00
Eugene Molotov
3b7e61fb55 [Arte7Bridge] Mitsu is no longer maintainer of the bridge
> That said, please also feel free to remove me as "maintainer" since that's simply no longer reality

https://github.com/RSS-Bridge/rss-bridge/issues/1906#issuecomment-765015048
2021-01-22 09:52:07 +05:00
Corentin Garcia
fbbd6a02c6 [DribbbleBridge] Fix pictures parsing (#1911) 2021-01-20 22:26:15 +05:00
sysadminstory
3534193032 [ZoneTelechargement] Update URL and fix typos (#1936) 2021-01-17 16:32:59 +05:00
sysadminstory
81fc8c89d4 [ExtremeDownloadBridge] Update URL (#1937) 2021-01-17 16:31:44 +05:00
Eugene Molotov
ea1de07fe5 [CI] Fix non-working phpcompatibility job (#1928) 2021-01-12 12:02:49 +05:00
Eugene Molotov
2de5ce8387 [CI] Fix phpcompatibility job running wrong scenario 2021-01-12 11:00:38 +05:00
Eugene Molotov
28f9215913 [CI] Replace Travis CI with Github Actions (#1926)
Travis stopped working again and no answers from support
2021-01-11 22:39:15 +05:00
FiveFilters.org
f927781750 [TwitterBridge] Add option to hide pinned tweet (#1908) 2021-01-10 13:50:06 +05:00
Jacques Heunis
e128ce807a [ItchioBridge] Add bridge (#1918) 2021-01-10 13:30:12 +05:00
Derrick Lee
6b870f0c3e [TwitterBridge] Fix username matching to be case insensitive with noretweet option (#1924) 2021-01-10 13:19:38 +05:00
JimDog546
5ed161943c [InstagramBridge] Remove redundant data collection for sidecar and video (#1920)
getInstagramSidecarData and getInstagramVideoData were unnecessarily calling getSinglePostData to retrieve data already present in collectData's call of getInstagramJSON. getSinglePostData sometimes doesn't retrieve data properly resulting in incomplete post information. Since the information needed is already present, pass it from collectData instead, eliminating the redundant data collection and improving reliability.
2021-01-10 13:14:58 +05:00
Corentin Garcia
1edec1aa45 [GenshinImpactBridge] Add bridge (#1912) 2020-12-30 23:30:02 +05:00
dawidsowa
3c285d50ec [RedditBridge] Rewrite to use JSON (#1781) 2020-12-23 22:42:15 +05:00
Clemens Neubauer
3aae00b56a [HDWallpapersBridge] Fix URLs (#1892) 2020-12-23 22:19:32 +05:00
sysadminstory
21798e8228 [ZoneTelechargement] Add support for Streaming links (#1858) 2020-12-23 22:13:10 +05:00
Eugene Molotov
3226a5e31e [core] Use more correct text to indicate errored response from upstream 2020-12-23 17:49:11 +05:00
Eugene Molotov
59bbc9d2e7 [VkBridge] Several improvements (#1802)
* Improved post author computation
* Show repost sources
* Handle second copy quote
* Fixed incorrect image uri's
2020-12-18 07:58:51 +05:00
Eugene Molotov
2ddd357a62 [JS] Fixed TypeError: textValue is null 2020-12-18 07:51:33 +05:00
Devon Hess
c302bca1e6 [IKWYDBridge] New bridge (#1874) 2020-12-13 16:08:25 +05:00
t0stiman
0b3082609d [RaceDepartmentBridge] added a bridge for racedepartment.com (#1681) 2020-12-13 15:50:54 +05:00
Lyra
810a2503c9 [core] Add configuration for bridges, allowing private bridges (#1343) 2020-12-12 21:05:22 +05:00
Eugene Molotov
56b2c516e4 [InstagramBridge] pauder is defacto not a maintainer of InstagramBridge 2020-12-09 10:47:48 +05:00
Eugene Molotov
fc81bed717 [BridgeAbstract] Correct getIcon method 2020-12-07 21:08:58 +04:00
Tobias Alexander Franke
56eb829a66 [EconomistBridge] Fixes for fetching new page structure (#1836) 2020-11-29 15:31:20 +05:00
Alex Kirk
7705d097e3 [SkimfeedBridge] Add parameter detection (#1877) 2020-11-29 15:28:07 +05:00
Peter Dave Hello
be9df41e07 [Dockerfile] Clean up apt cache, make image smaller (#1880) 2020-11-29 15:18:20 +05:00
Joseph
1e75f9d3d5 [ReporterreBridge + KernelBugTrackerBridge + BastaBridge] Use defaultLinkTo() (#1862) 2020-11-23 23:49:25 +05:00
Eugene Molotov
0755181555 [CeskaTelevizeBridge] Remove executable flag from bridge file 2020-11-19 16:19:46 +05:00
Joseph
e6c73a1fe3 [FlickrBridge] Add filter by media and sort by options (#1758) 2020-11-16 22:33:48 +05:00
David Pedersen
5729e069e9 [AppleMusicBridge] Use title from website (#1855) 2020-11-16 22:15:39 +05:00
David Pedersen
0b494d9c0e [AmazonPriceTrackerBridge] Add support for Swedish Amazon (#1856) 2020-11-16 22:13:23 +05:00
Eugene Molotov
c855d5089f Revert "[TelegramBridge] Prevent double encoding entities (#1182)"
This reverts commit 5e2f0fb626.
2020-11-11 23:31:24 +05:00
dawidsowa
6baf64f29e [Rule34pahealBridge] Use full size image (#1775) 2020-11-11 23:28:12 +05:00
Alex Balgavy
7d4b76be99 [SeznamZpravyBridge] New bridge (#1806) 2020-11-11 22:39:34 +05:00
493 changed files with 31157 additions and 14545 deletions

View File

@@ -1,4 +1,6 @@
.git
!.git/HEAD
!.git/refs/heads/*
.gitattributes
.github/*
.travis.yml

1
.gitattributes vendored
View File

@@ -1,5 +1,6 @@
# Auto detect text files and perform LF normalization
* text=auto
*.sh text eol=lf
# Custom for Visual Studio
*.cs diff=csharp

View File

@@ -1,49 +1,7 @@
### Pull request policy
* [Fix one issue per pull request](https://github.com/RSS-Bridge/rss-bridge/wiki/Pull-request-policy#fix-one-issue-per-pull-request)
* [Respect the coding style policy](https://github.com/RSS-Bridge/rss-bridge/wiki/Pull-request-policy#respect-the-coding-style-policy)
* [Properly name your commits](https://github.com/RSS-Bridge/rss-bridge/wiki/Pull-request-policy#properly-name-your-commits)
* When fixing a bridge (located in the `bridges` directory), write `[BridgeName] Feature` <br>(i.e. `[YoutubeBridge] Fix typo in video titles`).
* When fixing other files, use `[FileName] Feature` <br>(i.e. `[index.php] Add multilingual support`).
* When fixing a general problem that applies to multiple files, write `category: feature` <br>(i.e. `bridges: Fix various typos`).
Note that all pull-requests must pass all tests before they can be merged.
See the [Pull request policy page on the documentation](https://rss-bridge.github.io/rss-bridge/For_Developers/Pull_Request_policy.html) for more information on the pull request policy.
### Coding style
* [Whitespace](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace)
* [Add a new line at the end of a file](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace#add-a-new-line-at-the-end-of-a-file)
* [Do not add a whitespace before a semicolon](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace#add-a-new-line-at-the-end-of-a-file)
* [Do not add whitespace at start or end of a file or end of a line](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace#do-not-add-whitespace-at-start-or-end-of-a-file-or-end-of-a-line)
* [Indentation](https://github.com/RSS-Bridge/rss-bridge/wiki/Indentation)
* [Use tabs for indentation](https://github.com/RSS-Bridge/rss-bridge/wiki/Indentation#use-tabs-for-indentation)
* [Maximum line length](https://github.com/RSS-Bridge/rss-bridge/wiki/Maximum-line-length)
* [The maximum line length should not exceed 80 characters](https://github.com/RSS-Bridge/rss-bridge/wiki/Maximum-line-length#the-maximum-line-length-should-not-exceed-80-characters)
* [Strings](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings)
* [Whenever possible use single quoted strings](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings#whenever-possible-use-single-quote-strings)
* [Add spaces around the concatenation operator](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings#add-spaces-around-the-concatenation-operator)
* [Use a single string instead of concatenating](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings#use-a-single-string-instead-of-concatenating)
* [Constants](https://github.com/RSS-Bridge/rss-bridge/wiki/Constants)
* [Use UPPERCASE for constants](https://github.com/RSS-Bridge/rss-bridge/wiki/Constants#use-uppercase-for-constants)
* [Keywords](https://github.com/RSS-Bridge/rss-bridge/wiki/Keywords)
* [Use lowercase for `true`, `false` and `null`](https://github.com/RSS-Bridge/rss-bridge/wiki/Keywords#use-lowercase-for-true-false-and-null)
* [Operators](https://github.com/RSS-Bridge/rss-bridge/wiki/Operators)
* [Operators must have a space around them](https://github.com/RSS-Bridge/rss-bridge/wiki/Operators#operators-must-have-a-space-around-them)
* [Functions](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions)
* [Parameters with default values must appear last in functions](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions#parameters-with-default-values-must-appear-last-in-functions)
* [Calling functions](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions#calling-functions)
* [Do not add spaces after opening or before closing bracket](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions#do-not-add-spaces-after-opening-or-before-closing-bracket)
* [Structures](https://github.com/RSS-Bridge/rss-bridge/wiki/Structures)
* [Structures must always be formatted as multi-line blocks](https://github.com/RSS-Bridge/rss-bridge/wiki/Structures#structures-must-always-be-formatted-as-multi-line-blocks)
* [If-Statement](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement)
* [Use `elseif` instead of `else if`](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement#use-elseif-instead-of-else-if)
* [Do not write empty statements](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement#do-not-write-empty-statements)
* [Do not write unconditional if-statements](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement#do-not-write-unconditional-if-statements)
* [Classes](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes)
* [Use PascalCase for class names](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#use-pascalcase-for-class-names)
* [Do not use final statements inside final classes](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#do-not-use-final-statements-inside-final-classes)
* [Do not override methods to call their parent](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#do-not-override-methods-to-call-their-parent)
* [abstract and final declarations MUST precede the visibility declaration](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#abstract-and-final-declarations-must-precede-the-visibility-declaration)
* [static declaration MUST come after the visibility declaration](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#static-declaration-must-come-after-the-visibility-declaration)
* [Casting](https://github.com/RSS-Bridge/rss-bridge/wiki/Casting)
* [Do not add spaces when casting](https://github.com/RSS-Bridge/rss-bridge/wiki/Casting#do-not-add-spaces-when-casting)
See the [Coding style policy page on the documentation](https://rss-bridge.github.io/rss-bridge/For_Developers/Coding_style_policy.html) for more information on the coding style of the project.

View File

@@ -60,5 +60,5 @@ Please describe what you expect from the bridge. Whenever possible provide sampl
Keep in mind that opening a request does not guarantee the bridge being implemented! That depends entirely on the interest and time of others to make the bridge for you.
You can also implement your own bridge (with support of the community if needed). Find more information in the [RSS-Bridge Wiki](https://github.com/RSS-Bridge/rss-bridge/wiki/For-developers) developer section.
You can also implement your own bridge (with support of the community if needed). Find more information in the [RSS-Bridge Documentation](https://rss-bridge.github.io/rss-bridge/For_Developers/index.html) developer section.
-->

2
.github/prtester-requirements.txt vendored Normal file
View File

@@ -0,0 +1,2 @@
beautifulsoup4>=4.10.0
requests>=2.26.0

105
.github/prtester.py vendored Normal file
View File

@@ -0,0 +1,105 @@
import requests
import itertools
from bs4 import BeautifulSoup
from datetime import datetime
import os.path
# This script is specifically written to be used in automation for https://github.com/RSS-Bridge/rss-bridge
#
# This will scrape the whitelisted bridges in the current state (port 3000) and the PR state (port 3001) of
# RSS-Bridge, generate a feed for each of the bridges and save the output as html files.
# It also replaces the default static CSS link with a hardcoded link to @em92's public instance, so viewing
# the HTML file locally will actually work as designed.
def testBridges(bridges,status):
for bridge in bridges:
if bridge.get('data-ref'): # Some div entries are empty, this ignores those
bridgeid = bridge.get('id')
bridgeid = bridgeid.split('-')[1] # this extracts a readable bridge name from the bridge metadata
bridgestring = '/?action=display&bridge=' + bridgeid + '&format=Html'
forms = bridge.find_all("form")
formid = 1
for form in forms:
# a bridge can have multiple contexts, named 'forms' in html
# this code will produce a fully working formstring that should create a working feed when called
# this will create an example feed for every single context, to test them all
formstring = ''
errormessages = []
parameters = form.find_all("input")
lists = form.find_all("select")
# this for/if mess cycles through all available input parameters, checks if it required, then pulls
# the default or examplevalue and then combines it all together into the formstring
# if an example or default value is missing for a required attribute, it will throw an error
# any non-required fields are not tested!!!
for parameter in parameters:
if parameter.get('type') == 'hidden' and parameter.get('name') == 'context':
cleanvalue = parameter.get('value').replace(" ","+")
formstring = formstring + '&' + parameter.get('name') + '=' + cleanvalue
if parameter.get('type') == 'number' or parameter.get('type') == 'text':
if parameter.has_attr('required'):
if parameter.get('placeholder') == '':
if parameter.get('value') == '':
errormessages.append(parameter.get('name'))
else:
formstring = formstring + '&' + parameter.get('name') + '=' + parameter.get('value')
else:
formstring = formstring + '&' + parameter.get('name') + '=' + parameter.get('placeholder')
# same thing, just for checkboxes. If a checkbox is checked per default, it gets added to the formstring
if parameter.get('type') == 'checkbox':
if parameter.has_attr('checked'):
formstring = formstring + '&' + parameter.get('name') + '=on'
for listing in lists:
selectionvalue = ''
listname = listing.get('name')
if 'optgroup' in listing.contents[0].name:
listing = list(itertools.chain.from_iterable(listing))
for selectionentry in listing:
if 'selected' in selectionentry.attrs:
selectionvalue = selectionentry.get('value')
break
if selectionvalue == '':
selectionvalue = listing.contents[0].get('value')
formstring = formstring + '&' + listname + '=' + selectionvalue
if not errormessages:
# if all example/default values are present, form the full request string, run the request, replace the static css
# file with the url of em's public instance and then upload it to termpad.com, a pastebin-like-site.
r = requests.get(URL + bridgestring + formstring)
pagetext = r.text.replace('static/HtmlFormat.css','https://feed.eugenemolotov.ru/static/HtmlFormat.css')
pagetext = pagetext.encode("utf_8")
termpad = requests.post(url="https://termpad.com/", data=pagetext)
termpadurl = termpad.text
termpadurl = termpadurl.replace('termpad.com/','termpad.com/raw/')
termpadurl = termpadurl.replace('\n','')
with open(os.getcwd() + '/comment.txt', 'a+') as file:
file.write("\n")
file.write("| [`" + bridgeid + '-' + status + '-context' + str(formid) + "`](" + termpadurl + ") | " + date_time + " |")
else:
# if there are errors (which means that a required value has no example or default value), log out which error appeared
termpad = requests.post(url="https://termpad.com/", data=str(errormessages))
termpadurl = termpad.text
termpadurl = termpadurl.replace('termpad.com/','termpad.com/raw/')
termpadurl = termpadurl.replace('\n','')
with open(os.getcwd() + '/comment.txt', 'a+') as file:
file.write("\n")
file.write("| [`" + bridgeid + '-' + status + '-context' + str(formid) + "`](" + termpadurl + ") | " + date_time + " |")
formid += 1
gitstatus = ["current", "pr"]
now = datetime.now()
date_time = now.strftime("%Y-%m-%d, %H:%M:%S")
with open(os.getcwd() + '/comment.txt', 'w+') as file:
file.write(''' ## Pull request artifacts
| file | last change |
| ---- | ------ |''')
for status in gitstatus: # run this twice, once for the current version, once for the PR version
if status == "current":
port = "3000" # both ports are defined in the corresponding workflow .yml file
elif status == "pr":
port = "3001"
URL = "http://localhost:" + port
page = requests.get(URL) # Use python requests to grab the rss-bridge main page
soup = BeautifulSoup(page.content, "html.parser") # use bs4 to turn the page into soup
bridges = soup.find_all("section") # get a soup-formatted list of all bridges on the rss-bridge page
testBridges(bridges,status) # run the main scraping code with the list of bridges and the info if this is for the current version or the pr version

61
.github/workflows/dockerbuild.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Build Image on Commit and Release
on:
push:
branches:
- 'master'
tags:
- '20*'
env:
DOCKERHUB_SLUG: rssbridge/rss-bridge
GHCR_SLUG: ghcr.io/rss-bridge/rss-bridge
jobs:
bake:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2.3.4
-
name: Docker meta
id: docker_meta
uses: docker/metadata-action@v3.5.0
with:
images: |
${{ env.DOCKERHUB_SLUG }}
${{ env.GHCR_SLUG }}
tags: |
type=raw,value=latest
type=sha
type=ref,event=tag,enable=${{ startsWith(github.ref, 'refs/tags/20') }}
type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/20') }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.6.0
-
name: Login to DockerHub
uses: docker/login-action@v1.10.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v1.10.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/bake-action@v1.6.0
with:
files: |
./docker-bake.hcl
${{ steps.docker_meta.outputs.bake-file }}
targets: image-all
push: true

27
.github/workflows/documentation.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Documentation
on:
push:
paths:
- 'docs/**'
jobs:
documentation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false
- name: Setup PHP
uses: shivammathur/setup-php@2.17.1
with:
php-version: 8.0
- name: Install dependencies
run: composer global require daux/daux.io
- name: Generate documentation
run: daux generate
- name: Deploy same repository 🚀
uses: JamesIves/github-pages-deploy-action@v4.2.5
with:
folder: "static"
branch: gh-pages

48
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Lint
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
phpcs:
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['7.1', '7.2', '7.3', '7.4']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpcs
- run: phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p
phpcompatibility:
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['7.1', '7.4']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- run: composer global require dealerdirect/phpcodesniffer-composer-installer
- run: composer global require phpcompatibility/php-compatibility
- run: ~/.composer/vendor/bin/phpcs . --standard=phpcompatibility.xml --warning-severity=0 --extensions=php -p
executable_php_files_check:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- run: |
if find -name "*.php" -executable -type f -print -exec false {} +
then
echo 'Good, no executable php scripts found'
else
echo 'Please unmark php scripts above as non-executable'
exit 1
fi

69
.github/workflows/prhtmlgenerator.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: 'PR Testing'
on:
pull_request_target:
branches: [ master ]
jobs:
test-pr:
name: Generate HTML
runs-on: ubuntu-latest
# Needs additional permissions https://github.com/actions/first-interaction/issues/10#issuecomment-1041402989
steps:
- name: Check out self
uses: actions/checkout@v2.3.2
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Check out rss-bridge
run: |
PR=${{github.event.number}};
wget -O requirements.txt https://raw.githubusercontent.com/RSS-Bridge/rss-bridge/master/.github/prtester-requirements.txt;
wget https://raw.githubusercontent.com/RSS-Bridge/rss-bridge/master/.github/prtester.py;
wget https://patch-diff.githubusercontent.com/raw/$GITHUB_REPOSITORY/pull/$PR.patch;
touch DEBUG;
cat $PR.patch | grep " bridges/.*\.php" | sed "s= bridges/\(.*\)Bridge.php.*=\1=g" | sort | uniq > whitelist.txt
- name: Start Docker - Current
run: |
docker run -d -v $GITHUB_WORKSPACE/whitelist.txt:/app/whitelist.txt -v $GITHUB_WORKSPACE/DEBUG:/app/DEBUG -p 3000:80 ghcr.io/rss-bridge/rss-bridge:latest
- name: Start Docker - PR
run: |
docker build -t prbuild .;
docker run -d -v $GITHUB_WORKSPACE/whitelist.txt:/app/whitelist.txt -v $GITHUB_WORKSPACE/DEBUG:/app/DEBUG -p 3001:80 prbuild
- name: Setup python
uses: actions/setup-python@v2
with:
python-version: '3.7'
cache: 'pip'
- name: Install requirements
run: |
cd $GITHUB_WORKSPACE
pip install -r requirements.txt
- name: Run bridge tests
id: testrun
run: |
mkdir results;
python prtester.py;
body="$(cat comment.txt)";
body="${body//'%'/'%25'}";
body="${body//$'\n'/'%0A'}";
body="${body//$'\r'/'%0D'}";
echo "::set-output name=bodylength::${#body}"
echo "::set-output name=body::$body"
- name: Find Comment
if: ${{ steps.testrun.outputs.bodylength > 130 }}
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Pull request artifacts
- name: Create or update comment
if: ${{ steps.testrun.outputs.bodylength > 130 }}
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
${{ steps.testrun.outputs.body }}
edit-mode: replace

34
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
phpunit7:
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['7.1', '7.2', '7.3']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- run: composer global require phpunit/phpunit ^7
- run: phpunit --configuration=phpunit.xml --include-path=lib/
phpunit8:
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['7.3', '7.4', '8.0', '8.1']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- run: composer global require phpunit/phpunit ^8
- run: phpunit --configuration=phpunit.xml --include-path=lib/

2
.gitignore vendored
View File

@@ -213,6 +213,7 @@ pip-log.txt
# Unit test / coverage reports
.coverage
.phpunit.result.cache
.tox
#Translations
@@ -228,6 +229,7 @@ pip-log.txt
/whitelist.txt
DEBUG
config.ini.php
config/*
######################
## VisualStudioCode ##

View File

@@ -1,46 +0,0 @@
dist: trusty
language: php
install:
- composer global require dealerdirect/phpcodesniffer-composer-installer;
- composer global require phpcompatibility/php-compatibility;
- if [[ "$PHPUNIT" ]]; then
composer global require phpunit/phpunit ^$PHPUNIT;
fi
script:
- phpenv rehash
# Run PHP_CodeSniffer on all versions
- ~/.config/composer/vendor/bin/phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p;
# Check PHP compatibility for the lowest and highest supported version
- if [[ $TRAVIS_PHP_VERSION == "5.6" || $TRAVIS_PHP_VERSION == "7.3" ]]; then
~/.config/composer/vendor/bin/phpcs . --standard=phpcompatibility.xml --extensions=php -p;
fi
# Run unit tests on highest major version
- if [[ ${TRAVIS_PHP_VERSION:0:1} == "7" ]]; then
~/.config/composer/vendor/bin/phpunit --configuration=phpunit.xml --include-path=lib/;
fi
php:
- 7.3
env:
- PHPUNIT=6
- PHPUNIT=7
- PHPUNIT=8
matrix:
fast_finish: true
include:
- php: 5.6
env: PHPUNIT=
- php: 7.0
- php: 7.1
- php: 7.2
allow_failures:
- php: 7.3
env: PHPUNIT=7
- php: 7.3
env: PHPUNIT=8

View File

@@ -1,17 +1,24 @@
FROM php:7-apache
FROM php:7.4.29-fpm
ENV APACHE_DOCUMENT_ROOT=/app
LABEL description="RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites that don't have one."
LABEL repository="https://github.com/RSS-Bridge/rss-bridge"
LABEL website="https://github.com/RSS-Bridge/rss-bridge"
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& apt-get --yes update \
&& apt-get --yes --no-install-recommends install \
zlib1g-dev \
libmemcached-dev \
&& pecl install memcached \
&& docker-php-ext-enable memcached \
&& sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \
&& sed -ri -e 's/(MinProtocol\s*=\s*)TLSv1\.2/\1None/' /etc/ssl/openssl.cnf \
&& sed -ri -e 's/(CipherString\s*=\s*DEFAULT)@SECLEVEL=2/\1/' /etc/ssl/openssl.cnf
RUN apt-get update && \
apt-get install --yes --no-install-recommends \
nginx \
zlib1g-dev \
libzip-dev \
libmemcached-dev && \
docker-php-ext-install zip && \
pecl install memcached && \
docker-php-ext-enable memcached && \
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --chown=www-data:www-data ./ /app/
COPY ./config/nginx.conf /etc/nginx/sites-enabled/default
COPY --chown=www-data:www-data ./ /app/
EXPOSE 80
ENTRYPOINT ["/app/docker-entrypoint.sh"]

147
README.md
View File

@@ -1,6 +1,11 @@
![RSS-Bridge](static/logo_600px.png)
===
[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE) [![GitHub release](https://img.shields.io/github/release/rss-bridge/rss-bridge.svg?logo=github)](https://github.com/rss-bridge/rss-bridge/releases/latest) [![Debian Release](https://img.shields.io/badge/dynamic/json.svg?logo=debian&label=debian%20release&url=https%3A%2F%2Fsources.debian.org%2Fapi%2Fsrc%2Frss-bridge%2F&query=%24.versions%5B0%5D.version&colorB=blue)](https://tracker.debian.org/pkg/rss-bridge) [![Guix Release](https://img.shields.io/badge/guix%20release-unknown-blue.svg)](https://www.gnu.org/software/guix/packages/R/) [![Build Status](https://travis-ci.org/RSS-Bridge/rss-bridge.svg?branch=master)](https://travis-ci.org/RSS-Bridge/rss-bridge) [![Docker Build Status](https://img.shields.io/docker/build/rssbridge/rss-bridge.svg?logo=docker)](https://hub.docker.com/r/rssbridge/rss-bridge/)
[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE)
[![GitHub release](https://img.shields.io/github/release/rss-bridge/rss-bridge.svg?logo=github)](https://github.com/rss-bridge/rss-bridge/releases/latest)
[![irc.libera.chat](https://img.shields.io/badge/irc.libera.chat-%23rssbridge-blue.svg)](https://web.libera.chat/#rssbridge)
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#rssbridge:libera.chat)
[![Actions Status](https://img.shields.io/github/workflow/status/RSS-Bridge/rss-bridge/Tests/master?label=GitHub%20Actions&logo=github)](https://github.com/RSS-Bridge/rss-bridge/actions)
[![Docker Build Status](https://img.shields.io/docker/cloud/build/rssbridge/rss-bridge?logo=docker)](https://hub.docker.com/r/rssbridge/rss-bridge/)
RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites that don't have one. It can be used on webservers or as a stand-alone application in CLI mode.
@@ -10,17 +15,17 @@ Supported sites/pages (examples)
===
* `Bandcamp` : Returns last release from [bandcamp](https://bandcamp.com/) for a tag
* `Cryptome` : Returns the most recent documents from [Cryptome.org](http://cryptome.org/)
* `DansTonChat`: Most recent quotes from [danstonchat.com](http://danstonchat.com/)
* `Cryptome` : Returns the most recent documents from [Cryptome.org](https://cryptome.org/)
* `DansTonChat`: Most recent quotes from [danstonchat.com](https://danstonchat.com/)
* `DuckDuckGo`: Most recent results from [DuckDuckGo.com](https://duckduckgo.com/)
* `Facebook` : Returns the latest posts on a page or profile on [Facebook](https://facebook.com/)
* `FlickrExplore` : [Latest interesting images](http://www.flickr.com/explore) from Flickr
* `Facebook` : Returns the latest posts on a page or profile on [Facebook](https://facebook.com/) (There is an [issue](https://github.com/RSS-Bridge/rss-bridge/issues/2047) for public instances)
* `FlickrExplore` : [Latest interesting images](https://www.flickr.com/explore) from Flickr
* `GoogleSearch` : Most recent results from Google Search
* `Identi.ca` : Identica user timeline (Should be compatible with other Pump.io instances)
* `Instagram`: Most recent photos from an Instagram user
* `OpenClassrooms`: Lastest tutorials from [fr.openclassrooms.com](http://fr.openclassrooms.com/)
* `Instagram`: Most recent photos from an Instagram user (It is recommended to [configure](https://rss-bridge.github.io/rss-bridge/Bridge_Specific/Instagram.html) this bridge to work)
* `OpenClassrooms`: Lastest tutorials from [openclassrooms.com](https://openclassrooms.com/)
* `Pinterest`: Most recent photos from user or search
* `ScmbBridge`: Newest stories from [secouchermoinsbete.fr](http://secouchermoinsbete.fr/)
* `ScmbBridge`: Newest stories from [secouchermoinsbete.fr](https://secouchermoinsbete.fr/)
* `ThePirateBay` : Returns the newest indexed torrents from [The Pirate Bay](https://thepiratebay.se/) with keywords
* `Twitter` : Return keyword/hashtag search or user timeline
* `Wikipedia`: highlighted articles from [Wikipedia](https://wikipedia.org/) in English, German, French or Esperanto
@@ -39,25 +44,23 @@ RSS-Bridge is capable of producing several output formats:
* `Mrss` : MRSS feed, for use in feed readers
* `Plaintext` : Raw text, for consumption by other applications
You can extend RSS-Bridge with your own format, using the [Format API](https://github.com/RSS-Bridge/rss-bridge/wiki/Format-API)!
You can extend RSS-Bridge with your own format, using the [Format API](https://rss-bridge.github.io/rss-bridge/Format_API/index.html)!
Screenshot
===
Welcome screen:
![Screenshot](https://github.com/RSS-Bridge/rss-bridge/wiki/images/screenshot_rss-bridge_welcome.png)
***
![Screenshot](/static/screenshot_rss-bridge_welcome.png?raw=true)
RSS-Bridge hashtag (#rss-bridge) search on Twitter, in Atom format (as displayed by Firefox):
![Screenshot](https://github.com/RSS-Bridge/rss-bridge/wiki/images/screenshot_twitterbridge_atom.png)
![Screenshot](/static/screenshot_twitterbridge_atom.png?raw=true)
Requirements
===
RSS-Bridge requires PHP 5.6 or higher with following extensions enabled:
RSS-Bridge requires PHP 7.1 or higher with following extensions enabled:
- [`openssl`](https://secure.php.net/manual/en/book.openssl.php)
- [`libxml`](https://secure.php.net/manual/en/book.libxml.php)
@@ -66,26 +69,29 @@ RSS-Bridge requires PHP 5.6 or higher with following extensions enabled:
- [`curl`](https://secure.php.net/manual/en/book.curl.php)
- [`json`](https://secure.php.net/manual/en/book.json.php)
- [`filter`](https://secure.php.net/manual/en/book.filter.php)
- [`sqlite3`](http://php.net/manual/en/book.sqlite3.php) (only when using SQLiteCache)
- [`zip`](https://secure.php.net/manual/en/book.zip.php) (for some bridges)
- [`sqlite3`](https://www.php.net/manual/en/book.sqlite3.php) (only when using SQLiteCache)
Find more information on our [Wiki](https://github.com/rss-bridge/rss-bridge/wiki)
Find more information on our [Documentation](https://rss-bridge.github.io/rss-bridge/index.html)
Enable / Disable bridges
===
RSS-Bridge allows you to take full control over which bridges are displayed to the user. That way you can host your own RSS-Bridge service with your favorite collection of bridges!
Find more information on the [Wiki](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitelisting)
Find more information on the [Documentation](https://rss-bridge.github.io/rss-bridge/For_Hosts/Whitelisting.html)
**Notice**: By default, RSS-Bridge will only show a small subset of bridges. Make sure to read up on [whitelisting](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitelisting) to unlock the full potential of RSS-Bridge!
**Notice**: By default, RSS-Bridge will only show a small subset of bridges. Make sure to read up on [whitelisting](https://rss-bridge.github.io/rss-bridge/For_Hosts/Whitelisting.html) to unlock the full potential of RSS-Bridge!
Deploy
===
Thanks to the community, hosting your own instance of RSS-Bridge is as easy as clicking a button!
*Note: External providers' applications are packaged by 3rd parties. Use at your own discretion.*
[![Deploy on Scalingo](https://cdn.scalingo.com/deploy/button.svg)](https://my.scalingo.com/deploy?source=https://github.com/sebsauvage/rss-bridge)
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
[![Deploy to Cloudron](https://cloudron.io/img/button.svg)](https://www.cloudron.io/store/com.rssbridgeapp.cloudronapp.html)
Getting involved
===
@@ -97,51 +103,58 @@ There are many ways for you to getting involved with RSS-Bridge. Here are a few
- Request new features or suggest ideas (via [Issues](https://github.com/RSS-Bridge/rss-bridge/issues))
- Discuss bugs, features, ideas or [issues](https://github.com/RSS-Bridge/rss-bridge/issues)
- Add new bridges or improve the API
- Improve the [Wiki](https://github.com/RSS-Bridge/rss-bridge/wiki)
- Improve the [Documentation](https://rss-bridge.github.io/rss-bridge/)
- Host an instance of RSS-Bridge for your personal use or make it available to the community :sparkling_heart:
Authors
===
We are RSS-Bridge community, a group of developers continuing the project initiated by sebsauvage, webmaster of [sebsauvage.net](http://sebsauvage.net), author of [Shaarli](http://sebsauvage.net/wiki/doku.php?id=php:shaarli) and [ZeroBin](http://sebsauvage.net/wiki/doku.php?id=php:zerobin).
We are RSS-Bridge community, a group of developers continuing the project initiated by sebsauvage, webmaster of [sebsauvage.net](https://sebsauvage.net), author of [Shaarli](https://sebsauvage.net/wiki/doku.php?id=php:shaarli) and [ZeroBin](https://sebsauvage.net/wiki/doku.php?id=php:zerobin).
**Contributors** (sorted alphabetically):
<!--
Use this script to generate the list automatically (using the GitHub API):
https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
./contrib/prepare_release/fetch_contributors.php
-->
* [16mhz](https://github.com/16mhz)
* [adamchainz](https://github.com/adamchainz)
* [Ahiles3005](https://github.com/Ahiles3005)
* [akirk](https://github.com/akirk)
* [Albirew](https://github.com/Albirew)
* [aledeg](https://github.com/aledeg)
* [alex73](https://github.com/alex73)
* [alexAubin](https://github.com/alexAubin)
* [Alkarex](https://github.com/Alkarex)
* [AmauryCarrade](https://github.com/AmauryCarrade)
* [AntoineTurmel](https://github.com/AntoineTurmel)
* [arnd-s](https://github.com/arnd-s)
* [ArthurHoaro](https://github.com/ArthurHoaro)
* [Astalaseven](https://github.com/Astalaseven)
* [Astyan-42](https://github.com/Astyan-42)
* [austinhuang0131](https://github.com/austinhuang0131)
* [AxorPL](https://github.com/AxorPL)
* [ayacoo](https://github.com/ayacoo)
* [az5he6ch](https://github.com/az5he6ch)
* [azdkj532](https://github.com/azdkj532)
* [b1nj](https://github.com/b1nj)
* [benasse](https://github.com/benasse)
* [Binnette](https://github.com/Binnette)
* [BoboTiG](https://github.com/BoboTiG)
* [Bockiii](https://github.com/Bockiii)
* [captn3m0](https://github.com/captn3m0)
* [chemel](https://github.com/chemel)
* [Chouchen](https://github.com/Chouchen)
* [ckiw](https://github.com/ckiw)
* [cn-tools](https://github.com/cn-tools)
* [cnlpete](https://github.com/cnlpete)
* [corenting](https://github.com/corenting)
* [couraudt](https://github.com/couraudt)
* [csisoap](https://github.com/csisoap)
* [cyberjacob](https://github.com/cyberjacob)
* [da2x](https://github.com/da2x)
* [dabenzel](https://github.com/dabenzel)
* [Daiyousei](https://github.com/Daiyousei)
* [dawidsowa](https://github.com/dawidsowa)
* [DevonHess](https://github.com/DevonHess)
* [dhuschde](https://github.com/dhuschde)
* [disk0x](https://github.com/disk0x)
* [DJCrashdummy](https://github.com/DJCrashdummy)
* [Djuuu](https://github.com/Djuuu)
@@ -149,86 +162,129 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [dominik-th](https://github.com/dominik-th)
* [Draeli](https://github.com/Draeli)
* [Dreckiger-Dan](https://github.com/Dreckiger-Dan)
* [drego85](https://github.com/drego85)
* [drklee3](https://github.com/drklee3)
* [DRogueRonin](https://github.com/DRogueRonin)
* [dvikan](https://github.com/dvikan)
* [eggwhalefrog](https://github.com/eggwhalefrog)
* [em92](https://github.com/em92)
* [eMerzh](https://github.com/eMerzh)
* [EtienneM](https://github.com/EtienneM)
* [f0086](https://github.com/f0086)
* [fanch317](https://github.com/fanch317)
* [fatuuse](https://github.com/fatuuse)
* [fivefilters](https://github.com/fivefilters)
* [floviolleau](https://github.com/floviolleau)
* [fluffy-critter](https://github.com/fluffy-critter)
* [fmachen](https://github.com/fmachen)
* [Frenzie](https://github.com/Frenzie)
* [fulmeek](https://github.com/fulmeek)
* [ggiessen](https://github.com/ggiessen)
* [gileri](https://github.com/gileri)
* [Ginko-Aloe](https://github.com/Ginko-Aloe)
* [girlpunk](https://github.com/girlpunk)
* [Glandos](https://github.com/Glandos)
* [gloony](https://github.com/gloony)
* [GregThib](https://github.com/GregThib)
* [griffaurel](https://github.com/griffaurel)
* [Grummfy](https://github.com/Grummfy)
* [gsantner](https://github.com/gsantner)
* [guigot](https://github.com/guigot)
* [hollowleviathan](https://github.com/hollowleviathan)
* [hpacleb](https://github.com/hpacleb)
* [hunhejj](https://github.com/hunhejj)
* [husim0](https://github.com/husim0)
* [IceWreck](https://github.com/IceWreck)
* [imagoiq](https://github.com/imagoiq)
* [j0k3r](https://github.com/j0k3r)
* [JackNUMBER](https://github.com/JackNUMBER)
* [jannyba](https://github.com/jannyba)
* [jacquesh](https://github.com/jacquesh)
* [jakubvalenta](https://github.com/jakubvalenta)
* [JasonGhent](https://github.com/JasonGhent)
* [jcgoette](https://github.com/jcgoette)
* [jdesgats](https://github.com/jdesgats)
* [jdigilio](https://github.com/jdigilio)
* [JeremyRand](https://github.com/JeremyRand)
* [JimDog546](https://github.com/JimDog546)
* [jNullj](https://github.com/jNullj)
* [Jocker666z](https://github.com/Jocker666z)
* [johnnygroovy](https://github.com/johnnygroovy)
* [johnpc](https://github.com/johnpc)
* [joni1993](https://github.com/joni1993)
* [joshcoales](https://github.com/joshcoales)
* [jtojnar](https://github.com/jtojnar)
* [KamaleiZestri](https://github.com/KamaleiZestri)
* [kkoyung](https://github.com/kkoyung)
* [klimplant](https://github.com/klimplant)
* [KN4CK3R](https://github.com/KN4CK3R)
* [kolarcz](https://github.com/kolarcz)
* [kranack](https://github.com/kranack)
* [kraoc](https://github.com/kraoc)
* [krisu5](https://github.com/krisu5)
* [l1n](https://github.com/l1n)
* [laBecasse](https://github.com/laBecasse)
* [lagaisse](https://github.com/lagaisse)
* [lalannev](https://github.com/lalannev)
* [langfingaz](https://github.com/langfingaz)
* [lassana](https://github.com/lassana)
* [ldidry](https://github.com/ldidry)
* [Leomaradan](https://github.com/Leomaradan)
* [leyrer](https://github.com/leyrer)
* [liamka](https://github.com/liamka)
* [Limero](https://github.com/Limero)
* [LogMANOriginal](https://github.com/LogMANOriginal)
* [lorenzos](https://github.com/lorenzos)
* [lukasklinger](https://github.com/lukasklinger)
* [m0zes](https://github.com/m0zes)
* [Mar-Koeh](https://github.com/Mar-Koeh)
* [marcus-at-localhost](https://github.com/marcus-at-localhost)
* [marius8510000-bot](https://github.com/marius8510000-bot)
* [matthewseal](https://github.com/matthewseal)
* [mcbyte-it](https://github.com/mcbyte-it)
* [mdemoss](https://github.com/mdemoss)
* [melangue](https://github.com/melangue)
* [metaMMA](https://github.com/metaMMA)
* [mibe](https://github.com/mibe)
* [mickaelBert](https://github.com/mickaelBert)
* [mightymt](https://github.com/mightymt)
* [mitsukarenai](https://github.com/mitsukarenai)
* [Monocularity](https://github.com/Monocularity)
* [MonsieurPoutounours](https://github.com/MonsieurPoutounours)
* [mr-flibble](https://github.com/mr-flibble)
* [mro](https://github.com/mro)
* [mschwld](https://github.com/mschwld)
* [muekoeff](https://github.com/muekoeff)
* [mw80](https://github.com/mw80)
* [mxmehl](https://github.com/mxmehl)
* [Mynacol](https://github.com/Mynacol)
* [nel50n](https://github.com/nel50n)
* [niawag](https://github.com/niawag)
* [Niehztog](https://github.com/Niehztog)
* [NikNikYkt](https://github.com/NikNikYkt)
* [Nono-m0le](https://github.com/Nono-m0le)
* [ObsidianWitch](https://github.com/ObsidianWitch)
* [OliverParoczai](https://github.com/OliverParoczai)
* [obsiwitch](https://github.com/obsiwitch)
* [Ololbu](https://github.com/Ololbu)
* [ORelio](https://github.com/ORelio)
* [otakuf](https://github.com/otakuf)
* [Park0](https://github.com/Park0)
* [Paroleen](https://github.com/Paroleen)
* [Patricol](https://github.com/Patricol)
* [paulchen](https://github.com/paulchen)
* [PaulVayssiere](https://github.com/PaulVayssiere)
* [pellaeon](https://github.com/pellaeon)
* [PeterDaveHello](https://github.com/PeterDaveHello)
* [Peterr-K](https://github.com/Peterr-K)
* [Piranhaplant](https://github.com/Piranhaplant)
* [pirnz](https://github.com/pirnz)
* [pit-fgfjiudghdf](https://github.com/pit-fgfjiudghdf)
* [pitchoule](https://github.com/pitchoule)
* [pmaziere](https://github.com/pmaziere)
* [Pofilo](https://github.com/Pofilo)
* [prysme01](https://github.com/prysme01)
* [pubak42](https://github.com/pubak42)
* [Qluxzz](https://github.com/Qluxzz)
* [quentinus95](https://github.com/quentinus95)
* [quickwick](https://github.com/quickwick)
* [rakoo](https://github.com/rakoo)
* [RawkBob](https://github.com/RawkBob)
* [regisenguehard](https://github.com/regisenguehard)
* [Riduidel](https://github.com/Riduidel)
@@ -236,32 +292,51 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [Roliga](https://github.com/Roliga)
* [ronansalmon](https://github.com/ronansalmon)
* [rremizov](https://github.com/rremizov)
* [s0lesurviv0r](https://github.com/s0lesurviv0r)
* [sal0max](https://github.com/sal0max)
* [sebsauvage](https://github.com/sebsauvage)
* [shutosg](https://github.com/shutosg)
* [simon816](https://github.com/simon816)
* [Simounet](https://github.com/Simounet)
* [somini](https://github.com/somini)
* [SpangleLabs](https://github.com/SpangleLabs)
* [SqrtMinusOne](https://github.com/SqrtMinusOne)
* [squeek502](https://github.com/squeek502)
* [StelFux](https://github.com/StelFux)
* [stjohnjohnson](https://github.com/stjohnjohnson)
* [Stopka](https://github.com/Stopka)
* [Strubbl](https://github.com/Strubbl)
* [sublimz](https://github.com/sublimz)
* [sunchaserinfo](https://github.com/sunchaserinfo)
* [SuperSandro2000](https://github.com/SuperSandro2000)
* [sysadminstory](https://github.com/sysadminstory)
* [t0stiman](https://github.com/t0stiman)
* [tameroski](https://github.com/tameroski)
* [teromene](https://github.com/teromene)
* [tgkenney](https://github.com/tgkenney)
* [thefranke](https://github.com/thefranke)
* [ThePadawan](https://github.com/ThePadawan)
* [TheRadialActive](https://github.com/TheRadialActive)
* [theScrabi](https://github.com/theScrabi)
* [thezeroalpha](https://github.com/thezeroalpha)
* [thibaultcouraud](https://github.com/thibaultcouraud)
* [timendum](https://github.com/timendum)
* [TitiTestScalingo](https://github.com/TitiTestScalingo)
* [tomaszkane](https://github.com/tomaszkane)
* [tomershvueli](https://github.com/tomershvueli)
* [TotalCaesar659](https://github.com/TotalCaesar659)
* [tpikonen](https://github.com/tpikonen)
* [TReKiE](https://github.com/TReKiE)
* [triatic](https://github.com/triatic)
* [User123698745](https://github.com/User123698745)
* [VerifiedJoseph](https://github.com/VerifiedJoseph)
* [vitkabele](https://github.com/vitkabele)
* [WalterBarrett](https://github.com/WalterBarrett)
* [wtuuju](https://github.com/wtuuju)
* [xurxof](https://github.com/xurxof)
* [yamanq](https://github.com/yamanq)
* [yardenac](https://github.com/yardenac)
* [ymeister](https://github.com/ymeister)
* [yue-dongchen](https://github.com/yue-dongchen)
* [ZeNairolf](https://github.com/ZeNairolf)
Licenses
@@ -271,16 +346,18 @@ The source code for RSS-Bridge is [Public Domain](UNLICENSE).
RSS-Bridge uses third party libraries with their own license:
* [`Parsedown`](https://github.com/erusev/parsedown) licensed under the [MIT License](http://opensource.org/licenses/MIT)
* [`PHP Simple HTML DOM Parser`](http://simplehtmldom.sourceforge.net/) licensed under the [MIT License](http://opensource.org/licenses/MIT)
* [`php-urljoin`](https://github.com/fluffy-critter/php-urljoin) licensed under the [MIT License](http://opensource.org/licenses/MIT)
* [`Parsedown`](https://github.com/erusev/parsedown) licensed under the [MIT License](https://opensource.org/licenses/MIT)
* [`PHP Simple HTML DOM Parser`](https://simplehtmldom.sourceforge.io/docs/1.9/index.html) licensed under the [MIT License](https://opensource.org/licenses/MIT)
* [`php-urljoin`](https://github.com/fluffy-critter/php-urljoin) licensed under the [MIT License](https://opensource.org/licenses/MIT)
* [php polyfills](https://github.com/symfony/polyfill) licensed under the [MIT License](https://opensource.org/licenses/MIT)
* [`Laravel framework`](https://github.com/laravel/framework/) licensed under the [MIT License](https://opensource.org/licenses/MIT)
Technical notes
===
* RSS-Bridge uses caching to prevent services from banning your server for repeatedly updating feeds. The specific cache duration can be different between bridges. Cached files are deleted automatically after 24 hours.
* You can implement your own bridge, [following these instructions](https://github.com/RSS-Bridge/rss-bridge/wiki/Bridge-API).
* You can enable debug mode to disable caching. Find more information on the [Wiki](https://github.com/RSS-Bridge/rss-bridge/wiki/Debug-mode)
* You can implement your own bridge, [following these instructions](https://rss-bridge.github.io/rss-bridge/Bridge_API/index.html).
* You can enable debug mode to disable caching. Find more information on the [Wiki](https://rss-bridge.github.io/rss-bridge/For_Developers/Debug_mode.html)
Rant
===
@@ -293,6 +370,6 @@ You're not social when you hamper sharing by removing feeds. You're happy to hav
We want to share with friends, using open protocols: RSS, Atom, XMPP, whatever. Because no one wants to have *your* service with *your* applications using *your* API force-feeding them. Friends must be free to choose whatever software and service they want.
We are rebuilding bridges you have wilfully destroyed.
We are rebuilding bridges you have willfully destroyed.
Get your shit together: Put RSS/Atom back in.

View File

@@ -25,7 +25,7 @@ class ConnectivityAction extends ActionAbstract {
public function execute() {
if(!Debug::isEnabled()) {
returnError('This action is only available in debug mode!');
returnError('This action is only available in debug mode!', 400);
}
if(!isset($this->userData['bridge'])) {
@@ -55,7 +55,6 @@ class ConnectivityAction extends ActionAbstract {
private function reportBridgeConnectivity($bridgeName) {
$bridgeFac = new \BridgeFactory();
$bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
if(!$bridgeFac->isWhitelisted($bridgeName)) {
header('Content-Type: text/html');
@@ -84,12 +83,10 @@ class ConnectivityAction extends ActionAbstract {
try {
$reply = getContents($bridge::URI, array(), $curl_opts, true);
if($reply) {
if($reply['code'] === 200) {
$retVal['successful'] = true;
if (isset($reply['header'])) {
if (strpos($reply['header'], 'HTTP/1.1 301 Moved Permanently') !== false) {
$retVal['http_code'] = 301;
}
if (strpos(implode('', $reply['status_lines']), '301 Moved Permanently')) {
$retVal['http_code'] = 301;
}
}
} catch(Exception $e) {

View File

@@ -20,7 +20,6 @@ class DetectAction extends ActionAbstract {
or returnClientError('You must specify a format!');
$bridgeFac = new \BridgeFactory();
$bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
foreach($bridgeFac->getBridgeNames() as $bridgeName) {

View File

@@ -28,7 +28,6 @@ class DisplayAction extends ActionAbstract {
or returnClientError('You must specify a format!');
$bridgeFac = new \BridgeFactory();
$bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
// whitelist control
if(!$bridgeFac->isWhitelisted($bridge)) {
@@ -38,6 +37,7 @@ class DisplayAction extends ActionAbstract {
// Data retrieval
$bridge = $bridgeFac->create($bridge);
$bridge->loadConfiguration();
$noproxy = array_key_exists('_noproxy', $this->userData)
&& filter_var($this->userData['_noproxy'], FILTER_VALIDATE_BOOLEAN);
@@ -150,6 +150,7 @@ class DisplayAction extends ActionAbstract {
$infos = array(
'name' => $bridge->getName(),
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'icon' => $bridge->getIcon()
);
} catch(Error $e) {
@@ -243,8 +244,14 @@ class DisplayAction extends ActionAbstract {
$format = $formatFac->create($format);
$format->setItems($items);
$format->setExtraInfos($infos);
$format->setLastModified($cache->getTime());
$format->display();
$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(Error $e) {
error_log($e);
header('Content-Type: text/html', true, $e->getCode());

View File

@@ -18,7 +18,6 @@ class ListAction extends ActionAbstract {
$list->total = 0;
$bridgeFac = new \BridgeFactory();
$bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
foreach($bridgeFac->getBridgeNames() as $bridgeName) {
@@ -39,6 +38,7 @@ class ListAction extends ActionAbstract {
$list->bridges[$bridgeName] = array(
'status' => $status,
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'name' => $bridge->getName(),
'icon' => $bridge->getIcon(),
'parameters' => $bridge->getParameters(),

View File

@@ -1,8 +1,8 @@
{
"service": "Heroku",
"name": "RSS-Bridge",
"name": "rss-bridge-heroku",
"description": "RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites which don't have one.",
"repository": "https://github.com/RSS-Bridge/rss-bridge",
"repository": "https://github.com/RSS-Bridge/rss-bridge?1651005770",
"keywords": ["php", "rss-bridge", "rss"]
}

45
bridges/ABCNewsBridge.php Normal file
View File

@@ -0,0 +1,45 @@
<?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(
array(
'topic' => array(
'type' => 'list',
'name' => 'Region',
'title' => 'Choose state',
'values' => array(
'ACT' => 'act',
'NSW' => 'nsw',
'NT' => 'nt',
'QLD' => 'qld',
'SA' => 'sa',
'TAS' => 'tas',
'VIC' => 'vic',
'WA' => 'wa'
),
)
)
);
public function collectData() {
$url = 'https://www.abc.net.au/news/' . $this->getInput('topic');
$html = getSimpleHTMLDOM($url)->find('.YAJzu._2FvRw.ZWhbj._3BZxh', 0);
$html = defaultLinkTo($html, $this->getURI());
foreach($html->find('._2H7Su') as $article) {
$item = array();
$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,42 +0,0 @@
<?php
class ABCTabsBridge extends BridgeAbstract {
const MAINTAINER = 'kranack';
const NAME = 'ABC Tabs Bridge';
const URI = 'https://www.abc-tabs.com/';
const DESCRIPTION = 'Returns 22 newest tabs';
public function collectData(){
$html = '';
$html = getSimpleHTMLDOM(static::URI . 'tablatures/nouveautes.html')
or returnClientError('No results for this query.');
$table = $html->find('table#myTable', 0)->children(1);
foreach ($table->find('tr') as $tab) {
$item = array();
$item['author'] = $tab->find('td', 1)->plaintext
. ' - '
. $tab->find('td', 2)->plaintext;
$item['title'] = $tab->find('td', 1)->plaintext
. ' - '
. $tab->find('td', 2)->plaintext;
$item['content'] = 'Le '
. $tab->find('td', 0)->plaintext
. '<br> Par: '
. $tab->find('td', 5)->plaintext
. '<br> Type: '
. $tab->find('td', 3)->plaintext;
$item['id'] = static::URI
. $tab->find('td', 2)->find('a', 0)->getAttribute('href');
$item['uri'] = static::URI
. $tab->find('td', 2)->find('a', 0)->getAttribute('href');
$this->items[] = $item;
}
}
}

View File

@@ -12,8 +12,7 @@ class AO3Bridge extends BridgeAbstract {
'name' => 'url',
'required' => true,
// Example: F/F tag, complete works only
'exampleValue' => self::URI
. 'works?work_search[complete]=T&tag_id=F*s*F',
'exampleValue' => 'https://archiveofourown.org/works?work_search[complete]=T&tag_id=F*s*F',
),
),
'Bookmarks' => array(
@@ -37,8 +36,7 @@ class AO3Bridge extends BridgeAbstract {
// Feed for lists of works (e.g. recent works, search results, filtered tags,
// bookmarks, series, collections).
private function collectList($url) {
$html = getSimpleHTMLDOM($url)
or returnServerError('could not request AO3');
$html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, self::URI);
foreach($html->find('.index.group > li') as $element) {
@@ -65,8 +63,7 @@ class AO3Bridge extends BridgeAbstract {
// Feed for recent chapters of a specific work.
private function collectWork($id) {
$url = self::URI . "/works/$id/navigate";
$html = getSimpleHTMLDOM($url)
or returnServerError('could not request AO3');
$html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, self::URI);
$this->title = $html->find('h2 a', 0)->plaintext;

View File

@@ -0,0 +1,95 @@
<?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(
array(
'path' => array(
'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/'
)
)
);
public function collectData() {
$oldTz = date_default_timezone_get();
date_default_timezone_set('Europe/Berlin');
$pathComponents = explode('/', $this->getInput('path'));
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;
$rawJSON = getContents($url);
$processedJSON = json_decode($rawJSON);
foreach($processedJSON->teasers as $video) {
$item = array();
// 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'] = 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);
}
}

View File

@@ -10,8 +10,7 @@ class ASRockNewsBridge extends BridgeAbstract {
public function collectData() {
$html = getSimpleHTMLDOM(self::URI . '/news/index.asp')
or returnServerError('Could not request: ' . self::URI . '/news/index.asp');
$html = getSimpleHTMLDOM(self::URI . '/news/index.asp');
$html = defaultLinkTo($html, self::URI . '/news/');
@@ -20,17 +19,16 @@ class ASRockNewsBridge extends BridgeAbstract {
$articlePath = $a->href;
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT)
or returnServerError('Could not request: ' . $articlePath);
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT);
$articlePageHtml = defaultLinkTo($articlePageHtml, self::URI);
$contents = $articlePageHtml->find('div.Contents', 0);
$item['uri'] = $articlePath;
$item['title'] = $contents->find('h5', 0)->innertext;
$item['title'] = $contents->find('h3', 0)->innertext;
$contents->find('h5', 0)->outertext = '';
$contents->find('h3', 0)->outertext = '';
$item['content'] = $contents->innertext;
$item['timestamp'] = $this->extractDate($a->plaintext);

View File

@@ -3,12 +3,25 @@ class AcrimedBridge extends FeedExpander {
const MAINTAINER = 'qwertygc';
const NAME = 'Acrimed Bridge';
const URI = 'http://www.acrimed.org/';
const URI = 'https://www.acrimed.org/';
const CACHE_TIMEOUT = 4800; //2hours
const DESCRIPTION = 'Returns the newest articles';
const PARAMETERS = [
[
'limit' => [
'name' => 'limit',
'type' => 'number',
'defaultValue' => -1,
]
]
];
public function collectData(){
$this->collectExpandableDatas(static::URI . 'spip.php?page=backend');
$this->collectExpandableDatas(
static::URI . 'spip.php?page=backend',
$this->getInput('limit')
);
}
protected function parseItem($newsItem){

View File

@@ -11,6 +11,7 @@ class AlbionOnlineBridge extends BridgeAbstract {
'postcount' => array(
'name' => 'Limit',
'type' => 'number',
'required' => true,
'title' => 'Maximum number of items to return',
'defaultValue' => 5,
),
@@ -42,8 +43,7 @@ class AlbionOnlineBridge extends BridgeAbstract {
// Example: https://albiononline.com/en/changelog/1/5
$url = $api . $this->getInput('language') . '/changelog/1/' . $this->getInput('postcount');
$html = getSimpleHTMLDOM($url)
or returnServerError('Unable to get changelog data from "' . $url . '"!');
$html = getSimpleHTMLDOM($url);
foreach ($html->find('li') as $data) {
$item = array();
@@ -66,8 +66,7 @@ class AlbionOnlineBridge extends BridgeAbstract {
}
private function getFullChangelog($url) {
$html = getSimpleHTMLDOMCached($url)
or returnServerError('Unable to load changelog post from "' . $url . '"!');
$html = getSimpleHTMLDOMCached($url);
$html = defaultLinkTo($html, self::URI);
return $html->find('div.small-12.columns', 1)->innertext;
}

View File

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

View File

@@ -4,7 +4,7 @@ class AllocineFRBridge extends BridgeAbstract {
const MAINTAINER = 'superbaillot.net';
const NAME = 'Allo Cine Bridge';
const CACHE_TIMEOUT = 25200; // 7h
const URI = 'http://www.allocine.fr/';
const URI = 'https://www.allocine.fr';
const DESCRIPTION = 'Bridge for allocine.fr';
const PARAMETERS = array( array(
'category' => array(
@@ -35,26 +35,26 @@ class AllocineFRBridge extends BridgeAbstract {
if(!is_null($this->getInput('category'))) {
$categories = array(
'faux-raccord' => 'video/programme-12284/saison-37054/',
'fanzone' => 'video/programme-12298/saison-37059/',
'game-in-cine' => 'video/programme-12288/saison-22971/',
'pour-la-faire-courte' => 'video/programme-20960/saison-29678/',
'home-cinema' => 'video/programme-12287/saison-34703/',
'pils-par-ici-les-sorties' => 'video/programme-25789/saison-37253/',
'allocine-lemission-sur-lestream' => 'video/programme-25123/saison-36067/',
'give-me-five' => 'video/programme-21919/saison-34518/',
'aviez-vous-remarque' => 'video/programme-19518/saison-37084/',
'et-paf-il-est-mort' => 'video/programme-25113/saison-36657/',
'the-big-fan-theory' => 'video/programme-20403/saison-37419/',
'cliches' => 'video/programme-24834/saison-35591/',
'completement' => 'video/programme-23859/saison-34102/',
'fun-facts' => 'video/programme-23040/saison-32686/',
'origin-story' => 'video/programme-25667/saison-37041/'
'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/'
);
$category = $this->getInput('category');
if(array_key_exists($category, $categories)) {
return static::URI . $categories[$category];
return static::URI . $this->getLastSeasonURI($categories[$category]);
} else {
returnClientError('Emission inconnue');
}
@@ -63,6 +63,14 @@ class AllocineFRBridge extends BridgeAbstract {
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;
}
public function getName(){
if(!is_null($this->getInput('category'))) {
return self::NAME . ' : '
@@ -77,19 +85,17 @@ class AllocineFRBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request ' . $this->getURI() . ' !');
$html = getSimpleHTMLDOM($this->getURI());
$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 = array();
$title = $element->find('a[class*=meta-title-link]', 0);
$content = trim($element->outertext);
$content = trim(defaultLinkTo($element->outertext, static::URI));
// Replace image 'src' with the one in 'data-src'
$content = preg_replace('@src="data:image/gif;base64,[A-Za-z0-9+\/]*"@', '', $content);
@@ -100,7 +106,7 @@ class AllocineFRBridge extends BridgeAbstract {
$item['content'] = $content;
$item['title'] = trim($title->innertext);
$item['uri'] = static::URI . substr($title->href, 1);
$item['uri'] = static::URI . '/' . substr($title->href, 1);
$this->items[] = $item;
}
}

View File

@@ -12,6 +12,7 @@ class AmazonBridge extends BridgeAbstract {
'q' => array(
'name' => 'Keyword',
'required' => true,
'exampleValue' => 'watch',
),
'sort' => array(
'name' => 'Sort by',
@@ -41,6 +42,8 @@ class AmazonBridge extends BridgeAbstract {
'Mexico' => 'com.mx',
'Netherlands' => 'nl',
'Spain' => 'es',
'Sweden' => 'se',
'Turkey' => 'com.tr',
'United Kingdom' => 'co.uk',
'United States' => 'com',
),
@@ -48,6 +51,48 @@ class AmazonBridge extends BridgeAbstract {
),
));
public function collectData() {
$baseUrl = sprintf('https://www.amazon.%s', $this->getInput('tld'));
$url = sprintf(
'%s/s/?field-keywords=%s&sort=%s',
$baseUrl,
urlencode($this->getInput('q')),
$this->getInput('sort')
);
$dom = getSimpleHTMLDOM($url);
$elements = $dom->find('div.s-result-item');
foreach($elements as $element) {
$item = [];
$title = $element->find('h2', 0);
if (!$title) {
continue;
}
$item['title'] = $title->innertext;
$itemUrl = $element->find('a', 0)->href;
$item['uri'] = urljoin($baseUrl, $itemUrl);
$image = $element->find('img', 0);
if ($image) {
$item['content'] = '<img src="' . $image->getAttribute('src') . '" /><br />';
}
$price = $element->find('span.a-price > .a-offscreen', 0);
if ($price) {
$item['content'] .= $price->innertext;
}
$this->items[] = $item;
}
}
public function getName(){
if(!is_null($this->getInput('tld')) && !is_null($this->getInput('q'))) {
return 'Amazon.' . $this->getInput('tld') . ': ' . $this->getInput('q');
@@ -55,41 +100,4 @@ class AmazonBridge extends BridgeAbstract {
return parent::getName();
}
public function collectData() {
$uri = 'https://www.amazon.' . $this->getInput('tld') . '/';
$uri .= 's/?field-keywords=' . urlencode($this->getInput('q')) . '&sort=' . $this->getInput('sort');
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request Amazon.');
foreach($html->find('li.s-result-item') as $element) {
$item = array();
// Title
$title = $element->find('h2', 0);
if (is_null($title)) {
continue;
}
$item['title'] = html_entity_decode($title->innertext, ENT_QUOTES);
// Url
$uri = $title->parent()->getAttribute('href');
$uri = substr($uri, 0, strrpos($uri, '/'));
$item['uri'] = substr($uri, 0, strrpos($uri, '/'));
// Content
$image = $element->find('img', 0);
$price = $element->find('span.s-price', 0);
$price = ($price) ? $price->innertext : '';
$item['content'] = '<img src="' . $image->getAttribute('src') . '" /><br />' . $price;
$this->items[] = $item;
}
}
}

View File

@@ -1,7 +1,7 @@
<?php
class AmazonPriceTrackerBridge extends BridgeAbstract {
const MAINTAINER = 'captn3m0';
const MAINTAINER = 'captn3m0, sal0max';
const NAME = 'Amazon Price Tracker';
const URI = 'https://www.amazon.com/';
const CACHE_TIMEOUT = 3600; // 1h
@@ -20,7 +20,7 @@ class AmazonPriceTrackerBridge extends BridgeAbstract {
'name' => 'Country',
'type' => 'list',
'values' => array(
'Australia' => 'com.au',
'Australia' => 'com.au',
'Brazil' => 'com.br',
'Canada' => 'ca',
'China' => 'cn',
@@ -30,8 +30,10 @@ class AmazonPriceTrackerBridge extends BridgeAbstract {
'Italy' => 'it',
'Japan' => 'co.jp',
'Mexico' => 'com.mx',
'Netherlands' => 'nl',
'Netherlands' => 'nl',
'Spain' => 'es',
'Sweden' => 'se',
'Turkey' => 'com.tr',
'United Kingdom' => 'co.uk',
'United States' => 'com',
),
@@ -39,6 +41,17 @@ class AmazonPriceTrackerBridge extends BridgeAbstract {
),
));
const PRICE_SELECTORS = array(
'#priceblock_ourprice',
'.priceBlockBuyingPriceString',
'#newBuyBoxPrice',
'#tp_price_block_total_price_ww',
'span.offer-price',
'.a-color-price',
);
const WHITESPACE = " \t\n\r\0\x0B\xC2\xA0";
protected $title;
/**
@@ -53,7 +66,7 @@ class AmazonPriceTrackerBridge extends BridgeAbstract {
*/
public function getURI() {
if (!is_null($this->getInput('asin'))) {
return $this->getDomainName() . '/dp/' . $this->getInput('asin') . '/';
return $this->getDomainName() . '/dp/' . $this->getInput('asin');
}
return parent::getURI();
}
@@ -144,15 +157,46 @@ EOT;
return false;
}
private function scrapePriceGeneric($html) {
$priceDiv = $html->find('span.offer-price', 0) ?: $html->find('.a-color-price', 0);
private function scrapePriceTwister($html) {
$str = $html->find('.twister-plus-buying-options-price-data', 0);
preg_match('/^\s*([A-Z]{3}|£|\$)\s?([\d.,]+)\s*$/', $priceDiv->plaintext, $matches);
if (count($matches) === 3) {
$data = json_decode($str->innertext, true);
if(count($data) === 1) {
$data = $data[0];
return array(
'price' => $matches[2],
'currency' => $matches[1],
'displayPrice' => $data['displayPrice'],
'currency' => $data['currency'],
'shipping' => '0',
);
}
return false;
}
private function scrapePriceGeneric($html) {
$priceDiv = null;
foreach(self::PRICE_SELECTORS as $sel) {
$priceDiv = $html->find($sel, 0);
if ($priceDiv) {
break;
}
}
if (!$priceDiv) {
return false;
}
$priceString = str_replace(str_split(self::WHITESPACE), '', $priceDiv->plaintext);
preg_match('/(\d+\.\d{0,2})/', $priceString, $matches);
$price = $matches[0];
$currency = str_replace($price, '', $priceString);
if ($price != null && $currency != null) {
return array(
'price' => $price,
'currency' => $currency,
'shipping' => '0'
);
}
@@ -160,6 +204,21 @@ EOT;
return false;
}
private function renderContent($image, $data) {
$price = $data['displayPrice'];
if (!$price) {
$price = "{$data['price']} {$data['currency']}";
}
$html = "$image<br>Price: $price";
if ($data['shipping'] !== '0') {
$html .= "<br>Shipping: {$data['shipping']} {$data['currency']}</br>";
}
return $html;
}
/**
* Scrape method for Amazon product page
* @return [type] [description]
@@ -169,18 +228,16 @@ EOT;
$this->title = $this->getTitle($html);
$imageTag = $this->getImage($html);
$data = $this->scrapePriceFromMetrics($html) ?: $this->scrapePriceGeneric($html);
$data = $this->scrapePriceGeneric($html);
$item = array(
'title' => $this->title,
'uri' => $this->getURI(),
'content' => "$imageTag<br/>Price: {$data['price']} {$data['currency']}",
'content' => $this->renderContent($imageTag, $data),
// This is to ensure that feed readers notice the price change
'uid' => md5($data['price'])
);
if ($data['shipping'] !== '0') {
$item['content'] .= "<br>Shipping: {$data['shipping']} {$data['currency']}</br>";
}
$this->items[] = $item;
}
}

View File

@@ -142,8 +142,7 @@ class AnidexBridge extends BridgeAbstract {
$opt[CURLOPT_SSL_VERIFYPEER] = 0;
// Retrieve torrent listing from search results, which does not contain torrent description
$html = getSimpleHTMLDOM($search_url, $headers, $opt)
or returnServerError('Could not request Anidex: ' . $search_url);
$html = getSimpleHTMLDOM($search_url, $headers, $opt);
$links = $html->find('a');
$results = array();
foreach ($links as $link)

View File

@@ -37,10 +37,11 @@ class AnimeUltimeBridge extends BridgeAbstract {
$processedOK = 0;
foreach (array($thismonth, $lastmonth) as $requestFilter) {
//Retrive page contents
$url = self::URI . 'history-0-1/' . $requestFilter;
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request Anime-Ultime: ' . $url);
$html = getContents($url);
// 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
foreach($html->find('div.history', 0)->find('h3') as $daySection) {
@@ -87,8 +88,9 @@ class AnimeUltimeBridge extends BridgeAbstract {
if(!empty($item_uri)) {
// Retrieve description from description page
$html_item = getContents($item_uri)
or returnServerError('Could not request Anime-Ultime: ' . $item_uri);
$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

View File

@@ -35,7 +35,8 @@ class AppleAppStoreBridge extends BridgeAbstract {
'values' => array(
'US' => 'US',
'India' => 'IN',
'Canada' => 'CA'
'Canada' => 'CA',
'Germany' => 'DE',
),
'defaultValue' => 'US',
),
@@ -93,6 +94,7 @@ class AppleAppStoreBridge extends BridgeAbstract {
$headers = array(
"Authorization: Bearer $token",
'Origin: https://apps.apple.com',
);
$json = json_decode(getContents($uri, $headers), true);

View File

@@ -4,59 +4,52 @@ class AppleMusicBridge extends BridgeAbstract {
const NAME = 'Apple Music';
const URI = 'https://www.apple.com';
const DESCRIPTION = 'Fetches the latest releases from an artist';
const MAINTAINER = 'Limero';
const MAINTAINER = 'bockiii';
const PARAMETERS = array(array(
'url' => array(
'name' => 'Artist URL',
'exampleValue' => 'https://itunes.apple.com/us/artist/dunderpatrullen/329796274',
'artist' => array(
'name' => 'Artist ID',
'exampleValue' => '909253',
'required' => true,
),
'imgSize' => array(
'name' => 'Image size for thumbnails (in px)',
'type' => 'number',
'defaultValue' => 512,
'limit' => array(
'name' => 'Latest X Releases (max 50)',
'defaultValue' => '10',
'required' => true,
)
),
));
const CACHE_TIMEOUT = 21600; // 6 hours
public function collectData() {
$url = $this->getInput('url');
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request: ' . $url);
# Limit the amount of releases to 50
if ($this->getInput('limit') > 50) {
$limit = 50;
} else {
$limit = $this->getInput('limit');
}
$imgSize = $this->getInput('imgSize');
$url = 'https://itunes.apple.com/lookup?id='
. $this->getInput('artist')
. '&entity=album&limit='
. $limit .
'&sort=recent';
$html = getSimpleHTMLDOM($url);
// Grab the json data from the page
$html = $html->find('script[id=shoebox-ember-data-store]', 0);
$html = strstr($html, '{');
$html = substr($html, 0, -9);
$json = json_decode($html);
// Loop through each object
foreach ($json->included as $obj) {
if ($obj->type === 'lockup/album') {
foreach ($json->results as $obj) {
if ($obj->wrapperType === 'collection') {
$this->items[] = array(
'title' => $obj->attributes->artistName . ' - ' . $obj->attributes->name,
'uri' => $obj->attributes->url,
'timestamp' => $obj->attributes->releaseDate,
'enclosures' => $obj->relationships->artwork->data->id,
'title' => $obj->artistName . ' - ' . $obj->collectionName,
'uri' => $obj->collectionViewUrl,
'timestamp' => $obj->releaseDate,
'enclosures' => $obj->artworkUrl100,
'content' => '<a href=' . $obj->collectionViewUrl
. '><img src="' . $obj->artworkUrl100 . '" /></a><br><br>'
. $obj->artistName . ' - ' . $obj->collectionName
. '<br>'
. $obj->copyright,
);
} elseif ($obj->type === 'image') {
$images[$obj->id] = $obj->attributes->url;
}
}
// Add the images to each item
foreach ($this->items as &$item) {
$item['enclosures'] = array(
str_replace('{w}x{h}bb.{f}', $imgSize . 'x0w.jpg', $images[$item['enclosures']]),
);
}
// Sort the order to put the latest albums first
usort($this->items, function($a, $b){
return $a['timestamp'] < $b['timestamp'];
});
}
}

View File

@@ -10,7 +10,8 @@ class ArtStationBridge extends BridgeAbstract {
'Search Query' => array(
'q' => array(
'name' => 'Search term',
'required' => true
'required' => true,
'exampleValue' => 'bird'
)
)
);
@@ -39,15 +40,13 @@ class ArtStationBridge extends BridgeAbstract {
);
$jsonSearchURL = self::URI . '/api/v2/search/projects.json';
$jsonSearchStr = getContents($jsonSearchURL, $header, $opts)
or returnServerError('Could not fetch JSON for search query.');
$jsonSearchStr = getContents($jsonSearchURL, $header, $opts);
return json_decode($jsonSearchStr);
}
private function fetchProject($hashID) {
$jsonProjectURL = self::URI . '/projects/' . $hashID . '.json';
$jsonProjectStr = getContents($jsonProjectURL)
or returnServerError('Could not fetch JSON for project.');
$jsonProjectStr = getContents($jsonProjectURL);
return json_decode($jsonProjectStr);
}

View File

@@ -1,89 +1,122 @@
<?php
class Arte7Bridge extends BridgeAbstract {
const MAINTAINER = 'mitsukarenai';
const NAME = 'Arte +7';
const URI = 'https://www.arte.tv/';
const MAINTAINER = 'imagoiq';
const CACHE_TIMEOUT = 1800; // 30min
const DESCRIPTION = 'Returns newest videos from ARTE +7';
const API_TOKEN = 'Nzc1Yjc1ZjJkYjk1NWFhN2I2MWEwMmRlMzAzNjI5NmU3NWU3ODg4ODJjOWMxNTMxYzEzZGRjYjg2ZGE4MmIwOA';
const PARAMETERS = array(
'Catégorie (Français)' => array(
'catfr' => array(
'global' => [
'sort_by' => array(
'type' => 'list',
'name' => 'Catégorie',
'name' => 'Sort by',
'required' => false,
'defaultValue' => null,
'values' => array(
'Toutes les vidéos (français)' => null,
'Actu & société' => 'ACT',
'Séries & fiction' => 'SER',
'Cinéma' => 'CIN',
'Arts & spectacles classiques' => 'ARS',
'Default' => null,
'Video rights start date' => 'videoRightsBegin',
'Video rights end date' => 'videoRightsEnd',
'Brodcast date' => 'broadcastBegin',
'Creation date' => 'creationDate',
'Last modified' => 'lastModified',
'Number of views' => 'views',
'Number of views per period' => 'viewsPeriod',
'Available screens' => 'availableScreens',
'Episode' => 'episode'
),
),
'sort_direction' => array(
'type' => 'list',
'name' => 'Sort direction',
'required' => false,
'defaultValue' => 'DESC',
'values' => array(
'Ascending' => 'ASC',
'Descending' => 'DESC'
),
),
'exclude_trailers' => [
'name' => 'Exclude trailers',
'type' => 'checkbox',
'required' => false,
'defaultValue' => false
],
],
'Category' => array(
'lang' => array(
'type' => 'list',
'name' => 'Language',
'values' => array(
'Français' => 'fr',
'Deutsch' => 'de',
'English' => 'en',
'Español' => 'es',
'Polski' => 'pl',
'Italiano' => 'it'
),
),
'cat' => array(
'type' => 'list',
'name' => 'Category',
'values' => array(
'All videos' => null,
'News & society' => 'ACT',
'Series & fiction' => 'SER',
'Cinema' => 'CIN',
'Culture' => 'ARS',
'Culture pop' => 'CPO',
'Découverte' => 'DEC',
'Histoire' => 'HIST',
'Discovery' => 'DEC',
'History' => 'HIST',
'Science' => 'SCI',
'Autre' => 'AUT'
'Other' => 'AUT'
)
)
),
),
'Collection (Français)' => array(
'colfr' => array(
'name' => 'Collection id',
'required' => true,
'title' => 'ex. RC-014095 pour https://www.arte.tv/fr/videos/RC-014095/blow-up/'
)
),
'Catégorie (Allemand)' => array(
'catde' => array(
'Collection' => array(
'lang' => array(
'type' => 'list',
'name' => 'Catégorie',
'name' => 'Language',
'values' => array(
'Alle Videos (deutsch)' => null,
'Aktuelles & Gesellschaft' => 'ACT',
'Fernsehfilme & Serien' => 'SER',
'Kino' => 'CIN',
'Kunst & Kultur' => 'ARS',
'Popkultur & Alternativ' => 'CPO',
'Entdeckung' => 'DEC',
'Geschichte' => 'HIST',
'Wissenschaft' => 'SCI',
'Sonstiges' => 'AUT'
'Français' => 'fr',
'Deutsch' => 'de',
'English' => 'en',
'Español' => 'es',
'Polski' => 'pl',
'Italiano' => 'it'
)
)
),
'Collection (Allemand)' => array(
'colde' => array(
),
'col' => array(
'name' => 'Collection id',
'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'
)
)
);
public function collectData(){
switch($this->queriedContext) {
case 'Catégorie (Français)':
$category = $this->getInput('catfr');
$lang = 'fr';
case 'Category':
$category = $this->getInput('cat');
$collectionId = null;
break;
case 'Collection (Français)':
$lang = 'fr';
$collectionId = $this->getInput('colfr');
break;
case 'Catégorie (Allemand)':
$category = $this->getInput('catde');
$lang = 'de';
break;
case 'Collection (Allemand)':
$lang = 'de';
$collectionId = $this->getInput('colde');
case 'Collection':
$collectionId = $this->getInput('col');
$category = null;
break;
}
$url = 'https://api.arte.tv/api/opa/v3/videos?sort=-lastModified&limit=10&language='
$lang = $this->getInput('lang');
$sort_by = $this->getInput('sort_by');
$sort_direction = $this->getInput('sort_direction') == 'ASC' ? '' : '-';
$url = 'https://api.arte.tv/api/opa/v3/videos?limit=15&language='
. $lang
. ($sort_by != null ? '&sort=' . $sort_direction . $sort_by : '')
. ($category != null ? '&category.code=' . $category : '')
. ($collectionId != null ? '&collections.collectionId=' . $collectionId : '');
@@ -91,11 +124,15 @@ class Arte7Bridge extends BridgeAbstract {
'Authorization: Bearer ' . self::API_TOKEN
);
$input = getContents($url, $header)
or returnServerError('Could not request ARTE.');
$input = getContents($url, $header);
$input_json = json_decode($input, true);
foreach($input_json['videos'] as $element) {
if($this->getInput('exclude_trailers') && $element['platform'] == 'EXTRAIT') {
continue;
}
$durationSeconds = $element['durationSeconds'];
$item = array();
$item['uri'] = $element['url'];
@@ -107,10 +144,10 @@ class Arte7Bridge extends BridgeAbstract {
if(!empty($element['subtitle']))
$item['title'] = $element['title'] . ' | ' . $element['subtitle'];
$item['duration'] = round((int)$element['durationSeconds'] / 60);
$durationMinutes = round((int)$durationSeconds / 60);
$item['content'] = $element['teaserText']
. '<br><br>'
. $item['duration']
. $durationMinutes
. 'min<br><a href="'
. $item['uri']
. '"><img src="'

View File

@@ -20,13 +20,14 @@ class AsahiShimbunAJWBridge extends BridgeAbstract {
'Culture » Style' => 'culture/style',
'Culture » Movies' => 'culture/movies',
'Culture » Manga & Anime' => 'culture/manga_anime',
'Asia » China' => 'asia/china',
'Asia » Korean Peninsula' => 'asia/korean_peninsula',
'Asia » Around Asia' => 'asia/around_asia',
'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',
'defaultValue' => 'politics',
)
)
);
@@ -36,8 +37,7 @@ class AsahiShimbunAJWBridge extends BridgeAbstract {
}
public function collectData() {
$html = getSimpleHTMLDOM($this->getSectionURI($this->getInput('section')))
or returnServerError('Could not load content');
$html = getSimpleHTMLDOM($this->getSectionURI($this->getInput('section')));
foreach($html->find('#MainInner li a') as $element) {
if ($element->parent()->class == 'HeadlineTopImage-S') {

View File

@@ -10,14 +10,14 @@ class AskfmBridge extends BridgeAbstract {
'Ask.fm username' => array(
'u' => array(
'name' => 'Username',
'required' => true
'required' => true,
'exampleValue' => 'ApprovedAndReal'
)
)
);
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Requested username can\'t be found.');
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, self::URI);

View File

@@ -0,0 +1,270 @@
<?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
private $detectParamRegex = '/^https?:\/\/(?:www\.)?apnews\.com\/(?:[tag|hub]+\/)?([\w-]+)$/';
private $tagEndpoint = 'https://afs-prod.appspot.com/api/v2/feed/tag?tags=';
private $feedName = '';
public function detectParameters($url) {
$params = array();
if(preg_match($this->detectParamRegex, $url, $matches) > 0) {
$params['topic'] = $matches[1];
$params['context'] = 'Custom Topic';
return $params;
}
return null;
}
public function collectData() {
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() {
if (!is_null($this->getInput('topic'))) {
return self::URI . $this->getInput('topic');
}
return parent::getURI();
}
public function getName() {
if (!empty($this->feedName)) {
return $this->feedName . ' - Associated Press';
}
return parent::getName();
}
private function getTagURI() {
if (!is_null($this->getInput('topic'))) {
return $this->tagEndpoint . $this->getInput('topic');
}
return parent::getURI();
}
private function collectCardData() {
$json = getContents($this->getTagURI())
or returnServerError('Could not request: ' . $this->getTagURI());
$tagContents = json_decode($json, true);
if (empty($tagContents['tagObjs'])) {
returnClientError('Topic not found: ' . $this->getInput('topic'));
}
$this->feedName = $tagContents['tagObjs'][0]['name'];
foreach ($tagContents['cards'] as $card) {
$item = array();
// skip hub peeks & Notifications
if ($card['cardType'] == 'Hub Peek' || $card['cardType'] == 'Notification') {
continue;
}
$storyContent = $card['contents'][0];
switch($storyContent['contentType']) {
case 'web': // Skip link only content
continue 2;
case 'video':
$html = $this->processVideo($storyContent);
$item['enclosures'][] = 'https://storage.googleapis.com/afs-prod/media/'
. $storyContent['media'][0]['id'] . '/800.jpeg';
break;
default:
if (empty($storyContent['storyHTML'])) { // Skip if no storyHTML
continue 2;
}
$html = defaultLinkTo($storyContent['storyHTML'], self::URI);
$html = str_get_html($html);
$this->processMediaPlaceholders($html, $storyContent['id']);
$this->processHubLinks($html, $storyContent);
$this->processIframes($html);
if (!is_null($storyContent['leadPhotoId'])) {
$item['enclosures'][] = 'https://storage.googleapis.com/afs-prod/media/'
. $storyContent['leadPhotoId'] . '/800.jpeg';
}
}
$item['title'] = $card['contents'][0]['headline'];
$item['uri'] = self::URI . $card['shortId'];
if ($card['contents'][0]['localLinkUrl']) {
$item['uri'] = $card['contents'][0]['localLinkUrl'];
}
$item['timestamp'] = $storyContent['published'];
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'];
}
}
$item['content'] = $html;
foreach ($storyContent['tagObjs'] as $tag) {
$item['categories'][] = $tag['name'];
}
$this->items[] = $item;
if (count($this->items) >= 15) {
break;
}
}
}
private function processMediaPlaceholders($html, $id) {
if ($html->find('div.media-placeholder', 0)) {
// 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) {
$key = array_search($div->id, $storyContent['mediumIds']);
if (!isset($storyContent['media'][$key])) {
continue;
}
$media = $storyContent['media'][$key];
if ($media['type'] === 'Photo') {
$mediaUrl = $media['gcsBaseUrl'] . $media['imageRenderedSizes'][0] . $media['imageFileExtension'];
$mediaCaption = $media['caption'];
$div->outertext = <<<EOD
<figure><img loading="lazy" src="{$mediaUrl}"/><figcaption>{$mediaCaption}</figcaption></figure>
EOD;
}
if ($media['type'] === 'YouTube') {
$div->outertext = <<<EOD
<iframe src="https://www.youtube.com/embed/{$media['externalId']}" width="560" height="315">
</iframe>
EOD;
}
}
}
}
/*
Create full coverage links (HubLinks)
*/
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 ($div) {
$div->outertext = <<<EOD
<p><a href="{$url}">{$embed['calloutText']} {$embed['displayName']}</a></p>
EOD;
}
}
}
}
}
private function processVideo($storyContent) {
$video = $storyContent['media'][0];
if ($video['type'] === 'YouTube') {
$url = 'https://www.youtube.com/embed/' . $video['externalId'];
$html = <<<EOD
<iframe width="560" height="315" src="{$url}" frameborder="0" allowfullscreen></iframe>
EOD;
} else {
$html = <<<EOD
<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">
</video>
EOD;
}
return $html;
}
// Remove datawrapper.dwcdn.net iframes and related javaScript
private function processIframes($html) {
foreach ($html->find('iframe') as $index => $iframe) {
if (preg_match('/datawrapper\.dwcdn\.net/', $iframe->src)) {
$iframe->outertext = '';
if ($html->find('script', $index)) {
$html->find('script', $index)->outertext = '';
}
}
}
}
}

View File

@@ -29,8 +29,7 @@ class AtmoNouvelleAquitaineBridge extends BridgeAbstract {
public function collectData() {
$uri = self::URI . '/monair/commune/' . $this->getInput('cities');
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request ' . $uri);
$html = getSimpleHTMLDOM($uri);
$this->dom = $html->find('#block-system-main .city-prevision-map', 0);

View File

@@ -8,7 +8,8 @@ class AtmoOccitanieBridge extends BridgeAbstract {
const PARAMETERS = array(array(
'city' => array(
'name' => 'Ville',
'required' => true
'required' => true,
'exampleValue' => 'cahors'
)
));
const CACHE_TIMEOUT = 7200;
@@ -16,8 +17,7 @@ class AtmoOccitanieBridge extends BridgeAbstract {
public function collectData() {
$uri = self::URI . $this->getInput('city');
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request ' . $uri);
$html = getSimpleHTMLDOM($uri);
$generalMessage = $html->find('.landing-ville .city-banner .iqa-avertissement', 0)->innertext;
$recommendationsDom = $html->find('.landing-ville .recommandations', 0);

View File

@@ -7,51 +7,14 @@ class AutoJMBridge extends BridgeAbstract {
const DESCRIPTION = 'Suivre les offres de véhicules proposés par AutoJM en fonction des critères de filtrages';
const MAINTAINER = 'sysadminstory';
const PARAMETERS = array(
'Afficher les offres de véhicules disponible en fonction des critères du site AutoJM' => array(
'Afficher les offres de véhicules disponible sur la recheche AutoJM' => array(
'url' => array(
'name' => 'URL du modèle',
'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' => 'achat-voitures-neuves-peugeot-nouvelle-308-5p'
'exampleValue' => 'recherche?brands[]=peugeot&ranges[]=peugeot-nouvelle-308-2021-5p'
),
'energy' => array(
'name' => 'Carburant',
'type' => 'list',
'values' => array(
'-' => '',
'Diesel' => 1,
'Essence' => 3,
'Hybride' => 5
),
'title' => 'Carburant'
),
'transmission' => array(
'name' => 'Transmission',
'type' => 'list',
'values' => array(
'-' => '',
'Automatique' => 1,
'Manuelle' => 2
),
'title' => 'Transmission'
),
'priceMin' => array(
'name' => 'Prix minimum',
'type' => 'number',
'required' => false,
'title' => 'Prix minimum du véhicule',
'exampleValue' => '10000',
'defaultValue' => '0'
),
'priceMax' => array(
'name' => 'Prix maximum',
'type' => 'number',
'required' => false,
'title' => 'Prix maximum du véhicule',
'exampleValue' => '15000',
'defaultValue' => '150000'
)
)
);
const CACHE_TIMEOUT = 3600;
@@ -62,10 +25,8 @@ class AutoJMBridge extends BridgeAbstract {
public function getName() {
switch($this->queriedContext) {
case 'Afficher les offres de véhicules disponible en fonction des critères du site AutoJM':
$html = getSimpleHTMLDOMCached(self::URI . $this->getInput('url'), 86400);
$name = html_entity_decode($html->find('title', 0)->plaintext);
return $name;
case 'Afficher les offres de véhicules disponible sur la recheche AutoJM':
return 'AutoJM | Recherche de véhicules';
break;
default:
return parent::getName();
@@ -75,13 +36,8 @@ class AutoJMBridge extends BridgeAbstract {
public function collectData() {
$model_url = self::URI . $this->getInput('url');
// Build the GET data
$get_data = 'form[energy]=' . $this->getInput('energy') .
'&form[transmission]=' . $this->getInput('transmission') .
'&form[priceMin]=' . $this->getInput('priceMin') .
'&form[priceMin]=' . $this->getInput('priceMin');
// Get the number of result for this search
$search_url = self::URI . $this->getInput('url') . '&open=energy&onlyFilters=false';
// Set the header 'X-Requested-With' like the website does it
$header = array(
@@ -89,57 +45,91 @@ class AutoJMBridge extends BridgeAbstract {
);
// Get the JSON content of the form
$json = getContents($model_url . '?' . $get_data, $header)
or returnServerError('Could not request AutoJM.');
$json = getContents($search_url, $header);
// Extract the HTML content from the JSON result
$data = json_decode($json);
$html = str_get_html($data->results);
// Go through every car of the model
$list = $html->find('div[class=car-card]');
foreach ($list as $car) {
$nb_results = $data->nbResults;
$total_pages = ceil($nb_results / 15);
// Get the Finish name if this car is the first of a new finish
$prev_tag = $car->prev_sibling();
if($prev_tag->tag == 'div' && $prev_tag->class == 'results-title') {
$finish_name = $prev_tag->plaintext;
// Limit the number of page to analyse to 10
for($page = 1; $page <= $total_pages && $page <= 10; $page++) {
// Get the result the next page
$html = $this->getResults($page);
// Go through every car of the search
$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 = '';
}
// Construct the new item
$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;
}
// Get the info about the car offer
$image = $car->find('div[class=car-card__visual]', 0)->find('img', 0)->src;
$serie = $car->find('div[class=car-card__title]', 0)->plaintext;
$url = $car->find('a', 0)->href;
// Check if the car model is in stock or available only on order
if($car->find('span[class*=tag--dispo]', 0) != null) {
$availability = 'En Stock';
} else {
$availability = 'Sur commande';
}
$discount_html = $car->find('span[class=promo]', 0);
// Check if there is any discount dsiplayed
if ($discount_html != null) {
$discount = $discount_html->plaintext;
} else {
$discount = 'inconnue';
}
$price = $car->find('span[class=price]', 0)->plaintext;
// Construct the new item
$item = array();
$item['title'] = $finish_name . ' ' . $serie;
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />'
. $finish_name . ' ' . $serie . '</p>';
$item['content'] .= '<ul><li>Disponibilité : ' . $availability . '</li>';
$item['content'] .= '<li>Série : ' . $serie . '</li>';
$item['content'] .= '<li>Remise : ' . $discount . '</li>';
$item['content'] .= '<li>Prix : ' . $price . '</li></ul>';
// Add a fictionnal anchor to the RSS element URL, based on the item content ;
// As the URL could be identical even if the price change, some RSS reader will not show those offers as new items
$item['uri'] = $url . '#' . 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

@@ -18,8 +18,7 @@ class AwwwardsBridge extends BridgeAbstract {
private function fetchSites() {
Debug::log('Fetching all sites');
$sites = getSimpleHTMLDOM(self::SITESURI)
or returnServerError('Could not fetch JSON for sites.');
$sites = getSimpleHTMLDOM(self::SITESURI);
Debug::log('Parsing all JSON data');
foreach($sites->find('li[data-model]') as $site) {

View File

@@ -220,8 +220,7 @@ class BadDragonBridge extends BridgeAbstract {
public function collectData() {
switch($this->queriedContext) {
case 'Sales':
$sales = json_decode(getContents(self::URI . 'api/sales'))
or returnServerError('Failed to query BD API');
$sales = json_decode(getContents(self::URI . 'api/sales'));
foreach($sales as $sale) {
$item = array();
@@ -274,12 +273,10 @@ class BadDragonBridge extends BridgeAbstract {
}
break;
case 'Clearance':
$toyData = json_decode(getContents($this->inputToURL(true)))
or returnServerError('Failed to query BD API');
$toyData = json_decode(getContents($this->inputToURL(true)));
$productList = json_decode(getContents(self::URI
. 'api/inventory-toy/product-list'))
or returnServerError('Failed to query BD API');
. 'api/inventory-toy/product-list'));
foreach($toyData->toys as $toy) {
$item = array();

View File

@@ -3,23 +3,80 @@ 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';
const PARAMETERS = array(array(
'series_id' => array(
'name' => 'Series ID',
'type' => 'number',
'required' => true,
'exampleValue' => '12345'
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 = '';
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Series not found');
if($this -> queriedContext == 'By series')
$this -> collectDataBySeries();
else //queriedContext == 'By list'
$this -> collectDataByList();
}
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;
return self::URI;
}
public function getName(){
if(!empty($this->feedName)) {
return $this->feedName . ' - ' . self::NAME;
}
return parent::getName();
}
private function getSanitizedHash($string) {
return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string))));
}
private function filterText($text) {
return rtrim($text, '* ');
}
private function filterHTML($text) {
return $this->filterText(html_entity_decode($text));
}
private function findID($manga) {
// 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;
}
private function collectDataBySeries() {
$html = getSimpleHTMLDOM($this->getURI());
// content is an unstructured pile of divs, ugly to parse
$cols = $html->find('div#main_content div.row > div.text');
@@ -68,36 +125,62 @@ class BakaUpdatesMangaReleasesBridge extends BridgeAbstract {
$item['title'] = implode(' ', $title);
$item['uri'] = $this->getURI();
$item['uid'] = $this->getSanitizedHash($item['title']);
$item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']);
$this->items[] = $item;
}
}
public function getURI(){
$series_id = $this->getInput('series_id');
if (!empty($series_id)) {
return self::URI . 'releases.html?search=' . $series_id . '&stype=series';
private function collectDataByList() {
$this -> feedName = 'Releases';
$list = array();
$releasesHTML = getSimpleHTMLDOM(self::RELEASES_URL);
$list_id = $this -> getInput('list_id');
$listHTML = getSimpleHTMLDOM('https://www.mangaupdates.com/mylist.html?id=' . $list_id);
//get ids of the manga that the user follows,
$parts = $listHTML -> find('table#ptable tr > td.pl');
foreach($parts as $part) {
$list[] = $this -> findID($part);
}
return self::URI;
}
public function getName(){
if(!empty($this->feedName)) {
return $this->feedName . ' - ' . self::NAME;
//similar to above, but the divs are in groups of 3.
$cols = $releasesHTML -> find('div#main_content div.row > div.pbreak');
$rows = array_slice(array_chunk($cols, 3), 0);
foreach($rows as $cols) {
//check if current manga is in user's list.
$id = $this -> findId($cols[0]);
if(!array_search($id, $list)) continue;
$item = array();
$title = array();
$item['content'] = '';
$objTitle = $cols[0];
if ($objTitle) {
$title[] = $this->filterHTML($objTitle->plaintext);
$item['content'] .= '<p>Series: ' . $this->filterHTML($objTitle -> innertext) . '</p>';
}
$objVolChap = $cols[1];
if ($objVolChap && !empty($objVolChap->plaintext))
$title[] = $this -> filterHTML($objVolChap -> innertext);
$objAuthor = $cols[2];
if ($objAuthor && !empty($objAuthor->plaintext)) {
$item['author'] = $this->filterHTML($objAuthor -> plaintext);
$item['content'] .= '<p>Groups: ' . $this->filterHTML($objAuthor -> innertext) . '</p>';
}
$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;
}
return parent::getName();
}
private function getSanitizedHash($string) {
return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string))));
}
private function filterText($text) {
return rtrim($text, '* ');
}
private function filterHTML($text) {
return $this->filterText(html_entity_decode($text));
}
}

View File

@@ -11,7 +11,8 @@ class BandcampBridge extends BridgeAbstract {
'tag' => array(
'name' => 'tag',
'type' => 'text',
'required' => true
'required' => true,
'exampleValue' => 'hip-hop-rap'
)
),
'By band' => array(
@@ -19,6 +20,32 @@ class BandcampBridge extends BridgeAbstract {
'name' => 'band',
'type' => 'text',
'title' => 'Band name as seen in the band page URL',
'required' => true,
'exampleValue' => 'aesoprock'
),
'type' => array(
'name' => 'Articles are',
'type' => 'list',
'values' => array(
'Releases' => 'releases',
'Releases, new one when track list changes' => 'changes',
'Individual tracks' => 'tracks'
),
'defaultValue' => 'changes'
),
'limit' => array(
'name' => 'limit',
'type' => 'number',
'required' => true,
'title' => 'Number of releases to return',
'defaultValue' => 5
)
),
'By label' => array(
'label' => array(
'name' => 'label',
'type' => 'text',
'title' => 'label name as seen in the label page URL',
'required' => true
),
'type' => array(
@@ -43,13 +70,15 @@ class BandcampBridge extends BridgeAbstract {
'name' => 'band',
'type' => 'text',
'title' => 'Band name as seen in the album page URL',
'required' => true
'required' => true,
'exampleValue' => 'aesoprock'
),
'album' => array(
'name' => 'album',
'type' => 'text',
'title' => 'Album name as seen in the album page URL',
'required' => true
'required' => true,
'exampleValue' => 'appleseed'
),
'type' => array(
'name' => 'Articles are',
@@ -86,8 +115,7 @@ class BandcampBridge extends BridgeAbstract {
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $data
);
$content = getContents($url, $header, $opts)
or returnServerError('Could not complete request to: ' . $url);
$content = getContents($url, $header, $opts);
$json = json_decode($content);
@@ -122,12 +150,15 @@ class BandcampBridge extends BridgeAbstract {
}
break;
case 'By band':
case 'By label':
case 'By album':
$html = getSimpleHTMLDOMCached($this->getURI(), 86400);
$titleElement = $html->find('head meta[name=title]', 0)
or returnServerError('Unable to find title on: ' . $this->getURI());
$this->feedName = $titleElement->content;
if ($html->find('meta[name=title]', 0)) {
$this->feedName = $html->find('meta[name=title]', 0)->content;
} else {
$this->feedName = str_replace('Music | ', '', $html->find('title', 0)->plaintext);
}
$regex = '/band_id=(\d+)/';
if(preg_match($regex, $html, $matches) == false)
@@ -137,6 +168,7 @@ class BandcampBridge extends BridgeAbstract {
$tralbums = array();
switch($this->queriedContext) {
case 'By band':
case 'By label':
$query_data = array(
'band_id' => $band_id
);
@@ -272,8 +304,7 @@ class BandcampBridge extends BridgeAbstract {
private function apiGet($endpoint, $query_data) {
$url = self::URI . 'api/' . $endpoint . '?' . http_build_query($query_data);
$data = json_decode(getContents($url))
or returnServerError('API request to "' . $url . '" failed.');
$data = json_decode(getContents($url));
return $data;
}
@@ -287,6 +318,13 @@ class BandcampBridge extends BridgeAbstract {
. '?sort_field=date';
}
break;
case 'By label':
if(!is_null($this->getInput('label'))) {
return 'https://'
. $this->getInput('label')
. '.bandcamp.com/music';
}
break;
case 'By band':
if(!is_null($this->getInput('band'))) {
return 'https://'
@@ -321,6 +359,13 @@ class BandcampBridge extends BridgeAbstract {
return $this->getInput('band') . ' - Bandcamp Band';
}
break;
case 'By label':
if(isset($this->feedName)) {
return $this->feedName . ' - Bandcamp Label';
} elseif(!is_null($this->getInput('label'))) {
return $this->getInput('label') . ' - Bandcamp Label';
}
break;
case 'By album':
if(isset($this->feedName)) {
return $this->feedName . ' - Bandcamp Album';

View File

@@ -0,0 +1,160 @@
<?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
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());
$html = defaultLinkTo($html, self::URI);
$articles = $html->find('articles-list', 0);
foreach($articles->find('div.list-article') as $index => $article) {
$item = array();
$articlePath = $article->find('a.title', 0)->href;
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, 3600)
or returnServerError('Could not request: ' . $articlePath);
$item['uri'] = $articlePath;
$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;
if ($articlePageHtml->find('meta[property="article:tag"]', 0)) {
$item['categories'][] = $articlePageHtml->find('meta[property="article:tag"]', 0)->content;
}
$item['enclosures'][] = $articlePageHtml->find('meta[name="twitter:image"]', 0)->content;
$this->items[] = $item;
if (count($this->items) >= 10) {
break;
}
}
}
public function getURI() {
switch($this->queriedContext) {
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() {
switch($this->queriedContext) {
case 'Latest articles':
return $this->queriedContext . ' - Bandcamp Daily';
case 'Best of':
case 'Genres':
case 'Franchises':
// TODO Switch to array_key_first once php >= 7.3
$contentKey = key(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

@@ -8,8 +8,7 @@ class BastaBridge extends BridgeAbstract {
const DESCRIPTION = 'Returns the newest articles.';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend')
or returnServerError('Could not request Bastamag.');
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend');
$limit = 0;
@@ -19,13 +18,11 @@ class BastaBridge extends BridgeAbstract {
$item['title'] = $element->find('title', 0)->innertext;
$item['uri'] = $element->find('guid', 0)->plaintext;
$item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext);
// Replaces all relative image URLs by absolute URLs.
// Relative URLs always start with 'local/'!
$item['content'] = preg_replace(
'/src=["\']{1}([^"\']+)/ims',
'src=\'' . self::URI . '$1\'',
getSimpleHTMLDOM($item['uri'])->find('div.texte', 0)->innertext
);
$html = getSimpleHTMLDOM($item['uri']);
$html = defaultLinkTo($html, self::URI);
$item['content'] = $html->find('div.texte', 0)->innertext;
$this->items[] = $item;
$limit++;
}

View File

@@ -1,62 +1,34 @@
<?php
class BinanceBridge extends BridgeAbstract {
const NAME = 'Binance';
const URI = 'https://www.binance.com';
const DESCRIPTION = 'Subscribe to the Binance blog or the Binance Zendesk announcements.';
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
const PARAMETERS = array( array(
'category' => array(
'name' => 'category',
'type' => 'list',
'exampleValue' => 'Blog',
'title' => 'Select a category',
'values' => array(
'Blog' => 'Blog',
'Announcements' => 'Announcements'
)
)
));
public function getIcon() {
return 'https://bin.bnbstatic.com/static/images/common/favicon.ico';
}
public function getName() {
return self::NAME . ' ' . $this->getInput('category');
}
public function getURI() {
if ($this->getInput('category') == 'Blog')
return self::URI . '/en/blog';
else
return 'https://binance.zendesk.com/hc/en-us/categories/115000056351-Announcements';
}
protected function collectBlogData() {
$html = getSimpleHTMLDOM($this->getURI())
public function collectData() {
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not fetch Binance blog data.');
foreach($html->find('div[direction="row"]') as $element) {
$appData = $html->find('script[id="__APP_DATA"]');
$appDataJson = json_decode($appData[0]->innertext);
$date = $element->find('div[direction="column"]', 0);
$day = $date->find('div', 0)->innertext;
$month = $date->find('div', 1)->innertext;
$extractedDate = $day . ' ' . $month;
foreach($appDataJson->pageData->redux->blogList->blogList as $element) {
$abstract = $element->find('div[direction="column"]', 1);
$a = $abstract->find('a', 0);
$uri = self::URI . $a->href;
$title = $a->innertext;
$full = getSimpleHTMLDOMCached($uri);
$content = $full->find('div.desc', 1);
$date = $element->postTime;
$abstract = $element->brief;
$uri = self::URI . '/' . $element->lang . '/blog/' . $element->idStr;
$title = $element->title;
$content = $element->content;
$item = array();
$item['title'] = $title;
$item['uri'] = $uri;
$item['timestamp'] = strtotime($extractedDate);
$item['timestamp'] = substr($date, 0, -3);
$item['author'] = 'Binance';
$item['content'] = $content;
@@ -66,38 +38,4 @@ class BinanceBridge extends BridgeAbstract {
break;
}
}
protected function collectAnnouncementData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not fetch Zendesk announcement data.');
foreach($html->find('a.article-list-link') as $a) {
$title = $a->innertext;
$uri = 'https://binance.zendesk.com' . $a->href;
$full = getSimpleHTMLDOMCached($uri);
$content = $full->find('div.article-body', 0);
$date = $full->find('time', 0)->getAttribute('datetime');
$item = array();
$item['title'] = $title;
$item['uri'] = $uri;
$item['timestamp'] = strtotime($date);
$item['author'] = 'Binance';
$item['content'] = $content;
$this->items[] = $item;
if (count($this->items) >= 10)
break;
}
}
public function collectData() {
if ($this->getInput('category') == 'Blog')
$this->collectBlogData();
else
$this->collectAnnouncementData();
}
}

View File

@@ -1,119 +0,0 @@
<?php
class BingSearchBridge extends BridgeAbstract
{
const NAME = 'Bing search';
const URI = 'https://www.bing.com/';
const DESCRIPTION = 'Return images from bing search discover';
const MAINTAINER = 'DnAp';
const PARAMETERS = array(
'Image Discover' => array(
'category' => array(
'name' => 'Categories',
'type' => 'list',
'values' => self::IMAGE_DISCOVER_CATEGORIES
),
'image_size' => array(
'name' => 'Image size',
'type' => 'list',
'values' => array(
'Small' => 'turl',
'Full size' => 'imgurl'
)
)
)
);
const IMAGE_DISCOVER_CATEGORIES = array(
'Abstract' => 'abstract',
'Animals' => 'animals',
'Anime' => 'anime',
'Architecture' => 'architecture',
'Arts and Crafts' => 'arts-and-crafts',
'Beauty' => 'beauty',
'Cars and Motorcycles' => 'cars-and-motorcycles',
'Cats' => 'cats',
'Celebrities' => 'celebrities',
'Comics' => 'comics',
'DIY' => 'diy',
'Dogs' => 'dogs',
'Fitness' => 'fitness',
'Food and Drink' => 'food-and-drink',
'Funny' => 'funny',
'Gadgets' => 'gadgets',
'Gardening' => 'gardening',
'Geeky' => 'geeky',
'Hairstyles' => 'hairstyles',
'Home Decor' => 'home-decor',
'Marine Life' => 'marine-life',
'Men\'s Fashion' => 'men%27s-fashion',
'Nature' => 'nature',
'Outdoors' => 'outdoors',
'Parenting' => 'parenting',
'Phone Wallpapers' => 'phone-wallpapers',
'Photography' => 'photography',
'Quotes' => 'quotes',
'Recipes' => 'recipes',
'Snow' => 'snow',
'Tattoos' => 'tattoos',
'Travel' => 'travel',
'Video Games' => 'video-games',
'Weddings' => 'weddings',
'Women\'s Fashion' => 'women%27s-fashion',
);
public function getIcon()
{
return 'https://www.bing.com/sa/simg/bing_p_rr_teal_min.ico';
}
public function collectData()
{
$this->items = $this->imageDiscover($this->getInput('category'));
}
public function getName()
{
if ($this->getInput('category')) {
if (self::IMAGE_DISCOVER_CATEGORIES[$this->getInput('categories')] !== null) {
$category = self::IMAGE_DISCOVER_CATEGORIES[$this->getInput('categories')];
} else {
$category = 'Unknown';
}
return 'Best ' . $category . ' - Bing Image Discover';
}
return parent::getName();
}
private function imageDiscover($category)
{
$html = getSimpleHTMLDOM(self::URI . '/discover/' . $category)
or returnServerError('Could not request ' . self::NAME);
$sizeKey = $this->getInput('image_size');
$items = array();
foreach ($html->find('a.iusc') as $element) {
$data = json_decode(htmlspecialchars_decode($element->getAttribute('m')), true);
$item = array();
$item['title'] = basename(rtrim($data['imgurl'], '/'));
$item['uri'] = $data['imgurl'];
$item['content'] = '<a href="' . $data['imgurl'] . '">
<img src="' . $data[$sizeKey] . '" alt="' . $item['title'] . '"></a>
<p>Source: <a href="' . $this->curUrl($data['surl']) . '"> </a></p>';
$item['enclosures'] = $data['imgurl'];
$items[] = $item;
}
return $items;
}
private function curUrl($url)
{
if (strlen($url) <= 80) {
return $url;
}
return substr($url, 0, 80) . '...';
}
}

View File

@@ -13,8 +13,7 @@ class BlaguesDeMerdeBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request BDM.');
$html = getSimpleHTMLDOM(self::URI);
foreach($html->find('div.blague') as $element) {

1458
bridges/BookMyShowBridge.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,61 @@
<?php
require_once('GelbooruBridge.php');
class BooruprojectBridge extends GelbooruBridge {
class BooruprojectBridge extends DanbooruBridge {
const MAINTAINER = 'mitsukarenai';
const NAME = 'Booruproject';
const URI = 'http://booru.org/';
const URI = 'https://booru.org/';
const DESCRIPTION = 'Returns images from given page of booruproject';
const PARAMETERS = array(
'global' => array(
'p' => array(
'name' => 'page',
'defaultValue' => 0,
'type' => 'number'
),
't' => array(
'name' => 'tags'
'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
'required' => true,
'exampleValue' => 'rm'
)
)
);
const PATHTODATA = '.thumb';
const IDATTRIBUTE = 'id';
const TAGATTRIBUTE = 'title';
const PIDBYPAGE = 20;
protected function getFullURI(){
return $this->getURI()
. 'index.php?page=post&s=list&pid='
. ($this->getInput('p') ? ($this->getInput('p') - 1) * static::PIDBYPAGE : '')
. '&tags=' . urlencode($this->getInput('t'));
}
protected function getTags($element){
$tags = parent::getTags($element);
$tags = explode(' ', $tags);
// Remove statistics from the tags list (identified by colon)
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 'http://' . $this->getInput('i') . '.booru.org/';
return 'https://' . $this->getInput('i') . '.booru.org/';
}
return parent::getURI();

View File

@@ -2,7 +2,7 @@
class BrutBridge extends BridgeAbstract {
const NAME = 'Brut Bridge';
const URI = 'https://www.brut.media';
const DESCRIPTION = 'Returns 5 newest videos by category and edition';
const DESCRIPTION = 'Returns 10 newest videos by category and edition';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(array(
'category' => array(
@@ -38,48 +38,46 @@ class BrutBridge extends BridgeAbstract {
const CACHE_TIMEOUT = 1800; // 30 mins
private $videoId = '';
private $videoType = '';
private $videoImage = '';
private $jsonRegex = '/window\.__PRELOADED_STATE__ = ((?:.*)});/';
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$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 $index => $li) {
foreach($results->find('li.col-6.col-sm-4.col-md-3.col-lg-2.px-2.pb-4') as $li) {
$item = array();
$videoPath = self::URI . $li->children(0)->href;
$videoPageHtml = getSimpleHTMLDOMCached($videoPath, 3600);
$videoPageHtml = getSimpleHTMLDOMCached($videoPath, 3600)
or returnServerError('Could not request: ' . $videoPath);
$this->videoImage = $videoPageHtml->find('meta[name="twitter:image"]', 0)->content;
$this->processTwitterImage();
$description = $videoPageHtml->find('div.description', 0);
$json = $this->extractJson($videoPageHtml);
$id = array_keys((array) $json->media->index)[0];
$item['uri'] = $videoPath;
$item['title'] = $description->find('h1', 0)->plaintext;
$item['title'] = $json->media->index->$id->title;
$item['timestamp'] = $json->media->index->$id->published_at;
$item['enclosures'][] = $json->media->index->$id->media->thumbnail;
if ($description->find('div.date', 0)->children(0)) {
$description->find('div.date', 0)->children(0)->outertext = '';
$description = $json->media->index->$id->description;
$article = '';
if (is_null($json->media->index->$id->media->seo_article) === false) {
$article = markdownToHtml($json->media->index->$id->media->seo_article);
}
$item['content'] = $this->processContent(
$description
);
$item['timestamp'] = $this->processDate($description);
$item['enclosures'][] = $this->videoImage;
$item['content'] = <<<EOD
<video controls poster="{$json->media->index->$id->media->thumbnail}" preload="none">
<source src="{$json->media->index->$id->media->mp4_url}" type="video/mp4">
</video>
<p>{$description}</p>
{$article}
EOD;
$this->items[] = $item;
if (count($this->items) >= 5) {
if (count($this->items) >= 10) {
break;
}
}
@@ -109,51 +107,21 @@ class BrutBridge extends BridgeAbstract {
return parent::getName();
}
private function processDate($description) {
/**
* Extract JSON from page
*/
private function extractJson($html) {
if ($this->getInput('edition') === 'uk') {
$date = DateTime::createFromFormat('d/m/Y H:i', $description->find('div.date', 0)->innertext);
return strtotime($date->format('Y-m-d H:i:s'));
if (!preg_match($this->jsonRegex, $html, $parts)) {
returnServerError('Failed to extract data from page');
}
return strtotime($description->find('div.date', 0)->innertext);
}
$data = json_decode($parts[1]);
private function processContent($description) {
$content = '<video controls poster="' . $this->videoImage . '" preload="none">
<source src="https://content.brut.media/video/' . $this->videoId . '-' . $this->videoType . '-web.mp4"
type="video/mp4">
</video>';
$content .= '<p>' . $description->find('h2.mb-1', 0)->innertext . '</p>';
if ($description->find('div.text.pb-3', 0)->children(1)->class != 'date') {
$content .= '<p>' . $description->find('div.text.pb-3', 0)->children(1)->innertext . '</p>';
if ($data === false) {
returnServerError('Failed to decode extracted data');
}
return $content;
}
private function processTwitterImage() {
/**
* Extract video ID + type from twitter image
*
* Example (wrapped):
* https://img.brut.media/thumbnail/
* the-life-of-rita-moreno-2cce75b5-d448-44d2-a97c-ca50d6470dd4-square.jpg
* ?ts=1559337892
*/
$fpath = parse_url($this->videoImage, PHP_URL_PATH);
$fname = basename($fpath);
$fname = substr($fname, 0, strrpos($fname, '.'));
$parts = explode('-', $fname);
if (end($parts) === 'auto') {
$key = array_search('auto', $parts);
unset($parts[$key]);
}
$this->videoId = implode('-', array_splice($parts, -6, 5));
$this->videoType = end($parts);
return $data;
}
}

218
bridges/BukowskisBridge.php Normal file
View File

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

View File

@@ -41,8 +41,7 @@ class BundesbankBridge extends BridgeAbstract {
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('No response for ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());

View File

@@ -0,0 +1,89 @@
<?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
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>Spendenbetrag:</b><br>%s</p>
<p><b>Spender:</b><br>%s</p>
<p><b>Eingang der Spende:</b><br>%s</p>
TMPL;
public function getIcon()
{
return 'https://www.bundestag.de/static/appdata/includes/images/layout/favicon.ico';
}
public function collectData()
{
$ajaxUri = <<<URI
https://www.bundestag.de/ajax/filterlist/de/parlament/praesidium/parteienfinanzierung/fundstellen50000/462002-462002
URI;
// Get the main page
$html = getSimpleHTMLDOMCached($ajaxUri, self::CACHE_TIMEOUT)
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.
$firstAnchor = $html->find('a', 0)
or returnServerError('Could not find the proper HTML element.');
$url = 'https://www.bundestag.de' . $firstAnchor->href;
// Get the actual page with the soft money donations
$html = getSimpleHTMLDOMCached($url, self::CACHE_TIMEOUT)
or returnServerError('Could not request ' . $url);
$rows = $html->find('table.table > tbody > tr')
or returnServerError('Could not find the proper HTML elements.');
foreach($rows as $row) {
$item = $this->generateItemFromRow($row);
if (is_array($item)) {
$item['uri'] = $url;
$this->items[] = $item;
}
}
}
private function generateItemFromRow(simple_html_dom_node $row)
{
// The row must have 5 columns. There are monthly header rows, which are ignored here.
if(count($row->children) != 5)
return null;
$item = array();
// | column | paragraph inside column
$party = $row->children[0]->children[0]->innertext;
$amount = $row->children[1]->children[0]->innertext . ' €';
$donor = $row->children[2]->children[0]->innertext;
$date = $row->children[3]->children[0]->innertext;
$dip = $row->children[4]->children[0]->find('a.dipLink', 0);
// Strip whitespace from date string.
$date = str_replace(' ', '', $date);
$content = sprintf(self::CONTENT_TEMPLATE, $party, $amount, $donor, $date);
$item = array(
'title' => $party . ': ' . $amount,
'content' => $content,
'uid' => sha1($content),
);
// Try to get the link to the official document
if ($dip != null)
$item['enclosures'] = array($dip->href);
// Try to parse the date
$dateTime = DateTime::createFromFormat('d.m.Y', $date);
if ($dateTime !== false)
$item['timestamp'] = $dateTime->getTimestamp();
return $item;
}
}

View File

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

View File

@@ -53,8 +53,7 @@ class CNETBridge extends BridgeAbstract {
// Retrieve webpage
$pageUrl = self::URI . (empty($topic) ? 'news/' : $topic . '/');
$html = getSimpleHTMLDOM($pageUrl)
or returnServerError('Could not request CNET: ' . $pageUrl);
$html = getSimpleHTMLDOM($pageUrl);
// Process articles
foreach($html->find('div.assetBody, div.riverPost') as $element) {

View File

@@ -12,13 +12,13 @@ class CNETFranceBridge extends FeedExpander
'name' => 'Exclude by title',
'required' => false,
'title' => 'Title term, separated by semicolon (;)',
'defaultValue' => '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(
'name' => 'Exclude by url',
'required' => false,
'title' => 'URL term, separated by semicolon (;)',
'defaultValue' => 'bon-plan;bons-plans'
'exampleValue' => 'bon-plan;bons-plans'
)
)
);

View File

@@ -0,0 +1,140 @@
<?php
// CVE Details is a collection of CVEs, taken from the National Vulnerability
// Database (NVD) and other sources like the Exploit DB and Metasploit. The
// website categorizes it by vendor and product and attach the CWE category.
// There is a Atom feed available, but only logged in users can use it,
// 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
// a description of the vulnerability.
class CVEDetailsBridge extends BridgeAbstract {
const MAINTAINER = 'Aaron Fischer';
const NAME = 'CVE Details';
const CACHE_TIMEOUT = 60 * 60 * 6; // 6 hours
const DESCRIPTION = 'Report new CVE vulnerabilities for a given vendor (and product)';
const URI = 'https://www.cvedetails.com';
const PARAMETERS = array(array(
// The Vendor ID can be taken from the URL
'vendor_id' => array(
'name' => 'Vendor ID',
'type' => 'number',
'required' => true,
'exampleValue' => 74, // PHP
),
// The optional Product ID can be taken from the URL as well
'product_id' => array(
'name' => 'Product ID',
'type' => 'number',
'required' => false,
'exampleValue' => 128, // PHP
),
));
private $html = null;
private $vendor = '';
private $product = '';
// Return the URL to query.
// 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
// without the product ID).
private function _buildURL() {
$url = self::URI . '/vulnerability-list/vendor_id-' . $this->getInput('vendor_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
// number, which should be mostly accurate.
$url .= '?order=1'; // Order by CVE number DESC
return $url;
}
// Make the actual request to cvedetails.com and stores the response
// (HTML) for later use and extract vendor and product from it.
private function _fetchContent() {
$html = getSimpleHTMLDOM($this->_buildURL());
$this->html = defaultLinkTo($html, self::URI);
$vendor = $html->find('#contentdiv > h1 > a', 0);
if ($vendor == null) {
returnServerError('Invalid Vendor ID ' .
$this->getInput('vendor_id') .
' or Product ID ' .
$this->getInput('product_id'));
}
$this->vendor = $vendor->innertext;
$product = $html->find('#contentdiv > h1 > a', 1);
if ($product != null) {
$this->product = $product->innertext;
}
}
// Build the name of the feed.
public function getName() {
if ($this->getInput('vendor_id') == '') {
return self::NAME;
}
if ($this->html == null) {
$this->_fetchContent();
}
$name = 'CVE Vulnerabilities for ' . $this->vendor;
if ($this->product != '') {
$name .= '/' . $this->product;
}
return $name;
}
// Pull the data from the HTML response and fill the items..
public function collectData() {
if ($this->html == null) {
$this->_fetchContent();
}
foreach ($this->html->find('#vulnslisttable .srrowns') as $i => $tr) {
// There are some optional vulnerability types, which will be
// added to the categories as well as the CWE number -- which is
// always given.
$categories = array($this->vendor);
$enclosures = array();
$cwe = $tr->find('td', 2)->find('a', 0);
if ($cwe != null) {
$cwe = $cwe->innertext;
$categories[] = 'CWE-' . $cwe;
$enclosures[] = 'https://cwe.mitre.org/data/definitions/' . $cwe . '.html';
}
$c = $tr->find('td', 4)->innertext;
if (trim($c) != '') {
$categories[] = $c;
}
if ($this->product != '') {
$categories[] = $this->product;
}
// The CVE number itself
$title = $tr->find('td', 1)->find('a', 0)->innertext;
$this->items[] = array(
'uri' => $tr->find('td', 1)->find('a', 0)->href,
'title' => $title,
'timestamp' => $tr->find('td', 5)->innertext,
'content' => $tr->next_sibling()->innertext,
'categories' => $categories,
'enclosures' => $enclosures,
'uid' => $tr->find('td', 1)->find('a', 0)->innertext,
);
// We only want to fetch the latest 10 CVEs
if (count($this->items) >= 10) {
break;
}
}
}
}

View File

@@ -0,0 +1,41 @@
<?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() {
$this->collectExpandableDatas('https://www.carthrottle.com/rss', 10);
}
protected function parseItem($feedItem) {
$item = parent::parseItem($feedItem);
//fetch page
$articlePage = getSimpleHTMLDOMCached($feedItem->link)
or returnServerError('Could not retrieve ' . $feedItem->link);
$subtitle = $articlePage->find('p.standfirst', 0);
$article = $articlePage->find('div.content_field', 0);
$item['content'] = str_get_html($subtitle . $article);
//convert <iframe>s to <a>s. meant for embedded videos.
foreach($item['content']->find('iframe') as $found) {
$iframeUrl = $found->getAttribute('src');
if ($iframeUrl) {
$found->outertext = '<a href="' . $iframeUrl . '">' . $iframeUrl . '</a>';
}
}
//remove scripts from the text
foreach ($item['content']->find('script') as $remove) {
$remove->outertext = '';
}
return $item;
}
}

View File

@@ -13,8 +13,8 @@ class CastorusBridge extends BridgeAbstract {
'name' => 'ZIP code',
'type' => 'text',
'required' => true,
'exampleValue' => '74910, 74',
'title' => 'Insert ZIP code (complete or partial)'
'exampleValue' => '7',
'title' => 'Insert ZIP code (complete or partial). e.g: 78125 OR 781 OR 7'
)
),
'Get latest changes via city name' => array(
@@ -22,8 +22,8 @@ class CastorusBridge extends BridgeAbstract {
'name' => 'City name',
'type' => 'text',
'required' => true,
'exampleValue' => 'Seyssel, Seys',
'title' => 'Insert city name (complete or partial)'
'exampleValue' => 'Paris',
'title' => 'Insert city name (complete or partial). e.g: Paris OR Par OR P'
)
)
);
@@ -35,7 +35,7 @@ class CastorusBridge extends BridgeAbstract {
if(!$title)
returnServerError('Cannot find title!');
return htmlspecialchars(trim($title->plaintext));
return trim($title->plaintext);
}
// Extracts the url from an actitiy

View File

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

17
bridges/CeskaTelevizeBridge.php Executable file → Normal file
View File

@@ -13,7 +13,7 @@ class CeskaTelevizeBridge extends BridgeAbstract {
'url' => array(
'name' => 'url to the show',
'required' => true,
'exampleValue' => 'https://www.ceskatelevize.cz/porady/1097181328-udalosti/dily/'
'exampleValue' => 'https://www.ceskatelevize.cz/porady/1097181328-udalosti/'
)
)
);
@@ -38,7 +38,7 @@ class CeskaTelevizeBridge extends BridgeAbstract {
public function collectData() {
$url = $this->getInput('url');
$validUrl = '/^(https:\/\/www\.ceskatelevize\.cz\/porady\/\d+-[a-z0-9-]+\/)(dily\/((nove|vysilani)\/)?)?$/';
$validUrl = '/^(https:\/\/www\.ceskatelevize\.cz\/porady\/\d+-[a-z0-9-]+\/)(bonus\/)?$/';
if (!preg_match($validUrl, $url, $match)) {
returnServerError('Invalid url');
}
@@ -46,8 +46,7 @@ class CeskaTelevizeBridge extends BridgeAbstract {
$category = isset($match[4]) ? $match[4] : 'nove';
$fixedUrl = "{$match[1]}dily/{$category}/";
$html = getSimpleHTMLDOM($fixedUrl)
or returnServerError('Could not request Česká televize');
$html = getSimpleHTMLDOM($fixedUrl);
$this->feedUri = $fixedUrl;
$this->feedName = str_replace('Přehled dílů — ', '', $this->fixChars($html->find('title', 0)->plaintext));
@@ -55,17 +54,17 @@ class CeskaTelevizeBridge extends BridgeAbstract {
$this->feedName .= " ({$category})";
}
foreach ($html->find('.episodes-broadcast-content a.episode_list_item') as $element) {
$itemTitle = $element->find('.episode_list_item-title', 0);
$itemContent = $element->find('.episode_list_item-desc', 0);
$itemDate = $element->find('.episode_list_item-date', 0);
foreach ($html->find('#episodeListSection a[data-testid=next-link]') as $element) {
$itemTitle = $element->find('h3', 0);
$itemContent = $element->find('div[class^=content-]', 0);
$itemDate = $element->find('div[class^=playTime-] span', 0);
$itemThumbnail = $element->find('img', 0);
$itemUri = self::URI . $element->getAttribute('href');
$item = array(
'title' => $this->fixChars($itemTitle->plaintext),
'uri' => $itemUri,
'content' => '<img src="https:' . $itemThumbnail->getAttribute('src') . '" /><br />'
'content' => '<img src="' . $itemThumbnail->getAttribute('src') . '" /><br />'
. $this->fixChars($itemContent->plaintext),
'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext)
);

View File

@@ -1,28 +0,0 @@
<?php
class ChristianDailyReporterBridge extends BridgeAbstract {
const MAINTAINER = 'rogerdc';
const NAME = 'Christian Daily Reporter Unofficial RSS';
const URI = 'https://www.christiandailyreporter.com/';
const DESCRIPTION = 'The Unofficial Christian Daily Reporter RSS';
// const CACHE_TIMEOUT = 86400; // 1 day
public function getIcon() {
return self::URI . 'images/cdrfavicon.png';
}
public function collectData() {
$uri = 'https://www.christiandailyreporter.com/';
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request Christian Daily Reporter.');
foreach($html->find('div.top p a,div.column p a') as $element) {
$item = array();
// Title
$item['title'] = $element->innertext;
// URL
$item['uri'] = $element->href;
$this->items[] = $item;
}
}
}

403
bridges/CodebergBridge.php Normal file
View File

@@ -0,0 +1,403 @@
<?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;
const TEST_DETECT_PARAMETERS = array(
'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';
private $issueTitle = '';
private $urlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)(?:\/commits\/branch\/([\w]+))?/';
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]+)/';
public function detectParameters($url) {
$params = array();
// 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];
return $params;
}
// Issues
if(preg_match($this->issuesUrlRegex, $url, $matches)) {
$params['context'] = 'Issues';
$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];
return $params;
}
// Releases
if(preg_match($this->releasesUrlRegex, $url, $matches)) {
$params['context'] = 'Releases';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params;
}
// Commits
if(preg_match($this->urlRegex, $url, $matches)) {
$params['context'] = 'Commits';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
if (isset($matches[3])) {
$params['branch'] = $matches[3];
}
return $params;
}
return null;
}
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());
switch($this->queriedContext) {
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) {
case 'Commits':
if ($this->getBranch() === $this->defaultBranch) {
return $this->getRepo() . ' Commits';
}
return $this->getRepo() . ' Commits (' . $this->getBranch() . ' branch) - ' . self::NAME;
case 'Issues':
return $this->getRepo() . ' Issues - ' . self::NAME;
case 'Issue Comments':
return $this->issueTitle . ' - Issue Comments - ' . self::NAME;
case 'Pull Requests':
return $this->getRepo() . ' Pull Requests - ' . self::NAME;
case 'Releases':
return $this->getRepo() . ' Releases - ' . self::NAME;
default:
return parent::getName();
}
}
public function getURI() {
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();
}
}
private function getBranch() {
if ($this->getInput('branch')) {
return $this->getInput('branch');
}
return $this->defaultBranch;
}
private function getRepo() {
return $this->getInput('username') . '/' . $this->getInput('repo');
}
/**
* Extract commits
*/
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();
$message = $tr->find('td.message', 0);
$item['title'] = $message->find('span.message-wrapper', 0)->plaintext;
$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)) {
$message->find('pre.commit-body', 0)->style = '';
$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();
$number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext);
$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('span.time-since', 0)->title;
$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
// Fetch issue page
$issuePage = getSimpleHTMLDOMCached($item['uri'], 3600);
$issuePage = defaultLinkTo($issuePage, self::URI);
$item['content'] = $issuePage->find('div.timeline-item.comment.first', 0)->find('div.render-content.markup', 0);
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();
if ($div->class === 'timeline-item comment merge box') {
continue;
}
$item['title'] = $this->ellipsisTitle($div->find('div.render-content.markup', 0)->plaintext);
$item['uri'] = $div->find('span.text.grey', 0)->find('a', 1)->href;
$item['content'] = $div->find('div.render-content.markup', 0);
if ($div->find('div.dropzone-attachments', 0)) {
$item['content'] .= $div->find('div.dropzone-attachments', 0);
}
$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();
$number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext);
$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('span.time-since', 0)->title;
$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
// Fetch pull request page
$pullRequestPage = getSimpleHTMLDOMCached($item['uri'], 3600);
$pullRequestPage = defaultLinkTo($pullRequestPage, self::URI);
$item['content'] = $pullRequestPage->find('ui.timeline', 0)->find('div.render-content.markup', 0);
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();
$item['title'] = $li->find('h4', 0)->plaintext;
$item['uri'] = $li->find('h4', 0)->find('a', 0)->href;
$tag = $this->stripSvg($li->find('span.tag', 0));
$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>
<p>{$tag}</p>
<strong>Commit</strong>
<p>{$commit}</p>
{$downloads}
HTML;
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
$item['author'] = $li->find('span.author', 0)->find('a', 0)->plaintext;
$this->items[] = $item;
}
}
/**
* Extract downloads for a releases
*/
private function extractDownloads($html, $skipFirst = false) {
$downloads = '';
foreach ($html->find('a') as $index => $a) {
if ($skipFirst === true && $index === 0) {
continue;
}
$downloads .= <<<HTML
<a href="{$a->herf}">{$a->plaintext}</a><br>
HTML;
}
return <<<EOD
<strong>Downloads</strong>
<p>{$downloads}</p>
EOD;
}
/**
* Ellipsis title to first 100 characters
*/
private function ellipsisTitle($text) {
$length = 100;
if (strlen($text) > $length) {
$text = explode('<br>', wordwrap($text, $length, '<br>'));
return $text[0] . '...';
}
return $text;
}
/**
* Strip SVG tag
*/
private function stripSvg($html) {
if ($html->find('svg', 0)) {
$html->find('svg', 0)->outertext = '';
}
return $html;
}
}

View File

@@ -34,8 +34,7 @@ class CollegeDeFranceBridge extends BridgeAbstract {
* </li>
*/
$html = getSimpleHTMLDOM(self::URI
. 'components/search-audiovideo.jsp?fulltext=&siteid=1156951719600&lang=FR&type=all')
or returnServerError('Could not request CollegeDeFrance.');
. 'components/search-audiovideo.jsp?fulltext=&siteid=1156951719600&lang=FR&type=all');
foreach($html->find('a[data-target]') as $element) {
$item = array();

View File

@@ -7,8 +7,11 @@ class ComboiosDePortugalBridge extends BridgeAbstract {
const MAINTAINER = 'somini';
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI() . '/consultar-horarios/avisos')
or returnServerError('Could not load content');
# 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', array(), array(
CURLOPT_SSL_VERIFYPEER => 0,
));
foreach($html->find('.warnings-table a') as $element) {
$item = array();

View File

@@ -3,36 +3,33 @@ class ComicsKingdomBridge extends BridgeAbstract {
const MAINTAINER = 'stjohnjohnson';
const NAME = 'Comics Kingdom Unofficial RSS';
const URI = 'https://www.comicskingdom.com/';
const URI = 'https://comicskingdom.com/';
const CACHE_TIMEOUT = 21600; // 6h
const DESCRIPTION = 'Comics Kingdom Unofficial RSS';
const PARAMETERS = array( array(
'comicname' => array(
'name' => 'comicname',
'type' => 'text',
'exampleValue' => 'mutts',
'title' => 'The name of the comic in the URL after https://comicskingdom.com/',
'required' => true
)
));
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI(), array(), array(), true, false)
or returnServerError('Could not request Comics Kingdom: ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI(), array(), array(), true, false);
// Get author from first page
$author = $html->find('div.author p', 0)->plaintext
or returnServerError('Comics Kingdom comic does not exist: ' . $this->getURI());;
$author = $html->find('div.author p', 0);;
// Get current date/link
$link = $html->find('meta[property=og:url]', 0)->content;
for($i = 0; $i < 5; $i++) {
$link = $html->find('meta[property=og:url]', -1)->content;
for($i = 0; $i < 3; $i++) {
$item = array();
$page = getSimpleHTMLDOM($link)
or returnServerError('Could not request Comics Kingdom: ' . $link);
$page = getSimpleHTMLDOM($link);
$imagelink = $page->find('meta[property=og:image]', 0)->content;
$prevSlug = $page->find('slider-arrow[:is-left-arrow=true]', 0);
$link = $this->getURI() . '/' . $prevSlug->getAttribute('date-slug');
$date = explode('/', $link);
@@ -44,6 +41,8 @@ class ComicsKingdomBridge extends BridgeAbstract {
$item['content'] = '<img src="' . $imagelink . '" />';
$this->items[] = $item;
$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
}
}

View File

@@ -1,96 +0,0 @@
<?php
class ContainerLinuxReleasesBridge extends BridgeAbstract {
const MAINTAINER = 'captn3m0';
const NAME = 'Core OS Container Linux Releases Bridge';
const URI = 'https://coreos.com/releases/';
const DESCRIPTION = 'Returns the releases notes for Container Linux';
const STABLE = 'stable';
const BETA = 'beta';
const ALPHA = 'alpha';
const PARAMETERS = array(
array(
'channel' => array(
'name' => 'Release Channel',
'type' => 'list',
'defaultValue' => self::STABLE,
'values' => array(
'Stable' => self::STABLE,
'Beta' => self::BETA,
'Alpha' => self::ALPHA,
),
)
)
);
private function getReleaseFeed($jsonUrl) {
$json = getContents($jsonUrl)
or returnServerError('Could not request Core OS Website.');
return json_decode($json, true);
}
public function getIcon() {
return 'https://coreos.com/assets/ico/favicon.png';
}
public function collectData() {
$data = $this->getReleaseFeed($this->getJsonUri());
foreach ($data as $releaseVersion => $release) {
$item = array();
$item['uri'] = "https://coreos.com/releases/#$releaseVersion";
$item['title'] = $releaseVersion;
$content = $release['release_notes'];
$content .= <<<EOT
Major Software:
* Kernel: {$release['major_software']['kernel'][0]}
* Docker: {$release['major_software']['docker'][0]}
* etcd: {$release['major_software']['etcd'][0]}
EOT;
$item['timestamp'] = strtotime($release['release_date']);
// Based on https://gist.github.com/jbroadway/2836900
// Links
$regex = '/\[([^\[]+)\]\(([^\)]+)\)/';
$replacement = '<a href=\'\2\'>\1</a>';
$item['content'] = preg_replace($regex, $replacement, $content);
// Headings
$regex = '/^(.*)\:\s?$/m';
$replacement = '<h3>\1</h3>';
$item['content'] = preg_replace($regex, $replacement, $item['content']);
// Lists
$regex = '/\n\s*[\*|\-](.*)/';
$item['content'] = preg_replace_callback ($regex, function($regs) {
$item = $regs[1];
return sprintf ('<ul><li>%s</li></ul>', trim ($item));
}, $item['content']);
$this->items[] = $item;
}
}
private function getJsonUri() {
$channel = $this->getInput('channel');
return "https://coreos.com/releases/releases-$channel.json";
}
public function getURI() {
return self::URI;
}
public function getName(){
if(!is_null($this->getInput('channel'))) {
return 'Container Linux Releases: ' . $this->getInput('channel') . ' Channel';
}
return parent::getName();
}
}

View File

@@ -8,8 +8,7 @@ class CopieDoubleBridge extends BridgeAbstract {
const DESCRIPTION = 'CopieDouble';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request CopieDouble.');
$html = getSimpleHTMLDOM(self::URI);
$table = $html->find('table table', 2);

View File

@@ -1,55 +1,26 @@
<?php
class CourrierInternationalBridge extends BridgeAbstract {
class CourrierInternationalBridge extends FeedExpander {
const MAINTAINER = 'teromene';
const NAME = 'Courrier International Bridge';
const URI = 'https://www.courrierinternational.com/';
const CACHE_TIMEOUT = 300; // 5 min
const DESCRIPTION = 'Courrier International bridge';
const DESCRIPTION = 'Returns the newest articles';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Error.');
$this->collectExpandableDatas(static::URI . 'feed/all/rss.xml', 20);
}
$element = $html->find('article');
$article_count = 1;
protected function parseItem($feedItem){
$item = parent::parseItem($feedItem);
foreach($element as $article) {
$item = array();
$item['uri'] = $article->parent->getAttribute('href');
if(strpos($item['uri'], 'http') === false) {
$item['uri'] = self::URI . $item['uri'];
}
$page = getSimpleHTMLDOMCached($item['uri']);
$content = $page->find('.article-text', 0);
if(!$content) {
$content = $page->find('.depeche-text', 0);
}
$item['content'] = sanitize($content);
$item['title'] = strip_tags($article->find('.title', 0));
$dateTime = date_parse($page->find('time', 0));
$item['timestamp'] = mktime(
$dateTime['hour'],
$dateTime['minute'],
$dateTime['second'],
$dateTime['month'],
$dateTime['day'],
$dateTime['year']
);
$this->items[] = $item;
$article_count ++;
if($article_count > 5)
break;
$articlePage = getSimpleHTMLDOMCached($feedItem->link);
$content = $articlePage->find('.article-text, depeche-text', 0);
if (!$content) {
return $item;
}
$item['content'] = sanitize($content);
return $item;
}
}

View File

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

View File

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

98
bridges/CubariBridge.php Normal file
View File

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

View File

@@ -19,8 +19,7 @@ class CuriousCatBridge extends BridgeAbstract {
$url = self::URI . '/api/v2/profile?username=' . urlencode($this->getInput('username'));
$apiJson = getContents($url)
or returnServerError('Could not request: ' . $url);
$apiJson = getContents($url);
$apiData = json_decode($apiJson, true);

View File

@@ -0,0 +1,37 @@
<?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() {
return self::URI . 'favicon-32x32.png';
}
public function getURI(){
return self::URI . 'comics/latest#comic';
}
public function collectData() {
$html = getSimpleHTMLDOM($this->getUri());
foreach ($html->find('[class*=ComicImage]') as $element) {
$date = $element->find('[class^=Author__Right] p', 0)->plaintext;
$author = str_replace('by ', '', $element->find('[class^=Author__Right] p', 1)->plaintext);
$image = $element->find('img', 0)->src;
$link = $html->find('[rel=canonical]', 0)->href;
$item = array(
'uid' => $link,
'author' => $author,
'title' => $date,
'uri' => $link . '#comic',
'timestamp' => str_replace('.', '-', $date) . 'T00:00:00Z',
'content' => "<img src=\"$image\" />"
);
$this->items[] = $item;
}
}
}

View File

@@ -11,19 +11,22 @@ class DailymotionBridge extends BridgeAbstract {
'By username' => array(
'u' => array(
'name' => 'username',
'required' => true
'required' => true,
'exampleValue' => 'moviepilot',
)
),
'By playlist id' => array(
'p' => array(
'name' => 'playlist id',
'required' => true
'required' => true,
'exampleValue' => 'x6xyc6',
)
),
'From search results' => array(
's' => array(
'name' => 'Search keyword',
'required' => true
'required' => true,
'exampleValue' => 'matrix',
),
'pa' => array(
'name' => 'Page',
@@ -46,8 +49,7 @@ class DailymotionBridge extends BridgeAbstract {
if ($this->queriedContext === 'By username' || $this->queriedContext === 'By playlist id') {
$apiJson = getContents($this->getApiUrl())
or returnServerError('Could not request: ' . $this->getApiUrl());
$apiJson = getContents($this->getApiUrl());
$apiData = json_decode($apiJson, true);
@@ -72,8 +74,7 @@ class DailymotionBridge extends BridgeAbstract {
if ($this->queriedContext === 'From search results') {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request Dailymotion.');
$html = getSimpleHTMLDOM($this->getURI());
foreach($html->find('div.media a.preview_link') as $element) {
$item = array();
@@ -180,8 +181,7 @@ class DailymotionBridge extends BridgeAbstract {
$url = self::URI . 'playlist/' . $id;
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request: ' . $url);
$html = getSimpleHTMLDOM($url);
$title = $html->find('meta[property=og:title]', 0)->getAttribute('content');
return $title;

View File

@@ -15,7 +15,9 @@ class DanbooruBridge extends BridgeAbstract {
'type' => 'number'
),
't' => array(
'name' => 'tags'
'type' => 'text',
'name' => 'tags',
'exampleValue' => 'cosplay',
)
),
0 => array()
@@ -44,93 +46,23 @@ class DanbooruBridge extends BridgeAbstract {
$item['postid'] = (int)preg_replace('/[^0-9]/', '', $element->getAttribute(static::IDATTRIBUTE));
$item['timestamp'] = time();
$thumbnailUri = $element->find('img', 0)->src;
$item['tags'] = $this->getTags($element);
$item['categories'] = array_filter(explode(' ', $this->getTags($element)));
$item['title'] = $this->getName() . ' | ' . $item['postid'];
$item['content'] = '<a href="'
. $item['uri']
. '"><img src="'
. $thumbnailUri
. '" /></a><br>Tags: '
. $item['tags'];
. $this->getTags($element);
return $item;
}
public function collectData(){
$content = getContents($this->getFullURI())
or returnServerError('Could not request ' . $this->getName());
$html = Fix_Simple_Html_Dom::str_get_html($content);
$html = getSimpleHTMLDOMCached($this->getFullURI());
foreach($html->find(static::PATHTODATA) as $element) {
$this->items[] = $this->getItemFromElement($element);
}
}
}
/**
* This class is a monkey patch to 'extend' simplehtmldom to recognize <source>
* tags (HTML5) as self closing tag. This patch should be removed once
* simplehtmldom was fixed. This seems to be a issue with more tags:
* https://sourceforge.net/p/simplehtmldom/bugs/83/
*
* The tag itself is valid according to Mozilla:
*
* The HTML <picture> element serves as a container for zero or more <source>
* elements and one <img> element to provide versions of an image for different
* display device scenarios. The browser will consider each of the child <source>
* elements and select one corresponding to the best match found; if no matches
* are found among the <source> elements, the file specified by the <img>
* element's src attribute is selected. The selected image is then presented in
* the space occupied by the <img> element.
*
* -- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture
*
* Notice: This class uses parts of the original simplehtmldom, adjusted to pass
* the guidelines of RSS-Bridge (formatting)
*/
final class Fix_Simple_Html_Dom extends simple_html_dom {
/* copy from simple_html_dom, added 'source' at the end */
protected $self_closing_tags = array(
'img' => 1,
'br' => 1,
'input' => 1,
'meta' => 1,
'link' => 1,
'hr' => 1,
'base' => 1,
'embed' => 1,
'spacer' => 1,
'source' => 1
);
/* copy from simplehtmldom, changed 'simple_html_dom' to 'Fix_Simple_Html_Dom' */
public static function str_get_html($str,
$lowercase = true,
$forceTagsClosed = true,
$target_charset = DEFAULT_TARGET_CHARSET,
$stripRN = true,
$defaultBRText = DEFAULT_BR_TEXT,
$defaultSpanText = DEFAULT_SPAN_TEXT)
{
$dom = new Fix_Simple_Html_Dom(null,
$lowercase,
$forceTagsClosed,
$target_charset,
$stripRN,
$defaultBRText,
$defaultSpanText);
if (empty($str) || strlen($str) > MAX_FILE_SIZE) {
$dom->clear();
return false;
}
$dom->load($str, $lowercase, $stripRN);
return $dom;
}
}

View File

@@ -9,8 +9,7 @@ class DansTonChatBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI . 'latest.html')
or returnServerError('Could not request DansTonChat.');
$html = getSimpleHTMLDOM(self::URI . 'latest.html');
foreach($html->find('div.item') as $element) {
$item = array();

View File

@@ -33,7 +33,8 @@ class DarkReadingBridge extends FeedExpander {
'Insider Threats' => '663_Insider%20Threats',
'Vulnerability Management' => '664_Vulnerability%20Management',
)
)
),
'limit' => self::LIMIT,
));
public function collectData(){
@@ -48,22 +49,25 @@ class DarkReadingBridge extends FeedExpander {
if ($feed_id != '000') {
$feed_url .= '?f_n=' . $feed_id . '&f_ln=' . $feed_name;
}
$this->collectExpandableDatas($feed_url);
$limit = $this->getInput('limit') ?? 10;
$this->collectExpandableDatas($feed_url, $limit);
}
protected function parseItem($newsItem){
$item = parent::parseItem($newsItem);
if (empty($item['content']))
return null; //ignore dummy articles
$article = getSimpleHTMLDOMCached($item['uri'])
or returnServerError('Could not request Dark Reading: ' . $item['uri']);
$article = getSimpleHTMLDOMCached($item['uri']);
$item['content'] = $this->extractArticleContent($article);
$item['enclosures'] = array(); //remove author profile picture
$image = $article->find('meta[property="og:image"]', 0);
if (is_object($image)) {
$image = $image->content;
$item['enclosures'] = array($image);
}
return $item;
}
private function extractArticleContent($article){
$content = $article->find('div#article-main', 0)->innertext;
$content = $article->find('div.article-content', 0)->innertext;
foreach (array(
'<div class="divsplitter',
@@ -74,8 +78,6 @@ class DarkReadingBridge extends FeedExpander {
$content = stripRecursiveHTMLSection($content, 'div', $div_start);
}
$content = stripWithDelimiters($content, '<h1 ', '</h1>');
return $content;
}
}

View File

@@ -1,24 +0,0 @@
<?php
class DaveRamseyBlogBridge extends BridgeAbstract {
const MAINTAINER = 'johnpc';
const NAME = 'Dave Ramsey Blog';
const URI = 'https://www.daveramsey.com/blog';
const CACHE_TIMEOUT = 7200; // 2h
const DESCRIPTION = 'Returns blog posts from daveramsey.com';
public function collectData()
{
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request daveramsey.com.');
foreach ($html->find('.Post') as $element) {
$this->items[] = array(
'uri' => 'https://www.daveramsey.com' . $element->find('header > a', 0)->href,
'title' => $element->find('header > h2 > a', 0)->plaintext,
'tags' => $element->find('.Post-topic', 0)->plaintext,
'content' => $element->find('.Post-body', 0)->plaintext,
);
}
}
}

View File

@@ -9,7 +9,14 @@ class DavesTrailerPageBridge extends BridgeAbstract {
$html = getSimpleHTMLDOM(static::URI)
or returnClientError('No results for this query.');
foreach ($html->find('tr[!align]') as $tr) {
$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;
}
$item = array();
// title
@@ -21,6 +28,9 @@ class DavesTrailerPageBridge extends BridgeAbstract {
// uri
$item['uri'] = $tr->find('a', 3)->getAttribute('href');
// 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

@@ -2,7 +2,7 @@
class DerpibooruBridge extends BridgeAbstract {
const NAME = 'Derpibooru Bridge';
const URI = 'https://derpibooru.org/';
const DESCRIPTION = 'Returns newest posts from a Derpibooru search';
const DESCRIPTION = 'Returns newest images from a Derpibooru search';
const CACHE_TIMEOUT = 300; // 5min
const MAINTAINER = 'Roliga';
@@ -24,7 +24,8 @@ class DerpibooruBridge extends BridgeAbstract {
),
'q' => array(
'name' => 'Query',
'required' => true
'required' => true,
'exampleValue' => 'dog',
)
)
);
@@ -73,40 +74,42 @@ class DerpibooruBridge extends BridgeAbstract {
public function collectData(){
$queryJson = json_decode(getContents(
self::URI
. 'search.json?filter_id='
. 'api/v1/json/search/images?filter_id='
. urlencode($this->getInput('f'))
. '&q='
. urlencode($this->getInput('q'))
)) or returnServerError('Failed to query Derpibooru');
));
foreach($queryJson->search as $post) {
foreach($queryJson->images as $post) {
$item = array();
$postUri = self::URI . $post->id;
$item['uri'] = $postUri;
$item['title'] = $post->id;
$item['title'] = $post->name;
$item['timestamp'] = strtotime($post->created_at);
$item['author'] = $post->uploader;
$item['enclosures'] = array('https:' . $post->image);
$item['categories'] = explode(', ', $post->tags);
$item['enclosures'] = array($post->view_url);
$item['categories'] = $post->tags;
$item['content'] = '<p><a href="' // image preview
. $postUri
. '"><img src="https:'
. '"><img src="'
. $post->representations->medium
. '"></a></p><p>' // description
. $post->description
. '</p><p><b>Size:</b> ' // image size
. $post->width
. 'x'
. $post->height
. '<br><b>Source:</b> <a href="' // source link
. $post->height;
// source link
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

@@ -16,7 +16,7 @@ class DesoutterBridge extends BridgeAbstract {
'name' => 'Language',
'type' => 'list',
'title' => 'Select your language',
'defaultValue' => 'Corporate',
'defaultValue' => 'https://www.desouttertools.com/about-desoutter/news-events',
'values' => array(
'Corporate'
=> 'https://www.desouttertools.com/about-desoutter/news-events',
@@ -120,6 +120,7 @@ class DesoutterBridge extends BridgeAbstract {
'limit' => array(
'name' => 'Limit',
'type' => 'number',
'required' => true,
'defaultValue' => 3,
'title' => "Maximum number of items to return in the feed.\n0 = unlimited"
)
@@ -155,8 +156,7 @@ class DesoutterBridge extends BridgeAbstract {
}
*/
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());
@@ -184,8 +184,7 @@ class DesoutterBridge extends BridgeAbstract {
}
private function getFullNewsArticle($uri) {
$html = getSimpleHTMLDOMCached($uri)
or returnServerError('Unable to load full article!');
$html = getSimpleHTMLDOMCached($uri);
$html = defaultLinkTo($html, $this->getURI());
@@ -199,8 +198,7 @@ class DesoutterBridge extends BridgeAbstract {
* @return void
*/
private function extractNewsLanguages() {
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events')
or returnServerError('Error loading news!');
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events');
$html = defaultLinkTo($html, static::URI);
@@ -225,8 +223,7 @@ class DesoutterBridge extends BridgeAbstract {
* @return void
*/
private function extractIndustryLanguages() {
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news')
or returnServerError('Error loading news!');
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news');
$html = defaultLinkTo($html, static::URI);

View File

@@ -45,8 +45,7 @@ apple-icon-5c6fa9f2bce280428589c6195b7f1924206a53b782b371cfe2d02da932c8c173.png'
}
public function collectData() {
$html = getSimpleHTMLDOMCached($this->getURI())
or returnServerError('Could not request ' . $this->getURI());
$html = getSimpleHTMLDOMCached($this->getURI());
$html = defaultLinkTo($html, static::URI);
@@ -95,8 +94,7 @@ EOD;
}
private function getFullArticle($url) {
$html = getSimpleHTMLDOMCached($url)
or returnServerError('Unable to load article from "' . $url . '"!');
$html = getSimpleHTMLDOMCached($url);
$html = defaultLinkTo($html, static::URI);

View File

@@ -1,47 +1,407 @@
<?php
class DeveloppezDotComBridge extends FeedExpander {
const MAINTAINER = 'polopollo';
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 the 15 newest posts from DeveloppezDotCom (full text).';
const DESCRIPTION = 'Returns complete posts from developpez.com';
// Encodings used by Developpez.com in their articles body
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'
),
)
)
);
public function collectData(){
$this->collectExpandableDatas(self::URI . 'index/rss', 15);
/**
* 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;
}
protected function parseItem($newsItem){
/**
* Grabs the RSS item from Developpez.com
*/
public function collectData()
{
$url = $this->getRssUrl();
$this->collectExpandableDatas($url, 20);
}
/**
* 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.
*/
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
$item = parent::parseItem($newsItem);
$item['content'] = $this->extractContent($item['uri']);
// There is a bug in Developpez RSS, coma are writtent as '~?' in the
// title, so I have to fix it manually
$item['title'] = $this->fixComaInTitle($item['title']);
// We get the content of the full article behind the RSS item URL
$articleHTMLContent = getSimpleHTMLDOMCached($item['uri']);
// Here we call our custom parser
$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;
}
// Now we will attach video url in item
$videosUrl = $this->getAllVideoUrl($articleHTMLContent);
if (!empty($videosUrl)) {
$item['enclosures'] = array_merge($item['enclosures'], $videosUrl);
}
// Now we can look for the blog writer/creator
$author = $articleHTMLContent->find('[itemprop="creator"]', 0);
if (!empty($author)) {
$item['author'] = $author->outertext;
}
return $item;
}
// F***ing quotes from Microsoft Word badly encoded, here was the trick:
// http://stackoverflow.com/questions/1262038/how-to-replace-microsoft-encoded-quotes-in-php
private function convertSmartQuotes($string)
/**
* Replace '~?' by a proper coma ','
*/
private function fixComaInTitle($txt)
{
$search = array(chr(145),
chr(146),
chr(147),
chr(148),
chr(151));
$replace = array(
"'",
"'",
'"',
'"',
'-'
);
return str_replace($search, $replace, $string);
return str_replace('~?', ',', $txt);
}
private function extractContent($url){
$articleHTMLContent = getSimpleHTMLDOMCached($url);
$text = $this->convertSmartQuotes($articleHTMLContent->find('div.content', 0)->innertext);
$text = utf8_encode($text);
return trim($text);
/**
* Return the full article pointed by the url in the RSS item
* Since Developpez.com only provides a short abstract of the article, we
* use the url to retrieve the complete article and return it as the content
*/
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
// full content of articles without any encoding issues. What is very
// weird and ugly in Developpez.com is the fact the some paragraphs of
// 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
// 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
// pictures, the text and the links at the bottom of the article
$paragraphs = $divArticleEntry->nodes;
// This will store the complete decoded content
$fullText = '';
// For each paragraph, we will identify the encoding, then decode it
// and finally store the decoded content in $text
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);
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>
</p>';
}
// We take outertext to get the complete paragraph not only the text
// inside it. That way we still graph block <img> and so on.
$pTxt = $p->outertext;
// This will store the decoded text if we manage to decode it
$decodedTxt = '';
// This is the only way to properly decode each paragraph. I tried
// many stuffs but this is the only working way I found.
foreach (self::ENCONDINGS as $enc) {
// We check the encoding of the current paragraph
if (mb_check_encoding($pTxt, $enc)) {
// If the encoding is well recognized, we can convert from
// this encoding to UTF-8
$decodedTxt = iconv($enc, 'UTF-8', $pTxt);
}
}
// We should not trim the strings to avoid the <a> to be glued to the
// text like: the software<a href="...">started</a>to...
if (!empty($decodedTxt)) {
// We manage to decode the text, so we take the decoded version
return $this->formatParagraph($decodedTxt);
} else {
// Otherwise we take the non decoded version and hope it will
// be displayed not too ugly in the fulltext content
return $this->formatParagraph($pTxt);
}
}
/**
* Return true in $txt is a HTML tag and not plain text
*/
private function isHtmlTagNotTxt($txt)
{
$html = str_get_html($txt);
return $html && $html->root && count($html->root->children) > 0;
}
/**
* Will add a space before paragraph when needed
*/
private function formatParagraph($txt)
{
// If the paragraph is an html tag, we add a space before
if ($this->isHtmlTagNotTxt($txt)) {
// the first element is an html tag and not a text, so we can add a
// space before it
return ' ' . $txt;
}
// If the text start with word (not punctation), we had a space
$pattern = '/^\w/';
if (preg_match($pattern, $txt)) {
return ' ' . $txt;
}
return $txt;
}
/**
* Retrieve all video url in the article
*/
private function getAllVideoUrl($item)
{
// Array of video url
$url = array();
// Developpez use a div with the class video-container
$divsVideo = $item->find('div.video-container');
if (empty($divsVideo)) {
return $url;
}
// get the url of the video
foreach ($divsVideo as $div) {
$html = str_get_html($div->outertext);
$url[] = $this->getVideoUrl($html);
}
return $url;
}
/**
* Retrieve URL video. We have to check for the src of an iframe
* Work for Youtube. Will have to test for other video platform
*/
private function getVideoUrl($p)
{
$divVideo = $p->find('div.video-container', 0);
if (empty($divVideo)) {
return null;
}
$iframe = $divVideo->find('iframe', 0);
if (empty($iframe)) {
return null;
}
$src = trim($iframe->getAttribute('src'));
if (empty($src)) {
return null;
}
if (str_starts_with($src, '//')) {
$src = 'https:' . $src;
}
return $src;
}
}

View File

@@ -8,6 +8,7 @@ class DiarioDeNoticiasBridge extends BridgeAbstract {
'Tag' => array(
'n' => array(
'name' => 'Tag Name',
'required' => true,
'exampleValue' => 'rogerio-casanova',
)
)
@@ -56,8 +57,7 @@ class DiarioDeNoticiasBridge extends BridgeAbstract {
public function collectData() {
$archives = self::getURI();
$html = getSimpleHTMLDOMCached($archives)
or returnServerError('Could not load content');
$html = getSimpleHTMLDOMCached($archives);
foreach($html->find('article') as $element) {
$item = array();

View File

@@ -27,8 +27,7 @@ class DiarioDoAlentejoBridge extends BridgeAbstract {
public function collectData(){
/* This is slow as molasses (>30s!), keep the cache timeout high to avoid killing the host */
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx')
or returnServerError('Could not load content');
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx');
foreach($html->find('.list_news .item') as $element) {
$item = array();

View File

@@ -97,8 +97,7 @@ class DiceBridge extends BridgeAbstract {
$uri .= '&telecommute=true';
}
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request Dice.');
$html = getSimpleHTMLDOM($uri);
foreach($html->find('div.complete-serp-result-div') as $element) {
$item = array();
// Title

View File

@@ -9,8 +9,7 @@ class DilbertBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request Dilbert: ' . self::URI);
$html = getSimpleHTMLDOM(self::URI);
foreach($html->find('section.comic-item') as $element) {

View File

@@ -11,18 +11,26 @@ class DiscogsBridge extends BridgeAbstract {
'artistid' => array(
'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' => 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(
@@ -44,13 +52,11 @@ class DiscogsBridge extends BridgeAbstract {
if(!empty($this->getInput('artistid'))) {
$data = getContents('https://api.discogs.com/artists/'
. $this->getInput('artistid')
. '/releases?sort=year&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/releases?sort=year&sort_order=desc');
} elseif(!empty($this->getInput('labelid'))) {
$data = getContents('https://api.discogs.com/labels/'
. $this->getInput('labelid')
. '/releases?sort=year&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/releases?sort=year&sort_order=desc');
}
$jsonData = json_decode($data, true);
@@ -76,8 +82,7 @@ class DiscogsBridge extends BridgeAbstract {
if(!empty($this->getInput('username_wantlist'))) {
$data = getContents('https://api.discogs.com/users/'
. $this->getInput('username_wantlist')
. '/wants?sort=added&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/wants?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['wants'];
} elseif(!empty($this->getInput('username_folder'))) {
@@ -85,8 +90,7 @@ class DiscogsBridge extends BridgeAbstract {
. $this->getInput('username_folder')
. '/collection/folders/'
. $this->getInput('folderid')
. '/releases?sort=added&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/releases?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['releases'];
}
foreach($jsonData as $element) {

165
bridges/DockerHubBridge.php Normal file
View File

@@ -0,0 +1,165 @@
<?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
private $apiURL = 'https://hub.docker.com/v2/repositories/';
private $imageUrlRegex = '/hub\.docker\.com\/r\/([\w]+)\/([\w-]+)\/?/';
private $officialImageUrlRegex = '/hub\.docker\.com\/_\/([\w-]+)\/?/';
public function detectParameters($url) {
$params = array();
// user submitted image
if(preg_match($this->imageUrlRegex, $url, $matches)) {
$params['context'] = 'User Submitted Image';
$params['user'] = $matches[1];
$params['repo'] = $matches[2];
return $params;
}
// official image
if(preg_match($this->officialImageUrlRegex, $url, $matches)) {
$params['context'] = 'Official Image';
$params['repo'] = $matches[1];
return $params;
}
return null;
}
public function collectData() {
$json = getContents($this->getApiUrl());
$data = json_decode($json, false);
foreach ($data->results as $result) {
$item = array();
$lastPushed = date('Y-m-d H:i:s', strtotime($result->tag_last_pushed));
$item['title'] = $result->name;
$item['uid'] = $result->id;
$item['uri'] = $this->getTagUrl($result->name);
$item['author'] = $result->last_updater_username;
$item['timestamp'] = $result->tag_last_pushed;
$item['content'] = <<<EOD
<Strong>Tag</strong><br>
<p>{$result->name}</p>
<Strong>Last pushed</strong><br>
<p>{$lastPushed}</p>
<Strong>Images</strong><br>
{$this->getImages($result)}
EOD;
$this->items[] = $item;
}
}
public function getURI() {
if ($this->queriedContext === 'Official Image') {
return self::URI . '/_/' . $this->getRepo();
}
if ($this->getInput('repo')) {
return self::URI . '/r/' . $this->getRepo();
}
return parent::getURI();
}
public function 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');
}
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';
}
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;
}
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;
}
return self::URI . '/r/' . $this->getRepo() . '/tags?name=' . $name;
}
private function getImages($result) {
$html = <<<EOD
<table style="width:300px;"><thead><tr><th>Digest</th><th>OS/architecture</th></tr></thead></tbody>
EOD;
foreach ($result->images as $image) {
$layersUrl = $this->getLayerUrl($result->name, $image->digest);
$id = $this->getShortDigestId($image->digest);
$html .= <<<EOD
<tr>
<td><a href="{$layersUrl}">{$id}</a></td>
<td>{$image->os}/{$image->architecture}</td>
</tr>
EOD;
}
return $html . '</tbody></table>';
}
private function getShortDigestId($digest) {
$parts = explode(':', $digest);
return substr($parts[1], 0, 12);
}
}

View File

@@ -23,6 +23,7 @@ class DonnonsBridge extends BridgeAbstract {
'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'
)
@@ -40,8 +41,7 @@ class DonnonsBridge extends BridgeAbstract {
private function collectDataByPage($page) {
$uri = $this->getPageURI($page);
$html = getSimpleHTMLDOM($uri)
or returnServerError('No results for this query.');
$html = getSimpleHTMLDOM($uri);
$searchDiv = $html->find('div[id=search]', 0);
@@ -67,7 +67,9 @@ class DonnonsBridge extends BridgeAbstract {
$region = $json['availableAtOrFrom']['address']['addressRegion'];
// Grab info from HTML
$imageSrc = $element->find('img.ima-center', 0)->getAttribute('data-src');
$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;

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,7 @@ favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
}
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Error while downloading the website content');
$html = getSimpleHTMLDOM(self::URI);
$json = $this->loadEmbeddedJsonData($html);
@@ -86,7 +85,7 @@ favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
private function getImageTag($preview_path, $title){
return sprintf(
'<br /> <a href="%s"><img src="%s" alt="%s" /></a>',
'<br /> <a href="%s"><img srcset="%s" alt="%s" /></a>',
$this->getFullSizeImagePath($preview_path),
$preview_path,
$title
@@ -94,6 +93,11 @@ favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
}
private function getFullSizeImagePath($preview_path){
return explode('?compress=1', $preview_path)[0];
// Get last image from srcset
$src_set_urls = explode(',', $preview_path);
$url = end($src_set_urls);
$url = explode(' ', $url)[1];
return htmlspecialchars_decode($url);
}
}

205
bridges/Drive2ruBridge.php Normal file
View File

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

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