您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
remove tracking parameters
// ==UserScript== // @name TrackingParameterRemover // @namespace https://htsign.hateblo.jp // @version 20240926-rev1 // @description remove tracking parameters // @author htsign // @match *://*/* // @run-at document-start // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // ==/UserScript== const TRACKING_TAGS = [ '#?utm_campaign', '#?utm_content', '#?utm_int', '#?utm_medium', '#?utm_source', '_hsmi', '_openstat', 'action_object_map', 'action_ref_map', 'action_type_map', 'fb_action_ids', 'fb_action_types', 'fb_ref', 'fb_source', 'ga_campaign', 'ga_content', 'ga_medium', 'ga_place', 'ga_source', 'ga_term', 'gs_l', 'guccounter', 'guce_referrer', 'guce_referrer_sig', 'gws_rd', 'hmb_campaign', 'hmb_medium', 'hmb_source', 'ref_src', 'ref_url', 'utm_campaign', 'utm_cid', 'utm_content', 'utm_int', 'utm_language', 'utm_medium', 'utm_name', 'utm_place', 'utm_pubreferrer', 'utm_reader', 'utm_source', 'utm_swu', 'utm_term', 'utm_userid', 'utm_viz_id', 'yclid', '[email protected]', '[email protected]', '_encoding@amazon.*', 'linkCode@amazon.*', 'linkId@amazon.*', 'pd_rd_*@amazon.*', 'psc@amazon.*', 'qid@amazon.*', 'sbo@amazon.*', 'sprefix@amazon.*', 'sr@amazon.*', 'tag@amazon.*', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', 'bi?@google.*', 'client@google.*', 'dpr@google.*', 'ei@google.*', 'gws_rd@google.*', 'oq@google.*', 'sa@google.*', 'sei@google.*', 'source@google.*', 'ved@google.*', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]', ]; const createStyle = params => { const toKebab = s => s.replace(/(?<=[a-z])[A-Z]/g, m => `-${m.toLowerCase()}`); return Object.entries(params) .map(([key, val]) => `${toKebab(key)}: ${val};`) .join('; '); }; const createWrapper = () => { const PADDING = 20; const wrapper = Object.assign( document.createElement('div'), { style: createStyle({ position: 'fixed', width: 'fit-content', maxHeight: 'fit-content', left: `${PADDING}px`, top: `${PADDING}px`, zIndex: 2 ** 31 - 1, }), }, ); return document.body.appendChild(wrapper); }; GM_registerMenuCommand('open settings', async ev => { const wrapper = createWrapper(); const shadowRoot = wrapper.attachShadow({ mode: 'open' }); const css = ` :host { background-color: white; border: 2px solid #777; max-width: 600px; @media (prefers-color-scheme: dark) { background-color: #333; } } #list { display: block; white-space: pre; width: 400px; height: max(50vh, 600px); } #help { font: 16px sans-serif; } #help_pre { font: 14px monospace; line-height: 1; } #controller { display: flex; justify-content: space-between; } #content_controller { display: flex; } #reset_button { background-color: rgb(64 0 0); } `; /** @type {string[]} */ const trackingTags = GM_getValue('tracking_tags', TRACKING_TAGS); const listArea = Object.assign(document.createElement('textarea'), { id: 'list', value: trackingTags.join('\n'), }); const controllerBar = Object.assign(document.createElement('div'), { id: 'controller' }); const contentController = Object.assign(document.createElement('div'), { id: 'content_controller' }); const resetButton = Object.assign(document.createElement('button'), { id: 'reset_button', onclick() { listArea.value = trackingTags.join('\n'); }, textContent: 'Reset', }); const confirmButton = Object.assign(document.createElement('button'), { id: 'confirm_button', onclick() { GM_setValue('tracking_tags', listArea.value.split('\n')); }, textContent: 'Save', }); const closeButton = Object.assign(document.createElement('button'), { onclick() { wrapper.remove(); }, textContent: 'Close', }); const help = Object.assign(document.createElement('details'), { id: 'help' }); help.append( Object.assign(document.createElement('summary'), { textContent: 'Help' }), Object.assign(document.createElement('pre'), { id: 'help_pre', textContent: ` standard style: - <parameter-name>[@<host>[/<path>]] - e.g. "utm_source" - e.g. "[email protected]" for hash keywords: - #?<parameter-name>[@<host>[/<path>]] others: - blank lines are ignored - placing "//" at the beginning of a line causes that line to be ignored `.trim(), }), ); contentController.append(resetButton, confirmButton); controllerBar.append(contentController, closeButton); shadowRoot.append(listArea, help, controllerBar); shadowRoot.adoptedStyleSheets = [await new CSSStyleSheet().replace(css)]; }); (function() { 'use strict'; /** * @param {Location} loc * @returns {{ url: string, locationChanged: boolean }} */ const removeTracking = loc => { let locationChanged = false; const escapables = Object.freeze({ '.': '\.', }); const wildcardCharacters = Object.freeze({ '*': '.*', '?': '.', }); const wildcardKeys = Object.keys(wildcardCharacters); /** * @param {string} pattern * @returns {RegExp} */ const toRegExp = pattern => { /** * * @param {Record<string, string>} table * @param {string} s * @returns {string} */ const replace = (table, s) => Object.entries(table).reduce((acc, [f, t]) => acc.split(f).join(t), s); const sanitized = replace(escapables, pattern); const inner = replace(wildcardCharacters, sanitized); return new RegExp('^' + inner + '$', 'i'); }; /** * * @param {string} pattern * @param {string} s * @returns {boolean} */ const match = (pattern, s) => { if (wildcardKeys.some(c => pattern.includes(c))) { return toRegExp(pattern).test(s); } return pattern === s; }; /** * @param {string} domain * @param {URLSearchParams} params * @param {string} pattern * @returns {string} */ const deleteKeys = (domain, params, pattern) => { if (!domain || loc.hostname.split('.').some((_, i, arr) => match(domain, arr.slice(i).join('.')))) { for (const [key] of params) { if (match(pattern, key)) { params.delete(key); locationChanged = true; } } } return params.toString().split('%25').join('%'); }; /** * @param {string} search * @param {() => boolean} condition * @param {(arg: URLSearchParams) => any} callback * @returns {void} */ const proc = (search, condition, callback) => { const params = new URLSearchParams(search); if (params.size === 0) return; if (!condition()) return; callback(params); }; const url = new URL(loc); url.search = url.search.split('%25').join('\0'); // avoid to escape of original '%25' GM_getValue('tracking_tags', TRACKING_TAGS).forEach(tag => { if (tag === '' || tag.startsWith('//')) return; const [t, domain, pathname] = tag.split(/[@\/]/); if (t.startsWith('#?')) { proc( url.hash.slice(1), () => pathname == null || url.pathname === `/${pathname}`, params => { url.hash = deleteKeys(domain, params, t.slice(2)); }, ); } else { if (!url.search) return; proc( url.search.slice(1).replace(/%(?!25)/g, '%25'), () => pathname == null || url.pathname === `/${pathname}`, params => { url.search = deleteKeys(domain, params, t); }, ); } }); url.search = url.search.split('%00').join('%25'); // restore original '%25' return { url: url.href, locationChanged }; }; const { url, locationChanged } = removeTracking(location); if (locationChanged) { console.info(`TrackingParameterRemover: {${location.href}} => {${url}}`); location.replace(url); } }());
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址