├── proxies.txt ├── package.json ├── README.md └── index.js /proxies.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voyager-craftworld-bot", 3 | "version": "1.0.0", 4 | "description": "Automated bot for Voyager CraftWorld daily tasks and chest spinning", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "dev": "node index.js", 9 | "test": "echo \"No tests specified\" && exit 0" 10 | }, 11 | "keywords": [ 12 | "voyager", 13 | "craftworld", 14 | "bot", 15 | "automation", 16 | "crypto", 17 | "airdrop", 18 | "web3", 19 | "ethereum", 20 | "siwe", 21 | "gaming" 22 | ], 23 | "author": "Airdrop Insiders", 24 | "license": "MIT", 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/vikitoshi/Voyager-CraftWorld-Bot.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/vikitoshi/Voyager-CraftWorld-Bot/issues" 31 | }, 32 | "homepage": "https://github.com/vikitoshi/Voyager-CraftWorld-Bot#readme", 33 | "engines": { 34 | "node": ">=16.0.0", 35 | "npm": ">=7.0.0" 36 | }, 37 | "dependencies": { 38 | "axios": "^1.6.7", 39 | "dotenv": "^16.4.1", 40 | "ethers": "^6.10.0", 41 | "siwe": "^2.1.4", 42 | "https-proxy-agent": "^7.0.2", 43 | "http-proxy-agent": "^7.0.0", 44 | "socks-proxy-agent": "^8.0.2" 45 | }, 46 | "devDependencies": { 47 | "nodemon": "^3.0.3" 48 | }, 49 | "optionalDependencies": {}, 50 | "peerDependencies": {}, 51 | "funding": { 52 | "type": "telegram", 53 | "url": "https://t.me/AirdropInsiderID" 54 | } 55 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Voyager CraftWorld Auto Bot 2 | 3 | This bot automatically handles daily login, quest claiming, and chest spinning for multiple wallets on the Voyager CraftWorld platform. 4 | 5 | ## 📋 Features 6 | 7 | - **Multi-wallet support** - Process multiple wallets automatically 8 | - **Daily quest automation** - Auto-claim ready quests 9 | - **Chest spinning** - Auto-spin daily free chests and coin chests 10 | - **Proxy support** - Use HTTP/HTTPS/SOCKS4/SOCKS5 proxies for each wallet 11 | - **Daily reset countdown** - Automatically waits for next daily reset 12 | - **Session management** - Handle authentication and sessions automatically 13 | 14 | ## 🎯 Getting Started 15 | 16 | ### 1. Register Your Account 17 | First, create your Voyager CraftWorld account : 18 | 👉 **[Register Here](https://voyager.preview.craft-world.gg?code=f4d376)** 19 | 20 | ### 2. Join Our Community 21 | Stay updated with the latest airdrops and updates: 22 | 🔗 **[Join Airdrop Insiders Telegram](https://t.me/AirdropInsiderID)** 23 | 24 | ## 🛠️ Installation 25 | 26 | ### Prerequisites 27 | - Node.js (v16 or higher) 28 | - npm or yarn package manager 29 | 30 | ### Clone the Repository 31 | ```bash 32 | git clone https://github.com/vikitoshi/Voyager-CraftWorld-Bot.git 33 | cd Voyager-CraftWorld-Bot 34 | ``` 35 | 36 | ### Install Dependencies 37 | ```bash 38 | npm install 39 | ``` 40 | 41 | ## ⚙️ Configuration 42 | 43 | ### 1. Environment Setup 44 | Create a `.env` file in the root directory: 45 | 46 | ```env 47 | # Add your private keys (as many as you need) 48 | PRIVATE_KEY_1=your_first_private_key_here 49 | PRIVATE_KEY_2=your_second_private_key_here 50 | PRIVATE_KEY_3=your_third_private_key_here 51 | # Add more as needed... 52 | ``` 53 | 54 | ### 2. Proxy Setup (Optional) 55 | Create a `proxies.txt` file in the root directory if you want to use proxies: 56 | 57 | ```txt 58 | # HTTP Proxies 59 | http://proxy1.example.com:8080 60 | http://username:password@proxy2.example.com:8080 61 | 62 | # HTTPS Proxies 63 | https://proxy3.example.com:8080 64 | https://username:password@proxy4.example.com:8080 65 | 66 | # SOCKS4 Proxies 67 | socks4://proxy5.example.com:1080 68 | 69 | # SOCKS5 Proxies 70 | socks5://proxy6.example.com:1080 71 | socks5://username:password@proxy7.example.com:1080 72 | ``` 73 | 74 | **Proxy Format Examples:** 75 | - `http://proxy.example.com:8080` 76 | - `https://user:pass@proxy.example.com:8080` 77 | - `socks4://proxy.example.com:1080` 78 | - `socks5://user:pass@proxy.example.com:1080` 79 | 80 | ## 🚀 Usage 81 | 82 | ### Start the Bot 83 | ```bash 84 | node index.js 85 | ``` 86 | 87 | ### What the Bot Does 88 | 1. **Authentication** - Signs in to each wallet using SIWE (Sign-In with Ethereum) 89 | 2. **Daily Login** - Completes daily login quest 90 | 3. **Quest Claiming** - Automatically claims all ready quests 91 | 4. **Chest Spinning** - Opens available daily chests: 92 | - Daily Chest (Free) 93 | - Sturdy Chest (Coin) 94 | 5. **Daily Loop** - Waits for next daily reset and repeats 95 | 96 | ## 📁 Project Structure 97 | 98 | ``` 99 | Voyager-CraftWorld-Bot/ 100 | ├── README.md 101 | ├── package.json 102 | ├── .env # Your private keys (create this) 103 | ├── proxies.txt # Your proxy list (optional) 104 | └── paste.txt # Main bot script 105 | ``` 106 | 107 | ## 🔧 Advanced Configuration 108 | 109 | ### Multiple Wallets 110 | Add as many private keys as needed in your `.env` file: 111 | ```env 112 | PRIVATE_KEY_1=0x... 113 | PRIVATE_KEY_2=0x... 114 | PRIVATE_KEY_3=0x... 115 | PRIVATE_KEY_4=0x... 116 | # etc... 117 | ``` 118 | 119 | ## ⚠️ Important Notes 120 | 121 | - **Private Key Security**: Never share your private keys. Keep your `.env` file secure. 122 | - **Rate Limiting**: The bot includes delays between requests to avoid rate limiting. 123 | - **Daily Reset**: The bot automatically waits for the next daily reset (00:00 UTC). 124 | - **Proxy Testing**: All proxies are tested before use. Only working proxies are utilized. 125 | 126 | ## 🛡️ Security Best Practices 127 | 128 | 1. **Use Dedicated Wallets**: Create separate wallets specifically for this bot. 129 | 2. **Limited Funds**: Only keep minimal funds needed for transactions. 130 | 3. **Secure Environment**: Run the bot on a secure server or local machine. 131 | 4. **Regular Updates**: Keep the bot updated with the latest version. 132 | 133 | ## 🐛 Troubleshooting 134 | 135 | ### Common Issues 136 | 137 | **"No private keys found"** 138 | - Make sure your `.env` file exists and contains `PRIVATE_KEY_1=...` format 139 | 140 | **"Proxy connection failed"** 141 | - Verify your proxy credentials and format in `proxies.txt` 142 | - The bot will work without proxies if none are available 143 | 144 | **"Authentication failed"** 145 | - Check if your private key is valid 146 | - Ensure your wallet has some ETH for gas fees 147 | 148 | **"Rate limit reached"** 149 | - The bot includes automatic delays, but you can increase them if needed 150 | - Consider using fewer wallets or longer delays 151 | 152 | ## 📈 Performance Tips 153 | 154 | - **Optimal Wallet Count**: 3-10 wallets work best for stability 155 | - **Proxy Quality**: Use high-quality, stable proxies for better success rates 156 | - **Network Stability**: Ensure stable internet connection for 24/7 operation 157 | 158 | ## 🤝 Contributing 159 | 160 | 1. Fork the repository 161 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 162 | 3. Commit your changes (`git commit -m 'Add amazing feature'`) 163 | 4. Push to the branch (`git push origin feature/amazing-feature`) 164 | 5. Open a Pull Request 165 | 166 | ## 📄 License 167 | 168 | This project is for educational purposes. Use at your own risk. 169 | 170 | ## 📞 Support 171 | 172 | - **Telegram Channel**: [@AirdropInsiderID](https://t.me/AirdropInsiderID) 173 | - **GitHub Issues**: [Create an issue](https://github.com/vikitoshi/Voyager-CraftWorld-Bot/issues) 174 | 175 | ## 🎉 Credits 176 | 177 | Developed by **Airdrop Insiders** team for the community. 178 | 179 | --- 180 | 181 | ⭐ **Don't forget to star this repository if it helped you!** 182 | 183 | 🔗 **Register Voyager**: https://voyager.preview.craft-world.gg?code=f4d376 184 | 📱 **Join Telegram**: https://t.me/AirdropInsiderID -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const axios = require('axios'); 3 | const { ethers } = require('ethers'); 4 | const { SiweMessage } = require('siwe'); 5 | const { HttpsProxyAgent } = require('https-proxy-agent'); 6 | const { HttpProxyAgent } = require('http-proxy-agent'); 7 | const { SocksProxyAgent } = require('socks-proxy-agent'); 8 | const fs = require('fs'); 9 | const path = require('path'); 10 | 11 | const colors = { 12 | reset: "\x1b[0m", 13 | cyan: "\x1b[36m", 14 | green: "\x1b[32m", 15 | yellow: "\x1b[33m", 16 | red: "\x1b[31m", 17 | white: "\x1b[37m", 18 | bold: "\x1b[1m", 19 | blue: "\x1b[34m", 20 | magenta: "\x1b[35m" 21 | }; 22 | 23 | const logger = { 24 | info: (msg) => console.log(`${colors.green}[✓] ${msg}${colors.reset}`), 25 | warn: (msg) => console.log(`${colors.yellow}[⚠] ${msg}${colors.reset}`), 26 | error: (msg) => console.log(`${colors.red}[✗] ${msg}${colors.reset}`), 27 | success: (msg) => console.log(`${colors.green}[✅] ${msg}${colors.reset}`), 28 | loading: (msg) => console.log(`${colors.cyan}[⟳] ${msg}${colors.reset}`), 29 | step: (msg) => console.log(`${colors.white}[➤] ${msg}${colors.reset}`), 30 | countdown: (msg) => process.stdout.write(`\r${colors.blue}[⏰] ${msg}${colors.reset}`), 31 | banner: () => { 32 | console.log(`${colors.cyan}${colors.bold}`); 33 | console.log(`---------------------------------------------`); 34 | console.log(` Voyager CW Auto Bot - Airdrop Insiders `); 35 | console.log(`---------------------------------------------${colors.reset}`); 36 | console.log(); 37 | } 38 | }; 39 | 40 | const userAgents = [ 41 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", 42 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", 43 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", 44 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", 45 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", 46 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", 47 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", 48 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", 49 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", 50 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0", 51 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0", 52 | "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0", 53 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0", 54 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15", 55 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 OPR/106.0.0.0" 56 | ]; 57 | 58 | function getRandomUserAgent() { 59 | return userAgents[Math.floor(Math.random() * userAgents.length)]; 60 | } 61 | 62 | function loadProxies() { 63 | try { 64 | const proxiesPath = path.join(__dirname, 'proxies.txt'); 65 | if (!fs.existsSync(proxiesPath)) { 66 | logger.warn("proxies.txt not found. Running without proxy support."); 67 | return []; 68 | } 69 | 70 | const proxiesData = fs.readFileSync(proxiesPath, 'utf8'); 71 | const proxies = proxiesData 72 | .split('\n') 73 | .map(line => line.trim()) 74 | .filter(line => line && !line.startsWith('#')) 75 | .map(proxy => parseProxy(proxy)) 76 | .filter(proxy => proxy !== null); 77 | 78 | logger.info(`Loaded ${proxies.length} proxies from proxies.txt`); 79 | return proxies; 80 | } catch (error) { 81 | logger.error(`Error loading proxies: ${error.message}`); 82 | return []; 83 | } 84 | } 85 | 86 | function parseProxy(proxyString) { 87 | try { 88 | 89 | let cleanProxy = proxyString.replace(/^(https?|socks[45]?):\/\//, ''); 90 | 91 | let auth = null; 92 | let hostPort = cleanProxy; 93 | 94 | if (cleanProxy.includes('@')) { 95 | const [authPart, hostPortPart] = cleanProxy.split('@'); 96 | auth = authPart; 97 | hostPort = hostPortPart; 98 | } 99 | 100 | const [host, port] = hostPort.split(':'); 101 | 102 | if (!host || !port) { 103 | logger.warn(`Invalid proxy format: ${proxyString}`); 104 | return null; 105 | } 106 | 107 | let type = 'http'; 108 | if (proxyString.toLowerCase().includes('socks5')) { 109 | type = 'socks5'; 110 | } else if (proxyString.toLowerCase().includes('socks4')) { 111 | type = 'socks4'; 112 | } else if (proxyString.toLowerCase().includes('https')) { 113 | type = 'https'; 114 | } 115 | 116 | return { 117 | type, 118 | host, 119 | port: parseInt(port), 120 | auth, 121 | original: proxyString 122 | }; 123 | } catch (error) { 124 | logger.warn(`Failed to parse proxy: ${proxyString} - ${error.message}`); 125 | return null; 126 | } 127 | } 128 | 129 | function createProxyAgent(proxy) { 130 | try { 131 | let proxyUrl; 132 | 133 | if (proxy.auth) { 134 | proxyUrl = `${proxy.type}://${proxy.auth}@${proxy.host}:${proxy.port}`; 135 | } else { 136 | proxyUrl = `${proxy.type}://${proxy.host}:${proxy.port}`; 137 | } 138 | 139 | switch (proxy.type) { 140 | case 'socks4': 141 | case 'socks5': 142 | return new SocksProxyAgent(proxyUrl); 143 | case 'https': 144 | return new HttpsProxyAgent(proxyUrl); 145 | case 'http': 146 | default: 147 | return new HttpProxyAgent(proxyUrl); 148 | } 149 | } catch (error) { 150 | logger.error(`Failed to create proxy agent for ${proxy.original}: ${error.message}`); 151 | return null; 152 | } 153 | } 154 | 155 | async function testProxy(proxy, timeout = 10000) { 156 | try { 157 | const agent = createProxyAgent(proxy); 158 | if (!agent) return false; 159 | 160 | const response = await axios.get('https://httpbin.org/ip', { 161 | httpsAgent: agent, 162 | httpAgent: agent, 163 | timeout, 164 | headers: { 165 | 'User-Agent': getRandomUserAgent() 166 | } 167 | }); 168 | 169 | logger.success(`Proxy ${proxy.host}:${proxy.port} is working - IP: ${response.data.origin}`); 170 | return true; 171 | } catch (error) { 172 | logger.error(`Proxy ${proxy.host}:${proxy.port} failed: ${error.message}`); 173 | return false; 174 | } 175 | } 176 | 177 | async function getWorkingProxy(proxies) { 178 | if (proxies.length === 0) { 179 | return null; 180 | } 181 | 182 | const shuffledProxies = [...proxies].sort(() => Math.random() - 0.5); 183 | 184 | for (const proxy of shuffledProxies) { 185 | logger.loading(`Testing proxy ${proxy.host}:${proxy.port}...`); 186 | if (await testProxy(proxy)) { 187 | return proxy; 188 | } 189 | } 190 | 191 | logger.warn("No working proxy found, continuing without proxy"); 192 | return null; 193 | } 194 | 195 | function createAxiosConfig(proxy = null, additionalHeaders = {}) { 196 | const userAgent = getRandomUserAgent(); 197 | const config = { 198 | headers: { 199 | 'User-Agent': userAgent, 200 | ...additionalHeaders 201 | }, 202 | timeout: 30000 203 | }; 204 | 205 | if (proxy) { 206 | const agent = createProxyAgent(proxy); 207 | if (agent) { 208 | config.httpsAgent = agent; 209 | config.httpAgent = agent; 210 | } 211 | } 212 | 213 | return config; 214 | } 215 | 216 | const baseHeaders = { 217 | accept: "*/*", 218 | "accept-language": "en-US,en;q=0.9", 219 | "content-type": "application/json", 220 | "priority": "u=1, i", 221 | "sec-ch-ua-mobile": "?0", 222 | "sec-ch-ua-platform": "\"Windows\"", 223 | "sec-fetch-dest": "empty", 224 | "sec-fetch-mode": "cors", 225 | "sec-fetch-site": "same-origin", 226 | "Referer": "https://voyager.preview.craft-world.gg/missions/social", 227 | "Referrer-Policy": "strict-origin-when-cross-origin" 228 | }; 229 | 230 | const appCheckToken = "eyJraWQiOiJrTFRMakEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxOjU0MzEyMzE3NDQyOndlYjo1ODVmNjIzNTRkYjUzYzE0MmJlZDFiIiwiYXVkIjpbInByb2plY3RzXC81NDMxMjMxNzQ0MiIsInByb2plY3RzXC9nYW1lLXByZXZpZXctNTFjMmEiXSwicHJvdmlkZXIiOiJyZWNhcHRjaGFfdjMiLCJpc3MiOiJodHRwczpcL1wvZmlyZWJhc2VhcHBjaGVjay5nb29nbGVhcGlzLmNvbVwvNTQzMTIzMTc0NDIiLCJleHAiOjE3NTAwNTIyNDQsImlhdCI6MTc0OTk2NTg0NCwianRpIjoiUmFETTZUdUNqR2tLSEhFU05pa0xzLVJrdUxnRGJTbU1XeHF4N2xYZ0dNayJ9.AOfqTMnxe4v6Ze_yzB4MbZ1vatoRSMY0N3QoHDc4NV6fJVom9Re-XLbR7KA7njZicRtZu9sWUTTnAXlIePnkgP3SQXtx-28c9ze2O2waJMvUPqAeH4PSSck7KhD3YyggwfLzZgTlj2d7NdImGLdOhVZdVmWq-HSAUV95nLFvgSzEbi-SBO0PJqUWrTsq1_CSMnJtNQfKQ1g4_2jrhHvvupNpQFIg20z1-vm9u_Kal8LaZHrJdqkONRvk4SVPjkIdPzxng4vZ14PooF82SVsVq4WRJDPawzdPqDlSiMadJYKqwlNu-2JZL4jNPWJUtZEbQ8OD4-mgYsdAPUxysKSXck81RlIPuvUkaqHX5MbhSRtatjRRoJBedS-8oUfbcX-rYTesQdQ94gkz9--QuJD7U1-uR_GZxwlnfrVQT-DtbQTgBBq2Wh6TPSu8Hn7-XDefUsXFineIwSbBVSGlJbNm_TrjOJinbqNqYyOTiTtl6tfswpF3Zxrm0QzyVaL7TCAD"; 231 | 232 | function extractSessionCookie(response) { 233 | const setCookieHeader = response.headers['set-cookie']; 234 | if (setCookieHeader) { 235 | const sessionCookie = setCookieHeader.find(cookie => cookie.startsWith('session=')); 236 | if (sessionCookie) { 237 | return sessionCookie.split(';')[0]; 238 | } 239 | } 240 | return null; 241 | } 242 | 243 | async function getShopChestLimits(sessionCookie, proxy = null) { 244 | const shopChestsQuery = ` 245 | query GetShopChests { 246 | account { 247 | getShopChests { 248 | id 249 | name 250 | tier 251 | icon 252 | description 253 | acquisitionMethod 254 | dailyPurchases 255 | dailyLimit 256 | price { 257 | unit 258 | amount 259 | } 260 | reward { 261 | chance 262 | crystals 263 | equipmentId 264 | } 265 | requirement { 266 | nft { 267 | collectionIds 268 | dailyLimit { 269 | minNFTAmount 270 | chestCount 271 | } 272 | } 273 | } 274 | } 275 | } 276 | } 277 | `; 278 | 279 | const config = createAxiosConfig(proxy, { 280 | ...baseHeaders, 281 | cookie: sessionCookie, 282 | Referer: "https://voyager.preview.craft-world.gg/shop" 283 | }); 284 | 285 | const response = await axios.post("https://voyager.preview.craft-world.gg/graphql", { 286 | query: shopChestsQuery, 287 | variables: {} 288 | }, config); 289 | 290 | return response.data.data.account.getShopChests; 291 | } 292 | 293 | async function getQuestProgress(sessionCookie, proxy = null) { 294 | const questProgressQuery = ` 295 | query QuestProgress { 296 | account { 297 | questProgresses { 298 | quest { 299 | id 300 | name 301 | description 302 | reward 303 | type 304 | target 305 | starsInProgress 306 | starsComplete 307 | actionLink 308 | externalId 309 | groupId 310 | period 311 | tab 312 | data 313 | liveOpConfig { 314 | startAt 315 | duration 316 | } 317 | } 318 | status 319 | progress 320 | multiplierBonus 321 | isForeshadowed 322 | } 323 | questPoints 324 | loyaltyMultiplier 325 | rank { 326 | rankId 327 | name 328 | subRank 329 | nextRankId 330 | nextRank 331 | nextSubRank 332 | nextRankRequiredPoints 333 | divisionId 334 | nextDivisionId 335 | } 336 | profile { 337 | displayName 338 | avatarUrl 339 | } 340 | } 341 | } 342 | `; 343 | 344 | const config = createAxiosConfig(proxy, baseHeaders); 345 | config.headers.cookie = sessionCookie; 346 | 347 | const response = await axios.post("https://voyager.preview.craft-world.gg/graphql", { 348 | query: questProgressQuery, 349 | variables: {} 350 | }, config); 351 | 352 | return response.data.data.account; 353 | } 354 | 355 | async function spinChestIfAvailable(chestId, chestName, sessionCookie, chests, proxy = null) { 356 | const chest = chests.find(c => c.id === chestId); 357 | if (!chest) { 358 | logger.error(`Chest ${chestId} not found in shop data`); 359 | return 0; 360 | } 361 | 362 | const remainingSpins = chest.dailyLimit - chest.dailyPurchases; 363 | logger.info(`${chestName}: ${chest.dailyPurchases}/${chest.dailyLimit} used (${remainingSpins} remaining)`); 364 | 365 | if (remainingSpins <= 0) { 366 | logger.warn(`${chestName} daily limit reached, skipping...`); 367 | return 0; 368 | } 369 | 370 | const freeChestMutation = ` 371 | mutation BuyAndOpenChestMutation($chestId: String!, $transactionHash: String) { 372 | buyAndOpenChest(chestId: $chestId, transactionHash: $transactionHash) { 373 | crystals 374 | equipment { 375 | ownedEquipmentId 376 | slot 377 | tier 378 | name 379 | description 380 | crystalsOnDestroy 381 | definitionId 382 | baseMultiplier 383 | } 384 | } 385 | } 386 | `; 387 | 388 | let successfulSpins = 0; 389 | 390 | for (let i = 0; i < remainingSpins; i++) { 391 | try { 392 | logger.loading(`Spinning ${chestName} (${i + 1}/${remainingSpins})...`); 393 | 394 | const config = createAxiosConfig(proxy, { 395 | ...baseHeaders, 396 | cookie: sessionCookie, 397 | Referer: "https://voyager.preview.craft-world.gg/shop" 398 | }); 399 | 400 | const response = await axios.post("https://voyager.preview.craft-world.gg/graphql", { 401 | query: freeChestMutation, 402 | variables: { chestId } 403 | }, config); 404 | 405 | const chestData = response.data.data?.buyAndOpenChest; 406 | if (chestData) { 407 | if (chestData.equipment) { 408 | logger.success(`${chestName} #${i + 1}: ${chestData.equipment.name} (${chestData.equipment.tier})`); 409 | } else if (chestData.crystals) { 410 | logger.success(`${chestName} #${i + 1}: ${chestData.crystals} crystals`); 411 | } else { 412 | logger.success(`${chestName} #${i + 1}: opened successfully`); 413 | } 414 | successfulSpins++; 415 | } else { 416 | logger.error(`Failed to open ${chestName} #${i + 1}: Invalid response`); 417 | break; 418 | } 419 | 420 | if (i < remainingSpins - 1) { 421 | await new Promise(resolve => setTimeout(resolve, 1000)); 422 | } 423 | } catch (error) { 424 | logger.error(`Error opening ${chestName} #${i + 1}: ${error.message}`); 425 | if (error.response?.status === 429) { 426 | logger.warn("Rate limit reached, stopping chest spins"); 427 | break; 428 | } 429 | } 430 | } 431 | 432 | return successfulSpins; 433 | } 434 | 435 | async function claimAllReadyQuests(sessionCookie, proxy = null) { 436 | let totalClaimed = 0; 437 | let attempts = 0; 438 | const maxAttempts = 10; 439 | 440 | const claimMutation = ` 441 | mutation CompleteQuest($questId: String!) { 442 | completeQuest(questId: $questId) { 443 | success 444 | } 445 | } 446 | `; 447 | 448 | while (attempts < maxAttempts) { 449 | attempts++; 450 | logger.loading(`Checking for ready quests (attempt ${attempts})...`); 451 | 452 | const account = await getQuestProgress(sessionCookie, proxy); 453 | const readyQuests = account.questProgresses.filter(progress => progress.status === "READY_TO_CLAIM"); 454 | 455 | if (readyQuests.length === 0) { 456 | logger.info("No more quests ready to claim"); 457 | break; 458 | } 459 | 460 | logger.step(`Found ${readyQuests.length} quest(s) ready to claim`); 461 | 462 | for (const progress of readyQuests) { 463 | try { 464 | logger.loading(`Claiming quest: ${progress.quest.name}`); 465 | 466 | const config = createAxiosConfig(proxy, { 467 | ...baseHeaders, 468 | cookie: sessionCookie, 469 | Referer: "https://voyager.preview.craft-world.gg/missions/daily" 470 | }); 471 | 472 | const claimResponse = await axios.post("https://voyager.preview.craft-world.gg/graphql", { 473 | query: claimMutation, 474 | variables: { questId: progress.quest.id } 475 | }, config); 476 | 477 | if (claimResponse.data.data.completeQuest.success) { 478 | logger.success(`Quest "${progress.quest.name}" claimed successfully`); 479 | totalClaimed++; 480 | } else { 481 | logger.error(`Failed to claim quest "${progress.quest.name}"`); 482 | } 483 | 484 | await new Promise(resolve => setTimeout(resolve, 1000)); 485 | } catch (error) { 486 | logger.error(`Error claiming quest "${progress.quest.name}": ${error.message}`); 487 | } 488 | } 489 | 490 | await new Promise(resolve => setTimeout(resolve, 2000)); 491 | } 492 | 493 | return totalClaimed; 494 | } 495 | 496 | function getNextDailyReset() { 497 | const now = new Date(); 498 | const tomorrow = new Date(now); 499 | tomorrow.setUTCDate(tomorrow.getUTCDate() + 1); 500 | tomorrow.setUTCHours(0, 0, 0, 0); 501 | return tomorrow; 502 | } 503 | 504 | function formatTimeRemaining(ms) { 505 | const hours = Math.floor(ms / (1000 * 60 * 60)); 506 | const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60)); 507 | const seconds = Math.floor((ms % (1000 * 60)) / 1000); 508 | return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; 509 | } 510 | 511 | function startCountdown() { 512 | const nextReset = getNextDailyReset(); 513 | 514 | const updateCountdown = () => { 515 | const now = new Date(); 516 | const timeRemaining = nextReset.getTime() - now.getTime(); 517 | 518 | if (timeRemaining <= 0) { 519 | console.log(`\n${colors.green}[🎉] Daily reset occurred! Starting new cycle...${colors.reset}`); 520 | return true; 521 | } 522 | 523 | const timeString = formatTimeRemaining(timeRemaining); 524 | logger.countdown(`Next daily reset in: ${timeString} | Press Ctrl+C to stop`); 525 | 526 | return false; 527 | }; 528 | 529 | return new Promise((resolve) => { 530 | const interval = setInterval(() => { 531 | if (updateCountdown()) { 532 | clearInterval(interval); 533 | resolve(); 534 | } 535 | }, 1000); 536 | 537 | process.on('SIGINT', () => { 538 | clearInterval(interval); 539 | console.log(`\n${colors.yellow}[👋] Bot stopped by user${colors.reset}`); 540 | process.exit(0); 541 | }); 542 | }); 543 | } 544 | 545 | async function processWallet(privateKey, index, proxy = null) { 546 | try { 547 | const proxyInfo = proxy ? `${proxy.host}:${proxy.port}` : 'No proxy'; 548 | logger.step(`Processing wallet ${index + 1} | Proxy: ${proxyInfo}`); 549 | 550 | const wallet = new ethers.Wallet(privateKey); 551 | const address = wallet.address; 552 | logger.info(`Wallet address: ${address}`); 553 | 554 | logger.loading("Fetching authentication payload..."); 555 | const config1 = createAxiosConfig(proxy, baseHeaders); 556 | const payloadResponse = await axios.post("https://voyager.preview.craft-world.gg/auth/payload", { 557 | address, 558 | chainId: "2020" 559 | }, config1); 560 | const payload = payloadResponse.data.payload; 561 | 562 | logger.loading("Signing SIWE message..."); 563 | const siweMessage = new SiweMessage({ 564 | domain: payload.domain, 565 | address, 566 | statement: payload.statement, 567 | uri: payload.uri, 568 | version: payload.version, 569 | chainId: payload.chain_id, 570 | nonce: payload.nonce, 571 | issuedAt: payload.issued_at, 572 | expirationTime: payload.expiration_time 573 | }); 574 | const signature = await wallet.signMessage(siweMessage.toMessage()); 575 | logger.success("SIWE message signed"); 576 | 577 | logger.loading("Authenticating with signature..."); 578 | const config2 = createAxiosConfig(proxy, { ...baseHeaders, "x-firebase-appcheck": appCheckToken }); 579 | const loginResponse = await axios.post("https://voyager.preview.craft-world.gg/auth/login", { 580 | payload: { signature, payload } 581 | }, config2); 582 | const customToken = loginResponse.data.customToken; 583 | 584 | logger.loading("Signing in with custom token..."); 585 | const config3 = createAxiosConfig(proxy, { 586 | ...baseHeaders, 587 | "x-client-version": "Chrome/JsCore/11.8.0/FirebaseCore-web", 588 | "x-firebase-appcheck": appCheckToken, 589 | "x-firebase-gmpid": "1:54312317442:web:585f62354db53c142bed1b" 590 | }); 591 | config3.referrerPolicy = "no-referrer"; 592 | 593 | const signInResponse = await axios.post("https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=AIzaSyDgDDykbRrhbdfWUpm1BUgj4ga7d_-wy_g", { 594 | token: customToken, 595 | returnSecureToken: true 596 | }, config3); 597 | const idToken = signInResponse.data.idToken; 598 | logger.success("Signed in successfully"); 599 | 600 | logger.loading("Fetching user information..."); 601 | const config4 = createAxiosConfig(proxy, { 602 | ...baseHeaders, 603 | "x-client-version": "Chrome/JsCore/11.8.0/FirebaseCore-web", 604 | "x-firebase-appcheck": appCheckToken, 605 | "x-firebase-gmpid": "1:54312317442:web:585f62354db53c142bed1b" 606 | }); 607 | config4.referrerPolicy = "no-referrer"; 608 | 609 | const userInfoResponse = await axios.post("https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=AIzaSyDgDDykbRrhbdfWUpm1BUgj4ga7d_-wy_g", { 610 | idToken 611 | }, config4); 612 | const userInfo = userInfoResponse.data.users[0]; 613 | logger.info(`User ID: ${userInfo.localId}`); 614 | logger.info(`Last Login: ${new Date(parseInt(userInfo.lastLoginAt)).toISOString()}`); 615 | logger.info(`Created At: ${new Date(parseInt(userInfo.createdAt)).toISOString()}`); 616 | 617 | logger.loading("Creating session..."); 618 | const config5 = createAxiosConfig(proxy, baseHeaders); 619 | const sessionResponse = await axios.post("https://voyager.preview.craft-world.gg/api/1/session/login", { 620 | token: idToken 621 | }, config5); 622 | 623 | const sessionCookie = extractSessionCookie(sessionResponse); 624 | if (!sessionCookie) { 625 | throw new Error("Failed to extract session cookie"); 626 | } 627 | logger.success("Session created and cookie extracted"); 628 | 629 | logger.loading("Fetching account information..."); 630 | const account = await getQuestProgress(sessionCookie, proxy); 631 | logger.info(`User Profile: ${account.profile.displayName || "N/A"}`); 632 | logger.info(`Quest Points: ${account.questPoints}`); 633 | logger.info(`Loyalty Multiplier: ${account.loyaltyMultiplier}`); 634 | logger.info(`Rank: ${account.rank.name} (SubRank: ${account.rank.subRank})`); 635 | logger.info(`Next Rank: ${account.rank.nextRank} (${account.rank.nextRankRequiredPoints} points needed)`); 636 | 637 | logger.loading("Performing daily login..."); 638 | const dailyLoginMutation = ` 639 | mutation CompleteQuest($questId: String!) { 640 | completeQuest(questId: $questId) { 641 | success 642 | } 643 | } 644 | `; 645 | try { 646 | const config6 = createAxiosConfig(proxy, { 647 | ...baseHeaders, 648 | cookie: sessionCookie, 649 | Referer: "https://voyager.preview.craft-world.gg/missions/daily" 650 | }); 651 | 652 | const dailyLoginResponse = await axios.post("https://voyager.preview.craft-world.gg/graphql", { 653 | query: dailyLoginMutation, 654 | variables: { questId: "daily_login" } 655 | }, config6); 656 | 657 | if (dailyLoginResponse.data.data.completeQuest.success) { 658 | logger.success("Daily login completed"); 659 | } else { 660 | logger.warn("Daily login already completed or failed"); 661 | } 662 | } catch (error) { 663 | logger.warn(`Daily login error: ${error.message}`); 664 | } 665 | 666 | logger.step("Starting quest claiming process..."); 667 | const totalClaimedQuests = await claimAllReadyQuests(sessionCookie, proxy); 668 | logger.success(`Total quests claimed: ${totalClaimedQuests}`); 669 | 670 | logger.step("Starting chest spinning process..."); 671 | const shopChests = await getShopChestLimits(sessionCookie, proxy); 672 | 673 | const freeChestSpins = await spinChestIfAvailable("free_uncommon_chest_1", "Daily Chest (Free)", sessionCookie, shopChests, proxy); 674 | 675 | if (freeChestSpins > 0) { 676 | await new Promise(resolve => setTimeout(resolve, 2000)); 677 | } 678 | 679 | const coinChestSpins = await spinChestIfAvailable("coin_common_chest_1", "Sturdy Chest (Coin)", sessionCookie, shopChests, proxy); 680 | 681 | logger.success(`Chest spinning completed: ${freeChestSpins} free chests, ${coinChestSpins} coin chests`); 682 | 683 | return { 684 | address, 685 | questsClaimed: totalClaimedQuests, 686 | chestsOpened: freeChestSpins + coinChestSpins, 687 | proxy: proxyInfo, 688 | success: true 689 | }; 690 | 691 | } catch (error) { 692 | logger.error(`Error processing wallet ${index + 1}: ${error.message}`); 693 | return { 694 | address: "Unknown", 695 | questsClaimed: 0, 696 | chestsOpened: 0, 697 | proxy: proxy ? `${proxy.host}:${proxy.port}` : 'No proxy', 698 | success: false, 699 | error: error.message 700 | }; 701 | } 702 | } 703 | 704 | async function main() { 705 | logger.banner(); 706 | 707 | const privateKeys = Object.keys(process.env) 708 | .filter(key => key.startsWith('PRIVATE_KEY_')) 709 | .map(key => process.env[key]); 710 | 711 | if (privateKeys.length === 0) { 712 | logger.error("No private keys found in .env file"); 713 | return; 714 | } 715 | 716 | const proxies = loadProxies(); 717 | 718 | let workingProxies = []; 719 | if (proxies.length > 0) { 720 | logger.step("Testing proxy connectivity..."); 721 | for (const proxy of proxies) { 722 | if (await testProxy(proxy, 5000)) { 723 | workingProxies.push(proxy); 724 | } 725 | } 726 | logger.info(`${workingProxies.length}/${proxies.length} proxies are working`); 727 | } 728 | 729 | while (true) { 730 | const results = []; 731 | 732 | for (let i = 0; i < privateKeys.length; i++) { 733 | let selectedProxy = null; 734 | 735 | if (workingProxies.length > 0) { 736 | selectedProxy = workingProxies[i % workingProxies.length]; 737 | logger.info(`Using proxy: ${selectedProxy.host}:${selectedProxy.port} for wallet ${i + 1}`); 738 | } 739 | 740 | const result = await processWallet(privateKeys[i], i, selectedProxy); 741 | results.push(result); 742 | logger.info(`Finished processing wallet ${i + 1}`); 743 | 744 | if (i < privateKeys.length - 1) { 745 | logger.step("Waiting 5 seconds before processing next wallet..."); 746 | await new Promise(resolve => setTimeout(resolve, 5000)); 747 | } 748 | } 749 | 750 | console.log(`\n${colors.magenta}${colors.bold}--- DAILY SUMMARY ---${colors.reset}`); 751 | const successful = results.filter(r => r.success); 752 | const totalQuests = successful.reduce((sum, r) => sum + r.questsClaimed, 0); 753 | const totalChests = successful.reduce((sum, r) => sum + r.chestsOpened, 0); 754 | 755 | logger.success(`Processed ${successful.length}/${results.length} wallets successfully`); 756 | logger.success(`Total quests claimed: ${totalQuests}`); 757 | logger.success(`Total chests opened: ${totalChests}`); 758 | 759 | results.forEach((result, index) => { 760 | const status = result.success ? colors.green + "✅" : colors.red + "❌"; 761 | console.log(`${status} Wallet ${index + 1}: ${result.address} | Proxy: ${result.proxy} | Quests: ${result.questsClaimed} | Chests: ${result.chestsOpened}${colors.reset}`); 762 | }); 763 | 764 | if (results.some(r => !r.success)) { 765 | logger.warn("Failed wallets:"); 766 | results.filter(r => !r.success).forEach((r, idx) => { 767 | logger.error(`Wallet ${results.indexOf(r) + 1}: ${r.error}`); 768 | }); 769 | } 770 | 771 | console.log(`\n${colors.cyan}[⏳] All wallets processed. Waiting for next daily reset...${colors.reset}\n`); 772 | 773 | await startCountdown(); 774 | 775 | if (proxies.length > 0) { 776 | logger.step("Re-testing proxy connectivity for next cycle..."); 777 | workingProxies = []; 778 | for (const proxy of proxies) { 779 | if (await testProxy(proxy, 3000)) { 780 | workingProxies.push(proxy); 781 | } 782 | } 783 | logger.info(`${workingProxies.length}/${proxies.length} proxies ready for next cycle`); 784 | } 785 | } 786 | } 787 | 788 | main().catch(error => { 789 | logger.error(`Main execution error: ${error.message}`); 790 | process.exit(1); 791 | }); --------------------------------------------------------------------------------