Adds 'no access' or safety warnings to Google search results.
当前为
// ==UserScript==
// @name Google Search Link Checker
// @namespace http://tampermonkey.net/
// @version 1.3
// @description Adds 'no access' or safety warnings to Google search results.
// @author Bui Quoc Dung
// @match https://www.google.com/search*
// @grant GM_xmlhttpRequest
// @connect *
// ==/UserScript==
(function () {
'use strict';
async function checkLinkStatus(url) {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: "HEAD",
url,
timeout: 5000,
onload: res => {
resolve((res.status >= 200 && res.status < 400) || [0, 403, 405, 429].includes(res.status));
},
onerror: (error) => {
resolve(!!(error && error.status === 0 && error.finalUrl));
},
ontimeout: () => {
resolve(false);
}
});
});
}
async function checkSafetyStatus(url) {
try {
const hostname = new URL(url).hostname;
const apiUrl = `https://check.getsafeonline.org/check/${hostname}?inputUrl=${hostname}`;
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: "GET",
url: apiUrl,
timeout: 10000,
onload: res => {
if (res.status >= 200 && res.status < 300) {
const parser = new DOMParser();
const doc = parser.parseFromString(res.responseText, 'text/html');
const warningElement = doc.querySelector('div.font-light.text-2xl.mt-6 span.text-primary-red.font-bold');
if (warningElement) {
resolve(warningElement.textContent.trim());
} else {
resolve(null);
}
} else {
resolve(null);
}
},
onerror: () => resolve(null),
ontimeout: () => resolve(null)
});
});
} catch (e) {
console.error("Could not parse URL for safety check:", url, e);
return Promise.resolve(null);
}
}
function processLink(link) {
if (!link || link.dataset.statusChecked) return;
link.dataset.statusChecked = 'true';
const url = link.href;
if (!url || url.startsWith('javascript:') || url.startsWith('#')) return;
const container = link.closest('.MjjYud');
if (!container || container.querySelector('.access-safety-info')) return;
const description = container.querySelector('.VwiC3b'); // description
if (!description) return;
const infoDiv = document.createElement('div');
infoDiv.className = 'access-safety-info';
infoDiv.style.marginTop = '4px';
infoDiv.style.fontSize = '1em';
infoDiv.style.display = 'flex';
infoDiv.style.flexWrap = 'wrap';
infoDiv.style.gap = '8px';
description.parentElement.appendChild(infoDiv);
checkLinkStatus(url).then(accessible => {
if (!accessible) {
const tag = document.createElement('span');
tag.textContent = '[no access]';
tag.style.color = 'orange';
infoDiv.appendChild(tag);
}
});
checkSafetyStatus(url).then(safetyText => {
if (safetyText) {
const tag = document.createElement('span');
tag.textContent = `[${safetyText}]`;
tag.style.color = 'red';
infoDiv.appendChild(tag);
}
});
}
let scanTimeoutId = null;
function scanLinksDebounced() {
clearTimeout(scanTimeoutId);
scanTimeoutId = setTimeout(() => {
document.querySelectorAll('div.MjjYud a:has(h3)').forEach(processLink); // all links
}, 300);
}
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
for (const node of m.addedNodes) {
if (node.nodeType === 1 &&
(node.querySelector('div.MjjYud a:has(h3), ') ||
node.matches('.MjjYud'))) {
scanLinksDebounced();
return;
}
}
}
});
const container = document.getElementById('center_col') || document.getElementById('rcnt') || document.body;
observer.observe(container, { childList: true, subtree: true });
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', () => setTimeout(scanLinksDebounced, 1000));
} else {
setTimeout(scanLinksDebounced, 1000);
}
})();