diff --git a/extensions/easydict/CHANGELOG.md b/extensions/easydict/CHANGELOG.md index cad5c23ad3c2..485c8f48f854 100644 --- a/extensions/easydict/CHANGELOG.md +++ b/extensions/easydict/CHANGELOG.md @@ -1,10 +1,17 @@ # `Easydict` Changelog +## [v2.9.3] - 2024-09-13 + +### 🐞 Fixed + +- Fixed Bing language detection API failed, which caused the query process to hang. https://github.com/raycast/extensions/issues/14357 + ## [v2.9.2] - 2024-08-25 ### 💎 Improvement - Support HTTP OpenAI API endpoint, thanks to [rookiezn](https://github.com/rookiezn)'s PR. +- Support changing OpenAI model, default to use `gpt-4o-mini`. - DeepL translation supports Arabic language. - Removed the deprecated built-in DeepL API key. - Removed support for the official Youdao translation API, now only use the web API by default. diff --git a/extensions/easydict/package.json b/extensions/easydict/package.json index 0a086da3f8c4..efd5ce30fc89 100644 --- a/extensions/easydict/package.json +++ b/extensions/easydict/package.json @@ -512,6 +512,29 @@ } ] }, + { + "title": "Bing Host", + "name": "bingHost", + "type": "dropdown", + "label": "Bing Host", + "required": false, + "description": "Bing Host Type", + "default": "", + "data": [ + { + "title": "Auto", + "value": "" + }, + { + "title": "cn.bing.com", + "value": "cn.bing.com" + }, + { + "title": "www.bing.com", + "value": "www.bing.com" + } + ] + }, { "title": "Switch Boxes", "name": "enableAutomaticQuerySelectedText", @@ -537,14 +560,6 @@ "description": "Turned on by default, the speed and accuracy of language detection is well balanced. Turn off if you want more accurate language detection, but slower ⚠️ .", "default": true }, - { - "name": "enableBaiduLanguageDetect", - "type": "checkbox", - "label": "Enable Baidu Language Detect", - "required": false, - "description": "⚠️ If turn off it, the language detection will be slower, even cause timeout.", - "default": true - }, { "name": "enableAutomaticPlayWordAudio", "type": "checkbox", @@ -569,6 +584,15 @@ "description": "⚠️ Note that it should only be enabled if needed, as it can slow down requests.", "default": false }, + { + "title": "Langauge Detection", + "name": "enableBaiduLanguageDetect", + "type": "checkbox", + "label": "Enable Baidu Language Detect", + "required": false, + "description": "Turn on/off Baidu Language Detect.", + "default": true + }, { "title": "Apple System Features", "name": "enableAppleTranslate", diff --git a/extensions/easydict/src/checkIP.ts b/extensions/easydict/src/checkIP.ts deleted file mode 100644 index 0a0ded22ec88..000000000000 --- a/extensions/easydict/src/checkIP.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * @author: tisfeng - * @createTime: 2022-09-17 22:22 - * @lastEditor: tisfeng - * @lastEditTime: 2022-09-27 23:25 - * @fileName: checkIP.ts - * - * Copyright (c) 2022 by tisfeng, All Rights Reserved. - */ - -import { LocalStorage } from "@raycast/api"; -import axios from "axios"; -import { requestCostTime } from "./axiosConfig"; -import { isChineseIPKey } from "./consts"; -import { myDecrypt } from "./preferences"; - -/** - * Check if ip is in China. If error, default is true. - * - * If request is successful, store value in LocalStorage. - */ -export function checkIfIpInChina(): Promise { - console.log(`check if ip in China`); - - return new Promise((resolve) => { - getCurrentIpInfo() - .then((ipInfo) => { - const country = ipInfo.country; - const isChina = country === "CN"; - LocalStorage.setItem(isChineseIPKey, isChina); - console.warn(`---> ip country: ${country}`); - resolve(isChina); - }) - .catch((error) => { - console.error(`checkIfIpInChina error: ${error}`); - resolve(true); - }); - }); -} - -/** - * Get current ip info. - * - * Ref: https://ipinfo.io/developers - * - * * Note: Free usage of our API is limited to 50,000 API requests per month. If you exceed that limit, we'll return a 429 HTTP status code to you. - * - * curl https://ipinfo.io - { - "ip": "120.240.53.42", - "city": "Zhanjiang", - "region": "Guangdong", - "country": "CN", - "loc": "21.2339,110.3875", - "org": "AS9808 China Mobile Communications Group Co., Ltd.", - "timezone": "Asia/Shanghai", - "readme": "https://ipinfo.io/missingauth" - } - */ -async function getCurrentIpInfo() { - try { - const token = myDecrypt("U2FsdGVkX1+sExqLZVqT0q3vOVDXqul2TMJeiD9aJRk="); - const url = `https://ipinfo.io/?token=${token}`; - const res = await axios.get(url); - console.log(`---> ip info: ${JSON.stringify(res.data, null, 4)}, cost ${res.headers[requestCostTime]} ms`); - return Promise.resolve(res.data); - } catch (error) { - console.error(`getCurrentIp error: ${error}`); - return Promise.reject(error); - } -} - -/** - * Get current ip address, return 114.37.203.235. - * - * Ref: https://blog.csdn.net/uikoo9/article/details/113820051 - */ -export async function getCurrentIp(): Promise { - const url = "http://icanhazip.com/"; - try { - const res = await axios.get(url); - const ip = res.data.trim(); - console.warn(`---> current ip: ${ip}, cost ${res.headers["requestCostTime"]} ms`); - return Promise.resolve(ip); - } catch (error) { - console.error(`getCurrentIp error: ${error}`); - return Promise.reject(error); - } -} diff --git a/extensions/easydict/src/consts.ts b/extensions/easydict/src/consts.ts index f59358a82e4a..3ded0731b49b 100644 --- a/extensions/easydict/src/consts.ts +++ b/extensions/easydict/src/consts.ts @@ -13,29 +13,4 @@ export const userAgent = export const clipboardQueryTextKey = "clipboardQueryTextKey"; -export const isChineseIPKey = "isChineseIP"; - export const networkTimeout = 15000; - -export enum YoudaoErrorCode { - Success = "0", - TargetLanguageNotSupported = "102", - TranslatedTextTooLong = "103", - InvalidApplicationID = "108", // 应用ID无效 - InvalidSignature = "202", // 签名无效,AppSecret不正确 - AccessFrequencyLimited = "207", - TranslationQueryFailed = "302", // 翻译查询失败, such as 'con' 😓 - InsufficientAccountBalance = "401", -} - -// https://fanyi-api.baidu.com/doc/21 -export enum BaiduErrorCode { - Success = "52000", - AccessFrequencyLimited = "54003", - InsufficientAccountBalance = "54004", - TargetLanguageNotSupported = "58001", -} - -export const youdaoErrorCodeUrl = encodeURI( - "https://ai.youdao.com/DOCSIRMA/html/自然语言翻译/API文档/文本翻译服务/文本翻译服务-API文档.html#section-11" -); diff --git a/extensions/easydict/src/preferences.ts b/extensions/easydict/src/preferences.ts index 00da71684dc7..0d02f9e48275 100644 --- a/extensions/easydict/src/preferences.ts +++ b/extensions/easydict/src/preferences.ts @@ -64,6 +64,8 @@ export interface MyPreferences { openAIAPIKey: string; openAIAPIURL: string; openAIModel: string; + + bingHost: string; } /** diff --git a/extensions/easydict/src/releaseVersion/versionInfo.ts b/extensions/easydict/src/releaseVersion/versionInfo.ts index adebb1f27867..bed4399048aa 100644 --- a/extensions/easydict/src/releaseVersion/versionInfo.ts +++ b/extensions/easydict/src/releaseVersion/versionInfo.ts @@ -26,27 +26,25 @@ export class Easydict { static repo = "Raycast-Easydict"; // * NOTE: this is new version info, don't use it directly. Use getCurrentStoredVersionInfo() instead. - version = "2.9.2"; - buildNumber = 28; - versionDate = "2024-08-25"; + version = "2.9.3"; + buildNumber = 29; + versionDate = "2024-09-13"; isNeedPrompt = true; hasPrompted = false; // * always default false, only show once, then should be set to true. releaseMarkdown = ` ## [v${this.version}] - ${this.versionDate} -### 💎 优化 +### 🐞 修复 -- 支持 HTTP OpenAI API endpoint,感谢 [rookiezn](https://github.com/rookiezn) 的 PR https://github.com/tisfeng/Raycast-Easydict/pull/51。 -- DeepL 翻译支持阿拉伯语 AR。 -- 移除了已废弃的内置的 DeepL API key。 -- 移除了对有道翻译官方 API 的支持,现默认只使用网页 API。 +- 修复了 Bing 语种识别 API 失败,导致查询过程卡住问题。https://github.com/raycast/extensions/issues/14357 -### 🐞 修复 +--- -- 修复了 argument 参数可能丢失问题。https://github.com/tisfeng/Raycast-Easydict/pull/63 -`; +### 🐞 Fixed +- Fixed Bing language detection API failed, which caused the query process to hang. https://github.com/raycast/extensions/issues/14357 +`; getRepoUrl() { return `${githubUrl}/${Easydict.author}/${Easydict.repo}`; } diff --git a/extensions/easydict/src/translation/microsoft/bing.ts b/extensions/easydict/src/translation/microsoft/bing.ts index 2bf45eeaf900..c71efca4959f 100644 --- a/extensions/easydict/src/translation/microsoft/bing.ts +++ b/extensions/easydict/src/translation/microsoft/bing.ts @@ -12,12 +12,12 @@ import { LocalStorage } from "@raycast/api"; import axios, { AxiosRequestConfig } from "axios"; import qs from "qs"; import { requestCostTime } from "../../axiosConfig"; -import { checkIfIpInChina } from "../../checkIP"; -import { isChineseIPKey, userAgent } from "../../consts"; +import { userAgent } from "../../consts"; import { DetectedLangModel, LanguageDetectType } from "../../detectLanguage/types"; import { QueryWordInfo } from "../../dictionary/youdao/types"; import { autoDetectLanguageItem, englishLanguageItem } from "../../language/consts"; import { getBingLangCode, getYoudaoLangCodeFromBingCode } from "../../language/languages"; +import { myPreferences } from "../../preferences"; import { QueryTypeResult, RequestErrorInfo, TranslationType } from "../../types"; import { getTypeErrorInfo } from "../../utils"; import { BingConfig, BingTranslateResult } from "./types"; @@ -25,18 +25,12 @@ import { BingConfig, BingTranslateResult } from "./types"; console.log(`enter bing.ts`); const bingConfigKey = "BingConfig"; - -// * bing tld depends ip, if ip is in china, `must` use cn.bing.com, otherwise use www.bing.com. And vice versa. -let bingTld: string | undefined; let bingConfig: BingConfig | undefined; -// First check user ip, then check if bing token expired, if expired, get a new one. else use the stored one as bingConfig. -LocalStorage.getItem(isChineseIPKey).then((isChineseIP) => { - if (isChineseIP !== undefined) { - bingTld = getBingTld(isChineseIP); - checkIfBingTokenExpired(); - } -}); +const defaultBingHost = "www.bing.com"; + +// * bing host depends ip, if ip is in china, `must` use cn.bing.com, otherwise use www.bing.com. And vice versa. +let bingHost = myPreferences.bingHost || defaultBingHost; /** * Request Microsoft Bing Web Translator. @@ -50,19 +44,29 @@ export async function requestWebBingTranslate(queryWordInfo: QueryWordInfo): Pro const type = TranslationType.Bing; - if (!bingConfig) { - console.log(`no stored bingConfig, get a new one`); + const isExpired = await checkIfBingTokenExpired(); + console.log(`bing token expired: ${isExpired}`); + + if (isExpired) { + console.log(`bing token expired, request new one`); bingConfig = await requestBingConfig(); - if (!bingConfig) { - console.error(`get bingConfig failed`); + } else { + const storedBingConfig = await LocalStorage.getItem(bingConfigKey); + if (storedBingConfig) { + bingConfig = JSON.parse(storedBingConfig) as BingConfig; + console.log(`use stored bingConfig: ${JSON.stringify(bingConfig, null, 4)}`); + } + } - const errorInfo: RequestErrorInfo = { - type: type, - message: "Get bing config failed", - }; + if (!bingConfig) { + console.error(`get bingConfig failed`); - return Promise.reject(errorInfo); - } + const errorInfo: RequestErrorInfo = { + type: type, + message: "Get bing config failed", + }; + + return Promise.reject(errorInfo); } // console.log(`request with bingConfig: ${JSON.stringify(bingConfig, null, 4)}`); @@ -82,13 +86,8 @@ export async function requestWebBingTranslate(queryWordInfo: QueryWordInfo): Pro const IIDString = `${IID}.${requestCount}`; - if (!bingTld) { - const isChineseIP = await checkIfIpInChina(); - bingTld = getBingTld(isChineseIP); - } - - const url = `https://${bingTld}.bing.com/ttranslatev3?isVertical=1&IG=${IG}&IID=${IIDString}`; - // console.log(`bing url: ${url}`); + const url = `https://${bingHost}/ttranslatev3?isVertical=1&IG=${IG}&IID=${IIDString}`; + console.log(`bing url: ${url}`); const config: AxiosRequestConfig = { method: "post", @@ -102,26 +101,25 @@ export async function requestWebBingTranslate(queryWordInfo: QueryWordInfo): Pro return new Promise((resolve, reject) => { axios(config) .then(function (response) { + const finalUrl = response.request.res.responseUrl; + console.log(`bing finalUrl: ${finalUrl}`); + + // get host + bingHost = new URL(finalUrl).host; const responseData = response.data; console.warn(`bing translate cost time: ${response.headers[requestCostTime]}`); // If bing translate response is empty, may be ip has been changed, bing tld is not correct, so check ip again, then request again. if (!responseData) { - console.warn(`bing translate response is empty, tld: ${bingTld}, check ip again, then request again`); - checkIfIpInChina().then((isIpInChina) => { - const tld = getBingTld(isIpInChina); - bingTld = tld; - console.log(`bing tld is changed to: ${bingTld}, try request token and bing translate again`); - - requestBingConfig().then((bingConfig) => { - if (bingConfig) { - requestWebBingTranslate(queryWordInfo) - .then((result) => resolve(result)) - .catch((error) => reject(error)); - } else { - reject(undefined); - } - }); + console.warn(`bing translate response is empty, change to use new host: ${bingHost}, then request again`); + requestBingConfig().then((bingConfig) => { + if (bingConfig) { + requestWebBingTranslate(queryWordInfo) + .then((result) => resolve(result)) + .catch((error) => reject(error)); + } else { + reject(undefined); + } }); } else { console.log(`bing response: ${JSON.stringify(responseData, null, 4)}`); @@ -195,22 +193,16 @@ export async function bingDetect(text: string): Promise { } /** - * Request Bing Translator API Token from web. + * Request Bing Translator API Token from web, and store it. * * Ref: https://github.com/plainheart/bing-translate-api/blob/master/src/index.js */ async function requestBingConfig(): Promise { console.log(`start requestBingConfig`); - - // * tld should depends on user current IP. - if (!bingTld) { - const isChineseIP = await checkIfIpInChina(); - bingTld = getBingTld(isChineseIP); - } - console.log(`config bingTld: ${bingTld}`); + console.log(`config bingTld: ${bingHost}`); return new Promise((resolve) => { - const url = `https://${bingTld}.bing.com/translator`; + const url = `https://${bingHost}/translator`; console.log(`get bing config url: ${url}`); axios @@ -223,18 +215,19 @@ async function requestBingConfig(): Promise { if (config) { bingConfig = config; resolve(config); + console.log(`getBingConfig from web: ${JSON.stringify(config, null, 4)}`); LocalStorage.setItem(bingConfigKey, JSON.stringify(config)); } else { console.warn(`parse bing config failed, html: ${html}`); console.log(`try check if ip in china`); - checkIfIpInChina().then((isIpInChina) => { - const tld = getBingTld(isIpInChina); - bingTld = tld; - console.log(`bing tld is changed to: ${bingTld}, try request bing config again`); - requestBingConfig() - .then((result) => resolve(result)) - .catch(() => resolve(undefined)); - }); + + const finalUrl = response.request.res.responseUrl; + bingHost = new URL(finalUrl).host; + + console.warn(`get bing config failed, host: ${bingHost}, change host, then request again`); + requestBingConfig() + .then((result) => resolve(result)) + .catch(() => resolve(undefined)); } }) .catch((error) => { @@ -276,42 +269,30 @@ function parseBingConfig(html: string): BingConfig | undefined { /** * Check if token expired, if expired, get a new one. else use the stored one as bingConfig. */ -function checkIfBingTokenExpired(): Promise { +async function checkIfBingTokenExpired(): Promise { console.log(`check if bing token expired`); - return new Promise((resolve) => { - LocalStorage.getItem(bingConfigKey).then((value) => { - if (!value) { - requestBingConfig(); - return resolve(true); - } - - // console.log(`stored bingConfig: ${JSON.stringify(value, null, 4)}`); - const config = JSON.parse(value) as BingConfig; - const { key, expirationInterval } = config; - const tokenStartTime = parseInt(key); - const expiration = parseInt(expirationInterval); - // default expiration is 10 min, for better experience, we get a new token after 5 min. - const tokenUsedTime = Date.now() - tokenStartTime; - const isExpired = tokenUsedTime > expiration; - if (isExpired) { - console.log(`bing token expired, request new one`); - requestBingConfig(); - } else { - bingConfig = config; - if (tokenUsedTime > expiration / 2) { - requestBingConfig(); - } - } - resolve(isExpired); - }); - }); -} + const value = await LocalStorage.getItem(bingConfigKey); + if (!value) { + requestBingConfig(); + return true; + } -/** - * Get bing tld from ip. Chinese ip, use cn.bing.com, otherwise use www.bing.com - */ -function getBingTld(isChineseIP: boolean): string { - const tld = isChineseIP ? "cn" : "www"; - console.log(`get bing tld: ${tld}`); - return tld; + // console.log(`stored bingConfig: ${JSON.stringify(value, null, 4)}`); + const config = JSON.parse(value) as BingConfig; + const { key, expirationInterval } = config; + const tokenStartTime = parseInt(key); + const expiration = parseInt(expirationInterval); + // default expiration is 10 min, for better experience, we get a new token after 5 min. + const tokenUsedTime = Date.now() - tokenStartTime; + const isExpired = tokenUsedTime > expiration; + if (isExpired) { + console.log(`bing token expired, request new one`); + requestBingConfig(); + } else { + bingConfig = config; + if (tokenUsedTime > expiration / 2) { + requestBingConfig(); + } + } + return isExpired; }