channel-worker 2.5.152 → 2.5.153
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 +1 -1
- package/scripts/upload_facebook.js +101 -4
package/package.json
CHANGED
|
@@ -166,6 +166,7 @@ async function navigateFacebookReelCreate(page, log) {
|
|
|
166
166
|
await page.goto(FB_REEL_CREATE_URL, { waitUntil: 'commit', timeout: 60_000 });
|
|
167
167
|
await page.waitForLoadState('domcontentloaded', { timeout: 60_000 }).catch(() => {});
|
|
168
168
|
await pause(page, 7000);
|
|
169
|
+
await dismissCookieConsent(page, log);
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
// FB drops a "Thông báo mới" toast at the BOTTOM-LEFT of the wall (e.g. "… và
|
|
@@ -430,6 +431,84 @@ async function dismissBsOnboarding(page, log) {
|
|
|
430
431
|
}
|
|
431
432
|
}
|
|
432
433
|
|
|
434
|
+
// Hộp hỏi cookie của Facebook — "Cho phép sử dụng cookie của Facebook trên
|
|
435
|
+
// trình duyệt này?" — là lớp phủ giữa màn hình, che cả trang và ăn mọi cú bấm.
|
|
436
|
+
// Sự cố 06-07/10/2026, kênh "Mẹo làm tóc tại nhà" (profile kenh111,
|
|
437
|
+
// win-worker-2): 7/7 lượt đăng hỏng, cả 14 ảnh dump no-advance-2 đều là hộp
|
|
438
|
+
// này. Bấm "Thước phim" trượt → nhảy /reels/create/ bị đẩy về feed /reel/<id
|
|
439
|
+
// của người khác> → bấm Tiếp trượt → "composer closed (before publish)".
|
|
440
|
+
// Profile có phiên nuôi thì thoát vì nurture/warmup bấm "Cho phép tất cả
|
|
441
|
+
// cookie"; profile không được nuôi thì kẹt mãi, thử lại bao nhiêu lần cũng thế.
|
|
442
|
+
//
|
|
443
|
+
// Bấm CÙNG lựa chọn với nurture_facebook/warmup_facebook (cho phép tất cả) để
|
|
444
|
+
// một profile không mang hai lựa chọn cookie khác nhau; không thấy nút đó mới
|
|
445
|
+
// lui sang "Từ chối cookie không bắt buộc" — việc cần là gỡ lớp phủ.
|
|
446
|
+
const COOKIE_CONSENT_RE = /Cho phép sử dụng cookie của Facebook|Allow (?:the )?use of cookies from Facebook/i;
|
|
447
|
+
const COOKIE_CONSENT_VERBS = ['Cho phép tất cả cookie', 'Allow all cookies', 'Từ chối cookie không bắt buộc', 'Decline optional cookies'];
|
|
448
|
+
|
|
449
|
+
function isCookieConsentText(txt) {
|
|
450
|
+
return COOKIE_CONSENT_RE.test(String(txt || ''));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Tìm hộp hỏi cookie đang hiện; thấy nút thì đánh dấu `__fbpw_cookie__`.
|
|
454
|
+
// Trả { found, verb } — verb null nghĩa là có hộp mà không thấy nút nào khớp.
|
|
455
|
+
// Không ném lỗi: trang vừa đóng / trang giả thiếu evaluate → coi như không có.
|
|
456
|
+
async function findCookieConsent(page) {
|
|
457
|
+
try {
|
|
458
|
+
return await page.evaluate(({ src, verbs }) => {
|
|
459
|
+
document.querySelectorAll('[__fbpw_cookie__]').forEach((el) => el.removeAttribute('__fbpw_cookie__'));
|
|
460
|
+
const re = new RegExp(src, 'i');
|
|
461
|
+
const shown = (el) => {
|
|
462
|
+
const r = el.getBoundingClientRect();
|
|
463
|
+
if (r.width < 8 || r.height < 8) return false;
|
|
464
|
+
const cs = getComputedStyle(el);
|
|
465
|
+
return cs.visibility !== 'hidden' && cs.display !== 'none';
|
|
466
|
+
};
|
|
467
|
+
for (const dlg of document.querySelectorAll("[role='dialog']")) {
|
|
468
|
+
if (!shown(dlg) || !re.test((dlg.innerText || '').slice(0, 600))) continue;
|
|
469
|
+
const btns = [...dlg.querySelectorAll("[role='button'], button")].filter(shown);
|
|
470
|
+
for (const v of verbs) {
|
|
471
|
+
const b = btns.find((x) => (x.innerText || '').trim() === v || (x.getAttribute('aria-label') || '').trim() === v);
|
|
472
|
+
if (b) {
|
|
473
|
+
b.setAttribute('__fbpw_cookie__', '1');
|
|
474
|
+
return { found: true, verb: v };
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return { found: true, verb: null };
|
|
478
|
+
}
|
|
479
|
+
return { found: false, verb: null };
|
|
480
|
+
}, { src: COOKIE_CONSENT_RE.source, verbs: COOKIE_CONSENT_VERBS }) || { found: false, verb: null };
|
|
481
|
+
} catch {
|
|
482
|
+
return { found: false, verb: null };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Gỡ hộp hỏi cookie nếu có. KHÔNG ném lỗi: gỡ không được thì luồng cũ chạy
|
|
487
|
+
// tiếp, và composerClosedError sẽ gọi đúng tên bệnh trong câu lỗi.
|
|
488
|
+
async function dismissCookieConsent(page, log) {
|
|
489
|
+
let hit = await findCookieConsent(page);
|
|
490
|
+
if (!hit.found) return { found: false, dismissed: false, verb: null };
|
|
491
|
+
const verb = hit.verb;
|
|
492
|
+
for (let attempt = 1; attempt <= 2 && hit.verb; attempt++) {
|
|
493
|
+
log('info', `[fb-pw] hộp hỏi cookie của Facebook đang che trang — bấm "${hit.verb}" (${attempt}/2)`);
|
|
494
|
+
try {
|
|
495
|
+
await page.locator("[__fbpw_cookie__='1']").first().click({ timeout: 3000 });
|
|
496
|
+
} catch {
|
|
497
|
+
// Lớp khác đè lên nút → bấm thẳng trong DOM.
|
|
498
|
+
await page.evaluate(() => { const b = document.querySelector("[__fbpw_cookie__='1']"); if (b) b.click(); }).catch(() => {});
|
|
499
|
+
}
|
|
500
|
+
await pause(page, 1500);
|
|
501
|
+
hit = await findCookieConsent(page);
|
|
502
|
+
if (!hit.found) {
|
|
503
|
+
log('info', '[fb-pw] đã gỡ hộp hỏi cookie của Facebook');
|
|
504
|
+
return { found: true, dismissed: true, verb };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
await page.evaluate(() => document.querySelectorAll('[__fbpw_cookie__]').forEach((el) => el.removeAttribute('__fbpw_cookie__'))).catch(() => {});
|
|
508
|
+
log('warn', `[fb-pw] hộp hỏi cookie của Facebook VẪN che trang${verb ? ` sau khi bấm "${verb}"` : ' — không thấy nút "Cho phép tất cả cookie"/"Từ chối cookie không bắt buộc"'}`);
|
|
509
|
+
return { found: true, dismissed: false, verb };
|
|
510
|
+
}
|
|
511
|
+
|
|
433
512
|
// ── Large-file upload bypass ───────────────────────────────────────────────
|
|
434
513
|
// Playwright caps file transfers at 50MB when the browser is reached over
|
|
435
514
|
// connectOverCDP (our Nstbrowser case):
|
|
@@ -1813,9 +1892,19 @@ function safeComposerRetryError(message) {
|
|
|
1813
1892
|
async function composerClosedError(page, log, fallbackMessage) {
|
|
1814
1893
|
let fbError = false;
|
|
1815
1894
|
try { fbError = classifyComposerText(await readPageAlerts(page)).fbError; } catch { /* đọc lỗi thì cứ dùng câu cũ */ }
|
|
1816
|
-
if (
|
|
1817
|
-
|
|
1818
|
-
|
|
1895
|
+
if (fbError) {
|
|
1896
|
+
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');
|
|
1897
|
+
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}`);
|
|
1898
|
+
}
|
|
1899
|
+
// Hộp hỏi cookie (xem dismissCookieConsent) vẫn còn = gỡ không được → nói
|
|
1900
|
+
// thẳng tên bệnh, đừng để người đọc đi soi file video / ảnh bìa.
|
|
1901
|
+
let cookieWall = false;
|
|
1902
|
+
try { cookieWall = isCookieConsentText(await readComposerText(page)); } catch { /* đọc lỗi thì cứ dùng câu cũ */ }
|
|
1903
|
+
if (cookieWall) {
|
|
1904
|
+
log('warn', '[fb-pw] hộp hỏi cookie của Facebook vẫn che trang — composer không bấm được, không phải lỗi bài/file');
|
|
1905
|
+
return safeComposerRetryError(`Facebook hiện hộp hỏi cookie ("Cho phép sử dụng cookie của Facebook…") che trang, bot không gỡ được — mở profile bấm "Cho phép tất cả cookie" — ${fallbackMessage}`);
|
|
1906
|
+
}
|
|
1907
|
+
return safeComposerRetryError(fallbackMessage);
|
|
1819
1908
|
}
|
|
1820
1909
|
|
|
1821
1910
|
// 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
|
|
@@ -2365,7 +2454,7 @@ async function runOnce({ page, payload, log }) {
|
|
|
2365
2454
|
} = payload || {};
|
|
2366
2455
|
if (!video_url) throw new Error('No video_url provided');
|
|
2367
2456
|
|
|
2368
|
-
log('info', '[fb-pw] selectors version=2026.
|
|
2457
|
+
log('info', '[fb-pw] selectors version=2026.10.07-cookie-consent');
|
|
2369
2458
|
|
|
2370
2459
|
page.on('dialog', (d) => { d.accept().catch(() => {}); });
|
|
2371
2460
|
|
|
@@ -2442,6 +2531,8 @@ async function runOnce({ page, payload, log }) {
|
|
|
2442
2531
|
await page.waitForLoadState('networkidle', { timeout: 30_000 }).catch(() => {});
|
|
2443
2532
|
await pause(page, 4000);
|
|
2444
2533
|
log('info', `[fb-pw] home loaded — url=${page.url()}`);
|
|
2534
|
+
// Hộp hỏi cookie che cả trang → gỡ trước khi đọc/bấm bất cứ thứ gì.
|
|
2535
|
+
await dismissCookieConsent(page, log);
|
|
2445
2536
|
// Fail with a stable account-state code before selectors turn a logout or
|
|
2446
2537
|
// checkpoint into a generic "Reels entry not found". The API consumes the
|
|
2447
2538
|
// code and opens the Facebook auto-publish circuit breaker.
|
|
@@ -2497,6 +2588,7 @@ async function runOnce({ page, payload, log }) {
|
|
|
2497
2588
|
+ (candidates.length > 1 ? ` (ứng viên ${opened.tried}/${candidates.length})` : ''));
|
|
2498
2589
|
await navigateFacebookHome(page, log);
|
|
2499
2590
|
await pause(page, 2500);
|
|
2591
|
+
await dismissCookieConsent(page, log);
|
|
2500
2592
|
} else {
|
|
2501
2593
|
log('warn', '[fb-pw] không tìm được link tab Reels trước khi đăng — bỏ qua ảnh nền');
|
|
2502
2594
|
}
|
|
@@ -4895,6 +4987,11 @@ module.exports.__testables = {
|
|
|
4895
4987
|
classifyComposerText,
|
|
4896
4988
|
readPageAlerts,
|
|
4897
4989
|
composerClosedError,
|
|
4990
|
+
// Hộp hỏi cookie che cả trang (kenh111, 06-07/10).
|
|
4991
|
+
isCookieConsentText,
|
|
4992
|
+
findCookieConsent,
|
|
4993
|
+
dismissCookieConsent,
|
|
4994
|
+
COOKIE_CONSENT_VERBS,
|
|
4898
4995
|
isComposerDialogSignature,
|
|
4899
4996
|
discoverReelsTabUrl,
|
|
4900
4997
|
scrapeReelTileIds,
|