channel-worker 2.5.140 → 2.5.141

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.
@@ -347,8 +347,11 @@ class CommandPoller {
347
347
  });
348
348
  if (res.status === 'needs_verify') {
349
349
  // Anti-bot bounce — báo sạch, KHÔNG thử lại để khỏi đâm thêm captcha.
350
- console.warn(`[shopee] lấy link: needs_verify — ${res.reason}`);
351
- await this.api.updateCommand(command._id, { status: 'done', result: { needs_verify: true, reason: res.reason } });
350
+ // Ghi kèm tên profile: có hai phiên (Shopee1, Shopee2) và người xử lý
351
+ // phải biết mở đúng cái nào để kéo captcha.
352
+ const reason = `${res.reason} [${profileName}]`;
353
+ console.warn(`[shopee] lấy link: needs_verify — ${reason}`);
354
+ await this.api.updateCommand(command._id, { status: 'done', result: { needs_verify: true, reason } });
352
355
  return;
353
356
  }
354
357
  if (res.status !== 'ok') {
@@ -1,4 +1,4 @@
1
- const { findAffiliatePage, isBlockedState } = require('./shopee-scraper');
1
+ const { findAffiliatePage, isBlockedState, isAntiBotBody, ANTI_BOT_REASON } = require('./shopee-scraper');
2
2
 
3
3
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4
4
 
@@ -72,7 +72,10 @@ async function fetchConversionReport(ctx, {
72
72
  }
73
73
  }, { pageSize, pageNum, start: window.start, end: window.end });
74
74
 
75
- if (isBlockedState(page.url(), result) || result.json?.code === 90309999) {
75
+ if (isAntiBotBody(result.json)) {
76
+ return { status: 'needs_verify', rows, reason: ANTI_BOT_REASON };
77
+ }
78
+ if (isBlockedState(page.url(), result)) {
76
79
  return { status: 'needs_verify', rows, reason: `blocked while reading report (http ${result.status}, code ${result.json?.code})` };
77
80
  }
78
81
  if (result.status !== 200 || result.json?.code !== 0) {
@@ -146,7 +149,10 @@ async function fetchClickReport(ctx, {
146
149
  }
147
150
  }, { pageSize, pageNum, start: window.start, end: window.end });
148
151
 
149
- if (isBlockedState(page.url(), result) || result.json?.code === 90309999) {
152
+ if (isAntiBotBody(result.json)) {
153
+ return { status: 'needs_verify', rows, reason: ANTI_BOT_REASON };
154
+ }
155
+ if (isBlockedState(page.url(), result)) {
150
156
  return { status: 'needs_verify', rows, reason: `blocked while reading click report (http ${result.status}, code ${result.json?.code})` };
151
157
  }
152
158
  if (result.status !== 200 || result.json?.code !== 0) {
@@ -228,7 +234,10 @@ async function fetchSocialMediaAccounts(ctx, { timeoutMs = 45000, log = null } =
228
234
  }
229
235
  });
230
236
 
231
- if (isBlockedState(page.url(), result) || result.json?.code === 90309999) {
237
+ if (isAntiBotBody(result.json)) {
238
+ return { status: 'needs_verify', accounts: [], reason: ANTI_BOT_REASON };
239
+ }
240
+ if (isBlockedState(page.url(), result)) {
232
241
  return { status: 'needs_verify', accounts: [], reason: `blocked while reading media info (http ${result.status}, code ${result.json?.code})` };
233
242
  }
234
243
  if (result.status !== 200 || result.json?.code !== 0) {
@@ -183,6 +183,21 @@ function isBlockedState(pageUrl, res) {
183
183
  return false;
184
184
  }
185
185
 
186
+ // Thân phản hồi mang mã chặn chống bot của Shopee.
187
+ //
188
+ // 25/09/2026, lượt lấy link thật trên Shopee2: http 200, thân
189
+ // `{ error: 90309999, is_customized: false, is_login: true }`, rồi tab affiliate
190
+ // bị đẩy sang verify/traffic → verify/captcha. Mã nằm ở `error`, KHÔNG ở `code`
191
+ // — các chỗ so `json.code === 90309999` trước đây trượt hết, nên lấy link ra
192
+ // `failed` với lỗi thô "error 90309999" và cửa nghỉ captcha của ShopeeRate
193
+ // (chỉ đọc `result.needs_verify`) không đóng. Xét cả hai chỗ cho chắc.
194
+ const ANTI_BOT_CODE = 90309999;
195
+ function isAntiBotBody(json) {
196
+ if (!json || typeof json !== 'object') return false;
197
+ return Number(json.error) === ANTI_BOT_CODE || Number(json.code) === ANTI_BOT_CODE;
198
+ }
199
+ const ANTI_BOT_REASON = `Shopee chặn chống bot (error ${ANTI_BOT_CODE}) — mở profile kéo captcha rồi thử lại`;
200
+
186
201
  // Tab affiliate nào đang mở — so bằng HOSTNAME, không bằng chuỗi con.
187
202
  //
188
203
  // 11/09/2026, đo trên profile Shopee1 thật: tab captcha cũ
@@ -385,7 +400,10 @@ async function scrapeOffers(ctx, {
385
400
  } catch (e) { return { status: 0, error: String(e.message) }; }
386
401
  }, { st, offset, page_limit, mid, listType, kw, ft: filter_types });
387
402
 
388
- if (isBlockedState(page.url(), res) || res.json?.code === 90309999) {
403
+ if (isAntiBotBody(res.json)) {
404
+ return { status: 'needs_verify', products: [...byId.values()], raw_count: rawCount, reason: ANTI_BOT_REASON };
405
+ }
406
+ if (isBlockedState(page.url(), res)) {
389
407
  return { status: 'needs_verify', products: [...byId.values()], raw_count: rawCount, reason: `blocked mid-scrape (http ${res.status}, code ${res.json?.code})` };
390
408
  }
391
409
  if (res.status !== 200 || res.json?.code !== 0) {
@@ -501,6 +519,9 @@ async function generateOfferLink(ctx, { items = [], subIds = [], timeoutMs = 450
501
519
  } catch (e) { return { status: 0, error: String(e.message) }; }
502
520
  }, { list, subs });
503
521
 
522
+ if (isAntiBotBody(res.json)) {
523
+ return { status: 'needs_verify', links: [], reason: ANTI_BOT_REASON };
524
+ }
504
525
  if (isBlockedState(page.url(), res)) {
505
526
  return { status: 'needs_verify', links: [], reason: `blocked (http ${res.status})` };
506
527
  }
@@ -555,4 +576,4 @@ function parseProductUrl(url = '') {
555
576
  return null;
556
577
  }
557
578
 
558
- module.exports = { ingestProduct, scrapeOffers, generateOfferLink, linkFailureReason, cleanSubId, parseProductUrl, parseOfferRow, parsePdpItem, categoryGroup, pctToNumber, AFFILIATE_CATEGORIES, applyFilters, isBlockedState, findAffiliatePage, pruneTabs, isJunkTab };
579
+ module.exports = { ingestProduct, scrapeOffers, generateOfferLink, linkFailureReason, cleanSubId, parseProductUrl, parseOfferRow, parsePdpItem, categoryGroup, pctToNumber, AFFILIATE_CATEGORIES, applyFilters, isBlockedState, isAntiBotBody, ANTI_BOT_REASON, findAffiliatePage, pruneTabs, isJunkTab };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.5.140",
3
+ "version": "2.5.141",
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": {