// ==UserScript== // @name 网址纠错2.0 - 反抗银狐假网站 // @namespace https://xiaote.data.blog/wzjc-pro // @version 2.1 // @description 全新升级,除了错误网址纠正,还新增了搜索引擎官网推荐、虚假官网/下载站提醒,阻止假网站 // @author 小特_XT // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @connect * // @license MIT // ==/UserScript== (function () { 'use strict'; // ========== UI 常量(对齐模板) ========== const PRIMARY_COLOR = '#ff6a00'; const ERROR_COLOR = '#e74c3c'; const BORDER_COLOR = '#dddddd'; const FONT_FAMILY = '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif'; // ========== 存储键 ========== const WHITELIST_KEY = 'urlCorrectionWhitelist'; const KEYWORD_MAP_KEY = 'searchKeywordMap'; const SEARCH_DISMISSED_KEY = 'searchDismissed'; const FAKE_DISMISSED_KEY = 'fakeSiteDismissed'; // ========== 默认关键词映射(知名软件库,持续完善) ========== const DEFAULT_KEYWORD_MAP = { // 影音娱乐 "酷我音乐": { triggers: ["酷我音乐","酷我","kuwo"], url: "https://www.kuwo.cn/" }, "QQ音乐": { triggers: ["qq音乐","qqmusic","qq音乐下载"], url: "https://y.qq.com/" }, "酷狗音乐": { triggers: ["酷狗音乐","酷狗","kugou"], url: "https://www.kugou.com/" }, "网易云音乐": { triggers: ["网易云音乐","网易云","cloudmusic"], url: "https://music.163.com/" }, "汽水音乐": { triggers: ["汽水音乐","qishui","汽水音乐下载"], url: "https://www.qishui.com/" }, "抖音": { triggers: ["抖音","douyin","tiktok国内"], url: "https://www.douyin.com/" }, "快手": { triggers: ["快手","kuaishou"], url: "https://www.kuaishou.com/" }, "哔哩哔哩": { triggers: ["哔哩哔哩","b站","bilibili","b站下载"], url: "https://www.bilibili.com/" }, "腾讯视频": { triggers: ["腾讯视频","v.qq","腾讯视频下载"], url: "https://v.qq.com/" }, "爱奇艺": { triggers: ["爱奇艺","iqiyi","爱奇艺下载"], url: "https://www.iqiyi.com/" }, "优酷": { triggers: ["优酷","youku","优酷下载"], url: "https://www.youku.com/" }, "芒果TV": { triggers: ["芒果tv","mgtv"], url: "https://www.mgtv.com/" }, "西瓜视频": { triggers: ["西瓜视频","ixigua"], url: "https://www.ixigua.com/" }, "斗鱼": { triggers: ["斗鱼","douyu"], url: "https://www.douyu.com/" }, "虎牙": { triggers: ["虎牙","huya"], url: "https://www.huya.com/" }, // 社交/通讯 "微信": { triggers: ["微信","wechat","weixin","微信下载"], url: "https://weixin.qq.com/" }, "QQ": { triggers: ["qq","腾讯qq","qq下载"], url: "https://im.qq.com/" }, "微博": { triggers: ["微博","weibo","新浪微博"], url: "https://weibo.com/" }, "知乎": { triggers: ["知乎","zhihu"], url: "https://www.zhihu.com/" }, "钉钉": { triggers: ["钉钉","dingtalk","钉钉下载"], url: "https://www.dingtalk.com/" }, "企业微信": { triggers: ["企业微信","wecom"], url: "https://work.weixin.qq.com/" }, "飞书": { triggers: ["飞书","feishu","lark"], url: "https://www.feishu.cn/" }, // 购物/生活 "淘宝": { triggers: ["淘宝","taobao","淘宝网"], url: "https://www.taobao.com/" }, "京东": { triggers: ["京东","jd","京东商城"], url: "https://www.jd.com/" }, "拼多多": { triggers: ["拼多多","pdd","拼多多商城"], url: "https://www.pinduoduo.com/" }, "美团": { triggers: ["美团","meituan","美团外卖"], url: "https://www.meituan.com/" }, "饿了么": { triggers: ["饿了么","eleme"], url: "https://www.ele.me/" }, "高德地图": { triggers: ["高德地图","amap","高德导航"], url: "https://www.amap.com/" }, "百度地图": { triggers: ["百度地图","map.baidu"], url: "https://map.baidu.com/" }, // 工具/效率 "百度网盘": { triggers: ["百度网盘","百度云","pan.baidu"], url: "https://pan.baidu.com/" }, "阿里云盘": { triggers: ["阿里云盘","aliyundrive"], url: "https://www.aliyundrive.com/" }, "WPS Office": { triggers: ["wps","wps下载","wps office"], url: "https://www.wps.cn/" }, "有道云笔记": { triggers: ["有道云笔记","note.youdao"], url: "https://note.youdao.com/" }, "印象笔记": { triggers: ["印象笔记","evernote"], url: "https://www.yinxiang.com/" }, "搜狗输入法": { triggers: ["搜狗输入法","sogou输入法"], url: "https://pinyin.sogou.com/" }, "讯飞输入法": { triggers: ["讯飞输入法","iflytek输入法"], url: "https://srf.xunfei.cn/" }, // 安全/浏览器 "火绒安全": { triggers: ["火绒","火绒安全","huorong"], url: "https://www.huorong.cn/" }, "360安全卫士": { triggers: ["360安全卫士","360安全"], url: "https://weishi.360.cn/" }, "腾讯电脑管家": { triggers: ["腾讯电脑管家","电脑管家"], url: "https://guanjia.qq.com/" }, "Chrome": { triggers: ["chrome","谷歌浏览器","chrome下载"], url: "https://www.google.cn/intl/zh-CN/chrome/" }, "Edge": { triggers: ["edge","microsoft edge"], url: "https://www.microsoft.com/edge" }, "Firefox": { triggers: ["firefox","火狐浏览器"], url: "https://www.mozilla.org/firefox/" }, // 设计/创意 "Photoshop": { triggers: ["ps","photoshop","ps下载"], url: "https://www.adobe.com/products/photoshop.html" }, "Illustrator": { triggers: ["ai","illustrator"], url: "https://www.adobe.com/products/illustrator.html" }, "Premiere Pro": { triggers: ["pr","premiere"], url: "https://www.adobe.com/products/premiere.html" }, "After Effects": { triggers: ["ae","after effects"], url: "https://www.adobe.com/products/aftereffects.html" }, "AutoCAD": { triggers: ["autocad","cad"], url: "https://www.autodesk.com/products/autocad" }, "Blender": { triggers: ["blender","blender下载"], url: "https://www.blender.org/" }, // 开发/IT "Visual Studio Code": { triggers: ["vscode","visual studio code"], url: "https://code.visualstudio.com/" }, "Git": { triggers: ["git","git下载","git-scm"], url: "https://git-scm.com/" }, "Node.js": { triggers: ["nodejs","node","node下载"], url: "https://nodejs.org/" }, "Python": { triggers: ["python","python下载","python官网"], url: "https://www.python.org/" }, "MySQL": { triggers: ["mysql","mysql下载"], url: "https://www.mysql.com/" }, "IntelliJ IDEA": { triggers: ["idea","intellij idea"], url: "https://www.jetbrains.com/idea/" }, "PyCharm": { triggers: ["pycharm","pycharm下载"], url: "https://www.jetbrains.com/pycharm/" }, "GitHub": { triggers: ["github","github官网"], url: "https://github.com/" }, // 工具箱 "XT工具箱": { triggers: ["XT工具箱","xttool"], url: "https://xiaote.data.blog/tool" }, }; // ========== 错误网址纠正映射 ========== const URL_CORRECTION_MAP = { "bilibil.com": ["https://bilibili.com","哔哩哔哩"], "douying.com": ["https://douyin.com","抖音"], "baidv.com": ["https://baidu.com","百度"], "ta0bao.com": ["https://taobao.com","淘宝"], "jdl.cn": ["https://jd.com","京东"], "wei6o.com": ["https://weibo.com","微博"], "zhih1.com": ["https://zhihu.com","知乎"], "tout1ao.com": ["https://toutiao.com","今日头条"], "chrome.zuitie.cn": ["https://www.google.cn/intl/zh-CN/chrome/","Chrome官方"], "https://www.goguge.com/": ["https://www.google.cn/intl/zh-CN/chrome/","Chrome官方"], "https://google-chrom.cn/": ["https://www.google.cn/intl/zh-CN/chrome/","Chrome官方"], "www.chrome.net.cn": ["https://www.google.cn/intl/zh-CN/chrome/","Chrome官方"] }; // ========== 数据读写 ========== const loadJSON = (key, fallback) => { try { return JSON.parse(GM_getValue(key, JSON.stringify(fallback))); } catch { return fallback; } }; const saveJSON = (key, data) => GM_setValue(key, JSON.stringify(data)); const loadWhitelist = () => loadJSON(WHITELIST_KEY, []); const saveWhitelist = (list) => saveJSON(WHITELIST_KEY, list); const loadKeywordMap = () => loadJSON(KEYWORD_MAP_KEY, DEFAULT_KEYWORD_MAP); const saveKeywordMap = (map) => saveJSON(KEYWORD_MAP_KEY, map); const getDismissed = (key) => { const obj = loadJSON(key, {}); const today = new Date().toDateString(); const cleaned = {}; for (const [k, v] of Object.entries(obj)) { if (v === today) cleaned[k] = v; } return cleaned; }; const setDismissed = (key, item) => { const obj = getDismissed(key); obj[item] = new Date().toDateString(); saveJSON(key, obj); }; const isDismissed = (key, item) => getDismissed(key)[item] !== undefined; // ========== 搜索引擎检测 ========== const isSearchEngine = () => { const host = location.hostname.replace(/^www\./, ''); const engines = [ 'baidu.com', 'm.baidu.com', 'bing.com', 'cn.bing.com', 'sogou.com', 'm.sogou.com', 'so.com', 'm.so.com', 'google.com', 'google.com.hk', 'google.co.jp', 'google.co.uk', 'google.co.in', 'search.yahoo.com', 'search.yahoo.co.jp', 'duckduckgo.com', 'yandex.com', 'yandex.ru' ]; return engines.some(e => host === e || host.endsWith('.' + e)); }; const getSearchQuery = () => { const url = new URL(location.href); const host = url.hostname; if (host.includes('baidu.com')) return url.searchParams.get('wd') || url.searchParams.get('word'); if (host.includes('bing.com') || host.includes('google.com')) return url.searchParams.get('q'); if (host.includes('sogou.com')) return url.searchParams.get('query'); if (host.includes('so.com')) return url.searchParams.get('q'); if (host.includes('yahoo.com')) return url.searchParams.get('p'); if (host.includes('duckduckgo.com')) return url.searchParams.get('q'); if (host.includes('yandex')) return url.searchParams.get('text'); return null; }; // ========== 关键词匹配 ========== const matchKeyword = (text, keywordMap) => { if (!text) return null; const lower = text.toLowerCase().trim(); for (const [name, data] of Object.entries(keywordMap)) { for (const trigger of data.triggers) { if (lower.includes(trigger.toLowerCase())) { return { name, url: data.url }; } } } return null; }; // ========== 通用横幅生成(16dp 内边距,简约) ========== const showBanner = (options) => { const { id, contentHtml } = options; if (document.getElementById(id)) return; const banner = document.createElement('div'); banner.id = id; banner.innerHTML = `
`; document.body.prepend(banner); return banner; }; // 注入样式(高特异性,防冲突,16dp 内边距) GM_addStyle(` #search-banner, #fake-banner { position: fixed; top: 0; left: 0; right: 0; z-index: 2147483647; } .xt-banner-inner { display: flex; align-items: center; justify-content: space-between; padding: 16px 16px; background: #ffffff; border-bottom: 2px solid ${PRIMARY_COLOR}; font-family: ${FONT_FAMILY}; font-size: 14px; line-height: 1.5; color: #333333; box-sizing: border-box; width: 100%; max-width: 100vw; } @media (prefers-color-scheme: dark) { .xt-banner-inner { background: #1e1e1e; color: #e0e0e0; } } .xt-banner-text { flex: 1 1 auto; min-width: 0; margin-right: 16px; word-break: break-word; } .xt-banner-link { color: #3498db; text-decoration: none; font-weight: 500; } .xt-banner-actions { display: flex; align-items: center; gap: 8px; white-space: nowrap; flex-shrink: 0; } .xt-btn { display: inline-block; padding: 6px 16px; border-radius: 16px; font-size: 14px; font-weight: 500; cursor: pointer; text-decoration: none; transition: background-color 0.2s; border: 1px solid transparent; background: transparent; line-height: 1.2; } .xt-btn-primary { color: ${PRIMARY_COLOR}; border: 1px solid ${PRIMARY_COLOR}; } .xt-btn-primary:hover { background: rgba(255, 106, 0, 0.08); } .xt-btn-close { color: #666666; border: 1px solid ${BORDER_COLOR}; margin-left: 4px; } .xt-btn-close:hover { background: #f0f0f0; } @media (prefers-color-scheme: dark) { .xt-btn-close { color: #aaaaaa; } .xt-btn-close:hover { background: #3a3a3a; } } .xt-warning { color: ${ERROR_COLOR}; font-size: 12px; margin-top: 4px; line-height: 1.4; } @media (max-width: 600px) { .xt-banner-inner { flex-direction: column; align-items: stretch; padding: 16px; } .xt-banner-text { margin-right: 0; margin-bottom: 10px; width: 100%; } .xt-banner-actions { justify-content: flex-end; width: 100%; } .xt-btn { padding: 8px 16px; font-size: 14px; } } `); // ========== 1. 搜索引擎官网推荐 ========== const checkSearchRecommend = () => { if (!isSearchEngine()) return; const query = getSearchQuery(); if (!query) return; const map = loadKeywordMap(); const match = matchKeyword(query, map); if (match && !isDismissed(SEARCH_DISMISSED_KEY, match.name)) { const id = 'search-banner'; const html = ` `; const banner = showBanner({ id, contentHtml: html }); if (banner) { banner.querySelector('#search-close').onclick = () => { banner.remove(); setDismissed(SEARCH_DISMISSED_KEY, match.name); }; } } }; // ========== 2. 虚假官网/下载站检测(过滤搜索引擎) ========== const checkFakeOfficial = () => { if (isSearchEngine()) return; const title = document.title; if (!title || isDismissed(FAKE_DISMISSED_KEY, location.hostname)) return; const map = loadKeywordMap(); const match = matchKeyword(title, map); if (!match) return; const correctHost = new URL(match.url).hostname.replace(/^www\./, ''); const currentHost = location.hostname.replace(/^www\./, ''); if (correctHost === currentHost) return; const id = 'fake-banner'; const html = ` `; const banner = showBanner({ id, contentHtml: html }); if (banner) { banner.querySelector('#fake-close').onclick = () => { banner.remove(); setDismissed(FAKE_DISMISSED_KEY, location.hostname); }; } }; // ========== 3. 原错误网址纠正 ========== const checkUrlCorrection = () => { const currentUrl = location.href; const whitelist = loadWhitelist(); const cancelled = sessionStorage.getItem('urlCorrectCancel') === currentUrl; if (cancelled) return; for (const [wrong, [correct, name]] of Object.entries(URL_CORRECTION_MAP)) { if (currentUrl.includes(wrong)) { const isWhite = whitelist.some(w => { try { return new URL(w).href === new URL(currentUrl).href; } catch { return false; } }); if (!isWhite && confirm(`您可能想要访问的是 ${name}(${correct}),是否需要跳转?`)) { location.href = correct; } else { sessionStorage.setItem('urlCorrectCancel', currentUrl); } break; } } }; // ========== TXT 配置导入函数 ========== const parseTxtConfig = (text) => { const lines = text.split(/\r?\n/); const newMap = {}; for (let line of lines) { line = line.trim(); if (!line || line.startsWith('#')) continue; // 忽略空行和注释 // 格式:软件名,触发词1|触发词2,官网URL const parts = line.split(','); if (parts.length < 3) continue; const name = parts[0].trim(); const triggersStr = parts[1].trim(); const url = parts.slice(2).join(',').trim(); // 支持 URL 中可能存在的逗号 if (!name || !triggersStr || !url) continue; const triggers = triggersStr.split('|').map(t => t.trim()).filter(Boolean); if (triggers.length === 0) continue; newMap[name] = { triggers, url }; } return newMap; }; const importFromTxt = (txtContent) => { try { const imported = parseTxtConfig(txtContent); if (Object.keys(imported).length === 0) return false; const currentMap = loadKeywordMap(); const merged = { ...currentMap, ...imported }; // 导入的覆盖同名项 saveKeywordMap(merged); return Object.keys(imported).length; } catch (e) { console.error('导入配置失败:', e); return false; } }; // ========== 设置面板(增加导入功能) ========== const openSettings = () => { const whitelist = loadWhitelist(); const map = loadKeywordMap(); const html = `| 名称 | 触发词 | 官网 | 操作 |
|---|---|---|---|
| ${n} | ${d.triggers.join(',')} | ${d.url} |