channel-worker 2.5.35 → 2.5.37
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_photo.js +69 -28
package/package.json
CHANGED
|
@@ -221,45 +221,86 @@ async function run({ page, payload, log }) {
|
|
|
221
221
|
|
|
222
222
|
// 6) Click "Đăng".
|
|
223
223
|
log('info', '[fbphoto] clicking publish…');
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
224
|
+
// Tag the first VISIBLE + ENABLED button matching `verbs`. Mirrors the
|
|
225
|
+
// proven upload_facebook.js findByVerbs: searches ALL [role='dialog']
|
|
226
|
+
// (not just the first — FB renders extra hidden dialogs), matches
|
|
227
|
+
// innerText||textContent (button label often lives in a nested span), then
|
|
228
|
+
// falls back to the whole document. Returns the matched verb, or null.
|
|
229
|
+
const tagCta = async (verbs) => page.evaluate((vbs) => {
|
|
230
|
+
const tryScope = (roots, verb) => {
|
|
231
|
+
for (const root of roots) {
|
|
232
|
+
let best = null;
|
|
233
|
+
for (const el of root.querySelectorAll("button, [role='button']")) {
|
|
234
|
+
const t = (el.innerText || el.textContent || '').trim();
|
|
235
|
+
const a = (el.getAttribute('aria-label') || '').trim();
|
|
236
|
+
if (t !== verb && a !== verb) continue;
|
|
237
|
+
const r = el.getBoundingClientRect();
|
|
238
|
+
if (r.width < 8 || r.height < 8) continue;
|
|
239
|
+
const cs = getComputedStyle(el);
|
|
240
|
+
if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') continue;
|
|
241
|
+
if (el.getAttribute('aria-disabled') === 'true' || el.disabled) continue;
|
|
242
|
+
best = el;
|
|
233
243
|
}
|
|
244
|
+
if (best) return best;
|
|
234
245
|
}
|
|
246
|
+
return null;
|
|
247
|
+
};
|
|
248
|
+
for (const v of vbs) {
|
|
249
|
+
const dlgs = Array.from(document.querySelectorAll("[role='dialog']"));
|
|
250
|
+
let el = tryScope(dlgs, v) || tryScope([document], v);
|
|
251
|
+
if (el) { el.setAttribute('__fbphoto_cta__', '1'); return v; }
|
|
235
252
|
}
|
|
236
|
-
return
|
|
237
|
-
},
|
|
253
|
+
return null;
|
|
254
|
+
}, verbs).catch(() => null);
|
|
255
|
+
const clickTaggedCta = async () => {
|
|
256
|
+
await page.locator("[__fbphoto_cta__='1']").click({ timeout: 5000 }).catch(() => {});
|
|
257
|
+
await page.evaluate(() => document.querySelectorAll('[__fbphoto_cta__]').forEach((e) => e.removeAttribute('__fbphoto_cta__'))).catch(() => {});
|
|
258
|
+
};
|
|
238
259
|
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
260
|
+
// Step loop: the FB photo composer is MULTI-STEP — "Tiếp" advances through
|
|
261
|
+
// audience/settings screens; only the LAST step shows "Đăng"/"Chia sẻ ngay"
|
|
262
|
+
// (the failing run stopped here: it saw a "Tiếp", not a publish button).
|
|
263
|
+
// Prefer a publish button when present; else advance via "Tiếp". Retry a
|
|
264
|
+
// few times when neither is enabled yet (FB disables while ingesting).
|
|
265
|
+
const nextVerbs = ['Tiếp', 'Tiếp theo', 'Next', 'Continue'];
|
|
266
|
+
let published = false;
|
|
267
|
+
let stalls = 0;
|
|
268
|
+
for (let step = 0; step < 8 && !published; step++) {
|
|
269
|
+
const pubHit = await tagCta(publishVerbs);
|
|
270
|
+
if (pubHit) { log('info', `[fbphoto] publish via "${pubHit}" (step ${step + 1})`); await clickTaggedCta(); published = true; break; }
|
|
271
|
+
const nextHit = await tagCta(nextVerbs);
|
|
272
|
+
if (nextHit) { log('info', `[fbphoto] advance via "${nextHit}" (step ${step + 1})`); await clickTaggedCta(); await page.waitForTimeout(2800); stalls = 0; continue; }
|
|
273
|
+
// Neither publish nor Tiếp enabled — FB still ingesting the photo. Wait.
|
|
274
|
+
if (++stalls > 10) break; // ~30s of no CTA → give up
|
|
275
|
+
if (stalls === 1 || stalls === 6) {
|
|
276
|
+
// Diagnostic: what buttons ARE in the composer right now?
|
|
277
|
+
const btns = await page.evaluate(() => {
|
|
278
|
+
const out = [];
|
|
279
|
+
for (const dlg of document.querySelectorAll("[role='dialog']")) {
|
|
280
|
+
for (const el of dlg.querySelectorAll("button, [role='button']")) {
|
|
281
|
+
const t = (el.innerText || el.textContent || '').trim().slice(0, 30);
|
|
282
|
+
const r = el.getBoundingClientRect();
|
|
283
|
+
if (t && r.width > 20 && r.height > 8) out.push(`${t}${el.getAttribute('aria-disabled') === 'true' ? '(disabled)' : ''}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return [...new Set(out)].slice(0, 20);
|
|
287
|
+
}).catch(() => []);
|
|
288
|
+
log('info', `[fbphoto] composer buttons: ${JSON.stringify(btns)}`);
|
|
248
289
|
}
|
|
290
|
+
log('info', `[fbphoto] no CTA yet at step ${step + 1} — waiting…`);
|
|
291
|
+
step--; // don't count a pure wait against the step budget
|
|
292
|
+
await page.waitForTimeout(3000);
|
|
249
293
|
}
|
|
250
|
-
if (!
|
|
294
|
+
if (!published) {
|
|
251
295
|
await dumpFailure(page, 'no-publish-btn', log);
|
|
252
|
-
throw new Error('FB: nút "Đăng"
|
|
296
|
+
throw new Error('FB: không tìm thấy nút "Đăng" sau các bước "Tiếp" (composer đổi layout?).');
|
|
253
297
|
}
|
|
254
298
|
|
|
255
|
-
//
|
|
299
|
+
// Verify commit + retry — a click can register without FB posting.
|
|
256
300
|
await page.waitForTimeout(6000);
|
|
257
301
|
for (let commitTry = 0; commitTry < 3 && (await composerStillOpen(page, publishVerbs)); commitTry++) {
|
|
258
|
-
log('warn', `[fbphoto] composer still open — re-clicking
|
|
259
|
-
if (await
|
|
260
|
-
await page.locator("[__fbphoto_pub__='1']").click({ timeout: 5000 }).catch(() => {});
|
|
261
|
-
await page.evaluate(() => document.querySelectorAll('[__fbphoto_pub__]').forEach((e) => e.removeAttribute('__fbphoto_pub__'))).catch(() => {});
|
|
262
|
-
}
|
|
302
|
+
log('warn', `[fbphoto] composer still open — re-clicking publish (retry ${commitTry + 1}/3)`);
|
|
303
|
+
if (await tagCta(publishVerbs)) await clickTaggedCta();
|
|
263
304
|
await page.waitForTimeout(6000);
|
|
264
305
|
}
|
|
265
306
|
if (await composerStillOpen(page, publishVerbs)) {
|