channel-worker 2.5.57 → 2.5.59
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 +4 -2
- package/scripts/upload_facebook.js +36 -2
package/package.json
CHANGED
package/scripts/lib/human.js
CHANGED
|
@@ -127,8 +127,10 @@ async function humanWheel(page, dy) {
|
|
|
127
127
|
// space / punctuation, a rare typo fixed with Backspace. Uses keyboard.type
|
|
128
128
|
// for the character itself so IME/Unicode (Vietnamese) goes through unchanged.
|
|
129
129
|
async function humanType(page, text, { typo = 0.025, base = 95 } = {}) {
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
// Duyệt theo CODE POINT, không theo đơn vị UTF-16: emoji ngoài BMP (👶 = cặp
|
|
131
|
+
// surrogate D83D DC76) mà gõ từng nửa là FB nhận 2 ký tự lỗi "��" (founder
|
|
132
|
+
// bắt được 2026-08-23 trên caption Reels); ✨ (1 đơn vị) thì không sao.
|
|
133
|
+
for (const ch of Array.from(String(text || ''))) {
|
|
132
134
|
if (typo > 0 && chance(typo) && /[a-z]/i.test(ch)) {
|
|
133
135
|
const wrong = String.fromCharCode(ch.charCodeAt(0) + (chance(0.5) ? 1 : -1));
|
|
134
136
|
await page.keyboard.type(wrong);
|
|
@@ -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']")) {
|
|
@@ -926,7 +951,7 @@ async function runOnce({ page, payload, log }) {
|
|
|
926
951
|
if (!f) continue;
|
|
927
952
|
try {
|
|
928
953
|
await f.click({ timeout: 3000 });
|
|
929
|
-
const descText = (fbCaption || description || title || '').toString().slice(0, 2100);
|
|
954
|
+
const descText = Array.from((fbCaption || description || title || '').toString()).slice(0, 2100).join(''); // cắt theo code point, không chặt đôi emoji
|
|
930
955
|
if (!descText) break;
|
|
931
956
|
await humanType(page, descText, { typo: 0, base: descText.length > 600 ? 60 : 95 }); // long captions: faster cadence, still keystrokes
|
|
932
957
|
// Blur commits the Lexical/React value and re-runs the form's
|
|
@@ -971,7 +996,7 @@ async function runOnce({ page, payload, log }) {
|
|
|
971
996
|
if (probe) {
|
|
972
997
|
try {
|
|
973
998
|
const f = page.locator(probe);
|
|
974
|
-
const descText = (fbCaption || description || title || '').toString().slice(0, 2100);
|
|
999
|
+
const descText = Array.from((fbCaption || description || title || '').toString()).slice(0, 2100).join(''); // cắt theo code point, không chặt đôi emoji
|
|
975
1000
|
await f.click({ timeout: 3000 });
|
|
976
1001
|
await humanType(page, descText, { typo: 0, base: descText.length > 600 ? 60 : 95 }); // long captions: faster cadence, still keystrokes
|
|
977
1002
|
await page.keyboard.press('Tab').catch(() => {});
|
|
@@ -2043,6 +2068,9 @@ async function runOnce({ page, payload, log }) {
|
|
|
2043
2068
|
}
|
|
2044
2069
|
|
|
2045
2070
|
// No publish yet — advance via Tiếp.
|
|
2071
|
+
// Đóng toast thông báo TRƯỚC: nó nổi đúng vùng thao tác và cướp cú click.
|
|
2072
|
+
const _toasts = await dismissToasts(page);
|
|
2073
|
+
if (_toasts) log('info', `[fb-pw] đóng ${_toasts} toast thông báo trước khi bấm Tiếp`);
|
|
2046
2074
|
const next = await findByVerbs(nextVerbs);
|
|
2047
2075
|
if (!next) {
|
|
2048
2076
|
// Final "Cài đặt thước phim" step: the only action is "Đăng", which FB
|
|
@@ -2062,6 +2090,12 @@ async function runOnce({ page, payload, log }) {
|
|
|
2062
2090
|
if (waitResult.enabled) { step--; continue; }
|
|
2063
2091
|
await dumpInventory(page, log, `no-advance-${step + 1}`);
|
|
2064
2092
|
await dumpFailure(page, `no-advance-${step + 1}`, log);
|
|
2093
|
+
// Composer biến mất (URL nhảy sang /reel/… vì toast cướp click, hoặc
|
|
2094
|
+
// FB tự đóng) → CHẠY LẠI cả lượt upload thay vì báo lỗi khó hiểu.
|
|
2095
|
+
// An toàn vì chưa hề bấm Đăng ở nhánh này.
|
|
2096
|
+
if (!(await hasVisibleReelComposer(page))) {
|
|
2097
|
+
throw safeComposerRetryError(`FB composer đóng giữa chừng (đang ở ${page.url().slice(0, 80)}) — chạy lại lượt đăng`);
|
|
2098
|
+
}
|
|
2065
2099
|
if (!fillState.description) {
|
|
2066
2100
|
throw new Error(`FB final description field not found at step ${step + 1}`);
|
|
2067
2101
|
}
|