V2.3.0 Optimization (#380)

* 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
This commit is contained in:
Light
2025-10-16 17:59:53 +02:00
committed by GitHub
parent 4d928d7dd9
commit abd6117db3
37 changed files with 2392 additions and 4104 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@ dist/
node_modules/
.vscode/
.github/
diagnostic/
accounts.json
notes
accounts.dev.json

View File

@@ -17,7 +17,7 @@
<br>
![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)
<br><br>
<br>
**Made with ❤️ by the open source community**
<br>
![discord-avatar-128-ULDXD](https://github.com/user-attachments/assets/c33b0ee7-c56c-4f14-b177-851627236457)
<br><br>
<img src="https://capsule-render.vercel.app/api?type=waving&height=120&color=gradient&customColorList=0,2,2,5,6,8&section=footer" />
</div>
</div>

View File

@@ -1,94 +1,159 @@
# 👤 Accounts & TOTP (2FA)
# 👤 Accounts & 2FA Setup
<div align="center">
**🔐 Secure Microsoft account setup with 2FA support**
*Everything you need to configure authentication*
</div>
**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 Timebased OneTime Passwords (2FA). If set, the bot generates the 6digit code automatically when asked by Microsoft.
- `recoveryEmail` (optional): used to validate masked recovery prompts.
- `proxy` (optional): peraccount 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 reenable.
**[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
### 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)**

View File

@@ -1,64 +1,53 @@
# 💳 Buy Mode
<div align="center">
**🛒 Manual redemption with live point monitoring**
*Track your spending while maintaining full control*
</div>
**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)**

View File

@@ -1,145 +1,49 @@
# 📊 Discord Conclusion Webhook
# 📊 Discord Webhooks
<div align="center">
**🎯 Comprehensive session summaries via Discord**
*Complete execution reports delivered instantly*
</div>
**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
**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)**

View File

@@ -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 didnt yield points. |
| delay.min / delay.max | string/number | 35min | Delay between searches (ms or time string). |
---
## humanization
Humanlike 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 | 150450ms | 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: `<sessionPath>/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 | 12hour format time (only if useAmPm=true). |
| time24 | 09:00 | 24hour 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 didnt yield points. |
### 3. Background Mode (Headless)| delay.min / delay.max | string/number | 35min | Delay between searches (ms or time string). |
```jsonc---
{## humanization
"browser": {Humanlike 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 | 150450ms | 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. |
<details>
<summary><strong>Click to expand all options</strong></summary>---
## 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: `<sessionPath>/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 | 12hour format time (only if useAmPm=true). |
"clickRandomResults": true| time24 | 09:00 | 24hour 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
</details>
### 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)**

View File

@@ -1,32 +1,24 @@
# 🔍 Diagnostics & Error Capture
# 🔍 Diagnostics
<div align="center">
**🛠️ Automatic error screenshots and HTML snapshots**
*Debug smarter with visual evidence*
</div>
**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/<timestamp>-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
**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)**

View File

@@ -1,90 +1,167 @@
# 🐳 Docker Guide
<div align="center">
**⚡ Lightweight containerized deployment**
*Automated Microsoft Rewards with minimal Docker footprint*
</div>
**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 Chromiums 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
**[← Back to Hub](./index.md)** | **[Getting Started](./getting-started.md)**

View File

@@ -1,136 +1,148 @@
# 🚀 Getting Started
<div align="center">
**🎯 From zero to earning Microsoft Rewards points in minutes**
*Complete setup guide for beginners*
</div>
**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)
<div align="center">
### **🎬 One Command, Total Automation**
</div>
```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)
<details>
<summary><strong>📖 Prefer step-by-step? Click here</strong></summary>
<summary><strong>Click to expand</strong></summary>
### 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
```
</details>
---
## 🎯 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
**[← Back to Hub](./index.md)** | **[All Docs](./index.md)**

View File

@@ -1,32 +1,24 @@
# 🤖 Humanization (Human Mode)
# 🤖 Humanization
<div align="center">
**🎭 Natural automation that mimics human behavior**
*Subtle gestures for safer operation*
</div>
**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)**

View File

@@ -1,62 +1,79 @@
# 📚 Microsoft Rewards Script V2 Docs
# 📚 Documentation Hub
<div align="center">
**🎯 Your companion for mastering the automation stack**
*Curated guides, verified against the current codebase*
</div>
**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 |
| **[<EFBFBD> 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)**

View File

@@ -1,339 +1,112 @@
# 💾 Job State Persistence
# 💾 Job State
<div align="center">
**🔄 Resume interrupted tasks and track progress across runs**
*Never lose your progress again*
</div>
**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
**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)**

View File

@@ -1,86 +1,79 @@
# 📱 NTFY Push Notifications
<div align="center">
**🔔 Real-time push notifications to your devices**
*Stay informed wherever you are*
</div>
**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
**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)**

View File

