From abd6117db3c2244b1377ddde9f5d7d7307fd4a91 Mon Sep 17 00:00:00 2001 From: Light <123307773+LightZirconite@users.noreply.github.com> Date: Thu, 16 Oct 2025 17:59:53 +0200 Subject: [PATCH] V2.3.0 Optimization (#380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated README.md to reflect version 2.1 and improve the presentation of Microsoft Rewards Automation features. * Updated version to 2.1.5 in README.md and package.json, added new license and legal notice sections, and improved the configuration script for a better user experience. * Mise à jour des messages de journalisation et ajout de vérifications pour le chargement des quiz et la présence des options avant de procéder. Suppression de fichiers de configuration obsolètes. * Added serial protection dialog management for message forwarding, including closing by button or escape. * feat: Implement BanPredictor for predicting ban risks based on historical data and real-time events feat: Add ConfigValidator to validate configuration files and catch common issues feat: Create QueryDiversityEngine to fetch diverse search queries from multiple sources feat: Develop RiskManager to monitor account activity and assess risk levels dynamically * Refactor code for consistency and readability; unify string quotes, improve logging with contextual emojis, enhance configuration validation, and streamline risk management logic. * feat: Refactor BrowserUtil and Login classes for improved button handling and selector management; implement unified selector system and enhance activity processing logic in Workers class. * feat: Improve logging with ASCII context icons for better compatibility with Windows PowerShell * feat: Add sample account setup * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * feat: Update Node.js engine requirement to >=20.0.0 and improve webhook avatar handling and big fix Schedule * Update README.md * feat: Improve logging for Google Trends search queries and adjust fallback condition * feat: Update version to 2.2.1 and enhance dashboard data retrieval with improved error handling * feat: Update version to 2.2.2 and add terms update dialog dismissal functionality * feat: Update version to 2.2.2 and require Node.js engine >=20.0.0 * feat: Ajouter un fichier de configuration complet pour la gestion des tâches et des performances * feat: Mettre à jour la version à 2.2.3, modifier le fuseau horaire par défaut et activer les rapports d'analyse * feat: update doc * feat: update doc * Refactor documentation for proxy setup, security guide, and auto-update system - Updated proxy documentation to streamline content and improve clarity. - Revised security guide to emphasize best practices and incident response. - Simplified auto-update documentation, enhancing user understanding of the update process. - Removed redundant sections and improved formatting for better readability. * feat: update version to 2.2.7 in package.json * feat: update version to 2.2.7 in README.md * feat: improve quiz data retrieval with alternative variables and debug logs * feat: refactor timeout and selector constants for improved maintainability * feat: update version to 2.2.8 in package.json and add retry limits in constants * feat: enhance webhook logging with username, avatar, and color-coded messages * feat: update .gitignore to include diagnostic folder and bump version to 2.2.8 in package-lock.json * feat: updated version to 2.3.0 and added new constants to improve the handling of delays and colors in logs --- .gitignore | 1 + README.md | 12 +- docs/accounts.md | 209 ++++-- docs/buy-mode.md | 210 ++---- docs/conclusionwebhook.md | 351 ++-------- docs/config.md | 800 ++++++++++++++++------- docs/diagnostics.md | 216 ++---- docs/docker.md | 191 ++++-- docs/getting-started.md | 206 +++--- docs/humanization.md | 334 +++------- docs/index.md | 107 +-- docs/jobstate.md | 355 ++-------- docs/ntfy.md | 421 ++---------- docs/proxy.md | 623 ++---------------- docs/schedule.md | 692 +++----------------- docs/security.md | 323 ++++----- docs/update.md | 431 ++---------- package-lock.json | 6 +- package.json | 4 +- src/browser/BrowserFunc.ts | 190 ++++-- src/browser/BrowserUtil.ts | 36 + src/config.jsonc | 304 +++++---- src/constants.ts | 67 ++ src/functions/Login.ts | 45 +- src/functions/activities/ABC.ts | 23 +- src/functions/activities/Poll.ts | 9 +- src/functions/activities/Quiz.ts | 17 +- src/functions/activities/Search.ts | 6 +- src/functions/activities/SearchOnBing.ts | 15 +- src/functions/activities/ThisOrThat.ts | 5 +- src/functions/activities/UrlReward.ts | 2 +- src/index.ts | 192 ++---- src/util/ConclusionWebhook.ts | 23 +- src/util/Load.ts | 3 +- src/util/Logger.ts | 52 +- src/util/Ntfy.ts | 10 +- src/util/QueryDiversityEngine.ts | 5 +- 37 files changed, 2392 insertions(+), 4104 deletions(-) create mode 100644 src/constants.ts diff --git a/.gitignore b/.gitignore index 79a3071..a4ee868 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ dist/ node_modules/ .vscode/ .github/ +diagnostic/ accounts.json notes accounts.dev.json diff --git a/README.md b/README.md index 52b8815..88742cf 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@
-![Version](https://img.shields.io/badge/v2.1.5-blue?style=for-the-badge&logo=github&logoColor=white) +![Version](https://img.shields.io/badge/v2.3.0-blue?style=for-the-badge&logo=github&logoColor=white) ![License](https://img.shields.io/badge/ISC-00D9FF?style=for-the-badge) ![Stars](https://img.shields.io/github/stars/TheNetsky/Microsoft-Rewards-Script?style=for-the-badge&color=blue) ![Status](https://img.shields.io/badge/Active-00C851?style=for-the-badge) @@ -466,16 +466,10 @@ See [LICENSE](./LICENSE) for details • [NOTICE](./NOTICE) for disclaimers ![Stars](https://img.shields.io/github/stars/TheNetsky/Microsoft-Rewards-Script?style=social) -

+
**Made with ❤️ by the open source community** -
- -![discord-avatar-128-ULDXD](https://github.com/user-attachments/assets/c33b0ee7-c56c-4f14-b177-851627236457) - -

- - + \ No newline at end of file diff --git a/docs/accounts.md b/docs/accounts.md index 9e09db3..fb5bc9d 100644 --- a/docs/accounts.md +++ b/docs/accounts.md @@ -1,94 +1,159 @@ -# 👤 Accounts & TOTP (2FA) +# 👤 Accounts & 2FA Setup -
- -**🔐 Secure Microsoft account setup with 2FA support** -*Everything you need to configure authentication* - -
+**Add your Microsoft accounts with secure TOTP authentication** --- -## 📍 File Location & Options +## 📍 Quick Start -The bot needs Microsoft account credentials to log in and complete activities. Here's how to provide them: +### Basic Setup (No 2FA) -### **Default Location** -``` -src/accounts.json -``` - -### **Environment Overrides** (Docker/CI) -- **`ACCOUNTS_FILE`** — Path to accounts file (e.g., `/data/accounts.json`) -- **`ACCOUNTS_JSON`** — Inline JSON string (useful for CI/CD) - -The loader tries: `ACCOUNTS_JSON` → `ACCOUNTS_FILE` → default locations in project root. - -## Schema -Each account has at least `email` and `password`. - -``` +**Edit** `src/accounts.json`: +```json { "accounts": [ { - "email": "email_1", - "password": "password_1", + "email": "your@email.com", + "password": "your_password" + } + ] +} +``` + +**That's it!** Run `npm start` to test. + +--- + +## 🔐 Add 2FA/TOTP (Recommended) + +### Why Use TOTP? +- ✅ **Automated login** — No manual code entry +- ✅ **More secure** — Better than SMS +- ✅ **Works 24/7** — Scheduler-friendly + +### How to Get Your TOTP Secret + +1. **Open Microsoft Account** → Security → Advanced security options +2. **Add authenticator app** → Click "Set up" +3. **Choose "I want to use a different app"** +4. Microsoft shows a **QR code** + **secret key** +5. **Copy the secret key** (starts with letters/numbers) +6. **Add to** `accounts.json`: + +```json +{ + "accounts": [ + { + "email": "your@email.com", + "password": "your_password", + "totp": "JBSWY3DPEHPK3PXP" + } + ] +} +``` + +--- + +## 🎯 Multiple Accounts + +```json +{ + "accounts": [ + { + "email": "account1@email.com", + "password": "password1", + "totp": "SECRET1" + }, + { + "email": "account2@email.com", + "password": "password2", + "totp": "SECRET2" + } + ] +} +``` + +--- + +## 🌐 Per-Account Proxy (Optional) + +```json +{ + "accounts": [ + { + "email": "your@email.com", + "password": "password", "totp": "", - "recoveryEmail": "your_email@domain.com", "proxy": { "proxyAxios": true, - "url": "", - "port": 0, - "username": "", - "password": "" + "url": "proxy.example.com", + "port": 8080, + "username": "proxyuser", + "password": "proxypass" } } ] } ``` -- `totp` (optional): Base32 secret for Time‑based One‑Time Passwords (2FA). If set, the bot generates the 6‑digit code automatically when asked by Microsoft. -- `recoveryEmail` (optional): used to validate masked recovery prompts. -- `proxy` (optional): per‑account proxy config. See the [Proxy guide](./proxy.md). - -## How to get your TOTP secret -1) In your Microsoft account security settings, add an authenticator app. -2) When shown the QR code, choose the option to enter the code manually — this reveals the Base32 secret. -3) Copy that secret (only the text after `secret=` if you have an otpauth URL) into the `totp` field. - -Security tips: -- Never commit real secrets to Git. -- Prefer `ACCOUNTS_FILE` or `ACCOUNTS_JSON` in production. - -## Examples -- Single account, no 2FA: -``` -{"accounts":[{"email":"a@b.com","password":"pass","totp":"","recoveryEmail":"","proxy":{"proxyAxios":true,"url":"","port":0,"username":"","password":""}}]} -``` - -- Single account with TOTP secret: -``` -{"accounts":[{"email":"a@b.com","password":"pass","totp":"JBSWY3DPEHPK3PXP","recoveryEmail":"","proxy":{"proxyAxios":true,"url":"","port":0,"username":"","password":""}}]} -``` - -- Multiple accounts: -``` -{"accounts":[ - {"email":"a@b.com","password":"pass","totp":"","recoveryEmail":"" ,"proxy":{"proxyAxios":true,"url":"","port":0,"username":"","password":""}}, - {"email":"c@d.com","password":"pass","totp":"","recoveryEmail":"" ,"proxy":{"proxyAxios":true,"url":"","port":0,"username":"","password":""}} -]} -``` - -## Troubleshooting -- “accounts file not found”: ensure the file exists, or set `ACCOUNTS_FILE` to the correct path. -- 2FA prompt not filled: verify `totp` is a valid Base32 secret; time on the host/container should be correct. -- Locked account: the bot will log and skip; resolve manually then re‑enable. +→ **[Full Proxy Guide](./proxy.md)** --- -## 🔗 Related Guides +## 🔒 Environment Variables (Docker/CI) -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Docker](./docker.md)** — Container deployment with accounts -- **[Security](./security.md)** — Account protection and incident response -- **[NTFY Notifications](./ntfy.md)** — Get alerts for login issues \ No newline at end of file +### Option 1: File Path +```bash +export ACCOUNTS_FILE=/path/to/accounts.json +``` + +### Option 2: Inline JSON +```bash +export ACCOUNTS_JSON='{"accounts":[{"email":"test@example.com","password":"pass"}]}' +``` + +--- + +## 🛠️ Troubleshooting + +| Problem | Solution | +|---------|----------| +| **"accounts.json not found"** | Create file or set `ACCOUNTS_FILE` env var | +| **"2FA prompt not auto-filled"** | Check TOTP secret is valid Base32 | +| **"Invalid TOTP"** | Verify system time is correct | +| **"Account locked"** | Manually unlock in Microsoft Account | +| **"Login timeout"** | Check internet connection, try proxy | + +### 2FA Not Working? + +1. **Check secret format** — Should be Base32 (only letters/numbers, no spaces) +2. **Verify system time** — Must be accurate (NTP sync) +3. **Test manually** — Use authenticator app to verify code works +4. **Remove backup codes** — Some security settings block TOTP + +--- + +## 🔒 Security Tips + +- 🔐 **Use strong passwords** — Unique for each account +- 🔑 **Enable TOTP** — More secure than SMS +- 📁 **Restrict file permissions** — `chmod 600 accounts.json` (Linux) +- 🔄 **Rotate passwords** — Change every 90 days +- 🚫 **Never commit** — Add `accounts.json` to `.gitignore` + +--- + +## 📚 Next Steps + +**TOTP setup?** +→ **[Security Guide](./security.md)** for best practices + +**Ready for automation?** +→ **[Scheduler Setup](./schedule.md)** + +**Need proxies?** +→ **[Proxy Guide](./proxy.md)** + +--- + +**[← Back to Hub](./index.md)** | **[Getting Started](./getting-started.md)** diff --git a/docs/buy-mode.md b/docs/buy-mode.md index d12e2a3..8f29b3f 100644 --- a/docs/buy-mode.md +++ b/docs/buy-mode.md @@ -1,64 +1,53 @@ # 💳 Buy Mode -
- -**🛒 Manual redemption with live point monitoring** -*Track your spending while maintaining full control* - -
+**Manually redeem rewards while monitoring points** --- -## 🎯 What is Buy Mode? +## 💡 What Is It? -Buy Mode allows you to **manually redeem rewards** while the script **passively monitors** your point balance. Perfect for safe redemptions without automation interference. +Launches browser and **passively monitors** your points balance while you manually shop/redeem. -> ℹ️ Buy Mode automatically launches the browser in a visible window (headless=false) so you can interact with captchas and checkout flows. Use `FORCE_HEADLESS=1` only if you understand the limitations. - -### **Key Features** -- 👀 **Passive monitoring** — No clicks or automation -- 🔄 **Real-time tracking** — Instant spending alerts -- 📱 **Live notifications** — Discord/NTFY integration -- ⏱️ **Configurable duration** — Set your own time limit -- 📊 **Session summary** — Complete spending report +**Use case:** Safely redeem gift cards without automation interference. --- -## 🚀 How to Use +## ⚡ Quick Start -### **Command Options** ```bash -# Monitor specific account npm start -- -buy your@email.com - -# Monitor first account in accounts.json -npm start -- -buy - -# Alternative: Enable in config (see below) ``` -### **What Happens Next** -1. **🖥️ Dual Tab System Opens** - - **Monitor Tab** — Background monitoring (auto-refresh) - - **User Tab** — Your control for redemptions/browsing +**What happens:** +1. Opens 2 browser tabs: + - **Monitor tab** — Background point tracking (auto-refresh) + - **Your tab** — Use this for manual purchases +2. Monitors points every ~10 seconds +3. Alerts you when spending detected -2. **📊 Passive Point Tracking** - - Reads balance every ~10 seconds - - Detects spending when points decrease - - Zero interference with your browsing +--- -3. **🔔 Real-time Alerts** - - Instant notifications when spending detected - - Shows amount spent + current balance - - Tracks cumulative session spending +## 🎯 Example Usage + +### Redeem Gift Card + +```bash +npm start -- -buy myaccount@outlook.com +``` + +1. Script opens Microsoft Rewards in browser +2. Use the **user tab** to browse and redeem +3. **Monitor tab** tracks your balance in background +4. Get notification when points decrease --- ## ⚙️ Configuration -### **Set Duration in Config** -Add to `src/config.jsonc`: -```json +**Set max session time:** + +**Edit** `src/config.jsonc`: +```jsonc { "buyMode": { "enabled": false, @@ -67,142 +56,53 @@ Add to `src/config.jsonc`: } ``` -| Setting | Default | Description | -|---------|---------|-------------| -| `enabled` | `false` | Force buy mode without CLI flag | -| `maxMinutes` | `45` | Auto-stop after N minutes | - -### **Enable Notifications** -Buy mode works with existing notification settings: -```json -{ - "conclusionWebhook": { - "enabled": true, - "url": "https://discord.com/api/webhooks/YOUR_URL" - }, - "ntfy": { - "enabled": true, - "url": "https://ntfy.sh", - "topic": "rewards" - } -} -``` - --- -## 🖥️ Terminal Output +## 🔔 Notifications -### **Startup** +Buy mode sends alerts when: +- 💳 **Points spent** — Shows amount and new balance +- 📉 **Balance changes** — Tracks cumulative spending + +**Example alert:** ``` - ███╗ ███╗███████╗ ██████╗ ██╗ ██╗██╗ ██╗ - ████╗ ████║██╔════╝ ██╔══██╗██║ ██║╚██╗ ██╔╝ - ██╔████╔██║███████╗ ██████╔╝██║ ██║ ╚████╔╝ - ██║╚██╔╝██║╚════██║ ██╔══██╗██║ ██║ ╚██╔╝ - ██║ ╚═╝ ██║███████║ ██████╔╝╚██████╔╝ ██║ - ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ - - Manual Purchase Mode • Passive Monitoring - -[BUY-MODE] Opening dual-tab system for safe redemptions... -[BUY-MODE] Monitor tab: Background point tracking -[BUY-MODE] User tab: Your control for purchases/browsing +💳 Spend detected (Buy Mode) +Account: user@email.com +Spent: -500 points +Current: 12,500 points +Session spent: 1,200 points ``` -### **Live Monitoring** -``` -[BUY-MODE] Current balance: 15,000 points -[BUY-MODE] 🛒 Spending detected: -500 points (new balance: 14,500) -[BUY-MODE] Session total spent: 500 points -``` - ---- - -## 📋 Use Cases - -| Scenario | Benefit | -|----------|---------| -| **🎁 Gift Card Redemption** | Track exact point cost while redeeming safely | -| **🛍️ Microsoft Store Purchases** | Monitor spending across multiple items | -| **✅ Account Verification** | Ensure point changes match expected activity | -| **📊 Spending Analysis** | Real-time tracking of reward usage patterns | -| **🔒 Safe Browsing** | Use Microsoft Rewards normally with monitoring | - --- ## 🛠️ Troubleshooting | Problem | Solution | |---------|----------| -| **Monitor tab closes** | Script auto-reopens in background | -| **No spending alerts** | Check webhook/NTFY config; verify notifications enabled | +| **Monitor tab closes** | Script auto-reopens it | +| **No spending alerts** | Check webhook/NTFY config | | **Session too short** | Increase `maxMinutes` in config | -| **Login failures** | Verify account credentials in `accounts.json` | -| **Points not updating** | Check internet connection; try refresh | --- -## 🔗 Related Guides +## ⚠️ Important Notes -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Accounts & 2FA](./accounts.md)** — Microsoft account setup -- **[NTFY Notifications](./ntfy.md)** — Mobile push alerts -- **[Discord Webhooks](./conclusionwebhook.md)** — Server notifications +- ✅ **Browser visible** — Always runs in visible mode +- ✅ **No automation** — Script only monitors, never clicks +- ✅ **Safe** — Use your browsing tab normally +- ✅ **Notifications** — Uses existing webhook/NTFY settings -## Terminal Output +--- -When you start buy mode, you'll see: +## 📚 Next Steps -``` - ███╗ ███╗███████╗ ██████╗ ██╗ ██╗██╗ ██╗ - ████╗ ████║██╔════╝ ██╔══██╗██║ ██║╚██╗ ██╔╝ - ██╔████╔██║███████╗ ██████╔╝██║ ██║ ╚████╔╝ - ██║╚██╔╝██║╚════██║ ██╔══██╗██║ ██║ ╚██╔╝ - ██║ ╚═╝ ██║███████║ ██████╔╝╚██████╔╝ ██║ - ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ - - Manual Purchase Mode • Passive Monitoring +**Setup notifications?** +→ **[Discord Webhooks](./conclusionwebhook.md)** +→ **[NTFY Push](./ntfy.md)** -[BUY-MODE] Buy mode ENABLED for your@email.com. We'll open 2 tabs: - (1) monitor tab (auto-refreshes), (2) your browsing tab -[BUY-MODE] The monitor tab may refresh every ~10s. Use the other tab... -[BUY-MODE] Opened MONITOR tab (auto-refreshes to track points) -[BUY-MODE] Opened USER tab (use this one to redeem/purchase freely) -[BUY-MODE] Logged in as your@email.com. Buy mode is active... -``` +**Back to automation?** +→ **[Getting Started](./getting-started.md)** -During monitoring: -``` -[BUY-MODE] Detected spend: -500 points (current: 12,500) -[BUY-MODE] Monitor tab was closed; reopening in background... -``` +--- -## Features - -- ✅ **Non-intrusive**: No clicks or navigation in your browsing tab -- ✅ **Real-time alerts**: Instant notifications when points are spent -- ✅ **Auto-recovery**: Reopens monitor tab if accidentally closed -- ✅ **Webhook support**: Works with Discord and NTFY notifications -- ✅ **Configurable duration**: Set your own monitoring time limit -- ✅ **Session tracking**: Complete summary of spending activity - -## Use Cases - -- **Manual redemptions**: Redeem gift cards or rewards while tracking spending -- **Account verification**: Monitor point changes during manual account activities -- **Spending analysis**: Track how points are being used in real-time -- **Safe browsing**: Use Microsoft Rewards normally while monitoring balance - -## Notes - -- Monitor tab runs in background and may refresh periodically -- Your main browsing tab is completely under your control -- Session data is saved automatically for future script runs -- Buy mode works with existing notification configurations -- No automation or point collection occurs in this mode - -## Troubleshooting - -- **Monitor tab closed**: Script automatically reopens it in background -- **No notifications**: Check webhook/NTFY configuration in `config.jsonc` -- **Session timeout**: Increase `maxMinutes` if you need longer monitoring -- **Login issues**: Ensure account credentials are correct in `accounts.json` +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/conclusionwebhook.md b/docs/conclusionwebhook.md index f318773..7ca46ae 100644 --- a/docs/conclusionwebhook.md +++ b/docs/conclusionwebhook.md @@ -1,145 +1,49 @@ -# 📊 Discord Conclusion Webhook +# 📊 Discord Webhooks -
- -**🎯 Comprehensive session summaries via Discord** -*Complete execution reports delivered instantly* - -
+**Get run summaries in Discord** --- -## 🎯 What is the Conclusion Webhook? +## 💡 What Is It? -The conclusion webhook sends a **detailed summary notification** at the end of each script execution via Discord, providing a complete overview of the session's results across all accounts. - -### **Key Features** -- 📊 **Session overview** — Total accounts processed, success/failure counts -- 💎 **Points summary** — Starting points, earned points, final totals -- ⏱️ **Performance metrics** — Execution times, efficiency statistics -- ❌ **Error reporting** — Issues encountered during execution -- 💳 **Buy mode detection** — Point spending alerts and tracking -- 🎨 **Rich embeds** — Color-coded, well-formatted Discord messages +Sends a **rich embed** to your Discord server after each run with: +- 📊 Total accounts processed +- 💎 Points earned +- ⏱️ Execution time +- ❌ Errors encountered --- -## ⚙️ Configuration +## ⚡ Quick Start -### **Basic Setup** -```json +### 1. Create Webhook in Discord + +1. **Open Discord** → Right-click channel +2. **Edit Channel** → **Integrations** tab +3. **Create Webhook** +4. **Copy webhook URL** + +### 2. Configure Script + +**Edit** `src/config.jsonc`: +```jsonc { "notifications": { "conclusionWebhook": { "enabled": true, - "url": "https://discord.com/api/webhooks/123456789/abcdef-webhook-token-here" + "url": "https://discord.com/api/webhooks/123456789/abcdef-your-webhook-token" } } } ``` -### **Configuration Options** - -| Setting | Description | Example | -|---------|-------------|---------| -| `enabled` | Enable conclusion webhook | `true` | -| `url` | Discord webhook URL | Full webhook URL from Discord | +**That's it!** You'll get a summary after each run. --- -## 🚀 Discord Setup +## 📋 Example Summary -### **Step 1: Create Webhook** -1. **Open Discord** and go to your server -2. **Right-click** on the channel for notifications -3. **Select "Edit Channel"** -4. **Go to "Integrations" tab** -5. **Click "Create Webhook"** - -### **Step 2: Configure Webhook** -- **Name** — "MS Rewards Summary" -- **Avatar** — Upload rewards icon (optional) -- **Channel** — Select appropriate channel -- **Copy webhook URL** - -### **Step 3: Add to Config** -```json -{ - "notifications": { - "conclusionWebhook": { - "enabled": true, - "url": "YOUR_COPIED_WEBHOOK_URL_HERE" - } - } -} ``` - ---- - -## 📋 Message Format - -### **Rich Embed Summary** - -#### **Header Section** -``` -🎯 Microsoft Rewards Summary -⏰ Completed at 2025-01-20 14:30:15 -📈 Total Runtime: 25m 36s -``` - -#### **Account Statistics** -``` -📊 Accounts: 3 • 0 with issues -``` - -#### **Points Overview** -``` -💎 Points: 15,230 → 16,890 (+1,660) -``` - -#### **Performance Metrics** -``` -⏱️ Average Duration: 8m 32s -📈 Cumulative Runtime: 25m 36s -``` - -#### **Buy Mode Detection** (if applicable) -``` -💳 Buy Mode Activity Detected -Total Spent: 1,200 points across 2 accounts -``` - -### **Account Breakdown** - -#### **Successful Account** -``` -👤 user@example.com -Points: 5,420 → 6,140 (+720) -Duration: 7m 23s -Status: ✅ Completed successfully -``` - -#### **Failed Account** -``` -👤 problem@example.com -Points: 3,210 → 3,210 (+0) -Duration: 2m 15s -Status: ❌ Failed - Login timeout -``` - -#### **Buy Mode Account** -``` -💳 spender@example.com -Session Spent: 500 points -Available: 12,500 points -Status: 💳 Purchase activity detected -``` - ---- - -## 📊 Message Examples - -### **Successful Session** -```discord 🎯 Microsoft Rewards Summary 📊 Accounts: 3 • 0 with issues @@ -163,97 +67,13 @@ Duration: 8m 32s Status: ✅ Completed successfully ``` -### **Session with Issues** -```discord -🎯 Microsoft Rewards Summary - -📊 Accounts: 3 • 1 with issues -💎 Points: 15,230 → 15,950 (+720) -⏱️ Average Duration: 6m 15s -📈 Cumulative Runtime: 18m 45s - -👤 user1@example.com -Points: 5,420 → 6,140 (+720) -Duration: 7m 23s -Status: ✅ Completed successfully - -👤 user2@example.com -Points: 4,810 → 4,810 (+0) -Duration: 2m 15s -Status: ❌ Failed - Login timeout - -👤 user3@example.com -Points: 5,000 → 5,000 (+0) -Duration: 9m 07s -Status: ⚠️ Partially completed - Quiz failed -``` - -### **Buy Mode Detection** -```discord -🎯 Microsoft Rewards Summary - -📊 Accounts: 2 • 0 with issues -💎 Points: 25,500 → 24,220 (-1,280) -💳 Buy Mode Activity Detected -Total Spent: 1,500 points across 1 account - -👤 buyer@example.com -Points: 15,000 → 13,500 (-1,500) -Duration: 12m 34s -Status: 💳 Buy mode detected -Activities: Purchase completed, searches skipped - -👤 normal@example.com -Points: 10,500 → 10,720 (+220) -Duration: 8m 45s -Status: ✅ Completed successfully -``` - --- -## 🤝 Integration with Other Notifications +## 🎯 Advanced: Separate Channels -### **Webhook vs Conclusion Webhook** +Use different webhooks for different notifications: -| Feature | Real-time Webhook | Conclusion Webhook | -|---------|------------------|-------------------| -| **Timing** | During execution | End of session only | -| **Content** | Errors, warnings, progress | Comprehensive summary | -| **Frequency** | Multiple per session | One per session | -| **Purpose** | Immediate alerts | Session overview | - -### **Recommended Combined Setup** -```json -{ - "notifications": { - "webhook": { - "enabled": true, - "url": "https://discord.com/api/webhooks/.../real-time" - }, - "conclusionWebhook": { - "enabled": true, - "url": "https://discord.com/api/webhooks/.../summary" - }, - "ntfy": { - "enabled": true, - "url": "https://ntfy.sh", - "topic": "rewards-mobile" - } - } -} -``` - -### **Benefits of Combined Setup** -- ⚡ **Real-time webhook** — Immediate error alerts -- 📊 **Conclusion webhook** — Comprehensive session summary -- 📱 **NTFY** — Mobile notifications for critical issues - ---- - -## 🎛️ Advanced Configuration - -### **Multiple Webhooks** -```json +```jsonc { "notifications": { "webhook": { @@ -268,67 +88,8 @@ Status: ✅ Completed successfully } ``` -### **Channel Organization** - -#### **Recommended Discord Structure** -- **#rewards-errors** — Real-time error notifications (webhook) -- **#rewards-summary** — End-of-run summaries (conclusionWebhook) -- **#rewards-logs** — Detailed text logs (manual uploads) - -#### **Channel Settings** -- **Notification settings** — Configure per your preference -- **Webhook permissions** — Limit to specific channels -- **Message history** — Enable for tracking trends - ---- - -## 🔒 Security & Privacy - -### **Webhook Security Best Practices** -- 🔐 Use **dedicated Discord server** for notifications -- 🎯 **Limit permissions** to specific channels only -- 🔄 **Regenerate URLs** if compromised -- 🚫 **Don't share** webhook URLs publicly - -### **Data Transmission** -- ✅ **Summary statistics** only -- ✅ **Points and email** addresses -- ❌ **No passwords** or sensitive tokens -- ❌ **No personal information** beyond emails - -### **Data Retention** -- 💾 **Discord stores** messages per server settings -- 🗑️ **No local storage** by the script -- ✂️ **Manual deletion** possible anytime -- 📝 **Webhook logs** may be retained by Discord - ---- - -## 🧪 Testing & Debugging - -### **Manual Webhook Test** -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -d '{"content":"Test message from rewards script"}' \ - "YOUR_WEBHOOK_URL_HERE" -``` - -### **Script Debug Mode** -```powershell -$env:DEBUG_REWARDS_VERBOSE=1; npm start -``` - -### **Success Indicators** -``` -[INFO] Sending conclusion webhook... -[INFO] Conclusion webhook sent successfully -``` - -### **Error Messages** -``` -[ERROR] Failed to send conclusion webhook: Invalid webhook URL -``` +- **`webhook`** — Real-time errors during execution +- **`conclusionWebhook`** — End-of-run summary --- @@ -336,54 +97,26 @@ $env:DEBUG_REWARDS_VERBOSE=1; npm start | Problem | Solution | |---------|----------| -| **No summary received** | Check webhook URL; verify Discord permissions | -| **Malformed messages** | Validate webhook URL; check Discord server status | -| **Missing information** | Ensure script completed; check for execution errors | -| **Rate limited** | Single webhook per session prevents this | +| **No message received** | Check webhook URL is complete | +| **"Invalid webhook"** | Regenerate webhook in Discord | +| **Partial data** | Ensure script completed fully | -### **Common Fixes** -- ✅ **Webhook URL** — Must be complete Discord webhook URL -- ✅ **Channel permissions** — Webhook must have send permissions -- ✅ **Server availability** — Discord server must be accessible -- ✅ **Script completion** — Summary only sent after full execution +### Test Webhook Manually + +```bash +curl -X POST -H "Content-Type: application/json" -d '{"content":"Test message"}' "YOUR_WEBHOOK_URL" +``` --- -## ⚡ Performance Impact +## 📚 Next Steps -### **Resource Usage** -- 📨 **Single HTTP request** at script end -- ⚡ **Non-blocking operation** — No execution delays -- 💾 **Payload size** — Typically < 2KB -- 🌐 **Delivery time** — Usually < 1 second +**Want mobile alerts?** +→ **[NTFY Push Notifications](./ntfy.md)** -### **Benefits** -- ✅ **No impact** on account processing -- ✅ **Minimal memory** footprint -- ✅ **No disk storage** required -- ✅ **Negligible bandwidth** usage +**Need detailed logs?** +→ **[Diagnostics Guide](./diagnostics.md)** --- -## 🎨 Customization - -### **Embed Features** -- 🎨 **Color-coded** status indicators -- 🎭 **Emoji icons** for visual clarity -- 📊 **Structured fields** for easy reading -- ⏰ **Timestamps** and duration info - -### **Discord Integration** -- 💬 **Thread notifications** support -- 👥 **Role mentions** (configure in webhook) -- 🔍 **Searchable messages** for history -- 📂 **Archive functionality** for records - ---- - -## 🔗 Related Guides - -- **[NTFY Notifications](./ntfy.md)** — Mobile push notifications -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Buy Mode](./buy-mode.md)** — Manual purchasing with monitoring -- **[Security](./security.md)** — Privacy and data protection \ No newline at end of file +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/config.md b/docs/config.md index 5ba7044..4664b28 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,283 +1,605 @@ -# ⚙️ Configuration Guide +# ⚙️ Configuration Guide# ⚙️ Configuration Guide -This page documents every field in the configuration file. The default ships as `src/config.jsonc` so you get inline `//` guidance without editor warnings, and the loader still accepts traditional `config.json` files if you prefer plain JSON. -Looking for ready-to-use presets? Check `docs/config-presets/` for curated examples such as `balanced.jsonc` (full automation with humanization) and `minimal.jsonc` (lean runs with quick scheduling). -> NOTE: Previous versions had `logging.live` (live streaming webhook); it was removed and replaced by a simple `logging.redactEmails` flag. +**Customize script behavior in `src/config.jsonc`**This page documents every field in the configuration file. The default ships as `src/config.jsonc` so you get inline `//` guidance without editor warnings, and the loader still accepts traditional `config.json` files if you prefer plain JSON. + + + +---Looking for ready-to-use presets? Check `docs/config-presets/` for curated examples such as `balanced.jsonc` (full automation with humanization) and `minimal.jsonc` (lean runs with quick scheduling). + + + +## ⚡ Quick Start (Essentials)> NOTE: Previous versions had `logging.live` (live streaming webhook); it was removed and replaced by a simple `logging.redactEmails` flag. + + + +### Minimal Working Config--- ---- ## Top-Level Fields -### baseURL -Internal Microsoft Rewards base. Leave it unless you know what you are doing. +```jsonc -### sessionPath -Directory where session data (cookies / fingerprints / job-state) is stored. +{### baseURL + + "humanization": {Internal Microsoft Rewards base. Leave it unless you know what you are doing. + + "enabled": true // Natural behavior (recommended) + + },### sessionPath + + "workers": {Directory where session data (cookies / fingerprints / job-state) is stored. + + "doDailySet": true, + + "doDesktopSearch": true,--- + + "doMobileSearch": true## browser + + }| Key | Type | Default | Description | + +}|-----|------|---------|-------------| + +```| headless | boolean | false | Run browser UI-less. Set to `false` to keep the browser visible (default). | ---- -## browser -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| headless | boolean | false | Run browser UI-less. Set to `false` to keep the browser visible (default). | | globalTimeout | string/number | "30s" | Max time for common Playwright operations. Accepts ms number or time string (e.g. `"45s"`, `"2min"`). | +**That's all you need!** Everything else has good defaults. + --- -## execution + +---## execution + | Key | Type | Default | Description | -|-----|------|---------|-------------| + +## 🎯 Popular Configurations|-----|------|---------|-------------| + | parallel | boolean | false | Run desktop + mobile simultaneously (higher resource usage). | -| runOnZeroPoints | boolean | false | Skip full run early if there are zero points available (saves time). | + +### 1. Daily Automation| runOnZeroPoints | boolean | false | Skip full run early if there are zero points available (saves time). | + | clusters | number | 1 | Number of process clusters (multi-process concurrency). | -| passesPerRun | number | 1 | Advanced: extra full passes per started run. | ---- -## buyMode -Manual redeem / purchase assistance. -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| enabled (CLI `-buy`) | boolean | false | Enable buy mode (usually via CLI argument). | -| maxMinutes | number | 45 | Max session length for buy mode. | +```jsonc| passesPerRun | number | 1 | Advanced: extra full passes per started run. | ---- -## fingerprinting.saveFingerprint -Persist browser fingerprints per device type for consistency. -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| mobile | boolean | false | Save/reuse a consistent mobile fingerprint. | -| desktop | boolean | false | Save/reuse a consistent desktop fingerprint. | - ---- -## search -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| useLocalQueries | boolean | false | Use locale-specific query sources instead of global ones. | - -### search.settings -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| useGeoLocaleQueries | boolean | false | Blend geo / locale into chosen queries. | -| scrollRandomResults | boolean | true | Random scroll during search pages to look natural. | -| clickRandomResults | boolean | true | Occasionally click safe results. | -| retryMobileSearchAmount | number | 2 | Retries if mobile searches didn’t yield points. | -| delay.min / delay.max | string/number | 3–5min | Delay between searches (ms or time string). | - ---- -## humanization -Human‑like behavior simulation. -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| enabled | boolean | true | Global on/off. | -| stopOnBan | boolean | true | Stop processing further accounts if a ban is detected. | -| immediateBanAlert | boolean | true | Fire notification immediately upon ban detection. | -| actionDelay.min/max | number/string | 150–450ms | Random micro-delay per action. | -| gestureMoveProb | number | 0.4 | Probability of a small mouse move gesture. | -| gestureScrollProb | number | 0.2 | Probability of a small scroll gesture. | -| allowedWindows | string[] | [] | Local time windows (e.g. `["08:30-11:00","19:00-22:00"]`). Outside windows, run waits. | - ---- -## vacation -Random contiguous block of days off per month. -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| enabled | boolean | false | Activate monthly break behavior. | -| minDays | number | 3 | Minimum skipped days per month. | -| maxDays | number | 5 | Maximum skipped days per month. | - ---- -## retryPolicy -Generic transient retry/backoff. -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| maxAttempts | number | 3 | Max tries for retryable blocks. | -| baseDelay | number | 1000 | Initial delay in ms. | -| maxDelay | number/string | 30s | Max backoff delay. | -| multiplier | number | 2 | Exponential backoff multiplier. | -| jitter | number | 0.2 | Randomization factor (0..1). | - ---- -## workers -Enable/disable scripted task categories. -| Key | Default | Description | -|-----|---------|-------------| -| doDailySet | true | Daily set activities. | -| doMorePromotions | true | Promotional tasks. | -| doPunchCards | true | Punch card flows. | -| doDesktopSearch | true | Desktop searches. | -| doMobileSearch | true | Mobile searches. | -| doDailyCheckIn | true | Daily check-in. | -| doReadToEarn | true | Reading tasks. | -| bundleDailySetWithSearch | false | Immediately start desktop search bundle after daily set. | - ---- -## proxy -| Key | Default | Description | -|-----|---------|-------------| -| proxyGoogleTrends | true | Route Google Trends fetch through proxy if set. | -| proxyBingTerms | true | Route Bing query source fetch through proxy if set. | - ---- -## notifications -Manages notification channels (Discord webhooks, NTFY, etc.). - -### notifications.webhook -Primary webhook (can be used for summary or generic messages). -| Key | Default | Description | -|-----|---------|-------------| -| enabled | false | Allow sending webhook-based notifications and live log streaming. | -| url | "" | Webhook endpoint. | - -### notifications.conclusionWebhook -Rich end-of-run summary (if enabled separately). -| Key | Default | Description | -|-----|---------|-------------| -| enabled | false | Enable run summary posting. | -| url | "" | Webhook endpoint. | - -### notifications.ntfy -Lightweight push notifications. -| Key | Default | Description | -|-----|---------|-------------| -| enabled | false | Enable NTFY push. | -| url | "" | Base NTFY server URL (e.g. https://ntfy.sh). | -| topic | rewards | Topic/channel name. | -| authToken | "" | Bearer token if your server requires auth. | - ---- -## logging -| Key | Type | Description | -|-----|------|-------------| -| excludeFunc | string[] | Log buckets suppressed in console + any webhook usage. | -| webhookExcludeFunc | string[] | Buckets suppressed specifically for webhook output. | -| redactEmails | boolean | If true, email addresses are partially masked in logs. | -| liveWebhookUrl | string | Optional override URL for live log streaming (falls back to `notifications.webhook.url`). | - -_Removed fields_: `live.enabled`, `live.url`, `live.redactEmails` — replaced by `redactEmails` only. - ---- -## diagnostics -Capture evidence when something fails. -| Key | Default | Description | -|-----|---------|-------------| -| enabled | true | Master switch for diagnostics. | -| saveScreenshot | true | Save screenshot on failure. | -| saveHtml | true | Save HTML snapshot on failure. | -| maxPerRun | 2 | Cap artifacts per run per failure type. | -| retentionDays | 7 | Old run artifacts pruned after this many days. | - ---- -## jobState -Checkpoint system to avoid duplicate work. -| Key | Default | Description | -|-----|---------|-------------| -| enabled | true | Enable job state tracking. | -| dir | "" | Custom directory (default: `/job-state`). | - ---- -## schedule -Built-in scheduler (avoids external cron inside container or host). -| Key | Default | Description | -|-----|---------|-------------| -| enabled | false | Enable scheduling loop. | -| useAmPm | false | If true, parse `time12`; else use `time24`. | -| time12 | 9:00 AM | 12‑hour format time (only if useAmPm=true). | -| time24 | 09:00 | 24‑hour format time (only if useAmPm=false). | -| timeZone | America/New_York | IANA zone string (e.g. Europe/Paris). | -| runImmediatelyOnStart | false | Run one pass instantly in addition to daily schedule. | - -_Legacy_: If both `time12` and `time24` are empty, a legacy `time` (HH:mm) may still be read. - ---- -## update -Auto-update behavior after a run. -| Key | Default | Description | -|-----|---------|-------------| -| git | true | Pull latest git changes after run. | -| docker | false | Recreate container (if running in Docker orchestration). | -| scriptPath | setup/update/update.mjs | Custom script executed for update flow. | - ---- -## Security / Best Practices -- Keep `redactEmails` true if you share logs publicly. -- Use a private NTFY instance or secure Discord webhooks (do not leak URLs). -- Avoid setting `headless` false on untrusted remote servers. - ---- -## Minimal Example -```jsonc { - "browser": { "headless": true }, - "execution": { "parallel": false }, - "workers": { "doDailySet": true, "doDesktopSearch": true, "doMobileSearch": true }, - "logging": { "redactEmails": true } -} + + "humanization": { "enabled": true },--- + + "schedule": {## buyMode + + "enabled": true,Manual redeem / purchase assistance. + + "time": "09:00",| Key | Type | Default | Description | + + "timeZone": "America/New_York"|-----|------|---------|-------------| + + }| enabled (CLI `-buy`) | boolean | false | Enable buy mode (usually via CLI argument). | + +}| maxMinutes | number | 45 | Max session length for buy mode. | + ``` -## Common Tweaks -| Goal | Change | -|------|--------| -| Faster dev feedback | Set `browser.headless` to false and shorten search delays. | -| Reduce detection risk | Keep humanization enabled, add vacation window. | -| Silent mode | Add more buckets to `excludeFunc`. | -| Skip mobile searches | Set `workers.doMobileSearch=false`. | -| Use daily schedule | Set `schedule.enabled=true` and adjust `time24` + `timeZone`. | +--- + +→ **[Full Scheduler Guide](./schedule.md)**## fingerprinting.saveFingerprint + +Persist browser fingerprints per device type for consistency. + +---| Key | Type | Default | Description | + +|-----|------|---------|-------------| + +### 2. With Notifications| mobile | boolean | false | Save/reuse a consistent mobile fingerprint. | + +| desktop | boolean | false | Save/reuse a consistent desktop fingerprint. | + +```jsonc + +{--- + + "humanization": { "enabled": true },## search + + "conclusionWebhook": {| Key | Type | Default | Description | + + "enabled": true,|-----|------|---------|-------------| + + "url": "https://discord.com/api/webhooks/YOUR_WEBHOOK"| useLocalQueries | boolean | false | Use locale-specific query sources instead of global ones. | + + } + +}### search.settings + +```| Key | Type | Default | Description | + +|-----|------|---------|-------------| + +→ **[Discord Setup](./conclusionwebhook.md)** | **[NTFY Setup](./ntfy.md)**| useGeoLocaleQueries | boolean | false | Blend geo / locale into chosen queries. | + +| scrollRandomResults | boolean | true | Random scroll during search pages to look natural. | + +---| clickRandomResults | boolean | true | Occasionally click safe results. | + +| retryMobileSearchAmount | number | 2 | Retries if mobile searches didn’t yield points. | + +### 3. Background Mode (Headless)| delay.min / delay.max | string/number | 3–5min | Delay between searches (ms or time string). | + + + +```jsonc--- + +{## humanization + + "browser": {Human‑like behavior simulation. + + "headless": true| Key | Type | Default | Description | + + },|-----|------|---------|-------------| + + "humanization": { "enabled": true }| enabled | boolean | true | Global on/off. | + +}| stopOnBan | boolean | true | Stop processing further accounts if a ban is detected. | + +```| immediateBanAlert | boolean | true | Fire notification immediately upon ban detection. | + +| actionDelay.min/max | number/string | 150–450ms | Random micro-delay per action. | + +**Note:** Set `headless: false` to see the browser during development.| gestureMoveProb | number | 0.4 | Probability of a small mouse move gesture. | + +| gestureScrollProb | number | 0.2 | Probability of a small scroll gesture. | + +---| allowedWindows | string[] | [] | Local time windows (e.g. `["08:30-11:00","19:00-22:00"]`). Outside windows, run waits. | + + + +### 4. Skip When No Points--- + +## vacation + +```jsoncRandom contiguous block of days off per month. + +{| Key | Type | Default | Description | + + "execution": {|-----|------|---------|-------------| + + "runOnZeroPoints": false| enabled | boolean | false | Activate monthly break behavior. | + + }| minDays | number | 3 | Minimum skipped days per month. | + +}| maxDays | number | 5 | Maximum skipped days per month. | + +``` --- + +Saves time by skipping accounts with 0 available points.## retryPolicy + +Generic transient retry/backoff. + +---| Key | Type | Default | Description | + +|-----|------|---------|-------------| + +### 5. Multiple Accounts Faster| maxAttempts | number | 3 | Max tries for retryable blocks. | + +| baseDelay | number | 1000 | Initial delay in ms. | + +```jsonc| maxDelay | number/string | 30s | Max backoff delay. | + +{| multiplier | number | 2 | Exponential backoff multiplier. | + + "execution": {| jitter | number | 0.2 | Randomization factor (0..1). | + + "parallel": false, // Desktop + mobile simultaneously + + "clusters": 1 // Process multiple accounts in parallel--- + + }## workers + +}Enable/disable scripted task categories. + +```| Key | Default | Description | + +|-----|---------|-------------| + +⚠️ **Higher detection risk** with parallel execution.| doDailySet | true | Daily set activities. | + +| doMorePromotions | true | Promotional tasks. | + +---| doPunchCards | true | Punch card flows. | + +| doDesktopSearch | true | Desktop searches. | + +## 🛡️ Anti-Ban Settings| doMobileSearch | true | Mobile searches. | + +| doDailyCheckIn | true | Daily check-in. | + +### Humanization (Recommended)| doReadToEarn | true | Reading tasks. | + +| bundleDailySetWithSearch | false | Immediately start desktop search bundle after daily set. | + +```jsonc + +{--- + + "humanization": {## proxy + + "enabled": true,| Key | Default | Description | + + "actionDelay": { "min": 150, "max": 450 },|-----|---------|-------------| + + "gestureMoveProb": 0.4,| proxyGoogleTrends | true | Route Google Trends fetch through proxy if set. | + + "gestureScrollProb": 0.2,| proxyBingTerms | true | Route Bing query source fetch through proxy if set. | + + "randomOffDaysPerWeek": 1 + + }--- + +}## notifications + +```Manages notification channels (Discord webhooks, NTFY, etc.). + + + +→ **[Full Humanization Guide](./humanization.md)**### notifications.webhook + +Primary webhook (can be used for summary or generic messages). + +---| Key | Default | Description | + +|-----|---------|-------------| + +### Vacation Mode| enabled | false | Allow sending webhook-based notifications and live log streaming. | + +| url | "" | Webhook endpoint. | + +```jsonc + +{### notifications.conclusionWebhook + + "vacation": {Rich end-of-run summary (if enabled separately). + + "enabled": true,| Key | Default | Description | + + "minDays": 3,|-----|---------|-------------| + + "maxDays": 5| enabled | false | Enable run summary posting. | + + }| url | "" | Webhook endpoint. | + +} + +```### notifications.ntfy + +Lightweight push notifications. + +Skips 3-5 random consecutive days per month.| Key | Default | Description | + +|-----|---------|-------------| + +---| enabled | false | Enable NTFY push. | + +| url | "" | Base NTFY server URL (e.g. https://ntfy.sh). | + +## 🔧 Advanced Options| topic | rewards | Topic/channel name. | + +| authToken | "" | Bearer token if your server requires auth. | + +
+ +Click to expand all options--- + +## logging + +### Browser| Key | Type | Description | + +|-----|------|-------------| + +```jsonc| excludeFunc | string[] | Log buckets suppressed in console + any webhook usage. | + +{| webhookExcludeFunc | string[] | Buckets suppressed specifically for webhook output. | + + "browser": {| redactEmails | boolean | If true, email addresses are partially masked in logs. | + + "headless": false,| liveWebhookUrl | string | Optional override URL for live log streaming (falls back to `notifications.webhook.url`). | + + "globalTimeout": "30s" + + }_Removed fields_: `live.enabled`, `live.url`, `live.redactEmails` — replaced by `redactEmails` only. + +} + +```--- + +## diagnostics + +### Workers (Tasks)Capture evidence when something fails. + +| Key | Default | Description | + +```jsonc|-----|---------|-------------| + +{| enabled | true | Master switch for diagnostics. | + + "workers": {| saveScreenshot | true | Save screenshot on failure. | + + "doDailySet": true,| saveHtml | true | Save HTML snapshot on failure. | + + "doMorePromotions": true,| maxPerRun | 2 | Cap artifacts per run per failure type. | + + "doPunchCards": true,| retentionDays | 7 | Old run artifacts pruned after this many days. | + + "doDesktopSearch": true, + + "doMobileSearch": true,--- + + "doDailyCheckIn": true,## jobState + + "doReadToEarn": trueCheckpoint system to avoid duplicate work. + + }| Key | Default | Description | + +}|-----|---------|-------------| + +```| enabled | true | Enable job state tracking. | + +| dir | "" | Custom directory (default: `/job-state`). | + +### Search Behavior + +--- + +```jsonc## schedule + +{Built-in scheduler (avoids external cron inside container or host). + + "search": {| Key | Default | Description | + + "useLocalQueries": false,|-----|---------|-------------| + + "settings": {| enabled | false | Enable scheduling loop. | + + "useGeoLocaleQueries": false,| useAmPm | false | If true, parse `time12`; else use `time24`. | + + "scrollRandomResults": true,| time12 | 9:00 AM | 12‑hour format time (only if useAmPm=true). | + + "clickRandomResults": true| time24 | 09:00 | 24‑hour format time (only if useAmPm=false). | + + }| timeZone | America/New_York | IANA zone string (e.g. Europe/Paris). | + + }| runImmediatelyOnStart | false | Run one pass instantly in addition to daily schedule. | + +} + +```_Legacy_: If both `time12` and `time24` are empty, a legacy `time` (HH:mm) may still be read. + + + +### Diagnostics--- + +## update + +```jsoncAuto-update behavior after a run. + +{| Key | Default | Description | + + "diagnostics": {|-----|---------|-------------| + + "enabled": true,| git | true | Pull latest git changes after run. | + + "saveScreenshot": true,| docker | false | Recreate container (if running in Docker orchestration). | + + "saveHtml": true,| scriptPath | setup/update/update.mjs | Custom script executed for update flow. | + + "maxPerRun": 2, + + "retentionDays": 7--- + + }## Security / Best Practices + +}- Keep `redactEmails` true if you share logs publicly. + +```- Use a private NTFY instance or secure Discord webhooks (do not leak URLs). + +- Avoid setting `headless` false on untrusted remote servers. + +### Job State + +--- + +```jsonc## Minimal Example + +{```jsonc + + "jobState": {{ + + "enabled": true, "browser": { "headless": true }, + + "dir": "" // Empty = use default location "execution": { "parallel": false }, + + } "workers": { "doDailySet": true, "doDesktopSearch": true, "doMobileSearch": true }, + +} "logging": { "redactEmails": true } + +```} + +``` + +### Auto-Update + +## Common Tweaks + +```jsonc| Goal | Change | + +{|------|--------| + + "update": {| Faster dev feedback | Set `browser.headless` to false and shorten search delays. | + + "git": true,| Reduce detection risk | Keep humanization enabled, add vacation window. | + + "docker": false,| Silent mode | Add more buckets to `excludeFunc`. | + + "scriptPath": "setup/update/update.mjs"| Skip mobile searches | Set `workers.doMobileSearch=false`. | + + }| Use daily schedule | Set `schedule.enabled=true` and adjust `time24` + `timeZone`. | + +} + +```--- + ## NEW INTELLIGENT FEATURES +
+ ### riskManagement -Dynamic risk assessment and ban prediction. -| Key | Type | Default | Description | +---Dynamic risk assessment and ban prediction. + + + +## 🎛️ Intelligent Features (v2.2+)| Key | Type | Default | Description | + |-----|------|---------|-------------| -| enabled | boolean | true | Enable risk-aware throttling. | + +### Risk Management| enabled | boolean | true | Enable risk-aware throttling. | + | autoAdjustDelays | boolean | true | Automatically increase delays when captchas/errors are detected. | -| stopOnCritical | boolean | false | Stop execution if risk score exceeds threshold. | -| banPrediction | boolean | true | Enable ML-style pattern analysis to predict ban risk. | -| riskThreshold | number | 75 | Risk score (0-100) above which bot pauses or alerts. | -**How it works:** Monitors captchas, errors, timeouts, and account patterns. Dynamically adjusts delays (e.g., 1x → 2.5x) and warns you before bans happen. +```jsonc| stopOnCritical | boolean | false | Stop execution if risk score exceeds threshold. | ---- -### analytics -Performance dashboard and metrics tracking. +{| banPrediction | boolean | true | Enable ML-style pattern analysis to predict ban risk. | + + "riskManagement": {| riskThreshold | number | 75 | Risk score (0-100) above which bot pauses or alerts. | + + "enabled": true, + + "autoAdjustDelays": true,**How it works:** Monitors captchas, errors, timeouts, and account patterns. Dynamically adjusts delays (e.g., 1x → 2.5x) and warns you before bans happen. + + "banPrediction": true, + + "riskThreshold": 75--- + + }### analytics + +}Performance dashboard and metrics tracking. + +``` | Key | Type | Default | Description | -|-----|------|---------|-------------| + +Dynamically adjusts delays when detecting captchas/errors.|-----|------|---------|-------------| + | enabled | boolean | true | Track points earned, success rates, execution times. | -| retentionDays | number | 30 | How long to keep analytics data. | -| exportMarkdown | boolean | true | Generate human-readable markdown reports. | -| webhookSummary | boolean | false | Send analytics summary via webhook. | -**Output location:** `analytics/` folder (JSON files per account per day). +---| retentionDays | number | 30 | How long to keep analytics data. | + +| exportMarkdown | boolean | true | Generate human-readable markdown reports. | + +### Query Diversity| webhookSummary | boolean | false | Send analytics summary via webhook. | + + + +```jsonc**Output location:** `analytics/` folder (JSON files per account per day). + +{ + + "queryDiversity": {--- + + "enabled": true,### queryDiversity + + "sources": ["google-trends", "reddit", "local-fallback"],Multi-source search query generation. + + "maxQueriesPerSource": 10 + + }| Key | Type | Default | Description | + +}|-----|------|---------|-------------| + +```| enabled | boolean | true | Use diverse sources instead of just Google Trends. | + +| sources | array | `["google-trends", "reddit", "local-fallback"]` | Which sources to query (google-trends, reddit, news, wikipedia, local-fallback). | + +Uses multiple search sources to avoid patterns.| maxQueriesPerSource | number | 10 | Max queries to fetch per source. | + +| cacheMinutes | number | 30 | Cache duration to avoid hammering APIs. | --- -### queryDiversity -Multi-source search query generation. - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| enabled | boolean | true | Use diverse sources instead of just Google Trends. | -| sources | array | `["google-trends", "reddit", "local-fallback"]` | Which sources to query (google-trends, reddit, news, wikipedia, local-fallback). | -| maxQueriesPerSource | number | 10 | Max queries to fetch per source. | -| cacheMinutes | number | 30 | Cache duration to avoid hammering APIs. | **Why?** Reduces patterns by mixing Reddit posts, news headlines, Wikipedia topics instead of predictable Google Trends. ---- -### dryRun -Test mode: simulate execution without actually running tasks. - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| dryRun | boolean | false | When true, logs actions but doesn't execute (useful for testing config). | - -**Use case:** Validate new config changes, estimate execution time, debug issues without touching accounts. +### Analytics --- -## Changelog Notes + +```jsonc### dryRun + +{Test mode: simulate execution without actually running tasks. + + "analytics": { + + "enabled": true,| Key | Type | Default | Description | + + "retentionDays": 30,|-----|------|---------|-------------| + + "exportMarkdown": true| dryRun | boolean | false | When true, logs actions but doesn't execute (useful for testing config). | + + } + +}**Use case:** Validate new config changes, estimate execution time, debug issues without touching accounts. + +``` + +--- + +Tracks points earned, success rates, execution times.## Changelog Notes + - **v2.2.0**: Added risk-aware throttling, analytics dashboard, query diversity, ban prediction, dry-run mode. -- Removed live webhook streaming complexity; now simpler logging. + +---- Removed live webhook streaming complexity; now simpler logging. + - Centralized redaction logic under `logging.redactEmails`. +### Dry Run (Test Mode) + If something feels undocumented or unclear, open a documentation issue or extend this page. + +```jsonc +{ + "dryRun": true +} +``` + +**Or via CLI:** +```bash +npm start -- --dry-run +``` + +Simulates execution without actually running tasks. + +--- + +## 🛠️ Troubleshooting + +| Issue | Solution | +|-------|----------| +| **Config not loading** | Check JSON syntax (trailing commas OK in `.jsonc`) | +| **Script ignoring config** | Verify file is `src/config.jsonc` | +| **Errors after update** | Compare with example config | + +--- + +## 📚 Next Steps + +**Setup scheduler?** +→ **[Scheduler Guide](./schedule.md)** + +**Want notifications?** +→ **[Discord Webhooks](./conclusionwebhook.md)** + +**Need proxies?** +→ **[Proxy Guide](./proxy.md)** + +--- + +**[← Back to Hub](./index.md)** | **[Getting Started](./getting-started.md)** diff --git a/docs/diagnostics.md b/docs/diagnostics.md index af20cb0..fafd182 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -1,32 +1,24 @@ -# 🔍 Diagnostics & Error Capture +# 🔍 Diagnostics -
- -**🛠️ Automatic error screenshots and HTML snapshots** -*Debug smarter with visual evidence* - -
+**Auto-capture errors with screenshots and HTML** --- -## 🎯 What is Diagnostics? +## 💡 What Is It? -The diagnostics system **automatically captures** error screenshots and HTML snapshots when issues occur during script execution, providing visual evidence for troubleshooting. +When errors occur, the script automatically saves: +- 📸 **Screenshots** — Visual error capture +- 📄 **HTML snapshots** — Page source -### **Key Features** -- 📸 **Auto-screenshot** — Visual error capture -- 📄 **HTML snapshots** — Complete page source -- 🚦 **Rate limiting** — Prevents storage bloat -- 🗂️ **Auto-cleanup** — Configurable retention -- 🔒 **Privacy-safe** — Local storage only +Helps you debug issues without re-running the script. --- -## ⚙️ Configuration +## ⚡ Quick Start -### **Basic Setup** -Add to `src/config.jsonc`: -```json +**Already enabled by default!** + +```jsonc { "diagnostics": { "enabled": true, @@ -38,134 +30,42 @@ Add to `src/config.jsonc`: } ``` -### **Configuration Options** - -| Setting | Default | Description | -|---------|---------|-------------| -| `enabled` | `true` | Master toggle for diagnostics capture | -| `saveScreenshot` | `true` | Capture PNG screenshots on errors | -| `saveHtml` | `true` | Save page HTML content on errors | -| `maxPerRun` | `2` | Maximum captures per script run | -| `retentionDays` | `7` | Auto-delete reports older than N days | - --- -## 🚀 How It Works +## 📁 Where Are Files Saved? -### **Automatic Triggers** -The system captures when these errors occur: -- ⏱️ **Page navigation timeouts** -- 🎯 **Element selector failures** -- 🔐 **Authentication errors** -- 🌐 **Network request failures** -- ⚡ **JavaScript execution errors** - -### **Capture Process** -1. **Error Detection** — Script encounters unhandled error -2. **Visual Capture** — Screenshot + HTML snapshot -3. **Safe Storage** — Local `reports/` folder -4. **Continue Execution** — No blocking or interruption - ---- - -## 📁 File Structure - -### **Storage Organization** ``` reports/ -├── 2025-01-20/ +├── 2025-10-16/ │ ├── error_abc123_001.png │ ├── error_abc123_001.html -│ ├── error_def456_002.png -│ └── error_def456_002.html -└── 2025-01-21/ +│ └── error_def456_002.png +└── 2025-10-17/ └── ... ``` -> 🔐 Security incidents (login blocks, recovery mismatches) are stored separately under `diagnostics/security-incidents/-slug/` and always include both a screenshot and HTML snapshot for investigation. - -### **File Naming Convention** -``` -error_[runId]_[sequence].[ext] -``` -- **RunId** — Unique identifier for each script execution -- **Sequence** — Incremental counter (001, 002, etc.) -- **Extension** — `.png` for screenshots, `.html` for source +**Auto-cleanup:** Files older than 7 days are deleted automatically. --- -## 🧹 Retention Management +## 🎯 When It Captures -### **Automatic Cleanup** -- Runs after each script completion -- Deletes entire date folders older than `retentionDays` -- Prevents unlimited disk usage growth - -### **Manual Cleanup** -```powershell -# Remove all diagnostic reports -Remove-Item -Recurse -Force reports/ - -# Remove reports older than 3 days -Get-ChildItem reports/ | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-3)} | Remove-Item -Recurse -Force -``` +- ⏱️ **Timeouts** — Page navigation failures +- 🎯 **Element not found** — Selector errors +- 🔐 **Login failures** — Authentication issues +- 🌐 **Network errors** — Request failures --- -## 📊 Use Cases +## 🔧 Configuration Options -| Scenario | Benefit | -|----------|---------| -| **🐛 Development & Debugging** | Visual confirmation of page state during errors | -| **🔍 Element Detection Issues** | HTML source analysis for selector problems | -| **📈 Production Monitoring** | Evidence collection for account issues | -| **⚡ Performance Analysis** | Timeline reconstruction of automation failures | - ---- - -## ⚡ Performance Impact - -### **Resource Usage** -- **Screenshots** — ~100-500KB each -- **HTML files** — ~50-200KB each -- **CPU overhead** — Minimal (only during errors) -- **Memory impact** — Asynchronous, non-blocking - -### **Storage Optimization** -- Daily cleanup prevents accumulation -- Rate limiting via `maxPerRun` -- Configurable retention period - ---- - -## 🎛️ Environment Settings - -### **Development Mode** -```json -{ - "diagnostics": { - "enabled": true, - "maxPerRun": 5, - "retentionDays": 14 - } -} -``` - -### **Production Mode** -```json -{ - "diagnostics": { - "enabled": true, - "maxPerRun": 2, - "retentionDays": 3 - } -} -``` - -### **Debug Verbose Logging** -```powershell -$env:DEBUG_REWARDS_VERBOSE=1; npm start -``` +| Setting | Default | Description | +|---------|---------|-------------| +| `enabled` | `true` | Enable diagnostics | +| `saveScreenshot` | `true` | Capture PNG screenshots | +| `saveHtml` | `true` | Save page HTML | +| `maxPerRun` | `2` | Max captures per run | +| `retentionDays` | `7` | Auto-delete after N days | --- @@ -173,55 +73,31 @@ $env:DEBUG_REWARDS_VERBOSE=1; npm start | Problem | Solution | |---------|----------| -| **No captures despite errors** | Check `enabled: true`; verify `reports/` write permissions | -| **Excessive storage usage** | Reduce `maxPerRun`; decrease `retentionDays` | -| **Missing screenshots** | Verify browser screenshot API; check memory availability | -| **Cleanup not working** | Ensure script completes successfully for auto-cleanup | +| **No captures despite errors** | Check `enabled: true` | +| **Too many files** | Reduce `retentionDays` | +| **Permission denied** | Check `reports/` write access | -### **Common Capture Locations** -- **Login issues** — Authentication page screenshots -- **Activity failures** — Element detection errors -- **Network problems** — Timeout and connection errors -- **Navigation issues** — Page load failures +### Manual Cleanup ---- +```powershell +# Delete all diagnostic reports +Remove-Item -Recurse -Force reports/ -## 🔗 Integration - -### **With Notifications** -Diagnostics complement [Discord Webhooks](./conclusionwebhook.md) and [NTFY](./ntfy.md): -- **Webhooks** — Immediate error alerts -- **Diagnostics** — Visual evidence for investigation -- **Combined** — Complete error visibility - -### **With Development Workflow** -```bash -# 1. Run script with diagnostics -npm start - -# 2. Check for captures after errors -ls reports/$(date +%Y-%m-%d)/ - -# 3. Analyze screenshots and HTML -# Open .png files for visual state -# Review .html files for DOM structure +# Keep last 3 days only +Get-ChildItem reports/ | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-3)} | Remove-Item -Recurse ``` --- -## 🔒 Privacy & Security +## 📚 Next Steps -- **Local Only** — All captures stored locally -- **No Uploads** — Zero external data transmission -- **Account Info** — May contain sensitive data -- **Secure Storage** — Use appropriate folder permissions -- **Regular Cleanup** — Recommended for sensitive environments +**Need live notifications?** +→ **[Discord Webhooks](./conclusionwebhook.md)** +→ **[NTFY Push](./ntfy.md)** + +**Security issues?** +→ **[Security Guide](./security.md)** --- -## 🔗 Related Guides - -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Discord Webhooks](./conclusionwebhook.md)** — Error notification alerts -- **[NTFY Notifications](./ntfy.md)** — Mobile push notifications -- **[Security](./security.md)** — Privacy and data protection \ No newline at end of file +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/docker.md b/docs/docker.md index 3217740..073bb46 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,90 +1,167 @@ # 🐳 Docker Guide -
- -**⚡ Lightweight containerized deployment** -*Automated Microsoft Rewards with minimal Docker footprint* - -
+**Run the script in a container** --- -## 🚀 Quick Start Checklist +## ⚡ Quick Start -1. `src/accounts.json` populated with your Microsoft credentials -2. `src/config.jsonc` present (defaults are fine; comments stay intact) -3. Docker + Docker Compose installed locally (Desktop app or CLI) +### 1. Create Required Files + +Ensure you have: +- `src/accounts.json` with your credentials +- `src/config.jsonc` (uses defaults if missing) + +### 2. Start Container ```bash -# Build and start the container (scheduler runs automatically) +docker compose up -d +``` + +### 3. View Logs + +```bash +docker logs -f microsoft-rewards-script +``` + +**That's it!** Script runs automatically. + +--- + +## 🎯 What's Included + +The Docker setup: +- ✅ **Chromium Headless Shell** — Lightweight browser +- ✅ **Scheduler enabled** — Daily automation +- ✅ **Volume mounts** — Persistent sessions +- ✅ **Force headless** — Required for containers + +--- + +## 📁 Mounted Volumes + +| Host Path | Container Path | Purpose | +|-----------|----------------|---------| +| `./src/accounts.json` | `/usr/src/.../accounts.json` | Account credentials (read-only) | +| `./src/config.jsonc` | `/usr/src/.../config.json` | Configuration (read-only) | +| `./sessions` | `/usr/src/.../sessions` | Cookies & fingerprints | + +--- + +## 🌍 Environment Variables + +### Set Timezone + +```yaml +services: + rewards: + environment: + TZ: Europe/Paris +``` + +### Use Inline JSON + +```bash +docker run -e ACCOUNTS_JSON='{"accounts":[...]}' ... +``` + +### Custom Config Path + +```bash +docker run -e ACCOUNTS_FILE=/custom/path/accounts.json ... +``` + +--- + +## 🔧 Common Commands + +```bash +# Start container docker compose up -d -# Stream logs from the running container +# View logs docker logs -f microsoft-rewards-script -# Stop the stack when you are done +# Stop container docker compose down -``` -The compose file uses the same Playwright build as local runs but forces headless mode inside the container via `FORCE_HEADLESS=1`, matching the bundled image. +# Rebuild image +docker compose build --no-cache + +# Restart container +docker compose restart +``` --- -## 📦 What the Compose File Mounts +## 🛠️ Troubleshooting -| Host path | Container path | Purpose | -|-----------|----------------|---------| -| `./src/accounts.json` | `/usr/src/microsoft-rewards-script/accounts.json` | Read-only account credentials | -| `./src/config.jsonc` | `/usr/src/microsoft-rewards-script/config.json` | Read-only runtime configuration | -| `./sessions` | `/usr/src/microsoft-rewards-script/sessions` | Persisted cookies & fingerprints | +| Problem | Solution | +|---------|----------| +| **"accounts.json not found"** | Mount file in `docker-compose.yml` | +| **"Browser launch failed"** | Ensure `FORCE_HEADLESS=1` is set | +| **"Permission denied"** | Check file permissions (`chmod 644`) | +| **Scheduler not running** | Verify `schedule.enabled: true` in config | -Prefer environment variables? The loader accepts the same overrides as local runs: +### Debug Container ```bash -ACCOUNTS_FILE=/custom/accounts.json -ACCOUNTS_JSON='[{"email":"name@example.com","password":"hunter2"}]' +# Enter container shell +docker exec -it microsoft-rewards-script /bin/bash + +# Check Node.js version +docker exec -it microsoft-rewards-script node --version + +# View config +docker exec -it microsoft-rewards-script cat config.json ``` --- -## 🌍 Useful Environment Variables +## 🎛️ Custom Configuration -- `TZ` — set container timezone (`Europe/Paris`, `America/New_York`, etc.) -- `NODE_ENV=production` — default; keeps builds lean -- `FORCE_HEADLESS=1` — required in Docker (Chromium Headless Shell only) -- Scheduler tuning (optional): - - `SCHEDULER_DAILY_JITTER_MINUTES_MIN` / `SCHEDULER_DAILY_JITTER_MINUTES_MAX` - - `SCHEDULER_PASS_TIMEOUT_MINUTES` - - `SCHEDULER_FORK_PER_PASS` +### Use Built-in Scheduler + +**Default** `docker-compose.yml`: +```yaml +services: + rewards: + build: . + command: ["npm", "run", "start:schedule"] +``` + +### Single Run (Manual) + +```yaml +services: + rewards: + build: . + command: ["node", "./dist/index.js"] +``` + +### External Cron (Alternative) + +```yaml +services: + rewards: + environment: + CRON_SCHEDULE: "0 7,16,20 * * *" + RUN_ON_START: "true" +``` --- -## 🧠 Browser Footprint +## 📚 Next Steps -The Docker image installs Chromium Headless Shell via `npx playwright install --with-deps --only-shell`. This keeps the image compact while retaining Chromium’s Edge-compatible user agent. Installing full Edge or all browsers roughly doubles the footprint and adds instability, so we ship only the shell. +**Need 2FA?** +→ **[Accounts & TOTP Setup](./accounts.md)** + +**Want notifications?** +→ **[Discord Webhooks](./conclusionwebhook.md)** + +**Scheduler config?** +→ **[Scheduler Guide](./schedule.md)** --- -## 🔁 Alternate Commands - -- **Default:** `npm run start:schedule` (inside container) — keeps the scheduler alive -- **Single pass:** `docker compose run --rm app node ./dist/index.js` -- **Custom script:** Override `command:` in `compose.yaml` to suit your workflow - ---- - -## 💡 Tips - -- Add TOTP secrets to `accounts.json` so the bot can respond to MFA prompts automatically -- Keep the `sessions` volume; deleting it forces fresh logins and can trigger security reviews -- Mixing proxies? Configure per-account proxies in `accounts.json` (see [Proxy Setup](./proxy.md)) -- Want notifications? Layer [NTFY](./ntfy.md) or [Discord Webhooks](./conclusionwebhook.md) on top once the container is stable - ---- - -## 🔗 Related Guides - -- **[Getting Started](./getting-started.md)** — Prep work before switching to containers -- **[Accounts & 2FA](./accounts.md)** — Ensure every account can pass MFA headlessly -- **[Scheduler](./schedule.md)** — If you prefer a host-side cron instead of Docker -- **[Diagnostics](./diagnostics.md)** — Capture logs and debug a failing container \ No newline at end of file +**[← Back to Hub](./index.md)** | **[Getting Started](./getting-started.md)** diff --git a/docs/getting-started.md b/docs/getting-started.md index 89c9c67..5ce7187 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,136 +1,148 @@ # 🚀 Getting Started -
- -**🎯 From zero to earning Microsoft Rewards points in minutes** -*Complete setup guide for beginners* - -
+**From zero to your first run in 10 minutes** --- ## ✅ Requirements -- **Node.js 18+** (22 recommended) — [Download here](https://nodejs.org/) +- **Node.js 20+** → [Download here](https://nodejs.org/) - **Microsoft accounts** with email + password -- **Optional:** Docker for containerized deployment +- *Optional:* Docker for containers --- ## ⚡ Quick Setup (Recommended) -
- -### **🎬 One Command, Total Automation** - -
- -```bash -# 🪟 Windows -setup/setup.bat - -# 🐧 Linux/macOS/WSL -bash setup/setup.sh - -# 🌍 Any platform -npm run setup +### Windows +```powershell +setup\setup.bat ``` -**That's it!** The wizard will: -- ✅ Help you create `src/accounts.json` with your Microsoft credentials -- ✅ Install all dependencies automatically -- ✅ Build the TypeScript project -- ✅ Start earning points immediately +### Linux / macOS +```bash +bash setup/setup.sh +``` + +### What Does It Do? + +1. ✅ Asks for your Microsoft credentials +2. ✅ Creates `accounts.json` automatically +3. ✅ Installs dependencies +4. ✅ Builds the project +5. ✅ Runs your first automation (optional) + +**That's it! 🎉** --- -## 🛠️ Manual Setup +## 🎯 After Installation + +### 1️⃣ Enable Scheduler (Recommended) + +Run automatically once per day: + +**Edit** `src/config.jsonc`: +```jsonc +{ + "schedule": { + "enabled": true, + "time": "09:00", + "timeZone": "America/New_York" + } +} +``` + +**Start scheduler:** +```bash +npm run start:schedule +``` + +→ **[Full Scheduler Guide](./schedule.md)** + +--- + +### 2️⃣ Add Notifications (Optional) + +Get a summary after each run: + +```jsonc +{ + "conclusionWebhook": { + "enabled": true, + "url": "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL" + } +} +``` + +→ **[Discord Setup](./conclusionwebhook.md)** | **[NTFY Setup](./ntfy.md)** + +--- + +### 3️⃣ Enable Humanization (Anti-Ban) + +More natural behavior: + +```jsonc +{ + "humanization": { + "enabled": true + } +} +``` + +→ **[Humanization Guide](./humanization.md)** + +--- + +## 🛠️ Common Issues + +| Problem | Solution | +|---------|----------| +| **"Node.js not found"** | Install Node.js 20+ and restart terminal | +| **"accounts.json missing"** | Run `setup/setup.bat` or create manually | +| **"Login failed"** | Check email/password in `accounts.json` | +| **"2FA prompt"** | Add TOTP secret → [2FA Guide](./accounts.md) | +| **Script crashes** | Check [Diagnostics Guide](./diagnostics.md) | + +--- + +## 🔧 Manual Setup (Advanced)
-📖 Prefer step-by-step? Click here +Click to expand -### 1️⃣ **Configure Your Accounts** ```bash +# 1. Configure accounts cp src/accounts.example.json src/accounts.json -# Edit accounts.json with your Microsoft credentials -``` +# Edit accounts.json with your credentials -### 2️⃣ **Install Dependencies & Build** -```bash +# 2. Install & build npm install npm run build -``` -### 3️⃣ **Choose Your Mode** -```bash -# Single run (test it works) +# 3. Run npm start - -# Automated daily scheduler (set and forget) -npm run start:schedule ```
--- -## 🎯 What Happens Next? +## 📚 Next Steps -The script will automatically: -- 🔍 **Search Bing** for points (desktop + mobile) -- 📅 **Complete daily sets** (quizzes, polls, activities) -- 🎁 **Grab promotions** and bonus opportunities -- 🃏 **Work on punch cards** (multi-day challenges) -- ✅ **Daily check-ins** for easy points -- 📚 **Read articles** for additional rewards +**Everything works?** +→ **[Setup Scheduler](./schedule.md)** for daily automation -**All while looking completely natural to Microsoft!** 🤖 +**Need 2FA?** +→ **[Accounts & TOTP Guide](./accounts.md)** + +**Want Docker?** +→ **[Docker Guide](./docker.md)** + +**Having issues?** +→ **[Diagnostics](./diagnostics.md)** --- -## 🐳 Docker Alternative - -If you prefer containers: - -```bash -# Ensure accounts.json and config.jsonc exist -docker compose up -d - -# Follow logs -docker logs -f microsoft-rewards-script -``` - -**[Full Docker Guide →](./docker.md)** - ---- - -## 🔧 Next Steps - -Once running, explore these guides: - -| Priority | Guide | Why Important | -|----------|-------|---------------| -| **High** | **[Accounts & 2FA](./accounts.md)** | Set up TOTP for secure automation | -| **High** | **[Scheduling](./schedule.md)** | Configure automated daily runs | -| **Medium** | **[Notifications](./ntfy.md)** | Get alerts on your phone | -| **Low** | **[Humanization](./humanization.md)** | Advanced anti-detection | - ---- - -## 🆘 Need Help? - -**Script not starting?** → [Troubleshooting Guide](./diagnostics.md) -**Login issues?** → [Accounts & 2FA Setup](./accounts.md) -**Want Docker?** → [Container Guide](./docker.md) - -**Found a bug?** [Report it here](https://github.com/TheNetsky/Microsoft-Rewards-Script/issues) -**Need support?** [Join our Discord](https://discord.gg/KRBFxxsU) - ---- - -## 🔗 Related Guides - -- **[Accounts & 2FA](./accounts.md)** — Add Microsoft accounts with TOTP -- **[Docker](./docker.md)** — Deploy with containers -- **[Scheduler](./schedule.md)** — Automate daily execution -- **[Discord Webhooks](./conclusionwebhook.md)** — Get run summaries \ No newline at end of file +**[← Back to Hub](./index.md)** | **[All Docs](./index.md)** diff --git a/docs/humanization.md b/docs/humanization.md index 2533aa1..c77e3ea 100644 --- a/docs/humanization.md +++ b/docs/humanization.md @@ -1,32 +1,24 @@ -# 🤖 Humanization (Human Mode) +# 🤖 Humanization -
- -**🎭 Natural automation that mimics human behavior** -*Subtle gestures for safer operation* - -
+**Make automation look natural to avoid detection** --- -## 🎯 What is Humanization? +## 💡 What Is It? -Human Mode adds **subtle human-like behavior** to make your automation look and feel more natural. It's designed to be **safe by design** with minimal, realistic gestures. +Humanization adds **random delays** and **subtle gestures** to mimic real human behavior. -### **Key Features** -- 🎲 **Random delays** — Natural pause variation -- 🖱️ **Micro movements** — Subtle mouse gestures -- 📜 **Tiny scrolls** — Minor page adjustments -- ⏰ **Time windows** — Run during specific hours -- 📅 **Random off days** — Skip days naturally -- 🔒 **Safe by design** — Never clicks random elements +### Why Use It? +- ✅ **Lower detection risk** — Looks less like a bot +- ✅ **Natural patterns** — Random timing, mouse moves +- ✅ **Built-in** — No configuration needed --- -## ⚙️ Configuration +## ⚡ Quick Start -### **Simple Setup (Recommended)** -```json +**Edit** `src/config.jsonc`: +```jsonc { "humanization": { "enabled": true @@ -34,229 +26,62 @@ Human Mode adds **subtle human-like behavior** to make your automation look and } ``` -### **Advanced Configuration** -```json +**That's it!** Default settings work for most users. + +--- + +## 🎯 What It Does + +### Random Delays +- **150-450ms pauses** between actions +- Mimics human decision-making time +- Prevents robotic patterns + +### Subtle Gestures +- **Mouse movements** — Small cursor adjustments (40% chance) +- **Scrolling** — Minor page movements (20% chance) +- **Never clicks** random elements (safe by design) + +### Temporal Patterns +- **Random off days** — Skip 1 day per week by default +- **Time windows** — Run only during certain hours (optional) + +--- + +## 🎛️ Presets + +### Default (Recommended) +```jsonc { "humanization": { - "enabled": true, - "actionDelay": { "min": 150, "max": 450 }, - "gestureMoveProb": 0.4, - "gestureScrollProb": 0.2, - "allowedWindows": ["08:00-10:30", "20:00-22:30"], - "randomOffDaysPerWeek": 1 + "enabled": true } } ``` -### **Configuration Options** - -| Setting | Default | Description | -|---------|---------|-------------| -| `enabled` | `true` | Master toggle for all humanization | -| `actionDelay` | `{min: 150, max: 450}` | Random pause between actions (ms) | -| `gestureMoveProb` | `0.4` | Probability (0-1) for tiny mouse moves | -| `gestureScrollProb` | `0.2` | Probability (0-1) for minor scrolls | -| `allowedWindows` | `[]` | Time windows for script execution | -| `randomOffDaysPerWeek` | `1` | Skip N random days per week. Set to `0` to disable (scheduler logs reference this setting explicitly). | +Balanced safety and speed. --- -## 🎭 How It Works - -### **Action Delays** -- **Random pauses** between automation steps -- **Natural variation** mimics human decision time -- **Configurable range** allows fine-tuning - -### **Gesture Simulation** -- **Micro mouse moves** — Tiny cursor adjustments (safe zones only) -- **Minor scrolls** — Small page movements (non-interactive areas) -- **Probability-based** — Not every action includes gestures -- **Centralized controller** — The `Humanizer` service now drives all gesture + pause behavior so every module uses the same probabilities and timing windows. - -### **Temporal Patterns** -- **Time windows** — Only run during specified hours -- **Random off days** — Skip days to avoid rigid patterns -- **Natural scheduling** — Mimics human usage patterns - ---- - -## 🎯 Usage Examples - -### **Default Setup (Recommended)** -```json -{ - "humanization": { "enabled": true } -} -``` -✅ **Best for most users** — Balanced safety and naturalness - -### **Minimal Humanization** -```json -{ - "humanization": { - "enabled": true, - "gestureMoveProb": 0.1, - "gestureScrollProb": 0.1, - "actionDelay": { "min": 100, "max": 200 } - } -} -``` -⚡ **Faster execution** with minimal gestures - -### **Maximum Natural Behavior** -```json +### Conservative (More Natural) +```jsonc { "humanization": { "enabled": true, "actionDelay": { "min": 300, "max": 800 }, "gestureMoveProb": 0.6, "gestureScrollProb": 0.4, - "allowedWindows": ["08:30-11:00", "19:00-22:00"], - "randomOffDaysPerWeek": 2 - } -} -``` -🎭 **Most human-like** but slower execution - -### **Disabled Humanization** -```json -{ - "humanization": { "enabled": false } -} -``` -🚀 **Fastest execution** — automation optimized - ---- - -## ⏰ Time Windows - -### **Setup** -```json -{ - "humanization": { - "enabled": true, - "allowedWindows": ["08:00-10:30", "20:00-22:30"] - } -} -``` - -### **Behavior** -- Script **waits** until next allowed window -- Uses **local time** for scheduling -- **Multiple windows** supported per day -- **Empty array** `[]` = no time restrictions - -### **Examples** -```json -// Morning and evening windows -"allowedWindows": ["08:00-10:30", "20:00-22:30"] - -// Lunch break only -"allowedWindows": ["12:00-13:00"] - -// Extended evening window -"allowedWindows": ["18:00-23:00"] - -// No restrictions -"allowedWindows": [] -``` - ---- - -## 📅 Random Off Days - -### **Purpose** -Mimics natural human behavior by skipping random days per week. - -### **Configuration** -```json -{ - "humanization": { - "randomOffDaysPerWeek": 1 // Skip 1 random day per week - } -} -``` - -### **Options** -- `0` — Never skip days -- `1` — Skip 1 random day per week (default) -- `2` — Skip 2 random days per week -- `3+` — Higher values for more irregular patterns - ---- - -## 🔒 Safety Features - -### **Safe by Design** -- ✅ **Never clicks** arbitrary elements -- ✅ **Gestures only** in safe zones -- ✅ **Minor movements** — pixel-level adjustments -- ✅ **Probability-based** — Natural randomness -- ✅ **Non-interactive areas** — Avoids clickable elements - -### **Buy Mode Compatibility** -- **Passive monitoring** remains unaffected -- **No interference** with manual actions -- **Background tasks** only for monitoring - ---- - -## 📊 Performance Impact - -| Setting | Speed Impact | Natural Feel | Recommendation | -|---------|--------------|--------------|----------------| -| **Disabled** | Fastest | Robotic | Development only | -| **Default** | Moderate | Balanced | **Recommended** | -| **High probability** | Slower | Very natural | Conservative users | -| **Time windows** | Delayed start | Realistic | Scheduled execution | - ---- - -## 🛠️ Troubleshooting - -| Problem | Solution | -|---------|----------| -| **Script too slow** | Reduce `actionDelay` values; lower probabilities | -| **Too robotic** | Increase probabilities; add time windows | -| **Runs outside hours** | Check `allowedWindows` format (24-hour time) | -| **Skipping too many days** | Reduce `randomOffDaysPerWeek` | -| **Gestures interfering** | Lower probabilities or disable specific gestures | - -### **Debug Humanization** -```powershell -$env:DEBUG_HUMANIZATION=1; npm start -``` - ---- - -## 🎛️ Presets - -### **Conservative** -```json -{ - "humanization": { - "enabled": true, - "actionDelay": { "min": 200, "max": 600 }, - "gestureMoveProb": 0.6, - "gestureScrollProb": 0.4, - "allowedWindows": ["08:00-10:00", "20:00-22:00"], "randomOffDaysPerWeek": 2 } } ``` -### **Balanced (Default)** -```json -{ - "humanization": { - "enabled": true - } -} -``` +Slower but safer. -### **Performance** -```json +--- + +### Fast (Less Natural) +```jsonc { "humanization": { "enabled": true, @@ -268,11 +93,68 @@ $env:DEBUG_HUMANIZATION=1; npm start } ``` +Faster execution, higher risk. + --- -## 🔗 Related Guides +## ⏰ Time Windows (Optional) -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Scheduler](./schedule.md)** — Automated timing and execution -- **[Security](./security.md)** — Privacy and detection avoidance -- **[Buy Mode](./buy-mode.md)** — Manual purchasing with monitoring +Run only during specific hours: + +```jsonc +{ + "humanization": { + "enabled": true, + "allowedWindows": ["08:00-10:30", "20:00-22:30"] + } +} +``` + +Script **waits** until next allowed window if started outside. + +--- + +## 📅 Random Off Days + +Skip random days per week: + +```jsonc +{ + "humanization": { + "enabled": true, + "randomOffDaysPerWeek": 1 // Skip 1 random day/week + } +} +``` + +**Options:** +- `0` — Never skip days +- `1` — Skip 1 day/week (default) +- `2` — Skip 2 days/week + +--- + +## 🛠️ Troubleshooting + +| Problem | Solution | +|---------|----------| +| **Too slow** | Lower `actionDelay`, reduce probabilities | +| **Too fast/robotic** | Increase delays, higher probabilities | +| **Not running at all** | Check `allowedWindows` time format | + +--- + +## 📚 Next Steps + +**Need vacation mode?** +→ See [Scheduler Vacation](./schedule.md#vacation-mode) + +**Want scheduling?** +→ **[Scheduler Guide](./schedule.md)** + +**More security?** +→ **[Security Guide](./security.md)** + +--- + +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/index.md b/docs/index.md index bcacca4..7224f39 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,62 +1,79 @@ -# 📚 Microsoft Rewards Script V2 Docs +# 📚 Documentation Hub -
- -**🎯 Your companion for mastering the automation stack** -*Curated guides, verified against the current codebase* - -
+**Complete guide to automate Microsoft Rewards** --- -## 🚀 Quick Navigation +## 🚀 Start Here (In Order) -### **Essential Setup** -| Guide | Why you should read it | -|-------|------------------------| -| **[🎬 Getting Started](./getting-started.md)** | Install, configure, and run the bot in minutes | -| **[👤 Accounts & 2FA](./accounts.md)** | Add Microsoft accounts, enable TOTP, and secure logins | -| **[⚙️ Configuration Reference](./config.md)** | Understand every option in `src/config.jsonc` | +### For Complete Beginners -### **Run & Operate** -| Guide | Focus | -|-------|-------| -| **[⏰ Scheduling](./schedule.md)** | Cron-style automation and daily cadence | -| **[🐳 Docker](./docker.md)** | Container deployment with prewired headless settings | -| **[🛠️ Diagnostics](./diagnostics.md)** | Troubleshooting, log capture, and support checklist | -| **[🧠 Humanization](./humanization.md)** | Natural browser behavior and ban avoidance | -| **[🌐 Proxy Setup](./proxy.md)** | Per-account proxy routing and geo-tuning | -| **[📊 Job State](./jobstate.md)** | How runs persist progress and recover | -| **[🔄 Auto Update](./update.md)** | Keep the script current without manual pulls | -| **[🛡️ Security Notes](./security.md)** | Threat model, secrets handling, and best practices | +1. **[Getting Started](./getting-started.md)** — Install and run in 10 minutes +2. **[Accounts & 2FA](./accounts.md)** — Add your Microsoft accounts +3. **[Basic Config](./config.md#quick-start)** — 5 essential options +4. **[Scheduler](./schedule.md#quick-start)** — Automate daily runs -### **Notifications & Control** -| Guide | Purpose | -|-------|---------| -| **[📱 NTFY Push](./ntfy.md)** | Real-time phone notifications | -| **[� Discord Webhooks](./conclusionwebhook.md)** | Detailed run summaries in your server | - -### **Special Modes** -| Guide | Purpose | -|-------|---------| -| **[💳 Buy Mode](./buy-mode.md)** | Assisted manual redemption and live monitoring | +**You're all set! 🎉** --- -## 🧭 Reading Paths +## 🔥 Popular Features -- **First install:** Getting Started → Accounts & 2FA → Configuration Reference → Scheduling **or** Docker -- **Docker-first:** Getting Started prerequisites → Docker → Diagnostics → Notifications (NTFY or Webhooks) -- **Optimizing runs:** Humanization → Schedule tuning → Proxy → Job State → Update +### Notifications & Monitoring +- **[Discord Webhooks](./conclusionwebhook.md)** — Get run summaries +- **[NTFY Push](./ntfy.md)** — Mobile alerts -Each guide now links back to the most relevant follow-up topics so you can jump between setup, operations, and troubleshooting without losing context. +### Anti-Ban & Privacy +- **[Humanization](./humanization.md)** — Natural behavior simulation +- **[Proxy Setup](./proxy.md)** — Change your IP (optional) + +### Deployment +- **[Docker](./docker.md)** — Container deployment +- **[Diagnostics](./diagnostics.md)** — Troubleshooting --- -## 🔗 Useful Shortcuts +## 📖 All Documentation -- Need sample configs? → [Config presets](./config-presets/) -- Want a scripted environment? → [Scheduler](./schedule.md) -- Looking to self-audit? → [Diagnostics](./diagnostics.md) + [Security](./security.md) +### Configuration & Setup +- [Complete Configuration Reference](./config.md) — All options explained +- [Scheduler Setup](./schedule.md) — Automated timing +- [Job State](./jobstate.md) — Progress tracking +- [Auto-Update](./update.md) — Keep script current -If something feels out of sync with the code, open an issue or ping us on Discord—the docs are maintained to match the current defaults (`src/config.jsonc`, visible browsers by default, Docker headless enforcement via `FORCE_HEADLESS=1`). +### Advanced Features +- [Buy Mode](./buy-mode.md) — Manual purchase monitoring +- [Security Guide](./security.md) — Privacy & incident response + +--- + +## 🆘 Need Help? + +**Technical issue?** → [Diagnostics Guide](./diagnostics.md) +**Login problem?** → [Accounts & 2FA](./accounts.md#troubleshooting) +**Banned?** → [Security Guide](./security.md) + +**Join Discord** → [Support Server](https://discord.gg/KRBFxxsU) + +--- + +## 🎯 Quick Links by Use Case + +### "I just installed the script" +→ [Getting Started](./getting-started.md) → [Accounts](./accounts.md) → [Scheduler](./schedule.md) + +### "I want daily automation" +→ [Scheduler Guide](./schedule.md) → [Humanization](./humanization.md) + +### "I need notifications" +→ [Discord Webhooks](./conclusionwebhook.md) or [NTFY](./ntfy.md) + +### "I want to use Docker" +→ [Docker Guide](./docker.md) + +### "Something's broken" +→ [Diagnostics](./diagnostics.md) → [Security](./security.md) + +--- + +**[← Back to README](../README.md)** diff --git a/docs/jobstate.md b/docs/jobstate.md index 8ea189d..f92e052 100644 --- a/docs/jobstate.md +++ b/docs/jobstate.md @@ -1,339 +1,112 @@ -# 💾 Job State Persistence +# 💾 Job State -
- -**🔄 Resume interrupted tasks and track progress across runs** -*Never lose your progress again* - -
+**Resume interrupted tasks automatically** --- -## 🎯 What is Job State Persistence? +## 💡 What Is It? -Job state persistence allows the script to **resume interrupted tasks** and **track progress** across multiple runs, ensuring no work is lost when the script is stopped or crashes. +Saves progress after each completed task. If script crashes or stops, it resumes exactly where it left off. -### **Key Features** -- 🔄 **Resumable tasks** — Pick up exactly where you left off -- 📅 **Daily tracking** — Date-specific progress monitoring -- 👤 **Per-account isolation** — Independent progress for each account -- 🛡️ **Corruption protection** — Atomic writes prevent data loss -- 🚀 **Performance optimized** — Minimal overhead +**Already enabled by default!** + +--- + +## ⚡ How It Works + +### Progress Tracking + +``` +sessions/job-state/ +├── account1@email.com/ +│ ├── daily-set-2025-10-16.json +│ ├── desktop-search-2025-10-16.json +│ └── mobile-search-2025-10-16.json +└── account2@email.com/ + └── ... +``` + +- ✅ **Per-account** — Independent progress +- ✅ **Date-specific** — Fresh start each day +- ✅ **Auto-cleanup** — Old files remain for history + +--- + +## 🎯 Benefits + +### Interrupted Runs + +| Scenario | Without Job State | With Job State | +|----------|-------------------|----------------| +| **Power outage** | Start from beginning | Resume from last task | +| **Manual stop** | Lose all progress | Pick up where left off | +| **Network failure** | Redo everything | Continue remaining tasks | --- ## ⚙️ Configuration -### **Basic Setup** -```json +**Already enabled:** +```jsonc { "jobState": { "enabled": true, - "dir": "" + "dir": "" // Empty = use default location } } ``` -### **Configuration Options** - -| Setting | Description | Default | -|---------|-------------|---------| -| `enabled` | Enable job state persistence | `true` | -| `dir` | Custom directory for state files | `""` (uses `sessions/job-state`) | - ---- - -## 🏗️ How It Works - -### **State Tracking** -- 📋 **Monitors completion** status of individual activities -- 🔍 **Tracks progress** for daily sets, searches, and promotional tasks -- ❌ **Prevents duplicates** when script restarts - -### **Storage Structure** -``` -sessions/job-state/ -├── account1@email.com/ -│ ├── daily-set-2025-01-20.json -│ ├── desktop-search-2025-01-20.json -│ └── mobile-search-2025-01-20.json -└── account2@email.com/ - ├── daily-set-2025-01-20.json - └── promotional-tasks-2025-01-20.json -``` - -### **State File Format** -```json +**Custom location:** +```jsonc { - "date": "2025-01-20", - "account": "user@email.com", - "type": "daily-set", - "completed": [ - "daily-check-in", - "quiz-1", - "poll-1" - ], - "remaining": [ - "quiz-2", - "search-desktop" - ], - "lastUpdate": "2025-01-20T10:30:00.000Z" + "jobState": { + "enabled": true, + "dir": "/custom/path/job-state" + } } ``` --- -## 🚀 Key Benefits +## 🧹 Maintenance -### **Resumable Tasks** -- ✅ **Script restarts** pick up where they left off -- ✅ **Individual completion** is remembered -- ✅ **Avoid re-doing** completed activities +### Reset Progress (Fresh Start) -### **Daily Reset** -- 📅 **Date-specific** state files -- 🌅 **New day** automatically starts fresh tracking -- 📚 **History preserved** for analysis - -### **Account Isolation** -- 👤 **Separate state** per account -- ⚡ **Parallel processing** doesn't interfere -- 📊 **Independent progress** tracking - ---- - -## 📋 Use Cases - -### **Interrupted Executions** -| Scenario | Benefit | -|----------|---------| -| **Network issues** | Resume when connection restored | -| **System reboots** | Continue after restart | -| **Manual termination** | Pick up from last checkpoint | -| **Resource exhaustion** | Recover without losing progress | - -### **Selective Reruns** -| Feature | Description | -|---------|-------------| -| **Skip completed sets** | Avoid redoing finished daily activities | -| **Resume searches** | Continue partial search sessions | -| **Retry failed tasks** | Target only problematic activities | -| **Account targeting** | Process specific accounts only | - -### **Progress Monitoring** -- 📊 **Track completion rates** across accounts -- 🔍 **Identify problematic** activities -- ⏱️ **Monitor task duration** trends -- 🐛 **Debug stuck** or slow tasks - ---- - -## 🛠️ Technical Implementation - -### **Checkpoint Strategy** -- 💾 **State saved** after each completed activity -- ⚛️ **Atomic writes** prevent corruption -- 🔒 **Lock-free design** for concurrent access - -### **Performance Optimization** -- ⚡ **Minimal I/O overhead** — Fast state updates -- 🧠 **In-memory caching** — Reduce disk access -- 📥 **Lazy loading** — Load state files on demand - -### **Error Handling** -- 🔧 **Corrupted files** are rebuilt automatically -- 📁 **Missing directories** created as needed -- 🎯 **Graceful degradation** when disabled - ---- - -## 🗂️ File Management - -### **Automatic Behavior** -- 📅 **Date-specific files** — New files for each day -- 💾 **Preserved history** — Old files remain for reference -- 🚀 **No auto-deletion** — Manual cleanup recommended - -### **Manual Maintenance** ```powershell -# Clean state files older than 7 days -Get-ChildItem sessions/job-state -Recurse -Filter "*.json" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item - -# Reset all job state (start fresh) +# Reset all accounts Remove-Item -Recurse -Force sessions/job-state/ -# Reset specific account state +# Reset one account Remove-Item -Recurse -Force sessions/job-state/user@email.com/ ``` ---- +### Cleanup Old Files -## 📊 Example Workflows - -### **Interrupted Daily Run** -``` -Day 1 - 10:30 AM: -✅ Account A: Daily set completed -🔄 Account B: 3/5 daily tasks done -❌ Script crashes - -Day 1 - 2:00 PM: -🚀 Script restarts -✅ Account A: Skipped (already complete) -🔄 Account B: Resumes with 2 remaining tasks -``` - -### **Multi-Day Tracking** -``` -Monday: -📅 daily-set-2025-01-20.json created -✅ All tasks completed - -Tuesday: -📅 daily-set-2025-01-21.json created -🔄 Fresh start for new day -📚 Monday's progress preserved -``` - ---- - -## 🔍 Debugging Job State - -### **State Inspection** ```powershell -# View current state for account -Get-Content sessions/job-state/user@email.com/daily-set-2025-01-20.json | ConvertFrom-Json - -# List all state files -Get-ChildItem sessions/job-state -Recurse -Filter "*.json" -``` - -### **Debug Output** -Enable verbose logging to see state operations: -```powershell -$env:DEBUG_REWARDS_VERBOSE=1; npm start -``` - -Sample output: -``` -[INFO] Loading job state for user@email.com (daily-set) -[INFO] Found 3 completed tasks, 2 remaining -[INFO] Skipping completed task: daily-check-in -[INFO] Starting task: quiz-2 +# Keep last 7 days only +Get-ChildItem sessions/job-state -Recurse -Filter "*.json" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item ``` --- ## 🛠️ Troubleshooting -| Problem | Cause | Solution | -|---------|-------|----------| -| **Tasks not resuming** | Missing/corrupt state files | Check file permissions; verify directory exists | -| **Duplicate execution** | Clock sync issues | Ensure system time is accurate | -| **Excessive files** | No cleanup schedule | Implement regular state file cleanup | -| **Permission errors** | Write access denied | Verify sessions/ directory is writable | - -### **Common Issues** - -#### **Tasks Not Resuming** -``` -[ERROR] Failed to load job state: Permission denied -``` -**Solutions:** -- ✅ Check file/directory permissions -- ✅ Verify state directory exists -- ✅ Ensure write access to sessions/ - -#### **Duplicate Task Execution** -``` -[WARN] Task appears to be running twice -``` -**Solutions:** -- ✅ Check for corrupt state files -- ✅ Verify system clock synchronization -- ✅ Clear state for affected account - -#### **Storage Growth** -``` -[INFO] Job state directory: 2.3GB (1,247 files) -``` -**Solutions:** -- ✅ Implement regular cleanup schedule -- ✅ Remove old state files (7+ days) -- ✅ Monitor disk space usage +| Problem | Solution | +|---------|----------| +| **Tasks not resuming** | Check file permissions | +| **Duplicate execution** | Ensure system time is accurate | +| **Excessive files** | Implement cleanup schedule | --- -## 🤝 Integration Features +## 📚 Next Steps -### **Session Persistence** -- 🍪 **Works alongside** browser session storage -- 🔐 **Complements** cookie and fingerprint persistence -- 🌐 **Independent of** proxy and authentication state +**Need scheduler?** +→ **[Scheduler Guide](./schedule.md)** -### **Clustering** -- ⚡ **Isolated state** per cluster worker -- 🚫 **No shared state** between parallel processes -- 📁 **Worker-specific** directories - -### **Scheduling** -- ⏰ **Persists across** scheduled runs -- 🌅 **Daily reset** at midnight automatically -- 🔄 **Long-running continuity** maintained +**Want diagnostics?** +→ **[Diagnostics Guide](./diagnostics.md)** --- -## ⚙️ Advanced Configuration - -### **Custom State Directory** -```json -{ - "jobState": { - "enabled": true, - "dir": "/custom/path/to/state" - } -} -``` - -### **Disabling Job State** -```json -{ - "jobState": { - "enabled": false - } -} -``` - -**Effects when disabled:** -- ❌ **Tasks restart** from beginning each run -- ❌ **No progress tracking** between sessions -- ❌ **Potential duplicate work** on interruptions -- ✅ **Slightly faster startup** (no state loading) - ---- - -## 📊 Best Practices - -### **Development** -- ✅ **Enable for testing** — Consistent behavior -- 🧹 **Clear between changes** — Fresh state for major updates -- 🔍 **Monitor for debugging** — State files reveal execution flow - -### **Production** -- ✅ **Always enabled** — Reliability is critical -- 💾 **Regular backups** — State directory backups -- 📊 **Monitor disk usage** — Prevent storage growth - -### **Maintenance** -- 🗓️ **Weekly cleanup** — Remove old state files -- 🔍 **Health checks** — Verify state integrity -- 📝 **Usage monitoring** — Track storage trends - ---- - -## 🔗 Related Guides - -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Scheduler](./schedule.md)** — Automated timing and execution -- **[Diagnostics](./diagnostics.md)** — Error capture and debugging -- **[Security](./security.md)** — Privacy and data protection \ No newline at end of file +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/ntfy.md b/docs/ntfy.md index bbd3f9e..d6cdc59 100644 --- a/docs/ntfy.md +++ b/docs/ntfy.md @@ -1,86 +1,79 @@ # 📱 NTFY Push Notifications -
- -**🔔 Real-time push notifications to your devices** -*Stay informed wherever you are* - -
+**Get alerts on your phone instantly** --- -## 🎯 What is NTFY? +## 💡 What Is NTFY? -NTFY is a **simple HTTP-based pub-sub notification service** that sends push notifications to your phone, desktop, or web browser. Perfect for real-time alerts about script events and errors. +Simple push notification service that sends alerts to your phone/desktop. -### **Key Features** -- 📱 **Mobile & Desktop** — Push to any device -- 🆓 **Free & Open Source** — No vendor lock-in -- 🏠 **Self-hostable** — Complete privacy control -- ⚡ **Real-time delivery** — Instant notifications -- 🔒 **Authentication support** — Secure topics - -### **Official Links** -- **Website** — [ntfy.sh](https://ntfy.sh) -- **Documentation** — [docs.ntfy.sh](https://docs.ntfy.sh) -- **GitHub** — [binwiederhier/ntfy](https://github.com/binwiederhier/ntfy) +**Free to use:** No account required for basic features. --- -## ⚙️ Configuration +## ⚡ Quick Start -### **Basic Setup** -```json +### 1. Install NTFY App + +- **Android:** [Google Play](https://play.google.com/store/apps/details?id=io.heckel.ntfy) +- **iOS:** [App Store](https://apps.apple.com/app/ntfy/id1625396347) + +### 2. Choose a Topic Name + +Pick any unique name (e.g., `rewards-myname-2025`) + +### 3. Subscribe in App + +Open NTFY app → Add subscription → Enter your topic name + +### 4. Configure Script + +**Edit** `src/config.jsonc`: +```jsonc { "notifications": { "ntfy": { "enabled": true, "url": "https://ntfy.sh", - "topic": "rewards-script", - "authToken": "" - } - } -} -``` - -### **Configuration Options** - -| Setting | Description | Example | -|---------|-------------|---------| -| `enabled` | Enable NTFY notifications | `true` | -| `url` | NTFY server URL | `"https://ntfy.sh"` | -| `topic` | Notification topic name | `"rewards-script"` | -| `authToken` | Authentication token (optional) | `"tk_abc123..."` | - ---- - -## 🚀 Setup Options - -### **Option 1: Public Service (Easiest)** -```json -{ - "notifications": { - "ntfy": { - "enabled": true, - "url": "https://ntfy.sh", - "topic": "your-unique-topic-name" + "topic": "rewards-myname-2025" } } } ``` -**Pros:** -- ✅ No server setup required -- ✅ Always available -- ✅ Free to use +**That's it!** You'll get push notifications on your phone. -**Cons:** -- ❌ Public server (less privacy) -- ❌ Rate limits apply -- ❌ Dependent on external service +--- -### **Option 2: Self-Hosted (Recommended)** -```json +## 🔔 What Notifications You Get + +- 🚨 **Errors** — Script crashes, login failures +- ⚠️ **Warnings** — Missing points, suspicious activity +- 🏆 **Milestones** — Account completed successfully +- 💳 **Buy mode** — Point spending detected +- 📊 **Summary** — End-of-run report + +--- + +## 🔒 Use Private Server (Optional) + +### Self-Host NTFY + +**Docker:** +```yaml +services: + ntfy: + image: binwiederhier/ntfy + ports: + - "80:80" + volumes: + - ./ntfy-data:/var/lib/ntfy + command: serve +``` + +**Then configure:** +```jsonc { "notifications": { "ntfy": { @@ -93,315 +86,33 @@ NTFY is a **simple HTTP-based pub-sub notification service** that sends push not } ``` -**Self-Hosted Setup:** -```yaml -# docker-compose.yml -version: '3.8' -services: - ntfy: - image: binwiederhier/ntfy - container_name: ntfy - ports: - - "80:80" - volumes: - - ./data:/var/lib/ntfy - command: serve -``` - ---- - -## 🔒 Authentication - -### **When You Need Auth** -Authentication tokens are **optional** but required for: -- 🔐 **Private topics** with username/password -- 🏠 **Private NTFY servers** with authentication -- 🛡️ **Preventing spam** on your topic - -### **Getting an Auth Token** - -#### **Method 1: Command Line** -```bash -ntfy token -``` - -#### **Method 2: Web Interface** -1. Visit your NTFY server (e.g., `https://ntfy.sh`) -2. Go to **Account** section -3. Generate **new access token** - -#### **Method 3: API** -```bash -curl -X POST -d '{"label":"rewards-script"}' \ - -H "Authorization: Bearer YOUR_LOGIN_TOKEN" \ - https://ntfy.sh/v1/account/tokens -``` - -### **Token Format** -- Tokens start with `tk_` (e.g., `tk_abc123def456...`) -- Use Bearer authentication format -- Tokens are permanent until revoked - ---- - -## 📲 Receiving Notifications - -### **Mobile Apps** -- **Android** — [NTFY on Google Play](https://play.google.com/store/apps/details?id=io.heckel.ntfy) -- **iOS** — [NTFY on App Store](https://apps.apple.com/app/ntfy/id1625396347) -- **F-Droid** — Available for Android - -### **Desktop Options** -- **Web Interface** — Visit your NTFY server URL -- **Desktop Apps** — Available for Linux, macOS, Windows -- **Browser Extension** — Chrome/Firefox extensions - -### **Setup Steps** -1. **Install** NTFY app on your device -2. **Add subscription** to your topic name -3. **Enter server URL** (if self-hosted) -4. **Test** with a manual message - ---- - -## 🔔 Notification Types - -### **Error Notifications** -**Priority:** Max 🚨 | **Trigger:** Script errors and failures -``` -[ERROR] DESKTOP [LOGIN] Failed to login: Invalid credentials -``` - -### **Warning Notifications** -**Priority:** High ⚠️ | **Trigger:** Important warnings -``` -[WARN] MOBILE [SEARCH] Didn't gain expected points from search -``` - -### **Info Notifications** -**Priority:** Default 🏆 | **Trigger:** Important milestones -``` -[INFO] MAIN [TASK] Started tasks for account user@email.com -``` - -### **Buy Mode Notifications** -**Priority:** High 💳 | **Trigger:** Point spending detected -``` -💳 Spend detected (Buy Mode) -Account: user@email.com -Spent: -500 points -Current: 12,500 points -Session spent: 1,200 points -``` - -### **Conclusion Summary** -**End-of-run summary with rich formatting:** -``` -🎯 Microsoft Rewards Summary -Accounts: 3 • 0 with issues -Total: 15,230 -> 16,890 (+1,660) -Average Duration: 8m 32s -Cumulative Runtime: 25m 36s -``` - ---- - -## 🤝 Integration with Discord - -### **Complementary Setup** -Use **both** NTFY and Discord for comprehensive monitoring: - -```json -{ - "notifications": { - "webhook": { - "enabled": true, - "url": "https://discord.com/api/webhooks/..." - }, - "conclusionWebhook": { - "enabled": true, - "url": "https://discord.com/api/webhooks/..." - }, - "ntfy": { - "enabled": true, - "url": "https://ntfy.sh", - "topic": "rewards-script" - } - } -} -``` - -### **Coverage Comparison** - -| Feature | NTFY | Discord | -|---------|------|---------| -| **Mobile push** | ✅ Instant | ❌ App required | -| **Rich formatting** | ❌ Text only | ✅ Embeds + colors | -| **Desktop alerts** | ✅ Native | ✅ App notifications | -| **Offline delivery** | ✅ Queued | ❌ Real-time only | -| **Self-hosted** | ✅ Easy | ❌ Complex | - ---- - -## 🎛️ Advanced Configuration - -### **Custom Topic Names** -Use descriptive, unique topic names: -```json -{ - "topic": "rewards-production-server1" -} -{ - "topic": "msn-rewards-home-pc" -} -{ - "topic": "rewards-dev-testing" -} -``` - -### **Environment-Specific** -```json -{ - "notifications": { - "ntfy": { - "enabled": true, - "url": "https://ntfy.internal.lan", - "topic": "homelab-rewards", - "authToken": "tk_homelab_token" - } - } -} -``` - ---- - -## 🧪 Testing & Debugging - -### **Manual Test Message** -```bash -# Public server (no auth) -curl -d "Test message from rewards script" https://ntfy.sh/your-topic - -# With authentication -curl -H "Authorization: Bearer tk_your_token" \ - -d "Authenticated test message" \ - https://ntfy.sh/your-topic -``` - -### **Script Debug Mode** -```powershell -$env:DEBUG_REWARDS_VERBOSE=1; npm start -``` - -### **Server Health Check** -```bash -# Check NTFY server status -curl -s https://ntfy.sh/v1/health - -# List your topics (with auth) -curl -H "Authorization: Bearer tk_your_token" \ - https://ntfy.sh/v1/account/topics -``` - --- ## 🛠️ Troubleshooting | Problem | Solution | |---------|----------| -| **No notifications** | Check topic spelling; verify app subscription | -| **Auth failures** | Verify token format (`tk_`); check token validity | -| **Wrong server** | Test server URL in browser; check HTTPS/HTTP | -| **Rate limits** | Switch to self-hosted; reduce notification frequency | +| **No notifications** | Check topic name matches exactly | +| **Wrong server** | Verify URL includes `https://` | +| **Auth failures** | Token must start with `tk_` | -### **Common Fixes** -- ✅ **Topic name** — Must match exactly between config and app -- ✅ **Server URL** — Include `https://` and check accessibility -- ✅ **Token format** — Must start with `tk_` for authentication -- ✅ **Network** — Verify firewall/proxy settings +### Test Manually ---- - -## 🏠 Homelab Integration - -### **Official Support** -NTFY is included in: -- **Debian Trixie** (testing) -- **Ubuntu** (latest versions) - -### **Popular Integrations** -- **Sonarr/Radarr** — Download completion notifications -- **Prometheus** — Alert manager integration -- **Home Assistant** — Automation notifications -- **Portainer** — Container status alerts - -### **Docker Stack Example** -```yaml -version: '3.8' -services: - ntfy: - image: binwiederhier/ntfy - container_name: ntfy - ports: - - "80:80" - volumes: - - ./ntfy-data:/var/lib/ntfy - environment: - - NTFY_BASE_URL=https://ntfy.yourdomain.com - command: serve - - rewards: - build: . - depends_on: - - ntfy - environment: - - NTFY_URL=http://ntfy:80 +```bash +# Send test message +curl -d "Test from rewards script" https://ntfy.sh/your-topic ``` --- -## 🔒 Privacy & Security +## 📚 Next Steps -### **Public Server (ntfy.sh)** -- ⚠️ Messages pass through public infrastructure -- ⚠️ Topic names visible in logs -- ✅ Suitable for non-sensitive notifications +**Want Discord too?** +→ **[Discord Webhooks](./conclusionwebhook.md)** -### **Self-Hosted Server** -- ✅ Complete control over data -- ✅ Private network deployment possible -- ✅ Recommended for sensitive information - -### **Best Practices** -- 🔐 Use **unique, non-guessable** topic names -- 🔑 Enable **authentication** for sensitive notifications -- 🏠 Use **self-hosted server** for maximum privacy -- 🔄 **Regularly rotate** authentication tokens - -### **Data Retention** -- 📨 Messages are **not permanently stored** -- ⏱️ Delivery attempts **retried** for short periods -- 🗑️ **No long-term** message history +**Need detailed logs?** +→ **[Diagnostics Guide](./diagnostics.md)** --- -## ⚡ Performance Impact - -### **Script Performance** -- ✅ **Minimal overhead** — Fire-and-forget notifications -- ✅ **Non-blocking** — Failed notifications don't affect script -- ✅ **Asynchronous** — No execution delays - -### **Network Usage** -- 📊 **Low bandwidth** — Text-only messages -- ⚡ **HTTP POST** — Simple, efficient protocol -- 🔄 **Retry logic** — Automatic failure recovery - ---- - -## 🔗 Related Guides - -- **[Discord Webhooks](./conclusionwebhook.md)** — Rich notification embeds -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Buy Mode](./buy-mode.md)** — Manual purchasing notifications -- **[Security](./security.md)** — Privacy and data protection \ No newline at end of file +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/proxy.md b/docs/proxy.md index 706992f..94c15fe 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -1,611 +1,126 @@ -# 🌐 Proxy Configuration +# 🌐 Proxy Setup -
- -**🔒 Route traffic through proxy servers for privacy and flexibility** -*Enhanced anonymity and geographic control* - -
+**Route traffic through proxy servers** --- -## 🎯 What Are Proxies? +## 💡 Do You Need a Proxy? -Proxies act as **intermediaries** between your script and Microsoft's servers, providing enhanced privacy, geographic flexibility, and network management capabilities. +**Most users DON'T need proxies.** Only use if: +- ✅ You run many accounts from same IP +- ✅ You want geographic flexibility +- ✅ Your IP is already flagged -### **Key Benefits** -- 🎭 **IP masking** — Hide your real IP address -- 🌍 **Geographic flexibility** — Appear to browse from different locations -- ⚡ **Rate limiting** — Distribute requests across multiple IPs -- 🔧 **Network control** — Route traffic through specific servers -- 🔒 **Privacy enhancement** — Add layer of anonymity +**Otherwise, skip this guide.** --- -## ⚙️ Configuration +## ⚡ Quick Start -### **Basic Setup** -```json -{ - "browser": { - "proxy": { - "enabled": false, - "server": "proxy.example.com:8080", - "username": "", - "password": "", - "bypass": [] - } - } -} -``` +### Per-Account Proxy -### **Configuration Options** - -| Setting | Description | Example | -|---------|-------------|---------| -| `enabled` | Enable proxy usage | `true` | -| `server` | Proxy server address and port | `"proxy.example.com:8080"` | -| `username` | Proxy authentication username | `"proxyuser"` | -| `password` | Proxy authentication password | `"proxypass123"` | -| `bypass` | Domains to bypass proxy | `["localhost", "*.internal.com"]` | - ---- - -## 🔌 Supported Proxy Types - -### **HTTP Proxies** -**Most common type for web traffic** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "http://proxy.example.com:8080", - "username": "user", - "password": "pass" - } - } -} -``` - -### **HTTPS Proxies** -**Encrypted proxy connections** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "https://secure-proxy.example.com:8080", - "username": "user", - "password": "pass" - } - } -} -``` - -### **SOCKS Proxies** -**Support for SOCKS4 and SOCKS5** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "socks5://socks-proxy.example.com:1080", - "username": "user", - "password": "pass" - } - } -} -``` - ---- - -## 🏢 Popular Proxy Providers - -### **Residential Proxies (Recommended)** -**High-quality IPs from real devices** - -#### **Top Providers** -- **Bright Data** (formerly Luminati) — Premium quality -- **Smartproxy** — User-friendly dashboard -- **Oxylabs** — Enterprise-grade -- **ProxyMesh** — Developer-focused - -#### **Configuration Example** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "rotating-residential.brightdata.com:22225", - "username": "customer-username-session-random", - "password": "your-password" - } - } -} -``` - -### **Datacenter Proxies** -**Fast and affordable server-based IPs** - -#### **Popular Providers** -- **SquidProxies** — Reliable performance -- **MyPrivateProxy** — Dedicated IPs -- **ProxyRack** — Budget-friendly -- **Storm Proxies** — Rotating options - -#### **Configuration Example** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "datacenter.squidproxies.com:8080", - "username": "username", - "password": "password" - } - } -} -``` - -### **Free Proxies** -**⚠️ Not recommended for production use** - -#### **Risks** -- ❌ Unreliable connections -- ❌ Potential security issues -- ❌ Often blocked by services -- ❌ Poor performance - ---- - -## 🔐 Authentication Methods - -### **Username/Password (Most Common)** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "username": "your-username", - "password": "your-password" - } - } -} -``` - -### **IP Whitelisting** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "username": "", - "password": "" - } - } -} -``` - -**Setup Steps:** -1. Contact proxy provider -2. Provide your server's IP address -3. Configure whitelist in provider dashboard -4. Remove credentials from config - -### **Session-Based Authentication** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "session-proxy.example.com:8080", - "username": "customer-session-sticky123", - "password": "your-password" - } - } -} -``` - ---- - -## 🚫 Bypass Configuration - -### **Local Development** -**Bypass proxy for local services** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "bypass": [ - "localhost", - "127.0.0.1", - "*.local", - "*.internal" - ] - } - } -} -``` - -### **Specific Domains** -**Route certain domains directly** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "bypass": [ - "*.microsoft.com", - "login.live.com", - "account.microsoft.com" - ] - } - } -} -``` - -### **Advanced Patterns** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "bypass": [ - "*.intranet.*", - "192.168.*.*", - "10.*.*.*", - "" - ] - } - } -} -``` - ---- - -## 🎛️ Advanced Configurations - -### **Per-Account Proxies** -**Different proxies for different accounts** +**Edit** `src/accounts.json`: ```json { "accounts": [ { - "email": "user1@example.com", - "password": "password1", + "email": "your@email.com", + "password": "password", "proxy": { - "enabled": true, - "server": "proxy1.example.com:8080" - } - }, - { - "email": "user2@example.com", - "password": "password2", - "proxy": { - "enabled": true, - "server": "proxy2.example.com:8080" + "proxyAxios": true, + "url": "proxy.example.com", + "port": 8080, + "username": "proxyuser", + "password": "proxypass" } } ] } ``` -### **Failover Configuration** -**Multiple proxy servers for redundancy** +**That's it!** Script uses proxy for this account only. + +--- + +## 🎯 Proxy Types + +### HTTP Proxy (Most Common) + ```json { - "browser": { - "proxy": { - "enabled": true, - "servers": [ - "primary-proxy.example.com:8080", - "backup-proxy.example.com:8080", - "emergency-proxy.example.com:8080" - ], - "username": "user", - "password": "pass" - } + "proxy": { + "proxyAxios": true, + "url": "http://proxy.example.com", + "port": 8080, + "username": "user", + "password": "pass" } } ``` -### **Geographic Routing** -**Location-specific proxy selection** +### SOCKS5 Proxy + ```json { - "browser": { - "proxy": { - "enabled": true, - "regions": { - "us": "us-proxy.example.com:8080", - "eu": "eu-proxy.example.com:8080", - "asia": "asia-proxy.example.com:8080" - }, - "defaultRegion": "us" - } + "proxy": { + "proxyAxios": true, + "url": "socks5://proxy.example.com", + "port": 1080, + "username": "user", + "password": "pass" } } ``` --- -## 🔒 Security & Environment Variables +## 🏢 Recommended Providers -### **Credential Protection** -**Secure proxy authentication** +### Residential Proxies (Best) +- **Bright Data** — Premium quality, expensive +- **Smartproxy** — User-friendly +- **Oxylabs** — Enterprise-grade -**Environment Variables:** -```powershell -# Set in environment -$env:PROXY_USERNAME="your-username" -$env:PROXY_PASSWORD="your-password" -``` +### Datacenter Proxies (Cheaper) +- **SquidProxies** — Reliable +- **MyPrivateProxy** — Dedicated IPs -**Configuration:** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "username": "${PROXY_USERNAME}", - "password": "${PROXY_PASSWORD}" - } - } -} -``` - -### **HTTPS Verification** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "verifySSL": true, - "rejectUnauthorized": true - } - } -} -``` - -### **Connection Encryption** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "https://encrypted-proxy.example.com:8080", - "tls": { - "enabled": true, - "version": "TLSv1.3" - } - } - } -} -``` - ---- - -## 🧪 Testing & Debugging - -### **Manual Tests** -```bash -# Test proxy connection -curl --proxy proxy.example.com:8080 http://httpbin.org/ip - -# Test with authentication -curl --proxy user:pass@proxy.example.com:8080 http://httpbin.org/ip - -# Test geolocation -curl --proxy proxy.example.com:8080 http://ipinfo.io/json -``` - -### **Script Debug Mode** -```powershell -$env:DEBUG_PROXY=1; npm start -``` - -### **Health Check Script** -```bash -#!/bin/bash -PROXY="proxy.example.com:8080" -curl --proxy $PROXY --connect-timeout 10 http://httpbin.org/status/200 -echo "Proxy health: $?" -``` +⚠️ **Avoid free proxies** — Unreliable and often blocked. --- ## 🛠️ Troubleshooting -| Problem | Error | Solution | -|---------|-------|----------| -| **Connection Failed** | `ECONNREFUSED` | Verify server address/port; check firewall | -| **Auth Failed** | `407 Proxy Authentication Required` | Verify username/password; check IP whitelist | -| **Timeout** | `Request timeout` | Increase timeout values; try different server | -| **SSL Error** | `certificate verify failed` | Disable SSL verification; update certificates | +| Problem | Solution | +|---------|----------| +| **"Connection refused"** | Check proxy URL and port | +| **"407 Auth required"** | Verify username/password | +| **"Timeout"** | Try different proxy server | +| **"SSL error"** | Use HTTP instead of HTTPS | -### **Common Error Messages** +### Test Proxy Manually -#### **Connection Issues** -``` -[ERROR] Proxy connection failed: ECONNREFUSED -``` -**Solutions:** -- ✅ Verify proxy server address and port -- ✅ Check proxy server is running -- ✅ Confirm firewall allows connections -- ✅ Test with different proxy server +```bash +# Windows (PowerShell) +curl --proxy http://user:pass@proxy.com:8080 http://httpbin.org/ip -#### **Authentication Issues** -``` -[ERROR] Proxy authentication failed: 407 Proxy Authentication Required -``` -**Solutions:** -- ✅ Verify username and password -- ✅ Check account is active with provider -- ✅ Confirm IP is whitelisted (if applicable) -- ✅ Try different authentication method - -#### **Performance Issues** -``` -[ERROR] Proxy timeout: Request timeout -``` -**Solutions:** -- ✅ Increase timeout values -- ✅ Check proxy server performance -- ✅ Try different proxy server -- ✅ Reduce concurrent connections - ---- - -## ⚡ Performance Optimization - -### **Connection Settings** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "timeouts": { - "connect": 30000, - "request": 60000, - "idle": 120000 - }, - "connectionPooling": true, - "maxConnections": 10 - } - } -} -``` - -### **Compression Settings** -```json -{ - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "compression": true, - "gzip": true - } - } -} -``` - -### **Monitoring Metrics** -- **Connection Success Rate** — % of successful proxy connections -- **Response Time** — Average request latency through proxy -- **Bandwidth Usage** — Data transferred through proxy -- **Error Rate** — % of failed requests via proxy - ---- - -## 🐳 Container Integration - -### **Docker Environment** -```dockerfile -# Dockerfile -ENV PROXY_ENABLED=true -ENV PROXY_SERVER=proxy.example.com:8080 -ENV PROXY_USERNAME=user -ENV PROXY_PASSWORD=pass -``` - -### **Kubernetes ConfigMap** -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: rewards-proxy-config -data: - proxy.json: | - { - "enabled": true, - "server": "proxy.example.com:8080", - "username": "user", - "password": "pass" - } -``` - -### **Environment-Specific** -```json -{ - "development": { - "proxy": { "enabled": false } - }, - "staging": { - "proxy": { - "enabled": true, - "server": "staging-proxy.example.com:8080" - } - }, - "production": { - "proxy": { - "enabled": true, - "server": "prod-proxy.example.com:8080" - } - } -} +# Linux/macOS +curl --proxy http://user:pass@proxy.com:8080 http://httpbin.org/ip ``` --- -## 📊 Best Practices +## 📚 Next Steps -### **Proxy Selection** -- 🏆 **Residential > Datacenter** — Better for avoiding detection -- 💰 **Paid > Free** — Reliability and security -- 🔄 **Multiple providers** — Redundancy and failover -- 🌍 **Geographic diversity** — Flexibility and compliance +**Proxy working?** +→ **[Setup Scheduler](./schedule.md)** -### **Configuration Management** -- 🔑 **Environment variables** — Secure credential storage -- 🧪 **Test before deploy** — Verify configuration works -- 📊 **Monitor performance** — Track availability and speed -- 🔄 **Backup configs** — Ready failover options +**Need humanization?** +→ **[Humanization Guide](./humanization.md)** -### **Security Guidelines** -- 🔒 **HTTPS proxies** — Encrypted connections when possible -- 🛡️ **SSL verification** — Verify certificates -- 🔄 **Rotate credentials** — Regular password updates -- 👁️ **Monitor access** — Watch for unauthorized usage +**Multiple accounts?** +→ **[Accounts Guide](./accounts.md)** --- -## ⚖️ Legal & Compliance - -### **Terms of Service** -- 📋 Review Microsoft's Terms of Service -- 📄 Understand proxy provider's acceptable use policy -- 🌍 Ensure compliance with local regulations -- 🗺️ Consider geographic restrictions - -### **Data Privacy** -- 🔍 Understand data flow through proxy -- 📝 Review proxy provider's data retention policies -- 🔐 Implement additional encryption if needed -- 📊 Monitor proxy logs and access - -### **Rate Limiting** -- ⏱️ Respect Microsoft's rate limits -- ⏸️ Implement proper delays between requests -- 🚦 Monitor for IP blocking or throttling -- 🔄 Use proxy rotation to distribute load - ---- - -## 🔗 Related Guides - -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Security](./security.md)** — Privacy and data protection -- **[Docker](./docker.md)** — Container deployment with proxies -- **[Humanization](./humanization.md)** — Natural behavior patterns \ No newline at end of file +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/schedule.md b/docs/schedule.md index a504bed..155a1ec 100644 --- a/docs/schedule.md +++ b/docs/schedule.md @@ -1,232 +1,37 @@ -# ⏰ Scheduler & Automation +# ⏰ Scheduler -
- -**🚀 Built-in scheduler for automated daily execution** -*Set it and forget it* - -
+**Automate daily script execution** --- -## 🎯 What is the Scheduler? +## ⚡ Quick Start -The built-in scheduler provides **automated script execution** at specified times without requiring external cron jobs or task schedulers. +### Basic Setup -### **Key Features** -- 📅 **Daily automation** — Run at the same time every day -- 🌍 **Timezone aware** — Handles DST automatically -- 🔄 **Multiple passes** — Execute script multiple times per run -- 🏖️ **Vacation mode** — Skip random days monthly -- 🎲 **Jitter support** — Randomize execution times -- 📅 **Humanization off-days** — Weekly random skips (disable via `humanization.randomOffDaysPerWeek`) -- ⚡ **Immediate start** — Option to run on startup - ---- - -## ⚙️ Configuration - -### **Basic Setup** -```json +**Edit** `src/config.jsonc`: +```jsonc { "schedule": { "enabled": true, "time": "09:00", - "timeZone": "America/New_York", - "runImmediatelyOnStart": true - }, - "passesPerRun": 2 -} -``` - -### **Advanced Setup with Vacation Mode** -```json -{ - "schedule": { - "enabled": true, - "time": "10:00", - "timeZone": "Europe/Paris", - "runImmediatelyOnStart": false - }, - "passesPerRun": 3, - "vacation": { - "enabled": true, - "minDays": 3, - "maxDays": 5 + "timeZone": "America/New_York" } } ``` -### **Configuration Options** - -| Setting | Default | Description | -|---------|---------|-------------| -| `enabled` | `false` | Enable built-in scheduler | -| `time` | `"09:00"` | Daily execution time (24-hour format) | -| `timeZone` | `"UTC"` | IANA timezone identifier | -| `runImmediatelyOnStart` | `true` | Execute once on process startup | -| `passesPerRun` | `1` | Number of complete runs per execution | -| `vacation.enabled` | `false` | Skip random monthly off-block | -| `vacation.minDays` | `3` | Minimum vacation days | -| `vacation.maxDays` | `5` | Maximum vacation days | -| `cron` | `undefined` | Optional cron expression (string or array) for advanced scheduling | - -### **Cron Expressions (Advanced)** - -You can now drive the scheduler with classic cron syntax instead of a single daily time. Provide either a string or an array in `schedule.cron`. - -```json -{ - "schedule": { - "enabled": true, - "cron": [ - "0 7 * * *", // every day at 07:00 - "30 19 * * 1-5" // weekdays at 19:30 - ], - "timeZone": "Europe/Paris" - } -} -``` - -- Supports 5-field and 6-field cron expressions (`second minute hour day month weekday`). -- When `cron` is set, the legacy `time`, `time12`, `time24`, and daily jitter env vars are ignored. -- The scheduler still honors vacation mode, weekly random off-days, run-on-start, and watchdog features. - ---- - -## 🚀 How It Works - -### **Daily Scheduling** -1. **Calculate next run** — Timezone-aware scheduling -2. **Wait until time** — Minimal resource usage -3. **Execute passes** — Run script specified number of times -4. **Schedule next day** — Automatic DST adjustment - -### **Startup Behavior** - -#### **Immediate Start Enabled (`true`)** -- **Before scheduled time** → Run immediately + wait for next scheduled time -- **After scheduled time** → Run immediately + wait for tomorrow's time - -#### **Immediate Start Disabled (`false`)** -- **Any time** → Always wait for next scheduled time - -### **Multiple Passes** -- Each pass processes **all accounts** through **all tasks** -- Useful for **maximum point collection** -- Higher passes = **more points** but **increased detection risk** - ---- - -## 🏖️ Vacation Mode - -### **Monthly Off-Blocks** -Vacation mode randomly selects a **contiguous block of days** each month to skip execution. - -### **Configuration** -```json -{ - "vacation": { - "enabled": true, - "minDays": 3, - "maxDays": 5 - } -} -``` - -### **How It Works** -- **Random selection** — Different days each month -- **Contiguous block** — Skip consecutive days, not scattered -- **Independent** — Works with weekly random off-days -- **Logged** — Shows selected vacation period - -### **Example Output** -``` -[SCHEDULE] Selected vacation block this month: 2025-01-15 → 2025-01-18 -[SCHEDULE] Skipping run - vacation mode (3 days remaining) -``` - ---- - -## 🌍 Supported Timezones - -### **North America** -- `America/New_York` — Eastern Time -- `America/Chicago` — Central Time -- `America/Denver` — Mountain Time -- `America/Los_Angeles` — Pacific Time -- `America/Phoenix` — Arizona (no DST) - -### **Europe** -- `Europe/London` — GMT/BST -- `Europe/Paris` — CET/CEST -- `Europe/Berlin` — CET/CEST -- `Europe/Rome` — CET/CEST -- `Europe/Moscow` — MSK - -### **Asia Pacific** -- `Asia/Tokyo` — JST -- `Asia/Shanghai` — CST -- `Asia/Kolkata` — IST -- `Australia/Sydney` — AEST/AEDT -- `Pacific/Auckland` — NZST/NZDT - ---- - -## 🎲 Randomization & Watchdog - -### **Environment Variables** -```powershell -# Add random delay before first run (5-20 minutes) -$env:SCHEDULER_INITIAL_JITTER_MINUTES_MIN=5 -$env:SCHEDULER_INITIAL_JITTER_MINUTES_MAX=20 - -# Add daily jitter to scheduled time (2-10 minutes) -$env:SCHEDULER_DAILY_JITTER_MINUTES_MIN=2 -$env:SCHEDULER_DAILY_JITTER_MINUTES_MAX=10 - -# Kill stuck passes after N minutes -$env:SCHEDULER_PASS_TIMEOUT_MINUTES=180 - -# Run each pass in separate process (recommended) -$env:SCHEDULER_FORK_PER_PASS=true -``` - -### **Benefits** -- **Avoid patterns** — Prevents exact-time repetition -- **Protection** — Kills stuck processes -- **Isolation** — Process separation for stability - ---- - -## 🖥️ Running the Scheduler - -### **Development Mode** -```powershell -npm run ts-schedule -``` - -### **Production Mode** -```powershell -npm run build +**Start scheduler:** +```bash npm run start:schedule ``` -### **Background Execution** -```powershell -# Windows Background (PowerShell) -Start-Process -NoNewWindow -FilePath "npm" -ArgumentList "run", "start:schedule" - -# Alternative: Windows Task Scheduler (recommended) -# Create scheduled task via GUI or schtasks command -``` +**That's it!** Script runs automatically at 9 AM daily. --- -## 📊 Usage Examples +## 🎯 Common Configurations -### **Basic Daily Automation** -```json +### Morning Run +```jsonc { "schedule": { "enabled": true, @@ -235,437 +40,140 @@ Start-Process -NoNewWindow -FilePath "npm" -ArgumentList "run", "start:schedule" } } ``` -⏰ **Perfect for morning routine** — Catch daily resets -### **Multiple Daily Passes** -```json -{ - "schedule": { - "enabled": true, - "time": "10:00", - "timeZone": "Europe/London", - "runImmediatelyOnStart": false - }, - "passesPerRun": 3 -} -``` -🔄 **Maximum points** with higher detection risk - -### **Conservative with Vacation** -```json +### Evening Run +```jsonc { "schedule": { "enabled": true, "time": "20:00", - "timeZone": "America/Los_Angeles" - }, - "passesPerRun": 1, - "vacation": { - "enabled": true, - "minDays": 4, - "maxDays": 6 + "timeZone": "Europe/Paris" } } ``` -🏖️ **Natural patterns** with monthly breaks + +### Multiple Passes Per Day +```jsonc +{ + "schedule": { + "enabled": true, + "time": "10:00", + "timeZone": "America/Los_Angeles" + }, + "passesPerRun": 2 +} +``` --- -## 🐳 Docker Integration +## 🌍 Common Timezones -### **Built-in Scheduler (Recommended)** -```yaml -services: - microsoft-rewards-script: - build: . - environment: - TZ: Europe/Paris - command: ["npm", "run", "start:schedule"] -``` -- Uses `passesPerRun` from config -- Single long-running process -- No external cron needed +| Region | Timezone | +|--------|----------| +| **US East** | `America/New_York` | +| **US West** | `America/Los_Angeles` | +| **UK** | `Europe/London` | +| **France** | `Europe/Paris` | +| **Germany** | `Europe/Berlin` | -### **External Cron (Project Default)** -```yaml -services: - microsoft-rewards-script: - build: . - environment: - CRON_SCHEDULE: "0 7,16,20 * * *" - RUN_ON_START: "true" -``` -- Uses `run_daily.sh` with random delays -- Multiple cron executions -- Lockfile prevents overlaps +[All timezones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) --- -## 📋 Logging Output +## 🎲 Advanced: Cron Expressions -### **Scheduler Initialization** -``` -[SCHEDULE] Scheduler initialized for daily 09:00 America/New_York -[SCHEDULE] Next run scheduled for 2025-01-21 09:00:00 EST +Want more control? Use cron: + +```jsonc +{ + "schedule": { + "enabled": true, + "cron": "0 9 * * *", // Every day at 9 AM + "timeZone": "America/New_York" + } +} ``` -### **Daily Execution** -``` -[SCHEDULE] Starting scheduled run (pass 1 of 2) -[SCHEDULE] Completed scheduled run in 12m 34s -[SCHEDULE] Next run scheduled for 2025-01-22 09:00:00 EST +### Cron Examples +```bash +"0 7 * * *" # Every day at 7:00 AM +"30 20 * * *" # Every day at 8:30 PM +"0 9,21 * * *" # Twice daily: 9 AM and 9 PM +"0 10 * * 1-5" # Weekdays only at 10 AM ``` -### **Time Calculations** -``` -[SCHEDULE] Current time: 2025-01-20 15:30:00 EDT -[SCHEDULE] Target time: 2025-01-21 09:00:00 EDT -[SCHEDULE] Waiting 17h 30m until next run +[Cron syntax helper](https://crontab.guru/) + +--- + +## 🏖️ Vacation Mode (Optional) + +Skip random days each month to look more natural: + +```jsonc +{ + "vacation": { + "enabled": true, + "minDays": 3, + "maxDays": 5 + } +} ``` +**Example:** Script will randomly skip 3-5 consecutive days per month. + --- ## 🛠️ Troubleshooting | Problem | Solution | |---------|----------| -| **Scheduler not running** | Check `enabled: true`; verify timezone format | -| **Wrong execution time** | Verify system clock; check DST effects | -| **Memory growth** | Restart process weekly; monitor logs | -| **Missed executions** | Check system sleep/hibernation; verify process | - -### **Debug Commands** -```powershell -# Test timezone calculation -node -e "console.log(new Date().toLocaleString('en-US', {timeZone: 'America/New_York'}))" - -# Verify config syntax -node -e "const fs=require('fs');const strip=input=>{let out='',inString=false,stringChar='',inLine=false,inBlock=false;for(let i=0;i schedule.log 2>&1 & - -# Windows (background service - requires additional setup) -# Recommend using Task Scheduler or Windows Service wrapper -``` - -## Process Management - -### Long-Running Process -- Scheduler runs continuously -- Automatically handles timezone changes -- Graceful handling of system clock adjustments - -### Memory Management -- Minimal memory footprint between runs -- Garbage collection after each execution -- No memory leaks in long-running processes - -### Error Recovery -- Failed runs don't affect future scheduling -- Automatic retry on next scheduled time -- Error logging for troubleshooting - -## Logging Output - -### Scheduler Events -``` -[SCHEDULE] Scheduler initialized for daily 09:00 America/New_York -[SCHEDULE] Next run scheduled for 2025-09-21 09:00:00 EST -[SCHEDULE] Starting scheduled run (pass 1 of 2) -[SCHEDULE] Completed scheduled run in 12m 34s -[SCHEDULE] Next run scheduled for 2025-09-22 09:00:00 EST -``` - -### Time Calculations -``` -[SCHEDULE] Current time: 2025-09-20 15:30:00 EDT -[SCHEDULE] Target time: 2025-09-21 09:00:00 EDT -[SCHEDULE] Waiting 17h 30m until next run -``` - -## Integration with Other Features - -### Docker Compatibility -- Scheduler works in Docker containers -- Alternative to external cron jobs -- Timezone handling in containerized environments - -### Buy Mode Exclusion -- Scheduler only runs automation mode -- Buy mode (`-buy`) ignores scheduler settings -- Manual executions bypass scheduler - -### Clustering -- Scheduler runs only in single-process mode -- Clustering disabled when scheduler is active -- Use scheduler OR clustering, not both - -## Best Practices - -### Optimal Timing -- **Morning runs**: Catch daily resets and new activities -- **Evening runs**: Complete remaining tasks before midnight -- **Avoid peak hours**: Reduce detection risk during high traffic - -### Timezone Selection -- Use your local timezone for easier monitoring -- Consider Microsoft Rewards server timezone -- Account for daylight saving time changes - -### Multiple Passes -- **2-3 passes**: Good balance of points vs. detection risk -- **More passes**: Higher detection risk -- **Single pass**: Safest but may miss some points - -### Monitoring -- Check logs regularly for errors -- Monitor point collection trends -- Verify scheduler is running as expected - -## Troubleshooting - -### Common Issues - -**Scheduler not running:** -- Check `enabled: true` in config -- Verify timezone format is correct -- Ensure no syntax errors in config.jsonc (remember it allows comments) - -**Wrong execution time:** -- Verify system clock is accurate -- Check timezone identifier spelling -- Consider daylight saving time effects - -**Memory growth over time:** -- Restart scheduler process weekly -- Monitor system resource usage -- Check for memory leaks in logs - -**Missed executions:** -- System was sleeping/hibernating -- Process was killed or crashed -- Clock was adjusted significantly +| **Scheduler not running** | Check `enabled: true` in config | +| **Wrong execution time** | Verify timezone spelling | +| **Runs multiple times** | Only use ONE scheduler instance | +| **Missed run** | Check if computer was off/sleeping | ### Debug Commands -```bash -# Test timezone calculation + +**Check timezone:** +```powershell node -e "console.log(new Date().toLocaleString('en-US', {timeZone: 'America/New_York'}))" - -# Verify config syntax -node -e "const fs=require('fs');const strip=input=>{let out='',inString=false,stringChar='',inLine=false,inBlock=false;for(let i=0;i - -**🛡️ Comprehensive security measures and incident response** -*Protect your accounts and maintain privacy* - - +**Protect your accounts and handle security incidents** --- -## 🎯 Security Overview +## ⚠️ Important Disclaimer -This guide explains how the script **detects security-related issues**, what it does automatically, and how you can **resolve incidents** safely. +**Using automation violates Microsoft's Terms of Service.** -### **Security Features** -- 🚨 **Automated detection** — Recognizes account compromise attempts -- 🛑 **Emergency halting** — Stops all automation during incidents -- 🔔 **Strong alerts** — Immediate notifications via Discord/NTFY -- 📋 **Recovery guidance** — Step-by-step incident resolution -- 🔒 **Privacy protection** — Local-only operation by default +Your accounts **may be banned**. Use at your own risk. --- -## 🚨 Security Incidents & Resolutions +## 🛡️ Best Practices -### **Recovery Email Mismatch** +### ✅ DO -#### **Symptoms** -During Microsoft login, the page shows a masked recovery email like `ko*****@hacker.net` that **doesn't match** your expected recovery email pattern. +- **Enable humanization** — Natural behavior reduces detection +- **Use 2FA/TOTP** — More secure authentication +- **Run 1-2x daily max** — Don't be greedy +- **Test on secondary accounts** — Never risk your main account +- **Enable vacation mode** — Random off days look natural +- **Monitor regularly** — Check diagnostics and logs -#### **What the Script Does** -- 🛑 **Halts automation** for the current account (leaves page open for manual action) -- 🚨 **Sends strong alerts** to all channels and engages global standby -- ⏸️ **Stops processing** — No further accounts are processed -- 🔔 **Repeats reminders** every 5 minutes until intervention +### ❌ DON'T -#### **Likely Causes** -- ⚠️ **Account takeover** — Recovery email changed by someone else -- 🔄 **Recent change** — You changed recovery email but forgot to update config +- **Run on main account** — Too risky +- **Schedule hourly** — Obvious bot pattern +- **Ignore warnings** — Security alerts matter +- **Use shared proxies** — Higher detection risk +- **Skip humanization** — Robotic behavior gets caught -#### **How to Fix** -1. **🔍 Verify account security** in Microsoft Account settings -2. **📝 Update config** if you changed recovery email yourself: +--- + +## 🚨 Security Incidents + +### Recovery Email Mismatch + +**What:** Login shows unfamiliar recovery email (e.g., `ko*****@hacker.net`) + +**Action:** +1. **Stop immediately** — Script halts automatically +2. **Check Microsoft Account** → Security settings +3. **Update config** if you changed email yourself: ```json { - "email": "your@email.com", "recoveryEmail": "ko*****@hacker.net" } ``` -3. **🔐 Change password** and review sign-in activity if compromise suspected -4. **🚀 Restart script** to resume normal operation - -#### **Prevention** -- ✅ Keep `recoveryEmail` in `accounts.json` up to date -- ✅ Use strong unique passwords and MFA -- ✅ Regular security reviews +4. **Change password** if compromise suspected --- -### **"We Can't Sign You In" (Blocked)** +### "We Can't Sign You In" (Blocked) -#### **Symptoms** -Microsoft presents a page titled **"We can't sign you in"** during login attempts. +**What:** Microsoft blocks login attempt -#### **What the Script Does** -- 🛑 **Stops automation** and leaves page open for manual recovery -- 🚨 **Sends strong alert** with high priority notifications -- ⏸️ **Engages global standby** to avoid processing other accounts - -#### **Likely Causes** -- ⏱️ **Temporary lock** — Rate limiting or security check from Microsoft -- 🚫 **Account restrictions** — Ban related to unusual activity -- 🔒 **Verification required** — SMS code, authenticator, or other challenges - -#### **How to Fix** -1. **✅ Complete verification** challenges (SMS, authenticator, etc.) -2. **⏸️ Pause activity** for 24-48h if blocked repeatedly -3. **🔧 Reduce concurrency** and increase delays between actions -4. **🌐 Check proxies** — Ensure consistent IP/country -5. **📞 Appeal if needed** — Contact Microsoft if ban is suspected - -#### **Prevention** -- ✅ **Respect rate limits** — Use humanization settings -- ✅ **Avoid patterns** — Don't run too many accounts from same IP -- ✅ **Geographic consistency** — Use proxies from your actual region -- ✅ **Human-like timing** — Avoid frequent credential retries +**Action:** +1. **Wait 24-48 hours** — Temporary locks usually lift +2. **Complete any challenges** — SMS, authenticator, etc. +3. **Reduce frequency** — Run less often +4. **Enable humanization** — If not already enabled +5. **Check proxy** — Ensure consistent IP/location --- -## 🔐 Privacy & Data Protection +## 🔐 Account Security -### **Local-First Architecture** -- 💾 **All data local** — Credentials, sessions, logs stored locally only -- 🚫 **No telemetry** — Zero data collection or external reporting -- 🔒 **No cloud storage** — Everything remains on your machine +### Strong Credentials -### **Credential Security** ```json { "accounts": [ { - "email": "user@example.com", - "password": "secure-password-here", - "totpSecret": "optional-2fa-secret" + "email": "your@email.com", + "password": "strong-unique-password", + "totp": "JBSWY3DPEHPK3PXP" } ] } ``` -**Best Practices:** -- 🔐 **Strong passwords** — Unique, complex passwords per account -- 🔑 **2FA enabled** — Time-based one-time passwords when possible -- 📁 **File permissions** — Restrict access to `accounts.json` -- 🔄 **Regular rotation** — Change passwords periodically +- ✅ **Unique passwords** per account +- ✅ **TOTP enabled** for all accounts +- ✅ **Strong passwords** (16+ characters) +- 🔄 **Rotate every 90 days** -### **Session Management** -- 🍪 **Persistent cookies** — Stored locally in `sessions/` directory -- 🔒 **Encrypted storage** — Session data protected at rest -- ⏰ **Automatic expiry** — Old sessions cleaned up automatically -- 🗂️ **Per-account isolation** — No session data mixing +### File Permissions + +```bash +# Linux/macOS - Restrict access +chmod 600 src/accounts.json + +# Windows - Right-click → Properties → Security +# Remove all users except yourself +``` --- ## 🌐 Network Security -### **Proxy Configuration** +### Use Proxies (Optional) + ```json { - "browser": { - "proxy": { - "enabled": true, - "server": "proxy.example.com:8080", - "username": "user", - "password": "pass" - } + "proxy": { + "proxyAxios": true, + "url": "proxy.example.com", + "port": 8080, + "username": "user", + "password": "pass" } } ``` -**Security Benefits:** -- 🎭 **IP masking** — Hide your real IP address -- 🌍 **Geographic flexibility** — Appear from different locations -- 🔒 **Traffic encryption** — HTTPS proxy connections -- 🛡️ **Detection avoidance** — Rotate IPs to avoid patterns +**Benefits:** +- IP masking +- Geographic flexibility +- Reduces pattern detection -### **Traffic Analysis Protection** -- 🔐 **HTTPS only** — All Microsoft communications encrypted -- 🚫 **No plaintext passwords** — Credentials protected in transit -- 🛡️ **Certificate validation** — SSL/TLS verification enabled -- 🔍 **Deep packet inspection** resistant +→ **[Full Proxy Guide](./proxy.md)** --- -## 🛡️ Anti-Detection Measures +## 📊 Monitoring -### **Humanization** -```json +### Enable Diagnostics + +```jsonc { - "humanization": { + "diagnostics": { "enabled": true, - "actionDelay": { "min": 150, "max": 450 }, - "gestureMoveProb": 0.4, - "gestureScrollProb": 0.2 + "saveScreenshot": true, + "saveHtml": true } } ``` -**Natural Behavior Simulation:** -- ⏱️ **Random delays** — Variable timing between actions -- 🖱️ **Mouse movements** — Subtle cursor adjustments -- 📜 **Scrolling gestures** — Natural page interactions -- 🎲 **Randomized patterns** — Avoid predictable automation +→ **[Diagnostics Guide](./diagnostics.md)** -### **Browser Fingerprinting** -- 🌐 **Real user agents** — Authentic browser identification -- 📱 **Platform consistency** — Mobile/desktop specific headers -- 🔧 **Plugin simulation** — Realistic browser capabilities -- 🖥️ **Screen resolution** — Appropriate viewport dimensions +### Enable Notifications ---- - -## 📊 Monitoring & Alerting - -### **Real-Time Monitoring** -```json +```jsonc { - "notifications": { - "webhook": { - "enabled": true, - "url": "https://discord.com/api/webhooks/..." - }, - "ntfy": { - "enabled": true, - "url": "https://ntfy.sh", - "topic": "rewards-security" - } + "conclusionWebhook": { + "enabled": true, + "url": "https://discord.com/api/webhooks/..." } } ``` -**Alert Types:** -- 🚨 **Security incidents** — Account compromise attempts -- ⚠️ **Login failures** — Authentication issues -- 🔒 **Account blocks** — Access restrictions detected -- 📊 **Performance anomalies** — Unusual execution patterns - -### **Log Analysis** -- 📝 **Detailed logging** — All actions recorded locally -- 🔍 **Error tracking** — Failed operations highlighted -- 📊 **Performance metrics** — Timing and success rates -- 🛡️ **Security events** — Incident timeline reconstruction +→ **[Webhook Setup](./conclusionwebhook.md)** --- -## 🧪 Security Testing +## 🛠️ Incident Response -### **Penetration Testing** -```powershell -# Test credential handling -$env:DEBUG_SECURITY=1; npm start +### Account Compromised -# Test session persistence -$env:DEBUG_SESSIONS=1; npm start +1. **Stop all automation** +2. **Change password immediately** +3. **Check sign-in activity** in Microsoft Account +4. **Enable 2FA** if not already +5. **Review security info** (recovery email, phone) +6. **Contact Microsoft** if unauthorized access -# Test proxy configuration -$env:DEBUG_PROXY=1; npm start -``` +### Temporary Ban -### **Vulnerability Assessment** -- 🔍 **Regular audits** — Check for security issues -- 📦 **Dependency scanning** — Monitor npm packages -- 🔒 **Code review** — Manual security analysis -- 🛡️ **Threat modeling** — Identify attack vectors +1. **Pause automation** for 48-72 hours +2. **Reduce frequency** when resuming +3. **Increase delays** in humanization +4. **Use proxy** from your region +5. **Monitor closely** after resuming --- -## 📋 Security Checklist +## 🔗 Privacy Tips -### **Initial Setup** -- ✅ **Strong passwords** for all accounts -- ✅ **2FA enabled** where possible -- ✅ **File permissions** restricted to user only -- ✅ **Proxy configured** if desired -- ✅ **Notifications set up** for alerts - -### **Regular Maintenance** -- ✅ **Password rotation** every 90 days -- ✅ **Session cleanup** weekly -- ✅ **Log review** for anomalies -- ✅ **Security updates** for dependencies -- ✅ **Backup verification** of configurations - -### **Incident Response** -- ✅ **Alert investigation** within 15 minutes -- ✅ **Account verification** when suspicious -- ✅ **Password changes** if compromise suspected -- ✅ **Activity review** in Microsoft account settings -- ✅ **Documentation** of incidents and resolutions +- 🔐 **Local-only** — All data stays on your machine +- 🚫 **No telemetry** — Script doesn't phone home +- 📁 **File security** — Restrict permissions +- 🔄 **Regular backups** — Keep config backups +- 🗑️ **Clean logs** — Delete old diagnostics --- -## 🚨 Emergency Procedures +## 📚 Next Steps -### **Account Compromise Response** -1. **🛑 Immediate shutdown** — Stop all script activity -2. **🔒 Change passwords** — Update all affected accounts -3. **📞 Contact Microsoft** — Report unauthorized access -4. **🔍 Audit activity** — Review recent sign-ins and changes -5. **🛡️ Enable additional security** — Add 2FA, recovery options -6. **📋 Document incident** — Record timeline and actions taken +**Setup humanization?** +→ **[Humanization Guide](./humanization.md)** -### **Detection Evasion** -1. **⏸️ Temporary suspension** — Pause automation for 24-48h -2. **🔧 Reduce intensity** — Lower pass counts and frequencies -3. **🌐 Change IPs** — Rotate proxies or VPN endpoints -4. **⏰ Adjust timing** — Modify scheduling patterns -5. **🎭 Increase humanization** — More natural behavior simulation +**Need proxies?** +→ **[Proxy Guide](./proxy.md)** + +**Want monitoring?** +→ **[Diagnostics](./diagnostics.md)** --- -## 🔗 Quick Reference Links - -When the script detects a security incident, it opens this guide directly to the relevant section: - -- **[Recovery Email Mismatch](#recovery-email-mismatch)** — Email change detection -- **[Account Blocked](#we-cant-sign-you-in-blocked)** — Login restriction handling - ---- - -## 🔗 Related Guides - -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Accounts & 2FA](./accounts.md)** — Microsoft account setup -- **[Proxy Configuration](./proxy.md)** — Network privacy and routing -- **[Humanization](./humanization.md)** — Natural behavior patterns +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/docs/update.md b/docs/update.md index 9efad73..2c86c69 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,371 +1,48 @@ -# 🔄 Auto-Update System +# 🔄 Auto-Update -
- -**🚀 Automatic updates to keep your installation current** -*Set it and forget it* - -
+**Keep script up to date automatically** --- -## 🎯 What is Auto-Update? +## 💡 What Is It? -The automatic update system runs **after script completion** to keep your installation current with the latest features, bug fixes, and security patches. +After each run, script checks for updates and installs them automatically. -### **Key Features** -- 🔄 **Automatic updates** — Runs after each script completion -- 🛡️ **Safe by design** — Fast-forward only Git updates -- 🐳 **Docker support** — Container image updates -- 🛠️ **Custom scripts** — Extensible update process -- 🔒 **Error resilient** — Failed updates don't break main script +**Already enabled by default!** + +--- + +## ⚡ How It Works + +### After Each Run + +1. **Fetch latest** from GitHub +2. **Pull changes** (safe fast-forward only) +3. **Install dependencies** (`npm ci`) +4. **Rebuild** (`npm run build`) + +**No action needed from you!** --- ## ⚙️ Configuration -### **Basic Setup** -```json +```jsonc { "update": { - "git": true, - "docker": false, - "scriptPath": "setup/update/update.mjs" - } -} -``` - -### **Configuration Options** - -| Setting | Description | Default | -|---------|-------------|---------| -| `git` | Enable Git-based updates | `true` | -| `docker` | Enable Docker container updates | `false` | -| `scriptPath` | Path to custom update script | `"setup/update/update.mjs"` | - ---- - -## 🚀 Update Methods - -### **Git Updates (`git: true`)** - -#### **What It Does** -- 📥 **Fetches latest changes** from remote repository -- ⚡ **Fast-forward only pulls** (safe updates) -- 📦 **Reinstalls dependencies** (`npm ci`) -- 🔨 **Rebuilds the project** (`npm run build`) - -#### **Requirements** -- ✅ Git installed and available in PATH -- ✅ Repository is a Git clone (not downloaded ZIP) -- ✅ No uncommitted local changes -- ✅ Internet connectivity - -#### **Process** -```bash -git fetch --all --prune -git pull --ff-only -npm ci -npm run build -``` - -### **Docker Updates (`docker: true`)** - -#### **What It Does** -- 📥 **Pulls latest container images** -- 🔄 **Restarts services** with new images -- 💾 **Preserves configurations** and mounted volumes - -#### **Requirements** -- ✅ Docker and Docker Compose installed -- ✅ `docker-compose.yml` file present -- ✅ Proper container registry access - -#### **Process** -```bash -docker compose pull -docker compose up -d -``` - ---- - -## 🛠️ Custom Update Scripts - -### **Default Script** -- **Path** — `setup/update/update.mjs` -- **Format** — ES modules -- **Arguments** — Command line flags - -### **Script Arguments** -- `--git` — Enable Git update process -- `--docker` — Enable Docker update process -- Both flags can be combined - -### **Custom Script Example** -```javascript -// custom-update.mjs -import { execSync } from 'child_process' - -const args = process.argv.slice(2) - -if (args.includes('--git')) { - console.log('🔄 Running custom Git update...') - execSync('git pull && npm install', { stdio: 'inherit' }) -} - -if (args.includes('--docker')) { - console.log('🐳 Running custom Docker update...') - execSync('docker-compose pull && docker-compose up -d', { stdio: 'inherit' }) -} -``` - ---- - -## ⏰ Execution Timing - -### **When Updates Run** -| Scenario | Update Runs | -|----------|-------------| -| **Normal completion** | ✅ All accounts processed successfully | -| **Error completion** | ✅ Script finished with errors but completed | -| **Interruption** | ❌ Script killed or crashed mid-execution | - -### **Update Sequence** -1. **🏁 Main script completion** — All accounts processed -2. **📊 Conclusion webhook** sent (if enabled) -3. **🚀 Update process begins** -4. **📥 Git updates** (if enabled) -5. **🐳 Docker updates** (if enabled) -6. **🔚 Process exits** - ---- - -## 🛡️ Safety Features - -### **Git Safety** -- ⚡ **Fast-forward only** — Prevents overwriting local changes -- 📦 **Dependency verification** — Ensures `npm ci` succeeds -- 🔨 **Build validation** — Confirms TypeScript compilation works - -### **Error Handling** -- ✅ **Update failures** don't break main script -- 🔇 **Silent failures** — Errors logged but don't crash process -- 🔄 **Rollback protection** — Failed updates don't affect current installation - -### **Concurrent Execution** -- 🔒 **Single update process** — Multiple instances don't conflict -- 🚫 **Lock-free design** — No file locking needed -- 🎯 **Independent updates** — Each script copy updates separately - ---- - -## 📊 Monitoring Updates - -### **Log Output** -``` -[UPDATE] Starting post-run update process -[UPDATE] Git update enabled, Docker update disabled -[UPDATE] Running: git fetch --all --prune -[UPDATE] Running: git pull --ff-only -[UPDATE] Running: npm ci -[UPDATE] Running: npm run build -[UPDATE] Update completed successfully -``` - -### **Update Verification** -```powershell -# Check if updates are pending -git status - -# View recent commits -git log --oneline -5 - -# Verify build status -npm run build -``` - ---- - -## 📋 Use Cases - -### **Development Environment** -| Benefit | Description | -|---------|-------------| -| **Synchronized** | Keep local installation current with repository | -| **Automated** | Automatic dependency updates | -| **Seamless** | Integration of bug fixes and features | - -### **Production Deployment** -| Benefit | Description | -|---------|-------------| -| **Security** | Automated security patches | -| **Features** | Updates without manual intervention | -| **Consistent** | Same update process across servers | - -### **Docker Environments** -| Benefit | Description | -|---------|-------------| -| **Images** | Container image updates | -| **Security** | Patches in base images | -| **Automated** | Service restarts | - ---- - -## 📋 Best Practices - -### **Git Configuration** -- 🧹 **Clean working directory** — Commit or stash local changes -- 🌿 **Stable branch** — Use `main` or `stable` for auto-updates -- 📝 **Regular commits** — Keep repository history clean -- 💾 **Backup data** — Sessions and accounts before updates - -### **Docker Configuration** -- 🏷️ **Image tagging** — Use specific tags, not `latest` for production -- 💾 **Volume persistence** — Ensure data volumes are mounted -- 🔗 **Service dependencies** — Configure proper startup order -- 🎯 **Resource limits** — Set appropriate memory and CPU limits - -### **Monitoring** -- 📝 **Check logs regularly** — Monitor update success/failure -- 🧪 **Test after updates** — Verify script functionality -- 💾 **Backup configurations** — Preserve working setups -- 📊 **Version tracking** — Record successful versions - ---- - -## 🛠️ Troubleshooting - -### **Git Issues** - -| Error | Solution | -|-------|----------| -| **"Not a git repository"** | Clone repository instead of downloading ZIP | -| **"Local changes would be overwritten"** | Commit or stash local changes | -| **"Fast-forward not possible"** | Repository diverged - reset to remote state | - -#### **Git Reset Command** -```powershell -# Reset to remote state (⚠️ loses local changes) -git fetch origin -git reset --hard origin/main -``` - -### **Docker Issues** - -| Error | Solution | -|-------|----------| -| **"Docker not found"** | Install Docker and Docker Compose | -| **"Permission denied"** | Add user to docker group | -| **"No docker-compose.yml"** | Create compose file or use custom script | - -#### **Docker Permission Fix** -```powershell -# Windows: Ensure Docker Desktop is running -# Linux: Add user to docker group -sudo usermod -aG docker $USER -``` - -### **Network Issues** - -| Error | Solution | -|-------|----------| -| **"Could not resolve host"** | Check internet connectivity | -| **"Connection timeout"** | Check firewall and proxy settings | - ---- - -## 🔧 Manual Updates - -### **Git Manual Update** -```powershell -git fetch --all --prune -git pull --ff-only -npm ci -npm run build -``` - -### **Docker Manual Update** -```powershell -docker compose pull -docker compose up -d -``` - -### **Dependencies Only** -```powershell -npm ci -npm run build -``` - ---- - -## ⚙️ Update Configuration - -### **Complete Disable** -```json -{ - "update": { - "git": false, - "docker": false - } -} -``` - -### **Selective Enable** -```json -{ - "update": { - "git": true, // Keep Git updates - "docker": false // Disable Docker updates - } -} -``` - -### **Custom Script Path** -```json -{ - "update": { - "git": true, - "docker": false, - "scriptPath": "my-custom-update.mjs" + "git": true, // Auto-update from Git + "docker": false // Docker container updates (if using Docker) } } ``` --- -## 🔒 Security Considerations +## 🐳 Docker Updates -### **Git Security** -- ✅ **Trusted remote** — Updates pull from configured remote only -- ⚡ **Fast-forward only** — Prevents malicious rewrites -- 📦 **NPM registry** — Dependencies from official registry +If using Docker: -### **Docker Security** -- 🏷️ **Verified images** — Container images from configured registries -- ✍️ **Image signatures** — Verify when possible -- 🔍 **Security scanning** — Regular scanning of base images - -### **Script Execution** -- 👤 **Same permissions** — Update scripts run with same privileges -- 🚫 **No escalation** — No privilege escalation during updates -- 🔍 **Review scripts** — Custom scripts should be security reviewed - ---- - -## 🎯 Environment Examples - -### **Development** -```json -{ - "update": { - "git": true, - "docker": false - } -} -``` - -### **Production** -```json +```jsonc { "update": { "git": false, @@ -374,22 +51,54 @@ npm run build } ``` -### **Hybrid** -```json -{ - "update": { - "git": true, - "docker": true, - "scriptPath": "setup/update/production-update.mjs" - } -} +Pulls latest Docker image and restarts container. + +--- + +## 🛠️ Manual Update + +### Git +```bash +git pull +npm ci +npm run build +``` + +### Docker +```bash +docker compose pull +docker compose up -d ``` --- -## 🔗 Related Guides +## ⚠️ Troubleshooting -- **[Getting Started](./getting-started.md)** — Initial setup and configuration -- **[Docker](./docker.md)** — Container deployment and management -- **[Scheduler](./schedule.md)** — Automated timing and execution -- **[Security](./security.md)** — Privacy and data protection \ No newline at end of file +| Problem | Solution | +|---------|----------| +| **"Not a git repository"** | Clone repo (don't download ZIP) | +| **"Local changes"** | Commit or stash your changes | +| **"Update failed"** | Check internet connection | + +### Reset to Remote + +```bash +git fetch origin +git reset --hard origin/v2 +npm ci +npm run build +``` + +--- + +## 📚 Next Steps + +**Need security tips?** +→ **[Security Guide](./security.md)** + +**Setup scheduler?** +→ **[Scheduler Guide](./schedule.md)** + +--- + +**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)** diff --git a/package-lock.json b/package-lock.json index d825b5e..8dff8c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "microsoft-rewards-script", - "version": "2.1.5", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "microsoft-rewards-script", - "version": "2.1.5", + "version": "2.3.0", "license": "ISC", "dependencies": { "axios": "^1.8.4", @@ -34,7 +34,7 @@ "typescript": "^5.5.4" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "funding": { "type": "github", diff --git a/package.json b/package.json index 93c0675..8947da1 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "microsoft-rewards-script", - "version": "2.1.5", + "version": "2.3.0", "description": "Automatically do tasks for Microsoft Rewards but in TS!", "private": true, "main": "index.js", "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "repository": { "type": "git", diff --git a/src/browser/BrowserFunc.ts b/src/browser/BrowserFunc.ts index a206a33..0c6e159 100644 --- a/src/browser/BrowserFunc.ts +++ b/src/browser/BrowserFunc.ts @@ -4,9 +4,10 @@ import { AxiosRequestConfig } from 'axios' import { MicrosoftRewardsBot } from '../index' import { saveSessionData } from '../util/Load' +import { TIMEOUTS, RETRY_LIMITS, SELECTORS, URLS } from '../constants' -import { Counters, DashboardData, MorePromotion, PromotionalItem } from './../interface/DashboardData' -import { QuizData } from './../interface/QuizData' +import { Counters, DashboardData, MorePromotion, PromotionalItem } from '../interface/DashboardData' +import { QuizData } from '../interface/QuizData' import { AppUserData } from '../interface/AppUserData' import { EarnablePoints } from '../interface/Points' @@ -34,34 +35,47 @@ export default class BrowserFunc { await page.goto(this.bot.config.baseURL) - const maxIterations = 5 // Maximum iterations set to 5 - - for (let iteration = 1; iteration <= maxIterations; iteration++) { - await this.bot.utils.wait(3000) + for (let iteration = 1; iteration <= RETRY_LIMITS.GO_HOME_MAX; iteration++) { + await this.bot.utils.wait(TIMEOUTS.LONG) await this.bot.browser.utils.tryDismissAllMessages(page) - // Check if account is suspended (multiple heuristics) - const suspendedByHeader = await page.waitForSelector('#suspendedAccountHeader', { state: 'visible', timeout: 1500 }).then(() => true).catch(() => false) - let suspendedByText = false - if (!suspendedByHeader) { - try { - const text = (await page.textContent('body')) || '' - suspendedByText = /account has been suspended|suspended due to unusual activity/i.test(text) - } catch { /* ignore */ } - } - if (suspendedByHeader || suspendedByText) { - this.bot.log(this.bot.isMobile, 'GO-HOME', 'This account appears suspended!', 'error') - throw new Error('Account has been suspended!') - } - try { - // If activities are found, exit the loop - await page.waitForSelector('#more-activities', { timeout: 1000 }) + // If activities are found, exit the loop (SUCCESS - account is OK) + await page.waitForSelector(SELECTORS.MORE_ACTIVITIES, { timeout: 1000 }) this.bot.log(this.bot.isMobile, 'GO-HOME', 'Visited homepage successfully') break } catch (error) { - // Continue if element is not found + // Activities not found yet - check if it's because account is suspended + // Only check suspension if we can't find activities (reduces false positives) + const suspendedByHeader = await page.waitForSelector(SELECTORS.SUSPENDED_ACCOUNT, { state: 'visible', timeout: 500 }).then(() => true).catch(() => false) + + if (suspendedByHeader) { + this.bot.log(this.bot.isMobile, 'GO-HOME', `Account suspension detected by header selector (iteration ${iteration})`, 'error') + throw new Error('Account has been suspended!') + } + + // Secondary check: look for suspension text in main content area only + try { + const mainContent = (await page.locator('#contentContainer, #main, .main-content').first().textContent({ timeout: 500 }).catch(() => '')) || '' + const suspensionPatterns = [ + /account\s+has\s+been\s+suspended/i, + /suspended\s+due\s+to\s+unusual\s+activity/i, + /your\s+account\s+is\s+temporarily\s+suspended/i + ] + + const isSuspended = suspensionPatterns.some(pattern => pattern.test(mainContent)) + if (isSuspended) { + this.bot.log(this.bot.isMobile, 'GO-HOME', `Account suspension detected by content text (iteration ${iteration})`, 'error') + throw new Error('Account has been suspended!') + } + } catch (e) { + // Ignore errors in text check - not critical + this.bot.log(this.bot.isMobile, 'GO-HOME', `Suspension text check skipped: ${e}`, 'warn') + } + + // Not suspended, just activities not loaded yet - continue to next iteration + this.bot.log(this.bot.isMobile, 'GO-HOME', `Activities not found yet (iteration ${iteration}/${RETRY_LIMITS.GO_HOME_MAX}), retrying...`, 'warn') } // Below runs if the homepage was unable to be visited @@ -70,14 +84,14 @@ export default class BrowserFunc { if (currentURL.hostname !== dashboardURL.hostname) { await this.bot.browser.utils.tryDismissAllMessages(page) - await this.bot.utils.wait(2000) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) await page.goto(this.bot.config.baseURL) } else { this.bot.log(this.bot.isMobile, 'GO-HOME', 'Visited homepage successfully') break } - await this.bot.utils.wait(5000) + await this.bot.utils.wait(TIMEOUTS.VERY_LONG) } } catch (error) { @@ -127,6 +141,14 @@ export default class BrowserFunc { } } + // Wait a bit longer for scripts to load, especially on mobile + await this.bot.utils.wait(this.bot.isMobile ? TIMEOUTS.LONG : TIMEOUTS.MEDIUM) + + // Wait for the more-activities element to ensure page is fully loaded + await target.waitForSelector(SELECTORS.MORE_ACTIVITIES, { timeout: TIMEOUTS.DASHBOARD_WAIT }).catch(() => { + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', 'Activities element not found, continuing anyway', 'warn') + }) + let scriptContent = await target.evaluate(() => { const scripts = Array.from(document.querySelectorAll('script')) const targetScript = scripts.find(script => script.innerText.includes('var dashboard')) @@ -135,18 +157,36 @@ export default class BrowserFunc { }) if (!scriptContent) { - await this.bot.browser.utils.captureDiagnostics(target, 'dashboard-data-missing').catch(()=>{}) + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', 'Dashboard script not found on first try, attempting recovery', 'warn') + await this.bot.browser.utils.captureDiagnostics(target, 'dashboard-data-missing').catch((e) => { + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', `Failed to capture diagnostics: ${e}`, 'warn') + }) + // Force a navigation retry once before failing hard try { await this.goHome(target) - await target.waitForLoadState('domcontentloaded', { timeout: 5000 }).catch(()=>{}) - } catch {/* ignore */} + await target.waitForLoadState('domcontentloaded', { timeout: TIMEOUTS.VERY_LONG }).catch((e) => { + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', `Wait for load state failed: ${e}`, 'warn') + }) + await this.bot.utils.wait(this.bot.isMobile ? TIMEOUTS.LONG : TIMEOUTS.MEDIUM) + } catch (e) { + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', `Recovery navigation failed: ${e}`, 'warn') + } + const retryContent = await target.evaluate(() => { const scripts = Array.from(document.querySelectorAll('script')) const targetScript = scripts.find(script => script.innerText.includes('var dashboard')) return targetScript?.innerText ? targetScript.innerText : null }).catch(()=>null) + if (!retryContent) { + // Log additional debug info + const scriptsDebug = await target.evaluate(() => { + const scripts = Array.from(document.querySelectorAll('script')) + return scripts.map(s => s.innerText.substring(0, 100)).join(' | ') + }).catch(() => 'Unable to get script debug info') + + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', `Available scripts preview: ${scriptsDebug}`, 'warn') throw this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', 'Dashboard data not found within script', 'error') } scriptContent = retryContent @@ -154,18 +194,37 @@ export default class BrowserFunc { // Extract the dashboard object from the script content const dashboardData = await target.evaluate((scriptContent: string) => { - // Extract the dashboard object using regex - const regex = /var dashboard = (\{.*?\});/s - const match = regex.exec(scriptContent) + // Try multiple regex patterns for better compatibility + const patterns = [ + /var dashboard = (\{.*?\});/s, // Original pattern + /var dashboard=(\{.*?\});/s, // No spaces + /var\s+dashboard\s*=\s*(\{.*?\});/s, // Flexible whitespace + /dashboard\s*=\s*(\{[\s\S]*?\});/ // More permissive + ] - if (match && match[1]) { - return JSON.parse(match[1]) + for (const regex of patterns) { + const match = regex.exec(scriptContent) + if (match && match[1]) { + try { + return JSON.parse(match[1]) + } catch (e) { + // Try next pattern if JSON parsing fails + continue + } + } } + return null + }, scriptContent) if (!dashboardData) { - await this.bot.browser.utils.captureDiagnostics(target, 'dashboard-data-parse').catch(()=>{}) + // Log a snippet of the script content for debugging + const scriptPreview = scriptContent.substring(0, 200) + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', `Script preview: ${scriptPreview}`, 'warn') + await this.bot.browser.utils.captureDiagnostics(target, 'dashboard-data-parse').catch((e) => { + this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', `Failed to capture diagnostics: ${e}`, 'warn') + }) throw this.bot.log(this.bot.isMobile, 'GET-DASHBOARD-DATA', 'Unable to parse dashboard script', 'error') } @@ -263,7 +322,7 @@ export default class BrowserFunc { : 'us' const userDataRequest: AxiosRequestConfig = { - url: 'https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613', + url: URLS.APP_USER_DATA, method: 'GET', headers: { 'Authorization': `Bearer ${accessToken}`, @@ -319,38 +378,73 @@ export default class BrowserFunc { */ async getQuizData(page: Page): Promise { try { + // Wait for page to be fully loaded + await page.waitForLoadState('domcontentloaded') + await this.bot.utils.wait(TIMEOUTS.MEDIUM) + const html = await page.content() const $ = load(html) - const scriptContent = $('script') - .toArray() - .map(el => $(el).text()) - .find(t => t.includes('_w.rewardsQuizRenderInfo')) || '' + // Try multiple possible variable names + const possibleVariables = [ + '_w.rewardsQuizRenderInfo', + 'rewardsQuizRenderInfo', + '_w.quizRenderInfo', + 'quizRenderInfo' + ] - if (scriptContent) { - const regex = /_w\.rewardsQuizRenderInfo\s*=\s*({.*?});/s + let scriptContent = '' + let foundVariable = '' + + for (const varName of possibleVariables) { + scriptContent = $('script') + .toArray() + .map(el => $(el).text()) + .find(t => t.includes(varName)) || '' + + if (scriptContent) { + foundVariable = varName + break + } + } + + if (scriptContent && foundVariable) { + // Escape dots in variable name for regex + const escapedVar = foundVariable.replace(/\./g, '\\.') + const regex = new RegExp(`${escapedVar}\\s*=\\s*({.*?});`, 's') const match = regex.exec(scriptContent) if (match && match[1]) { const quizData = JSON.parse(match[1]) + this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', `Found quiz data using variable: ${foundVariable}`, 'log') return quizData } else { - throw this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', 'Quiz data not found within script', 'error') + throw this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', `Variable ${foundVariable} found but could not extract JSON data`, 'error') } } else { + // Log available scripts for debugging + const allScripts = $('script') + .toArray() + .map(el => $(el).text()) + .filter(t => t.length > 0) + .map(t => t.substring(0, 100)) + + this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', `Script not found. Tried variables: ${possibleVariables.join(', ')}`, 'error') + this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', `Found ${allScripts.length} scripts on page`, 'warn') + throw this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', 'Script containing quiz data not found', 'error') } } catch (error) { - throw this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', 'An error occurred:' + error, 'error') + throw this.bot.log(this.bot.isMobile, 'GET-QUIZ-DATA', 'An error occurred: ' + error, 'error') } } async waitForQuizRefresh(page: Page): Promise { try { - await page.waitForSelector('span.rqMCredits', { state: 'visible', timeout: 10000 }) - await this.bot.utils.wait(2000) + await page.waitForSelector(SELECTORS.QUIZ_CREDITS, { state: 'visible', timeout: TIMEOUTS.DASHBOARD_WAIT }) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) return true } catch (error) { @@ -361,8 +455,8 @@ export default class BrowserFunc { async checkQuizCompleted(page: Page): Promise { try { - await page.waitForSelector('#quizCompleteContainer', { state: 'visible', timeout: 2000 }) - await this.bot.utils.wait(2000) + await page.waitForSelector(SELECTORS.QUIZ_COMPLETE, { state: 'visible', timeout: TIMEOUTS.MEDIUM_LONG }) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) return true } catch (error) { @@ -402,7 +496,7 @@ export default class BrowserFunc { // Save cookies await saveSessionData(this.bot.config.sessionPath, browser, email, this.bot.isMobile) - await this.bot.utils.wait(2000) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) // Close browser await browser.close() diff --git a/src/browser/BrowserUtil.ts b/src/browser/BrowserUtil.ts index 3525aa2..610f341 100644 --- a/src/browser/BrowserUtil.ts +++ b/src/browser/BrowserUtil.ts @@ -40,6 +40,12 @@ export default class BrowserUtil { closeButtons: 'button[aria-label*="close" i], button:has-text("Close"), button:has-text("Dismiss"), button:has-text("Got it"), button:has-text("OK"), button:has-text("Ok")' } as const + private static readonly TERMS_UPDATE_SELECTORS = { + titleId: '#iTOUTitle', + titleText: /we're updating our terms/i, + nextButton: 'button[data-testid="primaryButton"]:has-text("Next"), button[type="submit"]:has-text("Next")' + } as const + constructor(bot: MicrosoftRewardsBot) { this.bot = bot } @@ -57,6 +63,7 @@ export default class BrowserUtil { count += await this.dismissStandardButtons(page) count += await this.dismissOverlayButtons(page) count += await this.dismissStreakDialog(page) + count += await this.dismissTermsUpdateDialog(page) return count } @@ -135,6 +142,35 @@ export default class BrowserUtil { } } + private async dismissTermsUpdateDialog(page: Page): Promise { + try { + const { titleId, titleText, nextButton } = BrowserUtil.TERMS_UPDATE_SELECTORS + + // Check if terms update page is present + const titleById = page.locator(titleId) + const titleByText = page.locator('h1').filter({ hasText: titleText }) + + const hasTitle = await titleById.isVisible({ timeout: 200 }).catch(() => false) || + await titleByText.first().isVisible({ timeout: 200 }).catch(() => false) + + if (!hasTitle) return 0 + + // Click the Next button + const nextBtn = page.locator(nextButton).first() + if (await nextBtn.isVisible({ timeout: 500 }).catch(() => false)) { + await nextBtn.click({ timeout: 1000 }).catch(() => {}) + this.bot.log(this.bot.isMobile, 'DISMISS-ALL-MESSAGES', 'Dismissed: Terms Update Dialog (Next)') + // Wait a bit for navigation + await page.waitForTimeout(1000) + return 1 + } + + return 0 + } catch { + return 0 + } + } + async getLatestTab(page: Page): Promise { try { await this.bot.utils.wait(1000) diff --git a/src/config.jsonc b/src/config.jsonc index ab360cc..dcfc373 100644 --- a/src/config.jsonc +++ b/src/config.jsonc @@ -1,54 +1,112 @@ { - // Base URL for Rewards dashboard and APIs (do not change unless you know what you're doing) + // ============================================================ + // 🌐 GENERAL CONFIGURATION + // ============================================================ + + // Base URL for Microsoft Rewards dashboard (do not change unless necessary) "baseURL": "https://rewards.bing.com", - // Where to store sessions (cookies, fingerprints) + + // Directory to store sessions (cookies, browser fingerprints) "sessionPath": "sessions", + // Dry-run mode: simulate execution without actually running tasks (useful for testing) + "dryRun": false, + + + // ============================================================ + // 🖥️ BROWSER CONFIGURATION + // ============================================================ + "browser": { - // Keep headless=false so the browser window stays visible by default + // false = visible window | true = headless mode (invisible) "headless": false, - // Max time to wait for common operations (supports ms/s/min: e.g. 30000, "30s", "2min") + // Max timeout for operations (supports: 30000, "30s", "2min") "globalTimeout": "30s" }, - "execution": { - // Run desktop+mobile in parallel (needs more resources). If false, runs sequentially. - "parallel": false, - // If false and there are 0 points available, the run is skipped early to save time. - "runOnZeroPoints": false, - // Number of account clusters (processes) to run concurrently. - "clusters": 1, - // Number of passes per invocation (advanced; usually 1). - "passesPerRun": 1 - }, - - "buyMode": { - // Manual purchase/redeem mode. Use CLI -buy to enable, or set buyMode.enabled in config. - // Session duration cap in minutes. - "maxMinutes": 45 - }, - "fingerprinting": { - // Persist browser fingerprints per device type to improve consistency across runs + // Persist browser fingerprints to improve consistency across runs "saveFingerprint": { "mobile": true, "desktop": true } }, + + // ============================================================ + // ⚙️ EXECUTION & PERFORMANCE + // ============================================================ + + "execution": { + // true = Desktop + Mobile in parallel (faster, more resources) + // false = Sequential (slower, fewer resources) + "parallel": false, + // If false, skip execution when 0 points are available + "runOnZeroPoints": false, + // Number of account clusters (processes) to run concurrently + "clusters": 1, + // Number of passes per invocation (usually 1) + "passesPerRun": 1 + }, + + "schedule": { + // Built-in scheduler (no cron needed in containers) + "enabled": false, + // Time format options: + // - US style with AM/PM → useAmPm: true and time12 (e.g., "9:00 AM") + // - 24-hour style → useAmPm: false and time24 (e.g., "09:00") + "useAmPm": false, + "time12": "9:00 AM", + "time24": "09:00", + // IANA timezone (e.g., "Europe/Paris", "America/New_York" check schedule.md) + "timeZone": "Europe/Paris", + // If true, run immediately on process start + "runImmediatelyOnStart": false + }, + + "jobState": { + // Save state to avoid duplicate work across restarts + "enabled": true, + // Custom state directory (empty = defaults to sessionPath/job-state) + "dir": "" + }, + + + // ============================================================ + // 🎯 TASKS & WORKERS + // ============================================================ + + "workers": { + // Select which tasks the bot should complete on desktop/mobile + "doDailySet": true, // Daily set tasks + "doMorePromotions": true, // More promotions section + "doPunchCards": true, // Punch cards + "doDesktopSearch": true, // Desktop searches + "doMobileSearch": true, // Mobile searches + "doDailyCheckIn": true, // Daily check-in + "doReadToEarn": true, // Read to earn + // If true, run desktop searches right after Daily Set + "bundleDailySetWithSearch": true + }, + + + // ============================================================ + // 🔍 SEARCH CONFIGURATION + // ============================================================ + "search": { // Use locale-specific query sources "useLocalQueries": true, "settings": { - // Add geo/locale signal into query selection + // Use region-specific queries (at, fr, us, etc.) "useGeoLocaleQueries": true, - // Randomly scroll search result pages to look more natural + // Randomly scroll search result pages (more natural behavior) "scrollRandomResults": true, // Occasionally click a result (safe targets only) "clickRandomResults": true, - // Number of times to retry mobile searches if points didn’t progress + // Number of retries if mobile searches don't progress "retryMobileSearchAmount": 2, - // Delay between searches (supports numbers in ms or time strings) + // Delay between searches "delay": { "min": "1min", "max": "5min" @@ -56,38 +114,70 @@ } }, - "humanization": { - // Global Human Mode switch. true=adds subtle micro-gestures & pauses. false=classic behavior. + "queryDiversity": { + // Multi-source query generation: Reddit, News, Wikipedia instead of only Google Trends "enabled": true, - // If true, as soon as a ban is detected on any account, stop processing remaining accounts - // (ban detection is based on centralized heuristics and error signals) + // Available sources: google-trends, reddit, news, wikipedia, local-fallback + "sources": ["google-trends", "reddit", "local-fallback"], + // Max queries to fetch per source + "maxQueriesPerSource": 10, + // Cache duration in minutes (avoids hammering APIs) + "cacheMinutes": 30 + }, + + + // ============================================================ + // 🤖 HUMANIZATION & NATURAL BEHAVIOR + // ============================================================ + + "humanization": { + // Human Mode: adds subtle micro-gestures & pauses to mimic real users + "enabled": true, + // If a ban is detected on any account, stop processing remaining accounts "stopOnBan": true, - // If true, immediately send an alert (webhook/NTFY) when a ban is detected + // Immediately send an alert (webhook/NTFY) when a ban is detected "immediateBanAlert": true, - // Extra random pause between actions (ms or time string e.g., "300ms", "1s") + // Extra random pause between actions "actionDelay": { - "min": 500, - "max": 2200 + "min": 500, // 0.5 seconds minimum + "max": 2200 // 2.2 seconds maximum }, - // Probability (0..1) to move mouse a tiny bit in between actions + // Probability (0-1) to move mouse slightly between actions "gestureMoveProb": 0.65, - // Probability (0..1) to perform a very small scroll + // Probability (0-1) to perform a small scroll "gestureScrollProb": 0.4, - // Optional local-time windows for execution (e.g., ["08:30-11:00", "19:00-22:00"]). - // If provided, runs will wait until inside a window before starting. + // Optional execution time windows (e.g., ["08:30-11:00", "19:00-22:00"]) + // If specified, waits until inside a window before starting "allowedWindows": [] }, - - // Optional monthly "vacation" block: skip a contiguous range of days to look more human. - // This is independent of weekly random off-days. When enabled, each month a random - // block between minDays and maxDays is selected (e.g., 2–4 days) and all runs within - // that date range are skipped. The chosen block is logged at the start of the month. + "vacation": { + // Monthly "vacation" block: skip a random range of days each month + // Each month, a random period between minDays and maxDays is selected + // and all runs within that date range are skipped (more human-like behavior) "enabled": true, "minDays": 2, "maxDays": 4 }, + + // ============================================================ + // 🛡️ RISK MANAGEMENT & SECURITY + // ============================================================ + + "riskManagement": { + // Dynamic delay adjustment based on detected risk signals + "enabled": true, + // Automatically increase delays when captchas/errors are detected + "autoAdjustDelays": true, + // Stop execution if risk level reaches critical threshold + "stopOnCritical": false, + // Enable ML-based ban prediction based on patterns + "banPrediction": true, + // Risk threshold (0-100). If exceeded, bot pauses or alerts you + "riskThreshold": 75 + }, + "retryPolicy": { // Generic retry/backoff for transient failures "maxAttempts": 3, @@ -97,18 +187,10 @@ "jitter": 0.2 }, - "workers": { - // Select what the bot should complete on desktop/mobile - "doDailySet": true, - "doMorePromotions": true, - "doPunchCards": true, - "doDesktopSearch": true, - "doMobileSearch": true, - "doDailyCheckIn": true, - "doReadToEarn": true, - // If true, run a desktop search bundle right after Daily Set - "bundleDailySetWithSearch": true - }, + + // ============================================================ + // 🌐 PROXY + // ============================================================ "proxy": { // Control which outbound calls go through your proxy @@ -116,13 +198,18 @@ "proxyBingTerms": true }, + + // ============================================================ + // 🔔 NOTIFICATIONS + // ============================================================ + "notifications": { - // Live logs (Discord or similar). URL is your webhook endpoint. + // Live logs webhook (Discord or similar). URL = your webhook endpoint "webhook": { "enabled": false, "url": "" }, - // Rich end-of-run summary (Discord or similar) + // Rich end-of-run summary webhook (Discord or similar) "conclusionWebhook": { "enabled": false, "url": "" @@ -136,9 +223,14 @@ } }, + + // ============================================================ + // 📊 LOGGING & DIAGNOSTICS + // ============================================================ + "logging": { - // Logging controls (see docs/config.md). Remove redactEmails or set false to show full emails. - // Filter out noisy log buckets locally and for any webhook summaries + // Logging controls (see docs/config.md) + // Filter out noisy log buckets locally and for webhook summaries "excludeFunc": [ "SEARCH-CLOSE-TABS", "LOGIN-NO-PROMPT", @@ -149,12 +241,12 @@ "LOGIN-NO-PROMPT", "FLOW" ], - // Email redaction toggle (previously logging.live.redactEmails) + // Email redaction toggle (true = secure, false = full emails) "redactEmails": true }, "diagnostics": { - // Capture minimal evidence on failures (screenshots/HTML) and prune old runs + // Capture minimal evidence on failures (screenshots/HTML) "enabled": true, "saveScreenshot": true, "saveHtml": true, @@ -162,75 +254,45 @@ "retentionDays": 7 }, - - - "jobState": { - // Checkpoint to avoid duplicate work across restarts - "enabled": true, - // Custom state directory (defaults to sessionPath/job-state if empty) - "dir": "" - }, - - "schedule": { - // Built-in scheduler (no cron needed in container). Uses the IANA time zone below. - "enabled": false, - // Choose YOUR preferred time format: - // - US style with AM/PM → set useAmPm: true and edit time12 (e.g., "9:00 AM") - // - 24-hour style → set useAmPm: false and edit time24 (e.g., "09:00") - // Back-compat: if both time12/time24 are empty, the legacy "time" (HH:mm) will be used if present. - "useAmPm": false, - "time12": "9:00 AM", - "time24": "09:00", - // IANA timezone for scheduling (set to your region), e.g. "Europe/Paris" or "America/New_York" - "timeZone": "America/New_York", - // If true, run one pass immediately when the process starts - "runImmediatelyOnStart": false - }, - - "update": { - // Optional post-run auto-update - "git": true, - "docker": false, - // Custom updater script path (relative to repo root) - "scriptPath": "setup/update/update.mjs" - }, - - // NEW INTELLIGENT FEATURES - "riskManagement": { - // Risk-Aware Throttling: dynamically adjusts delays based on detected risk signals - "enabled": true, - // Automatically increase delays when captchas/errors are detected - "autoAdjustDelays": true, - // Stop execution if risk level reaches critical (score > riskThreshold) - "stopOnCritical": false, - // Enable ML-style ban prediction based on patterns - "banPrediction": true, - // Risk threshold (0-100). If exceeded, bot pauses or alerts you. - "riskThreshold": 75 - }, - "analytics": { - // Performance Dashboard: track points earned, success rates, execution times + // 📈 Performance Dashboard: tracks points earned, success rates, execution times + // Useful for monitoring your stats over time. Disable if you don't need it. + // WHAT IT DOES: + // - Collects daily/weekly/monthly statistics + // - Calculates success rates for each activity type + // - Tracks average execution times + // - Generates trend reports + // - Can export to Markdown or send via webhook "enabled": true, // How long to keep analytics data (days) "retentionDays": 30, // Generate markdown summary reports "exportMarkdown": true, // Send analytics summary via webhook - "webhookSummary": false + "webhookSummary": true }, - "queryDiversity": { - // Multi-source query generation: use Reddit, News, Wikipedia instead of just Google Trends - "enabled": true, - // Which sources to use (google-trends, reddit, news, wikipedia, local-fallback) - "sources": ["google-trends", "reddit", "local-fallback"], - // Max queries to fetch per source - "maxQueriesPerSource": 10, - // Cache duration in minutes (avoids hammering APIs) - "cacheMinutes": 30 + + // ============================================================ + // 🛒 BUY MODE + // ============================================================ + + "buyMode": { + // Manual purchase/redeem mode. Use CLI -buy to enable + // Session duration cap in minutes + "maxMinutes": 45 }, - // Dry-run mode: simulate execution without actually running tasks (useful for testing config) - "dryRun": false + + // ============================================================ + // 🔄 UPDATES + // ============================================================ + + "update": { + // Post-run auto-update settings + "git": true, + "docker": false, + // Custom updater script path (relative to repo root) + "scriptPath": "setup/update/update.mjs" + } } diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..3a80f8f --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,67 @@ +/** + * Central constants file for the Microsoft Rewards Script + * Defines timeouts, retry limits, and other magic numbers used throughout the application + */ + +export const TIMEOUTS = { + SHORT: 500, + MEDIUM: 1500, + MEDIUM_LONG: 2000, + LONG: 3000, + VERY_LONG: 5000, + EXTRA_LONG: 10000, + DASHBOARD_WAIT: 10000, + LOGIN_MAX: 180000, // 3 minutes + NETWORK_IDLE: 5000 +} as const + +export const RETRY_LIMITS = { + MAX_ITERATIONS: 5, + DASHBOARD_RELOAD: 2, + MOBILE_SEARCH: 3, + ABC_MAX: 15, + POLL_MAX: 15, + QUIZ_MAX: 15, + QUIZ_ANSWER_TIMEOUT: 10000, + GO_HOME_MAX: 5 +} as const + +export const DELAYS = { + ACTION_MIN: 1000, + ACTION_MAX: 3000, + SEARCH_DEFAULT_MIN: 2000, + SEARCH_DEFAULT_MAX: 5000, + BROWSER_CLOSE: 2000, + TYPING_DELAY: 20, + SEARCH_ON_BING_WAIT: 5000, + SEARCH_ON_BING_COMPLETE: 3000, + SEARCH_ON_BING_FOCUS: 200, + SEARCH_BAR_TIMEOUT: 15000, + QUIZ_ANSWER_WAIT: 2000, + THIS_OR_THAT_START: 2000 +} as const + +export const SELECTORS = { + MORE_ACTIVITIES: '#more-activities', + SUSPENDED_ACCOUNT: '#suspendedAccountHeader', + QUIZ_COMPLETE: '#quizCompleteContainer', + QUIZ_CREDITS: 'span.rqMCredits' +} as const + +export const URLS = { + REWARDS_BASE: 'https://rewards.bing.com', + REWARDS_SIGNIN: 'https://rewards.bing.com/signin', + APP_USER_DATA: 'https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613' +} as const + +export const DISCORD = { + MAX_EMBED_LENGTH: 1900, + RATE_LIMIT_DELAY: 500, + WEBHOOK_TIMEOUT: 10000, + DEBOUNCE_DELAY: 750, + COLOR_RED: 0xFF0000, + COLOR_CRIMSON: 0xDC143C, + COLOR_ORANGE: 0xFFA500, + COLOR_BLUE: 0x3498DB, + COLOR_GREEN: 0x00D26A +} as const diff --git a/src/functions/Login.ts b/src/functions/Login.ts index 1e3d2fd..c576cad 100644 --- a/src/functions/Login.ts +++ b/src/functions/Login.ts @@ -202,6 +202,10 @@ export class Login { // --------------- 2FA Handling --------------- private async handle2FA(page: Page) { try { + // Dismiss any popups/dialogs before checking 2FA (Terms Update, etc.) + await this.bot.browser.utils.tryDismissAllMessages(page) + await this.bot.utils.wait(500) + if (this.currentTotpSecret) { const totpSelector = await this.ensureTotpInput(page) if (totpSelector) { @@ -273,10 +277,45 @@ export class Login { } catch {/* ignore */} } - // Manual prompt + // Manual prompt with periodic page check this.bot.log(this.bot.isMobile, 'LOGIN', 'Waiting for user 2FA code (SMS / Email / App fallback)') const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) - const code: string = await new Promise(res => rl.question('Enter 2FA code:\n', ans => { rl.close(); res(ans.trim()) })) + + // Monitor page changes while waiting for user input + let userInput: string | null = null + let checkInterval: NodeJS.Timeout | null = null + + const inputPromise = new Promise(res => { + rl.question('Enter 2FA code:\n', ans => { + if (checkInterval) clearInterval(checkInterval) + rl.close() + res(ans.trim()) + }) + }) + + // Check every 2 seconds if user manually progressed past the dialog + checkInterval = setInterval(async () => { + try { + await this.bot.browser.utils.tryDismissAllMessages(page) + // Check if we're no longer on 2FA page + const still2FA = await page.locator('input[name="otc"]').first().isVisible({ timeout: 500 }).catch(() => false) + if (!still2FA) { + this.bot.log(this.bot.isMobile, 'LOGIN', 'Page changed during 2FA wait (user may have clicked Next)', 'warn') + if (checkInterval) clearInterval(checkInterval) + rl.close() + userInput = 'skip' // Signal to skip submission + } + } catch {/* ignore */} + }, 2000) + + const code = await inputPromise + if (checkInterval) clearInterval(checkInterval) + + if (code === 'skip' || userInput === 'skip') { + this.bot.log(this.bot.isMobile, 'LOGIN', 'Skipping 2FA code submission (page progressed)') + return + } + await page.fill('input[name="otc"]', code) await page.keyboard.press('Enter') this.bot.log(this.bot.isMobile, 'LOGIN', '2FA code submitted') @@ -729,7 +768,7 @@ export class Login { } private getDocsUrl(anchor?: string) { - const base = process.env.DOCS_BASE?.trim() || 'https://github.com/LightZirconite/Microsoft-Rewards-Script-Private/blob/V2/docs/security.md' + const base = process.env.DOCS_BASE?.trim() || 'https://github.com/LightZirconite/Microsoft-Rewards-Script-Private/blob/v2/docs/security.md' const map: Record = { 'recovery-email-mismatch':'#recovery-email-mismatch', 'we-cant-sign-you-in':'#we-cant-sign-you-in-blocked' diff --git a/src/functions/activities/ABC.ts b/src/functions/activities/ABC.ts index 72e61d7..e1827fe 100644 --- a/src/functions/activities/ABC.ts +++ b/src/functions/activities/ABC.ts @@ -1,6 +1,7 @@ import { Page } from 'rebrowser-playwright' import { Workers } from '../Workers' +import { RETRY_LIMITS, TIMEOUTS } from '../../constants' export class ABC extends Workers { @@ -11,34 +12,32 @@ export class ABC extends Workers { try { let $ = await this.bot.browser.func.loadInCheerio(page) - // Don't loop more than 15 in case unable to solve, would lock otherwise - const maxIterations = 15 let i - for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) { - await page.waitForSelector('.wk_OptionClickClass', { state: 'visible', timeout: 10000 }) + for (i = 0; i < RETRY_LIMITS.ABC_MAX && !$('span.rw_icon').length; i++) { + await page.waitForSelector('.wk_OptionClickClass', { state: 'visible', timeout: TIMEOUTS.DASHBOARD_WAIT }) const answers = $('.wk_OptionClickClass') const answer = answers[this.bot.utils.randomNumber(0, 2)]?.attribs['id'] - await page.waitForSelector(`#${answer}`, { state: 'visible', timeout: 10000 }) + await page.waitForSelector(`#${answer}`, { state: 'visible', timeout: TIMEOUTS.DASHBOARD_WAIT }) - await this.bot.utils.wait(2000) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) await page.click(`#${answer}`) // Click answer - await this.bot.utils.wait(4000) - await page.waitForSelector('div.wk_button', { state: 'visible', timeout: 10000 }) + await this.bot.utils.wait(TIMEOUTS.LONG + 1000) + await page.waitForSelector('div.wk_button', { state: 'visible', timeout: TIMEOUTS.DASHBOARD_WAIT }) await page.click('div.wk_button') // Click next question button page = await this.bot.browser.utils.getLatestTab(page) $ = await this.bot.browser.func.loadInCheerio(page) - await this.bot.utils.wait(1000) + await this.bot.utils.wait(TIMEOUTS.MEDIUM) } - await this.bot.utils.wait(4000) + await this.bot.utils.wait(TIMEOUTS.LONG + 1000) await page.close() - if (i === maxIterations) { - this.bot.log(this.bot.isMobile, 'ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn') + if (i === RETRY_LIMITS.ABC_MAX) { + this.bot.log(this.bot.isMobile, 'ABC', `Failed to solve quiz, exceeded max iterations of ${RETRY_LIMITS.ABC_MAX}`, 'warn') } else { this.bot.log(this.bot.isMobile, 'ABC', 'Completed the ABC successfully') } diff --git a/src/functions/activities/Poll.ts b/src/functions/activities/Poll.ts index 526a2e0..57b4ec5 100644 --- a/src/functions/activities/Poll.ts +++ b/src/functions/activities/Poll.ts @@ -1,6 +1,7 @@ import { Page } from 'rebrowser-playwright' import { Workers } from '../Workers' +import { TIMEOUTS } from '../../constants' export class Poll extends Workers { @@ -11,12 +12,14 @@ export class Poll extends Workers { try { const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}` - await page.waitForSelector(buttonId, { state: 'visible', timeout: 10000 }).catch(() => { }) // We're gonna click regardless or not - await this.bot.utils.wait(2000) + await page.waitForSelector(buttonId, { state: 'visible', timeout: TIMEOUTS.DASHBOARD_WAIT }).catch((e) => { + this.bot.log(this.bot.isMobile, 'POLL', `Could not find poll button: ${e}`, 'warn') + }) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) await page.click(buttonId) - await this.bot.utils.wait(4000) + await this.bot.utils.wait(TIMEOUTS.LONG + 1000) await page.close() this.bot.log(this.bot.isMobile, 'POLL', 'Completed the poll successfully') diff --git a/src/functions/activities/Quiz.ts b/src/functions/activities/Quiz.ts index c858024..4bbe619 100644 --- a/src/functions/activities/Quiz.ts +++ b/src/functions/activities/Quiz.ts @@ -1,6 +1,7 @@ import { Page } from 'rebrowser-playwright' import { Workers } from '../Workers' +import { RETRY_LIMITS, TIMEOUTS, DELAYS } from '../../constants' export class Quiz extends Workers { @@ -10,19 +11,19 @@ export class Quiz extends Workers { try { // Check if the quiz has been started or not - const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { state: 'visible', timeout: 2000 }).then(() => true).catch(() => false) + const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { state: 'visible', timeout: TIMEOUTS.MEDIUM_LONG }).then(() => true).catch(() => false) if (quizNotStarted) { await page.click('#rqStartQuiz') } else { this.bot.log(this.bot.isMobile, 'QUIZ', 'Quiz has already been started, trying to finish it') } - await this.bot.utils.wait(2000) + await this.bot.utils.wait(TIMEOUTS.MEDIUM_LONG) let quizData = await this.bot.browser.func.getQuizData(page) // Verify quiz is actually loaded before proceeding - const firstOptionExists = await page.waitForSelector('#rqAnswerOption0', { state: 'attached', timeout: 5000 }).then(() => true).catch(() => false) + const firstOptionExists = await page.waitForSelector('#rqAnswerOption0', { state: 'attached', timeout: TIMEOUTS.VERY_LONG }).then(() => true).catch(() => false) if (!firstOptionExists) { this.bot.log(this.bot.isMobile, 'QUIZ', 'Quiz options not found - page may not have loaded correctly. Skipping.', 'warn') await page.close() @@ -37,7 +38,7 @@ export class Quiz extends Workers { const answers: string[] = [] for (let i = 0; i < quizData.numberOfOptions; i++) { - const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10000 }).catch(() => null) + const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: TIMEOUTS.DASHBOARD_WAIT }).catch(() => null) if (!answerSelector) { this.bot.log(this.bot.isMobile, 'QUIZ', `Option ${i} not found - quiz structure may have changed. Skipping remaining options.`, 'warn') @@ -60,7 +61,7 @@ export class Quiz extends Workers { // Click the answers for (const answer of answers) { - await page.waitForSelector(answer, { state: 'visible', timeout: 2000 }) + await page.waitForSelector(answer, { state: 'visible', timeout: DELAYS.QUIZ_ANSWER_WAIT }) // Click the answer on page await page.click(answer) @@ -82,7 +83,7 @@ export class Quiz extends Workers { for (let i = 0; i < quizData.numberOfOptions; i++) { - const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10000 }).catch(() => null) + const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: RETRY_LIMITS.QUIZ_ANSWER_TIMEOUT }).catch(() => null) if (!answerSelector) { this.bot.log(this.bot.isMobile, 'QUIZ', `Option ${i} not found for ${quizData.numberOfOptions}-option quiz. Skipping.`, 'warn') @@ -112,12 +113,12 @@ export class Quiz extends Workers { return } - await this.bot.utils.wait(2000) + await this.bot.utils.wait(DELAYS.QUIZ_ANSWER_WAIT) } } // Done with - await this.bot.utils.wait(2000) + await this.bot.utils.wait(DELAYS.QUIZ_ANSWER_WAIT) await page.close() this.bot.log(this.bot.isMobile, 'QUIZ', 'Completed the quiz successfully') diff --git a/src/functions/activities/Search.ts b/src/functions/activities/Search.ts index e65107d..593b717 100644 --- a/src/functions/activities/Search.ts +++ b/src/functions/activities/Search.ts @@ -277,8 +277,10 @@ export class Search extends Workers { } const mappedTrendsData = trendsData.map(query => [query[0], query[9]!.slice(1)]) - if (mappedTrendsData.length < 90) { - this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', 'Insufficient search queries, falling back to US', 'warn') + this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', `Found ${mappedTrendsData.length} search queries for ${geoLocale}`) + + if (mappedTrendsData.length < 30 && geoLocale.toUpperCase() !== 'US') { + this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', `Insufficient search queries (${mappedTrendsData.length} < 30), falling back to US`, 'warn') return this.getGoogleTrends() } diff --git a/src/functions/activities/SearchOnBing.ts b/src/functions/activities/SearchOnBing.ts index 561d49b..aa0f999 100644 --- a/src/functions/activities/SearchOnBing.ts +++ b/src/functions/activities/SearchOnBing.ts @@ -3,6 +3,7 @@ import * as fs from 'fs' import path from 'path' import { Workers } from '../Workers' +import { DELAYS } from '../../constants' import { MorePromotion, PromotionalItem } from '../../interface/DashboardData' @@ -13,7 +14,7 @@ export class SearchOnBing extends Workers { this.bot.log(this.bot.isMobile, 'SEARCH-ON-BING', 'Trying to complete SearchOnBing') try { - await this.bot.utils.wait(5000) + await this.bot.utils.wait(DELAYS.SEARCH_ON_BING_WAIT) await this.bot.browser.utils.tryDismissAllMessages(page) @@ -21,20 +22,20 @@ export class SearchOnBing extends Workers { const searchBar = '#sb_form_q' const box = page.locator(searchBar) - await box.waitFor({ state: 'attached', timeout: 15000 }) + await box.waitFor({ state: 'attached', timeout: DELAYS.SEARCH_BAR_TIMEOUT }) await this.bot.browser.utils.tryDismissAllMessages(page) - await this.bot.utils.wait(200) + await this.bot.utils.wait(DELAYS.SEARCH_ON_BING_FOCUS) try { - await box.focus({ timeout: 2000 }).catch(() => { /* ignore */ }) + await box.focus({ timeout: DELAYS.THIS_OR_THAT_START }).catch(() => { /* ignore */ }) await box.fill('') - await this.bot.utils.wait(200) - await page.keyboard.type(query, { delay: 20 }) + await this.bot.utils.wait(DELAYS.SEARCH_ON_BING_FOCUS) + await page.keyboard.type(query, { delay: DELAYS.TYPING_DELAY }) await page.keyboard.press('Enter') } catch { const url = `https://www.bing.com/search?q=${encodeURIComponent(query)}` await page.goto(url) } - await this.bot.utils.wait(3000) + await this.bot.utils.wait(DELAYS.SEARCH_ON_BING_COMPLETE) await page.close() diff --git a/src/functions/activities/ThisOrThat.ts b/src/functions/activities/ThisOrThat.ts index f6e6edb..5cf7b0f 100644 --- a/src/functions/activities/ThisOrThat.ts +++ b/src/functions/activities/ThisOrThat.ts @@ -1,6 +1,7 @@ import { Page } from 'rebrowser-playwright' import { Workers } from '../Workers' +import { DELAYS } from '../../constants' export class ThisOrThat extends Workers { @@ -11,14 +12,14 @@ export class ThisOrThat extends Workers { try { // Check if the quiz has been started or not - const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { state: 'visible', timeout: 2000 }).then(() => true).catch(() => false) + const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { state: 'visible', timeout: DELAYS.THIS_OR_THAT_START }).then(() => true).catch(() => false) if (quizNotStarted) { await page.click('#rqStartQuiz') } else { this.bot.log(this.bot.isMobile, 'THIS-OR-THAT', 'ThisOrThat has already been started, trying to finish it') } - await this.bot.utils.wait(2000) + await this.bot.utils.wait(DELAYS.THIS_OR_THAT_START) // Solving const quizData = await this.bot.browser.func.getQuizData(page) diff --git a/src/functions/activities/UrlReward.ts b/src/functions/activities/UrlReward.ts index b5c310e..cbc94f4 100644 --- a/src/functions/activities/UrlReward.ts +++ b/src/functions/activities/UrlReward.ts @@ -9,7 +9,7 @@ export class UrlReward extends Workers { this.bot.log(this.bot.isMobile, 'URL-REWARD', 'Trying to complete UrlReward') try { - this.bot.utils.wait(2000) + await this.bot.utils.wait(2000) await page.close() diff --git a/src/index.ts b/src/index.ts index 1b0b6b6..efdbad7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import BrowserUtil from './browser/BrowserUtil' import { log } from './util/Logger' import Util from './util/Utils' import { loadAccounts, loadConfig, saveSessionData } from './util/Load' +import { DISCORD } from './constants' import { Login } from './functions/Login' import { Workers } from './functions/Workers' @@ -66,8 +67,7 @@ export class MicrosoftRewardsBot { private heartbeatFile?: string private heartbeatTimer?: NodeJS.Timeout - //@ts-expect-error Will be initialized later - public axios: Axios + public axios!: Axios constructor(isMobile: boolean) { this.isMobile = isMobile @@ -650,10 +650,15 @@ export class MicrosoftRewardsBot { // Cleanup heartbeat timer/file at end of run if (this.heartbeatTimer) { try { clearInterval(this.heartbeatTimer) } catch { /* ignore */ } } if (this.heartbeatFile) { try { if (fs.existsSync(this.heartbeatFile)) fs.unlinkSync(this.heartbeatFile) } catch { /* ignore */ } } - // After conclusion, run optional auto-update - await this.runAutoUpdate().catch(() => {/* ignore update errors */}) + // After conclusion, run optional auto-update (only if not in scheduler mode) + if (!process.env.SCHEDULER_HEARTBEAT_FILE) { + await this.runAutoUpdate().catch(() => {/* ignore update errors */}) + } + } + // Only exit if not spawned by scheduler + if (!process.env.SCHEDULER_HEARTBEAT_FILE) { + process.exit() } - process.exit() } /** Send immediate ban alert if configured. */ @@ -669,7 +674,7 @@ export class MicrosoftRewardsBot { { title, description: desc, - color: 0xFF0000 + color: DISCORD.COLOR_RED } ] }) @@ -956,20 +961,7 @@ export class MicrosoftRewardsBot { let accountsWithErrors = 0 let successes = 0 - type DiscordField = { name: string; value: string; inline?: boolean } - type DiscordFooter = { text: string } - type DiscordEmbed = { - title?: string - description?: string - color?: number - fields?: DiscordField[] - timestamp?: string - footer?: DiscordFooter - } - - const accountFields: DiscordField[] = [] - const accountLines: string[] = [] - + // Calculate summary statistics for (const s of summaries) { totalCollected += s.totalCollected totalInitial += s.initialTotal @@ -977,44 +969,12 @@ export class MicrosoftRewardsBot { totalDuration += s.durationMs if (s.errors.length) accountsWithErrors++ else successes++ - - const statusEmoji = s.banned?.status ? '🚫' : (s.errors.length ? '⚠️' : '✅') - const diff = s.totalCollected - const duration = formatDuration(s.durationMs) - - // Build embed fields (Discord) - const valueLines: string[] = [ - `Points: ${s.initialTotal} → ${s.endTotal} ( +${diff} )`, - `Breakdown: 🖥️ ${s.desktopCollected} | 📱 ${s.mobileCollected}`, - `Duration: ⏱️ ${duration}` - ] - if (s.banned?.status) { - valueLines.push(`Banned: ${s.banned.reason || 'detected by heuristics'}`) - } - if (s.errors.length) { - valueLines.push(`Errors: ${s.errors.slice(0, 2).join(' | ')}`) - } - accountFields.push({ - name: `${statusEmoji} ${s.email}`.substring(0, 256), - value: valueLines.join('\n').substring(0, 1024), - inline: false - }) - - // Build plain text lines (NTFY) - const lines = [ - `${statusEmoji} ${s.email}`, - ` Points: ${s.initialTotal} → ${s.endTotal} ( +${diff} )`, - ` 🖥️ ${s.desktopCollected} | 📱 ${s.mobileCollected}`, - ` Duration: ${duration}` - ] - if (s.banned?.status) lines.push(` Banned: ${s.banned.reason || 'detected by heuristics'}`) - if (s.errors.length) lines.push(` Errors: ${s.errors.slice(0, 2).join(' | ')}`) - accountLines.push(lines.join('\n') + '\n') } const avgDuration = totalDuration / totalAccounts + const avgPointsPerAccount = Math.round(totalCollected / totalAccounts) - // Read package version (best-effort) + // Read package version let version = 'unknown' try { const pkgPath = path.join(process.cwd(), 'package.json') @@ -1025,80 +985,70 @@ export class MicrosoftRewardsBot { } } catch { /* ignore */ } - // Discord/Webhook embeds with chunking (limits: 10 embeds/message, 25 fields/embed) - const MAX_EMBEDS = 10 - const MAX_FIELDS = 25 + // Build clean embed with account details + type DiscordField = { name: string; value: string; inline?: boolean } + type DiscordEmbed = { + title?: string + description?: string + color?: number + fields?: DiscordField[] + thumbnail?: { url: string } + timestamp?: string + footer?: { text: string; icon_url?: string } + } - const baseFields = [ - { - name: 'Global Totals', - value: [ - `Total Points: ${totalInitial} → ${totalEnd} ( +${totalCollected} )`, - `Accounts: ✅ ${successes} • ⚠️ ${accountsWithErrors} (of ${totalAccounts})`, - `Average Duration: ${formatDuration(avgDuration)}`, - `Cumulative Runtime: ${formatDuration(totalDuration)}` - ].join('\n') - } - ] + const accountDetails: string[] = [] + for (const s of summaries) { + const statusIcon = s.banned?.status ? '🚫' : (s.errors.length ? '⚠️' : '✅') + const line = `${statusIcon} **${s.email}** → +${s.totalCollected}pts (🖥️${s.desktopCollected} 📱${s.mobileCollected}) • ${formatDuration(s.durationMs)}` + accountDetails.push(line) + if (s.banned?.status) accountDetails.push(` └ Banned: ${s.banned.reason || 'detected'}`) + if (s.errors.length > 0) accountDetails.push(` └ Errors: ${s.errors.slice(0, 2).join(', ')}`) + } - // Prepare embeds: first embed for totals, subsequent for accounts - const embeds: DiscordEmbed[] = [] - const headerEmbed: DiscordEmbed = { - title: '🎯 Microsoft Rewards Summary', - description: `Processed **${totalAccounts}** account(s)${accountsWithErrors ? ` • ${accountsWithErrors} with issues` : ''}`, - color: accountsWithErrors ? 0xFFAA00 : 0x32CD32, - fields: baseFields, + const embed: DiscordEmbed = { + title: '🎯 Microsoft Rewards - Daily Summary', + description: [ + '**📊 Global Statistics**', + `├ Total Points: **${totalInitial}** → **${totalEnd}** (+**${totalCollected}**)`, + `├ Accounts: ✅ ${successes} • ${accountsWithErrors > 0 ? `⚠️ ${accountsWithErrors}` : ''} (${totalAccounts} total)`, + `├ Average: **${avgPointsPerAccount}pts/account** • **${formatDuration(avgDuration)}/account**`, + `└ Runtime: **${formatDuration(totalDuration)}**`, + '', + '**📈 Account Details**', + ...accountDetails + ].filter(Boolean).join('\n'), + color: accountsWithErrors > 0 ? DISCORD.COLOR_ORANGE : DISCORD.COLOR_GREEN, + thumbnail: { + url: 'https://media.discordapp.net/attachments/1421163952972369931/1421929950377939125/Gc.png' + }, timestamp: new Date().toISOString(), - footer: { text: `Run ${this.runId}${version !== 'unknown' ? ` • v${version}` : ''}` } - } - embeds.push(headerEmbed) - - // Chunk account fields across remaining embeds - const fieldsPerEmbed = Math.min(MAX_FIELDS, 25) - const availableEmbeds = MAX_EMBEDS - embeds.length - const chunks: DiscordField[][] = [] - for (let i = 0; i < accountFields.length; i += fieldsPerEmbed) { - chunks.push(accountFields.slice(i, i + fieldsPerEmbed)) - } - - const includedChunks = chunks.slice(0, availableEmbeds) - for (const [idx, chunk] of includedChunks.entries()) { - const chunkEmbed: DiscordEmbed = { - title: `Accounts ${idx * fieldsPerEmbed + 1}–${Math.min((idx + 1) * fieldsPerEmbed, accountFields.length)}`, - color: accountsWithErrors ? 0xFFAA00 : 0x32CD32, - fields: chunk, - timestamp: new Date().toISOString() - } - embeds.push(chunkEmbed) - } - - const omitted = chunks.length - includedChunks.length - if (omitted > 0 && embeds.length > 0) { - // Add a small note to the last embed about omitted accounts - const last = embeds[embeds.length - 1]! - const noteField: DiscordField = { name: 'Note', value: `And ${omitted * fieldsPerEmbed} more account entries not shown due to Discord limits.`, inline: false } - if (last.fields && Array.isArray(last.fields)) { - last.fields = [...last.fields, noteField].slice(0, MAX_FIELDS) + footer: { + text: `MS Rewards Bot v${version} • Run ${this.runId}`, + icon_url: 'https://media.discordapp.net/attachments/1421163952972369931/1421929950377939125/Gc.png' } } - // NTFY-compatible plain text (includes per-account breakdown) + // NTFY plain text fallback const fallback = [ - 'Microsoft Rewards Summary', - `Accounts: ${totalAccounts}${accountsWithErrors ? ` • ${accountsWithErrors} with issues` : ''}`, - `Total: ${totalInitial} -> ${totalEnd} (+${totalCollected})`, - `Average Duration: ${formatDuration(avgDuration)}`, - `Cumulative Runtime: ${formatDuration(totalDuration)}`, + '🎯 Microsoft Rewards Summary', + `Accounts: ${totalAccounts} (✅${successes} ${accountsWithErrors > 0 ? `⚠️${accountsWithErrors}` : ''})`, + `Total: ${totalInitial}→${totalEnd} (+${totalCollected})`, + `Average: ${avgPointsPerAccount}pts/account • ${formatDuration(avgDuration)}`, + `Runtime: ${formatDuration(totalDuration)}`, '', - ...accountLines + ...summaries.map(s => { + const st = s.banned?.status ? '🚫' : (s.errors.length ? '⚠️' : '✅') + return `${st} ${s.email}: +${s.totalCollected}pts (🖥️${s.desktopCollected} 📱${s.mobileCollected})` + }) ].join('\n') - // Send both when any channel is enabled: Discord gets embeds, NTFY gets fallback - if (conclusionWebhookEnabled || ntfyEnabled || webhookEnabled) { - await ConclusionWebhook(cfg, fallback, { embeds }) - } + // Send webhook + if (conclusionWebhookEnabled || ntfyEnabled || webhookEnabled) { + await ConclusionWebhook(cfg, fallback, { embeds: [embed] }) + } - // Write local JSON report for observability + // Write local JSON report try { const fs = await import('fs') const path = await import('path') @@ -1119,7 +1069,7 @@ export class MicrosoftRewardsBot { log('main','REPORT',`Failed to save report: ${e instanceof Error ? e.message : e}`,'warn') } - // Optionally cleanup old diagnostics folders + // Cleanup old diagnostics try { const days = cfg.diagnostics?.retentionDays if (typeof days === 'number' && days > 0) { @@ -1128,6 +1078,7 @@ export class MicrosoftRewardsBot { } catch (e) { log('main','REPORT',`Failed diagnostics cleanup: ${e instanceof Error ? e.message : e}`,'warn') } + } /** Reserve one diagnostics slot for this run (caps captures). */ @@ -1209,7 +1160,7 @@ export class MicrosoftRewardsBot { { title, description: desc, - color: 0xFF0000 + color: DISCORD.COLOR_RED } ] }) @@ -1252,8 +1203,6 @@ function formatDuration(ms: number): string { } async function main() { - // CommunityReporter disabled per project policy - // (previously: init + global hooks for uncaughtException/unhandledRejection) const rewardsBot = new MicrosoftRewardsBot(false) const crashState = { restarts: 0 } @@ -1307,7 +1256,6 @@ async function main() { if (require.main === module) { main().catch(error => { log('main', 'MAIN-ERROR', `Error running bots: ${error}`, 'error') - // CommunityReporter disabled process.exit(1) }) } \ No newline at end of file diff --git a/src/util/ConclusionWebhook.ts b/src/util/ConclusionWebhook.ts index fe7f0ec..2265ef5 100644 --- a/src/util/ConclusionWebhook.ts +++ b/src/util/ConclusionWebhook.ts @@ -2,23 +2,20 @@ import axios from 'axios' import { Config } from '../interface/Config' import { Ntfy } from './Ntfy' -// Light obfuscation of the avatar URL (base64). Prevents casual editing in config. -const AVATAR_B64 = 'aHR0cHM6Ly9tZWRpYS5kaXNjb3JkYXBwLm5ldC9hdHRhY2htZW50cy8xNDIxMTYzOTUyOTcyMzY5OTMxLzE0MjExNjQxNDU5OTQyNDAxMTAvbXNuLnBuZz93aWR0aD01MTImZWlnaHQ9NTEy' -function getAvatarUrl(): string { - try { return Buffer.from(AVATAR_B64, 'base64').toString('utf-8') } catch { return '' } -} +// Avatar URL for webhook (new clean logo) +const AVATAR_URL = 'https://media.discordapp.net/attachments/1421163952972369931/1421929950377939125/Gc.png' type WebhookContext = 'summary' | 'ban' | 'security' | 'compromised' | 'spend' | 'error' | 'default' function pickUsername(ctx: WebhookContext, fallbackColor?: number): string { switch (ctx) { - case 'summary': return '📊 MS Rewards Summary' - case 'ban': return '🚫 Ban Alert' - case 'security': return '🔐 Security Alert' - case 'compromised': return '⚠️ Security Issue' - case 'spend': return '💳 Spend Notice' - case 'error': return '❌ Error Report' - default: return fallbackColor === 0xFF0000 ? '❌ Error Report' : '🎯 MS Rewards' + case 'summary': return 'MS Rewards - Daily Summary' + case 'ban': return 'MS Rewards - Ban Detected' + case 'security': return 'MS Rewards - Security Alert' + case 'compromised': return 'MS Rewards - Account Compromised' + case 'spend': return 'MS Rewards - Purchase Notification' + case 'error': return 'MS Rewards - Error Report' + default: return fallbackColor === 0xFF0000 ? 'MS Rewards - Error Report' : 'MS Rewards Bot' } } @@ -55,7 +52,7 @@ export async function ConclusionWebhook(config: Config, content: string, payload const firstColor = payload?.embeds && payload.embeds[0]?.color const ctx: WebhookContext = payload?.context || (firstColor === 0xFF0000 ? 'error' : 'default') body.username = pickUsername(ctx, firstColor) - body.avatar_url = getAvatarUrl() + body.avatar_url = AVATAR_URL // Post to conclusion webhook if configured const postWithRetry = async (url: string, label: string) => { diff --git a/src/util/Load.ts b/src/util/Load.ts index 8ec50b4..c255448 100644 --- a/src/util/Load.ts +++ b/src/util/Load.ts @@ -69,8 +69,9 @@ function stripJsonComments(input: string): string { // Normalize both legacy (flat) and new (nested) config schemas into the flat Config interface function normalizeConfig(raw: unknown): Config { + // Using any here is necessary to support both legacy flat config and new nested config structures // eslint-disable-next-line @typescript-eslint/no-explicit-any - const n: any = (raw as any) || {} + const n = (raw || {}) as any // Browser / execution const headless = n.browser?.headless ?? n.headless ?? false diff --git a/src/util/Logger.ts b/src/util/Logger.ts index efb6b7b..ce8b76c 100644 --- a/src/util/Logger.ts +++ b/src/util/Logger.ts @@ -3,6 +3,11 @@ import chalk from 'chalk' import { Ntfy } from './Ntfy' import { loadConfig } from './Load' +import { DISCORD } from '../constants' + +// Avatar URL for webhook (consistent with ConclusionWebhook) +const WEBHOOK_AVATAR_URL = 'https://media.discordapp.net/attachments/1421163952972369931/1421929950377939125/Gc.png' +const WEBHOOK_USERNAME = 'MS Rewards - Live Logs' type WebhookBuffer = { lines: string[] @@ -30,20 +35,31 @@ async function sendBatch(url: string, buf: WebhookBuffer) { while (buf.lines.length > 0) { const next = buf.lines[0]! const projected = currentLength + next.length + (chunk.length > 0 ? 1 : 0) - if (projected > 1900 && chunk.length > 0) break + if (projected > DISCORD.MAX_EMBED_LENGTH && chunk.length > 0) break buf.lines.shift() chunk.push(next) currentLength = projected } - const content = chunk.join('\n').slice(0, 1900) + const content = chunk.join('\n').slice(0, DISCORD.MAX_EMBED_LENGTH) if (!content) { continue } + // Enhanced webhook payload with embed, username and avatar + const payload = { + username: WEBHOOK_USERNAME, + avatar_url: WEBHOOK_AVATAR_URL, + embeds: [{ + description: `\`\`\`\n${content}\n\`\`\``, + color: determineColorFromContent(content), + timestamp: new Date().toISOString() + }] + } + try { - await axios.post(url, { content }, { headers: { 'Content-Type': 'application/json' }, timeout: 10000 }) - await new Promise(resolve => setTimeout(resolve, 500)) + await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' }, timeout: DISCORD.WEBHOOK_TIMEOUT }) + await new Promise(resolve => setTimeout(resolve, DISCORD.RATE_LIMIT_DELAY)) } catch (error) { // Re-queue failed batch at front and exit loop buf.lines = chunk.concat(buf.lines) @@ -54,6 +70,32 @@ async function sendBatch(url: string, buf: WebhookBuffer) { buf.sending = false } +function determineColorFromContent(content: string): number { + const lower = content.toLowerCase() + // Security/Ban alerts - Red + if (lower.includes('[banned]') || lower.includes('[security]') || lower.includes('suspended') || lower.includes('compromised')) { + return DISCORD.COLOR_RED + } + // Errors - Dark Red + if (lower.includes('[error]') || lower.includes('✗')) { + return DISCORD.COLOR_CRIMSON + } + // Warnings - Orange/Yellow + if (lower.includes('[warn]') || lower.includes('⚠')) { + return DISCORD.COLOR_ORANGE + } + // Success - Green + if (lower.includes('[ok]') || lower.includes('✓') || lower.includes('complet')) { + return DISCORD.COLOR_GREEN + } + // Info/Main - Blue + if (lower.includes('[main]')) { + return DISCORD.COLOR_BLUE + } + // Default - Gray + return 0x95A5A6 // Gray +} + function enqueueWebhookLog(url: string, line: string) { const buf = getBuffer(url) buf.lines.push(line) @@ -61,7 +103,7 @@ function enqueueWebhookLog(url: string, line: string) { buf.timer = setTimeout(() => { buf.timer = undefined void sendBatch(url, buf) - }, 750) + }, DISCORD.DEBOUNCE_DELAY) } } diff --git a/src/util/Ntfy.ts b/src/util/Ntfy.ts index b3d447a..b8e2ae4 100644 --- a/src/util/Ntfy.ts +++ b/src/util/Ntfy.ts @@ -20,14 +20,8 @@ export async function Ntfy(message: string, type: keyof typeof NOTIFICATION_TYPE ...(config.authToken && { Authorization: `Bearer ${config.authToken}` }) } - const response = await axios.post(`${config.url}/${config.topic}`, message, { headers }) - - if (response.status === 200) { - console.log('NTFY notification successfully sent.') - } else { - console.error(`NTFY notification failed with status ${response.status}`) - } + await axios.post(`${config.url}/${config.topic}`, message, { headers }) } catch (error) { - console.error('Failed to send NTFY notification:', error) + // Silently fail - NTFY is a non-critical notification service } } \ No newline at end of file diff --git a/src/util/QueryDiversityEngine.ts b/src/util/QueryDiversityEngine.ts index a9a3ef5..b640e7f 100644 --- a/src/util/QueryDiversityEngine.ts +++ b/src/util/QueryDiversityEngine.ts @@ -43,7 +43,7 @@ export class QueryDiversityEngine { const queries = await this.getFromSource(sourceName) allQueries.push(...queries.slice(0, this.config.maxQueriesPerSource)) } catch (error) { - console.warn(`Failed to fetch from ${sourceName}:`, error instanceof Error ? error.message : error) + // Silently fail and try other sources } } @@ -89,7 +89,8 @@ export class QueryDiversityEngine { queries = this.getLocalFallback(20) break default: - console.warn(`Unknown source: ${source}`) + // Unknown source, skip silently + break } this.cache.set(source, {