// ==UserScript== // @name PT Helper // @name:zh-CN PT 助手 // @version 0.1.0 // @namespace https://github.com/amorphobia/pt-helper // @description A helper for private trackers // @description:zh-CN 私密种子站点的助手 // @author amorphobia // @homepage https://github.com/amorphobia/pt-helper // @icon data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+IDxzdmcgaWQ9IkxheWVyXzEiIGRhdGEtbmFtZT0iTGF5ZXIgMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmQzOGQ7fS5jbHMtMntmaWxsOiM2ZDQxMzU7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5zaGFyZS1maWxsZWQ8L3RpdGxlPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iNDA1LjMzIiBjeT0iMTA2LjY3IiByPSI4NS4zMyIvPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iNDA1LjMzIiBjeT0iNDA1LjMzIiByPSI4NS4zMyIvPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iMTA2LjY3IiBjeT0iMjU2IiByPSI4NS4zMyIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTQwNS4zMywyOTguNjdhMTA2LjU0LDEwNi41NCwwLDAsMC04My45LDQwLjg2TDIwOS42OSwyODMuNjZhMTA2Ljc4LDEwNi43OCwwLDAsMCwwLTU1LjMxbDExMS43NS01NS44N2ExMDYuMjcsMTA2LjI3LDAsMSwwLTE5LjEzLTM4LjE1TDE5MC41NiwxOTAuMmExMDYuNjcsMTA2LjY3LDAsMSwwLDAsMTMxLjZsMTExLjc1LDU1Ljg3YTEwNi42NywxMDYuNjcsMCwxLDAsMTAzLTc5Wm0wLTI1NmE2NCw2NCwwLDEsMS02NCw2NEE2NC4wNyw2NC4wNywwLDAsMSw0MDUuMzMsNDIuNjdaTTEwNi42NywzMjBhNjQsNjQsMCwxLDEsNjQtNjRBNjQuMDcsNjQuMDcsMCwwLDEsMTA2LjY3LDMyMFpNNDA1LjMzLDQ2OS4zM2E2NCw2NCwwLDEsMSw2NC02NEE2NC4wNyw2NC4wNywwLDAsMSw0MDUuMzMsNDY5LjMzWiIvPjwvc3ZnPg== // @supportURL https://github.com/amorphobia/pt-helper/issues // @license AGPL-3.0-or-later // @match *://hhanclub.top/* // @match *://nanyangpt.com/* // @match *://pt.sjtu.edu.cn/* // @match *://tjupt.org/* // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_openInTab // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // @grant GM_xmlhttpRequest // @noframes // @downloadURL none // ==/UserScript== /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ([ /* 0 */, /* 1 */ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Hhanclub = void 0; const index_1 = __webpack_require__(2); class Hhanclub extends index_1.NexusPHP { constructor() { super("hhanclub.top"); this.menu_items = [ { "id": "bannerFold", "type": "switch", "display": "自动折叠横幅(隐藏时折叠设置无效)", "name": "自动折叠横幅", "value": true }, { "id": "bannerHide", "type": "switch", "display": "隐藏横幅(隐藏时折叠设置无效)", "name": "隐藏横幅", "value": false } ].concat(this.menu_items); } onLoad() { super.onLoad(); if (this.getHostValue("bannerHide")) { this.css += ` td.clear.nowrap img { display: none; }`; } else if (this.getHostValue("bannerFold")) { const banner = document.querySelector("td.clear.nowrap"); const original_height = banner === null || banner === void 0 ? void 0 : banner.clientHeight; this.css += ` td.clear.nowrap img { height: 10px; overflow: hidden; transition: height 0.5s; } td.clear.nowrap img:hover { height: ${original_height}px; }`; } } } exports.Hhanclub = Hhanclub; /***/ }), /* 2 */ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.NexusPHP = void 0; const common_1 = __webpack_require__(3); class NexusPHP extends common_1.Common { constructor(host) { super(host); this.passkey = ""; this.menu_items = [ { "id": "thanks", "type": "switch", "display": "自动说谢谢", "name": "自动说谢谢", "value": true } ].concat(this.menu_items); } onLoad() { super.onLoad(); this.getPasskey(); if (this.getHostValue("thanks") && location.href.indexOf("/details.php") >= 0) { this.sayThanks(); } } getPasskey() { const value = this.getHostValue("passkey"); let passkey = ""; if (value) { passkey = String(value); } if (passkey != "") { this.passkey = passkey; return; } const cp_url = "https://" + this.host + "/usercp.php"; GM_xmlhttpRequest({ method: "GET", url: cp_url, onload: (response) => { if (response.status != 200) { console.log("Failed to get passkey."); return; } const container = document.implementation.createHTMLDocument().documentElement; container.innerHTML = response.responseText; const tds = container.querySelectorAll("td.rowfollow"); for (const td of tds) { const tc = td; const re = /[\w\d]{32}/; const result = re.exec(tc.innerText); if (result) { this.setHostValue("passkey", result[0]); this.passkey = result[0]; return; } } }, onabort: () => { console.log("Abort to get passkey"); }, onerror: () => { console.log("Error to get passkey"); } }); } sayThanks() { this.wait(2000).then(() => { const input = document.querySelector("#saythanks"); if (input && !input.disabled) { input.click(); } }).catch(() => { console.log("Failed to say thanks."); }); } } exports.NexusPHP = NexusPHP; /***/ }), /* 3 */ /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Common = void 0; const helper_home = "https://github.com/amorphobia/pt-helper"; const num_emoji = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"]; class Common { constructor(host) { this.host = ""; this.menu_items = [{ "id": "feedback", "type": "link", "display": "💬反馈与建议", "value": helper_home + "/issues" }]; this.host = host; this.registered_items = []; this.css = ""; } init() { for (const item of this.menu_items) { item.id = this.host + "_" + item.id; if ((item.type == "switch" || item.type == "selection") && GM_getValue(item.id) == null) { GM_setValue(item.id, item.value); } } this.registerMenu(); } onLoad() { } addStyle() { if (typeof GM_addStyle !== "undefined") { GM_addStyle(this.css); } else { const style = document.createElement("style"); style.appendChild(document.createTextNode(this.css)); (document.querySelector("head") || document.documentElement).appendChild(style); } } registerMenu() { for (const item of this.registered_items) { GM_unregisterMenuCommand(item); } this.registered_items = []; for (const item of this.menu_items) { const value = GM_getValue(item.id); if (value && value != null) { item.value = value; } let reg_item; switch (item.type) { case "switch": reg_item = GM_registerMenuCommand(`${item.value ? "✅" : "❌"}${item.display}`, () => { this.toggleSwitch(item); }); break; case "selection": reg_item = GM_registerMenuCommand(`${num_emoji[item.value]}${item.display[item.value]}`, () => { this.nextSelection(item); }); break; case "link": reg_item = GM_registerMenuCommand(`${item.display}`, () => { GM_openInTab(item.value, { active: true, insert: true, setParent: true }); }); break; case "text": reg_item = GM_registerMenuCommand(`${item.display}`, () => { }); break; default: console.log(`Unrecognized menu item: ${item.id}`); break; } if (reg_item !== undefined) { this.registered_items.push(reg_item); } } } toggleSwitch(item) { const status = item.value ? "关闭" : "开启"; GM_setValue(item.id, !item.value); GM_notification({ text: `已${status}「${item.name}」\n(点击刷新网页后生效)`, timeout: 3500, onclick: () => { location.reload(); } }); this.registerMenu(); } nextSelection(item) { const new_value = (item.value + 1) % item.display.length; GM_setValue(item.id, new_value); GM_notification({ text: `切换为「${item.display[new_value]}」\n(点击刷新网页后生效)`, timeout: 3500, onclick: () => { location.reload(); } }); this.registerMenu(); } wait(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } getHostValue(id) { return GM_getValue(this.host + "_" + id); } setHostValue(id, value) { GM_setValue(this.host + "_" + id, value); } } exports.Common = Common; /***/ }), /* 4 */ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.NanyangPT = void 0; const NexusPHP_1 = __webpack_require__(2); class NanyangPT extends NexusPHP_1.NexusPHP { constructor() { super("nanyangpt.com"); this.menu_items = [ { "id": "bannerHide", "type": "switch", "display": "隐藏横幅", "name": "隐藏横幅", "value": false } ].concat(this.menu_items); } onLoad() { super.onLoad(); if (this.getHostValue("bannerHide")) { const info = document.querySelector("#info_block"); const info_height = (info === null || info === void 0 ? void 0 : info.clientHeight) ? info.clientHeight + 5 : 30; this.css += ` table.head { display: none; } table.mainouter { margin-top: ${info_height}px; }`; } } } exports.NanyangPT = NanyangPT; /***/ }), /* 5 */ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SJTU = void 0; const index_1 = __webpack_require__(2); class SJTU extends index_1.NexusPHP { constructor() { super("pt.sjtu.edu.cn"); } } exports.SJTU = SJTU; /***/ }), /* 6 */ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.TJUPT = void 0; const index_1 = __webpack_require__(2); class TJUPT extends index_1.NexusPHP { constructor() { super("tjupt.org"); this.menu_items = [ { "id": "bannerFold", "type": "switch", "display": "自动折叠横幅(隐藏时折叠设置无效)", "name": "自动折叠横幅", "value": true }, { "id": "bannerHide", "type": "switch", "display": "隐藏横幅(隐藏时折叠设置无效)", "name": "隐藏横幅", "value": false }, { "id": "stickyHide", "type": "selection", "display": [ "显示所有置顶", "隐藏一重置顶", "隐藏一、二重置顶", "隐藏所有置顶" ], "value": 0 }, { "id": "directLink", "type": "switch", "display": "种子直链按钮(左键点击按钮复制直链)", "name": "种子直链", "value": true }, { "id": "colorBlind", "type": "switch", "display": "色盲模式", "name": "色盲模式", "value": false } ].concat(this.menu_items); } onLoad() { var _a; super.onLoad(); if (this.getHostValue("bannerHide")) { this.css += ` .logo_img img { display: none; }`; } else if (this.getHostValue("bannerFold")) { const logo_img = document.querySelector(".logo_img"); const original_height = logo_img === null || logo_img === void 0 ? void 0 : logo_img.clientHeight; this.css += ` .logo_img { height: 10px; overflow: hidden; transition: height 0.5s; } .logo_img:hover { height: ${original_height}px; }`; } switch (this.getHostValue("stickyHide")) { case 3: this.css += ` .triple_sticky_bg { display: none; }`; case 2: this.css += ` .double_sticky_bg { display: none; }`; case 1: this.css += ` .sticky_bg { display: none; }`; default: break; } if (this.getHostValue("directLink") && this.passkey != "") { const id_re = /id=[\d]+/; const tds = document.querySelectorAll("table.torrentname > tbody > tr:nth-of-type(1) > td:nth-of-type(3)"); for (const td of tds) { const dl = td.querySelector("a"); const result = id_re.exec((_a = dl === null || dl === void 0 ? void 0 : dl.href) !== null && _a !== void 0 ? _a : ""); if (!result) { continue; } const direct_link = `https://www.${this.host}/download.php?${result[0]}&passkey=${this.passkey}`; const img = document.createElement("img"); img.setAttribute("src", "pic/trans.gif"); img.setAttribute("class", "torrent_direct_link"); img.setAttribute("alt", "DL"); const a = document.createElement("a"); a.setAttribute("title", "左键单击复制,链接中包含个人秘钥Passkey,切勿泄露!"); a.setAttribute("onclick", "return false"); a.setAttribute("id", "direct_link"); a.setAttribute("href", direct_link); a.setAttribute("data-clipboard-text", direct_link); a.appendChild(img); td.prepend(a); } this.css += ` img.torrent_direct_link { width: 16px; height: 16px; background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAH5QTFRFR3BMyKN4cz0Td3d3sLCw6enp////qHg4oaGhcz0TlpaWkV8opnU2lmQrjVklqHg4m2kvo3I03ruJiFMhn24y4r+N2raG5cSP9+jQg04e1rKD68uUnYpv6ceS0q5/fkkaoaGhzqp8h4eHr6+v+Pj4ekQXdkAV+/v78fHxy6Z6f0p3WgAAAAp0Uk5TAP///////5aWlrne7esAAACHSURBVBjTbc5HEsIwEERRA5qxLeecc77/BTEN0oq/m1ddKhnG36xxtPRhBq2UxyFlG5gAt5zXk+hc59IFRM3yQksTAdKOf3UpICxYCEFEXIQAL2NCnHkAJ/4s7jh2AH6uFrkPSOrvgrhOAFWvFn0FGCYWhDemAbBd6h/XBtgfuh1gP3X2fb4BlrkIUt3i2kgAAAAASUVORK5CYII='); padding-bottom: 1px; }`; location.assign("javascript:registerClipboardJS('#direct_link');void(0)"); } if (this.getHostValue("colorBlind")) { if (location.href.indexOf("/classes.php") >= 0) { const spans = document.querySelectorAll("table.main > tbody > tr > td:nth-of-type(2) > ul > li > span[style=\"color: green\"]"); for (const span of spans) { span.setAttribute("style", "color: blue"); } } } } getPasskey() { const value = this.getHostValue("passkey"); let passkey = ""; if (value) { passkey = String(value); } if (passkey != "") { this.passkey = passkey; return; } const link = document.querySelector("[title=\"Latest Torrents\"]"); const re = /passkey=([\d\w]+)/; const result = re.exec(link.href); this.passkey = result && result.length > 1 ? result[1] : ""; if (this.passkey != "") { this.setHostValue("passkey", this.passkey); } } } exports.TJUPT = TJUPT; /***/ }) /******/ ]); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. (() => { var exports = __webpack_exports__; Object.defineProperty(exports, "__esModule", ({ value: true })); const hhanclub_top_1 = __webpack_require__(1); const nanyangpt_com_1 = __webpack_require__(4); const pt_sjtu_edu_cn_1 = __webpack_require__(5); const index_1 = __webpack_require__(6); const host = window.location.host; const sites = new Map([ ["hhanclub.top", new hhanclub_top_1.Hhanclub()], ["nanyangpt.com", new nanyangpt_com_1.NanyangPT()], ["pt.sjtu.edu.cn", new pt_sjtu_edu_cn_1.SJTU()], ["tjupt.org", new index_1.TJUPT()] ]); const site = sites.get(host); if (site) { site.init(); site.onLoad(); site.addStyle(); } })(); /******/ })() ;