channel-worker 2.5.99 → 2.5.101

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.99",
3
+ "version": "2.5.101",
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": {
@@ -28,22 +28,17 @@ function parseViews(txt) {
28
28
  return Math.round(num * mul);
29
29
  }
30
30
 
31
- async function findReelsTabHref(page) {
32
- return page.evaluate(() => {
33
- const direct = document.querySelectorAll("a[href*='sk=reels_tab']");
34
- for (const a of direct) { const h = a.getAttribute('href') || ''; if (h) return h.startsWith('http') ? h : `https://www.facebook.com${h}`; }
35
- for (const a of document.querySelectorAll("a[href*='/profile.php?id=']")) {
36
- const m = (a.getAttribute('href') || '').match(/\/profile\.php\?id=(\d+)/);
37
- if (m) return `https://www.facebook.com/profile.php?id=${m[1]}&sk=reels_tab`;
38
- }
39
- for (const a of document.querySelectorAll("a[aria-label*='Dòng thời gian'], a[aria-label*='Timeline']")) {
40
- const h = a.getAttribute('href') || '';
41
- const m = h.match(/^\/([A-Za-z0-9._-]{3,})\/?(?:\?|$)/);
42
- if (m && !['profile.php', 'reel', 'video', 'videos'].includes(m[1])) return `https://www.facebook.com/${m[1]}/?sk=reels_tab`;
43
- }
44
- return null;
45
- }).catch(() => null);
46
- }
31
+ // Tìm link tab Reels: DÙNG CHUNG với script đăng (upload_facebook), không giữ
32
+ // bản riêng nữa. Bản riêng cũ chỉ có 3 chiến lược (sk=reels_tab / profile.php
33
+ // / "Dòng thời gian") — trên TRANG CHỦ của Page hiện nay không có anchor nào
34
+ // như thế, chỉ shortcut /<id số>/ hoặc /<handle>/reels/ (chiến lược 4-5,
35
+ // thêm script đăng 09/09). Đo 30h tới 10/09 15:15: 15/21 lượt bám bài hỏng
36
+ // "không tìm được link tab Reels" — Góc Người Trồng Nấm 6 lượt, Chuyện Nhà
37
+ // Nông 3, Nông Trại Phục Linh 2… — trong khi CÙNG profile đó script đăng đêm
38
+ // 09/09 tìm được tab để so trước/sau. Bản riêng còn thiếu cả bộ lọc "anchor
39
+ // của bài trong bảng tin" (gốc 2 link page lạ sáng 10/09) nên có thể quét
40
+ // nhầm tab Reels của page KHÁC mà không hay.
41
+ const { discoverReelsTabUrl } = require('./upload_facebook').__testables;
47
42
 
48
43
  // Số người theo dõi của page, đọc ngay trên chính trang đang đứng (header hiện
49
44
  // ở mọi tab của page/profile) — không tốn thêm lượt mở profile nào. Ô
