显示网站 IP 和 ISP 信息

显示当前网站的 IP、ISP 信息,支持自定义 DNS 查询并可拖动窗口。当前脚本由 ChatGpt 生成

// ==UserScript==
// @name         显示网站 IP 和 ISP 信息
// @namespace    https://tools.0x5c0f.cc/
// @version      1.5.0
// @description  显示当前网站的 IP、ISP 信息,支持自定义 DNS 查询并可拖动窗口。当前脚本由 ChatGpt 生成
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      tools.0x5c0f.cc
// ==/UserScript==

(function () {
    'use strict';

    const API_BASE = '//tools.0x5c0f.cc/rz/api/v1/utils/resolve';
    const DEFAULT_DNS = '8.8.8.8';
    const STORAGE_KEYS = {
        LEFT: 'panel_left',
        TOP: 'panel_top',
        DNS: 'dns_server',
    };

    const domain = location.hostname;

    function getStoredDNS() {
        return GM_getValue(STORAGE_KEYS.DNS, DEFAULT_DNS);
    }

    function savePosition(left, top) {
        GM_setValue(STORAGE_KEYS.LEFT, left + 'px');
        GM_setValue(STORAGE_KEYS.TOP, top + 'px');
    }

    function createPanel() {
        const panel = document.createElement('div');
        panel.id = 'ip-info-panel';
        panel.style.position = 'fixed';
        panel.style.zIndex = '9999999';
        panel.style.background = 'rgba(0,0,0,0.5)';
        panel.style.color = 'white';
        panel.style.padding = '10px';
        panel.style.borderRadius = '8px';
        panel.style.fontSize = '14px';
        panel.style.maxWidth = '300px';
        panel.style.maxHeight = '60vh';
        panel.style.overflowY = 'auto';
        panel.style.cursor = 'move';
        panel.style.userSelect = 'none';

        const left = GM_getValue(STORAGE_KEYS.LEFT, '10px');
        const top = GM_getValue(STORAGE_KEYS.TOP, '10px');
        panel.style.left = left;
        panel.style.top = top;

        document.body.appendChild(panel);
        enableDrag(panel);
        return panel;
    }

    function enableDrag(el) {
        let offsetX = 0, offsetY = 0, dragging = false;

        el.addEventListener('mousedown', e => {
            if (e.target.tagName === 'INPUT') return;
            dragging = true;
            offsetX = e.clientX - el.offsetLeft;
            offsetY = e.clientY - el.offsetTop;
        });

        document.addEventListener('mousemove', e => {
            if (!dragging) return;
            el.style.left = `${e.clientX - offsetX}px`;
            el.style.top = `${e.clientY - offsetY}px`;
        });

        document.addEventListener('mouseup', () => {
            if (!dragging) return;
            dragging = false;
            savePosition(el.offsetLeft, el.offsetTop);
        });
    }

    function fetchIPInfo(dnsServer, callback) {
        const url = `${API_BASE}?domain=${domain}&dns_server=${encodeURIComponent(dnsServer)}&show_ipinfo=false`;

        GM_xmlhttpRequest({
            method: 'GET',
            url,
            onload: (res) => {
                try {
                    const data = JSON.parse(res.responseText);
                    callback(null, data);
                } catch (err) {
                    callback(err);
                }
            },
            onerror: (err) => callback(err)
        });
    }

    function renderPanel(panel, data, dnsServer) {
        let html = `<strong>${data.domain}</strong><br>`;
        html += `DNS: ${dnsServer}<br>`;
        html += `IPv4: ${data.ipv4_addresses.length} | IPv6: ${data.ipv6_addresses.length}<br>`;
        html += `<em id="toggle-detail" style="cursor:pointer;">(点击展开详情)</em>`;

        let detailHTML = '<div id="ip-info-detail" style="display:none; margin-top:8px; line-height:1.5; padding:6px; border-top:1px solid #ccc; background-color: rgba(255,255,255,0.05); word-break: break-all;">';
        data.ipv4_addresses.forEach(ip => {
            detailHTML += `<div><strong>IPv4</strong>: ${ip}</div>`;
        });
        data.ipv6_addresses.forEach(ip => {
            detailHTML += `<div><strong>IPv6</strong>: ${ip}</div>`;
        });
        data.ipinfo.forEach(info => {
            detailHTML += `<div style="margin-top:6px; padding:4px 0; border-bottom:1px solid #666;">`;
            detailHTML += `<div><strong>IP</strong>: ${info.ip}</div>`;
            if (info.org) detailHTML += `<div><strong>ISP</strong>: ${info.org}</div>`;
            if (info.country) detailHTML += `<div><strong>国家</strong>: ${info.country}</div>`;
            detailHTML += `</div>`;
        });
        detailHTML += '</div>';

        html += detailHTML;
        html += `<input id="dns-input" type="text" placeholder="DNS 服务器 (默认 8.8.8.8)" value="${dnsServer}" style="width:100%;margin-top:8px;padding:4px;border:none;border-bottom:1px solid white;background:transparent;color:white;outline:none;box-sizing:border-box;">`;

        panel.innerHTML = html;

        document.getElementById('dns-input').addEventListener('change', e => {
            const newDns = e.target.value || DEFAULT_DNS;
            GM_setValue(STORAGE_KEYS.DNS, newDns);
            fetchIPInfo(newDns, (err, newData) => {
                if (!err) renderPanel(panel, newData, newDns);
            });
        });

        document.getElementById('toggle-detail').addEventListener('click', () => {
            const detail = document.getElementById('ip-info-detail');
            const toggle = document.getElementById('toggle-detail');
            const showing = detail.style.display !== 'none';
            detail.style.display = showing ? 'none' : 'block';
            toggle.textContent = showing ? '(点击展开详情)' : '(点击关闭详情)';
        });
    }

    const panel = createPanel();
    const storedDNS = getStoredDNS();
    fetchIPInfo(storedDNS, (err, data) => {
        if (err || !data) {
            panel.innerHTML = '<strong>获取 IP 信息失败</strong>';
            return;
        }
        renderPanel(panel, data, storedDNS);
    });
})();