您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
尝试征服世界!
// ==UserScript== // @name 12306火车查询脚本 // @namespace http://tampermonkey.net/ // @version 2025-09-11 // @description 尝试征服世界! // @author Dean // @match https://kyfw.12306.cn/otn/leftTicket/init* // @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net // @grant none // @license MIT // ==/UserScript== (function () { "use strict"; // 定义需要查询的火车列表 var train_list = ["K179", "K180"]; var train_date = "2025-09-26"; // 默认查询间隔时长为10分钟 var DEFAULT_INTERVAL = 300000; // 刷新延迟时间为3秒 var REFRESH_DELAY = 3000; // 内部属性 var intervalId = null; var logIndex = 0; // 检查火车信息 var checkTrain = function (train_list) { var t_list = document.getElementById("t-list"); var rows = t_list.getElementsByTagName("tr"); log("查询到" + rows.length + "辆车次,尝试获取目标车次状态..."); for (var i = 0; i < rows.length; i++) { var cells = rows[i].cells; var letterText = ""; var emit = false; var booking = false; var stationFromTo = ""; for (var j = 0; j < cells.length; j++) { if (j === 0) { const trainResult = checkTrainNumber(cells[j], train_list); emit = trainResult.checkTrainStatus; if (emit) { rows[i].className = ""; rows[i].style.backgroundColor = "lightgreen"; } letterText = trainResult.letterText; stationFromTo = trainResult.stationFromTo; letterText = `${stationFromTo} ${letterText}`; } else if (j == 1 && emit) { } else if (j === 12 && emit) { const trainStatus = checkTrainStatus(cells[j], letterText); booking = trainStatus.booking; letterText += trainStatus.letterText; // break trainLoop } } if (letterText && emit) { log(letterText + "--->预订状态:" + booking); if (booking) { // if (bookingBtn) { // 开始预定 // log(`开始预定 ---->${letterText}`) // log(bookingBtn) // bookingBtn.getElementsByTagName('a')[0].click() // setTimeout(() => { // checkUser() // }, 1000); // } else { sendMessage("12306火车查询", letterText); sendStrongMessage(letterText); log("已查询到指定车次,定时逻辑关闭。如需重新开启,请刷新页面..."); clearInterval(intervalId); // } } } } }; var checkUser = function () { var loginModal = document.getElementById("login"); if (loginModal) { // 未登录,输入账号密码 // var J_userName = document.getElementById('J-userName') // var J_password = document.getElementById('J-password') // if (J_userName && J_password) { // J_userName.value = username // J_password.value = password // log(`密码输入完成...✅✅✅`) // document.getElementById('J-login').click() // } } }; // 检查火车车次是否在查询列表中 var checkTrainNumber = function (cell, train_list) { var numberTag = cell.getElementsByClassName("number"); var number = numberTag[0]?.innerText; if (number) { if (train_list.includes(number)) { var cdzTags = cell.getElementsByClassName("cdz"); var stationFromTo = ""; if (cdzTags) { var from = cdzTags[0].getElementsByTagName("strong")[0]; var to = cdzTags[0].getElementsByTagName("strong")[1]; stationFromTo = `${from.innerHTML} -> ${to.innerHTML}`; } return { letterText: number, checkTrainStatus: true, stationFromTo }; } else { return { letterText: "未查询到车次", checkTrainStatus: false }; } } else { return {}; } }; // 检查火车状态并发送消息 var checkTrainStatus = function (cell) { var a_link = cell.getElementsByTagName("a"); if (a_link && a_link.length > 0 && a_link[0].tagName === "A") { const letterText = "[" + cell.innerText + "]\n可以预定了,赶快进入12306预定吧!!!"; return { letterText, booking: true }; } else { const letterText = "[" + cell.innerHTML + "]"; return { letterText, booking: false }; } }; var shotToast = function (message) { var toastContainer = document.getElementById("message-container"); if (toastContainer) { toastContainer.updateContentAndScrollToTop( `<div>${message}</div><br/>` + toastContainer.innerHTML ); } }; var log = function (log, showData = false) { console.log(new Date().toTimeString() + ": ", log); shotToast(showData ? new Date().toTimeString() + ": " + log : log); }; var sendStrongMessage = function (message) { setInterval(() => sendMessage("12306强提醒", message), 10000); }; // 发送消息 var sendMessage = function (train, letterText) { log(letterText); // 检查浏览器是否支持Notification API if ("Notification" in window) { // 请求用户权限 Notification.requestPermission().then((permission) => { if (permission === "granted") { // 如果用户授权,则创建通知 const notification = new Notification(train, { body: letterText, icon: "https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net", sound: "system", }); // 可以添加事件监听器来处理点击事件等 notification.onclick = function (event) { window.focus(); }; } }); } else { alert("你的浏览器不支持通知功能"); } }; var createToastContainer = function () { var toastContainer = document.createElement("div"); toastContainer.id = "toast-container"; toastContainer.style.position = "fixed"; toastContainer.style.width = "500px"; toastContainer.style.top = "20px"; toastContainer.style.right = "20px"; toastContainer.style.zIndex = "9999"; toastContainer.style.padding = "10px"; toastContainer.style.boxShadow = "0 4px 8px rgba(0,0,0,0.1)"; toastContainer.style.borderRadius = "5px"; toastContainer.style.backgroundColor = "rgba(60,60,60,0.9)"; toastContainer.style.color = "#fff"; toastContainer.style.fontFamily = "Arial, sans-serif"; toastContainer.style.fontSize = "14px"; toastContainer.innerHTML = `<div><H1>12306火车票监控预警已启动<H1><br/></div>`; document.body.appendChild(toastContainer); }; var createMessageContainer = function () { var toastContainer = document.createElement("div"); toastContainer.id = "message-container"; toastContainer.style.position = "fixed"; toastContainer.style.width = "500px"; toastContainer.style.top = "160px"; toastContainer.style.right = "20px"; toastContainer.style.zIndex = "9999"; toastContainer.style.padding = "10px"; toastContainer.style.boxShadow = "0 4px 8px rgba(0,0,0,0.1)"; toastContainer.style.borderRadius = "5px"; toastContainer.style.backgroundColor = "rgba(60,60,60,0.9)"; toastContainer.style.color = "#fff"; toastContainer.style.fontFamily = "Arial, sans-serif"; toastContainer.style.fontSize = "14px"; toastContainer.style.overflowY = "auto"; // 添加这一行来实现垂直滚动 toastContainer.style.maxHeight = "80%"; // 设置最大高度,超过这个高度就会显示滚动条 document.body.appendChild(toastContainer); // 定义一个方法来更新innerHTML并滚动到顶部 toastContainer.updateContentAndScrollToTop = function (htmlContent) { this.innerHTML = htmlContent; this.scrollTop = 0; // 滚动到顶部 }; }; var startCheckSelDate = function () { document.title = "12306火车票监控预警已启动"; document.querySelector("link[rel*='icon']").type = "image/svg+xml"; document.querySelector("link[rel*='icon']").href = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /></svg>'; createToastContainer(); createMessageContainer(); var fromStationText = document.getElementById("fromStationText"); var toStationText = document.getElementById("toStationText"); var train_date_input = document.getElementById("train_date"); if (train_date_input && fromStationText && toStationText) { train_date_input.value = train_date; var value = train_date_input.value; var toastContainer = document.getElementById('toast-container') if (toastContainer) { toastContainer.innerHTML = toastContainer.innerHTML + `监控车次: 由${fromStationText.value} 发往 ${toStationText.value}的${train_list}<br/> 监听日期: ${value}<br/><p>间隔时长: ${DEFAULT_INTERVAL / 60000}分钟</p>` } } else { log("监控车次的日期错误,请检查后重试...❌❌❌"); sendMessage("12306监控车次异常", "监控车次的日期错误,请检查后重试...❌❌❌"); } }; const validCheckTrainStart = function () { var currentHour = new Date().getHours(); if (currentHour < 24 && currentHour > 6) { return startCheckTrain; } else { log("12306封禁期,暂不做任何处理...✊✊✊"); return () => { }; } }; // 创建一个更直观的监控信息面板 function createMonitorPanel() { const panel = document.createElement("div"); panel.id = "monitor-panel"; panel.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 350px; padding: 20px; background-color: rgba(33, 33, 33, 0.95); color: #fff; border-radius: 12px; z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; max-height: 85vh; overflow-y: auto; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); `; panel.innerHTML = ` <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"> <h2 style="margin: 0; color: #4CAF50; font-size: 1.5em;">12306 监控面板</h2> <div style="width: 10px; height: 10px; background: #4CAF50; border-radius: 50%; animation: pulse 2s infinite;"></div> </div> <style> @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.2); opacity: 0.5; } 100% { transform: scale(1); opacity: 1; } } .monitor-section { background: rgba(255, 255, 255, 0.05); padding: 12px; border-radius: 8px; margin-bottom: 12px; } .monitor-section h3 { margin: 0 0 8px 0; color: #4CAF50; font-size: 1.1em; } .train-tag { display: inline-block; background-color: #4CAF50; color: white; padding: 4px 8px; margin: 2px; border-radius: 4px; font-size: 0.9em; transition: all 0.3s ease; } .train-tag:hover { transform: translateY(-2px); box-shadow: 0 2px 4px rgba(0,0,0,0.2); } .log-entry { padding: 8px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); font-size: 0.9em; transition: background-color 0.3s ease; } .log-entry:hover { background-color: rgba(255, 255, 255, 0.05); } .timestamp { color: #888; font-size: 0.8em; } </style> <div id="monitor-info" class="monitor-section"></div> <div id="train-list" class="monitor-section"></div> <div id="next-refresh" class="monitor-section"></div> <div id="log-container" class="monitor-section" style="max-height: 300px; overflow-y: auto;"></div> <button id="reset-config" style=" width: 100%; padding: 10px; background-color: #f44336; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; transition: all 0.3s ease; margin-top: 10px; ">重置配置</button> `; document.body.appendChild(panel); // 添加按钮悬停效果 const resetButton = document.getElementById("reset-config"); resetButton.addEventListener("mouseenter", () => { resetButton.style.backgroundColor = "#d32f2f"; }); resetButton.addEventListener("mouseleave", () => { resetButton.style.backgroundColor = "#f44336"; }); // 添加重置配置按钮的事件监听 resetButton.addEventListener("click", resetConfig); } // 创建日期选择提醒 function createDateReminder() { const reminder = document.createElement("div"); reminder.id = "date-reminder"; reminder.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgba(0, 0, 0, 0.8); color: #fff; padding: 20px; border-radius: 10px; text-align: center; z-index: 10000; `; reminder.innerHTML = ` <h3>请选择查询日期</h3> <p>点击日期输入框选择您要查询的日期</p> <button id="close-reminder" style=" margin-top: 10px; padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; ">我知道了</button> `; document.body.appendChild(reminder); document.getElementById("close-reminder").addEventListener("click", () => { reminder.style.display = "none"; }); } // 触发日期选择器并设置监听 function triggerDatePicker() { const dateInput = document.getElementById("train_date"); if (dateInput) { dateInput.focus(); // 模拟点击事件以打开日期选择器 const event = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); dateInput.dispatchEvent(event); // 添加日期变更监听 dateInput.addEventListener('change', function () { updateMonitorDate(this.value); }); } } // 更新监控日期并刷新页面 function updateMonitorDate(newDate) { train_date = newDate; // 更新全局变量 // 更新localStorage中的日期 localStorage.setItem("train_date", newDate); // 更新监控信息显示 const fromStationText = document.getElementById("fromStationText"); const toStationText = document.getElementById("toStationText"); updateMonitorInfo(fromStationText.value, toStationText.value, newDate); // 立即执行一次查询 log(`日期已更新为 ${newDate},正在刷新查询...`); validCheckTrainStart()(JSON.parse(localStorage.getItem("train_list")) || train_list); // 更新下次刷新时间 updateNextRefreshTime(); // 可选:如果你想重新加载整个页面,可以取消下面这行的注释 // window.location.href = new URL(window.location.href).searchParams.set('date', newDate).toString(); } // 更新监控信息 function updateMonitorInfo(fromStation, toStation, date) { const infoDiv = document.getElementById("monitor-info"); infoDiv.innerHTML = ` <h3>监控信息</h3> <div style="display: grid; grid-template-columns: auto 1fr; gap: 8px;"> <span style="color: #888;">出发站:</span> <span style="color: #fff;">${fromStation}</span> <span style="color: #888;">到达站:</span> <span style="color: #fff;">${toStation}</span> <span style="color: #888;">日期:</span> <span style="color: #fff;">${date}</span> <span style="color: #888;">刷新间隔:</span> <span style="color: #fff;">${DEFAULT_INTERVAL / 60000}分钟</span> </div> `; } // 更新列车列表 function updateTrainList(trainList) { const listDiv = document.getElementById("train-list"); listDiv.innerHTML = "<h3>监控车次</h3><div style='display: flex; flex-wrap: wrap; gap: 4px;'>"; trainList.forEach(train => { listDiv.innerHTML += `<span class="train-tag">${train}</span>`; }); listDiv.innerHTML += "</div>"; } // 更新下次刷新时间 function updateNextRefreshTime() { const nextRefreshDiv = document.getElementById("next-refresh"); const nextRefreshTime = new Date(Date.now() + DEFAULT_INTERVAL); nextRefreshDiv.innerHTML = ` <h3>下次刷新</h3> <div style="display: flex; align-items: center; gap: 8px;"> <span style="color: #4CAF50;">${nextRefreshTime.toLocaleTimeString()}</span> <div style="flex-grow: 1; height: 2px; background: linear-gradient(to right, #4CAF50, transparent);"></div> </div> `; } // 优化日志显示 function log(message, showTimestamp = true) { const logContainer = document.getElementById("log-container"); if (!logContainer) { console.error("Log container not found"); return; } const logEntry = document.createElement("div"); logEntry.className = "log-entry"; if (showTimestamp) { const timestamp = new Date().toLocaleTimeString(); logEntry.innerHTML = ` <span class="timestamp">[${timestamp}]</span> <span style="margin-left: 8px;">${message}</span> `; } else { logEntry.textContent = message; } // 添加新日志时的动画效果 logEntry.style.opacity = "0"; logEntry.style.transform = "translateY(-10px)"; logContainer.insertBefore(logEntry, logContainer.firstChild); // 触发动画 setTimeout(() => { logEntry.style.transition = "all 0.3s ease"; logEntry.style.opacity = "1"; logEntry.style.transform = "translateY(0)"; }, 50); // 限制日志条目数量,保持最新的20条 while (logContainer.children.length > 20) { logContainer.removeChild(logContainer.lastChild); } // 自动滚动到顶部 logContainer.scrollTop = 0; console.log(message); } // 重置配置函数 function resetConfig() { localStorage.removeItem("12306_first_visit"); localStorage.removeItem("train def setup_web_channel_js(self"); log("配置已重置,请刷新页面以应用更改。"); } // 开始查询火车信息 function startCheckTrain(trainList) { const queryTicket = document.getElementById("query_ticket"); log("尝试刷新车次列表..."); if (queryTicket) { queryTicket.click(); log("刷新车次列表成功 ✅"); setTimeout(() => { checkTrain(trainList); }, REFRESH_DELAY); } else { log("刷新车次列表失败,请检查后重试 ❌", true); sendMessage("12306刷新车次失败", "刷新车次列表失败,请检查后重试"); } updateNextRefreshTime(); } // 主函数 function main(trainList) { createMonitorPanel(); const fromStationText = document.getElementById("fromStationText"); const toStationText = document.getElementById("toStationText"); const trainDateInput = document.getElementById("train_date"); if (fromStationText && toStationText && trainDateInput) { // 检查是否是首次访问或配置已重置 if (!localStorage.getItem("12306_first_visit")) { createDateReminder(); triggerDatePicker(); localStorage.setItem("12306_first_visit", "true"); // 保存初始配置 localStorage.setItem("train_list", JSON.stringify(trainList)); localStorage.setItem("train_date", trainDateInput.value); } else { // 从 localStorage 加载配置 trainList = JSON.parse(localStorage.getItem("train_list")) || trainList; train_date = localStorage.getItem("train_date") || train_date; trainDateInput.value = train_date; } updateMonitorInfo(fromStationText.value, toStationText.value, trainDateInput.value); updateTrainList(trainList); log(`监控已启动,正在查询指定车次`, true); validCheckTrainStart()(trainList); intervalId = setInterval(() => { log(`定时刷新,重新查询车次`, true); validCheckTrainStart()(trainList); }, DEFAULT_INTERVAL); } else { log("获取车站信息失败,请检查页面是否正确加载", true); } } // 确保在页面加载完成后再执行脚本 window.addEventListener('load', () => { main(train_list); }); })();