mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-23 08:21:04 +00:00
V2.4.0 (#381)
* 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 * feat: refactor ConclusionWebhook to improve structure and enhance message formatting * feat: update setup scripts and version to 2.3.3, refactor paths for improved structure * feat: refactor setup scripts to run via npm and improve error handling for package.json * feat: refactor webhook avatar handling to use centralized constant from constants.ts * feat: mettre à jour la version à 2.3.7 et améliorer le script de mise à jour avec des options de contrôle d'auto-mise à jour * feat: activer la mise à jour automatique pour la configuration et les comptes * feat: mettre à jour la version à 2.3.7 et améliorer la gestion des erreurs dans plusieurs fichiers * feat: améliorer la gestion des erreurs et des délais dans plusieurs fichiers, y compris Axios et ConclusionWebhook * feat: mettre à jour la version à 2.4.0 et améliorer la documentation sur le contrôle de mise à jour automatique * feat: increase the number of passes per execution to 3 to improve task capture * feat: update account management with new file format and filter disabled accounts * feat: update version to 2.4.0, add reinstallation warning and support .jsonc extensions for configuration files * fix: fix formatting of reinstallation message in README * feat: add an important update notice in the README to recommend a complete reinstallation * fix: remove backup instructions from installation guide in README * fix: update notice in README for configuration file changes and fresh installation instructions * fix: fix typographical error in README update notice * Fix: Update avatar URL in Discord config and remove optional webhook properties * exploit: add customization options for webhooks and improve notification format
This commit is contained in:
@@ -13,6 +13,12 @@ type DateTimeInstance = ReturnType<typeof DateTime.fromJSDate>
|
||||
|
||||
function resolveTimeParts(schedule: Config['schedule'] | undefined): { tz: string; hour: number; minute: number } {
|
||||
const tz = (schedule?.timeZone && IANAZone.isValidZone(schedule.timeZone)) ? schedule.timeZone : 'UTC'
|
||||
|
||||
// Warn if an invalid timezone was provided
|
||||
if (schedule?.timeZone && !IANAZone.isValidZone(schedule.timeZone)) {
|
||||
void log('main', 'SCHEDULER', `Invalid timezone "${schedule.timeZone}" provided. Falling back to UTC. Valid zones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones`, 'warn')
|
||||
}
|
||||
|
||||
// Determine source string
|
||||
let src = ''
|
||||
if (typeof schedule?.useAmPm === 'boolean') {
|
||||
@@ -114,13 +120,28 @@ async function runOnePassWithWatchdog(): Promise<void> {
|
||||
// Heartbeat-aware watchdog configuration
|
||||
// If a child is actively updating its heartbeat file, we allow it to run beyond the legacy timeout.
|
||||
// Defaults are generous to allow first-day passes to finish searches with delays.
|
||||
const staleHeartbeatMin = Number(
|
||||
process.env.SCHEDULER_STALE_HEARTBEAT_MINUTES || process.env.SCHEDULER_PASS_TIMEOUT_MINUTES || 30
|
||||
const parseEnvNumber = (key: string, fallback: number, min: number, max: number): number => {
|
||||
const val = Number(process.env[key] || fallback)
|
||||
if (isNaN(val) || val < min || val > max) {
|
||||
void log('main', 'SCHEDULER', `Invalid ${key}="${process.env[key]}". Using default ${fallback}`, 'warn')
|
||||
return fallback
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
const staleHeartbeatMin = parseEnvNumber(
|
||||
process.env.SCHEDULER_STALE_HEARTBEAT_MINUTES ? 'SCHEDULER_STALE_HEARTBEAT_MINUTES' : 'SCHEDULER_PASS_TIMEOUT_MINUTES',
|
||||
30, 5, 1440
|
||||
)
|
||||
const graceMin = Number(process.env.SCHEDULER_HEARTBEAT_GRACE_MINUTES || 15)
|
||||
const hardcapMin = Number(process.env.SCHEDULER_PASS_HARDCAP_MINUTES || 480) // 8 hours
|
||||
const graceMin = parseEnvNumber('SCHEDULER_HEARTBEAT_GRACE_MINUTES', 15, 1, 120)
|
||||
const hardcapMin = parseEnvNumber('SCHEDULER_PASS_HARDCAP_MINUTES', 480, 30, 1440)
|
||||
const checkEveryMs = 60_000 // check once per minute
|
||||
|
||||
// Validate: stale should be >= grace
|
||||
if (staleHeartbeatMin < graceMin) {
|
||||
await log('main', 'SCHEDULER', `Warning: STALE_HEARTBEAT (${staleHeartbeatMin}m) < GRACE (${graceMin}m). Adjusting stale to ${graceMin}m`, 'warn')
|
||||
}
|
||||
|
||||
// Fork per pass: safer because we can terminate a stuck child without killing the scheduler
|
||||
const forkPerPass = String(process.env.SCHEDULER_FORK_PER_PASS || 'true').toLowerCase() !== 'false'
|
||||
|
||||
@@ -147,6 +168,8 @@ async function runOnePassWithWatchdog(): Promise<void> {
|
||||
let finished = false
|
||||
const startedAt = Date.now()
|
||||
|
||||
let killTimeout: NodeJS.Timeout | undefined
|
||||
|
||||
const killChild = async (signal: NodeJS.Signals) => {
|
||||
try {
|
||||
await log('main', 'SCHEDULER', `Sending ${signal} to stuck child PID ${child.pid}`,'warn')
|
||||
@@ -162,7 +185,8 @@ async function runOnePassWithWatchdog(): Promise<void> {
|
||||
if (runtimeMin >= hardcapMin) {
|
||||
log('main', 'SCHEDULER', `Pass exceeded hard cap of ${hardcapMin} minutes; terminating...`, 'warn')
|
||||
void killChild('SIGTERM')
|
||||
setTimeout(() => { try { child.kill('SIGKILL') } catch { /* ignore */ } }, 10_000)
|
||||
if (killTimeout) clearTimeout(killTimeout)
|
||||
killTimeout = setTimeout(() => { try { child.kill('SIGKILL') } catch { /* ignore */ } }, 10_000)
|
||||
return
|
||||
}
|
||||
// Before grace, don't judge
|
||||
@@ -175,19 +199,23 @@ async function runOnePassWithWatchdog(): Promise<void> {
|
||||
if (ageMin >= staleHeartbeatMin) {
|
||||
log('main', 'SCHEDULER', `Heartbeat stale for ${ageMin}m (>=${staleHeartbeatMin}m). Terminating child...`, 'warn')
|
||||
void killChild('SIGTERM')
|
||||
setTimeout(() => { try { child.kill('SIGKILL') } catch { /* ignore */ } }, 10_000)
|
||||
if (killTimeout) clearTimeout(killTimeout)
|
||||
killTimeout = setTimeout(() => { try { child.kill('SIGKILL') } catch { /* ignore */ } }, 10_000)
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
// If file missing after grace, consider stale
|
||||
log('main', 'SCHEDULER', 'Heartbeat file missing after grace. Terminating child...', 'warn')
|
||||
const msg = err instanceof Error ? err.message : String(err)
|
||||
log('main', 'SCHEDULER', `Heartbeat file check failed: ${msg}. Terminating child...`, 'warn')
|
||||
void killChild('SIGTERM')
|
||||
setTimeout(() => { try { child.kill('SIGKILL') } catch { /* ignore */ } }, 10_000)
|
||||
if (killTimeout) clearTimeout(killTimeout)
|
||||
killTimeout = setTimeout(() => { try { child.kill('SIGKILL') } catch { /* ignore */ } }, 10_000)
|
||||
}
|
||||
}, checkEveryMs)
|
||||
|
||||
child.on('exit', async (code, signal) => {
|
||||
finished = true
|
||||
clearInterval(timer)
|
||||
if (killTimeout) clearTimeout(killTimeout)
|
||||
// Cleanup heartbeat file
|
||||
try { if (fs.existsSync(hbFile)) fs.unlinkSync(hbFile) } catch { /* ignore */ }
|
||||
if (signal) {
|
||||
@@ -203,6 +231,7 @@ async function runOnePassWithWatchdog(): Promise<void> {
|
||||
child.on('error', async (err) => {
|
||||
finished = true
|
||||
clearInterval(timer)
|
||||
if (killTimeout) clearTimeout(killTimeout)
|
||||
try { if (fs.existsSync(hbFile)) fs.unlinkSync(hbFile) } catch { /* ignore */ }
|
||||
await log('main', 'SCHEDULER', `Failed to spawn child: ${err instanceof Error ? err.message : String(err)}`, 'error')
|
||||
resolve()
|
||||
@@ -286,9 +315,21 @@ async function main() {
|
||||
let running = false
|
||||
|
||||
// Optional initial jitter before the first run (to vary start time)
|
||||
const initJitterMin = Number(process.env.SCHEDULER_INITIAL_JITTER_MINUTES_MIN || process.env.SCHEDULER_INITIAL_JITTER_MIN || 0)
|
||||
const initJitterMax = Number(process.env.SCHEDULER_INITIAL_JITTER_MINUTES_MAX || process.env.SCHEDULER_INITIAL_JITTER_MAX || 0)
|
||||
const initialJitterBounds: [number, number] = [isFinite(initJitterMin) ? initJitterMin : 0, isFinite(initJitterMax) ? initJitterMax : 0]
|
||||
const parseJitter = (minKey: string, maxKey: string, fallbackMin: string, fallbackMax: string): [number, number] => {
|
||||
const minVal = Number(process.env[minKey] || process.env[fallbackMin] || 0)
|
||||
const maxVal = Number(process.env[maxKey] || process.env[fallbackMax] || 0)
|
||||
if (isNaN(minVal) || minVal < 0) {
|
||||
void log('main', 'SCHEDULER', `Invalid ${minKey}="${process.env[minKey]}". Using 0`, 'warn')
|
||||
return [0, isNaN(maxVal) || maxVal < 0 ? 0 : maxVal]
|
||||
}
|
||||
if (isNaN(maxVal) || maxVal < 0) {
|
||||
void log('main', 'SCHEDULER', `Invalid ${maxKey}="${process.env[maxKey]}". Using 0`, 'warn')
|
||||
return [minVal, 0]
|
||||
}
|
||||
return [minVal, maxVal]
|
||||
}
|
||||
|
||||
const initialJitterBounds = parseJitter('SCHEDULER_INITIAL_JITTER_MINUTES_MIN', 'SCHEDULER_INITIAL_JITTER_MINUTES_MAX', 'SCHEDULER_INITIAL_JITTER_MIN', 'SCHEDULER_INITIAL_JITTER_MAX')
|
||||
const applyInitialJitter = (initialJitterBounds[0] > 0 || initialJitterBounds[1] > 0)
|
||||
|
||||
if (runImmediate && !running) {
|
||||
@@ -327,10 +368,9 @@ async function main() {
|
||||
// Optional daily jitter to further randomize the exact start time each day
|
||||
let extraMs = 0
|
||||
if (cronExpressions.length === 0) {
|
||||
const dailyJitterMin = Number(process.env.SCHEDULER_DAILY_JITTER_MINUTES_MIN || process.env.SCHEDULER_DAILY_JITTER_MIN || 0)
|
||||
const dailyJitterMax = Number(process.env.SCHEDULER_DAILY_JITTER_MINUTES_MAX || process.env.SCHEDULER_DAILY_JITTER_MAX || 0)
|
||||
const djMin = isFinite(dailyJitterMin) ? dailyJitterMin : 0
|
||||
const djMax = isFinite(dailyJitterMax) ? dailyJitterMax : 0
|
||||
const dailyJitterBounds = parseJitter('SCHEDULER_DAILY_JITTER_MINUTES_MIN', 'SCHEDULER_DAILY_JITTER_MINUTES_MAX', 'SCHEDULER_DAILY_JITTER_MIN', 'SCHEDULER_DAILY_JITTER_MAX')
|
||||
const djMin = dailyJitterBounds[0]
|
||||
const djMax = dailyJitterBounds[1]
|
||||
if (djMin > 0 || djMax > 0) {
|
||||
const mn = Math.max(0, Math.min(djMin, djMax))
|
||||
const mx = Math.max(0, Math.max(djMin, djMax))
|
||||
@@ -373,6 +413,6 @@ async function main() {
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e)
|
||||
void log('main', 'SCHEDULER', `Fatal error: ${e instanceof Error ? e.message : String(e)}`, 'error')
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user