From 2fb9615da64a11bfbe2e9c56398ba0bb300382d6 Mon Sep 17 00:00:00 2001 From: Yesterday17 Date: Tue, 11 Aug 2020 01:28:55 +0800 Subject: [PATCH 1/2] fix: caiyun api and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用网页版的请求方式以绕过测试 token 的使用限额 --- config/caiyunApi.js | 98 ++++++++++++++++++++++++ config/config.json | 9 +-- src/main/translate/ExternalApi.ts | 3 + test/unit/specs/main/Api.spec.ts | 36 ++++----- test/unit/specs/main/ExternalApi.spec.ts | 4 +- 5 files changed, 121 insertions(+), 29 deletions(-) create mode 100644 config/caiyunApi.js diff --git a/config/caiyunApi.js b/config/caiyunApi.js new file mode 100644 index 0000000..f15ac10 --- /dev/null +++ b/config/caiyunApi.js @@ -0,0 +1,98 @@ +var JWT_GENERATE_URL = + "https://api.interpreter.caiyunai.com/v1/user/jwt/generate"; +var TRANSLATE_URL = "https://api.interpreter.caiyunai.com/v1/translator"; +var ORIGIN_URL = "https://fanyi.caiyunapp.com"; +var USER_AGENT = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36"; +var AUTH_TOKEN = "token:qgemv4jr1y38jyq6vhvi"; + +//////////////////////////////////////////////////////// + +var jwt; +var browserId; + +if (!browserId) { + browserId = randomBrowserId(); +} + +jwtCheck() + .then(() => translate()) + .catch((e) => callback(`error: ${e}`)); + +//////////////////////////////////////////////////////// + +function jwtCheck() { + if ( + !jwt || + JSON.parse(Buffer.from(jwt.split(".")[1], "base64")).exp < Date.now / 1000 + ) { + return renewJWT().then((t) => (jwt = t)); + } else { + return Promise.resolve(); + } +} + +function renewJWT() { + return Request.post(JWT_GENERATE_URL, { + headers: { + Origin: ORIGIN_URL, + Referer: ORIGIN_URL, + "User-Agent": USER_AGENT, + "X-Authorization": AUTH_TOKEN, + }, + body: { + browser_id: browserId, + }, + json: true, + }).then((j) => j.jwt); +} + +function translate() { + return Request.post(TRANSLATE_URL, { + gzip: true, + headers: { + Origin: ORIGIN_URL, + Referer: ORIGIN_URL, + "T-Authorization": jwt, + "X-Authorization": AUTH_TOKEN, + "User-Agent": USER_AGENT, + }, + body: { + source: text, + trans_type: "ja2zh", + request_id: "web_fanyi", + media: "text", + os_type: "web", + dict: false, + cached: false, + replaced: true, + browser_id: browserId, + }, + json: true, + }).then((json) => { + callback(decrypt(json.target)); + }); +} + +//////////////////////////////////////////////////////// + +function randomBrowserId() { + var characters = "abcdefghijklmnopqrstuvwxyz0123456789"; + var result = ""; + for (var i = 0; i < 32; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; +} + +function rocn(str, n = 13) { + return str.replace(/[a-zA-Z]/g, function(c) { + return String.fromCharCode( + (c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + n) ? c : c - 26 + ); + }); +} + +function decrypt(str) { + return base64toutf8(rocn(str)); +} diff --git a/config/config.json b/config/config.json index 2f7df30..1da770f 100644 --- a/config/config.json +++ b/config/config.json @@ -50,12 +50,9 @@ }, { "enable": false, - "method": "POST", - "name": "彩云", - "requestBodyFormat": "J{\"source\": %TEXT%, \"trans_type\": \"ja2zh\", \"request_id\": \"demo\", \"detect\": \"true\"}", - "requestHeaders": "{\"X-Authorization\": \"token 3975l6lr5pcbvidl6jl2\"}", - "responseBodyPattern": "J%RESPONSE%.target", - "url": "https://api.interpreter.caiyunai.com/v1/translator" + "external": true, + "jsFile": "config\\caiyunApi.js", + "name": "彩云" }, { "enable": false, diff --git a/src/main/translate/ExternalApi.ts b/src/main/translate/ExternalApi.ts index c6d5a21..04deef6 100644 --- a/src/main/translate/ExternalApi.ts +++ b/src/main/translate/ExternalApi.ts @@ -69,6 +69,9 @@ export default class ExternalApi implements yuki.Translator { const hash = crypto.createHash('md5') return hash.update(data).digest(encoding) }, + base64toutf8: (data: string): string => { + return Buffer.from(data, "base64").toString("utf-8"); + }, crypto: { createHash: crypto.createHash, createHmac: crypto.createHmac diff --git a/test/unit/specs/main/Api.spec.ts b/test/unit/specs/main/Api.spec.ts index a369f97..5f1a929 100644 --- a/test/unit/specs/main/Api.spec.ts +++ b/test/unit/specs/main/Api.spec.ts @@ -3,6 +3,10 @@ import Api from '../../../../src/main/translate/Api' import TranslationManager from '../../../../src/main/translate/TranslationManager' describe('Api', () => { + before(() => { + (global as any).__baseDir = __dirname + }) + it('gets translation from form and parses with regex', (done) => { const googleCN = new Api({ name: 'googleCN', @@ -16,7 +20,7 @@ describe('Api', () => { '悠真くんを攻略すれば210円か。なるほどなぁ…', (translation) => { try { - expect(translation).to.equal('如果捕获了尤马坤,则为210日元。我知道了 ...') + expect(translation).to.equal('如果您捕获尤马坤,它是210日元。我懂了...') } catch (e) { return done(e) } @@ -36,14 +40,10 @@ describe('Api', () => { enable: true }, { - enable: true, - method: 'POST', name: 'caiyun', - requestBodyFormat: - 'J{"source": %TEXT%, "trans_type": "ja2zh", "request_id": "demo", "detect": "true"}', - requestHeaders: '{"X-Authorization": "token 3975l6lr5pcbvidl6jl2"}', - responseBodyPattern: 'J%RESPONSE%.target', - url: 'https://api.interpreter.caiyunai.com/v1/translator' + external: true, + jsFile: '../../../../config/caiyunApi.js', + enable: true } ] @@ -56,11 +56,8 @@ describe('Api', () => { console.log(translations) try { expect(translations.original).to.equal('悠真くんを攻略すれば210円か。なるほどなぁ…') - expect(translations.translations.googleCN).to.equal('如果捕获了尤马坤,则为210日元。我知道了 ...') - expect(translations.translations.caiyun).to.be.oneOf([ - '攻下悠真的话是210日元吗。 原来如此', - "ERR: TypeError: Cannot read property 'target' of undefined" - ]) + expect(translations.translations.googleCN).to.equal('如果您捕获尤马坤,它是210日元。我懂了...') + expect(translations.translations.caiyun).to.equal('攻下悠真的话是210日元吗。原来如此') } catch (e) { return done(e) } @@ -120,14 +117,10 @@ describe('Api', () => { enable: true }, { - enable: false, - method: 'POST', name: 'caiyun', - requestBodyFormat: 'J{"source": %TEXT%, "trans_type": "ja2zh", ' + - '"request_id": "web_fanyi", "os_type": "web", ' + - '"dict": "false", "cached": "false", "replaced": "false"}', - responseBodyPattern: 'J%RESPONSE%.target', - url: 'https://api.interpreter.caiyunai.com/v1/translator' + external: true, + jsFile: '../../../../config/caiyunApi.js', + enable: true } ] @@ -140,7 +133,8 @@ describe('Api', () => { expect(translations).to.deep.equal({ original: '悠真くんを攻略すれば210円か。なるほどなぁ…', translations: { - googleCN: '如果捕获了尤马坤,则为210日元。我知道了 ...' + googleCN: '如果您捕获尤马坤,它是210日元。我懂了...', + caiyun: '攻下悠真的话是210日元吗。原来如此' } }) } catch (e) { diff --git a/test/unit/specs/main/ExternalApi.spec.ts b/test/unit/specs/main/ExternalApi.spec.ts index 0a7646d..161c303 100644 --- a/test/unit/specs/main/ExternalApi.spec.ts +++ b/test/unit/specs/main/ExternalApi.spec.ts @@ -10,7 +10,7 @@ describe('ExternalApi', () => { const qq = new ExternalApi({ enable: true, external: true, - jsFile: '..\\..\\temp\\qqApi.js', + jsFile: '../../temp/qqApi.js', name: 'qq' }) @@ -31,7 +31,7 @@ describe('ExternalApi', () => { const qq = new ExternalApi({ enable: true, external: true, - jsFile: '..\\..\\temp\\qqApi.js', + jsFile: '../../temp/qqApi.js', name: 'qq' }) From aed107514dc4dfc479d9f3316a095e2c9aadbbf0 Mon Sep 17 00:00:00 2001 From: Yesterday17 Date: Tue, 11 Aug 2020 22:20:28 +0800 Subject: [PATCH 2/2] fix: pass Buffer to context --- config/caiyunApi.js | 4 ++-- src/main/translate/ExternalApi.ts | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/config/caiyunApi.js b/config/caiyunApi.js index f15ac10..32b39ed 100644 --- a/config/caiyunApi.js +++ b/config/caiyunApi.js @@ -86,7 +86,7 @@ function randomBrowserId() { } function rocn(str, n = 13) { - return str.replace(/[a-zA-Z]/g, function(c) { + return str.replace(/[a-zA-Z]/g, function (c) { return String.fromCharCode( (c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + n) ? c : c - 26 ); @@ -94,5 +94,5 @@ function rocn(str, n = 13) { } function decrypt(str) { - return base64toutf8(rocn(str)); + return Buffer.from(rocn(str), "base64").toString("utf-8"); } diff --git a/src/main/translate/ExternalApi.ts b/src/main/translate/ExternalApi.ts index 04deef6..a3765b3 100644 --- a/src/main/translate/ExternalApi.ts +++ b/src/main/translate/ExternalApi.ts @@ -63,15 +63,13 @@ export default class ExternalApi implements yuki.Translator { private createVmContext () { this.responseVmContext = vm.createContext({ + Buffer, Request: request, text: '', md5: (data: string, encoding: crypto.HexBase64Latin1Encoding) => { const hash = crypto.createHash('md5') return hash.update(data).digest(encoding) }, - base64toutf8: (data: string): string => { - return Buffer.from(data, "base64").toString("utf-8"); - }, crypto: { createHash: crypto.createHash, createHmac: crypto.createHmac