6 Commits

Author SHA1 Message Date
TheNetsky
ef6ad569ff 1.4.6 2024-05-09 10:04:11 +02:00
Netsky
da9ba91c5c Merge pull request #98 from jordyamc/main
Bug when detecting earnable points from more promotions
2024-05-07 15:12:10 +02:00
Jordy Mendoza
deb2d58b1b - Fix bug where the script was using x.activityType instead of x.promotionType this was causing that the script wasn't detecting earnable points from more promotions 2024-04-21 23:50:33 -06:00
Netsky
66a82c2584 Merge pull request #89 from mgrimace/docker
Clarify docker instructions, add basic compose.yaml support
2024-04-04 16:43:57 +02:00
mgrimace
8a022d5983 Clarify docker instructions, add basic compose.yaml support
This should help folks get started with docker and hopefully make it easier for testing
2024-04-03 17:59:16 -04:00
TheNetsky
64048e35d7 1.4.5
- Fix login not working
- Sessions are now saved after logging in
- Check if promotions, and searches are available before counting total point amount
2024-03-01 12:19:48 +01:00
11 changed files with 67 additions and 27 deletions

3
.gitignore vendored
View File

@@ -5,4 +5,5 @@ package-lock.json
accounts.json accounts.json
notes notes
accounts.dev.json accounts.dev.json
accounts.main.json accounts.main.json
.DS_Store

View File

@@ -17,9 +17,26 @@ Under development, however mainly for personal use!
## Docker (Experimental) ## ## Docker (Experimental) ##
1. Download the source code 1. Download the source code
2. Make changes to your `accounts.json` and/or `config.json` 2. Make changes to your `accounts.json`
3. Run `docker build -t microsoft-rewards-script-docker .` 3. Make sure to change `"headless": false` to `"headless": true` in your `config.json`
- Docker container has to be recreated for any changes regarding the `config.json` and/or `accounts.json`! 4. Note, the container has to be recreated for any changes regarding the `config.json` and/or `accounts.json`!
### Option 1: build and run with docker run
1. Run `docker build -t microsoft-rewards-script-docker .` to build the container
2. Run the container with `docker run --name netsky -d microsoft-rewards-script-docker` or, omit the detached flag `-d` to view the script output in your terminal.
3. Optionally, change the name of the container by changing `--name netsky` to your preferred container name
4. The container will exit after completing the script, run it again using `docker start netsky`
5. If you are running the container `-d` detached, you can view logs with `docker logs netsky`
### Option 2: use docker compose
1. A basic docker compose.yaml has been provided, which can be run with `docker compose up -d` or, omit the detached flag `-d` to view the script output in your terminal.
2. The container will exit after completing the script, run it again using `docker start netsky`
3. If you are running the container `-d` detached, you can view logs with `docker logs netsky`
## Config ## ## Config ##
| Setting | Description | Default | | Setting | Description | Default |
@@ -35,6 +52,7 @@ Under development, however mainly for personal use!
| workers.doPunchCards | Complete punchcards | `true` | | workers.doPunchCards | Complete punchcards | `true` |
| workers.doDesktopSearch | Complete daily desktop searches | `true` | | workers.doDesktopSearch | Complete daily desktop searches | `true` |
| workers.doMobileSearch | Complete daily mobile searches | `true` | | workers.doMobileSearch | Complete daily mobile searches | `true` |
| globalTimeout | The length before the action gets timeout | 30 seconds (30.000 miliseconds) |
| searchSettings.useGeoLocaleQueries | Generate search queries based on your geo-location | `false` (Uses EN-US generated queries) | | searchSettings.useGeoLocaleQueries | Generate search queries based on your geo-location | `false` (Uses EN-US generated queries) |
| scrollRandomResults | Scroll randomly in search results | `true` | | scrollRandomResults | Scroll randomly in search results | `true` |
| searchSettings.clickRandomResults | Visit random website from search result| `true` | | searchSettings.clickRandomResults | Visit random website from search result| `true` |

