/** * 隐私政策页面 - 动态展示访客信息 * 使用腾讯地图IP定位API + ipapi.co获取网络信息 * 使用navigator.userAgent解析设备信息 */ (function () { 'use strict'; const CONFIG = { TENCENT_KEY: 'FXNBZ-T5DCB-ABWUU-NXPTT-4AXGH-IUBAQ', CACHE_KEY: 'privacy_ip_cache', CACHE_TTL: 1000 * 60 * 60, // 1小时 }; // ========== 页面检测 ========== function isPrivacyPage() { const p = window.location.pathname; return p === '/privacy/' || p === '/privacy' || p === '/privacy/index.html'; } // ========== 缓存 ========== function getFromCache() { try { const raw = localStorage.getItem(CONFIG.CACHE_KEY); if (!raw) return null; const { data, ts } = JSON.parse(raw); if (Date.now() - ts > CONFIG.CACHE_TTL) { localStorage.removeItem(CONFIG.CACHE_KEY); return null; } return data; } catch { return null; } } function setCache(data) { try { localStorage.setItem(CONFIG.CACHE_KEY, JSON.stringify({ data, ts: Date.now() })); } catch { /* quota exceeded, ignore */ } } // ========== 腾讯地图IP定位 (JSONP) ========== function fetchTencentIP() { return new Promise((resolve, reject) => { const cb = `_privacy_qqmap_${Date.now()}`; const script = document.createElement('script'); script.src = `https://apis.map.qq.com/ws/location/v1/ip?key=${encodeURIComponent(CONFIG.TENCENT_KEY)}&output=jsonp&callback=${cb}`; window[cb] = (res) => { cleanup(); if (res.status !== 0) return reject(new Error(res.message)); const { ad_info } = res.result; resolve({ ip: res.result.ip || '', country: ad_info.nation || '', province: ad_info.province || '', city: ad_info.city || '', }); }; script.onerror = () => { cleanup(); reject(new Error('JSONP request failed')); }; function cleanup() { document.head.removeChild(script); delete window[cb]; } document.head.appendChild(script); }); } // ========== 运营商检测 (ipapi.co, HTTPS) ========== function fetchISP() { return fetch('https://ipapi.co/json/') .then(r => r.json()) .then(d => d.org || '获取失败'); } // ========== UA解析 ========== function parseUserAgent() { const ua = navigator.userAgent; return { os: detectOS(ua), browser: detectBrowser(ua) }; } function detectOS(ua) { if (/Windows NT 10\.0/.test(ua)) return 'Windows 10/11'; if (/Windows NT 6\.3/.test(ua)) return 'Windows 8.1'; if (/Windows NT 6\.2/.test(ua)) return 'Windows 8'; if (/Windows NT 6\.1/.test(ua)) return 'Windows 7'; if (/Windows/.test(ua)) return 'Windows'; const macMatch = ua.match(/Mac OS X ([\d_]+)/); if (macMatch) return 'macOS ' + macMatch[1].replace(/_/g, '.'); const androidMatch = ua.match(/Android ([\d.]+)/); if (androidMatch) return 'Android ' + androidMatch[1]; const iosMatch = ua.match(/OS ([\d_]+)/); if (/iPhone|iPad|iPod/.test(ua) && iosMatch) return 'iOS ' + iosMatch[1].replace(/_/g, '.'); if (/Linux/.test(ua)) return 'Linux'; return '未知'; } function detectBrowser(ua) { if (/Edg\//.test(ua)) return 'Edge ' + (ua.match(/Edg\/([\d.]+)/) || [])[1]; if (/OPR\//.test(ua)) return 'Opera ' + (ua.match(/OPR\/([\d.]+)/) || [])[1]; if (/Chrome\//.test(ua)) return 'Chrome ' + (ua.match(/Chrome\/([\d.]+)/) || [])[1]; if (/Firefox\//.test(ua)) return 'Firefox ' + (ua.match(/Firefox\/([\d.]+)/) || [])[1]; if (/Safari\//.test(ua)) return 'Safari ' + (ua.match(/Version\/([\d.]+)/) || [])[1]; return '未知'; } // ========== 表格操作 ========== function findPrivacyTable() { const tables = document.querySelectorAll('#article-container table'); for (const table of tables) { const rows = table.querySelectorAll('tbody tr'); for (const row of rows) { const firstCell = row.querySelector('td:first-child'); if (firstCell && firstCell.textContent.trim() === 'IP地址') { return table; } } } return null; } function getCellByLabel(table, label) { const rows = table.querySelectorAll('tbody tr'); for (const row of rows) { const firstCell = row.querySelector('td:first-child'); if (firstCell && firstCell.textContent.trim() === label) { return row.querySelector('td:nth-child(2)'); } } return null; } function fillCell(table, label, value) { const cell = getCellByLabel(table, label); if (!cell) return; cell.classList.remove('privacy-cell-loading'); cell.textContent = value || '获取失败'; } function addLoadingClass(table) { const labels = ['IP地址', '国家', '省份', '城市', '运营商', '操作系统', '浏览器']; labels.forEach(label => { const cell = getCellByLabel(table, label); if (cell) cell.classList.add('privacy-cell-loading'); }); } // ========== 主逻辑 ========== function init() { if (!isPrivacyPage()) return; const table = findPrivacyTable(); if (!table) return; addLoadingClass(table); const cached = getFromCache(); if (cached) { fillAll(table, cached); return; } // 并行获取数据 Promise.allSettled([fetchTencentIP(), fetchISP()]).then(([tencentRes, ispRes]) => { const tencent = tencentRes.status === 'fulfilled' ? tencentRes.value : {}; const isp = ispRes.status === 'fulfilled' ? ispRes.value : '获取失败'; const ua = parseUserAgent(); const data = { ip: tencent.ip || '获取失败', country: tencent.country || '获取失败', province: tencent.province || '获取失败', city: tencent.city || '获取失败', isp: isp, os: ua.os, browser: ua.browser, }; fillAll(table, data); setCache(data); }); } function fillAll(table, data) { fillCell(table, 'IP地址', data.ip); fillCell(table, '国家', data.country); fillCell(table, '省份', data.province); fillCell(table, '城市', data.city); fillCell(table, '运营商', data.isp); fillCell(table, '操作系统', data.os); fillCell(table, '浏览器', data.browser); } // ========== 初始化(含Pjax兼容) ========== function safeInit() { // 延迟执行,等待主题的 addTableWrap() 完成 setTimeout(init, 100); } document.addEventListener('DOMContentLoaded', safeInit); document.addEventListener('pjax:complete', safeInit); })();