└── cursor-auto-fill.user.js /cursor-auto-fill.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Cursor试用生成绑卡自动填写 3 | // @namespace http://tampermonkey.net/ 4 | // @version 2.6.0 5 | // @description 自动填写 Cursor 试用页面的支付信息,支付失败自动重试 6 | // @author Yan 7 | // @match https://checkout.stripe.com/c/pay/* 8 | // @match https://www.google.com/* 9 | // @match https://www.baidu.com/* 10 | // @grant GM_setValue 11 | // @grant GM_getValue 12 | // @grant GM_addStyle 13 | // @grant GM_registerMenuCommand 14 | // @run-at document-end 15 | // ==/UserScript== 16 | 17 | (function() { 18 | 'use strict'; 19 | 20 | // ===== 全局状态控制 ===== 21 | let isRunning = false; // 是否正在执行 22 | let shouldStop = false; // 是否应该停止 23 | 24 | // ===== 添加CSS样式 ===== 25 | const style = document.createElement('style'); 26 | style.textContent = ` 27 | @keyframes cursor-pulse { 28 | 0%, 100% { opacity: 1; } 29 | 50% { opacity: 0.5; } 30 | } 31 | 32 | @keyframes cursor-spin { 33 | from { transform: rotate(0deg); } 34 | to { transform: rotate(360deg); } 35 | } 36 | 37 | @keyframes cursor-fade-in { 38 | from { opacity: 0; transform: translateY(10px); } 39 | to { opacity: 1; transform: translateY(0); } 40 | } 41 | 42 | #cursor-auto-fill-container * { 43 | box-sizing: border-box !important; 44 | } 45 | 46 | #cursor-auto-fill-panel { 47 | position: fixed !important; 48 | top: 24px !important; 49 | right: 24px !important; 50 | max-width: 420px !important; 51 | min-width: 360px !important; 52 | width: auto !important; 53 | background: #ffffff !important; 54 | border-radius: 20px !important; 55 | box-shadow: 0 24px 48px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.05) !important; 56 | z-index: 2147483647 !important; 57 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important; 58 | overflow: visible !important; 59 | animation: cursor-fade-in 0.4s ease !important; 60 | } 61 | 62 | #cursor-auto-fill-header { 63 | background: #ffffff !important; 64 | padding: 24px !important; 65 | color: #000000 !important; 66 | border-bottom: 1px solid #f0f0f0 !important; 67 | } 68 | 69 | #cursor-auto-fill-header-row { 70 | display: flex !important; 71 | justify-content: space-between !important; 72 | align-items: center !important;· 73 | } 74 | 75 | #cursor-auto-fill-header-title { 76 | font-size: 20px !important; 77 | font-weight: 700 !important; 78 | color: #000000 !important; 79 | letter-spacing: -0.5px !important; 80 | } 81 | 82 | #cursor-auto-fill-toggle { 83 | background: #f5f5f5 !important; 84 | border: none !important; 85 | color: #666 !important; 86 | width: 36px !important; 87 | height: 36px !important; 88 | border-radius: 10px !important; 89 | cursor: pointer !important; 90 | font-size: 22px !important; 91 | line-height: 1 !important; 92 | transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important; 93 | } 94 | 95 | #cursor-auto-fill-toggle:hover { 96 | background: #e8e8e8 !important; 97 | transform: scale(1.05) !important; 98 | } 99 | 100 | #cursor-auto-fill-content { 101 | padding: 20px 24px 24px !important; 102 | background: #fafafa !important; 103 | } 104 | 105 | .cursor-section { 106 | background: #ffffff !important; 107 | border-radius: 16px !important; 108 | padding: 20px !important; 109 | margin-bottom: 16px !important; 110 | border: 1px solid #e8e8e8 !important; 111 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; 112 | } 113 | 114 | .cursor-section:hover { 115 | border-color: #d0d0d0 !important; 116 | box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06) !important; 117 | transform: translateY(-2px) !important; 118 | } 119 | 120 | .cursor-section-title { 121 | color: #000000 !important; 122 | font-size: 15px !important; 123 | font-weight: 600 !important; 124 | margin-bottom: 16px !important; 125 | letter-spacing: -0.3px !important; 126 | padding-bottom: 12px !important; 127 | border-bottom: 1px solid #f5f5f5 !important; 128 | } 129 | 130 | .cursor-config-row { 131 | display: flex !important; 132 | justify-content: space-between !important; 133 | align-items: center !important; 134 | margin-bottom: 12px !important; 135 | } 136 | 137 | .cursor-config-row:last-child { 138 | margin-bottom: 0 !important; 139 | } 140 | 141 | .cursor-config-label { 142 | color: #333 !important; 143 | font-size: 14px !important; 144 | font-weight: 500 !important; 145 | } 146 | 147 | .cursor-toggle-switch { 148 | position: relative !important; 149 | display: inline-block !important; 150 | width: 50px !important; 151 | height: 28px !important; 152 | } 153 | 154 | .cursor-toggle-switch input { 155 | opacity: 0 !important; 156 | width: 0 !important; 157 | height: 0 !important; 158 | position: absolute !important; 159 | pointer-events: none !important; 160 | } 161 | 162 | .cursor-toggle-slider { 163 | position: absolute !important; 164 | cursor: pointer !important; 165 | top: 0 !important; 166 | left: 0 !important; 167 | right: 0 !important; 168 | bottom: 0 !important; 169 | background-color: #e0e0e0 !important; 170 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; 171 | border-radius: 28px !important; 172 | } 173 | 174 | .cursor-toggle-dot { 175 | position: absolute !important; 176 | height: 22px !important; 177 | width: 22px !important; 178 | left: 3px !important; 179 | bottom: 3px !important; 180 | background-color: white !important; 181 | border-radius: 50% !important; 182 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; 183 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; 184 | } 185 | 186 | #cursor-bin-input { 187 | width: 100% !important; 188 | padding: 14px 16px !important; 189 | border: 1.5px solid #e0e0e0 !important; 190 | border-radius: 12px !important; 191 | font-size: 14px !important; 192 | font-family: 'SF Mono', Monaco, Consolas, monospace !important; 193 | background: #ffffff !important; 194 | color: #000000 !important; 195 | transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important; 196 | } 197 | 198 | #cursor-bin-input:focus { 199 | outline: none !important; 200 | border-color: #000000 !important; 201 | box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.05) !important; 202 | } 203 | 204 | .cursor-button-group { 205 | display: flex !important; 206 | gap: 12px !important; 207 | margin-top: 20px !important; 208 | } 209 | 210 | .cursor-btn { 211 | flex: 1 !important; 212 | padding: 14px 20px !important; 213 | border: none !important; 214 | border-radius: 12px !important; 215 | font-size: 15px !important; 216 | font-weight: 600 !important; 217 | cursor: pointer !important; 218 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; 219 | letter-spacing: -0.2px !important; 220 | } 221 | 222 | .cursor-btn-start { 223 | background: #000000 !important; 224 | color: white !important; 225 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; 226 | border: none !important; 227 | } 228 | 229 | .cursor-btn-start:hover { 230 | background: #1a1a1a !important; 231 | transform: translateY(-3px) !important; 232 | box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25) !important; 233 | } 234 | 235 | .cursor-btn-start:active { 236 | transform: translateY(-1px) !important; 237 | } 238 | 239 | .cursor-btn-running { 240 | background: #007AFF !important; 241 | color: white !important; 242 | box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3) !important; 243 | border: none !important; 244 | } 245 | 246 | .cursor-btn-running:hover { 247 | background: #0066CC !important; 248 | transform: scale(1.02) !important; 249 | box-shadow: 0 6px 16px rgba(0, 122, 255, 0.4) !important; 250 | } 251 | 252 | .cursor-btn-stop { 253 | background: #ffffff !important; 254 | color: #ff3b30 !important; 255 | border: 2px solid #ff3b30 !important; 256 | box-shadow: 0 2px 8px rgba(255, 59, 48, 0.15) !important; 257 | } 258 | 259 | .cursor-btn-stop:hover { 260 | background: #fff5f5 !important; 261 | transform: translateY(-3px) !important; 262 | box-shadow: 0 6px 16px rgba(255, 59, 48, 0.3) !important; 263 | } 264 | 265 | .cursor-btn-stop:active { 266 | transform: translateY(-1px) !important; 267 | } 268 | 269 | .cursor-btn-icon { 270 | display: inline-block !important; 271 | margin-right: 6px !important; 272 | } 273 | 274 | .cursor-btn-icon.spinning { 275 | animation: cursor-spin 1s linear infinite !important; 276 | } 277 | 278 | #cursor-log-container { 279 | background: #ffffff !important; 280 | border-radius: 16px !important; 281 | padding: 20px !important; 282 | max-height: 260px !important; 283 | overflow-y: auto !important; 284 | border: 1px solid #e8e8e8 !important; 285 | } 286 | 287 | #cursor-log-title { 288 | color: #000000 !important; 289 | font-size: 15px !important; 290 | font-weight: 600 !important; 291 | margin-bottom: 16px !important; 292 | letter-spacing: -0.3px !important; 293 | padding-bottom: 12px !important; 294 | border-bottom: 1px solid #f5f5f5 !important; 295 | } 296 | 297 | #cursor-auto-fill-logs { 298 | font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace !important; 299 | font-size: 12px !important; 300 | line-height: 2 !important; 301 | } 302 | 303 | #cursor-auto-fill-logs::-webkit-scrollbar { 304 | width: 6px !important; 305 | } 306 | 307 | #cursor-auto-fill-logs::-webkit-scrollbar-track { 308 | background: transparent !important; 309 | } 310 | 311 | #cursor-auto-fill-logs::-webkit-scrollbar-thumb { 312 | background: #d8d8d8 !important; 313 | border-radius: 10px !important; 314 | transition: background 0.2s !important; 315 | } 316 | 317 | #cursor-auto-fill-logs::-webkit-scrollbar-thumb:hover { 318 | background: #b8b8b8 !important; 319 | } 320 | 321 | .cursor-log-item { 322 | margin: 8px 0 !important; 323 | padding: 0 !important; 324 | animation: cursor-fade-in 0.3s ease !important; 325 | } 326 | 327 | .cursor-log-timestamp { 328 | color: #999 !important; 329 | font-weight: 500 !important; 330 | margin-right: 8px !important; 331 | } 332 | 333 | .cursor-log-info .cursor-log-text { 334 | color: #007AFF !important; 335 | } 336 | 337 | .cursor-log-success .cursor-log-text { 338 | color: #34C759 !important; 339 | font-weight: 600 !important; 340 | } 341 | 342 | .cursor-log-error .cursor-log-text { 343 | color: #FF3B30 !important; 344 | font-weight: 600 !important; 345 | } 346 | 347 | .cursor-log-warning .cursor-log-text { 348 | color: #FF9500 !important; 349 | font-weight: 600 !important; 350 | } 351 | 352 | .cursor-status-badge { 353 | display: inline-block !important; 354 | padding: 6px 12px !important; 355 | border-radius: 8px !important; 356 | font-size: 13px !important; 357 | font-weight: 600 !important; 358 | margin-top: 12px !important; 359 | letter-spacing: -0.2px !important; 360 | } 361 | 362 | .cursor-status-idle { 363 | background: #f5f5f5 !important; 364 | color: #666 !important; 365 | } 366 | 367 | .cursor-status-running { 368 | background: #e3f2fd !important; 369 | color: #007AFF !important; 370 | animation: cursor-pulse 2s ease-in-out infinite !important; 371 | } 372 | 373 | .cursor-status-stopped { 374 | background: #ffebee !important; 375 | color: #ff3b30 !important; 376 | } 377 | `; 378 | document.head.appendChild(style); 379 | 380 | // ===== 配置管理 ===== 381 | const CONFIG_KEY = 'cursor_auto_fill_config'; 382 | const QUEUE_KEY = 'cursor_url_queue'; 383 | const defaultConfig = { 384 | autoFill: true, 385 | autoSubmit: true, 386 | bin: '379240xxxxxxxxx', 387 | batchMode: false, 388 | maxConcurrent: 1 // 单页面模式 389 | }; 390 | 391 | function getConfig() { 392 | const saved = GM_getValue(CONFIG_KEY); 393 | return saved ? JSON.parse(saved) : defaultConfig; 394 | } 395 | 396 | function saveConfig(config) { 397 | GM_setValue(CONFIG_KEY, JSON.stringify(config)); 398 | } 399 | 400 | let config = getConfig(); 401 | 402 | // ===== URL队列管理 ===== 403 | class URLQueue { 404 | constructor() { 405 | this.queue = this.load(); 406 | } 407 | 408 | load() { 409 | const saved = GM_getValue(QUEUE_KEY); 410 | return saved ? JSON.parse(saved) : { urls: [], current: 0, status: 'idle' }; 411 | } 412 | 413 | save() { 414 | GM_setValue(QUEUE_KEY, JSON.stringify(this.queue)); 415 | } 416 | 417 | addURLs(urlText) { 418 | const urls = urlText.split('\n') 419 | .map(url => url.trim()) 420 | .filter(url => url && url.startsWith('http')); 421 | 422 | this.queue = { 423 | urls: urls.map(url => ({ url, status: 'pending', timestamp: Date.now() })), 424 | current: 0, 425 | status: 'idle' 426 | }; 427 | this.save(); 428 | logger.log(`已添加 ${urls.length} 个URL到队列`, 'success'); 429 | return urls.length; 430 | } 431 | 432 | getNext(count = 5) { 433 | const pending = this.queue.urls.filter(item => item.status === 'pending'); 434 | const next = pending.slice(0, count); 435 | next.forEach(item => item.status = 'processing'); 436 | this.save(); 437 | return next.map(item => item.url); 438 | } 439 | 440 | markCompleted(url) { 441 | const item = this.queue.urls.find(item => item.url === url); 442 | if (item) { 443 | item.status = 'completed'; 444 | this.queue.current++; 445 | this.save(); 446 | } 447 | } 448 | 449 | getStats() { 450 | const total = this.queue.urls.length; 451 | const completed = this.queue.urls.filter(item => item.status === 'completed').length; 452 | const processing = this.queue.urls.filter(item => item.status === 'processing').length; 453 | const pending = this.queue.urls.filter(item => item.status === 'pending').length; 454 | return { total, completed, processing, pending }; 455 | } 456 | 457 | clear() { 458 | this.queue = { urls: [], current: 0, status: 'idle' }; 459 | this.save(); 460 | } 461 | 462 | isComplete() { 463 | return this.queue.urls.length > 0 && 464 | this.queue.urls.every(item => item.status === 'completed'); 465 | } 466 | } 467 | 468 | const urlQueue = new URLQueue(); 469 | 470 | // ===== 日志系统 ===== 471 | class Logger { 472 | constructor() { 473 | this.logs = []; 474 | this.maxLogs = 100; 475 | } 476 | 477 | log(message, type = 'info') { 478 | const timestamp = new Date().toLocaleTimeString(); 479 | const logEntry = { timestamp, message, type }; 480 | this.logs.push(logEntry); 481 | if (this.logs.length > this.maxLogs) { 482 | this.logs.shift(); 483 | } 484 | console.log(`[Cursor Auto Fill] ${timestamp} [${type.toUpperCase()}] ${message}`); 485 | this.updateLogDisplay(); 486 | this.autoScroll(); 487 | } 488 | 489 | updateLogDisplay() { 490 | const logContainer = document.getElementById('cursor-auto-fill-logs'); 491 | if (logContainer) { 492 | logContainer.innerHTML = this.logs.map(log => 493 | `
494 | ${log.timestamp} 495 | ${log.message} 496 |
` 497 | ).join(''); 498 | } 499 | } 500 | 501 | autoScroll() { 502 | const logContainer = document.getElementById('cursor-log-container'); 503 | if (logContainer) { 504 | // 丝滑滚动到底部 505 | logContainer.scrollTo({ 506 | top: logContainer.scrollHeight, 507 | behavior: 'smooth' 508 | }); 509 | } 510 | } 511 | } 512 | 513 | const logger = new Logger(); 514 | 515 | // ===== 更新UI状态 ===== 516 | function updateUIState(state) { 517 | const actionBtn = document.getElementById('cursor-action-btn'); 518 | const btnIcon = document.getElementById('cursor-btn-icon'); 519 | const btnText = document.getElementById('cursor-btn-text'); 520 | const statusBadge = document.getElementById('cursor-status-badge'); 521 | 522 | if (state === 'running') { 523 | // 执行中状态 524 | if (actionBtn) { 525 | actionBtn.className = 'cursor-btn cursor-btn-running'; 526 | } 527 | if (btnIcon) { 528 | btnIcon.className = 'cursor-btn-icon spinning'; 529 | btnIcon.textContent = '⏸'; 530 | } 531 | if (btnText) btnText.textContent = '执行中...'; 532 | if (statusBadge) { 533 | statusBadge.className = 'cursor-status-badge cursor-status-running'; 534 | statusBadge.textContent = '● 执行中'; 535 | statusBadge.style.setProperty('background', '#e3f2fd', 'important'); 536 | statusBadge.style.setProperty('color', '#007AFF', 'important'); 537 | } 538 | } else if (state === 'stopped') { 539 | // 已停止状态 540 | if (actionBtn) { 541 | actionBtn.className = 'cursor-btn cursor-btn-stop'; 542 | } 543 | if (btnIcon) { 544 | btnIcon.className = 'cursor-btn-icon'; 545 | btnIcon.textContent = '■'; 546 | } 547 | if (btnText) btnText.textContent = '已停止'; 548 | if (statusBadge) { 549 | statusBadge.className = 'cursor-status-badge cursor-status-stopped'; 550 | statusBadge.textContent = '● 已停止'; 551 | statusBadge.style.setProperty('background', '#ffebee', 'important'); 552 | statusBadge.style.setProperty('color', '#ff3b30', 'important'); 553 | } 554 | } else { 555 | // 待机状态 556 | if (actionBtn) { 557 | actionBtn.className = 'cursor-btn cursor-btn-start'; 558 | } 559 | if (btnIcon) { 560 | btnIcon.className = 'cursor-btn-icon'; 561 | btnIcon.textContent = '▶'; 562 | } 563 | if (btnText) btnText.textContent = '开始填写'; 564 | if (statusBadge) { 565 | statusBadge.className = 'cursor-status-badge cursor-status-idle'; 566 | statusBadge.textContent = '○ 待机中'; 567 | statusBadge.style.setProperty('background', '#f5f5f5', 'important'); 568 | statusBadge.style.setProperty('color', '#666', 'important'); 569 | } 570 | } 571 | } 572 | 573 | // ===== Luhn算法生成卡号 ===== 574 | function generateCardNumber(bin) { 575 | logger.log('开始生成卡号...', 'info'); 576 | 577 | let cardNumber = ''; 578 | for (let char of bin) { 579 | if (char.toLowerCase() === 'x') { 580 | cardNumber += Math.floor(Math.random() * 10); 581 | } else { 582 | cardNumber += char; 583 | } 584 | } 585 | 586 | cardNumber = cardNumber.slice(0, -1); 587 | 588 | let sum = 0; 589 | let shouldDouble = true; 590 | 591 | for (let i = cardNumber.length - 1; i >= 0; i--) { 592 | let digit = parseInt(cardNumber[i]); 593 | 594 | if (shouldDouble) { 595 | digit *= 2; 596 | if (digit > 9) { 597 | digit -= 9; 598 | } 599 | } 600 | 601 | sum += digit; 602 | shouldDouble = !shouldDouble; 603 | } 604 | 605 | const checkDigit = (10 - (sum % 10)) % 10; 606 | cardNumber += checkDigit; 607 | 608 | logger.log(`卡号生成成功: ${cardNumber.replace(/(\d{4})/g, '$1 ').trim()}`, 'success'); 609 | return cardNumber; 610 | } 611 | 612 | // ===== 生成随机卡片信息 ===== 613 | function generateCardInfo() { 614 | // 每次都随机生成379240开头的卡号 615 | const cardNumber = generateCardNumber(config.bin); 616 | 617 | const now = new Date(); 618 | const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0'); 619 | const year = String(now.getFullYear() + Math.floor(Math.random() * 2) + 1).slice(-2); 620 | 621 | // AmEx卡(34/37开头)CVV是4位,其他卡是3位 622 | const cardPrefix = cardNumber.substring(0, 2); 623 | const isAmex = cardPrefix === '34' || cardPrefix === '37'; 624 | 625 | logger.log(`🔍 卡号前缀: ${cardPrefix}, 是否AmEx: ${isAmex}`, 'info'); 626 | 627 | const cvv = isAmex 628 | ? String(Math.floor(Math.random() * 9000) + 1000) // 4位:1000-9999 629 | : String(Math.floor(Math.random() * 900) + 100); // 3位:100-999 630 | 631 | logger.log(`✅ 生成到期日: ${month}/${year}, CVV: ${cvv} (${isAmex ? 'AmEx-4位' : '普通-3位'})`, 'success'); 632 | 633 | return { cardNumber, month, year, cvv }; 634 | } 635 | 636 | // ===== 生成随机美国地址 ===== 637 | function generateUSAddress() { 638 | logger.log('生成随机美国地址...', 'info'); 639 | 640 | const firstNames = ['James', 'John', 'Robert', 'Michael', 'William', 'David', 'Richard', 'Joseph', 'Thomas', 'Charles']; 641 | const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez']; 642 | 643 | const streets = ['Main St', 'Oak Ave', 'Pine St', 'Maple Ave', 'Cedar St', 'Elm St', 'Washington St', 'Lake St', 'Hill St', 'Park Ave']; 644 | const cities = [ 645 | { name: 'New York', state: 'New York', zip: '10001' }, 646 | { name: 'Los Angeles', state: 'California', zip: '90001' }, 647 | { name: 'Chicago', state: 'Illinois', zip: '60601' }, 648 | { name: 'Houston', state: 'Texas', zip: '77001' }, 649 | { name: 'Phoenix', state: 'Arizona', zip: '85001' }, 650 | { name: 'Philadelphia', state: 'Pennsylvania', zip: '19101' }, 651 | { name: 'San Antonio', state: 'Texas', zip: '78201' }, 652 | { name: 'San Diego', state: 'California', zip: '92101' }, 653 | { name: 'Dallas', state: 'Texas', zip: '75201' }, 654 | { name: 'San Jose', state: 'California', zip: '95101' } 655 | ]; 656 | 657 | const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]; 658 | const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]; 659 | const fullName = `${firstName} ${lastName}`; 660 | 661 | const streetNumber = Math.floor(Math.random() * 9000) + 1000; 662 | const street = streets[Math.floor(Math.random() * streets.length)]; 663 | const address1 = `${streetNumber} ${street}`; 664 | 665 | const cityInfo = cities[Math.floor(Math.random() * cities.length)]; 666 | 667 | logger.log(`地址生成成功: ${fullName}, ${address1}, ${cityInfo.name}, ${cityInfo.state}`, 'success'); 668 | 669 | return { 670 | name: fullName, 671 | address1: address1, 672 | city: cityInfo.name, 673 | state: cityInfo.state, 674 | zip: cityInfo.zip 675 | }; 676 | } 677 | 678 | // ===== 等待元素出现 ===== 679 | function waitForElement(selector, timeout = 10000) { 680 | return new Promise((resolve, reject) => { 681 | if (shouldStop) { 682 | reject(new Error('用户停止执行')); 683 | return; 684 | } 685 | 686 | const element = document.querySelector(selector); 687 | if (element) { 688 | return resolve(element); 689 | } 690 | 691 | const observer = new MutationObserver(() => { 692 | if (shouldStop) { 693 | observer.disconnect(); 694 | reject(new Error('用户停止执行')); 695 | return; 696 | } 697 | 698 | const element = document.querySelector(selector); 699 | if (element) { 700 | observer.disconnect(); 701 | resolve(element); 702 | } 703 | }); 704 | 705 | observer.observe(document.body, { 706 | childList: true, 707 | subtree: true 708 | }); 709 | 710 | setTimeout(() => { 711 | observer.disconnect(); 712 | reject(new Error(`等待元素超时: ${selector}`)); 713 | }, timeout); 714 | }); 715 | } 716 | 717 | // ===== 等待一段时间 ===== 718 | function sleep(ms) { 719 | return new Promise(resolve => { 720 | if (shouldStop) { 721 | resolve(); 722 | return; 723 | } 724 | setTimeout(resolve, ms); 725 | }); 726 | } 727 | 728 | // ===== 填充输入框(使用setter触发React)===== 729 | async function fillInput(selector, value, label) { 730 | if (shouldStop) { 731 | throw new Error('用户停止执行'); 732 | } 733 | 734 | try { 735 | logger.log(`填写${label}...`, 'info'); 736 | const input = await waitForElement(selector, 8000); 737 | 738 | // 后台模式下减少延迟 739 | const isBackground = document.hidden; 740 | const delay = isBackground ? 5 : 15; // 后台模式下加快速度 741 | 742 | input.focus(); 743 | await sleep(isBackground ? 30 : 100); 744 | 745 | const nativeInputValueSetter = Object.getOwnPropertyDescriptor( 746 | window.HTMLInputElement.prototype, 747 | 'value' 748 | ).set; 749 | 750 | nativeInputValueSetter.call(input, ''); 751 | input.dispatchEvent(new Event('input', { bubbles: true })); 752 | await sleep(isBackground ? 20 : 50); 753 | 754 | for (let i = 0; i < value.length; i++) { 755 | if (shouldStop) throw new Error('用户停止执行'); 756 | 757 | const currentValue = value.substring(0, i + 1); 758 | nativeInputValueSetter.call(input, currentValue); 759 | 760 | input.dispatchEvent(new KeyboardEvent('keydown', { 761 | bubbles: true, 762 | cancelable: true, 763 | key: value[i], 764 | code: `Key${value[i].toUpperCase()}` 765 | })); 766 | 767 | input.dispatchEvent(new Event('input', { 768 | bubbles: true, 769 | cancelable: true 770 | })); 771 | 772 | input.dispatchEvent(new KeyboardEvent('keyup', { 773 | bubbles: true, 774 | key: value[i], 775 | code: `Key${value[i].toUpperCase()}` 776 | })); 777 | 778 | await sleep(delay); 779 | } 780 | 781 | await sleep(isBackground ? 50 : 100); 782 | input.dispatchEvent(new Event('change', { bubbles: true })); 783 | await sleep(isBackground ? 50 : 100); 784 | 785 | if (input.value !== value) { 786 | nativeInputValueSetter.call(input, value); 787 | input.dispatchEvent(new Event('input', { bubbles: true })); 788 | await sleep(isBackground ? 50 : 100); 789 | } 790 | 791 | input.blur(); 792 | await sleep(isBackground ? 200 : 400); 793 | 794 | const actualValue = input.value; 795 | const normalizedActual = actualValue.replace(/[\s\/\-]/g, ''); 796 | const normalizedExpected = value.replace(/[\s\/\-]/g, ''); 797 | 798 | if (normalizedActual === normalizedExpected || actualValue === value) { 799 | logger.log(`✓ ${label}填写完成: ${actualValue}`, 'success'); 800 | } else { 801 | logger.log(`⚠ ${label}值不匹配 (期望:${value}, 实际:${actualValue})`, 'warning'); 802 | } 803 | 804 | return true; 805 | } catch (error) { 806 | if (error.message === '用户停止执行') { 807 | throw error; 808 | } 809 | logger.log(`❌ 填写${label}失败: ${error.message}`, 'error'); 810 | return false; 811 | } 812 | } 813 | 814 | // ===== 填写表单 ===== 815 | async function fillForm() { 816 | // 防止重复执行 817 | if (isRunning) { 818 | logger.log('⚠ 正在执行中,请勿重复点击', 'warning'); 819 | return; 820 | } 821 | 822 | isRunning = true; 823 | shouldStop = false; 824 | updateUIState('running'); 825 | 826 | try { 827 | logger.log('========== 开始自动填写 ==========', 'info'); 828 | 829 | await sleep(500); 830 | 831 | // 1. 检查并展开银行卡区域 832 | logger.log('检查银行卡区域状态...', 'info'); 833 | 834 | const cardRadio = document.querySelector('input[type="radio"][value="card"]'); 835 | const isAlreadyExpanded = cardRadio && cardRadio.checked; 836 | 837 | if (isAlreadyExpanded) { 838 | logger.log('✓ 银行卡区域已展开', 'success'); 839 | await sleep(200); 840 | } else { 841 | logger.log('点击展开银行卡区域...', 'info'); 842 | const cardButton = document.querySelector('[data-testid="card-accordion-item-button"]'); 843 | 844 | if (cardButton) { 845 | cardButton.click(); 846 | await sleep(800); 847 | 848 | const radioAfterClick = document.querySelector('input[type="radio"][value="card"]'); 849 | if (radioAfterClick && radioAfterClick.checked) { 850 | logger.log('✓ 银行卡区域已展开', 'success'); 851 | } else { 852 | throw new Error('银行卡区域展开失败'); 853 | } 854 | } else { 855 | throw new Error('未找到银行卡展开按钮'); 856 | } 857 | } 858 | 859 | if (shouldStop) throw new Error('用户停止执行'); 860 | 861 | // 等待输入框渲染 862 | logger.log('等待输入框渲染...', 'info'); 863 | await waitForElement('input[name="number"], input[placeholder*="卡号"], input[autocomplete="cc-number"]', 5000); 864 | logger.log('✓ 输入框已就绪', 'success'); 865 | 866 | // 生成信息 867 | const cardInfo = generateCardInfo(); 868 | const address = generateUSAddress(); 869 | 870 | // 2. 填写卡号 871 | await fillInput( 872 | 'input[name="number"], input[placeholder*="卡号"], input[autocomplete="cc-number"]', 873 | cardInfo.cardNumber, 874 | '卡号' 875 | ); 876 | 877 | // 3. 填写到期日 878 | await fillInput( 879 | 'input[name="expiry"], input[placeholder*="到期"], input[autocomplete="cc-exp"]', 880 | `${cardInfo.month}${cardInfo.year}`, 881 | '到期日' 882 | ); 883 | 884 | // 4. 填写CVV 885 | await fillInput( 886 | 'input[name="cvc"], input[placeholder*="CVC"], input[placeholder*="安全码"], input[autocomplete="cc-csc"]', 887 | cardInfo.cvv, 888 | 'CVV' 889 | ); 890 | 891 | // 5. 填写持卡人姓名 892 | await fillInput( 893 | 'input[name="name"], input[placeholder*="姓名"], input[autocomplete="cc-name"]', 894 | address.name, 895 | '持卡人姓名' 896 | ); 897 | 898 | if (shouldStop) throw new Error('用户停止执行'); 899 | 900 | // 6. 点击"手动输入地址"按钮(如果存在) 901 | logger.log('查找"手动输入地址"按钮...', 'info'); 902 | const manualAddressButton = Array.from(document.querySelectorAll('button')).find(btn => 903 | btn.textContent.includes('手动输入地址') || 904 | btn.textContent.includes('Enter address manually') 905 | ); 906 | 907 | if (manualAddressButton) { 908 | logger.log('点击"手动输入地址"...', 'info'); 909 | manualAddressButton.click(); 910 | await sleep(500); 911 | logger.log('✓ 已展开手动输入', 'success'); 912 | } 913 | 914 | // 7. 选择国家 - 美国 915 | logger.log('选择国家:美国...', 'info'); 916 | const allSelects = document.querySelectorAll('select'); 917 | const countrySelect = document.querySelector('select[name="billingCountry"]') || allSelects[0]; 918 | 919 | if (countrySelect) { 920 | logger.log(`当前国家: ${countrySelect.value}`, 'info'); 921 | 922 | let usOption = null; 923 | for (let option of countrySelect.options) { 924 | if (option.value === 'US' || option.textContent.trim() === '美国') { 925 | usOption = option; 926 | break; 927 | } 928 | } 929 | 930 | if (usOption) { 931 | countrySelect.value = usOption.value; 932 | countrySelect.dispatchEvent(new Event('input', { bubbles: true })); 933 | countrySelect.dispatchEvent(new Event('change', { bubbles: true })); 934 | await sleep(1500); 935 | logger.log('✓ 已选择美国', 'success'); 936 | } 937 | } 938 | 939 | if (shouldStop) throw new Error('用户停止执行'); 940 | 941 | // 8. 填写地址 942 | await fillInput( 943 | 'input[name="line1"], input[placeholder*="地址"]', 944 | address.address1, 945 | '地址' 946 | ); 947 | 948 | // 9. 填写城市 949 | await fillInput( 950 | 'input[name="city"], input[placeholder*="城市"]', 951 | address.city, 952 | '城市' 953 | ); 954 | 955 | // 10. 填写邮编 956 | await fillInput( 957 | 'input[name="zip"], input[placeholder*="邮编"]', 958 | address.zip, 959 | '邮编' 960 | ); 961 | 962 | await sleep(800); 963 | 964 | // 11. 检查州 965 | const allSelectsAfter = document.querySelectorAll('select'); 966 | const stateSelect = allSelectsAfter.length > 1 ? allSelectsAfter[1] : null; 967 | 968 | if (stateSelect && stateSelect.value) { 969 | logger.log(`✓ 州已自动选择: ${stateSelect.value}`, 'success'); 970 | } 971 | 972 | logger.log('========== 所有字段填写完成 ==========', 'success'); 973 | 974 | if (shouldStop) throw new Error('用户停止执行'); 975 | 976 | // 检查并提交,等待跳转完成 977 | const submitted = await checkAndSubmit(); 978 | return submitted; 979 | 980 | } catch (error) { 981 | if (error.message === '用户停止执行') { 982 | logger.log('========== 执行已停止 ==========', 'warning'); 983 | updateUIState('stopped'); 984 | } else { 985 | logger.log(`❌ 错误: ${error.message}`, 'error'); 986 | updateUIState('idle'); 987 | } 988 | return false; 989 | } finally { 990 | isRunning = false; 991 | } 992 | } 993 | 994 | // ===== 此函数已废弃,不再使用 ===== 995 | // function waitForNavigation() - 已移除,点击提交后直接2秒关闭 996 | 997 | // ===== 检查并提交 ===== 998 | async function checkAndSubmit() { 999 | try { 1000 | logger.log('检查提交按钮状态...', 'info'); 1001 | 1002 | const submitButton = document.querySelector('button[type="submit"]'); 1003 | if (!submitButton) { 1004 | logger.log('未找到提交按钮', 'warning'); 1005 | updateUIState('idle'); 1006 | return false; 1007 | } 1008 | 1009 | const checkButtonReady = () => { 1010 | const isBasicReady = !submitButton.disabled && 1011 | !submitButton.hasAttribute('disabled') && 1012 | submitButton.offsetParent !== null; 1013 | 1014 | const processingText = submitButton.querySelector('[class*="processing"]'); 1015 | const isProcessing = processingText && processingText.offsetParent !== null; 1016 | 1017 | const classList = submitButton.className; 1018 | const hasDisabledClass = classList.includes('disabled') || classList.includes('Disabled'); 1019 | 1020 | return isBasicReady && !isProcessing && !hasDisabledClass; 1021 | }; 1022 | 1023 | logger.log('快速检测按钮状态(最多5秒)...', 'info'); 1024 | 1025 | // 等待按钮就绪 - 优化:每次只等0.2秒 1026 | let attempts = 0; 1027 | const maxAttempts = 25; // 25次 × 0.2秒 = 5秒 1028 | 1029 | while (attempts < maxAttempts && !checkButtonReady()) { 1030 | if (shouldStop) { 1031 | updateUIState('stopped'); 1032 | return false; 1033 | } 1034 | await sleep(200); // 每0.2秒检测一次,提速5倍 1035 | attempts++; 1036 | } 1037 | 1038 | if (!checkButtonReady()) { 1039 | logger.log('⚠ 按钮未就绪,但继续尝试提交', 'warning'); 1040 | } else { 1041 | logger.log(`✓ 提交按钮已就绪!(用时 ${(attempts * 0.2).toFixed(1)}秒)`, 'success'); 1042 | } 1043 | 1044 | if (config.autoSubmit) { 1045 | logger.log('立即提交...', 'info'); 1046 | 1047 | if (!shouldStop) { 1048 | submitButton.click(); 1049 | logger.log('✓ 已点击提交按钮!', 'success'); 1050 | 1051 | // 等待3秒检测支付结果 1052 | await sleep(3000); 1053 | 1054 | // 检测是否支付失败 1055 | if (isPaymentFailed()) { 1056 | logger.log('❌❌❌ 支付失败,需要重试!', 'error'); 1057 | updateUIState('idle'); 1058 | return false; // 返回false表示失败,需要重试 1059 | } 1060 | 1061 | logger.log('✅ 提交完成!', 'success'); 1062 | updateUIState('idle'); 1063 | 1064 | // 标记当前URL完成并跳转到下一个 1065 | await jumpToNextURL(); 1066 | 1067 | return true; 1068 | } 1069 | } else { 1070 | logger.log('自动提交未启用,请手动点击"开始试用"', 'info'); 1071 | updateUIState('idle'); 1072 | return false; 1073 | } 1074 | 1075 | } catch (error) { 1076 | logger.log(`检查提交按钮时出错: ${error.message}`, 'error'); 1077 | updateUIState('idle'); 1078 | return false; 1079 | } 1080 | } 1081 | 1082 | // ===== 停止执行 ===== 1083 | function stopExecution() { 1084 | shouldStop = true; 1085 | logger.log('正在停止执行...', 'warning'); 1086 | updateUIState('stopped'); 1087 | 1088 | setTimeout(() => { 1089 | shouldStop = false; 1090 | updateUIState('idle'); 1091 | }, 2000); 1092 | } 1093 | 1094 | // ===== 检测支付是否失败 ===== 1095 | function isPaymentFailed() { 1096 | const pageText = document.body.textContent || document.body.innerText || ''; 1097 | const pageHTML = document.body.innerHTML || ''; 1098 | 1099 | // 检测"支付失败"相关文本 1100 | const failedIndicators = [ 1101 | '您的卡被拒绝', 1102 | '卡被拒绝', 1103 | '支付失败', 1104 | '付款失败', 1105 | 'card was declined', 1106 | 'payment failed', 1107 | 'card declined', 1108 | 'declined', 1109 | 'was declined', 1110 | '交易失败', 1111 | '无法处理', 1112 | 'cannot process', 1113 | 'unable to process' 1114 | ]; 1115 | 1116 | for (const indicator of failedIndicators) { 1117 | if (pageText.includes(indicator) || pageHTML.includes(indicator)) { 1118 | logger.log(`❌ 检测到支付失败: "${indicator}"`, 'error'); 1119 | return true; 1120 | } 1121 | } 1122 | 1123 | return false; 1124 | } 1125 | 1126 | // ===== 检测页面是否已完成支付 ===== 1127 | function isAlreadyCompleted() { 1128 | const pageText = document.body.textContent || document.body.innerText || ''; 1129 | const pageHTML = document.body.innerHTML || ''; 1130 | 1131 | logger.log('检测页面是否已被使用...', 'info'); 1132 | 1133 | // 检测"已完成"相关文本(中英文) 1134 | const completedIndicators = [ 1135 | '您已全部完成', 1136 | '您已经完成付款', 1137 | '本结账会话已超时', 1138 | '结账会话已超时', 1139 | 'already completed', 1140 | 'payment completed', 1141 | 'session expired', 1142 | 'checkout session has expired', 1143 | 'session has expired', 1144 | '会话已超时', 1145 | '已超时' 1146 | ]; 1147 | 1148 | for (const indicator of completedIndicators) { 1149 | if (pageText.includes(indicator) || pageHTML.includes(indicator)) { 1150 | logger.log(`✓✓✓ 检测到已使用标志: "${indicator}"`, 'success'); 1151 | return true; 1152 | } 1153 | } 1154 | 1155 | // 检查是否没有输入框(说明不是正常的支付页面) 1156 | const hasInputs = document.querySelectorAll('input[type="text"], input[autocomplete]').length > 0; 1157 | const hasSubmitButton = document.querySelector('button[type="submit"]') !== null; 1158 | 1159 | if (!hasInputs && !hasSubmitButton) { 1160 | logger.log('✓ 页面没有输入框和提交按钮,可能已使用', 'info'); 1161 | return true; 1162 | } 1163 | 1164 | logger.log('页面正常,未检测到已使用标志', 'info'); 1165 | return false; 1166 | } 1167 | 1168 | // ===== 检测Cursor试用页面 ===== 1169 | function isCursorTrialPage() { 1170 | if (!window.location.href.includes('checkout.stripe.com')) { 1171 | return false; 1172 | } 1173 | 1174 | const title = document.title; 1175 | if (title.includes('Cursor')) { 1176 | logger.log('✓ 检测到 Cursor 试用页面', 'success'); 1177 | return true; 1178 | } 1179 | 1180 | const pageText = document.body.textContent; 1181 | if (pageText.includes('Cursor Ultra') || pageText.includes('试用 Cursor')) { 1182 | logger.log('✓ 检测到 Cursor 试用页面', 'success'); 1183 | return true; 1184 | } 1185 | 1186 | return false; 1187 | } 1188 | 1189 | // ===== 等待页面加载 ===== 1190 | function waitForPageLoad() { 1191 | return new Promise((resolve) => { 1192 | let attempts = 0; 1193 | const maxAttempts = 10; // 改为5秒超时 1194 | 1195 | const checkInterval = setInterval(() => { 1196 | attempts++; 1197 | 1198 | if (isCursorTrialPage()) { 1199 | clearInterval(checkInterval); 1200 | logger.log(`✓ 检测到Cursor页面 (用时${attempts}秒)`, 'success'); 1201 | resolve(true); 1202 | return; 1203 | } 1204 | 1205 | if (attempts >= maxAttempts) { 1206 | clearInterval(checkInterval); 1207 | logger.log('✓ 页面检测完成,继续执行', 'info'); 1208 | resolve(true); // 改为总是返回true,不阻塞 1209 | } 1210 | }, 1000); // 每秒检测一次 1211 | }); 1212 | } 1213 | 1214 | // ===== 创建UI界面 ===== 1215 | function createUI() { 1216 | const oldUI = document.getElementById('cursor-auto-fill-container'); 1217 | if (oldUI) { 1218 | oldUI.remove(); 1219 | } 1220 | 1221 | const container = document.createElement('div'); 1222 | container.id = 'cursor-auto-fill-container'; 1223 | 1224 | container.innerHTML = ` 1225 |
1226 |
1227 |
1228 |
Cursor 自动填写
1229 | 1230 |
1231 |
○ 待机中
1232 |
1233 |
1234 |
1235 |
功能设置
1236 |
1237 | 1238 | 1244 |
1245 |
1246 | 1247 | 1253 |
1254 |
1255 | 1256 |
1257 |
BIN 配置
1258 | 1259 |
1260 | 1261 |
1262 |
批量处理
1263 |
1264 | 1265 | 1268 |
1269 |
1270 | 1271 | 1272 |
1273 | 1276 |
1277 | 1278 |
1279 | 1283 |
1284 | 1285 |
1286 |
执行日志
1287 |
1288 |
1289 |
1290 |
1291 | `; 1292 | 1293 | document.body.appendChild(container); 1294 | 1295 | // JavaScript强制设置所有样式 1296 | const panel = document.getElementById('cursor-auto-fill-panel'); 1297 | if (panel) { 1298 | panel.style.setProperty('position', 'fixed', 'important'); 1299 | panel.style.setProperty('top', '24px', 'important'); 1300 | panel.style.setProperty('right', '24px', 'important'); 1301 | panel.style.setProperty('max-width', '420px', 'important'); 1302 | panel.style.setProperty('min-width', '360px', 'important'); 1303 | panel.style.setProperty('width', 'auto', 'important'); 1304 | panel.style.setProperty('background', '#ffffff', 'important'); 1305 | panel.style.setProperty('border-radius', '20px', 'important'); 1306 | panel.style.setProperty('box-shadow', '0 24px 48px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.05)', 'important'); 1307 | panel.style.setProperty('z-index', '2147483647', 'important'); 1308 | panel.style.setProperty('overflow', 'visible', 'important'); 1309 | panel.style.setProperty('font-family', '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', 'important'); 1310 | } 1311 | 1312 | const header = document.getElementById('cursor-auto-fill-header'); 1313 | if (header) { 1314 | header.style.setProperty('background', '#ffffff', 'important'); 1315 | header.style.setProperty('padding', '24px', 'important'); 1316 | header.style.setProperty('border-bottom', '1px solid #f0f0f0', 'important'); 1317 | } 1318 | 1319 | const headerRow = document.getElementById('cursor-auto-fill-header-row'); 1320 | if (headerRow) { 1321 | headerRow.style.setProperty('display', 'flex', 'important'); 1322 | headerRow.style.setProperty('justify-content', 'space-between', 'important'); 1323 | headerRow.style.setProperty('align-items', 'center', 'important'); 1324 | headerRow.style.setProperty('margin-bottom', '12px', 'important'); 1325 | } 1326 | 1327 | const headerTitle = document.getElementById('cursor-auto-fill-header-title'); 1328 | if (headerTitle) { 1329 | headerTitle.style.setProperty('font-size', '20px', 'important'); 1330 | headerTitle.style.setProperty('font-weight', '700', 'important'); 1331 | headerTitle.style.setProperty('color', '#000000', 'important'); 1332 | headerTitle.style.setProperty('letter-spacing', '-0.5px', 'important'); 1333 | } 1334 | 1335 | const toggleHeaderBtn = document.getElementById('cursor-auto-fill-toggle'); 1336 | if (toggleHeaderBtn) { 1337 | toggleHeaderBtn.style.setProperty('background', '#f5f5f5', 'important'); 1338 | toggleHeaderBtn.style.setProperty('border', 'none', 'important'); 1339 | toggleHeaderBtn.style.setProperty('color', '#666', 'important'); 1340 | toggleHeaderBtn.style.setProperty('width', '36px', 'important'); 1341 | toggleHeaderBtn.style.setProperty('height', '36px', 'important'); 1342 | toggleHeaderBtn.style.setProperty('border-radius', '10px', 'important'); 1343 | toggleHeaderBtn.style.setProperty('cursor', 'pointer', 'important'); 1344 | toggleHeaderBtn.style.setProperty('font-size', '22px', 'important'); 1345 | toggleHeaderBtn.style.setProperty('line-height', '1', 'important'); 1346 | } 1347 | 1348 | const statusBadge = document.getElementById('cursor-status-badge'); 1349 | if (statusBadge) { 1350 | statusBadge.style.setProperty('display', 'inline-block', 'important'); 1351 | statusBadge.style.setProperty('padding', '6px 12px', 'important'); 1352 | statusBadge.style.setProperty('border-radius', '8px', 'important'); 1353 | statusBadge.style.setProperty('font-size', '13px', 'important'); 1354 | statusBadge.style.setProperty('font-weight', '600', 'important'); 1355 | } 1356 | 1357 | const content = document.getElementById('cursor-auto-fill-content'); 1358 | if (content) { 1359 | content.style.setProperty('padding', '20px 24px 24px', 'important'); 1360 | content.style.setProperty('background', '#fafafa', 'important'); 1361 | } 1362 | 1363 | const actionBtn = document.getElementById('cursor-action-btn'); 1364 | if (actionBtn) { 1365 | actionBtn.style.setProperty('background', '#000000', 'important'); 1366 | actionBtn.style.setProperty('color', 'white', 'important'); 1367 | actionBtn.style.setProperty('border', 'none', 'important'); 1368 | actionBtn.style.setProperty('box-shadow', '0 4px 12px rgba(0, 0, 0, 0.15)', 'important'); 1369 | } 1370 | 1371 | const btnIcon = document.getElementById('cursor-btn-icon'); 1372 | if (btnIcon) { 1373 | btnIcon.style.setProperty('display', 'inline-block', 'important'); 1374 | btnIcon.style.setProperty('margin-right', '6px', 'important'); 1375 | } 1376 | 1377 | const btnText = document.getElementById('cursor-btn-text'); 1378 | if (btnText) { 1379 | btnText.style.setProperty('display', 'inline-block', 'important'); 1380 | } 1381 | 1382 | // 设置所有section样式 1383 | const sections = document.querySelectorAll('.cursor-section'); 1384 | sections.forEach(section => { 1385 | section.style.setProperty('background', '#ffffff', 'important'); 1386 | section.style.setProperty('border-radius', '16px', 'important'); 1387 | section.style.setProperty('padding', '20px', 'important'); 1388 | section.style.setProperty('margin-bottom', '16px', 'important'); 1389 | section.style.setProperty('border', '1px solid #e8e8e8', 'important'); 1390 | }); 1391 | 1392 | // 设置section标题 1393 | const sectionTitles = document.querySelectorAll('.cursor-section-title'); 1394 | sectionTitles.forEach(title => { 1395 | title.style.setProperty('color', '#000000', 'important'); 1396 | title.style.setProperty('font-size', '15px', 'important'); 1397 | title.style.setProperty('font-weight', '600', 'important'); 1398 | title.style.setProperty('margin-bottom', '16px', 'important'); 1399 | title.style.setProperty('padding-bottom', '12px', 'important'); 1400 | title.style.setProperty('border-bottom', '1px solid #f5f5f5', 'important'); 1401 | }); 1402 | 1403 | // 设置config rows 1404 | const configRows = document.querySelectorAll('.cursor-config-row'); 1405 | configRows.forEach(row => { 1406 | row.style.setProperty('display', 'flex', 'important'); 1407 | row.style.setProperty('justify-content', 'space-between', 'important'); 1408 | row.style.setProperty('align-items', 'center', 'important'); 1409 | row.style.setProperty('margin-bottom', '12px', 'important'); 1410 | }); 1411 | 1412 | // 设置config labels 1413 | const configLabels = document.querySelectorAll('.cursor-config-label'); 1414 | configLabels.forEach(label => { 1415 | label.style.setProperty('color', '#333', 'important'); 1416 | label.style.setProperty('font-size', '14px', 'important'); 1417 | label.style.setProperty('font-weight', '500', 'important'); 1418 | }); 1419 | 1420 | // 设置toggle switches 1421 | const toggleSwitches = document.querySelectorAll('.cursor-toggle-switch'); 1422 | toggleSwitches.forEach(toggle => { 1423 | toggle.style.setProperty('position', 'relative', 'important'); 1424 | toggle.style.setProperty('display', 'inline-block', 'important'); 1425 | toggle.style.setProperty('width', '50px', 'important'); 1426 | toggle.style.setProperty('height', '28px', 'important'); 1427 | }); 1428 | 1429 | // 强制隐藏所有复选框input 1430 | const toggleInputs = document.querySelectorAll('.cursor-toggle-switch input'); 1431 | toggleInputs.forEach(input => { 1432 | input.style.setProperty('opacity', '0', 'important'); 1433 | input.style.setProperty('width', '0', 'important'); 1434 | input.style.setProperty('height', '0', 'important'); 1435 | input.style.setProperty('position', 'absolute', 'important'); 1436 | input.style.setProperty('pointer-events', 'none', 'important'); 1437 | input.style.setProperty('visibility', 'hidden', 'important'); 1438 | input.style.setProperty('display', 'none', 'important'); 1439 | }); 1440 | 1441 | // 设置toggle sliders 1442 | const toggleSliders = document.querySelectorAll('.cursor-toggle-slider'); 1443 | toggleSliders.forEach(slider => { 1444 | slider.style.setProperty('position', 'absolute', 'important'); 1445 | slider.style.setProperty('cursor', 'pointer', 'important'); 1446 | slider.style.setProperty('top', '0', 'important'); 1447 | slider.style.setProperty('left', '0', 'important'); 1448 | slider.style.setProperty('right', '0', 'important'); 1449 | slider.style.setProperty('bottom', '0', 'important'); 1450 | slider.style.setProperty('background-color', '#e0e0e0', 'important'); 1451 | slider.style.setProperty('border-radius', '28px', 'important'); 1452 | slider.style.setProperty('transition', 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', 'important'); 1453 | }); 1454 | 1455 | // 设置toggle dots(开关圆点) 1456 | const toggleDots = document.querySelectorAll('.cursor-toggle-dot'); 1457 | toggleDots.forEach(dot => { 1458 | dot.style.setProperty('position', 'absolute', 'important'); 1459 | dot.style.setProperty('height', '22px', 'important'); 1460 | dot.style.setProperty('width', '22px', 'important'); 1461 | dot.style.setProperty('left', '3px', 'important'); 1462 | dot.style.setProperty('bottom', '3px', 'important'); 1463 | dot.style.setProperty('background-color', 'white', 'important'); 1464 | dot.style.setProperty('border-radius', '50%', 'important'); 1465 | dot.style.setProperty('transition', 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', 'important'); 1466 | dot.style.setProperty('box-shadow', '0 2px 4px rgba(0, 0, 0, 0.1)', 'important'); 1467 | }); 1468 | 1469 | // 处理开关选中状态 1470 | const autoFillInput = document.getElementById('cursor-auto-fill-toggle-input'); 1471 | const autoSubmitInput = document.getElementById('cursor-auto-submit-toggle'); 1472 | 1473 | function updateToggleState(input) { 1474 | const slider = input.nextElementSibling; 1475 | const dot = slider ? slider.querySelector('.cursor-toggle-dot') : null; 1476 | 1477 | if (input.checked) { 1478 | if (slider) slider.style.setProperty('background-color', '#000000', 'important'); 1479 | if (dot) dot.style.setProperty('transform', 'translateX(22px)', 'important'); 1480 | } else { 1481 | if (slider) slider.style.setProperty('background-color', '#e0e0e0', 'important'); 1482 | if (dot) dot.style.setProperty('transform', 'translateX(0)', 'important'); 1483 | } 1484 | } 1485 | 1486 | if (autoFillInput) updateToggleState(autoFillInput); 1487 | if (autoSubmitInput) updateToggleState(autoSubmitInput); 1488 | 1489 | // 设置BIN输入框 1490 | const binInput = document.getElementById('cursor-bin-input'); 1491 | if (binInput) { 1492 | binInput.style.setProperty('width', '100%', 'important'); 1493 | binInput.style.setProperty('padding', '14px 16px', 'important'); 1494 | binInput.style.setProperty('border', '1.5px solid #e0e0e0', 'important'); 1495 | binInput.style.setProperty('border-radius', '12px', 'important'); 1496 | binInput.style.setProperty('font-size', '14px', 'important'); 1497 | binInput.style.setProperty('background', '#ffffff', 'important'); 1498 | binInput.style.setProperty('color', '#000000', 'important'); 1499 | binInput.style.setProperty('font-family', '"SF Mono", Monaco, Consolas, monospace', 'important'); 1500 | } 1501 | 1502 | // 设置按钮组 1503 | const btnGroup = document.querySelector('.cursor-button-group'); 1504 | if (btnGroup) { 1505 | btnGroup.style.setProperty('margin-top', '20px', 'important'); 1506 | btnGroup.style.setProperty('margin-bottom', '20px', 'important'); 1507 | } 1508 | 1509 | // 设置所有按钮通用样式 1510 | const allBtns = document.querySelectorAll('.cursor-btn'); 1511 | allBtns.forEach(btn => { 1512 | btn.style.setProperty('width', '100%', 'important'); 1513 | btn.style.setProperty('padding', '14px 20px', 'important'); 1514 | btn.style.setProperty('border-radius', '12px', 'important'); 1515 | btn.style.setProperty('font-size', '15px', 'important'); 1516 | btn.style.setProperty('font-weight', '600', 'important'); 1517 | btn.style.setProperty('cursor', 'pointer', 'important'); 1518 | btn.style.setProperty('transition', 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', 'important'); 1519 | btn.style.setProperty('letter-spacing', '-0.2px', 'important'); 1520 | }); 1521 | 1522 | // 设置日志容器 1523 | const logContainer = document.getElementById('cursor-log-container'); 1524 | if (logContainer) { 1525 | logContainer.style.setProperty('background', '#ffffff', 'important'); 1526 | logContainer.style.setProperty('border-radius', '16px', 'important'); 1527 | logContainer.style.setProperty('padding', '20px', 'important'); 1528 | logContainer.style.setProperty('max-height', '260px', 'important'); 1529 | logContainer.style.setProperty('overflow-y', 'auto', 'important'); 1530 | logContainer.style.setProperty('border', '1px solid #e8e8e8', 'important'); 1531 | } 1532 | 1533 | // 设置日志标题 1534 | const logTitle = document.getElementById('cursor-log-title'); 1535 | if (logTitle) { 1536 | logTitle.style.setProperty('color', '#000000', 'important'); 1537 | logTitle.style.setProperty('font-size', '15px', 'important'); 1538 | logTitle.style.setProperty('font-weight', '600', 'important'); 1539 | logTitle.style.setProperty('margin-bottom', '16px', 'important'); 1540 | logTitle.style.setProperty('padding-bottom', '12px', 'important'); 1541 | logTitle.style.setProperty('border-bottom', '1px solid #f5f5f5', 'important'); 1542 | } 1543 | 1544 | // 设置日志区域 1545 | const logs = document.getElementById('cursor-auto-fill-logs'); 1546 | if (logs) { 1547 | logs.style.setProperty('font-family', '"SF Mono", Monaco, Consolas, monospace', 'important'); 1548 | logs.style.setProperty('font-size', '12px', 'important'); 1549 | logs.style.setProperty('line-height', '2', 'important'); 1550 | } 1551 | 1552 | // 定时检查并设置日志项颜色(因为日志是动态添加的) 1553 | setInterval(() => { 1554 | const logItems = document.querySelectorAll('.cursor-log-item'); 1555 | logItems.forEach(item => { 1556 | const timestamp = item.querySelector('.cursor-log-timestamp'); 1557 | const text = item.querySelector('.cursor-log-text'); 1558 | 1559 | if (timestamp) { 1560 | timestamp.style.setProperty('color', '#a0a0a0', 'important'); 1561 | timestamp.style.setProperty('font-weight', '500', 'important'); 1562 | } 1563 | 1564 | if (text) { 1565 | if (item.classList.contains('cursor-log-success')) { 1566 | text.style.setProperty('color', '#34C759', 'important'); 1567 | text.style.setProperty('font-weight', '600', 'important'); 1568 | } else if (item.classList.contains('cursor-log-error')) { 1569 | text.style.setProperty('color', '#FF3B30', 'important'); 1570 | text.style.setProperty('font-weight', '600', 'important'); 1571 | } else if (item.classList.contains('cursor-log-warning')) { 1572 | text.style.setProperty('color', '#FF9500', 'important'); 1573 | text.style.setProperty('font-weight', '600', 'important'); 1574 | } else if (item.classList.contains('cursor-log-info')) { 1575 | text.style.setProperty('color', '#007AFF', 'important'); 1576 | } 1577 | } 1578 | }); 1579 | }, 100); // 每100ms检查一次 1580 | 1581 | logger.log('✓ UI界面已创建', 'success'); 1582 | 1583 | setupUIEvents(); 1584 | makeDraggable(); 1585 | logger.updateLogDisplay(); 1586 | } 1587 | 1588 | // ===== 设置UI事件 ===== 1589 | function setupUIEvents() { 1590 | // 折叠按钮 1591 | const toggleBtn = document.getElementById('cursor-auto-fill-toggle'); 1592 | const content = document.getElementById('cursor-auto-fill-content'); 1593 | let isCollapsed = false; 1594 | 1595 | toggleBtn.addEventListener('click', (e) => { 1596 | e.stopPropagation(); 1597 | isCollapsed = !isCollapsed; 1598 | content.style.display = isCollapsed ? 'none' : 'block'; 1599 | toggleBtn.textContent = isCollapsed ? '+' : '−'; 1600 | }); 1601 | 1602 | // 自动填写开关 1603 | const autoFillInput = document.getElementById('cursor-auto-fill-toggle-input'); 1604 | autoFillInput.addEventListener('change', (e) => { 1605 | config.autoFill = e.target.checked; 1606 | saveConfig(config); 1607 | logger.log(`自动填写已${config.autoFill ? '开启' : '关闭'}`, 'info'); 1608 | updateToggleVisual(e.target); 1609 | }); 1610 | 1611 | // 自动提交开关 1612 | const autoSubmitInput = document.getElementById('cursor-auto-submit-toggle'); 1613 | autoSubmitInput.addEventListener('change', (e) => { 1614 | config.autoSubmit = e.target.checked; 1615 | saveConfig(config); 1616 | logger.log(`自动提交已${config.autoSubmit ? '开启' : '关闭'}`, 'info'); 1617 | updateToggleVisual(e.target); 1618 | }); 1619 | 1620 | // 更新开关视觉状态的函数 1621 | function updateToggleVisual(input) { 1622 | const slider = input.nextElementSibling; 1623 | const dot = slider ? slider.querySelector('.cursor-toggle-dot') : null; 1624 | 1625 | if (input.checked) { 1626 | if (slider) slider.style.setProperty('background-color', '#000000', 'important'); 1627 | if (dot) dot.style.setProperty('transform', 'translateX(22px)', 'important'); 1628 | } else { 1629 | if (slider) slider.style.setProperty('background-color', '#e0e0e0', 'important'); 1630 | if (dot) dot.style.setProperty('transform', 'translateX(0)', 'important'); 1631 | } 1632 | } 1633 | 1634 | // BIN输入 1635 | document.getElementById('cursor-bin-input').addEventListener('change', (e) => { 1636 | config.bin = e.target.value; 1637 | saveConfig(config); 1638 | logger.log(`BIN已更新: ${config.bin}`, 'info'); 1639 | }); 1640 | 1641 | // 批量处理按钮 1642 | document.getElementById('cursor-batch-start').addEventListener('click', (e) => { 1643 | e.stopPropagation(); 1644 | const urlText = document.getElementById('cursor-url-batch').value; 1645 | if (!urlText.trim()) { 1646 | logger.log('请输入URL列表', 'warning'); 1647 | return; 1648 | } 1649 | 1650 | const count = urlQueue.addURLs(urlText); 1651 | if (count > 0) { 1652 | document.getElementById('cursor-batch-stats').style.display = 'block'; 1653 | updateBatchStats(); 1654 | startBatchProcessing(); 1655 | } 1656 | }); 1657 | 1658 | // 清空队列按钮 1659 | document.getElementById('cursor-batch-clear').addEventListener('click', (e) => { 1660 | e.stopPropagation(); 1661 | urlQueue.clear(); 1662 | document.getElementById('cursor-url-batch').value = ''; 1663 | document.getElementById('cursor-batch-stats').style.display = 'none'; 1664 | logger.log('队列已清空', 'info'); 1665 | }); 1666 | 1667 | // 动作按钮(开始/停止切换) 1668 | document.getElementById('cursor-action-btn').addEventListener('click', (e) => { 1669 | e.stopPropagation(); 1670 | 1671 | if (isRunning) { 1672 | // 当前正在执行,点击停止 1673 | logger.log('========== 用户停止执行 ==========', 'warning'); 1674 | stopExecution(); 1675 | } else { 1676 | // 当前待机,点击开始 1677 | logger.log('========== 手动触发填写 ==========', 'info'); 1678 | fillForm(); 1679 | } 1680 | }); 1681 | } 1682 | 1683 | // ===== 使面板可拖拽 ===== 1684 | function makeDraggable() { 1685 | const panel = document.getElementById('cursor-auto-fill-panel'); 1686 | const header = document.getElementById('cursor-auto-fill-header'); 1687 | let isDragging = false; 1688 | let currentX, currentY, initialX, initialY; 1689 | 1690 | header.addEventListener('mousedown', (e) => { 1691 | if (e.target.id === 'cursor-auto-fill-toggle') return; 1692 | isDragging = true; 1693 | initialX = e.clientX - panel.offsetLeft; 1694 | initialY = e.clientY - panel.offsetTop; 1695 | }); 1696 | 1697 | document.addEventListener('mousemove', (e) => { 1698 | if (isDragging) { 1699 | e.preventDefault(); 1700 | currentX = e.clientX - initialX; 1701 | currentY = e.clientY - initialY; 1702 | 1703 | panel.style.left = currentX + 'px'; 1704 | panel.style.top = currentY + 'px'; 1705 | panel.style.right = 'auto'; 1706 | } 1707 | }); 1708 | 1709 | document.addEventListener('mouseup', () => { 1710 | isDragging = false; 1711 | }); 1712 | } 1713 | 1714 | // ===== 批量处理逻辑 ===== 1715 | let batchMonitorInterval = null; // 全局监控interval 1716 | 1717 | function updateBatchStats() { 1718 | const stats = urlQueue.getStats(); 1719 | const totalEl = document.getElementById('stat-total'); 1720 | const completedEl = document.getElementById('stat-completed'); 1721 | const processingEl = document.getElementById('stat-processing'); 1722 | const pendingEl = document.getElementById('stat-pending'); 1723 | 1724 | if (totalEl) totalEl.textContent = stats.total; 1725 | if (completedEl) completedEl.textContent = stats.completed; 1726 | if (processingEl) processingEl.textContent = stats.processing; 1727 | if (pendingEl) pendingEl.textContent = stats.pending; 1728 | } 1729 | 1730 | // 从文本框中删除已完成的所有URL 1731 | function updateTextareaWithPendingURLs() { 1732 | const textarea = document.getElementById('cursor-url-batch'); 1733 | if (!textarea) return; 1734 | 1735 | // 获取队列中所有completed的URL 1736 | const completedURLs = urlQueue.queue.urls 1737 | .filter(item => item.status === 'completed') 1738 | .map(item => item.url); 1739 | 1740 | if (completedURLs.length === 0) return; 1741 | 1742 | const currentText = textarea.value; 1743 | const lines = currentText.split('\n'); 1744 | 1745 | // 过滤掉所有已完成的URL 1746 | const completedIDs = completedURLs.map(url => { 1747 | const match = url.match(/\/pay\/([^\/\?]+)/); 1748 | return match ? match[1] : null; 1749 | }).filter(id => id); 1750 | 1751 | const filteredLines = lines.filter(line => { 1752 | const lineMatch = line.trim().match(/\/pay\/([^\/\?]+)/); 1753 | if (lineMatch && completedIDs.includes(lineMatch[1])) { 1754 | return false; // 过滤掉已完成的 1755 | } 1756 | // 如果不是URL格式的行(如空行),保留 1757 | return line.trim().length === 0 || !line.includes('checkout.stripe.com'); 1758 | }); 1759 | 1760 | const newText = filteredLines.join('\n').trim(); 1761 | if (newText !== currentText.trim()) { 1762 | textarea.value = newText; 1763 | logger.log(`✓ 已从列表中移除 ${completedURLs.length} 个完成的URL`, 'info'); 1764 | } 1765 | } 1766 | 1767 | // ===== 单页面批量处理 ===== 1768 | function startBatchProcessing() { 1769 | const stats = urlQueue.getStats(); 1770 | logger.log(`========== 单页面批量处理 v2.4.0 ==========`, 'success'); 1771 | logger.log(`共 ${stats.total} 个URL,当前页面逐个处理`, 'info'); 1772 | updateBatchStats(); 1773 | 1774 | // 只获取1个URL,在当前标签页打开 1775 | const nextURLs = urlQueue.getNext(1); 1776 | if (nextURLs.length > 0) { 1777 | const nextURL = nextURLs[0]; 1778 | logger.log(`🚀 1秒后跳转到第1个URL...`, 'success'); 1779 | logger.log(`URL: ${nextURL.substring(0, 60)}...`, 'info'); 1780 | setTimeout(() => { 1781 | window.location.href = nextURL; 1782 | }, 1000); 1783 | } else { 1784 | logger.log('❌ 没有待处理的URL', 'warning'); 1785 | } 1786 | } 1787 | 1788 | function isBatchMode() { 1789 | // 检查当前URL是否在队列中 1790 | const currentURL = window.location.href; 1791 | 1792 | // 必须是Stripe支付页面 1793 | if (!currentURL.includes('checkout.stripe.com/c/pay/')) { 1794 | return false; 1795 | } 1796 | 1797 | // 检查队列中是否有匹配的processing状态URL 1798 | const inQueue = urlQueue.queue.urls.some(item => { 1799 | // 提取pay/后面的ID进行匹配 1800 | const itemMatch = item.url.match(/\/pay\/([^\/\?]+)/); 1801 | const currentMatch = currentURL.match(/\/pay\/([^\/\?]+)/); 1802 | 1803 | if (itemMatch && currentMatch) { 1804 | return itemMatch[1] === currentMatch[1] && item.status === 'processing'; 1805 | } 1806 | return false; 1807 | }); 1808 | 1809 | return inQueue; 1810 | } 1811 | 1812 | // ===== 跳转到下一个URL ===== 1813 | async function jumpToNextURL() { 1814 | // 1. 标记当前URL完成 1815 | const currentURL = window.location.href; 1816 | const currentMatch = currentURL.match(/\/pay\/([^\/\?]+)/); 1817 | 1818 | if (currentMatch) { 1819 | const targetItem = urlQueue.queue.urls.find(item => { 1820 | const itemMatch = item.url.match(/\/pay\/([^\/\?]+)/); 1821 | return itemMatch && itemMatch[1] === currentMatch[1]; 1822 | }); 1823 | 1824 | if (targetItem) { 1825 | targetItem.status = 'completed'; 1826 | urlQueue.save(); 1827 | logger.log(`✓ URL已标记为完成: ${targetItem.url.substring(0, 50)}...`, 'success'); 1828 | 1829 | // 从文本框删除 1830 | updateTextareaWithPendingURLs(); 1831 | } 1832 | } 1833 | 1834 | // 2. 获取下一个URL 1835 | const stats = urlQueue.getStats(); 1836 | logger.log(`进度: ${stats.completed}/${stats.total} 完成`, 'info'); 1837 | 1838 | const nextURLs = urlQueue.getNext(1); 1839 | 1840 | if (nextURLs.length > 0) { 1841 | const nextURL = nextURLs[0]; 1842 | logger.log(`⏭️ 立即跳转到下一个URL...`, 'success'); 1843 | logger.log(`下一个: ${nextURL.substring(0, 60)}...`, 'info'); 1844 | 1845 | // 直接跳转(不关闭标签页) 1846 | window.location.href = nextURL; 1847 | } else { 1848 | // 全部完成 1849 | logger.log('========== 🎉 全部URL处理完成! ==========', 'success'); 1850 | logger.log(`✅ 共完成 ${stats.completed} 个URL`, 'success'); 1851 | } 1852 | } 1853 | 1854 | // ===== 后台执行保活机制 ===== 1855 | function keepAlive() { 1856 | // 防止后台标签页被浏览器暂停 1857 | // 使用多种策略保持脚本活跃 1858 | 1859 | // 1. 定时发送console日志(轻量级) 1860 | setInterval(() => { 1861 | console.log('[Cursor Auto Fill] Keepalive ping:', new Date().toLocaleTimeString()); 1862 | }, 10000); 1863 | 1864 | // 2. 使用requestAnimationFrame保持活跃 1865 | function rafKeepAlive() { 1866 | requestAnimationFrame(rafKeepAlive); 1867 | } 1868 | rafKeepAlive(); 1869 | 1870 | // 3. 监听visibilitychange事件 1871 | document.addEventListener('visibilitychange', () => { 1872 | if (document.hidden) { 1873 | console.log('[Cursor Auto Fill] 标签页进入后台,继续执行...'); 1874 | } else { 1875 | console.log('[Cursor Auto Fill] 标签页回到前台'); 1876 | } 1877 | }); 1878 | 1879 | logger.log('✓ 后台保活机制已启动', 'info'); 1880 | } 1881 | 1882 | // ===== 检测页面类型 ===== 1883 | function getPageType() { 1884 | const url = window.location.href; 1885 | if (url.includes('checkout.stripe.com/c/pay/')) { 1886 | return 'stripe'; 1887 | } else if (url.includes('cursor.com') || url.includes('google.com') || url.includes('baidu.com')) { 1888 | return 'config'; 1889 | } 1890 | return 'other'; 1891 | } 1892 | 1893 | // ===== 创建配置专用UI(简化版)===== 1894 | function createConfigOnlyUI() { 1895 | const oldUI = document.getElementById('cursor-auto-fill-container'); 1896 | if (oldUI) { 1897 | oldUI.remove(); 1898 | } 1899 | 1900 | const container = document.createElement('div'); 1901 | container.id = 'cursor-auto-fill-container'; 1902 | 1903 | container.innerHTML = ` 1904 |
1905 |
1906 |
1907 |
Cursor 批量配置
1908 | 1909 |
1910 |
📍 当前页面:配置模式
1911 |
1912 |
1913 |
1914 |
批量URL配置
1915 |
1916 | 1917 | 1922 |
1923 |
1924 | 1925 | 1926 |
1927 | 1931 |
1932 | 1933 |
1934 |
执行日志
1935 |
1936 |
1937 |
1938 |
1939 | `; 1940 | 1941 | document.body.appendChild(container); 1942 | 1943 | logger.log('✓ 配置面板已创建', 'success'); 1944 | logger.log('💡 提示:在此配置URL,脚本会自动打开标签页并处理', 'info'); 1945 | 1946 | // 设置事件 1947 | setupConfigUIEvents(); 1948 | makeDraggable(); 1949 | logger.updateLogDisplay(); 1950 | 1951 | // 加载已有队列 1952 | if (urlQueue.queue.urls.length > 0) { 1953 | document.getElementById('cursor-batch-stats').style.display = 'block'; 1954 | updateBatchStats(); 1955 | setInterval(updateBatchStats, 2000); 1956 | } 1957 | } 1958 | 1959 | // ===== 配置UI事件 ===== 1960 | function setupConfigUIEvents() { 1961 | // 折叠按钮 1962 | const toggleBtn = document.getElementById('cursor-auto-fill-toggle'); 1963 | const content = document.getElementById('cursor-auto-fill-content'); 1964 | let isCollapsed = false; 1965 | 1966 | toggleBtn.addEventListener('click', (e) => { 1967 | e.stopPropagation(); 1968 | isCollapsed = !isCollapsed; 1969 | content.style.display = isCollapsed ? 'none' : 'block'; 1970 | toggleBtn.textContent = isCollapsed ? '+' : '−'; 1971 | }); 1972 | 1973 | // 批量处理按钮 1974 | document.getElementById('cursor-batch-start').addEventListener('click', (e) => { 1975 | e.stopPropagation(); 1976 | const urlText = document.getElementById('cursor-url-batch').value; 1977 | if (!urlText.trim()) { 1978 | logger.log('⚠️ 请输入URL列表', 'warning'); 1979 | return; 1980 | } 1981 | 1982 | const count = urlQueue.addURLs(urlText); 1983 | if (count > 0) { 1984 | document.getElementById('cursor-batch-stats').style.display = 'block'; 1985 | updateBatchStats(); 1986 | startBatchProcessing(); 1987 | } 1988 | }); 1989 | 1990 | // 清空队列按钮 1991 | document.getElementById('cursor-batch-clear').addEventListener('click', (e) => { 1992 | e.stopPropagation(); 1993 | urlQueue.clear(); 1994 | document.getElementById('cursor-url-batch').value = ''; 1995 | document.getElementById('cursor-batch-stats').style.display = 'none'; 1996 | logger.log('队列已清空', 'info'); 1997 | }); 1998 | } 1999 | 2000 | // ===== 主函数 ===== 2001 | async function main() { 2002 | logger.log('✓ 脚本已加载', 'info'); 2003 | 2004 | // 检测页面类型 2005 | const pageType = getPageType(); 2006 | logger.log(`当前页面类型: ${pageType}`, 'info'); 2007 | 2008 | // 立即启动保活机制(后台执行关键) 2009 | keepAlive(); 2010 | 2011 | await sleep(500); 2012 | 2013 | // 根据页面类型创建不同的UI 2014 | if (pageType === 'config') { 2015 | // 配置页面 - 只显示批量配置UI 2016 | createConfigOnlyUI(); 2017 | logger.log('🎯 配置模式已启动,请输入URL开始批量处理', 'success'); 2018 | return; 2019 | } 2020 | 2021 | // Stripe支付页面 - 显示完整UI 2022 | createUI(); 2023 | 2024 | logger.log('正在检测页面...', 'info'); 2025 | 2026 | // 检查页面可见性状态 2027 | const isHidden = document.hidden; 2028 | if (isHidden) { 2029 | logger.log('⚠️ 检测到标签页在后台,强制执行模式已启用', 'warning'); 2030 | } 2031 | 2032 | const isCursor = await waitForPageLoad(); 2033 | 2034 | if (!isCursor) { 2035 | logger.log('这不是 Cursor 试用页面,脚本待机中', 'warning'); 2036 | return; 2037 | } 2038 | 2039 | // 检查是否是批量模式 2040 | const batchMode = isBatchMode(); 2041 | 2042 | // 等待0.5秒让页面内容完全加载 2043 | await sleep(500); 2044 | 2045 | // 检查页面是否已经完成支付(已使用的URL) 2046 | const alreadyCompleted = isAlreadyCompleted(); 2047 | if (alreadyCompleted) { 2048 | logger.log('========== 检测到此URL已被使用 ==========', 'warning'); 2049 | logger.log('⏭️ 跳过填写,直接跳转下一个', 'info'); 2050 | 2051 | if (batchMode) { 2052 | // 批量模式下,直接跳转下一个 2053 | await sleep(1000); 2054 | await jumpToNextURL(); 2055 | } else { 2056 | logger.log('此URL无需处理,已经使用过了', 'warning'); 2057 | } 2058 | return; 2059 | } 2060 | 2061 | if (config.autoFill || batchMode) { 2062 | const mode = batchMode ? '批量模式' : '自动填写'; 2063 | const delay = 0; // 立即开始,不等待 2064 | 2065 | logger.log(`${mode}:立即开始...`, 'info'); 2066 | 2067 | // 使用setImmediate或setTimeout 2068 | setTimeout(async () => { 2069 | logger.log('========== 自动开始填写 ==========', 'info'); 2070 | logger.log(`当前标签页状态: ${document.hidden ? '后台' : '前台'}`, 'info'); 2071 | 2072 | // 重试逻辑:最多重试3次 2073 | let retryCount = 0; 2074 | const maxRetries = 3; 2075 | let success = false; 2076 | 2077 | while (retryCount < maxRetries && !success) { 2078 | if (retryCount > 0) { 2079 | logger.log(`========== 第 ${retryCount + 1} 次尝试 ==========`, 'warning'); 2080 | } 2081 | 2082 | try { 2083 | success = await fillForm(); 2084 | 2085 | if (!success && retryCount < maxRetries - 1) { 2086 | logger.log(`⚠️ 支付失败,2秒后重新生成卡号并重试...`, 'warning'); 2087 | await sleep(2000); 2088 | retryCount++; 2089 | } else if (!success) { 2090 | logger.log(`❌ 已重试 ${maxRetries} 次,全部失败`, 'error'); 2091 | if (batchMode) { 2092 | logger.log('跳过该URL,3秒后继续下一个...', 'warning'); 2093 | await sleep(3000); 2094 | await jumpToNextURL(); 2095 | } 2096 | } else { 2097 | // 成功,fillForm内部已经处理了跳转逻辑 2098 | } 2099 | } catch (error) { 2100 | logger.log(`执行出错: ${error.message}`, 'error'); 2101 | 2102 | if (batchMode) { 2103 | logger.log('⚠️ 发生错误,3秒后跳过该URL,继续下一个...', 'warning'); 2104 | await sleep(3000); 2105 | await jumpToNextURL(); 2106 | } 2107 | break; 2108 | } 2109 | } 2110 | }, delay); 2111 | } else { 2112 | logger.log('自动填写未启用,请点击"开始填写"按钮', 'info'); 2113 | } 2114 | 2115 | // 如果有队列,定时更新统计 2116 | if (urlQueue.queue.urls.length > 0) { 2117 | document.getElementById('cursor-batch-stats').style.display = 'block'; 2118 | updateBatchStats(); 2119 | setInterval(updateBatchStats, 2000); 2120 | } 2121 | } 2122 | 2123 | if (document.readyState === 'loading') { 2124 | document.addEventListener('DOMContentLoaded', main); 2125 | } else { 2126 | main(); 2127 | } 2128 | 2129 | })(); 2130 | --------------------------------------------------------------------------------