-
Notifications
You must be signed in to change notification settings - Fork 1
/
beachpatrol.js
executable file
·144 lines (128 loc) · 5 KB
/
beachpatrol.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env node
import { chromium, firefox } from 'playwright-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
import fs from 'fs';
import { createServer } from 'net';
import path from 'path';
import { URL } from 'url';
const HOME_DIR = process.env.HOME;
if (!HOME_DIR) {
throw new Error('HOME environment variable not set.');
}
const SUPPORTED_BROWSERS = ['chromium', 'firefox'];
// if --help/-h, print usage
if (process.argv.includes('--help') || process.argv.includes('-h')) {
console.log('Usage: beachpatrol [--profile <profile_name>] [--incognito] [--headless]');
console.log();
console.log('Launches a browser with the specified profile.');
console.log('Opens a socket to listen for commands. Commands can be sent with');
console.log('the \'beachmsg\' command.');
console.log();
console.log('Options:');
console.log(' --profile <profile_name> Use the specified profile. Default: default');
console.log(' --browser <browser_name> Use the specified browser. Default: chromium');
console.log(` Supported browsers: ${SUPPORTED_BROWSERS.join(', ')}`);
console.log(' --incognito Launch browser in incognito mode');
console.log(' --headless Launch browser in headless mode');
process.exit(0);
}
// Argument parsing
let profileName = 'default';
if (process.argv.includes('--profile')) {
const profileIndex = process.argv.indexOf('--profile');
profileName = process.argv[profileIndex + 1];
}
let incognito = false;
if (process.argv.includes('--incognito')) {
incognito = true;
}
let headless = false;
if (process.argv.includes('--headless')) {
headless = true;
}
let browser = 'chromium';
if (process.argv.includes('--browser')) {
const browserIndex = process.argv.indexOf('--browser');
browser = process.argv[browserIndex + 1];
// bail out if browser is not supported
if (!SUPPORTED_BROWSERS.includes(browser)) {
console.error(`Error: Unsupported browser ${browser}`);
console.error(`Supported browsers: ${SUPPORTED_BROWSERS.join(', ')}`);
process.exit(1);
}
}
// prepare profile directory, create if it doesn't exist
const profileDir = path.join(HOME_DIR, `.config/beachpatrol/profiles/${browser}/${profileName}`);
if (!fs.existsSync(profileDir)) {
fs.mkdirSync(profileDir, { recursive: true });
}
const browserCommand = browser === 'chromium' ? chromium : firefox;
// prepare launch options and hide automation
const launchOptions = {
headless: false,
viewport: null, // Let browser decide viewport
args: [],
ignoreDefaultArgs: ['--enable-automation'], // No "controlled by automation" infobar
};
if (process.env.XDG_SESSION_TYPE === 'wayland') {
// if running on wayland, add the needed chromium wayland flag
launchOptions.args.push('--ozone-platform-hint=wayland');
};
browserCommand.use(StealthPlugin());
if (incognito) {
if (browser === 'chromium') {
launchOptions.args.push('--incognito');
} else if (browser === 'firefox') {
launchOptions.args.push('-private-window');
}
}
// Launch browser with specified profile and args
let browserContext;
if (incognito) {
const browser = await browserCommand.launch(launchOptions);
browserContext = await browser.newContext();
// the 'launch' method does not open a page by default, so we need to open one
await browserContext.newPage();
} else {
browserContext = await browserCommand.launchPersistentContext(profileDir, launchOptions);
}
// prepare UNIX socket to listen for commands
const SOCKET_PATH = '/tmp/beachpatrol.sock';
if (fs.existsSync(SOCKET_PATH)) {
fs.unlinkSync(SOCKET_PATH);
}
// Listen for commands
const server = createServer((socket) => {
socket.on('data', async (data) => {
const message = data.toString().trim().split(' '); // Splitting message into parts
const [commandName, ...args] = message;
const COMMANDS_DIR = 'commands';
const projectRoot = new URL('.', import.meta.url).pathname;
const commandFilePath = path.join(projectRoot, COMMANDS_DIR, `${commandName}.js`);
// log command
console.log(`Received command: ${commandName} ${args.join(' ')}`);
// Check if command script exists.
if (!fs.existsSync(commandFilePath)) {
const ERROR_MESSAGE = `Error: Command script ${commandName}.js does not exist.`;
console.log(ERROR_MESSAGE);
socket.write(`${ERROR_MESSAGE}\n`);
} else {
// Import and run the command
try {
// import with a timestamp to avoid caching
const modulePath = path.resolve(commandFilePath);
const {default: command} = await import(`${modulePath}?t=${Date.now()}`);
await command(browserContext, ...args);
const SUCCESS_MESSAGE = 'Command executed successfully.';
console.log(SUCCESS_MESSAGE);
socket.write(`${SUCCESS_MESSAGE}\n`);
} catch (error) {
const ERROR_MESSAGE = `Error: Failed to execute command. ${error.message}`;
console.log(ERROR_MESSAGE);
socket.write(`${ERROR_MESSAGE}\n`);
}
}
});
});
server.listen(SOCKET_PATH);
console.log(`beachpatrol listening on ${SOCKET_PATH}`);