feat: translate blacklist

This commit is contained in:
Gabe Yuan
2023-11-24 17:07:29 +08:00
parent 7bc39dd1bc
commit 22c50e7765
8 changed files with 200 additions and 193 deletions

View File

@@ -8,27 +8,83 @@ import {
MSG_TRANS_TOGGLE_STYLE, MSG_TRANS_TOGGLE_STYLE,
MSG_TRANS_GETRULE, MSG_TRANS_GETRULE,
MSG_TRANS_PUTRULE, MSG_TRANS_PUTRULE,
MSG_TRANSLATE_SELECTED,
MSG_OPEN_TRANBOX,
APP_LCNAME, APP_LCNAME,
DEFAULT_TRANBOX_SETTING, DEFAULT_TRANBOX_SETTING,
} from "./config"; } from "./config";
import { getRulesWithDefault, getFabWithDefault } from "./libs/storage"; import { getFabWithDefault, getSettingWithDefault } from "./libs/storage";
import { Translator } from "./libs/translator"; import { Translator } from "./libs/translator";
import { sendIframeMsg, sendParentMsg } from "./libs/iframe"; import { isIframe, sendIframeMsg, sendParentMsg } from "./libs/iframe";
import { matchRule } from "./libs/rules";
import Slection from "./views/Selection"; import Slection from "./views/Selection";
import { touchTapListener } from "./libs/touch"; import { touchTapListener } from "./libs/touch";
import { debounce } from "./libs/utils"; import { debounce, genEventName } from "./libs/utils";
import { handlePing, injectScript } from "./libs/gm";
import { browser } from "./libs/browser";
import { runWebfix } from "./libs/webfix";
import { matchRule } from "./libs/rules";
import { trySyncAllSubRules } from "./libs/subRules";
import { isInBlacklist } from "./libs/blacklist";
export async function runTranslator(setting) { /**
const href = document.location.href; * 油猴脚本设置页面
const rules = await getRulesWithDefault(); */
const rule = await matchRule(rules, href, setting); function runSettingPage() {
const translator = new Translator(rule, setting); if (GM?.info?.script?.grant?.includes("unsafeWindow")) {
unsafeWindow.GM = GM;
return { translator, rule }; unsafeWindow.APP_INFO = {
name: process.env.REACT_APP_NAME,
version: process.env.REACT_APP_VERSION,
};
} else {
const ping = genEventName();
window.addEventListener(ping, handlePing);
// window.eval(`(${injectScript})("${ping}")`); // eslint-disable-line
const script = document.createElement("script");
script.textContent = `(${injectScript})("${ping}")`;
document.head.append(script);
}
} }
export function runIframe(setting) { /**
* 插件监听后端事件
* @param {*} translator
*/
function runtimeListener(translator) {
browser?.runtime.onMessage.addListener(async ({ action, args }) => {
switch (action) {
case MSG_TRANS_TOGGLE:
translator.toggle();
sendIframeMsg(MSG_TRANS_TOGGLE);
break;
case MSG_TRANS_TOGGLE_STYLE:
translator.toggleStyle();
sendIframeMsg(MSG_TRANS_TOGGLE_STYLE);
break;
case MSG_TRANS_GETRULE:
break;
case MSG_TRANS_PUTRULE:
translator.updateRule(args);
sendIframeMsg(MSG_TRANS_PUTRULE, args);
break;
case MSG_TRANSLATE_SELECTED:
window.dispatchEvent(new CustomEvent(MSG_TRANSLATE_SELECTED));
break;
case MSG_OPEN_TRANBOX:
window.dispatchEvent(new CustomEvent(MSG_OPEN_TRANBOX));
break;
default:
return { error: `message action is unavailable: ${action}` };
}
return { data: translator.rule };
});
}
/**
* iframe 页面执行
* @param {*} setting
*/
function runIframe(setting) {
let translator; let translator;
window.addEventListener("message", (e) => { window.addEventListener("message", (e) => {
const { action, args } = e.data || {}; const { action, args } = e.data || {};
@@ -52,7 +108,12 @@ export function runIframe(setting) {
sendParentMsg(MSG_TRANS_GETRULE); sendParentMsg(MSG_TRANS_GETRULE);
} }
export async function showFab(translator) { /**
* 悬浮按钮
* @param {*} translator
* @returns
*/
async function showFab(translator) {
const fab = await getFabWithDefault(); const fab = await getFabWithDefault();
if (fab.isHide) { if (fab.isHide) {
return; return;
@@ -80,10 +141,12 @@ export async function showFab(translator) {
); );
} }
export function showTransbox({ /**
tranboxSetting = DEFAULT_TRANBOX_SETTING, * 划词翻译
transApis, * @param {*} param0
}) { * @returns
*/
function showTransbox({ tranboxSetting = DEFAULT_TRANBOX_SETTING, transApis }) {
if (!tranboxSetting?.transOpen) { if (!tranboxSetting?.transOpen) {
return; return;
} }
@@ -110,7 +173,11 @@ export function showTransbox({
); );
} }
export function windowListener(rule) { /**
* 监听来自iframe页面消息
* @param {*} rule
*/
function windowListener(rule) {
window.addEventListener("message", (e) => { window.addEventListener("message", (e) => {
const { action } = e.data || {}; const { action } = e.data || {};
switch (action) { switch (action) {
@@ -122,14 +189,23 @@ export function windowListener(rule) {
}); });
} }
export function showErr(message) { /**
* 显示错误信息到页面顶部
* @param {*} message
*/
function showErr(message) {
const $err = document.createElement("div"); const $err = document.createElement("div");
$err.innerText = `KISS-Translator: ${message}`; $err.innerText = `KISS-Translator: ${message}`;
$err.style.cssText = "background:red; color:#fff;"; $err.style.cssText = "background:red; color:#fff;";
document.body.prepend($err); document.body.prepend($err);
} }
export function touchOperation(translator) { /**
* 监听触屏操作
* @param {*} translator
* @returns
*/
function touchOperation(translator) {
const { touchTranslate = 2 } = translator.setting; const { touchTranslate = 2 } = translator.setting;
if (touchTranslate === 0) { if (touchTranslate === 0) {
return; return;
@@ -141,3 +217,63 @@ export function touchOperation(translator) {
}); });
touchTapListener(handleTap, touchTranslate); touchTapListener(handleTap, touchTranslate);
} }
/**
* 入口函数
*/
export async function run(isUserscript = false) {
try {
const href = document.location.href;
// 设置页面
if (
isUserscript &&
(href.includes(process.env.REACT_APP_OPTIONSPAGE_DEV) ||
href.includes(process.env.REACT_APP_OPTIONSPAGE) ||
href.includes(process.env.REACT_APP_OPTIONSPAGE2))
) {
runSettingPage();
return;
}
// 读取设置信息
const setting = await getSettingWithDefault();
// 黑名单
if (isInBlacklist(href, setting)) {
return;
}
// 适配iframe
if (isIframe) {
runIframe(setting);
return;
}
// 不规范网页修复
await runWebfix(setting);
// 翻译网页
const rule = await matchRule(href, setting);
const translator = new Translator(rule, setting);
// 监听消息
windowListener(rule);
!isUserscript && runtimeListener(translator);
// 划词翻译
showTransbox(setting);
// 浮球按钮
await showFab(translator);
// 触屏操作
touchOperation(translator);
// 同步订阅规则
isUserscript && (await trySyncAllSubRules(setting));
} catch (err) {
console.error("[KISS-Translator]", err);
showErr(err.message);
}
}

View File

@@ -352,8 +352,8 @@ export const I18N = {
en: `URL pattern`, en: `URL pattern`,
}, },
pattern_helper: { pattern_helper: {
zh: `1、支持星号(*)通配符。2、多个URL支持英文逗号“,”分隔。`, zh: `1、支持星号(*)通配符。2、多个URL英文逗号“,”分隔。`,
en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs can be separated by English commas ",".`, en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs separated by English commas ",".`,
}, },
selector_helper: { selector_helper: {
zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`, zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`,
@@ -683,4 +683,8 @@ export const I18N = {
zh: `四指轻触`, zh: `四指轻触`,
en: `Four finger tap`, en: `Four finger tap`,
}, },
translate_blacklist: {
zh: `禁用翻译名单`,
en: `Translate Blacklist`,
},
}; };

View File

@@ -412,6 +412,13 @@ export const DEFAULT_SHORTCUTS = {
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度 export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度 export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度
export const TRANS_NEWLINE_LENGTH = 20; // 换行字符数 export const TRANS_NEWLINE_LENGTH = 20; // 换行字符数
export const DEFAULT_BLACKLIST = [
"https://fishjar.github.io/kiss-translator/options.html",
"https://translate.google.com",
"https://www.deepl.com/translator",
"oapi.dingtalk.com",
"login.dingtalk.com",
]; // 禁用翻译名单
export const DEFAULT_SETTING = { export const DEFAULT_SETTING = {
darkMode: false, // 深色模式 darkMode: false, // 深色模式
@@ -433,6 +440,7 @@ export const DEFAULT_SETTING = {
inputRule: DEFAULT_INPUT_RULE, // 输入框设置 inputRule: DEFAULT_INPUT_RULE, // 输入框设置
tranboxSetting: DEFAULT_TRANBOX_SETTING, // 划词翻译设置 tranboxSetting: DEFAULT_TRANBOX_SETTING, // 划词翻译设置
touchTranslate: 2, // 触屏翻译 touchTranslate: 2, // 触屏翻译
blacklist: DEFAULT_BLACKLIST.join(",\n"), // 禁用翻译名单
}; };
export const DEFAULT_RULES = [GLOBLA_RULE]; export const DEFAULT_RULES = [GLOBLA_RULE];

View File

@@ -1,89 +1,3 @@
import { browser } from "./libs/browser"; import { run } from "./common";
import {
MSG_TRANS_TOGGLE,
MSG_TRANS_TOGGLE_STYLE,
MSG_TRANSLATE_SELECTED,
MSG_OPEN_TRANBOX,
MSG_TRANS_GETRULE,
MSG_TRANS_PUTRULE,
} from "./config";
import { getSettingWithDefault } from "./libs/storage";
import { isIframe, sendIframeMsg } from "./libs/iframe";
import { runWebfix } from "./libs/webfix";
import {
runIframe,
runTranslator,
showFab,
showTransbox,
windowListener,
showErr,
touchOperation,
} from "./common";
function runtimeListener(translator) { run();
browser?.runtime.onMessage.addListener(async ({ action, args }) => {
switch (action) {
case MSG_TRANS_TOGGLE:
translator.toggle();
sendIframeMsg(MSG_TRANS_TOGGLE);
break;
case MSG_TRANS_TOGGLE_STYLE:
translator.toggleStyle();
sendIframeMsg(MSG_TRANS_TOGGLE_STYLE);
break;
case MSG_TRANS_GETRULE:
break;
case MSG_TRANS_PUTRULE:
translator.updateRule(args);
sendIframeMsg(MSG_TRANS_PUTRULE, args);
break;
case MSG_TRANSLATE_SELECTED:
window.dispatchEvent(new CustomEvent(MSG_TRANSLATE_SELECTED));
break;
case MSG_OPEN_TRANBOX:
window.dispatchEvent(new CustomEvent(MSG_OPEN_TRANBOX));
break;
default:
return { error: `message action is unavailable: ${action}` };
}
return { data: translator.rule };
});
}
/**
* 入口函数
*/
(async () => {
try {
// 读取设置信息
const setting = await getSettingWithDefault();
// 适配iframe
if (isIframe) {
runIframe(setting);
return;
}
// 不规范网页修复
await runWebfix(setting);
// 翻译网页
const { translator, rule } = await runTranslator(setting);
// 监听消息
windowListener(rule);
runtimeListener(translator);
// 划词翻译
showTransbox(setting);
// 浮球按钮
await showFab(translator);
// 触屏操作
touchOperation(translator);
} catch (err) {
console.error("[KISS-Translator]", err);
showErr(err.message);
}
})();

13
src/libs/blacklist.js Normal file
View File

@@ -0,0 +1,13 @@
import { isMatch } from "./utils";
import { DEFAULT_BLACKLIST } from "../config";
/**
* 检查是否在黑名单中
* @param {*} href
* @param {*} param1
* @returns
*/
export const isInBlacklist = (
href,
{ blacklist = DEFAULT_BLACKLIST.join(",\n") }
) => blacklist.split(",").some((url) => isMatch(href, url.trim()));

View File

@@ -21,7 +21,6 @@ import { trySyncRules } from "./sync";
* @returns * @returns
*/ */
export const matchRule = async ( export const matchRule = async (
rules,
href, href,
{ {
injectRules = true, injectRules = true,
@@ -29,7 +28,7 @@ export const matchRule = async (
owSubrule = DEFAULT_OW_RULE, owSubrule = DEFAULT_OW_RULE,
} }
) => { ) => {
rules = [...rules]; const rules = await getRulesWithDefault();
if (injectRules) { if (injectRules) {
try { try {
const selectedSub = subrulesList.find((item) => item.selected); const selectedSub = subrulesList.find((item) => item.selected);

View File

@@ -1,82 +1,3 @@
import { getSettingWithDefault } from "./libs/storage"; import { run } from "./common";
import { trySyncAllSubRules } from "./libs/subRules";
import { isIframe } from "./libs/iframe";
import { handlePing, injectScript } from "./libs/gm";
import { genEventName } from "./libs/utils";
import { runWebfix } from "./libs/webfix";
import {
runIframe,
runTranslator,
showFab,
showTransbox,
windowListener,
showErr,
touchOperation,
} from "./common";
function runSettingPage() { run(true);
if (GM?.info?.script?.grant?.includes("unsafeWindow")) {
unsafeWindow.GM = GM;
unsafeWindow.APP_INFO = {
name: process.env.REACT_APP_NAME,
version: process.env.REACT_APP_VERSION,
};
} else {
const ping = genEventName();
window.addEventListener(ping, handlePing);
// window.eval(`(${injectScript})("${ping}")`); // eslint-disable-line
const script = document.createElement("script");
script.textContent = `(${injectScript})("${ping}")`;
document.head.append(script);
}
}
/**
* 入口函数
*/
(async () => {
try {
// 设置页面
if (
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE_DEV) ||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE) ||
document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE2)
) {
runSettingPage();
return;
}
// 读取设置信息
const setting = await getSettingWithDefault();
// 适配iframe
if (isIframe) {
runIframe(setting);
return;
}
// 不规范网页修复
await runWebfix(setting);
// 翻译网页
const { translator, rule } = await runTranslator(setting);
// 监听消息
windowListener(rule);
// 划词翻译
showTransbox(setting);
// 浮球按钮
await showFab(translator);
// 触屏操作
touchOperation(translator);
// 同步订阅规则
await trySyncAllSubRules(setting);
} catch (err) {
console.error("[KISS-Translator]", err);
showErr(err.message);
}
})();

View File

@@ -23,6 +23,7 @@ import {
OPT_SHORTCUT_STYLE, OPT_SHORTCUT_STYLE,
OPT_SHORTCUT_POPUP, OPT_SHORTCUT_POPUP,
OPT_SHORTCUT_SETTING, OPT_SHORTCUT_SETTING,
DEFAULT_BLACKLIST,
} from "../../config"; } from "../../config";
import { useShortcut } from "../../hooks/Shortcut"; import { useShortcut } from "../../hooks/Shortcut";
import ShortcutInput from "./ShortcutInput"; import ShortcutInput from "./ShortcutInput";
@@ -90,6 +91,7 @@ export default function Settings() {
mouseKey = OPT_MOUSEKEY_DISABLE, mouseKey = OPT_MOUSEKEY_DISABLE,
detectRemote = false, detectRemote = false,
touchTranslate = 2, touchTranslate = 2,
blacklist = DEFAULT_BLACKLIST.join(",\n"),
} = setting; } = setting;
const { isHide = false } = fab || {}; const { isHide = false } = fab || {};
@@ -268,6 +270,16 @@ export default function Settings() {
</Box> </Box>
</> </>
)} )}
<TextField
size="small"
label={i18n("translate_blacklist")}
helperText={i18n("pattern_helper")}
name="blacklist"
defaultValue={blacklist}
onChange={handleChange}
multiline
/>
</Stack> </Stack>
</Box> </Box>
); );