// ==UserScript== // @name 🔥🔥起点中文网VIP内容免费解析🔥🔥 // @namespace https://www.softrr.cn/ // @version 1.1.0 // @author hackhase // @description 起点中文网VIP内容免费解析,支持VIP章节,支持本章节内容直接下载成txt文件。 // @license MIT // @icon https://qdfepccdn.qidian.com/www.qidian.com/favicon/qd_icon.ico // @match *://*.qidian.com/chapter/* // @require https://cdn.jsdelivr.net/npm/vue@3.3.11/dist/vue.global.prod.js // @require data:application/javascript,%3Bwindow.Vue%3DVue%3B // @connect www.softrr.cn // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant unsafeWindow // @downloadURL none // ==/UserScript== (o=>{if(typeof GM_addStyle=="function"){GM_addStyle(o);return}const e=document.createElement("style");e.textContent=o,document.head.append(e)})(" :root{font-family:Inter,Avenir,Helvetica,Arial,sans-serif;font-size:16px;line-height:24px;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}.card{padding:2em}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}.modal-wrapper[data-v-05e9b29a]{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:9999}.modal[data-v-05e9b29a]{background-color:#fff;padding:20px;border-radius:5px}.header[data-v-05e9b29a]{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.header h2[data-v-05e9b29a]{margin:0;font-size:20px;font-weight:700}.header button[data-v-05e9b29a]{border:none;background-color:transparent;font-size:20px;cursor:pointer}.content[data-v-05e9b29a]{max-height:400px;overflow:auto;font-size:16px;display:flex;justify-content:space-between}.content .produce p[data-v-05e9b29a]{margin-top:15px}.content .produce .ipt[data-v-05e9b29a]{border:1px solid #ccc;margin-top:15px;width:200px;height:30px;border-radius:5px;padding-left:10px}.content .img[data-v-05e9b29a]{display:flex;align-items:center;justify-content:center}.content .img img[data-v-05e9b29a]{width:180px}input[data-v-05e9b29a]::-webkit-input-placeholder{color:#aab2bd;font-size:14px;padding-left:5px}.copy[data-v-51a71ca5]{width:160px;position:fixed;right:10px;top:80px;color:#111;z-index:999;display:flex;flex-direction:column}.copy .prase[data-v-51a71ca5],.copy .download[data-v-51a71ca5]{width:80px;height:80px;background-color:red;color:#fff;border-radius:50%}.copy .prase[data-v-51a71ca5]:hover{background-color:#87ceeb;color:#fff}.copy .download[data-v-51a71ca5]{background-color:#171602}.copy .download[data-v-51a71ca5]:hover{background-color:#a087eb;color:#fff} "); (function (vue) { 'use strict'; var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)(); const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _withScopeId$1 = (n) => (vue.pushScopeId("data-v-05e9b29a"), n = n(), vue.popScopeId(), n); const _hoisted_1$1 = { class: "modal" }; const _hoisted_2$1 = { class: "header" }; const _hoisted_3 = { class: "content" }; const _hoisted_4 = { class: "produce" }; const _hoisted_5 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("p", null, "1、扫描右侧公众号,点击关注!", -1)); const _hoisted_6 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("p", null, "2、在软件爬取者后台回复:验证码", -1)); const _hoisted_7 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("p", null, "3、在下方输入框输入获取的验证码后回车", -1)); const _hoisted_8 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "img" }, [ /* @__PURE__ */ vue.createElementVNode("img", { src: "https://www.softrr.cn/assets/pqz-daa4b840.jpg", alt: "" }) ], -1)); const _sfc_main$1 = { __name: "Model", props: { title: { type: String, required: true }, code: { type: String || Number } }, setup(__props, { expose: __expose }) { const props = __props; const visible = vue.ref(false); const openModal = () => { visible.value = true; }; const closeModal = () => { visible.value = false; }; __expose({ visible, openModal, closeModal }); const codeValue = vue.ref(); const enterCode = () => { if (codeValue.value == props.code) { localStorage.setItem("code", codeValue.value); visible.value = false; alert("验证成功,请再次点击解析!"); codeValue.value = ""; } else { alert("验证码错误,请重新输入!"); codeValue.value = ""; } }; return (_ctx, _cache) => { return vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", { class: "modal-wrapper", onClick: vue.withModifiers(closeModal, ["self"]) }, [ vue.createElementVNode("div", _hoisted_1$1, [ vue.createElementVNode("div", _hoisted_2$1, [ vue.createElementVNode("h2", null, vue.toDisplayString(__props.title), 1), vue.createElementVNode("button", { onClick: closeModal }, "X") ]), vue.createElementVNode("div", _hoisted_3, [ vue.createElementVNode("div", _hoisted_4, [ _hoisted_5, _hoisted_6, _hoisted_7, vue.withDirectives(vue.createElementVNode("input", { class: "ipt", type: "text", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => codeValue.value = $event), onKeydown: vue.withKeys(enterCode, ["enter"]), placeholder: "请输入验证码后按回车" }, null, 544), [ [vue.vModelText, codeValue.value] ]) ]), _hoisted_8 ]) ]) ], 512)), [ [vue.vShow, visible.value] ]); }; } }; const Model = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-05e9b29a"]]); var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; function getDefaultExportFromCjs(x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; } var FileSaver_min = { exports: {} }; (function(module, exports) { (function(a, b) { b(); })(commonjsGlobal, function() { function b(a2, b2) { return "undefined" == typeof b2 ? b2 = { autoBom: false } : "object" != typeof b2 && (console.warn("Deprecated: Expected third argument to be a object"), b2 = { autoBom: !b2 }), b2.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a2.type) ? new Blob(["\uFEFF", a2], { type: a2.type }) : a2; } function c(a2, b2, c2) { var d2 = new XMLHttpRequest(); d2.open("GET", a2), d2.responseType = "blob", d2.onload = function() { g(d2.response, b2, c2); }, d2.onerror = function() { console.error("could not download file"); }, d2.send(); } function d(a2) { var b2 = new XMLHttpRequest(); b2.open("HEAD", a2, false); try { b2.send(); } catch (a3) { } return 200 <= b2.status && 299 >= b2.status; } function e(a2) { try { a2.dispatchEvent(new MouseEvent("click")); } catch (c2) { var b2 = document.createEvent("MouseEvents"); b2.initMouseEvent("click", true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null), a2.dispatchEvent(b2); } } var f = "object" == typeof window && window.window === window ? window : "object" == typeof self && self.self === self ? self : "object" == typeof commonjsGlobal && commonjsGlobal.global === commonjsGlobal ? commonjsGlobal : void 0, a = f.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent), g = f.saveAs || ("object" != typeof window || window !== f ? function() { } : "download" in HTMLAnchorElement.prototype && !a ? function(b2, g2, h) { var i = f.URL || f.webkitURL, j = document.createElement("a"); g2 = g2 || b2.name || "download", j.download = g2, j.rel = "noopener", "string" == typeof b2 ? (j.href = b2, j.origin === location.origin ? e(j) : d(j.href) ? c(b2, g2, h) : e(j, j.target = "_blank")) : (j.href = i.createObjectURL(b2), setTimeout(function() { i.revokeObjectURL(j.href); }, 4e4), setTimeout(function() { e(j); }, 0)); } : "msSaveOrOpenBlob" in navigator ? function(f2, g2, h) { if (g2 = g2 || f2.name || "download", "string" != typeof f2) navigator.msSaveOrOpenBlob(b(f2, h), g2); else if (d(f2)) c(f2, g2, h); else { var i = document.createElement("a"); i.href = f2, i.target = "_blank", setTimeout(function() { e(i); }); } } : function(b2, d2, e2, g2) { if (g2 = g2 || open("", "_blank"), g2 && (g2.document.title = g2.document.body.innerText = "downloading..."), "string" == typeof b2) return c(b2, d2, e2); var h = "application/octet-stream" === b2.type, i = /constructor/i.test(f.HTMLElement) || f.safari, j = /CriOS\/[\d]+/.test(navigator.userAgent); if ((j || h && i || a) && "undefined" != typeof FileReader) { var k = new FileReader(); k.onloadend = function() { var a2 = k.result; a2 = j ? a2 : a2.replace(/^data:[^;]*;/, "data:attachment/file;"), g2 ? g2.location.href = a2 : location = a2, g2 = null; }, k.readAsDataURL(b2); } else { var l = f.URL || f.webkitURL, m = l.createObjectURL(b2); g2 ? g2.location = m : location.href = m, g2 = null, setTimeout(function() { l.revokeObjectURL(m); }, 4e4); } }); f.saveAs = g.saveAs = g, module.exports = g; }); })(FileSaver_min); var FileSaver_minExports = FileSaver_min.exports; const FileSaver = /* @__PURE__ */ getDefaultExportFromCjs(FileSaver_minExports); const _withScopeId = (n) => (vue.pushScopeId("data-v-51a71ca5"), n = n(), vue.popScopeId(), n); const _hoisted_1 = { class: "copy" }; const _hoisted_2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("br", null, null, -1)); const _sfc_main = { __name: "App", setup(__props) { const getCode = () => { return new Promise((resolve, reject) => { _GM_xmlhttpRequest({ method: "GET", url: `https://www.softrr.cn/crawler/getCode`, headers: { Referer: "https://www.softrr.cn/", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.289 Safari/537.36" }, onload: function(res) { resolve(JSON.parse(res.response).data[0].code); } }); }); }; const model = vue.ref(""); vue.ref("https://www.softrr.cn/crawler/getDushuge?"); const title = vue.ref("为了减少端口压力,防止滥用,采取必要的验证手段。"); var baseUrl = window.location.href; if (baseUrl.includes("book")) { var bookName = document.querySelector("#bookName").innerText.trim(); } else if (baseUrl.includes("chapter")) { var bookName = document.querySelector("#r-breadcrumbs").children[6].innerText.trim(); } var chartName = document.querySelector(".title").innerHTML.split("<")[0].trim(); const getDushuge = () => { return new Promise((resolve, reject) => { _GM_xmlhttpRequest({ method: "GET", url: `https://www.softrr.cn/crawler/getDushuge?name=${bookName}&chartName=${chartName}`, headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.289 Safari/537.36" }, onload: function(res) { resolve(JSON.parse(res.response).data); }, onerror: function(error) { reject("服务器错误,请稍后再试!", error); } }); }); }; const code = vue.ref(); const onPrase = async () => { code.value = await getCode(); let locaCode = localStorage.getItem("code") || ""; if (locaCode == code.value) { const data = await getDushuge(); document.querySelector("main").innerHTML = data; document.querySelectorAll("section")[1].style.display = "none"; } else { model.value.openModal(); } }; const onDown = async () => { code.value = await getCode(); let locaCode = localStorage.getItem("code") || ""; if (locaCode == code.value) { let data = await getDushuge(); data = data.replace(/<\/p>
/g, "\n").replace("
", "").replace("
", ""); const blob = new Blob([data], { type: "text/plain" }); FileSaver.saveAs(blob, chartName + ".txt"); } else { model.value.openModal(); } }; return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [ vue.createElementVNode("button", { onClick: onPrase, class: "prase" }, "解析"), vue.createElementVNode("button", { onClick: onDown, class: "download" }, [ vue.createTextVNode("本章"), _hoisted_2, vue.createTextVNode("下载") ]), vue.createVNode(Model, { title: title.value, code: code.value, ref_key: "model", ref: model }, null, 8, ["title", "code"]) ]); }; } }; const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-51a71ca5"]]); vue.createApp(App).mount( (() => { const app = document.createElement("div"); document.body.append(app); return app; })() ); })(Vue);