feat(route): add ainvest (#13348)

* feat(route): add ainvest

* style: fix code style

* fix: feed title
This commit is contained in:
Tony
2023-09-21 08:57:26 +08:00
committed by GitHub
parent 81e36b80c3
commit 46d32af2c5
9 changed files with 199 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const { getHeaders, randomString, encryptAES, decryptAES } = require('./utils');
module.exports = async (ctx) => {
const key = randomString(16);
const { data: response } = await got.post('https://api.ainvest.com/gw/socialcenter/v1/edu/article/listArticle', {
headers: getHeaders(key),
searchParams: {
timestamp: Date.now(),
},
data: encryptAES(
JSON.stringify({
batch: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 30,
startId: null,
tags: {
in: ['markettrends', 'premarket', 'companyinsights', 'macro'],
and: ['web', 'creationplatform'],
},
}),
key
),
});
const { data } = JSON.parse(decryptAES(response, key));
const items = data.map((item) => ({
title: item.title,
description: item.content,
link: item.sourceUrl,
pubDate: parseDate(item.postDate, 'x'),
category: [item.nickName, ...item.tags.map((tag) => tag.code)],
}));
ctx.state.data = {
title: 'AInvest - Latest Articles',
link: 'https://www.ainvest.com/news',
language: 'en',
item: items,
};
};
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
'/article': ['TonyRL'],
'/news': ['TonyRL'],
};
+39
View File
@@ -0,0 +1,39 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const { getHeaders, randomString, decryptAES } = require('./utils');
module.exports = async (ctx) => {
const key = randomString(16);
const { data: response } = await got('https://api.ainvest.com/gw/news_f10/v1/newsFlash/getNewsData', {
headers: getHeaders(key),
searchParams: {
terminal: 'web',
tab: 'all',
page: 1,
size: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50,
lastId: '',
timestamp: Date.now(),
},
});
const { data } = JSON.parse(decryptAES(response, key));
const items = data.content.map((item) => ({
title: item.title,
description: item.content,
link: item.sourceUrl,
pubDate: parseDate(item.publishTime, 'x'),
category: item.tagList.map((tag) => tag.nameEn),
author: item.userInfo.nickname,
upvotes: item.likeCount,
comments: item.commentCount,
}));
ctx.state.data = {
title: 'AInvest - Latest News',
link: 'https://www.ainvest.com/news',
language: 'en',
item: items,
};
};
+19
View File
@@ -0,0 +1,19 @@
module.exports = {
'ainvest.com': {
_name: 'AInvest',
'.': [
{
title: 'Latest Article',
docs: 'https://docs.rsshub.app/finance#ainvest',
source: ['/news'],
target: '/ainvest/article',
},
{
title: 'Latest News',
docs: 'https://docs.rsshub.app/finance#ainvest',
source: ['/news'],
target: '/ainvest/news',
},
],
},
};
+4
View File
@@ -0,0 +1,4 @@
module.exports = (router) => {
router.get('/article', require('./article'));
router.get('/news', require('./news'));
};
+73
View File
@@ -0,0 +1,73 @@
const crypto = require('crypto');
const CryptoJS = require('crypto-js');
const { KJUR, KEYUTIL, hextob64 } = require('jsrsasign');
const publicKey =
'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCARnxLlrhTK28bEV7s2IROjT73KLSjfqpKIvV8L+Yhe4BrF0Ut4oOH728HZlbSF0C3N0vXZjLAFesoS4v1pYOjVCPXl920Lh2seCv82m0cK78WMGuqZTfA44Nv7JsQMHC3+J6IZm8YD53ft2d8mYBFgKektduucjx8sObe7eRyoQIDAQAB';
const randomString = (length) => {
if (length > 32) {
throw Error('Max length is 32.');
}
return uuidv4().replace(/-/g, '').substring(0, length);
};
const uuidv4 = () => crypto.randomUUID();
/**
* @param {string} str
* @returns {CryptoJS.lib.WordArray}
*/
const MD5 = (str) => CryptoJS.MD5(str);
const encryptAES = (data, key) => {
if (typeof key === 'string') {
key = MD5(key);
}
return CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(data), key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}).toString();
};
const decryptAES = (data, key) => {
if (typeof key === 'string') {
key = MD5(key);
}
return CryptoJS.AES.decrypt(data, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}).toString(CryptoJS.enc.Utf8);
};
const encryptRSA = (data) => {
// Original code:
// var n = new JSEncrypt();
// n.setPublicKey(pubKey);
// return n.encrypt(message);
// Note: Server will reject the public key if it's encrypted using crypto.publicEncrypt().
let pubKey = `-----BEGIN PUBLIC KEY-----${publicKey}-----END PUBLIC KEY-----`;
pubKey = KEYUTIL.getKey(pubKey);
return hextob64(KJUR.crypto.Cipher.encrypt(data, pubKey));
};
const getHeaders = (key) => {
const fingerPrint = uuidv4();
return {
'content-type': 'application/json',
'ovse-trace': uuidv4(),
callertype: 'USER',
fingerprint: encryptAES(fingerPrint, MD5(key)),
onetimeskey: encryptRSA(key),
timestamp: encryptAES(Date.now(), key),
referer: 'https://www.ainvest.com/',
};
};
module.exports = {
randomString,
encryptAES,
decryptAES,
getHeaders,
};
+1
View File
@@ -113,6 +113,7 @@
"jsdom": "22.1.0",
"json-bigint": "1.0.0",
"json5": "2.2.3",
"jsrsasign": "10.8.6",
"koa": "2.14.2",
"koa-basic-auth": "4.0.0",
"koa-favicon": "2.1.0",
+7
View File
@@ -95,6 +95,9 @@ dependencies:
json5:
specifier: 2.2.3
version: 2.2.3
jsrsasign:
specifier: 10.8.6
version: 10.8.6
koa:
specifier: 2.14.2
version: 2.14.2
@@ -5096,6 +5099,10 @@ packages:
verror: 1.10.0
dev: false
/jsrsasign@10.8.6:
resolution: {integrity: sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw==}
dev: false
/jwa@2.0.0:
resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
dependencies:
+10
View File
@@ -38,6 +38,16 @@
</Route>
## AInvest {#ainvest}
### Latest Article {#ainvest-latest-article}
<Route author="TonyRL" example="/ainvest/article" path="/ainvest/article" radar="1"/>
### Latest News {#ainvest-latest-news}
<Route author="TonyRL" example="/ainvest/news" path="/ainvest/news" radar="1"/>
## BigQuant {#bigquant}
### 专题报告 {#bigquant-zhuan-ti-bao-gao}