channel-worker 2.5.121 → 2.5.122

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.121",
3
+ "version": "2.5.122",
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": {
@@ -11,7 +11,7 @@
11
11
  // caption hơn, API tự biết là không đủ để ghép và để nguyên cho người.
12
12
  //
13
13
  // Payload: { channel_id, post_ids: ['1234567890', …] }
14
- // Trả về: { captions: [{ post_id, og, page_text, url }], read, asked }
14
+ // Trả về: { captions: [{ post_id, og, page_text, blocks, url }], read, asked }
15
15
  //
16
16
  // Việc SO SÁNH nằm ở API (services/ReelCaptionMatch) chứ không ở đây: một bảng
17
17
  // luật, test offline được, và worker không cần biết tiêu đề của idea.
@@ -23,17 +23,52 @@ const SETTLE_MS = 4000;
23
23
  // một kênh hiếm khi quá vài cái; nhiều hơn thì để lượt sau, đừng giữ profile lâu.
24
24
  const MAX_POSTS = 8;
25
25
 
26
- // og:description là mô tả của ĐÚNG reel đang mở. Văn bản trang là đường lui —
27
- // trình xem reel cuộn cả reel kế bên vào DOM nên API chỉ tin phần đầu của nó.
26
+ // Đuôi Facebook thêm vào khi cắt caption dài. Phải gỡ, không thì khối chữ
27
+ // không còn là "khúc đầu của tiêu đề" và luật ghép theo tiền tố trượt.
28
+ const MORE_TAIL_RE = /(?:[…\.]{1,3}\s*)?(?:see more|xem thêm|see translation|xem bản dịch)\s*$/i;
29
+
30
+ function cleanBlock(s) {
31
+ return String(s == null ? '' : s)
32
+ .replace(/\s+/g, ' ')
33
+ .trim()
34
+ .replace(MORE_TAIL_RE, '')
35
+ .replace(/[…\s]+$/, '')
36
+ .trim();
37
+ }
38
+
39
+ // Lấy mọi đường có thể có caption. ĐO TẬN TAY 20/09/2026 trên reel
40
+ // 4007094836262842 (kênh "tán gái", profile kenh-25), trang mở trong phiên ĐÃ
41
+ // ĐĂNG NHẬP:
42
+ // - `document.querySelectorAll('meta[property]')` → **RỖNG**. Trang reel của
43
+ // phiên đăng nhập không có thẻ og nào, nên og luôn 0 ký tự. Giữ lại vì
44
+ // phiên khác (chưa đăng nhập, share link) vẫn có.
45
+ // - `[role='main']` chỉ có **18 ký tự** — caption KHÔNG nằm trong đó. Bản cũ
46
+ // đọc đúng chỗ này nên 3 bài "đăng mập mờ" của kênh treo 2 ngày, ghép=0.
47
+ // - `body.innerText` (331 ký tự) CÓ caption nhưng dính rác đầu trang
48
+ // ("Number of unread notifications 20+ 119 433K 2.4K 1.9K <tên trang> …").
49
+ // - Khối `span[dir='auto']` dài nhất CHÍNH LÀ caption, kèm đuôi "… See more".
50
+ // Nên: trả cả ba — og, page_text (đổi sang body), và `blocks` = các khối
51
+ // dir=auto đã gỡ đuôi, để API so khớp trên từng khối (ReelCaptionMatch).
28
52
  async function readCaption(page) {
29
53
  return page.evaluate(() => {
54
+ const flat = (t) => String(t || '').replace(/\s+/g, ' ').trim();
30
55
  const meta = (p) => (document.querySelector(`meta[property="${p}"]`) || {}).content || '';
31
- const main = document.querySelector("[role='main']") || document.body;
56
+ const blocks = [...document.querySelectorAll("span[dir='auto'], div[dir='auto']")]
57
+ .map((el) => flat(el.innerText))
58
+ .filter((t) => t.length >= 20)
59
+ .sort((a, b) => b.length - a.length)
60
+ .slice(0, 6);
32
61
  return {
33
62
  og: (meta('og:description') || meta('og:title') || '').slice(0, 1000),
34
- page_text: String((main && main.innerText) || '').replace(/\s+/g, ' ').trim().slice(0, 1500),
63
+ page_text: flat(document.body && document.body.innerText).slice(0, 1500),
64
+ blocks,
35
65
  };
36
- }).catch(() => ({ og: '', page_text: '' }));
66
+ }).then((c) => ({
67
+ og: cleanBlock(c.og).slice(0, 1000),
68
+ page_text: c.page_text,
69
+ // Bỏ trùng sau khi gỡ đuôi: khối cha và khối con thường ra cùng một chuỗi.
70
+ blocks: [...new Set((c.blocks || []).map(cleanBlock).filter((t) => t.length >= 20))].slice(0, 6),
71
+ })).catch(() => ({ og: '', page_text: '', blocks: [] }));
37
72
  }
38
73
 
39
74
  async function run({ page, payload = {}, log }) {
@@ -50,9 +85,10 @@ async function run({ page, payload = {}, log }) {
50
85
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: NAV_TIMEOUT_MS });
51
86
  await page.waitForTimeout(SETTLE_MS);
52
87
  const c = await readCaption(page);
53
- captions.push({ post_id: id, og: c.og, page_text: c.page_text, url });
54
- log('info', `[fb-verify] reel ${id}: og ${c.og.length} ký tự, trang ${c.page_text.length} ký tự`
55
- + (c.og ? ` — "${c.og.slice(0, 60)}"` : ''));
88
+ captions.push({ post_id: id, og: c.og, page_text: c.page_text, blocks: c.blocks, url });
89
+ const best = (c.blocks && c.blocks[0]) || c.og || '';
90
+ log('info', `[fb-verify] reel ${id}: og ${c.og.length} ký tự, trang ${c.page_text.length} ký tự, `
91
+ + `${(c.blocks || []).length} khối` + (best ? ` — "${best.slice(0, 60)}"` : ' — KHÔNG đọc được chữ nào'));
56
92
  } catch (e) {
57
93
  // Một reel đọc hỏng KHÔNG được giết cả lượt: các reel còn lại vẫn ghép được.
58
94
  log('warn', `[fb-verify] reel ${id} đọc hỏng: ${String(e && e.message || e).slice(0, 90)}`);
@@ -63,4 +99,4 @@ async function run({ page, payload = {}, log }) {
63
99
  }
64
100
 
65
101
  module.exports = { run };
66
- module.exports.__testables = { readCaption, MAX_POSTS, SETTLE_MS };
102
+ module.exports.__testables = { readCaption, cleanBlock, MORE_TAIL_RE, MAX_POSTS, SETTLE_MS };