mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-10 18:36:17 +00:00
1.5.2
- Updated packages - Cleanup - Improved message dismissal - Improved login functionality With help from @LightZirconite
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "microsoft-rewards-script",
|
"name": "microsoft-rewards-script",
|
||||||
"version": "1.5.1",
|
"version": "1.5.2",
|
||||||
"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": {
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.8.4",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"fingerprint-generator": "^2.1.62",
|
"fingerprint-generator": "^2.1.66",
|
||||||
"fingerprint-injector": "^2.1.62",
|
"fingerprint-injector": "^2.1.66",
|
||||||
"http-proxy-agent": "^7.0.2",
|
"http-proxy-agent": "^7.0.2",
|
||||||
"https-proxy-agent": "^7.0.6",
|
"https-proxy-agent": "^7.0.6",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Browser {
|
|||||||
const context = await newInjectedContext(browser as any, { fingerprint: fingerprint })
|
const context = await newInjectedContext(browser as any, { fingerprint: fingerprint })
|
||||||
|
|
||||||
// Set timeout to preferred amount
|
// Set timeout to preferred amount
|
||||||
context.setDefaultTimeout(this.bot.utils.stringToMs(this.bot.config?.globalTimeout ?? 30_000))
|
context.setDefaultTimeout(this.bot.utils.stringToMs(this.bot.config?.globalTimeout ?? 30000))
|
||||||
|
|
||||||
await context.addCookies(sessionData.cookies)
|
await context.addCookies(sessionData.cookies)
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ export default class BrowserFunc {
|
|||||||
|
|
||||||
async waitForQuizRefresh(page: Page): Promise<boolean> {
|
async waitForQuizRefresh(page: Page): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('span.rqMCredits', { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector('span.rqMCredits', { state: 'visible', timeout: 10000 })
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default class BrowserUtil {
|
|||||||
|
|
||||||
async tryDismissAllMessages(page: Page): Promise<boolean> {
|
async tryDismissAllMessages(page: Page): Promise<boolean> {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
|
{ selector: 'button[type="submit"]', label: 'Submit Button' },
|
||||||
{ selector: '#acceptButton', label: 'AcceptButton' },
|
{ selector: '#acceptButton', label: 'AcceptButton' },
|
||||||
{ selector: '.ext-secondary.ext-button', label: '"Skip for now" Button' },
|
{ selector: '.ext-secondary.ext-button', label: '"Skip for now" Button' },
|
||||||
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
||||||
@@ -23,21 +24,25 @@ export default class BrowserUtil {
|
|||||||
{ selector: '.ms-Button.ms-Button--primary', label: 'Primary Button' },
|
{ selector: '.ms-Button.ms-Button--primary', label: 'Primary Button' },
|
||||||
{ selector: '.c-glyph.glyph-cancel', label: 'Mobile Welcome Button' },
|
{ selector: '.c-glyph.glyph-cancel', label: 'Mobile Welcome Button' },
|
||||||
{ selector: '.maybe-later', label: 'Mobile Rewards App Banner' },
|
{ selector: '.maybe-later', label: 'Mobile Rewards App Banner' },
|
||||||
{ selector: '//div[@id="cookieConsentContainer"]//button[contains(text(), "Accept")]', label: 'Accept Cookie Consent Container' },
|
{ selector: '//div[@id="cookieConsentContainer"]//button[contains(text(), "Accept")]', label: 'Accept Cookie Consent Container', isXPath: true },
|
||||||
{ selector: '#bnp_btn_accept', label: 'Bing Cookie Banner' },
|
{ selector: '#bnp_btn_accept', label: 'Bing Cookie Banner' },
|
||||||
{ selector: '#reward_pivot_earn', label: 'Reward Coupon Accept' }
|
{ selector: '#reward_pivot_earn', label: 'Reward Coupon Accept' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const dismissTasks = buttons.map(async (button) => {
|
const dismissTasks = buttons.map(async (button) => {
|
||||||
try {
|
try {
|
||||||
const element = page.locator(button.selector)
|
const element = button.isXPath
|
||||||
|
? page.locator(`xpath=${button.selector}`)
|
||||||
|
: page.locator(button.selector)
|
||||||
|
|
||||||
if (await element.first().isVisible({ timeout: 1000 })) {
|
if (await element.first().isVisible({ timeout: 1000 })) {
|
||||||
await element.first().click({ timeout: 1000 })
|
await element.first().click({ timeout: 1000 })
|
||||||
|
await page.waitForTimeout(500)
|
||||||
this.bot.log(this.bot.isMobile, 'DISMISS-ALL-MESSAGES', `Dismissed: ${button.label}`)
|
this.bot.log(this.bot.isMobile, 'DISMISS-ALL-MESSAGES', `Dismissed: ${button.label}`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch {
|
||||||
// Ignore errors and continue
|
// Silent fail
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class Login {
|
|||||||
// Check if account is locked
|
// Check if account is locked
|
||||||
await this.checkAccountLocked(page)
|
await this.checkAccountLocked(page)
|
||||||
|
|
||||||
const isLoggedIn = await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 }).then(() => true).catch(() => false)
|
const isLoggedIn = await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10000 }).then(() => true).catch(() => false)
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
await this.execLogin(page, email, password)
|
await this.execLogin(page, email, password)
|
||||||
@@ -86,30 +86,80 @@ export class Login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async enterEmail(page: Page, email: string) {
|
private async enterEmail(page: Page, email: string) {
|
||||||
const emailPrefilled = await page.waitForSelector('#userDisplayName', { timeout: 2_000 }).catch(() => null)
|
const emailInputSelector = 'input[type="email"]'
|
||||||
if (emailPrefilled) {
|
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email already prefilled by Microsoft')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.fill('#i0116', email)
|
try {
|
||||||
await page.click('#idSIButton9')
|
// Wait for email field
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email entered successfully')
|
const emailField = await page.waitForSelector(emailInputSelector, { state: 'visible', timeout: 2000 }).catch(() => null)
|
||||||
|
if (!emailField) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email field not found', 'warn')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
// Check if email is prefilled
|
||||||
|
const emailPrefilled = await page.waitForSelector('#userDisplayName', { timeout: 5000 }).catch(() => null)
|
||||||
|
if (emailPrefilled) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email already prefilled by Microsoft')
|
||||||
|
} else {
|
||||||
|
// Else clear and fill email
|
||||||
|
await page.fill(emailInputSelector, '')
|
||||||
|
await this.bot.utils.wait(500)
|
||||||
|
await page.fill(emailInputSelector, email)
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextButton = await page.waitForSelector('button[type="submit"]', { timeout: 2000 }).catch(() => null)
|
||||||
|
if (nextButton) {
|
||||||
|
await nextButton.click()
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email entered successfully')
|
||||||
|
} else {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Next button not found after email entry', 'warn')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', `Email entry failed: ${error}`, 'error')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async enterPassword(page: Page, password: string) {
|
private async enterPassword(page: Page, password: string) {
|
||||||
|
const passwordInputSelector = 'input[type="password"]'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#i0118', { state: 'visible', timeout: 2000 })
|
// Wait for password field
|
||||||
await this.bot.utils.wait(2000)
|
const passwordField = await page.waitForSelector(passwordInputSelector, { state: 'visible', timeout: 5000 }).catch(() => null)
|
||||||
await page.fill('#i0118', password)
|
if (!passwordField) {
|
||||||
await page.click('#idSIButton9')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password field not found, possibly 2FA required', 'warn')
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entered successfully')
|
await this.handle2FA(page)
|
||||||
} catch {
|
return
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entry failed or 2FA required')
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
// Clear and fill password
|
||||||
|
await page.fill(passwordInputSelector, '')
|
||||||
|
await this.bot.utils.wait(500)
|
||||||
|
await page.fill(passwordInputSelector, password)
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
const nextButton = await page.waitForSelector('button[type="submit"]', { timeout: 2000 }).catch(() => null)
|
||||||
|
if (nextButton) {
|
||||||
|
await nextButton.click()
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entered successfully')
|
||||||
|
} else {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Next button not found after password entry', 'warn')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', `Password entry failed: ${error}`, 'error')
|
||||||
await this.handle2FA(page)
|
await this.handle2FA(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async handle2FA(page: Page) {
|
private async handle2FA(page: Page) {
|
||||||
try {
|
try {
|
||||||
const numberToPress = await this.get2FACode(page)
|
const numberToPress = await this.get2FACode(page)
|
||||||
@@ -138,7 +188,7 @@ export class Login {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const button = await page.waitForSelector('button[aria-describedby="pushNotificationsTitle errorDescription"]', { state: 'visible', timeout: 2000 }).catch(() => null)
|
const button = await page.waitForSelector('button[aria-describedby="pushNotificationsTitle errorDescription"]', { state: 'visible', timeout: 2000 }).catch(() => null)
|
||||||
if (button) {
|
if (button) {
|
||||||
await this.bot.utils.wait(60_000)
|
await this.bot.utils.wait(60000)
|
||||||
await button.click()
|
await button.click()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@@ -148,7 +198,7 @@ export class Login {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.click('button[aria-describedby="confirmSendTitle"]').catch(() => {})
|
await page.click('button[aria-describedby="confirmSendTitle"]').catch(() => { })
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
|
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
|
||||||
return await element.textContent()
|
return await element.textContent()
|
||||||
@@ -162,7 +212,7 @@ export class Login {
|
|||||||
this.bot.log(this.bot.isMobile, 'LOGIN', `Press the number ${numberToPress} on your Authenticator app to approve the login`)
|
this.bot.log(this.bot.isMobile, 'LOGIN', `Press the number ${numberToPress} on your Authenticator app to approve the login`)
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'If you press the wrong number or the "DENY" button, try again in 60 seconds')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'If you press the wrong number or the "DENY" button, try again in 60 seconds')
|
||||||
|
|
||||||
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60_000 })
|
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60000 })
|
||||||
|
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Login successfully approved!')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Login successfully approved!')
|
||||||
break
|
break
|
||||||
@@ -203,7 +253,7 @@ export class Login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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: 10000 })
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Successfully logged into the rewards portal')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Successfully logged into the rewards portal')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +322,7 @@ export class Login {
|
|||||||
currentUrl = new URL(page.url())
|
currentUrl = new URL(page.url())
|
||||||
await this.bot.utils.wait(5000)
|
await this.bot.utils.wait(5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = new URLSearchParams()
|
const body = new URLSearchParams()
|
||||||
body.append('grant_type', 'authorization_code')
|
body.append('grant_type', 'authorization_code')
|
||||||
body.append('client_id', this.clientId)
|
body.append('client_id', this.clientId)
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export class Workers {
|
|||||||
Due to common false timeout on this function, we're ignoring the error regardless, if it worked then it's faster,
|
Due to common false timeout on this function, we're ignoring the error regardless, if it worked then it's faster,
|
||||||
if it didn't then it gave enough time for the page to load.
|
if it didn't then it gave enough time for the page to load.
|
||||||
*/
|
*/
|
||||||
await activityPage.waitForLoadState('networkidle', { timeout: 10_000 }).catch(() => { })
|
await activityPage.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => { })
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
switch (activity.promotionType) {
|
switch (activity.promotionType) {
|
||||||
|
|||||||
@@ -15,18 +15,18 @@ export class ABC extends Workers {
|
|||||||
const maxIterations = 15
|
const maxIterations = 15
|
||||||
let i
|
let i
|
||||||
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
||||||
await page.waitForSelector('.wk_OptionClickClass', { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector('.wk_OptionClickClass', { state: 'visible', timeout: 10000 })
|
||||||
|
|
||||||
const answers = $('.wk_OptionClickClass')
|
const answers = $('.wk_OptionClickClass')
|
||||||
const answer = answers[this.bot.utils.randomNumber(0, 2)]?.attribs['id']
|
const answer = answers[this.bot.utils.randomNumber(0, 2)]?.attribs['id']
|
||||||
|
|
||||||
await page.waitForSelector(`#${answer}`, { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector(`#${answer}`, { state: 'visible', timeout: 10000 })
|
||||||
|
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
await page.click(`#${answer}`) // Click answer
|
await page.click(`#${answer}`) // Click answer
|
||||||
|
|
||||||
await this.bot.utils.wait(4000)
|
await this.bot.utils.wait(4000)
|
||||||
await page.waitForSelector('div.wk_button', { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector('div.wk_button', { state: 'visible', timeout: 10000 })
|
||||||
await page.click('div.wk_button') // Click next question button
|
await page.click('div.wk_button') // Click next question button
|
||||||
|
|
||||||
page = await this.bot.browser.utils.getLatestTab(page)
|
page = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class Poll extends Workers {
|
|||||||
try {
|
try {
|
||||||
const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}`
|
const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}`
|
||||||
|
|
||||||
await page.waitForSelector(buttonId, { state: 'visible', timeout: 10_000 }).catch(() => { }) // We're gonna click regardless or not
|
await page.waitForSelector(buttonId, { state: 'visible', timeout: 10000 }).catch(() => { }) // We're gonna click regardless or not
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
await page.click(buttonId)
|
await page.click(buttonId)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class Quiz extends Workers {
|
|||||||
const answers: string[] = []
|
const answers: string[] = []
|
||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10_000 })
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10000 })
|
||||||
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
||||||
|
|
||||||
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
||||||
@@ -59,7 +59,7 @@ export class Quiz extends Workers {
|
|||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
|
|
||||||
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10_000 })
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10000 })
|
||||||
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
||||||
|
|
||||||
if (dataOption === correctOption) {
|
if (dataOption === correctOption) {
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export class Search extends Workers {
|
|||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
|
|
||||||
const searchBar = '#sb_form_q'
|
const searchBar = '#sb_form_q'
|
||||||
await searchPage.waitForSelector(searchBar, { state: 'visible', timeout: 10_000 })
|
await searchPage.waitForSelector(searchBar, { state: 'visible', timeout: 10000 })
|
||||||
await searchPage.click(searchBar) // Focus on the textarea
|
await searchPage.click(searchBar) // Focus on the textarea
|
||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
await searchPage.keyboard.down(platformControlKey)
|
await searchPage.keyboard.down(platformControlKey)
|
||||||
@@ -312,7 +312,7 @@ export class Search extends Workers {
|
|||||||
await this.closeContinuePopup(page)
|
await this.closeContinuePopup(page)
|
||||||
|
|
||||||
// Stay for 10 seconds for page to load and "visit"
|
// Stay for 10 seconds for page to load and "visit"
|
||||||
await this.bot.utils.wait(10_000)
|
await this.bot.utils.wait(10000)
|
||||||
|
|
||||||
// Will get current tab if no new one is created, this will always be the visited site or the result page if it failed to click
|
// Will get current tab if no new one is created, this will always be the visited site or the result page if it failed to click
|
||||||
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class SearchOnBing extends Workers {
|
|||||||
const query = await this.getSearchQuery(activity.title)
|
const query = await this.getSearchQuery(activity.title)
|
||||||
|
|
||||||
const searchBar = '#sb_form_q'
|
const searchBar = '#sb_form_q'
|
||||||
await page.waitForSelector(searchBar, { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector(searchBar, { state: 'visible', timeout: 10000 })
|
||||||
await page.click(searchBar)
|
await page.click(searchBar)
|
||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
await page.keyboard.type(query)
|
await page.keyboard.type(query)
|
||||||
|
|||||||
Reference in New Issue
Block a user