// ==UserScript==
// @name 套壳油猴的广告拦截脚本(副本)
// @author Lemon399
// @version 2.2.5
// @description 将 ABP 中的元素隐藏规则转换为 CSS 使用
// @require https://gf.qytechs.cn/scripts/452263-extended-css/code/extended-css.js?version=1099366
// @resource jiekouAD https://code.gitlink.org.cn/damengzhu/banad/raw/branch/main/jiekouAD.txt
// @resource CSSRule https://code.gitlink.org.cn/damengzhu/abpmerge/raw/branch/main/CSSRule.txt
// @match *://*/*
// @run-at document-start
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_addStyle
// @namespace https://lemon399-bitbucket-io.vercel.app/
// @source https://gitee.com/lemon399/tampermonkey-cli/tree/master/projects/abp_parse
// @connect code.gitlink.org.cn
// @copyright GPL-3.0
// @license GPL-3.0
// ==/UserScript==
(function (vm, ExtendedCss) {
"use strict";
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P
? value
: new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done
? resolve(result.value)
: adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
const onlineRules = [
{
标识: "jiekouAD",
地址: "https://code.gitlink.org.cn/damengzhu/banad/raw/branch/main/jiekouAD.txt",
在线更新: !!1,
},
{
标识: "CSSRule",
地址: "https://code.gitlink.org.cn/damengzhu/abpmerge/raw/branch/main/CSSRule.txt",
在线更新: !!1,
},
],
defaultRules = `
! 没有 ## #@# #?# #@?#
! #$# #@$# #$?# #@$?# 的行和
! 开头为 ! 的行会忽略
!
! 由于语法限制,内置规则中
! 一个反斜杠需要改成两个,像这样 \\
!
! 若要修改地址,请注意同步修改
! 头部的 @connect 和 @resource
`;
function isValidConfig(obj, ref) {
let valid = typeof obj == "object";
if (valid)
Object.getOwnPropertyNames(obj).forEach((k) => {
if (!ref.hasOwnProperty(k)) valid = false;
});
return valid;
}
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function runNeed(condition, fn, option, ...args) {
let ok = false;
const defaultOption = {
count: 20,
delay: 200,
failFn: () => null,
};
if (option && isValidConfig(option, defaultOption))
Object.assign(defaultOption, option);
new Promise((resolve, reject) =>
__awaiter(this, void 0, void 0, function* () {
for (let c = 0; !ok && c < defaultOption.count; c++) {
yield sleep(defaultOption.delay);
ok = condition.call(null, c + 1);
}
ok ? resolve() : reject();
})
).then(fn.bind(null, ...args), defaultOption.failFn);
}
function findMatches(string, res) {
let result = [-1, null];
res.forEach((re, i) => {
const match = string.match(re);
if (match) result = [i, match];
});
return result;
}
function getEtag(header) {
const result = findMatches(header, [
/(e|E)tag: \"(\w+)\"/,
// WebMonkey 系
/(e|E)tag: \[\"(\w+)\"\]/,
// 书签地球
/(e|E)tag=\"(\w+)\"/,
]);
return result[1] ? result[1][2] : null;
}
function makeRuleBox() {
return {
black: [],
white: [],
};
}
function domainChecker(domains) {
const results = [],
urlSuffix = /\.+?[\w-]+$/.exec(location.hostname);
let mostMatch = {
long: 0,
result: false,
};
domains.forEach((domain) => {
if (domain.endsWith(".*") && Array.isArray(urlSuffix)) {
domain = domain.replace(".*", urlSuffix[0]);
}
const invert = domain[0] == "~";
if (invert) domain = domain.slice(1);
const result = location.hostname.endsWith(domain);
results.push(result !== invert);
if (result) {
if (domain.length > mostMatch.long) {
mostMatch = {
long: domain.length,
result: result !== invert,
};
}
}
});
return mostMatch.long > 0 ? mostMatch.result : results.includes(true);
}
function hasSome(str, arr) {
return arr.some((word) => str.includes(word));
}
function ruleSpliter(rule) {
const result = findMatches(rule, [
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?##([^\s^+].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#@#([^\s+].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#\?#([^\s].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#@\?#([^\s].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#\$#([^\s].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#@\$#([^\s].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#\$\?#([^\s].*)/,
/^(~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*)(,~?[\w-]+(\.[\w-]+)*(\.[\w-]+|\.\*))*)?#@\$\?#([^\s].*)/,
]),
group = result[1];
if (group && (!group[1] || domainChecker(group[1].split(",")))) {
const sel = group.pop();
if (sel) {
return {
black: result[0] % 2 ? "white" : "black",
type: Math.floor(result[0] / 2),
sel,
};
}
}
}
function ruleLoader(rule) {
if (
hasSome(rule, [
":matches-path(",
":min-text-length(",
":watch-attr(",
":-abp-properties(",
":matches-property(",
])
)
return;
// ## -> #?#
if (
/(\w|^)#@?#/.test(rule) &&
hasSome(rule, [
":has(",
":-abp-has(",
"[-ext-has=",
":has-text(",
"contains(",
"-abp-contains(",
"[-ext-contains=",
"matches-css(",
"[-ext-matches-css=",
"matches-css-before(",
"[-ext-matches-css-before=",
"matches-css-after(",
"[-ext-matches-css-after=",
"matches-attr(",
"nth-ancestor(",
"upward(",
"xpath(",
"remove()",
"not(",
"if-not(",
])
) {
rule = rule.replace(/(\w|^)##/, "$1#?#").replace(/(\w|^)#@#/, "$1#@?#");
}
// :style(...) 转换
// example.com#?##id:style(color: red)
// example.com#$?##id { color: red }
if (rule.includes(":style(")) {
rule = rule
.replace(/(\w|^)##/, "$1#$#")
.replace(/(\w|^)#@#/, "$1#@$#")
.replace(/(\w|^)#\?#/, "$1#$?#")
.replace(/(\w|^)#@\?#/, "$1#@$?#")
.replace(/:style\(/, " { ")
.replace(/\)$/, " }");
}
return ruleSpliter(rule);
}
const selectors = makeRuleBox(),
extSelectors = makeRuleBox(),
styles = makeRuleBox(),
extStyles = makeRuleBox(),
values = {
get black() {
return gmValue("get", false, "ajs_disabled_domains", "");
},
set black(v) {
gmValue("set", false, "ajs_disabled_domains", v);
},
get rules() {
return gmValue("get", true, "ajs_saved_abprules", {});
},
set rules(v) {
gmValue("set", true, "ajs_saved_abprules", v);
},
get time() {
return gmValue("get", false, "ajs_rules_ver", "0/0/0 0:0:0");
},
set time(v) {
gmValue("set", false, "ajs_rules_ver", v);
},
get etags() {
return gmValue("get", true, "ajs_rules_etags", {});
},
set etags(v) {
gmValue("set", true, "ajs_rules_etags", v);
},
},
data = {
disabled: false,
updating: false,
receivedRules: "",
allRules: "",
presetCss:
" {display: none !important;width: 0 !important;height: 0 !important;} ",
hideCss: "",
extraCss: "",
appliedCount: 0,
isFrame: vm.unsafeWindow.self !== vm.unsafeWindow.top,
isClean: false,
mutex: "__lemon__abp__parser__$__",
timeout: 5000,
xTimeout: 700,
},
menus = {
disable: {
id: undefined,
get text() {
return data.disabled ? "在此网站启用拦截" : "在此网站禁用拦截";
},
},
update: {
id: undefined,
get text() {
const time = values.time;
return data.updating
? "正在更新..."
: `点击更新: ${time.slice(0, 1) === "0" ? "未知时间" : time}`;
},
},
count: {
id: undefined,
get text() {
return data.isClean
? "已清空,点击刷新重新加载规则"
: `点击清空: ${data.appliedCount} / ${
data.allRules.split("\n").length
}`;
},
},
};
function gmMenu(name, cb) {
if (
typeof vm.GM_registerMenuCommand != "function" ||
typeof vm.GM_unregisterMenuCommand != "function" ||
data.isFrame
)
return;
if (typeof menus[name].id != "undefined") {
vm.GM_unregisterMenuCommand(menus[name].id);
menus[name].id = undefined;
}
if (typeof cb == "function") {
menus[name].id = vm.GM_registerMenuCommand(menus[name].text, cb);
}
}
function gmValue(action, json, key, value) {
switch (action) {
case "get":
let v;
try {
v = vm.GM_getValue(key, json ? JSON.stringify(value) : value);
} catch (error) {
return;
}
return json && typeof v == "string" ? JSON.parse(v) : v;
case "set":
try {
value === null
? vm.GM_deleteValue(key)
: vm.GM_setValue(key, json ? JSON.stringify(value) : value);
} catch (error) {
vm.GM_deleteValue(key);
}
break;
}
}
function promiseXhr(details) {
return __awaiter(this, void 0, void 0, function* () {
let loaded = false;
try {
return yield new Promise((resolve, reject) => {
vm.GM_xmlhttpRequest(
Object.assign(
{
onload(e) {
loaded = true;
resolve(e);
},
onabort: reject.bind(null, "abort"),
onerror(e) {
reject({
error: "error",
resp: e,
});
},
ontimeout: reject.bind(null, "timeout"),
onreadystatechange(e_1) {
// X 浏览器超时中断
if (e_1.readyState === 4) {
setTimeout(() => {
if (!loaded)
reject({
error: "X timeout",
resp: e_1,
});
}, data.xTimeout);
}
// Via 浏览器超时中断,不给成功状态...
if (e_1.readyState === 3) {
setTimeout(() => {
if (!loaded)
reject({
error: "Via timeout",
resp: e_1,
});
}, data.timeout);
}
},
timeout: data.timeout,
},
details
)
);
});
} catch (error) {}
});
}
function storeRule(name, resp) {
const savedRules = values.rules,
savedEtags = values.etags;
if (resp.responseHeaders) {
const etag = getEtag(resp.responseHeaders);
if (etag) {
savedEtags[name] = etag;
values.etags = savedEtags;
}
}
if (resp.responseText) {
savedRules[name] = resp.responseText;
values.rules = savedRules;
if (Object.keys(values.rules).length === 0) {
data.receivedRules += "\n" + resp.responseText + "\n";
}
}
}
function fetchRuleBody(rule) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const getResp = yield promiseXhr({
method: "GET",
responseType: "text",
url: rule.地址,
});
if (
getResp &&
(getResp === null || getResp === void 0
? void 0
: getResp.responseText) &&
((_a = getResp.responseText) === null || _a === void 0
? void 0
: _a.length) > 0
) {
storeRule(rule.标识, getResp);
return true;
} else return false;
});
}
function fetchRule(rule) {
return new Promise((resolve, reject) =>
__awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
const headResp = yield promiseXhr({
method: "HEAD",
responseType: "text",
url: rule.地址,
});
if (!headResp) {
reject("HEAD 失败");
} else {
if (
(headResp === null || headResp === void 0
? void 0
: headResp.responseText) &&
((_a = headResp.responseText) === null || _a === void 0
? void 0
: _a.length) > 0
) {
storeRule(rule.标识, headResp);
resolve();
} else {
const etag = getEtag(
typeof headResp.responseHeaders == "string"
? headResp.responseHeaders
: (_c = (_b = headResp).getAllResponseHeaders) === null ||
_c === void 0
? void 0
: _c.call(_b)
),
savedEtags = values.etags;
if (etag) {
if (etag !== savedEtags[rule.标识]) {
(yield fetchRuleBody(rule)) ? resolve() : reject("GET 失败");
} else reject("ETag 一致");
} else {
(yield fetchRuleBody(rule)) ? resolve() : reject("GET 失败");
}
}
}
})
);
}
function fetchRules() {
return __awaiter(this, void 0, void 0, function* () {
data.updating = true;
gmMenu("update", () => undefined);
for (const rule of onlineRules) {
rule.在线更新 && (yield fetchRule(rule).catch((error) => {}));
}
values.time = new Date().toLocaleString("zh-CN");
gmMenu("count", cleanRules);
initRules();
});
}
function performUpdate(force) {
if (force) {
return fetchRules();
} else {
return new Date(values.time).getDate() !== new Date().getDate()
? fetchRules()
: Promise.resolve();
}
}
function switchDisabledStat() {
const disaList = values.black.length === 0 ? [] : values.black.split(",");
data.disabled = !disaList.includes(location.hostname);
if (data.disabled) {
disaList.push(location.hostname);
} else {
disaList.splice(disaList.indexOf(location.hostname), 1);
}
values.black = disaList.join(",");
location.reload();
}
function initRules() {
const abpRules = values.rules;
if (typeof vm.GM_getResourceText == "function") {
onlineRules.forEach((rule) => {
let resRule;
try {
resRule = vm.GM_getResourceText(rule.标识);
} catch (error) {
resRule = "";
}
if (resRule && !abpRules[rule.标识]) abpRules[rule.标识] = resRule;
});
}
const abpKeys = Object.keys(abpRules);
abpKeys.forEach((name) => {
data.receivedRules += "\n" + abpRules[name] + "\n";
});
data.allRules = defaultRules + data.receivedRules;
if (abpKeys.length !== 0) {
data.updating = false;
gmMenu("update", () =>
__awaiter(this, void 0, void 0, function* () {
yield performUpdate(true);
location.reload();
})
);
}
return data.receivedRules.length;
}
function styleApply() {
if (data.hideCss.length > 0) {
if (typeof vm.GM_addStyle == "function") {
vm.GM_addStyle(data.hideCss);
} else {
runNeed(
() => !!document.documentElement,
() => {
const elem = document.createElement("style");
elem.textContent = data.hideCss;
document.documentElement.appendChild(elem);
}
);
}
}
if (data.extraCss.length > 0) {
runNeed(
() => !!document.documentElement,
() => new ExtendedCss({ styleSheet: data.extraCss }).apply()
);
}
}
function cleanRules() {
if (confirm(`是否清空存储规则 (${Object.keys(values.rules).length}) ?`)) {
values.rules = {};
values.time = "0/0/0 0:0:0";
values.etags = {};
data.appliedCount = 0;
data.allRules = "";
data.isClean = true;
gmMenu("update");
gmMenu("count", () => location.reload());
}
}
function parseRules() {
styles.black
.filter((v) => !styles.white.includes(v))
.forEach((s) => {
data.hideCss += `${s} `;
data.appliedCount++;
});
extStyles.black
.filter((v) => !extStyles.white.includes(v))
.forEach((s) => {
data.extraCss += `${s} `;
data.appliedCount++;
});
selectors.black
.filter((v) => !selectors.white.includes(v))
.forEach((s, i, a) => {
data.hideCss += `${i == 0 ? "" : ","}${s}`;
if (i == a.length - 1) data.hideCss += data.presetCss;
data.appliedCount++;
});
extSelectors.black
.filter((v) => !extSelectors.white.includes(v))
.forEach((s, i, a) => {
data.extraCss += `${i == 0 ? "" : ","}${s}`;
if (i == a.length - 1) data.extraCss += data.presetCss;
data.appliedCount++;
});
gmMenu("count", cleanRules);
styleApply();
}
function splitRules() {
data.allRules.split("\n").forEach((rule) => {
const ruleObj = ruleLoader(rule),
boxes = [selectors, extSelectors, styles, extStyles];
if (typeof ruleObj != "undefined") {
if (
ruleObj.black == "black" &&
boxes[ruleObj.type].white.includes(ruleObj.sel)
)
return;
boxes[ruleObj.type][ruleObj.black].push(ruleObj.sel);
}
});
parseRules();
}
function main() {
return __awaiter(this, void 0, void 0, function* () {
data.disabled = values.black.split(",").includes(location.hostname);
gmMenu("disable", switchDisabledStat);
if (data.disabled || (initRules() === 0 && data.isFrame)) return;
if (data.receivedRules.length === 0) yield performUpdate(true);
splitRules();
yield performUpdate(false);
if (data.appliedCount === 0) splitRules();
});
}
function runOnce(key, func, ...params) {
if (key in vm.unsafeWindow) return;
vm.unsafeWindow[key] = true;
func === null || func === void 0 ? void 0 : func(...params);
}
runOnce(data.mutex, main);
})(
{
unsafeWindow: typeof unsafeWindow == "object" ? unsafeWindow : window,
GM_registerMenuCommand:
typeof GM_registerMenuCommand == "function"
? GM_registerMenuCommand
: undefined,
GM_unregisterMenuCommand:
typeof GM_unregisterMenuCommand == "function"
? GM_unregisterMenuCommand
: undefined,
GM_getValue: typeof GM_getValue == "function" ? GM_getValue : undefined,
GM_deleteValue:
typeof GM_deleteValue == "function" ? GM_deleteValue : undefined,
GM_setValue: typeof GM_setValue == "function" ? GM_setValue : undefined,
GM_xmlhttpRequest:
typeof GM_xmlhttpRequest == "function" ? GM_xmlhttpRequest : undefined,
GM_getResourceText:
typeof GM_getResourceText == "function" ? GM_getResourceText : undefined,
GM_addStyle: typeof GM_addStyle == "function" ? GM_addStyle : undefined,
},
ExtendedCss
);