mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-11 23:59:56 +08:00
feat: leboncoin.fr (#2749)
* feat: add leboncoin.fr ad feed * docs: add leboncoin/ad/:query route
This commit is contained in:
@@ -520,6 +520,18 @@ GitHub provides some official RSS feeds:
|
|||||||
|
|
||||||
<RouteEn author="HenryQW" example="/parcel/hermesuk/[tracking number]" path="/parcel/hermesuk/:tracking" :paramsDesc="['Tracking number']"/>
|
<RouteEn author="HenryQW" example="/parcel/hermesuk/[tracking number]" path="/parcel/hermesuk/:tracking" :paramsDesc="['Tracking number']"/>
|
||||||
|
|
||||||
|
## E-commerce
|
||||||
|
|
||||||
|
### leboncoin
|
||||||
|
|
||||||
|
Transform any search into a feed.
|
||||||
|
|
||||||
|
<RouteEn author="Platane" example="/leboncoin/ad/category=10&locations=Paris_75015" path="/leboncoin/ad/:query" :paramsDesc="['search page querystring']">
|
||||||
|
|
||||||
|
For instance, in https://www.leboncoin.fr/recherche/?**category=10&locations=Paris_75015**, the query is **category=10&locations=Paris_75015**
|
||||||
|
|
||||||
|
</RouteEn>
|
||||||
|
|
||||||
## Uncategorized
|
## Uncategorized
|
||||||
|
|
||||||
### EZTV
|
### EZTV
|
||||||
|
|||||||
@@ -1597,6 +1597,9 @@ router.get('/aliyun/database_month', require('./routes/aliyun/database_month'));
|
|||||||
// 礼物说
|
// 礼物说
|
||||||
router.get('/liwushuo/index', require('./routes/liwushuo/index.js'));
|
router.get('/liwushuo/index', require('./routes/liwushuo/index.js'));
|
||||||
|
|
||||||
|
// leboncoin
|
||||||
|
router.get('/leboncoin/ad/:query', require('./routes/leboncoin/ad.js'));
|
||||||
|
|
||||||
// DHL
|
// DHL
|
||||||
router.get('/dhl/:id', require('./routes/dhl/shipment-tracking'));
|
router.get('/dhl/:id', require('./routes/dhl/shipment-tracking'));
|
||||||
|
|
||||||
|
|||||||
131
lib/routes/leboncoin/ad.js
Normal file
131
lib/routes/leboncoin/ad.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
const querystring = require('querystring');
|
||||||
|
const got = require('@/utils/got');
|
||||||
|
|
||||||
|
const API_KEY = 'ba0c2dad52b3ec';
|
||||||
|
const SEARCH_ENDPOINT = 'https://api.leboncoin.fr/finder/search';
|
||||||
|
|
||||||
|
// convert the querystring read from the url to the object expected by the rest API
|
||||||
|
const convertQueryToFilters = (query) => {
|
||||||
|
const queryObject = querystring.parse(query);
|
||||||
|
|
||||||
|
const filters = { keywords: {}, location: {}, ranges: {}, enums: {} };
|
||||||
|
|
||||||
|
queryObject.ad_type = queryObject.ad_type || 'offer';
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(queryObject)) {
|
||||||
|
// category
|
||||||
|
if (key === 'category') {
|
||||||
|
filters.category = { id: queryObject.category };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// location.locations
|
||||||
|
if (key === 'locations') {
|
||||||
|
filters.location.locations = value
|
||||||
|
.split(',')
|
||||||
|
.map((l) => {
|
||||||
|
if (l.startsWith('r_')) {
|
||||||
|
return { locationType: 'region', region_id: l.slice(2) };
|
||||||
|
}
|
||||||
|
if (l.startsWith('dn_')) {
|
||||||
|
return { locationType: 'department_near', department_id: l.slice(3) };
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
if (l.match(/.+_.+/)) {
|
||||||
|
const [city, zipcode] = l.split('_');
|
||||||
|
return { locationType: 'city', city, zipcode };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keywords.text
|
||||||
|
if (key === 'text') {
|
||||||
|
filters.keywords.text = value;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// location.area
|
||||||
|
if (key === 'lat' || key === 'lng' || key === 'radius') {
|
||||||
|
filters.location.area = {
|
||||||
|
lat: parseFloat(queryObject.lat),
|
||||||
|
lng: parseFloat(queryObject.lng),
|
||||||
|
radius: parseFloat(queryObject.radius),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// range type
|
||||||
|
{
|
||||||
|
const m = value.match(/^(\d+|min)-(\d+|max)$/);
|
||||||
|
|
||||||
|
if (m) {
|
||||||
|
const [, min, max] = m;
|
||||||
|
|
||||||
|
filters.ranges = filters.ranges || {};
|
||||||
|
filters.ranges[key] = {};
|
||||||
|
if (min !== 'min') {
|
||||||
|
filters.ranges[key].min = parseInt(min);
|
||||||
|
}
|
||||||
|
if (max !== 'max') {
|
||||||
|
filters.ranges[key].max = parseInt(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enum type
|
||||||
|
filters.enums[key] = value.split(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildDescription = (ad) =>
|
||||||
|
// the description is made of
|
||||||
|
// - first the thumbnail
|
||||||
|
// - the ad description
|
||||||
|
// - all the images in thumbnail version, and link to large version
|
||||||
|
[
|
||||||
|
(ad.images && ad.images.thumb_url && `<img referrerpolicy="no-referrer" src="${ad.images.thumb_url}">`) || '',
|
||||||
|
'',
|
||||||
|
...ad.body.split(/\n/),
|
||||||
|
'',
|
||||||
|
((ad.images && ad.images.urls_thumb && ad.images.urls_thumb.map((url, i) => `<a href=${ad.images.urls_large[i]}><img referrerpolicy="no-referrer" src="${url}"></a>`)) || []).join(''),
|
||||||
|
].join('<br>');
|
||||||
|
|
||||||
|
module.exports = async (ctx) => {
|
||||||
|
const query = ctx.params.query;
|
||||||
|
|
||||||
|
const response = await got({
|
||||||
|
headers: {
|
||||||
|
api_key: API_KEY,
|
||||||
|
origin: 'https://www.leboncoin.fr',
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
url: SEARCH_ENDPOINT,
|
||||||
|
body: JSON.stringify({ filters: convertQueryToFilters(query), limit: 50, limit_alu: 3, sort_by: 'time', sort_order: 'desc' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.state.data = {
|
||||||
|
title: `ads for ${query.replace(/&/g, ' ')}`,
|
||||||
|
description: `ads for ${query.replace(/&/g, ' ')}`,
|
||||||
|
link: `https://www.leboncoin.fr/recherche/?${query}`,
|
||||||
|
language: 'fr',
|
||||||
|
item: (response.data.ads || [])
|
||||||
|
.filter((ad) => ad.status === 'active')
|
||||||
|
.map((ad) => ({
|
||||||
|
title: (ad.price && ad.price[0] ? `${ad.price[0]}€ - ` : '') + ad.subject,
|
||||||
|
description: buildDescription(ad),
|
||||||
|
pubDate: new Date(ad.first_publication_date).toUTCString(),
|
||||||
|
link: ad.url,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user