mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-09 17:06:15 +00:00
Merge pull request #18 from Rempacious/email-fix
Email cutoff fix due to type() and improved internal scheduling
This commit is contained in:
10
package-lock.json
generated
10
package-lock.json
generated
@@ -6,7 +6,7 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "microsoft-rewards-bot",
|
||||
"version": "2.60.1",
|
||||
"version": "3.0.0",
|
||||
"license": "CC-BY-NC-SA-4.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.8.4",
|
||||
@@ -450,7 +450,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
|
||||
"integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
@@ -738,7 +737,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -989,7 +987,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.19",
|
||||
"caniuse-lite": "^1.0.30001751",
|
||||
@@ -1562,7 +1559,6 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@@ -3217,7 +3213,6 @@
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz",
|
||||
"integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.52.0"
|
||||
},
|
||||
@@ -3977,7 +3972,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -4291,4 +4285,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class BrowserUtil {
|
||||
{ selector: '#idSIButton9', label: 'PrimaryLoginButton' },
|
||||
{ selector: '.ms-Button.ms-Button--primary', label: 'Primary Generic' },
|
||||
{ selector: '.c-glyph.glyph-cancel', label: 'Mobile Welcome Cancel' },
|
||||
{ selector: '.maybe-later, button[data-automation-id*="maybeLater" i]', label: 'Maybe Later' },
|
||||
{ selector: '.maybe-later, button[data-automation-id*="maybeLater" i], a.dashboardPopUpPopUpCloseButton', label: 'Maybe Later' },
|
||||
{ selector: '#bnp_btn_reject', label: 'Bing Cookie Reject' },
|
||||
{ selector: '#bnp_btn_accept', label: 'Bing Cookie Accept' },
|
||||
{ selector: '#bnp_close_link', label: 'Bing Cookie Close' },
|
||||
|
||||
@@ -322,7 +322,8 @@ export class Login {
|
||||
if (checkCount % 3 === 0) {
|
||||
await Promise.allSettled([
|
||||
this.passkeyHandler.handlePasskeyPrompts(page, 'oauth'),
|
||||
this.totpHandler.tryAutoTotp(page, 'mobile-oauth')
|
||||
this.totpHandler.tryAutoTotp(page, 'mobile-oauth'),
|
||||
this.bot.browser.utils.tryDismissAllMessages(page)
|
||||
])
|
||||
}
|
||||
|
||||
@@ -837,7 +838,8 @@ export class Login {
|
||||
if (checkCount % 3 === 0) {
|
||||
await Promise.allSettled([
|
||||
this.passkeyHandler.handlePasskeyPrompts(page, 'main'),
|
||||
this.totpHandler.tryAutoTotp(page, 'post-password wait')
|
||||
this.totpHandler.tryAutoTotp(page, 'post-password wait'),
|
||||
this.bot.browser.utils.tryDismissAllMessages(page)
|
||||
])
|
||||
} else {
|
||||
await this.passkeyHandler.handlePasskeyPrompts(page, 'main')
|
||||
|
||||
@@ -719,7 +719,10 @@ export class MicrosoftRewardsBot {
|
||||
return
|
||||
}
|
||||
|
||||
process.exit()
|
||||
// Don't exit here - let the caller decide (enables scheduler mode)
|
||||
// For one-time runs, the caller (bootstrap) will exit after run() returns
|
||||
// For scheduled mode, the scheduler keeps the process alive
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1214,6 +1217,10 @@ async function main(): Promise<void> {
|
||||
// One-time execution (scheduling disabled)
|
||||
await rewardsBot.initialize()
|
||||
await rewardsBot.run()
|
||||
|
||||
// Explicit exit for one-time runs (no scheduler to keep alive)
|
||||
log('main', 'MAIN', 'One-time run completed. Exiting.', 'log', 'green')
|
||||
gracefulExit(0)
|
||||
} catch (e) {
|
||||
log('main', 'MAIN-ERROR', 'Fatal during run: ' + (e instanceof Error ? e.message : e), 'error')
|
||||
gracefulExit(1)
|
||||
|
||||
@@ -98,6 +98,9 @@ export class HumanTyping {
|
||||
*
|
||||
* PATTERN: Humans type emails in 3 parts: [name] @ [domain]
|
||||
*
|
||||
* CRITICAL FIX: Previous version called type() for domain which cleared the field,
|
||||
* erasing the localPart. Now we use typeAppend() to preserve existing content.
|
||||
*
|
||||
* @param locator Playwright locator (email input)
|
||||
* @param email Email address
|
||||
* @returns Promise<void>
|
||||
@@ -111,20 +114,78 @@ export class HumanTyping {
|
||||
return
|
||||
}
|
||||
|
||||
// IMPROVEMENT: Type local part (fast)
|
||||
// IMPROVEMENT: Type local part (fast) - this clears and types
|
||||
await this.type(locator, localPart, 1.3)
|
||||
|
||||
// IMPROVEMENT: Slight pause before @ (humans verify username)
|
||||
await this.delay(50, 200)
|
||||
|
||||
// Type @ symbol (slightly slower - special key)
|
||||
// CRITICAL: Use pressSequentially, NOT type() which would clear the field
|
||||
await locator.pressSequentially('@', { delay: 100 }).catch(() => { })
|
||||
|
||||
// IMPROVEMENT: Slight pause after @ (humans verify domain)
|
||||
await this.delay(50, 150)
|
||||
|
||||
// Type domain (fast)
|
||||
await this.type(locator, domain, 1.4)
|
||||
// CRITICAL FIX: Use typeAppend() to NOT clear the field (preserve localPart + @)
|
||||
await this.typeAppend(locator, domain, 1.4)
|
||||
|
||||
// VERIFICATION: Check that input contains the full email
|
||||
await this.delay(100, 200)
|
||||
const actualValue = await locator.inputValue().catch(() => '')
|
||||
if (!actualValue.includes(localPart) || !actualValue.includes('@') || !actualValue.includes(domain)) {
|
||||
console.warn(`[HumanTyping.typeEmail] WARNING: Email may not have been typed correctly. Expected: ${email}, Got: ${actualValue}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Type text without clearing the field first (append to existing content)
|
||||
*
|
||||
* CRITICAL: Use this when you want to ADD to existing text, not replace it
|
||||
*
|
||||
* @param locator Playwright locator (input field)
|
||||
* @param text Text to append
|
||||
* @param speed Typing speed multiplier (1.0 = normal, 0.5 = slow, 2.0 = fast)
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
private static async typeAppend(locator: Locator, text: string, speed: number = 1.0): Promise<void> {
|
||||
// Type each character with variable timing (no clearing)
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const char = text[i]
|
||||
if (!char) continue // Skip undefined characters
|
||||
|
||||
// Natural typing speed variance
|
||||
const baseDelay = 40 + Math.random() * 40 // 40-80ms
|
||||
const charDelay = Math.floor(baseDelay / speed)
|
||||
|
||||
// Slower on special characters
|
||||
const isSpecialChar = /[^a-zA-Z0-9@.]/.test(char)
|
||||
const finalDelay = isSpecialChar ? charDelay * 1.5 : charDelay
|
||||
|
||||
await locator.pressSequentially(char, { delay: finalDelay }).catch(() => {
|
||||
// Typing failed - continue (character may have been typed)
|
||||
})
|
||||
|
||||
// Occasional micro-pauses (10% chance)
|
||||
if (Math.random() < 0.1 && i > 0) {
|
||||
await this.delay(100, 300)
|
||||
}
|
||||
|
||||
// Burst typing pattern (30% chance)
|
||||
if (Math.random() < 0.3 && i < text.length - 2) {
|
||||
const burstLength = Math.floor(Math.random() * 2) + 2 // 2-3 chars
|
||||
for (let j = 0; j < burstLength && i + 1 < text.length; j++) {
|
||||
i++
|
||||
const nextChar = text[i]
|
||||
if (nextChar) {
|
||||
await locator.pressSequentially(nextChar, { delay: 10 }).catch(() => { })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Short pause after typing
|
||||
await this.delay(100, 300)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user