chatgpt-plugin/utils/translate.js
2024-03-08 14:38:39 +08:00

213 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import md5 from 'md5'
import _ from 'lodash'
import { Config } from './config.js'
import { ChatGPTAPI } from './openai/chatgpt-api.js'
import { newFetch } from './proxy.js'
import { CustomGoogleGeminiClient } from '../client/CustomGoogleGeminiClient.js'
import XinghuoClient from './xinghuo/xinghuo.js'
import { QwenApi } from './alibaba/qwen-api.js'
// 代码参考https://github.com/yeyang52/yenai-plugin/blob/b50b11338adfa5a4ef93912eefd2f1f704e8b990/model/api/funApi.js#L25
export const translateLangSupports = [
{ code: 'ar', label: '阿拉伯语', abbr: '阿', alphabet: 'A' },
{ code: 'de', label: '德语', abbr: '德', alphabet: 'D' },
{ code: 'ru', label: '俄语', abbr: '俄', alphabet: 'E' },
{ code: 'fr', label: '法语', abbr: '法', alphabet: 'F' },
{ code: 'ko', label: '韩语', abbr: '韩', alphabet: 'H' },
{ code: 'nl', label: '荷兰语', abbr: '荷', alphabet: 'H' },
{ code: 'pt', label: '葡萄牙语', abbr: '葡', alphabet: 'P' },
{ code: 'ja', label: '日语', abbr: '日', alphabet: 'R' },
{ code: 'th', label: '泰语', abbr: '泰', alphabet: 'T' },
{ code: 'es', label: '西班牙语', abbr: '西', alphabet: 'X' },
{ code: 'en', label: '英语', abbr: '英', alphabet: 'Y' },
{ code: 'it', label: '意大利语', abbr: '意', alphabet: 'Y' },
{ code: 'vi', label: '越南语', abbr: '越', alphabet: 'Y' },
{ code: 'id', label: '印度尼西亚语', abbr: '印', alphabet: 'Y' },
{ code: 'zh-CHS', label: '中文', abbr: '中', alphabet: 'Z' }
]
const API_ERROR = '出了点小问题,待会再试试吧'
export async function translateOld (msg, to = 'auto') {
let from = 'auto'
if (to !== 'auto') to = translateLangSupports.find(item => item.abbr == to)?.code
if (!to) return `未找到翻译的语种,支持的语言为:\n${translateLangSupports.map(item => item.abbr).join('')}\n`
// 翻译结果为空的提示
const RESULT_ERROR = '找不到翻译结果'
// API 请求错误提示
const API_ERROR = '翻译服务暂不可用,请稍后再试'
const qs = (obj) => {
let res = ''
for (const [k, v] of Object.entries(obj)) { res += `${k}=${encodeURIComponent(v)}&` }
return res.slice(0, res.length - 1)
}
const appVersion = '5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4750.0'
const payload = {
from,
to,
bv: md5(appVersion),
client: 'fanyideskweb',
doctype: 'json',
version: '2.1',
keyfrom: 'fanyi.web',
action: 'FY_BY_DEFAULT',
smartresult: 'dict'
}
const headers = {
Host: 'fanyi.youdao.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102',
Referer: 'https://fanyi.youdao.com/',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Cookie: 'OUTFOX_SEARCH_USER_ID_NCOO=133190305.98519628; OUTFOX_SEARCH_USER_ID="2081065877@10.169.0.102";'
}
const api = 'https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
const key = 'Ygy_4c=r#e#4EX^NUGUc5'
try {
if (Array.isArray(msg)) {
const results = []
for (let i = 0; i < msg.length; i++) {
const item = msg[i]
const lts = '' + new Date().getTime()
const salt = lts + parseInt(String(10 * Math.random()), 10)
const sign = md5(payload.client + item + salt + key)
const postData = qs(Object.assign({ i: item, lts, sign, salt }, payload))
let { errorCode, translateResult } = await fetch(api, {
method: 'POST',
body: postData,
headers
}).then(res => res.json()).catch(err => console.error(err))
if (errorCode !== 0) return API_ERROR
translateResult = _.flattenDeep(translateResult)?.map(item => item.tgt).join('\n')
if (!translateResult) results.push(RESULT_ERROR)
else results.push(translateResult)
}
return results
} else {
const i = msg // 翻译的内容
const lts = '' + new Date().getTime()
const salt = lts + parseInt(String(10 * Math.random()), 10)
const sign = md5(payload.client + i + salt + key)
const postData = qs(Object.assign({ i, lts, sign, salt }, payload))
let { errorCode, translateResult } = await fetch(api, {
method: 'POST',
body: postData,
headers
}).then(res => res.json()).catch(err => console.error(err))
if (errorCode !== 0) return API_ERROR
translateResult = _.flattenDeep(translateResult)?.map(item => item.tgt).join('\n')
if (!translateResult) return RESULT_ERROR
return translateResult
}
} catch (err) {
return API_ERROR
}
}
/**
*
* @param msg 要翻译的
* @param from 语种
* @param to 语种
* @param ai ai来源支持openai, gemini, xh, qwen
* @returns {Promise<*|string>}
*/
export async function translate (msg, to = 'auto', from = 'auto', ai = Config.translateSource) {
try {
let lang = '中'
if (to !== 'auto') {
lang = translateLangSupports.find(item => item.abbr == to)?.code
}
if (!lang) return `未找到翻译的语种,支持的语言为:\n${translateLangSupports.map(item => item.abbr).join('')}\n`
// if ai is not in the list, throw error
if (!['openai', 'gemini', 'xh', 'qwen'].includes(ai)) throw new Error('ai来源错误')
let system = `You will be provided with a sentence in the language with language code [${from}], and your task is to translate it into [${lang}]. Just print the result without any other words.`
if (Array.isArray(msg)) {
let result = []
for (let i = 0; i < msg.length; i++) {
let item = msg[i]
let res = await translate(item, to, from, ai)
result.push(res)
}
return result
}
switch (ai) {
case 'openai': {
let api = new ChatGPTAPI({
apiBaseUrl: Config.openAiBaseUrl,
apiKey: Config.apiKey,
fetch: newFetch
})
const res = await api.sendMessage(msg, {
systemMessage: system,
completionParams: {
model: 'gpt-3.5-turbo'
}
})
return res.text
}
case 'gemini': {
let client = new CustomGoogleGeminiClient({
key: Config.geminiKey,
model: Config.geminiModel,
baseUrl: Config.geminiBaseUrl,
debug: Config.debug
})
let option = {
stream: false,
onProgress: (data) => {
if (Config.debug) {
logger.info(data)
}
},
system
}
let res = await client.sendMessage(msg, option)
return res.text
}
case 'xh': {
let client = new XinghuoClient({
ssoSessionId: Config.xinghuoToken
})
let response = await client.sendMessage(msg, { system })
return response.text
}
case 'qwen': {
let completionParams = {
parameters: {
top_p: Config.qwenTopP || 0.5,
top_k: Config.qwenTopK || 50,
seed: Config.qwenSeed > 0 ? Config.qwenSeed : Math.floor(Math.random() * 114514),
temperature: Config.qwenTemperature || 1,
enable_search: !!Config.qwenEnableSearch
}
}
if (Config.qwenModel) {
completionParams.model = Config.qwenModel
}
let opts = {
apiKey: Config.qwenApiKey,
debug: false,
systemMessage: system,
completionParams,
fetch: newFetch
}
let client = new QwenApi(opts)
let option = {
timeoutMs: 600000,
completionParams
}
let result
try {
result = await client.sendMessage(msg, option)
} catch (err) {
logger.error(err)
throw new Error(err)
}
return result.text
}
}
} catch (e) {
logger.error(e)
logger.info('基于LLM的翻译失败转用老版翻译')
return await translateOld(msg, to)
}
}