channel-worker 2.5.49 → 2.5.51
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/package.json +1 -1
- package/scripts/nurture_facebook.js +62 -0
package/package.json
CHANGED
|
@@ -351,6 +351,52 @@ async function followVisiblePage(page, log) {
|
|
|
351
351
|
return ok;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
// Is this profile browsing AS A PAGE rather than as the person?
|
|
355
|
+
//
|
|
356
|
+
// Why it matters: a Page's feed contains only the Page's own posts, so there is
|
|
357
|
+
// nothing to nurture — measured live: 1 post, feed bottoms out after ~1500px.
|
|
358
|
+
// And a profile already switched into a Page has, by definition, got its Page —
|
|
359
|
+
// which is the whole goal of nurturing. So this ends the session immediately and
|
|
360
|
+
// takes the channel off the schedule.
|
|
361
|
+
//
|
|
362
|
+
// Signal: the left nav carries Page-only entries (Công cụ chuyên nghiệp / Trung
|
|
363
|
+
// tâm quảng cáo / Ads Manager) and lacks the personal "Bạn bè" entry.
|
|
364
|
+
async function detectProfileMode(page) {
|
|
365
|
+
return page.evaluate(() => {
|
|
366
|
+
const navs = [...document.querySelectorAll("[role='navigation']")];
|
|
367
|
+
const navText = navs.map((n) => n.innerText || '').join(' | ');
|
|
368
|
+
const hrefs = navs.flatMap((n) => [...n.querySelectorAll('a[href]')].map((a) => a.getAttribute('href') || ''));
|
|
369
|
+
const proLink = hrefs.some((h) => /professional_dashboard|adsmanager|ad_center|ads\/manage|business\.facebook/i.test(h));
|
|
370
|
+
const proText = /Công cụ chuyên nghiệp|Professional dashboard|Trung tâm quảng cáo|Ad Center|Trình quản lý quảng cáo|Ads Manager/i.test(navText);
|
|
371
|
+
const hasFriends = /\bBạn bè\b|\bFriends\b/i.test(navText);
|
|
372
|
+
return {
|
|
373
|
+
isPage: (proLink || proText) && !hasFriends,
|
|
374
|
+
why: `proLink=${proLink} proText=${proText} friends=${hasFriends}`,
|
|
375
|
+
navSample: navText.replace(/\s+/g, ' ').slice(0, 140),
|
|
376
|
+
};
|
|
377
|
+
}).catch(() => ({ isPage: false, why: 'detect failed', navSample: '' }));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// debug:true only — screenshot the feed and hand back a viewable URL. Cheaper
|
|
381
|
+
// than another round of guessing at selectors from log lines.
|
|
382
|
+
async function dumpFeedShot(page, log, tag) {
|
|
383
|
+
try {
|
|
384
|
+
const fs = require('fs');
|
|
385
|
+
const os = require('os');
|
|
386
|
+
const path = require('path');
|
|
387
|
+
const dir = path.join(os.tmpdir(), 'cm-worker-pw');
|
|
388
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
389
|
+
const png = path.join(dir, `nurture-${tag}-${Date.now()}.png`);
|
|
390
|
+
await page.screenshot({ path: png, fullPage: false, timeout: 8000 }).catch(() => {});
|
|
391
|
+
if (fs.existsSync(png) && typeof log.uploadDebugScreenshot === 'function') {
|
|
392
|
+
const url = await log.uploadDebugScreenshot(png, { tag });
|
|
393
|
+
log('warn', `[nurture-fb][dbg] screenshot ${tag}: ${url && url.url ? url.url : JSON.stringify(url)}`);
|
|
394
|
+
}
|
|
395
|
+
} catch (e) {
|
|
396
|
+
log('info', `[nurture-fb][dbg] screenshot failed: ${String(e.message || e).slice(0, 80)}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
354
400
|
// ─── main ───────────────────────────────────────────────────────────────────
|
|
355
401
|
async function run({ page, payload, log }) {
|
|
356
402
|
const t0 = Date.now();
|
|
@@ -377,7 +423,22 @@ async function run({ page, payload, log }) {
|
|
|
377
423
|
await assertAccountUsable(page, log);
|
|
378
424
|
await dismissDialogs(page, log);
|
|
379
425
|
|
|
426
|
+
// Already a Page? Then the goal is met — stop here, don't fake a session.
|
|
427
|
+
const mode = await detectProfileMode(page);
|
|
428
|
+
if (payload.debug) log('info', `[nurture-fb][dbg] mode ${mode.why} nav="${mode.navSample}"`);
|
|
429
|
+
if (mode.isPage) {
|
|
430
|
+
log('info', '[nurture-fb] profile đang dùng dưới dạng TRANG (đã có Page) — bỏ qua, không cần nuôi nữa');
|
|
431
|
+
return {
|
|
432
|
+
profile_mode: 'page', skipped: true,
|
|
433
|
+
phase, day_index: dayIndex,
|
|
434
|
+
posts_seen: 0, likes: 0, videos_watched: 0, pages_followed: 0, posts_opened: 0,
|
|
435
|
+
duration_sec: Math.round((Date.now() - t0) / 1000),
|
|
436
|
+
account_status: 'ok',
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
380
440
|
await centerMouse(page); // wheel events need the cursor over the feed column
|
|
441
|
+
if (payload.debug) await dumpFeedShot(page, log, 'feed-start');
|
|
381
442
|
|
|
382
443
|
let postsSeen = 0, likes = 0, videos = 0, pagesFollowed = 0, postsOpened = 0;
|
|
383
444
|
let postsSinceLike = likeGap; // allow the first like once enough posts scroll by
|
|
@@ -475,6 +536,7 @@ async function run({ page, payload, log }) {
|
|
|
475
536
|
log('info', `[nurture-fb] session done — posts=${postsSeen} likes=${likes}/${likesTarget} videos=${videos} pages=${pagesFollowed} opened=${postsOpened} duration=${durationSec}s`);
|
|
476
537
|
return {
|
|
477
538
|
phase,
|
|
539
|
+
profile_mode: 'personal',
|
|
478
540
|
day_index: dayIndex,
|
|
479
541
|
posts_seen: postsSeen,
|
|
480
542
|
likes,
|