@@ -517,7 +512,7 @@ async function run({ page, payload, log }) {
517
512
  if (!tabUrl) {
518
513
  await page.goto(page_url || 'https://www.facebook.com/', { waitUntil: 'domcontentloaded', timeout: 45_000 });
519
514
  await page.waitForTimeout(4000);
520
- tabUrl = await findReelsTabHref(page);
515
+ tabUrl = await discoverReelsTabUrl(page, log); // có log → in mẫu href khi không khớp
521
516
  }
522
517
  if (!tabUrl) throw new Error('không tìm được link tab Reels của page');
523
518
  log('info', `[fb-stats] ${mode} · reels_tab: ${tabUrl.slice(0, 100)}`);
@@ -565,4 +560,4 @@ async function run({ page, payload, log }) {
565
560
  return { ok: true, mode, tiles: tiles.length, written: d?.written || 0, matched: d?.matched || 0, rising: d?.rising || 0, alerts: d?.alerts || 0, followers };
566
561
  }
567
562
 
568
- module.exports = { run, __testables: { parseViews, scrapeFollowers, parseExact, parseVnPostTime, runInsights, dumpInsightsFailure } };
563
+ module.exports = { run, __testables: { parseViews, scrapeFollowers, parseExact, parseVnPostTime, runInsights, dumpInsightsFailure, discoverReelsTabUrl } };
@@ -744,6 +744,19 @@ function diffNewReelIds(beforeIds, afterIds) {
744
744
  return (afterIds || []).filter((id) => !before.has(id));
745
745
  }
746
746
 
747
+ // ID từ gói mạng: tab Reels xác nhận chỉ chứng minh reel THUỘC PAGE MÌNH, không
748
+ // chứng minh nó MỚI. Ca 10/09 16:26 "ẩm thực đồng quê 01": snapshotLen=0 nên cả
749
+ // 40 ID bắt được đều "mới", ID đầu tiên 1600960624772777 có trên tab (là bài
750
+ // 07:55 sáng CỦA CHÍNH KÊNH) → nhận → API thấy link trùng idea khác → gỡ → bài
751
+ // thành "mập mờ". Ảnh nền tab Reels chụp trước khi mở composer là bằng chứng
752
+ // "cũ": ID nằm trong ảnh nền thì KHÔNG BAO GIỜ là bài vừa đăng. Thuần, test được.
753
+ function networkIdVerdict({ id, beforeIds, tabIds }) {
754
+ if (!id) return 'none';
755
+ if ((beforeIds || []).includes(id)) return 'stale'; // đã có trước khi đăng
756
+ if ((tabIds || []).includes(id)) return 'confirmed'; // của page mình và mới
757
+ return 'candidate'; // chưa thấy trên tab, chờ
758
+ }
759
+
747
760
  // Chữ ký của chính composer (mọi bước của wizard). Dùng để KHÔNG bao giờ bấm
748
761
  // nhầm vào nút của composer khi đang định bấm nút của hộp ảnh bìa.
749
762
  const COMPOSER_SIGNATURE_RE = /Cài đặt thước phim|Tạo thước phim|Chỉnh sửa thước phim|Reel settings|Create (?:a )?reel|Edit reel/i;
@@ -1033,7 +1046,15 @@ async function runOnce({ page, payload, log }) {
1033
1046
  log('info', `[fb-pw] after Thước phim click — url=${afterUrl}`);
1034
1047
  // Sanity check: clicking the sidebar Reels link navigates to /reel/<id>.
1035
1048
  // That means we clicked the wrong button — fail fast so the user knows.
1036
- if (/\/reel\/\d+/.test(afterUrl)) {
1049
+ // 10/09 19:12 "phụ kiện điện tử 02": URL thành /reel/1850660615918243 nhưng
1050
+ // ảnh dump cho thấy hộp "Tạo thước phim" (Thêm video / Tải lên) ĐANG MỞ đè
1051
+ // lên feed Reels — Facebook giờ vẽ trình tạo như một lớp phủ trên /reel/<id>
1052
+ // chứ không điều hướng sang /reels/create/. Phán "bị đá về feed" theo URL
1053
+ // là báo oan tài khoản ("cần người đăng nhập thử tay") trong khi composer
1054
+ // đã sẵn sàng. Có composer thì cứ đi tiếp; chỉ khi KHÔNG có mới là bị đá.
1055
+ if (/\/reel\/\d+/.test(afterUrl) && (await hasVisibleReelComposer(page))) {
1056
+ log('info', `[fb-pw] url=/reel/<id> nhưng hộp Tạo thước phim đang mở đè lên feed — không phải bị đá, đi tiếp`);
1057
+ } else if (/\/reel\/\d+/.test(afterUrl)) {
1037
1058
  // HAI nguyên nhân hoàn toàn khác nhau, trước đây gộp chung một câu báo
1038
1059
  // "wrong button matched" — và câu đó gửi người đọc đi tìm lỗi DOM không
1039
1060
  // hề tồn tại (kênh 6a2e66b3 hỏng suốt từ 27/08 vì hiểu nhầm chỗ này):
@@ -3007,14 +3028,19 @@ async function runOnce({ page, payload, log }) {
3007
3028
  // total=220 → 88 ID mới, cái đầu là reel của page lạ và đã bị ghi vào DB).
3008
3029
  // Chỉ nhận khi tab Reels CỦA CHÍNH PAGE xác nhận có ID đó; không xác
3009
3030
  // nhận được thì để làm ứng viên, lượt chờ 75s dưới đây kiểm lại.
3010
- const firstNew = trulyNew[0];
3031
+ // Bỏ qua mọi ID đã có trong ảnh nền (bài cũ của chính mình) — xem
3032
+ // networkIdVerdict. Lấy ID mới đầu tiên KHÔNG nằm trong ảnh nền.
3011
3033
  const tabIds = reelsTabUrl ? await scrapeReelTileIds(page) : [];
3012
- if (tabIds.includes(firstNew)) {
3034
+ const firstNew = trulyNew.find((id) => networkIdVerdict({ id, beforeIds: reelIdsBefore, tabIds }) !== 'stale') || null;
3035
+ const verdict = networkIdVerdict({ id: firstNew, beforeIds: reelIdsBefore, tabIds });
3036
+ if (verdict === 'confirmed') {
3013
3037
  postUrl = `https://www.facebook.com/reel/${firstNew}/`;
3014
3038
  log('info', `[fb-pw] post URL from network + tab Reels xác nhận: ${postUrl} (snapshotLen=${capturedReelIdsSnapshotLen} total=${capturedReelIds.length} new=${trulyNew.length})`);
3015
- } else {
3039
+ } else if (verdict === 'candidate') {
3016
3040
  networkCandidate = firstNew;
3017
3041
  log('warn', `[fb-pw] gói mạng cho ID ${firstNew} nhưng tab Reels của page CHƯA xác nhận (${tabIds.length} tile) — giữ làm ứng viên, chờ kiểm lại`);
3042
+ } else {
3043
+ log('warn', `[fb-pw] gói mạng chỉ có ID đã nằm trong ảnh nền (${trulyNew.length} ID, toàn bài cũ của mình) — không nhận, chờ so trước/sau`);
3018
3044
  }
3019
3045
  }
3020
3046
  }
@@ -3034,7 +3060,7 @@ async function runOnce({ page, payload, log }) {
3034
3060
  if (fresh2) {
3035
3061
  postUrl = fresh2.href.startsWith('http') ? fresh2.href : `https://www.facebook.com${fresh2.href}`;
3036
3062
  log('info', `[fb-pw] post URL from reels_tab RETRY (id=${fresh2.id}, timestamp="${fresh2.timestamp}"): ${postUrl}`);
3037
- } else if (networkCandidate && (await scrapeReelTileIds(page)).includes(networkCandidate)) {
3063
+ } else if (networkCandidate && networkIdVerdict({ id: networkCandidate, beforeIds: reelIdsBefore, tabIds: await scrapeReelTileIds(page) }) === 'confirmed') {
3038
3064
  postUrl = `https://www.facebook.com/reel/${networkCandidate}/`;
3039
3065
  log('info', `[fb-pw] post URL: ứng viên gói mạng đã được tab Reels xác nhận sau lượt chờ: ${postUrl}`);
3040
3066
  } else if (reelIdsBefore) {
@@ -3181,6 +3207,7 @@ module.exports.__testables = {
3181
3207
  confirmPublishDialog,
3182
3208
  diffNewReelIds,
3183
3209
  pickReelsTabUrl,
3210
+ networkIdVerdict,
3184
3211
  isComposerDialogSignature,
3185
3212
  discoverReelsTabUrl,
3186
3213
  scrapeReelTileIds,