mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-08 22:19:40 +08:00
feat: add kuaidi100 (#4020)
This commit is contained in:
@@ -531,3 +531,17 @@ type 为 all 时,category 参数不支持 cost 和 free
|
|||||||
### はてな匿名ダイアリー - 人気記事アーカイブ
|
### はてな匿名ダイアリー - 人気記事アーカイブ
|
||||||
|
|
||||||
<Route author="masakichi" example="/hatena/anonymous_diary/archive" path="/hatena/anonymous_diary/archive"/>
|
<Route author="masakichi" example="/hatena/anonymous_diary/archive" path="/hatena/anonymous_diary/archive"/>
|
||||||
|
|
||||||
|
## 快递 100
|
||||||
|
|
||||||
|
### 快递订单追踪
|
||||||
|
|
||||||
|
<Route author="NeverBehave" example="/kuaidi100/track/shunfeng/SF1007896781640/0383" path="/track/:number/:id/:phone?" :paramsDesc="['快递公司代号', '订单号', '手机号后四位(仅顺丰)']">
|
||||||
|
|
||||||
|
快递公司代号如果不能确定,可通过下方快递列表获得。
|
||||||
|
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
### 支持的快递公司列表
|
||||||
|
|
||||||
|
<Route author="NeverBehave" example="/kuaidi100/company" path="/kuaidi100/company"/>
|
||||||
|
|||||||
@@ -2260,6 +2260,10 @@ router.get('/haohaozhu/discover/:keyword?', require('./routes/haohaozhu/discover
|
|||||||
// 东北大学
|
// 东北大学
|
||||||
router.get('/neu/news/:type', require('./routes/universities/neu/news'));
|
router.get('/neu/news/:type', require('./routes/universities/neu/news'));
|
||||||
|
|
||||||
|
// 快递100
|
||||||
|
router.get('/kuaidi100/track/:number/:id/:phone?', require('./routes/kuaidi100/index'));
|
||||||
|
router.get('/kuaidi100/company', require('./routes/kuaidi100/supported_company'));
|
||||||
|
|
||||||
// 稻草人书屋
|
// 稻草人书屋
|
||||||
router.get('/dcrsw/:name/:count?', require('./routes/novel/dcrsw'));
|
router.get('/dcrsw/:name/:count?', require('./routes/novel/dcrsw'));
|
||||||
|
|
||||||
|
|||||||
51
lib/routes/kuaidi100/index.js
Normal file
51
lib/routes/kuaidi100/index.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
module.exports = async (ctx) => {
|
||||||
|
// number is shorthand for company
|
||||||
|
// id is ticket number
|
||||||
|
// phone for shunfeng :)
|
||||||
|
const { number, id, phone } = ctx.params;
|
||||||
|
|
||||||
|
// I am doing these to avoid invaild request.
|
||||||
|
// First, check if code is vaild
|
||||||
|
const { status, message, company } = await utils.checkCode(ctx, number, id, phone);
|
||||||
|
|
||||||
|
let data;
|
||||||
|
let query;
|
||||||
|
const time = new Date().toString();
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
query = await utils.getQuery(ctx, number, id, phone);
|
||||||
|
if (query.status !== '200') {
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
context: query.message,
|
||||||
|
time,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
data = query.data;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
context: message,
|
||||||
|
time,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe we can look into isCheck, condition, and state :)
|
||||||
|
// But I just want to make it work for now.
|
||||||
|
ctx.state.data = {
|
||||||
|
title: `快递 ${company.name}-${number}`,
|
||||||
|
link: 'https://www.kuaidi100.com',
|
||||||
|
description: `快递 ${company.name}-${number}`,
|
||||||
|
item: data.map((item) => ({
|
||||||
|
title: item.context,
|
||||||
|
description: item.context,
|
||||||
|
pubDate: new Date(item.time || item.ftime).toUTCString(),
|
||||||
|
link: 'https://www.kuaidi100.com',
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
};
|
||||||
17
lib/routes/kuaidi100/supported_company.js
Normal file
17
lib/routes/kuaidi100/supported_company.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
module.exports = async (ctx) => {
|
||||||
|
const ls = await utils.company(ctx);
|
||||||
|
ctx.state.data = {
|
||||||
|
title: `快递100 快递列表`,
|
||||||
|
link: 'https://www.kuaidi100.com',
|
||||||
|
description: `快递100 所支持的快递列表及其查询名称`,
|
||||||
|
item: ls.map((item) => ({
|
||||||
|
title: item.name,
|
||||||
|
description: item.number,
|
||||||
|
category: item.comTypeName,
|
||||||
|
pubDate: new Date().toUTCString(),
|
||||||
|
link: item.siteUrl,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
};
|
||||||
216
lib/routes/kuaidi100/utils.js
Normal file
216
lib/routes/kuaidi100/utils.js
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
const got = require('@/utils/got');
|
||||||
|
const wwwid_key = 'kuaidi100-wwwid';
|
||||||
|
const csrf_key = 'kuaidi100-csrf';
|
||||||
|
const query_count = 'kuaidi100-cookie-count';
|
||||||
|
const max_query_count = 30;
|
||||||
|
|
||||||
|
async function getCookie(ctx) {
|
||||||
|
// Check if this key should be replace? every 30 times should be fine.
|
||||||
|
await shouldUpdateCookie(ctx);
|
||||||
|
let wwwid = await ctx.cache.get(wwwid_key);
|
||||||
|
let csrf = await ctx.cache.get(csrf_key);
|
||||||
|
if (!wwwid || !csrf) {
|
||||||
|
const indexResponse = await got({
|
||||||
|
method: 'get',
|
||||||
|
url: 'https://www.kuaidi100.com/?from=appstore',
|
||||||
|
headers: {
|
||||||
|
// App store
|
||||||
|
Referer: 'https://apps.apple.com/cn/app/%E5%BF%AB%E9%80%92100-%E5%8F%8C11%E5%AF%84%E4%BB%B6%E9%80%80%E8%B4%A7-%E4%B8%8A%E5%BF%AB%E9%80%92100/id458270120',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const set_cookie = indexResponse.headers['set-cookie'];
|
||||||
|
if (set_cookie) {
|
||||||
|
for (const e of set_cookie) {
|
||||||
|
if (e.indexOf('WWWID') === 0) {
|
||||||
|
wwwid = e.split(';')[0];
|
||||||
|
} else if (e.indexOf('csrftoken') === 0) {
|
||||||
|
csrf = e.split(';')[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.cache.set(wwwid_key, wwwid, 600);
|
||||||
|
ctx.cache.set(csrf_key, csrf, 600);
|
||||||
|
// We have acquired new cookie. It may due to cache timeout.
|
||||||
|
// Force update counter and don't wait it finished.
|
||||||
|
shouldUpdateCookie(ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
wwwid,
|
||||||
|
csrf,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example Company
|
||||||
|
{
|
||||||
|
"available": false, <- 这个不知道是什么的可用性,反正不是可查询的
|
||||||
|
"canOrder": false,
|
||||||
|
"comTypeName": "国内运输商",
|
||||||
|
"contactTel": "626-818-2750",
|
||||||
|
"createTime": 1486202888000,
|
||||||
|
"hasNetwork": false,
|
||||||
|
"id": 7172420,
|
||||||
|
"name": "金岸物流",
|
||||||
|
"number": "jinan",
|
||||||
|
"shortName": "金岸物流",
|
||||||
|
"shortNumber": "jinan",
|
||||||
|
"siteUrl": "http://www.gpl-express.com/",
|
||||||
|
"type": 0,
|
||||||
|
"version": 776
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async function getCompanyList(ctx) {
|
||||||
|
// Using date as cache key and it will automatically expired by 1d
|
||||||
|
const key = `kuaidi100-company-name-${new Date().toISOString().split('T')[0]}`;
|
||||||
|
let list = await ctx.cache.get(key);
|
||||||
|
if (!list) {
|
||||||
|
const cookie = await getCookie(ctx);
|
||||||
|
const wwwid = cookie.wwwid;
|
||||||
|
const companyResponse = await got({
|
||||||
|
method: 'post',
|
||||||
|
url: 'https://www.kuaidi100.com/company.do?method=js&t=201701051440',
|
||||||
|
headers: {
|
||||||
|
Referer: 'https://www.kuaidi100.com/',
|
||||||
|
Cookie: wwwid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
list = companyResponse.body;
|
||||||
|
|
||||||
|
// Parsing the js file
|
||||||
|
try {
|
||||||
|
list = list
|
||||||
|
.substr(12)
|
||||||
|
.replace(/};/g, '}')
|
||||||
|
.replace(/'/g, '"');
|
||||||
|
list = JSON.parse(list);
|
||||||
|
list = list.company;
|
||||||
|
} catch (e) {
|
||||||
|
list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.cache.set(key, list);
|
||||||
|
} else {
|
||||||
|
list = JSON.parse(list);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function shouldUpdateCookie(ctx, forcedUpdate = false) {
|
||||||
|
if (forcedUpdate) {
|
||||||
|
ctx.cache.set(query_count, 0);
|
||||||
|
} else {
|
||||||
|
const count = ctx.cache.get(query_count);
|
||||||
|
if (!count) {
|
||||||
|
ctx.cache.set(query_count, 1);
|
||||||
|
} else {
|
||||||
|
if (count > max_query_count) {
|
||||||
|
ctx.cache.set(query_count, 0);
|
||||||
|
await clearCookie(ctx);
|
||||||
|
} else {
|
||||||
|
ctx.cache.set(query_count, count + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearCookie(ctx) {
|
||||||
|
ctx.cache.set(wwwid_key, null);
|
||||||
|
ctx.cache.set(csrf_key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
company: async (ctx) => await getCompanyList(ctx),
|
||||||
|
checkCode: async (ctx, number, id, phone) => {
|
||||||
|
const list = await getCompanyList(ctx);
|
||||||
|
const company = list.find((c) => c.number === number);
|
||||||
|
if (!company) {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
message: '快递公司编号不受支持!',
|
||||||
|
company: {
|
||||||
|
name: '未知',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (number.indexOf('shunfeng') !== -1 && !isNaN(phone) && String(phone).length !== 4) {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
message: '顺丰查询需要手机号后四位!',
|
||||||
|
company,
|
||||||
|
};
|
||||||
|
} else if (company.checkReg) {
|
||||||
|
return {
|
||||||
|
status: true,
|
||||||
|
regex: new RegExp(company.checkReg).test(id),
|
||||||
|
company,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
status: true,
|
||||||
|
regex: undefined,
|
||||||
|
company,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example Query
|
||||||
|
{
|
||||||
|
"message": "ok",
|
||||||
|
"nu": "73123917441103",
|
||||||
|
"ischeck": "1",
|
||||||
|
"condition": "F00",
|
||||||
|
"com": "zhongtong",
|
||||||
|
"status": "200",
|
||||||
|
"state": "3",
|
||||||
|
"data": [{
|
||||||
|
"time": "2019-12-12 12:41:15",
|
||||||
|
"ftime": "2019-12-12 12:41:15",
|
||||||
|
"context": "【温州市】 快件已由【菜鸟的温州燎原华庭7栋103号店】代签收, 如有问题请电联(18581563547 / 4001787878), 感谢您使用中通快递, 期待再次为您服务!",
|
||||||
|
"location": ""
|
||||||
|
}
|
||||||
|
...]
|
||||||
|
Example failed Query
|
||||||
|
{
|
||||||
|
"message": "快递公司参数异常:验证码错误",
|
||||||
|
"nu": "",
|
||||||
|
"ischeck": "0",
|
||||||
|
"condition": "",
|
||||||
|
"com": "",
|
||||||
|
"status": "408",
|
||||||
|
"state": "0",
|
||||||
|
"data": []
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getQuery: async (ctx, number, id, phone) => {
|
||||||
|
const query_key = `kuaidi100-query-${number}-${id}`;
|
||||||
|
let query = await ctx.cache.get(query_key);
|
||||||
|
if (!query) {
|
||||||
|
const cookie = await getCookie(ctx);
|
||||||
|
const queryResponse = await got({
|
||||||
|
method: 'get',
|
||||||
|
url: `https://www.kuaidi100.com/query?type=${number}&postid=${id}&temp=${Math.random()}&phone=${phone ? phone : ''}`,
|
||||||
|
headers: {
|
||||||
|
Referer: 'https://www.kuaidi100.com/',
|
||||||
|
Cookie: `${cookie.csrf}; ${cookie.wwwid}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
query = queryResponse.data;
|
||||||
|
if (query.ischeck === '0' && query.status === '200') {
|
||||||
|
// Not yet complete, don't cache for now.
|
||||||
|
// Per owner suggestion
|
||||||
|
} else {
|
||||||
|
ctx.cache.set(query_key, query); // Finished, or error id
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query = JSON.parse(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user