mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-17 21:43:59 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf7f7ac790 | ||
|
|
f7aa5039f9 | ||
|
|
e082fb03f0 | ||
|
|
0303b8c605 | ||
|
|
2fea17c415 | ||
|
|
c5beccb54b |
@@ -12,6 +12,7 @@ RUN apt-get update && apt-get install -y jq
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Check if "headless" is set to "true" in the config.json file
|
# Check if "headless" is set to "true" in the config.json file
|
||||||
|
# DELETE BELOW IF YOU WANT TO RUN THE DOCKER SCRIPT HEADFULL!
|
||||||
RUN HEADLESS=$(cat src/config.json | jq -r .headless) \
|
RUN HEADLESS=$(cat src/config.json | jq -r .headless) \
|
||||||
&& if [ "$HEADLESS" != "true" ]; then \
|
&& if [ "$HEADLESS" != "true" ]; then \
|
||||||
echo "Error: 'headless' in src/config.json is not true."; \
|
echo "Error: 'headless' in src/config.json is not true."; \
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -12,16 +12,21 @@ Under development, however mainly for personal use!
|
|||||||
6. Run `npm run start` to start the built script
|
6. Run `npm run start` to start the built script
|
||||||
|
|
||||||
## Notes ##
|
## Notes ##
|
||||||
- If you end the script without closing the browser window first (only with headless as false), you'll be left with hanging chrome instances using resources. Use taskmanager to kill these or use the included `npm run chrome-kill-win` script. (Windows)
|
- If you end the script without closing the browser window first (only with headless as false), you'll be left with hanging chrome instances using resources. Use taskmanager to kill these or use the included `npm run kill-chrome-win` script. (Windows)
|
||||||
- If you automate this script, set it to run at least 2 times a day to make sure it picked up all tasks, set `"runOnZeroPoints": false` so it doesn't run when no points are found.
|
- If you automate this script, set it to run at least 2 times a day to make sure it picked up all tasks, set `"runOnZeroPoints": false` so it doesn't run when no points are found.
|
||||||
- Docker container has to be recreated for any changes regardings the `config.json` and/or `accounts.json`.
|
|
||||||
|
## Docker (Experimental) ##
|
||||||
|
1. Download the source code
|
||||||
|
2. Make changes to your `accounts.json` and/or `config.json`
|
||||||
|
3. Run `docker build -t microsoft-rewards-script-docker .`
|
||||||
|
- Docker container has to be recreated for any changes regarding the `config.json` and/or `accounts.json`!
|
||||||
|
|
||||||
## Config ##
|
## Config ##
|
||||||
| Setting | Description | Default |
|
| Setting | Description | Default |
|
||||||
| :------------- |:-------------| :-----|
|
| :------------- |:-------------| :-----|
|
||||||
| baseURL | MS Rewards page | `https://rewards.bing.com` |
|
| baseURL | MS Rewards page | `https://rewards.bing.com` |
|
||||||
| sessionPath | Path to where you want sessions/fingerprints to be stored | `sessions` (In src/browser/sessions) |
|
| sessionPath | Path to where you want sessions/fingerprints to be stored | `sessions` (In ./browser/sessions) |
|
||||||
| headless | If the browser window should be visable be ran in the background | `false` (Browser is visable) |
|
| headless | If the browser window should be visible be ran in the background | `false` (Browser is visible) |
|
||||||
| runOnZeroPoints | Run the rest of the script if 0 points can be earned | `false` (Will not run on 0 points) |
|
| runOnZeroPoints | Run the rest of the script if 0 points can be earned | `false` (Will not run on 0 points) |
|
||||||
| clusters | Amount of instances ran on launch, 1 per account | `1` (Will run 1 account at the time) |
|
| clusters | Amount of instances ran on launch, 1 per account | `1` (Will run 1 account at the time) |
|
||||||
| saveFingerprint | Re-use the same fingerprint each time | `false` (Will generate a new fingerprint each time) |
|
| saveFingerprint | Re-use the same fingerprint each time | `false` (Will generate a new fingerprint each time) |
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "microsoft-rewards-script",
|
"name": "microsoft-rewards-script",
|
||||||
"version": "1.4.2",
|
"version": "1.4.4",
|
||||||
"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": {
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"ts-start": "ts-node ./src/index.ts",
|
"ts-start": "ts-node ./src/index.ts",
|
||||||
"dev": "ts-node ./src/index.ts -dev",
|
"dev": "ts-node ./src/index.ts -dev",
|
||||||
"kill-chrome-win": "powershell -Command \"Get-Process | Where-Object { $_.MainModule.FileVersionInfo.FileDescription -eq 'Google Chrome for Testing' } | ForEach-Object { Stop-Process -Id $_.Id -Force }\"",
|
"kill-chrome-win": "powershell -Command \"Get-Process | Where-Object { $_.MainModule.FileVersionInfo.FileDescription -eq 'Google Chrome for Testing' } | ForEach-Object { Stop-Process -Id $_.Id -Force }\"",
|
||||||
"create-docker" : "docker build -t microsoft-rewards-script-docker ."
|
"create-docker": "docker build -t microsoft-rewards-script-docker ."
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Bing Rewards",
|
"Bing Rewards",
|
||||||
@@ -26,17 +26,17 @@
|
|||||||
"author": "Netsky",
|
"author": "Netsky",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.0",
|
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-modules-newline": "^0.0.6",
|
"eslint-plugin-modules-newline": "^0.0.6",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.7",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"fingerprint-generator": "^2.1.46",
|
"fingerprint-generator": "^2.1.48",
|
||||||
"fingerprint-injector": "^2.1.46",
|
"fingerprint-injector": "^2.1.48",
|
||||||
"playwright": "^1.40.1",
|
"playwright": "^1.41.2",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,13 +95,13 @@ export default class BrowserFunc {
|
|||||||
const scripts = Array.from(document.querySelectorAll('script'))
|
const scripts = Array.from(document.querySelectorAll('script'))
|
||||||
const targetScript = scripts.find(script => script.innerText.includes('var dashboard'))
|
const targetScript = scripts.find(script => script.innerText.includes('var dashboard'))
|
||||||
|
|
||||||
if (targetScript) {
|
return targetScript?.innerText ? targetScript.innerText : null
|
||||||
return targetScript.innerText
|
|
||||||
} else {
|
|
||||||
throw this.bot.log('GET-DASHBOARD-DATA', 'Script containing dashboard data not found', 'error')
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!scriptContent) {
|
||||||
|
throw this.bot.log('GET-DASHBOARD-DATA', 'Dashboard data not found within script', 'error')
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the dashboard object from the script content
|
// Extract the dashboard object from the script content
|
||||||
const dashboardData = await this.bot.homePage.evaluate(scriptContent => {
|
const dashboardData = await this.bot.homePage.evaluate(scriptContent => {
|
||||||
// Extract the dashboard object using regex
|
// Extract the dashboard object using regex
|
||||||
@@ -110,11 +110,13 @@ export default class BrowserFunc {
|
|||||||
|
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
return JSON.parse(match[1])
|
return JSON.parse(match[1])
|
||||||
} else {
|
|
||||||
throw this.bot.log('GET-DASHBOARD-DATA', 'Dashboard data not found within script', 'error')
|
|
||||||
}
|
}
|
||||||
}, scriptContent)
|
}, scriptContent)
|
||||||
|
|
||||||
|
if (!dashboardData) {
|
||||||
|
throw this.bot.log('GET-DASHBOARD-DATA', 'Unable to parse dashboard script', 'error')
|
||||||
|
}
|
||||||
|
|
||||||
return dashboardData
|
return dashboardData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default class BrowserUtil {
|
|||||||
|
|
||||||
async tryDismissAllMessages(page: Page): Promise<boolean> {
|
async tryDismissAllMessages(page: Page): Promise<boolean> {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
|
{ selector: '#acceptButton', label: 'AcceptButton' },
|
||||||
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
||||||
{ selector: '#iShowSkip', label: 'iShowSkip' },
|
{ selector: '#iShowSkip', label: 'iShowSkip' },
|
||||||
{ selector: '#iNext', label: 'iNext' },
|
{ selector: '#iNext', label: 'iNext' },
|
||||||
|
|||||||
@@ -53,35 +53,41 @@ export class Login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async execLogin(page: Page, email: string, password: string) {
|
private async execLogin(page: Page, email: string, password: string) {
|
||||||
await page.type('#i0116', email)
|
|
||||||
await page.click('#idSIButton9')
|
|
||||||
|
|
||||||
this.bot.log('LOGIN', 'Email entered successfully')
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#i0118', { state: 'visible', timeout: 2000 })
|
// Enter email
|
||||||
await this.bot.utils.wait(2000)
|
await page.fill('#i0116', email)
|
||||||
|
|
||||||
await page.type('#i0118', password)
|
|
||||||
await page.click('#idSIButton9')
|
await page.click('#idSIButton9')
|
||||||
|
|
||||||
// When erroring at this stage it means a 2FA code is required
|
this.bot.log('LOGIN', 'Email entered successfully')
|
||||||
} catch (error) {
|
|
||||||
this.bot.log('LOGIN', '2FA code required')
|
|
||||||
|
|
||||||
// Wait for user input
|
try {
|
||||||
const code = await new Promise<string>((resolve) => {
|
// Enter password
|
||||||
rl.question('Enter 2FA code:\n', (input) => {
|
await page.waitForSelector('#i0118', { state: 'visible', timeout: 2000 })
|
||||||
rl.close()
|
await this.bot.utils.wait(2000)
|
||||||
resolve(input)
|
|
||||||
|
await page.fill('#i0118', password)
|
||||||
|
await page.click('#idSIButton9')
|
||||||
|
|
||||||
|
// When erroring at this stage it means a 2FA code is required
|
||||||
|
} catch (error) {
|
||||||
|
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.type('input[name="otc"]', code)
|
await page.fill('input[name="otc"]', code)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
}
|
||||||
|
|
||||||
} finally {
|
|
||||||
this.bot.log('LOGIN', 'Password entered successfully')
|
this.bot.log('LOGIN', 'Password entered successfully')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('LOGIN', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentURL = new URL(page.url())
|
const currentURL = new URL(page.url())
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class Workers {
|
|||||||
// Punch Card
|
// Punch Card
|
||||||
async doPunchCard(page: Page, data: DashboardData) {
|
async doPunchCard(page: Page, data: DashboardData) {
|
||||||
|
|
||||||
const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion.complete) ?? [] // Only return uncompleted punch cards
|
const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion?.complete) ?? [] // Only return uncompleted punch cards
|
||||||
|
|
||||||
if (!punchCardsUncompleted.length) {
|
if (!punchCardsUncompleted.length) {
|
||||||
this.bot.log('PUNCH-CARD', 'All "Punch Cards" have already been completed')
|
this.bot.log('PUNCH-CARD', 'All "Punch Cards" have already been completed')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Page } from 'playwright'
|
import { Page } from 'playwright'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { platform } from 'os'
|
||||||
|
|
||||||
import { Workers } from '../Workers'
|
import { Workers } from '../Workers'
|
||||||
|
|
||||||
@@ -128,20 +129,25 @@ export class Search extends Workers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async bingSearch(searchPage: Page, query: string) {
|
private async bingSearch(searchPage: Page, query: string) {
|
||||||
|
const platformControlKey = platform() === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
// Try a max of 5 times
|
// Try a max of 5 times
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
try {
|
try {
|
||||||
// Go back to the top
|
|
||||||
await searchPage.keyboard.press('Home')
|
// Go to top of the page
|
||||||
|
await searchPage.evaluate(() => {
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
})
|
||||||
|
|
||||||
const searchBar = '#sb_form_q'
|
const searchBar = '#sb_form_q'
|
||||||
await searchPage.waitForSelector(searchBar, { state: 'attached', timeout: 10_000 })
|
await searchPage.waitForSelector(searchBar, { state: 'visible', timeout: 10_000 })
|
||||||
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('Control')
|
await searchPage.keyboard.down(platformControlKey)
|
||||||
await searchPage.keyboard.press('A')
|
await searchPage.keyboard.press('A')
|
||||||
await searchPage.keyboard.press('Backspace')
|
await searchPage.keyboard.press('Backspace')
|
||||||
await searchPage.keyboard.up('Control')
|
await searchPage.keyboard.up(platformControlKey)
|
||||||
await searchPage.keyboard.type(query)
|
await searchPage.keyboard.type(query)
|
||||||
await searchPage.keyboard.press('Enter')
|
await searchPage.keyboard.press('Enter')
|
||||||
|
|
||||||
@@ -252,10 +258,11 @@ export class Search extends Workers {
|
|||||||
|
|
||||||
private async randomScroll(page: Page) {
|
private async randomScroll(page: Page) {
|
||||||
try {
|
try {
|
||||||
// Press the arrow down key to scroll
|
const scrollAmount = this.bot.utils.randomNumber(5, 5000)
|
||||||
for (let i = 0; i < this.bot.utils.randomNumber(5, 600); i++) {
|
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.evaluate((scrollAmount) => {
|
||||||
}
|
window.scrollBy(0, scrollAmount)
|
||||||
|
}, scrollAmount)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.bot.log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
|
this.bot.log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ export class MicrosoftRewardsBot {
|
|||||||
|
|
||||||
// Desktop
|
// Desktop
|
||||||
async Desktop(account: Account) {
|
async Desktop(account: Account) {
|
||||||
|
this.isMobile = false
|
||||||
|
|
||||||
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
||||||
this.homePage = await browser.newPage()
|
this.homePage = await browser.newPage()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user