// ==UserScript== // @name 绯月校验保存补充 // @version 1.2 // @icon https://gitee.com/miaolapd/KF_Online_Assistant/raw/master/icon.png // @description 保存绯月的帖子和用户校验值到本地,并且在需要时补充 // @include https://*kfpromax.com/* // @include https://*9shenmi.com/* // @include https://*kfmax.com/* // @include https://*bakabbs.com/* // @include https://*365gal.com/* // @include https://*365galgame.com/* // @include https://*fygal.com/* // @namespace https://greasyfork.org/users/1143233 // @run-at document-end // @downloadURL none // ==/UserScript== (async function() { 'use strict'; let db; let isProcessing = false; // 初始化 IndexedDB function initDB() { return new Promise((resolve, reject) => { const request = indexedDB.open('ForumLinkParserDB', 1); request.onerror = reject; request.onsuccess = e => { db = e.target.result; resolve(); }; request.onupgradeneeded = e => { db = e.target.result; db.createObjectStore('linkCache', { keyPath: 'id' }); }; }); } // 从 IndexedDB 获取或存储缓存项 function dbOperation(storeName, mode, action, data) { return new Promise((resolve, reject) => { const transaction = db.transaction([storeName], mode); const store = transaction.objectStore(storeName); const request = action === 'get' ? store.get(data) : store.put(data); request.onerror = reject; request.onsuccess = e => resolve(e.target.result); }); } // 处理 URL,提取 id 和 sf function processUrl(url, needSf = true) { try { const params = url.searchParams; const uid = params.get('uid'); const tid = params.get('tid'); const sf = params.get('sf'); if (needSf && !sf) return null; if (uid && uid !== 'null' && uid !== '') { return { id: 'u' + uid, sf }; } if (tid) { return { id: 't' + tid, sf }; } } catch (error) { console.error('Error processing URL:', error); } return null; } // 检查当前URL并在必要时补充sf参数 async function checkCurrentUrl() { try { const currentUrl = new URL(window.location.href); if (currentUrl.searchParams.has('sf')) return; const result = processUrl(currentUrl, false); if (!result) return; const cacheItem = await dbOperation('linkCache', 'readonly', 'get', result.id); if (cacheItem?.sf) { currentUrl.searchParams.append('sf', cacheItem.sf); console.log('[校验补充] 当前页面补充校验参数,准备跳转'); window.location.href = currentUrl.href; } } catch (error) { console.error('Error checking current URL:', error); } } async function parseLinks() { if (isProcessing) return; isProcessing = true; try { const links = document.getElementsByTagName('a'); const newData = new Map(); let addedCount = 0; // 第一步:收集有 sf 的链接数据 for (const link of links) { const href = link.getAttribute('href'); if (!href) continue; try { const url = new URL(href, window.location.origin); const result = processUrl(url); if (result) { newData.set(result.id, result.sf); } } catch { continue; } } // 第二步:存储新数据 for (const [id, sf] of newData) { const existing = await dbOperation('linkCache', 'readonly', 'get', id); if (!existing) { await dbOperation('linkCache', 'readwrite', 'put', { id, sf }); addedCount++; } } if (addedCount > 0) { console.log(`[校验补充] 新增了 ${addedCount} 条校验数据`); } // 第三步:补充没有 sf 的链接 for (const link of links) { const href = link.getAttribute('href'); if (!href) continue; try { const url = new URL(href, window.location.origin); if (url.searchParams.has('sf')) continue; const result = processUrl(url, false); if (!result) continue; const cacheItem = await dbOperation('linkCache', 'readonly', 'get', result.id); if (cacheItem?.sf) { url.searchParams.append('sf', cacheItem.sf); link.setAttribute('href', url.href); } } catch { continue; } } } finally { isProcessing = false; } } try { await initDB(); // 先检查当前URL await checkCurrentUrl(); // 然后处理页面链接 await parseLinks(); // 使用防抖包装 parseLinks let timeout; const observer = new MutationObserver(() => { clearTimeout(timeout); timeout = setTimeout(parseLinks, 250); }); observer.observe(document.body, { childList: true, subtree: true }); } catch (error) { console.error('Initialization error:', error); } })();