@@ -1,611 +1,126 @@
# 🌐 Proxy Configuration
# 🌐 Proxy Setup
<div align="center">
**🔒 Route traffic through proxy servers for privacy and flexibility**
*Enhanced anonymity and geographic control*
</div>
**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.*.*.*",
"<local>"
]
}
}
}
```
---
## 🎛️ 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
**[← Back to Hub](./index.md)** | **[Config Guide](./config.md)**

View File

@@ -1,232 +1,37 @@
# ⏰ Scheduler & Automation
# ⏰ Scheduler
<div align="center">
**🚀 Built-in scheduler for automated daily execution**
*Set it and forget it*
</div>
**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<input.length;i++){const ch=input[i],next=input[i+1];if(inLine){if(ch==='\n'||ch==='\r'){inLine=false;out+=ch;}continue;}if(inBlock){if(ch==='*'&&next==='/' ){inBlock=false;i++;}continue;}if(inString){out+=ch;if(ch==='\\'){i++;if(i<input.length)out+=input[i];continue;}if(ch===stringChar)inString=false;continue;}if(ch==='"'||ch==='\''){inString=true;stringChar=ch;out+=ch;continue;}if(ch==='/'&&next==='/' ){inLine=true;i++;continue;}if(ch==='/'&&next==='*' ){inBlock=true;i++;continue;}out+=ch;}return out;};console.log(JSON.parse(strip(fs.readFileSync('src/config.jsonc','utf8'))));"
# Check running processes
Get-Process | Where-Object {$_.ProcessName -eq "node"}
```
---
## ⚡ Performance & Best Practices
### **Optimal Timing**
- **🌅 Morning (7-10 AM)** — Catch daily resets
- **🌆 Evening (7-10 PM)** — Complete remaining tasks
- **❌ Avoid peak hours** — Reduce detection during high traffic
### **Pass Recommendations**
- **1 pass** — Safest, good for most users
- **2-3 passes** — Balance of points vs. risk
- **4+ passes** — Higher risk, development only
### **Monitoring**
- ✅ Check logs regularly for errors
- ✅ Monitor point collection trends
- ✅ Verify scheduler status weekly
---
## 🔗 Alternative Solutions
### **Windows Task Scheduler**
```powershell
# Create scheduled task
schtasks /create /tn "MS-Rewards" /tr "npm start" /sc daily /st 09:00 /sd 01/01/2025
```
### **PowerShell Scheduled Job**
```powershell
# Register scheduled job
Register-ScheduledJob -Name "MSRewards" -ScriptBlock {cd "C:\path\to\project"; npm start} -Trigger (New-JobTrigger -Daily -At 9am)
```
---
## 🔗 Related Guides
- **[Getting Started](./getting-started.md)** — Initial setup and configuration
- **[Humanization](./humanization.md)** — Natural behavior patterns
- **[Docker](./docker.md)** — Container deployment
- **[Job State](./jobstate.md)** — Execution state management
## Usage Examples
### Basic Daily Run
```json
{
"schedule": {
"enabled": true,
"time": "08:00",
"timeZone": "America/New_York"
}
}
```
### Multiple Daily Passes
```json
{
"schedule": {
"enabled": true,
"time": "10:00",
"timeZone": "Europe/London",
"runImmediatelyOnStart": false
},
"passesPerRun": 3
}
```
### Development Testing
```json
{
"schedule": {
"enabled": true,
"time": "00:01",
"timeZone": "UTC",
"runImmediatelyOnStart": true
}
}
```
## Supported Timezones
Common IANA timezone identifiers:
### 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)
### UTC Variants
- `UTC` (Coordinated Universal Time)
- `GMT` (Greenwich Mean Time)
## Running the Scheduler
### Development Mode
```bash
npm run ts-schedule
```
### Production Mode
```bash
npm run build
npm run start:schedule
```
### Optional Randomization and Watchdog
You can introduce slight randomness to the start times and protect against stuck runs:
- `SCHEDULER_INITIAL_JITTER_MINUTES_MIN` / `SCHEDULER_INITIAL_JITTER_MINUTES_MAX`
- Adds a onetime random delay before the very first run after the scheduler starts.
- Example: `SCHEDULER_INITIAL_JITTER_MINUTES_MIN=5` and `SCHEDULER_INITIAL_JITTER_MINUTES_MAX=20` delays the first run by 520 minutes.
- `SCHEDULER_DAILY_JITTER_MINUTES_MIN` / `SCHEDULER_DAILY_JITTER_MINUTES_MAX`
- Adds an extra random delay to each daily scheduled execution.
- Example: 210 minutes of daily jitter to avoid exact same second each day.
- `SCHEDULER_PASS_TIMEOUT_MINUTES`
- Kills a stuck pass after N minutes (default 180). Useful if the underlying browser gets stuck.
- `SCHEDULER_FORK_PER_PASS`
- Defaults to `true`. When `true`, each pass runs in a child Node process so a stuck pass can be terminated without killing the scheduler. Set to `false` to run passes inprocess (not recommended).
### Background Execution
```bash
# Linux/macOS (background process)
nohup npm run start:schedule > 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<input.length;i++){const ch=input[i],next=input[i+1];if(inLine){if(ch==='\n'||ch==='\r'){inLine=false;out+=ch;}continue;}if(inBlock){if(ch==='*'&&next==='/' ){inBlock=false;i++;}continue;}if(inString){out+=ch;if(ch==='\\'){i++;if(i<input.length)out+=input[i];continue;}if(ch===stringChar)inString=false;continue;}if(ch==='"'||ch==='\''){inString=true;stringChar=ch;out+=ch;continue;}if(ch==='/'&&next==='/' ){inLine=true;i++;continue;}if(ch==='/'&&next==='*' ){inBlock=true;i++;continue;}out+=ch;}return out;};console.log(JSON.parse(strip(fs.readFileSync('src/config.jsonc','utf8'))));"
# Check process status
ps aux | grep "start:schedule"
```
## Alternative Solutions
### External Cron (Linux/macOS)
```bash
# Crontab entry for 9 AM daily
0 9 * * * cd /path/to/MSN-V2 && npm start
# Multiple times per day
0 9,15,21 * * * cd /path/to/MSN-V2 && npm start
**Validate config:**
```powershell
npm run typecheck
```
### Windows Task Scheduler
- Create scheduled task via Task Scheduler
- Set trigger for daily execution
- Configure action to run `npm start` in project directory
---
### Docker Cron
```dockerfile
# Add to Dockerfile
RUN apt-get update && apt-get install -y cron
COPY crontab /etc/cron.d/rewards-cron
RUN crontab /etc/cron.d/rewards-cron
```
### Docker + Built-in Scheduler
Au lieu d'utiliser cron, vous pouvez lancer le scheduler intégré dans le conteneur (un seul process longvivant) :
## 🐳 Docker Integration
### Built-in Scheduler (Recommended)
```yaml
services:
microsoft-rewards-script:
rewards:
build: .
command: ["npm", "run", "start:schedule"]
environment:
TZ: Europe/Paris
command: ["npm", "run", "start:schedule"]
```
Dans ce mode :
- `passesPerRun` fonctionne (exécutera plusieurs passes à chaque horaire interne défini par `src/config.jsonc`).
- Vous n'avez plus besoin de `CRON_SCHEDULE` ni de `run_daily.sh`.
Uses config from `src/config.jsonc`.
### Docker + External Cron (par défaut du projet)
Si vous préférez la planification par cron système dans le conteneur (valeur par défaut du projet) :
- Utilisez `CRON_SCHEDULE` (ex.: `0 7,16,20 * * *`).
- `run_daily.sh` introduit un délai aléatoire (par défaut 550 min) et un lockfile pour éviter les chevauchements.
- `RUN_ON_START=true` déclenche une exécution immédiate au démarrage du conteneur (sans délai aléatoire).
---
## Performance Considerations
## 📚 Next Steps
### System Resources
- Minimal CPU usage between runs
- Low memory footprint when idle
- No network activity during waiting periods
**Want natural behavior?**
**[Humanization Guide](./humanization.md)**
### Startup Time
- Fast initialization (< 1 second)
- Quick timezone calculations
- Immediate scheduling of next run
**Need notifications?**
**[Discord Webhooks](./conclusionwebhook.md)**
### Reliability
- Robust error handling
- Automatic recovery from failures
- Consistent execution timing
**Docker setup?**
**[Docker Guide](./docker.md)**
---
**[← Back to Hub](./index.md)** | **[Getting Started](./getting-started.md)**

View File

@@ -1,296 +1,201 @@
# 🔒 Security & Privacy Guide
# 🔒 Security Guide
<div align="center">
**🛡️ Comprehensive security measures and incident response**
*Protect your accounts and maintain privacy*
</div>
**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)**

View File

@@ -1,371 +1,48 @@
# 🔄 Auto-Update System
# 🔄 Auto-Update
<div align="center">
**🚀 Automatic updates to keep your installation current**
*Set it and forget it*
</div>
**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
| 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)**

6
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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<QuizData> {
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<boolean> {
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<boolean> {
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()

View File

@@ -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<number> {
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<Page> {
try {
await this.bot.utils.wait(1000)

View File

@@ -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 didnt 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., 24 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"
}
}

67
src/constants.ts Normal file
View File

@@ -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

View File

@@ -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<string>(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<string,string> = {
'recovery-email-mismatch':'#recovery-email-mismatch',
'we-cant-sign-you-in':'#we-cant-sign-you-in-blocked'

View File

@@ -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')
}

View File

@@ -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')

View File

@@ -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')

View File

@@ -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()
}

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)
})
}

View File

@@ -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) => {

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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, {