您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
吧啦吧啦
// ==UserScript== // @name B漫接口文档类型自动获取 // @namespace mimiko/bilibili-manga-api-doc // @version 0.0.12 // @description 吧啦吧啦 // @author Mimiko // @license MIT // @match *://comic.bilibili.co/api-doc/* // @grant GM.addStyle // @run-at document-start // ==/UserScript== // https://greasyfork.org/zh-CN/scripts/470030-b%E6%BC%AB%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3%E7%B1%BB%E5%9E%8B%E8%87%AA%E5%8A%A8%E8%8E%B7%E5%8F%96 "use strict"; (() => { if (window.top !== window.self) return; // variables const regexOptional = new RegExp( ["不传", "不填", "可选", "选传", "选填", "非必传", "非必填"].join("|"), ); // functions /** 将字符串转为小驼峰 */ const camelCase = (input) => input .replace(/[^a-zA-Z0-9]/g, " ") .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => index === 0 ? word.toLowerCase() : word.toUpperCase(), ) .replace(/\s+/g, ""); /** 复制到剪贴板 */ const copy = (content) => { if (navigator.clipboard) { navigator.clipboard.writeText(content); return; } const textarea = document.createElement("textarea"); textarea.value = content; document.body.appendChild(textarea); textarea.select(); document.execCommand("copy"); document.body.removeChild(textarea); }; /** 格式化;核心方法 */ const format = (input) => input // [{ ... }] -> { ... }[] .replace(/: \[{/g, ": {") .replace(/}],/g, "}[],") // {} -> Record<string, never> .replace(/{}/g, "Record<string, never>") // bool -> boolean .replace(/false, \/\/ type<bool>/g, "boolean //") // i32 -> number .replace(/0, \/\/ type<int32>/g, "number //") // i64 -> `${bigint}` .replace(/"0", \/\/ type<int64>/g, "`${bigint}` //") // double -> number .replace(/0.0, \/\/ type<double>/g, "number //") // float -> number .replace(/0.0, \/\/ type<float>/g, "number //") // string -> string .replace(/"", \/\/ type<string>/g, "string //") // i32[] -> number[] .replace(/\[0], \/\/ list<int32>/g, "number[] //") // i64[] -> `${bigint}`[] .replace(/\["0"], \/\/ list<int64>/g, "`${bigint}`[] //") // string[] -> string[] .replace(/\[""], \/\/ list<string>/g, "string[] //") // UNKNOWN -> unknown .replace(/UNKNOWN, \/\/ type<>/g, "unknown //") // {"0": false} -> Record<string, boolean> .replace( /: {(.*?)\n(\s*)"0": false\n\s*}/g, ": $1\n$2Record<string, boolean>", ) // {"": ""} -> Record<string, string> .replace(/: {(.*?)\n(\s*)"": ""\n\s*}/g, ": $1\n$2Record<string, string>") // {"0": {}} -> Record<string, {}> .replace(/: {(.*?)\n(\s*)"0": {/g, ": $1\n$2Record<string, {") // {"0": 0} -> Record<string, number> .replace(/: {(.*?)\n(\s*)"0": 0\n\s*}/g, ": $1\n$2Record<string, number>") .replace(/}\n\s*},/g, "}>") // remove useless comma .replace(/},\n/g, "}\n") .replace(/>,\n/g, ">\n") // remove useless comment .replace(/\/\/ list<.*/g, "") .replace(/\/\/ map<.*/g, "") .replace(/}, \/\/ type<.*/g, "},") // remove empty inline comment .replace(/\s*\/\/\s*$/gm, "") // '// xxx' -> /** xxx */ .replace(/\/\/\s*(.*?)\n/g, "/** $1 */\n") // */\n/** -> * .replace(/\*\/\n(\s*)\/\*\*/g, "\n$1 *") // replace 4 spaces with 2 spaces .replace(/ {4}/g, " ") // optional parameters .replace(/\/\*\*.+?\*\/\n.+?:/g, (text) => { if (regexOptional.test(text)) text = text.replace(/:$/, "?:"); return text; }); /** 生成代码 */ const getContent = (btn) => { const data = pick(btn); if (!data) return; const name = upperFirst(camelCase(data.name.split("/").pop() ?? "")); if (!name) return; const { description, url } = data; const request = format(data.request); const response = format(data.response); const head = ` /** * ${data.name} * @description ${description} * @see ${url} */`; return ` ${head} export type Request${name} = ${request} ${head} export type Response${name} = ${response} `; }; /** 注入按钮 */ const inject = (callback) => { // h1 document.querySelectorAll("h1").forEach((h1) => { const btn = document.createElement("button"); btn.addEventListener("click", onClickAll); btn.classList.add("btn", "btn-primary"); btn.innerText = "复制全部类型"; btn.style.marginLeft = "10px"; h1.appendChild(btn); }); // h2 document.querySelectorAll("h2").forEach((h2, i) => { h2.dataset.name = h2.textContent?.trim() ?? ""; const btn = document.createElement("button"); btn.addEventListener("click", () => onClick(btn)); btn.classList.add("btn", "btn-primary"); btn.innerText = "复制类型"; btn.style.marginLeft = "10px"; btn.dataset.index = i.toString(); h2.appendChild(btn); }); // .sourceCode document.querySelectorAll(".sourceCode").forEach((div) => { div.dataset.code = div.textContent?.trim() ?? ""; }); // callback callback(); }; /** 主函数 */ const main = () => { GM.addStyle(".sourceCode { pointer-events: none; }"); window.addEventListener("load", () => inject(() => GM.addStyle(".sourceCode { pointer-events: auto; }")), ); }; /** “复制类型” */ const onClick = (btn) => { const content = getContent(btn); if (!content) { alert("获取类型失败"); return; } copy(content); }; /** “复制全部类型” */ const onClickAll = () => { const content = [...document.querySelectorAll("h2 button")] .map(getContent) .join(""); copy(content); }; /** 获取页面文本 */ const pick = (btn) => { const h2 = btn.parentElement; if (!h2) return; const { name } = h2.dataset; if (!name) return; const url = [ window.location.origin, window.location.pathname, "#", name.toLowerCase().replace(/\./g, "").replace(/\//g, ""), ].join(""); const p = h2.nextElementSibling; if (!p) return; const description = p.textContent?.trim(); if (!description) return; const { index } = btn.dataset; if (!index) return; const i = parseInt(index); const divReq = document.getElementById(`cb${1 + i * 2}`); if (!divReq) return; const request = divReq.dataset.code; if (!request) return; const divRes = document.getElementById(`cb${2 + i * 2}`); if (!divRes) return; const response = divRes.dataset.code; if (!response) return; return { description, name, request, response, url, }; }; /** 首字母大写 */ const upperFirst = (input) => input.charAt(0).toUpperCase() + input.slice(1); // execute main(); })();