8
compose.yaml Normal file
View File

@@ -0,0 +1,8 @@
services:
microsoft-rewards-script:
build: .
container_name: netsky
environment:
- NODE_ENV=production
volumes:
- .:/usr/src/microsoft-rewards-script

View File

@@ -1,6 +1,6 @@
{ {
"name": "microsoft-rewards-script", "name": "microsoft-rewards-script",
"version": "1.4.4", "version": "1.4.6",
"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": {
@@ -34,9 +34,9 @@
"dependencies": { "dependencies": {
"axios": "^1.6.7", "axios": "^1.6.7",
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"fingerprint-generator": "^2.1.48", "fingerprint-generator": "^2.1.49",
"fingerprint-injector": "^2.1.48", "fingerprint-injector": "^2.1.49",
"playwright": "^1.41.2", "playwright": "^1.42.0",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
} }
} }

View File

@@ -43,6 +43,9 @@ class Browser {
const context = await newInjectedContext(browser, { fingerprint: fingerpint }) const context = await newInjectedContext(browser, { fingerprint: fingerpint })
// Set timeout to preferred amount
context.setDefaultTimeout(this.bot.config?.globalTimeout ?? 30_000)
await context.addCookies(sessionData.cookies) await context.addCookies(sessionData.cookies)
if (this.bot.config.saveFingerprint) { if (this.bot.config.saveFingerprint) {

View File

@@ -142,7 +142,9 @@ export default class BrowserFunc {
let totalEarnablePoints = 0 let totalEarnablePoints = 0
// Desktop Search Points // Desktop Search Points
data.userStatus.counters.pcSearch.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress)) if (data.userStatus.counters.pcSearch?.length) {
data.userStatus.counters.pcSearch.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress))
}
// Mobile Search Points // Mobile Search Points
if (data.userStatus.counters.mobileSearch?.length) { if (data.userStatus.counters.mobileSearch?.length) {
@@ -153,12 +155,14 @@ export default class BrowserFunc {
data.dailySetPromotions[this.bot.utils.getFormattedDate()]?.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress)) data.dailySetPromotions[this.bot.utils.getFormattedDate()]?.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress))
// More Promotions // More Promotions
data.morePromotions.forEach(x => { if (data.morePromotions?.length) {
// Only count points from supported activities data.morePromotions.forEach(x => {
if (['quiz', 'urlreward'].includes(x.activityType)) { // Only count points from supported activities
totalEarnablePoints += (x.pointProgressMax - x.pointProgress) if (['quiz', 'urlreward'].includes(x.promotionType)) {
} totalEarnablePoints += (x.pointProgressMax - x.pointProgress)
}) }
})
}
return totalEarnablePoints return totalEarnablePoints
} catch (error) { } catch (error) {

View File

@@ -12,6 +12,7 @@
"doDesktopSearch": true, "doDesktopSearch": true,
"doMobileSearch": true "doMobileSearch": true
}, },
"globalTimeout": 30000,
"searchSettings": { "searchSettings": {
"useGeoLocaleQueries": false, "useGeoLocaleQueries": false,
"scrollRandomResults": true, "scrollRandomResults": true,
@@ -26,4 +27,4 @@
"enabled": false, "enabled": false,
"url": "" "url": ""
} }
} }

View File

