// Biến cho logic chu kỳ
let inCycle = false;
let cycleBetNumber = 0; // Vị trí lệnh trong chu kỳ hiện tại
let cycleConsecutiveWins = 0; // Số lần thắng liên tiếp trong chu kỳ hiện tại
// Đếm số lệnh đã mở
let totalOrdersPlaced = 0;
// Thành công/Thất bại đều chuyển kênh sau mỗi vòng
const WebSocket = require('ws');
const fs = require('fs');
let wsUrls;
try {
const data = fs.readFileSync(__dirname + '\\data.txt', 'utf8');
wsUrls = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
if (wsUrls.length === 0) throw new Error('empty');
} catch (e) {
throw new Error('Vui lòng tạo file data.txt trong thư mục bot và thêm các URL WebSocket, mỗi URL trên một dòng.');
}
if (wsUrls.length === 0) throw new Error('empty');
let currentUrlIndex = 0;
let socket = null;
let betTimeout = null; // Timeout để reset trạng thái bet nếu bị treo
let currentBet = null, currentBalance = 0, betPlaced = false, balanceAfterBet = 0, gameEnded = false;
let isWaitingReverseBet = false; // Đánh dấu lệnh đặt trong lúc chờ đảo chiều
// Biến lưu kết quả ván trước
let lastResult = null; // 'Player' hoặc 'Banker'
let waitForReverse = true; // Chỉ vào lệnh khi có đảo chiều
let cycleStartPending = false; // Đã phát hiện đảo chiều và chờ mở chu kỳ chính ở BetsOpen kế tiếp
let betPattern = [40, 40, 60, 60, 60, 100, 100, 100, 160, 160, 160, 260, 160];
let choicePattern = [
'Banker', 'Player', 'Banker', 'Banker',
'Player', 'Banker', 'Player', 'Player',
'Banker', 'Player', 'Banker', 'Banker',
'Banker'
]; // Player -> Banker
let choicePattern2 = [
'Player', 'Banker', 'Player', 'Player',
'Banker', 'Player', 'Banker', 'Banker',
'Player', 'Banker', 'Player', 'Player',
'Player'
]; // Banker -> Player
let currentChoice = choicePattern[0]; // Lựa chọn hiện tại
let currentPattern = choicePattern; // Mẫu lựa chọn hiện tại
let currentBetIndex = 0, currentBetAmount = betPattern[0];
let initialBalance = 0;
let originalBalance = 0; // Vốn ban đầu thực sự (không bị reset)
let consecutiveWins = 0;
let consecutiveLosses = 0; // Đếm số lần thua liên tiếp
function setupSocketHandlers(ws) {
ws.on('open', () => {
console.log(`✅ Đã kết nối kênh ${currentUrlIndex + 1}\n⏳ Chờ đợi vòng mới...\n`);
// Reset khi kết nối thành công
// ...existing code...
// ...existing code...
// Chỉ reset bet state nếu không đang đợi kết quả (để tránh mất track)
// Nếu betPlaced = true, nghĩa là đang đợi kết quả từ vòng trước khi mất kết nối
if (!betPlaced) {
currentBet = null;
gameEnded = false;
isWaitingReverseBet = false;
} else {
console.log('⚠️ Phát hiện cược chưa có kết quả, tiếp tục theo dõi...');
// Set timeout 2 phút để reset nếu không nhận được kết quả
if (betTimeout) clearTimeout(betTimeout);
betTimeout = setTimeout(() => {
console.log('⚠️ Timeout: Không nhận kết quả sau 2 phút, reset trạng thái...');
currentBet = null;
betPlaced = false;
gameEnded = false;
isWaitingReverseBet = false;
balanceAfterBet = 0;
betTimeout = null;
}, 120000);
}
});
ws.on('message', (data) => {
try {
const parsed = JSON.parse(data);
if (parsed.type === 'baccarat.gameState') {
// Phát hiện kết quả ván trước (sửa lấy từ parsed.args.gameData.result.winner)
let winner = undefined;
if (parsed.args && parsed.args.gameData && parsed.args.gameData.result && parsed.args.gameData.result.winner) {
winner = parsed.args.gameData.result.winner;
}
if (winner && (winner === 'Player' || winner === 'Banker')) {
console.log(`[DEBUG] lastResult:`, lastResult, '| current:', winner);
if (lastResult !== null && winner !== lastResult) {
waitForReverse = false; // Đã có đảo chiều
cycleStartPending = true;
// Chọn pattern phù hợp với hướng đảo chiều
if (lastResult === 'Player' && winner === 'Banker') {
currentPattern = choicePattern;
} else if (lastResult === 'Banker' && winner === 'Player') {
currentPattern = choicePattern2;
}
} else if (!cycleStartPending && !inCycle && !isWaitingReverseBet) {
waitForReverse = true;
}
lastResult = winner;
}
if (parsed.args.betting === 'BetsClosed' && currentBet && betPlaced) {
gameEnded = true;
return;
}
if (parsed.args.betting === 'BetsOpen' && !currentBet) {
// Nếu đang trong chu kỳ thì vào lệnh liên tục, không cần chờ đảo chiều
if (!inCycle) {
// Chỉ vào lệnh khi có đảo chiều để bắt đầu chu kỳ mới
if (waitForReverse && !cycleStartPending) {
// Trong lúc chờ đảo chiều, mở 1 lệnh theo lastResult với số tiền cố định 20
if (lastResult === 'Player' || lastResult === 'Banker') {
currentChoice = lastResult;
currentBetAmount = 20;
currentBet = currentChoice;
betPlaced = false;
isWaitingReverseBet = true;
totalOrdersPlaced++;
console.log(`⏳ Chờ đảo chiều: vào lệnh dò ${currentChoice} - 20 điểm (Lệnh thứ ${totalOrdersPlaced})`);
ws.send(JSON.stringify({
id: Math.random().toString(36).substring(2, 12),
type: "baccarat.playerBetRequest",
args: {
gameId: parsed.args.gameId,
replyId: `baccarat.playerBetRequest-${Math.floor(Math.random() * 1e9)}-${Date.now()}`,
timestamp: Date.now(),
betTags: {openMwTables: 1, mwLayout: 8, btTableView: "1", orientation: "landscape"},
action: {name: "Chips", chips: {[currentChoice]: currentBetAmount}},
correlationId: Math.random().toString(16).substring(2, 18)
}
}));
}
return;
}
waitForReverse = true; // Reset lại trạng thái chờ đảo chiều cho vòng tiếp theo
cycleStartPending = false;
inCycle = true;
cycleBetNumber = 0;
cycleConsecutiveWins = 0;
isWaitingReverseBet = false;
currentBetAmount = betPattern[currentBetIndex]; // Khôi phục lại giá trị lệnh chu kỳ chính
}
// Dừng bot khi đến lệnh có giá trị betPattern[10]
if (currentBetIndex === betPattern.length - 1) {
console.log(`\n🛑 Đã thực hiện lệnh cuối cùng (betPattern[10]: ${betPattern[10]}). Dừng bot!`);
if (betTimeout) clearTimeout(betTimeout);
if (socket) socket.close();
process.exit(0);
return;
}
cycleBetNumber++;
currentChoice = currentPattern[currentBetIndex];
currentBet = currentChoice;
betPlaced = false;
totalOrdersPlaced++;
console.log(`📋 Thực hiện lệnh ${cycleBetNumber} của chu kỳ: ${currentChoice} - ${currentBetAmount} điểm [${currentBetIndex + 1}/${betPattern.length}]`);
ws.send(JSON.stringify({
id: Math.random().toString(36).substring(2, 12),
type: "baccarat.playerBetRequest",
args: {
gameId: parsed.args.gameId,
replyId: `baccarat.playerBetRequest-${Math.floor(Math.random() * 1e9)}-${Date.now()}`,
timestamp: Date.now(),
betTags: {openMwTables: 1, mwLayout: 8, btTableView: "1", orientation: "landscape"},
action: {name: "Chips", chips: {[currentChoice]: currentBetAmount}},
correlationId: Math.random().toString(16).substring(2, 18)
}
}));
}
}
if (parsed.type === 'balanceUpdated') {
const newBalance = parsed.args.balance;
const change = newBalance - currentBalance;
if (initialBalance === 0) {
initialBalance = newBalance;
originalBalance = newBalance; // Lưu vốn ban đầu thực sự
console.log(`💰 Số dư ban đầu: ${initialBalance} điểm`);
}
if (currentBet && change === -currentBetAmount && !betPlaced) {
console.log(`� ${newBalance} điểm (${change})`);
betPlaced = true;
balanceAfterBet = newBalance;
currentBalance = newBalance;
// Set timeout 2 phút để reset nếu không nhận được kết quả
if (betTimeout) clearTimeout(betTimeout);
betTimeout = setTimeout(() => {
console.log('⚠️ Timeout: Không nhận kết quả sau 2 phút, reset trạng thái...');
currentBet = null;
betPlaced = false;
gameEnded = false;
isWaitingReverseBet = false;
balanceAfterBet = 0;
betTimeout = null;
}, 120000);
return;
}
currentBalance = newBalance;
// ====== LOGIC DỪNG BOT KHI LÃI 10% ======
if (originalBalance > 0 && currentBalance >= originalBalance * 1.1) {
console.log('🎉 Đã lãi 10% so với vốn ban đầu!');
originalBalance = currentBalance; // Cập nhật lại vốn gốc để tính mốc 10% tiếp theo
}
if (gameEnded && betPlaced) {
gameEnded = false;
const changeAmount = currentBalance - balanceAfterBet;
const isDraw = Math.abs(changeAmount - currentBetAmount) < 1;
const isWin = changeAmount > currentBetAmount * 0.9;
const oldIndex = currentBetIndex;
const oldAmount = currentBetAmount;
if (betTimeout) {
clearTimeout(betTimeout);
betTimeout = null;
}
console.log('\n📊 ===== KẾT QUẢ =====');
if (isDraw) {
console.log('⚪ HÒA VỐN!');
console.log(`💎 ${currentBalance} điểm (+${changeAmount})`);
console.log('🔄 Giữ nguyên giá trị và thực hiện lại...');
console.log('====================\n');
currentBet = null;
betPlaced = false;
isWaitingReverseBet = false;
balanceAfterBet = 0;
return;
}
if (isWaitingReverseBet) {
if (isWin) {
console.log(`✅ Lệnh chờ đảo chiều THẮNG 💎 ${currentBalance} điểm`);
console.log('🔄 Tiếp tục chờ đảo chiều để bắt đầu chu kỳ chính...');
} else {
console.log(`❌ Lệnh chờ đảo chiều THUA`);
if (!waitForReverse || cycleStartPending) {
cycleStartPending = true;
console.log(`🔁 Đảo chiều phát hiện (${lastResult})! Chu kỳ chính sẽ bắt đầu vòng tới.`);
} else {
console.log('🔄 Tiếp tục chờ đảo chiều để bắt đầu chu kỳ chính...');
}
}
console.log('====================\n');
currentBet = null;
betPlaced = false;
isWaitingReverseBet = false;
balanceAfterBet = 0;
return;
}
console.log(isWin ? '✅✅✅ THÀNH CÔNG! ✅✅✅' : '❌ THẤT BẠI!');
if (isWin) console.log(`💎 ${currentBalance} điểm (+${changeAmount})`);
// ...existing code...
if (isWin) {
consecutiveWins++;
consecutiveLosses = 0;
if (inCycle) {
cycleConsecutiveWins++;
}
// Reset và chuyển bàn nếu thắng ở lệnh 1/2 hoặc đủ 2 thắng liên tiếp trong chu kỳ
if (inCycle && (cycleBetNumber <= 2 || cycleConsecutiveWins >= 2)) {
const resetReason = cycleBetNumber <= 2
? `Thắng lệnh ${cycleBetNumber} của chu kỳ`
: `Thắng ${cycleConsecutiveWins} lần liên tiếp trong chu kỳ`;
console.log(`🏆 ${resetReason}! Reset về bước 1 và chuyển bàn.`);
currentBetIndex = 0;
consecutiveWins = 0;
inCycle = false;
lastResult = null;
waitForReverse = true;
cycleStartPending = false;
cycleBetNumber = 0;
cycleConsecutiveWins = 0;
currentBetAmount = betPattern[currentBetIndex];
currentChoice = currentPattern[currentBetIndex];
// Chuyển kênh
currentUrlIndex = (currentUrlIndex + 1) % wsUrls.length;
console.log(`🔀 Chuyển sang kênh ${currentUrlIndex + 1}/${wsUrls.length}`);
if (socket) {
socket.close();
}
setTimeout(() => {
connectToWebSocket();
}, 1000);
console.log('====================\n');
currentBet = null;
betPlaced = false;
isWaitingReverseBet = false;
balanceAfterBet = 0;
return;
}
// Thắng bước lẻ thì tiến 1, thắng bước chẵn thì lùi 2 (không nhỏ hơn 0)
if ((currentBetIndex + 1) % 2 === 1) {
// Bước lẻ (index bắt đầu từ 0, nên +1), nhưng khác bước 1
if (currentBetIndex > 0) {
currentBetIndex = Math.min(currentBetIndex + 1, betPattern.length - 1);
console.log(`✅ Thắng bước lẻ (khác bước 1) → Tiến 1 bước! (${consecutiveWins} liên tiếp)`);
} else {
console.log(`✅ Thắng bước lẻ (bước 1) → Giữ nguyên! (${consecutiveWins} liên tiếp)`);
}
} else {
// Bước chẵn
currentBetIndex = Math.max(currentBetIndex - 2, 0);
console.log(`✅ Thắng bước chẵn → Lùi 2 bước! (${consecutiveWins} liên tiếp)`);
}
currentBetAmount = betPattern[currentBetIndex];
currentChoice = choicePattern[currentBetIndex];
} else {
consecutiveWins = 0;
consecutiveLosses++;
if (inCycle) {
cycleConsecutiveWins = 0;
}
console.log(`💔 Thất bại liên tiếp: ${consecutiveLosses} lần`);
currentBetIndex = Math.min(currentBetIndex + 1, betPattern.length - 1);
currentBetAmount = betPattern[currentBetIndex];
currentChoice = choicePattern[currentBetIndex];
console.log(`📊 ${oldAmount} điểm (bước ${oldIndex + 1}) → ${currentBetAmount} điểm (bước ${currentBetIndex + 1}) - Lệnh: ${currentChoice}`);
}
console.log('====================\n');
currentBet = null;
betPlaced = false;
isWaitingReverseBet = false;
balanceAfterBet = 0;
}
}
} catch(e) {}
});
ws.on('error', (error) => {
console.error('❌ Lỗi:', error.message);
});
ws.on('close', (code) => {
console.log(`🔌 Đã đóng kết nối. Code: ${code}`);
// ...existing code...
// Không tự động kết nối lại nữa
});
}
function connectToWebSocket() {
socket = new WebSocket(wsUrls[currentUrlIndex]);
setupSocketHandlers(socket);
}
// Khởi động kết nối ban đầu
connectToWebSocket();
process.on('SIGINT', () => {
console.log('\n⚠️ Đang đóng kết nối...');
isManualClose = true; // Đánh dấu là đóng thủ công
if (betTimeout) clearTimeout(betTimeout);
if (socket) socket.close();
process.exit();
});