您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Faster, smarter Amazon enhancer. Strictly detects Amazon Monthly Payments (e.g., Pay $xx/mo for N months). No Affirm/Klarna/Prime Visa/financing noise. Lazy Camel/Keepa, SPA-aware, saved gear position, immediate monthly refresh.
// ==UserScript== // @name Amazon Enhancer: Monthly + ReviewMeta + Camel + Keepa + UI + Dark + Review Highlights (Enhanced) // @namespace Eliminater74 // @version 1.5.2 // @description Faster, smarter Amazon enhancer. Strictly detects Amazon Monthly Payments (e.g., Pay $xx/mo for N months). No Affirm/Klarna/Prime Visa/financing noise. Lazy Camel/Keepa, SPA-aware, saved gear position, immediate monthly refresh. // @author Eliminater74 // @license MIT // @match https://www.amazon.com/* // @match https://www.amazon.co.uk/* // @match https://www.amazon.de/* // @match https://www.amazon.fr/* // @match https://www.amazon.it/* // @match https://www.amazon.es/* // @match https://www.amazon.ca/* // @match https://smile.amazon.com/* // @grant GM_xmlhttpRequest // @connect reviewmeta.com // @run-at document-end // ==/UserScript== (function () { 'use strict'; // ----------------------------- // Settings & utils // ----------------------------- const SETTINGS_KEY = 'amazonEnhancerSettings'; const GEAR_POS_KEY = 'amazonEnhancerGearPos'; const defaultSettings = { showReviewMeta: true, showCamel: true, showKeepa: true, theme: 'auto', highlightBestReviews: true, hideAds: true, showSoldBy: true, stickyPriceBox: true, autoSortReviews: true, expandReviewsQA: true, highlightMonthlyPayments: true, filterOnlyMonthly: false, // extras hideFbtAndRecs: true, forceVerifiedPurchase: false, keyboardShortcut: true, primeOnlyFilter: false }; const $ = (s, r=document)=>r.querySelector(s); const $$ = (s, r=document)=>Array.from(r.querySelectorAll(s)); const onIdle=(fn)=>(window.requestIdleCallback||setTimeout)(fn,0); const debounce=(fn,ms=200)=>{let t;return(...a)=>{clearTimeout(t);t=setTimeout(()=>fn(...a),ms);};}; const throttle=(fn,ms=200)=>{let p=0;return(...a)=>{const n=Date.now();if(n-p>=ms){p=n;fn(...a);}};}; const settings = Object.assign({}, defaultSettings, loadJSON(SETTINGS_KEY) || {}); saveJSON(SETTINGS_KEY, settings); function loadJSON(k){ try{ return JSON.parse(localStorage.getItem(k)||'{}'); }catch{ return {}; } } function saveJSON(k,v){ localStorage.setItem(k, JSON.stringify(v)); } function getLocale(){ const h=location.hostname; if(h.includes('.co.uk'))return'uk'; if(h.includes('.de'))return'de'; if(h.includes('.fr'))return'fr'; if(h.includes('.es'))return'es'; if(h.includes('.it'))return'it'; if(h.includes('.ca'))return'ca'; return'us'; } const locale=getLocale(); function detectASIN(){ const m=location.href.match(/\/([A-Z0-9]{10})(?:[/?]|$)/); if(m) return m[1]; const meta=$('input#ASIN')||$('[name="ASIN.0"]')||$('[name="ASIN"]'); if(meta?.value) return meta.value; const body=document.body.getAttribute('data-asin')||document.body.getAttribute('data-asin-candidate'); if(body && /^[A-Z0-9]{10}$/.test(body)) return body; const el=$('[data-asin]'); const g=el?.getAttribute('data-asin'); return /^[A-Z0-9]{10}$/.test(g||'')?g:null; } let ASIN=detectASIN(); // ----------------------------- // Theme // ----------------------------- applyTheme(settings.theme); function applyTheme(mode){ const html=document.documentElement; const want=(mode==='auto')?(matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'):mode; html.setAttribute('data-enhancer-theme',want); $('#amazon-enhancer-theme-style')?.remove(); const style=document.createElement('style'); style.id='amazon-enhancer-theme-style'; style.textContent=` [data-enhancer-theme="dark"] .amazon-enhancer-box{background:#1d1d1d!important;color:#f0f0f0!important;border-color:#555!important} [data-enhancer-theme="dark"] .amazon-enhancer-box a{color:#7dddf2!important} [data-enhancer-theme="dark"] .amazon-enhancer-panel{background:#2c2c2c!important;color:#eee!important;border-color:#555!important} .highlighted-review{border:2px solid gold!important;background:#fffbea!important} .monthly-badge{position:absolute;top:10px;left:10px;background:#0099cc!important;color:#fff!important;padding:2px 6px;font-weight:bold;font-size:12px;border-radius:4px;z-index:2147483647!important} `; document.head.appendChild(style); } // ----------------------------- // UI (gear + panel) // ----------------------------- createToggleUI(); function createToggleUI(){ const gear=document.createElement('div'); gear.id='amazon-enhancer-gear'; gear.textContent='⚙️'; gear.style.cssText=`position:fixed;width:40px;height:40px;font-size:22px;background:#222;color:#fff;border:2px solid #888;border-radius:50%; display:flex;justify-content:center;align-items:center;cursor:move;box-shadow:0 0 12px rgba(0,0,0,.8);z-index:2147483647;`; restoreGearPosition(gear); const panel=document.createElement('div'); panel.className='amazon-enhancer-panel'; panel.style.cssText=`position:fixed;border:1px solid #ccc;padding:10px;border-radius:8px;z-index:2147483646;background:#fff;display:none; right:20px;bottom:70px;max-width:300px;font-family:system-ui,Segoe UI,Arial,sans-serif;font-size:13px;line-height:1.3;`; const toggles=[ ['showReviewMeta','ReviewMeta'], ['showCamel','CamelCamelCamel (lazy)'], ['showKeepa','Keepa (lazy)'], ['highlightBestReviews','Highlight Best Reviews'], ['hideAds','Hide Sponsored Ads'], ['showSoldBy','Highlight “Sold by” box'], ['stickyPriceBox','Sticky Price Box'], ['autoSortReviews','Auto Sort Reviews (recent)'], ['expandReviewsQA','Expand Q&A/Reviews'], ['highlightMonthlyPayments','Highlight Monthly Payments'], ['filterOnlyMonthly','Filter: Only Amazon Monthly Payments'], ['hideFbtAndRecs','Hide FBT & Recommendations'], ['forceVerifiedPurchase','Reviews: Verified Purchase only'], ['primeOnlyFilter','Search: Prime only'], ['keyboardShortcut','Keyboard Toggle (Alt+E)'] ]; panel.innerHTML = ` ${toggles.map(([k,l])=>`<label style="display:block;margin:4px 0;"><input type="checkbox" id="${k}" ${settings[k]?'checked':''}> ${l}</label>`).join('')} <label style="display:block;margin-top:6px;">Theme: <select id="themeSelect"> <option value="auto" ${settings.theme==='auto'?'selected':''}>Auto</option> <option value="light" ${settings.theme==='light'?'selected':''}>Light</option> <option value="dark" ${settings.theme==='dark'?'selected':''}>Dark</option> </select> </label> `; toggles.forEach(([key])=>{ const el=panel.querySelector('#'+key); el.addEventListener('change', e=>{ settings[key]=e.target.checked; saveJSON(SETTINGS_KEY, settings); if (key==='filterOnlyMonthly' || key==='highlightMonthlyPayments') { rescanMonthly(true); return; } if (key==='primeOnlyFilter') { primeOnlyFilter(true); return; } if (key==='hideAds' || key==='hideFbtAndRecs') { hideSponsored(); hideFbtAndRecs(); return; } if (key==='showReviewMeta' || key==='showCamel' || key==='showKeepa') { renderDataBoxes(); return; } }); }); panel.querySelector('#themeSelect').addEventListener('change', e=>{ settings.theme=e.target.value; saveJSON(SETTINGS_KEY, settings); applyTheme(settings.theme); }); // drag/click let isDragging=false, startX=0, startY=0, moved=false; gear.addEventListener('mousedown', e=>{ isDragging=true;moved=false; startX=e.clientX-gear.getBoundingClientRect().left; startY=e.clientY-gear.getBoundingClientRect().top; e.preventDefault(); }); document.addEventListener('mousemove', e=>{ if(!isDragging)return; gear.style.left=(e.clientX-startX)+'px'; gear.style.top=(e.clientY-startY)+'px'; gear.style.right='auto'; gear.style.bottom='auto'; moved=true; }); document.addEventListener('mouseup', ()=>{ if(!isDragging)return; isDragging=false; if(moved) saveJSON(GEAR_POS_KEY, {left:gear.style.left, top:gear.style.top}); }); gear.addEventListener('click', ()=>{ if(moved)return; panel.style.display=panel.style.display==='none'?'block':'none'; }); gear.ondragstart=()=>false; if (settings.keyboardShortcut) { document.addEventListener('keydown', e=>{ if (e.altKey && (/^e$/i).test(e.key)) { e.preventDefault(); panel.style.display=panel.style.display==='none'?'block':'none'; } }); } document.body.appendChild(gear); document.body.appendChild(panel); } function restoreGearPosition(gear){ const pos=loadJSON(GEAR_POS_KEY); if(pos?.left && pos?.top){ gear.style.left=pos.left; gear.style.top=pos.top; gear.style.right='auto'; gear.style.bottom='auto'; } else { gear.style.bottom='20px'; gear.style.right='20px'; } } // ----------------------------- // Data boxes (lazy) // ----------------------------- function appendToTarget(el){ const t = $('#unifiedPrice_feature_div') || $('#corePrice_feature_div') || $('#title')?.closest('.a-section'); if (t) t.appendChild(el); } function renderDataBoxes(){ $$('.amazon-enhancer-box').forEach(n=>n.remove()); const asin=ASIN||detectASIN(); if(!asin) return; if (settings.showReviewMeta) injectReviewMeta(asin); if (settings.showCamel) injectCamelLazy(asin); if (settings.showKeepa) injectKeepaLazy(asin); } function injectCamelLazy(asin){ const div=document.createElement('div'); div.className='amazon-enhancer-box'; div.style.cssText='margin-top:10px;padding:10px;border:1px solid #ccc;'; div.innerHTML=` <b>CamelCamelCamel:</b> <div style="margin-top:6px;"> <a href="https://${locale}.camelcamelcamel.com/product/${asin}" target="_blank" rel="noopener"> <img data-src="https://charts.camelcamelcamel.com/${locale}/${asin}/amazon-new-used.png?force=1&zero=0&w=600&h=340" alt="Price history (Camel)" style="max-width:100%;height:auto;opacity:.001;"> </a> </div>`; appendToTarget(div); lazyImageLoad($('img[data-src]',div)); } function injectKeepaLazy(asin){ const div=document.createElement('div'); div.className='amazon-enhancer-box'; div.style.cssText='margin-top:10px;padding:10px;border:1px solid #ccc;'; div.innerHTML=` <b>Keepa:</b> <div style="margin-top:6px;"> <a href="https://keepa.com/#!product/1-${asin}" target="_blank" rel="noopener"> <img data-src="https://graph.keepa.com/pricehistory.png?used=1&amazon=1&new=1&domain=${locale}&asin=${asin}" alt="Price history (Keepa)" style="max-width:100%;height:auto;opacity:.001;"> </a> </div>`; appendToTarget(div); lazyImageLoad($('img[data-src]',div)); } function lazyImageLoad(img){ if(!img)return; const io=new IntersectionObserver((es,o)=>{ es.forEach(e=>{ if(e.isIntersecting){ img.src=img.dataset.src; img.style.opacity='1'; img.removeAttribute('data-src'); o.unobserve(e.target); } }); },{rootMargin:'200px'}); io.observe(img); } function injectReviewMeta(asin){ const url=`https://reviewmeta.com/amazon${locale==='us'?'':'-'+locale}/${asin}`; GM_xmlhttpRequest({ method:'GET', url, onload: res=>{ const doc=new DOMParser().parseFromString(res.responseText,'text/html'); const stars=doc.querySelector('#adjusted-rating-large')?.textContent?.trim(); const percent=Array.from(doc.querySelectorAll('small')).find(e=>/potentially unnatural/i.test(e.textContent||''))?.querySelector('span span')?.textContent?.trim(); const div=document.createElement('div'); div.className='amazon-enhancer-box'; div.style.cssText='margin-top:10px;padding:10px;border:1px solid #ccc;'; div.innerHTML = stars ? `<b>ReviewMeta Adjusted:</b> <span style="color:firebrick">${stars}/5</span><br><b>Fake Reviews:</b> <span style="color:firebrick">${percent||'—'}</span><br><a href="${url}" target="_blank" rel="noopener" style="color:green;">View on ReviewMeta</a>` : `<b style="color:red;">ReviewMeta data not found.</b><br><a href="${url}" target="_blank" rel="noopener">Submit product</a>`; appendToTarget(div); }}); } // ----------------------------- // PDP tweaks // ----------------------------- function highlightReviews(){ const reviews=$$('.review'); reviews.sort((a,b)=>{ const ha=parseInt((a.innerText.match(/(\d[\d,]*)\s+people?\s+found this helpful/i)||[])[1]?.replace(/,/g,'')||'0',10); const hb=parseInt((b.innerText.match(/(\d[\d,]*)\s+people?\s+found this helpful/i)||[])[1]?.replace(/,/g,'')||'0',10); return hb-ha; }).slice(0,3).forEach(el=>el.classList.add('highlighted-review')); } function autoSortReviews(){ const sel=$('select[name="sortBy"]'); if(sel){ if(sel.value!=='recent') sel.value='recent'; sel.dispatchEvent(new Event('change',{bubbles:true})); } } function forceVerifiedOnly(){ if(!settings.forceVerifiedPurchase)return; const cb=$$('input[type=checkbox]').find(n=>/verified purchase/i.test(n.closest('label')?.textContent||'')); if(cb && !cb.checked){ cb.click(); return; } const link=$$('a').find(a=>/verified purchase/i.test(a.textContent||'')); if(link) link.click(); } function expandSections(){ $$('.a-expander-prompt, .a-expander-prompt-content, .a-expander-header').forEach(e=>e.click()); $$('span.a-expander-prompt, button[aria-label*="See more"]').forEach(b=>b.click()); } function makeStickyPriceBox(){ const box=$('#corePrice_feature_div')||$('#unifiedPrice_feature_div'); if(box){ box.style.position='sticky'; box.style.top='0'; box.style.background='#fff'; box.style.zIndex=9999; box.style.borderBottom='2px solid #ccc'; } } function showSoldByBox(){ const el=$('#merchant-info'); if(el) el.style.border='2px dashed orange'; } // ----------------------------- // Search: strict Amazon Monthly Payments detector // ----------------------------- const money = { us: '\\$', uk: '£', de: '€', fr: '€', es: '€', it: '€', ca: '\\$' }[locale] || '\\$'; // Inclusion (Amazon Monthly Payments): // - Pay $xx/mo for N months // - N monthly payments // - Pay $xx for N months const INCLUDE = [ new RegExp(`\\bPay\\s+${money}\\s?\\d[\\d,]*(?:\\.\\d{2})?\\s*\\/\\s*mo\\.?\\s*(?:for|x)\\s*\\d+\\s*months`, 'i'), /\b\d+\s+monthly payments\b/i, new RegExp(`\\bPay\\s+${money}\\s?\\d[\\d,]*(?:\\.\\d{2})?\\s*(?:for|x)\\s*\\d+\\s*months`, 'i'), /\bAmazon\s+Monthly\s+Payments?\b/i ]; // Exclusion (financing/credit/BNPL/etc.) const EXCLUDE = /\b(Affirm|Klarna|Afterpay|Zip|Prime\s*Visa|Store\s*Card|APR|interest|financing|lease|subscription|per\s*month\b|\/\s*month\b)\b/i; const rescanMonthly = (function(){ function isAmazonMonthlyNode(node){ if(!node) return false; const t = (node.textContent||'').replace(/\s+/g,' ').trim(); if (!t) return false; if (EXCLUDE.test(t)) return false; return INCLUDE.some(rx => rx.test(t)); } function resultHasAmazonMonthly(result){ // Quick aria-label hit first (Amazon sometimes sets it) const aria = result.querySelector('[aria-label*="Monthly"][aria-label*="month" i]'); if (aria && isAmazonMonthlyNode(aria)) return true; // Scan limited children to stay fast const tags=['span','div','i','a','button']; let scanned=0; for(const tag of tags){ const nodes=result.getElementsByTagName(tag); for(let i=0;i<nodes.length && scanned<100;i++,scanned++){ if (isAmazonMonthlyNode(nodes[i])) return true; } } return false; } function clearBadge(r){ $('.monthly-badge',r)?.remove(); } function addBadge(r){ if($('.monthly-badge',r)) return; const b=document.createElement('div'); b.className='monthly-badge'; b.textContent='💳 Monthly Payments'; if(getComputedStyle(r).position==='static') r.style.position='relative'; r.appendChild(b); } function run(){ const results=$$('[data-component-type="s-search-result"], .s-result-item'); if(!results.length) return; for(const r of results){ clearBadge(r); const match = resultHasAmazonMonthly(r); if (match){ if (settings.highlightMonthlyPayments) addBadge(r); r.style.display=''; } else if (settings.filterOnlyMonthly){ r.style.display='none'; } else { r.style.display=''; } } } const debounced=debounce(run,150); const api=(immediate=false)=> immediate?run():debounced(); api.force=()=>run(); return api; })(); function primeOnlyFilter(immediate=false){ const run=()=>{ if(!settings.primeOnlyFilter) return; const results=$$('[data-component-type="s-search-result"], .s-result-item'); results.forEach(r=>{ const prime = r.querySelector('i[aria-label*="Prime"], span[aria-label*="Prime"]'); r.style.display = prime ? '' : 'none'; }); }; return immediate?run():onIdle(run); } function hideSponsored(){ if(!settings.hideAds) return; const sels=[ '[data-component-type="sp-sponsored-result"]', '[data-cel-widget^="sp_"]', 'div.AdHolder', 'div[data-ad-feedback]' ]; sels.forEach(s=> $$(s).forEach(el=> el.style.display='none')); $$('div[aria-label*="Sponsored"]').forEach(el=> el.style.display='none'); } function hideFbtAndRecs(){ if(!settings.hideFbtAndRecs) return; const labels=[/Frequently bought together/i,/Customers who bought/i,/Products related to this item/i,/Inspired by your browsing history/i]; $$('h2, h3').forEach(h=>{ if(labels.some(rx=>rx.test(h.textContent||''))){ const cont=h.closest('.a-section')||h.parentElement; if(cont) cont.style.display='none'; } }); } // ----------------------------- // SPA handling // ----------------------------- function pageInit(){ ASIN=detectASIN(); renderDataBoxes(); if(settings.highlightBestReviews) onIdle(highlightReviews); if(settings.hideAds) onIdle(hideSponsored); if(settings.showSoldBy) onIdle(showSoldByBox); if(settings.stickyPriceBox) onIdle(makeStickyPriceBox); if(settings.autoSortReviews) onIdle(autoSortReviews); if(settings.expandReviewsQA) onIdle(expandSections); if(settings.forceVerifiedPurchase) onIdle(forceVerifiedOnly); if(settings.hideFbtAndRecs) onIdle(hideFbtAndRecs); if(settings.highlightMonthlyPayments || settings.filterOnlyMonthly) rescanMonthly(true); if(settings.primeOnlyFilter) primeOnlyFilter(true); } const rescanAll = throttle(()=>{ hideSponsored(); hideFbtAndRecs(); rescanMonthly(); // debounced if (settings.primeOnlyFilter) primeOnlyFilter(true); }, 400); const mo=new MutationObserver(()=>{ rescanAll(); }); mo.observe(document.body, {childList:true, subtree:true}); (function hookHistory(){ const push=history.pushState; history.pushState=function(){ const r=push.apply(this,arguments); setTimeout(pageInit,50); return r; }; window.addEventListener('popstate',()=> setTimeout(pageInit,50)); })(); // Start pageInit(); })();