@@ -2,6 +2,7 @@ import { Page } from 'playwright'
import readline from 'readline' import readline from 'readline'
import { MicrosoftRewardsBot } from '../index' import { MicrosoftRewardsBot } from '../index'
import { saveSessionData } from '../util/Load'
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
@@ -26,14 +27,12 @@ export class Login {
if (!isLoggedIn) { if (!isLoggedIn) {
// Check if account is locked // Check if account is locked
const isLocked = await page.waitForSelector('.serviceAbusePageContainer', { state: 'visible', timeout: 10_000 }).then(() => true).catch(() => false) const isLocked = await page.waitForSelector('.serviceAbusePageContainer', { state: 'visible', timeout: 1000 }).then(() => true).catch(() => false)
if (isLocked) { if (isLocked) {
this.bot.log('LOGIN', 'This account has been locked!', 'error') this.bot.log('LOGIN', 'This account has been locked!', 'error')
throw new Error('Account has been locked!') throw new Error('Account has been locked!')
} }
await page.waitForSelector('#loginHeader', { state: 'visible', timeout: 10_000 })
await this.execLogin(page, email, password) await this.execLogin(page, email, password)
this.bot.log('LOGIN', 'Logged into Microsoft successfully') this.bot.log('LOGIN', 'Logged into Microsoft successfully')
} else { } else {
@@ -43,6 +42,9 @@ export class Login {
// Check if logged in to bing // Check if logged in to bing
await this.checkBingLogin(page) await this.checkBingLogin(page)
// Save session
await saveSessionData(this.bot.config.sessionPath, page.context(), email, this.bot.isMobile)
// We're done logging in // We're done logging in
this.bot.log('LOGIN', 'Logged in successfully') this.bot.log('LOGIN', 'Logged in successfully')

View File

@@ -178,11 +178,11 @@ export class MicrosoftRewardsBot {
} }
// Save cookies // Save cookies
const cookies = await browser.cookies() await saveSessionData(this.config.sessionPath, browser, account.email, this.isMobile)
await saveSessionData(this.config.sessionPath, account.email, this.isMobile, cookies)
// Close desktop browser // Close desktop browser
return await this.closeBrowser(browser, account.email) await this.closeBrowser(browser, account.email)
return
} }
// Mobile // Mobile
@@ -242,13 +242,13 @@ export class MicrosoftRewardsBot {
log('MAIN-POINTS', `The script collected ${this.collectedPoints} points today`) log('MAIN-POINTS', `The script collected ${this.collectedPoints} points today`)
// Close mobile browser // Close mobile browser
return await this.closeBrowser(browser, account.email) await this.closeBrowser(browser, account.email)
return
} }
private async closeBrowser(browser: BrowserContext, email: string) { private async closeBrowser(browser: BrowserContext, email: string) {
// Save cookies // Save cookies
const cookies = await browser.cookies() await saveSessionData(this.config.sessionPath, browser, email, this.isMobile)
await saveSessionData(this.config.sessionPath, email, this.isMobile, cookies)
// Close browser // Close browser
await browser.close() await browser.close()

View File

@@ -5,6 +5,7 @@ export interface Config {
runOnZeroPoints: boolean; runOnZeroPoints: boolean;
clusters: number; clusters: number;
workers: Workers; workers: Workers;
globalTimeout: number;
searchSettings: SearchSettings; searchSettings: SearchSettings;
webhook: Webhook; webhook: Webhook;
saveFingerprint: boolean; saveFingerprint: boolean;

View File

@@ -1,4 +1,4 @@
import { Cookie } from 'playwright' import { BrowserContext, Cookie } from 'playwright'
import { BrowserFingerprintWithHeaders } from 'fingerprint-generator' import { BrowserFingerprintWithHeaders } from 'fingerprint-generator'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
@@ -67,8 +67,10 @@ export async function loadSessionData(sessionPath: string, email: string, isMobi
} }
} }
export async function saveSessionData(sessionPath: string, email: string, isMobile: boolean, cookies: Cookie[]): Promise<string> { export async function saveSessionData(sessionPath: string, browser: BrowserContext, email: string, isMobile: boolean): Promise<string> {
try { try {
const cookies = await browser.cookies()
// Fetch path // Fetch path
const sessionDir = path.join(__dirname, '../browser/', sessionPath, email) const sessionDir = path.join(__dirname, '../browser/', sessionPath, email)