channel-worker 2.5.118 → 2.5.120
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 +24 -1
- package/lib/shopee-report.js +80 -1
- package/package.json +2 -2
package/lib/command-poller.js
CHANGED
|
@@ -370,7 +370,26 @@ class CommandPoller {
|
|
|
370
370
|
return;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
const clickResult = await report.fetchClickReport(conn.context, {
|
|
374
|
+
days: payload.days || 90,
|
|
375
|
+
log: (message) => console.log(message),
|
|
376
|
+
});
|
|
377
|
+
if (clickResult.status === 'needs_verify') {
|
|
378
|
+
await this.api.updateCommand(command._id, {
|
|
379
|
+
status: 'done',
|
|
380
|
+
result: { needs_verify: true, reason: clickResult.reason, count: result.rows.length, click_count: clickResult.rows?.length || 0 },
|
|
381
|
+
});
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (clickResult.status !== 'ok') {
|
|
385
|
+
await this.api.updateCommand(command._id, {
|
|
386
|
+
status: 'failed', error: String(clickResult.reason || 'Không đọc được báo cáo click Shopee').slice(0, 500),
|
|
387
|
+
});
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
373
391
|
const saved = await this.api.affiliateReportResult(result.rows, {
|
|
392
|
+
clicks: clickResult.rows,
|
|
374
393
|
last_report_date: result.last_report_date,
|
|
375
394
|
last_update_time: result.last_update_time,
|
|
376
395
|
from: result.from,
|
|
@@ -383,10 +402,14 @@ class CommandPoller {
|
|
|
383
402
|
saved: saved?.saved || 0,
|
|
384
403
|
matched: saved?.matched || 0,
|
|
385
404
|
unmatched: saved?.unmatched || 0,
|
|
405
|
+
click_count: clickResult.rows.length,
|
|
406
|
+
click_saved: saved?.click_saved || 0,
|
|
407
|
+
click_matched: saved?.click_matched || 0,
|
|
408
|
+
click_unmatched: saved?.click_unmatched || 0,
|
|
386
409
|
last_report_date: result.last_report_date,
|
|
387
410
|
},
|
|
388
411
|
});
|
|
389
|
-
console.log(`[shopee-report] đã lưu ${saved?.saved || 0}/${result.rows.length}
|
|
412
|
+
console.log(`[shopee-report] đã lưu ${saved?.saved || 0}/${result.rows.length} đơn, ${saved?.click_saved || 0}/${clickResult.rows.length} click`);
|
|
390
413
|
} catch (err) {
|
|
391
414
|
console.error(`[shopee-report] đồng bộ thất bại: ${err.message}`);
|
|
392
415
|
await this.api.updateCommand(command._id, { status: 'failed', error: String(err.message || err).slice(0, 500) });
|
package/lib/shopee-report.js
CHANGED
|
@@ -104,4 +104,83 @@ async function fetchConversionReport(ctx, {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
async function fetchClickReport(ctx, {
|
|
108
|
+
days = 90,
|
|
109
|
+
pageSize = 100,
|
|
110
|
+
maxPages = 200,
|
|
111
|
+
delayMs = 250,
|
|
112
|
+
timeoutMs = 60000,
|
|
113
|
+
log = null,
|
|
114
|
+
} = {}) {
|
|
115
|
+
let page = findAffiliatePage(ctx.pages());
|
|
116
|
+
let opened = false;
|
|
117
|
+
if (!page) {
|
|
118
|
+
page = await ctx.newPage();
|
|
119
|
+
opened = true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
if (!page.url().includes('affiliate.shopee.vn/')) {
|
|
124
|
+
await page.goto('https://affiliate.shopee.vn/report/click_report', {
|
|
125
|
+
waitUntil: 'domcontentloaded', timeout: timeoutMs,
|
|
126
|
+
}).catch(() => {});
|
|
127
|
+
await sleep(1500);
|
|
128
|
+
}
|
|
129
|
+
if (isBlockedState(page.url(), null)) {
|
|
130
|
+
return { status: 'needs_verify', rows: [], reason: 'session at captcha/login — re-verify via VNC' };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const window = reportWindow(days);
|
|
134
|
+
const rows = [];
|
|
135
|
+
let total = null;
|
|
136
|
+
|
|
137
|
+
for (let pageNum = 1; pageNum <= maxPages; pageNum++) {
|
|
138
|
+
const result = await page.evaluate(async (q) => {
|
|
139
|
+
const url = `/api/v1/click_report/list?page_size=${q.pageSize}&page_num=${q.pageNum}`
|
|
140
|
+
+ `&click_time_s=${q.start}&click_time_e=${q.end}`;
|
|
141
|
+
try {
|
|
142
|
+
const r = await fetch(url, { credentials: 'include' });
|
|
143
|
+
return { status: r.status, json: await r.json().catch(() => null) };
|
|
144
|
+
} catch (e) {
|
|
145
|
+
return { status: 0, error: String(e?.message || e) };
|
|
146
|
+
}
|
|
147
|
+
}, { pageSize, pageNum, start: window.start, end: window.end });
|
|
148
|
+
|
|
149
|
+
if (isBlockedState(page.url(), result) || result.json?.code === 90309999) {
|
|
150
|
+
return { status: 'needs_verify', rows, reason: `blocked while reading click report (http ${result.status}, code ${result.json?.code})` };
|
|
151
|
+
}
|
|
152
|
+
if (result.status !== 200 || result.json?.code !== 0) {
|
|
153
|
+
return { status: 'failed', rows, reason: result.json?.msg || result.error || `click report HTTP ${result.status}` };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const data = result.json?.data || {};
|
|
157
|
+
const batch = Array.isArray(data.list) ? data.list : [];
|
|
158
|
+
total = Number(data.total_count || 0);
|
|
159
|
+
rows.push(...batch.map((row) => ({
|
|
160
|
+
click_id: row.click_id,
|
|
161
|
+
click_time: row.click_time,
|
|
162
|
+
sub_id: row.sub_id,
|
|
163
|
+
click_region: row.click_region,
|
|
164
|
+
referrer: row.referrer,
|
|
165
|
+
direct_source: row.direct_source,
|
|
166
|
+
})));
|
|
167
|
+
if (log) log(`[shopee-report] click trang ${pageNum}: ${batch.length} dòng (${rows.length}/${total})`);
|
|
168
|
+
if (!batch.length || rows.length >= total || batch.length < pageSize) break;
|
|
169
|
+
await sleep(delayMs);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (total != null && rows.length < total) {
|
|
173
|
+
return { status: 'failed', rows, reason: `click pagination stopped at ${rows.length}/${total} rows` };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
status: 'ok', rows, total: total ?? rows.length,
|
|
178
|
+
from: new Date(window.start * 1000).toISOString(),
|
|
179
|
+
to: new Date(window.end * 1000).toISOString(),
|
|
180
|
+
};
|
|
181
|
+
} finally {
|
|
182
|
+
if (opened) await page.close().catch(() => {});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = { fetchConversionReport, fetchClickReport, reportWindow };
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "channel-worker",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.120",
|
|
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": {
|
|
7
|
-
"channel-worker": "
|
|
7
|
+
"channel-worker": "bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|