Compare commits
No commits in common. "b14530537af213448988f53337814f73e2f6e24f" and "1ddcf1d8afbe1ed9daf41670352b2b5a023fc3e0" have entirely different histories.
b14530537a
...
1ddcf1d8af
4 changed files with 1 additions and 225 deletions
|
|
@ -1,18 +1,5 @@
|
||||||
# start with `docker compose up`
|
# start with `docker compose up`
|
||||||
services:
|
services:
|
||||||
flaresolverr:
|
|
||||||
container_name: flaresolverr
|
|
||||||
image: flaresolverr/flaresolverr:latest
|
|
||||||
ports:
|
|
||||||
- "8191:8191"
|
|
||||||
environment:
|
|
||||||
- LOG_LEVEL=info
|
|
||||||
- LOG_HTML=false
|
|
||||||
- CAPTCHA_SOLVER=none
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- fgc-network
|
|
||||||
|
|
||||||
free-games-claimer:
|
free-games-claimer:
|
||||||
container_name: fgc # is printed in front of every output line
|
container_name: fgc # is printed in front of every output line
|
||||||
image: ghcr.io/vogler/free-games-claimer # otherwise image name will be free-games-claimer-free-games-claimer
|
image: ghcr.io/vogler/free-games-claimer # otherwise image name will be free-games-claimer-free-games-claimer
|
||||||
|
|
@ -28,15 +15,6 @@ services:
|
||||||
# - EMAIL=foo@bar.org
|
# - EMAIL=foo@bar.org
|
||||||
# - NOTIFY='tgram://...'
|
# - NOTIFY='tgram://...'
|
||||||
- EG_MODE=new
|
- EG_MODE=new
|
||||||
- FLARESOLVERR_URL=http://flaresolverr:8191/v1
|
|
||||||
networks:
|
|
||||||
- fgc-network
|
|
||||||
depends_on:
|
|
||||||
- flaresolverr
|
|
||||||
|
|
||||||
networks:
|
|
||||||
fgc-network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
fgc:
|
fgc:
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import { cfg } from './src/config.js';
|
||||||
import { EPIC_CLIENT_ID, GRAPHQL_ENDPOINT, FREE_GAMES_PROMOTIONS_ENDPOINT, STORE_HOMEPAGE_EN, EPIC_PURCHASE_ENDPOINT, ID_LOGIN_ENDPOINT } from './src/constants.js';
|
import { EPIC_CLIENT_ID, GRAPHQL_ENDPOINT, FREE_GAMES_PROMOTIONS_ENDPOINT, STORE_HOMEPAGE_EN, EPIC_PURCHASE_ENDPOINT, ID_LOGIN_ENDPOINT } from './src/constants.js';
|
||||||
import { setPuppeteerCookies } from './src/cookie.js';
|
import { setPuppeteerCookies } from './src/cookie.js';
|
||||||
import { getAccountAuth, setAccountAuth } from './src/device-auths.js';
|
import { getAccountAuth, setAccountAuth } from './src/device-auths.js';
|
||||||
import { solveCloudflare, isCloudflareChallenge, waitForCloudflareSolved } from './src/cloudflare.js';
|
|
||||||
|
|
||||||
// Fetch Free Games from API using page.evaluate (browser context)
|
// Fetch Free Games from API using page.evaluate (browser context)
|
||||||
const fetchFreeGamesAPI = async page => {
|
const fetchFreeGamesAPI = async page => {
|
||||||
|
|
@ -170,63 +169,6 @@ const ensureLoggedIn = async (page, context) => {
|
||||||
return await cfFrame.count() > 0 || await cfText.count() > 0;
|
return await cfFrame.count() > 0 || await cfText.count() > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const solveCloudflareChallenge = async () => {
|
|
||||||
try {
|
|
||||||
console.log('🔍 Detecting Cloudflare challenge...');
|
|
||||||
|
|
||||||
// Check if FlareSolverr is available
|
|
||||||
const flaresolverrUrl = cfg.flaresolverr_url || 'http://localhost:8191/v1';
|
|
||||||
const healthResponse = await fetch(`${flaresolverrUrl}/health`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!healthResponse.ok) {
|
|
||||||
console.warn('⚠️ FlareSolverr not available at', flaresolverrUrl);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send request to FlareSolverr
|
|
||||||
const response = await fetch(`${flaresolverrUrl}/request`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
cmd: 'request.get',
|
|
||||||
url: URL_CLAIM,
|
|
||||||
maxTimeout: 60000,
|
|
||||||
session: 'epic-games',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data.status !== 'ok') {
|
|
||||||
console.warn('FlareSolverr failed:', data.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const solution = data.solution;
|
|
||||||
|
|
||||||
// Apply cookies to the browser context
|
|
||||||
const cookies = solution.cookies.map(cookie => ({
|
|
||||||
name: cookie.name,
|
|
||||||
value: cookie.value,
|
|
||||||
domain: cookie.domain,
|
|
||||||
path: cookie.path || '/',
|
|
||||||
secure: cookie.secure,
|
|
||||||
httpOnly: cookie.httpOnly,
|
|
||||||
}));
|
|
||||||
|
|
||||||
await context.addCookies(cookies);
|
|
||||||
|
|
||||||
console.log('✅ Cloudflare challenge solved by FlareSolverr');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('FlareSolverr error:', error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let loginAttempts = 0;
|
let loginAttempts = 0;
|
||||||
const MAX_LOGIN_ATTEMPTS = 3;
|
const MAX_LOGIN_ATTEMPTS = 3;
|
||||||
|
|
||||||
|
|
@ -239,12 +181,7 @@ const ensureLoggedIn = async (page, context) => {
|
||||||
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
|
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
if (await isChallenge()) {
|
if (await isChallenge()) {
|
||||||
console.warn('Cloudflare challenge detected. Attempting to solve with FlareSolverr...');
|
console.warn('Cloudflare challenge detected. Solve the captcha in the browser (no automation).');
|
||||||
const solved = await solveCloudflareChallenge();
|
|
||||||
if (solved) {
|
|
||||||
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await notify('epic-games (new): Cloudflare challenge, please solve manually in browser.');
|
await notify('epic-games (new): Cloudflare challenge, please solve manually in browser.');
|
||||||
await page.waitForTimeout(cfg.login_timeout);
|
await page.waitForTimeout(cfg.login_timeout);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
||||||
import { cfg } from './config.js';
|
|
||||||
|
|
||||||
const FLARESOLVERR_URL = process.env.FLARESOLVERR_URL || 'http://localhost:8191/v1';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if FlareSolverr is available
|
|
||||||
*/
|
|
||||||
export const checkFlareSolverr = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${FLARESOLVERR_URL}/health`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.ok;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solve Cloudflare challenge using FlareSolverr
|
|
||||||
* @param {Object} page - Playwright page object
|
|
||||||
* @param {string} url - The URL to visit
|
|
||||||
* @returns {Promise<Object|null>} - Solution object with cookies and user agent
|
|
||||||
*/
|
|
||||||
export const solveCloudflare = async (page, url) => {
|
|
||||||
try {
|
|
||||||
console.log('🔍 Detecting Cloudflare challenge...');
|
|
||||||
|
|
||||||
// Check if FlareSolverr is available
|
|
||||||
if (!await checkFlareSolverr()) {
|
|
||||||
console.warn('⚠️ FlareSolverr not available at', FLARESOLVERR_URL);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send request to FlareSolverr
|
|
||||||
const response = await fetch(`${FLARESOLVERR_URL}/request`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
cmd: 'request.get',
|
|
||||||
url: url,
|
|
||||||
maxTimeout: 60000,
|
|
||||||
session: 'epic-games',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data.status !== 'ok') {
|
|
||||||
console.warn('FlareSolverr failed:', data.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const solution = data.solution;
|
|
||||||
|
|
||||||
// Apply cookies to the browser context
|
|
||||||
const cookies = solution.cookies.map(cookie => ({
|
|
||||||
name: cookie.name,
|
|
||||||
value: cookie.value,
|
|
||||||
domain: cookie.domain,
|
|
||||||
path: cookie.path || '/',
|
|
||||||
secure: cookie.secure,
|
|
||||||
httpOnly: cookie.httpOnly,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Get the browser context from the page
|
|
||||||
const context = page.context();
|
|
||||||
await context.addCookies(cookies);
|
|
||||||
|
|
||||||
console.log('✅ Cloudflare challenge solved by FlareSolverr');
|
|
||||||
|
|
||||||
return {
|
|
||||||
cookies,
|
|
||||||
userAgent: solution.userAgent,
|
|
||||||
html: solution.html,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('FlareSolverr error:', error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if Cloudflare challenge is present on the page
|
|
||||||
* @param {Object} page - Playwright page object
|
|
||||||
* @returns {Promise<boolean>} - True if Cloudflare challenge is detected
|
|
||||||
*/
|
|
||||||
export const isCloudflareChallenge = async page => {
|
|
||||||
try {
|
|
||||||
// Check for Cloudflare iframe
|
|
||||||
const cfFrame = page.locator('iframe[title*="Cloudflare"], iframe[src*="challenges"]');
|
|
||||||
if (await cfFrame.count() > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for Cloudflare text
|
|
||||||
const cfText = page.locator('text=Verify you are human, text=Checking your browser');
|
|
||||||
if (await cfText.count() > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for specific Cloudflare URLs
|
|
||||||
const url = page.url();
|
|
||||||
if (url.includes('cloudflare') || url.includes('challenges')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for Cloudflare challenge to be solved
|
|
||||||
* @param {Object} page - Playwright page object
|
|
||||||
* @param {number} timeout - Timeout in milliseconds
|
|
||||||
* @returns {Promise<boolean>} - True if challenge is solved
|
|
||||||
*/
|
|
||||||
export const waitForCloudflareSolved = async (page, timeout = 60000) => {
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
while (Date.now() - startTime < timeout) {
|
|
||||||
if (!await isCloudflareChallenge(page)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
@ -35,8 +35,6 @@ export const cfg = {
|
||||||
eg_password: process.env.EG_PASSWORD || process.env.PASSWORD,
|
eg_password: process.env.EG_PASSWORD || process.env.PASSWORD,
|
||||||
eg_otpkey: process.env.EG_OTPKEY,
|
eg_otpkey: process.env.EG_OTPKEY,
|
||||||
eg_parentalpin: process.env.EG_PARENTALPIN,
|
eg_parentalpin: process.env.EG_PARENTALPIN,
|
||||||
// Cloudflare bypass
|
|
||||||
flaresolverr_url: process.env.FLARESOLVERR_URL || 'http://localhost:8191/v1',
|
|
||||||
// auth prime-gaming
|
// auth prime-gaming
|
||||||
pg_email: process.env.PG_EMAIL || process.env.EMAIL,
|
pg_email: process.env.PG_EMAIL || process.env.EMAIL,
|
||||||
pg_password: process.env.PG_PASSWORD || process.env.PASSWORD,
|
pg_password: process.env.PG_PASSWORD || process.env.PASSWORD,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue