點擊畫面按鈕後才開始自動審核流程,支援 iframe 和小循環操作,修復確定按鈕偵測問題,偵測到按鈕才跳金鑰驗證
// ==UserScript==
// @name EMS 審核自動化(手動觸發+iframe支援)
// @namespace http://tampermonkey.net/
// @version 3.0(金鑰版)
// @description 點擊畫面按鈕後才開始自動審核流程,支援 iframe 和小循環操作,修復確定按鈕偵測問題,偵測到按鈕才跳金鑰驗證
// @match https://ems.mohw.gov.tw/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const KEY_URL = "https://catjohnny.github.io/ems-key-auth/emsauth.json";
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function waitForSelector(selector, root = document, timeout = 30000) {
return new Promise((resolve, reject) => {
const start = Date.now();
const timer = setInterval(() => {
const el = root.querySelector(selector);
if (el) {
clearInterval(timer);
resolve(el);
} else if (Date.now() - start > timeout) {
clearInterval(timer);
reject(new Error("等待超時:" + selector));
}
}, 500);
});
}
function decodeBase64(str) {
try {
return atob(str);
} catch {
return "";
}
}
async function fetchValidKeys() {
try {
const res = await fetch(KEY_URL + "?t=" + Date.now());
const data = await res.json();
return data.valid_keys.map(decodeBase64);
} catch (e) {
alert("❌ 金鑰伺服器連線失敗!");
return [];
}
}
async function verifyKey() {
const inputKey = prompt("🔒 請輸入金鑰(陳家和設計):");
const validKeys = await fetchValidKeys();
if (!validKeys.includes(inputKey)) {
alert("❌ 金鑰錯誤,無法使用本腳本。");
return false;
}
return true;
}
async function processAudit() {
const start = parseInt(prompt("請輸入起始筆數,例如 1:"), 10);
const end = parseInt(prompt("請輸入結束筆數,例如 206:"), 10);
const iframe = document.querySelector("iframe");
const root = iframe?.contentDocument || document;
const buttons = root.querySelectorAll("button.btn-info[onclick^='rowAction.OpenStatus']");
if (!buttons.length) {
alert("⚠️ 找不到任何審核按鈕!");
return;
}
for (let i = start - 1; i < end && i < buttons.length; i++) {
try {
console.log(`✅ 點擊第 ${i + 1} 筆審核`);
buttons[i].scrollIntoView();
buttons[i].click();
await delay(800);
await waitForSelector("#CP_STATUS_A");
document.querySelector("#CP_STATUS_A").click();
await delay(500);
await waitForSelector("button.btn-success[onclick='setCourseStatus()']");
document.querySelector("button.btn-success[onclick='setCourseStatus()']").click();
await delay(500);
const confirmBtn = [...document.querySelectorAll("span.l-btn-text")].find(span => span.textContent.trim() === "確定");
if (confirmBtn) {
confirmBtn.click();
} else {
throw new Error("找不到確定按鈕");
}
await delay(2500);
} catch (err) {
console.warn(`❌ 第 ${i + 1} 筆失敗:`, err);
alert(`第 ${i + 1} 筆失敗,請手動處理後按確定繼續`);
}
}
alert("🎉 所有審核完成!");
}
function addStartButton() {
const btn = document.createElement("button");
btn.textContent = "▶️ 開始自動審核 3.0版";
btn.style.position = "fixed";
btn.style.bottom = "20px";
btn.style.right = "20px";
btn.style.zIndex = "9999";
btn.style.padding = "10px 15px";
btn.style.backgroundColor = "#28a745";
btn.style.color = "white";
btn.style.border = "none";
btn.style.borderRadius = "5px";
btn.style.boxShadow = "0 2px 6px rgba(0,0,0,0.2)";
btn.style.cursor = "pointer";
btn.addEventListener("click", processAudit);
document.body.appendChild(btn);
}
// ✅ 保留原功能 + 改成偵測到按鈕才跳金鑰
waitForSelector("button.btn-info[onclick^='rowAction.OpenStatus']")
.then(async () => {
console.log("✅ 偵測到審核按鈕");
const passed = await verifyKey();
if (!passed) return;
console.log("🔓 金鑰驗證成功,加入開始按鈕");
addStartButton();
})
.catch(err => console.error("❌ 未能偵測到審核按鈕:", err));
})();