Greasy Fork is available in English.

显示网站ip

在所有网站右下角显示ip,增加鼠标穿透,通过谷歌dns查询ip,兼容ipv4,ipv6,通过百度查询归属地,保存查询成功的ip与归属地,在最新查询失败时获取,在右下角显示ipv4/ipv6与归属地

  1. // ==UserScript==
  2. // @name 显示网站ip
  3. // @namespace http://tampermonkey.net/
  4. // @version 2025-03-02
  5. // @description 在所有网站右下角显示ip,增加鼠标穿透,通过谷歌dns查询ip,兼容ipv4,ipv6,通过百度查询归属地,保存查询成功的ip与归属地,在最新查询失败时获取,在右下角显示ipv4/ipv6与归属地
  6. // @author Rakiwine
  7. // @match *://*/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
  9. // @grant GM_getResourceText
  10. // @grant GM_addStyle
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_log
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. (function () {
  19. 'use strict';
  20. let button_style = {
  21. backgroundColor: 'rgba(0, 0, 0, 0.5)',// 黑色半透明
  22. color: 'white',// 文字颜色
  23. textShadow: '1px 1px 3px rgba(0, 0, 0, 0.7)', // 增加文字阴影,提升可读性
  24. border: 'none',// 去掉边框
  25. borderRadius: '16px',// 圆角
  26. height: '50px',// 高度
  27. padding: '0 20px',// 左右内边距
  28. fontSize: '16px',// 字体大小
  29. cursor: 'pointer',// 鼠标悬停时显示手型光标
  30. outline: 'none',// 去掉焦点时的轮廓
  31. position: 'fixed',// 固定定位
  32. bottom: '10px',// 距离顶部 10px
  33. right: '10px',// 距离右边 10px
  34. zIndex: '1000',// 确保按钮在最上层
  35. pointerEvents: 'none',// 点击穿透
  36. }
  37.  
  38. // 显示加载提示
  39. let buttonId = "ip_button_id_" + generateRandomID(10);
  40. let ip_button = document.createElement('button');
  41. ip_button.id = buttonId; // 设置按钮的 ID 为唯一标识符
  42. Object.assign(ip_button.style, button_style);
  43. ip_button.textContent = '加载中...';
  44. document.body.appendChild(ip_button);
  45.  
  46. // 生成随机ID
  47. function generateRandomID(length) {
  48. const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$';
  49. let result = '';
  50.  
  51. for (let i = 0; i < length; i++) {
  52. result += characters.charAt(Math.floor(Math.random() * characters.length));
  53. }
  54.  
  55. return result;
  56. }
  57.  
  58. // 跨域 获取 IP 地址
  59. function getIP(domain) {
  60. return new Promise((resolve, reject) => {
  61. GM_xmlhttpRequest({
  62. method: 'GET',
  63. url: `https://dns.google/resolve?name=${domain}`,
  64. onload: function (response) {
  65. const data = JSON.parse(response.responseText);
  66.  
  67. if (data.Comment) {
  68. resolve(data.Comment)
  69. } else {
  70. reject("失败72");
  71. }
  72. },
  73. onerror: function (error) {
  74. reject("失败76");
  75. }
  76. });
  77. });
  78. }
  79.  
  80. // 跨域 获取归属地
  81. function getLocation(ip, ipv6) {
  82. return new Promise((resolve, reject) => {
  83. ip = encodeURIComponent(ip);
  84. let url;
  85.  
  86. if (ipv6) {
  87. url = `https://qifu-api.baidubce.com/ip/geo/v1/ipv6/district?ip=${ip}`
  88. } else {
  89. url = `https://qifu-api.baidubce.com/ip/geo/v1/district?ip=${ip}`
  90. }
  91.  
  92. GM_xmlhttpRequest({
  93. method: 'GET',
  94. url: url,
  95. onload: function (response) {
  96. try {
  97. const data = JSON.parse(response.responseText);
  98.  
  99. if (data && data.data && data.data.country) {
  100. resolve(`${data.data.country} | ${data.data.isp}`);
  101. } else {
  102. reject("失败104");
  103. }
  104. } catch (error) {
  105. reject("失败107");
  106. }
  107. },
  108. onerror: function (error) {
  109. reject("失败111");
  110. }
  111. });
  112. });
  113. }
  114.  
  115. let cachedIp = GM_getValue(location.origin + '_cachedIp', "失败117");
  116. let cachedDistrict = GM_getValue(location.origin + '_cachedDistrict', "失败118");
  117.  
  118. getIP(location.origin).then(Comment => {
  119.  
  120. // 匹配 IPv4 地址
  121. const ipv4Regex = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
  122. // 匹配 IPv6 地址
  123. const ipv6Regex = /\b([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b/g;
  124.  
  125. const ipv4Matches = Comment.match(ipv4Regex);
  126. const ipv6Matches = Comment.match(ipv6Regex);
  127.  
  128. let ip;
  129. // 先尝试匹配 IPv4,如果没有匹配到,再尝试匹配 IPv6
  130. if (ipv4Matches && ipv4Matches.length > 0) {
  131. ip = ipv4Matches[0]
  132.  
  133. GM_setValue(location.origin + '_cachedIp', ip); // 更新缓存
  134.  
  135. // 获取归属地信息
  136. getLocation(ip, false).then(district => {
  137. ip_button.textContent = `${ip} | ${district}`;
  138.  
  139. GM_setValue(location.origin + '_cachedDistrict', district); // 更新缓存
  140.  
  141. GM_log("143", location.origin, buttonId, ip, district)
  142. }).catch(error => {
  143. GM_log("145", error, location.origin, buttonId, ip, cachedDistrict)
  144.  
  145. ip_button.textContent = `${ip} | ${cachedDistrict}`;
  146. });
  147.  
  148. } else if (ipv6Matches && ipv6Matches.length > 0) {
  149. ip = ipv6Matches[0]
  150.  
  151. GM_setValue(location.origin + '_cachedIp', ip); // 更新缓存
  152.  
  153. // 获取归属地信息
  154. getLocation(ip, true).then(district => {
  155. ip_button.textContent = `${ip} | ${district}`;
  156.  
  157. GM_setValue(location.origin + '_cachedDistrict', district); // 更新缓存
  158.  
  159. GM_log("161", location.origin, buttonId, ip, district)
  160. }).catch(error => {
  161. GM_log("163", error, location.origin, buttonId, ip, cachedDistrict)
  162.  
  163. ip_button.textContent = `${ip} | ${cachedDistrict}`;
  164. });
  165.  
  166. } else {
  167. ip_button.textContent = `${cachedIp} | ${cachedDistrict}`;
  168.  
  169. GM_log("171", location.origin, cachedIp, cachedDistrict)
  170. }
  171.  
  172. }).catch(error => {
  173.  
  174. ip_button.textContent = `${cachedIp} | ${cachedDistrict}`;
  175.  
  176. GM_log("178", location.origin, error, cachedIp, cachedDistrict)
  177. });
  178.  
  179. })();