channel-worker 2.5.58 → 2.5.60
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/lib/human.js +12 -0
- package/scripts/upload_facebook.js +51 -1
package/package.json
CHANGED
package/scripts/lib/human.js
CHANGED
|
@@ -96,6 +96,18 @@ async function humanClick(page, target, { hoverMs = [250, 900], timeout = 4000,
|
|
|
96
96
|
await page.waitForTimeout(randInt(hoverMs[0], hoverMs[1]));
|
|
97
97
|
// Tiny drift while "deciding" — people rarely hold a pixel.
|
|
98
98
|
if (chance(0.5)) await page.mouse.move(p.x + rand(-1.5, 1.5), p.y + rand(-1.5, 1.5)).catch(() => {});
|
|
99
|
+
// KIỂM TRA VẬT CẢN trước khi nhấn — thứ Playwright làm ở loc.click() mà
|
|
100
|
+
// đường "kiểu người" này bỏ qua. 23/08 đăng FB fail 3 lượt liên tiếp: toast
|
|
101
|
+
// "Thông báo mới" nổi vài giây đúng góc dưới-trái, trùng nút "Tiếp" của
|
|
102
|
+
// composer; mouse.down rơi vào toast → FB mở reel, composer kẹt ở bước 1.
|
|
103
|
+
// Vật cản thường tự tan → chờ tới 6s; vẫn còn thì giao cho click chuẩn (có
|
|
104
|
+
// actionability + retry của Playwright) thay vì nhấn bừa.
|
|
105
|
+
const blocked = () => target.evaluate((el, pt) => {
|
|
106
|
+
const h = document.elementFromPoint(pt.x, pt.y);
|
|
107
|
+
return !(h && (el === h || el.contains(h) || h.contains(el)));
|
|
108
|
+
}, p).catch(() => false);
|
|
109
|
+
for (let i = 0; i < 12 && await blocked(); i++) await page.waitForTimeout(500);
|
|
110
|
+
if (await blocked()) { await target.click({ timeout }); return; }
|
|
99
111
|
await page.mouse.down();
|
|
100
112
|
await page.waitForTimeout(randInt(55, 140));
|
|
101
113
|
await page.mouse.up();
|
|
@@ -414,6 +414,31 @@ function safeComposerRetryError(message) {
|
|
|
414
414
|
return err;
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
+
// 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
|
|
418
|
+
// CÚ CLICK: 23/08 hai lần liên tiếp bot bấm "Tiếp" thì FB nhảy sang xem reel
|
|
419
|
+
// cũ, composer đóng → "final description field not found". Đóng toast trước
|
|
420
|
+
// mỗi bước bấm; không có toast thì trả 0, rất rẻ.
|
|
421
|
+
async function dismissToasts(page) {
|
|
422
|
+
return page.evaluate(() => {
|
|
423
|
+
let closed = 0;
|
|
424
|
+
const isToast = (el) => {
|
|
425
|
+
const t = (el.innerText || '').slice(0, 120);
|
|
426
|
+
return /Thông báo mới|New notification|vừa bình luận|thích thước phim|liked your/i.test(t);
|
|
427
|
+
};
|
|
428
|
+
const roots = [...document.querySelectorAll("[role='dialog'], [role='alert'], [role='status'], div[data-visualcompletion='ignore-dynamic']")];
|
|
429
|
+
for (const r of roots) {
|
|
430
|
+
const rect = r.getBoundingClientRect();
|
|
431
|
+
if (rect.width < 40 || rect.height < 30) continue;
|
|
432
|
+
if (!isToast(r)) continue;
|
|
433
|
+
// Nút đóng: aria-label Đóng/Close, hoặc <div role=button> chứa dấu X.
|
|
434
|
+
const btn = r.querySelector("[aria-label='Đóng'], [aria-label='Close'], [aria-label*='óng thông báo']")
|
|
435
|
+
|| [...r.querySelectorAll("[role='button'], button")].find((b) => (b.getAttribute('aria-label') || '').length < 12);
|
|
436
|
+
if (btn) { btn.click(); closed++; }
|
|
437
|
+
}
|
|
438
|
+
return closed;
|
|
439
|
+
}).catch(() => 0);
|
|
440
|
+
}
|
|
441
|
+
|
|
417
442
|
async function hasVisibleReelComposer(page) {
|
|
418
443
|
return page.evaluate(() => {
|
|
419
444
|
for (const dlg of document.querySelectorAll("[role='dialog']")) {
|
|
@@ -1208,7 +1233,8 @@ async function runOnce({ page, payload, log }) {
|
|
|
1208
1233
|
// iterations, throw with diagnostics.
|
|
1209
1234
|
let published = false;
|
|
1210
1235
|
let customThumbDone = false;
|
|
1211
|
-
let pubWaitDone = false;
|
|
1236
|
+
let pubWaitDone = false;
|
|
1237
|
+
let reelViewerRecoveries = 0; // bấm Tiếp trượt vào toast → FB mở reel viewer, xem bên dưới
|
|
1212
1238
|
for (let step = 0; step < 7 && !published; step++) {
|
|
1213
1239
|
await pause(page, 3000);
|
|
1214
1240
|
await fillMetadata();
|
|
@@ -2043,6 +2069,9 @@ async function runOnce({ page, payload, log }) {
|
|
|
2043
2069
|
}
|
|
2044
2070
|
|
|
2045
2071
|
// No publish yet — advance via Tiếp.
|
|
2072
|
+
// Đóng toast thông báo TRƯỚC: nó nổi đúng vùng thao tác và cướp cú click.
|
|
2073
|
+
const _toasts = await dismissToasts(page);
|
|
2074
|
+
if (_toasts) log('info', `[fb-pw] đóng ${_toasts} toast thông báo trước khi bấm Tiếp`);
|
|
2046
2075
|
const next = await findByVerbs(nextVerbs);
|
|
2047
2076
|
if (!next) {
|
|
2048
2077
|
// Final "Cài đặt thước phim" step: the only action is "Đăng", which FB
|
|
@@ -2062,6 +2091,12 @@ async function runOnce({ page, payload, log }) {
|
|
|
2062
2091
|
if (waitResult.enabled) { step--; continue; }
|
|
2063
2092
|
await dumpInventory(page, log, `no-advance-${step + 1}`);
|
|
2064
2093
|
await dumpFailure(page, `no-advance-${step + 1}`, log);
|
|
2094
|
+
// Composer biến mất (URL nhảy sang /reel/… vì toast cướp click, hoặc
|
|
2095
|
+
// FB tự đóng) → CHẠY LẠI cả lượt upload thay vì báo lỗi khó hiểu.
|
|
2096
|
+
// An toàn vì chưa hề bấm Đăng ở nhánh này.
|
|
2097
|
+
if (!(await hasVisibleReelComposer(page))) {
|
|
2098
|
+
throw safeComposerRetryError(`FB composer đóng giữa chừng (đang ở ${page.url().slice(0, 80)}) — chạy lại lượt đăng`);
|
|
2099
|
+
}
|
|
2065
2100
|
if (!fillState.description) {
|
|
2066
2101
|
throw new Error(`FB final description field not found at step ${step + 1}`);
|
|
2067
2102
|
}
|
|
@@ -2111,6 +2146,21 @@ async function runOnce({ page, payload, log }) {
|
|
|
2111
2146
|
await dumpFailure(page, `tiep-not-clickable-${step + 1}`, log);
|
|
2112
2147
|
throw new Error(`FB Tiếp not clickable at step ${step + 1}: ${e.message}`);
|
|
2113
2148
|
}
|
|
2149
|
+
// Cú bấm trượt vào thứ khác (toast thông báo / mục thông báo) → FB mở
|
|
2150
|
+
// reel viewer đè lên, nhưng composer VẪN CÒN ở bước cũ (kiểm kê 23/08:
|
|
2151
|
+
// dialog "Tạo thước phim… Tiếp" vẫn đó bên dưới viewer). Đóng viewer
|
|
2152
|
+
// bằng Escape rồi bấm lại — khỏi phải chạy lại cả lượt upload.
|
|
2153
|
+
if (/\/reel\/\d+/.test(page.url()) && (await composerStepLabel()) === stepLabelBefore && stepLabelBefore) {
|
|
2154
|
+
reelViewerRecoveries++;
|
|
2155
|
+
log('warn', `[fb-pw] bấm Tiếp bị trượt → FB mở reel viewer (${page.url().slice(0, 60)}), composer vẫn ở bước cũ — đóng viewer, bấm lại (${reelViewerRecoveries}/3)`);
|
|
2156
|
+
await page.keyboard.press('Escape').catch(() => {});
|
|
2157
|
+
await page.waitForTimeout(1500);
|
|
2158
|
+
if (/\/reel\/\d+/.test(page.url())) {
|
|
2159
|
+
await page.locator("[aria-label='Đóng'], [aria-label='Close']").first().click({ timeout: 3000 }).catch(() => {});
|
|
2160
|
+
await page.waitForTimeout(1500);
|
|
2161
|
+
}
|
|
2162
|
+
if (reelViewerRecoveries <= 3 && await hasVisibleReelComposer(page)) { step--; continue; }
|
|
2163
|
+
}
|
|
2114
2164
|
|
|
2115
2165
|
// BS-only side-wizard jump fallback — DO NOT run in Page-wall mode.
|
|
2116
2166
|
// The BS composer has a left-side wizard with clickable step headers
|