├── data.txt ├── proxy.txt ├── package.json ├── .env ├── README.md ├── checkAPI.js ├── config ├── config.js └── userAgents.js ├── utils.js ├── zoo.js └── zoo-proxy.js /data.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /proxy.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "async": "^3.2.6", 4 | "axios": "^1.7.2", 5 | "colors": "^1.4.0", 6 | "crypto": "^1.0.1", 7 | "dotenv": "^16.4.7", 8 | "https-proxy-agent": "^7.0.5", 9 | "luxon": "^3.5.0", 10 | "readline": "^1.3.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | #id ignore tasks, don't worry if you don't understand 2 | SKIP_TASKS =["join_tg","ton_wallet_connect","boost_tg5","boost_tg6","boinkers"] 3 | BASE_URL="https://api.zoo.team" 4 | ENABLE_DEBUG=false 5 | 6 | # auto update true/false 7 | ADVANCED_ANTI_DETECTION=true 8 | 9 | #Break time (minutes) 10 | TIME_SLEEP=60 11 | 12 | #set false if you want optimize ram 13 | AUTO_SHOW_COUNT_DOWN_TIME_SLEEP=true 14 | 15 | #onduty 16 | AUTO_TAsk=true 17 | 18 | #feedtheanimals 19 | AUTO_FEED=true 20 | 21 | #buy zoo 22 | AUTO_BUY_ANIMAL=true 23 | 24 | #zoo upgrade 25 | AUTO_UPGRADE_ANIMAL=true 26 | MAX_LEVEL_UPGRADE_ANIMAL=3 27 | 28 | 29 | #Timeout between API requests 30 | DELAY_BETWEEN_REQUESTS=[1,5] 31 | 32 | #Waiting time before running bot 33 | DELAY_START_BOT=[1,5] 34 | 35 | 36 | #Number of threads 37 | MAX_THEADS=20 38 | 39 | #number of streams for non-proxy version 40 | MAX_THEADS_NO_PROXY=1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ZOO BOT 2 | 3 | ## BOT FEATURE 4 | 5 | - Auto Buy Animal 6 | - Auto Feed Animal 7 | - Auto task 8 | - Auto upgrade 9 | - Auto Join Alliance 10 | - Auto Complete Quest/Quiz 11 | 12 | ## COOMANDS 13 | ``` 14 | pkg install nodejs-lts 15 | ``` 16 | ``` 17 | pkg install git 18 | ``` 19 | ```bash 20 | git clone https://github.com/Not-D4rkCipherX/Zoo-Bot.git 21 | ``` 22 | ```bash 23 | cd Zoo-Bot 24 | ``` 25 | 26 | 2. **Instal Requirements:** 27 | ```bash 28 | npm i 29 | ``` 30 | 3. **ADD ACCOUNTS** 31 | ``` 32 | nano data.txt 33 | ``` 34 | 4.**START THE BOT** 35 | ```bash 36 | node zoo.js 37 | ``` 38 | For Proxy : 39 | ``` 40 | node zoo-proxy.js 41 | ``` 42 | For those using multiple accounts, it's recommended to use a proxy (if using only one account, there's no need to create the proxy.txt file). 43 | 44 | --- 45 | 46 | ## PROXY FORMAT 47 | 48 | ```bash 49 | http://username:passwoord@hostname:port 50 | socks5://username:password@hostname:port 51 | ``` 52 | **TUTORIAL AVAILABLE ON MY YT** 53 | -------------------------------------------------------------------------------- /checkAPI.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const { log } = require("./utils"); // Adjust the path as necessary 3 | const settings = require("./config/config"); 4 | 5 | const urlChecking = "https://raw.githubusercontent.com/Not-D4rkCipherX/APIs-checking/refs/heads/main/endpoints.json"; 6 | 7 | async function checkBaseUrl() { 8 | console.log("Checking api...".blue); 9 | if (settings.ADVANCED_ANTI_DETECTION) { 10 | const result = await getBaseApi(urlChecking); 11 | if (result.endpoint) { 12 | log("No change in api!", "success"); 13 | return result; 14 | } 15 | } else { 16 | return { 17 | endpoint: settings.BASE_URL, 18 | message: 19 | "", 20 | }; 21 | } 22 | } 23 | 24 | async function getBaseApi(url) { 25 | try { 26 | const response = await axios.get(url); 27 | const content = response.data; 28 | if (content?.zoo) { 29 | return { endpoint: content.zoo, message: content.copyright }; 30 | } else { 31 | return { 32 | endpoint: null, 33 | message: 34 | "", 35 | }; 36 | } 37 | } catch (e) { 38 | return { 39 | endpoint: null, 40 | message: 41 | "", 42 | }; 43 | } 44 | } 45 | 46 | module.exports = { checkBaseUrl }; 47 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const { _isArray } = require("../utils.js"); 3 | 4 | const settings = { 5 | TIME_SLEEP: process.env.TIME_SLEEP ? parseInt(process.env.TIME_SLEEP) : 8, 6 | MAX_THEADS: process.env.MAX_THEADS ? parseInt(process.env.MAX_THEADS) : 10, 7 | MAX_LEVEL_UPGRADE_ANIMAL: process.env.MAX_LEVEL_UPGRADE_ANIMAL ? parseInt(process.env.MAX_LEVEL_UPGRADE_ANIMAL) : 10, 8 | MAX_THEADS_NO_PROXY: process.env.MAX_THEADS_NO_PROXY ? parseInt(process.env.MAX_THEADS_NO_PROXY) : 10, 9 | MAX_AMOUNT_GACHA: process.env.MAX_AMOUNT_GACHA ? parseInt(process.env.MAX_AMOUNT_GACHA) : 100, 10 | 11 | SKIP_TASKS: process.env.SKIP_TASKS ? JSON.parse(process.env.SKIP_TASKS.replace(/'/g, '"')) : [], 12 | TYPE_UPGRADE_HERO: process.env.TYPE_UPGRADE_HERO ? JSON.parse(process.env.TYPE_UPGRADE_HERO.replace(/'/g, '"')) : [], 13 | CODE_GATEWAY: process.env.CODE_GATEWAY ? JSON.parse(process.env.CODE_GATEWAY.replace(/'/g, '"')) : [], 14 | 15 | AUTO_TASK: process.env.AUTO_TASK ? process.env.AUTO_TASK.toLowerCase() === "true" : false, 16 | AUTO_UPGRADE_ANIMAL: process.env.AUTO_UPGRADE_ANIMAL ? process.env.AUTO_UPGRADE_ANIMAL.toLowerCase() === "true" : false, 17 | ENABLE_MAP_RANGE_CHALLENGE: process.env.ENABLE_MAP_RANGE_CHALLENGE ? process.env.ENABLE_MAP_RANGE_CHALLENGE.toLowerCase() === "true" : false, 18 | 19 | AUTO_SHOW_COUNT_DOWN_TIME_SLEEP: process.env.AUTO_SHOW_COUNT_DOWN_TIME_SLEEP ? process.env.AUTO_SHOW_COUNT_DOWN_TIME_SLEEP.toLowerCase() === "true" : false, 20 | AUTO_CLAIM_BONUS: process.env.AUTO_CLAIM_BONUS ? process.env.AUTO_CLAIM_BONUS.toLowerCase() === "true" : false, 21 | ENABLE_ADVANCED_MERGE: process.env.ENABLE_ADVANCED_MERGE ? process.env.ENABLE_ADVANCED_MERGE.toLowerCase() === "true" : false, 22 | ENABLE_DEBUG: process.env.ENABLE_DEBUG ? process.env.ENABLE_DEBUG.toLowerCase() === "true" : false, 23 | 24 | AUTO_FEED: process.env.AUTO_FEED ? process.env.AUTO_FEED.toLowerCase() === "true" : false, 25 | CONNECT_WALLET: process.env.CONNECT_WALLET ? process.env.CONNECT_WALLET.toLowerCase() === "true" : false, 26 | 27 | ADVANCED_ANTI_DETECTION: process.env.ADVANCED_ANTI_DETECTION ? process.env.ADVANCED_ANTI_DETECTION.toLowerCase() === "true" : false, 28 | AUTO_BUY_ANIMAL: process.env.AUTO_BUY_ANIMAL ? process.env.AUTO_BUY_ANIMAL.toLowerCase() === "true" : false, 29 | 30 | API_ID: process.env.API_ID ? process.env.API_ID : null, 31 | BASE_URL: process.env.BASE_URL ? process.env.BASE_URL : "https://api.zoo.team", 32 | REF_ID: process.env.REF_ID ? process.env.REF_ID : "T_5475888734", 33 | 34 | DELAY_BETWEEN_REQUESTS: process.env.DELAY_BETWEEN_REQUESTS && _isArray(process.env.DELAY_BETWEEN_REQUESTS) ? JSON.parse(process.env.DELAY_BETWEEN_REQUESTS) : [1, 5], 35 | DELAY_START_BOT: process.env.DELAY_START_BOT && _isArray(process.env.DELAY_START_BOT) ? JSON.parse(process.env.DELAY_START_BOT) : [1, 15], 36 | MAP_RANGE_CHALLENGE: process.env.MAP_RANGE_CHALLENGE && _isArray(process.env.MAP_RANGE_CHALLENGE) ? JSON.parse(process.env.MAP_RANGE_CHALLENGE) : [0, 0], 37 | }; 38 | 39 | module.exports = settings; 40 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const colors = require("colors"); 3 | const { DateTime } = require("luxon"); 4 | const path = require("path"); 5 | 6 | require("dotenv").config(); 7 | 8 | function _isArray(obj) { 9 | if (Array.isArray(obj) && obj.length > 0) { 10 | return true; 11 | } 12 | 13 | try { 14 | const parsedObj = JSON.parse(obj); 15 | return Array.isArray(parsedObj) && parsedObj.length > 0; 16 | } catch (e) { 17 | return false; 18 | } 19 | } 20 | 21 | function findOptimalElement(array, coins) { 22 | let optimalElement = null; 23 | 24 | for (const item of array) { 25 | // Kiểm tra điều kiện enterFee thấp hơn coins 26 | if (item.enterFee < coins) { 27 | // Nếu chưa có optimalElement, hoặc bonus cao hơn 28 | if (!optimalElement || item.bonus > optimalElement.bonus) { 29 | optimalElement = item; 30 | } else if (item.bonus === optimalElement.bonus) { 31 | // Nếu bonus bằng nhau, kiểm tra enterFee gần hơn với coins 32 | if (coins - item.enterFee < coins - optimalElement.enterFee) { 33 | optimalElement = item; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return optimalElement; 40 | } 41 | 42 | function splitIdPet(num) { 43 | const numStr = num.toString(); 44 | const firstPart = numStr.slice(0, 3); // Lấy 3 ký tự đầu tiên 45 | const secondPart = numStr.slice(3); // Lấy phần còn lại 46 | 47 | return [parseInt(firstPart), parseInt(secondPart)]; 48 | } 49 | 50 | // Hàm để ghi đè biến môi trường 51 | const envFilePath = path.join(__dirname, ".env"); 52 | async function updateEnv(variable, value) { 53 | // Đọc file .env 54 | fs.readFile(envFilePath, "utf8", (err, data) => { 55 | if (err) { 56 | console.log("Không thể đọc file .env:", err); 57 | return; 58 | } 59 | // console.log(value, variable); 60 | // Tạo hoặc cập nhật biến trong file 61 | const regex = new RegExp(`^${variable}=.*`, "m"); 62 | const newData = data.replace(regex, `${variable}=${value}`); 63 | 64 | // Kiểm tra nếu biến không tồn tại trong file, thêm vào cuối 65 | if (!regex.test(data)) { 66 | newData += `\n${variable}=${value}`; 67 | } 68 | 69 | // Ghi lại file .env 70 | fs.writeFile(envFilePath, newData, "utf8", (err) => { 71 | if (err) { 72 | console.error("Không thể ghi file .env:", err); 73 | } else { 74 | // console.log(`Đã cập nhật ${variable} thành ${value}`); 75 | } 76 | }); 77 | }); 78 | } 79 | 80 | function sleep(seconds = null) { 81 | if (seconds && typeof seconds === "number") return new Promise((resolve) => setTimeout(resolve, seconds * 1000)); 82 | 83 | let DELAY_BETWEEN_REQUESTS = process.env.DELAY_BETWEEN_REQUESTS && _isArray(process.env.DELAY_BETWEEN_REQUESTS) ? JSON.parse(process.env.DELAY_BETWEEN_REQUESTS) : [1, 5]; 84 | if (seconds && Array.isArray(seconds)) { 85 | DELAY_BETWEEN_REQUESTS = seconds; 86 | } 87 | min = DELAY_BETWEEN_REQUESTS[0]; 88 | max = DELAY_BETWEEN_REQUESTS[1]; 89 | 90 | return new Promise((resolve) => { 91 | const delay = Math.floor(Math.random() * (max - min + 1)) + min; 92 | setTimeout(resolve, delay * 1000); 93 | }); 94 | } 95 | 96 | function saveToken(id, token) { 97 | const tokens = JSON.parse(fs.readFileSync("token.json", "utf8")); 98 | tokens[id] = token; 99 | fs.writeFileSync("token.json", JSON.stringify(tokens, null, 4)); 100 | } 101 | 102 | function getToken(id) { 103 | const tokens = JSON.parse(fs.readFileSync("token.json", "utf8")); 104 | return tokens[id] || null; 105 | } 106 | function isTokenExpired(token) { 107 | if (!token) return true; 108 | 109 | try { 110 | const [, payload] = token.split("."); 111 | if (!payload) return true; 112 | 113 | const decodedPayload = JSON.parse(Buffer.from(payload, "base64").toString()); 114 | const now = Math.floor(Date.now() / 1000); 115 | 116 | if (!decodedPayload.exp) { 117 | // console.log("Eternal token".yellow); 118 | return false; 119 | } 120 | 121 | const expirationDate = new Date(decodedPayload.exp * 1000); 122 | const isExpired = now > decodedPayload.exp; 123 | 124 | console.log(`Token expires after: ${expirationDate.toLocaleString()}`.magenta); 125 | console.log(`Token status: ${isExpired ? "Expired".yellow : "Valid".green}`); 126 | 127 | return isExpired; 128 | } catch (error) { 129 | console.log(`Error checking token: ${error.message}`.red); 130 | return true; 131 | } 132 | } 133 | 134 | function generateRandomHash() { 135 | const characters = "0123456789abcdef"; 136 | let hash = "0x"; // Bắt đầu bằng "0x" 137 | 138 | for (let i = 0; i < 64; i++) { 139 | // 64 ký tự cho hash 140 | const randomIndex = Math.floor(Math.random() * characters.length); 141 | hash += characters[randomIndex]; 142 | } 143 | 144 | return hash; 145 | } 146 | 147 | function getRandomElement(arr) { 148 | const randomIndex = Math.floor(Math.random() * arr.length); 149 | return arr[randomIndex]; 150 | } 151 | 152 | function getRandomNumber(min, max) { 153 | return Math.floor(Math.random() * (max - min) + min); 154 | } 155 | 156 | function loadData(file) { 157 | try { 158 | const datas = fs.readFileSync(file, "utf8").replace(/\r/g, "").split("\n").filter(Boolean); 159 | if (datas?.length <= 0) { 160 | console.log(colors.red(`Không tìm thấy dữ liệu ${file}`)); 161 | return []; 162 | } 163 | return datas; 164 | } catch (error) { 165 | console.log(`Không tìm thấy file ${file}`.red); 166 | return []; 167 | } 168 | } 169 | 170 | async function saveData(data, filename) { 171 | fs.writeFileSync(filename, data.join("\n")); 172 | } 173 | 174 | function log(msg, type = "info") { 175 | switch (type) { 176 | case "success": 177 | console.log(`[*] ${msg}`.green); 178 | break; 179 | case "custom": 180 | console.log(`[*] ${msg}`.magenta); 181 | break; 182 | case "error": 183 | console.log(`[!] ${msg}`.red); 184 | break; 185 | case "warning": 186 | console.log(`[*] ${msg}`.yellow); 187 | break; 188 | default: 189 | console.log(`[*] ${msg}`.blue); 190 | } 191 | } 192 | 193 | function saveJson(id, value, filename) { 194 | const data = JSON.parse(fs.readFileSync(filename, "utf8")); 195 | data[id] = value; 196 | fs.writeFileSync(filename, JSON.stringify(data, null, 4)); 197 | } 198 | 199 | function getItem(id, filename) { 200 | const data = JSON.parse(fs.readFileSync(filename, "utf8")); 201 | return data[id] || null; 202 | } 203 | 204 | function getOrCreateJSON(id, value, filename) { 205 | let item = getItem(id, filename); 206 | if (item) { 207 | return item; 208 | } 209 | item = saveJson(id, value, filename); 210 | return item; 211 | } 212 | 213 | module.exports = { 214 | _isArray, 215 | saveJson, 216 | getRandomNumber, 217 | updateEnv, 218 | saveToken, 219 | splitIdPet, 220 | getToken, 221 | isTokenExpired, 222 | generateRandomHash, 223 | getRandomElement, 224 | loadData, 225 | saveData, 226 | log, 227 | getOrCreateJSON, 228 | sleep, 229 | findOptimalElement, 230 | }; 231 | -------------------------------------------------------------------------------- /config/userAgents.js: -------------------------------------------------------------------------------- 1 | const user_agents = [ 2 | //Samsung Galaxy S22 5G 3 | "Mozilla/5.0 (Linux; Android 13; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 4 | "Mozilla/5.0 (Linux; Android 13; SM-S901U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 5 | 6 | //Samsung Galaxy S22 Ultra 5G 7 | "Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 8 | "Mozilla/5.0 (Linux; Android 13; SM-S908U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", 9 | 10 | //Samsung Galaxy S21 5G 11 | "Mozilla/5.0 (Linux; Android 13; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 12 | "Mozilla/5.0 (Linux; Android 13; SM-G991U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 13 | 14 | //Samsung Galaxy S21 Ultra 5G 15 | "Mozilla/5.0 (Linux; Android 13; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 16 | "Mozilla/5.0 (Linux; Android 13; SM-G998U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 17 | 18 | //Samsung Galaxy A53 5G 19 | "Mozilla/5.0 (Linux; Android 13; SM-A536B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 20 | "Mozilla/5.0 (Linux; Android 13; SM-A536U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 21 | 22 | //Samsung Galaxy A51 23 | "Mozilla/5.0 (Linux; Android 13; SM-A515F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 24 | "Mozilla/5.0 (Linux; Android 13; SM-A515U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 25 | 26 | //Samsung Galaxy S10 27 | "Mozilla/5.0 (Linux; Android 12; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 28 | "Mozilla/5.0 (Linux; Android 12; SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 29 | 30 | //Google Pixel 6 31 | "Mozilla/5.0 (Linux; Android 13; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 32 | 33 | //Google Pixel 6a 34 | "Mozilla/5.0 (Linux; Android 13; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 35 | 36 | //Google Pixel 6 Pro 37 | "Mozilla/5.0 (Linux; Android 13; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 38 | 39 | //Google Pixel 7 40 | "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 41 | 42 | //Google Pixel 7 Pro 43 | "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 44 | 45 | //Motorola Moto G Pure 46 | "Mozilla/5.0 (Linux; Android 12; moto g pure) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 47 | 48 | //Motorola Moto G Stylus 5G 49 | "Mozilla/5.0 (Linux; Android 12; moto g stylus 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 50 | 51 | //Motorola Moto G Stylus 5G (2022) 52 | "Mozilla/5.0 (Linux; Android 12; moto g stylus 5G (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 53 | 54 | //Motorola Moto G 5G (2022) 55 | "Mozilla/5.0 (Linux; Android 12; moto g 5G (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 56 | 57 | //Motorola Moto G Power (2022) 58 | "Mozilla/5.0 (Linux; Android 12; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 59 | 60 | //Motorola Moto G Power (2021) 61 | "Mozilla/5.0 (Linux; Android 11; moto g power (2021)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 62 | 63 | //Redmi Note 9 Pro 64 | "Mozilla/5.0 (Linux; Android 12; Redmi Note 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 65 | 66 | //Redmi Note 8 Pro 67 | "Mozilla/5.0 (Linux; Android 11; Redmi Note 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 68 | 69 | //Huawei P30 Pro 70 | "Mozilla/5.0 (Linux; Android 10; VOG-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 71 | 72 | //Huawei P30 lite 73 | "Mozilla/5.0 (Linux; Android 10; MAR-LX1A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 74 | 75 | //Redmi Note 10 Pro 76 | "Mozilla/5.0 (Linux; Android 13; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 77 | 78 | //Xiaomi Poco X3 Pro 79 | "Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 80 | 81 | //Redmi Note 11 Pro 5G 82 | "Mozilla/5.0 (Linux; Android 12; 2201116SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 83 | 84 | //OnePlus Nord N200 5G 85 | "Mozilla/5.0 (Linux; Android 12; DE2118) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 86 | 87 | //Apple iPhone SE (3rd generation) 88 | "Mozilla/5.0 (iPhone14,6; U; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/19E241 Safari/602.1", 89 | 90 | //iPhone 13 Pro Max 91 | "Mozilla/5.0 (iPhone14,3; U; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/19A346 Safari/602.1", 92 | 93 | //iPhone 12 94 | "Mozilla/5.0 (iPhone13,2; U; CPU iPhone OS 14_0like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/15E148 Safari/602.1", 95 | 96 | //iPhone 11 97 | "Mozilla/5.0 (iPhone12,1; U; CPU iPhone OS 13_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/15E148 Safari/602.1", 98 | 99 | //Apple iPhone XR (Safari) 100 | "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1", 101 | 102 | //Apple iPhone XS (Chrome) 103 | "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1", 104 | 105 | //Apple iPhone XS Max (Firefox) 106 | "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15", 107 | 108 | //Apple iPhone X 109 | "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", 110 | 111 | //Apple iPhone 8 112 | "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1", 113 | 114 | //Apple iPhone 8 Plus 115 | "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A5370a Safari/604.1", 116 | 117 | //Apple iPhone 7 118 | "Mozilla/5.0 (iPhone9,3; U; CPU iPhone OS 10_0_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1", 119 | 120 | //Apple iPhone 7 Plus 121 | "Mozilla/5.0 (iPhone9,4; U; CPU iPhone OS 10_0_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1", 122 | 123 | //Apple iPhone 6 124 | "Mozilla/5.0 (Apple-iPhone7C2/1202.466; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3", 125 | 126 | //Samsung Galaxy S23 Ultra 5G 127 | "Mozilla/5.0 (Linux; Android 13; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 128 | "Mozilla/5.0 (Linux; Android 13; SM-S918U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 129 | 130 | //Samsung Galaxy S23 5G 131 | "Mozilla/5.0 (Linux; Android 13; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 132 | "Mozilla/5.0 (Linux; Android 13; SM-S911U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 133 | 134 | //Samsung Galaxy Note 20 Ultra 5G 135 | "Mozilla/5.0 (Linux; Android 13; SM-N986B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 136 | "Mozilla/5.0 (Linux; Android 13; SM-N986U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 137 | 138 | //Samsung Galaxy Note 20 5G 139 | "Mozilla/5.0 (Linux; Android 13; SM-N981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 140 | "Mozilla/5.0 (Linux; Android 13; SM-N981U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 141 | 142 | //OnePlus 9 Pro 143 | "Mozilla/5.0 (Linux; Android 13; LE2121) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 144 | "Mozilla/5.0 (Linux; Android 13; LE2125) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 145 | 146 | //OnePlus 9 147 | "Mozilla/5.0 (Linux; Android 13; LE2113) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 148 | "Mozilla/5.0 (Linux; Android 13; LE2115) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 149 | 150 | //OnePlus 8T 151 | "Mozilla/5.0 (Linux; Android 13; KB2001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 152 | "Mozilla/5.0 (Linux; Android 13; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 153 | 154 | //Huawei P40 Pro 155 | "Mozilla/5.0 (Linux; Android 10; ELS-NX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 156 | "Mozilla/5.0 (Linux; Android 10; ELS-N04) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 157 | 158 | //Huawei P40 159 | "Mozilla/5.0 (Linux; Android 10; ANA-NX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 160 | "Mozilla/5.0 (Linux; Android 10; ANA-N29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 161 | 162 | //Xiaomi Mi 11 163 | "Mozilla/5.0 (Linux; Android 13; M2011K2C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 164 | "Mozilla/5.0 (Linux; Android 13; M2011K2G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 165 | 166 | //Xiaomi Mi 11 Ultra 167 | "Mozilla/5.0 (Linux; Android 13; M2102K1C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 168 | "Mozilla/5.0 (Linux; Android 13; M2102K1G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 169 | 170 | //Xiaomi Mi 11i 171 | "Mozilla/5.0 (Linux; Android 13; M2012K11G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 172 | 173 | //Oppo Find X5 Pro 174 | "Mozilla/5.0 (Linux; Android 13; CPH2305) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 175 | "Mozilla/5.0 (Linux; Android 13; CPH2307) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 176 | 177 | //Oppo Find X3 Pro 178 | "Mozilla/5.0 (Linux; Android 13; CPH2173) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 179 | 180 | //Oppo Reno 6 Pro 5G 181 | "Mozilla/5.0 (Linux; Android 13; CPH2247) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 182 | 183 | //Vivo X70 Pro+ 184 | "Mozilla/5.0 (Linux; Android 13; V2114) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 185 | 186 | //Vivo X60 Pro+ 187 | "Mozilla/5.0 (Linux; Android 13; V2056A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 188 | 189 | //LG Velvet 5G 190 | "Mozilla/5.0 (Linux; Android 13; LM-G900N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 191 | 192 | //Sony Xperia 1 III 193 | "Mozilla/5.0 (Linux; Android 13; XQ-BC72) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 194 | 195 | //Sony Xperia 5 II 196 | "Mozilla/5.0 (Linux; Android 13; XQ-AS72) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 197 | 198 | //Nokia 8.3 5G 199 | "Mozilla/5.0 (Linux; Android 13; Nokia 8.3 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 200 | 201 | //Nokia 7.2 202 | "Mozilla/5.0 (Linux; Android 12; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 203 | 204 | //Realme GT 2 Pro 205 | "Mozilla/5.0 (Linux; Android 13; RMX3301) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 206 | 207 | //Realme X7 Pro 208 | "Mozilla/5.0 (Linux; Android 13; RMX2121) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 209 | 210 | //Asus ROG Phone 5 211 | "Mozilla/5.0 (Linux; Android 13; ASUS_I005DA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 212 | 213 | //Asus Zenfone 8 214 | "Mozilla/5.0 (Linux; Android 13; ASUS_I006DA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 215 | 216 | //Google Pixel 7 Pro 217 | "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 218 | 219 | //Google Pixel 7 220 | "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 221 | 222 | //Google Pixel 6 Pro 223 | "Mozilla/5.0 (Linux; Android 13; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 224 | 225 | //Google Pixel 6 226 | "Mozilla/5.0 (Linux; Android 13; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 227 | 228 | //Motorola Moto G Power (2022) 229 | "Mozilla/5.0 (Linux; Android 12; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 230 | 231 | //Motorola Moto G Stylus (2022) 232 | "Mozilla/5.0 (Linux; Android 12; moto g stylus (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 233 | 234 | //ZTE Axon 30 Ultra 235 | "Mozilla/5.0 (Linux; Android 13; A2022U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 236 | 237 | //ZTE Nubia Red Magic 7 Pro 238 | "Mozilla/5.0 (Linux; Android 13; NX709J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", 239 | //Apple iPad 240 | "Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1", 241 | "Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1", 242 | "Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Mobile/15E148 Safari/604.1", 243 | "Mozilla/5.0 (iPad; CPU OS 12_4_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1", 244 | "Mozilla/5.0 (iPad; CPU OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1", 245 | //Samsung Galaxy Tab 246 | "Mozilla/5.0 (Linux; Android 12; SM-T970) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Safari/537.36", 247 | "Mozilla/5.0 (Linux; Android 11; SM-T860) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Safari/537.36", 248 | "Mozilla/5.0 (Linux; Android 10; SM-T510) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Safari/537.36", 249 | "Mozilla/5.0 (Linux; Android 9; SM-T720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Safari/537.36", 250 | "Mozilla/5.0 (Linux; Android 8.1.0; SM-T580) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Safari/537.36", 251 | ]; 252 | 253 | module.exports = user_agents; 254 | -------------------------------------------------------------------------------- /zoo.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const axios = require("axios"); 4 | const colors = require("colors"); 5 | const readline = require("readline"); 6 | const crypto = require("crypto"); 7 | const { DateTime } = require("luxon"); 8 | const { HttpsProxyAgent } = require("https-proxy-agent"); 9 | const user_agents = require("./config/userAgents"); 10 | const settings = require("./config/config"); 11 | const { sleep, loadData, findOptimalElement } = require("./utils"); 12 | const { checkBaseUrl } = require("./checkAPI"); 13 | 14 | class ZooAPIClient { 15 | constructor() { 16 | this.headers = { 17 | Accept: "*/*", 18 | "Accept-Encoding": "gzip, deflate, br", 19 | "Accept-Language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", 20 | "Content-Type": "application/json", 21 | Origin: "https://game.zoo.team", 22 | Referer: "https://game.zoo.team/", 23 | "Sec-Ch-Ua": '"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"', 24 | "Sec-Ch-Ua-Mobile": "?0", 25 | "Sec-Ch-Ua-Platform": '"Windows"', 26 | "Sec-Fetch-Dest": "empty", 27 | "Sec-Fetch-Mode": "cors", 28 | "Sec-Fetch-Site": "same-site", 29 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", 30 | "Is-Beta-Server": "null", 31 | }; 32 | this.cachedData = null; 33 | this.proxyList = []; 34 | this.loadProxies(); 35 | this.session_name = null; 36 | this.session_user_agents = this.#load_session_data(); 37 | this.baseURL = settings.BASE_URL; 38 | } 39 | #load_session_data() { 40 | try { 41 | const filePath = path.join(process.cwd(), "session_user_agents.json"); 42 | const data = fs.readFileSync(filePath, "utf8"); 43 | return JSON.parse(data); 44 | } catch (error) { 45 | if (error.code === "ENOENT") { 46 | return {}; 47 | } else { 48 | throw error; 49 | } 50 | } 51 | } 52 | 53 | #get_random_user_agent() { 54 | const randomIndex = Math.floor(Math.random() * user_agents.length); 55 | return user_agents[randomIndex]; 56 | } 57 | 58 | #get_user_agent() { 59 | if (this.session_user_agents[this.session_name]) { 60 | return this.session_user_agents[this.session_name]; 61 | } 62 | 63 | this.log(`Generating user agents...`); 64 | const newUserAgent = this.#get_random_user_agent(); 65 | this.session_user_agents[this.session_name] = newUserAgent; 66 | this.#save_session_data(this.session_user_agents); 67 | return newUserAgent; 68 | } 69 | 70 | #save_session_data(session_user_agents) { 71 | const filePath = path.join(process.cwd(), "session_user_agents.json"); 72 | fs.writeFileSync(filePath, JSON.stringify(session_user_agents, null, 2)); 73 | } 74 | 75 | #get_platform(userAgent) { 76 | const platformPatterns = [ 77 | { pattern: /iPhone/i, platform: "ios" }, 78 | { pattern: /Android/i, platform: "android" }, 79 | { pattern: /iPad/i, platform: "ios" }, 80 | ]; 81 | 82 | for (const { pattern, platform } of platformPatterns) { 83 | if (pattern.test(userAgent)) { 84 | return platform; 85 | } 86 | } 87 | 88 | return "Unknown"; 89 | } 90 | 91 | set_headers() { 92 | const platform = this.#get_platform(this.#get_user_agent()); 93 | this.headers["sec-ch-ua"] = `"Not)A;Brand";v="99", "${platform} WebView";v="127", "Chromium";v="127`; 94 | this.headers["sec-ch-ua-platform"] = platform; 95 | this.headers["User-Agent"] = this.#get_user_agent(); 96 | } 97 | 98 | loadProxies() { 99 | try { 100 | const proxyFile = path.join(__dirname, "proxy.txt"); 101 | if (fs.existsSync(proxyFile)) { 102 | this.proxyList = fs.readFileSync(proxyFile, "utf8").replace(/\r/g, "").split("\n").filter(Boolean); 103 | } 104 | } catch (error) { 105 | this.log("Error loading proxies: " + error.message, "error"); 106 | } 107 | } 108 | 109 | async checkProxyIP(proxy) { 110 | try { 111 | const proxyAgent = new HttpsProxyAgent(proxy); 112 | const response = await axios.get("https://api.ipify.org?format=json", { 113 | httpsAgent: proxyAgent, 114 | timeout: 10000, 115 | }); 116 | if (response.status === 200) { 117 | return response.data.ip; 118 | } else { 119 | throw new Error(`Unable to check proxy IP. Status code: ${response.status}`); 120 | } 121 | } catch (error) { 122 | throw new Error(`Error when checking proxy IP: ${error.message}`); 123 | } 124 | } 125 | 126 | getAxiosConfig(index) { 127 | if (this.proxyList.length > 0 && index < this.proxyList.length) { 128 | return { 129 | httpsAgent: new HttpsProxyAgent(this.proxyList[index]), 130 | timeout: 30000, 131 | }; 132 | } 133 | return { timeout: 30000 }; 134 | } 135 | 136 | async createApiHash(timestamp, data) { 137 | const combinedData = `${timestamp}_${data}`; 138 | const encodedData = encodeURIComponent(combinedData); 139 | return crypto.createHash("md5").update(encodedData).digest("hex"); 140 | } 141 | 142 | async login(initData, accountIndex) { 143 | if (!initData) { 144 | return { success: false, error: "initData is required" }; 145 | } 146 | 147 | try { 148 | const hash = initData.split("hash=")[1]?.split("&")[0]; 149 | if (!hash) { 150 | throw new Error("Could not extract hash from initData"); 151 | } 152 | 153 | const currentTime = Math.floor(Date.now() / 1000); 154 | const userData = JSON.parse(decodeURIComponent(initData.split("user=")[1].split("&")[0])); 155 | const startParam = initData.split("start_param=")[1]?.split("&")[0] || ""; 156 | const chatInstance = initData.split("chat_instance=")[1]?.split("&")[0] || ""; 157 | 158 | const payload = { 159 | data: { 160 | initData: initData, 161 | startParam: startParam, 162 | photoUrl: userData.photo_url || "", 163 | platform: "android", 164 | chatId: "", 165 | chatType: "channel", 166 | chatInstance: chatInstance, 167 | }, 168 | }; 169 | 170 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 171 | const headers = { 172 | ...this.headers, 173 | "api-hash": apiHash, 174 | "Api-Key": hash, 175 | "Api-Time": currentTime, 176 | }; 177 | 178 | const response = await axios.post(`${this.baseURL}/telegram/auth`, payload, { 179 | headers, 180 | ...this.getAxiosConfig(accountIndex), 181 | }); 182 | 183 | if (response.status === 200 && response.data.success) { 184 | return { success: true, data: response.data.data }; 185 | } else { 186 | return { success: false, error: response.data.message }; 187 | } 188 | } catch (error) { 189 | return { success: false, error: error.message }; 190 | } 191 | } 192 | 193 | async finishOnboarding(initData, accountIndex) { 194 | try { 195 | const hash = initData.split("hash=")[1]?.split("&")[0]; 196 | if (!hash) { 197 | throw new Error("Could not extract hash from initData"); 198 | } 199 | 200 | const currentTime = Math.floor(Date.now() / 1000); 201 | const payload = { data: 1 }; 202 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 203 | 204 | const headers = { 205 | ...this.headers, 206 | "api-hash": apiHash, 207 | "Api-Key": hash, 208 | "Api-Time": currentTime, 209 | }; 210 | 211 | const response = await axios.post(`${this.baseURL}/hero/onboarding/finish`, payload, { 212 | headers, 213 | ...this.getAxiosConfig(accountIndex), 214 | }); 215 | 216 | if (response.status === 200 && response.data.success) { 217 | return { success: true, data: response.data.data }; 218 | } else { 219 | return { success: false, error: response.data.message }; 220 | } 221 | } catch (error) { 222 | return { success: false, error: error.message }; 223 | } 224 | } 225 | 226 | async getUserData(initData, accountIndex) { 227 | if (!initData) { 228 | return { success: false, error: "initData is required" }; 229 | } 230 | 231 | try { 232 | const hash = initData.split("hash=")[1]?.split("&")[0]; 233 | if (!hash) { 234 | throw new Error("Could not extract hash from initData"); 235 | } 236 | 237 | const currentTime = Math.floor(Date.now() / 1000); 238 | const dataPayload = JSON.stringify({ data: {} }); 239 | const apiHash = await this.createApiHash(currentTime, dataPayload); 240 | 241 | const headers = { 242 | ...this.headers, 243 | "api-hash": apiHash, 244 | "Api-Key": hash, 245 | "Api-Time": currentTime, 246 | }; 247 | 248 | const response = await axios.post( 249 | `${this.baseURL}/user/data/all`, 250 | { data: {} }, 251 | { 252 | headers, 253 | ...this.getAxiosConfig(accountIndex), 254 | } 255 | ); 256 | 257 | if (response.status === 200 && response.data.success) { 258 | this.cachedData = response.data.data; 259 | return { success: true, data: response.data.data }; 260 | } else { 261 | return { success: false, error: response.data.message }; 262 | } 263 | } catch (error) { 264 | return { success: false, error: error.message }; 265 | } 266 | } 267 | 268 | async getUserDataAfter(initData, accountIndex) { 269 | try { 270 | const hash = initData.split("hash=")[1]?.split("&")[0]; 271 | if (!hash) { 272 | throw new Error("Could not extract hash from initData"); 273 | } 274 | 275 | const currentTime = Math.floor(Date.now() / 1000); 276 | const dataPayload = JSON.stringify({ data: {} }); 277 | const apiHash = await this.createApiHash(currentTime, dataPayload); 278 | 279 | const headers = { 280 | ...this.headers, 281 | "api-hash": apiHash, 282 | "Api-Key": hash, 283 | "Api-Time": currentTime, 284 | }; 285 | 286 | const response = await axios.post( 287 | `${this.baseURL}/user/data/after`, 288 | { data: {} }, 289 | { 290 | headers, 291 | ...this.getAxiosConfig(accountIndex), 292 | } 293 | ); 294 | 295 | if (response.status === 200 && response.data.success) { 296 | return { success: true, data: response.data.data }; 297 | } else { 298 | return { success: false, error: response.data.message }; 299 | } 300 | } catch (error) { 301 | return { success: false, error: error.message }; 302 | } 303 | } 304 | 305 | async claimDailyReward(initData, rewardIndex, accountIndex) { 306 | try { 307 | const hash = initData.split("hash=")[1]?.split("&")[0]; 308 | if (!hash) { 309 | throw new Error("Could not extract hash from initData"); 310 | } 311 | 312 | const currentTime = Math.floor(Date.now() / 1000); 313 | const payload = { data: rewardIndex }; 314 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 315 | 316 | const headers = { 317 | ...this.headers, 318 | "api-hash": apiHash, 319 | "Api-Key": hash, 320 | "Api-Time": currentTime, 321 | }; 322 | 323 | const response = await axios.post(`${this.baseURL}/quests/daily/claim`, payload, { 324 | headers, 325 | ...this.getAxiosConfig(accountIndex), 326 | }); 327 | 328 | if (response.status === 200 && response.data.success) { 329 | return { success: true, data: response.data.data }; 330 | } else { 331 | return { success: false, error: response.data.message }; 332 | } 333 | } catch (error) { 334 | return { success: false, error: error.message }; 335 | } 336 | } 337 | 338 | async AnswerDaily(hash, accountIndex, questKey, checkData) { 339 | const url = `${this.baseURL}/quests/check`; 340 | const currentTime = Math.floor(Date.now() / 1000); 341 | const payload = { data: [questKey, checkData] }; 342 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 343 | 344 | const headers = { 345 | ...this.headers, 346 | "api-hash": `${apiHash}`, 347 | "Api-Key": `${hash}`, 348 | "Api-Time": `${currentTime}`, 349 | }; 350 | 351 | try { 352 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 353 | if (response.status === 200 && response.data.success) { 354 | return await this.claimQuest(hash, accountIndex, questKey, checkData); 355 | } else { 356 | this.log(`Task test "${questKey}" failed: ${response.data.error}`, "warning"); 357 | return { success: false, error: response.data.error }; 358 | } 359 | } catch (error) { 360 | this.log(`Error checking task "${questKey}": ${error.message}`, "error"); 361 | return { success: false, error: error.message }; 362 | } 363 | } 364 | 365 | async getAliance(hash, accountIndex) { 366 | const url = `${this.baseURL}/alliance/rating`; 367 | const currentTime = Math.floor(Date.now() / 1000); 368 | const payload = {}; 369 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 370 | 371 | const headers = { 372 | ...this.headers, 373 | "api-hash": `${apiHash}`, 374 | "Api-Key": `${hash}`, 375 | "Api-Time": `${currentTime}`, 376 | }; 377 | 378 | try { 379 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 380 | if (response.status === 200 && response.data.success) { 381 | return response.data; 382 | } 383 | } catch (error) { 384 | this.log(`Error when getting Aliancee: ${error.message}`, "error"); 385 | return { success: false, error: error.message }; 386 | } 387 | } 388 | 389 | async joinAliance(hash, accountIndex, id) { 390 | const url = `${this.baseURL}/alliance/join`; 391 | const currentTime = Math.floor(Date.now() / 1000); 392 | const payload = { data: id }; 393 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 394 | 395 | const headers = { 396 | ...this.headers, 397 | "api-hash": `${apiHash}`, 398 | "Api-Key": `${hash}`, 399 | "Api-Time": `${currentTime}`, 400 | }; 401 | 402 | try { 403 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 404 | if (response.status === 200 && response.data.success) { 405 | // const { alliance } = response.data; 406 | this.log(`Join aliance success!`, "success"); 407 | return response.data; 408 | } 409 | } catch (error) { 410 | this.log(`Lỗi khi join Aliance: ${error.message}`, "error"); 411 | return { success: false, error: error.message }; 412 | } 413 | } 414 | 415 | async handleAliance(initData, accountIndex) { 416 | this.log(`Checking aliance avaliable...`); 417 | try { 418 | const hash = initData.split("hash=")[1]?.split("&")[0]; 419 | if (!hash) { 420 | return; 421 | } 422 | 423 | const userDataResult = await this.getUserData(initData, accountIndex); 424 | if (!userDataResult.success) { 425 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 426 | } 427 | const { hero, dbData } = userDataResult.data; 428 | 429 | const result = await this.getAliance(hash, accountIndex); 430 | if (!result.success) return; 431 | const alliances = result.data 432 | .map((item) => { 433 | // Tìm bonus dựa trên exp 434 | const matchingLevel = dbData.dbAlliance.reverse().find((item2) => item.exp >= item2.exp); 435 | 436 | // Nếu tìm thấy, thêm bonus vào đối tượng 437 | return { 438 | ...item, 439 | bonus: matchingLevel ? matchingLevel.bonus : 0, // Nếu không tìm thấy, gán bonus là 0 440 | }; 441 | }) 442 | .sort((a, b) => b.bonus - a.bonus); 443 | const alliance = findOptimalElement(alliances, hero.coins); 444 | if (!alliance) return this.log(`No alliance available to join!`, "warning"); 445 | await this.joinAliance(hash, accountIndex, alliance.id); 446 | } catch (error) {} 447 | } 448 | 449 | async setQuiz(hash, accountIndex, questKey, result) { 450 | const url = `${this.baseURL}/quiz/result/set`; 451 | const currentTime = Math.floor(Date.now() / 1000); 452 | const payload = { 453 | data: { 454 | key: questKey, 455 | result: result, 456 | }, 457 | }; 458 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 459 | 460 | const headers = { 461 | ...this.headers, 462 | "api-hash": `${apiHash}`, 463 | "Api-Key": `${hash}`, 464 | "Api-Time": `${currentTime}`, 465 | }; 466 | 467 | try { 468 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 469 | if (response.status === 200 && response.data.success) { 470 | return response.data; 471 | } 472 | } catch (error) { 473 | this.log(`Error when checking quiz "${questKey}": ${error.message}`, "error"); 474 | return { success: false, error: error.message }; 475 | } 476 | } 477 | 478 | async claimQuiz(hash, accountIndex, questKey) { 479 | const payload = { data: { key: questKey } }; 480 | const currentTime = Math.floor(Date.now() / 1000); 481 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 482 | const url = `${this.baseURL}/quiz/claim`; 483 | const headers = { 484 | ...this.headers, 485 | "api-hash": `${apiHash}`, 486 | "Api-Key": `${hash}`, 487 | "Api-Time": `${currentTime}`, 488 | }; 489 | 490 | try { 491 | const response = await axios.post(url, payload, { 492 | headers, 493 | ...this.getAxiosConfig(accountIndex), 494 | }); 495 | if (response.status === 200 && response.data.success) { 496 | return { success: true, data: response.data }; 497 | } else { 498 | return { success: false, error: response.data.error }; 499 | } 500 | } catch (error) { 501 | this.log(`Error when claiming quiz"${questKey}": ${error.message}`, "error"); 502 | return { success: false, error: error.message }; 503 | } 504 | } 505 | 506 | async claimQuest(hash, accountIndex, questKey, checkData = null) { 507 | const payload = { data: [questKey, checkData] }; 508 | const currentTime = Math.floor(Date.now() / 1000); 509 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 510 | const url = `${this.baseURL}/quests/claim`; 511 | const headers = { 512 | ...this.headers, 513 | "api-hash": `${apiHash}`, 514 | "Api-Key": `${hash}`, 515 | "Api-Time": `${currentTime}`, 516 | }; 517 | 518 | try { 519 | const response = await axios.post(url, payload, { 520 | headers, 521 | ...this.getAxiosConfig(accountIndex), 522 | }); 523 | if (response.status === 200 && response.data.success) { 524 | this.log(`Claim the mission "${questKey}" successfully and receive a reward.`, "success"); 525 | return { success: true, data: response.data }; 526 | } else { 527 | return { success: false, error: response.data.error }; 528 | } 529 | } catch (error) { 530 | this.log(`Error when claiming quest "${questKey}": ${error.message}`, "error"); 531 | return { success: false, error: error.message }; 532 | } 533 | } 534 | 535 | async completeAllQuests(initData, accountIndex) { 536 | try { 537 | const hash = initData.split("hash=")[1]?.split("&")[0]; 538 | if (!hash) { 539 | throw new Error("Could not extract hash from initData"); 540 | } 541 | const userDataResult = await this.getUserData(initData, accountIndex); 542 | if (!userDataResult.success) { 543 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 544 | } 545 | const { dbData } = userDataResult.data; 546 | const quests = dbData.dbQuests.filter((q) => !settings.SKIP_TASKS.includes(q.key) && (q.actionTo == "" || !q.actionTo)); 547 | 548 | for (const quest of quests) { 549 | if (quest.checkType === "donate_ton" || quest.checkType === "invite" || quest.checkType === "username" || quest.checkType === "ton_wallet_transaction") { 550 | continue; 551 | } 552 | if (quest.checkType === "checkCode") { 553 | await this.AnswerDaily(hash, accountIndex, quest.key, quest.checkData); 554 | continue; 555 | } 556 | const claimResult = await this.claimQuest(hash, accountIndex, quest.key); 557 | if (claimResult.success === true) { 558 | this.log(`Complete quest ${quest.key} | "${quest.title}", get ${quest.reward} reward.`, "success"); 559 | } else if (claimResult.error === "already rewarded") { 560 | this.log(`Quest ${quest.key} "${quest.title}" was previously completed.`, "warning"); 561 | } else { 562 | this.log(`The task could not be completed or needs to be done manually${quest.key} | "${quest.title}": ${claimResult.error}`, "warning"); 563 | } 564 | await new Promise((resolve) => setTimeout(resolve, 1000)); 565 | } 566 | 567 | const quizs = dbData.dbQuizzes; 568 | if (quizs.length <= 0) return; 569 | let res = await this.setQuiz(hash, accountIndex, quizs[0].key, quizs[0].answers[0].key); 570 | if (!res.success) return; 571 | const { quizzes } = res.data; 572 | const quizsAvaliable = quizs.filter((item1) => { 573 | const found = quizzes.find((element) => element.key === item1.key); 574 | // Nếu không tìm thấy trong array2 hoặc found.isReward là false thì giữ lại 575 | return !found || !found.isRewarded; 576 | }); 577 | 578 | for (const quiz of quizsAvaliable) { 579 | this.log(`Start quiz ${quiz.title}...`, "info"); 580 | const result = quiz.answers[0].key; 581 | res = await this.setQuiz(hash, accountIndex, quiz.key, result); 582 | if (!res.success) continue; 583 | // const { quizzes } = res.data; 584 | // if(quizzes) 585 | // const checkIsReward = quizzes.find((q) => q.key === quiz.key); 586 | // if (checkIsReward && checkIsReward?.isRewarded) continue; 587 | 588 | const claimResult = await this.claimQuiz(hash, accountIndex, quiz.key); 589 | if (claimResult.success === true) { 590 | this.log(`Complete quiz ${quiz.key} | "${quiz.title}", get ${quiz.reward} reward.`, "success"); 591 | } else if (claimResult.error === "already rewarded") { 592 | this.log(`quiz ${quiz.key} "${quiz.title}" was completed previously.`, "warning"); 593 | } else { 594 | this.log(`Cannot complete or need to do it manually quiz ${quiz.key} | "${quiz.title}": ${claimResult.error}`, "warning"); 595 | } 596 | await new Promise((resolve) => setTimeout(resolve, 1000)); 597 | } 598 | } catch (error) { 599 | this.log(`Error getting quiz list: ${error.message}`, "error"); 600 | } 601 | } 602 | 603 | async handleAutoFeed(initData, accountIndex) { 604 | try { 605 | const hash = initData.split("hash=")[1]?.split("&")[0]; 606 | if (!hash) { 607 | throw new Error("Could not extract hash from initData"); 608 | } 609 | 610 | const userDataResult = await this.getUserData(initData, accountIndex); 611 | if (!userDataResult.success) { 612 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 613 | } 614 | 615 | const { hero, feed } = userDataResult.data; 616 | 617 | if (feed.isNeedFeed) { 618 | if (!hero.onboarding.includes("20")) { 619 | const currentTime = Math.floor(Date.now() / 1000); 620 | const payload = { data: 20 }; 621 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 622 | 623 | const headers = { 624 | ...this.headers, 625 | "api-hash": apiHash, 626 | "Api-Key": hash, 627 | "Api-Time": currentTime, 628 | }; 629 | 630 | const onboardingResponse = await axios.post(`${this.baseURL}/hero/onboarding/finish`, payload, { 631 | headers, 632 | ...this.getAxiosConfig(accountIndex), 633 | }); 634 | 635 | if (!onboardingResponse.data.success) { 636 | throw new Error("Failed to complete onboarding step 20"); 637 | } 638 | } 639 | 640 | const currentTime = Math.floor(Date.now() / 1000); 641 | const feedPayload = { data: "instant" }; 642 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(feedPayload)); 643 | 644 | const headers = { 645 | ...this.headers, 646 | "api-hash": apiHash, 647 | "Api-Key": hash, 648 | "Api-Time": currentTime, 649 | }; 650 | 651 | const feedResponse = await axios.post(`${this.baseURL}/autofeed/buy`, feedPayload, { 652 | headers, 653 | ...this.getAxiosConfig(accountIndex), 654 | }); 655 | 656 | if (feedResponse.data.success) { 657 | this.log("Feed the animals successfully", "success"); 658 | return { success: true, data: feedResponse.data }; 659 | } 660 | } 661 | 662 | return { success: true }; 663 | } catch (error) { 664 | return { success: false, error: error.message }; 665 | } 666 | } 667 | 668 | async buyOrUpgradeAnimals(initData, accountIndex) { 669 | try { 670 | const hash = initData.split("hash=")[1]?.split("&")[0]; 671 | if (!hash) { 672 | throw new Error("Could not extract hash from initData"); 673 | } 674 | 675 | const userDataResult = await this.getUserData(initData, accountIndex); 676 | if (!userDataResult.success) { 677 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 678 | } 679 | 680 | const { animals, hero, dbData } = userDataResult.data; 681 | const existingKeys = new Set(animals.map((animal) => animal.key)); 682 | const usedPositions = new Set(animals.map((animal) => animal.position)); 683 | 684 | if (settings.AUTO_BUY_ANIMAL) { 685 | for (const dbAnimal of dbData.dbAnimals) { 686 | if (!existingKeys.has(dbAnimal.key)) { 687 | const level1Price = dbAnimal.levels[0].price; 688 | 689 | if (hero.coins >= level1Price) { 690 | let position = 1; 691 | while (usedPositions.has(position)) { 692 | position++; 693 | } 694 | 695 | const currentTime = Math.floor(Date.now() / 1000); 696 | const payload = { data: { position, animalKey: dbAnimal.key } }; 697 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 698 | 699 | const headers = { 700 | ...this.headers, 701 | "api-hash": apiHash, 702 | "Api-Key": hash, 703 | "Api-Time": currentTime, 704 | }; 705 | 706 | const response = await axios.post(`${this.baseURL}/animal/buy`, payload, { 707 | headers, 708 | ...this.getAxiosConfig(accountIndex), 709 | }); 710 | 711 | if (response.status === 200 && response.data.success) { 712 | this.log(`Buy successfully ${dbAnimal.title}`, "success"); 713 | usedPositions.add(position); 714 | existingKeys.add(dbAnimal.key); 715 | } 716 | } 717 | } 718 | } 719 | } 720 | if (settings.AUTO_UPGRADE_ANIMAL) { 721 | for (const animal of animals) { 722 | const dbAnimal = dbData.dbAnimals.find((dba) => dba.key === animal.key); 723 | if (dbAnimal) { 724 | if (animal.level >= settings.MAX_LEVEL_UPGRADE_ANIMAL) continue; 725 | const nextLevel = animal.level + 1; 726 | const nextLevelData = dbAnimal.levels.find((l) => l.level === nextLevel); 727 | 728 | if (nextLevelData && hero.coins >= nextLevelData.price) { 729 | const currentTime = Math.floor(Date.now() / 1000); 730 | const payload = { data: { position: animal.position, animalKey: animal.key } }; 731 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 732 | 733 | const headers = { 734 | ...this.headers, 735 | "api-hash": apiHash, 736 | "Api-Key": hash, 737 | "Api-Time": currentTime, 738 | }; 739 | 740 | try { 741 | const response = await axios.post(`${this.baseURL}/animal/buy`, payload, { 742 | headers, 743 | ...this.getAxiosConfig(accountIndex), 744 | }); 745 | 746 | if (response.status === 200 && response.data.success) { 747 | this.log(`Upgrade ${dbAnimal.title} successfully to level ${nextLevel}`, "success"); 748 | } 749 | } catch (error) { 750 | if (error.response?.status === 500) { 751 | this.log(`Failed to upgrade ${dbAnimal.title}: ${error.message}`, "error"); 752 | } 753 | } 754 | } 755 | } 756 | } 757 | } 758 | 759 | return { success: true }; 760 | } catch (error) { 761 | return { success: false, error: error.message }; 762 | } 763 | } 764 | 765 | log(msg, type = "info") { 766 | const timestamp = new Date().toLocaleTimeString(); 767 | switch (type) { 768 | case "success": 769 | console.log(`[${timestamp}] [✓] ${msg}`.green); 770 | break; 771 | case "custom": 772 | console.log(`[${timestamp}] [*] ${msg}`.magenta); 773 | break; 774 | case "error": 775 | console.log(`[${timestamp}] [✗] ${msg}`.red); 776 | break; 777 | case "warning": 778 | console.log(`[${timestamp}] [!] ${msg}`.yellow); 779 | break; 780 | default: 781 | console.log(`[${timestamp}] [ℹ] ${msg}`.blue); 782 | } 783 | } 784 | 785 | calculateWaitTimeInSeconds(nextFeedTime) { 786 | const now = DateTime.local(); 787 | const feedTime = DateTime.fromFormat(nextFeedTime, "yyyy-MM-dd HH:mm:ss", { zone: "UTC" }).setZone("local"); 788 | const diffInSeconds = Math.max(0, Math.floor(feedTime.diff(now, "seconds").seconds)); 789 | return diffInSeconds; 790 | } 791 | 792 | async countdown(seconds) { 793 | const endTime = DateTime.local().plus({ seconds }); 794 | 795 | for (let i = seconds; i > 0; i--) { 796 | const currentTime = DateTime.local().toLocaleString(DateTime.TIME_WITH_SECONDS); 797 | const remainingTime = endTime.diff(DateTime.local()); 798 | const remainingMinutes = Math.floor(remainingTime.as("minutes")); 799 | const remainingSeconds = Math.floor(remainingTime.as("seconds")) % 60; 800 | 801 | readline.cursorTo(process.stdout, 0); 802 | process.stdout.write(`[${currentTime}] [*]Wait ${remainingMinutes} minutes ${remainingSeconds} seconds to continue...`); 803 | await new Promise((resolve) => setTimeout(resolve, 1000)); 804 | } 805 | readline.cursorTo(process.stdout, 0); 806 | readline.clearLine(process.stdout, 0); 807 | } 808 | 809 | async main() { 810 | this.log(`Script By (https://www.youtube.com/@D4rkCipherX)`, "warning"); 811 | try { 812 | const { endpoint: hasIDAPI, message } = await checkBaseUrl(); 813 | if (!hasIDAPI) return console.log(`Could not find API ID, try again later!`.red); 814 | console.log(`${message}`.yellow); 815 | this.baseURL = hasIDAPI; 816 | const data = loadData("data.txt"); 817 | 818 | let nextCycleWaitTime = settings.TIME_SLEEP * 60; // Default wait time in seconds (24 hours) 819 | 820 | while (true) { 821 | let firstAccountFeedTime = null; 822 | 823 | for (let i = 0; i < data.length; i++) { 824 | const initData = data[i]; 825 | try { 826 | const userData = JSON.parse(decodeURIComponent(initData.split("user=")[1].split("&")[0])); 827 | const username = userData.username || "Unknown"; 828 | this.session_name = userData.id; 829 | this.set_headers(); 830 | let proxyIP = "No proxy"; 831 | if (this.proxyList[i]) { 832 | try { 833 | proxyIP = await this.checkProxyIP(this.proxyList[i]); 834 | } catch (proxyError) { 835 | this.log(`Proxy check failed: ${proxyError.message}`, "warning"); 836 | } 837 | } 838 | 839 | console.log(`========== Account ${i + 1} | ${username.green}==========`); 840 | 841 | this.log(`Signing in...`, "info"); 842 | const loginResult = await this.login(initData, i); 843 | if (loginResult.success) { 844 | this.log("Successful login!", "success"); 845 | 846 | const userDataResult = await this.getUserData(initData, i); 847 | if (userDataResult.success) { 848 | const { hero, feed, alliance, profile } = userDataResult.data; 849 | this.log(`User: ${(profile.firstName || "") + (profile.lastName || "")} | Coins: ${hero.tokens} | Food: ${hero.coins}`); 850 | if (i === 0 && !feed.isNeedFeed && feed.nextFeedTime) { 851 | firstAccountFeedTime = feed.nextFeedTime; 852 | const localFeedTime = DateTime.fromFormat(feed.nextFeedTime, "yyyy-MM-dd HH:mm:ss", { zone: "UTC" }).setZone("local"); 853 | 854 | this.log(`Next feeding time: ${localFeedTime.toFormat("yyyy-MM-dd HH:mm:ss")}`, "info"); 855 | } 856 | 857 | if (Array.isArray(hero.onboarding) && hero.onboarding.length === 0) { 858 | this.log("Completing onboarding...", "info"); 859 | const onboardingResult = await this.finishOnboarding(initData, i); 860 | if (onboardingResult.success) { 861 | this.log("Completed onboarding successfully!", "success"); 862 | } 863 | } 864 | 865 | if (!alliance?.id || alliance?.length == 0) { 866 | await this.handleAliance(initData, i); 867 | } 868 | 869 | if (settings.AUTO_FEED) { 870 | await this.handleAutoFeed(initData, i); 871 | } 872 | 873 | if (settings.AUTO_TASK) { 874 | await this.completeAllQuests(initData, i); 875 | } 876 | 877 | if (settings.AUTO_BUY_ANIMAL || settings.AUTO_UPGRADE_ANIMAL) { 878 | await this.buyOrUpgradeAnimals(initData, i); 879 | } 880 | 881 | const dataAfterResult = await this.getUserDataAfter(initData, i); 882 | if (dataAfterResult.success) { 883 | const { dailyRewards } = dataAfterResult.data; 884 | for (let day = 1; day <= 16; day++) { 885 | if (dailyRewards[day] === "canTake") { 886 | this.log(`Receiving reward on ${day}...`, "info"); 887 | const claimResult = await this.claimDailyReward(initData, day, i); 888 | if (claimResult.success) { 889 | this.log("Daily attendance successful!", "success"); 890 | } 891 | break; 892 | } 893 | } 894 | } 895 | 896 | const finalData = await this.getUserData(initData, i); 897 | if (finalData.success) { 898 | this.log(`Coins: ${finalData.data.hero.tokens} | Food: ${finalData.data.hero.coins}`, "custom"); 899 | } 900 | } 901 | } else { 902 | this.log(`Login failed: ${loginResult.error}`, "warning"); 903 | } 904 | 905 | await new Promise((resolve) => setTimeout(resolve, 2000)); 906 | } catch (error) { 907 | this.log(`Error processing account ${i + 1}: ${error.message}`, "error"); 908 | continue; 909 | } 910 | } 911 | 912 | this.log(`Complete all accounts.`, "custom"); 913 | 914 | if (firstAccountFeedTime) { 915 | nextCycleWaitTime = this.calculateWaitTimeInSeconds(firstAccountFeedTime); 916 | const waitMinutes = Math.floor(nextCycleWaitTime / 60); 917 | const waitSeconds = nextCycleWaitTime % 60; 918 | this.log(`Wait ${waitMinutes} minutes ${waitSeconds} seconds until next feeding`, "info"); 919 | } else { 920 | this.log(`Using default timeout ${settings.TIME_SLEEP} minutes`, "info"); 921 | } 922 | 923 | await sleep(nextCycleWaitTime); 924 | } 925 | } catch (error) { 926 | this.log(`Main process error: ${error.message}`, "error"); 927 | } 928 | } 929 | } 930 | 931 | const client = new ZooAPIClient(); 932 | client.main().catch((err) => { 933 | client.log(err.message, "error"); 934 | process.exit(1); 935 | }); 936 | -------------------------------------------------------------------------------- /zoo-proxy.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const axios = require("axios"); 4 | const colors = require("colors"); 5 | const readline = require("readline"); 6 | const crypto = require("crypto"); 7 | const { DateTime } = require("luxon"); 8 | const { HttpsProxyAgent } = require("https-proxy-agent"); 9 | const user_agents = require("./config/userAgents"); 10 | const settings = require("./config/config"); 11 | const { sleep, loadData, getRandomNumber, findOptimalElement } = require("./utils"); 12 | const { checkBaseUrl } = require("./checkAPI"); 13 | const { Worker, isMainThread, parentPort, workerData } = require("worker_threads"); 14 | 15 | class ZooAPIClient { 16 | constructor(queryId, accountIndex, proxy, baseURL) { 17 | this.headers = { 18 | Accept: "*/*", 19 | "Accept-Encoding": "gzip, deflate, br", 20 | "Accept-Language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", 21 | "Content-Type": "application/json", 22 | Origin: "https://game.zoo.team", 23 | Referer: "https://game.zoo.team/", 24 | "Sec-Ch-Ua": '"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"', 25 | "Sec-Ch-Ua-Mobile": "?0", 26 | "Sec-Ch-Ua-Platform": '"Windows"', 27 | "Sec-Fetch-Dest": "empty", 28 | "Sec-Fetch-Mode": "cors", 29 | "Sec-Fetch-Site": "same-site", 30 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", 31 | "Is-Beta-Server": "null", 32 | }; 33 | this.cachedData = null; 34 | this.proxyList = []; 35 | this.loadProxies(); 36 | this.session_name = null; 37 | this.session_user_agents = this.#load_session_data(); 38 | this.baseURL = baseURL; 39 | this.queryId = queryId; 40 | this.accountIndex = accountIndex; 41 | this.proxy = proxy; 42 | this.proxyIP = null; 43 | } 44 | #load_session_data() { 45 | try { 46 | const filePath = path.join(process.cwd(), "session_user_agents.json"); 47 | const data = fs.readFileSync(filePath, "utf8"); 48 | return JSON.parse(data); 49 | } catch (error) { 50 | if (error.code === "ENOENT") { 51 | return {}; 52 | } else { 53 | throw error; 54 | } 55 | } 56 | } 57 | 58 | #get_random_user_agent() { 59 | const randomIndex = Math.floor(Math.random() * user_agents.length); 60 | return user_agents[randomIndex]; 61 | } 62 | 63 | #get_user_agent() { 64 | if (this.session_user_agents[this.session_name]) { 65 | return this.session_user_agents[this.session_name]; 66 | } 67 | 68 | this.log(`Generating user agent...`); 69 | const newUserAgent = this.#get_random_user_agent(); 70 | this.session_user_agents[this.session_name] = newUserAgent; 71 | this.#save_session_data(this.session_user_agents); 72 | return newUserAgent; 73 | } 74 | 75 | #save_session_data(session_user_agents) { 76 | const filePath = path.join(process.cwd(), "session_user_agents.json"); 77 | fs.writeFileSync(filePath, JSON.stringify(session_user_agents, null, 2)); 78 | } 79 | 80 | #get_platform(userAgent) { 81 | const platformPatterns = [ 82 | { pattern: /iPhone/i, platform: "ios" }, 83 | { pattern: /Android/i, platform: "android" }, 84 | { pattern: /iPad/i, platform: "ios" }, 85 | ]; 86 | 87 | for (const { pattern, platform } of platformPatterns) { 88 | if (pattern.test(userAgent)) { 89 | return platform; 90 | } 91 | } 92 | 93 | return "Unknown"; 94 | } 95 | 96 | set_headers() { 97 | const platform = this.#get_platform(this.#get_user_agent()); 98 | this.headers["sec-ch-ua"] = `"Not)A;Brand";v="99", "${platform} WebView";v="127", "Chromium";v="127`; 99 | this.headers["sec-ch-ua-platform"] = platform; 100 | this.headers["User-Agent"] = this.#get_user_agent(); 101 | } 102 | 103 | createUserAgent() { 104 | try { 105 | const telegramauth = this.queryId; 106 | const userData = JSON.parse(decodeURIComponent(telegramauth.split("user=")[1].split("&")[0])); 107 | this.session_name = userData.id; 108 | this.#get_user_agent(); 109 | } catch (error) { 110 | this.log(`Can't create user agent, try get new query_id: ${error.message}`, "error"); 111 | return; 112 | } 113 | } 114 | 115 | loadProxies() { 116 | try { 117 | const proxyFile = path.join(__dirname, "proxy.txt"); 118 | if (fs.existsSync(proxyFile)) { 119 | this.proxyList = fs.readFileSync(proxyFile, "utf8").replace(/\r/g, "").split("\n").filter(Boolean); 120 | } 121 | } catch (error) { 122 | this.log("Error loading proxies: " + error.message, "error"); 123 | } 124 | } 125 | 126 | async checkProxyIP(proxy) { 127 | try { 128 | const proxyAgent = new HttpsProxyAgent(proxy); 129 | const response = await axios.get("https://api.ipify.org?format=json", { 130 | httpsAgent: proxyAgent, 131 | timeout: 10000, 132 | }); 133 | if (response.status === 200) { 134 | return response.data.ip; 135 | } else { 136 | throw new Error(`Unable to check proxy IP. Status code: ${response.status}`); 137 | } 138 | } catch (error) { 139 | throw new Error(`Error when checking proxy IP: ${error.message}`); 140 | } 141 | } 142 | 143 | getAxiosConfig(index) { 144 | if (this.proxyList.length > 0 && index < this.proxyList.length) { 145 | return { 146 | httpsAgent: new HttpsProxyAgent(this.proxyList[index]), 147 | timeout: 30000, 148 | }; 149 | } 150 | return { timeout: 30000 }; 151 | } 152 | 153 | async createApiHash(timestamp, data) { 154 | const combinedData = `${timestamp}_${data}`; 155 | const encodedData = encodeURIComponent(combinedData); 156 | return crypto.createHash("md5").update(encodedData).digest("hex"); 157 | } 158 | 159 | async login(initData, accountIndex) { 160 | if (!initData) { 161 | return { success: false, error: "initData is required" }; 162 | } 163 | 164 | try { 165 | const hash = initData.split("hash=")[1]?.split("&")[0]; 166 | if (!hash) { 167 | throw new Error("Could not extract hash from initData"); 168 | } 169 | 170 | const currentTime = Math.floor(Date.now() / 1000); 171 | const userData = JSON.parse(decodeURIComponent(initData.split("user=")[1].split("&")[0])); 172 | const startParam = initData.split("start_param=")[1]?.split("&")[0] || ""; 173 | const chatInstance = initData.split("chat_instance=")[1]?.split("&")[0] || ""; 174 | 175 | const payload = { 176 | data: { 177 | initData: initData, 178 | startParam: startParam, 179 | photoUrl: userData.photo_url || "", 180 | platform: "android", 181 | chatId: "", 182 | chatType: "channel", 183 | chatInstance: chatInstance, 184 | }, 185 | }; 186 | 187 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 188 | const headers = { 189 | ...this.headers, 190 | "api-hash": apiHash, 191 | "Api-Key": hash, 192 | "Api-Time": currentTime, 193 | }; 194 | 195 | const response = await axios.post(`${this.baseURL}/telegram/auth`, payload, { 196 | headers, 197 | ...this.getAxiosConfig(accountIndex), 198 | }); 199 | 200 | if (response.status === 200 && response.data.success) { 201 | return { success: true, data: response.data.data }; 202 | } else { 203 | return { success: false, error: response.data.message }; 204 | } 205 | } catch (error) { 206 | return { success: false, error: error.message }; 207 | } 208 | } 209 | 210 | async finishOnboarding(initData, accountIndex) { 211 | try { 212 | const hash = initData.split("hash=")[1]?.split("&")[0]; 213 | if (!hash) { 214 | throw new Error("Could not extract hash from initData"); 215 | } 216 | 217 | const currentTime = Math.floor(Date.now() / 1000); 218 | const payload = { data: 1 }; 219 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 220 | 221 | const headers = { 222 | ...this.headers, 223 | "api-hash": apiHash, 224 | "Api-Key": hash, 225 | "Api-Time": currentTime, 226 | }; 227 | 228 | const response = await axios.post(`${this.baseURL}/hero/onboarding/finish`, payload, { 229 | headers, 230 | ...this.getAxiosConfig(accountIndex), 231 | }); 232 | 233 | if (response.status === 200 && response.data.success) { 234 | return { success: true, data: response.data.data }; 235 | } else { 236 | return { success: false, error: response.data.message }; 237 | } 238 | } catch (error) { 239 | return { success: false, error: error.message }; 240 | } 241 | } 242 | 243 | async getUserData(initData, accountIndex) { 244 | if (!initData) { 245 | return { success: false, error: "initData is required" }; 246 | } 247 | 248 | try { 249 | const hash = initData.split("hash=")[1]?.split("&")[0]; 250 | if (!hash) { 251 | throw new Error("Could not extract hash from initData"); 252 | } 253 | 254 | const currentTime = Math.floor(Date.now() / 1000); 255 | const dataPayload = JSON.stringify({ data: {} }); 256 | const apiHash = await this.createApiHash(currentTime, dataPayload); 257 | 258 | const headers = { 259 | ...this.headers, 260 | "api-hash": apiHash, 261 | "Api-Key": hash, 262 | "Api-Time": currentTime, 263 | }; 264 | 265 | const response = await axios.post( 266 | `${this.baseURL}/user/data/all`, 267 | { data: {} }, 268 | { 269 | headers, 270 | ...this.getAxiosConfig(accountIndex), 271 | } 272 | ); 273 | 274 | if (response.status === 200 && response.data.success) { 275 | this.cachedData = response.data.data; 276 | return { success: true, data: response.data.data }; 277 | } else { 278 | return { success: false, error: response.data.message }; 279 | } 280 | } catch (error) { 281 | return { success: false, error: error.message }; 282 | } 283 | } 284 | 285 | async getUserDataAfter(initData, accountIndex) { 286 | try { 287 | const hash = initData.split("hash=")[1]?.split("&")[0]; 288 | if (!hash) { 289 | throw new Error("Could not extract hash from initData"); 290 | } 291 | 292 | const currentTime = Math.floor(Date.now() / 1000); 293 | const dataPayload = JSON.stringify({ data: {} }); 294 | const apiHash = await this.createApiHash(currentTime, dataPayload); 295 | 296 | const headers = { 297 | ...this.headers, 298 | "api-hash": apiHash, 299 | "Api-Key": hash, 300 | "Api-Time": currentTime, 301 | }; 302 | 303 | const response = await axios.post( 304 | `${this.baseURL}/user/data/after`, 305 | { data: {} }, 306 | { 307 | headers, 308 | ...this.getAxiosConfig(accountIndex), 309 | } 310 | ); 311 | 312 | if (response.status === 200 && response.data.success) { 313 | return { success: true, data: response.data.data }; 314 | } else { 315 | return { success: false, error: response.data.message }; 316 | } 317 | } catch (error) { 318 | return { success: false, error: error.message }; 319 | } 320 | } 321 | 322 | async claimDailyReward(initData, rewardIndex, accountIndex) { 323 | try { 324 | const hash = initData.split("hash=")[1]?.split("&")[0]; 325 | if (!hash) { 326 | throw new Error("Could not extract hash from initData"); 327 | } 328 | 329 | const currentTime = Math.floor(Date.now() / 1000); 330 | const payload = { data: rewardIndex }; 331 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 332 | 333 | const headers = { 334 | ...this.headers, 335 | "api-hash": apiHash, 336 | "Api-Key": hash, 337 | "Api-Time": currentTime, 338 | }; 339 | 340 | const response = await axios.post(`${this.baseURL}/quests/daily/claim`, payload, { 341 | headers, 342 | ...this.getAxiosConfig(accountIndex), 343 | }); 344 | 345 | if (response.status === 200 && response.data.success) { 346 | return { success: true, data: response.data.data }; 347 | } else { 348 | return { success: false, error: response.data.message }; 349 | } 350 | } catch (error) { 351 | return { success: false, error: error.message }; 352 | } 353 | } 354 | 355 | async AnswerDaily(hash, accountIndex, questKey, checkData) { 356 | const url = `${this.baseURL}/quests/check`; 357 | const currentTime = Math.floor(Date.now() / 1000); 358 | const payload = { data: [questKey, checkData] }; 359 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 360 | 361 | const headers = { 362 | ...this.headers, 363 | "api-hash": `${apiHash}`, 364 | "Api-Key": `${hash}`, 365 | "Api-Time": `${currentTime}`, 366 | }; 367 | 368 | try { 369 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 370 | if (response.status === 200 && response.data.success) { 371 | return await this.claimQuest(hash, accountIndex, questKey, checkData); 372 | } else { 373 | this.log(`Quest check "${questKey}" failed: ${response.data.error}`, "warning"); 374 | return { success: false, error: response.data.error }; 375 | } 376 | } catch (error) { 377 | this.log(`Error checking quest"${questKey}": ${error.message}`, "error"); 378 | return { success: false, error: error.message }; 379 | } 380 | } 381 | 382 | async getAliance(hash, accountIndex) { 383 | const url = `${this.baseURL}/alliance/rating`; 384 | const currentTime = Math.floor(Date.now() / 1000); 385 | const payload = {}; 386 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 387 | 388 | const headers = { 389 | ...this.headers, 390 | "api-hash": `${apiHash}`, 391 | "Api-Key": `${hash}`, 392 | "Api-Time": `${currentTime}`, 393 | }; 394 | 395 | try { 396 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 397 | if (response.status === 200 && response.data.success) { 398 | return response.data; 399 | } 400 | } catch (error) { 401 | this.log(`Error when getting Alliance: ${error.message}`, "error"); 402 | return { success: false, error: error.message }; 403 | } 404 | } 405 | 406 | async joinAliance(hash, accountIndex, id) { 407 | const url = `${this.baseURL}/alliance/join`; 408 | const currentTime = Math.floor(Date.now() / 1000); 409 | const payload = { data: id }; 410 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 411 | 412 | const headers = { 413 | ...this.headers, 414 | "api-hash": `${apiHash}`, 415 | "Api-Key": `${hash}`, 416 | "Api-Time": `${currentTime}`, 417 | }; 418 | 419 | try { 420 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 421 | if (response.status === 200 && response.data.success) { 422 | // const { alliance } = response.data; 423 | this.log(`Join aliance success!`, "success"); 424 | return response.data; 425 | } 426 | } catch (error) { 427 | this.log(`Error when joining Alliance: ${error.message}`, "error"); 428 | return { success: false, error: error.message }; 429 | } 430 | } 431 | 432 | async handleAliance(initData, accountIndex) { 433 | this.log(`Checking aliance avaliable...`); 434 | try { 435 | const hash = initData.split("hash=")[1]?.split("&")[0]; 436 | if (!hash) { 437 | return; 438 | } 439 | 440 | const userDataResult = await this.getUserData(initData, accountIndex); 441 | if (!userDataResult.success) { 442 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 443 | } 444 | const { hero, dbData } = userDataResult.data; 445 | 446 | const result = await this.getAliance(hash, accountIndex); 447 | if (!result.success) return; 448 | const alliances = result.data 449 | .map((item) => { 450 | // Tìm bonus dựa trên exp 451 | const matchingLevel = dbData.dbAlliance.reverse().find((item2) => item.exp >= item2.exp); 452 | 453 | // Nếu tìm thấy, thêm bonus vào đối tượng 454 | return { 455 | ...item, 456 | bonus: matchingLevel ? matchingLevel.bonus : 0, // Nếu không tìm thấy, gán bonus là 0 457 | }; 458 | }) 459 | .sort((a, b) => b.bonus - a.bonus); 460 | const alliance = findOptimalElement(alliances, hero.coins); 461 | if (!alliance) return this.log(`No alliance available to join!`, "warning"); 462 | await this.joinAliance(hash, accountIndex, alliance.id); 463 | } catch (error) {} 464 | } 465 | 466 | async setQuiz(hash, accountIndex, questKey, result) { 467 | const url = `${this.baseURL}/quiz/result/set`; 468 | const currentTime = Math.floor(Date.now() / 1000); 469 | const payload = { 470 | data: { 471 | key: questKey, 472 | result: result, 473 | }, 474 | }; 475 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 476 | 477 | const headers = { 478 | ...this.headers, 479 | "api-hash": `${apiHash}`, 480 | "Api-Key": `${hash}`, 481 | "Api-Time": `${currentTime}`, 482 | }; 483 | 484 | try { 485 | const response = await axios.post(url, payload, { headers, ...this.getAxiosConfig(accountIndex) }); 486 | if (response.status === 200 && response.data.success) { 487 | return response.data; 488 | } 489 | } catch (error) { 490 | this.log(`Error while checking quiz "${questKey}": ${error.message}`, "error"); 491 | return { success: false, error: error.message }; 492 | } 493 | } 494 | 495 | async claimQuiz(hash, accountIndex, questKey) { 496 | const payload = { data: { key: questKey } }; 497 | const currentTime = Math.floor(Date.now() / 1000); 498 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 499 | const url = `${this.baseURL}/quiz/claim`; 500 | const headers = { 501 | ...this.headers, 502 | "api-hash": `${apiHash}`, 503 | "Api-Key": `${hash}`, 504 | "Api-Time": `${currentTime}`, 505 | }; 506 | 507 | try { 508 | const response = await axios.post(url, payload, { 509 | headers, 510 | ...this.getAxiosConfig(accountIndex), 511 | }); 512 | if (response.status === 200 && response.data.success) { 513 | return { success: true, data: response.data }; 514 | } else { 515 | return { success: false, error: response.data.error }; 516 | } 517 | } catch (error) { 518 | this.log(`Error when claiming quiz "${questKey}": ${error.message}`, "error"); 519 | return { success: false, error: error.message }; 520 | } 521 | } 522 | 523 | async claimQuest(hash, accountIndex, questKey, checkData = null) { 524 | const payload = { data: [questKey, checkData] }; 525 | const currentTime = Math.floor(Date.now() / 1000); 526 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 527 | const url = `${this.baseURL}/quests/claim`; 528 | const headers = { 529 | ...this.headers, 530 | "api-hash": `${apiHash}`, 531 | "Api-Key": `${hash}`, 532 | "Api-Time": `${currentTime}`, 533 | }; 534 | 535 | try { 536 | const response = await axios.post(url, payload, { 537 | headers, 538 | ...this.getAxiosConfig(accountIndex), 539 | }); 540 | if (response.status === 200 && response.data.success) { 541 | this.log(`Claim quest "${questKey}" successfully, receive reward.`, "success"); 542 | return { success: true, data: response.data }; 543 | } else { 544 | return { success: false, error: response.data.error }; 545 | } 546 | } catch (error) { 547 | this.log(`Error when claiming task"${questKey}": ${error.message}`, "error"); 548 | return { success: false, error: error.message }; 549 | } 550 | } 551 | 552 | async completeAllQuests(initData, accountIndex) { 553 | try { 554 | const hash = initData.split("hash=")[1]?.split("&")[0]; 555 | if (!hash) { 556 | throw new Error("Could not extract hash from initData"); 557 | } 558 | const userDataResult = await this.getUserData(initData, accountIndex); 559 | if (!userDataResult.success) { 560 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 561 | } 562 | const { dbData } = userDataResult.data; 563 | const quests = dbData.dbQuests.filter((q) => !settings.SKIP_TASKS.includes(q.key) && (q.actionTo == "" || !q.actionTo)); 564 | 565 | for (const quest of quests) { 566 | if (quest.checkType === "donate_ton" || quest.checkType === "invite" || quest.checkType === "username" || quest.checkType === "ton_wallet_transaction") { 567 | continue; 568 | } 569 | if (quest.checkType === "checkCode") { 570 | await this.AnswerDaily(hash, accountIndex, quest.key, quest.checkData); 571 | continue; 572 | } 573 | const claimResult = await this.claimQuest(hash, accountIndex, quest.key); 574 | if (claimResult.success === true) { 575 | this.log(`Complete quest ${quest.key} | "${quest.title}", receive ${quest.reward} reward.`, "success"); 576 | } else if (claimResult.error === "already rewarded") { 577 | this.log(`Quest ${quest.key} "${quest.title}" has been completed before.`, "warning"); 578 | } else { 579 | this.log(`Quest cannot be completed or needs to be done manually${quest.key} | "${quest.title}": ${claimResult.error}`, "warning"); 580 | } 581 | await new Promise((resolve) => setTimeout(resolve, 1000)); 582 | } 583 | 584 | const quizs = dbData.dbQuizzes; 585 | if (quizs.length <= 0) return; 586 | let res = await this.setQuiz(hash, accountIndex, quizs[0].key, quizs[0].answers[0].key); 587 | if (!res.success) return; 588 | const { quizzes } = res.data; 589 | const quizsAvaliable = quizs.filter((item1) => { 590 | const found = quizzes.find((element) => element.key === item1.key); 591 | // Nếu không tìm thấy trong array2 hoặc found.isReward là false thì giữ lại 592 | return !found || !found.isRewarded; 593 | }); 594 | 595 | for (const quiz of quizsAvaliable) { 596 | this.log(`Start quiz ${quiz.title}...`, "info"); 597 | const result = quiz.answers[0].key; 598 | res = await this.setQuiz(hash, accountIndex, quiz.key, result); 599 | if (!res.success) continue; 600 | // const { quizzes } = res.data; 601 | // if(quizzes) 602 | // const checkIsReward = quizzes.find((q) => q.key === quiz.key); 603 | // if (checkIsReward && checkIsReward?.isRewarded) continue; 604 | 605 | const claimResult = await this.claimQuiz(hash, accountIndex, quiz.key); 606 | if (claimResult.success === true) { 607 | this.log(`Complete quiz ${quiz.key} | "${quiz.title}", receive ${quiz.reward} reward.`, "success"); 608 | } else if (claimResult.error === "already rewarded") { 609 | this.log(`quiz ${quiz.key} "${quiz.title}" has been completed before.`, "warning"); 610 | } else { 611 | this.log(`Cannot complete or need to do it manually quiz ${quiz.key} | "${quiz.title}": ${claimResult.error}`, "warning"); 612 | } 613 | await new Promise((resolve) => setTimeout(resolve, 1000)); 614 | } 615 | } catch (error) { 616 | this.log(`Error while getting quiz list: ${error.message}`, "error"); 617 | } 618 | } 619 | 620 | async handleAutoFeed(initData, accountIndex) { 621 | try { 622 | const hash = initData.split("hash=")[1]?.split("&")[0]; 623 | if (!hash) { 624 | throw new Error("Could not extract hash from initData"); 625 | } 626 | 627 | const userDataResult = await this.getUserData(initData, accountIndex); 628 | if (!userDataResult.success) { 629 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 630 | } 631 | 632 | const { hero, feed } = userDataResult.data; 633 | 634 | if (feed.isNeedFeed) { 635 | if (!hero.onboarding.includes("20")) { 636 | const currentTime = Math.floor(Date.now() / 1000); 637 | const payload = { data: 20 }; 638 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 639 | 640 | const headers = { 641 | ...this.headers, 642 | "api-hash": apiHash, 643 | "Api-Key": hash, 644 | "Api-Time": currentTime, 645 | }; 646 | 647 | const onboardingResponse = await axios.post(`${this.baseURL}/hero/onboarding/finish`, payload, { 648 | headers, 649 | ...this.getAxiosConfig(accountIndex), 650 | }); 651 | 652 | if (!onboardingResponse.data.success) { 653 | throw new Error("Failed to complete onboarding step 20"); 654 | } 655 | } 656 | 657 | const currentTime = Math.floor(Date.now() / 1000); 658 | const feedPayload = { data: "instant" }; 659 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(feedPayload)); 660 | 661 | const headers = { 662 | ...this.headers, 663 | "api-hash": apiHash, 664 | "Api-Key": hash, 665 | "Api-Time": currentTime, 666 | }; 667 | 668 | const feedResponse = await axios.post(`${this.baseURL}/autofeed/buy`, feedPayload, { 669 | headers, 670 | ...this.getAxiosConfig(accountIndex), 671 | }); 672 | 673 | if (feedResponse.data.success) { 674 | this.log("Feeding animals successfully", "success"); 675 | return { success: true, data: feedResponse.data }; 676 | } 677 | } 678 | 679 | return { success: true }; 680 | } catch (error) { 681 | return { success: false, error: error.message }; 682 | } 683 | } 684 | 685 | async buyOrUpgradeAnimals(initData, accountIndex) { 686 | try { 687 | const hash = initData.split("hash=")[1]?.split("&")[0]; 688 | if (!hash) { 689 | throw new Error("Could not extract hash from initData"); 690 | } 691 | 692 | const userDataResult = await this.getUserData(initData, accountIndex); 693 | if (!userDataResult.success) { 694 | throw new Error(`Failed to get user data: ${userDataResult.error}`); 695 | } 696 | 697 | const { animals, hero, dbData } = userDataResult.data; 698 | const existingKeys = new Set(animals.map((animal) => animal.key)); 699 | const usedPositions = new Set(animals.map((animal) => animal.position)); 700 | 701 | if (settings.AUTO_BUY_ANIMAL) { 702 | for (const dbAnimal of dbData.dbAnimals) { 703 | if (!existingKeys.has(dbAnimal.key)) { 704 | const level1Price = dbAnimal.levels[0].price; 705 | 706 | if (hero.coins >= level1Price) { 707 | let position = 1; 708 | while (usedPositions.has(position)) { 709 | position++; 710 | } 711 | 712 | const currentTime = Math.floor(Date.now() / 1000); 713 | const payload = { data: { position, animalKey: dbAnimal.key } }; 714 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 715 | 716 | const headers = { 717 | ...this.headers, 718 | "api-hash": apiHash, 719 | "Api-Key": hash, 720 | "Api-Time": currentTime, 721 | }; 722 | 723 | const response = await axios.post(`${this.baseURL}/animal/buy`, payload, { 724 | headers, 725 | ...this.getAxiosConfig(accountIndex), 726 | }); 727 | 728 | if (response.status === 200 && response.data.success) { 729 | this.log(`Buy successfully ${dbAnimal.title}`, "success"); 730 | usedPositions.add(position); 731 | existingKeys.add(dbAnimal.key); 732 | } 733 | } 734 | } 735 | } 736 | } 737 | if (settings.AUTO_UPGRADE_ANIMAL) { 738 | for (const animal of animals) { 739 | const dbAnimal = dbData.dbAnimals.find((dba) => dba.key === animal.key); 740 | if (dbAnimal) { 741 | if (animal.level >= settings.MAX_LEVEL_UPGRADE_ANIMAL) continue; 742 | const nextLevel = animal.level + 1; 743 | const nextLevelData = dbAnimal.levels.find((l) => l.level === nextLevel); 744 | 745 | if (nextLevelData && hero.coins >= nextLevelData.price) { 746 | const currentTime = Math.floor(Date.now() / 1000); 747 | const payload = { data: { position: animal.position, animalKey: animal.key } }; 748 | const apiHash = await this.createApiHash(currentTime, JSON.stringify(payload)); 749 | 750 | const headers = { 751 | ...this.headers, 752 | "api-hash": apiHash, 753 | "Api-Key": hash, 754 | "Api-Time": currentTime, 755 | }; 756 | 757 | try { 758 | const response = await axios.post(`${this.baseURL}/animal/buy`, payload, { 759 | headers, 760 | ...this.getAxiosConfig(accountIndex), 761 | }); 762 | 763 | if (response.status === 200 && response.data.success) { 764 | this.log(`${dbAnimal.title} successfully upgraded to level ${nextLevel}`, "success"); 765 | } 766 | } catch (error) { 767 | if (error.response?.status === 500) { 768 | this.log(`Cannot upgrade ${dbAnimal.title}: ${error.message}`, "error"); 769 | } 770 | } 771 | } 772 | } 773 | } 774 | } 775 | 776 | return { success: true }; 777 | } catch (error) { 778 | return { success: false, error: error.message }; 779 | } 780 | } 781 | 782 | log(msg, type = "info") { 783 | const timestamp = new Date().toLocaleTimeString(); 784 | const accountPrefix = `[Account ${this.accountIndex + 1}]`; 785 | const ipPrefix = this.proxyIP ? `[${this.proxyIP}]` : "[Unknown IP]"; 786 | 787 | switch (type) { 788 | case "success": 789 | console.log(`[${timestamp}]${accountPrefix}${ipPrefix} [✓] ${msg}`.green); 790 | break; 791 | case "custom": 792 | console.log(`[${timestamp}]${accountPrefix}${ipPrefix} [*] ${msg}`.magenta); 793 | break; 794 | case "error": 795 | console.log(`[${timestamp}]${accountPrefix}${ipPrefix} [✗] ${msg}`.red); 796 | break; 797 | case "warning": 798 | console.log(`[${timestamp}]${accountPrefix}${ipPrefix} [!] ${msg}`.yellow); 799 | break; 800 | default: 801 | console.log(`[${timestamp}]${accountPrefix}${ipPrefix} [ℹ] ${msg}`.blue); 802 | } 803 | } 804 | 805 | calculateWaitTimeInSeconds(nextFeedTime) { 806 | const now = DateTime.local(); 807 | const feedTime = DateTime.fromFormat(nextFeedTime, "yyyy-MM-dd HH:mm:ss", { zone: "UTC" }).setZone("local"); 808 | const diffInSeconds = Math.max(0, Math.floor(feedTime.diff(now, "seconds").seconds)); 809 | return diffInSeconds; 810 | } 811 | 812 | async countdown(seconds) { 813 | const endTime = DateTime.local().plus({ seconds }); 814 | 815 | for (let i = seconds; i > 0; i--) { 816 | const currentTime = DateTime.local().toLocaleString(DateTime.TIME_WITH_SECONDS); 817 | const remainingTime = endTime.diff(DateTime.local()); 818 | const remainingMinutes = Math.floor(remainingTime.as("minutes")); 819 | const remainingSeconds = Math.floor(remainingTime.as("seconds")) % 60; 820 | 821 | readline.cursorTo(process.stdout, 0); 822 | process.stdout.write(`[${currentTime}] [*]Wait ${remainingMinutes} minutes ${remainingSeconds} seconds to continue...`); 823 | await new Promise((resolve) => setTimeout(resolve, 1000)); 824 | } 825 | readline.cursorTo(process.stdout, 0); 826 | readline.clearLine(process.stdout, 0); 827 | } 828 | 829 | async runAccount() { 830 | const i = this.accountIndex; 831 | try { 832 | const initData = this.queryId; 833 | const queryData = JSON.parse(decodeURIComponent(initData.split("user=")[1].split("&")[0])); 834 | const firstName = queryData.first_name || ""; 835 | const lastName = queryData.last_name || ""; 836 | this.session_name = queryData.id; 837 | 838 | if (this.proxy) { 839 | try { 840 | this.proxyIP = await this.checkProxyIP(this.proxy); 841 | } catch (proxyError) { 842 | this.log(`Proxy check failed: ${proxyError.message}`, "warning"); 843 | } 844 | } 845 | const timesleep = getRandomNumber(settings.DELAY_START_BOT[0], settings.DELAY_START_BOT[1]); 846 | console.log(`=========Account ${i + 1}| ${firstName + " " + lastName} | ${this.proxyIP} | Starts in ${timesleep} seconds...`.green); 847 | this.set_headers(); 848 | await sleep(timesleep); 849 | 850 | this.log(`Logging in...`, "info"); 851 | const loginResult = await this.login(initData, i); 852 | if (loginResult.success) { 853 | this.log("Login successful!", "success"); 854 | 855 | const userDataResult = await this.getUserData(initData, i); 856 | if (userDataResult.success) { 857 | const { hero, feed, alliance, profile } = userDataResult.data; 858 | this.log(`User: ${(profile.firstName || "") + (profile.lastName || "")} | Coins: ${hero.tokens} | Food: ${hero.coins}`); 859 | 860 | if (Array.isArray(hero.onboarding) && hero.onboarding.length === 0) { 861 | this.log("Completing onboarding...", "info"); 862 | const onboardingResult = await this.finishOnboarding(initData, i); 863 | if (onboardingResult.success) { 864 | this.log("Completed onboarding successfully!", "success"); 865 | } 866 | } 867 | 868 | if (!alliance?.id || alliance?.length == 0) { 869 | await this.handleAliance(initData, i); 870 | } 871 | 872 | if (settings.AUTO_FEED) { 873 | await sleep(1); 874 | await this.handleAutoFeed(initData, i); 875 | } 876 | await sleep(1); 877 | await this.completeAllQuests(initData, i); 878 | await sleep(1); 879 | 880 | if (settings.AUTO_BUY_ANIMAL || settings.AUTO_UPGRADE_ANIMAL) { 881 | await this.buyOrUpgradeAnimals(initData, i); 882 | } 883 | 884 | const dataAfterResult = await this.getUserDataAfter(initData, i); 885 | if (dataAfterResult.success) { 886 | const { dailyRewards } = dataAfterResult.data; 887 | for (let day = 1; day <= 16; day++) { 888 | if (dailyRewards[day] === "canTake") { 889 | this.log(`Claiming daily reward ${day}...`, "info"); 890 | const claimResult = await this.claimDailyReward(initData, day, i); 891 | if (claimResult.success) { 892 | this.log("Daily check-in successful!", "success"); 893 | } 894 | break; 895 | } 896 | } 897 | } 898 | 899 | const finalData = await this.getUserData(initData, i); 900 | if (finalData.success) { 901 | this.log(`Coins: ${finalData.data.hero.tokens} | Food: ${finalData.data.hero.coins}`, "custom"); 902 | } 903 | } 904 | } else { 905 | this.log(`Login failed: ${loginResult.error}`, "warning"); 906 | } 907 | 908 | await new Promise((resolve) => setTimeout(resolve, 2000)); 909 | } catch (error) { 910 | this.log(`Error processing account ${i + 1}: ${error.message}`, "error"); 911 | return; 912 | } 913 | } 914 | } 915 | 916 | async function runWorker(workerData) { 917 | const { queryId, accountIndex, proxy, hasIDAPI } = workerData; 918 | const to = new ZooAPIClient(queryId, accountIndex, proxy, hasIDAPI); 919 | try { 920 | await Promise.race([to.runAccount(), new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 24 * 60 * 60 * 1000))]); 921 | parentPort.postMessage({ 922 | accountIndex, 923 | }); 924 | } catch (error) { 925 | parentPort.postMessage({ accountIndex, error: error.message }); 926 | } finally { 927 | if (!isMainThread) { 928 | parentPort.postMessage("taskComplete"); 929 | } 930 | } 931 | } 932 | 933 | async function main() { 934 | const queryIds = loadData("data.txt"); 935 | const proxies = loadData("proxy.txt"); 936 | 937 | if (queryIds.length > proxies.length) { 938 | console.log("The number of proxies and data must be equal..".red); 939 | console.log(`Data: ${queryIds.length}`); 940 | console.log(`Proxy: ${proxies.length}`); 941 | process.exit(1); 942 | } 943 | console.log("Script by (https://t.me/D4rkCipherX)".yellow); 944 | let maxThreads = settings.MAX_THEADS; 945 | 946 | const { endpoint: hasIDAPI, message } = await checkBaseUrl(); 947 | if (!hasIDAPI) return console.log(`API ID not found, try again later!`.red); 948 | console.log(`${message}`.yellow); 949 | // process.exit(); 950 | queryIds.map((val, i) => new ZooAPIClient(val, i, proxies[i], hasIDAPI).createUserAgent()); 951 | 952 | await sleep(1); 953 | while (true) { 954 | let currentIndex = 0; 955 | const errors = []; 956 | 957 | while (currentIndex < queryIds.length) { 958 | const workerPromises = []; 959 | const batchSize = Math.min(maxThreads, queryIds.length - currentIndex); 960 | for (let i = 0; i < batchSize; i++) { 961 | const worker = new Worker(__filename, { 962 | workerData: { 963 | hasIDAPI, 964 | queryId: queryIds[currentIndex], 965 | accountIndex: currentIndex, 966 | proxy: proxies[currentIndex % proxies.length], 967 | }, 968 | }); 969 | 970 | workerPromises.push( 971 | new Promise((resolve) => { 972 | worker.on("message", (message) => { 973 | if (message === "taskComplete") { 974 | worker.terminate(); 975 | } 976 | if (settings.ENABLE_DEBUG) { 977 | console.log(message); 978 | } 979 | resolve(); 980 | }); 981 | worker.on("error", (error) => { 982 | console.log(`Worker error for account ${currentIndex}: ${error.message}`); 983 | worker.terminate(); 984 | resolve(); 985 | }); 986 | worker.on("exit", (code) => { 987 | worker.terminate(); 988 | if (code !== 0) { 989 | errors.push(`Worker for account ${currentIndex} exited with code: ${code}`); 990 | } 991 | resolve(); 992 | }); 993 | }) 994 | ); 995 | 996 | currentIndex++; 997 | } 998 | 999 | await Promise.all(workerPromises); 1000 | 1001 | if (errors.length > 0) { 1002 | errors.length = 0; 1003 | } 1004 | 1005 | if (currentIndex < queryIds.length) { 1006 | await new Promise((resolve) => setTimeout(resolve, 3000)); 1007 | } 1008 | } 1009 | await sleep(3); 1010 | console.log("Join (https://t.me/D4rkCipherX)".yellow); 1011 | console.log(`=============Complete all accounts | Wait ${settings.TIME_SLEEP} minutes=============`.magenta); 1012 | await sleep(settings.TIME_SLEEP * 60); 1013 | } 1014 | } 1015 | 1016 | if (isMainThread) { 1017 | main().catch((error) => { 1018 | console.log("It's a mistake:", error); 1019 | process.exit(1); 1020 | }); 1021 | } else { 1022 | runWorker(workerData); 1023 | } 1024 | --------------------------------------------------------------------------------