mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-10 02:16:18 +00:00
1.4.8
This commit is contained in:
@@ -28,6 +28,10 @@ module.exports = {
|
|||||||
'error',
|
'error',
|
||||||
'never'
|
'never'
|
||||||
],
|
],
|
||||||
|
'@typescript-eslint/no-explicit-any':
|
||||||
|
['warn', {
|
||||||
|
fixToUnknown: true // This line is optional and only relevant if you are using TypeScript
|
||||||
|
}],
|
||||||
'comma-dangle': 'off',
|
'comma-dangle': 'off',
|
||||||
'@typescript-eslint/comma-dangle': 'error',
|
'@typescript-eslint/comma-dangle': 'error',
|
||||||
'prefer-arrow-callback': 'error'
|
'prefer-arrow-callback': 'error'
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "microsoft-rewards-script",
|
"name": "microsoft-rewards-script",
|
||||||
"version": "1.4.7",
|
"version": "1.4.8",
|
||||||
"description": "Automatically do tasks for Microsoft Rewards but in TS!",
|
"description": "Automatically do tasks for Microsoft Rewards but in TS!",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -26,17 +26,17 @@
|
|||||||
"author": "Netsky",
|
"author": "Netsky",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
"@typescript-eslint/eslint-plugin": "^7.17.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-modules-newline": "^0.0.6",
|
"eslint-plugin-modules-newline": "^0.0.6",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.4",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0",
|
||||||
"fingerprint-generator": "^2.1.51",
|
"fingerprint-generator": "^2.1.54",
|
||||||
"fingerprint-injector": "^2.1.51",
|
"fingerprint-injector": "^2.1.54",
|
||||||
"playwright": "^1.44.1",
|
"playwright": "^1.46.1",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,78 +56,110 @@ export class Login {
|
|||||||
|
|
||||||
private async execLogin(page: Page, email: string, password: string) {
|
private async execLogin(page: Page, email: string, password: string) {
|
||||||
try {
|
try {
|
||||||
// Enter email
|
await this.enterEmail(page, email)
|
||||||
await page.fill('#i0116', email)
|
await this.enterPassword(page, password)
|
||||||
await page.click('#idSIButton9')
|
await this.checkLoggedIn(page)
|
||||||
|
} catch (error: any) {
|
||||||
this.bot.log('LOGIN', 'Email entered successfully')
|
this.bot.log('LOGIN', 'An error occurred: ' + error.message, 'error')
|
||||||
|
|
||||||
try {
|
|
||||||
// Enter password
|
|
||||||
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')
|
|
||||||
|
|
||||||
// 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.');
|
|
||||||
|
|
||||||
let numberToPress: string | null = await (await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })).textContent();
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
this.bot.log('LOGIN', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const currentURL = new URL(page.url())
|
private async enterEmail(page: Page, email: string) {
|
||||||
|
await page.fill('#i0116', email)
|
||||||
|
await page.click('#idSIButton9')
|
||||||
|
this.bot.log('LOGIN', 'Email entered successfully')
|
||||||
|
}
|
||||||
|
|
||||||
while (currentURL.pathname !== '/' || currentURL.hostname !== 'rewards.bing.com') {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60000 })
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.fill('input[name="otc"]', code)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
this.bot.log('LOGIN', '2FA code entered successfully')
|
||||||
|
}
|
||||||
|
|
||||||
|
private async checkLoggedIn(page: Page) {
|
||||||
|
const targetHostname = 'rewards.bing.com'
|
||||||
|
const targetPathname = '/'
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
await this.bot.browser.utils.tryDismissAllMessages(page)
|
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
|
// Wait for login to complete
|
||||||
await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 })
|
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> {
|
private async checkBingLogin(page: Page): Promise<void> {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export class Workers {
|
|||||||
morePromotions.push(data.promotionalItem as unknown as MorePromotion)
|
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) {
|
if (!activitiesUncompleted.length) {
|
||||||
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
|
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
|
||||||
@@ -132,13 +132,13 @@ export class Workers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let selector = `[data-bi-id="${activity.offerId}"]`
|
let selector = `[data-bi-id^="${activity.offerId}"]`
|
||||||
|
|
||||||
if (punchCard) {
|
if (punchCard) {
|
||||||
selector = await this.bot.browser.func.getPunchCardActivity(activityPage, activity)
|
selector = await this.bot.browser.func.getPunchCardActivity(activityPage, activity)
|
||||||
|
|
||||||
} else if (activity.name.toLowerCase().includes('membercenter')) {
|
} 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
|
// Click element, it will be opened in a new tab
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export class Search extends Workers {
|
|||||||
// Mobile search doesn't seem to like related queries?
|
// 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) })
|
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
|
// Loop over Google search queries
|
||||||
for (let i = 0; i < queries.length; i++) {
|
for (let i = 0; i < queries.length; i++) {
|
||||||
const query = queries[i] as string
|
const query = queries[i] as string
|
||||||
|
|||||||
Reference in New Issue
Block a user