您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tool khoáng
// ==UserScript== // @name Tool full options // @namespace http://tampermonkey.net/ // @version 0.3.2 // @description Tool khoáng // @author Optimized by KeshiNguyen // @match *://*/* // @run-at document-idle // @grant GM.xmlHttpRequest // @grant GM_notification // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @connect discord.com // ==/UserScript== 'use strict'; (function() { //Check update function checkForUpdates() { const currentVersion = GM_info.script.version; const lastVersion = GM_getValue('lastVersion', ''); if (lastVersion && lastVersion !== currentVersion) { // Hiển thị thông báo cập nhật GM_notification({ text: `Script đã được cập nhật từ ${lastVersion} lên ${currentVersion}. Nhấp để xem thay đổi.`, title: 'Cập nhật Script', image: 'https://greasyfork.org/assets/blacklogo96-ea600e6a6acf4efc2c543cc4a6f5c6c3506a567aeda3e3b8c347c2baab4d2f90.png', onclick: function() { // Mở trang changelog hoặc Greasy Fork window.open('https://greasyfork.org/en/scripts/549540-tool-full-options/versions', '_blank'); } }); // Ghi log vào console console.log(`[Script Update] Đã cập nhật từ ${lastVersion} lên ${currentVersion}`); } // Lưu phiên bản hiện tại GM_setValue('lastVersion', currentVersion); } checkForUpdates(); const isGameDomain = () => { return /cmangax\d+\.com|cnovel/.test(location.hostname); }; if (!isGameDomain()) return; let target = ""; let reload = 10 * 1000; const URL = { ENERGY: (character_id) => `/api/character_energy_mine?character=${character_id}`, HMK_AREA: (area) => `/api/score_list?type=battle_mine&area=${area}`, OTHER: (id) => `api/get_data_by_id?table=game_character&data=info,other&id=${id}`, }; let diffTimeServer = 2 * 60 * 60; //lệch 2 giờ // Thêm CSS cho giao diện GM_addStyle(` .keshi-miner-ui { position: fixed; bottom: 140px; left: 20px; z-index: 10000; font-family: Arial, sans-serif; } .keshi-miner-button { background: linear-gradient(135deg, #4ecca3 0%, #2a9d8f 100%); color: white; border: none; border-radius: 50%; width: 60px; height: 60px; font-size: 16px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; } .keshi-miner-button:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); } .keshi-miner-menu { position: absolute; bottom: 70px; left: 0; min-width: 800px; background: #16213e; border-radius: 10px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); overflow: hidden; display: none; min-height: 1000px; font-size: 18px; } .keshi-miner-menu.active { display: block; } .keshi-menu-header { padding: 15px; background: #0f3460; text-align: center; font-weight: bold; font-size: 18px; color: #4ecca3; } .keshi-tabs { display: flex; background: #0f3460; } .keshi-tab { flex: 1; padding: 12px; text-align: center; cursor: pointer; transition: background 0.3s; color: white; } .keshi-tab.active { background: #4ecca3; color: #16213e; font-weight: bold; } .keshi-tab-content { padding: 15px; overflow-y: auto; color: white; } .keshi-tab-pane { display: none; } .keshi-tab-pane.active { display: block; } .keshi-sub-tabs { display: flex; margin-bottom: 15px; background: #1a1a2e; border-radius: 5px; overflow: hidden; } .keshi-sub-tab { flex: 1; padding: 8px; text-align: center; cursor: pointer; transition: background 0.3s; color: white; min-heigth: 500px; } .keshi-sub-tab.active { background: #4ecca3; color: #16213e; font-weight: bold; } .keshi-form-group { margin-bottom: 15px; } .keshi-form-group label { display: block; margin-bottom: 5px; font-weight: bold; color: #4ecca3; } .keshi-form-group input, .keshi-form-group select { width: 100%; padding: 10px; border: none; border-radius: 5px; background: #1a1a2e; color: white; border: 1px solid #0f3460; } .keshi-btn { padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; transition: all 0.3s; } .keshi-btn-primary { background: #4ecca3; color: #16213e; } .keshi-btn-primary:hover { background: #2a9d8f; } .keshi-btn-danger { background: #e94560; color: white; } .keshi-btn-danger:hover { background: #c1334d; } .keshi-target-list { margin-top: 20px; overflow-y: auto; } .keshi-target-item { padding: 10px; background: #1a1a2e; border-radius: 5px; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center; border-left: 3px solid #4ecca3; color: white; } .keshi-target-info { flex: 1; } .keshi-target-actions { display: flex; gap: 5px; } .keshi-miner-list { margin-top: 15px; max-height: 800px; overflow-y: auto; } .keshi-miner-item { padding: 12px; background: #1a1a2e; border-radius: 5px; margin-bottom: 10px; border-left: 3px solid #0f3460; color: white; font-size: 18px; } .keshi-miner-header { display: flex; justify-content: space-between; margin-bottom: 8px; } .keshi-miner-area { font-weight: bold; color: #4ecca3; } .keshi-miner-rare { color: #e94560; } .keshi-miner-details { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; font-size: 0.9rem; } .keshi-status-indicator { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; } .keshi-status-active { background: #4ecca3; } .keshi-status-inactive { background: #e94560; } .keshi-mining-controls { display: flex; gap: 10px; margin-bottom: 15px; } .keshi-stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-top: 15px; } .keshi-stat-card { background: #1a1a2e; border-radius: 8px; padding: 15px; text-align: center; border-left: 4px solid #4ecca3; } .keshi-stat-value { font-size: 24px; font-weight: bold; color: #4ecca3; margin: 10px 0; } .keshi-stat-label { font-size: 14px; color: #cfd0d4; } .keshi-clear-data { margin-top: 20px; text-align: center; } .keshi-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); display: flex; justify-content: center; align-items: center; z-index: 10001; } .keshi-stat-item { display: grid; grid-template-columns: 250px 1fr; align-items: center; gap: 10px; font-size: 22px; padding: 12px; margin: 4px 0; border-left: 6px solid transparent; background: #1a1a1a; border-radius: 6px; } .keshi-stat-item span { font-weight: bold; } /* màu theo trạng thái */ .keshi-stat-item.success { border-left-color: #4CAF50; /* xanh lá */ } .keshi-stat-item.fail { border-left-color: #f44336; /* đỏ */ } .keshi-stat-item.register { border-left-color: #ffffff; /* trắng */ } .keshi-modal-content { background: #16213e; border-radius: 10px; padding: 20px; width: 400px; max-width: 90%; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5); } .keshi-detail-miner { display: flex; justify-content: space-between; padding: 15px; } .keshi-modal-header { font-size: 18px; font-weight: bold; color: #4ecca3; margin-bottom: 15px; text-align: center; } .keshi-character-info { display: flex; margin-bottom: 20px; align-items: center; } .keshi-character-avatar { width: 80px; height: 80px; border-radius: 50%; margin-right: 15px; border: 3px solid #4ecca3; } .keshi-character-details { flex: 1; } .keshi-character-name { font-size: 16px; font-weight: bold; color: white; margin-bottom: 5px; } .keshi-character-guild { font-size: 14px; color: #cfd0d4; margin-bottom: 5px; } .keshi-character-id { font-size: 14px; color: #cfd0d4; } .keshi-modal-actions { display: flex; justify-content: center; gap: 10px; } .keshi-loading { text-align: center; padding: 20px; color: #4ecca3; } .battle-detail { } `); const RARE = { 4: { name: '🔥 Truyền Thuyết', src: '/assets/img/level/icon/mine/4.png',color: '#ff0000' }, 3: { name: '📜 Sử Thi', src: '/assets/img/level/icon/mine/3.png', color: '#c700ff' }, 2: { name: '🛡️ Hiếm', src: '/assets/img/level/icon/mine/2.png', color: '#0099ff' }, 1: { name: '⚔️ Thường', src: '/assets/img/level/icon/mine/1.png', color: '#666666' }, }; const ACTION = { attack: { name: "Tấn công"}, isAttacked: { name: "Bị tấn công"}, buy: { name: "Mua"}, read: { name: "Đọc truyện"}, sign: { name: "Tham gia"}, donate: { name: "Cống"}, } const COLOR_CODE = { success: {color: "#4CAF50"}, fail: {color: "#f44336"}, register: {color: "#ffffff"}, } const waitForGameKeys = (timeout = 5000) => { return new Promise((resolve, reject) => { const start = Date.now(); const check = () => { const scripts = document.getElementsByTagName('script'); for (const script of scripts) { const player_id = script.textContent.match(/my_character\s*=\s*['"]?(\d+)['"]?/); const token_user = script.textContent.match(/token_user\s*=\s*['"]?(\d+)['"]?/); const token_character = script.textContent.match(/token_character\s*=\s*['"]?([a-zA-Z0-9]+)['"]?/); if (player_id && token_character) { return resolve({ player_id: parseInt(player_id[1], 10), token_character: token_character[1], token_user: parseInt(token_user[1], 10), }); } } if (Date.now() - start > timeout) { return reject("Không tìm thấy thông tin player/token trong thời gian cho phép."); } setTimeout(check, 200); }; check(); }); }; const getTime = (time) => { const options = { timeZone: 'Asia/Ho_Chi_Minh', // Múi giờ Việt Nam day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }; return time.toLocaleString('vi-VN', options) } function deepMerge(target, source) { for (const key in source) { if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) { if (!target[key] || typeof target[key] !== "object") { target[key] = {}; } deepMerge(target[key], source[key]) } else { target[key] = source[key] } } return target; } class MinerUI { constructor() { if (!window.MiningManager) { let _targets = []; window.MiningManager = { get targets() { return _targets; }, set targets(val) { _targets = val; //console.log("[MiningManager] cập nhật targets:", _targets); if (window.minerUIInstance) { window.minerUIInstance.renderMiningList(); } }, me: [] }; } if (!window.SManager) { window.SManager = { save(key, data) { try { localStorage.setItem(`keshi_miner_${key}`, JSON.stringify(data)); return true; } catch (e) { console.error('Lỗi khi lưu dữ liệu:', e); return false; } }, load(key) { try { const data = localStorage.getItem(`keshi_miner_${key}`); return data ? JSON.parse(data) : null; } catch (e) { console.error('Lỗi khi tải dữ liệu:', e); return null; } }, remove(key) { try { localStorage.removeItem(`keshi_miner_${key}`); return true; } catch (e) { console.error('Lỗi khi xóa dữ liệu:', e); return false; } } }; } if (!window.AccountManager) { let _accounts = window.SManager.load("accounts") ?? []; /* account: { info: {id, name, level, guild, legendary}, activity: { daily: {dungeon: {turn: ?? / max_turn}, dragon_tomb: {sign_at, total: 10000}, word: {event: ??/3, total_sign: ??/20, repair: ,detail_turn: [{time: {start_at, end_at}, treasure: {gold, item:[]}, repair:{fee} }], legendary, guild_transport: {sign_at, upgrade, total},treasure_find: {turn: ??/max_turn, detail: {turn_1: {sign_at}, turn_2: {}}} }, weekly: {battle_doa, pet_champion, battle_champion, black_prison, guild_battle} }, mine: {} } */ window.AccountManager = { accounts: _accounts, get accounts() { return _accounts; }, loadAccounts() { const accountsSaved = window.SManager.load("acccounts") ?? []; if (Array.isArray(accountsSaved)) _accounts = accountsSaved; return _accounts; }, addAccount(account) { const exists = _accounts.some(acc => acc.info?.id === account?.info?.id); if (!exists) { _accounts.push(account); this.save(); } return null }, updateAccount(account_id, field_update={}) { const idx = _accounts.findIndex(acc => acc.info?.id === account_id) if (idx===-1) { console.warn(`[AccountManager] Account ${account_id} not found`); return null; } _accounts[idx] = deepMerge(_accounts[idx], field_update) this.save(); return _accounts[idx] }, removeAccount(account_id) { const before = _accounts.length; _accounts = _accounts.filter(acc => acc.info?.id !== account_id); if (_accounts.length < before) { this.save(); return true; } return true; }, save() { window.SManager.save("accounts", _accounts); return true; } } } if (!window.TargetManager) { const baseManager = { targets: [], loadTargets() { const saved = window.SManager.load("targets"); this.targets = saved || []; console.log("load target from localStorage") return this.targets; }, saveTargets() { return window.SManager.save("targets", this.targets); }, updateProcess(target_id) { const target = this.targets.find(t => t.id == target_id) if (!target) return false; target.processing = !target.processing; this.saveTargets(); return target.processing; }, addTarget(target) { if (this.targets.some(t => t.id === target.id)) return false; target.addedAt = new Date().toISOString(); this.targets.push(target); return this.saveTargets(); }, removeTarget(targetId) { this.targets = this.targets.filter(t => t.id !== targetId); return this.saveTargets(); }, clearAllTargets() { this.targets = []; return this.saveTargets(); } } window.TargetManager = new Proxy(baseManager, { set(target, prop, value) { if (prop === "targets") { const old = target[prop]; if (JSON.stringify(old) !== JSON.stringify(value)){ target[prop] = value; //console.log("[TargetManager] targets thay đổi:", value); target.saveTargets(); if (window.autoMiner) window.autoMiner.init(); if (window.minerUIInstance) window.minerUIInstance.renderTargetList(); } } target[prop] = value; return true; } }) } if (!window.StatsManager) { let _stats = window.SManager.load("stats") ?? {}; const todayKey = getTime(new Date()).split(" ")[1]; _stats[todayKey] ??= []; window.StatsManager = { get stats() { return _stats; }, set stats(val) { if (val && typeof val === 'object') { _stats = val; window.SManager.save('stats', _stats); if (window.minerUIInstance && typeof window.minerUIInstance.updateStatsDisplay === 'function') { window.minerUIInstance.updateStatsDisplay(); } } else { console.error("StatsManager: stats phải là object theo ngày"); } }, loadStats() { const saved = window.SManager.load('stats'); if (saved && typeof saved === 'object') _stats = saved; return _stats; }, getToday() { const statDate = getTime(new Date()).split(' ')[1] return this.get(statDate) }, add(stat) { //stat {time, action, map, detail, target, status, other, } if(!stat || typeof stat !== 'object' || !stat.time) { alert("Lỗi dữ liệu đầu vào") return false }; // Check phạm vi thời gian (không quá 10 phút trước và không vượt tương lai) if ((new Date().getTime() - stat.time > 10 * 60 * 1000) || stat?.time > new Date().getTime()) { alert("Thời gian không thuộc phạm vi cho phép") return false; } const statDate = getTime(new Date(stat.time)).split(' ')[1] //test if (!_stats[statDate]) { _stats[statDate] = []; } _stats[statDate].push(stat) window.SManager.save("stats", _stats) console.log("Đã lưu thành công 1 stat mới:::", stat); console.log(window.StatsManager.loadStats()) if (window.minerUIInstance && typeof window.minerUIInstance.updateStatsDisplay === 'function') { window.minerUIInstance.updateStatsDisplay(stat); } return true }, get(dateKey) { return _stats[dateKey] || null; } }; window.SManager.save("stats", _stats); } this.miningManager = window.MiningManager; this.targetManager = window.TargetManager; this.statManager = window.StatsManager; this.accountManager = window.AccountManager; window.minerUIInstance = this; // Tạo container UI this.minerUI = document.createElement('div'); this.minerUI.className = 'keshi-miner-ui'; document.body.appendChild(this.minerUI); this.initUI(); //console.log("DEBUG StorageManager:", window.StorageManager); //console.log("DEBUG typeof load:", typeof window.StorageManager.load); this.targetManager.loadTargets(); this.statManager.loadStats(); this.statManager.getToday(); this.renderTargetList(); this.renderMiningList(); this.renderStatsList({ mode: "all" }); this.renderStatsList({ mode: "today" }); this.renderCloneList(); } initUI() { this.minerUI.innerHTML = ` <button class="keshi-miner-button" id="keshiMinerToggle">Tool</button> <div class="keshi-miner-menu" id="keshiMinerMenu"> <div class="keshi-tabs"> <div class="keshi-tab active" data-tab="status">Trang thái</div> <div class="keshi-tab" data-tab="dig">Dí khoáng</div> <div class="keshi-tab" data-tab="clone">Clone</div> <div class="keshi-tab" data-tab="stats">Thống kê</div> <div class="keshi-tab" data-tab="settings">Cài đặt</div> </div> <div class="keshi-tab-content"> <div class="keshi-tab-pane" id="keshi-status-pane"> <div class="keshi-sub-tabs"> <div class="keshi-sub-tab active" data-subtab="activity">Hoạt động</div> <div class="keshi-sub-tab" data-subtab="word">Cửu giới</div> <div class="keshi-sub-tab" data-subtab="mine">Khoáng</div> </div> <div class="keshi-sub-tab-content"> <div class="keshi-tab-pane active" id="keshi-activity-pane"> <div class="keshi-miner-list" id="keshi-activity-list"></div> </div> <div class="keshi-tab-pane" id="keshi-word-pane"> <div class="keshi-miner-list" id="keshi-word-list"></div> </div> <div class="keshi-tab-pane" id="keshi-mine-pane"> <div class="keshi-miner-list" id="keshi-mine-list"></div> </div> </div> </div> <div class="keshi-tab-pane" id="keshi-clone-pane"> <div class="keshi-sub-tabs"> <div class="keshi-sub-tab active" data-subtab="clone-accounts">List account</div> <div class="keshi-sub-tab " data-subtab="clone-status">Status</div> <div class="keshi-sub-tab" data-subtab="clone-mine">Khoáng</div> </div> <div class="keshi-sub-tab-content"> <div class="keshi-tab-pane active" id="keshi-clone-accounts-pane"> <div class="keshi-form-group"> <label for="keshiCloneId">ID:</label> <input type="text" id="keshiCloneId" placeholder="Nhập ID clone"> </div> <button class="keshi-btn keshi-btn-primary" id="keshiAddClone">Thêm clone</button> <div class="keshi-target-list" id="keshi-clone-list"></div> </div> <div class="keshi-tab-pane" id="keshi-clone-status-pane"> <div class="keshi-miner-list" id="keshi-clone-status"></div> </div> <div class="keshi-tab-pane" id="keshi-clone-mine-pane"> <div class="keshi-miner-list" id="keshi-clone-mine"></div> </div> </div> </div> <div class="keshi-tab-pane active" id="keshi-dig-pane"> <div class="keshi-sub-tabs"> <div class="keshi-sub-tab active" data-subtab="setting">Mục tiêu</div> <div class="keshi-sub-tab" data-subtab="miner">Khoáng</div> </div> <div class="keshi-sub-tab-content"> <div class="keshi-tab-pane active" id="keshi-setting-pane"> <div class="keshi-form-group"> <label for="keshiTargetId">ID:</label> <input type="text" id="keshiTargetId" placeholder="Nhập ID"> </div> <div class="keshi-form-group"> <label for="keshiTargetNickname">Biệt danh:</label> <input type="text" id="keshiTargetNickname" placeholder="Nhập biệt danh"> </div> <div class="keshi-form-group"> <label for="keshiTargetMode">Chế độ:</label> <select id="keshiTargetMode"> <option value="UntilDead">Dí đến chết</option> <option value="Once">Dí 1 lần</option> </select> </div> <div class="keshi-form-group"> <label for="keshiTargetLimit">Giới hạn dí:</label> <input type="number" id="keshiTargetLimit" placeholder="Số lượt tối đa trong ngày...(Mặc định k giới hạn) min="1" step="1""> </div> <button class="keshi-btn keshi-btn-primary" id="keshiAddTarget">Thêm mục tiêu</button> <div class="keshi-target-list" id="keshiTargetList"></div> </div> <div class="keshi-tab-pane" id="keshi-miner-pane"> <div class="keshi-miner-list" id="keshiMinerList"></div> </div> </div> </div> <div class="keshi-tab-pane" id="keshi-stats-pane"> <div class="keshi-sub-tabs"> <div class="keshi-sub-tab active" data-subtab="today">Hôm nay</div> <div class="keshi-sub-tab" data-subtab="all">Toàn bộ</div> </div> <div class="keshi-sub-tab-content"> <div class="keshi-tab-pane active" id="keshi-today-pane"> <div class="keshi-target-list" id="keshi-stat-today"></div> </div> <div class="keshi-tab-pane" id="keshi-all-pane"> <div class="keshi-miner-list" id="keshi-stat-all"></div> </div> </div> </div> <div class="keshi-tab-pane" id="keshi-settings-pane"> <div class="keshi-sub-tabs"> <div class="keshi-sub-tab active" data-subtab="mine">Khoáng</div> <div class="keshi-sub-tab" data-subtab="activity">Hoạt động</div> </div> <div class="keshi-sub-tab-content"> <div class="keshi-tab-pane active" id="keshi-mine-pane"> </div> <div class="keshi-tab-pane" id="keshi-acitivity-pane"> </div> </div> </div> </div> </div> <!-- Modal xác nhận thêm mục tiêu --> <div class="keshi-modal" id="keshiConfirmModal" style="display: none;"> <div class="keshi-modal-content"> <div class="keshi-modal-header">Xác nhận thêm mục tiêu</div> <div id="keshiCharacterInfo"><div class="keshi-loading">Đang tải thông tin...</div></div> <div class="keshi-modal-actions"> <button class="keshi-btn keshi-btn-primary" id="keshiConfirmAdd">Thêm</button> <button class="keshi-btn keshi-btn-danger" id="keshiCancelAdd">Hủy</button> </div> </div> </div> `; this.setupEventListeners(); } setupEventListeners() { const minerToggle = this.minerUI.querySelector('#keshiMinerToggle'); const minerMenu = this.minerUI.querySelector('#keshiMinerMenu'); // Toggle menu minerToggle.addEventListener('click', e => { e.stopPropagation(); minerMenu.classList.toggle('active'); }); // Close menu if clicked outside document.addEventListener('click', e => { if (!this.minerUI.contains(e.target)) minerMenu.classList.remove('active'); }); // Tabs chính this.minerUI.querySelectorAll('.keshi-tab').forEach(tab => { tab.addEventListener('click', () => { this.minerUI.querySelectorAll('.keshi-tab').forEach(t => t.classList.remove('active')); tab.classList.add('active'); this.minerUI.querySelectorAll('.keshi-tab-pane').forEach(p => p.classList.remove('active')); const tabId = tab.dataset.tab; const tabPane = this.minerUI.querySelector(`#keshi-${tabId}-pane`); tabPane.classList.add('active'); const firstSubTab = tabPane.querySelector('.keshi-sub-tab.active') || tabPane.querySelector('.keshi-sub-tab'); if (firstSubTab) firstSubTab.click(); }); }); // Sub-tabs this.minerUI.querySelectorAll('.keshi-sub-tab').forEach(subTab => { subTab.addEventListener('click', () => { // bỏ active khỏi tất cả subtab this.minerUI.querySelectorAll('.keshi-sub-tab').forEach(t => t.classList.remove('active')); subTab.classList.add('active'); // bỏ active khỏi tất cả pane trong cùng cha const parentContent = subTab.closest('.keshi-tab-pane').querySelector('.keshi-sub-tab-content'); parentContent.querySelectorAll('.keshi-tab-pane').forEach(p => p.classList.remove('active')); // active pane đúng theo data-subtab const subtabId = subTab.dataset.subtab; const targetPane = parentContent.querySelector(`#keshi-${subtabId}-pane`); if (targetPane) targetPane.classList.add('active'); }); }); // Sau khi setupEventListeners // Tìm tab đang active và trigger click const activeTab = this.minerUI.querySelector('.keshi-tab.active'); if (activeTab) activeTab.click(); // Với subtab cũng vậy const activeSubTab = this.minerUI.querySelector('.keshi-sub-tab.active'); if (activeSubTab) activeSubTab.click(); // Thêm mục tiêu this.minerUI.querySelector('#keshiAddTarget').addEventListener('click', () => this.handleAddTarget()); this.minerUI.querySelector('#keshiAddClone').addEventListener('click', () => this.handleAddClone()); } renderTargetList() { //console.log(this.targetManager.targets) const targetList = this.minerUI.querySelector('#keshiTargetList'); targetList.innerHTML = ''; if (!this.targetManager.targets.length) { targetList.innerHTML = '<p>Chưa có mục tiêu nào. Hãy thêm mục tiêu mới.</p>'; return; } this.targetManager.targets.forEach(target => { const targetElement = document.createElement('div'); targetElement.className = 'keshi-target-item'; targetElement.innerHTML = ` <div class="keshi-target-info"> <div><strong>ID:</strong> ${target.id}</div> <div><strong>Tên:</strong> ${target.name}</div> <div><strong>Biệt danh:</strong> ${target.nickname || 'N/A'}</div> <div><strong>Chế độ:</strong> ${target.mode == "UntilDead" ? "Dí đến chết" : "Đấm 1 lần"}</div> <div><strong>Trạng thái:</strong> ${target.processing ? "Đang dí" : "Đã dừng"}</div> </div> <div class="keshi-target-actions"> <button class="keshi-btn keshi-btn-danger" data-id="${target.id}">Xóa</button> </div> `; targetElement.querySelector('.keshi-btn-danger').addEventListener('click', () => { if (confirm('Bạn có chắc chắn muốn xóa mục tiêu này?')) { this.targetManager.removeTarget(target.id); this.renderTargetList(); } }); targetList.appendChild(targetElement); }); } renderMiningList() { const minerList = this.minerUI.querySelector('#keshiMinerList'); minerList.innerHTML = ''; if(!this.targetManager.targets.length) { minerList.innerHTML = '<p>Chưa có mục tiêu nào.</p>'; return; } if (!this.miningManager.targets.length) { minerList.innerHTML = '<p>Đang tải dữ liệu</p>'; return; } this.miningManager.targets.forEach(target => { const minerItem = document.createElement('div'); minerItem.className = 'keshi-miner-item'; minerItem.dataset.targetId = target.id; minerItem.innerHTML = ` <div class="keshi-miner-header"> <div style="display: flex; align-items: center;"> <img src="${target?.avatar}" style="width: 40px; height: 40px; border-radius: 50%; margin-right: 10px;" onerror="this.src='https://via.placeholder.com/40'"> <div> <div><strong>${target?.name}</strong></div> <div>ID: ${target.id}</div> </div> </div> <span class="keshi-miner-status" data-status="idle">${target.processing ? "Đang tiến hành" : "Chưa bắt đầu"}</span> </div> <div class="keshi-miner-details"> <div><strong>Guild:</strong> ${target?.guild || 'N/A'}</div> <div><strong>Biệt danh:</strong> ${target?.nickname || 'N/A'}</div> <div><strong>Chế độ:</strong> ${target.mode}</div> <div><strong>Lần cuối dí:</strong> <span class="keshi-last-mined">Chưa có</span></div> </div> <hr> <div class=""> <div class="keshi-detail-miner"><strong>Số lượt còn lại:</strong>${target?.energy?.current}</div> <div class="keshi-detail-miner"><strong>Lần thêm lượt tiếp theo lúc:</strong>${new Date(target?.energy?.next_energy * 1000 ).toLocaleString("vi-VN", {timeZone: "Asia/Ho_Chi_Minh"})}</div> <div class="keshi-detail-miner"><strong>Trạng thái khoáng:</strong> ${target?.miner ? '<span style="font-size:18px;">🟢Đang đào khoáng</span>' : '<span style="font-size:18px;">🔴Off</span>'}</div> ${target?.miner ? ` <div class="keshi-detail-miner"><strong>Đang ngồi khoáng ở tầng:</strong> ${target?.miner?.area || 'N/A'}</div> <div class="keshi-detail-miner"><strong>Rare:</strong> ${RARE[target?.miner?.rare]?.name}</div> <div class="keshi-detail-miner"><strong>Thời gian:</strong> <span class="keshi-last-mined">Chưa có</span></div> <div class="keshi-detail-miner"><strong>Mine id:</strong> ${target?.miner?.mine_id || 'N/A'}</div> <div class="keshi-detail-miner"><strong>Dùng khiên:</strong> ${target?.miner?.isProtect ? "Có khiên" : "Không"}</div> ` : ''} </div> <div class="keshi-mining-controls" style="margin-top: 10px;"> <button class="keshi-btn keshi-btn-${target.processing ? "danger" : "primary"} keshi-start-mining" data-target-color = {} data-target-id="${target.id}">${target.processing ? "Dừng lại" : "Bắt đầu"}</button> </div> <div class="keshi-mining-results" style="margin-top: 10px; display: none;"> <div class="keshi-mining-stats"> <span class="keshi-stat-success">Thành công: 0</span> <span class="keshi-stat-fail">Thất bại: 0</span> </div> </div> `; minerList.appendChild(minerItem); const button = minerItem.querySelector(".keshi-start-mining"); button.addEventListener("click", () => { const targetId = button.dataset.targetId; const newProcessingState = window.TargetManager.updateProcess(targetId); button.textContent = newProcessingState ? "Dừng lại" : "Bắt đầu"; button.classList.remove('keshi-btn-danger', 'keshi-btn-primary'); button.classList.add(`keshi-btn-${newProcessingState ? "danger" : "primary"}`); // Cập nhật cả trạng thái hiển thị const statusSpan = minerItem.querySelector('.keshi-miner-status'); statusSpan.textContent = newProcessingState ? "Đang tiến hành" : "Chưa bắt đầu"; statusSpan.dataset.status = newProcessingState ? "processing" : "idle"; }); }); } renderCloneList() { const cloneList = this.minerUI.querySelector('#keshi-clone-list'); cloneList.innerHTML = ''; //console.log("Render clone list:", this.accountManager.accounts) if (!this.accountManager.accounts.length) { cloneList.innerHTML = '<p>Chưa có clone nào. Hãy thêm clone mới.</p>'; return; } this.accountManager.accounts.forEach(account => { const accountElement = document.createElement('div'); accountElement.className = 'keshi-target-item'; accountElement.innerHTML = ` <div class="keshi-target-info"> <div><strong>ID:</strong> ${account.id}</div> <div><strong>Tên:</strong> ${account.name}</div> <div><strong>Tên:</strong> ${account.guild}</div> </div> <div class="keshi-target-actions"> <button class="keshi-btn keshi-btn-danger" data-id="${target.id}">Xóa</button> </div> `; accountElement.querySelector('.keshi-btn-danger').addEventListener('click', () => { if (confirm('Bạn có chắc chắn muốn xóa mục tiêu này?')) { this.accountManager.removeAccount(target.id); this.renderCloneList(); } }); cloneList.appendChild(accountElement); }); } renderDetailCloneList() { const detailCloneList = this.minerUI.querySelector('#keshi-detail-clone-list'); detailCloneList.innerHTML = ''; const accounts = this.accountManager.accounts; if (!accounts || !accounts.length) { detailCloneList.innerHTML = '<div class="keshi-stat-item"><div><span>Chưa có dữ liệu</span></div></div>'; return; } accounts.forEach(account => { const {info = {}, activity = {}} = account; const {id, name, level, guild, legendary} = info; const daily = activity?.daily || {}; }) } generateLogMessage(stat) { if (!stat) { return ` <div class="keshi-stat-item "> <div><span>Chưa có dữ liệu</span></div> </div> `; } let message = ""; const time = getTime( new Date(stat?.time)); const timeSpan = `<div class="time">${time}</div>`; // class mặc định let statusClass = ""; switch (stat.action) { case 'attack': case 'isAttacked': { statusClass = (stat.action === "attack") === (stat.detail.success) ? "success" : "fail" const targetText = stat.detail.target ? `<span>${stat.detail.target.name} ID ${stat.detail.target.id}</span>` : ""; const detailText = stat.detail ? `<a class="detail" onclick="battle_id = ${stat.detail.battle_id};frame_load('battle');"> <Chi tiết: <span>Chiến báo ${stat.detail.battle_id}</span>></a>` : ''; if (stat.action === 'attack') { message = `Bạn đã <span style="color: ${COLOR_CODE[statusClass].color}"> ${statusClass === "success" ? "tiêu diệt" : "thất bại khi tấn công"}</span> ${targetText} trong ${stat.detail.map}.${detailText}`; } else if (stat.action === "isAttacked") { message = `Bạn đã <span style="color: ${COLOR_CODE[statusClass].color}"> ${statusClass === "success" ? "bị tiêu diệt" : "bị tấn công"}</span> bởi ${targetText} trong ${stat.detail.map}.${detailText}`; statusClass = stat.detail.success ? "fail" : "success"; } break; } case 'buy': { const quantity = stat.detail?.quantity || 0; const itemName = stat.detail?.item || 'vật phẩm'; const price = stat.detail?.price || 0; message = `Bạn đã mua ${quantity} ${itemName} với giá ${price}lt.`; statusClass = "register"; // ví dụ coi như giao dịch thành công break; } case 'read': { message = `Bạn đã ${ACTION[stat.action]} ${stat.detail}`; statusClass = "register"; break; } default: { message = `Bạn đã ${ACTION[stat.action]} ${stat.other || ''}`; statusClass = "register"; break; } } //console.log(message) return ` <div class="keshi-stat-item ${statusClass}"> <div><span>${timeSpan}</span></div> <div class="flex">${message}</div> </div> `; } generateDayBlock(date, statArray) { const sortedStats = [...statArray].sort((a, b) => b.time - a.time); // sắp xếp mới → cũ return ` <div class="keshi-stat-day" data-date="${date}"> <h3>Nhật ký ngày ${date}</h3> ${statArray?.length ? sortedStats.map(stat => this.generateLogMessage(stat)).join("") : "Chưa có dữ liệu gì về ngày hôm nay"} <hr> </div> `; } parseDDMMYYYY(dateStr) { const [day, month, year] = dateStr.split("/").map(Number); return new Date(year, month - 1, day); } renderStatsList({ mode = "all" } = {}) { let container; if (mode === "all") { container = this.minerUI.querySelector('#keshi-stat-all'); } else if (mode === "today") { container = this.minerUI.querySelector('#keshi-stat-today'); } else { console.error("Sai mode:", mode); return; } //console.log("mode",mode) if (!container) { console.error("Không tìm thấy container để render:", mode); return; } container.innerHTML = ''; const stats = (mode === "all") ? this.statManager.stats : this.statManager.getToday(); /*if (!stats || Object.keys(stats).length === 0) { container.innerHTML = '<p>Chưa có thông tin nào.</p>'; return; }*/ let html = ''; if (mode === "all") { // render tất cả ngày, mới → cũ html = Object.entries(stats) .sort(([dateA], [dateB]) => this.parseDDMMYYYY(dateB) - this.parseDDMMYYYY(dateA)) .map(([date, statArray]) => this.generateDayBlock(date, statArray)) .join(""); } else { // render chỉ hôm nay const todayKey = getTime(new Date()).split(" ")[1]; html = this.generateDayBlock(todayKey, stats); } container.innerHTML = html; } updateStatsDisplay(stat) { if (!stat) return; const newItemHTML = this.generateLogMessage(stat); const todayKey = getTime(new Date(stat.time)).split(" ")[1]; // --- Update tab "today" --- const todayContainer = this.minerUI.querySelector("#keshi-stat-today"); if (todayContainer) { let todayBlock = todayContainer.querySelector(`.keshi-stat-day[data-date="${todayKey}"]`); if (!todayBlock) { todayContainer.insertAdjacentHTML("afterbegin", `<div class="keshi-stat-day" data-date="${todayKey}"> <h3>Nhật ký ngày ${todayKey}</h3> ${newItemHTML} <hr> </div>` ); } else { const h3 = todayBlock.querySelector("h3"); h3.insertAdjacentHTML("afterend", newItemHTML); } } // --- Update tab "all" --- const allContainer = this.minerUI.querySelector("#keshi-stat-all"); if (allContainer) { let dateBlock = allContainer.querySelector(`.keshi-stat-day[data-date="${todayKey}"]`); if (!dateBlock) { allContainer.insertAdjacentHTML("afterbegin", `<div class="keshi-stat-day" data-date="${todayKey}"> <h3>Nhật ký ngày ${todayKey}</h3> ${newItemHTML} <hr> </div>` ); } else { const h3 = dateBlock.querySelector("h3"); h3.insertAdjacentHTML("afterend", newItemHTML); } } } handleAddTarget() { const targetId = this.minerUI.querySelector('#keshiTargetId').value; const targetNickname = this.minerUI.querySelector('#keshiTargetNickname').value; const targetMode = this.minerUI.querySelector('#keshiTargetMode').value; const targetLimit = parseInt(this.minerUI.querySelector('#keshiTargetLimit').value) || "infinity"; if (!targetId) return alert('Vui lòng nhập ít nhất ID!'); if (this.targetManager.targets.some(t => t.id === targetId)) { return alert('ID này đã tồn tại trong danh sách!'); } const confirmModal = this.minerUI.querySelector('#keshiConfirmModal'); confirmModal.style.display = 'flex'; fetch(`/api/get_data_by_id?table=game_character&data=info&id=${targetId}`) .then(res => res.json()) .then(data => { const info = JSON.parse(data.info); const name = info.name; const guild = info.guild ? info.guild.name : 'Không có guild'; const avatar = `/assets/tmp/avatar/${info.avatar}`; const characterInfo = this.minerUI.querySelector('#keshiCharacterInfo'); characterInfo.innerHTML = ` <div class="keshi-character-info"> <img src="${avatar}" class="keshi-character-avatar"> <div> <div>${info?.id}</div> <div>${name}</div> <div>Guild: ${guild}</div> </div> </div> <div class="keshi-form-group"> <label>Biệt danh:</label> <input type="text" id="keshiConfirmNickname" value="${targetNickname || ''}"> </div> <div class="keshi-form-group"> <label>Chế độ:</label> <select id="keshiConfirmMode"> <option value="UntilDead" ${targetMode === 'UntilDead' ? 'selected' : ''}>Dí đến chết</option> <option value="Once" ${targetMode === 'Once' ? 'selected' : ''}>Dí 1 lần</option> </select> </div> <div class="keshi-form-group"> <label>Giới hạn dí mỗi ngày:</label> <input type="text" id="keshiConfirmNickname" value="${targetLimit || ''}"> </div> `; // Xác nhận thêm this.minerUI.querySelector('#keshiConfirmAdd').onclick = async () => { const confirmedNickname = this.minerUI.querySelector('#keshiConfirmNickname').value; const confirmedMode = this.minerUI.querySelector('#keshiConfirmMode').value; const newTarget = { id: targetId, name, nickname: confirmedNickname, mode: confirmedMode, guild, avatar }; this.targetManager.addTarget(newTarget); this.renderTargetList(); confirmModal.style.display = 'none'; this.minerUI.querySelector('#keshiTargetId').value = ''; this.minerUI.querySelector('#keshiTargetNickname').value = ''; if (window.autoMiner) { console.log("⚡ Reload AutoMiner after adding target..."); await window.autoMiner.init(); } }; this.minerUI.querySelector('#keshiCancelAdd').onclick = () => confirmModal.style.display = 'none'; }) .catch(err => console.error('Lỗi khi lấy thông tin nhân vật:', err)); } handleAddClone() { const cloneId = this.minerUI.querySelector('#keshiCloneId').value; if (!cloneId) return alert('Vui lòng nhập ít nhất ID!'); if (this.accountManager.accounts.some(t => t.id === cloneId)) { return alert('ID này đã tồn tại trong danh sách!'); } const confirmModal = this.minerUI.querySelector('#keshiConfirmModal'); confirmModal.style.display = 'flex'; fetch(`/api/get_data_by_id?table=game_character&data=info&id=${cloneId}`) .then(res => res.json()) .then(data => { const info = JSON.parse(data.info); const name = info.name; const guild = info.guild ? info.guild.name : 'Không có guild'; const avatar = `/assets/tmp/avatar/${info.avatar}`; const characterInfo = this.minerUI.querySelector('#keshiCharacterInfo'); characterInfo.innerHTML = ` <div class="keshi-character-info"> <img src="${avatar}" class="keshi-character-avatar"> <div> <div>${info?.id}</div> <div>${name}</div> <div>Guild: ${guild}</div> </div> </div> `; this.minerUI.querySelector('#keshiConfirmAdd').onclick = async () => { const newTarget = { info: {id: cloneId, name, guild, avatar }}; this.accountManager.addAccount(newTarget); this.renderCloneList(); confirmModal.style.display = 'none'; this.minerUI.querySelector('#keshi-clone-id').value = ''; if (window.autoMiner) { console.log("⚡ Reload AutoMiner after adding target..."); await window.autoMiner.init(); } }; this.minerUI.querySelector('#keshiCancelAdd').onclick = () => confirmModal.style.display = 'none'; }) .catch(err => console.error('Lỗi khi lấy thông tin nhân vật:', err)); } } class AutoMiner { constructor(targetManager, miningManager, statsManager,accountMangaer,urlConfig, diffTimeServer = 0) { this.TargetManager = targetManager; this.MiningManager = miningManager; this.StatsManager = statsManager; this.URL = urlConfig; this.diffTimeServer = diffTimeServer; this.AccountManager= accountMangaer; // Tự động khởi tạo khi tạo instance console.log("[DEBUG] StatsManager:", this.StatsManager); this.init(); setInterval(() => { this.init(); },5 * 60 * 1000); } async processMiner(list_target, miner, area, index) { try { if (!list_target.includes(parseInt(miner.target, 10))) return null; const data = JSON.parse(miner.data); const reward = data.miner?.reward; const isProtect = !!data.miner?.protect; if (!reward || typeof reward !== 'object' || isProtect) return null; return { area, rare: data.rare, stt: index + 1, mine_id: parseInt(miner.id_score, 10), character_id: parseInt(miner.target, 10), author: data.miner?.info?.name, isProtect }; } catch (e) { console.error(`Error processing miner: ${e}`); return null; } } async processArea(targetIds, area) { try { const url = this.URL.HMK_AREA(area); const response = await fetch(url); const miners = await response.json(); const minersPromises = miners.map((miner, index) => this.processMiner(targetIds, miner, area, index)); const areaResults = await Promise.all(minersPromises); return areaResults.filter(item => item !== null); } catch (e) { console.error(`Error processing area ${area}: ${e}`); GM_notification(`Lỗi khi xử lý tầng ${area}: ${e.message}`, 'Lỗi'); return []; } } async getEnergy(id) { try { const url = this.URL.ENERGY(id); const response = await fetch(url); if (response.status === 200) { const data = await response.json(); return { current: data.current, next_energy: data.time + this.diffTimeServer }; } } catch (e) { console.error('Lỗi khi check lượt đánh:', e); window.location.reload(); } } async getHmkArea(targetIds) { const areas = Array.from({ length: 11 }, (_, i) => i + 1); const areaPromises = areas.map(area => this.processArea(targetIds, area)); const allResults = await Promise.all(areaPromises); return allResults.flatMap(arr => arr); } async processBattle (mine_id) { try { const response = await fetch( `/assets/ajax/character_activity.php`, { method: 'POST', headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `action=battle_mine_challenge&mine_id=${mine_id}&target=private` } ); //attackEndTime = Date.now(); const responseText = await response.text(); if (!responseText || responseText.trim() == "" ||responseText.includes("<!--empty-->")) { window.location.reload() } const parser = new DOMParser(); const doc = parser.parseFromString(responseText, "text/html"); const scripts = doc.getElementsByTagName('script'); let isWeak = false; let battle_id = null; let popupData = {}; let popupDataRaw = {}; let isSuccess = false;// trạng thái request let errorMessage = ''; let battleLoaded = false; let status = true; //thành công hay thất bại for (const script of scripts) { const scriptContent = script.textContent; const battleIdMatch = scriptContent.match(/battle_id\s*=\s*'([^']+)'/); if (battleIdMatch) { battle_id = battleIdMatch[1]; console.log(`[DEBUG] Found battle_id: ${battle_id}`); } if (scriptContent.includes("alertify.success")) { isSuccess = true; console.log("Tấn công thành công"); } else if (scriptContent.includes("alertify.error")) { isSuccess = false; status = false; const errorMatch = scriptContent.match(/alertify\.error\('([^']+)'\)/); if (errorMatch) { errorMessage = errorMatch[1]; console.log('[DEBUG] Lỗi khi khiêu chiến:', errorMessage); } } const popupMatch = scriptContent.match(/popup_data\s*=\s*({[\s\S]*?})\s*;/); if (popupMatch) { try { const rawData = JSON.parse(popupMatch[1]); console.log('[DEBUG] Parsed popup_data:', rawData); popupData = Object.fromEntries( Object.entries(rawData) .filter(([key]) => key !== 'gold' && key !== 'mine_ore') .map(([key, value]) => { if (value && typeof value === 'object' && 'amount' in value) { return [key, value.amount]; } return [key, value]; }) ); popupDataRaw = Object.fromEntries( Object.entries(rawData) .filter(([key]) => key) .map(([key, value]) => { if (value && typeof value === 'object' && 'amount' in value) { return [key, value.amount]; } return [key, value]; }) ); console.log("[processBattle] done parse popup_data"); } catch (e) { console.error('[DEBUG] Lỗi parse popup_data:', e); } } } return { success: isSuccess, popupData: popupData, popupDataRaw: popupDataRaw, error: errorMessage, status: status, rawResponse: responseText, battle_id: battle_id ? battle_id : null }; } catch (error) { console.error('[DEBUG] Lỗi processBattle:', { error: error, stack: error.stack }); return { success: false, error: error?.message || error, //rawResponse: responseText }; } }; async autoAttack () { try { if (!this.MiningManager?.targets?.length) { console.warn("[autoAttack] Không có targets để xử lý"); return; } //tạo khóa bảo vẹ trong trường hợp k mặc đồ = cách check set hoặc for (const target of this.MiningManager.targets) { if (!target.processing || !target.miner) continue; const miner = target.miner; const doAttack = async () => { const energy = await this.getEnergy(window?.my_character); if (energy.current <= 0) { const wait_time =energy.next_energy * 1000 - Date.now(); console.log(`[autoAttack] Hết lượt đánh, chờ thêm lượt sau ${Math.ceil(wait_time / 1000)} giây...`); await new Promise(res => setTimeout(res, wait_time + 2000)); return doAttack(); } console.log(`[autoAttack] Tấn công target ${target.id} - mine_id: ${miner.mine_id}`); const response = await this.processBattle(miner.mine_id); const stat = { time: Date.now(), action: "attack", detail: { target: { id: target.id, name: target.name, guild: target?.guild || "" }, success: response?.status, map: "Hồng mao khoáng", battle_id: response?.battle_id || null, error: response?.error || null } }; this.StatsManager.add(stat); // Nếu thất bại và mode != Once thì lặp lại if (response?.status === false && target.mode !== "Once") { console.log(`[autoAttack] Attack thất bại, thử lại target ${target.id}...`) await new Promise(res => setTimeout(res, 2000)) return doAttack() } return response; }; if(miner?.isProtect === true ||(miner?.isProtect && miner.isProtect.time)) { const delay = miner.isProtect.time || 5 * 60 * 1000; setTimeout(async () => { if(!target.processing || !target.miner) return; if (!target.miner.isProtect) { await doAttack() } else { console.log(`[autoAttack] Target ${target.id} vẫn còn bảo vệ, bỏ qua.`) } }, delay) } else { await doAttack() } } } catch (e){ console.error("[autoAttack] Lỗi:", e); } } async getOther(id) { const resposne = await fetch(this.URL.OTHER(id)) if (resposne.status === 200) { const data = await resposne.jon() const info = JSON.parse(data?.info) const other = JSON.parse(data?.other) return { info: { name: info.name, avatar: info.avatar, id: info.id, guild: info.guild.name }, other: { energy: other.energy, word: other.word, legendary: other.legendary, ancient: other.ancient, time: other.time, treasure_find: other.treasure_find, guild_quest: other.guild_quest, donate: other.donate, guild_transpot: other.guild_transpot, word_daily_event: other.word_daily_event, training: other.training } } } return null } async processAccountList() { const accounts = this.AccountManager.accounts; if (!accounts || !accounts.length) { console.warn("[processAccountList] Không có tài khoản để xử lý"); return; } const accountPromises = accounts.map(async (account) => { const characterId = account.id; const characterData = await this.getOther(characterId); account.activity.daily = { dungeon: { current: parseInt(characterData?.other?.energy?.current), buy: parseInt(characterData?.other?.energy?.buy), turn: 30 - parseInt(characterData?.other?.energy?.remain) + parseInt(characterData?.other?.energy?.buy) || 0 - parseInt(characterData?.other?.energy?.current) || 0, max: parseInt(account?.activity?.daily?.dungeon?.max) || 40 }, word: { event: { current: parseInt(characterData?.other?.word_daily_event?.day) == new Date().getDate() ? parseInt(characterData?.other?.word_daily_event?.current) : 0, }, total_sign: parseInt(characterData?.other?.day) == new Date().getDate() ? parseInt(characterData?.other?.word?.num) : 0, }, guild_transport: parseInt(characterData?.other?.guild_transpot?.day) === new Date().getDate(), treasure_find: { current: parseInt(characterData?.other?.treasure_find?.day) === new Date().getDate() ? parseInt(characterData?.other?.treasure_find?.num) : 0, max: account.activity.daily.treasure_find.max ?? 3 } }; }); await Promise.all(accountPromises); } async init() { try { console.log("Init data") this.TargetManager.loadTargets(); console.log(this.StatsManager) // Xóa dữ liệu cũ this.MiningManager.targets = []; console.log("mining::::", this.MiningManager.targets) const targetIds = this.TargetManager.targets.map(target => parseInt(target.id, 10)); const hmkArea = await this.getHmkArea(targetIds); const minersByCharacterId = {}; hmkArea.forEach(miner => { minersByCharacterId[miner.character_id] = miner; }); //console.log(hmkArea) // Dùng map để tạo mảng mới rồi gán thẳng const updatedTargets = await Promise.all(this.TargetManager.targets.map(async target => { const targetId = parseInt(target.id, 10); // Lấy thông tin energy const energy = await this.getEnergy(targetId); // Kiểm tra khoáng const minerInfo = minersByCharacterId[targetId] || false; return { ...target, energy: { current: energy.current, next_energy: energy.next_energy }, miner: minerInfo, lastUpdated: Date.now() }; })); this.MiningManager.targets = updatedTargets; console.log("mining",this.MiningManager.targets) await this.autoAttack() } catch (e) { console.error('Lỗi AutoMiner init:', e); } } } window.addEventListener('load', async () => { try { const { player_id, token_character, token_user } = await waitForGameKeys(); window.my_character = player_id; window.token_character = token_character; window.token_user = token_user; const minerUI = new MinerUI(); window.autoMiner = new AutoMiner(minerUI.targetManager, minerUI.miningManager, minerUI.statManager, minerUI.accountManager,URL, diffTimeServer); await window.autoMiner.init(); } catch (error) { console.error('Lỗi khi khởi động script:', error); } }); })();