// ==UserScript== // @name 自动填写身份证号码 // @namespace http://tampermonkey.net/ // @version 0.3 // @description 使用前请添加乘客的姓名和随机的身份证号,脚本会自动生成所有可能的身份证号码,并自动填写到12306的乘客信息页面上用以验算身份证信息是否正确,我愿称之为绝配小工具! // @author chiupam // @icon https://kyfw.12306.cn/otn/images/favicon.ico // @match https://kyfw.12306.cn/otn/passengers/init // @match https://kyfw.12306.cn/otn/view/passengers.html // @match https://kyfw.12306.cn/otn/view/passenger_edit.html* // @grant GM_setValue // @grant GM_getValue // @license GNU GPLv3 // @downloadURL https://update.greasyfork.icu/scripts/535141/%E8%87%AA%E5%8A%A8%E5%A1%AB%E5%86%99%E8%BA%AB%E4%BB%BD%E8%AF%81%E5%8F%B7%E7%A0%81.user.js // @updateURL https://update.greasyfork.icu/scripts/535141/%E8%87%AA%E5%8A%A8%E5%A1%AB%E5%86%99%E8%BA%AB%E4%BB%BD%E8%AF%81%E5%8F%B7%E7%A0%81.meta.js // ==/UserScript== (function () { 'use strict'; // ===== 用户配置区域(使用前请修改) ===== // 12306 乘客信息页面的姓名 const username = ""; // 必填:请填写乘客姓名,例如:"张三" // 12306 乘客信息页面的身份证号(部分已知的信息) // 格式1:月日未知 - 例如:"1101011990xxxx3319"(中间4位用x表示) // 格式2:序列码未知 - 例如:"110101199001018xxxx" (已知前14位,最后4位未知) // 格式3:序列码未知但知道性别 - 例如:"11010119900821xx1x"(男性) "11010119900821xx2x"(女性) const knowID = ""; // 必填:请填写部分已知的身份证号 // 自动操作延迟时间(毫秒) const SAVE_DELAY = 5000; // 填写后等待点击保存的时间 const CONFIRM_DELAY = 5000; // 等待点击确认按钮的时间 const CHECK_DELAY = 5000; // 检查状态的间隔时间 // ===== 核心逻辑代码 ===== // 检查用户配置 if (!username || !knowID) { alert('请先在脚本中配置乘客姓名和部分已知的身份证号!'); console.error('配置错误:乘客姓名和身份证号不能为空'); return; } // 开始执行脚本 let { func, args } = chooseType(knowID); const ID_LIST = func(...args); // 如果没有生成有效的身份证号,提示用户检查输入 if (!ID_LIST || ID_LIST.length === 0) { alert('未能生成有效的身份证号码,请检查输入格式!'); console.error('生成身份证号失败:请检查输入格式'); return; } console.log(`已生成 ${ID_LIST.length} 个可能的身份证号码`); /** * 计算身份证校验码 * @param {string} id17 - 身份证前17位 * @returns {string} 校验码 */ function calculateCheckDigit(id17) { const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']; let total = 0; for (let i = 0; i < 17; i++) { total += parseInt(id17[i], 10) * weights[i]; } const remainder = total % 11; return checkCodes[remainder]; } /** * 判断是否为闰年 * @param {number} year - 年份 * @returns {boolean} 是否为闰年 */ function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); } /** * 生成月日未知的身份证号列表 * @param {string} addressCode - 地址码 * @param {string} birthYear - 出生年 * @param {string} sequenceCode - 顺序码 * @param {string} genderCode - 性别码 * @param {string} targetCheck - 目标校验码 * @param {string} outputMode - 输出模式 * @returns {Array|void} 身份证号列表或控制台输出 */ function idMonthDay(addressCode, birthYear, sequenceCode, genderCode, targetCheck, outputMode = 'list') { const validIds = []; const leap = isLeapYear(parseInt(birthYear, 10)); for (let month = 1; month <= 12; month++) { let maxDay; if ([4, 6, 9, 11].includes(month)) { maxDay = 30; } else if (month === 2) { maxDay = leap ? 29 : 28; } else { maxDay = 31; } for (let day = 1; day <= maxDay; day++) { const monthStr = String(month).padStart(2, '0'); const dayStr = String(day).padStart(2, '0'); const id17 = `${addressCode}${birthYear}${monthStr}${dayStr}${sequenceCode}${genderCode}`; const checkDigit = calculateCheckDigit(id17); if (checkDigit === targetCheck) { const fullId = id17 + checkDigit; validIds.push(fullId); if (outputMode === 'str') { console.log(`月份: ${monthStr}, 日期: ${dayStr}, 完整号码: ${fullId}`); } } } } if (outputMode === 'str') { console.log(`共找到 ${validIds.length} 个校验码为 ${targetCheck} 的身份证号码。`); } else if (outputMode === 'list') { return validIds; } } /** * 生成序列码未知的身份证号列表 * @param {string} addressCode - 地址码 * @param {string} birthYear - 出生年 * @param {string} birthMonth - 出生月 * @param {string} birthDay - 出生日 * @param {string} genderCode - 性别码 * @param {string} outputMode - 输出模式 * @returns {Array|void} 身份证号列表或控制台输出 */ function idFromPrefix(addressCode, birthYear, birthMonth, birthDay, genderCode, outputMode = 'list') { const validIds = []; const prefix = `${addressCode}${birthYear}${birthMonth}${birthDay}`; let sequenceRange; const genderNum = parseInt(genderCode, 10); if (!isNaN(genderNum)) { // 如果指定了性别码,只生成对应性别的号码(奇数为男,偶数为女) sequenceRange = (genderNum % 2 === 1) ? [1, 3, 5, 7, 9] : [0, 2, 4, 6, 8]; } else { // 如果未指定性别,生成所有可能的顺序码 sequenceRange = [...Array(10).keys()]; } for (let area = 1; area < 100; area++) { const areaCode = String(area).padStart(2, '0'); for (const i of sequenceRange) { const id17 = `${prefix}${areaCode}${i}`; const checkDigit = calculateCheckDigit(id17); const fullId = id17 + checkDigit; validIds.push(fullId); if (outputMode === 'str') { console.log(`月份: ${birthMonth}, 日期: ${birthDay}, 序列码:${areaCode},性别码:${i},校验码:${checkDigit}, 完整号码: ${fullId}`); } } } if (outputMode === 'str') { console.log(`共找到 ${validIds.length} 个身份证号码。`); } else if (outputMode === 'list') { return validIds; } } /** * 根据输入的身份证号格式选择生成方法 * @param {string} idNumber - 部分已知的身份证号 * @param {string} outputMode - 输出模式 * @returns {Object} 函数和参数 */ function chooseType(idNumber, outputMode = 'list') { if (idNumber.slice(10, 14) === 'xxxx') { // 处理月日未知的情况 const addressCode = idNumber.slice(0, 6); const birthYear = idNumber.slice(6, 10); const sequenceCode = idNumber.slice(14, 16); const genderCode = idNumber[16]; const checkDigit = idNumber[17]; return { func: idMonthDay, args: [addressCode, birthYear, sequenceCode, genderCode, checkDigit, outputMode] }; } else { // 处理序列码未知的情况 const addressCode = idNumber.slice(0, 6); const birthYear = idNumber.slice(6, 10); const birthMonth = idNumber.slice(10, 12); const birthDay = idNumber.slice(12, 14); const genderCode = idNumber[16] || ''; // 性别码可能未知 return { func: idFromPrefix, args: [addressCode, birthYear, birthMonth, birthDay, genderCode, outputMode] }; } } /** * 创建日志显示面板 * @returns {HTMLElement} 日志面板DOM元素 */ function createLogDisplay() { // 如果已存在日志面板,直接返回 const existingLog = document.getElementById('scriptLog'); if (existingLog) return existingLog; const logDiv = document.createElement('div'); logDiv.id = 'scriptLog'; logDiv.style.cssText = ` position: fixed; top: 10px; left: 10px; width: 400px; height: 600px; background: rgba(0, 0, 0, 0.8); color: #fff; padding: 10px; border-radius: 5px; font-size: 14px; z-index: 9999; display: flex; flex-direction: column; box-shadow: 0 0 10px rgba(0,0,0,0.5); `; // 创建标题栏 const titleBar = document.createElement('div'); titleBar.style.cssText = ` display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px solid rgba(255,255,255,0.3); `; const title = document.createElement('div'); title.textContent = '自动填写身份证号码 - 状态面板'; title.style.fontWeight = 'bold'; const closeButton = document.createElement('button'); closeButton.textContent = '隐藏'; closeButton.style.cssText = ` background: #333; color: white; border: none; padding: 3px 8px; border-radius: 3px; cursor: pointer; `; closeButton.onclick = function() { logDiv.style.height = '30px'; logDiv.style.width = '200px'; closeButton.textContent = '显示'; closeButton.onclick = function() { logDiv.style.height = '600px'; logDiv.style.width = '400px'; closeButton.textContent = '隐藏'; closeButton.onclick = arguments.callee.caller; }; }; titleBar.appendChild(title); titleBar.appendChild(closeButton); // 创建状态面板 const statusPanel = document.createElement('div'); statusPanel.id = 'statusPanel'; statusPanel.style.cssText = ` margin-bottom: 10px; padding: 10px; background: rgba(255, 255, 255, 0.1); border-radius: 5px; `; const currentIndex = GM_getValue('currentIndex', 0); const nextId = currentIndex < ID_LIST.length ? ID_LIST[currentIndex] : '已用完'; statusPanel.innerHTML = `
乘客: ${username}
身份证号: ${lastId}
`; document.body.appendChild(successNotice); document.getElementById('closeSuccess').addEventListener('click', () => { successNotice.remove(); }); hasLoggedError = true; } } }); } // 首次检查延迟 setTimeout(checkAndClick, 3000); // 设置页面变化监听 const observer = new MutationObserver((mutations) => { if (currentIndex < ID_LIST.length && !GM_getValue('isPaused', false)) { setTimeout(checkAndClick, 3000); } }); const config = { childList: true, subtree: true }; observer.observe(document.body, config); } })();