├── mail.txt ├── address.txt ├── pvkey.txt ├── addressERC20.txt ├── addressNFT.txt ├── contractERC20.txt ├── contractNFT.txt ├── banner.js ├── emitter.js ├── .gitignore ├── package.json ├── scripts ├── gasPump.js ├── wlGtx.js ├── clober.js ├── inari.js ├── wlNovadubs.js ├── nftCollection.js ├── sendToken.js ├── deployToken.js └── sendTx.js ├── README.md ├── index.js ├── tui.js └── bot.js /mail.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /address.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pvkey.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addressERC20.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addressNFT.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /contractERC20.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /contractNFT.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /banner.js: -------------------------------------------------------------------------------- 1 | // banner.js 2 | // Exports the banner text to be displayed in the TUI. 3 | 4 | const bannerText = "RISE TESTNET SCRIPT BY CRYPTO WITH SHASHI | CWS"; 5 | 6 | module.exports = bannerText; 7 | 8 | -------------------------------------------------------------------------------- /emitter.js: -------------------------------------------------------------------------------- 1 | // emitter.js 2 | // Creates and exports a single EventEmitter instance to be shared across modules. 3 | // This helps prevent circular dependency issues. 4 | 5 | const EventEmitter = require('eventemitter3'); 6 | 7 | // Create a single instance of the event emitter 8 | const emitter = new EventEmitter(); 9 | 10 | // Export the instance 11 | module.exports = emitter; 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | package-lock.json 7 | *.env 8 | *.lock 9 | 10 | # Input files (optional, depends if you commit them) 11 | # pvkey.txt 12 | # address.txt 13 | # addressERC20.txt 14 | # contractERC20.txt 15 | # contractNFT.txt 16 | # mail.txt 17 | 18 | # OS generated files 19 | .DS_Store 20 | .DS_Store? 21 | ._* 22 | .Spotlight-V100 23 | .Trashes 24 | ehthumbs.db 25 | Thumbs.db 26 | 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rise-testnet-js-bot", 3 | "version": "1.0.0", 4 | "description": "JavaScript bot for Rise Testnet with a Blessed TUI.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "rise", 12 | "testnet", 13 | "blockchain", 14 | "tui", 15 | "blessed", 16 | "automation" 17 | ], 18 | "author": "Crypto With Shashi | CWS", 19 | "license": "MIT", 20 | "dependencies": { 21 | "blessed": "^0.1.81", 22 | "chalk": "^4.1.2", 23 | "eventemitter3": "^5.0.1" 24 | 25 | 26 | }, 27 | "engines": { 28 | "node": ">=16.0.0" 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /scripts/gasPump.js: -------------------------------------------------------------------------------- 1 | // scripts/gasPump.js 2 | // Placeholder for GasPump Swap logic. 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runGasPump(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[gasPump] Starting GasPump Swap interaction (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Get GasPump contract address and ABI 11 | // - Connect to RPC 12 | // - Define swap parameters 13 | // - Interact with the GasPump contract 14 | // - Emit 'log' and 'success' events 15 | 16 | emitter.emit('log', { level: 'WAIT', message: '[gasPump] Simulating swap operation...' }); 17 | await new Promise(resolve => setTimeout(resolve, 4000)); 18 | 19 | emitter.emit('success', '[gasPump] Simulated GasPump swap successful.'); 20 | emitter.emit('log', { level: 'INFO', message: '[gasPump] Process finished.' }); 21 | } 22 | 23 | module.exports = { runGasPump }; 24 | 25 | -------------------------------------------------------------------------------- /scripts/wlGtx.js: -------------------------------------------------------------------------------- 1 | // scripts/wlGtx.js 2 | // Placeholder for WL GTX Dex interaction logic. 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runWlGtx(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[wlGtx] Starting WL GTX Dex interaction (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Get WL GTX contract address and ABI 11 | // - Connect to RPC 12 | // - Interact with the WL GTX contract (specific function depends on its purpose) 13 | // - Emit 'log' and 'success' events 14 | 15 | emitter.emit('log', { level: 'WAIT', message: '[wlGtx] Simulating interaction...' }); 16 | await new Promise(resolve => setTimeout(resolve, 2800)); 17 | 18 | emitter.emit('success', '[wlGtx] Simulated WL GTX Dex interaction successful.'); 19 | emitter.emit('log', { level: 'INFO', message: '[wlGtx] Process finished.' }); 20 | } 21 | 22 | module.exports = { runWlGtx }; 23 | 24 | -------------------------------------------------------------------------------- /scripts/clober.js: -------------------------------------------------------------------------------- 1 | // scripts/clober.js 2 | // Placeholder for Clober Swap logic (ETH <-> WETH). 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runClober(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[clober] Starting Clober Swap interaction (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Get Clober contract address and ABI 11 | // - Connect to RPC 12 | // - Define wrap/unwrap parameters 13 | // - Interact with the Clober contract 14 | // - Emit 'log' and 'success' events 15 | 16 | emitter.emit('log', { level: 'WAIT', message: '[clober] Simulating ETH/WETH swap...' }); 17 | await new Promise(resolve => setTimeout(resolve, 3000)); 18 | 19 | emitter.emit('success', '[clober] Simulated Clober swap successful.'); 20 | emitter.emit('log', { level: 'INFO', message: '[clober] Process finished.' }); 21 | } 22 | 23 | module.exports = { runClober }; 24 | 25 | -------------------------------------------------------------------------------- /scripts/inari.js: -------------------------------------------------------------------------------- 1 | // scripts/inari.js 2 | // Placeholder for Inari Finance logic (Deposit/Withdraw). 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runInari(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[inari] Starting Inari Finance interaction (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Add sub-menu or logic for Deposit/Withdraw 11 | // - Get Inari contract address and ABI 12 | // - Connect to RPC 13 | // - Interact with the Inari contract 14 | // - Emit 'log' and 'success' events 15 | 16 | emitter.emit('log', { level: 'WAIT', message: '[inari] Simulating deposit/withdraw...' }); 17 | await new Promise(resolve => setTimeout(resolve, 3200)); 18 | 19 | emitter.emit('success', '[inari] Simulated Inari Finance operation successful.'); 20 | emitter.emit('log', { level: 'INFO', message: '[inari] Process finished.' }); 21 | } 22 | 23 | module.exports = { runInari }; 24 | 25 | -------------------------------------------------------------------------------- /scripts/wlNovadubs.js: -------------------------------------------------------------------------------- 1 | // scripts/wlNovadubs.js 2 | // Placeholder for WL Novadubs interaction logic. 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runWlNovadubs(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[wlNovadubs] Starting WL Novadubs interaction (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Get WL Novadubs contract address and ABI 11 | // - Connect to RPC 12 | // - Interact with the WL Novadubs contract (specific function depends on its purpose) 13 | // - Emit 'log' and 'success' events 14 | 15 | emitter.emit('log', { level: 'WAIT', message: '[wlNovadubs] Simulating interaction...' }); 16 | await new Promise(resolve => setTimeout(resolve, 3100)); 17 | 18 | emitter.emit('success', '[wlNovadubs] Simulated WL Novadubs interaction successful.'); 19 | emitter.emit('log', { level: 'INFO', message: '[wlNovadubs] Process finished.' }); 20 | } 21 | 22 | module.exports = { runWlNovadubs }; 23 | 24 | -------------------------------------------------------------------------------- /scripts/nftCollection.js: -------------------------------------------------------------------------------- 1 | // scripts/nftCollection.js 2 | // Placeholder for NFT Collection management (Deploy, Mint, Burn). 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runNftCollection(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[nftCollection] Starting NFT management (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Add sub-menu or logic for Deploy/Mint/Burn actions 11 | // - Read NFT contract details (address, ABI) 12 | // - Connect to RPC 13 | // - Interact with the NFT contract based on the chosen action 14 | // - Emit 'log' and 'success' events 15 | 16 | emitter.emit('log', { level: 'WAIT', message: '[nftCollection] Simulating NFT operation (e.g., Mint)...' }); 17 | await new Promise(resolve => setTimeout(resolve, 3500)); 18 | 19 | emitter.emit('success', '[nftCollection] Simulated NFT operation completed.'); 20 | emitter.emit('log', { level: 'INFO', message: '[nftCollection] Process finished.' }); 21 | } 22 | 23 | module.exports = { runNftCollection }; 24 | 25 | -------------------------------------------------------------------------------- /scripts/sendToken.js: -------------------------------------------------------------------------------- 1 | // scripts/sendToken.js 2 | // Placeholder for Send ERC20 Token logic. 3 | 4 | const { emitter } = require('../bot'); // Access the global emitter 5 | 6 | async function runSendToken(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[sendToken] Starting ERC20 token sending (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Read contract address (from file or config) 11 | // - Read recipient addresses (from addressERC20.txt or random) 12 | // - Connect to RPC 13 | // - Iterate through wallets 14 | // - Construct and send token transfer transactions 15 | // - Emit 'log' and 'success' events 16 | 17 | emitter.emit('log', { level: 'WAIT', message: '[sendToken] Simulating token transfer...' }); 18 | await new Promise(resolve => setTimeout(resolve, 2500)); // Simulate async work 19 | 20 | emitter.emit('success', '[sendToken] Simulated ERC20 token sent successfully.'); 21 | emitter.emit('log', { level: 'INFO', message: '[sendToken] Process finished.' }); 22 | } 23 | 24 | module.exports = { runSendToken }; 25 | 26 | -------------------------------------------------------------------------------- /scripts/deployToken.js: -------------------------------------------------------------------------------- 1 | // scripts/deployToken.js 2 | // Placeholder for Deploy ERC20 Token logic. 3 | 4 | const { emitter } = require('../bot'); 5 | 6 | async function runDeployToken(language = 'en') { 7 | emitter.emit('log', { level: 'INFO', message: `[deployToken] Starting ERC20 deployment (Lang: ${language})...` }); 8 | 9 | // TODO: Implement actual web3/ethers.js logic here 10 | // - Read contract ABI/Bytecode 11 | // - Connect to RPC 12 | // - Use a wallet from pvkey.txt 13 | // - Deploy the contract 14 | // - Emit 'log' and 'success' events (include contract address on success) 15 | 16 | emitter.emit('log', { level: 'WAIT', message: '[deployToken] Simulating deployment...' }); 17 | await new Promise(resolve => setTimeout(resolve, 4000)); 18 | 19 | const simulatedAddress = '0x' + [...Array(40)].map(() => Math.floor(Math.random() * 16).toString(16)).join(''); 20 | emitter.emit('success', `[deployToken] Simulated ERC20 deployed at ${simulatedAddress}`); 21 | emitter.emit('log', { level: 'INFO', message: '[deployToken] Process finished.' }); 22 | } 23 | 24 | module.exports = { runDeployToken }; 25 | 26 | -------------------------------------------------------------------------------- /scripts/sendTx.js: -------------------------------------------------------------------------------- 1 | // scripts/sendTx.js 2 | // Placeholder for Send Transaction logic. 3 | 4 | // *** CHANGE: Require the shared emitter instance *** 5 | const emitter = require('../emitter'); 6 | // Import ethers or other necessary libraries when implementing 7 | // const { ethers } = require('ethers'); 8 | 9 | async function runSendTx(language = 'en', /* Add other params like wallets, addresses */) { 10 | emitter.emit('log', { level: 'INFO', message: `[sendTx] Starting transaction sending process (Language: ${language})...` }); 11 | 12 | // TODO: Implement actual web3/ethers.js logic here 13 | // - Read addresses from address.txt or generate random ones (pass addresses as param?) 14 | // - Connect to RPC 15 | // - Iterate through wallets (pass wallets as param?) 16 | // - Construct and send transactions using wallet keys 17 | // - Emit 'log' and 'success' events 18 | 19 | try { 20 | emitter.emit('log', { level: 'WAIT', message: '[sendTx] Simulating transaction delay...' }); 21 | await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 1500)); // Simulate async work 22 | 23 | // Simulate success or failure 24 | if (Math.random() > 0.15) { // 85% success rate 25 | emitter.emit('success', '[sendTx] Simulated transaction sent successfully.'); 26 | } else { 27 | emitter.emit('log', { level: 'ERROR', message: '[sendTx] Simulated transaction failed!' }); 28 | } 29 | 30 | } catch (error) { 31 | emitter.emit('log', { level: 'ERROR', message: `[sendTx] Error during simulation: ${error.message}` }); 32 | } finally { 33 | emitter.emit('log', { level: 'INFO', message: '[sendTx] Process finished.' }); 34 | } 35 | } 36 | 37 | module.exports = { runSendTx }; 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RISE Testnet Bot 2 | 3 | A JavaScript-based automation bot for interacting with various smart contracts and DeFi protocols on the RISE testnet. This bot supports multiple features such as token transactions, NFT interactions, and DeFi protocol operations via a terminal user interface (TUI). 4 | 5 | 6 | 7 | ## Features 8 | 9 | - Interact with multiple DeFi protocols (e.g., Clober, Inari, WL DEXes) 10 | - Deploy and send ERC20 tokens 11 | - Interact with NFT collections 12 | - Execute general smart contract transactions 13 | - Manage multiple wallets via private keys 14 | - Terminal-based UI for real-time interaction and monitoring 15 | 16 | 17 | ## Project Structure 18 | 19 | ``` 20 | rise-testnet-js-bot/ 21 | ├── node_modules/ # Generated by npm install (contains dependencies) 22 | ├── scripts/ # Folder containing individual task scripts 23 | │ ├── clober.js # Clober Swap logic 24 | │ ├── deployToken.js # Deploy Token logic 25 | │ ├── gasPump.js # GasPump Swap logic 26 | │ ├── inari.js # Inari Finance logic 27 | │ ├── nftCollection.js # NFT Collection logic 28 | │ ├── sendToken.js # Send ERC20 Token logic 29 | │ ├── sendTx.js # Send Transaction logic 30 | │ ├── wlGtx.js # WL GTX Dex logic 31 | │ └── wlNovadubs.js # WL Novadubs logic 32 | ├── .gitignore # Specifies intentionally untracked files for Git 33 | ├── address.txt # Input: Recipient addresses for sendTx.js (Optional) 34 | ├── addressERC20.txt # Input: Recipient addresses for sendToken.js (Optional) 35 | ├── addressNFT.txt # Input: Addresses related to NFT operations (Optional) 36 | ├── banner.js # Exports the banner text string for the TUI 37 | ├── bot.js # Core bot logic, state management, simulation loop 38 | ├── contractERC20.txt # Input: ERC20 contract addresses to interact with (Optional) 39 | ├── contractNFT.txt # Input: NFT contract addresses to interact with (Optional) 40 | ├── emitter.js # Creates and exports the shared EventEmitter instance 41 | ├── index.js # Main entry point: handles startup, prompts, initializes TUI/Bot 42 | ├── mail.txt # Input: Email addresses (Optional) 43 | ├── package-lock.json # Generated by npm (records exact dependency versions) 44 | ├── package.json # Project metadata and dependencies 45 | ├── pvkey.txt # Input: Private keys (One per line, REQUIRED for bot operation) 46 | ├── README.md # Project documentation 47 | └── tui.js # Blessed TUI implementation (layout, event handling) 48 | ``` 49 | 50 | 51 | 52 | 53 | 54 | ## Prerequisites 55 | 56 | - Node.js (v16 or higher) 57 | - npm 58 | 59 | ## Installation 60 | 61 | ```bash 62 | git clone https://github.com/cryptowithshashi/RISE-TESTNET-BOT 63 | cd RISE-TESTNET-BOT 64 | ``` 65 | 66 | ```bash 67 | npm install 68 | ``` 69 | 70 | ## Setup 71 | 72 | - Add your private keys in `pvkey.txt` (one per line). 73 | - (Optional) Fill `address.txt`, `addressERC20.txt`, and `addressNFT.txt` for respective script targets. 74 | - Add token and NFT contract addresses in `contractERC20.txt` and `contractNFT.txt` respectively. 75 | - Customize `mail.txt` if email interaction is needed. 76 | 77 | 78 | ## Execute the code 79 | 80 | Run the bot using: 81 | 82 | ```bash 83 | node index.js 84 | ``` 85 | 86 | Navigate through the TUI to select and run desired operations. 87 | 88 | 89 | 90 | ## Notes 91 | 92 | - **Private Keys** are required. Make sure this file is **never shared**. 93 | - Ensure sufficient testnet funds in wallets for transactions to succeed. 94 | - The bot is designed for **testnet** usage. Never use mainnet private keys. 95 | 96 | 97 | 98 | ## ABOUT ME 99 | 100 | Twitter -- https://x.com/SHASHI522004 101 | 102 | Github -- https://github.com/cryptowithshashi 103 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // index.js 2 | // Main entry point for the application. 3 | // Prompts user for repetitions, initializes the TUI, and starts the bot logic. 4 | 5 | const readline = require('readline'); 6 | // *** CHANGE: Require the shared emitter instance *** 7 | const emitter = require('./emitter'); 8 | // *** CHANGE: Only require necessary functions *** 9 | const { initializeTui } = require('./tui'); 10 | const { initializeBot, runSimulation } = require('./bot'); 11 | 12 | // --- Function to ask for repetitions --- 13 | // (Keep askForRepetitions function as in your version) 14 | function askForRepetitions() { 15 | // Create a readline interface 16 | const rl = readline.createInterface({ 17 | input: process.stdin, 18 | output: process.stdout 19 | }); 20 | 21 | return new Promise((resolve) => { 22 | const promptUser = () => { 23 | rl.question('How many times to repeat the tasks per wallet? (Enter a number > 0): ', (answer) => { 24 | const parsedAnswer = parseInt(answer, 10); 25 | 26 | // Validate the input 27 | if (!isNaN(parsedAnswer) && parsedAnswer > 0) { 28 | rl.close(); // Close the readline interface 29 | resolve(parsedAnswer); // Resolve the promise with the valid number 30 | } else { 31 | console.log('Invalid input. Please enter a number greater than 0.'); 32 | promptUser(); // Ask again 33 | } 34 | }); 35 | }; 36 | promptUser(); // Initial prompt 37 | }); 38 | } 39 | 40 | 41 | // --- Error Handling --- 42 | // (Keep the existing process.on('uncaughtException') and process.on('unhandledRejection') handlers) 43 | // It's generally better to let these log and exit, trying to interact with TUI here can be unreliable. 44 | process.on('uncaughtException', (error) => { 45 | console.error('\nFATAL UNCAUGHT EXCEPTION:', error); 46 | // Attempt to clean up screen if possible, otherwise just exit 47 | try { 48 | // Check if screen exists and destroy it (screen is defined in tui.js scope) 49 | // This might not work reliably if the error happened before tui initialization 50 | if (typeof screen !== 'undefined' && screen && screen.destroy) { 51 | screen.destroy(); 52 | console.error('Attempted TUI cleanup.'); 53 | } 54 | } catch (e) { 55 | console.error("Error destroying screen during exception:", e); 56 | } finally { 57 | process.exit(1); // Exit after attempting cleanup 58 | } 59 | }); 60 | 61 | process.on('unhandledRejection', (reason, promise) => { 62 | console.error('\nUNHANDLED REJECTION:'); 63 | console.error('Reason:', reason); 64 | // Avoid interacting with TUI here, just log 65 | if (emitter) { 66 | // Maybe emit a simple console log event if needed elsewhere 67 | // emitter.emit('log', { level: 'ERROR', message: `UNHANDLED REJECTION: ${reason}` }); 68 | } 69 | // Decide if you want to exit or just log 70 | // process.exit(1); 71 | }); 72 | 73 | 74 | // --- Main Application Flow --- 75 | (async () => { 76 | try { 77 | // 1. Get the number of repetitions from the user first 78 | console.log("Starting Rise Testnet Bot..."); 79 | const repetitions = await askForRepetitions(); 80 | // Clear the console before starting TUI (optional) 81 | // console.clear(); // Uncomment if desired 82 | console.log(`\nOkay, running ${repetitions} tasks per wallet. Initializing TUI...`); 83 | 84 | // 2. Initialize the TUI 85 | // This sets up the screen and attaches listeners to the *shared emitter* 86 | initializeTui(); 87 | // *** Use the shared emitter *** 88 | emitter.emit('log', { level: 'INFO', message: 'TUI Initialized.' }); 89 | emitter.emit('log', { level: 'INFO', message: `Actions per wallet set to: ${repetitions}` }); 90 | 91 | // 3. Initialize the Bot Logic (load wallets, etc.) 92 | // This will also emit logs to the shared emitter for the TUI to display 93 | const initSuccess = await initializeBot(); 94 | 95 | if (initSuccess) { 96 | // 4. If initialization succeeded, start the simulation loop 97 | // Pass the shared emitter if needed, although runSimulation uses the require internally now 98 | await runSimulation(repetitions); // runSimulation now handles its own interval/stop logic 99 | } else { 100 | emitter.emit('log', { level: 'ERROR', message: 'Bot initialization failed. Cannot start simulation.' }); 101 | // Keep TUI running to show the error. User can exit with Ctrl+C. 102 | } 103 | } catch (error) { 104 | // Catch errors from askForRepetitions or other synchronous parts 105 | console.error("\nError during startup:", error); 106 | // Ensure TUI is cleaned up if it was initialized 107 | try { 108 | if (typeof screen !== 'undefined' && screen && screen.destroy) { 109 | screen.destroy(); 110 | } 111 | } catch (e) { 112 | // Ignore cleanup errors 113 | } 114 | process.exit(1); 115 | } 116 | })(); // Execute the async function immediately 117 | 118 | // Application runs: prompts -> initializes TUI -> initializes bot -> runs simulation. 119 | // TUI handles Ctrl+C for graceful exit via the 'stop' event. 120 | // Created by crypto with shashi 121 | -------------------------------------------------------------------------------- /tui.js: -------------------------------------------------------------------------------- 1 | // tui.js 2 | // Implements the Blessed Terminal User Interface (TUI). 3 | // Created by crypto with shashi 4 | 5 | const blessed = require('blessed'); 6 | const bannerText = require('./banner'); 7 | // *** CHANGE: Require the shared emitter instance *** 8 | const emitter = require('./emitter'); 9 | // *** CHANGE: Only require necessary function(s) from bot.js *** 10 | const { getCurrentStatusInfo } = require('./bot'); 11 | 12 | // --- TUI Elements --- 13 | // (Keep screen, bannerBox, mainLogBox, successLogBox, statusBox declarations) 14 | let screen; 15 | let bannerBox; 16 | let mainLogBox; 17 | let successLogBox; 18 | let statusBox; 19 | 20 | // --- Helper Functions --- 21 | // (Keep getTimestamp, getLogStyle, addLogMessage functions as they were in your version) 22 | /** 23 | * Gets the current timestamp in HH:MM:SS format. 24 | * @returns {string} Formatted timestamp. 25 | */ 26 | const getTimestamp = () => { 27 | // Use 'en-GB' for HH:MM:SS format consistently 28 | return new Date().toLocaleTimeString('en-GB'); 29 | }; 30 | 31 | /** 32 | * Maps log levels to icons and colors. 33 | * @param {string} level - The log level (INFO, WARN, ERROR, SUCCESS, WAIT). 34 | * @returns {object} Object containing icon and color tag. 35 | */ 36 | const getLogStyle = (level) => { 37 | switch (level.toUpperCase()) { 38 | case 'INFO': return { icon: 'ℹ️ ', colorTag: '{blue-fg}' }; // Informational 39 | case 'SUCCESS': return { icon: '✅', colorTag: '{green-fg}' }; // Success 40 | case 'WARN': return { icon: '⚠️ ', colorTag: '{yellow-fg}' }; // Warning 41 | case 'ERROR': return { icon: '🚨', colorTag: '{red-fg}' }; // Error 42 | case 'WAIT': return { icon: '⌛', colorTag: '{magenta-fg}' }; // Waiting/Pending 43 | default: return { icon: '➡️ ', colorTag: '{white-fg}' }; // Default 44 | } 45 | }; 46 | 47 | /** 48 | * Adds a log message to a Blessed box, handling scrolling. 49 | * @param {blessed.Widgets.Log} box - The Blessed Log widget. 50 | * @param {string} message - The message to add. 51 | */ 52 | const addLogMessage = (box, message) => { 53 | if (!box || !screen) return; // Ensure elements exist 54 | box.log(message); // Use log method which handles scrolling 55 | screen.render(); // Re-render the screen after update 56 | }; 57 | 58 | 59 | // --- TUI Initialization --- 60 | const initializeTui = () => { 61 | // Create a screen object. 62 | screen = blessed.screen({ 63 | smartCSR: true, 64 | title: 'Rise Testnet JS Bot', 65 | fullUnicode: true, 66 | dockBorders: true, 67 | autoPadding: true 68 | }); 69 | 70 | // --- Box 1: Top Banner --- 71 | // (Keep bannerBox definition as in your version) 72 | bannerBox = blessed.box({ 73 | parent: screen, 74 | top: 0, 75 | left: 0, 76 | width: '100%', 77 | height: 3, // Minimal height for one line + borders 78 | content: `{center}{bold}${bannerText}{/bold}{/center}`, 79 | tags: true, 80 | border: { 81 | type: 'line' 82 | }, 83 | style: { 84 | fg: 'white', 85 | border: { 86 | fg: 'blue' 87 | }, 88 | bold: true 89 | } 90 | }); 91 | 92 | // --- Box 2: Main Log Area --- 93 | // (Keep mainLogBox definition as in your version, ensure height is correct) 94 | mainLogBox = blessed.log({ // Use Log widget for automatic scrolling 95 | parent: screen, 96 | label: ' Main Log ', // Label appears on the border 97 | top: 3, // Below banner 98 | left: 0, 99 | width: '65%', 100 | // *** Ensure height calculation is correct, e.g., 100% minus banner height *** 101 | height: '100%-3', 102 | tags: true, 103 | border: { 104 | type: 'line' 105 | }, 106 | style: { 107 | fg: 'white', 108 | border: { 109 | fg: 'cyan' 110 | }, 111 | label: { // Style the label 112 | fg: 'cyan', 113 | bold: true 114 | } 115 | }, 116 | scrollable: true, 117 | alwaysScroll: true, // Scroll automatically on new content 118 | scrollbar: { // Add a scrollbar 119 | ch: ' ', 120 | track: { bg: 'grey' }, 121 | style: { bg: 'cyan' } 122 | }, 123 | mouse: true, // Enable mouse interaction (scrolling) 124 | keys: true, // Enable key interaction (scrolling) 125 | vi: true // Use vi keys for scrolling (j/k) 126 | }); 127 | 128 | 129 | // --- Box 3: Success Log Area --- 130 | // (Keep successLogBox definition as in your version) 131 | successLogBox = blessed.log({ 132 | parent: screen, 133 | label: ' Success Events ', 134 | top: 3, // Below banner 135 | right: 0, // Align to the right 136 | width: '35%', // Remaining width 137 | // Adjusted height: 50% of the space below the banner 138 | height: '50%-3', 139 | tags: true, 140 | border: { 141 | type: 'line' 142 | }, 143 | style: { 144 | fg: 'white', 145 | border: { 146 | fg: 'green' 147 | }, 148 | label: { 149 | fg: 'green', 150 | bold: true 151 | } 152 | }, 153 | scrollable: true, 154 | alwaysScroll: true, 155 | scrollbar: { 156 | ch: ' ', 157 | track: { bg: 'grey' }, 158 | style: { bg: 'green' } 159 | }, 160 | mouse: true, 161 | keys: true, 162 | vi: true 163 | }); 164 | 165 | // --- Box 4: Status / Info Area --- 166 | // (Keep statusBox definition as in your version) 167 | statusBox = blessed.box({ // Use Box, content will be overwritten 168 | parent: screen, 169 | label: ' Status Info ', 170 | // *** Using top and bottom for positioning *** 171 | top: '50%', // Start at 50% down 172 | bottom: 0, // Go all the way to the bottom edge of the screen 173 | right: 0, 174 | width: '35%', // Same width as Success Log 175 | tags: true, 176 | border: { 177 | type: 'line' 178 | }, 179 | style: { 180 | fg: 'white', 181 | border: { 182 | fg: 'yellow' 183 | }, 184 | label: { 185 | fg: 'yellow', 186 | bold: true 187 | } 188 | }, 189 | scrollable: true, // Keep scrollable in case content overflows 190 | alwaysScroll: true, 191 | scrollbar: { 192 | ch: ' ', 193 | track: { bg: 'grey' }, 194 | style: { bg: 'yellow' } 195 | }, 196 | mouse: true, 197 | keys: true, 198 | vi: true 199 | }); 200 | 201 | 202 | // --- Event Listeners --- 203 | 204 | // Listener for general logs 205 | emitter.on('log', ({ level, message }) => { 206 | if (!mainLogBox) return; 207 | const { icon, colorTag } = getLogStyle(level); 208 | const formattedMessage = `[${getTimestamp()}] ${colorTag}${icon}${message}{/}`; 209 | addLogMessage(mainLogBox, formattedMessage); // addLogMessage now calls screen.render() 210 | }); 211 | 212 | // Listener for success-only logs 213 | emitter.on('success', (message) => { 214 | if (!successLogBox) return; 215 | const { icon, colorTag } = getLogStyle('SUCCESS'); 216 | const formattedMessage = `[${getTimestamp()}] ${colorTag}${icon}${message}{/}`; 217 | addLogMessage(successLogBox, formattedMessage); // addLogMessage now calls screen.render() 218 | }); 219 | 220 | // Listener for status updates 221 | emitter.on('statusUpdate', (info) => { 222 | if (!statusBox || !screen) return; 223 | // *** Make sure info properties exist before accessing *** 224 | const { wallets = [], mails = [], status = 'N/A' } = info || {}; 225 | 226 | let content = `{bold}--- Status: ${status} ---{/bold}\n\n`; 227 | content += `{bold}Wallets Loaded (${wallets.length}):{/bold}\n`; 228 | if (wallets && wallets.length > 0) { 229 | wallets.forEach(w => content += ` - ${w.id || 'N/A'}: ${w.maskedKey || 'N/A'}\n`); 230 | } else { 231 | content += ` (No wallets loaded)\n`; 232 | } 233 | content += `\n{bold}Mails Loaded (${mails.length}):{/bold}\n`; 234 | if (mails && mails.length > 0) { 235 | mails.forEach(m => content += ` - ${m.id || 'N/A'}: ${m.maskedEmail || 'N/A'}\n`); 236 | } else { 237 | content += ` (No mails loaded)\n`; 238 | } 239 | 240 | statusBox.setContent(content); 241 | screen.render(); // Render after updating content 242 | }); 243 | 244 | // --- Keybindings --- 245 | // (Keep screen.key(['escape', 'q', 'C-c']) handler as in your version) 246 | screen.key(['escape', 'q', 'C-c'], (ch, key) => { 247 | // *** Emit 'stop' to signal the simulation loop to clear its interval *** 248 | emitter.emit('stop'); 249 | emitter.emit('log', { level: 'INFO', message: 'Exit requested. Cleaning up...' }); 250 | 251 | // Give a brief moment for the stop event to be processed before destroying 252 | setTimeout(() => { 253 | if (screen) { 254 | screen.destroy(); 255 | } 256 | console.log('TUI closed gracefully.'); 257 | process.exit(0); 258 | }, 100); // 100ms delay 259 | }); 260 | 261 | 262 | // --- Initial Rendering --- 263 | // *** Use the imported function to get initial status *** 264 | const initialStatus = getCurrentStatusInfo(); 265 | // *** Emit status update AFTER attaching listener *** 266 | emitter.emit('statusUpdate', initialStatus); 267 | 268 | screen.render(); 269 | mainLogBox.focus(); 270 | 271 | // Handle terminal resize 272 | screen.on('resize', () => { 273 | // Re-emit status to potentially redraw elements correctly 274 | emitter.emit('statusUpdate', getCurrentStatusInfo()); 275 | screen.render(); 276 | }); 277 | }; 278 | 279 | // Export the initialization function 280 | module.exports = { initializeTui }; 281 | 282 | -------------------------------------------------------------------------------- /bot.js: -------------------------------------------------------------------------------- 1 | // bot.js 2 | // Contains the core bot logic, reads input files, 3 | // manages state, and emits events for the TUI. 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const emitter = require('./emitter'); // Use the shared emitter 8 | 9 | // --- Configuration --- 10 | const PVKEY_FILE = path.join(__dirname, 'pvkey.txt'); 11 | const MAIL_FILE = path.join(__dirname, 'mail.txt'); 12 | // Add other file paths if needed 13 | 14 | // --- State --- 15 | let wallets = []; 16 | let mails = []; 17 | let status = 'Initializing...'; 18 | let simulationInterval = null; // To hold the interval ID for stopping 19 | 20 | // --- Task Definitions --- 21 | // Define the tasks to cycle through during simulation 22 | // Names should correspond to your script files/actions 23 | const SIMULATED_TASKS = [ 24 | { name: 'Send TX', script: 'sendTx.js' }, // Example mapping 25 | { name: 'Deploy Token', script: 'deployToken.js' }, 26 | { name: 'Send Token', script: 'sendToken.js' }, 27 | { name: 'NFT Collection', script: 'nftCollection.js' }, 28 | { name: 'GasPump Swap', script: 'gasPump.js' }, 29 | { name: 'Clober Swap', script: 'clober.js' }, 30 | { name: 'Inari Finance', script: 'inari.js' }, 31 | { name: 'WL GTX Dex', script: 'wlGtx.js' }, 32 | { name: 'WL Novadubs', script: 'wlNovadubs.js' }, 33 | ]; 34 | 35 | 36 | // --- Helper Functions --- 37 | // (readFileLines, maskString, loadWallets, loadMails, updateStatus, getCurrentStatusInfo functions remain the same as the previous version) 38 | /** 39 | * Reads lines from a file asynchronously. 40 | * @param {string} filePath - The path to the file. 41 | * @returns {Promise} A promise that resolves with an array of lines. 42 | */ 43 | const readFileLines = async (filePath) => { 44 | try { 45 | const content = await fs.promises.readFile(filePath, 'utf8'); 46 | return content.split(/\r?\n/).filter(line => line.trim() !== ''); 47 | } catch (error) { 48 | if (error.code === 'ENOENT') { 49 | emitter.emit('log', { level: 'WARN', message: `File not found: ${filePath}. Required for operation.` }); 50 | if (filePath === PVKEY_FILE || filePath === MAIL_FILE) { 51 | throw new Error(`Critical file missing: ${path.basename(filePath)}`); 52 | } 53 | return []; 54 | } 55 | emitter.emit('log', { level: 'ERROR', message: `Error reading file ${filePath}: ${error.message}` }); 56 | throw error; 57 | } 58 | }; 59 | 60 | /** 61 | * Masks a sensitive string. 62 | * @param {string} str - The string to mask. 63 | * @param {number} visibleCharsStart - Number of characters visible at the start. 64 | * @param {number} visibleCharsEnd - Number of characters visible at the end. 65 | * @returns {string} The masked string. 66 | */ 67 | const maskString = (str, visibleCharsStart = 4, visibleCharsEnd = 4) => { 68 | if (!str || str.length <= visibleCharsStart + visibleCharsEnd) { 69 | return str || ''; 70 | } 71 | const start = str.substring(0, visibleCharsStart); 72 | const end = str.substring(str.length - visibleCharsEnd); 73 | return `${start}...${end}`; 74 | }; 75 | 76 | /** 77 | * Loads wallets from pvkey.txt. Throws error if file is missing/empty. 78 | */ 79 | const loadWallets = async () => { 80 | const keys = await readFileLines(PVKEY_FILE); 81 | if (keys.length === 0) { 82 | throw new Error(`No private keys found in ${PVKEY_FILE}. Cannot operate.`); 83 | } 84 | wallets = keys.map((key, index) => ({ 85 | id: `Wallet ${index + 1}`, 86 | key: key, 87 | maskedKey: maskString(key, 6, 4) 88 | })); 89 | emitter.emit('log', { level: 'INFO', message: `Loaded ${wallets.length} wallets.` }); 90 | }; 91 | 92 | /** 93 | * Loads email addresses from mail.txt. 94 | */ 95 | const loadMails = async () => { 96 | try { 97 | const lines = await readFileLines(MAIL_FILE); 98 | if (lines.length === 0) { 99 | emitter.emit('log', { level: 'WARN', message: `${MAIL_FILE} is empty or not found. Mail-related features may be limited.` }); 100 | mails = []; 101 | return; 102 | } 103 | mails = lines.map((line, index) => { 104 | const parts = line.split(':'); 105 | const email = parts.length > 1 ? parts[1].trim() : line.trim(); 106 | const id = parts.length > 1 ? parts[0].trim() : `Mail ${index + 1}`; 107 | return { 108 | id: id, 109 | email: email, 110 | maskedEmail: maskString(email, 4, 10) 111 | }; 112 | }); 113 | emitter.emit('log', { level: 'INFO', message: `Loaded ${mails.length} mails.` }); 114 | } catch (error) { 115 | emitter.emit('log', { level: 'ERROR', message: `Failed to load mails: ${error.message}` }); 116 | mails = []; 117 | } 118 | }; 119 | 120 | /** 121 | * Updates the overall status and emits an event. 122 | * @param {string} newStatus - The new status message. 123 | */ 124 | const updateStatus = (newStatus) => { 125 | status = newStatus; 126 | emitter.emit('statusUpdate', getCurrentStatusInfo()); 127 | }; 128 | 129 | /** 130 | * Gets the current status information object. 131 | * @returns {object} Status information payload. 132 | */ 133 | const getCurrentStatusInfo = () => ({ 134 | wallets: wallets.map(w => ({ id: w.id, maskedKey: w.maskedKey })), 135 | mails: mails.map(m => ({ id: m.id, maskedEmail: m.maskedEmail })), 136 | status: status 137 | }); 138 | 139 | 140 | // --- Core Bot Logic --- 141 | 142 | /** 143 | * Initializes the bot by loading data. 144 | * Returns true on success, false on failure. 145 | */ 146 | const initializeBot = async () => { 147 | emitter.emit('log', { level: 'INFO', message: 'Bot initializing...' }); 148 | try { 149 | updateStatus('Loading Wallets...'); 150 | await loadWallets(); 151 | 152 | updateStatus('Loading Mails...'); 153 | await loadMails(); 154 | 155 | updateStatus('Idle'); 156 | emitter.emit('log', { level: 'INFO', message: 'Bot initialized successfully.' }); 157 | return true; 158 | 159 | } catch (error) { 160 | emitter.emit('log', { level: 'ERROR', message: `Initialization failed: ${error.message}` }); 161 | updateStatus('Initialization Error'); 162 | return false; 163 | } 164 | }; 165 | 166 | /** 167 | * Runs the main simulation/task loop, cycling through defined tasks. 168 | * @param {number} repetitions - How many times to repeat tasks *in total* per wallet. 169 | */ 170 | const runSimulation = async (repetitions) => { 171 | if (wallets.length === 0) { 172 | emitter.emit('log', { level: 'ERROR', message: 'No wallets loaded. Cannot start simulation.' }); 173 | updateStatus('Error - No Wallets'); 174 | return; 175 | } 176 | if (SIMULATED_TASKS.length === 0) { 177 | emitter.emit('log', { level: 'ERROR', message: 'No simulated tasks defined. Cannot start simulation.' }); 178 | updateStatus('Error - No Tasks'); 179 | return; 180 | } 181 | 182 | updateStatus('Running'); 183 | emitter.emit('log', { level: 'INFO', message: `Starting simulation (${repetitions} tasks per wallet)...` }); 184 | 185 | let walletIndex = 0; 186 | let repetitionCounter = 0; // Counts total tasks done for the current wallet 187 | let overallTaskIndex = 0; // Used to cycle through SIMULATED_TASKS 188 | 189 | // Clear any previous interval 190 | if (simulationInterval) { 191 | clearInterval(simulationInterval); 192 | } 193 | 194 | simulationInterval = setInterval(async () => { 195 | const currentWallet = wallets[walletIndex]; 196 | repetitionCounter++; 197 | 198 | // Determine which task to simulate based on the overall index 199 | const taskIndex = overallTaskIndex % SIMULATED_TASKS.length; 200 | const currentTask = SIMULATED_TASKS[taskIndex]; 201 | overallTaskIndex++; // Increment for the next cycle 202 | 203 | // *** CHANGE: Log includes task name and repetition count *** 204 | emitter.emit('log', { level: 'INFO', message: `Simulating ${currentTask.name} (${repetitionCounter}/${repetitions}) for ${currentWallet.id}...` }); 205 | 206 | // Simulate different actions (replace with actual script calls later) 207 | // You could eventually call the actual script function: 208 | // const scriptFunction = require(`./scripts/${currentTask.script}`).runFunction; // Assuming each script exports its main function 209 | // await scriptFunction( /* parameters */ ); 210 | const rand = Math.random(); 211 | if (rand < 0.7) { 212 | emitter.emit('log', { level: 'WAIT', message: `${currentTask.name} (${currentWallet.id}): Waiting for result...` }); 213 | await new Promise(resolve => setTimeout(resolve, 1500 + Math.random() * 1000)); // Simulate delay 214 | emitter.emit('success', `${currentTask.name} (${currentWallet.id}) completed.`); 215 | } else if (rand < 0.9) { 216 | emitter.emit('log', { level: 'WARN', message: `${currentTask.name} (${currentWallet.id}): Minor issue detected.` }); 217 | } else { 218 | emitter.emit('log', { level: 'ERROR', message: `${currentTask.name} (${currentWallet.id}): Failed!` }); 219 | } 220 | 221 | // Check if repetitions for the current wallet are complete 222 | if (repetitionCounter >= repetitions) { 223 | repetitionCounter = 0; // Reset counter for the next wallet 224 | walletIndex++; // Move to the next wallet 225 | 226 | // Loop back to the first wallet if we've gone through all of them 227 | if (walletIndex >= wallets.length) { 228 | walletIndex = 0; 229 | emitter.emit('log', { level: 'INFO', message: 'Completed a full cycle through all wallets. Repeating...' }); 230 | } 231 | emitter.emit('log', { level: 'INFO', message: `Moving to ${wallets[walletIndex].id}` }); 232 | } 233 | 234 | }, 3000); // Interval between individual tasks 235 | 236 | // Listen for the stop event from TUI 237 | emitter.once('stop', () => { 238 | if (simulationInterval) { 239 | clearInterval(simulationInterval); 240 | simulationInterval = null; 241 | emitter.emit('log', { level: 'INFO', message: 'Simulation stopped by user.' }); 242 | updateStatus('Stopped'); 243 | } 244 | }); 245 | }; 246 | 247 | // --- Exports --- 248 | module.exports = { 249 | initializeBot, 250 | runSimulation, 251 | getCurrentStatusInfo 252 | }; 253 | 254 | // created by crypto with shashi 255 | --------------------------------------------------------------------------------