您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自動投注巴哈小瑪莉(含防破產保護、開獎統計)。
// ==UserScript== // @name 巴哈小瑪莉自動投注器 // @description 自動投注巴哈小瑪莉(含防破產保護、開獎統計)。 // @author 由Gemini產生 // @version 1.0 // @match https://now.gamer.com.tw/chat_list.php* // @icon https://ani.gamer.com.tw/apple-touch-icon-144.jpg // @require https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js // @grant GM_addStyle // @noframes // @license MIT // @namespace https://greasyfork.org/users/1308487 // ==/UserScript== (function() { 'use strict'; // =================================================================== // 1. 【設定與數據儲存區】 // =================================================================== const STORAGE_KEY = 'maryBettingData'; const PLAYER_NAME = '巴哈小瑪莉'; // 固定的開獎機器人名稱 const INPUT_FIELD_ID = 'msg_input'; const SEND_BUTTON_SELECTOR = '.chat_inputarea .btn-send.is-ready'; // 預設投注指令,方便集中管理 const DEFAULT_BET_COMMAND = '/mary 0 10 10 0 0 0 0 0 0'; // 【修改】: 投注間隔,以秒為單位,用於設定 UI 預設值 const DEFAULT_BET_INTERVAL_SEC = 5; // 【新增】: 轉換單位常數 const MS_PER_SECOND = 1000; let isRunning = false; let autoBetTimer = null; let totalCommandsSent = 0; let totalDraws = 0; let initialBahbi = 0; let currentBahbi = 0; let isSafetyPaused = false; // 動態儲存當前分析機器人名稱,不進行持久化儲存 let currentAnalyzerName = null; // 預設安全水位線設定 (已修正註釋語法) let safetyConfig = { // 百分比虧損停止 (30%) percentLoss: 30, // 絕對金額虧損停止 absoluteLoss: 500, // 最低庫存停止 minInventory: 1000 }; // 選項的固定順序鍵名 const OPTION_ORDER = [ '2', '5', '10', '15', '20-grape', '20-bell', '30', '40', '100' ]; // 賠率映射表 const PAYOUT_MAP = { '2': 2, '5': 5, '10': 10, '15': 15, '20-grape': 20, '20-bell': 20, '30': 30, '40': 40, '100': 100 }; let drawingCounts = { '2': 0, '5': 0, '10': 0, '15': 0, '20-grape': 0, '20-bell': 0, '30': 0, '40': 0, '100': 0 }; const DISPLAY_NAMES = { '2': '🍒 (2)', '5': '🍎 (5)', '10': '🍊 (10)', '15': '🍇 (15)', '20-grape': '🍉 (20)', '20-bell': '🔔 (20)', '30': '⭐ (30)', '40': '7️⃣ (40)', '100': '🎰 (100)' }; const SYMBOL_MAP = { '🍒': '2', '🍎': '5', '🍊': '10', '🍇': '15', '🍉': '20-grape', '🔔': '20-bell', '⭐': '30', '7️⃣': '40', '🎰': '100' }; // 儲存 UI 位置 (預設右上角) let uiPosition = { top: 20, left: window.innerWidth - 270 }; // =================================================================== // 2. 【數據持久化函數】: 儲存與載入 // =================================================================== function saveData() { const data = { counts: drawingCounts, totalDraws: totalDraws, totalCommandsSent: totalCommandsSent, initialBahbi: initialBahbi, currentBahbi: currentBahbi, safetyConfig: safetyConfig, isSafetyPaused: isSafetyPaused, uiPosition: uiPosition }; try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch (e) { console.error('[數據] 儲存數據失敗:', e); } } function loadData() { try { const storedData = localStorage.getItem(STORAGE_KEY); if (storedData) { const data = JSON.parse(storedData); Object.keys(drawingCounts).forEach(key => { if (data.counts && data.counts[key] !== undefined) { drawingCounts[key] = data.counts[key]; } }); totalDraws = data.totalDraws || 0; totalCommandsSent = data.totalCommandsSent || 0; initialBahbi = data.initialBahbi || 0; currentBahbi = data.currentBahbi || 0; if (data.safetyConfig) { safetyConfig = data.safetyConfig; } isSafetyPaused = data.isSafetyPaused || false; if (data.uiPosition) { uiPosition = data.uiPosition; } else { uiPosition.left = window.innerWidth - 270; } console.log(`[數據] 成功載入歷史數據。總開獎次數: ${totalDraws}`); } } catch (e) { console.error('[數據] 載入數據失敗或數據格式錯誤:', e); } } function resetData() { if (!confirm('確定要歸零所有開獎數據嗎?此操作不可撤銷!')) { return; } Object.keys(drawingCounts).forEach(key => { drawingCounts[key] = 0; }); totalDraws = 0; totalCommandsSent = 0; initialBahbi = 0; currentBahbi = 0; isSafetyPaused = false; // 偵測到的機器人名稱也重置 currentAnalyzerName = null; saveData(); displayStats(); updateBahbiStatus(); console.log('[數據] 所有數據已歸零並儲存。'); } // =================================================================== // 3. 【數據分析與安全檢查】: 核心邏輯 // =================================================================== /** * 檢查是否觸發安全水位線 */ function checkSafetyLimits() { if (!isRunning || initialBahbi <= 0) return false; const currentLoss = initialBahbi - currentBahbi; const limitReached = document.getElementById('safety-limit-text'); const percentLimit = initialBahbi * (safetyConfig.percentLoss / 100); if (currentLoss >= percentLimit && safetyConfig.percentLoss > 0) { stopAutoBet(`[資金保護] 虧損達到 ${safetyConfig.percentLoss}% 水位線!`); limitReached.textContent = `已觸發:虧損達 ${safetyConfig.percentLoss}%`; isSafetyPaused = true; return true; } if (currentLoss >= safetyConfig.absoluteLoss && safetyConfig.absoluteLoss > 0) { stopAutoBet(`[資金保護] 虧損達到 ${safetyConfig.absoluteLoss.toLocaleString()} 元!`); limitReached.textContent = `已觸發:虧損達 ${safetyConfig.absoluteLoss.toLocaleString()}`; isSafetyPaused = true; return true; } if (currentBahbi <= safetyConfig.minInventory && safetyConfig.minInventory > 0) { stopAutoBet(`[資金保護] 庫存低於最低 ${safetyConfig.minInventory.toLocaleString()} 元!`); limitReached.textContent = `已觸發:庫存低於 ${safetyConfig.minInventory.toLocaleString()}`; isSafetyPaused = true; return true; } if (isSafetyPaused) { limitReached.textContent = `檢查中...`; isSafetyPaused = false; } limitReached.textContent = '檢查中...'; return false; } /** * 更新巴幣狀態顯示 */ function updateBahbiStatus(newBahbi = null) { const bahbiStatusEl = document.getElementById('bahbi-status'); if (!bahbiStatusEl) return; if (newBahbi !== null) { currentBahbi = newBahbi; } if (currentBahbi > 0 && initialBahbi > 0) { const change = currentBahbi - initialBahbi; const sign = change >= 0 ? '+' : ''; const formattedChange = `${sign}${change.toLocaleString()}`; bahbiStatusEl.textContent = `巴幣: ${currentBahbi.toLocaleString()} (${formattedChange})`; checkSafetyLimits(); } else if (initialBahbi > 0) { bahbiStatusEl.textContent = `巴幣: ${initialBahbi.toLocaleString()} (+0)`; } else { bahbiStatusEl.textContent = `巴幣: - (未追蹤)`; } } /** * 計算並標記正期望值的項目 */ function calculateExpectedValue() { const dataTableBody = document.getElementById('data-table-body'); if (!dataTableBody || totalDraws === 0) return; let maxExpectedValue = 0; let bestOptionKey = null; // 1. 計算每個選項的期望值 (Expected Value, EV) const evMap = {}; OPTION_ORDER.forEach(key => { const count = drawingCounts[key]; const payout = PAYOUT_MAP[key]; const probability = count / totalDraws; const expectedValue = probability * payout; // EV = P * R evMap[key] = expectedValue; // 尋找最高的 EV if (expectedValue > 1.0 && expectedValue > maxExpectedValue) { maxExpectedValue = expectedValue; bestOptionKey = key; } }); // 2. 遍歷表格行並進行標記 const rows = dataTableBody.querySelectorAll('tr'); rows.forEach((row, index) => { const key = OPTION_ORDER[index]; const ev = evMap[key] || 0; // 確保有 EV 值 const probabilityCell = row.cells[2]; // 移除舊樣式 probabilityCell.style.backgroundColor = 'transparent'; probabilityCell.style.color = 'white'; probabilityCell.style.fontWeight = 'normal'; probabilityCell.style.border = 'none'; if (ev > 1.0) { // 如果是正期望值 (> 1.0) probabilityCell.style.backgroundColor = '#FFEB3B'; // 黃底 probabilityCell.style.color = 'black'; if (key === bestOptionKey) { // 如果是最高的正期望值 probabilityCell.style.fontWeight = 'bold'; probabilityCell.style.border = '1px solid #FF9800'; } } }); } /** * 計算並在 UI 介面下方顯示統計數據 */ function displayStats() { let tableRowsHTML = ''; let totalCount = 0; OPTION_ORDER.forEach(key => { const displayName = DISPLAY_NAMES[key] || key; const count = drawingCounts[key]; totalCount += count; const probability = totalDraws > 0 ? (count / totalDraws * 100).toFixed(3) : '0.000'; tableRowsHTML += ` <tr> <td>${displayName}</td> <td style="text-align: right;">${count}</td> <td class="probability-cell" style="text-align: right;">${probability}%</td> </tr> `; }); const dataTableBody = document.getElementById('data-table-body'); const totalRow = document.getElementById('data-table-total'); if (dataTableBody) { dataTableBody.innerHTML = tableRowsHTML; // 數據載入後立即計算並標記正期望值 calculateExpectedValue(); } if (totalRow) { totalRow.innerHTML = ` <td>總計</td> <td style="text-align: right;">${totalDraws}</td> <td style="text-align: right;">100.000%</td> `; } let statusText = isSafetyPaused ? '安全暫停' : (isRunning ? '執行中' : '已停止'); let statusColor = isSafetyPaused ? '#FF9800' : (isRunning ? '#4CAF50' : '#ffeb3b'); updateUIStatus(statusText, statusColor); } /** * 解析單條訊息中的開獎結果 */ function parseSingleResult(messageText, userName) { const resultLineMatch = messageText.match(/開獎結果[::][\s\S]*?\([\s\S]*?(\d+)[\s\S]*?\)/); if (!resultLineMatch) return false; const resultLine = resultLineMatch[0]; let keyToUpdate = null; for (const symbol in SYMBOL_MAP) { if (resultLine.includes(symbol)) { keyToUpdate = SYMBOL_MAP[symbol]; break; } } if (keyToUpdate && drawingCounts[keyToUpdate] !== undefined) { drawingCounts[keyToUpdate]++; totalDraws++; const bahbiRegex = /剩餘巴幣[::]\s*([\d,]+)/; const bahbiMatch = messageText.match(bahbiRegex); if (bahbiMatch) { const remainingBahbi = parseInt(bahbiMatch[1].replace(/,/g, ''), 10); if (remainingBahbi > 0) { if (initialBahbi === 0) { initialBahbi = remainingBahbi; } updateBahbiStatus(remainingBahbi); } } // 第一次成功解析分析機器人發出的結果時,儲存其名稱 if (!currentAnalyzerName && userName !== PLAYER_NAME) { currentAnalyzerName = userName; console.log(`[偵測] 成功識別當前分析機器人為: ${currentAnalyzerName}`); } saveData(); displayStats(); // 每次開獎都更新並重新計算 EV return true; } return false; } /** * 檢查單一訊息是否為開獎結果,並確保不會重複計數 */ function checkAndParseNewMessage(messageElement) { if (messageElement.dataset.parsed === 'true') { return false; } const userNameEl = messageElement.querySelector('.now_user-info .now_user-nickname'); const userName = userNameEl ? userNameEl.textContent.trim() : null; const messageText = messageElement.textContent.trim(); if (!messageText) { return false; } const isResult = messageText.includes('開獎結果'); if (!isResult) return false; // 1. 判斷是否為「巴哈小瑪莉」發出的系統開獎結果 const dateRegex = /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/; const dateMatch = messageText.match(dateRegex); const isSystemResult = (userName === PLAYER_NAME || !userNameEl) && dateMatch; // 2. 判斷是否為分析機器人發出的結果 let isAnalyzerMessage = false; if (userName && userName !== PLAYER_NAME) { // 如果已經偵測到名稱,使用該名稱 if (currentAnalyzerName && userName === currentAnalyzerName) { isAnalyzerMessage = true; } // 如果尚未偵測到名稱,或這是新的分析機器人 (非巴哈小瑪莉) else if (!currentAnalyzerName || (currentAnalyzerName && userName !== currentAnalyzerName)) { // 允許通過解析,並在 parseSingleResult 中更新 currentAnalyzerName isAnalyzerMessage = true; } } if (isSystemResult || isAnalyzerMessage) { if (parseSingleResult(messageText, userName)) { messageElement.dataset.parsed = 'true'; return true; } } return false; } // =================================================================== // 4. 【控制與執行函數】 // =================================================================== function updateUIStatus(status, color) { const statusElement = document.getElementById('ui-status'); if (statusElement) { statusElement.textContent = status; statusElement.style.color = color; } const startButton = document.getElementById('start-bet'); const stopButton = document.getElementById('stop-bet'); if (startButton) startButton.disabled = isRunning || isSafetyPaused; if (stopButton) stopButton.disabled = !isRunning && !isSafetyPaused; const commandCountElement = document.getElementById('command-count'); if (commandCountElement) commandCountElement.textContent = `發送指令數: ${totalCommandsSent}`; } function readCurrentBahbiFromLastMessage() { const elements = document.querySelectorAll(".msg_container"); if (elements.length === 0) return 0; const lastMessage = elements[elements.length - 1]; const messageText = lastMessage.textContent; const bahbiRegex = /剩餘巴幣[::]\s*([\d,]+)/; const bahbiMatch = messageText.match(bahbiRegex); if (bahbiMatch) { return parseInt(bahbiMatch[1].replace(/,/g, ''), 10); } return 0; } function placeBet() { if (isSafetyPaused) { console.log('[自動投注] 因觸發安全水位線,暫停投注。'); return; } const inputField = document.getElementById(INPUT_FIELD_ID); const sendButton = document.querySelector(SEND_BUTTON_SELECTOR); const command = document.getElementById('bet-command').value; if (!inputField || !sendButton) { console.error(`找不到輸入框或發送按鈕!無法發送指令。`); stopAutoBet(); return; } if (inputField && sendButton) { inputField.textContent = command; inputField.dispatchEvent(new Event('input', { bubbles: true })); sendButton.click(); totalCommandsSent++; saveData(); console.log(`[自動投注] 已發送指令: ${command}`); updateUIStatus('執行中 (等待結果)', '#4CAF50'); } } function startAutoBet() { if (isRunning) return; isSafetyPaused = false; isRunning = true; safetyConfig.percentLoss = parseInt(document.getElementById('safety-percent-loss').value) || 0; safetyConfig.absoluteLoss = parseInt(document.getElementById('safety-absolute-loss').value.replace(/,/g, '')) || 0; safetyConfig.minInventory = parseInt(document.getElementById('safety-min-inventory').value.replace(/,/g, '')) || 0; const lastKnownBahbi = readCurrentBahbiFromLastMessage(); if (lastKnownBahbi > 0) { initialBahbi = lastKnownBahbi; currentBahbi = lastKnownBahbi; updateBahbiStatus(); document.getElementById('safety-limit-text').textContent = '檢查中...'; console.log(`[巴幣追蹤] 啟動時記錄初始巴幣: ${initialBahbi.toLocaleString()}`); } else { initialBahbi = 0; currentBahbi = 0; updateBahbiStatus(); document.getElementById('safety-limit-text').textContent = '請等待第一筆開獎結果以設定初始巴幣。'; console.warn('[巴幣追蹤] 無法從當前聊天記錄中讀取剩餘巴幣。追蹤將從下次成功開獎後開始。'); } const intervalSec = parseInt(document.getElementById('bet-interval-sec').value); // 【修改】: 使用 MS_PER_SECOND 進行計算 const intervalMs = intervalSec * MS_PER_SECOND; placeBet(); autoBetTimer = setInterval(placeBet, intervalMs); console.log(`[控制台] 自動投注已啟動,間隔 ${intervalSec} 秒。`); saveData(); } function stopAutoBet(reason = '[控制台] 手動終止。') { if (!isRunning && !isSafetyPaused) return; clearInterval(autoBetTimer); isRunning = false; if (isSafetyPaused) { isSafetyPaused = false; reason = '[資金保護解除] 已手動清除安全鎖定。'; document.getElementById('safety-limit-text').textContent = '安全鎖定已解除,請點擊執行重新啟動。'; } else { reason = '[控制台] 手動終止。'; } console.log(reason); updateUIStatus('已停止', '#ffeb3b'); updateBahbiStatus(); saveData(); } // =================================================================== // 5. 【核心 UI 函數】 // =================================================================== function makeDraggable(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; element.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; const targetTagName = e.target.tagName; if (targetTagName === 'INPUT' || targetTagName === 'BUTTON' || targetTagName === 'SELECT' || targetTagName === 'TEXTAREA' || e.target.closest('input, button, select, textarea')) { return; } e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; let newTop = element.offsetTop - pos2; let newLeft = element.offsetLeft - pos1; newTop = Math.max(0, Math.min(newTop, window.innerHeight - element.offsetHeight)); newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - element.offsetWidth)); element.style.top = newTop + "px"; element.style.left = newLeft + "px"; element.style.right = "auto"; uiPosition.top = newTop; uiPosition.left = newLeft; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; saveData(); console.log(`[UI] 位置已更新: Top: ${uiPosition.top}px, Left: ${uiPosition.left}px`); } } function createUI() { const uiContainer = document.createElement('div'); uiContainer.id = 'tampermonkey-control-ui'; // 使用 DEFAULT_BET_COMMAND 和 DEFAULT_BET_INTERVAL_SEC 常數 uiContainer.innerHTML = ` <div style="padding: 10px; border-radius: 5px; background: rgba(0, 0, 0, 0.85); color: white; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); font-size: 13px;"> <h4 style="margin: 0 0 10px 0; font-size: 16px; border-bottom: 1px solid #444; padding-bottom: 5px;">自動投注控制台 (可拖曳)</h4> <div style="margin-bottom: 8px;"> <label for="bet-command" style="display: block; font-size: 12px; margin-bottom: 3px;">投注指令</label> <input type="text" id="bet-command" value="${DEFAULT_BET_COMMAND}" style="width: 100%; padding: 4px; border: none; border-radius: 3px; background: #333; color: white;"> </div> <div style="margin-bottom: 12px; display: flex; align-items: center;"> <label for="bet-count" style="font-size: 12px; margin-right: 5px; white-space: nowrap;">投注間隔 (秒):</label> <input type="number" id="bet-interval-sec" value="${DEFAULT_BET_INTERVAL_SEC}" style="width: 50px; padding: 4px; border: none; border-radius: 3px; background: #333; color: white; text-align: center;"> <span id="ui-status" style="margin-left: auto; font-size: 14px; font-weight: bold; color: #ffeb3b;">已停止</span> </div> <div style="display: flex; justify-content: space-between;"> <button id="start-bet" style="padding: 6px 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; flex-grow: 1; margin-right: 5px;">執行</button> <button id="stop-bet" style="padding: 6px 10px; background: #F44336; color: white; border: none; border-radius: 3px; cursor: pointer; flex-grow: 1; margin-left: 5px;" disabled>終止</button> </div> <p id="command-count" style="font-size: 12px; margin: 10px 0 0 0; color: #ccc;">發送指令數: 0</p> <p id="bahbi-status" style="font-size: 12px; margin: 0; color: #aaa;">巴幣: - (未追蹤)</p> <p id="safety-limit-text" style="font-size: 12px; margin: 0; color: #ccc;">-</p> <h4 style="margin: 15px 0 5px 0; font-size: 14px; border-top: 1px solid #444; padding-top: 10px;"> 資金安全水位線 (Stop-Loss) </h4> <div class="safety-config-block" style="font-size: 12px; line-height: 1.5;"> <div class="safety-row"> <span class="safety-label">虧損達初始巴幣:</span> <input type="number" id="safety-percent-loss" value="${safetyConfig.percentLoss}" min="0" max="100" class="safety-input percent-input"> <span class="safety-unit">% 時停止</span> </div> <div class="safety-row"> <span class="safety-label">虧損達絕對金額:</span> <input type="text" id="safety-absolute-loss" value="${safetyConfig.absoluteLoss.toLocaleString()}" class="safety-input money-input"> <span class="safety-unit">元時停止</span> </div> <div class="safety-row"> <span class="safety-label">庫存低於:</span> <input type="text" id="safety-min-inventory" value="${safetyConfig.minInventory.toLocaleString()}" class="safety-input money-input"> <span class="safety-unit">元時停止</span> </div> </div> <h4 style="margin: 15px 0 5px 0; font-size: 14px; border-top: 1px solid #444; padding-top: 10px; display: flex; justify-content: space-between; align-items: center;"> <span>開獎數據分析</span> <button id="reset-data" style="padding: 2px 5px; font-size: 10px; background: #607D8B; color: white; border: none; border-radius: 3px; cursor: pointer;">歸零數據</button> </h4> <table style="width: 100%; border-collapse: collapse; font-size: 12px;"> <thead style="background: #222;"> <tr> <th style="padding: 4px; text-align: left;">選項</th> <th style="padding: 4px; text-align: right;">次數</th> <th style="padding: 4px; text-align: right;">機率</th> </tr> </thead> <tbody id="data-table-body"> </tbody> <tfoot style="border-top: 1px solid #444; font-weight: bold;"> <tr id="data-table-total"> </tr> </tfoot> </table> </div> `; // 設置 UI 樣式,使用儲存的位置,並添加移動游標 uiContainer.style.cssText = ` position: fixed; top: ${uiPosition.top}px; left: ${uiPosition.left}px; right: auto; z-index: 9999; width: 280px; font-family: Arial, sans-serif; cursor: move; `; document.body.appendChild(uiContainer); // 啟用拖曳功能 makeDraggable(uiContainer); // 綁定事件 document.getElementById('start-bet').addEventListener('click', startAutoBet); document.getElementById('stop-bet').addEventListener('click', stopAutoBet); document.getElementById('reset-data').addEventListener('click', resetData); // 綁定安全設定的輸入事件,使其自動儲存設定 document.getElementById('safety-percent-loss').addEventListener('change', updateSafetyConfig); document.getElementById('safety-absolute-loss').addEventListener('change', updateSafetyConfig); document.getElementById('safety-min-inventory').addEventListener('change', updateSafetyConfig); displayStats(); updateBahbiStatus(); document.getElementById('safety-limit-text').textContent = isSafetyPaused ? `安全暫停 (請點擊終止解除)` : '-'; } /** * 更新安全設定物件並儲存 */ function updateSafetyConfig() { safetyConfig.percentLoss = parseInt(document.getElementById('safety-percent-loss').value) || 0; safetyConfig.absoluteLoss = parseInt(document.getElementById('safety-absolute-loss').value.replace(/,/g, '')) || 0; safetyConfig.minInventory = parseInt(document.getElementById('safety-min-inventory').value.replace(/,/g, '')) || 0; // 重新格式化顯示,保持千分位 document.getElementById('safety-absolute-loss').value = safetyConfig.absoluteLoss.toLocaleString(); document.getElementById('safety-min-inventory').value = safetyConfig.minInventory.toLocaleString(); saveData(); console.log('[設定] 資金安全設定已更新。'); if (isRunning) { checkSafetyLimits(); } } function markExistingMessages() { const existingMessages = document.querySelectorAll(".msg_container"); existingMessages.forEach(el => { el.dataset.parsed = 'true'; }); console.log(`[啟動] 已標記 ${existingMessages.length} 條歷史訊息為已解析 (避免 F5 重複計數)。`); } function handleLayout() { const elements = document.querySelectorAll(".msg_container"); if (elements.length > 0) { const lastMessage = elements[elements.length - 1]; checkAndParseNewMessage(lastMessage); } } GM_addStyle(` .now_chatroom-container .chatroom .msg_container { margin-top: 12px !important; margin-bottom: 12px !important; } .now_chatroom-container.is-bpage { height: 90vh !important; } div.user-runes { display: none !important; } .chatroom { overscroll-behavior: contain; } #tampermonkey-control-ui table td, #tampermonkey-control-ui table th { padding: 4px; border-bottom: 1px dashed #333; } /* 針對控制台 UI 的樣式微調 */ #tampermonkey-control-ui input[type="number"], #tampermonkey-control-ui input[type="text"] { box-sizing: border-box; } /* 新增:統一機率欄位樣式 */ #tampermonkey-control-ui .probability-cell { padding: 4px; transition: background-color 0.3s, font-weight 0.3s; } /* V6.0 修正:資金保護區塊樣式調整 */ .safety-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 5px; } .safety-label { white-space: nowrap; margin-right: 5px; flex-shrink: 0; /* 讓 Label 佔據更多空間,把右邊擠小 */ width: 125px; } .safety-input { text-align: right; background: #333; color: white; border: none; padding: 2px; flex-grow: 1; /* 讓輸入框彈性縮放,但確保最小寬度 */ min-width: 40px; } .percent-input { width: 40px !important; flex-grow: 0 !important; } .money-input { /* 允許長數字顯示,但給予一個合理的最大寬度 */ max-width: 70px; width: auto; } .safety-unit { white-space: nowrap; width: 45px; /* 單位文字的固定寬度 */ text-align: right; margin-left: 5px; flex-shrink: 0; } `); (async function () { loadData(); createUI(); document.addEventListener("focus", function () { if (document.hidden) return; document.getElementById(INPUT_FIELD_ID)?.focus(); }); console.log('⏳ 腳本啟動延遲 2.5 秒,以等待頁面穩定和舊訊息載入...'); await new Promise(resolve => setTimeout(resolve, 2500)); const chatLog = document.querySelector("#BH-slave, .now_chatroom-container"); if (chatLog) { markExistingMessages(); const observer = new MutationObserver(handleLayout); observer.observe(chatLog, { childList: true, subtree: true }); console.log('✅ 數據分析腳本已穩定啟動,監聽聊天容器的 DOM 變動。'); } else { console.error('❌ 找不到聊天記錄容器!請檢查選擇器 #BH-slave, .now_chatroom-container 是否正確。'); } })(); })();