fix: Multiple bug fixes and code cleanup
- package.json: Add missing @eslint/js and globals devDependencies - docker-entrypoint.sh: Fix X11 lock file name (.X1-lock → .X11-lock) - epic-claimer-new.js: Use imported solveCloudflare/isCloudflareChallenge instead of duplicate implementations - src/cloudflare.js: Fix solveCloudflare to use cfg.flaresolverr_url, remove unused imports - epic-games.js: Remove unused code (getFreeGamesFromGraphQL, exchangeTokenForCookies, FREE_GAMES_QUERY, deviceAuthLoginSuccess variable) - Run eslint --fix to clean up trailing spaces
This commit is contained in:
parent
b14530537a
commit
48c861b3de
6 changed files with 166 additions and 174 deletions
|
|
@ -59,7 +59,7 @@ export BROWSER_DIR
|
||||||
# Remove X server display lock, fix for `docker compose up` which reuses container which made it fail after initial run, https://github.com/vogler/free-games-claimer/issues/31
|
# Remove X server display lock, fix for `docker compose up` which reuses container which made it fail after initial run, https://github.com/vogler/free-games-claimer/issues/31
|
||||||
# echo $DISPLAY
|
# echo $DISPLAY
|
||||||
# ls -l /tmp/.X11-unix/
|
# ls -l /tmp/.X11-unix/
|
||||||
rm -f /tmp/.X1-lock
|
rm -f /tmp/.X11-lock
|
||||||
|
|
||||||
# Ensure X11 socket dir exists with sane ownership/permissions.
|
# Ensure X11 socket dir exists with sane ownership/permissions.
|
||||||
mkdir -p /tmp/.X11-unix
|
mkdir -p /tmp/.X11-unix
|
||||||
|
|
|
||||||
|
|
@ -164,67 +164,12 @@ const ensureLoggedIn = async (page, context) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isChallenge = async () => {
|
// Use imported isCloudflareChallenge and solveCloudflare from src/cloudflare.js
|
||||||
const cfFrame = page.locator('iframe[title*="Cloudflare"], iframe[src*="challenges"]');
|
const isChallenge = async () => await isCloudflareChallenge(page);
|
||||||
const cfText = page.locator('text=Verify you are human');
|
|
||||||
return await cfFrame.count() > 0 || await cfText.count() > 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const solveCloudflareChallenge = async () => {
|
const solveCloudflareChallenge = async () => {
|
||||||
try {
|
const solution = await solveCloudflare(page, URL_CLAIM);
|
||||||
console.log('🔍 Detecting Cloudflare challenge...');
|
return solution !== null;
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
|
||||||
|
|
@ -76,27 +76,6 @@ if (cfg.debug_network) {
|
||||||
const notify_games = [];
|
const notify_games = [];
|
||||||
let user;
|
let user;
|
||||||
|
|
||||||
// GraphQL query for free games
|
|
||||||
const FREE_GAMES_QUERY = {
|
|
||||||
operationName: 'searchStoreQuery',
|
|
||||||
variables: {
|
|
||||||
allowCountries: 'US',
|
|
||||||
category: 'games/edition/base|software/edition/base|editors|bundles/games',
|
|
||||||
count: 1000,
|
|
||||||
country: 'US',
|
|
||||||
sortBy: 'relevancy',
|
|
||||||
sortDir: 'DESC',
|
|
||||||
start: 0,
|
|
||||||
withPrice: true,
|
|
||||||
},
|
|
||||||
extensions: {
|
|
||||||
persistedQuery: {
|
|
||||||
version: 1,
|
|
||||||
sha256Hash: '7d58e12d9dd8cb14c84a3ff18d360bf9f0caa96bf218f2c5fda68ba88d68a437',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate login redirect URL
|
// Generate login redirect URL
|
||||||
const generateLoginRedirect = redirectUrl => {
|
const generateLoginRedirect = redirectUrl => {
|
||||||
const loginRedirectUrl = new URL(ID_LOGIN_ENDPOINT);
|
const loginRedirectUrl = new URL(ID_LOGIN_ENDPOINT);
|
||||||
|
|
@ -115,53 +94,6 @@ const generateCheckoutUrl = offers => {
|
||||||
return generateLoginRedirect(checkoutUrl);
|
return generateLoginRedirect(checkoutUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get free games from GraphQL API (unused - kept for reference)
|
|
||||||
const getFreeGamesFromGraphQL = async () => {
|
|
||||||
const items = [];
|
|
||||||
let start = 0;
|
|
||||||
const pageLimit = 1000;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const response = await page.evaluate(async (query, startOffset) => {
|
|
||||||
const variables = { ...query.variables, start: startOffset };
|
|
||||||
const resp = await fetch(GRAPHQL_ENDPOINT, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
operationName: query.operationName,
|
|
||||||
variables: JSON.stringify(variables),
|
|
||||||
extensions: JSON.stringify(query.extensions),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return await resp.json();
|
|
||||||
}, [FREE_GAMES_QUERY, start]);
|
|
||||||
|
|
||||||
const elements = response.data?.Catalog?.searchStore?.elements;
|
|
||||||
if (!elements) break;
|
|
||||||
|
|
||||||
items.push(...elements);
|
|
||||||
start += pageLimit;
|
|
||||||
} while (items.length < pageLimit);
|
|
||||||
|
|
||||||
// Filter free games
|
|
||||||
const freeGames = items.filter(game => game.price?.totalPrice?.discountPrice === 0);
|
|
||||||
|
|
||||||
// Deduplicate by productSlug
|
|
||||||
const uniqueGames = new Map();
|
|
||||||
for (const game of freeGames) {
|
|
||||||
if (!uniqueGames.has(game.productSlug)) {
|
|
||||||
uniqueGames.set(game.productSlug, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(uniqueGames.values()).map(game => ({
|
|
||||||
offerId: game.id,
|
|
||||||
offerNamespace: game.namespace,
|
|
||||||
productName: game.title,
|
|
||||||
productSlug: game.productSlug || game.urlSlug,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get free games from promotions API (weekly free games)
|
// Get free games from promotions API (weekly free games)
|
||||||
const getFreeGamesFromPromotions = async () => {
|
const getFreeGamesFromPromotions = async () => {
|
||||||
const response = await page.evaluate(async () => {
|
const response = await page.evaluate(async () => {
|
||||||
|
|
@ -237,25 +169,6 @@ const loginWithDeviceAuth = async () => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Exchange token for cookies (alternative method - unused)
|
|
||||||
const exchangeTokenForCookies = async accessToken => {
|
|
||||||
try {
|
|
||||||
const cookies = await page.evaluate(async token => {
|
|
||||||
const resp = await fetch('https://store.epicgames.com/', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return await resp.headers.get('set-cookie');
|
|
||||||
}, accessToken);
|
|
||||||
|
|
||||||
return cookies;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save device auth
|
// Save device auth
|
||||||
const saveDeviceAuth = async (accessToken, refreshToken, expiresAt) => {
|
const saveDeviceAuth = async (accessToken, refreshToken, expiresAt) => {
|
||||||
const deviceAuth = {
|
const deviceAuth = {
|
||||||
|
|
@ -292,8 +205,8 @@ try {
|
||||||
if (cfg.time) console.timeEnd('startup');
|
if (cfg.time) console.timeEnd('startup');
|
||||||
if (cfg.time) console.time('login');
|
if (cfg.time) console.time('login');
|
||||||
|
|
||||||
// Try device auth first (unused - kept for reference)
|
// Try device auth first
|
||||||
const deviceAuthLoginSuccess = await loginWithDeviceAuth();
|
await loginWithDeviceAuth();
|
||||||
|
|
||||||
// If device auth failed, try regular login
|
// If device auth failed, try regular login
|
||||||
while (await page.locator('egs-navigation').getAttribute('isloggedin') != 'true') {
|
while (await page.locator('egs-navigation').getAttribute('isloggedin') != 'true') {
|
||||||
|
|
|
||||||
145
package-lock.json
generated
145
package-lock.json
generated
|
|
@ -18,11 +18,14 @@
|
||||||
"lowdb": "^7.0.1",
|
"lowdb": "^7.0.1",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"playwright-firefox": "^1.52.0",
|
"playwright-firefox": "^1.52.0",
|
||||||
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||||
|
"tough-cookie": "^4.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.26.0",
|
||||||
"@stylistic/eslint-plugin-js": "^4.2.0",
|
"@stylistic/eslint-plugin-js": "^4.2.0",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -124,6 +127,19 @@
|
||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@eslint/eslintrc/node_modules/globals": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.26.0",
|
"version": "9.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
|
||||||
|
|
@ -1583,9 +1599,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "14.0.0",
|
"version": "15.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||||
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -2362,11 +2378,22 @@
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/psl": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/lupomontero"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
|
|
@ -2494,6 +2521,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/range-parser": {
|
"node_modules/range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
|
@ -2520,6 +2553,12 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/resolve-from": {
|
"node_modules/resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
|
@ -2844,6 +2883,30 @@
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tough-cookie": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"psl": "^1.1.33",
|
||||||
|
"punycode": "^2.1.1",
|
||||||
|
"universalify": "^0.2.0",
|
||||||
|
"url-parse": "^1.5.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tough-cookie/node_modules/universalify": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
|
@ -2950,6 +3013,16 @@
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vali-date": {
|
"node_modules/vali-date": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
||||||
|
|
@ -3085,6 +3158,14 @@
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"minimatch": "^3.1.2",
|
"minimatch": "^3.1.2",
|
||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"globals": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@eslint/js": {
|
"@eslint/js": {
|
||||||
|
|
@ -4050,9 +4131,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"version": "14.0.0",
|
"version": "15.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||||
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"gopd": {
|
"gopd": {
|
||||||
|
|
@ -4570,11 +4651,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
},
|
},
|
||||||
|
"psl": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"puppeteer-extra-plugin": {
|
"puppeteer-extra-plugin": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
|
|
@ -4627,6 +4715,11 @@
|
||||||
"side-channel": "^1.1.0"
|
"side-channel": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||||
|
},
|
||||||
"range-parser": {
|
"range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
|
@ -4645,6 +4738,11 @@
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||||
|
},
|
||||||
"resolve-from": {
|
"resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
|
@ -4853,6 +4951,24 @@
|
||||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.33",
|
||||||
|
"punycode": "^2.1.1",
|
||||||
|
"universalify": "^0.2.0",
|
||||||
|
"url-parse": "^1.5.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
|
@ -4913,6 +5029,15 @@
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"requires": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vali-date": {
|
"vali-date": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@
|
||||||
"tough-cookie": "^4.1.4"
|
"tough-cookie": "^4.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.26.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
"@stylistic/eslint-plugin-js": "^4.2.0",
|
"@stylistic/eslint-plugin-js": "^4.2.0",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
||||||
import { cfg } from './config.js';
|
import { cfg } from './config.js';
|
||||||
|
|
||||||
const FLARESOLVERR_URL = process.env.FLARESOLVERR_URL || 'http://localhost:8191/v1';
|
const FLARESOLVERR_URL = process.env.FLARESOLVERR_URL || 'http://localhost:8191/v1';
|
||||||
|
|
@ -29,15 +28,23 @@ export const checkFlareSolverr = async () => {
|
||||||
export const solveCloudflare = async (page, url) => {
|
export const solveCloudflare = async (page, url) => {
|
||||||
try {
|
try {
|
||||||
console.log('🔍 Detecting Cloudflare challenge...');
|
console.log('🔍 Detecting Cloudflare challenge...');
|
||||||
|
|
||||||
// Check if FlareSolverr is available
|
// Check if FlareSolverr is available
|
||||||
if (!await checkFlareSolverr()) {
|
const flaresolverrUrl = cfg.flaresolverr_url || 'http://localhost:8191/v1';
|
||||||
console.warn('⚠️ FlareSolverr not available at', FLARESOLVERR_URL);
|
const healthResponse = await fetch(`${flaresolverrUrl}/health`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!healthResponse.ok) {
|
||||||
|
console.warn('⚠️ FlareSolverr not available at', flaresolverrUrl);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send request to FlareSolverr
|
// Send request to FlareSolverr
|
||||||
const response = await fetch(`${FLARESOLVERR_URL}/request`, {
|
const response = await fetch(`${flaresolverrUrl}/request`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -51,14 +58,14 @@ export const solveCloudflare = async (page, url) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.status !== 'ok') {
|
if (data.status !== 'ok') {
|
||||||
console.warn('FlareSolverr failed:', data.message);
|
console.warn('FlareSolverr failed:', data.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const solution = data.solution;
|
const solution = data.solution;
|
||||||
|
|
||||||
// Apply cookies to the browser context
|
// Apply cookies to the browser context
|
||||||
const cookies = solution.cookies.map(cookie => ({
|
const cookies = solution.cookies.map(cookie => ({
|
||||||
name: cookie.name,
|
name: cookie.name,
|
||||||
|
|
@ -68,13 +75,13 @@ export const solveCloudflare = async (page, url) => {
|
||||||
secure: cookie.secure,
|
secure: cookie.secure,
|
||||||
httpOnly: cookie.httpOnly,
|
httpOnly: cookie.httpOnly,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Get the browser context from the page
|
// Get the browser context from the page
|
||||||
const context = page.context();
|
const context = page.context();
|
||||||
await context.addCookies(cookies);
|
await context.addCookies(cookies);
|
||||||
|
|
||||||
console.log('✅ Cloudflare challenge solved by FlareSolverr');
|
console.log('✅ Cloudflare challenge solved by FlareSolverr');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cookies,
|
cookies,
|
||||||
userAgent: solution.userAgent,
|
userAgent: solution.userAgent,
|
||||||
|
|
@ -98,19 +105,19 @@ export const isCloudflareChallenge = async page => {
|
||||||
if (await cfFrame.count() > 0) {
|
if (await cfFrame.count() > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for Cloudflare text
|
// Check for Cloudflare text
|
||||||
const cfText = page.locator('text=Verify you are human, text=Checking your browser');
|
const cfText = page.locator('text=Verify you are human, text=Checking your browser');
|
||||||
if (await cfText.count() > 0) {
|
if (await cfText.count() > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for specific Cloudflare URLs
|
// Check for specific Cloudflare URLs
|
||||||
const url = page.url();
|
const url = page.url();
|
||||||
if (url.includes('cloudflare') || url.includes('challenges')) {
|
if (url.includes('cloudflare') || url.includes('challenges')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -125,13 +132,13 @@ export const isCloudflareChallenge = async page => {
|
||||||
*/
|
*/
|
||||||
export const waitForCloudflareSolved = async (page, timeout = 60000) => {
|
export const waitForCloudflareSolved = async (page, timeout = 60000) => {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
while (Date.now() - startTime < timeout) {
|
while (Date.now() - startTime < timeout) {
|
||||||
if (!await isCloudflareChallenge(page)) {
|
if (!await isCloudflareChallenge(page)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue