Refactor: Simplify recovery email handling and validation; update documentation for clarity

This commit is contained in:
2025-11-08 21:26:06 +01:00
parent 5e322af2c0
commit 773304fc85
7 changed files with 28 additions and 67 deletions

View File

@@ -21,7 +21,7 @@
} }
``` ```
> `recoveryEmail` is still **recommended**. It lets the bot verify Microsofts masked hint during login and alert you if the recovery address ever changes. You can opt out per account by setting `"recoveryRequired": false`. > `recoveryEmail` is **optional but recommended**. It lets the bot verify Microsoft's masked hint during login and alert you if the recovery address ever changes. Simply leave it empty (`""`) if not needed.
**That's it!** Run `npm start` to test. **That's it!** Run `npm start` to test.
@@ -60,9 +60,9 @@
--- ---
## 🚫 Skip the Recovery Email Check (Advanced) ## 🚫 Skip the Recovery Email (Advanced)
Most users should keep recovery validation enabled. If an account genuinely has no recovery address or you cannot disclose it, add `"recoveryRequired": false` to that entry. When disabled, the bot skips mismatch detection and logs a warning during startup validation. If an account genuinely has no recovery address or you prefer not to provide it, simply leave the `recoveryEmail` field empty:
```json ```json
{ {
@@ -70,13 +70,13 @@ Most users should keep recovery validation enabled. If an account genuinely has
{ {
"email": "example@outlook.com", "email": "example@outlook.com",
"password": "strong_password", "password": "strong_password",
"recoveryRequired": false "recoveryEmail": ""
} }
] ]
} }
``` ```
> Without a recovery email the bot cannot detect if Microsoft shows a different masked address. Enable this override only when you accept that risk. > The bot will automatically skip recovery validation when this field is empty. A warning will be logged during startup, but the bot will function normally.
--- ---

View File

