我让 GPT 写了个 APP 兑换码高亮助手 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
apanlin
V2EX    分享创造

我让 GPT 写了个 APP 兑换码高亮助手

  •  
  •   apanlin 16 天前 1642 次点击

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

    安装方式

    推荐使用 Tampermonkey/Violentmonkey

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

    使用方法

    • 打开 V2EX 帖子页面
    • 脚本会自动抓取作者的兑换码 + 评论
    • 高亮显示未使用(绿色)和已使用(红色)兑换码
    • 页面右下角显示统计信息

    完整脚本( v1.6 )

    // ==UserScript== // @name V2EX 兑换码高亮助手 (多页评论) // @namespace https://v2ex.com/ // @version 1.5 // @description 高亮显示作者发布的兑换码(正文 + 附言),抓取多页评论兑换码,评论中出现的默认已使用。 // @match 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); })(); 
    11 条回复    2025-10-17 23:10:31 +08:00
    korvin
        1
    korvin  
       16 天前
    哈哈,和我之前写的差不多 /t/1127520
    saimax
        2
    saimax  
       16 天前
    想法是好的,但实际情况兑换了回复的不足 1 成。所以没啥用
    HMYang33
        3
    HMYang33  
       16 天前
    基数不够大,如果是腾讯或谷歌做的,估计有点用
    apanlin
        4
    apanlin  
    OP
       16 天前
    @saimax 总有正义的大佬会把兑换失败的全都贴到评论区的, 所以还是能提高点效率
    acluxo
        5
    acluxo  
       16 天前
    直接复制全文丢到 AI 里
    callv
        6
    callv  
       16 天前
    我写的这个社区可以直接记录兑换,你们看看效果是不是更好一些。https://2libra.com/post/festival-things/IpsWhjF
    apanlin
        7
    apanlin  
    OP
       16 天前
    @korvin 哈哈 原来大佬做过了, 失敬失敬
    apanlin
        8
    apanlin  
    OP
       16 天前
    @acluxo 这倒是好主意, 直接丢链接过去 让 AI 自己解析识别应该也可以
    deplives
        9
    deplives  
       16 天前
    实际上没啥用,用码后回复的我觉得不到 1/10
    apanlin
        10
    apanlin  
    OP
       16 天前
    @callv 直接做到平台上确实好用
    apanlin
        11
    apanlin  
    OP
       16 天前
    @deplives 靠大家一起维护, 会有很多正义大佬把兑换失败的码贴到评论里的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     884 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 22:37 PVG 06:37 LAX 14:37 JFK 17:37
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86