channel-worker 2.5.111 → 2.5.112
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/command-poller.js +2 -0
- package/lib/shopee-scraper.js +54 -6
- package/package.json +1 -1
package/lib/command-poller.js
CHANGED
|
@@ -215,6 +215,7 @@ class CommandPoller {
|
|
|
215
215
|
page_limit: payload.page_limit ?? 20,
|
|
216
216
|
delay_ms: payload.delay_ms ?? 2500, // human-like pacing (anti-bot)
|
|
217
217
|
filters: payload.filters || {},
|
|
218
|
+
log: (m) => console.log(m), // để thấy số tab đã dọn
|
|
218
219
|
});
|
|
219
220
|
if (res.status === 'needs_verify') {
|
|
220
221
|
// Anti-bot bounce — DON'T fail/retry into more captchas. Report cleanly
|
|
@@ -304,6 +305,7 @@ class CommandPoller {
|
|
|
304
305
|
const res = await scraper.generateOfferLink(conn.context, {
|
|
305
306
|
items,
|
|
306
307
|
subIds: Array.isArray(payload.sub_ids) ? payload.sub_ids : [],
|
|
308
|
+
log: (m) => console.log(m), // để thấy số tab đã dọn
|
|
307
309
|
});
|
|
308
310
|
if (res.status === 'needs_verify') {
|
|
309
311
|
// Anti-bot bounce — báo sạch, KHÔNG thử lại để khỏi đâm thêm captcha.
|
package/lib/shopee-scraper.js
CHANGED
|
@@ -206,6 +206,51 @@ function findAffiliatePage(pages) {
|
|
|
206
206
|
return live.find((p) => urlPath(p.url()).startsWith('/offer')) || live[0] || null;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
// Tab nào là RÁC của các lượt trước — đóng được mà không mất gì.
|
|
210
|
+
//
|
|
211
|
+
// Chỉ nhận ba loại, cố ý hẹp: trang trắng, tab đang kẹt ở captcha/đăng nhập, và
|
|
212
|
+
// tab Shopee thừa ngoài cái mình sắp dùng. KHÔNG đụng tới tab của trang khác:
|
|
213
|
+
// profile này người vẫn mở tay để xác minh, đóng nhầm là cướp việc của họ.
|
|
214
|
+
function isJunkTab(url, keepUrl) {
|
|
215
|
+
if (url === keepUrl) return false;
|
|
216
|
+
if (!url || url === 'about:blank' || url === 'chrome://newtab/') return true;
|
|
217
|
+
if (isBlockedState(url, null)) return true; // captcha / buyer/login / is_from_login
|
|
218
|
+
try {
|
|
219
|
+
const h = new URL(url).hostname;
|
|
220
|
+
return h === 'affiliate.shopee.vn' || h === 'shopee.vn' || h === 'www.shopee.vn';
|
|
221
|
+
} catch { return false; }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Dọn tab trước khi làm việc.
|
|
225
|
+
//
|
|
226
|
+
// 11/09 đo trên chính profile Shopee1: 16 tab, và tab VỊ TRÍ 0 là captcha cũ từ
|
|
227
|
+
// lượt quét trước — `pages().find(u.includes('affiliate.shopee.vn'))` vớ trúng
|
|
228
|
+
// nó nên máy một mực đòi xác minh trong khi 15 tab kia đăng nhập bình thường.
|
|
229
|
+
// `findAffiliatePage` đã vá chỗ CHỌN, nhưng tab rác vẫn dồn lại mỗi ngày: mỗi
|
|
230
|
+
// lượt bị đá sang captcha để lại một tab, Chrome chậm dần, và người mở profile
|
|
231
|
+
// ra nhìn thì rối.
|
|
232
|
+
//
|
|
233
|
+
// Giữ đúng MỘT tab affiliate (cái sắp dùng) và đóng phần còn lại. Không bao giờ
|
|
234
|
+
// đóng tab cuối cùng — context không còn trang nào thì Chrome của NST có thể tự
|
|
235
|
+
// tắt, mất luôn phiên đăng nhập vừa xác minh tay.
|
|
236
|
+
async function pruneTabs(ctx, { keep = null, log = null } = {}) {
|
|
237
|
+
const pages = ctx.pages() || [];
|
|
238
|
+
if (pages.length <= 1) return 0;
|
|
239
|
+
const keepUrl = keep ? keep.url() : (findAffiliatePage(pages)?.url() || null);
|
|
240
|
+
let closed = 0;
|
|
241
|
+
for (const p of pages) {
|
|
242
|
+
if (p === keep) continue;
|
|
243
|
+
let u = '';
|
|
244
|
+
try { u = p.url(); } catch { continue; }
|
|
245
|
+
if (!isJunkTab(u, keepUrl)) continue;
|
|
246
|
+
if ((ctx.pages() || []).length <= 1) break;
|
|
247
|
+
await p.close().catch(() => {});
|
|
248
|
+
closed++;
|
|
249
|
+
}
|
|
250
|
+
if (closed && log) log(`[shopee] đã đóng ${closed} tab cũ, còn ${(ctx.pages() || []).length}`);
|
|
251
|
+
return closed;
|
|
252
|
+
}
|
|
253
|
+
|
|
209
254
|
// Apply the Flow-1 hard filters (criteria) to parsed rows. Defaults are lenient;
|
|
210
255
|
// the controller passes the agreed thresholds (commission/price/sold/rating/imgs).
|
|
211
256
|
function applyFilters(rows, f = {}) {
|
|
@@ -260,6 +305,7 @@ async function scrapeOffers(ctx, {
|
|
|
260
305
|
delay_ms = 2500, // human-like pause between calls (anti-bot)
|
|
261
306
|
filters = {},
|
|
262
307
|
timeoutMs = 45000,
|
|
308
|
+
log = null,
|
|
263
309
|
// legacy single-sort param (back-compat with the first build)
|
|
264
310
|
sort_type = null,
|
|
265
311
|
} = {}) {
|
|
@@ -284,8 +330,10 @@ async function scrapeOffers(ctx, {
|
|
|
284
330
|
if (cat?.group && !filters.category_groups) filters = { ...filters, category_groups: [cat.group] };
|
|
285
331
|
const listType = cat?.list_type || null;
|
|
286
332
|
|
|
287
|
-
|
|
288
|
-
|
|
333
|
+
let page = findAffiliatePage(ctx.pages());
|
|
334
|
+
// Dọn TRƯỚC khi mở thêm: tab rác của lượt trước không giúp gì cho lượt này,
|
|
335
|
+
// mà để lại thì lần sau còn nhiều hơn.
|
|
336
|
+
await pruneTabs(ctx, { keep: page, log }).catch(() => {});
|
|
289
337
|
let opened = false;
|
|
290
338
|
if (!page) { page = await ctx.newPage(); opened = true; }
|
|
291
339
|
try {
|
|
@@ -372,7 +420,7 @@ function cleanSubId(v) {
|
|
|
372
420
|
// Google Ads, không qua đường ghi nhận affiliate nên không ra hoa hồng).
|
|
373
421
|
//
|
|
374
422
|
// subIds: mảng tối đa 5 phần tử. Quy ước của mình: [kênh, video, đợt].
|
|
375
|
-
async function generateOfferLink(ctx, { items = [], subIds = [], timeoutMs = 45000 } = {}) {
|
|
423
|
+
async function generateOfferLink(ctx, { items = [], subIds = [], timeoutMs = 45000, log = null } = {}) {
|
|
376
424
|
const list = (items || [])
|
|
377
425
|
.map((it) => ({ itemId: String(it.item_id || ''), shopId: Number(it.shop_id) || 0, trace: '' }))
|
|
378
426
|
.filter((it) => it.itemId && it.shopId);
|
|
@@ -381,8 +429,8 @@ async function generateOfferLink(ctx, { items = [], subIds = [], timeoutMs = 450
|
|
|
381
429
|
const subs = {};
|
|
382
430
|
for (let i = 0; i < 5; i++) subs[`subId${i + 1}`] = cleanSubId(subIds[i]);
|
|
383
431
|
|
|
384
|
-
|
|
385
|
-
|
|
432
|
+
let page = findAffiliatePage(ctx.pages());
|
|
433
|
+
await pruneTabs(ctx, { keep: page, log }).catch(() => {});
|
|
386
434
|
let opened = false;
|
|
387
435
|
if (!page) { page = await ctx.newPage(); opened = true; }
|
|
388
436
|
try {
|
|
@@ -441,4 +489,4 @@ function parseProductUrl(url = '') {
|
|
|
441
489
|
return null;
|
|
442
490
|
}
|
|
443
491
|
|
|
444
|
-
module.exports = { ingestProduct, scrapeOffers, generateOfferLink, cleanSubId, parseProductUrl, parseOfferRow, parsePdpItem, categoryGroup, pctToNumber, AFFILIATE_CATEGORIES, applyFilters, isBlockedState, findAffiliatePage };
|
|
492
|
+
module.exports = { ingestProduct, scrapeOffers, generateOfferLink, cleanSubId, parseProductUrl, parseOfferRow, parsePdpItem, categoryGroup, pctToNumber, AFFILIATE_CATEGORIES, applyFilters, isBlockedState, findAffiliatePage, pruneTabs, isJunkTab };
|