├── proxies.txt ├── package.json ├── README.md └── index.js /proxies.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infts-auto-bot", 3 | "version": "1.0.0", 4 | "description": "A Node.js script for automating iNFT minting and interaction on the Sui testnet", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/vikitoshi/INFTS-Auto-Bot.git" 12 | }, 13 | "author": "vikitoshi", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@mysten/sui.js": "^0.49.1", 17 | "axios": "^1.7.2", 18 | "dotenv": "^16.4.5", 19 | "https-proxy-agent": "^7.0.5", 20 | "readline": "^1.3.0" 21 | } 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # INFTS Auto Bot 2 | 3 | An automated bot for minting and interacting with Intelligent NFTs (iNFTs) on the Sui testnet. 4 | 5 | ## Features ✨ 6 | 7 | - Automated minting of iNFTs on Sui testnet 8 | - Random NFT generation with unique names, descriptions, and images 9 | - Interaction with iNFTs through chat API 10 | - Proxy support for anonymity 11 | - Multi-wallet support 12 | 13 | ## Prerequisites 📋 14 | 15 | - Node.js (v18 or higher) 16 | - npm or yarn 17 | - Sui testnet wallets with private keys 18 | - (Optional) Proxies in `proxies.txt` file 19 | 20 | ## Installation ⚙️ 21 | 22 | 1. Clone the repository: 23 | ```bash 24 | git clone https://github.com/vikitoshi/INFTS-Auto-Bot.git 25 | cd INFTS-Auto-Bot 26 | ``` 27 | 28 | 2. Install dependencies: 29 | ```bash 30 | npm install 31 | ``` 32 | 33 | 3. Create a `.env` file with your private keys: 34 | ```env 35 | PRIVATE_KEY_1=your_sui_testnet_private_key_1 36 | PRIVATE_KEY_2=your_sui_testnet_private_key_2 37 | # Add more keys as needed 38 | ``` 39 | 40 | 4. (Optional) Add proxies to `proxies.txt`: 41 | ``` 42 | http://proxy1:port 43 | http://proxy2:port 44 | ``` 45 | 46 | ## Usage 🚀 47 | 48 | Run the bot: 49 | ```bash 50 | node index.js 51 | ``` 52 | 53 | The bot will: 54 | 1. Ask for the number of chat interactions to perform 55 | 2. Process each wallet sequentially: 56 | - Generate random NFT metadata 57 | - Upload image and metadata to Walrus storage 58 | - Mint the iNFT on Sui testnet 59 | - Perform the specified number of chat interactions 60 | 61 | ## Configuration ⚙️ 62 | 63 | You can modify the following constants in the code: 64 | - `INFT_PACKAGE`: The package ID of the iNFT contract 65 | - `ATOMA_API_KEY`: API key for Atoma network 66 | - Random generation parameters (names, descriptions, etc.) 67 | 68 | ## License 📄 69 | 70 | This project is open source and available under the [MIT License](LICENSE). 71 | 72 | ## Disclaimer ⚠️ 73 | 74 | This bot is for educational purposes only. Use at your own risk. The developers are not responsible for any misuse or loss of funds. 75 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); 2 | const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); 3 | const { TransactionBlock } = require('@mysten/sui.js/transactions'); 4 | const { decodeSuiPrivateKey } = require('@mysten/sui.js/cryptography'); 5 | const axios = require('axios'); 6 | const readline = require('readline'); 7 | const { HttpsProxyAgent } = require('https-proxy-agent'); 8 | const fs = require('fs'); 9 | require('dotenv').config(); 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 | }; 20 | 21 | const logger = { 22 | info: (msg) => console.log(`${colors.green}[✓] ${msg}${colors.reset}`), 23 | warn: (msg) => console.log(`${colors.yellow}[⚠] ${msg}${colors.reset}`), 24 | error: (msg) => console.log(`${colors.red}[✗] ${msg}${colors.reset}`), 25 | success: (msg) => console.log(`${colors.green}[✅] ${msg}${colors.reset}`), 26 | loading: (msg) => console.log(`${colors.cyan}[⟳] ${msg}${colors.reset}`), 27 | step: (msg) => console.log(`${colors.white}[➤] ${msg}${colors.reset}`), 28 | banner: () => { 29 | console.log(`${colors.cyan}${colors.bold}`); 30 | console.log(`---------------------------------------------`); 31 | console.log(` INFTs Auto Bot - Airdrop Insiders `); 32 | console.log(`---------------------------------------------${colors.reset}`); 33 | console.log(); 34 | } 35 | }; 36 | 37 | const userAgents = [ 38 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', 39 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', 40 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0', 41 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', 42 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1' 43 | ]; 44 | 45 | const getRandomUserAgent = () => userAgents[Math.floor(Math.random() * userAgents.length)]; 46 | 47 | const loadProxies = () => { 48 | try { 49 | const proxies = fs.readFileSync('proxies.txt', 'utf8') 50 | .split('\n') 51 | .map(line => line.trim()) 52 | .filter(line => line && !line.startsWith('#')); 53 | if (proxies.length === 0) throw new Error('No valid proxies found in proxies.txt'); 54 | return proxies; 55 | } catch (error) { 56 | logger.error(`Error loading proxies: ${error.message}`); 57 | return []; 58 | } 59 | }; 60 | 61 | const createProxyAgent = (proxy) => { 62 | try { 63 | let proxyUrl = proxy; 64 | if (!proxyUrl.startsWith('http')) { 65 | proxyUrl = `http://${proxy}`; 66 | } 67 | return new HttpsProxyAgent(proxyUrl); 68 | } catch (error) { 69 | logger.error(`Invalid proxy format: ${proxy}`); 70 | return null; 71 | } 72 | }; 73 | 74 | const loadPrivateKeys = () => { 75 | const keys = []; 76 | let i = 1; 77 | while (process.env[`PRIVATE_KEY_${i}`]) { 78 | keys.push(process.env[`PRIVATE_KEY_${i}`]); 79 | i++; 80 | } 81 | if (keys.length === 0) throw new Error('No PRIVATE_KEY found in .env'); 82 | return keys; 83 | }; 84 | 85 | const client = new SuiClient({ url: getFullnodeUrl('testnet') }); 86 | const proxies = loadProxies(); 87 | const privateKeys = loadPrivateKeys(); 88 | 89 | const ATOMA_API_KEY = 'B2BvuUbL9qdbEaPf75Wtm9iTfYIejq'; 90 | const WALRUS_PUBLISHER = 'https://publisher.walrus-testnet.walrus.space/v1/blobs?epochs=5'; 91 | const WALRUS_AGGREGATOR = 'https://aggregator.walrus-testnet.walrus.space/v1/blobs/'; 92 | const ATOMA_API = 'https://api.atoma.network/v1/chat/completions'; 93 | const INFT_PACKAGE = '0x7111b909689ec53115a2360c3fe9106c2c6f8e152dbc37d4a98bae51a37f8f62'; 94 | const PICSUM_URL = 'https://picsum.photos/800/600'; 95 | 96 | const namePrefixes = ['Star', 'Crypto', 'Pixel', 'Quantum', 'Astro', 'Neon', 'Galactic', 'Mystic', 'Cosmic', 'Vortex']; 97 | const nameSuffixes = ['Voyager', 'Beast', 'Pal', 'Gem', 'Buddy', 'Rider', 'Spark', 'Flare', 'Nova', 'Quest']; 98 | const descriptionTemplates = [ 99 | 'A {adjective} digital collectible infused with {theme} energy.', 100 | 'An adventurous NFT exploring the {theme} universe.', 101 | 'A {adjective} companion for your {context} journey.', 102 | 'A rare {item} powered by {theme} technology.', 103 | 'An intelligent NFT with a {adjective} aura.' 104 | ]; 105 | const adjectives = ['unique', 'radiant', 'mysterious', 'vibrant', 'epic', 'ethereal', 'dynamic', 'stellar', 'futuristic', 'enigmatic']; 106 | const themes = ['cosmic', 'blockchain', 'digital', 'quantum', 'celestial', 'virtual', 'interstellar', 'technological', 'metaverse', 'crypto']; 107 | const contexts = ['Web3', 'blockchain', 'NFT', 'decentralized', 'crypto']; 108 | const items = ['gem', 'artifact', 'token', 'relic', 'collectible']; 109 | const randomModels = ['analytical-model', 'creative-model', 'exploratory-model', 'default-model']; 110 | const randomPrompts = [ 111 | 'Hello, how can I assist you today?', 112 | 'Tell me about your favorite blockchain project!', 113 | 'What makes you unique in the NFT world?', 114 | 'Let’s explore the future of Web3 together!', 115 | 'Hi, I’m your smart NFT! What’s on your mind?', 116 | 'What’s the coolest thing about the blockchain?', 117 | 'How do you envision the metaverse?', 118 | 'What’s your favorite NFT trait?' 119 | ]; 120 | 121 | const generateRandomName = () => { 122 | const prefix = getRandomElement(namePrefixes); 123 | const suffix = getRandomElement(nameSuffixes); 124 | return `${prefix}${suffix}`; 125 | }; 126 | 127 | const generateRandomDescription = () => { 128 | const template = getRandomElement(descriptionTemplates); 129 | return template 130 | .replace('{adjective}', getRandomElement(adjectives)) 131 | .replace('{theme}', getRandomElement(themes)) 132 | .replace('{context}', getRandomElement(contexts)) 133 | .replace('{item}', getRandomElement(items)); 134 | }; 135 | 136 | const getRandomElement = (array) => array[Math.floor(Math.random() * array.length)]; 137 | 138 | const getInteractionCount = () => { 139 | const rl = readline.createInterface({ 140 | input: process.stdin, 141 | output: process.stdout 142 | }); 143 | 144 | return new Promise((resolve) => { 145 | rl.question('Enter the number of chat interactions to perform: ', (answer) => { 146 | const count = parseInt(answer); 147 | if (isNaN(count) || count < 1 || count > 100000) { 148 | logger.error('Invalid input. Using default value of 1 interaction.'); 149 | resolve(1); 150 | } else { 151 | resolve(count); 152 | } 153 | rl.close(); 154 | }); 155 | }); 156 | }; 157 | 158 | async function getRandomImage() { 159 | try { 160 | logger.loading('Fetching random image from Picsum...'); 161 | const proxy = proxies.length > 0 ? createProxyAgent(getRandomElement(proxies)) : null; 162 | const response = await axios.get(PICSUM_URL, { 163 | responseType: 'arraybuffer', 164 | httpsAgent: proxy, 165 | headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36' } 166 | }); 167 | if (response.headers['content-type'] !== 'image/jpeg') { 168 | throw new Error('Fetched image is not JPEG'); 169 | } 170 | logger.success('Image fetched successfully.'); 171 | return response.data; 172 | } catch (error) { 173 | logger.error(`Error fetching image from Picsum: ${error.message}`); 174 | throw error; 175 | } 176 | } 177 | 178 | async function retryOperation(operation, maxRetries = 3, delay = 1000) { 179 | for (let attempt = 1; attempt <= maxRetries; attempt++) { 180 | try { 181 | return await operation(); 182 | } catch (error) { 183 | logger.warn(`Attempt ${attempt} failed: ${error.message}`); 184 | if (attempt === maxRetries) { 185 | logger.error(`All ${maxRetries} attempts failed: ${error.message}`); 186 | throw error; 187 | } 188 | await new Promise(resolve => setTimeout(resolve, delay)); 189 | } 190 | } 191 | } 192 | 193 | async function uploadToWalrus(fileData, contentType, keypair) { 194 | return retryOperation(async () => { 195 | try { 196 | logger.loading('Uploading file to Walrus...'); 197 | const proxy = proxies.length > 0 ? createProxyAgent(getRandomElement(proxies)) : null; 198 | const response = await axios.put(WALRUS_PUBLISHER, fileData, { 199 | headers: { 200 | 'accept': '*/*', 201 | 'accept-language': 'en-US,en;q=0.9', 202 | 'content-type': contentType, 203 | 'priority': 'u=1, i', 204 | 'sec-ch-ua': '"Chromium";v="136", "Brave";v="136", "Not.A/Brand";v="99"', 205 | 'sec-ch-ua-mobile': '?0', 206 | 'sec-ch-ua-platform': '"Windows"', 207 | 'sec-fetch-dest': 'empty', 208 | 'sec-fetch-mode': 'cors', 209 | 'sec-fetch-site': 'cross-site', 210 | 'sec-gpc': '1', 211 | 'x-sui-address': keypair.getPublicKey().toSuiAddress(), 212 | 'x-sui-network': 'testnet', 213 | 'Referer': 'https://www.infts.xyz/', 214 | 'Referrer-Policy': 'strict-origin-when-cross-origin' 215 | }, 216 | httpsAgent: proxy, 217 | timeout: 30000 218 | }); 219 | 220 | logger.info(`Walrus response: ${JSON.stringify(response.data)}`); 221 | 222 | let blobId; 223 | if (response.data?.newlyCreated?.blobObject?.blobId) { 224 | blobId = response.data.newlyCreated.blobObject.blobId; 225 | } else if (response.data?.alreadyCertified?.blobId) { 226 | blobId = response.data.alreadyCertified.blobId; 227 | logger.info(`Blob already certified with blobId: ${blobId}`); 228 | } else { 229 | throw new Error('Invalid response from Walrus: blobId not found'); 230 | } 231 | 232 | logger.success('File uploaded to Walrus successfully.'); 233 | return blobId; 234 | } catch (error) { 235 | logger.error(`Error uploading to Walrus: ${error.message}`); 236 | if (error.response) { 237 | logger.error(`Walrus API response: ${JSON.stringify(error.response.data)}`); 238 | } 239 | throw error; 240 | } 241 | }); 242 | } 243 | 244 | async function uploadMetadata(metadata, keypair) { 245 | return retryOperation(async () => { 246 | try { 247 | logger.loading('Uploading metadata to Walrus...'); 248 | const proxy = proxies.length > 0 ? createProxyAgent(getRandomElement(proxies)) : null; 249 | const response = await axios.put(WALRUS_PUBLISHER, Buffer.from(JSON.stringify(metadata)), { 250 | headers: { 251 | 'accept': '*/*', 252 | 'accept-language': 'en-US,en;q=0.9', 253 | 'content-type': 'application/octet-stream', 254 | 'priority': 'u=1, i', 255 | 'sec-ch-ua': '"Chromium";v="136", "Brave";v="136", "Not.A/Brand";v="99"', 256 | 'sec-ch-ua-mobile': '?0', 257 | 'sec-ch-ua-platform': '"Windows"', 258 | 'sec-fetch-dest': 'empty', 259 | 'sec-fetch-mode': 'cors', 260 | 'sec-fetch-site': 'cross-site', 261 | 'sec-gpc': '1', 262 | 'x-sui-address': keypair.getPublicKey().toSuiAddress(), 263 | 'x-sui-network': 'testnet', 264 | 'Referer': 'https://www.infts.xyz/', 265 | 'Referrer-Policy': 'strict-origin-when-cross-origin' 266 | }, 267 | httpsAgent: proxy, 268 | timeout: 30000 269 | }); 270 | 271 | logger.info(`Walrus metadata response: ${JSON.stringify(response.data)}`); 272 | 273 | let blobId; 274 | if (response.data?.newlyCreated?.blobObject?.blobId) { 275 | blobId = response.data.newlyCreated.blobObject.blobId; 276 | } else if (response.data?.alreadyCertified?.blobId) { 277 | blobId = response.data.alreadyCertified.blobId; 278 | logger.info(`Metadata blob already certified with blobId: ${blobId}`); 279 | } else { 280 | throw new Error('Invalid response from Walrus: blobId not found'); 281 | } 282 | 283 | logger.success('Metadata uploaded successfully.'); 284 | return blobId; 285 | } catch (error) { 286 | logger.error(`Error uploading metadata: ${error.message}`); 287 | if (error.response) { 288 | logger.error(`Walrus API response: ${JSON.stringify(error.response.data)}`); 289 | } 290 | throw error; 291 | } 292 | }); 293 | } 294 | 295 | async function mintNFT(name, description, imageUrl, publicMetadataUrl, privateMetadataUrl, modelId, keypair) { 296 | try { 297 | logger.loading('Minting NFT on Sui...'); 298 | const tx = new TransactionBlock(); 299 | tx.moveCall({ 300 | target: `${INFT_PACKAGE}::inft_core::mint_nft`, 301 | arguments: [ 302 | tx.pure(name), 303 | tx.pure(description), 304 | tx.pure(imageUrl), 305 | tx.pure(publicMetadataUrl), 306 | tx.pure(privateMetadataUrl), 307 | tx.pure(modelId) 308 | ] 309 | }); 310 | tx.setSender(keypair.getPublicKey().toSuiAddress()); 311 | tx.setGasBudget(200000000); 312 | 313 | const result = await client.signAndExecuteTransactionBlock({ 314 | signer: keypair, 315 | transactionBlock: tx, 316 | options: { showRawEffects: true, showObjectChanges: true } 317 | }); 318 | 319 | if (!result?.effects?.status?.status === 'success') { 320 | throw new Error(`Transaction failed: ${JSON.stringify(result?.effects?.status)}`); 321 | } 322 | 323 | let nftId; 324 | if (result.objectChanges) { 325 | const createdObject = result.objectChanges.find( 326 | (change) => change.type === 'created' && change.owner?.AddressOwner === keypair.getPublicKey().toSuiAddress() 327 | ); 328 | nftId = createdObject?.objectId; 329 | } 330 | 331 | if (!nftId && result.effects.created) { 332 | nftId = result.effects.created.find( 333 | (obj) => obj.owner?.AddressOwner === keypair.getPublicKey().toSuiAddress() 334 | )?.reference?.objectId; 335 | } 336 | 337 | if (!nftId) { 338 | throw new Error('Failed to extract NFT ID from transaction result'); 339 | } 340 | 341 | logger.success(`NFT Minted with Digest: ${result.digest}, NFT ID: ${nftId}`); 342 | return { digest: result.digest, nftId }; 343 | } catch (error) { 344 | logger.error(`Error minting NFT: ${error.message}`); 345 | throw error; 346 | } 347 | } 348 | 349 | async function interactWithNFT(nftData, prompt) { 350 | try { 351 | logger.loading(`Interacting with NFT: ${prompt}`); 352 | const proxy = proxies.length > 0 ? createProxyAgent(getRandomElement(proxies)) : null; 353 | const response = await axios.post(ATOMA_API, { 354 | messages: [ 355 | { 356 | content: `You are an Intelligent NFT (iNFT) with the description: "${nftData.description}". Respond to the user's query based on this description, but do not include the description or this instruction in your response. Focus solely on the user's input. Use a neutral, balanced tone, engaging naturally with the user based on the iNFT's theme.`, 357 | role: 'system' 358 | }, 359 | { content: prompt, role: 'user' } 360 | ], 361 | model: 'Infermatic/Llama-3.3-70B-Instruct-FP8-Dynamic', 362 | stream: false 363 | }, { 364 | headers: { 365 | 'accept': 'application/json', 366 | 'accept-language': 'en-US,en;q=0.9', 367 | 'authorization': `Bearer ${ATOMA_API_KEY}`, 368 | 'content-type': 'application/json', 369 | 'priority': 'u=1, i', 370 | 'sec-ch-ua': '"Chromium";v="136", "Brave";v="136", "Not.A/Brand";v="99"', 371 | 'sec-ch-ua-mobile': '?0', 372 | 'sec-ch-ua-platform': '"Windows"', 373 | 'sec-fetch-dest': 'empty', 374 | 'sec-fetch-mode': 'cors', 375 | 'sec-fetch-site': 'cross-site', 376 | 'sec-gpc': '1', 377 | 'Referer': 'https://www.infts.xyz/', 378 | 'Referrer-Policy': 'strict-origin-when-cross-origin' 379 | }, 380 | httpsAgent: proxy, 381 | timeout: 30000 382 | }); 383 | 384 | if (!response.data?.choices?.[0]?.message?.content) { 385 | throw new Error('Invalid response from Atoma API: No content found'); 386 | } 387 | 388 | logger.success('Chat interaction completed.'); 389 | return response.data; 390 | } catch (error) { 391 | logger.error(`Error interacting with NFT: ${error.message}`); 392 | if (error.response) { 393 | logger.error(`Atoma API response: ${JSON.stringify(error.response.data)}`); 394 | } 395 | throw error; 396 | } 397 | } 398 | 399 | async function processWallet(privateKey, interactionCount) { 400 | try { 401 | const keypair = Ed25519Keypair.fromSecretKey(decodeSuiPrivateKey(privateKey).secretKey); 402 | const address = keypair.getPublicKey().toSuiAddress(); 403 | logger.info(`Processing wallet: ${address}`); 404 | 405 | logger.step('Generating random NFT data...'); 406 | const name = generateRandomName(); 407 | const description = generateRandomDescription(); 408 | const modelId = getRandomElement(randomModels); 409 | logger.info(`NFT Name: ${name}, Description: ${description}, Model: ${modelId}`); 410 | 411 | const imageData = await getRandomImage(); 412 | const imageBlobId = await uploadToWalrus(imageData, 'image/jpeg', keypair); 413 | const imageUrl = `${WALRUS_AGGREGATOR}${imageBlobId}`; 414 | logger.info(`Image uploaded: ${imageUrl}`); 415 | 416 | const publicMetadata = { 417 | name, 418 | description, 419 | externalLink: '', 420 | creationDate: new Date().toISOString(), 421 | creator: address 422 | }; 423 | const publicMetadataBlobId = await uploadMetadata(publicMetadata, keypair); 424 | const publicMetadataUrl = `${WALRUS_AGGREGATOR}${publicMetadataBlobId}`; 425 | logger.info(`Public metadata uploaded: ${publicMetadataUrl}`); 426 | 427 | const privateMetadata = { 428 | creatorNotes: 'Private notes for this NFT', 429 | creationDetails: { 430 | application: 'INFT Protocol', 431 | version: '1.0.0', 432 | timestamp: new Date().toISOString() 433 | } 434 | }; 435 | const privateMetadataBlobId = await uploadMetadata(privateMetadata, keypair); 436 | const privateMetadataUrl = `${WALRUS_AGGREGATOR}${privateMetadataBlobId}`; 437 | logger.info(`Private metadata uploaded: ${privateMetadataUrl}`); 438 | 439 | const { digest, nftId } = await mintNFT( 440 | name, 441 | description, 442 | imageUrl, 443 | publicMetadataUrl, 444 | privateMetadataUrl, 445 | modelId, 446 | keypair 447 | ); 448 | 449 | const nftData = { id: nftId, name, description, image_url: imageUrl, evolution_stage: 0, interaction_count: 0, atoma_model_id: modelId }; 450 | for (let i = 0; i < interactionCount; i++) { 451 | const randomPrompt = getRandomElement(randomPrompts); 452 | logger.step(`Interaction ${i + 1}/${interactionCount}: ${randomPrompt}`); 453 | const response = await interactWithNFT(nftData, randomPrompt); 454 | logger.info(`Response: ${response.choices?.[0]?.message?.content || 'No response content'}`); 455 | } 456 | 457 | logger.success(`Completed processing wallet: ${address}`); 458 | } catch (error) { 459 | logger.error(`Error processing wallet: ${error.message}`); 460 | } 461 | } 462 | 463 | async function main() { 464 | try { 465 | logger.banner(); 466 | const interactionCount = await getInteractionCount(); 467 | logger.info(`Interaction count set to: ${interactionCount}`); 468 | 469 | for (const privateKey of privateKeys) { 470 | await processWallet(privateKey, interactionCount); 471 | } 472 | 473 | logger.success('Bot execution completed successfully for all wallets.'); 474 | } catch (error) { 475 | logger.error(`Bot execution failed: ${error.message}`); 476 | } 477 | } 478 | 479 | main(); 480 | --------------------------------------------------------------------------------