V2EX 前天 16:57
V2EX 兑换码高亮助手脚本
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

针对 V2EX 论坛分享 APP 兑换码的痛点,该油猴脚本能自动抓取帖子作者发布的兑换码以及多页评论中的兑换码。脚本会将作者发布的未使用的兑换码高亮显示为绿色,已使用的兑换码高亮显示为红色。同时,它还能在页面右下角统计兑换码的总数、已用数和可用数,极大地提高了查找可用兑换码的效率。安装简便,推荐使用 Tampermonkey/Violentmonkey 浏览器扩展。

🚦 **功能实现:**该脚本的核心功能是自动识别并高亮显示 V2EX 帖子中的 APP 兑换码。它能够区分作者发布的兑换码和评论区用户分享的兑换码,并根据兑换码是否已被使用,分别以绿色(未使用)和红色(已使用)进行视觉标记,方便用户快速筛选。

🔍 **信息抓取与统计:**脚本不仅能解析帖子正文和作者附言中的兑换码,还能跨越多页抓取评论区信息,全面收集所有已知的兑换码。最终,在页面右下角提供一个直观的统计面板,显示兑换码的总数、已使用的数量以及当前可用的兑换码数量,让用户一目了然。

⚙️ **安装与使用便捷性:**该脚本兼容 Tampermonkey 或 Violentmonkey 等浏览器扩展,安装过程简单。用户只需安装扩展,然后粘贴脚本代码并保存,脚本便会在访问 V2EX 帖子页面时自动生效,无需手动触发,提供了无缝的使用体验。

看到有大佬分享 APP 兑换码, 但是试了好多, 都是用过的, 即便很多高素质大佬把使用过的贴到了评论区,依然非常难找出一个未使用的兑换码.
于是让 GPT 写了个油猴脚本, 把未使用的兑换码高亮出来方便查找.
当然这个前提是需要大家主动把已经使用的兑换码贴到评论里
高亮显示未使用(绿色)和已使用(红色)兑换码

安装方式

推荐使用 Tampermonkey/Violentmonkey

    安装浏览器扩展 Tampermonkey 点击 “创建新脚本”,粘贴下面完整脚本 保存后访问任意 V2EX 帖子页面,自动生效

使用方法

完整脚本( v1.6 )

