// ==UserScript==
// @name Hunter view
// @namespace http://tampermonkey.net/
// @version 0.5
// @description Send the current website to Hunter
// @author 0cat
// @match http://*/*
// @match https://*/*
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @connect *
// @license MIT
// @downloadURL https://update.greasyfork.icu/scripts/440243/Hunter%20view.user.js
// @updateURL https://update.greasyfork.icu/scripts/440243/Hunter%20view.meta.js
// ==/UserScript==
GM_registerMenuCommand("Hunter Login", openLoginForm, "l");
GM_registerMenuCommand("Hunter 识别结果", HunterFind, "s");
// 创建自定义输入表单
function openLoginForm() {
const body = document.getElementsByTagName('body')[0];
const loginDiv = document.createElement('div');
loginDiv.innerHTML = `
Login to Hunter
`;
body.appendChild(loginDiv);
document.getElementById('loginSubmit').onclick = function() {
const username = document.getElementById('hunterUsername').value;
const HunterKey = document.getElementById('hunterKey').value;
if (username && HunterKey) {
GM_setValue("username", username);
GM_setValue("HunterKey", HunterKey);
alert("Login successful!");
body.removeChild(loginDiv); // Remove form after login
} else {
alert("Please fill in both fields.");
}
};
document.getElementById('loginCancel').onclick = function() {
body.removeChild(loginDiv);
};
}
function HunterFind() {
var username = GM_getValue("username");
var HunterKey = GM_getValue("HunterKey");
if (!username || !HunterKey) {
console.error("No username or HunterKey found");
return;
}
const body = document.getElementsByTagName('body')[0];
const div = document.createElement('div');
div.innerHTML = ``;
body.appendChild(div);
// 隐藏/展开功能
const hideBtn = document.getElementById('hideBtn');
const expandBtn = document.getElementById('expandBtn');
const contentDiv = document.getElementById('contentDiv');
hideBtn.onclick = function() {
contentDiv.style.display = 'none';
hideBtn.style.display = 'none';
expandBtn.style.display = 'block';
};
expandBtn.onclick = function() {
contentDiv.style.display = 'block';
hideBtn.style.display = 'block';
expandBtn.style.display = 'none';
};
var target = window.location.hostname;
var isValidIP_reg = /(\d{1,3}\.){3}\d{1,3}/;
// 如果是 IP,则执行 RDAP 查询
if (isValidIP_reg.test(target)) {
// 发起 RDAP 请求
var rdap_url = `https://rdap.apnic.net/ip/${target}`;
GM_xmlhttpRequest({
method: "GET",
url: rdap_url,
onload: function(xhr) {
if (xhr.status !== 200) {
console.error("RDAP Request failed, status code:", xhr.status);
return;
}
const rdap_res = JSON.parse(xhr.responseText);
// 提取 CIDR 和描述
const handle = rdap_res.handle;
const cidr = rdap_res.cidr0_cidrs[0].v4prefix + "/" + rdap_res.cidr0_cidrs[0].length;
const description = rdap_res.remarks[0].description[0] || 'N/A';
// 将 CIDR 和描述信息填充到小卡片中
const rdap_cidr_element = document.getElementsByClassName('rdap_cidr')[0];
const rdap_description_element = document.getElementsByClassName('rdap_description')[0];
rdap_cidr_element.textContent = cidr || 'N/A';
rdap_description_element.textContent = description || 'N/A';
},
onerror: function(xhr) {
console.error("RDAP Request error:", xhr);
}
});
}
var Hunter_url = "https://hunter.qianxin.com/openApi/search?username=" + username + "&api-key=" + HunterKey + "&page=1&page_size=50&is_web=3&start_time=" + getNewDate("before", 6) + "&end_time=" + getNewDate() + "&search=";
var search, url;
if (isValidIP_reg.test(target)) {
search = btoa('ip=="' + target + '"');
url = Hunter_url + search;
} else {
search = btoa('domain=="' + target + '"');
url = Hunter_url + search;
}
if (!url) {
console.error("URL generation failed.");
return;
}
// 发起 Hunter 查询请求
try {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(xhr) {
if (xhr.status !== 200) {
console.error("Hunter Request failed, status code:", xhr.status);
return;
}
const res = JSON.parse(xhr.responseText);
const target_data = res.data?.arr || [];
if (target_data.length === 0) {
return;
}
// 获取 HTML 元素
const area = document.getElementsByClassName('area')[0];
const org = document.getElementsByClassName('org')[0];
const protocol = document.getElementsByClassName('protocol')[0];
const port = document.getElementsByClassName('port')[0];
const surplus = document.getElementsByClassName('surplus')[0];
const icpnumber = document.getElementsByClassName('icpnumber')[0];
const table_title = document.getElementsByClassName('table_title')[0];
const table_protocol = document.getElementsByClassName('table_protocol')[0];
const table_port = document.getElementsByClassName('table_port')[0];
const table_code = document.getElementsByClassName('table_code')[0];
const table_url = document.getElementsByClassName('table_url')[0];
// 遍历数据并展示到页面
let title_innerHTML = `标题
`;
let protocol_innerHTML = `协议
`;
let port_innerHTML = `端口
`;
let code_innerHTML = `状态码
`;
let url_innerHTML = `url
`;
target_data.forEach(item => {
title_innerHTML += `${item.web_title || 'N/A'}
`;
protocol_innerHTML += `${item.protocol || 'N/A'}
`;
code_innerHTML += `${item.status_code || 'N/A'}
`;
url_innerHTML += ``;
port_innerHTML += `${item.port || 'N/A'}
`;
});
table_title.innerHTML = title_innerHTML;
table_protocol.innerHTML = protocol_innerHTML;
table_code.innerHTML = code_innerHTML;
table_port.innerHTML = port_innerHTML;
table_url.innerHTML = url_innerHTML;
const target_location = target_data[0] || {};
area.textContent = [target_location.country || '', target_location.province || '', target_location.city || ''].filter(item => item).join('-') || 'N/A';
org.textContent = Array.from(new Set(target_data.filter(item => item.as_org).map(item => item.as_org))).join(',') || 'N/A';
icpnumber.textContent = target_data[0].number || 'N/A';
protocol.textContent = Array.from(new Set(target_data.filter(item => item.protocol).map(item => item.protocol))).join(',') || 'N/A';
port.textContent = Array.from(new Set(target_data.filter(item => item.port).map(item => item.port))).join(',') || 'N/A';
surplus.textContent = res.data.rest_quota?.split(':')[1] || 'N/A';
},
onerror: function(xhr) {
console.error("Hunter Request error:", xhr);
}
});
} catch (e) {
console.error("Error while sending request:", e);
}
}
function getNewDate(flag, many) {
const thirtyDays = [4, 6, 9, 11];
const thirtyOneDays = [1, 3, 5, 7, 8, 10, 12];
const currDate = new Date();
const year = currDate.getFullYear();
let month = currDate.getMonth() + 1;
let countDays = 0;
let targetDateMilli = 0;
let GMTDate = '';
let targetYear = '';
let targetMonth = '';
let targetDate = '';
let dealDate = '';
let hh = currDate.getHours() < 10 ? "0" + currDate.getHours() : currDate.getHours();
let mm = currDate.getMinutes() < 10 ? "0" + currDate.getMinutes() : currDate.getMinutes();
let ss = currDate.getSeconds() < 10 ? "0" + currDate.getSeconds() : currDate.getSeconds();
const isLeapYear = !!((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
for (let i = 0; i < many; i++) {
if (flag === 'before') {
month = month - 1 <= 0 ? 12 : month - 1;
} else {
month = month + 1 > 12 ? 1 : month + 1;
}
thirtyDays.includes(month) ? (countDays += 30) : thirtyOneDays.includes(month) ? (countDays += 31) : isLeapYear ? (countDays += 29) : (countDays += 28);
}
targetDateMilli = currDate.setDate(currDate.getDate() - (flag === 'before' ? countDays : countDays * -1));
GMTDate = new Date(targetDateMilli);
targetYear = GMTDate.getFullYear();
targetMonth = GMTDate.getMonth() + 1;
targetDate = GMTDate.getDate();
targetMonth = targetMonth.toString().padStart(2, '0');
targetDate = targetDate.toString().padStart(2, '0');
dealDate = `${targetYear}-${targetMonth}-${targetDate} ${hh}:${mm}:${ss}`;
return escape(dealDate);
}