This commit is contained in:
TheNetsky
2023-10-19 22:09:05 +02:00
parent 2366a3dd7f
commit e195f973cd
15 changed files with 81 additions and 34 deletions

View File

@@ -23,6 +23,7 @@ Under development, however mainly for personal use!
- [x] Mobile Searches - [x] Mobile Searches
- [x] Emulated Scrolling Support - [x] Emulated Scrolling Support
- [x] Emulated Link Clicking Support - [x] Emulated Link Clicking Support
- [x] Geo Locale Search Queries
- [x] Completing Daily Set - [x] Completing Daily Set
- [x] Completing More Promotions - [x] Completing More Promotions
- [x] Solving Quiz (10 point variant) - [x] Solving Quiz (10 point variant)
@@ -36,7 +37,7 @@ Under development, however mainly for personal use!
- [ ] Completing Shopping Game - [ ] Completing Shopping Game
- [ ] Completing Gaming Tab - [ ] Completing Gaming Tab
- [x] Clustering Support - [x] Clustering Support
- [ ] Proxy Support - [x] Proxy Support
## Disclaimer ## ## Disclaimer ##
Your account may be at risk of getting banned or suspended using this script, you've been warned! Your account may be at risk of getting banned or suspended using this script, you've been warned!

View File

@@ -1,6 +1,6 @@
{ {
"name": "microsoft-rewards-script", "name": "microsoft-rewards-script",
"version": "1.2.0", "version": "1.2.1",
"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": {

View File

@@ -1,10 +1,22 @@
[ [
{ {
"email": "email_1", "email": "email_1",
"password": "password_1" "password": "password_1",
"proxy": {
"url": "",
"port": 0,
"username": "",
"password": ""
}
}, },
{ {
"email": "email_2", "email": "email_2",
"password": "password_2" "password": "password_2",
"proxy": {
"url": "",
"port": 0,
"username": "",
"password": ""
}
} }
] ]

View File

@@ -1,16 +1,18 @@
import puppeteer from 'puppeteer-extra' import puppeteer from 'puppeteer-extra'
import StealthPlugin from 'puppeteer-extra-plugin-stealth' import stealthPlugin from 'puppeteer-extra-plugin-stealth'
import { getUserAgent } from '../util/UserAgent' import { getUserAgent } from '../util/UserAgent'
import { loadSesion } from './BrowserFunc' import { loadSesion } from './BrowserFunc'
import { AccountProxy } from '../interface/Account'
import { headless } from '../config.json' import { headless } from '../config.json'
puppeteer.use(StealthPlugin()) puppeteer.use(stealthPlugin())
class Browser { class Browser {
async createBrowser(email: string, isMobile: boolean) { async createBrowser(email: string, proxy: AccountProxy, isMobile: boolean) {
const userAgent = await getUserAgent(isMobile) const userAgent = await getUserAgent(isMobile)
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
@@ -21,7 +23,8 @@ class Browser {
'--mute-audio', '--mute-audio',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',
`--user-agent=${userAgent.userAgent}`, `--user-agent=${userAgent.userAgent}`,
isMobile ? '--window-size=568,1024' : '' isMobile ? '--window-size=568,1024' : '',
proxy.url ? `--proxy-server=${proxy.url}:${proxy.port}` : ''
] ]
}) })

View File

@@ -1,7 +1,7 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { load } from 'cheerio' import { CheerioAPI, load } from 'cheerio'
import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil' import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil'
import { getFormattedDate, wait } from './../util/Utils' import { getFormattedDate, wait } from './../util/Utils'
@@ -200,7 +200,7 @@ export async function loadSesion(email: string): Promise<string> {
} }
} }
export async function waitForQuizRefresh(page: Page) { export async function waitForQuizRefresh(page: Page): Promise<boolean> {
try { try {
await page.waitForSelector('#rqHeaderCredits', { visible: true, timeout: 5000 }) await page.waitForSelector('#rqHeaderCredits', { visible: true, timeout: 5000 })
await wait(2000) await wait(2000)
@@ -212,7 +212,7 @@ export async function waitForQuizRefresh(page: Page) {
} }
} }
export async function checkQuizCompleted(page: Page) { export async function checkQuizCompleted(page: Page): Promise<boolean> {
try { try {
await page.waitForSelector('#quizCompleteContainer', { visible: true, timeout: 1000 }) await page.waitForSelector('#quizCompleteContainer', { visible: true, timeout: 1000 })
await wait(2000) await wait(2000)
@@ -223,14 +223,14 @@ export async function checkQuizCompleted(page: Page) {
} }
} }
export async function refreshCheerio(page: Page) { export async function refreshCheerio(page: Page): Promise<CheerioAPI> {
const html = await page.content() const html = await page.content()
const $ = load(html) const $ = load(html)
return $ return $
} }
export async function getPunchCardActivity(page: Page, activity: PromotionalItem | MorePromotion) { export async function getPunchCardActivity(page: Page, activity: PromotionalItem | MorePromotion): Promise<string> {
let selector = '' let selector = ''
try { try {
const html = await page.content() const html = await page.content()

View File

@@ -62,7 +62,7 @@ export async function tryDismissBingCookieBanner(page: Page): Promise<void> {
} }
} }
export async function getLatestTab(page: Page) { export async function getLatestTab(page: Page): Promise<Page> {
try { try {
await wait(500) await wait(500)

View File

@@ -2,7 +2,7 @@
"baseURL": "https://rewards.bing.com", "baseURL": "https://rewards.bing.com",
"sessionPath": "sessions", "sessionPath": "sessions",
"headless": false, "headless": false,
"runOnZeroPoints": false, "runOnZeroPoints": true,
"clusters": 1, "clusters": 1,
"workers": { "workers": {
"doDailySet": true, "doDailySet": true,
@@ -12,6 +12,7 @@
"doMobileSearch": true "doMobileSearch": true
}, },
"searchSettings": { "searchSettings": {
"useGeoLocaleQueries": false,
"scrollRandomResults": true, "scrollRandomResults": true,
"clickRandomResults": true "clickRandomResults": true
}, },

View File

@@ -157,13 +157,14 @@ async function solveActivities(page: Page, activities: PromotionalItem[] | MoreP
await doUrlReward(activityPage) await doUrlReward(activityPage)
break break
// Misc // Misc, Usually UrlReward Type
default: default:
log('ACTIVITY', `Found activity type: "Misc" title: "${activity.title}"`) log('ACTIVITY', `Found activity type: "Misc" title: "${activity.title}"`)
await doUrlReward(activityPage) await doUrlReward(activityPage)
break break
} }
// Cooldown
await wait(1500) await wait(1500)
} catch (error) { } catch (error) {
log('ACTIVITY', 'An error occurred:' + error, 'error') log('ACTIVITY', 'An error occurred:' + error, 'error')

View File

@@ -13,10 +13,6 @@ import { GoogleTrends } from '../../interface/GoogleDailyTrends'
import { GoogleSearch } from '../../interface/Search' import { GoogleSearch } from '../../interface/Search'
export async function doSearch(page: Page, data: DashboardData, mobile: boolean) { export async function doSearch(page: Page, data: DashboardData, mobile: boolean) {
const locale = await page.evaluate(() => {
return navigator.language
})
log('SEARCH-BING', 'Starting bing searches') log('SEARCH-BING', 'Starting bing searches')
const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches
@@ -33,7 +29,7 @@ export async function doSearch(page: Page, data: DashboardData, mobile: boolean)
} }
// Generate search queries // Generate search queries
let googleSearchQueries = await getGoogleTrends(locale, missingPoints) as GoogleSearch[] let googleSearchQueries = await getGoogleTrends(data.userProfile.attributes.country, missingPoints)
googleSearchQueries = shuffleArray(googleSearchQueries) googleSearchQueries = shuffleArray(googleSearchQueries)
// Deduplicate the search terms // Deduplicate the search terms
@@ -185,10 +181,14 @@ async function bingSearch(page: Page, searchPage: Page, query: string) {
return await getSearchPoints(page) return await getSearchPoints(page)
} }
async function getGoogleTrends(locale: string, queryCount: number): Promise<GoogleSearch[]> { async function getGoogleTrends(geoLocale: string, queryCount: number): Promise<GoogleSearch[]> {
const queryTerms: GoogleSearch[] = [] const queryTerms: GoogleSearch[] = []
let i = 0 let i = 0
geoLocale = (searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toUpperCase() : 'US'
log('SEARCH-GOOGLE-TRENDS', `Generating search queries, can take a while! | GeoLocale: ${geoLocale}`)
while (queryCount > queryTerms.length) { while (queryCount > queryTerms.length) {
i += 1 i += 1
const date = new Date() const date = new Date()
@@ -197,7 +197,7 @@ async function getGoogleTrends(locale: string, queryCount: number): Promise<Goog
try { try {
const request = { const request = {
url: `https://trends.google.com/trends/api/dailytrends?geo=US&hl=en&ed=${formattedDate}&ns=15`, url: `https://trends.google.com/trends/api/dailytrends?geo=${geoLocale}&hl=en&ed=${formattedDate}&ns=15`,
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@@ -283,13 +283,11 @@ async function clickRandomLink(page: Page) {
// Check if the URL is different from the original one, don't loop more than 5 times. // Check if the URL is different from the original one, don't loop more than 5 times.
let i = 0 let i = 0
while (lastTabURL.href !== searchListingURL.href && i < 5) { while (lastTabURL.href !== searchListingURL.href && i < 5) {
// If hostname is still bing, (Bing images/news etc) // If hostname is still bing, (Bing images/news etc)
if (lastTabURL.hostname == searchListingURL.hostname) { if (lastTabURL.hostname == searchListingURL.hostname) {
await lastTab.goBack() await lastTab.goBack()
lastTab = await getLatestTab(page) // Get last opened tab lastTab = await getLatestTab(page) // Get last opened tab
lastTabURL = new URL(lastTab.url())
// If "goBack" didn't return to search listing (due to redirects) // If "goBack" didn't return to search listing (due to redirects)
if (lastTabURL.hostname !== searchListingURL.hostname) { if (lastTabURL.hostname !== searchListingURL.hostname) {

View File

@@ -2,9 +2,9 @@ import { Page } from 'puppeteer'
import { wait } from '../../util/Utils' import { wait } from '../../util/Utils'
import { log } from '../../util/Logger' import { log } from '../../util/Logger'
import { getQuizData } from '../../browser/BrowserFunc'
export async function doThisOrThat(page: Page) { export async function doThisOrThat(page: Page) {
return // Todo
log('THIS-OR-THAT', 'Trying to complete ThisOrThat') log('THIS-OR-THAT', 'Trying to complete ThisOrThat')
try { try {
@@ -22,6 +22,8 @@ export async function doThisOrThat(page: Page) {
await wait(2000) await wait(2000)
// Solving // Solving
const quizData = await getQuizData(page)
quizData // correctAnswer property is always null?
log('THIS-OR-THAT', 'Completed the ThisOrthat successfully') log('THIS-OR-THAT', 'Completed the ThisOrthat successfully')
} catch (error) { } catch (error) {

View File

@@ -2,6 +2,7 @@ import cluster from 'cluster'
import Browser from './browser/Browser' import Browser from './browser/Browser'
import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc' import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc'
import { log } from './util/Logger' import { log } from './util/Logger'
import { loadAccounts } from './util/Account' import { loadAccounts } from './util/Account'
import { chunkArray } from './util/Utils' import { chunkArray } from './util/Utils'
@@ -102,8 +103,18 @@ class MicrosoftRewardsBot {
// Desktop // Desktop
async Desktop(account: Account) { async Desktop(account: Account) {
const browser = await this.browserFactory.createBrowser(account.email, false) const browser = await this.browserFactory.createBrowser(account.email, account.proxy, false)
const page = await browser.newPage() const page = await browser.newPage()
let pages = await browser.pages()
// If for some reason the browser initializes with more than 2 pages, close these
while (pages.length > 2) {
await pages[0]?.close()
pages = await browser.pages()
}
// Log into proxy
await page.authenticate({ username: account.proxy.username, password: account.proxy.password })
log('MAIN', 'Starting DESKTOP browser') log('MAIN', 'Starting DESKTOP browser')
@@ -156,8 +167,17 @@ class MicrosoftRewardsBot {
// Mobile // Mobile
async Mobile(account: Account) { async Mobile(account: Account) {
const browser = await this.browserFactory.createBrowser(account.email, true) const browser = await this.browserFactory.createBrowser(account.email, account.proxy, true)
const page = await browser.newPage() const page = await browser.newPage()
let pages = await browser.pages()
// If for some reason the browser initializes with more than 2 pages, close these
while (pages.length > 2) {
await pages[0]?.close()
pages = await browser.pages()
}
// Log into proxy
await page.authenticate({ username: account.proxy.username, password: account.proxy.password })
log('MAIN', 'Starting MOBILE browser') log('MAIN', 'Starting MOBILE browser')

View File

@@ -1,4 +1,12 @@
export interface Account { export interface Account {
email: string; email: string;
password: string; password: string;
proxy: AccountProxy;
}
export interface AccountProxy {
url: string;
port: number;
password: string;
username: string;
} }

View File

@@ -1,7 +1,9 @@
import * as fs from 'fs' import * as fs from 'fs'
import path from 'path' import path from 'path'
export async function loadAccounts() { import { Account } from '../interface/Account'
export async function loadAccounts(): Promise<Account[]> {
try { try {
let file = 'accounts.json' let file = 'accounts.json'

View File

@@ -81,7 +81,7 @@ export async function getEdgeVersions() {
export function getSystemComponents(mobile: boolean): string { export function getSystemComponents(mobile: boolean): string {
const osId: string = mobile ? 'Linux' : 'Windows NT 10.0' const osId: string = mobile ? 'Linux' : 'Windows NT 10.0'
const uaPlatform: string = mobile ? 'Android 10' : 'Win64; x64' const uaPlatform: string = mobile ? 'Android 13' : 'Win64; x64'
if (mobile) { if (mobile) {
return `${uaPlatform}; ${osId}; K` return `${uaPlatform}; ${osId}; K`
@@ -90,7 +90,6 @@ export function getSystemComponents(mobile: boolean): string {
return `${uaPlatform}; ${osId}` return `${uaPlatform}; ${osId}`
} }
export async function getAppComponents(mobile: boolean) { export async function getAppComponents(mobile: boolean) {
const versions = await getEdgeVersions() const versions = await getEdgeVersions()
const edgeVersion = mobile ? versions.android : versions.windows as string const edgeVersion = mobile ? versions.android : versions.windows as string

View File

@@ -4,7 +4,7 @@ export async function wait(ms: number): Promise<void> {
}) })
} }
export function getFormattedDate(ms = Date.now()) { export function getFormattedDate(ms = Date.now()): string {
const today = new Date(ms) const today = new Date(ms)
const month = String(today.getMonth() + 1).padStart(2, '0') // January is 0 const month = String(today.getMonth() + 1).padStart(2, '0') // January is 0
const day = String(today.getDate()).padStart(2, '0') const day = String(today.getDate()).padStart(2, '0')
@@ -21,7 +21,7 @@ export function shuffleArray<T>(array: T[]): T[] {
return shuffledArray return shuffledArray
} }
export function randomNumber(min: number, max: number) { export function randomNumber(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min return Math.floor(Math.random() * (max - min + 1)) + min
} }