// ==UserScript== // @name ChatGPT 身份认证全自动助手 (V19.0 持续点击修复版) // @namespace http://tampermonkey.net/ // @version 19.0.1 // @description 自动从搜索结果页面提取数据并填充表单,智能检测页面加载 // @author CreatorEdition // @match https://gravelocator.cem.va.gov/* // @match https://services.sheerid.com/* // @match https://chatgpt.com/veterans-claim/* // @match https://chatgpt.com/veterans-claim // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // --- 核心配置 --- const FIXED_EMAIL = "test@email.com";//修改你的邮箱 const MIN_BIRTH_YEAR = 1930; const FILL_DELAY = 1000; // 在 sheerid 页面延迟1秒填写 const FIELD_MAP = { status: '#sid-military-status', branch: '#sid-branch-of-service', firstName: '#sid-first-name', lastName: '#sid-last-name', bMonth: '#sid-birthdate__month', bDay: '#sid-birthdate-day', bYear: '#sid-birthdate-year', dMonth: '#sid-discharge-date__month', dDay: '#sid-discharge-date-day', dYear: '#sid-discharge-date-year', email: '#sid-email' }; const SUBMIT_BTN_SELECTOR = '#sid-submit-btn-collect-info'; const RESULT_TABLE_SELECTOR = '#searchResults tbody'; const ERROR_BUTTON_SELECTOR = '.sid-error-button-container a.sid-btn'; // 固定配置 const FIXED_STATUS = "Military Veteran or Retiree"; const FIXED_DISCHARGE_YEAR = "2025"; const MONTH_MAP = { "01": "January", "02": "February", "03": "March", "04": "April", "05": "May", "06": "June", "07": "July", "08": "August", "09": "September", "10": "October", "11": "November", "12": "December" }; // --- 状态管理 --- function getQueue() { return GM_getValue('global_auth_queue', []); } function saveQueue(arr) { GM_setValue('global_auth_queue', arr); updateUI(); } function getCurrentTask() { return GM_getValue('current_active_task', null); } function setCurrentTask(task) { GM_setValue('current_active_task', task); } function getSubmitState() { return GM_getValue('is_submitting_flag', false); } function setSubmitState(bool) { GM_setValue('is_submitting_flag', bool); } function getIsRunning() { return GM_getValue('is_script_running', false); } function setIsRunning(bool) { GM_setValue('is_script_running', bool); updateUI(); } function getFillingStage() { return GM_getValue('filling_stage', 0); } function setFillingStage(stage) { GM_setValue('filling_stage', stage); } function getWaitingForRetry() { return GM_getValue('waiting_for_retry', false); } function setWaitingForRetry(bool) { GM_setValue('waiting_for_retry', bool); } function getClaimPageAttempts() { return GM_getValue('claim_page_attempts', 0); } function setClaimPageAttempts(count) { GM_setValue('claim_page_attempts', count); } function getInitialFillDelay() { return GM_getValue('initial_fill_delay_done', false); } function setInitialFillDelay(bool) { GM_setValue('initial_fill_delay_done', bool); } function getLastClickedUrl() { return GM_getValue('last_clicked_url', ''); } function setLastClickedUrl(url) { GM_setValue('last_clicked_url', url); } // --- 🔥 错误检测和自动重试 --- function checkForErrorAndRetry() { const errorBtn = document.querySelector(ERROR_BUTTON_SELECTOR); if (errorBtn) { const href = errorBtn.getAttribute('href'); log('⚠️ 检测到错误页面,准备重试...', '#ff6b6b'); statusArea.innerHTML = "🔄 检测到错误,自动重试中..."; statusArea.style.color = "orange"; // 标记为等待重试状态 setWaitingForRetry(true); setClaimPageAttempts(0); // 点击 Try Again 按钮 setTimeout(() => { log('🔄 点击 Try Again 按钮...', '#ffc107'); errorBtn.click(); }, 500); return true; } return false; } // --- 🔥 在 veterans-claim 页面持续尝试点击"验证资格条件"按钮 --- function checkClaimPageButton() { const currentUrl = window.location.href; // 只在 veterans-claim 页面运行 if (!currentUrl.includes('chatgpt.com/veterans-claim')) { return false; } const isRunning = getIsRunning(); const isWaitingRetry = getWaitingForRetry(); // 只有在运行状态或等待重试状态下才执行 if (!isRunning && !isWaitingRetry) { return false; } // 查找"验证资格条件"按钮 const buttons = Array.from(document.querySelectorAll('button.btn.relative.btn-primary')); let targetBtn = null; for (let btn of buttons) { const text = btn.textContent.trim(); if (text.includes('验证资格条件') || text.includes('验证') || text.includes('领取优惠') || text.includes('Verify')) { targetBtn = btn; break; } } if (targetBtn) { // 检查按钮是否被禁用(加载中) const isDisabled = targetBtn.disabled || targetBtn.hasAttribute('disabled') || targetBtn.classList.contains('cursor-not-allowed') || targetBtn.getAttribute('data-visually-disabled') !== null; const attempts = getClaimPageAttempts(); if (isDisabled) { // 按钮加载中 setClaimPageAttempts(attempts + 1); log(`⏳ 按钮加载中,等待... (尝试 ${attempts + 1})`, '#ffc107'); statusArea.innerHTML = `🔄 等待按钮激活中 (尝试 ${attempts + 1})...`; statusArea.style.color = "orange"; return true; // 继续等待 } else { // 按钮可用,准备点击 const lastUrl = getLastClickedUrl(); // 如果 URL 没有变化,说明还在同一页面,继续点击 if (lastUrl === currentUrl) { setClaimPageAttempts(attempts + 1); log(`🎯 持续点击按钮... (第 ${attempts + 1} 次)`, '#28a745'); statusArea.innerHTML = `🎯 持续点击按钮 (第 ${attempts + 1} 次)...`; } else { log(`✅ 按钮已激活,准备点击`, '#28a745'); statusArea.innerHTML = "🎯 按钮已激活,正在点击..."; setClaimPageAttempts(0); } setLastClickedUrl(currentUrl); setTimeout(() => { targetBtn.click(); log('🚀 已点击按钮,等待跳转...', '#0d6efd'); }, 300); return true; } } else { // 未找到按钮 const attempts = getClaimPageAttempts(); setClaimPageAttempts(attempts + 1); log(`⏳ 等待页面加载按钮... (尝试 ${attempts + 1})`, '#6c757d'); statusArea.innerHTML = `⏳ 等待页面加载 (尝试 ${attempts + 1})...`; return true; } } // --- 🔥 检测是否成功跳转到 SheerID 页面 --- function checkIfLeftClaimPage() { const currentUrl = window.location.href; const lastUrl = getLastClickedUrl(); // 如果从 claim 页面跳转到了其他页面 if (lastUrl.includes('chatgpt.com/veterans-claim') && currentUrl.includes('services.sheerid.com')) { log('✅ 成功跳转到 SheerID 页面', '#28a745'); setWaitingForRetry(false); setClaimPageAttempts(0); setFillingStage(0); setInitialFillDelay(false); setLastClickedUrl(''); return true; } return false; } // --- 页面初始化 --- function initLogic() { const currentUrl = window.location.href; const justSubmitted = getSubmitState(); if (justSubmitted) { console.log("✅ 提交完成,清除任务"); setCurrentTask(null); setSubmitState(false); setFillingStage(0); setInitialFillDelay(false); } // 检查是否成功跳转到 SheerID checkIfLeftClaimPage(); // 检查是否是错误页面 checkForErrorAndRetry(); // 如果在 sheerid 页面,重置初始延迟标记 if (currentUrl.includes('services.sheerid.com')) { const stage = getFillingStage(); if (stage === 0 && !getInitialFillDelay()) { log('📍 SheerID 页面已加载,准备延迟填写...', '#0d6efd'); } } } // --- UI 创建 --- function createPanel() { const div = document.createElement('div'); div.style = "position: fixed; bottom: 50px; right: 20px; width: 400px; background: #fff; border: 2px solid #6610f2; box-shadow: 0 5px 25px rgba(0,0,0,0.3); z-index: 999999; padding: 15px; border-radius: 8px; font-family: sans-serif; font-size: 13px;"; div.innerHTML = `
🚀 认证助手 V19.0 0
等待操作...
`; document.body.appendChild(div); return div; } const panel = createPanel(); const statusArea = document.getElementById('status_area'); const debugArea = document.getElementById('debug_area'); const queueCount = document.getElementById('queue_count'); const inputArea = document.getElementById('bulk_input'); const btnToggle = document.getElementById('btn_toggle'); const btnSkip = document.getElementById('btn_skip'); const btnAutoExtract = document.getElementById('btn_auto_extract'); const btnImport = document.getElementById('btn_import'); const btnReset = document.getElementById('btn_reset'); // --- 调试日志 --- function log(msg, color = '#333') { console.log(msg); debugArea.innerHTML = `${msg}`; } // --- 🔥 自动提取页面数据函数 --- function autoExtractFromPage() { const tableBody = document.querySelector(RESULT_TABLE_SELECTOR); if (!tableBody) { alert("❌ 未找到搜索结果表格!\n请确保在搜索结果页面上。"); return null; } const parsedList = []; let skippedCount = 0; // 按行分组(每个退伍军人占多行) const rows = Array.from(tableBody.querySelectorAll('tr')); let currentPerson = {}; let recordCount = 0; rows.forEach(row => { const header = row.querySelector('th.row-header'); const data = row.querySelector('td.results-info'); if (!header || !data) return; const label = header.textContent.trim().replace(':', ''); const value = data.textContent.trim(); if (label === 'Name') { // 新的记录开始 if (Object.keys(currentPerson).length > 0) { // 保存上一个记录 processAndAddPerson(currentPerson, parsedList); recordCount++; } currentPerson = { name: value }; } else { currentPerson[label] = value; } }); // 处理最后一条记录 if (Object.keys(currentPerson).length > 0) { processAndAddPerson(currentPerson, parsedList); recordCount++; } log(`✅ 从页面提取 ${recordCount} 条记录`, '#28a745'); return { list: parsedList, skipped: skippedCount }; } function processAndAddPerson(person, list) { // 解析姓名 let lastName = "", firstName = ""; if (person.name) { const parts = person.name.split(',').map(s => s.trim()); lastName = parts[0] || ""; firstName = parts[1] || ""; } // 解析军种 const branch = getExactBranch(person['Rank & Branch'] || ""); // 解析出生日期 const dob = person['Date of Birth'] || ""; const dobParts = dob.split('/'); const bMonth = dobParts[0] ? MONTH_MAP[dobParts[0]] : ""; const bDay = dobParts[1] || ""; const bYear = dobParts[2] || ""; // 解析死亡/退役日期 const dod = person['Date of Death'] || ""; const dodParts = dod.split('/'); const dMonth = dodParts[0] ? MONTH_MAP[dodParts[0]] : ""; const dDay = dodParts[1] || ""; // 过滤出生年份太早的 if (bYear && parseInt(bYear, 10) < MIN_BIRTH_YEAR) { log(`⚠️ 跳过 ${lastName} (${bYear} < 1930)`, '#ffc107'); return; } // 验证必填字段 if (firstName && lastName && bMonth && bDay && bYear) { list.push([ FIXED_STATUS, branch, firstName, lastName, bMonth, bDay, bYear, dMonth, dDay, FIXED_DISCHARGE_YEAR, FIXED_EMAIL ]); log(`✅ ${firstName} ${lastName} | ${branch}`, '#198754'); } } // --- 军种识别 --- function getExactBranch(text) { const upper = text.toUpperCase(); if (upper.includes("SPACE FORCE")) return "Space Force"; if (upper.includes("AIR NATIONAL GUARD") || upper.includes("ANG")) return "Air National Guard"; if (upper.includes("AIR FORCE RESERVE") || upper.includes("USAFR")) return "Air Force Reserve"; if (upper.includes("AIR FORCE") || upper.includes("USAF")) return "Air Force"; if (upper.includes("ARMY NATIONAL GUARD") || upper.includes("ARNG")) return "Army"; if (upper.includes("ARMY RESERVE") || upper.includes("USAR")) return "Army Reserve"; if (upper.includes("ARMY") || upper.includes("USA")) return "Army"; if (upper.includes("COAST GUARD RESERVE")) return "Coast Guard Reserve"; if (upper.includes("COAST GUARD") || upper.includes("USCG")) return "Coast Guard"; if (upper.includes("MARINE CORPS FORCE RESERVE")) return "Marine Corps Force Reserve"; if (upper.includes("MARINE") || upper.includes("USMC")) return "Marine Corps"; if (upper.includes("NAVY RESERVE") || upper.includes("USNR")) return "Navy Reserve"; if (upper.includes("NAVY") || upper.includes("USN")) return "Navy"; return "Army"; } // --- 手动解析数据 --- function parseRawData(text) { const parsedList = []; let skippedCount = 0; const blocks = text.split(/Name:\s*\n/g).filter(b => b.trim()); for (let block of blocks) { const nameLine = block.split('\n')[0].trim(); let lastName = "", firstName = ""; if (nameLine.includes(',')) { const parts = nameLine.split(',').map(s => s.trim()); lastName = parts[0]; firstName = parts[1] || ""; } else { lastName = nameLine; } const branch = getExactBranch(block); const dobMatch = block.match(/Date of Birth:\s*\n(\d{2})\/(\d{2})\/(\d{4})/); const bMonth = dobMatch ? MONTH_MAP[dobMatch[1]] : ""; const bDay = dobMatch ? dobMatch[2] : ""; const bYear = dobMatch ? dobMatch[3] : ""; const dodMatch = block.match(/Date of Death:\s*\n(\d{2})\/(\d{2})\/(\d{4})/); const dMonth = dodMatch ? MONTH_MAP[dodMatch[1]] : ""; const dDay = dodMatch ? dodMatch[2] : ""; if (bYear && parseInt(bYear, 10) < MIN_BIRTH_YEAR) { skippedCount++; continue; } if (firstName && lastName && bMonth && bDay && bYear) { parsedList.push([ FIXED_STATUS, branch, firstName, lastName, bMonth, bDay, bYear, dMonth, dDay, FIXED_DISCHARGE_YEAR, FIXED_EMAIL ]); } else { skippedCount++; } } return { list: parsedList, skipped: skippedCount }; } // --- 表单填充函数 --- function simulateClick(element) { if (!element) return; element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); element.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); element.click(); } function setNativeValue(element, value) { if (!element) return; const lastValue = element.value; element.value = value; const tracker = element._valueTracker; if (tracker) tracker.setValue(lastValue); element.dispatchEvent(new Event('input', { bubbles: true })); element.dispatchEvent(new Event('change', { bubbles: true })); element.dispatchEvent(new Event('blur', { bubbles: true })); } async function selectDropdown(selector, value, waitTime = 300) { const el = document.querySelector(selector); if (!el) return false; el.focus(); simulateClick(el); await new Promise(r => setTimeout(r, 150)); setNativeValue(el, value); el.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); await new Promise(r => setTimeout(r, waitTime)); return true; } // --- ⚡ 核心自动化逻辑 --- async function runAutomation() { const queue = getQueue(); const isRunning = getIsRunning(); const currentUrl = window.location.href; if (!isRunning) return; // 🔥 优先检查错误页面 if (checkForErrorAndRetry()) { return; } // 🔥 优先检查 claim 页面按钮(持续尝试点击) if (checkClaimPageButton()) { return; } let currentTask = getCurrentTask(); let stage = getFillingStage(); // 如果在 SheerID 页面且有任务,先等待1秒 if (currentUrl.includes('services.sheerid.com') && currentTask && stage === 0 && !getInitialFillDelay()) { statusArea.innerHTML = `⏳ SheerID 页面加载完成,等待 1 秒后开始填写...`; log('⏳ 延迟 1 秒后开始填写...', '#ffc107'); await new Promise(r => setTimeout(r, FILL_DELAY)); setInitialFillDelay(true); log('✅ 延迟完成,开始填写表单', '#28a745'); } // 1. 获取新任务 if (!currentTask && queue.length > 0) { currentTask = queue.shift(); saveQueue(queue); setCurrentTask(currentTask); setFillingStage(0); setInitialFillDelay(false); stage = 0; log(`🆕 载入: ${currentTask[2]} ${currentTask[3]}`, '#0d6efd'); // 如果在 SheerID 页面,立即延迟 if (currentUrl.includes('services.sheerid.com')) { statusArea.innerHTML = `⏳ 等待1秒后开始填写: ${currentTask[2]} ${currentTask[3]}`; await new Promise(r => setTimeout(r, FILL_DELAY)); setInitialFillDelay(true); } } // 2. 完成 if (!currentTask) { statusArea.innerHTML = "✅ 所有数据已处理完毕"; statusArea.style.color = "green"; log('🎉 全部完成!', '#198754'); setIsRunning(false); return; } statusArea.innerHTML = `处理中 (${stage+1}/3): ${currentTask[2]} ${currentTask[3]}`; const statusEl = document.querySelector(FIELD_MAP.status); const nameEl = document.querySelector(FIELD_MAP.firstName); try { // 阶段 0: Status if (stage === 0) { if (statusEl && statusEl.value !== FIXED_STATUS) { log('📝 填充 Status...', '#0d6efd'); await selectDropdown(FIELD_MAP.status, FIXED_STATUS, 600); } setFillingStage(1); return; } // 阶段 1: 详细信息 if (stage === 1 && nameEl) { log('📝 填充详细信息...', '#0d6efd'); await selectDropdown(FIELD_MAP.branch, currentTask[1], 200); setNativeValue(document.querySelector(FIELD_MAP.firstName), currentTask[2]); setNativeValue(document.querySelector(FIELD_MAP.lastName), currentTask[3]); await selectDropdown(FIELD_MAP.bMonth, currentTask[4], 150); setNativeValue(document.querySelector(FIELD_MAP.bDay), currentTask[5]); setNativeValue(document.querySelector(FIELD_MAP.bYear), currentTask[6]); await selectDropdown(FIELD_MAP.dMonth, currentTask[7], 150); setNativeValue(document.querySelector(FIELD_MAP.dDay), currentTask[8]); setNativeValue(document.querySelector(FIELD_MAP.dYear), currentTask[9]); setNativeValue(document.querySelector(FIELD_MAP.email), currentTask[10]); setFillingStage(2); return; } // 阶段 2: 提交 if (stage === 2) { const submitBtn = document.querySelector(SUBMIT_BTN_SELECTOR); if (submitBtn) { const isDisabled = submitBtn.getAttribute('aria-disabled') === 'true' || submitBtn.disabled || submitBtn.classList.contains('disabled'); if (!isDisabled) { log('🚀 提交表单...', '#198754'); setSubmitState(true); submitBtn.click(); setFillingStage(0); setInitialFillDelay(false); } else { log('⚠️ 提交按钮未激活,等待...', '#ffc107'); } } } } catch (e) { log(`❌ 错误: ${e.message}`, '#dc3545'); } } // --- UI 更新 --- function updateUI() { const queue = getQueue(); const isRunning = getIsRunning(); queueCount.innerText = queue.length; if (isRunning) { btnToggle.innerText = "⏸️ 运行中"; btnToggle.style.backgroundColor = "#198754"; btnToggle.style.color = "#fff"; } else { if (queue.length > 0) { btnToggle.innerText = "▶️ 启动"; btnToggle.style.backgroundColor = "#0d6efd"; btnToggle.style.color = "#fff"; statusArea.innerText = "⏸️ 已暂停"; } else { btnToggle.innerText = "🚫 无数据"; btnToggle.style.backgroundColor = "#e9ecef"; btnToggle.style.color = "#6c757d"; } } } // --- 按钮事件 --- btnToggle.onclick = () => { const queue = getQueue(); if (queue.length === 0 && !getCurrentTask()) { alert("请先提取或导入数据!"); return; } setIsRunning(!getIsRunning()); }; btnSkip.onclick = () => { const current = getCurrentTask(); if (!current && getQueue().length === 0) { alert("没有任务可以跳过"); return; } setCurrentTask(null); setSubmitState(false); setFillingStage(0); setWaitingForRetry(false); setClaimPageAttempts(0); setInitialFillDelay(false); setLastClickedUrl(''); if (!getIsRunning()) { setIsRunning(true); } statusArea.innerHTML = "⏭️ 已跳过!正在载入下一位..."; statusArea.style.color = "orange"; setTimeout(runAutomation, 100); }; // 🔥 自动提取按钮 btnAutoExtract.onclick = () => { const result = autoExtractFromPage(); if (!result) return; const newData = result.list; const skipped = result.skipped; if (newData.length === 0) { alert("未提取到有效数据"); return; } const currentQueue = getQueue(); saveQueue(currentQueue.concat(newData)); let msg = `✅ 成功提取 ${newData.length} 人`; if (skipped > 0) msg += `\n🚫 跳过 ${skipped} 人`; alert(msg); log(`✅ 提取完成: ${newData.length} 人`, '#28a745'); }; btnImport.onclick = () => { const text = inputArea.value; if (!text) return; try { const result = parseRawData(text); const newData = result.list; const skipped = result.skipped; if (newData.length === 0 && skipped === 0) { alert("无有效数据"); return; } const currentQueue = getQueue(); saveQueue(currentQueue.concat(newData)); inputArea.value = ""; let msg = `✅ 成功导入 ${newData.length} 人`; if (skipped > 0) msg += `\n🚫 跳过 ${skipped} 人`; alert(msg); } catch (e) { alert("解析错误: " + e.message); } }; btnReset.onclick = () => { if(confirm("确定清空全部数据?")) { saveQueue([]); setCurrentTask(null); setSubmitState(false); setFillingStage(0); setIsRunning(false); setWaitingForRetry(false); setClaimPageAttempts(0); setInitialFillDelay(false); setLastClickedUrl(''); log('🗑️ 已清空所有数据', '#dc3545'); location.reload(); } }; // --- 初始化 --- initLogic(); updateUI(); function loop() { runAutomation(); setTimeout(loop, 1500); } setTimeout(loop, 1000); log('✅ 脚本已加载 V19.0', '#198754'); })();