@@ -7,9 +7,8 @@
"enabled": true, "enabled": true,
"email": "", "email": "",
"password": "", "password": "",
"totp": "", "totp": "", // Optional: leave empty if no 2FA, or put your TOTP secret
"recoveryRequired": true, "recoveryEmail": "", // Optional: recovery email for security challenges
"recoveryEmail": "",
"proxy": { "proxy": {
"proxyAxios": false, "proxyAxios": false,
"url": "", "url": "",
@@ -24,7 +23,6 @@
"email": "", "email": "",
"password": "", "password": "",
"totp": "", "totp": "",
"recoveryRequired": true,
"recoveryEmail": "", "recoveryEmail": "",
"proxy": { "proxy": {
"proxyAxios": false, "proxyAxios": false,
@@ -40,7 +38,6 @@
"email": "", "email": "",
"password": "", "password": "",
"totp": "", "totp": "",
"recoveryRequired": true,
"recoveryEmail": "", "recoveryEmail": "",
"proxy": { "proxy": {
"proxyAxios": false, "proxyAxios": false,
@@ -56,7 +53,6 @@
"email": "", "email": "",
"password": "", "password": "",
"totp": "", "totp": "",
"recoveryRequired": true,
"recoveryEmail": "", "recoveryEmail": "",
"proxy": { "proxy": {
"proxyAxios": false, "proxyAxios": false,
@@ -72,7 +68,6 @@
"email": "", "email": "",
"password": "", "password": "",
"totp": "", "totp": "",
"recoveryRequired": true,
"recoveryEmail": "", "recoveryEmail": "",
"proxy": { "proxy": {
"proxyAxios": false, "proxyAxios": false,

View File

@@ -162,11 +162,7 @@ class Browser {
} }
async generateFingerprint() { async generateFingerprint() {
const fingerPrintData = new FingerprintGenerator().getFingerprint({ const fingerPrintData = new FingerprintGenerator().getFingerprint()
devices: this.bot.isMobile ? ['mobile'] : ['desktop'],
operatingSystems: this.bot.isMobile ? ['android'] : ['windows'],
browsers: [{ name: 'edge' }]
})
const updatedFingerPrintData = await updateFingerprintUserAgent(fingerPrintData, this.bot.isMobile) const updatedFingerPrintData = await updateFingerprintUserAgent(fingerPrintData, this.bot.isMobile)

View File

@@ -145,7 +145,7 @@
// Updates // Updates
"update": { "update": {
"enabled": true, // Enable automatic updates (default: true) "enabled": true, // Enable automatic updates (default: true) - DISABLED to preserve custom modifications
"method": "github-api", // Update method: "git" or "github-api" (recommended: "github-api") "method": "github-api", // Update method: "git" or "github-api" (recommended: "github-api")
// "git" = Uses Git commands (requires Git installed, can have conflicts) // "git" = Uses Git commands (requires Git installed, can have conflicts)
// "github-api" = Downloads ZIP from GitHub (no Git needed, no conflicts, recommended) // "github-api" = Downloads ZIP from GitHub (no Git needed, no conflicts, recommended)

View File

@@ -5,10 +5,8 @@ export interface Account {
password: string; password: string;
/** Optional TOTP secret in Base32 (e.g., from Microsoft Authenticator setup) */ /** Optional TOTP secret in Base32 (e.g., from Microsoft Authenticator setup) */
totp?: string; totp?: string;
/** Recovery email used during security challenge verification */ /** Recovery email used during security challenge verification. Leave empty if not needed. */
recoveryEmail?: string; recoveryEmail?: string;
/** Override to allow skipping recovery email checks for this account */
recoveryRequired?: boolean;
proxy: AccountProxy; proxy: AccountProxy;
} }

View File

@@ -379,27 +379,20 @@ export function loadAccounts(): Account[] {
} }
a.email = String(a.email).trim() a.email = String(a.email).trim()
a.password = String(a.password) a.password = String(a.password)
const recoveryRequired = a.recoveryRequired !== false
a.recoveryRequired = recoveryRequired
if (recoveryRequired) { // Simplified recovery email logic: if present and non-empty, validate it
if (typeof a.recoveryEmail !== 'string') { if (typeof a.recoveryEmail === 'string') {
throw new Error(`account ${a.email || '<unknown>'} must include a recoveryEmail string (or set "recoveryRequired": false)`) const trimmed = a.recoveryEmail.trim()
} if (trimmed !== '') {
a.recoveryEmail = String(a.recoveryEmail).trim()
if (!a.recoveryEmail || !/@/.test(a.recoveryEmail)) {
throw new Error(`account ${a.email} recoveryEmail must be a valid email address (got: "${a.recoveryEmail}") - set "recoveryRequired": false if not needed`)
}
} else {
if (typeof a.recoveryEmail === 'string' && a.recoveryEmail.trim() !== '') {
const trimmed = a.recoveryEmail.trim()
if (!/@/.test(trimmed)) { if (!/@/.test(trimmed)) {
throw new Error(`account ${a.email} recoveryEmail must be a valid email address`) throw new Error(`account ${a.email} recoveryEmail must be a valid email address (got: "${trimmed}")`)
} }
a.recoveryEmail = trimmed a.recoveryEmail = trimmed
} else { } else {
a.recoveryEmail = undefined a.recoveryEmail = undefined
} }
} else {
a.recoveryEmail = undefined
} }
if (!a.proxy || typeof a.proxy !== 'object') { if (!a.proxy || typeof a.proxy !== 'object') {

View File

@@ -1,8 +1,8 @@
import chalk from 'chalk'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import chalk from 'chalk'
import { Config } from '../interface/Config'
import { Account } from '../interface/Account' import { Account } from '../interface/Account'
import { Config } from '../interface/Config'
import { log } from './Logger' import { log } from './Logger'
interface ValidationError { interface ValidationError {
@@ -105,43 +105,22 @@ export class StartupValidator {
) )
} }
const recoveryRequired = account.recoveryRequired !== false // Simplified: only validate recovery email if provided
if (recoveryRequired) { if (account.recoveryEmail && typeof account.recoveryEmail === 'string' && account.recoveryEmail.trim() !== '') {
if (!account.recoveryEmail || typeof account.recoveryEmail !== 'string') { if (!/@/.test(account.recoveryEmail)) {
this.addError(
'accounts',
`${prefix}: Missing required field "recoveryEmail"`,
'Add your recovery/backup email address. This is required for security checks unless you explicitly disable it.\nExample: "recoveryEmail": "backup@gmail.com"',
'docs/accounts.md'
)
} else if (!/@/.test(account.recoveryEmail)) {
this.addError( this.addError(
'accounts', 'accounts',
`${prefix}: Recovery email format is invalid`, `${prefix}: Recovery email format is invalid`,
'Recovery email must be a valid email address (e.g., backup@gmail.com)' 'Recovery email must be a valid email address (e.g., backup@gmail.com)'
) )
} else if (account.recoveryEmail.trim() === '') {
this.addError(
'accounts',
`${prefix}: Recovery email cannot be empty`,
'Provide the actual recovery email associated with this Microsoft account'
)
} }
} else { } else {
if (!account.recoveryEmail || account.recoveryEmail.trim() === '') { this.addWarning(
this.addWarning( 'accounts',
'accounts', `${prefix}: No recovery email configured`,
`${prefix}: Recovery email checks disabled`, 'Recovery email is optional but recommended for security challenge verification',
'The bot will skip recovery-email mismatch detection for this account. Re-enable by removing "recoveryRequired": false.', 'docs/accounts.md'
'docs/accounts.md' )
)
} else if (!/@/.test(account.recoveryEmail)) {
this.addError(
'accounts',
`${prefix}: Recovery email format is invalid`,
'Recovery email must be a valid email address (e.g., backup@gmail.com)'
)
}
} }
// Optional but recommended: TOTP // Optional but recommended: TOTP