mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-09 17:06:15 +00:00
feat: Enhance update mechanism with anti-loop protection and improved logging
- Implemented a restart counter to prevent infinite update loops. - Added checks for update success using marker files. - Improved logging for update attempts and failures. - Created comprehensive documentation for npm commands and setup processes. - Introduced a new update system using GitHub API for seamless updates. - Added troubleshooting guidelines for common issues.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,3 +17,7 @@ accounts.main.jsonc
|
|||||||
.playwright-chromium-installed
|
.playwright-chromium-installed
|
||||||
*.log
|
*.log
|
||||||
.update-backup/
|
.update-backup/
|
||||||
|
.update-download.zip
|
||||||
|
.update-extract/
|
||||||
|
.update-happened
|
||||||
|
.update-restart-count
|
||||||
|
|||||||
362
docs/commands.md
Normal file
362
docs/commands.md
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
# NPM Commands Reference
|
||||||
|
|
||||||
|
This guide explains all available npm commands and when to use them.
|
||||||
|
|
||||||
|
## 🚀 Essential Commands
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
**Start the bot** - Use this to run the Microsoft Rewards Bot after setup.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Runs the compiled JavaScript from `dist/index.js`
|
||||||
|
- Checks for automatic updates (if enabled)
|
||||||
|
- Executes reward earning tasks
|
||||||
|
- Fastest way to run the bot
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Daily bot execution
|
||||||
|
- After setup is complete
|
||||||
|
- When you want to earn points
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run setup`
|
||||||
|
**First-time installation** - Run this only once when setting up the project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run setup
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
1. Creates `accounts.jsonc` from template
|
||||||
|
2. Guides you through account configuration
|
||||||
|
3. Installs dependencies (`npm install`)
|
||||||
|
4. Builds TypeScript (`npm run build`)
|
||||||
|
5. Installs Playwright browsers
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- First time installing the bot
|
||||||
|
- After fresh git clone
|
||||||
|
- To reconfigure accounts
|
||||||
|
|
||||||
|
**Important:** This does NOT start the bot automatically. After setup, run `npm start`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔨 Development Commands
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
**Build TypeScript to JavaScript** - Compiles the project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Compiles `src/*.ts` files to `dist/*.js`
|
||||||
|
- Generates source maps for debugging
|
||||||
|
- Required before running `npm start`
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- After modifying TypeScript source code
|
||||||
|
- Before starting the bot with `npm start`
|
||||||
|
- After pulling updates from git
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run dev`
|
||||||
|
**Development mode** - Run TypeScript directly without building.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Runs TypeScript files directly with `ts-node`
|
||||||
|
- No build step required
|
||||||
|
- Slower but convenient for development
|
||||||
|
- Includes `-dev` flag for debug features
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- During development/testing
|
||||||
|
- When making code changes
|
||||||
|
- Quick testing without full build
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run ts-start`
|
||||||
|
**TypeScript direct execution** - Like `dev` but without debug flags.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run ts-start
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Alternative to `npm run dev`
|
||||||
|
- Running TypeScript without full build
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧹 Maintenance Commands
|
||||||
|
|
||||||
|
### `npm run clean`
|
||||||
|
**Remove build artifacts** - Deletes the `dist` folder.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run clean
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Before fresh rebuild
|
||||||
|
- To clear stale compiled code
|
||||||
|
- Troubleshooting build issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run install-deps`
|
||||||
|
**Install all dependencies** - Fresh installation of dependencies and browsers.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run install-deps
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Runs `npm install` to install Node.js packages
|
||||||
|
- Installs Playwright Chromium browser
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- After deleting `node_modules`
|
||||||
|
- Setting up on new machine
|
||||||
|
- Troubleshooting dependency issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run typecheck`
|
||||||
|
**Check TypeScript types** - Validates code without building.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Checking for type errors
|
||||||
|
- Before committing code
|
||||||
|
- Part of CI/CD pipeline
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Testing & Quality
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
**Run unit tests** - Execute test suite.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Verifying code changes
|
||||||
|
- Before submitting pull requests
|
||||||
|
- Continuous integration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run lint`
|
||||||
|
**Check code style** - ESLint validation.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Checking code formatting
|
||||||
|
- Before commits
|
||||||
|
- Maintaining code quality
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Dashboard Commands
|
||||||
|
|
||||||
|
### `npm run dashboard`
|
||||||
|
**Start web dashboard only** - Web interface without bot execution.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Launches web interface on http://localhost:3000
|
||||||
|
- Provides monitoring and control panel
|
||||||
|
- Does NOT start reward earning
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Monitoring bot status
|
||||||
|
- Viewing logs remotely
|
||||||
|
- Configuring settings via UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm run dashboard-dev`
|
||||||
|
**Dashboard development mode** - TypeScript version of dashboard.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dashboard-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Dashboard development/testing
|
||||||
|
- Quick dashboard testing without build
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤖 Account Creation
|
||||||
|
|
||||||
|
### `npm run creator`
|
||||||
|
**Account creation wizard** - Create new Microsoft accounts.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run creator
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Creating new Microsoft accounts
|
||||||
|
- Bulk account creation
|
||||||
|
- Testing account setup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker Commands
|
||||||
|
|
||||||
|
### `npm run create-docker`
|
||||||
|
**Build Docker image** - Create containerized version.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run create-docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Deploying with Docker
|
||||||
|
- Creating container image
|
||||||
|
- Testing Docker setup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Troubleshooting Commands
|
||||||
|
|
||||||
|
### `npm run kill-chrome-win` (Windows only)
|
||||||
|
**Force close Chrome browsers** - Kill stuck browser processes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run kill-chrome-win
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Browser processes stuck
|
||||||
|
- Windows only
|
||||||
|
- Before restarting bot
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Command Comparison
|
||||||
|
|
||||||
|
| Command | Speed | Purpose | When to Use |
|
||||||
|
|---------|-------|---------|-------------|
|
||||||
|
| `npm start` | ⚡ Fast | Run bot | Daily use |
|
||||||
|
| `npm run dev` | 🐌 Slow | Development | Code changes |
|
||||||
|
| `npm run build` | ⏱️ Medium | Compile TS | Before start |
|
||||||
|
| `npm run setup` | ⏱️ Medium | First install | Once only |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### First-Time Setup
|
||||||
|
```bash
|
||||||
|
# 1. Run setup wizard
|
||||||
|
npm run setup
|
||||||
|
|
||||||
|
# 2. Start the bot
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Daily Usage
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### After Code Changes
|
||||||
|
```bash
|
||||||
|
# Method 1: Build then run (faster)
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
|
||||||
|
# Method 2: Direct run (slower)
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### After Pulling Updates
|
||||||
|
```bash
|
||||||
|
# If dependencies changed
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Rebuild
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Start bot
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
```bash
|
||||||
|
# Clean install
|
||||||
|
npm run clean
|
||||||
|
rm -rf node_modules package-lock.json
|
||||||
|
npm run install-deps
|
||||||
|
|
||||||
|
# Rebuild
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Test
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ FAQ
|
||||||
|
|
||||||
|
### Why does `npm run start` trigger updates?
|
||||||
|
The bot automatically checks for updates on startup (configurable in `config.jsonc`). To disable:
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"update": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### What's the difference between `npm start` and `npm run start`?
|
||||||
|
**No functional difference** - both run the same command. `npm start` is a shorthand for `npm run start`.
|
||||||
|
|
||||||
|
### Should I use `npm start` or `npm run dev`?
|
||||||
|
- **Production/Daily use:** `npm start` (faster)
|
||||||
|
- **Development:** `npm run dev` (no build needed)
|
||||||
|
|
||||||
|
### How do I completely reset the project?
|
||||||
|
```bash
|
||||||
|
npm run clean
|
||||||
|
rm -rf node_modules package-lock.json dist
|
||||||
|
npm run setup
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Need Help?
|
||||||
|
|
||||||
|
- **Getting Started:** [docs/getting-started.md](getting-started.md)
|
||||||
|
- **Configuration:** [docs/config.md](config.md)
|
||||||
|
- **Troubleshooting:** [docs/troubleshooting.md](troubleshooting.md)
|
||||||
|
- **Discord:** https://discord.gg/k5uHkx9mne
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
"homepage": "https://github.com/Obsidian-wtf/Microsoft-Rewards-Bot#readme",
|
"homepage": "https://github.com/Obsidian-wtf/Microsoft-Rewards-Bot#readme",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"pre-build": "npm i && npm run clean && node -e \"process.exit(process.env.SKIP_PLAYWRIGHT_INSTALL?0:1)\" || npx playwright install chromium",
|
"install-deps": "npm install && npx playwright install chromium",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
"postbuild": "node -e \"console.log('\\n✅ Build complete! Run \\\"npm start\\\" to launch the bot.\\n')\"",
|
||||||
"test": "node --test --loader ts-node/esm tests/**/*.test.ts",
|
"test": "node --test --loader ts-node/esm tests/**/*.test.ts",
|
||||||
"start": "node --enable-source-maps ./dist/index.js",
|
"start": "node --enable-source-maps ./dist/index.js",
|
||||||
"ts-start": "node --loader ts-node/esm ./src/index.ts",
|
"ts-start": "node --loader ts-node/esm ./src/index.ts",
|
||||||
@@ -28,7 +29,6 @@
|
|||||||
"dashboard": "node --enable-source-maps ./dist/index.js -dashboard",
|
"dashboard": "node --enable-source-maps ./dist/index.js -dashboard",
|
||||||
"dashboard-dev": "ts-node ./src/index.ts -dashboard",
|
"dashboard-dev": "ts-node ./src/index.ts -dashboard",
|
||||||
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
||||||
"prepare": "npm run build",
|
|
||||||
"setup": "node ./setup/update/setup.mjs",
|
"setup": "node ./setup/update/setup.mjs",
|
||||||
"kill-chrome-win": "powershell -Command \"Get-Process | Where-Object { $_.MainModule.FileVersionInfo.FileDescription -eq 'Google Chrome for Testing' } | ForEach-Object { Stop-Process -Id $_.Id -Force }\"",
|
"kill-chrome-win": "powershell -Command \"Get-Process | Where-Object { $_.MainModule.FileVersionInfo.FileDescription -eq 'Google Chrome for Testing' } | ForEach-Object { Stop-Process -Id $_.Id -Force }\"",
|
||||||
"create-docker": "docker build -t microsoft-rewards-bot ."
|
"create-docker": "docker build -t microsoft-rewards-bot ."
|
||||||
|
|||||||
123
setup/README.md
Normal file
123
setup/README.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# Setup Scripts
|
||||||
|
|
||||||
|
This folder contains setup and update scripts for the Microsoft Rewards Bot.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
### setup.bat / setup.sh
|
||||||
|
**First-time installation scripts** for Windows (.bat) and Linux/macOS (.sh).
|
||||||
|
|
||||||
|
**What they do:**
|
||||||
|
1. Check prerequisites (Node.js, npm)
|
||||||
|
2. Create `accounts.jsonc` from template
|
||||||
|
3. Guide you through account configuration
|
||||||
|
4. Install dependencies (`npm install`)
|
||||||
|
5. Build TypeScript project (`npm run build`)
|
||||||
|
6. Install Playwright Chromium browser
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Windows
|
||||||
|
.\setup\setup.bat
|
||||||
|
|
||||||
|
# Linux/macOS
|
||||||
|
./setup/setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** These scripts do NOT start the bot automatically. After setup, run:
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### update/update.mjs
|
||||||
|
**Automatic update script** that keeps your bot up-to-date with the latest version.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Two update methods: Git-based or GitHub API (no Git needed)
|
||||||
|
- Preserves your configuration and accounts
|
||||||
|
- No merge conflicts with GitHub API method
|
||||||
|
- Automatic dependency installation and rebuild
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Auto-detect method from config.jsonc
|
||||||
|
node setup/update/update.mjs
|
||||||
|
|
||||||
|
# Force GitHub API method (recommended)
|
||||||
|
node setup/update/update.mjs --no-git
|
||||||
|
|
||||||
|
# Force Git method
|
||||||
|
node setup/update/update.mjs --git
|
||||||
|
```
|
||||||
|
|
||||||
|
**Automatic updates:** The bot checks for updates on startup (controlled by `update.enabled` in config.jsonc).
|
||||||
|
|
||||||
|
### update/setup.mjs
|
||||||
|
**Interactive setup wizard** used by setup.bat/setup.sh.
|
||||||
|
|
||||||
|
This is typically not run directly - use the wrapper scripts instead.
|
||||||
|
|
||||||
|
## Quick Start Guide
|
||||||
|
|
||||||
|
### First-time setup:
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
```batch
|
||||||
|
.\setup\setup.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux/macOS:**
|
||||||
|
```bash
|
||||||
|
chmod +x setup/setup.sh
|
||||||
|
./setup/setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Daily usage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start the bot
|
||||||
|
npm start
|
||||||
|
|
||||||
|
# Start with TypeScript (development)
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# View dashboard
|
||||||
|
npm run dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration:
|
||||||
|
|
||||||
|
- **Accounts:** Edit `src/accounts.jsonc`
|
||||||
|
- **Settings:** Edit `src/config.jsonc`
|
||||||
|
- **Documentation:** See `docs/` folder
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "npm not found"
|
||||||
|
Install Node.js from https://nodejs.org/ (v20 or newer recommended)
|
||||||
|
|
||||||
|
### "Setup failed"
|
||||||
|
1. Delete `node_modules` folder
|
||||||
|
2. Delete `package-lock.json` file
|
||||||
|
3. Run setup again
|
||||||
|
|
||||||
|
### "Build failed"
|
||||||
|
```bash
|
||||||
|
npm run clean
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update issues
|
||||||
|
If automatic updates fail, manually update:
|
||||||
|
```bash
|
||||||
|
git pull origin main
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Need Help?
|
||||||
|
|
||||||
|
- **Documentation:** `docs/index.md`
|
||||||
|
- **Getting Started:** `docs/getting-started.md`
|
||||||
|
- **Troubleshooting:** `docs/troubleshooting.md`
|
||||||
|
- **Discord:** https://discord.gg/k5uHkx9mne
|
||||||
@@ -1,25 +1,76 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal EnableDelayedExpansion
|
||||||
REM Wrapper to run setup via npm (Windows)
|
|
||||||
REM Navigates to project root and runs npm run setup
|
REM ========================================
|
||||||
|
REM Microsoft Rewards Bot - Setup (Windows)
|
||||||
|
REM ========================================
|
||||||
|
REM This script performs first-time setup:
|
||||||
|
REM 1. Check prerequisites (Node.js, npm)
|
||||||
|
REM 2. Run setup wizard (accounts + config)
|
||||||
|
REM 3. Install dependencies
|
||||||
|
REM 4. Build TypeScript project
|
||||||
|
REM
|
||||||
|
REM After setup, run the bot with: npm start
|
||||||
|
REM ========================================
|
||||||
|
|
||||||
set SCRIPT_DIR=%~dp0
|
set SCRIPT_DIR=%~dp0
|
||||||
set PROJECT_ROOT=%SCRIPT_DIR%..
|
set PROJECT_ROOT=%SCRIPT_DIR%..
|
||||||
|
|
||||||
if not exist "%PROJECT_ROOT%\package.json" (
|
echo.
|
||||||
echo [ERROR] package.json not found in project root.
|
echo ========================================
|
||||||
|
echo Microsoft Rewards Bot - Setup
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Check if Node.js/npm are installed
|
||||||
|
where npm >nul 2>nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] npm not found!
|
||||||
|
echo.
|
||||||
|
echo Please install Node.js from: https://nodejs.org/
|
||||||
|
echo Recommended version: v20 or newer
|
||||||
|
echo.
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo Navigating to project root...
|
for /f "tokens=*" %%i in ('npm -v 2^>nul') do set NPM_VERSION=%%i
|
||||||
|
echo [OK] npm detected: v!NPM_VERSION!
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Check if package.json exists
|
||||||
|
if not exist "%PROJECT_ROOT%\package.json" (
|
||||||
|
echo [ERROR] package.json not found in project root.
|
||||||
|
echo Current directory: %CD%
|
||||||
|
echo Project root: %PROJECT_ROOT%
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Navigate to project root
|
||||||
cd /d "%PROJECT_ROOT%"
|
cd /d "%PROJECT_ROOT%"
|
||||||
|
|
||||||
echo Running setup script via npm...
|
REM Run setup script
|
||||||
|
echo Running setup wizard...
|
||||||
|
echo.
|
||||||
call npm run setup
|
call npm run setup
|
||||||
set EXITCODE=%ERRORLEVEL%
|
set EXITCODE=%ERRORLEVEL%
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo Setup finished with exit code %EXITCODE%.
|
if %EXITCODE% EQU 0 (
|
||||||
echo Press Enter to close.
|
echo ========================================
|
||||||
pause >NUL
|
echo Setup Complete!
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo To start the bot: npm start
|
||||||
|
echo.
|
||||||
|
) else (
|
||||||
|
echo ========================================
|
||||||
|
echo Setup Failed ^(Exit Code: %EXITCODE%^)
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
exit /b %EXITCODE%
|
exit /b %EXITCODE%
|
||||||
@@ -1,35 +1,84 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Wrapper to run setup via npm (Linux/macOS)
|
# ========================================
|
||||||
|
# Microsoft Rewards Bot - Setup (Linux/macOS)
|
||||||
|
# ========================================
|
||||||
|
# This script performs first-time setup:
|
||||||
|
# 1. Check prerequisites (Node.js, npm, Git)
|
||||||
|
# 2. Run setup wizard (accounts + config)
|
||||||
|
# 3. Install dependencies
|
||||||
|
# 4. Build TypeScript project
|
||||||
|
#
|
||||||
|
# After setup, run the bot with: npm start
|
||||||
|
# ========================================
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
|
||||||
echo "=== Prerequisite Check ==="
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " Microsoft Rewards Bot - Setup"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
echo "=== Prerequisites Check ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
if command -v npm >/dev/null 2>&1; then
|
if command -v npm >/dev/null 2>&1; then
|
||||||
NPM_VERSION="$(npm -v 2>/dev/null || true)"
|
NPM_VERSION="$(npm -v 2>/dev/null || echo 'unknown')"
|
||||||
echo "npm detected: ${NPM_VERSION}"
|
echo "[OK] npm detected: v${NPM_VERSION}"
|
||||||
else
|
else
|
||||||
echo "[ERROR] npm not detected."
|
echo "[ERROR] npm not found!"
|
||||||
echo " Install Node.js and npm from nodejs.org or your package manager"
|
echo ""
|
||||||
|
echo "Please install Node.js from: https://nodejs.org/"
|
||||||
|
echo "Recommended version: v20 or newer"
|
||||||
|
echo ""
|
||||||
|
echo "Alternatively, use your package manager:"
|
||||||
|
echo " • Ubuntu/Debian: sudo apt install nodejs npm"
|
||||||
|
echo " • macOS: brew install node"
|
||||||
|
echo " • Fedora: sudo dnf install nodejs npm"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v git >/dev/null 2>&1; then
|
if command -v git >/dev/null 2>&1; then
|
||||||
GIT_VERSION="$(git --version 2>/dev/null || true)"
|
GIT_VERSION="$(git --version 2>/dev/null | cut -d' ' -f3)"
|
||||||
echo "Git detected: ${GIT_VERSION}"
|
echo "[OK] Git detected: v${GIT_VERSION}"
|
||||||
else
|
else
|
||||||
echo "[WARN] Git not detected."
|
echo "[WARN] Git not detected (optional for setup, required for updates)"
|
||||||
echo " Install (Linux): e.g. 'sudo apt install git' (or your distro equivalent)."
|
echo " • Ubuntu/Debian: sudo apt install git"
|
||||||
|
echo " • macOS: brew install git"
|
||||||
|
echo " • Fedora: sudo dnf install git"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "${PROJECT_ROOT}/package.json" ]; then
|
if [ ! -f "${PROJECT_ROOT}/package.json" ]; then
|
||||||
|
echo ""
|
||||||
echo "[ERROR] package.json not found at ${PROJECT_ROOT}" >&2
|
echo "[ERROR] package.json not found at ${PROJECT_ROOT}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo ""
|
||||||
echo "=== Running setup script via npm ==="
|
echo "=== Running Setup Wizard ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
cd "${PROJECT_ROOT}"
|
cd "${PROJECT_ROOT}"
|
||||||
exec npm run setup
|
npm run setup
|
||||||
|
EXITCODE=$?
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ $EXITCODE -eq 0 ]; then
|
||||||
|
echo "========================================"
|
||||||
|
echo " Setup Complete!"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
echo "To start the bot: npm start"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "========================================"
|
||||||
|
echo " Setup Failed (Exit Code: $EXITCODE)"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $EXITCODE
|
||||||
|
|||||||
175
setup/update/README.md
Normal file
175
setup/update/README.md
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
# Update System
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The bot uses a **simplified GitHub API-based update system** that:
|
||||||
|
- ✅ Downloads latest code as ZIP archive
|
||||||
|
- ✅ No Git required
|
||||||
|
- ✅ No merge conflicts
|
||||||
|
- ✅ Preserves user files automatically
|
||||||
|
- ✅ Automatic dependency installation
|
||||||
|
- ✅ TypeScript rebuild
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. **Automatic Updates**: If enabled in `config.jsonc`, the bot checks for updates on every startup
|
||||||
|
2. **Download**: Latest code is downloaded as ZIP from GitHub
|
||||||
|
3. **Protection**: User files (accounts, config, sessions) are backed up
|
||||||
|
4. **Update**: Code files are replaced selectively
|
||||||
|
5. **Restore**: Protected files are restored
|
||||||
|
6. **Install**: Dependencies are installed (`npm ci`)
|
||||||
|
7. **Build**: TypeScript is compiled
|
||||||
|
8. **Restart**: Bot restarts automatically with new version
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
In `src/config.jsonc`:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"update": {
|
||||||
|
"enabled": true, // Enable/disable updates
|
||||||
|
"autoUpdateAccounts": false, // Protect accounts files (recommended: false)
|
||||||
|
"autoUpdateConfig": false // Protect config.jsonc (recommended: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protected Files
|
||||||
|
|
||||||
|
These files are **always protected** (never overwritten):
|
||||||
|
- `sessions/` - Browser session data
|
||||||
|
- `.playwright-chromium-installed` - Browser installation marker
|
||||||
|
|
||||||
|
These files are **conditionally protected** (based on config):
|
||||||
|
- `src/accounts.jsonc` - Protected unless `autoUpdateAccounts: true`
|
||||||
|
- `src/accounts.json` - Protected unless `autoUpdateAccounts: true`
|
||||||
|
- `src/config.jsonc` - Protected unless `autoUpdateConfig: true`
|
||||||
|
|
||||||
|
## Manual Update
|
||||||
|
|
||||||
|
Run the update manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node setup/update/update.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Update Detection
|
||||||
|
|
||||||
|
The bot uses marker files to prevent restart loops:
|
||||||
|
|
||||||
|
- `.update-happened` - Created when files are actually updated
|
||||||
|
- `.update-restart-count` - Tracks restart attempts (max 3)
|
||||||
|
|
||||||
|
If no updates are available, **no marker is created** and the bot won't restart.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Updates disabled
|
||||||
|
```
|
||||||
|
⚠️ Updates are disabled in config.jsonc
|
||||||
|
```
|
||||||
|
→ Set `update.enabled: true` in `src/config.jsonc`
|
||||||
|
|
||||||
|
### Download failed
|
||||||
|
```
|
||||||
|
❌ Download failed: [error]
|
||||||
|
```
|
||||||
|
→ Check your internet connection
|
||||||
|
→ Verify GitHub is accessible
|
||||||
|
|
||||||
|
### Extraction failed
|
||||||
|
```
|
||||||
|
❌ Extraction failed: [error]
|
||||||
|
```
|
||||||
|
→ Ensure you have one of: `unzip`, `tar`, or PowerShell (Windows)
|
||||||
|
|
||||||
|
### Build failed
|
||||||
|
```
|
||||||
|
⚠️ Update completed with build warnings
|
||||||
|
```
|
||||||
|
→ Check TypeScript errors above
|
||||||
|
→ May still work, but review errors
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
```
|
||||||
|
setup/update/
|
||||||
|
├── update.mjs # Main update script (468 lines)
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Flow
|
||||||
|
```
|
||||||
|
Start
|
||||||
|
↓
|
||||||
|
Check config (enabled?)
|
||||||
|
↓
|
||||||
|
Read user preferences (autoUpdate flags)
|
||||||
|
↓
|
||||||
|
Backup protected files
|
||||||
|
↓
|
||||||
|
Download ZIP from GitHub
|
||||||
|
↓
|
||||||
|
Extract archive
|
||||||
|
↓
|
||||||
|
Copy files selectively (skip protected)
|
||||||
|
↓
|
||||||
|
Restore protected files
|
||||||
|
↓
|
||||||
|
Cleanup temporary files
|
||||||
|
↓
|
||||||
|
Create marker (.update-happened) if files changed
|
||||||
|
↓
|
||||||
|
Install dependencies (npm ci)
|
||||||
|
↓
|
||||||
|
Build TypeScript
|
||||||
|
↓
|
||||||
|
Exit (bot auto-restarts if marker exists)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Previous System
|
||||||
|
|
||||||
|
The old update system (799 lines) supported two methods:
|
||||||
|
- Git method (required Git, had merge conflicts)
|
||||||
|
- GitHub API method
|
||||||
|
|
||||||
|
**New system**: Only GitHub API method (simpler, more reliable)
|
||||||
|
|
||||||
|
## Anti-Loop Protection
|
||||||
|
|
||||||
|
The bot has built-in protection against infinite restart loops:
|
||||||
|
|
||||||
|
1. **Marker detection**: Bot only restarts if `.update-happened` exists
|
||||||
|
2. **Restart counter**: Max 3 restart attempts (`.update-restart-count`)
|
||||||
|
3. **Counter cleanup**: Removed after successful run without updates
|
||||||
|
4. **No-update detection**: Marker NOT created if already up to date
|
||||||
|
|
||||||
|
This ensures the bot never gets stuck in an infinite update loop.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
No external dependencies required! The update system uses only Node.js built-in modules:
|
||||||
|
- `node:child_process` - Run shell commands
|
||||||
|
- `node:fs` - File system operations
|
||||||
|
- `node:https` - Download files
|
||||||
|
- `node:path` - Path manipulation
|
||||||
|
|
||||||
|
## Exit Codes
|
||||||
|
|
||||||
|
- `0` - Success (updated or already up to date)
|
||||||
|
- `1` - Error (download failed, extraction failed, etc.)
|
||||||
|
|
||||||
|
## NPM Scripts
|
||||||
|
|
||||||
|
- `npm run start` - Start bot (runs update check first if enabled)
|
||||||
|
- `npm run dev` - Start in dev mode (skips update check)
|
||||||
|
- `npm run build` - Build TypeScript manually
|
||||||
|
|
||||||
|
## Version Info
|
||||||
|
|
||||||
|
- Current version: **v2** (GitHub API only)
|
||||||
|
- Previous version: v1 (Dual Git/GitHub API)
|
||||||
|
- Lines of code: **468** (down from 799)
|
||||||
|
- Complexity: **Simple** (down from Complex)
|
||||||
@@ -1,31 +1,25 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
/**
|
/**
|
||||||
* Unified cross-platform setup script for Microsoft Rewards Script V2.
|
* Microsoft Rewards Bot - First-Time Setup Script
|
||||||
*
|
*
|
||||||
* Features:
|
* This script handles initial project setup:
|
||||||
* - Renames accounts.example.jsonc -> accounts.json (idempotent)
|
* 1. Creates accounts.jsonc from template
|
||||||
* - Guides user through account configuration (email, password, TOTP, proxy)
|
* 2. Guides user through account configuration
|
||||||
* - Explains config.jsonc structure and key settings
|
* 3. Installs dependencies (npm install)
|
||||||
* - Installs dependencies (npm install)
|
* 4. Builds TypeScript project (npm run build)
|
||||||
* - Builds TypeScript project (npm run build)
|
* 5. Installs Playwright Chromium browser
|
||||||
* - Installs Playwright Chromium browser (idempotent with marker)
|
|
||||||
* - Optional immediate start or manual start instructions
|
|
||||||
*
|
*
|
||||||
* V2 Updates:
|
* IMPORTANT: This script does NOT launch the bot automatically.
|
||||||
* - Enhanced prompts for new config.jsonc structure
|
* After setup, run: npm start
|
||||||
* - Explains humanization, scheduling, notifications
|
|
||||||
* - References updated documentation (docs/config.md, docs/accounts.md)
|
|
||||||
* - Improved user guidance for first-time setup
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { spawn } from 'child_process';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { spawn } from 'child_process';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
// Project root = two levels up from setup/update directory
|
|
||||||
const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
|
const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
|
||||||
const SRC_DIR = path.join(PROJECT_ROOT, 'src');
|
const SRC_DIR = path.join(PROJECT_ROOT, 'src');
|
||||||
|
|
||||||
@@ -33,18 +27,23 @@ function log(msg) { console.log(msg); }
|
|||||||
function warn(msg) { console.warn(msg); }
|
function warn(msg) { console.warn(msg); }
|
||||||
function error(msg) { console.error(msg); }
|
function error(msg) { console.error(msg); }
|
||||||
|
|
||||||
function renameAccountsIfNeeded() {
|
function createAccountsFile() {
|
||||||
const accounts = path.join(SRC_DIR, 'accounts.json');
|
const accounts = path.join(SRC_DIR, 'accounts.jsonc');
|
||||||
const example = path.join(SRC_DIR, 'accounts.example.jsonc');
|
const example = path.join(SRC_DIR, 'accounts.example.jsonc');
|
||||||
|
|
||||||
if (fs.existsSync(accounts)) {
|
if (fs.existsSync(accounts)) {
|
||||||
log('accounts.json already exists - skipping rename.');
|
log('✓ accounts.jsonc already exists - skipping creation');
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs.existsSync(example)) {
|
if (fs.existsSync(example)) {
|
||||||
log('Renaming accounts.example.jsonc to accounts.json...');
|
log('📝 Creating accounts.jsonc from template...');
|
||||||
fs.renameSync(example, accounts);
|
fs.copyFileSync(example, accounts);
|
||||||
|
log('✓ Created accounts.jsonc');
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
warn('Neither accounts.json nor accounts.example.jsonc found.');
|
error('❌ Template file accounts.example.jsonc not found!');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,20 +59,25 @@ async function prompt(question) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loopForAccountsConfirmation() {
|
async function guideAccountConfiguration() {
|
||||||
log('\n📝 Please configure your Microsoft accounts:');
|
log('\n<EFBFBD> ACCOUNT CONFIGURATION');
|
||||||
log(' - Open: src/accounts.json');
|
log('════════════════════════════════════════════════════════════');
|
||||||
log(' - Add your email and password for each account');
|
log('1. Open file: src/accounts.jsonc');
|
||||||
log(' - Optional: Add TOTP secret for 2FA (see docs/accounts.md)');
|
log('2. Add your Microsoft account credentials:');
|
||||||
log(' - Optional: Configure proxy settings per account');
|
log(' - email: Your Microsoft account email');
|
||||||
log(' - Save the file (Ctrl+S or Cmd+S)\n');
|
log(' - password: Your account password');
|
||||||
|
log(' - totp: (Optional) 2FA secret for automatic authentication');
|
||||||
|
log('3. Enable accounts by setting "enabled": true');
|
||||||
|
log('4. Save the file');
|
||||||
|
log('');
|
||||||
|
log('📚 Full guide: docs/accounts.md');
|
||||||
|
log('════════════════════════════════════════════════════════════\n');
|
||||||
|
|
||||||
// Keep asking until user says yes
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const ans = (await prompt('Have you configured your accounts in accounts.json? (yes/no): ')).toLowerCase();
|
const ans = (await prompt('Have you configured your accounts? (yes/no): ')).toLowerCase();
|
||||||
if (['yes', 'y'].includes(ans)) break;
|
if (['yes', 'y'].includes(ans)) break;
|
||||||
if (['no', 'n'].includes(ans)) {
|
if (['no', 'n'].includes(ans)) {
|
||||||
log('Please configure accounts.json and save the file, then answer yes.');
|
log('\n⏸️ Please configure src/accounts.jsonc and save it, then answer yes.\n');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
log('Please answer yes or no.');
|
log('Please answer yes or no.');
|
||||||
@@ -99,64 +103,72 @@ async function ensureNpmAvailable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startOnly() {
|
async function performSetup() {
|
||||||
log('Starting program (npm run start)...');
|
log('\n🚀 MICROSOFT REWARDS BOT - FIRST-TIME SETUP');
|
||||||
await ensureNpmAvailable();
|
log('════════════════════════════════════════════════════════════\n');
|
||||||
// Assume user already installed & built; if dist missing inform user.
|
|
||||||
const distIndex = path.join(PROJECT_ROOT, 'dist', 'index.js');
|
// Step 1: Create accounts file
|
||||||
if (!fs.existsSync(distIndex)) {
|
const accountsExisted = createAccountsFile();
|
||||||
warn('Build output not found. Running build first.');
|
|
||||||
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'build']);
|
// Step 2: Guide user through account configuration
|
||||||
await installPlaywrightBrowsers();
|
if (!accountsExisted) {
|
||||||
|
await guideAccountConfiguration();
|
||||||
} else {
|
} else {
|
||||||
// Even if build exists, ensure browsers are installed once.
|
log('✓ Using existing accounts.jsonc\n');
|
||||||
await installPlaywrightBrowsers();
|
|
||||||
}
|
}
|
||||||
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'start']);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fullSetup() {
|
|
||||||
renameAccountsIfNeeded();
|
|
||||||
await loopForAccountsConfirmation();
|
|
||||||
|
|
||||||
log('\n⚙️ Configuration Options (src/config.jsonc):');
|
// Step 3: Configuration guidance
|
||||||
log(' - browser.headless: Set to true for background operation');
|
log('\n⚙️ CONFIGURATION (src/config.jsonc)');
|
||||||
log(' - execution.clusters: Number of parallel account processes');
|
log('════════════════════════════════════════════════════════════');
|
||||||
log(' - workers: Enable/disable specific tasks (dailySet, searches, etc.)');
|
log('Key settings you may want to adjust:');
|
||||||
log(' - humanization: Add natural delays and behavior (recommended: enabled)');
|
log(' • browser.headless: false = visible browser, true = background');
|
||||||
log(' - schedule: Configure automated daily runs');
|
log(' • execution.clusters: Number of parallel accounts (default: 1)');
|
||||||
log(' - notifications: Discord webhooks, NTFY push alerts');
|
log(' • workers: Enable/disable specific tasks');
|
||||||
log(' 📚 Full guide: docs/config.md\n');
|
log(' • humanization.enabled: Add natural delays (recommended: true)');
|
||||||
|
log(' • scheduling.enabled: Automate with OS scheduler');
|
||||||
|
log('');
|
||||||
|
log('📚 Full configuration guide: docs/getting-started.md');
|
||||||
|
log('════════════════════════════════════════════════════════════\n');
|
||||||
|
|
||||||
const reviewConfig = (await prompt('Do you want to review config.jsonc now? (yes/no): ')).toLowerCase();
|
const reviewConfig = (await prompt('Review config.jsonc before continuing? (yes/no): ')).toLowerCase();
|
||||||
if (['yes', 'y'].includes(reviewConfig)) {
|
if (['yes', 'y'].includes(reviewConfig)) {
|
||||||
log('⏸️ Setup paused. Please review src/config.jsonc, then re-run this setup.');
|
log('\n⏸️ Setup paused.');
|
||||||
log(' Common settings to check:');
|
log('Please review and edit src/config.jsonc, then run: npm run setup\n');
|
||||||
log(' - browser.headless (false = visible browser, true = background)');
|
|
||||||
log(' - execution.runOnZeroPoints (false = skip when no points available)');
|
|
||||||
log(' - humanization.enabled (true = natural behavior, recommended)');
|
|
||||||
log(' - schedule.enabled (false = manual runs, true = automated scheduling)');
|
|
||||||
log('\n After editing config.jsonc, run: npm run setup');
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 4: Install dependencies
|
||||||
|
log('\n📦 Installing dependencies...');
|
||||||
await ensureNpmAvailable();
|
await ensureNpmAvailable();
|
||||||
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['install']);
|
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['install']);
|
||||||
|
|
||||||
|
// Step 5: Build TypeScript
|
||||||
|
log('\n🔨 Building TypeScript project...');
|
||||||
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'build']);
|
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'build']);
|
||||||
|
|
||||||
|
// Step 6: Install Playwright browsers
|
||||||
await installPlaywrightBrowsers();
|
await installPlaywrightBrowsers();
|
||||||
|
|
||||||
log('\n✅ Setup complete!');
|
// Final message
|
||||||
log(' - Accounts configured: src/accounts.json');
|
log('\n');
|
||||||
log(' - Configuration: src/config.jsonc');
|
log('═══════════════════════════════════════════════════════════');
|
||||||
log(' - Documentation: docs/index.md\n');
|
log('✅ SETUP COMPLETE!');
|
||||||
|
log('═══════════════════════════════════════════════════════════');
|
||||||
const start = (await prompt('Do you want to start the automation now? (yes/no): ')).toLowerCase();
|
log('');
|
||||||
if (['yes', 'y'].includes(start)) {
|
log('📁 Configuration files:');
|
||||||
await runCommand(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'start']);
|
log(' • Accounts: src/accounts.jsonc');
|
||||||
} else {
|
log(' • Config: src/config.jsonc');
|
||||||
log('\nFinished setup. To start later, run: npm start');
|
log('');
|
||||||
log('For automated scheduling, use your OS scheduler (see docs/schedule.md).');
|
log('📚 Documentation:');
|
||||||
}
|
log(' • Getting started: docs/getting-started.md');
|
||||||
|
log(' • Full docs: docs/index.md');
|
||||||
|
log('');
|
||||||
|
log('🚀 TO START THE BOT:');
|
||||||
|
log(' npm start');
|
||||||
|
log('');
|
||||||
|
log('⏰ FOR AUTOMATED SCHEDULING:');
|
||||||
|
log(' See: docs/getting-started.md (Scheduling section)');
|
||||||
|
log('═══════════════════════════════════════════════════════════\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installPlaywrightBrowsers() {
|
async function installPlaywrightBrowsers() {
|
||||||
@@ -178,30 +190,36 @@ async function installPlaywrightBrowsers() {
|
|||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
if (!fs.existsSync(SRC_DIR)) {
|
if (!fs.existsSync(SRC_DIR)) {
|
||||||
error('[ERROR] Cannot find src directory at ' + SRC_DIR);
|
error('❌ Cannot find src directory at ' + SRC_DIR);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
process.chdir(PROJECT_ROOT);
|
process.chdir(PROJECT_ROOT);
|
||||||
|
|
||||||
for (;;) {
|
// Check if already setup (dist exists and accounts configured)
|
||||||
log('============================');
|
const distExists = fs.existsSync(path.join(PROJECT_ROOT, 'dist', 'index.js'));
|
||||||
log(' Microsoft Rewards Setup ');
|
const accountsExists = fs.existsSync(path.join(SRC_DIR, 'accounts.jsonc'));
|
||||||
log('============================');
|
|
||||||
log('Select an option:');
|
if (distExists && accountsExists) {
|
||||||
log(' 1) Start program now (skip setup)');
|
log('\n⚠️ Setup appears to be already complete.');
|
||||||
log(' 2) Full first-time setup');
|
log(' • Build output: dist/index.js exists');
|
||||||
log(' 3) Exit');
|
log(' • Accounts: src/accounts.jsonc exists\n');
|
||||||
const choice = (await prompt('Enter choice (1/2/3): ')).trim();
|
|
||||||
if (choice === '1') { await startOnly(); break; }
|
const rerun = (await prompt('Run setup anyway? (yes/no): ')).toLowerCase();
|
||||||
if (choice === '2') { await fullSetup(); break; }
|
if (!['yes', 'y'].includes(rerun)) {
|
||||||
if (choice === '3') { log('Exiting.'); process.exit(0); }
|
log('\n💡 To start the bot: npm start');
|
||||||
log('\nInvalid choice. Please select 1, 2 or 3.\n');
|
log('💡 To rebuild: npm run build\n');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// After completing action, optionally pause if launched by double click on Windows (no TTY detection simple heuristic)
|
|
||||||
|
await performSetup();
|
||||||
|
|
||||||
|
// Pause if launched by double-click on Windows
|
||||||
if (process.platform === 'win32' && process.stdin.isTTY) {
|
if (process.platform === 'win32' && process.stdin.isTTY) {
|
||||||
log('\nDone. Press Enter to close.');
|
log('Press Enter to close...');
|
||||||
await prompt('');
|
await prompt('');
|
||||||
}
|
}
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
109
src/index.ts
109
src/index.ts
@@ -970,44 +970,79 @@ async function main(): Promise<void> {
|
|||||||
const bootstrap = async () => {
|
const bootstrap = async () => {
|
||||||
try {
|
try {
|
||||||
// Check for updates BEFORE initializing and running tasks
|
// Check for updates BEFORE initializing and running tasks
|
||||||
// CRITICAL: Only restart if update script explicitly indicates new version was installed
|
// Anti-loop protection: Track restart attempts
|
||||||
try {
|
const restartCounterPath = path.join(process.cwd(), '.update-restart-count')
|
||||||
const updateResult = await rewardsBot.runAutoUpdate().catch((e) => {
|
let restartCount = 0
|
||||||
log('main', 'UPDATE', `Auto-update check failed: ${e instanceof Error ? e.message : String(e)}`, 'warn')
|
if (fs.existsSync(restartCounterPath)) {
|
||||||
return -1
|
try {
|
||||||
})
|
const content = fs.readFileSync(restartCounterPath, 'utf8')
|
||||||
|
restartCount = parseInt(content, 10) || 0
|
||||||
// FIXED: Only restart on exit code 0 AND if update actually happened
|
} catch {
|
||||||
// The update script returns 0 even when no update is needed, which causes infinite loop
|
restartCount = 0
|
||||||
// Solution: Check for marker file that update script creates when actual update happens
|
}
|
||||||
if (updateResult === 0) {
|
}
|
||||||
const updateMarkerPath = path.join(process.cwd(), '.update-happened')
|
|
||||||
const updateHappened = fs.existsSync(updateMarkerPath)
|
// If we've restarted too many times (3+), something is wrong - skip update
|
||||||
|
if (restartCount >= 3) {
|
||||||
if (updateHappened) {
|
log('main', 'UPDATE', '⚠️ Too many restart attempts detected - skipping update to prevent loop', 'warn')
|
||||||
// Remove marker file
|
// Clean up counter file
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(updateMarkerPath)
|
fs.unlinkSync(restartCounterPath)
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore cleanup errors
|
// Ignore
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
log('main', 'UPDATE', '✅ Update successful - restarting with new version...', 'log', 'green')
|
try {
|
||||||
|
const updateResult = await rewardsBot.runAutoUpdate().catch((e) => {
|
||||||
// Restart the process with the same arguments
|
log('main', 'UPDATE', `Auto-update check failed: ${e instanceof Error ? e.message : String(e)}`, 'warn')
|
||||||
const { spawn } = await import('child_process')
|
return -1
|
||||||
const child = spawn(process.execPath, process.argv.slice(1), {
|
})
|
||||||
detached: true,
|
|
||||||
stdio: 'inherit'
|
if (updateResult === 0) {
|
||||||
})
|
const updateMarkerPath = path.join(process.cwd(), '.update-happened')
|
||||||
child.unref()
|
const updateHappened = fs.existsSync(updateMarkerPath)
|
||||||
process.exit(0)
|
|
||||||
} else {
|
if (updateHappened) {
|
||||||
log('main', 'UPDATE', 'Already up to date, continuing with bot execution')
|
// Remove marker file
|
||||||
}
|
try {
|
||||||
|
fs.unlinkSync(updateMarkerPath)
|
||||||
|
} catch {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment restart counter
|
||||||
|
restartCount++
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(restartCounterPath, String(restartCount))
|
||||||
|
} catch {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
log('main', 'UPDATE', `✅ Update successful - restarting with new version (attempt ${restartCount}/3)...`, 'log', 'green')
|
||||||
|
|
||||||
|
// Restart the process with the same arguments
|
||||||
|
const { spawn } = await import('child_process')
|
||||||
|
const child = spawn(process.execPath, process.argv.slice(1), {
|
||||||
|
detached: true,
|
||||||
|
stdio: 'inherit'
|
||||||
|
})
|
||||||
|
child.unref()
|
||||||
|
process.exit(0)
|
||||||
|
} else {
|
||||||
|
log('main', 'UPDATE', 'Already up to date, continuing with bot execution')
|
||||||
|
// Clean restart counter on successful non-update run
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(restartCounterPath)) {
|
||||||
|
fs.unlinkSync(restartCounterPath)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (updateError) {
|
||||||
|
log('main', 'UPDATE', `Update check failed (continuing): ${updateError instanceof Error ? updateError.message : String(updateError)}`, 'warn')
|
||||||
}
|
}
|
||||||
} catch (updateError) {
|
|
||||||
log('main', 'UPDATE', `Update check failed (continuing): ${updateError instanceof Error ? updateError.message : String(updateError)}`, 'warn')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await rewardsBot.initialize()
|
await rewardsBot.initialize()
|
||||||
|
|||||||
Reference in New Issue
Block a user