您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Автоочистка ссылок от UTM/трекеров и быстрая копия чистого URL (кнопка 🔗 рядом со ссылкой + меню).
// ==UserScript== // @name Clean Links + Copy (no UTM) // @namespace https://nikk.agency/ // @version 1.0.0 // @description Автоочистка ссылок от UTM/трекеров и быстрая копия чистого URL (кнопка 🔗 рядом со ссылкой + меню). // @author NAnews / NiKK // @license MIT // @match *://*/* // @run-at document-idle // @grant GM_setClipboard // @grant GM_registerMenuCommand // ==/UserScript== (function () { "use strict"; const TRACKING_PARAMS = new Set([ // универсальные "utm_source","utm_medium","utm_campaign","utm_term","utm_content","utm_name","utm_id","utm_reader","utm_brand", // соцсети/рекламные "fbclid","gclid","wbraid","gbraid","yclid","mc_cid","mc_eid","igshid","si","spm", "ref","ref_src","ref_url","campaign_id","adset_id","ad_id", // прочие популярные "mkt_tok","vero_id","sca_esv","_hsenc","_hsmi","ncid","trk","rb_clickid","ttclid", ]); const BUTTON_CLASS = "clean-link-copy-btn"; function cleanUrl(raw) { try { const url = new URL(raw, location.href); // чистим хеш-трекинг типа ?x#~:text=... if (url.hash && /~:text=/.test(url.hash)) url.hash = ""; // чистим параметры const p = url.searchParams; // удаляем все utm_* [...p.keys()].forEach((k) => { if (k.startsWith("utm_") || TRACKING_PARAMS.has(k)) p.delete(k); }); // если остались пустые search/hash — норм url.search = p.toString() ? "?" + p.toString() : ""; return url.toString(); } catch { return raw; } } function attachCopyButtons() { const links = document.querySelectorAll("a[href]:not([data-clean-processed])"); for (const a of links) { a.setAttribute("data-clean-processed", "1"); // переписываем href на чистый (не меняем видимую надпись) const cleaned = cleanUrl(a.href); if (cleaned && cleaned !== a.href) a.href = cleaned; // не добавляем кнопку в навигации, меню и т.п. (сокращаем шум) const rect = a.getBoundingClientRect(); const isTiny = rect.width < 20 || rect.height < 12; if (isTiny) continue; const btn = document.createElement("button"); btn.type = "button"; btn.textContent = "🔗Copy"; btn.title = "Скопировать чистый URL"; btn.className = BUTTON_CLASS; btn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); const url = cleanUrl(a.href); try { if (typeof GM_setClipboard === "function") { GM_setClipboard(url, { type: "text", mimetype: "text/plain" }); } else { navigator.clipboard?.writeText(url); } flash(a, "Скопировано!"); } catch { flash(a, "Не удалось скопировать"); } }); // обертка для позиционирования const wrapper = document.createElement("span"); wrapper.style.position = "relative"; a.parentNode.insertBefore(wrapper, a); wrapper.appendChild(a); wrapper.appendChild(btn); } } function flash(el, msg) { const note = document.createElement("span"); note.textContent = msg; note.style.cssText = ` position:absolute; z-index: 999999; top:-1.6em; right:0; padding:2px 6px; border-radius:6px; font:12px/1.2 system-ui, sans-serif; background: rgba(0,0,0,.75); color:#fff; pointer-events:none; `; el.closest("span")?.appendChild(note); setTimeout(() => note.remove(), 900); } // меню if (typeof GM_registerMenuCommand === "function") { GM_registerMenuCommand("Очистить все ссылки сейчас", () => { document.querySelectorAll("a[href]").forEach((a) => (a.href = cleanUrl(a.href))); alert("Готово: ссылки очищены."); }); GM_registerMenuCommand("Скопировать чистый URL этой страницы", () => { const cleaned = cleanUrl(location.href); if (typeof GM_setClipboard === "function") { GM_setClipboard(cleaned, { type: "text", mimetype: "text/plain" }); } else { navigator.clipboard?.writeText(cleaned); } alert("Скопировано:\n" + cleaned); }); } // стили кнопки const css = document.createElement("style"); css.textContent = ` .${BUTTON_CLASS}{ margin-left:6px; padding:2px 6px; border:1px solid rgba(0,0,0,.2); border-radius:6px; background:#fff; cursor:pointer; font:12px/1 system-ui,sans-serif; box-shadow:0 1px 2px rgba(0,0,0,.05); } .${BUTTON_CLASS}:hover{ background:#f5f5f5 } `; document.documentElement.appendChild(css); // первичный прогон и наблюдатель мутаций (для SPA/ленивой подгрузки) attachCopyButtons(); const mo = new MutationObserver(() => attachCopyButtons()); mo.observe(document.documentElement, { subtree: true, childList: true }); })();