// ==UserScript== // @name 福利吧小助手 // @namespace https://greasyfork.org/zh-CN/users/860681-aoguai // @version 1.0.5.6 // @description 一个用于增强福利吧功能的油猴脚本,提供链接提取、界面美化、编解码等功能。 // @author aoguai // @match http://fulibus.net/* // @match https://fulibus.net/* // @match http://fuliba2025.net/* // @match https://fuliba2025.net/* // @match http://*.fuliba2025.net/* // @match https://*.fuliba2025.net/* // @match http://fuliba20[0-9][0-9].net/* // @match https://fuliba20[0-9][0-9].net/* // @match http://fuliba[0-9][0-9].net/* // @match https://fuliba[0-9][0-9].net/* // @match http://f.uliba.net/* // @match https://f.uliba.net/* // @match http://*.wnflb20[0-9][0-9].com/* // @match https://*.wnflb20[0-9][0-9].com/* // @match http://*.wnflb[0-9][0-9].com/* // @match https://*.wnflb[0-9][0-9].com/* // @match http://www.wnflb*.com/* // @match https://www.wnflb*.com/* // @match http://wnflb2023.com/* // @match https://wnflb2023.com/* // @match http://*.wnflb2023.com/* // @match https://*.wnflb2023.com/* // @icon data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAC1puYAeG/TAHxz1CF5cNJJe3HQBnt1ygJ7c8wIgXy9AXt0zAd4c8oGdm7bAHp9qAAAAAAAAAAAAAAAAAAAAAAAioDaACckvAB4cNSQeW/V1X5z1CZ7cNV9em/Vsnpv1ZR4b9OyeW/UpH1x1SN8cNYArL9QAJOI2wB3a9YAf3LYL31v2YF+cdc5enDWtHlv1cR9cdeHe2/X6H1w2c57b9jzenDVmnlv1vN7b9aWhXfNA39z0gCEe9kAh3/YBHxw2Kp8btn/fG/Y1ntv1915cNXAe2/Y2Hpv1+98b9nwfG/Z+X1x14F4b9XRem/X73lw0kB4b9QAhnvcAI+N1wF9cNqLe27Z/npv1/95b9b/eHDT2Hhv1fJ6cNWxfG/Z1npv1/97cNe5eHHUl3hu1f93b9Ope3TMB7C74wB/ctsAgXTcD3pw15h5b9b8eG7V/3Zu0/x3btT+enDWx3pv1+t5b9b2enHTUHlx00l3b9T4d2/U1Hpy0BkAAAAAfXLWAIF02B98cNejeW/W+npv1/95b9b/eW/W8nlw1KZ8cdeTeW/W83lv1eB6b9fjeW/W/3lw1at+dtAKrafkAHdr1wB8cdd5e2/Y/3pv1v96b9f/em/X/Hlv1el5b9Tae3DX2Xpw1ux6b9f/enDX5Hxx2KB8c9YneG/WAKyn5AB6btcAe3HWTHlv1ud6cNbeeG/U+3pv1vR7cdaleG/U8npw1cx5b9bzeW/V9Xty1FaXiucCgXXaAMK95wAAAAAAfnbWAIR+1gJ7cdZZe3HVq3lw1KB7cNe0f3PZXXpv1uV5cNTKem/W5Xlv1OF5cdE3dnDTAIN1zAAAAAAAAAAAAAAAAACDd9wAmo3xAXty1kl6cNScenDVe31x1298cNjVfXDYwntv1/R5btX/eG/Uw3px0RR6cdEAAAAAAAAAAAAAAAAAAAAAAIF02gCBdNskem/W33lu1v97cNfRe2/X53xu2f58b9j6e2/Y/3lv1ct8c9QVfXPUAAAAAAAAAAAAAAAAAAAAAAB+cdkAf3LZRHlv1fd3btT/eG/V+npv1/58btr/e2/Y/3pv1/94b9Ovf3nMBH52zQAAAAAAAAAAAAAAAAAAAAAAfHLWAH1z1zF4b9Tjd2/U4Hpx1Yp7cdeMe3DYpHlv1vF5b9X+eHDSgW5Z6QB8eMoAAAAAAAAAAAAAAAAAAAAAAH521gB/d9YEeXLUMnly1Cx+e9IEf3vUAn921wd9ctg/e3HWVnp00xJ5ctMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AcAAPgDAADAAQAAgAEAAIAAAADAAAAAwAAAAMABAADAAwAAwAcAAOADAADwAwAA8AMAAPAHAADwBwAA//8AAA== // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/arrive/2.4.1/arrive.min.js // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js // @require https://greasyfork.org/scripts/403716-gm-config-cn/code/GM_config_CN.js // @connect keyfc.net // @connect fuliba123.com // @grant GM_info // @grant GM_setClipboard // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM_addStyle // @run-at document-start // @noframes // @license GNU General Public License v3.0 or later // @namespace https://greasyfork.org/scripts/522004 // @supportURL https://greasyfork.org/scripts/522004 // @homepageURL https://greasyfork.org/scripts/522004 // ==/UserScript== (function () { 'use strict'; if (typeof GM_config == 'undefined') { console.error( '福利吧小助手:\nGM_config 库文件加载失败,脚本无法正常工作,请尝试刷新网页以重新加载。', ); } else if (typeof document.arrive == 'undefined') { console.error( '福利吧小助手:\narrive 库文件加载失败,脚本可能无法正常工作,请尝试刷新网页以重新加载。', ); } else if (typeof CryptoJS == 'undefined') { console.warn( '福利吧小助手:\nCryptoJS 库文件加载失败,BASE64 编解码功能无法使用。', ); } else { console.debug('福利吧小助手:\n脚本工作环境正常。'); } const icon = { settings: 'url(https://www.svgrepo.com/download/529867/settings.svg)', codeGray: 'url(https://www.svgrepo.com/download/362599/code-bold.svg)', codeBlue: 'url(https://www.svgrepo.com/download/528184/code.svg)', modeNight: 'url(https://www.svgrepo.com/download/528178/cloudy-moon.svg)', modeDay: 'url(https://www.svgrepo.com/download/407540/sun.svg)', signatureAdd: 'url(https://www.svgrepo.com/download/528833/add-square.svg)', signatureMinus: 'url(https://www.svgrepo.com/download/529096/minus-square.svg)', collapsedYes: 'static/image/common/collapsed_yes.gif', collapsedNo: 'static/image/common/collapsed_no.gif', }; const style = { // 设置面板样式 settings: ` #myGoodBoyConfig { --primary: #4A90E2; --text: #333; --bg: #FFF; --border: #E0E0E0; font-family: system-ui, sans-serif; line-height: 1.6; } #myGoodBoyConfig .section_header { padding: 1rem; background: var(--primary); border-radius: 0.4rem; } #myGoodBoyConfig input[type="checkbox"] { width: 1.2em; height: 1.2em; } #myGoodBoyConfig button { padding: 0.6em 1.2em; border-radius: 0.4em; transition: opacity 0.2s; } #myGoodBoyConfig button:hover { opacity: 0.9; } `, // 代码面板样式 codePanel: ` #myCodePanel { width: 35vw !important; max-width: 25rem; min-height: 20rem; border-radius: 0.8rem; box-shadow: 0 0.2rem 1rem rgba(0,0,0,0.1); } .nav-tabs { display: flex; width: 100%; justify-content: space-evenly; margin: 10px 0; } .section_header { flex: 1; min-width: 120px; text-align: center; padding: 2px 12px; box-sizing: border-box; border-bottom: 2px solid transparent; } .section_header.active { font-weight: bold; } #myCodePanel textarea { width: 100%; min-height: 10em; padding: 0.8em; border-radius: 0.4em; } #myCodePanel button { padding: 0.6em 1.2em; border-radius: 0.4em; } .config_var { width: 95%; box-sizing: border-box; margin: 10px auto; padding: 5px; } .config_var textarea { width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; resize: vertical; } #myCodePanel .section_desc { width: 100% } `, // 夜间模式样式 night: ` html, body, .bm, .bdl, .bdl dt, .bdl dd.bdl_a a, .tb .a a, .pn, .fl .bm_h, .ct2_a, .ct3_a, .t_table, table.plhin { background-color: #282A36 !important; background-blend-mode: multiply; } .bdl dl.a, .tl .th, .tl .ts th, .tl .ts td, .pg a, .pg strong, .pgb a, .pg label, .bml .bm_h, #scrolltop a, .bmn, .bm_h, td.pls, .ad td.plc, div.exfm, .tb a, .tb_h, .ttp li.a a, div.uo a, input#addsubmit_btn, #gh .bm .bm_h, .jump_bdl li, .newthread tr th, .newthread tr td, .tl .threadpre td, .tl .threadpre:hover td, .nfl .f_c, #myCodePanel { background-color: #282A36 !important; } #nv, .card_gender_0, .card .o a, .tbn li.a, #p_btn a, #p_btn i { background: #40444D !important; } #toptb, .tedt .bar, .edt .bar, .edt .bbar, #post_extra_tb label.a, #extcreditmenu.a, #g_upmine.a, .tl #forumnewshow, .jump_bdl .a a, .jump_bdl .a a:hover, .psth, .pl .quote, .pm_tac, .pm .c, .pml .hover, #uhd, #flw_header .bar, .ttp a, .ttp strong, .bmw .bm_h, .GzList ul li a, .m_c, .m_c .o, .dt th, .section_header_holder p, #fx_checkin_menu,#fx_checkin_menub, .pl .blockcode ol li:hover { background-color: #40444D !important; } #nv li a:hover, #nv li.hover a, #nv li.hover a:hover, #nv > a { background: #1A1A1A !important; background-blend-mode: multiply; } .p_pop, .p_pof, .sllt, .tl #forumnewshow a:hover, #autopbn:hover, .pgbtn a:hover, #hiddenpoststip { background-color: #1A1A1A !important; } #threadlist > div > table > tbody > tr:hover > *, #threadlist > div > form > table > tbody > tr:not(.threadpre):hover > *, div.tl > form > table > tbody > tr:hover > * { background-color: #1A1A1A !important; } .p_pop a:hover { background: #3D3D3D !important; } a, #um, #um a, body, input, button, select, textarea, .xi2, .xi2 a, .pg a, .pg strong, .pgb a, .pg label, .jump_bdl a, div#forum_rules_37 font { color: #C0C0C0 !important; } .tps a, .chart em { color: #666 !important; } .GzList ul li a { color: #000 !important !important; } .tedt .pt, .px, .pt, .ps, select, input, #myCodePanel textarea, #myCodePanel button { background-color: #38383D !important; } .pl .blockcode { background: #38383D !important; } ul.cl.nav li a, ul.cl.nav li a:hover { background-repeat: no-repeat !important; background-position: 50% 5px !important; } #fastpostsmilie_88_td, #fx_checkin_topb, #hd h2 a,#newspecial, #newspecialtmp, #post_reply, #post_replytmp, .ico_fall, .ico_increase, .o img, fieldset legend, div.ac1 { mix-blend-mode: multiply; } .p_pop a, .tl #forumnewshow a { filter: brightness(.7); } #category_36 tbody td p>a, .bm_c tbody h2>a, .common font, .fl .bm_h h2 a, .tl th a, .xw0.xi1, .y.xg1 font { mix-blend-mode: color-dodge; } .ignore_notice { right: -3px !important; top: -53px !important; } .tps a:hover { background-color: #C0C0C0 !important; } .signature_switch_div { background-color: #00A1D6 !important; filter: brightness(1.5) contrast(1.2); box-shadow: 0 0 3px rgba(0,255,0,0.5); } `, blocked: ` .my-blocked { position: relative; opacity: 0.6; transition: opacity 0.3s; } .my-blocked::after { content: "🚫"; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 1.2em; } .my-blocked-alert { background: #fff0f0; border-left: 3px solid #ff4d4d; padding: 8px; margin: 5px 0; } `, // 基础样式 basic: ` :root { --link-color: #369; --hover-color: #2B65B7; } .container.my-custom-width { max-width: none !important; width: var(--custom-content-width) !important; margin-left: auto !important; margin-right: auto !important; } .my_good_boy_div_float { position: absolute; width: 20rem; padding: 1rem; margin: 0 1rem 1rem 0; border: 0.1rem solid var(--border); border-radius: 0.8rem; box-shadow: 0 0.2rem 0.8rem rgba(0,0,0,0.1); } .my_good_boy_div_basic { margin-top: 16px; } .my_good_boy_p { color: #FF4D4F; font-size: 15px; margin: 8px 0; } .my_good_boy_p_float { margin-left: 0 !important; } .my_good_boy_li { list-style-type: disc; white-space: nowrap; margin: 8px 0; } .my_good_boy_a { color: var(--link-color); text-decoration: none; } .my_good_boy_a:hover { color: var(--hover-color); text-decoration: underline; } .my_good_boy_a_copy { display: inline-block; font-size: 15px; margin-left: 20px; text-decoration: underline; cursor: pointer; } .a_copy_completed { color: #FF6600 !important; animation: fadeIn 0.3s; } .link_faild { color: #999 !important; text-decoration: line-through !important; } .show_more_link { text-decoration: underline; color: var(--primary-color); cursor: pointer; } #settingsIcon { display: block; float: right; cursor: pointer; margin: 4px 5px; width: 17px; height: 17px; background-image: ${icon.settings}; background-repeat: no-repeat; background-size: 16px auto; background-position: center; transition: transform 0.2s; } #settingsIcon:hover { opacity: 1; } .expand_box, .expand_box_h { bottom: 0; height: 60px; position: fixed; right: -6vw; transition: all 0.3s ease; width: 12vw; z-index: 999; } .expand_box_h { height: 120px; } .show_expand_box { right: 0; width: 6vw; } #myCodeSpan, #myNightSpan, #myNightSpan_2 { background-repeat: no-repeat; background-size: 32px auto; background-position: center; border-radius: 50%; color: #FFF; cursor: pointer; display: block; height: 38px; width: 38px; position: absolute; right: 1vw; transition: all 0.3s ease; } #myCodeSpan { background-image: ${icon.codeGray}; background-color: #787878; bottom: 10px; } #myCodeSpan:hover { box-shadow: 0 0 10px rgba(0,255,0,0.5); background-image: ${icon.codeBlue}; transform: scale(1.1); } #myNightSpan, #myNightSpan_2 { background-color: #00A1D6; } #myNightSpan { bottom: 10px; } #myNightSpan_2 { bottom: 60px; } #myNightSpan:hover, #myNightSpan_2:hover { box-shadow: 0 0 10px rgba(0,255,0,0.5); transform: scale(1.1); } .signature_switch_div { position: absolute; top: 5px; z-index: 100; width: 16px; height: 16px; display: block; position: relative; top: -5px; cursor: pointer; background-image: ${icon.signatureMinus}; background-size: 16px auto; background-repeat: no-repeat; background-position: center; transition: all 0.2s; } .signature_switch_div:hover { transform: scale(1.1); } .signature_switch_close { background-image: ${icon.signatureAdd}; } .signature_hide { display: none; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } `, }; // 定义网站地址常量 const SITE_URLS = { HOME: '', FORUM: '', }; const reg = { url: /^[\s\S]*?(magnet:\?xt=urn:btih:(?:[a-z0-9]{40}|[a-z0-9]{32})|ftp:\/\/\S*|ed2k:\/\/\S*|thunder:\/\/\S*|flashget:\/\/\S*|qqdl:\/\/\S*|xfplay:\/\/\S*|https?:\/\/[\w零一二三四五六七八九壹贰叁肆伍陆柒捌玖-]*\.?[\w零一二三四五六七八九壹贰叁肆伍陆柒捌玖-]+\.+\w+\S*)/i, download: /^[\s\S]*?(magnet:\?xt=urn:btih:(?:[a-z0-9]{40}|[a-z0-9]{32})|ed2k:\/\/\S*|thunder:\/\/\S*)/i, www: /^[\s\S]*?(www\.[\w-]+\.[a-z]+[\w#=%+?/-]*)/i, baidupan: /^https?:\/\/pan\.baidu\.com\/s(?:hare)?\/[\w=?&-]+$/i, baidupanIncludeCode: /^https?:\/\/pan\.baidu\.com\/s(?:hare)?\/[\w=?#&-]+$/i, code: /(?:提取)+[^a-z0-9解压]*([a-z0-9]{4})[^a-z0-9]*/i, baidupanCode: /^(?:(?:下载)?链接\S+\s+)?(?:[^解压]+\s+)?[^a-z0-9解压]*([a-z0-9]{4})[^a-z0-9]*(?:app)?[^a-z0-9]*$/i, singleCharCode: /^[a-z0-9]$/i, missingHeaderBaidupan: /^[\s\S]*?[^/\w-]?((? [v, k]), ); // 预编译正则表达式 const MAGNET_PREFIX_REGEX = /^magnet:\?xt=urn:btih:/; const TRIM_REGEX = /^\s+|\s+$/g; const HTTP_PROTOCOL_REGEX = /^https?\/\//i; const URL_PROTOCOL_REGEX = /^(https?:\/\/|ed2k:\/\/)/; const CONFIG = { DEFAULT_ACCESS_NUM: 90, MAX_ACCESS_NUM: 90, LEVEL_ACCESS_MAP: { '-1': 0, '0': 5, }, }; const IMAGE_SELECTOR = [ 'img[src*="avatar"]', 'img[id*="aimg"]', 'img.thumb', 'img[decoding="async"]', ].join(','); // 获取最新地址并设置常量 function initSiteUrls() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: 'https://fuliba123.com/', timeout: 10000, onload: function (response) { if (response.status === 200) { try { const parser = new DOMParser(); const doc = parser.parseFromString( response.responseText, 'text/html', ); // 获取所有链接元素 const linkElements = doc.querySelectorAll('a.card.no-c'); // 遍历链接元素设置常量 linkElements.forEach((element) => { const titleElement = element.querySelector('strong'); if (titleElement) { const title = titleElement.textContent.trim(); const url = element.href || element.getAttribute('data-url'); if (title === '福利吧') { SITE_URLS.HOME = url; // 添加首页地址到 @match addMatchPattern(url); } else if (title === '福利吧论坛') { SITE_URLS.FORUM = url; // 添加论坛地址到 @match addMatchPattern(url); } } }); if (SITE_URLS.HOME && SITE_URLS.FORUM) { console.debug( '福利吧小助手:\n网站地址初始化成功\n首页地址:' + SITE_URLS.HOME + '\n论坛地址:', SITE_URLS.FORUM, ); resolve(SITE_URLS); } else { reject(new Error('未找到所需的链接')); } } catch (error) { reject(error); } } else { reject(new Error(`请求失败,状态码:${response.status}`)); } }, onerror: function (error) { reject(new Error('网络请求错误:' + error)); }, ontimeout: function () { reject(new Error('请求超时')); }, }); }); } // 添加新的 match pattern function addMatchPattern(url) { try { const urlObj = new URL(url); const hostname = urlObj.hostname; // 构建符合要求的 match pattern const schemes = ['http', 'https']; // 只处理 HTTP/HTTPS 协议 const patterns = []; schemes.forEach((scheme) => { // 主域名匹配 patterns.push(`${scheme}://${hostname}/*`); // 子域名匹配(包含 www) patterns.push(`${scheme}://*.${hostname}/*`); }); // 获取当前脚本的元数据 const metadata = GM_info.script; // 添加新的 @match (如果不存在) patterns.forEach((pattern) => { if (!metadata.match || !metadata.match.includes(pattern)) { // 如果 match 数组不存在则创建它 if (!metadata.match) { metadata.match = []; } metadata.match.push(pattern); console.debug('福利吧小助手:\n添加新的 match pattern:', pattern); } }); } catch (error) { console.warn('福利吧小助手:\n添加 match pattern 失败:', error); } } const status = { nightEnable: GM_config.getValue('nightEnable', false), collapsedGwwzEnable: GM_config.getValue('collapsedGwwzEnable', false), accessNum: GM_config.getValue('accessNumber', 90), nightStyleDom: null, checkDate: GM_config.getValue('checkDate', '2000-1-1'), oldUrl: location.href, timer1: null, timer2: null, timer3: null, timer4: null, }; class Good_Boy { constructor(config, linkArray = []) { this.config = config; this.floatEnable = this.isMobilePage() ? false : config.get('displayPosition') == '右侧'; this.linkArray = linkArray; this.totalCount = linkArray.length; this.currentCount = 0; this.showMoreStatus = false; this.insertUlStatus = false; this.ulNode = document.createElement('ul'); } insertLinkItem(linkArray = []) { this.linkArray = this.linkArray.concat(linkArray); this.totalCount = this.linkArray.length; const maxLinkNumber = this.config.get('maxLinkNumber'); for ( let i = this.currentCount; i < this.totalCount && i < maxLinkNumber; i++ ) { const tempA = this.createLink(this.linkArray[i]); tempA && this.ulNode.appendChild(tempA); } this.currentCount = Math.min(this.totalCount, maxLinkNumber); if (maxLinkNumber > -1 && this.totalCount > maxLinkNumber) { const message = `[共提取到 ${this.totalCount} 个链接,仅显示前 ${maxLinkNumber} 个]:`; const pElement = this.ulNode.getElementsByTagName('p')[0]; pElement && (pElement.textContent = message); if (!this.showMoreStatus) { this.ulNode.appendChild(this.showMoreLink()); this.showMoreStatus = true; } } else { const message = `[共提取到 ${this.totalCount} 个链接]:`; const pElement = this.ulNode.getElementsByTagName('p')[0]; pElement && (pElement.textContent = message); } } isMobilePage() { return /android|webos|iphone|ipod|blackberry/i.test(navigator.userAgent); } getCutLinkText(linkText) { if (this.isMobilePage()) { return linkText.length >= 35 ? linkText.slice(0, 15) + ' ... ' + linkText.slice(-10) : linkText; } else if (this.floatEnable) { return linkText.length >= 50 ? linkText.slice(0, 25) + ' ... ' + linkText.slice(-15) : linkText; } return linkText.length >= 80 ? linkText.slice(0, 45) + ' ... ' + linkText.slice(-25) : linkText; } managePrefixCode(inputLink) { const { prefixCode, prefixHash, prefixReplace, prefixUrl, redircdn } = reg; if (prefixCode.test(inputLink)) { return inputLink.replace(prefixReplace, ''); } else if (prefixHash.test(inputLink)) { return inputLink.replace(prefixReplace, 'magnet:?xt=urn:btih:'); } else if (prefixUrl.test(inputLink)) { return inputLink.replace(prefixReplace, 'http://'); } else if (redircdn.test(inputLink)) { return inputLink.replace(redircdn, '$1').replace('______', '.'); } return inputLink; } createLink(linkHref) { const { baidupanIncludeCode } = reg; if (linkHref.length > 2000) { return null; } const linkLi = document.createElement('li'); linkLi.className = 'my_good_boy_li'; const linkA = document.createElement('a'); linkA.title = '点击访问'; linkHref = this.managePrefixCode(linkHref); linkA.className = 'my_good_boy_a'; linkA.href = encodeURI(linkHref); linkA.target = '_blank'; linkA.textContent = this.getCutLinkText(linkHref); linkA.style.color = this.config.get('linkColor'); linkLi.appendChild(linkA); if (this.config.get('copyEnable')) { const copyA = document.createElement('a'); copyA.className = 'my_good_boy_a_copy'; copyA.title = '复制链接'; copyA.href = 'javascript:;'; copyA.target = '_self'; copyA.textContent = '复制'; copyA.style.color = this.config.get('linkColor'); copyA.addEventListener( 'click', function () { GM_setClipboard(linkHref, 'text'); this.classList.add('a_copy_completed'); this.title = '复制成功'; status.timer1 && clearTimeout(status.timer1); status.timer1 = setTimeout(() => { this.classList.remove('a_copy_completed'); this.title = '复制链接'; }, 2000); }, { passive: true }, ); linkLi.appendChild(copyA); if ( baidupanIncludeCode.test(linkHref) && /(?:#|\?pwd=)[a-z0-9]{4}$/i.test(linkHref) ) { const codeValue = /#([a-z0-9]{4})$/i.exec(linkHref)[1]; const codeCopyA = document.createElement('a'); codeCopyA.className = 'my_good_boy_a_copy'; codeCopyA.title = '复制提取码'; codeCopyA.href = 'javascript:;'; codeCopyA.target = '_self'; codeCopyA.textContent = '复制提取码'; codeCopyA.style.color = this.config.get('linkColor'); codeCopyA.addEventListener( 'click', function () { GM_setClipboard(codeValue, 'text'); this.classList.add('a_copy_completed'); this.title = '复制成功'; status.timer2 && clearTimeout(status.timer2); status.timer2 = setTimeout(() => { this.classList.remove('a_copy_completed'); this.title = '复制提取码'; }, 2000); }, { passive: true }, ); linkLi.appendChild(codeCopyA); } } return linkLi; } showMoreLink() { const linkLi = document.createElement('li'); linkLi.className = 'my_good_boy_li'; const linkA = document.createElement('a'); linkA.className = 'my_good_boy_a show_more_link'; linkA.href = 'javascript:;'; linkA.title = '显示全部提取链接'; linkA.textContent = '显示全部'; linkA.target = '_self'; linkA.style.color = this.config.get('linkColor'); linkA.addEventListener( 'click', () => { this.ulNode.removeChild(linkLi); for (; this.currentCount < this.totalCount; this.currentCount++) { const tempA = this.createLink(this.linkArray[this.currentCount]); tempA && this.ulNode.appendChild(tempA); } this.ulNode.getElementsByTagName('p')[0].textContent = '[共提取到 ' + this.totalCount + ' 个链接]:'; this.showMoreStatus = false; }, false, ); linkLi.appendChild(linkA); return linkLi; } createGoodBoyFrame(container, linkArray = []) { if (this.insertUlStatus) { this.insertLinkItem(linkArray); } else { const goodBoyDiv = document.createElement('div'); goodBoyDiv.className = this.floatEnable ? 'my_good_boy_div_float' : 'my_good_boy_div_basic'; const goodBoyP = document.createElement('p'); goodBoyP.className = this.floatEnable ? 'my_good_boy_p my_good_boy_p_float' : 'my_good_boy_p'; this.ulNode.appendChild(goodBoyP); this.insertLinkItem(linkArray); goodBoyDiv.appendChild(this.ulNode); container.appendChild(goodBoyDiv); this.insertUlStatus = true; } } createGoodBoyElement(container, linkObj, textObj, nodeTextArray) { const backLinkArray = this.autoEvent( container, linkObj.linkObjArray, linkObj.baiduObjArray, linkObj.linksArray, textObj.hideTextArray, textObj.showTextArray, nodeTextArray, ); backLinkArray.length > 0 && this.createGoodBoyFrame(container, backLinkArray); } managePrefix(inputLinkArray) { const { prefixLinkMagnet, prefixLinkMagnetReplace } = reg; const returnLinkArray = []; inputLinkArray.forEach((item) => { returnLinkArray.push( prefixLinkMagnet.test(item) ? item.replace(prefixLinkMagnetReplace, '') : item, ); }); return returnLinkArray; } pickUpMagnet(item) { const { url, download, hash, md5 } = reg; let tempLink = ''; const item1 = item.replace(/[^a-z0-9:=?/\\.&%|-]/gi, ''); if (download.test(item1)) { tempLink = download.exec(item1)[1]; } else { const item2 = item.replace(/[^a-z0-9:=?/\\.,,。|\s+-]/gi, ''); if (!md5.test(item2) && hash.test(item2) && !url.test(item2)) { const tempHash = hash.exec(item2)[1]; if (!/^(?:[a-z]+|[0-9]+)$/i.test(tempHash)) { tempLink = 'magnet:?xt=urn:btih:' + tempHash; } } } return tempLink; } manageText(textArray) { const { url, download, www, missingHeaderBaidupan, missingHeaderBaidupanTest, pan, hash, md5, } = reg; const tempArray = []; textArray.forEach((item) => { let tempLink = ''; if (pan.test(item)) { tempLink = 'https://' + pan.exec(item)[1]; } else if (/magnet:|ed2k:|thunder:/i.test(item)) { if (download.test(item)) { tempLink = download.exec(item)[1]; } } else if (url.test(item)) { tempLink = url.exec(item)[1]; } else if (www.test(item)) { tempLink = 'http://' + www.exec(item)[1]; } else if (missingHeaderBaidupan.test(item)) { const linkMissValue = missingHeaderBaidupan.exec(item)[1]; tempLink = /^\//i.test(linkMissValue) ? 'https://pan.baidu.com' + linkMissValue : 'https://pan.baidu.com/' + linkMissValue; } else if (missingHeaderBaidupanTest.test(item)) { tempLink = 'https://pan.baidu.com/s/' + missingHeaderBaidupanTest.exec(item)[1]; } else if (!md5.test(item) && hash.test(item)) { tempLink = 'magnet:?xt=urn:btih:' + hash.exec(item)[1]; } else if (item.length > 32) { tempLink = this.pickUpMagnet(item); } filterLink(tempLink) && tempArray.push(tempLink); }); return this.managePrefix(tempArray); } decodeLink(inputLinkArray) { const { url } = reg; inputLinkArray.forEach((item, index) => { if (url.test(item)) { inputLinkArray[index] = decodeURI(item); } }); return inputLinkArray; } pickUpBaidupanCode(textArray) { const { code, baidupanCode, singleCharCode } = reg; const codeArray = []; const charArray = []; textArray.forEach((item) => { let tempValue = ''; if (code.test(item)) { tempValue = code.exec(item)[1]; } else if (baidupanCode.test(item)) { tempValue = baidupanCode.exec(item)[1]; } else if (singleCharCode.test(item)) { charArray.push(item); } if ( tempValue && !/\d{4}/i.test(tempValue) && codeArray.indexOf(tempValue) === -1 ) { codeArray.push(tempValue); } }); while (charArray.length > 0 && charArray.length % 4 === 0) { const tempCode = charArray.splice(0, 4); const charValue = tempCode.join(''); if (!/\d{4}/i.test(charValue) && codeArray.indexOf(charValue) === -1) { codeArray.push(charValue); } } return codeArray; } manageRepeatArray(inputLinkArray) { const outputLinkArray = []; for (let i = 0, l = inputLinkArray.length; i < l; i++) { for (let j = i + 1; j < l; j++) { if ( getPureLink(inputLinkArray[i]) === getPureLink(inputLinkArray[j]) ) { ++i; j = i; } } outputLinkArray.push(inputLinkArray[i]); } return outputLinkArray; } splitWrap(textArray) { let tempArray = []; textArray.forEach((text) => { if (text) { tempArray = tempArray.concat(text.split('\n').filter((v) => v)); } }); return tempArray; } autoEvent( container, linkObjArray, baiduObjArray, _linksArray, hideTextArray, showTextArray, nodeTextArray, ) { const { url, baidupan, hash, md5 } = reg; const linkArray = []; const linkTextArray = []; linkObjArray.forEach((item) => { linkArray.push(item.eleValue); linkTextArray.push(item.eleContainer); }); if (hideTextArray.length > 0) { hideTextArray = this.splitWrap(hideTextArray); } if (showTextArray.length > 0) { showTextArray = this.splitWrap(showTextArray); } if (nodeTextArray.length > 0) { nodeTextArray = this.splitWrap(nodeTextArray); } const concatTextArray = this.manageRepeatArray( this.decodeLink(hideTextArray), ); let concatLinkArray = this.manageRepeatArray( this.decodeLink(this.manageText(linkArray.concat(concatTextArray))), ); const generalTextArray = showTextArray.concat( nodeTextArray.concat(linkTextArray), ); const indexA = []; const codeB = this.pickUpBaidupanCode(concatTextArray); let codeC = []; if (codeB.length > 0) { for (let j = 0; j < concatLinkArray.length; j++) { if (baidupan.test(concatLinkArray[j])) { indexA.push(j); } } if (codeB.length === indexA.length) { for (let k = 0; k < indexA.length; k++) { if (!/\?pwd=/.test(concatLinkArray[indexA[k]])) { concatLinkArray[indexA[k]] += '?pwd=' + codeB[k]; } } } else if (codeB.length === baiduObjArray.length) { for (let l = 0; l < baiduObjArray.length; l++) { if (!/\?pwd=/.test(baiduObjArray[l].ele.href)) { baiduObjArray[l].ele.textContent = baiduObjArray[l].eleContainer + '?pwd=' + codeB[l]; baiduObjArray[l].ele.href = baiduObjArray[l].eleValue + '?pwd=' + codeB[l]; } } } } else { codeC = this.pickUpBaidupanCode(generalTextArray); if (codeC.length > 0) { for (let j = 0; j < concatLinkArray.length; j++) { if (baidupan.test(concatLinkArray[j])) { indexA.push(j); } } if (codeC.length === indexA.length) { for (let k = 0; k < indexA.length; k++) { concatLinkArray[indexA[k]] += '?pwd=' + codeC[k]; } } else if (codeC.length === baiduObjArray.length) { for (let l = 0; l < baiduObjArray.length; l++) { baiduObjArray[l].ele.textContent = baiduObjArray[l].eleContainer + '?pwd=' + codeC[l]; baiduObjArray[l].ele.href = baiduObjArray[l].eleValue + '?pwd=' + codeC[l]; } } } } let hashArray = []; generalTextArray.forEach((item) => { if (!url.test(item)) { if (!md5.test(item) && hash.test(item)) { hashArray.push('magnet:?xt=urn:btih:' + hash.exec(item)[1]); } else if (item.length > 32) { let temp_link = this.pickUpMagnet(item); temp_link && hashArray.push(temp_link); } } }); hashArray = this.decodeLink(hashArray); concatLinkArray = this.manageRepeatArray( concatLinkArray.concat(hashArray), ); const hideCodeTextArray = this.decodeCoreValues( container, hideTextArray.concat(generalTextArray), ); if (hideCodeTextArray.length > 0) { hideCodeTextArray.forEach((item, index) => { if (!url.test(item)) { if (!md5.test(item) && hash.test(item)) { hideCodeTextArray[index] = 'magnet:?xt=urn:btih:' + hash.exec(item)[1]; } else if (item.length > 32) { const tempLink = this.pickUpMagnet(item); if (tempLink) { hideCodeTextArray[index] = tempLink; } } } }); concatLinkArray = this.manageRepeatArray( concatLinkArray.concat(hideCodeTextArray), ); } concatLinkArray = concatLinkArray .map((item) => { if (generalTextArray.includes(item)) { return null; } return item; }) .filter((v) => v); return concatLinkArray; } foyuPromise(encoded) { return new Promise(function (resolve, _reject) { GM_xmlhttpRequest({ method: 'POST', url: 'https://keyfc.net/bbs/tools/tudou.aspx', data: 'orignalMsg=' + encoded.replace(/\s/g, '') + '&action=Decode', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, onload: function (res) { if (res.status == 200 && res.readyState == 4) { resolve( res.responseText.replace(/^<\/Message><\/BUDDHIST>$/g, ''), ); } else { console.warn('福利吧小助手:\n自动解码出错!'); resolve(''); } }, onerror: function () { console.error('福利吧小助手:\n网络链接出错!'); resolve(''); }, }); }); } async foyuDecode(container, encoded) { await this.foyuPromise(encoded).then((data) => { data && this.createGoodBoyFrame(container, [data]); }); } decodeCoreValues(container, textArray) { const { core, baijia, foyu } = reg; const decodeArray = []; textArray.forEach((item) => { if (core.test(item)) { decodeArray.push(coreValuesDecode(core.exec(item)[1])); } else if (baijia.test(item)) { decodeArray.push(baijiaDecode(baijia.exec(item)[1])); } else if (foyu.test(item)) { this.foyuDecode(container, foyu.exec(item)[1]); } }); return decodeArray; } } function randBin() { return Math.random() >= 0.5; } function str2Utf8(str) { const ENCODED_CHARS = /[A-Za-z0-9\-_.!~*'()]/g; return Array.from(str) .map((char) => { if (ENCODED_CHARS.test(char)) { ENCODED_CHARS.lastIndex = 0; return char.codePointAt(0).toString(16); } const code = char.charCodeAt(0); if (code < 128) { return code.toString(16).padStart(2, '0'); } if (code < 2048) { return ( ((code >> 6) | 0xc0).toString(16) + ((code & 0x3f) | 0x80).toString(16) ); } return ( ((code >> 12) | 0xe0).toString(16) + (((code >> 6) & 0x3f) | 0x80).toString(16) + ((code & 0x3f) | 0x80).toString(16) ); }) .join('') .toUpperCase(); } function hex2Duo(hexs) { const duo = new Array(hexs.length * 2); let index = 0; const len = hexs.length; for (let i = 0; i < len; i++) { const n = hexs[i] >= 'a' ? hexs.charCodeAt(i) - 87 : hexs[i] >= 'A' ? hexs.charCodeAt(i) - 55 : hexs.charCodeAt(i) - 48; if (n < 10) { duo[index++] = n; } else { if (randBin()) { duo[index++] = 10; duo[index++] = n - 10; } else { duo[index++] = 11; duo[index++] = n - 6; } } } return duo.slice(0, index); } function utf82Str(utfs) { const len = utfs.length * 2; const arr = new Array(len); for (let i = 0, j = 0; i < utfs.length; i++) { arr[j++] = '%'; arr[j++] = utfs[i]; } return decodeURIComponent(arr.join('')); } function duo2Hex(duo) { const l = duo.length; const result = new Array(l); let pos = 0; for (let i = 0; i < l; i++) { const curr = duo[i]; if (curr < 10) { result[pos++] = curr.toString(16).toUpperCase(); } else if (curr === 10) { result[pos++] = (duo[++i] + 10).toString(16).toUpperCase(); } else { result[pos++] = (duo[++i] + 6).toString(16).toUpperCase(); } } return result.slice(0, pos).join(''); } function duo2Values(duo) { return duo.map((d) => coreValues[2 * d] + coreValues[2 * d + 1]).join(''); } function coreValuesEncode(str) { return duo2Values(hex2Duo(str2Utf8(str))); } function coreValuesDecode(encoded) { const duo = []; for (let c of encoded) { let i = coreValues.indexOf(c); if (i === -1) { continue; } else if (i & 1) { continue; } else { duo.push(i >> 1); } } const hexs = duo2Hex(duo); let str; try { str = utf82Str(hexs); } catch (e) { throw e; } return str; } function isFloat(str) { return str == '右侧'; } function baijiaEncode(str) { const cleaned = str.replace(TRIM_REGEX, ''); const value = cleaned.replace(MAGNET_PREFIX_REGEX, ''); return Array.from(value) .map((char) => reverseMap.get(char) || '') .join(''); } function baijiaDecode(encoded) { const decoded = Array.from(encoded) .map((char) => baijiaValues.get(char) || '') .join(''); if (HTTP_PROTOCOL_REGEX.test(decoded)) { return decoded.replace(/^(https?)/, '$1:'); } if (URL_PROTOCOL_REGEX.test(decoded)) { return decoded; } return `magnet:?xt=urn:btih:${decoded}`; } function convertChineseNumber(textString) { return textString.replace( /[零一二三四五六七八九壹贰叁肆伍陆柒捌玖]/gi, (arg0) => { let index = '零一二三四五六七八九'.indexOf(arg0); if (index === -1) { index = '壹贰叁肆伍陆柒捌玖'.indexOf(arg0) + 1; } return index; }, ); } function filterLink(inputLink) { let status = true; if (inputLink) { for (let i in filterReg) { if (filterReg[i].test(inputLink)) { status = false; break; } } } else { status = false; } return status; } function getPureLink(inputLink, depthBoolean) { const { baidupanIncludeCode } = reg; let outputLink = inputLink; if (depthBoolean) { if (baidupanIncludeCode.test(outputLink)) { if (/#[a-z0-9]{4}$/i.test(outputLink)) { outputLink = outputLink.replace(/#[a-z0-9]{4}$/i, ''); } } else if ( /^https?:\/\/www\.bilibili\.com\/video\/av\d+\?from=search/i.test( outputLink, ) ) { outputLink = outputLink.replace(/\?from=search.*/, ''); } } let returnLink = outputLink .replace( /(^(?:\s+)?(?:\[url\])?(?:\s+)?(?:https?:\/\/)?|(?:\/+)?(?:\s+)?(?:\[\/url\])?$|%C2%A0$)/gi, '', ) .replace(/%C2%A0/gi, '%20'); try { returnLink = decodeURI(returnLink); } catch (e) { console.warn('福利吧小助手:\n出现编码转换错误。', outputLink); console.warn(e); returnLink = outputLink .replace( /(^(?:\s+)?(?:\[url\])?(?:\s+)?(?:https?:\/\/)?|(?:\/+)?(?:\s+)?(?:\[\/url\])?$|%C2%A0$)/gi, '', ) .replace(/%C2%A0/gi, '%20'); } return returnLink; } function contrastTextAndLink(textValue, linkValue) { let sta = true; textValue = getPureLink(textValue, true); linkValue = getPureLink(linkValue, true); if (/\s...\s/i.test(textValue)) { sta = false; } else if (textValue === linkValue) { sta = false; } return sta; } function findLink(container) { const { baidupan } = reg; const linkA = container.getElementsByTagName('a'); const tempObj = { linkObjArray: [], baiduObjArray: [], linksArray: [], }; const tempBaiduArray = []; for (let i = 0; i < linkA.length; i++) { if (linkA[i].closest('div.aimg_tip')) { continue; } const tempLink = linkA[i].href; if (filterLink(tempLink)) { const tempImg = linkA[i].querySelectorAll('img'); if ( tempImg.length === 0 || (tempImg.length > 0 && tempImg[0].src !== tempLink && tempImg[0].getAttribute('file') !== tempLink) ) { const tempText = linkA[i].innerText.replace(/^\s+|\s+$/gi, ''); if (contrastTextAndLink(tempText, tempLink)) { const linkObj = { eleValue: tempLink, eleContainer: tempText, ele: linkA[i], }; tempObj.linkObjArray.push(linkObj); } else if (baidupan.test(tempLink)) { const baiduObj = { eleValue: tempLink, eleContainer: tempText, ele: linkA[i], }; if (tempBaiduArray.indexOf(tempLink) === -1) { tempBaiduArray.push(tempLink); tempObj.baiduObjArray.push(baiduObj); } } } tempObj.linksArray.indexOf(tempLink) === -1 && tempObj.linksArray.push(tempLink); } } return tempObj; } // 定义常量,提高可读性和性能 const GRAY_THRESHOLD = 192; const OPACITY_THRESHOLD = 0.2; const GRAY_COEFFICIENTS = { R: 0.299, G: 0.587, B: 0.114, }; function judgeColor(color) { // 使用一个正则表达式同时匹配 rgb 和 rgba const matches = color.match( /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d*\.?\d+))?\)/i, ); if (!matches) return false; const [, r, g, b, a = 1] = matches.map((val) => +val); // 如果是 rgba 且透明度低于阈值,直接返回 if (a <= OPACITY_THRESHOLD) return true; // 计算灰度值 const grayLevel = r * GRAY_COEFFICIENTS.R + g * GRAY_COEFFICIENTS.G + b * GRAY_COEFFICIENTS.B; return grayLevel > GRAY_THRESHOLD; } // rgb 转 rgba 函数 function rgb2Rgba(colorString) { const matches = colorString.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/i); if (!matches) return colorString; const [, r, g, b] = matches; return `rgba(${r}, ${g}, ${b}, 1)`; } function display_Text(container, newTextColor, newTextBackgroundColor) { const { url } = reg; const tempObj = { hideTextArray: [], showTextArray: [], }; // 处理font标签 const textFont = container.getElementsByTagName('font'); for (let i = 0; i < textFont.length; i++) { if ( textFont[i].closest('.quote') || textFont[i].getElementsByClassName('aimg_tip').length !== 0 ) { continue; } const tempText = textFont[i].innerText.replace(/^\s+|\s+$/gi, ''); if (!/^\s*$/i.test(tempText)) { const textColor = window.getComputedStyle(textFont[i]).color; const textBackgroundColor = window.getComputedStyle( textFont[i], ).backgroundColor; if (judgeColor(textColor) && judgeColor(textBackgroundColor)) { textFont[i].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (rgb2Rgba(textBackgroundColor) === rgb2Rgba(textColor)) { textFont[i].style.backgroundColor = newTextBackgroundColor; textFont[i].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (!judgeColor(textBackgroundColor) && !judgeColor(textColor)) { textFont[i].style.backgroundColor = newTextBackgroundColor; textFont[i].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if ( textFont[i].childNodes.length === 1 && textFont[i].childNodes[0].nodeType === 3 ) { if (GM_config.get('extractEnable')) { if (url.test(tempText)) { textFont[i].innerHTML = textFont[i].innerHTML.replace( url, (_arg0, arg1) => { return ( '' + convertChineseNumber(arg1) + '' ); }, ); } } !tempObj.showTextArray.includes(tempText) && tempObj.showTextArray.push(tempText); } else if (textFont[i].childNodes.length > 1) { const tFont = textFont[i].childNodes; for (let child_index in tFont) { analysisText(tFont[child_index]); } !tempObj.showTextArray.includes(tempText) && tempObj.showTextArray.push(tempText); } } } // 处理table标签 const tempTable = container.getElementsByTagName('table'); for (let j = 0; j < tempTable.length; j++) { if ( !judgeColor(tempTable[j].style.backgroundColor) && !judgeColor(tempTable[j].style.color) ) { tempTable[j].style.backgroundColor = newTextBackgroundColor; } else if ( judgeColor(tempTable[j].style.color) && judgeColor(tempTable[j].style.backgroundColor) ) { tempTable[j].style.color = newTextColor; } } // 处理blockquote标签 const blockquotes = container.getElementsByTagName('blockquote'); for (let k = 0; k < blockquotes.length; k++) { // 检查blockquote本身的颜色 const blockquoteColor = window.getComputedStyle(blockquotes[k]).color; const blockquoteBgColor = window.getComputedStyle( blockquotes[k], ).backgroundColor; if ( judgeColor(blockquoteColor) || rgb2Rgba(blockquoteBgColor) === rgb2Rgba(blockquoteColor) ) { blockquotes[k].style.color = newTextColor; const tempText = blockquotes[k].innerText.replace(/^\s+|\s+$/gi, ''); if (!/^\s*$/i.test(tempText)) { tempObj.hideTextArray.push(tempText); } } } // 处理strong标签 const strongs = container.getElementsByTagName('strong'); for (let m = 0; m < strongs.length; m++) { const strongColor = window.getComputedStyle(strongs[m]).color; const strongBgColor = window.getComputedStyle(strongs[m]).backgroundColor; const tempText = strongs[m].innerText.replace(/^\s+|\s+$/gi, ''); if (!/^\s*$/i.test(tempText)) { if (judgeColor(strongColor) && judgeColor(strongBgColor)) { strongs[m].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (rgb2Rgba(strongBgColor) === rgb2Rgba(strongColor)) { strongs[m].style.backgroundColor = newTextBackgroundColor; strongs[m].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (!judgeColor(strongBgColor) && !judgeColor(strongColor)) { strongs[m].style.backgroundColor = newTextBackgroundColor; strongs[m].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } } } // 处理span标签 (通常用于隐藏文本) const spans = container.getElementsByTagName('span'); for (let n = 0; n < spans.length; n++) { const spanColor = window.getComputedStyle(spans[n]).color; const spanBgColor = window.getComputedStyle(spans[n]).backgroundColor; const tempText = spans[n].innerText.replace(/^\s+|\s+$/gi, ''); if (!/^\s*$/i.test(tempText)) { if (judgeColor(spanColor) && judgeColor(spanBgColor)) { spans[n].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (rgb2Rgba(spanBgColor) === rgb2Rgba(spanColor)) { spans[n].style.backgroundColor = newTextBackgroundColor; spans[n].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (spanColor === 'rgb(255, 255, 255)') { spans[n].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } } } return tempObj; } function text2A(node) { const { url } = reg; const tempSpan = document.createElement('span'); tempSpan.innerHTML = node.nodeValue.replace(url, (arg0, arg1) => { if (arg1.length > 2000) { return arg0; } return arg0.replace( arg1, '' + convertChineseNumber(arg1) + '', ); }); node.parentNode.replaceChild(tempSpan, node); } function analysisText(domPoint) { const { url } = reg; const nodeList = domPoint.childNodes; const nodeTextArray = []; for (let i in nodeList) { if (nodeList[i].nodeType === 3) { const tempText = nodeList[i].nodeValue.replace(/^\s+|\s+$/gi, ''); if (!/^\s*$/i.test(tempText)) { if (GM_config.get('extractEnable')) { if (url.test(tempText)) { text2A(nodeList[i]); } } nodeTextArray.push(tempText); } } else if ( nodeList[i].nodeType === 1 && !nodeList[i].className.includes('quote') && !nodeList[i].className.includes('pstatus') && !nodeList[i].className.includes('aimg_tip') && !nodeList[i].className.includes('blockcode') && nodeList[i].nodeName !== 'FONT' && nodeList[i].nodeName !== 'A' && nodeList[i].nodeName !== 'SCRIPT' && nodeList[i].childNodes.length > 0 ) { const recursiveArray = analysisText(nodeList[i]); for (let j in recursiveArray) { nodeTextArray.push(recursiveArray[j]); } } } return nodeTextArray; } function isMobilePage() { return /android|webos|iphone|ipod|blackberry/i.test(navigator.userAgent); } function getNowDate() { const now = new Date(); return now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate(); } function getUpdateTime(unixTime) { if (!unixTime) return ''; const formatter = new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, }); return ` (更新时间: ${formatter.format(new Date(unixTime)).replace(/\//g, '-')})`; } // 用户权限相关的纯函数 function calculateUserAccess(level) { if (level in CONFIG.LEVEL_ACCESS_MAP) { return CONFIG.LEVEL_ACCESS_MAP[level]; } return level >= 1 && level <= 8 ? level * 10 : CONFIG.DEFAULT_ACCESS_NUM; } // 样式处理相关的纯函数 function getPostStyle(type, color = '') { const styles = { blocked: 'color: #999; font-weight: bold; text-decoration-line: line-through; text-decoration-color: #000000;', restricted: 'color: #999; font-weight: bold;', highlighted: `font-weight: bold; color: ${color};`, }; return styles[type] || ''; } // 数字提取工具函数 function extractNumber(text) { if (!text) return 0; return Number(text.replace('+', '')); } // 获取最大点赞数 function getMaxAgreeCount(fontElements) { return Array.from(fontElements) .map((el) => extractNumber(el.textContent)) .reduce((max, current) => Math.max(max, current), 0); } function highlightPost() { // 确保DOM已经加载 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => initHighlight()); } else { initHighlight(); } } // 初始化高亮功能 function initHighlight() { try { updateUserAccessLevel(); initializePostHighlighting(); } catch (error) { console.warn('福利吧小助手:\n执行出错:', error.message); } } // 更新用户访问权限 function updateUserAccessLevel() { try { const upMineElement = document.getElementById('g_upmine'); if (!upMineElement) return; const levelMatch = /LV\.(-?[0-9])/.exec(upMineElement.textContent); if (!levelMatch) return; const newAccessNum = calculateUserAccess(levelMatch[1]); if (status.accessNum !== newAccessNum) { status.accessNum = newAccessNum; GM_config.setValue('accessNumber', newAccessNum); } } catch (error) { console.warn( '福利吧小助手:\n识别用户阅读权限出错,将使用存储中记录的用户阅读权限。', ); } console.debug('福利吧小助手:\n用户阅读权限:', status.accessNum); } // 初始化帖子高亮 function initializePostHighlighting() { // 缓存选择器字符串,避免重复创建字符串 const THREADLIST_SELECTOR = '#threadlisttableid'; // 立即检查目标元素 const threadlist = document.querySelector(THREADLIST_SELECTOR); if (threadlist) { processThreadList(threadlist); return; } // 创建单个共享的 MutationObserver 配置对象 const observerConfig = { childList: true, subtree: true, }; // 使用函数声明提升性能,避免在回调中重复创建函数 function threadlistCallback(mutations, obs) { const threadlist = document.querySelector(THREADLIST_SELECTOR); if (threadlist) { processThreadList(threadlist); obs.disconnect(); } } function bodyCallback(mutations, obs) { if (document.body) { obs.disconnect(); const observer = new MutationObserver(threadlistCallback); observer.observe(document.body, observerConfig); } } // 主逻辑 if (document.body) { const observer = new MutationObserver(threadlistCallback); observer.observe(document.body, observerConfig); } else { const bodyObserver = new MutationObserver(bodyCallback); bodyObserver.observe(document.documentElement, { childList: true, }); } } // 处理帖子列表 function processThreadList(tbody) { const items = tbody.querySelectorAll('.common, .new, .lock'); items.forEach(processPostItem); } // 处理单个帖子 function processPostItem(item) { const titleElement = item.querySelector('.xst'); if (!titleElement) return; // 如果已经是加粗样式,跳过处理 if (titleElement.style.fontWeight === '700') return; const readNum = extractNumber(item.querySelector('.xw1')?.textContent); // 处理阅读权限 if (readNum > CONFIG.MAX_ACCESS_NUM) { titleElement.setAttribute('style', getPostStyle('blocked')); return; } if (readNum > status.accessNum) { titleElement.setAttribute('style', getPostStyle('restricted')); return; } // 处理点赞数 const agreeNum = getMaxAgreeCount(item.querySelectorAll('font')); if (agreeNum >= GM_config.get('agreeThreshold')) { titleElement.setAttribute( 'style', getPostStyle('highlighted', GM_config.get('agreeColor')), ); return; } // 处理回复数 const replyNum = extractNumber( item.parentNode.querySelector('td.num a')?.textContent, ); if (replyNum >= GM_config.get('replyTHreshold')) { titleElement.setAttribute( 'style', getPostStyle('highlighted', GM_config.get('replyColor')), ); } } function createSettingsIcon() { const iconSpan = document.createElement('span'); iconSpan.id = 'settingsIcon'; iconSpan.title = '打开福利吧小助手设置'; iconSpan.addEventListener('click', () => { GM_config.open(); }); return iconSpan; } function operateCode(inputId, outputId, callback, waitStr = '') { const str = document.getElementById(inputId).value; if (str) { document.getElementById(outputId).value = waitStr; const result = callback(str, outputId); if (result) { document.getElementById(outputId).value = result; } } } function copyCode(copyBtn, resultId) { const copyStr = document.getElementById(resultId).value; if (copyStr) { GM_setClipboard(copyStr, 'text'); copyBtn.title = '复制成功'; copyBtn.classList.add('a_copy_completed'); status.timer3 && clearTimeout(status.timer3); status.timer3 = setTimeout(() => { copyBtn.classList.remove('a_copy_completed'); copyBtn.title = '将输出的结果复制到剪贴板中'; }, 2000); } } function createSwitchSpan(keyValue, domArray) { const switchSpan = document.createElement('span'); switchSpan.className = 'o'; const switchImg = document.createElement('img'); switchImg.id = keyValue + '_user_img'; switchImg.title = '收起/展开'; switchImg.alt = '收起/展开'; if (status[keyValue]) { domArray.forEach(function (item) { item.classList.add('collapsed_hide'); }); switchImg.src = icon.collapsedYes; } else { switchImg.src = icon.collapsedNo; } switchImg.addEventListener('click', () => { if (status[keyValue]) { domArray.forEach((item) => { item.classList.remove('collapsed_hide'); }); switchImg.src = icon.collapsedNo; status[keyValue] = false; } else { domArray.forEach((item) => { item.classList.add('collapsed_hide'); }); switchImg.src = icon.collapsedYes; status[keyValue] = true; } GM_config.setValue(keyValue, status[keyValue]); }); switchSpan.appendChild(switchImg); return switchSpan; } function createSignatureSwitch(signature, switchStatus = true) { const switchDiv = document.createElement('div'); switchDiv.className = 'signature_switch_div'; if (switchStatus) { switchDiv.title = '收起'; } else { switchDiv.title = '展开'; switchDiv.classList.add('signature_switch_close'); signature.classList.add('signature_hide'); } switchDiv.addEventListener( 'click', () => { if (switchStatus) { switchStatus = false; switchDiv.title = '展开'; switchDiv.classList.add('signature_switch_close'); signature.classList.add('signature_hide'); } else { switchStatus = true; switchDiv.title = '收起'; switchDiv.classList.remove('signature_switch_close'); signature.classList.remove('signature_hide'); } }, false, ); return switchDiv; } function applyContentBlocking() { if (!GM_config.get('blockUsersEnable')) return; const blockList = GM_config.get('blockUsersList') .split(',') .map((s) => s.trim()); GM_addStyle(style.basic); // 主题列表处理 blockList.forEach((userId) => { // 通过 uid 匹配 (从 space-uid-XXXXX.html 提取) const xpathByUid = `//tbody[contains(@id, 'normalthread')][.//td[contains(@class,'by')]//a[contains(@href, "space-uid-")]]`; const postsByUid = document.evaluate( xpathByUid, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null, ); // 处理 uid 匹配结果 Array.from({ length: postsByUid.snapshotLength }, (_, i) => postsByUid.snapshotItem(i), ).forEach((post) => { const userLinks = post.querySelectorAll('a[href*="space-uid-"]'); for (const link of userLinks) { const href = link.getAttribute('href'); const uidMatch = href.match(/space-uid-(\d+)\.html/); if (uidMatch && uidMatch[1] === userId) { post.innerHTML = `