channel-worker 2.5.108 → 2.5.110

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/lib/api-client.js CHANGED
@@ -68,7 +68,14 @@ class ApiClient {
68
68
  async getNextCommand(workerId) {
69
69
  // Daemon-handled types. `_pw` variants route to the Playwright pipeline
70
70
  // (lib/playwright-runner → scripts/<base>.js) instead of the extension.
71
- const workerTypes = 'launch_profile,close_profile,launch_veo3_profile,set_profile_proxy,save_file,set_thumbnail,set_tags,set_file_input,click_and_upload,type_text,verify_logins,update_extension,sync_youtube_stats,restart_worker,upload_youtube_pw,upload_tiktok_pw,upload_facebook_pw,upload_facebook_photo_pw,warmup_youtube_pw,warmup_facebook_pw,warmup_tiktok_pw,nurture_facebook_pw,fetch_facebook_reel_stats_pw,comment_facebook_pw,scrape_affiliate_products,ingest_shopee_product,get_affiliate_link';
71
+ //
72
+ // ⚠️ ĐÂY LÀ CỔNG THỨ HAI, DỄ QUÊN: API lọc lệnh trả về theo đúng danh sách
73
+ // này (WorkerController.getCommands → filter.type.$in). Thêm loại lệnh mới
74
+ // mà quên khai ở đây thì lệnh nằm PENDING vĩnh viễn, không một dòng lỗi
75
+ // nào — xảy ra 14/09 với verify_facebook_reels_pw (cổng thứ nhất là enum
76
+ // của app/models/WorkerCommand.js, cũng đã quên một lần cùng ngày).
77
+ // api/tests/publish-verify-wiring.test.js canh cả hai cổng.
78
+ const workerTypes = 'launch_profile,close_profile,launch_veo3_profile,set_profile_proxy,save_file,set_thumbnail,set_tags,set_file_input,click_and_upload,type_text,verify_logins,update_extension,sync_youtube_stats,restart_worker,upload_youtube_pw,upload_tiktok_pw,upload_facebook_pw,upload_facebook_photo_pw,warmup_youtube_pw,warmup_facebook_pw,warmup_tiktok_pw,nurture_facebook_pw,fetch_facebook_reel_stats_pw,verify_facebook_reels_pw,comment_facebook_pw,scrape_affiliate_products,ingest_shopee_product,get_affiliate_link';
72
79
  return this.request('GET', `/workers/commands?worker_id=${workerId}&types=${encodeURIComponent(workerTypes)}`);
73
80
  }
74
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.5.108",
3
+ "version": "2.5.110",
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": {
@@ -0,0 +1,66 @@
1
+ // Đọc CAPTION thật của một số reel để API ghép với bài "đăng mập mờ".
2
+ //
3
+ // Vì sao cần (chủ tịch hỏi 14/09): lượt quét tab Reels chỉ lấy post_id + lượt
4
+ // xem, nên bộ đối chiếu xuôi chỉ suy được "reel này MỚI xuất hiện sau lượt đăng
5
+ // đó". Đo 12 ca thật cùng ngày: 2 ca có 2-3 lượt đăng xen giữa hai lượt quét →
6
+ // suy luận thứ tự là đoán bừa. Caption thì chứng minh được: khi đăng, worker
7
+ // ghép "title + hashtag" vào ô mô tả thước phim, nên caption của bài mình LUÔN
8
+ // chứa nguyên tiêu đề.
9
+ //
10
+ // Script này CHỈ ĐỌC — không bấm, không sửa, không đăng gì. Hỏng thì trả về ít
11
+ // caption hơn, API tự biết là không đủ để ghép và để nguyên cho người.
12
+ //
13
+ // Payload: { channel_id, post_ids: ['1234567890', …] }
14
+ // Trả về: { captions: [{ post_id, og, page_text, url }], read, asked }
15
+ //
16
+ // Việc SO SÁNH nằm ở API (services/ReelCaptionMatch) chứ không ở đây: một bảng
17
+ // luật, test offline được, và worker không cần biết tiêu đề của idea.
18
+
19
+ const NAV_TIMEOUT_MS = 45_000;
20
+ // Đủ để khung reel dựng xong; script chỉ đọc nên không cần chờ lâu như lượt đăng.
21
+ const SETTLE_MS = 4000;
22
+ // Trần số reel đọc trong một lượt — mỗi cái một lần mở trang. Bài mập mờ của
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
+ const MAX_POSTS = 8;
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ó.
28
+ async function readCaption(page) {
29
+ return page.evaluate(() => {
30
+ const meta = (p) => (document.querySelector(`meta[property="${p}"]`) || {}).content || '';
31
+ const main = document.querySelector("[role='main']") || document.body;
32
+ return {
33
+ og: (meta('og:description') || meta('og:title') || '').slice(0, 1000),
34
+ page_text: String((main && main.innerText) || '').replace(/\s+/g, ' ').trim().slice(0, 1500),
35
+ };
36
+ }).catch(() => ({ og: '', page_text: '' }));
37
+ }
38
+
39
+ async function run({ page, payload = {}, log }) {
40
+ const ids = [...new Set((payload.post_ids || []).map((x) => String(x || '').trim()).filter((x) => /^\d{8,20}$/.test(x)))];
41
+ if (!ids.length) throw new Error('payload.post_ids rỗng — không có reel nào để đọc');
42
+ const asked = ids.length;
43
+ const take = ids.slice(0, MAX_POSTS);
44
+ log('info', `[fb-verify] đọc caption ${take.length}/${asked} reel`);
45
+
46
+ const captions = [];
47
+ for (const id of take) {
48
+ const url = `https://www.facebook.com/reel/${id}/`;
49
+ try {
50
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: NAV_TIMEOUT_MS });
51
+ await page.waitForTimeout(SETTLE_MS);
52
+ 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)}"` : ''));
56
+ } catch (e) {
57
+ // 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
+ log('warn', `[fb-verify] reel ${id} đọc hỏng: ${String(e && e.message || e).slice(0, 90)}`);
59
+ }
60
+ }
61
+ if (!captions.length) throw new Error(`không đọc được caption của reel nào (${take.length} lượt thử)`);
62
+ return { captions, read: captions.length, asked };
63
+ }
64
+
65
+ module.exports = { run };
66
+ module.exports.__testables = { readCaption, MAX_POSTS, SETTLE_MS };