channel-worker 2.5.35 → 2.5.36

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.35",
3
+ "version": "2.5.36",
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": {
@@ -221,45 +221,59 @@ async function run({ page, payload, log }) {
221
221
 
222
222
  // 6) Click "Đăng".
223
223
  log('info', '[fbphoto] clicking publish…');
224
- const clickPublish = async () => page.evaluate((vbs) => {
224
+ // Tag the first enabled button inside the composer matching `verbs`
225
+ // (bottom-half CTA preferred). Returns the matched verb, or null.
226
+ const tagCta = async (verbs) => page.evaluate((vbs) => {
225
227
  const dlg = document.querySelector("[role='dialog']") || document;
228
+ const btns = [...dlg.querySelectorAll("div[role='button'], button, [role='button']")];
226
229
  for (const v of vbs) {
227
- for (const b of dlg.querySelectorAll("div[role='button'], button, [role='button']")) {
230
+ for (const b of btns) {
228
231
  const t = (b.innerText || '').trim();
229
232
  const a = (b.getAttribute('aria-label') || '').trim();
230
233
  if ((t === v || a === v) && b.getAttribute('aria-disabled') !== 'true') {
231
- b.setAttribute('__fbphoto_pub__', '1');
232
- return true;
234
+ const r = b.getBoundingClientRect();
235
+ if (r.width < 8 || r.height < 8) continue;
236
+ b.setAttribute('__fbphoto_cta__', '1');
237
+ return v;
233
238
  }
234
239
  }
235
240
  }
236
- return false;
237
- }, publishVerbs).catch(() => false);
241
+ return null;
242
+ }, verbs).catch(() => null);
243
+ const clickTaggedCta = async () => {
244
+ await page.locator("[__fbphoto_cta__='1']").click({ timeout: 5000 }).catch(() => {});
245
+ await page.evaluate(() => document.querySelectorAll('[__fbphoto_cta__]').forEach((e) => e.removeAttribute('__fbphoto_cta__'))).catch(() => {});
246
+ };
238
247
 
239
- // Wait for the publish button to enable (image still processing).
240
- let clicked = false;
241
- for (let i = 0; i < 30 && !clicked; i++) {
242
- if (await clickPublish()) {
243
- await page.locator("[__fbphoto_pub__='1']").click({ timeout: 5000 }).catch(() => {});
244
- await page.evaluate(() => document.querySelectorAll('[__fbphoto_pub__]').forEach((e) => e.removeAttribute('__fbphoto_pub__'))).catch(() => {});
245
- clicked = true;
246
- } else {
247
- await page.waitForTimeout(2000);
248
- }
248
+ // Step loop: the FB photo composer is MULTI-STEP "Tiếp" advances through
249
+ // audience/settings screens; only the LAST step shows "Đăng"/"Chia sẻ ngay"
250
+ // (the failing run stopped here: it saw a "Tiếp", not a publish button).
251
+ // Prefer a publish button when present; else advance via "Tiếp". Retry a
252
+ // few times when neither is enabled yet (FB disables while ingesting).
253
+ const nextVerbs = ['Tiếp', 'Tiếp theo', 'Next', 'Continue'];
254
+ let published = false;
255
+ let stalls = 0;
256
+ for (let step = 0; step < 8 && !published; step++) {
257
+ const pubHit = await tagCta(publishVerbs);
258
+ if (pubHit) { log('info', `[fbphoto] publish via "${pubHit}" (step ${step + 1})`); await clickTaggedCta(); published = true; break; }
259
+ const nextHit = await tagCta(nextVerbs);
260
+ if (nextHit) { log('info', `[fbphoto] advance via "${nextHit}" (step ${step + 1})`); await clickTaggedCta(); await page.waitForTimeout(2800); stalls = 0; continue; }
261
+ // Neither publish nor Tiếp enabled — FB still ingesting the photo. Wait.
262
+ if (++stalls > 10) break; // ~30s of no CTA → give up
263
+ log('info', `[fbphoto] no CTA yet at step ${step + 1} — waiting…`);
264
+ step--; // don't count a pure wait against the step budget
265
+ await page.waitForTimeout(3000);
249
266
  }
250
- if (!clicked) {
267
+ if (!published) {
251
268
  await dumpFailure(page, 'no-publish-btn', log);
252
- throw new Error('FB: nút "Đăng" không khả dụng (ảnh chưa xử lý xong / không tìm thấy).');
269
+ throw new Error('FB: không tìm thấy nút "Đăng" sau các bước "Tiếp" (composer đổi layout?).');
253
270
  }
254
271
 
255
- // 7) Verify commit + retry — a click can register without FB posting.
272
+ // Verify commit + retry — a click can register without FB posting.
256
273
  await page.waitForTimeout(6000);
257
274
  for (let commitTry = 0; commitTry < 3 && (await composerStillOpen(page, publishVerbs)); commitTry++) {
258
- log('warn', `[fbphoto] composer still open — re-clicking "Đăng" (retry ${commitTry + 1}/3)`);
259
- if (await clickPublish()) {
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
- }
275
+ log('warn', `[fbphoto] composer still open — re-clicking publish (retry ${commitTry + 1}/3)`);
276
+ if (await tagCta(publishVerbs)) await clickTaggedCta();
263
277
  await page.waitForTimeout(6000);
264
278
  }
265
279
  if (await composerStillOpen(page, publishVerbs)) {