channel-worker 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/command-poller.js +70 -5
- package/lib/nst-manager.js +29 -6
- package/package.json +1 -1
package/lib/command-poller.js
CHANGED
|
@@ -46,6 +46,9 @@ class CommandPoller {
|
|
|
46
46
|
case 'close_profile':
|
|
47
47
|
await this.handleCloseProfile(command);
|
|
48
48
|
break;
|
|
49
|
+
case 'scan_facebook_pages':
|
|
50
|
+
await this.handleScanFacebookPages(command);
|
|
51
|
+
break;
|
|
49
52
|
case 'verify_logins':
|
|
50
53
|
await this.handleVerifyLogins(command);
|
|
51
54
|
break;
|
|
@@ -61,12 +64,11 @@ class CommandPoller {
|
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
async handleLaunchProfile(command) {
|
|
64
|
-
const { profile_id } = command.payload || {};
|
|
67
|
+
const { profile_id, channel_id } = command.payload || {};
|
|
65
68
|
console.log(`[commands] Launching Nstbrowser profile: ${profile_id}`);
|
|
66
69
|
|
|
67
70
|
try {
|
|
68
71
|
if (!this.nst) {
|
|
69
|
-
// No Nstbrowser SDK — try to get API key from dashboard settings
|
|
70
72
|
const apiKey = await this.api.getSetting('nst_api_key');
|
|
71
73
|
if (apiKey) {
|
|
72
74
|
this.nst = new NstManager(apiKey);
|
|
@@ -75,12 +77,22 @@ class CommandPoller {
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
// Fetch channel proxy if channel_id provided
|
|
81
|
+
let proxy = null;
|
|
82
|
+
if (channel_id) {
|
|
83
|
+
try {
|
|
84
|
+
const channel = await this.api.getChannel(channel_id);
|
|
85
|
+
proxy = channel?.proxy || null;
|
|
86
|
+
} catch { /* ignore */ }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const extensionPath = this.config.extension_path || '';
|
|
90
|
+
const result = await this.nst.launchProfile(profile_id, { proxy, extensionPath });
|
|
91
|
+
console.log(`[commands] Profile ${profile_id} launched${proxy ? ' (proxy: ' + proxy + ')' : ''}${extensionPath ? ' (ext: ' + extensionPath + ')' : ''}`);
|
|
80
92
|
|
|
81
93
|
await this.api.updateCommand(command._id, {
|
|
82
94
|
status: 'done',
|
|
83
|
-
result: { profile_id: result.profileId, launched_at: new Date().toISOString() },
|
|
95
|
+
result: { profile_id: result.profileId, launched_at: new Date().toISOString(), proxy: proxy || null },
|
|
84
96
|
});
|
|
85
97
|
} catch (err) {
|
|
86
98
|
console.error(`[commands] Failed to launch profile: ${err.message}`);
|
|
@@ -91,6 +103,59 @@ class CommandPoller {
|
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
|
|
106
|
+
async handleScanFacebookPages(command) {
|
|
107
|
+
const { profile_id } = command.payload || {};
|
|
108
|
+
console.log(`[commands] Scanning Facebook pages via profile: ${profile_id}`);
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
if (!this.nst) {
|
|
112
|
+
const apiKey = await this.api.getSetting('nst_api_key');
|
|
113
|
+
if (apiKey) {
|
|
114
|
+
this.nst = new NstManager(apiKey);
|
|
115
|
+
} else {
|
|
116
|
+
throw new Error('Nstbrowser API key not configured.');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Launch profile and get CDP endpoint
|
|
121
|
+
const { profileId, wsEndpoint } = await this.nst.connectProfile(profile_id);
|
|
122
|
+
|
|
123
|
+
// TODO: Use Puppeteer/Playwright to navigate facebook.com/pages and scrape page list
|
|
124
|
+
// For now, placeholder — worker needs puppeteer-core to actually scrape
|
|
125
|
+
console.log(`[commands] Browser connected at ${wsEndpoint}`);
|
|
126
|
+
console.log(`[commands] TODO: Navigate to facebook.com/pages and scrape page list`);
|
|
127
|
+
|
|
128
|
+
// Placeholder result — in real implementation, scrape pages from Facebook
|
|
129
|
+
const pages = [];
|
|
130
|
+
// const puppeteer = require('puppeteer-core');
|
|
131
|
+
// const browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint });
|
|
132
|
+
// const page = await browser.newPage();
|
|
133
|
+
// await page.goto('https://www.facebook.com/pages/?category=your_pages');
|
|
134
|
+
// pages = await page.evaluate(() => { /* scrape page names/urls */ });
|
|
135
|
+
|
|
136
|
+
// Send results to API
|
|
137
|
+
if (pages.length > 0) {
|
|
138
|
+
await this.api.request('POST', '/facebook-pages/scan-result', {
|
|
139
|
+
pages,
|
|
140
|
+
scanned_from_profile: profile_id,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await this.api.updateCommand(command._id, {
|
|
145
|
+
status: 'done',
|
|
146
|
+
result: { profile_id: profileId, pages_found: pages.length },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
console.log(`[commands] Facebook scan complete — ${pages.length} pages found`);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.error(`[commands] Facebook scan failed: ${err.message}`);
|
|
152
|
+
await this.api.updateCommand(command._id, {
|
|
153
|
+
status: 'failed',
|
|
154
|
+
error: err.message,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
94
159
|
async handleCloseProfile(command) {
|
|
95
160
|
const { profile_id } = command.payload || {};
|
|
96
161
|
console.log(`[commands] Closing profile: ${profile_id}`);
|
package/lib/nst-manager.js
CHANGED
|
@@ -57,7 +57,7 @@ class NstManager {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Create profile if not exists, return profileId
|
|
60
|
-
async ensureProfile(name) {
|
|
60
|
+
async ensureProfile(name, options = {}) {
|
|
61
61
|
const existing = await this.findProfile(name);
|
|
62
62
|
if (existing) {
|
|
63
63
|
console.log(`[nst] Profile "${name}" exists: ${existing}`);
|
|
@@ -65,7 +65,7 @@ class NstManager {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
console.log(`[nst] Creating profile "${name}"...`);
|
|
68
|
-
const
|
|
68
|
+
const profileData = {
|
|
69
69
|
name,
|
|
70
70
|
platform: 'Windows',
|
|
71
71
|
kernelMilestone: '132',
|
|
@@ -80,7 +80,17 @@ class NstManager {
|
|
|
80
80
|
hardwareConcurrency: 8,
|
|
81
81
|
deviceMemory: 8,
|
|
82
82
|
},
|
|
83
|
-
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Add extension load arg if provided
|
|
86
|
+
if (options.extensionPath) {
|
|
87
|
+
profileData.args = {
|
|
88
|
+
'--load-extension': options.extensionPath,
|
|
89
|
+
'--disable-extensions-except': options.extensionPath,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const res = await this.client.profiles().createProfile(profileData);
|
|
84
94
|
|
|
85
95
|
const profileId = res?.data?.profileId || res?.data?._id;
|
|
86
96
|
if (!profileId) throw new Error('Failed to create profile — no ID returned');
|
|
@@ -89,11 +99,19 @@ class NstManager {
|
|
|
89
99
|
return profileId;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
//
|
|
93
|
-
async
|
|
102
|
+
// Set proxy on profile
|
|
103
|
+
async setProxy(profileId, proxyUrl) {
|
|
104
|
+
if (!proxyUrl) return;
|
|
105
|
+
console.log(`[nst] Setting proxy for ${profileId}: ${proxyUrl}`);
|
|
106
|
+
await this.client.profiles().updateProfileProxy(profileId, { url: proxyUrl });
|
|
107
|
+
console.log(`[nst] Proxy set`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Launch browser — skip if already running, set proxy if provided
|
|
111
|
+
async launchProfile(profileIdOrName, options = {}) {
|
|
94
112
|
let profileId = profileIdOrName;
|
|
95
113
|
if (!this.isUUID(profileIdOrName)) {
|
|
96
|
-
profileId = await this.ensureProfile(profileIdOrName);
|
|
114
|
+
profileId = await this.ensureProfile(profileIdOrName, { extensionPath: options.extensionPath });
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
// Check if already running
|
|
@@ -102,6 +120,11 @@ class NstManager {
|
|
|
102
120
|
return { profileId, alreadyRunning: true };
|
|
103
121
|
}
|
|
104
122
|
|
|
123
|
+
// Set proxy before launch
|
|
124
|
+
if (options.proxy) {
|
|
125
|
+
await this.setProxy(profileId, options.proxy);
|
|
126
|
+
}
|
|
127
|
+
|
|
105
128
|
console.log(`[nst] Starting browser for profile: ${profileId}`);
|
|
106
129
|
const res = await this.client.browsers().startBrowser(profileId);
|
|
107
130
|
console.log(`[nst] Browser started`);
|