Skip to content

Instantly share code, notes, and snippets.

@zhanhongtao
Last active April 30, 2025 10:05
Show Gist options
  • Save zhanhongtao/198e58075d4b3415c2f0f5e394204c6a to your computer and use it in GitHub Desktop.
Save zhanhongtao/198e58075d4b3415c2f0f5e394204c6a to your computer and use it in GitHub Desktop.
auto-run-script-for-task
// ==UserScript==
// @name Task
// @namespace https://x181.cn/
// @version 0.41.2
// @author TT
// @description Hello world!
// @icon https://img.artproglobal.com/c/21736720685ad57d4fb8362b70f62f8d?width=512&height=512&hTag=d7a495871ba01409369e6c2231ad49f0
// @homepage https://x181.cn
// @downloadURL https://gist.github.com/zhanhongtao/198e58075d4b3415c2f0f5e394204c6a/raw/task.user.js
// @updateURL https://gist.github.com/zhanhongtao/198e58075d4b3415c2f0f5e394204c6a/raw/task.user.js
// @match *://*/*
// @connect localhost
// @connect x181.cn
// @connect api.telegram.org
// @connect *
// @grant GM.xmlHttpRequest
// @grant GM_getValue
// @grant GM_info
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
const Log = (flag = false) => {
return function log2(...rest) {
if (flag === false)
return;
let list2 = rest;
if (typeof window !== "undefined" && typeof top !== "undefined") {
list2 = [
`%cisTop: ${top === window}. url = ${location.href}.`,
"color: gray; font-size: 1.2em;",
...rest
];
}
console.log(...list2);
};
};
const log$2 = Log(false);
const defaultMeta = { pause: false };
function match(rule, value) {
if (Array.isArray(rule))
return rule.some((rule2) => match(rule2, value));
if (typeof rule === "boolean")
return rule;
if (typeof rule === "string")
return value.includes(rule);
if (rule instanceof RegExp)
return rule.test(value);
if (typeof rule === "function")
return rule(value);
if ("exec" in rule)
return rule.test(value);
let result = true;
if (result && rule.basic)
result = match(rule.basic, value);
if (result && rule.or)
result = rule.or.some((rule2) => match(rule2, value));
if (result && rule.not)
result = match(rule.not, value) == false;
return result;
}
function filter(list2, value) {
return list2.filter((task2) => match(task2.match, value));
}
async function run(task2) {
if (task2.top && top !== window)
return;
log$2("start run task = ", task2);
const scripts2 = Array.isArray(task2.script) ? task2.script : [task2.script];
scripts2.forEach((script) => {
try {
script(task2);
} catch (e) {
console.log(`run script failed`, e);
}
});
}
function core(scripts2, url) {
filter(scripts2, url).forEach((task2) => {
log$2("match.task", task2);
run({ ...task2, meta: task2.meta || { ...defaultMeta } });
});
}
function waitForSelector(selector, options) {
let node = null;
return waitForFunction(() => {
const selectors2 = Array.isArray(selector) ? selector : [selector];
const callback = (selector2) => {
node = document.querySelector(selector2);
return node != null;
};
return options ? options.type === "some" ? selectors2.some(callback) : selectors2.every(callback) : selectors2.some(callback);
}, { ...options, polling: 100 }).then(() => node);
}
const defaultWaitForFunctionOptions = {
timeout: 3e3,
polling: "raf"
};
function waitForFunction(fn, options) {
let t = Date.now();
const opt = { ...defaultWaitForFunctionOptions, ...options };
return new Promise((resolve, reject) => {
let f = () => {
const has = fn();
if (has)
resolve(true);
else if (typeof opt.polling === "number") {
if (opt.timeout === 0 || Date.now() - t < opt.timeout) {
setTimeout(f, opt.polling);
} else {
reject("timeout");
}
} else if (opt.polling === "raf") {
requestAnimationFrame(f);
} else if (opt.polling === "mutation") {
let timer = 0;
const observer = new MutationObserver(function() {
const has2 = fn();
if (has2 == false)
return;
if (timer)
clearTimeout(timer);
observer.disconnect();
resolve(true);
});
let node = document.querySelector("html");
if (node == null) {
observer.disconnect();
reject("no root node");
} else {
observer.observe(node, { childList: true });
timer = setTimeout(() => {
observer.disconnect();
reject("timeout");
}, opt.timeout);
}
} else {
reject("not support");
}
};
f();
});
}
function useBlockSelector(selector) {
return () => {
waitForSelector(selector).then(() => {
(Array.isArray(selector) ? selector : [selector]).forEach((selector2) => {
const node = document.querySelector(selector2);
if (node == null)
return;
const next = node.nextElementSibling;
if (next)
next.remove();
});
});
};
}
function useRedirect() {
return function() {
if (top !== window)
return;
let search = new URLSearchParams(location.search);
for (let value of search.values()) {
if (URL.canParse(value)) {
location.href = value;
}
}
};
}
function useRebuildUrl(fn) {
return () => {
const next = fn(location.href);
if (next)
location.href = next;
};
}
const task$4 = [
{
match: new URLPattern({ hostname: `outlook.live.com` }),
script: useBlockSelector(`[data-max-width="2400"]`)
},
{
match: [
"https://cdn.jellow.site/",
"https://cdnv2.ruguoapp.com/"
],
script: useRebuildUrl((url) => url.includes("?") ? url.replace(/\?.+$/, "") : "")
},
{
match: {
or: [
(url) => {
const u = new URL(url);
const pathname = u.pathname.toLowerCase();
const keywords = ["goto", "link", "redirect", "jump"];
return keywords.some((key) => pathname.includes(key));
},
new URLPattern({ "hostname": "(link|go).*" })
]
},
// https://github.com/einaregilsson/Redirector
script: useRedirect()
},
{
match: "http://uz.yurixu.com/",
script: () => {
const useHost = "www.newsmth.net";
const hosts = [
"www.newsmth.net",
"www.mysmth.net"
];
document.addEventListener("click", (e) => {
const target = e.target;
if (target == null)
return;
const link = target.closest("a");
if (link == null)
return;
if (hosts.some((host) => link.host === host)) {
link.href = link.href.replace(/^https?:\/\/(.+?)\//, () => `https://${useHost}/`);
}
}, true);
}
}
];
const bingTask = [
{
match: new URLPattern({ hostname: "cn.bing.com" }),
script: task$3,
top: true
}
];
const selectors = [
"#bgDiv",
".hp_top_cover",
".img_cont"
];
function task$3() {
let nodes = document.querySelectorAll(selectors.join(","));
if (nodes.length === 0)
return;
Array.from(nodes).some((node) => {
let style = window.getComputedStyle(node);
let backgroundImage = style.backgroundImage;
let res = "";
backgroundImage.replace(/url\(('|")?(.*?)\1\)/, (_, __, src) => {
res = src;
return "";
});
return res ? (console.log("%c wallpaper:", `color: red`, res), true) : false;
});
}
const doubanTask = [
{
match: "book.douban.com",
script: bookTask,
top: true
},
// 配置豆瓣读书...
{
// https://freembook.com/?
match: "https://zebra.9farm.com/",
script: bookBeforeSearchTask,
top: true
},
{
match: "movie.douban.com",
script: movieTask,
top: true
},
{
match: "fm.douban.com",
script: musicTask,
top: true
}
];
function createLinkElement(template, keyword) {
let a = document.createElement("a");
a.href = template.replaceAll("%s", encodeURIComponent(keyword));
a.target = "_blank";
a.setAttribute("target", "_blank");
a.setAttribute("rel", "noopener");
a.setAttribute("referrerpolicy", "no-referrer");
return a;
}
function createElement(source, title, target, keyword) {
let span = document.createElement("span");
span.className = "pl";
span.append(document.createTextNode(source));
const a = createLinkElement(target, keyword ? keyword : title);
a.append(document.createTextNode(title));
let fragment = document.createDocumentFragment();
fragment.appendChild(span);
fragment.appendChild(a);
let br = document.createElement("br");
fragment.appendChild(br);
return fragment;
}
function createElements(list2) {
let fragment = document.createDocumentFragment();
for (let i = 0; i < list2.length; ++i) {
let item = list2[i];
let element = createElement(
item.description,
item.title,
item.template,
item.keyword
);
fragment.appendChild(element);
}
return fragment;
}
function movieTask() {
let node = document.querySelector("#content > h1 span:first-child");
if (!node)
return;
let title = node.innerText.trim();
let infoNode = document.querySelector("#info");
if (!infoNode)
return;
let list2 = [
{
description: "g(ddz):",
title,
keyword: title,
template: "https://www.google.com/search?newwindow=1&safe=strict&source=hp&ei=--05W5X6CoqD8wX_zZmwCg&btnG=Google+%E6%90%9C%E7%B4%A2&q=%s&oq=beego&gs_l=psy-ab.3..0j0i203k1l2j0j0i203k1j0i10i203k1j0i203k1l4.6311.7597.0.7829.9.6.0.0.0.0.132.522.2j3.6.0....0...1c.1.64.psy-ab..3.6.609.6..35i39k1j0i131k1j0i67k1j0i10k1.87.jNu-vssuoKQ".replace("%s", `%s ${encodeURIComponent("site:dandanzan.club")}`)
}
];
let fragment = createElements(list2);
infoNode.appendChild(fragment);
}
function findCodeText(node) {
const pl = node.querySelectorAll("span.pl");
const list2 = Array.from(pl);
for (let i = 0; i < list2.length; ++i) {
const item = list2[i];
const text = item.innerHTML;
if (text.includes("ISBN")) {
let code = item.nextSibling;
if (code) {
const value = code.nodeValue;
if (value) {
return value.trim();
}
}
}
}
return "";
}
function bookTask() {
let node = document.querySelector("#wrapper > h1");
if (!node)
return;
let infoNode = document.querySelector("#info");
if (!infoNode)
return;
let title = node.innerText.trim();
let code = findCodeText(infoNode);
let list2 = [
// { description: 'z-lib:', title, template: 'https://zh.singlelogin.re/s/%s', keyword: title },
{ description: "Annas-Archive:", title, template: "https://annas-archive.org/search?q=%s", keyword: code }
];
let fragment = createElements(list2);
infoNode.appendChild(fragment);
}
function bookBeforeSearchTask() {
const qs = new URLSearchParams(location.search);
const q = qs.get("q");
if (q == null)
return;
const key = "session";
const value = JSON.stringify({
name: q,
author: "",
lang: "",
publisher: "",
ext: "",
isbn: "",
id: ""
});
const old = localStorage.getItem(key);
localStorage.setItem(key, value);
if (old != value) {
location.reload();
}
}
function musicTask() {
const _send = XMLHttpRequest.prototype.send;
const _open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(_, url) {
_open.apply(this, arguments);
this.__url = url;
};
XMLHttpRequest.prototype.send = function() {
let t = this.onreadystatechange;
this.onreadystatechange = function(e) {
if (t)
t.call(this, e);
const readyState = this.readyState;
if (readyState === 4) {
const type = this.responseType || "text";
if (type === "text" && this.response) {
let data;
try {
data = JSON.parse(this.response);
} catch (e2) {
console.log("douban.fm.parse.response.failed");
}
if (data && data.song) {
const songs = data.song;
songs.forEach((song) => {
console.log(`%c${song.title}`, "color: red", song.url);
});
}
}
}
return this;
};
_send.apply(this, arguments);
return this;
};
}
function sleep(n) {
return new Promise(function(resolve) {
setTimeout(resolve, n * 1e3);
});
}
const log$1 = Log(false);
const dubokuTask = {
match: [
"k.jhooslea.com",
"k.rbaa.top",
/\w+\.duboku\.(?:io|fun|com|tv|top)/,
/duboku\.(?:io|fun|com|tv|top)/
],
script: task$2
};
let entered = false;
let pageButtonInit = false;
function insertMovie() {
let selector = ".myui-header__menu";
let links = document.querySelectorAll(`${selector} > li > a`);
let is = Array.from(links).some((link) => link.getAttribute("href") == "/vodtype/1.html");
if (is)
return;
let box = document.querySelector(selector);
if (box == null)
return;
let children = box.children;
let newNode = document.createElement("li");
newNode.classList.add("hidden-sm", "hidden-xs");
if (/(?:vodtype|vodshow)\/1(-.*)?\.html/i.test(location.href))
newNode.classList.add("active");
newNode.innerHTML = `<a href="/vodtype/1.html">电影</a>`;
if (children.length > 1)
box.insertBefore(newNode, children[1]);
}
function task$2() {
if (top == null)
return;
insertMovie();
let path = top.location.href;
if (/voddetail/i.test(path)) {
if (pageButtonInit)
return;
let doc = top.document;
let latest = doc.querySelector("#playlist1 li:last-child a");
let posNode = doc.querySelector(".myui-content__operate");
if (posNode && latest) {
let playNode = posNode.firstElementChild;
if (playNode == null)
return;
let newNode = playNode.cloneNode(true);
newNode.href = latest.href;
newNode.innerHTML = `<i class="fa fa-play"></i> 播放最新一集`;
newNode.className = `btn btn-primary`;
posNode.appendChild(newNode);
pageButtonInit = true;
}
return;
}
let sidebar = document.querySelector(".myui-sidebar");
if (sidebar) {
sidebar.style.display = "none";
let previous = sidebar.previousElementSibling;
if (previous) {
previous.style.cssText = "width: 100% !important";
}
}
let videos = document.querySelectorAll("video");
if (videos.length === 0) {
log$1("No video tag. nextTick");
if (top !== window) {
sleep(1).then(task$2);
}
return;
}
let video = Array.from(videos).find((video2) => video2.id === "playerCnt_html5_api");
if (video == null) {
log$1("No video tag matched. nextTick");
if (top !== window)
sleep(1).then(task$2);
return;
}
if (entered)
return;
entered = true;
let promise = video.play();
if (promise) {
promise.catch(() => {
let playButton = document.querySelector(".vjs-big-play-button");
if (playButton) {
playButton.click();
}
});
}
video.addEventListener("ended", function() {
log$1("enter video ended handler.");
log$1("去 top 页面查找下一集");
if (top == null)
return;
let links = top.document.links;
for (let i = 0; i < links.length; ++i) {
let link = links[i];
let text = link.innerText;
if (text.indexOf("下集") > -1) {
log$1(`next url: ${link.href}`);
if (link.href !== top.location.href) {
top.location.href = link.href;
break;
}
}
}
});
}
function stopImmediatePropagation() {
["click"].forEach((name) => {
document.addEventListener(name, function(e) {
e.stopImmediatePropagation();
}, true);
});
}
function createHiddenNodesBySelectorsTask(selectors2) {
return () => {
selectors2.forEach((selector) => {
let nodes = document.querySelectorAll(selector);
nodes.forEach((node) => {
if (node)
node.style.display = "none";
});
});
};
}
function removeRemovedAdBlockerModal() {
let elements = document.querySelectorAll("cloudflare-app");
Array.from(elements).forEach((element) => {
let style = window.getComputedStyle(element, null);
let zIndex = style.getPropertyValue("z-index");
if (+zIndex >= 400) {
element.style.display = "none";
}
});
}
function remove(node) {
let previous;
while (node && (previous = node.previousSibling)) {
previous.remove();
}
}
function useObserver(selector, options = { childList: true }) {
let observer;
return (handler2) => {
return (task2) => {
if (observer)
observer.disconnect();
observer = new MutationObserver(() => {
const scripts2 = Array.isArray(handler2) ? handler2 : [handler2];
scripts2.forEach((script) => script(task2));
});
let node = document.querySelector(selector);
if (node)
observer.observe(node, options);
};
};
}
let fetchInit = false;
function fetchLocationPerMinute() {
fetch(location.href).finally(() => {
sleep(60).then(fetchLocationPerMinute);
});
}
function addEventListener(task2) {
let box = document.querySelector(".board-list");
if (box) {
let links = box.querySelectorAll("a");
Array.from(links).forEach((link) => {
link.setAttribute("target", "_blank");
});
const f = () => handler();
window.removeEventListener("hashchange", f);
window.addEventListener("hashchange", f);
} else {
sleep(0.2).then(() => addEventListener());
}
}
function handler(task2) {
addEventListener();
let body = document.body;
if (body)
body.style.cssText = "max-width: 1280px; margin: 0 auto;";
createHiddenNodesBySelectorsTask(["#ban_ner", "#banner_slider"])();
if (fetchInit == false) {
fetchInit = true;
fetchLocationPerMinute();
}
let style = document.createElement("style");
style.innerText = ".board-list tr { line-height: 2 } .board-list td { padding: 4px 0 } .title_9 a { font-size: 1.2em } .title_10 { width: 120px !important }";
let head = document.querySelector("head");
if (head)
head.appendChild(style);
}
const mysmthTask = {
match: [
"https://www.newsmth.net/",
"https://www.mysmth.net/"
],
script: useObserver("#body")(handler)
};
function nextTick(fn) {
return Promise.resolve().then(fn);
}
const log = Log(false);
const weChatTask = [
{
match: /https:\/\/mp\.weixin\.qq\.com\/s.*/,
script: task$1
},
{
match: /weixin110\.qq.com/,
script: linkTask
}
];
function linkTask() {
let node = document.querySelector("p");
if (node == null)
return;
let text = node.innerText.trim();
if (/https?:\/\//i.test(text)) {
location.href = text;
return;
}
}
function task$1() {
stopImmediatePropagation();
weChatTextToLink();
weChatQRCodeToLink();
logAudioLink();
}
function logAudioLink() {
let template = `https://res.wx.qq.com/voice/getvoice?mediaid=`;
sleep(1).then(() => {
if (typeof window == "undefined")
return;
if (typeof window.voiceList !== "undefined") {
let list2 = window.voiceList.voice_in_appmsg;
if (Array.isArray(list2)) {
list2.forEach((item, index) => {
let voice_id = item.voice_id;
let url = `${template}${voice_id}`;
log(`音频(${index + 1})地址: ${url}`);
});
}
}
});
}
let weChatQRCodeToLinkDone = false;
function weChatQRCodeToLink() {
if (weChatQRCodeToLinkDone)
return;
let nodes = document.querySelectorAll("span[data-src] img");
Array.from(nodes).forEach((node) => {
let a = document.createElement("a");
let p = node.parentNode;
let href = p.dataset.src;
if (href == null)
return;
a.href = href;
a.target = "_blank";
a.style.display = "block";
a.appendChild(document.createTextNode(href));
p.appendChild(a);
});
if (nodes.length) {
weChatQRCodeToLinkDone = true;
}
}
function weChatTextToLink() {
if (document.body == null) {
return sleep(0.2).then(() => {
nextTick(weChatTextToLink);
});
}
log("enter wechat textToLink func");
let walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
{
acceptNode: (node) => {
let p = node.parentNode;
while (p && ["A", "SCRIPT", "STYLE"].indexOf(p.nodeName) === -1) {
p = p.parentNode;
}
if (p)
return NodeFilter.FILTER_SKIP;
let text = node.nodeValue;
if (text && /https?:\/\//i.test(text)) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
}
}
);
let links = [];
while (walker.nextNode()) {
let node = walker.currentNode;
let text = node.nodeValue.toLowerCase();
let offset = text.indexOf("http");
let linkTextNode = node.splitText(offset);
if (linkTextNode == null)
continue;
if (linkTextNode.nodeValue == null)
continue;
let spaceOffset = linkTextNode.nodeValue.search(/\s|[))]/);
let linkNode = linkTextNode;
if (spaceOffset > -1) {
let t = linkTextNode.splitText(spaceOffset).previousSibling;
if (t)
linkNode = t;
}
let a = document.createElement("a");
if (linkNode.nodeValue)
a.href = linkNode.nodeValue;
a.setAttribute("target", "_blank");
linkNode.parentNode.insertBefore(a, linkNode);
a.appendChild(linkNode);
links.push(a);
}
if (links.length) {
suptolink(links);
}
return;
}
function suptolink(links) {
let sups = document.querySelectorAll("sup");
let lastFindedNode;
Array.from(sups).reverse().forEach((sup) => {
let text = sup.innerText.trim();
if (text === "")
return;
let link = findLinkByText(text, lastFindedNode || links[links.length - 1]);
if (link == null)
return;
lastFindedNode = link;
let a = document.createElement("a");
a.href = link.href;
a.setAttribute("target", "_blank");
sup.parentNode.insertBefore(a, sup);
a.appendChild(sup);
});
}
function findLinkByText(text, link) {
let find;
let node = link.previousSibling || link.parentNode;
while (node && (node.nodeType == 1 || node.nodeType === 3)) {
let t = node.innerText || node.nodeValue || "";
if (node.nodeType === 1) {
if (node.nodeName === "A")
link = node;
else {
let a = node.querySelector("a");
if (a)
link = a;
}
}
if (t.indexOf(text) > -1) {
find = link;
break;
}
node = node.previousSibling || node.parentNode;
}
return find;
}
function mutationObserver(meta, callback, selector, options = { childList: true }) {
if (meta == null)
return;
if (meta.observer) {
meta.observer.disconnect();
}
meta.observer = new MutationObserver(function() {
callback();
});
let node = document.querySelector(selector);
if (node) {
meta.observer.observe(node, options);
}
}
const zhihuTask = [
{
match: /^https?:\/\/.*?\.zhihu\.com/,
script: [
replaceExternalLinkTask,
replaceToRawImage
],
exclude: "link.zhihu.com"
},
{
// https://github.com/pillarjs/path-to-regexp
// https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
// https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
// new URLPattern(input, baseURL, options)
// options: { ignoreCase: false[default] }?
// baseURL: string?
// input: string, { protocol, username, password, hostname, port, pathname, search, hash, baseURL }
// Note: Omitted parts of the object will be treated as wildcards(*)
// example:
// new URLPattern({ hostname: ':sub.zhihu.com' })
// new URLPattern("/books/:id(\\d+)", "https://example.com")
// 语法
// a) 普通字符,严格匹配
// b) * 通配符匹配任意字符,贪婪性
// c) 命名 group - example: /book/:id 另外可以给 group 添加 (Regexp) 规则限制
// d) 可选/多次匹配 - {any}? - example: {www.}?zhihu.com 匹配 www.zhihu.com 和 zhihu.com; 期望匹配任意子域名,可以使用 {*.}?zhihu.com
// e) 正则表达式,使用括弧包裹 - example: /book/:id(\\d+). 注意:最外层括弧是 URLPattern 语法,不是正则的 group; 另外还有一些使用限制
// https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API#regex_matchers_limitations
// 正则表达式中的 ^ 字符只用在 protocol 字段中,$ 字符只用在 hash 字段中;
// example: new URLPattern({ protocol: "(^https?)" })
// 不支持 (?=) 和 (?!) 语法 - 前后环视
// 正则区间表达式 [] 中的括弧/特殊符号,同样需要做转义
// group 可以匿名,example: { pathname: '/book/(\\d+)' }
// group 修饰符有三种:?, +, *;
// 限制在 pathname 中:
// Automatic group prefixing in pathnames
// example:
// { pathname: '/book/:id?' } 会自动把 group name 前的 / 归到组定义中,这种行为便于使用修饰符
// 匹配 /book/123, /book 但不匹配 /book/
// 如果不期望把 / 归到组定义中,可以使用 {} 包裹。example:
// { pathname: '/book/{:id}?' }
// 匹配 /book/123, /book/ 但不匹配 /book
// pathname 会先 normalization 处理
// example:
// /foo/.bar/ -> /foot/bar/
match: new URLPattern("https://{*.}?zhihu.com"),
script: () => {
function updateTitle() {
const title = document.title;
document.title = title.replace(/^\(.*?\)/, "");
requestAnimationFrame(updateTitle);
}
updateTitle();
}
},
{
match: new URLPattern("/people/*", "https://www.zhihu.com/"),
script: removeReadQuestionsTask
},
{
match: new URLPattern("/question/*", "https://www.zhihu.com/"),
script: removeFixedHeader
}
];
function removeFixedHeader() {
function fixed() {
const t = document.querySelector(".Sticky.is-fixed");
if (t)
t.style.position = "relative";
requestAnimationFrame(fixed);
}
fixed();
}
function removeReadQuestionsTask() {
function handler2() {
let selector = "#Profile-activities .List-item";
let list2 = document.querySelectorAll(selector);
let max = 40;
if (list2.length > max) {
let index = list2.length - max;
let node = list2[index];
remove(node);
}
}
window.addEventListener("scroll", handler2);
}
function replaceExternalLinkTask({ meta }) {
function handler2() {
let selector = 'a[href^="https://link.zhihu.com/"]';
let links = document.querySelectorAll(selector);
Array.from(links).forEach((node) => {
let href = node.href;
let url = new URL(href);
let search = new URLSearchParams(url.search);
let target = search.get("target");
if (target) {
node.href = decodeURIComponent(target);
}
});
}
mutationObserver(meta, handler2, "body");
handler2();
}
function replaceToRawImage() {
document.addEventListener("click", (e) => {
const target = e.target;
if (target == null)
return;
const node = target;
if (node.nodeType != 1)
return;
if (node.nodeName !== "IMG")
return;
const token = node.dataset.originalToken;
if (token == null)
return;
const src = node.getAttribute("src");
if (src == null)
return;
const original = src.replace(/v2-.*?(?=\.)/, token);
node.setAttribute("src", original);
});
}
const weiboTask = [
{
match: /(?:t|weibo)\.cn/i,
script: task
},
{
match: /wx\d+\.sinaimg\.cn/i,
script: navigateToRawImageTask
}
];
function task() {
let node = document.querySelector(".link,.desc,.navigate_link");
if (node == null)
return;
if (node.tagName === "A") {
location.href = node.getAttribute("href");
return;
}
let text = node.innerText.trim();
if (/https?:\/\//i.test(text)) {
location.href = text;
return;
}
}
function navigateToRawImageTask() {
let url = location.href;
if (url.includes("/oslarge/"))
return;
let i = new URL(url);
i.pathname = `/oslarge` + i.pathname.replace(/\/[^\/]*/, "");
let newUrl = i.toString();
location.href = newUrl;
}
function toRedirectUrl(raw, source, dest) {
const p = new URLPattern(source);
const result = p.exec(raw);
if (!result)
return "";
const pathname = result.pathname;
const groups = pathname.groups;
let url = dest;
Object.keys(groups).forEach((key) => {
url = url.replace(`:${key}`, groups[key]);
});
return url;
}
function initialize(url, list2) {
for (let i = 0; i < list2.length; ++i) {
const config = list2[i];
if (config.skip)
continue;
let destination = url;
if (config.pattern) {
destination = toRedirectUrl(url, config.source, config.destination || config.source);
} else if (url == config.source && config.destination) {
destination = config.destination;
}
if (url !== destination) {
return destination;
}
}
return url;
}
const list = [
{ source: "https://store.pailixiang.com/album/:album/:name.jpg" },
{ source: "https://www.baidu.com/", destination: "https://google.com/", pattern: false, skip: true }
];
const last = initialize(location.href, list);
const redirectTask = {
match: (url) => last !== url,
script: () => {
location.href = last;
}
};
var _GM = /* @__PURE__ */ (() => typeof GM != "undefined" ? GM : void 0)();
var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
var _GM_openInTab = /* @__PURE__ */ (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)();
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
function promisifyRequest(request) {
return new Promise((resolve, reject) => {
request.oncomplete = request.onsuccess = () => resolve(request.result);
request.onabort = request.onerror = () => reject(request.error);
});
}
function createStore(dbName, storeName) {
const request = indexedDB.open(dbName);
request.onupgradeneeded = () => request.result.createObjectStore(storeName);
const dbp = promisifyRequest(request);
return (txMode, callback) => dbp.then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
}
let defaultGetStoreFunc;
function defaultGetStore() {
if (!defaultGetStoreFunc) {
defaultGetStoreFunc = createStore("keyval-store", "keyval");
}
return defaultGetStoreFunc;
}
function get(key, customStore = defaultGetStore()) {
return customStore("readonly", (store) => promisifyRequest(store.get(key)));
}
function set(key, value, customStore = defaultGetStore()) {
return customStore("readwrite", (store) => {
store.put(value, key);
return promisifyRequest(store.transaction);
});
}
function del(key, customStore = defaultGetStore()) {
return customStore("readwrite", (store) => {
store.delete(key);
return promisifyRequest(store.transaction);
});
}
function requestNotificationPermission() {
const p = Notification.permission;
if (p === "granted")
return Promise.resolve(true);
if (p === "denied")
return Promise.resolve(false);
return Notification.requestPermission().then((p2) => {
return Promise.resolve(p2 == "granted");
});
}
async function notify(message, options) {
const permission = await requestNotificationPermission();
if (!permission)
return;
if (!message)
return;
new Notification(message, options);
}
function post(path, data) {
let token = _GM_getValue("telegram-token");
if (!token) {
token = prompt(`Please input telegram token:`);
if (token)
_GM_setValue("telegram-token", token);
else
return Promise.reject("token is empty");
}
data.append("chat_id", "@connect2026");
const url = `https://api.telegram.org/bot${token}`;
return _GM.xmlHttpRequest({
method: "POST",
url: url + path,
data
});
}
function sendMessage(text, parse_mode = "") {
const data = new FormData();
data.append("text", text);
data.append("parse_mode", parse_mode);
return post("/sendMessage", data);
}
function sendPhoto(photo, caption = "") {
const data = new FormData();
data.append("photo", photo);
data.append("caption", caption);
return post("/sendPhoto", data);
}
function sendMediaGroup(group) {
const data = new FormData();
data.append("media", JSON.stringify(group));
return post("/sendMediaGroup", data);
}
const idbKey = "directory";
const id = "photo-viewer";
const userSaveButtonClass = "user-save-btn";
function ping(image) {
const domain = "https://x181.cn";
const url = `${domain}/api/us/save?url=${encodeURIComponent(image)}`;
return _GM_xmlhttpRequest({
method: "GET",
url,
headers: {
"Content-Type": "application/json"
}
});
}
function createTwitterSaveImageOption() {
function imageUrlMatcher(node) {
const urlRegexp = /\/media\//;
return urlRegexp.test(node.src);
}
function siblingsMatcher(node, debug = false) {
const p = node.previousElementSibling;
if (p == null)
return false;
const style = p.style.backgroundImage;
const url = style.replace(/url\("(.*?)"\)/, "$1");
const src = node.src;
const match2 = url == src;
if (match2 && debug)
console.log("url: ", url);
return match2;
}
function toImageUrl(rawImageUrl, debug = false) {
const src = rawImageUrl;
debug && console.log("url.raw", src);
const tmp = src.replace(/&name=.*$/, "");
debug && console.log("url.name.remove", tmp);
const url = tmp + "&name=4096x4096";
debug && console.log("url.name.fix", url);
return url;
}
function getImageUrls(node, debug = false) {
const images = node.querySelectorAll("img");
const matchUrlList = Array.from(images).filter((node2) => imageUrlMatcher(node2));
const checkedUrlList = matchUrlList.filter((node2) => siblingsMatcher(node2, debug));
return checkedUrlList.map((node2) => toImageUrl(node2.src, debug));
}
function getSingleImageUrl(node, debug = false) {
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const elements = document.elementsFromPoint(x, y);
for (let i = 0; i < elements.length; ++i) {
const element = elements[i];
if (element.nodeType !== 1)
continue;
if (element.nodeName != "IMG")
continue;
const pos = node.compareDocumentPosition(element);
if (!(pos & Node.DOCUMENT_POSITION_CONTAINED_BY))
continue;
const el = element;
const m1 = imageUrlMatcher(el);
if (!m1)
continue;
const m2 = siblingsMatcher(el, debug);
if (!m2)
continue;
return toImageUrl(el.src, debug);
}
return null;
}
const tweetDialogSelector = '[role="dialog"]';
const tweetSelector = 'article[data-testid="tweet"]';
function injectSaveButton(group) {
const is = group.querySelector("[data-save]");
if (is)
return;
if (group == null)
return;
group.append(createSaveButtonNode("save"));
group.append(createSaveButtonNode("telegram"));
}
let cancelInjectAction;
function doInjectAction(target) {
if (cancelInjectAction)
cancelInjectAction();
const dialog = target.querySelector(tweetDialogSelector);
if (dialog) {
const s = () => {
const group = dialog.querySelectorAll(`[role="group"] [role="group"]`);
if (group.length) {
const last2 = group[group.length - 1];
injectSaveButton(last2);
cancelInjectAction();
}
};
cancelInjectAction = action(s);
}
const list2 = target.querySelectorAll(tweetSelector);
Array.from(list2).forEach((node) => {
const urls = getImageUrls(node);
if (urls.length === 0)
return;
const group = node.querySelector('[role="group"]');
injectSaveButton(group);
});
}
function tryInjectSaveButton(node) {
const cancel = action(() => {
doInjectAction(node);
});
const observer = new MutationObserver(() => {
doInjectAction(node);
cancel();
});
observer.observe(node, { childList: true, subtree: true });
}
return {
inject() {
const selectors2 = ["#react-root"];
selectors2.forEach((selector) => {
waitForSelector(selector).then(() => {
const node = document.querySelector(selector);
if (node == null)
return;
tryInjectSaveButton(node);
});
});
},
query(element) {
const selectors2 = ['[role="dialog"]', tweetSelector];
for (let i = 0; i < selectors2.length; ++i) {
const node = element.closest(selectors2[i]);
if (node == null)
continue;
let urls = [];
const debug = true;
const is = node.closest(tweetSelector);
if (is)
urls = getImageUrls(node, debug);
else {
const single = getSingleImageUrl(node, debug);
if (single)
urls = [single];
}
if (urls.length === 0)
continue;
return urls.map((url) => {
return {
url,
filename: url.replace(/.*?\/media\/(.*?)\?.*/, "$1")
};
});
}
return [];
}
};
}
function createXHSSaveImageOption() {
function doInjectAction() {
const selector = ".bottom-container";
waitForSelector(selector).then(() => {
const node = document.querySelector(selector);
if (node == null)
return;
const is = node.querySelector("[data-save]");
if (is)
return;
node.append(
createSaveButtonNode()
);
});
}
return {
query() {
const selector = ".swiper-wrapper";
const node = document.querySelector(selector);
if (node == null)
return [];
const images = node.querySelectorAll("img");
if (images.length == 0)
return [];
return Array.from(images).map((img) => {
const url = img.src;
return {
url,
filename: url.replace(/^.*\//, "")
};
});
},
inject() {
const observer = new MutationObserver(() => {
doInjectAction();
});
observer.observe(document.body, { childList: true, subtree: true });
}
};
}
const twitterTask = [
{
match: /(?:x|twitter)\.com/,
script: createSaveImageScript(
createTwitterSaveImageOption()
)
},
{
match: new URLPattern("/explore/:id?", "https://www.xiaohongshu.com"),
script: createSaveImageScript(
createXHSSaveImageOption()
)
}
];
function createSaveImageScript(option) {
return () => saveImageScript(option);
}
function trySendToTelegram(images) {
const mediaList = images.map((item) => {
return {
type: "photo",
media: item.url,
caption: item.url,
// https://core.telegram.org/bots/api#formatting-options
// parse_mode: '', // Markdown, MarkdownV2, HTML
show_caption_above_media: false
};
});
return sendMediaGroup(mediaList);
}
async function saveImageScript(option) {
document.addEventListener("click", (e) => {
const target = e.target;
if (target == null)
return;
const can = target.closest(`.${userSaveButtonClass}`);
if (!can)
return;
const images = option.query(target);
if (images.length === 0)
return;
const type = can.dataset.save;
let task2 = void 0;
if (type == "save")
task2 = trySaveImagesToLocale(images);
if (type == "telegram")
task2 = trySendToTelegram(images);
if (task2) {
can.style.opacity = "0.4";
task2.finally(() => {
can.style.opacity = "";
});
}
e.preventDefault();
e.stopImmediatePropagation();
}, true);
option.inject();
}
function createSaveButtonNode(type = "save", cssText = "") {
const div = document.createElement("div");
div.dataset.save = type;
div.classList.add(userSaveButtonClass);
div.style.cssText = `cursor: pointer; display: flex;` + (cssText ? cssText : `align-self: center;`);
const telegram = `<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --><svg xmlns="http://www.w3.org/2000/svg" aria-label="Telegram" role="img" viewBox="0 0 512 512" width="24px" height="24px"><rect width="512" height="512" rx="15%" fill="#37aee2"/><path fill="#c8daea" d="M199 404c-11 0-10-4-13-14l-32-105 245-144"/><path fill="#a9c9dd" d="M199 404c7 0 11-4 16-8l45-43-56-34"/><path fill="#f6fbfe" d="M204 319l135 99c14 9 26 4 30-14l55-258c5-22-9-32-24-25L79 245c-21 8-21 21-4 26l83 26 190-121c9-5 17-3 11 4"/></svg>`;
const save = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 451.296 451.296" xml:space="preserve" width="24px" height="24px"><circle style="fill:#45b39c" cx="225.648" cy="225.638" r="225.638"/><path style="opacity:.1;enable-background:new" d="m450.855 239.322-99.451-99.451c-.076-.08-.13-.176-.209-.255l-39.537-39.491a12.776 12.776 0 0 0-9.026-3.76H109.178c-7.052 0-12.834 5.735-12.834 12.834v232.898c0 3.672 1.566 6.958 4.035 9.295l98.236 98.236c8.87 1.06 17.878 1.668 27.034 1.668 120.028 0 218.126-93.724 225.206-211.974z"/><path style="fill:#64798a" d="m351.176 139.612-39.501-39.501a12.823 12.823 0 0 0-9.066-3.755H109.186c-7.081 0-12.821 5.74-12.821 12.821V342.1c0 7.082 5.74 12.822 12.821 12.822H342.11c7.081 0 12.821-5.74 12.821-12.822V148.678c0-3.401-1.351-6.662-3.755-9.066z"/><path style="fill:#fff" d="M310.065 212.47H141.231c-6.129 0-11.098 4.969-11.098 11.098v88.637c0 6.129 4.969 11.098 11.098 11.098h168.833c6.129 0 11.098-4.969 11.098-11.098v-88.637c0-6.129-4.968-11.098-11.097-11.098z"/><path style="fill:#ebf0f3" d="M149.565 233.626h152.177v9.4H149.565zM149.565 263.168h152.177v9.4H149.565zM149.565 292.762h152.177v9.4H149.565zM156.714 96.355v58.059c0 5.443 4.413 9.856 9.856 9.856h118.156c5.443 0 9.856-4.413 9.856-9.856V96.355H156.714z"/><path style="fill:#3a556a" d="M259.491 107.622h15.698v44.022h-15.698z"/></svg>`;
div.innerHTML = type === "save" ? save : telegram;
return div;
}
async function onSelectFolder() {
const dirHandle = await window.showDirectoryPicker({ id, mode: "readwrite" });
await set(idbKey, dirHandle);
return dirHandle;
}
async function writeImage(item, dirHandler) {
const fileHandle = await get(item.url);
if (fileHandle) {
try {
const file = await fileHandle.getFile();
if (file)
return console.log(`url=${item.url} exists`);
} catch (e) {
if (e.code == DOMException.NOT_FOUND_ERR) {
console.log(`url=${item.url} file has removed, pls download regain`);
}
}
}
let filetype = "";
const image = await fetch(item.url).then((res) => {
const headers = res.headers;
const contentType = headers.get("Content-Type");
if (contentType)
filetype = contentType.replace(/image\//, "");
return res;
}).then((res) => res.blob());
const filename = item.filename + "." + filetype;
for await (let name of dirHandler.keys())
if (name == filename)
return;
const fileHandler = await dirHandler.getFileHandle(filename, { create: true });
const w = await fileHandler.createWritable();
await w.write(image);
await w.close();
await set(item.url, fileHandler);
}
async function onWriteImage(item, dirHandler) {
if (!item.url)
return;
ping(item.url);
await writeImage(item, dirHandler);
}
function onSaveImagesToLocal(list2, folder) {
return new Promise((resolve) => {
function save(list22, index = 0) {
if (list22.length === index) {
resolve(true);
return;
}
const p = onWriteImage(list22[index], folder);
p.finally(() => {
save(list22, index + 1);
});
}
save(list2);
});
}
async function trySaveImagesToLocale(images) {
let dirHandle = await get(idbKey);
if (!dirHandle)
dirHandle = await onSelectFolder();
const date = /* @__PURE__ */ new Date();
const dirname = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, "0")}${String(date.getDate()).padStart(2, "0")}`;
let permission = true;
let folder = null;
try {
folder = await dirHandle.getDirectoryHandle(dirname, { create: true });
} catch (e) {
permission = false;
}
if (permission === false) {
await del(idbKey);
return trySaveImagesToLocale(images);
}
if (folder == null) {
console.log("no folder selected.");
return;
}
try {
await onSaveImagesToLocal(images, folder);
notify("Saved!");
} catch (e) {
console.log("save directory handle failed");
}
}
function action(fn) {
let timer = 0;
function run2() {
if (typeof fn === "function") {
try {
fn();
} catch (e) {
}
}
timer = setTimeout(() => {
run2();
}, 60);
}
run2();
return function cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
}
};
}
function* treeWalker(root = document.body, filter2 = () => NodeFilter.FILTER_ACCEPT) {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter2);
do {
yield walker.currentNode;
const shadowRoot = walker.currentNode.shadowRoot;
for (let child = shadowRoot ? shadowRoot.firstElementChild : walker.currentNode.firstElementChild; child; child = child.nextElementSibling) {
yield* treeWalker(child, filter2);
}
} while (walker.nextNode());
}
function useInterval(n, handler2) {
return (task2) => {
const inner = () => {
const scripts2 = Array.isArray(handler2) ? handler2 : [handler2];
scripts2.forEach((script) => script(task2));
setTimeout(() => {
var _a;
if (((_a = task2.meta) == null ? void 0 : _a.pause) == false) {
inner();
}
}, n * 1e3);
};
inner();
};
}
const otherTask = [
{
match: "https://www.cguardian.com/auctions/live-bidding",
meta: { pause: false },
script: useInterval(0.1, (task2) => {
for (let node of treeWalker()) {
if (node.nodeName == "VIDEO") {
node.setAttribute("controls", "");
task2.meta.pause = true;
break;
}
}
})
},
{
match: "https://helloacm.com/",
script: removeRemovedAdBlockerModal
},
{
match: "https://www.flickr.com/photos/",
script: createHiddenNodesBySelectorsTask([
".facade-of-protection-zoom"
])
},
{
match: true,
script: () => {
const tags = [
"html",
"body",
".grayTheme"
];
tags.forEach((tag) => {
const nodeList = document.querySelectorAll(tag);
nodeList.forEach((node) => {
node.style.filter = "initial";
});
});
}
},
{
match: new URLPattern({ protocol: "https", hostname: "paulgraham.com" }),
script: function() {
const selectors2 = ["table", "td"];
selectors2.forEach((selector) => {
const elements = document.querySelectorAll(selector);
Array.from(elements).forEach((element) => {
if (element.hasAttribute("width")) {
element.removeAttribute("width");
}
});
});
}
},
{
match: true,
script: () => {
let contextmenuTarget = {
type: "",
payload: null
};
function clear() {
contextmenuTarget = {
type: "",
payload: null
};
}
function setValue(type, payload) {
contextmenuTarget = { type, payload };
}
window.addEventListener("contextmenu", (e) => {
const target = e.target;
if (target == null)
return;
const node = target;
let type = "url";
let payload = `${document.title}
${location.href}`;
if (node.nodeName === "IMG") {
let src = node.getAttribute("src");
if (src) {
const pattern = new URLPattern({ hostname: "{*.}?zhihu.com" });
if (pattern.test(location.href)) {
const token = node.getAttribute("data-original-token");
if (token)
src = src.replace(/v2-.*?(?=\.)/, token);
}
type = "image";
payload = src;
}
} else {
const selection = document.getSelection();
if (selection) {
const st = selection.type;
if (st === "Range") {
const raw = selection.toString();
const text = raw.trim();
if (text) {
type = "selection";
payload = text + `
Ref: [${document.title.trim() || location.href}](${location.href})`;
}
}
}
}
setValue(type, payload);
});
window.addEventListener("scroll", clear);
window.addEventListener("pointerdown", () => {
if (contextmenuTarget.type !== "")
clear();
});
_GM_registerMenuCommand("Telegram Channel[039]", () => {
switch (contextmenuTarget.type) {
case "image":
sendPhoto(contextmenuTarget.payload);
break;
case "selection":
sendMessage(contextmenuTarget.payload, "MarkdownV2");
break;
case "url":
sendMessage(contextmenuTarget.payload);
break;
default:
sendMessage(`${document.title}
${location.href}`);
break;
}
clear();
});
}
},
{
match: true,
script: () => {
_GM_registerMenuCommand("分享到 Telegram", function() {
const url = `https://t.me/share/url?url=${encodeURIComponent(top.location.href)}&text=${encodeURIComponent(top.document.title)}`;
_GM_openInTab(url, { active: true });
});
_GM_registerMenuCommand("更新脚本", function() {
const url = _GM_info.scriptUpdateURL;
if (url)
_GM_openInTab(url, { active: true });
});
}
}
];
_unsafeWindow.telegram = {
sendPhoto,
sendMediaGroup,
sendMessage
};
function toResponse(res) {
const headersText = (res.responseHeaders || "").trim();
const headers = headersText.split("\r\n").map((line) => line.split(":")).reduce((headers2, item) => {
const [key, value] = item;
if (key)
headers2.append(key, value);
return headers2;
}, new Headers());
return new Response(res.response, {
status: res.status,
statusText: res.statusText,
headers
});
}
function toRequest(req) {
const method = (req.method || "GET").toUpperCase();
const headers = {};
const rawHeaders = req.headers ? req.headers : {};
if (Array.isArray(rawHeaders)) {
rawHeaders.forEach(([key, value]) => {
headers[key] = value;
});
} else if (rawHeaders instanceof Headers) {
for (let key of rawHeaders.keys()) {
const value = rawHeaders.get(key);
if (value)
headers[key] = value;
}
}
const gmXhrRequest = {
// 补上...
url: "",
// string, usually one of GET, HEAD, POST, PUT, DELETE, ...
method,
// 特殊处理 [done]
headers,
redirect: req.redirect,
// default `follow`
// 特殊处理
// cookie,
// send the data string in binary mode
// binary,
// boolean
// nocache: req.cache,
// revalidate maybe cached content
// revalidate
// context,
// @NOTE: 让用户选择 - Response
// responseType,
// a MIME type for the request
// overrideMimeType
// don't send cookies with the request
anonymous: req.credentials == void 0 || req.credentials == "omit" ? true : false,
// user, password,
responseType: "blob"
};
const body = req.body;
if (body && method === "GET") {
gmXhrRequest.data = body;
}
return gmXhrRequest;
}
function _xmlHttpRequest(req) {
const raw = _GM.xmlHttpRequest(req);
const promise = raw.then(toResponse);
promise.abort = raw.abort;
return promise;
}
_unsafeWindow.x181 = {
post(url, options = {}) {
const req = toRequest(options);
return _xmlHttpRequest({ ...req, method: "POST", url });
},
get(url, options = {}) {
const req = toRequest(options);
return _xmlHttpRequest({ ...req, method: "GET", url });
},
fetch(url, options = {}) {
const method = String(options && options.method || "GET");
return method.toUpperCase() === "POST" ? this.post(url, options) : this.get(url, options);
},
async saveFile(dirHandler, filename, data) {
const fileHandler = await dirHandler.getFileHandle(filename, { create: true });
const w = await fileHandler.createWritable();
await w.write(data);
await w.close();
},
selectFolder() {
return window.showDirectoryPicker({ id: String(Date.now()), mode: "readwrite" });
},
async save(map) {
const folder = await this.selectFolder();
if (folder == null)
return;
for (let [filename, url] of Object.entries(map)) {
url = url.startsWith("//") ? "https:" + url : url.startsWith("/") ? location.origin + url : url;
const blob = await this.fetch(url).then((res) => res.blob());
await this.saveFile(folder, filename, blob);
}
}
};
const scripts = [
task$4,
bingTask,
doubanTask,
dubokuTask,
mysmthTask,
otherTask,
weiboTask,
weChatTask,
zhihuTask,
redirectTask,
twitterTask
//windTask,
].flat(Infinity);
core(scripts, location.href);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment