feature: Update the version to 2.60.0 and improve the handling of human gestures, etc.

This commit is contained in:
2025-11-15 12:35:09 +01:00
parent 41d06ee001
commit cf0611d841
13 changed files with 165 additions and 96 deletions

View File

@@ -105,6 +105,8 @@ export class HumanBehavior {
*/
async microGestures(context: string): Promise<void> {
try {
const gestureNotes: string[] = []
// 60% chance of mouse movement (humans move mouse A LOT)
if (Math.random() < 0.6) {
const x = Math.floor(Math.random() * 200) + 50 // Random x: 50-250px
@@ -115,8 +117,7 @@ export class HumanBehavior {
// Mouse move failed - page may be closed or unavailable
})
// VERBOSE logging disabled - too noisy
// log(false, 'CREATOR', `[${context}] 🖱️ Mouse moved to (${x}, ${y})`, 'log', 'gray')
gestureNotes.push(`mouse→(${x},${y})`)
}
// 30% chance of scroll (humans scroll to read content)
@@ -129,8 +130,11 @@ export class HumanBehavior {
// Scroll failed - page may be closed or unavailable
})
// VERBOSE logging disabled - too noisy
// log(false, 'CREATOR', `[${context}] 📜 Scrolled ${direction > 0 ? 'down' : 'up'} ${distance}px`, 'log', 'gray')
gestureNotes.push(`scroll ${direction > 0 ? '↓' : '↑'} ${distance}px`)
}
if (gestureNotes.length > 0) {
log(false, 'CREATOR', `[${context}] micro gestures: ${gestureNotes.join(', ')}`, 'log', 'gray')
}
} catch {
// Gesture execution failed - not critical for operation

View File

@@ -170,7 +170,7 @@ export default class BrowserFunc {
})
this.bot.log(this.bot.isMobile, 'GO-HOME-DEBUG',
`DOM Diagnostic - ` +
'DOM Diagnostic - ' +
`URL: ${diagnosticInfo.url}, ` +
`Title: "${diagnosticInfo.pageTitle}", ` +
`Elements with 'activit': ${diagnosticInfo.activitiesIdCount} [${diagnosticInfo.activitiesIds.join(', ')}], ` +

View File

@@ -19,10 +19,10 @@ export interface AccountResult {
email: string
pointsEarned: number
runDuration: number
initialPoints: number // Points avant l'exécution
finalPoints: number // Points après l'exécution
desktopPoints: number // Points gagnés sur Desktop
mobilePoints: number // Points gagnés sur Mobile
initialPoints: number // Points before execution
finalPoints: number // Points after execution
desktopPoints: number // Points earned on desktop
mobilePoints: number // Points earned on mobile
errors?: string[]
banned?: boolean
}
@@ -38,11 +38,13 @@ export interface SummaryData {
export class SummaryReporter {
private config: Config
private jobState: JobState
private jobState?: JobState
constructor(config: Config) {
this.config = config
this.jobState = new JobState(config)
if (config.jobState?.enabled !== false) {
this.jobState = new JobState(config)
}
}
/**
@@ -85,7 +87,7 @@ export class SummaryReporter {
description += ` | **🚫 Banned:** ${bannedCount}`
}
description += `\n\n**📊 Account Details**\n`
description += '\n\n**📊 Account Details**\n'
const accountsWithErrors: AccountResult[] = []
@@ -100,13 +102,13 @@ export class SummaryReporter {
description += `• Duration: ${durationSec}s\n`
// Collect accounts with errors for separate webhook
if ((account.errors?.length || account.banned) && account.email) {
if (this.hasAccountFailure(account)) {
accountsWithErrors.push(account)
}
}
// Footer summary
description += `\n**🌐 Total Balance**\n`
description += '\n**🌐 Total Balance**\n'
description += `${totalInitial} → **${totalFinal}** pts (+${summary.totalPoints})`
const color = bannedCount > 0 ? 0xFF0000 : summary.failureCount > 0 ? 0xFFAA00 : 0x00FF00
@@ -145,7 +147,7 @@ export class SummaryReporter {
// Error details
if (account.banned) {
errorDescription += `• Status: Account Banned/Suspended\n`
errorDescription += '• Status: Account Banned/Suspended\n'
if (account.errors?.length && account.errors[0]) {
errorDescription += `• Reason: ${account.errors[0]}\n`
}
@@ -153,14 +155,14 @@ export class SummaryReporter {
errorDescription += `• Error: ${account.errors[0]}\n`
}
errorDescription += `\n`
errorDescription += '\n'
}
errorDescription += `**📋 Recommended Actions:**\n`
errorDescription += `• Check account status manually\n`
errorDescription += `• Review error messages above\n`
errorDescription += `• Verify credentials if login failed\n`
errorDescription += `• Consider proxy rotation if rate-limited`
errorDescription += '**📋 Recommended Actions:**\n'
errorDescription += '• Check account status manually\n'
errorDescription += '• Review error messages above\n'
errorDescription += '• Verify credentials if login failed\n'
errorDescription += '• Consider proxy rotation if rate-limited'
await ConclusionWebhook(
this.config,
@@ -195,6 +197,10 @@ export class SummaryReporter {
* Update job state with completion status
*/
async updateJobState(summary: SummaryData): Promise<void> {
if (!this.jobState) {
return
}
try {
const day = summary.endTime.toISOString().split('T')?.[0]
if (!day) return
@@ -205,7 +211,7 @@ export class SummaryReporter {
day,
{
totalCollected: account.pointsEarned,
banned: false,
banned: account.banned ?? false,
errors: account.errors?.length ?? 0
}
)
@@ -237,13 +243,15 @@ export class SummaryReporter {
log('main', 'SUMMARY', '─'.repeat(80))
for (const account of summary.accounts) {
const status = account.errors?.length ? '❌ FAILED' : '✅ SUCCESS'
const status = this.hasAccountFailure(account) ? (account.banned ? '🚫 BANNED' : '❌ FAILED') : '✅ SUCCESS'
const duration = Math.round(account.runDuration / 1000)
log('main', 'SUMMARY', `${status} | ${account.email}`)
log('main', 'SUMMARY', ` Points: ${account.pointsEarned} | Duration: ${duration}s`)
if (account.errors?.length) {
if (account.banned) {
log('main', 'SUMMARY', ' Status: Account flagged as banned/suspended', 'error')
} else if (account.errors?.length) {
log('main', 'SUMMARY', ` Error: ${account.errors[0]}`, 'error')
}
}
@@ -267,8 +275,8 @@ export class SummaryReporter {
endTime: Date
): SummaryData {
const totalPoints = accounts.reduce((sum, acc) => sum + acc.pointsEarned, 0)
const successCount = accounts.filter(acc => !acc.errors?.length).length
const failureCount = accounts.length - successCount
const failureCount = accounts.filter(acc => this.hasAccountFailure(acc)).length
const successCount = accounts.length - failureCount
return {
accounts,
@@ -279,4 +287,8 @@ export class SummaryReporter {
failureCount
}
}
private hasAccountFailure(account: AccountResult): boolean {
return Boolean(account.errors?.length) || account.banned === true
}
}

View File

@@ -210,7 +210,7 @@ export class MicrosoftRewardsBot {
await this.runWorker()
} else {
// Neither primary nor worker - something's wrong with clustering
log('main', 'MAIN', `ERROR: Cluster mode failed - neither primary nor worker! Falling back to single-process mode.`, 'error')
log('main', 'MAIN', 'ERROR: Cluster mode failed - neither primary nor worker! Falling back to single-process mode.', 'error')
const passes = this.config.passesPerRun ?? 1
for (let pass = 1; pass <= passes; pass++) {
if (passes > 1) {
@@ -382,11 +382,21 @@ export class MicrosoftRewardsBot {
// Wait for chunk (either already received during init, or will arrive soon)
const chunk = await new Promise<Account[]>((resolve) => {
if ((global as any).__workerChunk) {
resolve((global as any).__workerChunk)
} else {
(process as unknown as { on: (ev: 'message', cb: (m: { chunk: Account[] }) => void) => void }).on('message', ({ chunk: c }: { chunk: Account[] }) => resolve(c))
if (global.__workerChunk) {
const bufferedChunk = global.__workerChunk
global.__workerChunk = undefined
resolve(bufferedChunk)
return
}
const handleMessage = (message: unknown): void => {
if (isWorkerChunkMessage(message)) {
process.off('message', handleMessage)
resolve(message.chunk)
}
}
process.on('message', handleMessage)
})
if (!chunk || chunk.length === 0) {
@@ -896,6 +906,20 @@ function isWorkerMessage(msg: unknown): msg is WorkerMessage {
return m.type === 'summary' && Array.isArray(m.data)
}
interface WorkerChunkMessage {
chunk: Account[]
}
function isWorkerChunkMessage(message: unknown): message is WorkerChunkMessage {
if (!message || typeof message !== 'object') return false
return Array.isArray((message as WorkerChunkMessage).chunk)
}
declare global {
// eslint-disable-next-line no-var
var __workerChunk: Account[] | undefined
}
// Use utility functions from Utils.ts
const shortErr = shortErrorMessage
const formatFullError = formatDetailedError
@@ -905,9 +929,14 @@ async function main(): Promise<void> {
// Workers initialize for ~2 seconds before reaching runWorker(), so messages
// sent by primary during initialization would be lost without this early listener
if (!cluster.isPrimary && cluster.worker) {
(process as unknown as { on: (ev: 'message', cb: (m: { chunk: Account[] }) => void) => void }).on('message', ({ chunk }: { chunk: Account[] }) => {
(global as any).__workerChunk = chunk
})
const bufferChunk = (message: unknown): void => {
if (isWorkerChunkMessage(message)) {
global.__workerChunk = message.chunk
process.off('message', bufferChunk)
}
}
process.on('message', bufferChunk)
}
// Check for dashboard mode flag (standalone dashboard)

View File

@@ -25,9 +25,16 @@ export function obfuscateWebhookUrl(url: string): string {
return Buffer.from(url).toString('base64')
}
const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
export function deobfuscateWebhookUrl(encoded: string): string {
const trimmed = encoded.trim()
if (!trimmed || !BASE64_REGEX.test(trimmed)) {
return ''
}
try {
return Buffer.from(encoded, 'base64').toString('utf-8')
return Buffer.from(trimmed, 'base64').toString('utf-8')
} catch {
return ''
}