// ==UserScript==// @name         V2EX 兑换码高亮助手 (多页评论)// @namespace    https://v2ex.com/// @version      1.5// @description  高亮显示作者发布的兑换码(正文 + 附言),抓取多页评论兑换码,评论中出现的默认已使用。// @match        https://www.v2ex.com/t/*// @match        https://v2ex.com/t/*// @grant        none// ==/UserScript==(function () {    'use strict';    const MIN_LEN = 10; // 兑换码最小长度    function extractCodes(text) {        const pattern = new RegExp(`\\b[A-Z0-9]{${MIN_LEN},}\\b`, 'g');        return new Set(text.match(pattern) || []);    }    function extractCodesFromReply(replyNode) {        const codes = new Set();         console.log('[V2EX Code Highlighter] replyNode:', replyNode);        // 遍历 replyNode 的子节点        replyNode.childNodes.forEach(node => {            if (node.nodeType === Node.TEXT_NODE) {                // 文本节点按空格分割                node.textContent.split(/\s+/).forEach(word => {                    //console.log('正在解析:', word)                    // 全局匹配所有 10 位以上大写字母或数字                    const pattern = /\b[A-Z0-9]{10,}\b/g;                    const matches = word.match(pattern) || [];                    matches.forEach(c => codes.add(c));                });            } else if (node.nodeName === 'BR') {                // <br> 就当作分隔,不需要处理            } else {                // 递归抓取子节点                extractCodesFromReply(node).forEach(c => codes.add(c));            }        });        //console.log('该评论最后得到:', codes)        return codes;    }    function replaceTextNodes(node, callback) {        const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, null, false);        const nodes = [];        let n;        while (n = walker.nextNode()) nodes.push(n);        for (const t of nodes) callback(t);    }    function highlightCodeSpan(code, used) {        const span = document.createElement('span');        span.textContent = code;        span.style.cssText = `            background-color: ${used ? 'red' : 'green'};            color: white;            font-weight: bold;            padding: 2px 4px;            border-radius: 4px;            margin: 0 2px;            font-family: monospace;        `;        span.title = used ? '已用' : '未用';        return span;    }    // 异步抓取评论页内容    async function fetchReplyCodes(url, authorName) {        const commentCodes = new Set();        try {            const res = await fetch(url);            const text = await res.text();            const parser = new DOMParser();            const doc = parser.parseFromString(text, 'text/html');            const replyNodes = doc.querySelectorAll('.reply_content');            replyNodes.forEach(r => {                const floorNode = r.closest('.cell');                const userLink = floorNode ? floorNode.querySelector('.dark, .username, a[href^="/member/"]') : null;                const userName = userLink ? userLink.textContent.trim() : '';                if (userName === authorName) return; // 跳过作者                extractCodesFromReply(r).forEach(c => commentCodes.add(c));            });        } catch (e) {            console.error('[V2EX Code Highlighter] Fetch page error:', url, e);        }        return commentCodes;    }    async function run() {        const mainPostNode = document.querySelector('#Main .topic_content');        if (!mainPostNode) return;        const authorNode = document.querySelector('#Main .header .fr a[href^="/member/"]');        if (!authorNode) return;        const authorName = authorNode.textContent.trim();        console.log('[V2EX Code Highlighter] Author:', authorName);        const mainCodes = new Set();        const commentCodes = new Set();        // 1️⃣ 抓取作者正文        extractCodes(mainPostNode.innerText).forEach(c => mainCodes.add(c));        // 2️⃣ 抓取作者附言        const subNotes = document.querySelectorAll('#Main .subtle .topic_content');        subNotes.forEach(note => {            extractCodes(note.innerText).forEach(c => mainCodes.add(c));        });        // 输出作者兑换码日志        console.log('[V2EX Code Highlighter] Author codes:', [...mainCodes]);        // 3️⃣ 获取评论页数        const psContainer = document.querySelector('.cell.ps_container');        let totalPages = 1;        if (psContainer) {            const pageLinks = psContainer.querySelectorAll('a.page_current, a.page_normal');            totalPages = Math.max(...Array.from(pageLinks).map(a => parseInt(a.textContent.trim())));        }        console.log('[V2EX Code Highlighter] totalPages:', totalPages);        // 4️⃣ 抓取所有评论页        const currentUrl = window.location.href.split('?')[0];        const pageUrls = [];        for (let p = 1; p <= totalPages; p++) {            pageUrls.push(`${currentUrl}?p=${p}`);        }        for (const url of pageUrls) {            const codes = await fetchReplyCodes(url, authorName);            codes.forEach(c => commentCodes.add(c));        }        console.log('[V2EX Code Highlighter] Comment codes (all pages):', [...commentCodes]);        // 5️⃣ 计算未用        const unusedCodes = [...mainCodes].filter(c => !commentCodes.has(c));        // 6️⃣ 高亮当前页面作者兑换码(正文 + 附言)        const authorContentNodes = [mainPostNode, ...Array.from(subNotes)];        authorContentNodes.forEach(node => {            replaceTextNodes(node, t => {                const text = t.textContent;                const codes = extractCodes(text);                if (!codes.size) return;                const frag = document.createDocumentFragment();                let remaining = text;                codes.forEach(c => {                    const parts = remaining.split(c);                    frag.appendChild(document.createTextNode(parts.shift()));                    const used = commentCodes.has(c);                    frag.appendChild(highlightCodeSpan(c, used));                    remaining = parts.join(c);                });                frag.appendChild(document.createTextNode(remaining));                t.parentNode.replaceChild(frag, t);            });        });        // 7️⃣ 页面右下角统计        const panel = document.createElement('div');        panel.style.cssText = `            position: fixed;            bottom: 10px;            right: 10px;            background: #222;            color: #fff;            padding: 10px 14px;            border-radius: 8px;            box-shadow: 0 0 6px rgba(0,0,0,0.5);            font-size: 13px;            z-index: 9999;            line-height: 1.5;        `;        panel.innerHTML = `            <b>兑换码统计</b><br>            总数: ${mainCodes.size}<br>            已用: ${commentCodes.size}<br>            可用: ${unusedCodes.length}        `;        document.body.appendChild(panel);    }    window.addEventListener('load', run);})();

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

V2EX 油猴脚本 兑换码 Tampermonkey 脚本 工具 V2EX Code Highlighter Redemption Code Userscript
相关文章