324 lines
10 KiB
JavaScript
324 lines
10 KiB
JavaScript
import { execSync, spawn } from 'child_process';
|
|
import { publicIpv4 } from 'public-ip';
|
|
import dotenv from 'dotenv';
|
|
import fs from 'fs';
|
|
import os from 'os';
|
|
|
|
const host = os.hostname();
|
|
const args = process.argv.slice(2);
|
|
|
|
const reportWebhook = process.env.REPORT_WEBHOOK;
|
|
const botConsoleWebhook = process.env.BOT_CONSOLE_WEBHOOK;
|
|
const botConsoleWebhookName = process.env.BOT_CONSOLE_WEBHOOK_NAME || 'Microsoft Rewards Bot';
|
|
const managerWebhook = process.env.MANAGER_WEBHOOK;
|
|
const managerWebhookName = process.env.MANAGER_WEBHOOK_NAME || 'Microsoft Rewards Bot Manager';
|
|
|
|
async function logManager(message, type) {
|
|
if (type === 'info') console.log(`\x1b[34m${message}\x1b[0m`);
|
|
else if (type === 'error') console.log(`\x1b[31m${message}\x1b[0m`);
|
|
else console.log(`\x1b[37m${message}\x1b[0m`);
|
|
|
|
if (managerWebhook) {
|
|
const payload = {
|
|
content: message,
|
|
username: managerWebhookName,
|
|
};
|
|
|
|
try {
|
|
await fetch(managerWebhook, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
catch (error) {
|
|
console.error(`[${host}] Error sending webhook message: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function logBotConsole(message) {
|
|
console.log(message);
|
|
|
|
if (botConsoleWebhook) {
|
|
const payload = {
|
|
content: message,
|
|
username: botConsoleWebhookName,
|
|
};
|
|
|
|
try {
|
|
await fetch(botConsoleWebhook, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
catch (error) {
|
|
console.error(`[${host}] Error sending webhook message: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function checkUpdate() {
|
|
try {
|
|
execSync('git fetch');
|
|
const currentHead = execSync('git rev-parse HEAD').toString().trim();
|
|
const remoteHead = execSync('git rev-parse "@{u}"').toString().trim();
|
|
if (currentHead !== remoteHead) {
|
|
logManager(`[${host}] Updates available! Please run the updater script.`, 'info');
|
|
}
|
|
}
|
|
catch (error) {
|
|
logManager('[${host}] Failed to check for updates.', 'error');
|
|
}
|
|
}
|
|
|
|
async function vpnConnect(vpnName) {
|
|
if (!vpnName) return logManager(`[${host}] Please provide the VPN name as an argument.`, 'error');
|
|
|
|
logManager(`[${host}] Disconnecting from VPNs`, 'info');
|
|
await vpnDisconnect();
|
|
|
|
const maxAttempts = process.env.RETRIES;
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
await execSync(`nmcli connection up "${vpnName}"`);
|
|
|
|
const status = await execSync('nmcli connection show --active').toString().includes(vpnName);
|
|
if (!status) {
|
|
logManager(`[${host}] Failed to connect to VPN: ${vpnName}. Retrying (attempt ${attempt} of ${maxAttempts})...`, 'error');
|
|
await sleep(1000);
|
|
}
|
|
else {
|
|
await logManager(`[${host}] VPN connection successfully established to ${vpnName} (IP: ${await publicIpv4() || 'Unknown'}).`, 'info');
|
|
return 0;
|
|
}
|
|
}
|
|
logManager(`[${host}] Maximum number of connection attempts reached. Failed to connect to VPN: ${vpnName}`, 'error');
|
|
await vpnDisconnect();
|
|
return 1;
|
|
}
|
|
|
|
function vpnDisconnect() {
|
|
const vpnConnection = execSync('nmcli connection show --active | awk \'/vpn/ {print $1}\'').toString().trim();
|
|
if (!vpnConnection) {
|
|
logManager(`[${host}] Successfully disconnected from all VPNs.`, 'info');
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
execSync(`nmcli connection down "${vpnConnection}"`);
|
|
const status = execSync('nmcli connection show --active | awk \'/vpn/ {print $1}\'').toString().trim();
|
|
if (!status) {
|
|
logManager(`[${host}] Successfully disconnected from all VPNs.`, 'info');
|
|
return 0;
|
|
}
|
|
else {
|
|
logManager(`[${host}] Failed to disconnect from all VPNs.`, 'error');
|
|
return 1;
|
|
}
|
|
}
|
|
catch (error) {
|
|
logManager(`[${host}] Failed to disconnect from all VPNs.`, 'error');
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
async function startBot(accountName) {
|
|
const accountPath = `./accounts/${accountName}.json`;
|
|
if (fs.existsSync(accountPath)) {
|
|
let commandSuffix = '';
|
|
|
|
if (reportWebhook === '1') {
|
|
commandSuffix += ` --discord ${reportWebhook}`;
|
|
}
|
|
|
|
if (process.env.BOT_BROWSER) {
|
|
commandSuffix += ' --browser ' + process.env.BOT_BROWSER;
|
|
}
|
|
|
|
let commandPrefix = `python -u ./Microsoft-Rewards-bot/ms_rewards_farmer.py --accounts-file ../accounts/${accountName}.json --dont-check-for-updates --shuffle --session --superfast --on-finish exit --no-webdriver-manager --skip-unusual`;
|
|
logManager(`[${host}] Script started for ${accountName}`);
|
|
|
|
if (isRootUser()) {
|
|
const containerEnv = fs.readFileSync('/proc/1/environ', 'utf8');
|
|
const isLXCContainer = containerEnv.includes('container=lxc') || containerEnv.includes('container=lxc-libvirt');
|
|
if (isLXCContainer) commandPrefix += ' --virtual-display';
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const childProcess = spawn('bash', ['-c', `${commandPrefix} ${commandSuffix}`], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
|
|
childProcess.stdout.on('data', (data) => {
|
|
const output = data.toString().trim();
|
|
logBotConsole(`[${host}][${accountName}] STDOUT: ${output}`);
|
|
if (output.includes('Press enter') || output.includes('Press any key')) {
|
|
setTimeout(() => { childProcess.stdin.write('\n'); }, 1000);
|
|
}
|
|
});
|
|
|
|
childProcess.stderr.on('data', (data) => {
|
|
const output = data.toString().trim();
|
|
logBotConsole(`[${host}][${accountName}] STDERR: ${output}`);
|
|
if (output.includes('Press enter') || output.includes('Press any key')) {
|
|
setTimeout(() => { childProcess.stdin.write('\n'); }, 1000);
|
|
}
|
|
});
|
|
|
|
childProcess.on('exit', (code) => {
|
|
if (code !== 0) {
|
|
logManager(`[${host}] Bot process for ${accountName} exited with code ${code}. Restarting...`, 'error');
|
|
startBot(accountName).then(resolve).catch(reject);
|
|
}
|
|
else {
|
|
const currentDate = new Date();
|
|
const formattedDate = `${currentDate.getDate()}-${currentDate.getMonth() + 1}-${currentDate.getFullYear()}`;
|
|
const logEntry = { accountName: accountName, date: formattedDate };
|
|
|
|
fs.readFile('batch_logs.json', 'utf8', (err, data) => {
|
|
if (err) {
|
|
logManager(`Failed to read batch_logs.json: ${err}`, 'error');
|
|
return resolve();
|
|
}
|
|
|
|
let logEntries = [];
|
|
try {
|
|
logEntries = JSON.parse(data);
|
|
}
|
|
catch (parseError) {
|
|
logManager(`Failed to parse batch_logs.json: ${parseError}`, 'error');
|
|
}
|
|
|
|
const existingEntryIndex = logEntries.findIndex(entry => entry.accountName === accountName);
|
|
if (existingEntryIndex !== -1) logEntries[existingEntryIndex] = logEntry;
|
|
else logEntries.push(logEntry);
|
|
|
|
fs.writeFile('batch_logs.json', JSON.stringify(logEntries, null, 2), err => {
|
|
if (err) logManager(`Failed to write to batch_logs.json: ${err}`, 'error');
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
childProcess.on('close', (code) => {
|
|
logManager(`[${accountName}] Bot process exited with code ${code}`);
|
|
});
|
|
|
|
childProcess.on('error', (err) => {
|
|
logManager(`[${host}] Failed to start bot for ${accountName}.`, 'error');
|
|
reject(err);
|
|
});
|
|
|
|
setTimeout(() => {
|
|
logManager(`[${host}] Bot process for ${accountName} exceeded the timeout. Killing the process...`, 'error');
|
|
childProcess.kill();
|
|
}, 150 * 60 * 1000);
|
|
});
|
|
}
|
|
else {
|
|
logManager(`[${host}] File ${accountPath} does not exist, skipping starting bot for this VPN!`, 'error');
|
|
return Promise.resolve({ error: `File ${accountPath} does not exist.` });
|
|
}
|
|
}
|
|
|
|
function sleep(ms) {
|
|
return new Promise((resolve) => {
|
|
setTimeout(resolve, ms);
|
|
});
|
|
}
|
|
|
|
function isRootUser() {
|
|
return process.getuid && process.getuid() === 0;
|
|
}
|
|
|
|
async function runAll() {
|
|
await checkUpdate();
|
|
const vpns = execSync('nmcli connection show | awk \'/vpn/ {print $1}\'').toString().trim().split('\n');
|
|
|
|
for (const vpn of vpns) {
|
|
const currentDate = new Date();
|
|
const formattedDate = `${currentDate.getDate()}-${currentDate.getMonth() + 1}-${currentDate.getFullYear()}`;
|
|
const logEntries = JSON.parse(fs.readFileSync('batch_logs.json', 'utf8'));
|
|
const entry = logEntries.find(entries => entries.accountName === vpn) || false;
|
|
if (entry && entry.date === formattedDate) {
|
|
logManager(`[${host}] ${vpn} already ran today!`, 'info');
|
|
continue;
|
|
}
|
|
logManager(`[${host}] Switching to VPN: [${vpn}]`, 'info');
|
|
const con = await vpnConnect(vpn);
|
|
if (con) continue;
|
|
await startBot(vpn);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
async function runBatch(vpn) {
|
|
logManager(`[${host}] Connecting to VPN: [${vpn}]`, 'info');
|
|
const con = await vpnConnect(vpn);
|
|
if (con) return 1;
|
|
await startBot(vpn);
|
|
return 0;
|
|
}
|
|
|
|
(async () => {
|
|
await checkUpdate();
|
|
if (fs.existsSync('.env')) {
|
|
dotenv.config({ path: '.env' });
|
|
logManager(`[${host}] Config file: .env.`, 'info');
|
|
}
|
|
else {
|
|
logManager(`[${host}] Config file: not found.`, 'error');
|
|
process.exit(1);
|
|
}
|
|
|
|
logManager(`[${host}] Bot Console Webhook: ${botConsoleWebhook ? 'True' : 'False'}\n[${host}] Bot Report Webhook: ${reportWebhook ? 'True' : 'False'}\n[${host}] Bot Manager Webhook: ${managerWebhook ? 'True' : 'False'}`);
|
|
logManager(`[${host}] Starting mcr-bot on host: ${host}`, 'info');
|
|
|
|
if (!fs.existsSync('batch_logs.json')) {
|
|
fs.writeFile('batch_logs.json', '[]', err => {
|
|
if (err) {
|
|
logManager(`Failed to create batch_logs.json: ${err}`, 'error');
|
|
process.exit(1);
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
const fileContent = await fs.readFile('batch_logs.json', 'utf-8');
|
|
try {
|
|
JSON.parse(fileContent);
|
|
}
|
|
catch (error) {
|
|
logManager(`[${host}] Existing log file is not in proper JSON format: ${error}`, 'error');
|
|
process.exit(1);
|
|
}
|
|
}
|
|
logManager(`[${host}] Log file: batch_logs.json`, 'info');
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
if (args[i].startsWith('--')) {
|
|
if (args[i] === '--accounts' && i + 1 < args.length) {
|
|
const accounts = args[i + 1];
|
|
return await runBatch(accounts);
|
|
}
|
|
else if (args[i] === '--all') {
|
|
const n = true;
|
|
while (n) {
|
|
try {
|
|
await runAll();
|
|
await sleep(1000);
|
|
}
|
|
catch (error) {
|
|
logManager(`[${host}] An error occurred: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
logManager(`[${host}] Missing arguments`);
|
|
})(); |