channel-worker 2.5.103 → 2.5.104

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.103",
3
+ "version": "2.5.104",
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": {
@@ -651,12 +651,21 @@ async function isCustomThumbApplied(page) {
651
651
  // xong quyền riêng tư của Thước phim" → Đăng xám mãi; đứng
652
652
  // 600s rồi "description field not found" (phụ kiện điện tử
653
653
  // 02 10/09, kol du lịch 11/09).
654
+ // fbError : toast ĐEN góc trái dưới "Đã xảy ra lỗi. Việc này có thể là
655
+ // do lỗi kỹ thuật và chúng tôi đang khắc phục rồi." → chính
656
+ // Facebook hỏng và tự gỡ composer. Toast này nằm NGOÀI mọi
657
+ // [role=dialog] nên readComposerText không thấy — đọc bằng
658
+ // readPageAlerts.
654
659
  // Thuần, test được; regex neo đầu dòng cho "Đang đăng" để không dính mô tả
655
660
  // bài có chữ "đang đăng" giữa câu.
656
661
  const COMPOSER_TEXT_RE = {
657
662
  rightsCheck: /Không hoàn tất được quy trình kiểm tra bản quyền|couldn['’]t complete (?:the )?(?:rights|copyright) check/i,
658
663
  publishing: /(?:^|\n)\s*(?:Đang đăng|Đang xuất bản|Publishing|Posting)\s*(?:\.{3}|…)?\s*(?:\n|$)/i,
659
664
  settingsStuck: /Vui lòng đợi tải xong quyền riêng tư của Thước phim|wait (?:for|until) (?:the )?reel(?:'s)? privacy/i,
665
+ // ĐÒI CẢ HAI VẾ trong cùng một quãng ngắn. Chỉ "Đã xảy ra lỗi" thôi thì quá
666
+ // dễ trúng caption bài trên feed (hàm đọc có nhánh quét cả trang), còn cặp
667
+ // "lỗi … lỗi kỹ thuật/khắc phục" là đúng khuôn toast của Facebook.
668
+ fbError: /Đã xảy ra lỗi[\s\S]{0,160}(?:lỗi kỹ thuật|đang khắc phục)|Something went wrong[\s\S]{0,160}technical (?:error|issue|problem)/i,
660
669
  };
661
670
  function classifyComposerText(txt) {
662
671
  const t = String(txt || '');
@@ -664,6 +673,7 @@ function classifyComposerText(txt) {
664
673
  rightsCheck: COMPOSER_TEXT_RE.rightsCheck.test(t),
665
674
  publishing: COMPOSER_TEXT_RE.publishing.test(t),
666
675
  settingsStuck: COMPOSER_TEXT_RE.settingsStuck.test(t),
676
+ fbError: COMPOSER_TEXT_RE.fbError.test(t),
667
677
  };
668
678
  }
669
679
  async function readComposerText(page) {
@@ -679,6 +689,34 @@ async function readComposerText(page) {
679
689
  return out.join('\n').slice(0, 8000);
680
690
  }).catch(() => '');
681
691
  }
692
+ // Chữ trong các lớp nổi NGOÀI composer: toast, alert, status.
693
+ //
694
+ // Vì sao cần: 13/09 ba kênh (kiến 01, kenh-54, kenh-55) cùng chết ở bước lưu
695
+ // ảnh bìa, ảnh dump cả ba đều có TOAST ĐEN góc trái dưới "Đã xảy ra lỗi… lỗi
696
+ // kỹ thuật… đang khắc phục rồi" — tức Facebook tự hỏng rồi gỡ composer. Câu
697
+ // lỗi cũ chỉ nói "composer closed", nên mỗi lần gặp lại phải mở ảnh dump mới
698
+ // biết là phía FB. Toast nằm ngoài [role=dialog] nên readComposerText mù.
699
+ //
700
+ // Quét theo lớp: role=alert/status + gốc portal `ignore-dynamic` (cùng bộ chọn
701
+ // mà dismissToasts đã dùng). Không thấy gì thì mới đọc cả trang — an toàn vì
702
+ // regex fbError đòi hai vế đi liền nhau.
703
+ async function readPageAlerts(page) {
704
+ return page.evaluate(() => {
705
+ const visible = (el) => {
706
+ const r = el.getBoundingClientRect();
707
+ if (r.width < 8 || r.height < 8) return false;
708
+ const cs = getComputedStyle(el);
709
+ return cs.visibility !== 'hidden' && cs.display !== 'none';
710
+ };
711
+ const out = [];
712
+ for (const el of document.querySelectorAll("[role='alert'], [role='status'], div[data-visualcompletion='ignore-dynamic']")) {
713
+ if (visible(el)) out.push(el.innerText || '');
714
+ }
715
+ const picked = out.join('\n').trim();
716
+ return (picked || (document.body ? document.body.innerText || '' : '')).slice(0, 8000);
717
+ }).catch(() => '');
718
+ }
719
+
682
720
  const PUBLISHING_WAIT_MS = 5 * 60 * 1000; // "Đang đăng": chờ tới 5 phút, không bấm lại
683
721
  const SETTINGS_STUCK_MS = 90 * 1000; // bảng cài đặt trống quá 90s: mở lại composer
684
722
 
@@ -690,6 +728,21 @@ function safeComposerRetryError(message) {
690
728
  return err;
691
729
  }
692
730
 
731
+ // Composer biến mất → nói RÕ vì sao, nếu Facebook có để lại toast lỗi.
732
+ //
733
+ // GIỮ NGUYÊN cụm "composer closed after saving thumbnail" ở cuối câu: luật
734
+ // `composer_closed` bên api/services/PublishRetry khớp theo cụm đó. Worker lên
735
+ // bản mới bằng chép nóng (có hiệu lực ngay), còn API thì deploy riêng — đổi
736
+ // hẳn câu lỗi là lỗi này rơi vào "lỗi lạ" (trần thử lại 1 lần) cho tới khi API
737
+ // kịp lên. Thêm vế giải thích ở ĐẦU, không thay phần đuôi.
738
+ async function composerClosedError(page, log, fallbackMessage) {
739
+ let fbError = false;
740
+ try { fbError = classifyComposerText(await readPageAlerts(page)).fbError; } catch { /* đọc lỗi thì cứ dùng câu cũ */ }
741
+ if (!fbError) return safeComposerRetryError(fallbackMessage);
742
+ log('warn', '[fb-pw] Facebook hiện toast "Đã xảy ra lỗi… lỗi kỹ thuật" — chính FB hỏng rồi gỡ composer, không phải lỗi bài/tài khoản');
743
+ return safeComposerRetryError(`Facebook báo lỗi kỹ thuật ("Đã xảy ra lỗi… chúng tôi đang khắc phục") rồi tự gỡ trình soạn — ${fallbackMessage}`);
744
+ }
745
+
693
746
  // Toast "Thông báo mới" (ai đó thích/bình luận reel) nổi lên góc trái và ĂN
694
747
  // CÚ CLICK: 23/08 hai lần liên tiếp bot bấm "Tiếp" thì FB nhảy sang xem reel
695
748
  // cũ, composer đóng → "final description field not found". Đóng toast trước
@@ -2036,7 +2089,7 @@ async function runOnce({ page, payload, log }) {
2036
2089
  // safe here because the publish branch has not run yet.
2037
2090
  if (!(await hasVisibleReelComposer(page))) {
2038
2091
  await dumpFailure(page, 'composer-closed-after-thumb-save', log);
2039
- throw safeComposerRetryError('FB Reel composer closed after saving thumbnail (before publish)');
2092
+ throw await composerClosedError(page, log, 'FB Reel composer closed after saving thumbnail (before publish)');
2040
2093
  }
2041
2094
  thumbApplied = true;
2042
2095
  log('info', `[fb-pw] page-wall thumb — modal closed after save (retries=${retryCount})`);
@@ -2723,7 +2776,11 @@ async function runOnce({ page, payload, log }) {
2723
2776
  // FB tự đóng) → CHẠY LẠI cả lượt upload thay vì báo lỗi khó hiểu.
2724
2777
  // An toàn vì chưa hề bấm Đăng ở nhánh này.
2725
2778
  if (!(await hasVisibleReelComposer(page))) {
2726
- throw safeComposerRetryError(`FB composer đóng giữa chừng (đang ${page.url().slice(0, 80)}) — chạy lại lượt đăng`);
2779
+ // Câu lỗi phải chứa "composer closed before publish": luật
2780
+ // `composer_closed` của PublishRetry khớp theo cụm đó. Bản cũ viết
2781
+ // thuần tiếng Việt nên nếu lượt trong phiên cũng hỏng thì lỗi rơi
2782
+ // vào "lỗi lạ" (trần thử lại 1 lần) thay vì được xếp lại 3 lần.
2783
+ throw await composerClosedError(page, log, `FB Reel composer closed (before publish) — đóng giữa chừng ở ${page.url().slice(0, 80)}, chạy lại lượt đăng`);
2727
2784
  }
2728
2785
  if (!fillState.description) {
2729
2786
  throw new Error(`FB final description field not found at step ${step + 1}`);
@@ -3298,6 +3355,8 @@ module.exports.__testables = {
3298
3355
  pickReelsTabUrl,
3299
3356
  networkIdVerdict,
3300
3357
  classifyComposerText,
3358
+ readPageAlerts,
3359
+ composerClosedError,
3301
3360
  isComposerDialogSignature,
3302
3361
  discoverReelsTabUrl,
3303
3362
  scrapeReelTileIds,