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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.5.49",
3
+ "version": "2.5.51",
4
4
  "description": "Channel Manager worker daemon — runs on remote machines to execute video pipeline jobs",
5
5
  "main": "lib/daemon.js",
6
6
  "bin": {
@@ -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,