diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..59c75f5
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,32 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+
+jobs:
+ Build:
+ if: startsWith(github.ref, 'refs/tags/v')
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - run: bash release.sh ${{ github.ref }}
+
+ - name: Release
+ uses: softprops/action-gh-release@v1
+ with:
+ draft: false
+ files: |
+ dist/*.bobplugin
+ env:
+ GITHUB_TOKEN: ${{ secrets.GT_TOKEN }}
+
+ - run: rm -rf dist
+
+ - uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ branch: main
+ commit_message: new release!
diff --git a/README.md b/README.md
index ba47312..b8380b3 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,37 @@
-# bob-plugin-doubao-translate
-基于 豆包Doubao API 的文本翻译、文本润色、语法纠错 Bob 插件。
+
+
Doubao Translator Bob Plugin
+
+
+## 简介
+
+基于 [豆包Doubao API](https://www.volcengine.com/product/doubao) 的文本翻译、文本润色、语法纠错 Bob 插件。
+
+### 语言模型
+
+* `Doubao-pro-128k`(默认使用)
+* `Doubao-pro-32k`
+* `Doubao-pro-4k`
+* `Doubao-lite-128k`
+* `Doubao-lite-32k`
+* `Doubao-lite-4k`
+* `Doubao-embedding`
+* `Moonshot-v1-128k`
+* `Moonshot-v1-128k`
+* `Moonshot-v1-128k`
+
+## 使用方法
+
+1. 安装 [Bob](https://bobtranslate.com/guide/#%E5%AE%89%E8%A3%85) (版本 >= 1.8.0),一款 macOS 平台的翻译和 OCR 软件
+
+2. 下载此插件: [bob-plugin-doubao-translate.bobplugin](https://github.com/djx30103/bob-plugin-doubao-translate/releases/latest)
+
+3. 安装此插件
+
+4. 去 [火山方舟控制台](https://console.volcengine.com/ark) 开通管理(开通服务) -> 模型推理(为每个模型创建接入点) -> API Key管理(创建 API Key)
+
+5. 把 API Key、推理点ID 填入 Bob 偏好设置 > 服务 > 此插件配置界面对应的输入框中,选择你要使用的模型,点击保存即可。
+
+## 感谢
+
+本仓库参考部分其他优秀源码,感谢[bob-plugin-cohere](https://github.com/missuo/bob-plugin-cohere)、[bob-plugin-gemini-translate](https://github.com/BrianShenCC/bob-plugin-gemini-translate)。
+
diff --git a/appcast.json b/appcast.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/appcast.json
@@ -0,0 +1 @@
+{}
diff --git a/release.sh b/release.sh
new file mode 100644
index 0000000..55a33c5
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+version=${1#refs/tags/v}
+zip -r -j bob-plugin-doubao-translate-$version.bobplugin src/*
+
+sha256_doubao=$(sha256sum bob-plugin-doubao-translate-$version.bobplugin | cut -d ' ' -f 1)
+echo $sha256_doubao
+
+download_link="https://github.com/djx30103/bob-plugin-doubao-translate/releases/download/v$version/bob-plugin-doubao-translate-$version.bobplugin"
+
+new_version="{\"version\": \"$version\", \"desc\": \"None\", \"sha256\": \"$sha256_doubao\", \"url\": \"$download_link\", \"minBobVersion\": \"1.8.0\"}"
+
+json_file='appcast.json'
+json_data=$(cat $json_file)
+
+updated_json=$(echo $json_data | jq --argjson new_version "$new_version" '.versions = [$new_version] + .versions')
+
+echo $updated_json > $json_file
+mkdir dist
+mv *.bobplugin dist
diff --git a/src/http.js b/src/http.js
new file mode 100644
index 0000000..9f921b2
--- /dev/null
+++ b/src/http.js
@@ -0,0 +1,70 @@
+var utils = require("./utils.js");
+
+const defaultUrl = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";
+const streamRequest = async ({ url, headers, body, query }) => {
+ let resultText = "";
+ return $http.streamRequest({
+ method: "POST",
+ url: url,
+ header: headers,
+ body: body,
+ streamHandler: (stream) => {
+
+ let streamText = stream.text;
+ const dataReg = /^data: /gm;
+ const doneReg = /\s*\[DONE\]\s*/;
+
+ // 使用正则表达式将 streamText 按 "data: " 分割为多个块
+ const dataBlocks = streamText.split(dataReg);
+
+ dataBlocks.forEach((block) => {
+ // 去除首尾空格和换行符
+ block = block.trim();
+
+ // 检测和移除 [DONE] 标记
+ if (doneReg.test(block)) {
+ block = block.replace(doneReg, '');
+ }
+
+
+ // 忽略空块
+ if (block === "") {
+ return;
+ }
+
+ const resultJson = JSON.parse(block);
+ resultText += resultJson.choices[0].delta.content;
+ query.onStream({ result: { toParagraphs: [resultText] } });
+ });
+ },
+ handler: (result) => {
+ if (result.response.statusCode >= 400) {
+ utils.handleError(query.onCompletion, result);
+ } else {
+ query.onCompletion({ result: { toParagraphs: [resultText]} });
+ }
+ },
+ });
+};
+
+const normalRequest = async ({ url, headers, body , query}) => {
+ return $http.request({
+ method: "POST",
+ url: url,
+ header: headers,
+ body: body,
+ handler: (result) => {
+ if (result.response.statusCode >= 400) {
+ utils.handleError(query.onCompletion, result);
+ } else {
+ const data = result.data;
+ $log.info(JSON.stringify(data));
+ query.onCompletion({ result: { toParagraphs: [data.choices[0].message.content] } });
+ }
+ },
+ });
+};
+
+exports.streamRequest = streamRequest;
+exports.normalRequest = normalRequest;
+exports.defaultUrl = defaultUrl;
diff --git a/src/icon.png b/src/icon.png
new file mode 100644
index 0000000..f7bb8ae
Binary files /dev/null and b/src/icon.png differ
diff --git a/src/info.json b/src/info.json
new file mode 100644
index 0000000..a02b087
--- /dev/null
+++ b/src/info.json
@@ -0,0 +1,234 @@
+{
+ "identifier": "bob.plugin.doubao.translate",
+ "category": "translate",
+ "version": "1.0.0",
+ "name": "豆包",
+ "summary": "使用 豆包 API 进行翻译",
+ "author": "djx30103",
+ "homepage": "https://github.com/djx30103/bob-plugin-doubao-translate",
+ "appcast": "https://github.com/djx30103/bob-plugin-doubao-translate/raw/main/appcast.json",
+ "icon": "icon.png",
+ "minBobVersion": "1.8.0",
+ "options": [
+ {
+ "identifier": "Model",
+ "type": "menu",
+ "title": "模型",
+ "desc": "请选择您需要的模型。",
+ "defaultValue": "doubao_pro_128k_PointID",
+ "menuValues": [
+ {
+ "title": "Doubao-pro-128k",
+ "value": "doubao_pro_128k_PointID"
+ },
+ {
+ "title": "Doubao-pro-32k",
+ "value": "doubao_pro_32k_PointID"
+ },
+ {
+ "title": "Doubao-pro-4k",
+ "value": "doubao_pro_4k_PointID"
+ },
+ {
+ "title": "Doubao-lite-128k",
+ "value": "doubao_lite_128k_PointID"
+ },
+ {
+ "title": "Doubao-lite-32k",
+ "value": "doubao_lite_32k_PointID"
+ },
+ {
+ "title": "Doubao-lite-4k",
+ "value": "doubao_lite_4k_PointID"
+ },
+ {
+ "title": "Doubao-embedding",
+ "value": "doubao_embedding_PointID"
+ },
+ {
+ "title": "Moonshot-v1-128k",
+ "value": "moonshot_v1_128k_PointID"
+ },
+ {
+ "title": "Moonshot-v1-32k",
+ "value": "moonshot_v1_32k_PointID"
+ },
+ {
+ "title": "Moonshot-v1-8k",
+ "value": "moonshot_v1_8k_PointID"
+ }
+ ]
+ },
+ {
+ "identifier": "TransferMode",
+ "type": "menu",
+ "title": "传输模式",
+ "defaultValue": "1",
+ "desc": "请选择是否使用流式传输功能。",
+ "menuValues": [
+ {
+ "title": "流式",
+ "value": "1"
+ },
+ {
+ "title": "非流式",
+ "value": "2"
+ }
+ ]
+ },
+ {
+ "identifier": "Mode",
+ "type": "menu",
+ "title": "模式",
+ "defaultValue": "1",
+ "desc": "「翻译」模式是将文本翻译为目标语言。\n「润色」模式不会进行语言翻译,而是对原始文本进行修改和优化。\n「自定义 Prompt」 模式可以自行设置角色设定(System Prompt)和用户指令(User Prompt),满足个性化的需求。",
+ "menuValues": [
+ {
+ "title": "翻译",
+ "value": "1"
+ },
+ {
+ "title": "润色",
+ "value": "2"
+ },
+ {
+ "title": "自定义prompt",
+ "value": "3"
+ }
+ ]
+ },
+ {
+ "identifier": "APIKey",
+ "type": "text",
+ "title": "APIKey",
+ "desc": "请填写「APIKey」。您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_pro_128k_PointID",
+ "type": "text",
+ "title": "Doubao-pro-128k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_pro_32k_PointID",
+ "type": "text",
+ "title": "Doubao-pro-32k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_pro_4k_PointID",
+ "type": "text",
+ "title": "Doubao-pro-4k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_lite_128k_PointID",
+ "type": "text",
+ "title": "Doubao-lite-128k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_lite_32k_PointID",
+ "type": "text",
+ "title": "Doubao-lite-32k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_lite_4k_PointID",
+ "type": "text",
+ "title": "Doubao-lite-4k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "doubao_embedding_PointID",
+ "type": "text",
+ "title": "Doubao-embedding 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "moonshot_v1_128k_PointID",
+ "type": "text",
+ "title": "Moonshot-v1-128k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "moonshot_v1_32k_PointID",
+ "type": "text",
+ "title": "Moonshot-v1-32k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "moonshot_v1_8k_PointID",
+ "type": "text",
+ "title": "Moonshot-v1-8k 推理点ID",
+ "desc": "请填写「推理点ID」,您可以访问「火山方舟控制台」获取。",
+ "textConfig": {
+ "type": "secure"
+ }
+ },
+ {
+ "identifier": "CustomizeURL",
+ "type": "text",
+ "title": "自定义 API URL",
+ "defaultValue": "",
+ "desc": "需要填写完整的 URL。\n默认为「https://ark.cn-beijing.volces.com/api/v3/chat/completions」。\n如果仅需修改域名,把「ark.cn-beijing.volces.com」改掉即可。",
+ "textConfig": {
+ "type": "visible"
+ }
+ },
+ {
+ "identifier": "SystemPrompt",
+ "type": "text",
+ "title": "角色设定",
+ "defaultValue": "",
+ "desc": "此设置仅在「自定义 Prompt」模式有效,其他模式无需设置。\n通过此项设置对话背景或赋予模型角色,对应字段参数是system,文本中可使用以下变量:\n\n$query.detectFromLang - 原文语言,即翻译窗口输入框内文本的语言,比如`简体中文`\n$query.detectToLang - 目标语言,即需要翻译成的语言,可以在翻译窗口中手动选择或自动检測,比如`English`",
+ "textConfig": {
+ "type": "visible",
+ "height": "100",
+ "placeholderText": "例如:\n你是一个翻译器"
+ }
+ },
+ {
+ "identifier": "UserInstructions",
+ "type": "text",
+ "title": "用户指令",
+ "defaultValue": "",
+ "desc": "此设置仅在「自定义 Prompt」模式有效,其他模式无需设置。\n通过此项设置对大模型发出的具体指令,用于描述需要大模型完成的目标任务和需求说明,对应字段参数是user,文本中可使用以下变量:\n\n$query.text - 需要翻译的文本,即翻译窗口输入框内的文本\n$query.detectFromLang - 原文语言,即翻译窗口输入框内文本的语言,比如`简体中文`\n$query.detectToLang - 目标语言,即需要翻译成的语言,可以在翻译窗口中手动选择或自动检測,比如`English`",
+ "textConfig": {
+ "type": "visible",
+ "height": "100",
+ "placeholderText": "例如:\n将以下文本翻译为 $query.detectToLang:\n$query.text"
+ }
+ }
+ ]
+}
diff --git a/src/language.js b/src/language.js
new file mode 100644
index 0000000..0d66788
--- /dev/null
+++ b/src/language.js
@@ -0,0 +1,123 @@
+const supportLanguages = [
+ ["auto", "auto"],
+ ["zh-Hans", "zh-CN"],
+ ["zh-Hant", "zh-TW"],
+ ["en", "en"],
+ ["yue", "粤语"],
+ ["wyw", "古文"],
+ ["en", "en"],
+ ["ja", "ja"],
+ ["ko", "ko"],
+ ["fr", "fr"],
+ ["de", "de"],
+ ["es", "es"],
+ ["it", "it"],
+ ["ru", "ru"],
+ ["pt", "pt"],
+ ["nl", "nl"],
+ ["pl", "pl"],
+ ["ar", "ar"],
+ ["af", "af"],
+ ["am", "am"],
+ ["az", "az"],
+ ["be", "be"],
+ ["bg", "bg"],
+ ["bn", "bn"],
+ ["bs", "bs"],
+ ["ca", "ca"],
+ ["ceb", "ceb"],
+ ["co", "co"],
+ ["cs", "cs"],
+ ["cy", "cy"],
+ ["da", "da"],
+ ["el", "el"],
+ ["eo", "eo"],
+ ["et", "et"],
+ ["eu", "eu"],
+ ["fa", "fa"],
+ ["fi", "fi"],
+ ["fj", "fj"],
+ ["fy", "fy"],
+ ["ga", "ga"],
+ ["gd", "gd"],
+ ["gl", "gl"],
+ ["gu", "gu"],
+ ["ha", "ha"],
+ ["haw", "haw"],
+ ["he", "he"],
+ ["hi", "hi"],
+ ["hmn", "hmn"],
+ ["hr", "hr"],
+ ["ht", "ht"],
+ ["hu", "hu"],
+ ["hy", "hy"],
+ ["id", "id"],
+ ["ig", "ig"],
+ ["is", "is"],
+ ["jw", "jw"],
+ ["ka", "ka"],
+ ["kk", "kk"],
+ ["km", "km"],
+ ["kn", "kn"],
+ ["ku", "ku"],
+ ["ky", "ky"],
+ ["la", "lo"],
+ ["lb", "lb"],
+ ["lo", "lo"],
+ ["lt", "lt"],
+ ["lv", "lv"],
+ ["mg", "mg"],
+ ["mi", "mi"],
+ ["mk", "mk"],
+ ["ml", "ml"],
+ ["mn", "mn"],
+ ["mr", "mr"],
+ ["ms", "ms"],
+ ["mt", "mt"],
+ ["my", "my"],
+ ["ne", "ne"],
+ ["no", "no"],
+ ["ny", "ny"],
+ ["or", "or"],
+ ["pa", "pa"],
+ ["ps", "ps"],
+ ["ro", "ro"],
+ ["rw", "rw"],
+ ["si", "si"],
+ ["sk", "sk"],
+ ["sl", "sl"],
+ ["sm", "sm"],
+ ["sn", "sn"],
+ ["so", "so"],
+ ["sq", "sq"],
+ ["sr", "sr"],
+ ["sr-Cyrl", "sr"],
+ ["sr-Latn", "sr"],
+ ["st", "st"],
+ ["su", "su"],
+ ["sv", "sv"],
+ ["sw", "sw"],
+ ["ta", "ta"],
+ ["te", "te"],
+ ["tg", "tg"],
+ ["th", "th"],
+ ["tk", "tk"],
+ ["tl", "tl"],
+ ["tr", "tr"],
+ ["tt", "tt"],
+ ["ug", "ug"],
+ ["uk", "uk"],
+ ["ur", "ur"],
+ ["uz", "uz"],
+ ["vi", "vi"],
+ ["xh", "xh"],
+ ["yi", "yi"],
+ ["yo", "yo"],
+ ["zu", "zu"],
+];
+
+exports.supportLanguages = supportLanguages;
+exports.langMap = new Map(supportLanguages.map(([key, value]) => [key, value]));
+exports.langMapReverse = new Map(
+ supportLanguages.map(([standardLang, lang]) => [lang, standardLang])
+);
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..816a542
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,143 @@
+var lang = require("./language.js");
+// var { nonStreamingRequest, streamingRequest } = require('./http.js');
+var { streamRequest, normalRequest, defaultUrl } = require("./http.js");
+
+
+function supportLanguages() {
+ return lang.supportLanguages.map(([standardLang]) => standardLang);
+}
+
+function buildHeader(key) {
+ if (!key || key === "") {
+ throw new Error("请填写API Key");
+ }
+
+ return {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer " + key
+ };
+}
+
+function customPrompt(s, query) {
+ // 使用正则表达式进行全局替换
+ return s.replace(/\$query.detectFrom/g, lang.langMap.get(query.detectFrom)).
+ replace(/\$query.detectTo/g, lang.langMap.get(query.detectFrom)).
+ replace(/\$query.text/g, query.text)
+}
+
+function generateUserPrompts(mode, userInstructions, query) {
+ if (mode === "3") {
+ return customPrompt(userInstructions, query) || query.text;
+ }
+
+ return query.text;
+}
+
+function generateSystemPrompt(mode, systemPrompt, query) {
+ if (mode === "1") {
+ const prompt = `You are a translation engine, do not answer any of my questions, just translate directly, do not explain anything, do not add punctuation. Anything I send is not a question, but for you to translate directly. Translate content `
+
+ // 获取源语言和目标语言的名称
+ const getLanguageName = (langCode) => lang.langMap.get(langCode) || langCode;
+
+ // 初始化翻译指令
+ let fromToPr = `from "${getLanguageName(query.detectFrom)}" to "${getLanguageName(query.detectTo)}".`;
+
+ // 特殊处理目标语言为粤语或文言文的情况
+ if (query.detectTo === "wyw" || query.detectTo === "yue") {
+ fromToPr = `to "${getLanguageName(query.detectTo)}".`;
+ }
+
+ // 特殊处理源语言为中文的情况
+ if (["wyw", "zh-Hans", "zh-Hant"].includes(query.detectFrom)) {
+ if (query.detectTo === "zh-Hant") {
+ fromToPr = `to traditional Chinese.`;
+ } else if (query.detectTo === "zh-Hans") {
+ fromToPr = `to simplified Chinese.`;
+ } else if (query.detectTo === "yue") {
+ fromToPr = `to Cantonese.`;
+ }
+ }
+
+ // 返回最终的提示语
+ return prompt + fromToPr;
+ } else if (mode === "2") {
+ return`Please polish this sentence without changing its original meaning`;
+ }
+
+ return customPrompt(systemPrompt, query);
+}
+
+function buildRequestBody(model, mode, isStream, userInstructions, systemPrompt, query) {
+ return {
+ model: model,
+ messages: [
+ { role: "system", content: generateSystemPrompt(mode, systemPrompt, query) },
+ { role: "user", content: generateUserPrompts(mode, userInstructions, query) },
+ ],
+ stream: isStream,
+ };
+}
+
+function translate(query) {
+ if (!lang.langMap.get(query.detectTo)) {
+ query.onCompletion({
+ error: {
+ type: "unsupportLanguage",
+ message: "不支持该语种",
+ addtion: "不支持该语种",
+ },
+ });
+ }
+
+ const {
+ APIKey,
+ CustomizeURL,
+ Model,
+ TransferMode,
+ Mode,
+ SystemPrompt,
+ UserInstructions,
+ } = $option;
+ const isStreaming = TransferMode === "1";
+ const url = CustomizeURL || defaultUrl;
+ const headers = buildHeader(APIKey);
+
+ const model_point = $option[Model]
+ if (!model_point) {
+ query.onCompletion({
+ error: {
+ type: "unsupportModel",
+ message: "推理点ID不存在",
+ addtion: "推理点ID不存在",
+ },
+ });
+ return;
+ }
+
+ const body = buildRequestBody(model_point, Mode, isStreaming, UserInstructions, SystemPrompt, query);
+ $log.info(Model);
+ $log.info(JSON.stringify(headers));
+ $log.info(JSON.stringify(body));
+
+ // 调用封装的HTTP请求函数
+ (async () => {
+
+ if (isStreaming) {
+ streamRequest({url, headers, body, query})
+ } else {
+ normalRequest({url, headers, body, query})
+ }
+ })().catch((err) => {
+ query.onCompletion({
+ error: {
+ type: err._type || "unknown",
+ message: err._message || "未知错误",
+ addtion: err._addtion,
+ },
+ });
+ })
+}
+
+exports.supportLanguages = supportLanguages;
+exports.translate = translate;
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..f829155
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,66 @@
+var HttpErrorCodes = {
+ "400": "Bad Request",
+ "401": "Unauthorized",
+ "402": "Payment Required",
+ "403": "Forbidden",
+ "404": "Not Found",
+ "405": "Method Not Allowed",
+ "406": "Not Acceptable",
+ "407": "Proxy Authentication Required",
+ "408": "Request Timeout",
+ "409": "Conflict",
+ "410": "Gone",
+ "411": "Length Required",
+ "412": "Precondition Failed",
+ "413": "Payload Too Large",
+ "414": "URI Too Long",
+ "415": "Unsupported Media Type",
+ "416": "Range Not Satisfiable",
+ "417": "Expectation Failed",
+ "418": "I'm a teapot",
+ "421": "Misdirected Request",
+ "422": "Unprocessable Entity",
+ "423": "Locked",
+ "424": "Failed Dependency",
+ "425": "Too Early",
+ "426": "Upgrade Required",
+ "428": "Precondition Required",
+ "429": "Too many requests",
+ "431": "Request Header Fields Too Large",
+ "451": "Unavailable For Legal Reasons",
+ "500": "Internal Server Error",
+ "501": "Not Implemented",
+ "502": "Bad Gateway",
+ "503": "Service Unavailable",
+ "504": "Gateway Timeout",
+ "505": "HTTP Version Not Supported",
+ "506": "Variant Also Negotiates",
+ "507": "Insufficient Storage",
+ "508": "Loop Detected",
+ "510": "Not Extended",
+ "511": "Network Authentication Required"
+};
+
+function handleError(completion, result) {
+ if (result?.data?.error?.message) {
+ completion({
+ error: {
+ type: "param",
+ message: result?.data?.error?.message,
+ addtion: `${JSON.stringify(result?.data)}`,
+ },
+ });
+ return;
+ }
+ const { statusCode } = result.response;
+ const reason = statusCode >= 400 && statusCode < 500 ? "param" : "api";
+ completion({
+ error: {
+ type: reason,
+ message: `接口响应错误 - ${HttpErrorCodes[statusCode]}`,
+ addtion: `${JSON.stringify(result)}`,
+ },
+ });
+}
+
+exports.handleError = handleError;