Compare commits
10 Commits
65d801b248
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| bb9a31cc78 | |||
| c6b571c68d | |||
| a63e92e91f | |||
| 92cb93b4cb | |||
| 6cf4096064 | |||
| 358fe53be9 | |||
| 50c5927be7 | |||
| cb427a9fa4 | |||
| 2ad2f9993e | |||
| 27b4f4a9cc |
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# Castorsrm
|
# Castorsrm
|
||||||
config.json
|
config.json
|
||||||
|
build/
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
Rice shirt rice money :(
|
Rice shirt rice money :(
|
||||||
|
|
||||||
|
## Notice
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This software is provided by the author as is, without any warranty. Use at your own risk
|
||||||
|
|
||||||
|
The view buffed should be around 1/2 (or 2/5) of the amount of proxy you chosen in the worst case, and possibly near the amount in the best case.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
This project uses Bun for dependency management and Node.js (or Bun on UNIX platforms) for the app execution.
|
This project uses Bun for dependency management and Node.js (or Bun on UNIX platforms) for the app execution.
|
||||||
|
|||||||
@ -8,14 +8,13 @@
|
|||||||
"dev-bun": "bun ./src/index.ts",
|
"dev-bun": "bun ./src/index.ts",
|
||||||
"start": "node ./dist/index.js",
|
"start": "node ./dist/index.js",
|
||||||
"start-bun": "bun ./dist/index.js",
|
"start-bun": "bun ./dist/index.js",
|
||||||
"build": "swc ./src -d dist"
|
"build": "swc ./src -d dist --strip-leading-paths"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swc-node/register": "^1.10.10",
|
"@swc-node/register": "^1.10.10",
|
||||||
"@swc/cli": "^0.6.0",
|
"@swc/cli": "^0.6.0",
|
||||||
"@swc/core": "^1.11.20",
|
"@swc/core": "^1.11.20",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest"
|
||||||
"pino-pretty": "^13.0.0"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
@ -23,6 +22,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"patchright": "^1.51.3",
|
"patchright": "^1.51.3",
|
||||||
"pino": "^9.6.0",
|
"pino": "^9.6.0",
|
||||||
|
"pino-pretty": "^13.0.0",
|
||||||
"playwright": "^1.51.1"
|
"playwright": "^1.51.1"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
class Config {
|
class Config {
|
||||||
playwright: {
|
playwright: {
|
||||||
browser: string;
|
browser: string;
|
||||||
|
change_viewport: boolean;
|
||||||
headless: boolean;
|
headless: boolean;
|
||||||
cdp: string;
|
cdp: string;
|
||||||
url: string;
|
url: string;
|
||||||
} = {
|
} = {
|
||||||
browser: "chromium",
|
browser: "chromium",
|
||||||
|
change_viewport: true,
|
||||||
headless: true,
|
headless: true,
|
||||||
cdp: "ws://127.0.0.1:9222",
|
cdp: "ws://127.0.0.1:9222",
|
||||||
url: "https://www.twitch.tv/",
|
url: "https://www.twitch.tv/",
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
const VERSION = "0.1.0";
|
||||||
const REFLECT4_SERVERS = [
|
const REFLECT4_SERVERS = [
|
||||||
"https://www.blockaway.net",
|
"https://www.blockaway.net",
|
||||||
"https://www.croxyproxy.com",
|
"https://www.croxyproxy.com",
|
||||||
@ -9,4 +10,4 @@ const REFLECT4_SERVERS = [
|
|||||||
"https://proxyium.com"
|
"https://proxyium.com"
|
||||||
];
|
];
|
||||||
|
|
||||||
export { REFLECT4_SERVERS };
|
export { REFLECT4_SERVERS, VERSION };
|
||||||
33
src/index.ts
33
src/index.ts
@ -1,12 +1,11 @@
|
|||||||
import { chromium, type Browser, type LaunchOptions } from "patchright";
|
import { chromium, type Browser, type LaunchOptions } from "patchright";
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import * as reflect4 from "./proxy/reflect4";
|
import * as reflect4 from "./proxy/reflect4.js";
|
||||||
import logger from "./logger";
|
import logger from "./logger.js";
|
||||||
import Config from "./config";
|
import Config from "./config.js";
|
||||||
|
import { VERSION } from "./constants.js";
|
||||||
|
|
||||||
const version = "0.1.0";
|
logger.info(`Castorsrm v${VERSION}`)
|
||||||
|
|
||||||
logger.info(`Castorsrm v${version} - https://github.com/teppyboy/castorsrm`)
|
|
||||||
logger.warn("This software is provided by the author as is, without any warranty. Use at your own risk.");
|
logger.warn("This software is provided by the author as is, without any warranty. Use at your own risk.");
|
||||||
|
|
||||||
let config = new Config();
|
let config = new Config();
|
||||||
@ -16,9 +15,16 @@ if (fs.existsSync("config.json")) {
|
|||||||
config = Config.fromJSON(text);
|
config = Config.fromJSON(text);
|
||||||
} else {
|
} else {
|
||||||
logger.info("No configuration file found. Using the default configuration.");
|
logger.info("No configuration file found. Using the default configuration.");
|
||||||
await fs.promises.writeFile("config.json", config.toJSON());
|
|
||||||
logger.info("Default configuration file 'config.json' created.");
|
logger.info("Default configuration file 'config.json' created.");
|
||||||
}
|
}
|
||||||
|
// Write the new config file in case we updated something.
|
||||||
|
await fs.promises.writeFile("config.json", config.toJSON());
|
||||||
|
|
||||||
|
// Validate configuration
|
||||||
|
if (config.proxy.count < 1) {
|
||||||
|
logger.error("Proxy count must be greater than 0.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
logger.level = process.env.LOG_LEVEL || config.logger.level;
|
logger.level = process.env.LOG_LEVEL || config.logger.level;
|
||||||
logger.info(`Logger level set to '${logger.level}'`);
|
logger.info(`Logger level set to '${logger.level}'`);
|
||||||
@ -59,7 +65,18 @@ if (config.proxy.mode === "reflect4") {
|
|||||||
logger.info(`Spawning ${config.proxy.count} proxies...`);
|
logger.info(`Spawning ${config.proxy.count} proxies...`);
|
||||||
for (let i = 0; i < config.proxy.count; i++) {
|
for (let i = 0; i < config.proxy.count; i++) {
|
||||||
logger.debug(`Spawning proxy ${i + 1}...`);
|
logger.debug(`Spawning proxy ${i + 1}...`);
|
||||||
tasks.push(reflect4.spawn(context, config.playwright.url));
|
tasks.push((async () => {
|
||||||
|
const spawnId = `${i + 1}`;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
await reflect4.spawn(context, config.playwright.url, config.playwright.change_viewport, spawnId);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`[${spawnId}] Error while running: ${e}`);
|
||||||
|
}
|
||||||
|
logger.warn(`[${spawnId}] Restarting in 3 seconds...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 3 * 1000));
|
||||||
|
}
|
||||||
|
})());
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
logger.info("All proxies spawned successfully.");
|
logger.info("All proxies spawned successfully.");
|
||||||
|
|||||||
@ -1,14 +1,26 @@
|
|||||||
import { type BrowserContext, type Locator } from "patchright";
|
import { devices, type BrowserContext, type Locator } from "patchright";
|
||||||
import logger from "../logger";
|
import logger from "../logger.js";
|
||||||
import * as twitch from "../website/twitch";
|
import * as twitch from "../website/twitch.js";
|
||||||
import * as constants from "../constants";
|
import * as constants from "../constants.js";
|
||||||
|
|
||||||
async function spawn(context: BrowserContext, targetUrl: string) {
|
async function spawn(context: BrowserContext, targetUrl: string, changeViewport: boolean = false, spawnId: string = "unknown") {
|
||||||
const spawnId = btoa(Math.random().toString()).substring(4,10);
|
|
||||||
const server = constants.REFLECT4_SERVERS[Math.floor(Math.random() * constants.REFLECT4_SERVERS.length)];
|
const server = constants.REFLECT4_SERVERS[Math.floor(Math.random() * constants.REFLECT4_SERVERS.length)];
|
||||||
logger.debug(`[${spawnId}] Using reflect4 server: ${server}`);
|
logger.debug(`[${spawnId}] Using reflect4 server: ${server}`);
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.goto(server);
|
if (changeViewport) {
|
||||||
|
logger.debug(`[${spawnId}] Changing viewport size...`);
|
||||||
|
const deviceName = Object.keys(devices)[Math.floor(Math.random() * Object.keys(devices).length)];
|
||||||
|
const device = devices[deviceName];
|
||||||
|
logger.debug(`[${spawnId}] Using device: ${deviceName}`);
|
||||||
|
await page.setViewportSize(device.viewport);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await page.goto(server);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`[${spawnId}] Error while navigating to proxy website: ${e}`);
|
||||||
|
await page.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
let targetInput: Locator | null = null;
|
let targetInput: Locator | null = null;
|
||||||
const allInput = await page.locator("input").all();
|
const allInput = await page.locator("input").all();
|
||||||
for (const input of allInput) {
|
for (const input of allInput) {
|
||||||
@ -20,12 +32,16 @@ async function spawn(context: BrowserContext, targetUrl: string) {
|
|||||||
}
|
}
|
||||||
if (!targetInput) {
|
if (!targetInput) {
|
||||||
logger.error(`[${spawnId}] Failed to find input field for URL input`);
|
logger.error(`[${spawnId}] Failed to find input field for URL input`);
|
||||||
return;
|
await page.close();
|
||||||
|
throw new Error(`Failed to find input field for URL input`);
|
||||||
}
|
}
|
||||||
await targetInput.fill(targetUrl);
|
await targetInput.fill(targetUrl);
|
||||||
await targetInput.press("Enter");
|
await targetInput.press("Enter");
|
||||||
|
logger.info(`[${spawnId}] Navigating to ${targetUrl}`);
|
||||||
|
await page.waitForTimeout(15000); // Wait for 15 second to let the page load
|
||||||
// Keep-alive the page open for 5 minutes then refresh
|
// Keep-alive the page open for 5 minutes then refresh
|
||||||
if (targetUrl.startsWith("https://www.twitch.tv/")) {
|
if (targetUrl.startsWith("https://www.twitch.tv/")) {
|
||||||
|
logger.info(`[${spawnId}] Twitch URL detected, using Twitch mode...`);
|
||||||
await twitch.keepAlive(page, spawnId);
|
await twitch.keepAlive(page, spawnId);
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`[${spawnId}] Unsupported URL: ${targetUrl}`);
|
logger.warn(`[${spawnId}] Unsupported URL: ${targetUrl}`);
|
||||||
|
|||||||
@ -1,5 +1,13 @@
|
|||||||
import type { Page } from "patchright";
|
import type { Page } from "patchright";
|
||||||
import logger from "../logger";
|
import logger from "../logger.js";
|
||||||
|
|
||||||
|
async function checkConsentButton(page: Page, spawnId: string = "unknown") {
|
||||||
|
const consentButton = page.locator("button[data-a-target='consent-banner-accept']");
|
||||||
|
if ((await consentButton.all()).length > 0) {
|
||||||
|
logger.debug(`[${spawnId}] Consent button found, clicking it...`);
|
||||||
|
await consentButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function keepAlive(page: Page, spawnId: string = "unknown") {
|
async function keepAlive(page: Page, spawnId: string = "unknown") {
|
||||||
try {
|
try {
|
||||||
@ -13,7 +21,10 @@ async function keepAlive(page: Page, spawnId: string = "unknown") {
|
|||||||
if ((await page.locator(".ScCoreButton-sc-ocjdkq-0.ggPgVz").all()).length > 0) {
|
if ((await page.locator(".ScCoreButton-sc-ocjdkq-0.ggPgVz").all()).length > 0) {
|
||||||
logger.debug(`[${spawnId}] Player encountered an error, refreshing the page...`);
|
logger.debug(`[${spawnId}] Player encountered an error, refreshing the page...`);
|
||||||
await page.reload({timeout: 0, waitUntil: "domcontentloaded"});
|
await page.reload({timeout: 0, waitUntil: "domcontentloaded"});
|
||||||
|
waitTime = 0;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
await checkConsentButton(page, spawnId);
|
||||||
if (waitTime > 5 * 60 * 1000) {
|
if (waitTime > 5 * 60 * 1000) {
|
||||||
logger.debug(`[${spawnId}] Waited for more than 5 minutes, refreshing the page...`);
|
logger.debug(`[${spawnId}] Waited for more than 5 minutes, refreshing the page...`);
|
||||||
await page.reload({timeout: 0, waitUntil: "domcontentloaded"});
|
await page.reload({timeout: 0, waitUntil: "domcontentloaded"});
|
||||||
@ -21,6 +32,7 @@ async function keepAlive(page: Page, spawnId: string = "unknown") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await page.close();
|
||||||
logger.error(`[${spawnId}] Error while keeping the page alive: ${e}`);
|
logger.error(`[${spawnId}] Error while keeping the page alive: ${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user