channel-worker 2.5.98 → 2.5.99

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.98",
3
+ "version": "2.5.99",
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": {
@@ -421,18 +421,71 @@ async function readAudiencePage(page, log) {
421
421
  return a;
422
422
  }
423
423
 
424
+ // Trang Insights hỏng thì chụp lại — script này trước 10/09 không dump gì nên
425
+ // 8 kênh kẹt chip "Hôm nay" hai ngày liền mà không có lấy một tấm ảnh để hiểu.
426
+ // Ngoài ảnh, liệt kê mọi chữ có thể là nhãn khoảng thời gian kèm vị trí: để
427
+ // biết chip thật đang ghi gì, và có cái "Hôm nay" lạ nào đứng TRƯỚC chip trong
428
+ // DOM (currentRangeLabel lấy phần tử khớp đầu tiên) hay không.
429
+ async function dumpInsightsFailure(page, tag, log) {
430
+ try {
431
+ const os = require('os'); const fs = require('fs'); const path = require('path');
432
+ const dir = path.join(os.tmpdir(), 'cm-worker-pw');
433
+ fs.mkdirSync(dir, { recursive: true });
434
+ const png = path.join(dir, `fb-${tag}-${Date.now()}.png`);
435
+ await page.screenshot({ path: png, fullPage: false, timeout: 8000 }).catch(() => {});
436
+ const labels = await page.evaluate(() => {
437
+ const clean = (v) => String(v || '').replace(/\s+/g, ' ').trim();
438
+ const out = [];
439
+ for (const e of document.querySelectorAll('div[role="button"],div,span')) {
440
+ const t = clean(e.innerText);
441
+ if (!t || t.length >= 70) continue;
442
+ if (!/(Hôm nay|Today|Tùy chỉnh|Custom|ngày qua|Last \d+ days|Năm nay|This year)/i.test(t)) continue;
443
+ const r = e.getBoundingClientRect();
444
+ out.push(`${e.tagName.toLowerCase()}[${e.getAttribute('role') || ''}] y=${Math.round(r.y)} w=${Math.round(r.width)} "${t}"`);
445
+ if (out.length >= 25) break;
446
+ }
447
+ return out;
448
+ }).catch(() => []);
449
+ labels.forEach((l, i) => log('info', ` ${tag} label[${i}] ${l}`));
450
+ let shot = null;
451
+ if (fs.existsSync(png) && typeof log.uploadDebugScreenshot === 'function') {
452
+ try { shot = await log.uploadDebugScreenshot(png, { tag }); } catch (e) { log('warn', `[insights] tải ảnh dump hỏng: ${e.message}`); }
453
+ }
454
+ log('warn', `[insights] dump ${tag} — url=${page.url()} screenshot=${shot?.signed_url || png}`,
455
+ shot ? { debug_screenshot: shot } : { debug_screenshot_local_path: png });
456
+ } catch {}
457
+ }
458
+
424
459
  // Lượt nền: hai trang view + một trang đối tượng, rồi gửi thẳng số thô về API.
425
460
  // Việc khớp bài với idea và tính hiệu để lòi ra "bài cũ đang nổ" làm ở API —
426
461
  // worker không giữ trạng thái ngày hôm qua.
427
- async function runInsights({ page, payload, log }) {
428
- // 7 ngày trước để làm ấm trang (lần điều hướng ĐẦU hay bị bỏ qua tham số),
429
- // rồi mới tới ngày hôm qua.
430
- const week = await readViewsPage(page, 'LAST_7D', log);
462
+ //
463
+ // THỨ TỰ (đổi 10/09): NGÀY HÔM QUA FOLLOWER 7 NGÀY, "7 ngày" hỏng
464
+ // KHÔNG chết cả lượt. Bản cũ đọc "7 ngày qua" trước (làm ấm trang) và ném lỗi
465
+ // khi chip không đổi mà trang đó chỉ để lấy danh sách bài. Đo 10/09: 8/63
466
+ // kênh (09/09: 11/61) kẹt chip "Hôm nay" đúng ở trang này, trong khi trang
467
+ // ngày hôm qua + follower của CHÍNH các kênh đó vẫn đọc được (log win-worker:
468
+ // chip "Tùy chỉnh: 9 Tháng 9" ra đều). Một chip kẹt làm mất view hôm qua +
469
+ // follower — hai số quan trọng nhất — cho cả ngày, /analytics báo "Chưa quét".
470
+ // Ngày hôm qua tự có vòng nạp lại 3 lần nên không cần trang khác làm ấm.
471
+ const DEFAULT_READERS = { readOneDay, readAudiencePage, readViewsPage, dump: dumpInsightsFailure };
472
+ async function runInsights({ page, payload, log }, R = DEFAULT_READERS) {
431
473
  const yIso = vnYesterdayIso();
432
- const yest = await readOneDay(page, yIso, log);
433
- const audience = await readAudiencePage(page, log);
474
+ const yest = await R.readOneDay(page, yIso, log);
475
+ const audience = await R.readAudiencePage(page, log);
476
+ let week = { range: 'LAST_7D', label: '', total: null, pct: null, posts: [] };
477
+ let weekError = null;
478
+ try {
479
+ week = await R.readViewsPage(page, 'LAST_7D', log);
480
+ } catch (e) {
481
+ weekError = String((e && e.message) || e);
482
+ log('warn', `[insights] 7 ngày qua hỏng (${weekError}) — vẫn ghi ngày hôm qua + follower, chỉ mất danh sách bài`);
483
+ await R.dump(page, 'insights-week', log);
484
+ }
434
485
  if (yest.views == null && week.total == null) {
435
- throw new Error('không đọc được tổng lượt xem trên trang Insights — FB đổi giao diện?');
486
+ throw new Error(weekError
487
+ ? `không đọc được cả ngày hôm qua lẫn 7 ngày — ${weekError}`
488
+ : 'không đọc được tổng lượt xem trên trang Insights — FB đổi giao diện?');
436
489
  }
437
490
  if (typeof log.apiRequest !== 'function') throw new Error('log.apiRequest không có — cần channel-worker ≥ 2.5.55');
438
491
  const d = await log.apiRequest('POST', '/analytics/fb-insights', {
@@ -448,6 +501,7 @@ async function runInsights({ page, payload, log }) {
448
501
  views_yesterday: yest.views, views_7d: week.total,
449
502
  posts: week.posts.length, matched: d?.matched ?? null,
450
503
  followers: audience.followers, unattributed: d?.unattributed ?? null,
504
+ ...(weekError ? { week_error: weekError } : {}),
451
505
  };
452
506
  }
453
507
 
@@ -511,4 +565,4 @@ async function run({ page, payload, log }) {
511
565
  return { ok: true, mode, tiles: tiles.length, written: d?.written || 0, matched: d?.matched || 0, rising: d?.rising || 0, alerts: d?.alerts || 0, followers };
512
566
  }
513
567
 
514
- module.exports = { run, __testables: { parseViews, scrapeFollowers, parseExact, parseVnPostTime } };
568
+ module.exports = { run, __testables: { parseViews, scrapeFollowers, parseExact, parseVnPostTime, runInsights, dumpInsightsFailure } };