// ==UserScript==
// @name Auto Mining Notifier
// @namespace http://tampermonkey.net/
// @version 2.7
// @description Tự động kiểm tra và thông báo kết quả khai thác
// @match https://cmangax2.com/*
// @grant GM.xmlHttpRequest
// @grant GM_notification
// @connect api.telegram.org
// @connect cmangax2.com
// @connect discord.com
// ==/UserScript==
(function() {
'use strict';
const priorityItem = {
"pet_heart_bag": "Túi thú tâm",
"add_option": "Tinh Luyện Châu",
"job_exp_3": "Thông Thạo Quyển Lv3",
"job_exp_2": "Thông Thạo Quyển Lv2",
"job_exp_1": "Thông Thạo Quyển Lv1",
"medicinal_exp_2": "Tăng Ích Đan Lv2",
"medicinal_exp_3": "Tăng Ích Đan Lv3",
"medicinal_exp_4": "Tăng Ích Đan Lv4",
"equipment_upgrade_2": "Trung phẩm Thiên Mộc Thạch",
"egg_super_fragment": "Mảnh trứng thần thú",
"egg_rare": "Trứng hiếm",
"pet_exp_chest" : "Rương thú đan"
};
const priceInMarket = {
"pet_heart_bag": 100,
"add_option": 25,
"job_exp_3": 3,
"job_exp_2": 0.4,
"job_exp_1": 0.1,
"medicinal_exp_1": 1.2,
"medicinal_exp_2": 2.4,
"medicinal_exp_3": 5,
"medicinal_exp_4": 8,
"equipment_upgrade_2": 25,
"egg_super_fragment": 5,
"egg_rare": 35,
"pet_exp_chest": 5
};
const RARE_COLORS = {
4: { name: '🔥 Truyền Thuyết', color: '#ff0000' },
3: { name: '📜 Sử Thi', color: '#c700ff' },
2: { name: '🛡️ Hiếm', color: '#0099ff' },
1: { name: '⚔️ Thường', color: '#666666' },
0: { name: '❌ Không xác định', color: '#000000' }
};
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' // Thay thế bằng webhook của bạn
},
MIN_VALUE: 0
};
let lastAttackTime = 0;
const attackCooldown = 5 * 60 * 1000; // 5 phút dưới dạng milliseconds
let isAttackInProgress = false;
let lastSentHash = ''; // Lưu trạng thái lần gửi cuối
let isRunning = false; // Cờ kiểm soát quá trình chạy
const BLACK_LIST = []
const baseUrl= "https://cmangax2.com"
const getCharacterId = () => {
const scripts = document.getElementsByTagName('script');
for (const script of scripts) {
// Regex cải tiến: Bắt cả trường hợp có khoảng trắng và dấu nháy
const match = script.textContent.match(/my_character\s*=\s*['"]?(\d+)['"]?/);
if (match) return parseInt(match[1], 10);
}
console.error('Không tìm thấy my_character trong script');
return null;
};
const getMineEnergy = async () => {
try {
const characterId = getCharacterId();
if (!characterId) {
console.error('Không tìm thấy character ID');
return 0;
}
const response = await fetch(
`${baseUrl}/api/character_energy_mine?character=${characterId}`
);
const data = await response.json();
return data.current || 0;
} catch (e) {
console.error('Lỗi khi check lượt đánh:', e);
return 0;
}
};
const calculateValue = (reward) => {
let total = 0;
const validItems = {};
for (const itemKey in reward) {
if (priorityItem[itemKey]) {
const amount = reward[itemKey].amount || 0;
const price = priceInMarket[itemKey] || 0;
if (amount > 0 && price > 0) {
const value = amount * price;
total += value;
const itemName = priorityItem[itemKey]
validItems[itemName] = (validItems[itemName] || 0) + amount;
}
}
}
return { total, validItems };
};
const processMiner = async (miner, area, index) => {
try {
const data = JSON.parse(miner.data);
const reward = data.miner?.reward;
//const isProtect = data.miner?.protect === true;
const isProtect = !!data.miner?.protect;
if (!reward || typeof reward !== 'object' || isProtect) return null;
const { total, validItems } = calculateValue(reward);
if (total <= 0) return null;
return {
area: 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,
total_value: total,
valid_items: validItems,
isProtect:isProtect
};
} catch (e) {
console.error(`Error processing miner: ${e}`);
return null;
}
};
const processArea = async (area) => {
try {
const response = await fetch(
`https://cmangax2.com/api/score_list?type=battle_mine&area=${area}`
);
const miners = await response.json();
const minersPromises = miners.map((miner, index) =>processMiner(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 [];
}
};
const processBattle = async (mine_id, target) => {
try {
console.log("mine_id", mine_id);
console.log(target)
const response = await fetch(
`${baseUrl}/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=${target}`
}
);
const responseText = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(responseText, "text/html");
const scripts = doc.getElementsByTagName('script');
let popupData = null;
let isSuccess = false;
let errorMessage = '';
for (const script of scripts) {
const scriptContent = script.textContent;
if (scriptContent.includes("alertify.success('Khiêu chiến thành công')")) {
isSuccess = true;
console.log('[DEBUG] Detected success message');
}
if (scriptContent.startsWith('popup_data =')) {
const dataString = scriptContent
.replace('popup_data =', '')
.replace(/;[\s\S]*$/, '');
console.log('[DEBUG] Raw popup_data string:', dataString);
try {
const rawData = JSON.parse(dataString.trim());
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];
})
);
} catch (e) {
console.error('[DEBUG] Lỗi parse popup_data:', e);
}
}
}
return {
success: isSuccess,
popupData: popupData,
error: errorMessage,
};
} catch (error) {
console.error('[DEBUG] Lỗi processBattle:', {
error: error,
stack: error.stack
});
return {
success: false,
error: error.message,
rawResponse: null
};
}
};
const getHmkLevel = async () => {
const areas = Array.from({ length: 11 }, (_, i) => i + 1);
const areaPromises = areas.map(area => processArea(area));
const allResults = await Promise.all(areaPromises);
return allResults
.flatMap(arr => arr)
.sort((a, b) => b.total_value - a.total_value);
};
const sendToTelegram = async (message) => {
const token = '8178445381:AAEL5AHPsPcsLYZa5qQvnWPI-3EQI3gMj04';
const chatId = '5709122878';
return new Promise((resolve) => {
GM.xmlHttpRequest({
method: 'POST',
url: `https://api.telegram.org/bot${token}/sendMessage`,
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify({
chat_id: chatId,
text: message,
parse_mode: 'HTML'
}),
onload: (response) => {
if (response.status !== 200) {
console.error('Lỗi Telegram:', response.responseText);
}
resolve();
},
onerror: (error) => {
console.error('Lỗi kết nối Telegram:', error);
resolve();
}
});
});
};
const sendToDiscord = async (message) => {
return new Promise((resolve) => {
GM.xmlHttpRequest({
method: 'POST',
url: NOTIFICATION_CONFIG.DISCORD.webhookUrl,
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify({
content: '📢 Thông báo khai thác mới',
embeds: [{
title: 'Chi tiết kết quả',
description: message,
color: 0x00ff00,
timestamp: new Date().toISOString()
}]
}),
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();
}
});
});
};
const formatForDiscord = (filtered) => {
const now = new Date();
const options = {
timeZone: 'Asia/Ho_Chi_Minh',
hour12: false,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
};
const vnTime = new Intl.DateTimeFormat('vi-VN', options)
.format(now)
.replace(/(\d+)\/(\d+)\/(\d+),/, '$1-$2-$3');
let message = `📊 **KẾT QUẢ KHAI THÁC MỚI LÚC ${vnTime}**\n\n`;
filtered.slice(0, 15).forEach((item, index) => {
const rare = parseInt(item.rare) || 0;
const rareInfo = RARE_COLORS[rare] || RARE_COLORS[0];
message += `🏷 **#${index + 1}**\n`
+ `├ Tầng: ${item.area}\n`
+ `├ Rare: ${rareInfo.name}\n`
+ `├ Vị trí: ${item.stt}\n`
+ `├ Mine id: ${item.mine_id}\n`
+ `├ Tác giả: ${item.author || 'Ẩn danh'} (ID: ${item.character_id})\n`
+ `├ Giá trị: ${item.total_value.toFixed(2)}💰\n`
+ `└ Vật phẩm:\n${Object.entries(item.valid_items)
.map(([name, amount]) => ` ↪ ${name} x${amount}`)
.join('\n')}\n\n`;
});
//console.log("message to discord", message)
return message;
};
const formatResults = (filtered) => {
const now = new Date();
const options = {
timeZone: 'Asia/Ho_Chi_Minh',
hour12: false,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
};
const vnTime = new Intl.DateTimeFormat('vi-VN', options)
.format(now)
.replace(/(\d+)\/(\d+)\/(\d+),/, '$1-$2-$3');
let message = `📊 <b>KẾT QUẢ KHAI THÁC MỚI LÚC ${vnTime}</b>\n\n`;
filtered.slice(0, 15).forEach((item, index) => {
const rare = parseInt(item.rare) || 0;
const rareInfo = RARE_COLORS[rare] || RARE_COLORS[0];
message += `🏷 <b>#${index + 1}</b>\n`
+ `┣ Tầng: ${item.area}\n`
+ `┣ Rare: ${rareInfo.name}\n`
+ `┣ Vị ttrí ${item.stt}\n`
+ `├ Mine id: ${item.mine_id}\n`
+ `┣ Tác giả: ${item.author || 'Ẩn danh'} id: ${item.character_id}\n`
+ `┣ Giá trị: ${item.total_value.toFixed(2)}💰\n`
+ `┗ Vật phẩm: ${Object.entries(item.valid_items)
.map(([name, amount]) => `${amount}x${name}`)
.join('\n')}\n\n`;
});
//console.log("message for tele", message)
return message;
};
const formatAttackResult = (mine, result, remainingAttacks) => {
const now = new Date();
const status = result.success ? '✅ THÀNH CÔNG' : '❌ THẤT BẠI';
let message = `⚔️ **KẾT QUẢ TẤN CÔNG MỎ ${mine.mine_id}** (${status})\n`
+ `⏰ Thời gian: ${now.toLocaleString('vi-VN')}\n`
+ `🏷 Tầng: ${mine.area} | Rare: ${RARE_COLORS[mine.rare].name}\n`
+ `🎯 Mục tiêu: ${mine.author || 'Ẩn danh'} (ID: ${mine.character_id})\n`;
if (result.success) {
const popupData = result.popupData || {};
const validItems = result.valid_items || {};
// 📥 Nhận được
message += "\n📥 **Nhận được:**\n";
let hasGain = false;
for (const [key, value] of Object.entries(validItems)) {
const itemName = priorityItem[key] || key;
message += `- ${itemName}: ${value}\n`;
hasGain = true;
}
if (!hasGain) message += "- Không có vật phẩm nào\n";
// 📤 Mất đi
message += "\n📤 **Mất đi:**\n";
let hasLoss = false;
if (mine.valid_items) {
mine.valid_items.forEach(originalItem => {
const itemName = Object.keys(originalItem)[0];
const originalAmount = Object.values(originalItem)[0];
const gainAmount = popupData[itemName] || 0;
const lostAmount = originalAmount - gainAmount;
if (lostAmount > 0) {
const displayName = priorityItem[itemName] || itemName;
message += `- ${displayName}: ${lostAmount}\n`;
hasLoss = true;
}
});
}
if (!hasLoss) message += "- Không mất vật phẩm\n";
} else {
message += `\n📛 Lý do: ${result.error || 'Do bạn yếu'}\n`;
}
message+= `\n Lượt đánh còn lại: ${remainingAttacks}\n`
return message;
};
const checkAndNotify = async () => {
if (isRunning || isAttackInProgress) return;
isRunning = true;
try {
const results = await getHmkLevel();
const filtered = results.filter(item =>
item.total_value >= NOTIFICATION_CONFIG.MIN_VALUE &&
item.isProtect === false &&
!BLACK_LIST.includes(item.character_id)
);
// Thêm thông tin lượt đánh vào log
const remainingAttacks = await getMineEnergy();
console.log(`Lượt đánh còn lại: ${remainingAttacks}`);
// Gửi thông tin các mỏ trước
const currentHash = JSON.stringify(filtered);
if (filtered.length > 0 && currentHash !== lastSentHash) {
lastSentHash = currentHash;
const messageAttack ='';
if (remainingAttacks > 0) {
isAttackInProgress = true;
const targetMine = filtered[0];
const battleResult = await processBattle(targetMine.mine_id, 'private');
console.log(`Kết quả tấn công: ${battleResult.success ? 'Thành công' : 'Thất bại'}`);
const remainingAttacks = await getMineEnergy()
messageAttack = formatAttackResult(targetMine, battleResult, remainingAttacks);
GM_notification(
`Tấn công mỏ ${battleResult.success ? 'thành công' : 'thất bại'}!`,
'Kết quả tấn công'
);
isAttackInProgress = false;
}
const telegramMessage = formatResults(filtered);
const discordMessage = formatForDiscord(filtered);
await Promise.all([
//sendToTelegram(telegramMessage),
sendToDiscord(discordMessage),
sendToDiscord(messageAttack)
]);
}
} catch (e) {
GM_notification(`Lỗi hệ thống: ${e.message}`, 'Lỗi');
isAttackInProgress = false;
} finally {
isRunning = false;
}
};
//Kiểm tra định kỳ
setTimeout(function runner() {
checkAndNotify();
setTimeout(runner, 30000);
}, 1000);
})();