Skip to content

Commit

Permalink
1.4.8
Browse files Browse the repository at this point in the history
  • Loading branch information
TheNetsky committed Aug 18, 2024
1 parent 755237c commit ce2a72e
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 66 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ module.exports = {
'error',
'never'
],
'@typescript-eslint/no-explicit-any':
['warn', {
fixToUnknown: true // This line is optional and only relevant if you are using TypeScript
}],
'comma-dangle': 'off',
'@typescript-eslint/comma-dangle': 'error',
'prefer-arrow-callback': 'error'
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "microsoft-rewards-script",
"version": "1.4.7",
"version": "1.4.8",
"description": "Automatically do tasks for Microsoft Rewards but in TS!",
"main": "index.js",
"engines": {
Expand All @@ -26,17 +26,17 @@
"author": "Netsky",
"license": "ISC",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"eslint": "^8.57.0",
"eslint-plugin-modules-newline": "^0.0.6",
"typescript": "^5.4.5"
"typescript": "^5.5.4"
},
"dependencies": {
"axios": "^1.7.2",
"cheerio": "^1.0.0-rc.12",
"fingerprint-generator": "^2.1.51",
"fingerprint-injector": "^2.1.51",
"playwright": "^1.44.1",
"axios": "^1.7.4",
"cheerio": "^1.0.0",
"fingerprint-generator": "^2.1.54",
"fingerprint-injector": "^2.1.54",
"playwright": "^1.46.1",
"ts-node": "^10.9.2"
}
}
142 changes: 87 additions & 55 deletions src/functions/Login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,78 +56,110 @@ export class Login {

private async execLogin(page: Page, email: string, password: string) {
try {
// Enter email
await page.fill('#i0116', email)
await page.click('#idSIButton9')
await this.enterEmail(page, email)
await this.enterPassword(page, password)
await this.checkLoggedIn(page)
} catch (error: any) {
this.bot.log('LOGIN', 'An error occurred: ' + error.message, 'error')
}
}

this.bot.log('LOGIN', 'Email entered successfully')
private async enterEmail(page: Page, email: string) {
await page.fill('#i0116', email)
await page.click('#idSIButton9')
this.bot.log('LOGIN', 'Email entered successfully')
}

try {
// Enter password
await page.waitForSelector('#i0118', { state: 'visible', timeout: 2000 })
await this.bot.utils.wait(2000)
private async enterPassword(page: Page, password: string) {
try {
await page.waitForSelector('#i0118', { state: 'visible', timeout: 2000 })
await this.bot.utils.wait(2000)
await page.fill('#i0118', password)
await page.click('#idSIButton9')
this.bot.log('LOGIN', 'Password entered successfully')
} catch {
this.bot.log('LOGIN', 'Password entry failed or 2FA required')
await this.handle2FA(page)
}
}

await page.fill('#i0118', password)
await page.click('#idSIButton9')
private async handle2FA(page: Page) {
try {
const numberToPress = await this.get2FACode(page)
if (numberToPress) {
// Authentictor App verification
await this.authAppVerification(page, numberToPress)
} else {
// SMS verification
await this.authSMSVerification(page)
}
} catch (error: any) {
this.bot.log('LOGIN', `2FA handling failed: ${error.message}`)
}
}

this.bot.log('LOGIN', 'Password entered successfully')
private async get2FACode(page: Page): Promise<string | null> {
try {
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
return await element.textContent()
} catch {
await page.click('button[aria-describedby="confirmSendTitle"]')
await this.bot.utils.wait(2000)
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
return await element.textContent()
}
}

// When erroring at this stage it means a 2FA code is required
} catch (error) {
// this.bot.log('LOGIN', 'App approval required because you have passwordless enabled.');
private async authAppVerification(page: Page, numberToPress: string | null) {
// eslint-disable-next-line no-constant-condition
while (true) {
try {
this.bot.log('LOGIN', `Press the number ${numberToPress} on your Authenticator app to approve the login`)
this.bot.log('LOGIN', 'If you press the wrong number or the "DENY" button, try again in 60 seconds')

let numberToPress: string | null = await (await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })).textContent();
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60000 })

if (!numberToPress) {
await page.click('button[aria-describedby="confirmSendTitle"]');
await this.bot.utils.wait(2000);
numberToPress = await (await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })).textContent();
}
this.bot.log('LOGIN', 'Login successfully approved!')
break
} catch {
this.bot.log('LOGIN', 'The code is expired. Trying to get a new code...')
await page.click('button[aria-describedby="pushNotificationsTitle errorDescription"]')
numberToPress = await this.get2FACode(page)
}
}
}

