Auto Claim Battle Mine Reward

Tự động nhận phần thưởng Battle Mine

// ==UserScript==
// @name         Auto Claim Battle Mine Reward
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Tự động nhận phần thưởng Battle Mine
// @author       Optimized by KeshiNguyen
// @match        *://*/*
// @run-at       document-idle
// @grant        GM.xmlHttpRequest
// @grant        GM_notification
// @connect      discord.com
// ==/UserScript==

(function() {
    'use strict';
    const isGameDomain = () => {
        return /cmangax\d+\.com|cnovel/.test(location.hostname);
    };

    if (!isGameDomain()) return;

    // ======= GIỮ TAB KHÔNG BỊ TREO =======
    const keepTabAwake = () => {
        const iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        document.body.appendChild(iframe);
        setInterval(() => {
            window.focus();
            document.dispatchEvent(new MouseEvent('mousemove'));
        }, 20000);
    };

    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_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]
                        });
                    }
                }

                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); // Retry sau 200ms
            };
            check();
        });
    };

    // ======= QUẢN LÝ TIMEOUT VÀ INTERVAL =======
    let intervals = [];
    let timeouts = [];
    let baseUrl = location.hostname;
    let last_mine_id = localStorage.getItem("cmanga_last_mine_id") || "";
    let last_state = "";
    const RARE = {
        1: "Thường",
        2: "Hiếm",
        3: "Sử thi",
        4: "Truyền thuyết"
    }

    const clearAll = () => {
        intervals.forEach(clearInterval);
        timeouts.forEach(clearTimeout);
        intervals = [];
        timeouts = [];
    };

    const NOTIFICATION_CONFIG = {
        TELEGRAM: {
            token: 'Thay bằng token bằng cách tìm botfather rồi nhập /mybots rồi chọn token',
            chatId: 'Thay bằng chat id bằng cách tìm userinfo rồi start là xong'
        },
        DISCORD: {
            webhookUrl: 'https://discord.com/api/webhooks/1374401953374666864/sXgxVbDOPQDBK29JFfNqmBRs_K8ZRSxY5t-EQ9W7TAbzx6QWJKWmyp0ukbGVmMYwfqc6'
        },
        MIN_VALUE: 25
    };

    const formatForDiscord = (result, now) => {
        if (!result) return null;

        let embed = {
            title: 'Thông báo khai thác mới',
            color: 0x00ff00,
            timestamp: new Date().toISOString()
        };

        if (result?.type === "miner") {
            const current_state = `mining_${result?.score_id}`;
            if (current_state === last_state) return null;
            last_state = current_state;

            embed.description = `📊 **NGỒI KHOÁNG LÚC ${now.toLocaleString('vi-VN')}**\n\n` +
                `Vị trí::: Tầng ${result?.area} loại ${result?.rare} score_id: ${result?.score_id}`;
        }
        else if (result?.type === "is_kicked") {
            const current_state = `is_kicked`;
            if (current_state === last_state) return null;
            last_state = current_state;

            embed.description = `📊 **BỊ TẤN CÔNG KHOÁNG LÚC ${now.toLocaleString('vi-VN')}**\n\n` +
                `Bị tấn công bởi ${result?.attacker ? `${result?.attacker} với id ${result?.id}` : 'ẩn danh'}`;
        }
        else {
            return null;
        }

        return embed;
    };

    const sendViaFetch = async (message) => {
        //const formattedMessage = formatForDiscord(message, new Date());
        try {
            const response = await fetch(NOTIFICATION_CONFIG.DISCORD.webhookUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    content: '📢 Thông báo khai thác mới (via fetch)',
                    embeds: [{
                        title: 'Chi tiết kết quả',
                        description: message,
                        color: 0x00ff00,
                        timestamp: new Date().toISOString()
                    }]
                })
            });

            if (!response.ok) {
                console.error('Lỗi Discord (fetch):', await response.text());
            }
        } catch (error) {
            console.error('Lỗi fetch khi gửi thông báo:', error);
        }
    };

    const sendToDiscord = async (message) => {
        try {
            const embed = formatForDiscord(message, new Date());
            if (!embed) return;
            const payload = {
                embeds: [embed]
            };
            // Kiểm tra môi trường
            if (typeof GM === 'undefined') {
                console.warn('GM không khả dụng, sử dụng fetch API');
                return await sendViaFetch(message);
            }

            if (!GM.xmlHttpRequest) {
                console.warn('GM.xmlHttpRequest không khả dụng, sử dụng fetch API');
                return await sendViaFetch(message);
            }

            return new Promise((resolve) => {
                GM.xmlHttpRequest({
                    method: 'POST',
                    url: NOTIFICATION_CONFIG.DISCORD.webhookUrl,
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify(payload),
                    onload: (response) => {
                        if (response.status < 200 || response.status >= 300) {
                            console.error('Lỗi Discord:', response.responseText);
                        }
                        resolve();
                    },
                    onerror: (error) => {
                        console.error('Lỗi kết nối Discord:', error);
                        resolve();
                    }
                });
            });
        } catch (error) {
            console.error('Lỗi khi gửi thông báo:', error);
        }
    };

    // ======= PHẦN CHÍNH NHẬN THƯỞNG =======
    function startAutoClaim() {
        const SCORE_URL = `/api/score_list?type=battle_mine_target&target=${window.my_character}`;
        const CLAIM_URL = `/assets/ajax/character_activity.php`;
        const ENERGY_URL = `/api/character_energy_mine?character=${window.my_character}`;
        const CHARACTER_ACTIVITY_URL = `/assets/ajax/character_activity.php`;
        const OTHER_URL = `/api/get_data_by_id?table=game_character&data=other&id=${window.my_character}&v=${Date.now()}`;

        async function fetchScore() {
            try {
                const res = await fetch(SCORE_URL);
                console.log(SCORE_URL);
                const json = await res.json();
                return json.length > 0 ? json[0] : null;
            } catch (err) {
                console.error('[x] Lỗi fetchScore:', err);
                return null;
            }
        }

        const sendRequest = async (url, action) => {
            return new Promise((resolve, reject) => {
                GM.xmlHttpRequest({
                    method: "POST",
                    url: url.toString(),
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded",
                        "X-Requested-With": "XMLHttpRequest"
                    },
                    data: `action=${action}`,
                    onload: function(response) {
                        const responseText = response.responseText;
                        if (/alertify\.error/.test(responseText)) {
                            console.error("❌ Lỗi khi đăng ký hoạt động");
                            return resolve({
                                success: false,
                                message: `Fail when sign activity with action ${action}`
                            });
                        } else if (/alertify\.success/.test(responseText)) {
                            console.log("✅ Response:", responseText);
                            return resolve({
                                success: true,
                                message: `Sign activity with action ${action} successfully`,
                                data: responseText
                            });
                        }
                    },
                    onerror: function(err) {
                        console.error("❌ Lỗi mạng khi gửi request:", err);
                        return reject({
                            success: false,
                            message: `Network error: ${err}`
                        });
                    }
                });
            });
        };

        async function attack(mine_id) {
            if (!mine_id) {
                console.warn('[!] mine_id rỗng khi cố gắng tấn công. Bỏ qua.');
                return false;
            }
            try {
                const attack_res = await sendRequest(CHARACTER_ACTIVITY_URL, `battle_mine_challenge&mine_id=${mine_id}&target=public`);
                if (!attack_res.success) {
                    console.error(`[x] Attack failed: ${attack_res.message}`);
                    console.log('Response attack:::', attack_res);
                    return false;
                }
                return true;
            } catch (e) {
                console.error("Lỗi trong quá trình khiêu chiến:", e);
                return false;
            }
        }

        async function claimReward() {
            try {
                const res = await fetch(CLAIM_URL, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: new URLSearchParams({
                        action: 'battle_mine_take_reward',
                        target: 'public'
                    })
                });
                const result = await res.json();
                console.log('[✓] Nhận thưởng:', result);
                clearAll();
                timeouts.push(setTimeout(checkAndClaimReward, 60000)); // Check lại sau 1 phút
            } catch (err) {
                console.error('[x] Lỗi claimReward:', err);
            }
        }

        async function checkAndClaimReward() {
            try {
                let score_res = await fetchScore();
                let message = "";
                if (!score_res) {
                    console.log('[!] Đã bị sút khỏi hmk');
                    const other = await fetch(OTHER_URL);
                    const other_json = await other.json();
                    let other_data = JSON.parse(other_json.other);
                    let battle_mine_info = other_data?.battle_mine?.war?.info;
                    message = { id: battle_mine_info?.id, attacker: battle_mine_info?.name, type: "is_kicked" };
                    if (message) {
                        await sendToDiscord(message);
                        console.log("Đã gửi tới discord")
                    }

                    const energy_res = await fetch(ENERGY_URL);
                    const json = await energy_res.json();
                    console.log(json);
                    const energy = json.current;
                    console.log("current energy:::", energy);
                    console.log("last_mine_id:::", last_mine_id);

                    if (parseInt(energy) > 8) {
                        const attack_res = await attack(last_mine_id);
                        console.log("attack_res", attack_res);
                        if (attack_res) {
                            score_res = await fetchScore();
                        }
                    }
                }

                if (!score_res) {
                    // Nếu không có score_res, chờ 1 phút rồi chạy lại
                    timeouts.push(setTimeout(checkAndClaimReward, 60000));
                    return;
                }

                let data = JSON.parse(score_res.data);
                console.log("data:::", data);
                last_mine_id = score_res.id_score;
                localStorage.setItem('cmanga_last_mine_id', last_mine_id);
                console.log("id_score:::", last_mine_id);
                let miner = data?.miner
                console.log(`[i] Thời gian hiện tại: ${miner.times} phút`);
                message = { type: 'miner', rare: RARE[data?.rare] , area: data?.area, score_id: score_res?.id_score };
                if (message) {
                    await sendToDiscord(message);
                    console.log("Đã gửi tới discord")
                }

                if (miner.times >= 60) {
                    await claimReward();
                } else {
                    const waitMinutes = 60 - miner.times;
                    const waitMs = waitMinutes * 60000;
                    console.log(`[~] Đợi ${waitMinutes} phút`);
                    timeouts.push(setTimeout(checkAndClaimReward, waitMs));
                    timeouts.push(setTimeout(checkAndClaimReward, 60000));
                }
            } catch (err) {
                console.error('[x] Lỗi checkAndClaim:', err);
            }
        }

        checkAndClaimReward();
    }

    // Khởi động script khi trang tải xong
    window.addEventListener('load', async () => {
        try {
            const { player_id, token_character } = await waitForGameKeys();
            window.my_character = player_id;
            window.token_character = token_character;
            keepTabAwake();
            startAutoClaim();
        } catch (error) {
            console.error('Lỗi khi khởi động script:', error);
        }
    });
})();