channel-worker 2.5.98 → 2.5.100
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
|
@@ -28,22 +28,17 @@ function parseViews(txt) {
|
|
|
28
28
|
return Math.round(num * mul);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (m && !['profile.php', 'reel', 'video', 'videos'].includes(m[1])) return `https://www.facebook.com/${m[1]}/?sk=reels_tab`;
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}).catch(() => null);
|
|
46
|
-
}
|
|
31
|
+
// Tìm link tab Reels: DÙNG CHUNG với script đăng (upload_facebook), không giữ
|
|
32
|
+
// bản riêng nữa. Bản riêng cũ chỉ có 3 chiến lược (sk=reels_tab / profile.php
|
|
33
|
+
// / "Dòng thời gian") — trên TRANG CHỦ của Page hiện nay không có anchor nào
|
|
34
|
+
// như thế, chỉ có shortcut /<id số>/ hoặc /<handle>/reels/ (chiến lược 4-5,
|
|
35
|
+
// thêm ở script đăng 09/09). Đo 30h tới 10/09 15:15: 15/21 lượt bám bài hỏng
|
|
36
|
+
// là "không tìm được link tab Reels" — Góc Người Trồng Nấm 6 lượt, Chuyện Nhà
|
|
37
|
+
// Nông 3, Nông Trại Phục Linh 2… — trong khi CÙNG profile đó script đăng đêm
|
|
38
|
+
// 09/09 tìm được tab để so trước/sau. Bản riêng còn thiếu cả bộ lọc "anchor
|
|
39
|
+
// của bài trong bảng tin" (gốc 2 link page lạ sáng 10/09) nên có thể quét
|
|
40
|
+
// nhầm tab Reels của page KHÁC mà không hay.
|
|
41
|
+
const { discoverReelsTabUrl } = require('./upload_facebook').__testables;
|
|
47
42
|
|
|
48
43
|
// Số người theo dõi của page, đọc ngay trên chính trang đang đứng (header hiện
|
|
49
44
|
// ở mọi tab của page/profile) — không tốn thêm lượt mở profile nào. Ô
|
|
@@ -421,18 +416,71 @@ async function readAudiencePage(page, log) {
|
|
|
421
416
|
return a;
|
|
422
417
|
}
|
|
423
418
|
|
|
419
|
+
// Trang Insights hỏng thì chụp lại — script này trước 10/09 không dump gì nên
|
|
420
|
+
// 8 kênh kẹt chip "Hôm nay" hai ngày liền mà không có lấy một tấm ảnh để hiểu.
|
|
421
|
+
// Ngoài ảnh, liệt kê mọi chữ có thể là nhãn khoảng thời gian kèm vị trí: để
|
|
422
|
+
// biết chip thật đang ghi gì, và có cái "Hôm nay" lạ nào đứng TRƯỚC chip trong
|
|
423
|
+
// DOM (currentRangeLabel lấy phần tử khớp đầu tiên) hay không.
|
|
424
|
+
async function dumpInsightsFailure(page, tag, log) {
|
|
425
|
+
try {
|
|
426
|
+
const os = require('os'); const fs = require('fs'); const path = require('path');
|
|
427
|
+
const dir = path.join(os.tmpdir(), 'cm-worker-pw');
|
|
428
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
429
|
+
const png = path.join(dir, `fb-${tag}-${Date.now()}.png`);
|
|
430
|
+
await page.screenshot({ path: png, fullPage: false, timeout: 8000 }).catch(() => {});
|
|
431
|
+
const labels = await page.evaluate(() => {
|
|
432
|
+
const clean = (v) => String(v || '').replace(/\s+/g, ' ').trim();
|
|
433
|
+
const out = [];
|
|
434
|
+
for (const e of document.querySelectorAll('div[role="button"],div,span')) {
|
|
435
|
+
const t = clean(e.innerText);
|
|
436
|
+
if (!t || t.length >= 70) continue;
|
|
437
|
+
if (!/(Hôm nay|Today|Tùy chỉnh|Custom|ngày qua|Last \d+ days|Năm nay|This year)/i.test(t)) continue;
|
|
438
|
+
const r = e.getBoundingClientRect();
|
|
439
|
+
out.push(`${e.tagName.toLowerCase()}[${e.getAttribute('role') || ''}] y=${Math.round(r.y)} w=${Math.round(r.width)} "${t}"`);
|
|
440
|
+
if (out.length >= 25) break;
|
|
441
|
+
}
|
|
442
|
+
return out;
|
|
443
|
+
}).catch(() => []);
|
|
444
|
+
labels.forEach((l, i) => log('info', ` ${tag} label[${i}] ${l}`));
|
|
445
|
+
let shot = null;
|
|
446
|
+
if (fs.existsSync(png) && typeof log.uploadDebugScreenshot === 'function') {
|
|
447
|
+
try { shot = await log.uploadDebugScreenshot(png, { tag }); } catch (e) { log('warn', `[insights] tải ảnh dump hỏng: ${e.message}`); }
|
|
448
|
+
}
|
|
449
|
+
log('warn', `[insights] dump ${tag} — url=${page.url()} screenshot=${shot?.signed_url || png}`,
|
|
450
|
+
shot ? { debug_screenshot: shot } : { debug_screenshot_local_path: png });
|
|
451
|
+
} catch {}
|
|
452
|
+
}
|
|
453
|
+
|
|
424
454
|
// Lượt nền: hai trang view + một trang đối tượng, rồi gửi thẳng số thô về API.
|
|
425
455
|
// Việc khớp bài với idea và tính hiệu để lòi ra "bài cũ đang nổ" làm ở API —
|
|
426
456
|
// worker không giữ trạng thái ngày hôm qua.
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
457
|
+
//
|
|
458
|
+
// THỨ TỰ (đổi 10/09): NGÀY HÔM QUA → FOLLOWER → 7 NGÀY, và "7 ngày" hỏng
|
|
459
|
+
// KHÔNG chết cả lượt. Bản cũ đọc "7 ngày qua" trước (làm ấm trang) và ném lỗi
|
|
460
|
+
// khi chip không đổi — mà trang đó chỉ để lấy danh sách bài. Đo 10/09: 8/63
|
|
461
|
+
// kênh (09/09: 11/61) kẹt chip "Hôm nay" đúng ở trang này, trong khi trang
|
|
462
|
+
// ngày hôm qua + follower của CHÍNH các kênh đó vẫn đọc được (log win-worker:
|
|
463
|
+
// chip "Tùy chỉnh: 9 Tháng 9" ra đều). Một chip kẹt làm mất view hôm qua +
|
|
464
|
+
// follower — hai số quan trọng nhất — cho cả ngày, /analytics báo "Chưa quét".
|
|
465
|
+
// Ngày hôm qua tự có vòng nạp lại 3 lần nên không cần trang khác làm ấm.
|
|
466
|
+
const DEFAULT_READERS = { readOneDay, readAudiencePage, readViewsPage, dump: dumpInsightsFailure };
|
|
467
|
+
async function runInsights({ page, payload, log }, R = DEFAULT_READERS) {
|
|
431
468
|
const yIso = vnYesterdayIso();
|
|
432
|
-
const yest = await readOneDay(page, yIso, log);
|
|
433
|
-
const audience = await readAudiencePage(page, log);
|
|
469
|
+
const yest = await R.readOneDay(page, yIso, log);
|
|
470
|
+
const audience = await R.readAudiencePage(page, log);
|
|
471
|
+
let week = { range: 'LAST_7D', label: '', total: null, pct: null, posts: [] };
|
|
472
|
+
let weekError = null;
|
|
473
|
+
try {
|
|
474
|
+
week = await R.readViewsPage(page, 'LAST_7D', log);
|
|
475
|
+
} catch (e) {
|
|
476
|
+
weekError = String((e && e.message) || e);
|
|
477
|
+
log('warn', `[insights] 7 ngày qua hỏng (${weekError}) — vẫn ghi ngày hôm qua + follower, chỉ mất danh sách bài`);
|
|
478
|
+
await R.dump(page, 'insights-week', log);
|
|
479
|
+
}
|
|
434
480
|
if (yest.views == null && week.total == null) {
|
|
435
|
-
throw new Error(
|
|
481
|
+
throw new Error(weekError
|
|
482
|
+
? `không đọc được cả ngày hôm qua lẫn 7 ngày — ${weekError}`
|
|
483
|
+
: 'không đọc được tổng lượt xem trên trang Insights — FB đổi giao diện?');
|
|
436
484
|
}
|
|
437
485
|
if (typeof log.apiRequest !== 'function') throw new Error('log.apiRequest không có — cần channel-worker ≥ 2.5.55');
|
|
438
486
|
const d = await log.apiRequest('POST', '/analytics/fb-insights', {
|
|
@@ -448,6 +496,7 @@ async function runInsights({ page, payload, log }) {
|
|
|
448
496
|
views_yesterday: yest.views, views_7d: week.total,
|
|
449
497
|
posts: week.posts.length, matched: d?.matched ?? null,
|
|
450
498
|
followers: audience.followers, unattributed: d?.unattributed ?? null,
|
|
499
|
+
...(weekError ? { week_error: weekError } : {}),
|
|
451
500
|
};
|
|
452
501
|
}
|
|
453
502
|
|
|
@@ -463,7 +512,7 @@ async function run({ page, payload, log }) {
|
|
|
463
512
|
if (!tabUrl) {
|
|
464
513
|
await page.goto(page_url || 'https://www.facebook.com/', { waitUntil: 'domcontentloaded', timeout: 45_000 });
|
|
465
514
|
await page.waitForTimeout(4000);
|
|
466
|
-
tabUrl = await
|
|
515
|
+
tabUrl = await discoverReelsTabUrl(page, log); // có log → in mẫu href khi không khớp
|
|
467
516
|
}
|
|
468
517
|
if (!tabUrl) throw new Error('không tìm được link tab Reels của page');
|
|
469
518
|
log('info', `[fb-stats] ${mode} · reels_tab: ${tabUrl.slice(0, 100)}`);
|
|
@@ -511,4 +560,4 @@ async function run({ page, payload, log }) {
|
|
|
511
560
|
return { ok: true, mode, tiles: tiles.length, written: d?.written || 0, matched: d?.matched || 0, rising: d?.rising || 0, alerts: d?.alerts || 0, followers };
|
|
512
561
|
}
|
|
513
562
|
|
|
514
|
-
module.exports = { run, __testables: { parseViews, scrapeFollowers, parseExact, parseVnPostTime } };
|
|
563
|
+
module.exports = { run, __testables: { parseViews, scrapeFollowers, parseExact, parseVnPostTime, runInsights, dumpInsightsFailure, discoverReelsTabUrl } };
|