if (numberToPress) {
while (true) {
try {
this.bot.log('LOGIN', 'Press the number below on your Authenticator app to approve the login');
this.bot.log('LOGIN', 'If you press the wrong number or the "Deny" button, try again in 60 seconds');
this.bot.log('LOGIN', 'Number to press: ' + numberToPress);
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60000 })
break;
} catch (error) {
this.bot.log('LOGIN', 'The code is expired. Trying to get the new code...');
(await page.waitForSelector('button[aria-describedby="pushNotificationsTitle errorDescription"]', { state: 'visible', timeout: 5000 })).click();
numberToPress = await (await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })).textContent();
}
}
this.bot.log('LOGIN', 'Login successfully approved!');
} else {
this.bot.log('LOGIN', '2FA code required')
// Wait for user input
const code = await new Promise<string>((resolve) => {
rl.question('Enter 2FA code:\n', (input) => {
rl.close()
resolve(input)
})
})

await page.fill('input[name="otc"]', code)
await page.keyboard.press('Enter')
private async authSMSVerification(page: Page) {
this.bot.log('LOGIN', 'SMS 2FA code required. Waiting for user input...')

}
}
const code = await new Promise<string>((resolve) => {
rl.question('Enter 2FA code:\n', (input) => {
rl.close()
resolve(input)
})
})

} catch (error) {
this.bot.log('LOGIN', 'An error occurred:' + error, 'error')
}
await page.fill('input[name="otc"]', code)
await page.keyboard.press('Enter')
this.bot.log('LOGIN', '2FA code entered successfully')
}

const currentURL = new URL(page.url())
private async checkLoggedIn(page: Page) {
const targetHostname = 'rewards.bing.com'
const targetPathname = '/'

while (currentURL.pathname !== '/' || currentURL.hostname !== 'rewards.bing.com') {
// eslint-disable-next-line no-constant-condition
while (true) {
await this.bot.browser.utils.tryDismissAllMessages(page)
currentURL.href = page.url()
const currentURL = new URL(page.url())
if (currentURL.hostname === targetHostname && currentURL.pathname === targetPathname) {
break
}
}

// Wait for login to complete
await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 })
this.bot.log('LOGIN', 'Successfully logged into the rewards portal')
}

private async checkBingLogin(page: Page): Promise<void> {
Expand Down
6 changes: 3 additions & 3 deletions src/functions/Workers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class Workers {
morePromotions.push(data.promotionalItem as unknown as MorePromotion)
}

const activitiesUncompleted = morePromotions?.filter(x => !x.complete && x.pointProgressMax > 0 && !x.attributes.is_unlocked) ?? [];
const activitiesUncompleted = morePromotions?.filter(x => !x.complete && x.pointProgressMax > 0 && !x.attributes.is_unlocked) ?? []

if (!activitiesUncompleted.length) {
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
Expand Down Expand Up @@ -132,13 +132,13 @@ export class Workers {
}


let selector = `[data-bi-id="${activity.offerId}"]`
let selector = `[data-bi-id^="${activity.offerId}"]`

if (punchCard) {
selector = await this.bot.browser.func.getPunchCardActivity(activityPage, activity)

} else if (activity.name.toLowerCase().includes('membercenter')) {
selector = `[data-bi-id="${activity.name}"]`
selector = `[data-bi-id^="${activity.name}"]`
}

// Click element, it will be opened in a new tab
Expand Down
2 changes: 2 additions & 0 deletions src/functions/activities/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export class Search extends Workers {
// Mobile search doesn't seem to like related queries?
googleSearchQueries.forEach(x => { this.bot.isMobile ? queries.push(x.topic) : queries.push(x.topic, ...x.related) })

await this.bot.browser.utils.tryDismissBingCookieBanner(page)

// Loop over Google search queries
for (let i = 0; i < queries.length; i++) {
const query = queries[i] as string
Expand Down

0 comments on commit ce2a72e

Please sign in to comment.