移除axios依赖,替换百度翻译为有道翻译。指令表支持关键词筛选。 (#434)

* feat: add support for ‘greeting’ and ‘global reply mode’ commands, improve variable naming and remove unnecessary backend output.

* feat: Add support for black and white lists, global reply mode and voice role settings, private chat switch, and active greeting configuration. Refactor some variable names and comment out redundant code for better readability and reduced backend output.

* feat: 为新功能完善了帮助面板

* docs: 完善了‘打招呼’的帮助说明

* Commit Type: feat, bugfix

Add functionality to view plugin command table, fix bug in blacklist/whitelist, and fix bug where chat mode can still be used in private messaging when disabled.

* Commit Type: feat, bugfix

Add functionality to view plugin command table, fix bug in blacklist/whitelist, and fix bug where chat mode can still be used in private messaging when disabled.

* refactor: Remove redundant log output.

* Refactor: optimize code logic

* Fix: 修复绘图指令表被抢指令的bug。

* Refactor:1. Add support for automatically translating replies to Japanese and generating voice messages in VITS voice mode (please monitor remaining quota after enabling). 2. Add translation function. 3. Add emotion configuration for Azure voice mode, allowing the robot to select appropriate emotional styles for replies.

* Refactor:Handle the issue of exceeding character setting limit caused by adding emotion configuration.

* Fix: fix bugs

* Refactor: Added error feedback to translation service

* Refactor: Added support for viewing the list of supported roles for each language mode, and fixed some bugs in the emotion switching feature of the auzre mode.

* Refactor: Optimized some command feedback and added owner restriction to chat record export function.

* Refactor: Optimized feedback when viewing role list to avoid excessive messages.

* Refactor: Optimized feedback when configuring multi-emotion mode.

* Feature: Added help instructions for translation feature.

* chore: Adjust help instructions for mood settings

* Fix: Fixed issue where only first line of multi-line replies were being read and Azure voice was pronouncing punctuation marks.

* Fix: Fixed bug where switching to Azure voice mode prompted for missing key and restricted ability to view voice role list to only when in voice mode.

* Refactor: Add image OCR function and support translation for both quoted text and image.

* fix: Fix issue with error caused by non-image input.

* Refactor: Optimize code to filter emojis that cannot be displayed properly in claude mode.

* Refactor: Optimize some code structures.

* fix: Fix the bug of returning only one result when entering multiple lines of text on Windows system.

* Refactor: Optimize code logic for better user experience

* Refactor: Fix the conflict issue with other plugin translation commands

* Refactor: Replace Baidu Translation with Youdao Translation to eliminate configuration steps; optimize translation experience; add missing dependency prompts instead of causing program errors.Optimize the experience of switching between voice mode and setting global reply mode.

* Refactor: Remove unused files and dependencies in the project.

* Feature: Add Youdao translation service to provide more comprehensive translation support.

* Refactor: Optimize translation experience

* Refactor: Optimize translation experience

* Feature: Add functionality of keyword search command

* Feature: Add functionality of keyword search command.

* Refactor: Remove redundant code

---------

Co-authored-by: Sean <1519059137@qq.com>
Co-authored-by: ikechan8370 <geyinchibuaa@gmail.com>
This commit is contained in:
Sean Murphy 2023-05-18 18:03:43 +08:00 committed by GitHub
parent 82b83bf015
commit f20248a805
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 363 additions and 260 deletions

View file

@ -1,141 +0,0 @@
import md5 from 'md5-node'
import axios from 'axios'
// noinspection NonAsciiCharacters
export const transMap = { : 'zh', : 'jp', : 'wyw', : 'en', : 'ru', : 'kr' }
const errOr = {
52001: '请求超时,请重试。',
52002: '系统错误,请重试。',
52003: '未授权用户请检查appid是否正确或者服务是否开通。',
54000: '必填参数为空,请检查是否少传参数。',
54001: '签名错误,请检查您的签名生成方法。',
54003: '访问频率受限,请降低您的调用频率,或进行身份认证后切换为高级版/尊享版。',
54004: '账户余额不足,请前往管理控制台为账户充值。',
54005: '长query请求频繁请降低长query的发送频率3s后再试。',
58000: '客户端IP非法检查个人资料里填写的IP地址是否正确可前往开发者信息-基本信息修改。',
58001: '译文语言方向不支持,检查译文语言是否在语言列表里。',
58002: '服务当前已关闭,请前往管理控制台开启服务。',
90107: '认证未通过或未生效,请前往我的认证查看认证进度。'
}
function Translate (config) {
this.requestNumber = 0 // 请求次数
this.config = {
showProgress: 1, // 是否显示进度
requestNumber: 1, // 最大请求次数
agreement: 'http', // 协议
...config
}
this.baiduApi = `${this.config.agreement}://api.fanyi.baidu.com/api/trans/vip/translate`
// 拼接url
this.createUrl = (domain, form) => {
let result = domain + '?'
for (let key in form) {
result += `${key}=${form[key]}&`
}
return result.slice(0, result.length - 1)
}
this.translate = async (value, ...params) => {
let result = ''
let from = 'auto'
let to = 'en'
if (params.length === 1) {
to = transMap[params[0]] || to
} else if (params.length === 2) {
from = transMap[params[0]] || from
to = transMap[params[1]] || to
}
if (typeof value === 'string') {
const res = await this.requestApi(value, { from, to })
result = res[0].dst
}
if (Array.isArray(value) || Object.prototype.toString.call(value) === '[object Object]') {
result = await this._createObjValue(value, { from, to })
}
return result
}
this.requestApi = (value, params) => {
if (this.requestNumber >= this.config.requestNumber) {
return new Promise((resolve) => {
setTimeout(() => {
this.requestApi(value, params).then((res) => {
resolve(res)
})
}, 1000)
})
}
this.requestNumber++
const { appid, secret } = this.config
const q = value
const salt = Math.random()
const sign = md5(`${appid}${q}${salt}${secret}`)
const fromData = {
q: encodeURIComponent(q),
sign,
appid,
salt,
from: params.from || 'auto',
to: params.to || 'en'
}
const fanyiApi = this.createUrl(this.baiduApi, fromData)
return new Promise((resolve, reject) => {
axios
.get(fanyiApi)
.then(({ data: res }) => {
if (!res.error_code) {
const resList = res.trans_result
resolve(resList)
} else {
const errCode = res.error_code
if (errOr[errCode]) {
reject(new Error('翻译出错了~' + errOr[errCode]))
} else {
reject(new Error('翻译出错了~' + errCode))
}
}
})
.finally(() => {
setTimeout(() => {
this.requestNumber--
}, 1000)
})
})
}
// 递归翻译数组或对象
this._createObjValue = async (value, parames) => {
let index = 0
const obj = Array.isArray(value) ? [] : {}
const strDatas = Array.isArray(value) ? value : Object.values(value)
const reqData = strDatas
.filter((item) => typeof item === 'string') // 过滤字符串
.join('\n')
const res = reqData ? await this.requestApi(reqData, parames) : []
for (let key in value) {
if (typeof value[key] === 'string') {
obj[key] = res[index].dst
index++
}
if (
Array.isArray(value[key]) ||
Object.prototype.toString.call(value[key]) === '[object Object]'
) {
obj[key] = await this.translate(value[key], parames) // 递归翻译
}
}
return obj
}
return this.translate
}
export default Translate

View file

@ -119,8 +119,6 @@ const defaultConfig = {
azureTTSSpeaker: 'zh-CN-XiaochenNeural',
voicevoxSpace: '',
voicevoxTTSSpeaker: '护士机器子T',
baiduTranslateAppId: '',
baiduTranslateSecret: '',
azureTTSEmotion: false,
enhanceAzureTTSEmotion: false,
autoJapanese: false,

97
utils/translate.js Normal file
View file

@ -0,0 +1,97 @@
import md5 from 'md5'
import _ from 'lodash'
// 代码参考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 translate (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
}
}