channel-worker 2.5.50 → 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.50",
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,32 @@ 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
+
354
380
  // debug:true only — screenshot the feed and hand back a viewable URL. Cheaper
355
381
  // than another round of guessing at selectors from log lines.
356
382
  async function dumpFeedShot(page, log, tag) {
@@ -397,6 +423,20 @@ async function run({ page, payload, log }) {
397
423
  await assertAccountUsable(page, log);
398
424
  await dismissDialogs(page, log);
399
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
+
400
440
  await centerMouse(page); // wheel events need the cursor over the feed column
401
441
  if (payload.debug) await dumpFeedShot(page, log, 'feed-start');
402
442
 
@@ -496,6 +536,7 @@ async function run({ page, payload, log }) {
496
536
  log('info', `[nurture-fb] session done — posts=${postsSeen} likes=${likes}/${likesTarget} videos=${videos} pages=${pagesFollowed} opened=${postsOpened} duration=${durationSec}s`);
497
537
  return {
498
538
  phase,
539
+ profile_mode: 'personal',
499
540
  day_index: dayIndex,
500
541
  posts_seen: postsSeen,
501
542
  likes,