siluzan-tso-cli 1.1.47-beta.1 → 1.1.47-beta.3
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/README.md +1 -1
- package/dist/index.js +126 -32
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/analytics/account-analytics.md +2 -0
- package/dist/skill/references/operations/hosted-automation-tiktok.md +2 -2
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
51
51
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
> **注意**:当前为测试版(1.1.47-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.47-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -105841,7 +105841,15 @@ function register5(program2) {
|
|
|
105841
105841
|
// src/commands/list-accounts/_shared.ts
|
|
105842
105842
|
init_dist();
|
|
105843
105843
|
init_auth();
|
|
105844
|
-
|
|
105844
|
+
var SCOPE_ACTIVATION_BYPASSED_HEADER = "x-scope-activation-bypassed";
|
|
105845
|
+
function isScopeActivationBypassed(headers) {
|
|
105846
|
+
if (!headers) return false;
|
|
105847
|
+
const raw = headers[SCOPE_ACTIVATION_BYPASSED_HEADER] ?? headers["X-Scope-Activation-Bypassed"];
|
|
105848
|
+
const v = raw?.trim().toLowerCase();
|
|
105849
|
+
return v === "1" || v === "true" || v === "yes";
|
|
105850
|
+
}
|
|
105851
|
+
function shouldExposeScopeActivatedSources(bypassed = false) {
|
|
105852
|
+
if (bypassed) return false;
|
|
105845
105853
|
return resolveCompanySourceType() === "Ctaiad";
|
|
105846
105854
|
}
|
|
105847
105855
|
var VALID_MEDIA_TYPES = [
|
|
@@ -105966,7 +105974,7 @@ async function fetchTikTokAccountByMediaCustomerId(config, mediaCustomerId, verb
|
|
|
105966
105974
|
const match = items2.find((it) => String(it.ma?.mediaCustomerId ?? "") === id);
|
|
105967
105975
|
return match ?? null;
|
|
105968
105976
|
}
|
|
105969
|
-
function stripListAccountsEnrichmentFields(item) {
|
|
105977
|
+
function stripListAccountsEnrichmentFields(item, exposeScopeActivatedSources = shouldExposeScopeActivatedSources()) {
|
|
105970
105978
|
delete item.ma.remainingAccountBudget;
|
|
105971
105979
|
delete item.ma.platformStatus;
|
|
105972
105980
|
delete item.ma.aritScore;
|
|
@@ -105982,7 +105990,7 @@ function stripListAccountsEnrichmentFields(item) {
|
|
|
105982
105990
|
]) {
|
|
105983
105991
|
delete maExt[key];
|
|
105984
105992
|
}
|
|
105985
|
-
if (!
|
|
105993
|
+
if (!exposeScopeActivatedSources) {
|
|
105986
105994
|
delete item.ma.scopeActivatedSources;
|
|
105987
105995
|
delete maExt["scopeActivatedSources"];
|
|
105988
105996
|
}
|
|
@@ -106087,8 +106095,8 @@ var COL_GOOGLE_BASE = [
|
|
|
106087
106095
|
{ key: "shared", header: "\u5206\u4EAB" }
|
|
106088
106096
|
];
|
|
106089
106097
|
var COL_GOOGLE_PACKAGE = { key: "packageStatus", header: "\u5957\u9910\u6FC0\u6D3B" };
|
|
106090
|
-
function googleTableColumns() {
|
|
106091
|
-
if (!
|
|
106098
|
+
function googleTableColumns(exposePackage = shouldExposeScopeActivatedSources()) {
|
|
106099
|
+
if (!exposePackage) return COL_GOOGLE_BASE;
|
|
106092
106100
|
const authIdx = COL_GOOGLE_BASE.findIndex((c) => c.key === "authStatus");
|
|
106093
106101
|
const cols = [...COL_GOOGLE_BASE];
|
|
106094
106102
|
cols.splice(authIdx + 1, 0, COL_GOOGLE_PACKAGE);
|
|
@@ -106262,8 +106270,7 @@ function printTikTokTable(items2, tableOpts) {
|
|
|
106262
106270
|
});
|
|
106263
106271
|
printCliTable(rows, COL_TIKTOK, tableOpts);
|
|
106264
106272
|
}
|
|
106265
|
-
function printGoogleTable(items2, tableOpts) {
|
|
106266
|
-
const exposePackage = shouldExposeScopeActivatedSources();
|
|
106273
|
+
function printGoogleTable(items2, tableOpts, exposePackage = shouldExposeScopeActivatedSources()) {
|
|
106267
106274
|
const rows = items2.map((item) => {
|
|
106268
106275
|
const ma = item.ma;
|
|
106269
106276
|
const platformStatus = ma.platformStatus ?? "-";
|
|
@@ -106289,7 +106296,7 @@ function printGoogleTable(items2, tableOpts) {
|
|
|
106289
106296
|
}
|
|
106290
106297
|
return row;
|
|
106291
106298
|
});
|
|
106292
|
-
printCliTable(rows, googleTableColumns(), tableOpts);
|
|
106299
|
+
printCliTable(rows, googleTableColumns(exposePackage), tableOpts);
|
|
106293
106300
|
}
|
|
106294
106301
|
var COL_DEFAULT_WITH_MEDIA = [
|
|
106295
106302
|
{ key: "mediaType", header: "\u5A92\u4F53\u7C7B\u578B" },
|
|
@@ -106364,6 +106371,7 @@ async function runListAccounts(opts) {
|
|
|
106364
106371
|
const platformCfg = opts.media ? PLATFORM_CONFIG[opts.media] : null;
|
|
106365
106372
|
let items2;
|
|
106366
106373
|
let total;
|
|
106374
|
+
let scopeActivationBypassed = false;
|
|
106367
106375
|
if (platformCfg) {
|
|
106368
106376
|
const params = new URLSearchParams();
|
|
106369
106377
|
for (const [k, v] of Object.entries(platformCfg.fixedParams)) {
|
|
@@ -106380,6 +106388,7 @@ async function runListAccounts(opts) {
|
|
|
106380
106388
|
try {
|
|
106381
106389
|
const res = await apiFetchWithHeaders2(url, config, {}, opts.verbose);
|
|
106382
106390
|
items2 = res.data ?? [];
|
|
106391
|
+
scopeActivationBypassed = isScopeActivationBypassed(res.headers);
|
|
106383
106392
|
const hit = res.headers["s-total-hits"];
|
|
106384
106393
|
if (hit !== void 0) total = parseInt(hit, 10) || void 0;
|
|
106385
106394
|
} catch (err) {
|
|
@@ -106389,22 +106398,22 @@ async function runListAccounts(opts) {
|
|
|
106389
106398
|
process.exit(1);
|
|
106390
106399
|
}
|
|
106391
106400
|
} else {
|
|
106392
|
-
let res;
|
|
106393
106401
|
try {
|
|
106394
|
-
res = await
|
|
106402
|
+
const res = await apiFetchWithHeaders2(url, config, {}, opts.verbose);
|
|
106403
|
+
items2 = Array.isArray(res.data?.mas) ? res.data.mas : [];
|
|
106404
|
+
total = typeof res.data?.total === "number" ? res.data.total : void 0;
|
|
106405
|
+
scopeActivationBypassed = isScopeActivationBypassed(res.headers);
|
|
106406
|
+
if (opts.media === "TikTok") {
|
|
106407
|
+
mergeTikTokAdList(items2, res.data?.adList);
|
|
106408
|
+
} else if (opts.media === "MetaAd") {
|
|
106409
|
+
mergeMetaFacebookAdList(items2, res.data?.adList);
|
|
106410
|
+
}
|
|
106395
106411
|
} catch (err) {
|
|
106396
106412
|
console.error(`
|
|
106397
106413
|
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
106398
106414
|
`);
|
|
106399
106415
|
process.exit(1);
|
|
106400
106416
|
}
|
|
106401
|
-
items2 = Array.isArray(res?.mas) ? res.mas : [];
|
|
106402
|
-
total = typeof res?.total === "number" ? res.total : void 0;
|
|
106403
|
-
if (opts.media === "TikTok") {
|
|
106404
|
-
mergeTikTokAdList(items2, res.adList);
|
|
106405
|
-
} else if (opts.media === "MetaAd") {
|
|
106406
|
-
mergeMetaFacebookAdList(items2, res.adList);
|
|
106407
|
-
}
|
|
106408
106417
|
}
|
|
106409
106418
|
} else {
|
|
106410
106419
|
const params = new URLSearchParams();
|
|
@@ -106416,6 +106425,7 @@ async function runListAccounts(opts) {
|
|
|
106416
106425
|
try {
|
|
106417
106426
|
const res = await apiFetchWithHeaders2(url, config, {}, opts.verbose);
|
|
106418
106427
|
items2 = res.data ?? [];
|
|
106428
|
+
scopeActivationBypassed = isScopeActivationBypassed(res.headers);
|
|
106419
106429
|
const hit = res.headers["s-total-hits"];
|
|
106420
106430
|
if (hit !== void 0) total = parseInt(hit, 10) || void 0;
|
|
106421
106431
|
} catch (err) {
|
|
@@ -106425,9 +106435,10 @@ async function runListAccounts(opts) {
|
|
|
106425
106435
|
process.exit(1);
|
|
106426
106436
|
}
|
|
106427
106437
|
}
|
|
106438
|
+
const exposeScopeActivated = shouldExposeScopeActivatedSources(scopeActivationBypassed);
|
|
106428
106439
|
if (items2.length > 0) {
|
|
106429
106440
|
for (const item of items2) {
|
|
106430
|
-
stripListAccountsEnrichmentFields(item);
|
|
106441
|
+
stripListAccountsEnrichmentFields(item, exposeScopeActivated);
|
|
106431
106442
|
}
|
|
106432
106443
|
}
|
|
106433
106444
|
const oauthFailWarning = (() => {
|
|
@@ -106469,7 +106480,7 @@ async function runListAccounts(opts) {
|
|
|
106469
106480
|
const isKwai = opts.media === "Kwai";
|
|
106470
106481
|
const tableBorderOpts = opts.unicode ? { plain: false } : void 0;
|
|
106471
106482
|
if (isGoogle) {
|
|
106472
|
-
printGoogleTable(items2, tableBorderOpts);
|
|
106483
|
+
printGoogleTable(items2, tableBorderOpts, exposeScopeActivated);
|
|
106473
106484
|
} else if (isYandex) {
|
|
106474
106485
|
printYandexTable(items2, tableBorderOpts);
|
|
106475
106486
|
} else if (isTikTok) {
|
|
@@ -134230,6 +134241,87 @@ function parseTikTokNumber(value) {
|
|
|
134230
134241
|
const n = Number(value);
|
|
134231
134242
|
return Number.isFinite(n) ? n : void 0;
|
|
134232
134243
|
}
|
|
134244
|
+
var TIKTOK_VALUE_METRICS = ["total_purchase_value"];
|
|
134245
|
+
var TIKTOK_OFFICIAL_ROAS_METRICS = [
|
|
134246
|
+
"complete_payment_roas",
|
|
134247
|
+
"total_active_pay_roas"
|
|
134248
|
+
];
|
|
134249
|
+
var TIKTOK_ROAS_COMPANION_METRICS = [
|
|
134250
|
+
"total_purchase_value",
|
|
134251
|
+
"complete_payment_roas",
|
|
134252
|
+
"total_active_pay_roas"
|
|
134253
|
+
];
|
|
134254
|
+
function splitTikTokCsv(raw) {
|
|
134255
|
+
return raw.split(",").map((x) => x.trim()).filter(Boolean);
|
|
134256
|
+
}
|
|
134257
|
+
function pickFirstMetric(row, keys) {
|
|
134258
|
+
let fallback;
|
|
134259
|
+
for (const key of keys) {
|
|
134260
|
+
const value = parseTikTokNumber(row[key]);
|
|
134261
|
+
if (value === void 0) continue;
|
|
134262
|
+
if (value > 0) return { key, value };
|
|
134263
|
+
fallback ??= { key, value };
|
|
134264
|
+
}
|
|
134265
|
+
return fallback;
|
|
134266
|
+
}
|
|
134267
|
+
function expandTikTokOfficialReportMetrics(metrics) {
|
|
134268
|
+
const parts = splitTikTokCsv(metrics);
|
|
134269
|
+
const needCompanions = parts.includes("complete_payment") || TIKTOK_ROAS_COMPANION_METRICS.some((k) => parts.includes(k));
|
|
134270
|
+
if (!needCompanions) return parts.join(",");
|
|
134271
|
+
for (const extra of TIKTOK_ROAS_COMPANION_METRICS) {
|
|
134272
|
+
if (!parts.includes(extra)) parts.push(extra);
|
|
134273
|
+
}
|
|
134274
|
+
return parts.join(",");
|
|
134275
|
+
}
|
|
134276
|
+
function pickTikTokOfficialRoas(row) {
|
|
134277
|
+
return pickFirstMetric(row, TIKTOK_OFFICIAL_ROAS_METRICS);
|
|
134278
|
+
}
|
|
134279
|
+
function pickTikTokRevenue(row) {
|
|
134280
|
+
const picked = pickFirstMetric(row, TIKTOK_VALUE_METRICS);
|
|
134281
|
+
if (!picked) return { revenue: void 0, revenueSource: void 0 };
|
|
134282
|
+
return { revenue: picked.value, revenueSource: picked.key };
|
|
134283
|
+
}
|
|
134284
|
+
function attachTikTokRoasFields(row) {
|
|
134285
|
+
const spend = parseTikTokNumber(row.spend);
|
|
134286
|
+
const official = pickTikTokOfficialRoas(row);
|
|
134287
|
+
const purchase = pickTikTokRevenue(row);
|
|
134288
|
+
if (purchase.revenue !== void 0) {
|
|
134289
|
+
row.revenue = purchase.revenue;
|
|
134290
|
+
row.revenueSource = purchase.revenueSource;
|
|
134291
|
+
} else if (official && official.value > 0 && spend !== void 0 && spend > 0) {
|
|
134292
|
+
row.revenue = official.value * spend;
|
|
134293
|
+
row.revenueSource = `${official.key}*spend`;
|
|
134294
|
+
}
|
|
134295
|
+
const revenue = parseTikTokNumber(row.revenue);
|
|
134296
|
+
if (official && official.value > 0) {
|
|
134297
|
+
row.roas = official.value;
|
|
134298
|
+
row.roasSource = official.key;
|
|
134299
|
+
} else if (spend !== void 0 && spend > 0 && revenue !== void 0) {
|
|
134300
|
+
row.roas = revenue / spend;
|
|
134301
|
+
row.roasSource = "computed";
|
|
134302
|
+
} else if (official) {
|
|
134303
|
+
row.roas = official.value;
|
|
134304
|
+
row.roasSource = official.key;
|
|
134305
|
+
}
|
|
134306
|
+
}
|
|
134307
|
+
function summarizeTikTokOfficialReport(items2) {
|
|
134308
|
+
let spend = 0;
|
|
134309
|
+
let revenue = 0;
|
|
134310
|
+
let hasRevenue = false;
|
|
134311
|
+
for (const row of items2) {
|
|
134312
|
+
spend += parseTikTokNumber(row.spend) ?? 0;
|
|
134313
|
+
const rowRevenue = parseTikTokNumber(row.revenue);
|
|
134314
|
+
if (rowRevenue === void 0) continue;
|
|
134315
|
+
revenue += rowRevenue;
|
|
134316
|
+
hasRevenue = true;
|
|
134317
|
+
}
|
|
134318
|
+
return {
|
|
134319
|
+
spend,
|
|
134320
|
+
revenue: hasRevenue ? revenue : 0,
|
|
134321
|
+
roas: spend > 0 && hasRevenue ? revenue / spend : void 0,
|
|
134322
|
+
itemCount: items2.length
|
|
134323
|
+
};
|
|
134324
|
+
}
|
|
134233
134325
|
function flattenTikTokReportRow(row) {
|
|
134234
134326
|
const dims = row.dimensions && typeof row.dimensions === "object" && !Array.isArray(row.dimensions) ? row.dimensions : {};
|
|
134235
134327
|
const metrics = row.metrics && typeof row.metrics === "object" && !Array.isArray(row.metrics) ? row.metrics : {};
|
|
@@ -134238,11 +134330,7 @@ function flattenTikTokReportRow(row) {
|
|
|
134238
134330
|
const n = parseTikTokNumber(v);
|
|
134239
134331
|
out[k] = n === void 0 ? v : n;
|
|
134240
134332
|
}
|
|
134241
|
-
|
|
134242
|
-
const payment = parseTikTokNumber(out.complete_payment);
|
|
134243
|
-
if (spend !== void 0 && spend > 0 && payment !== void 0) {
|
|
134244
|
-
out.roas = payment / spend;
|
|
134245
|
-
}
|
|
134333
|
+
attachTikTokRoasFields(out);
|
|
134246
134334
|
return out;
|
|
134247
134335
|
}
|
|
134248
134336
|
function pickAdGroupBidField(bidType) {
|
|
@@ -134450,6 +134538,7 @@ async function fetchAdEntities(config, opts) {
|
|
|
134450
134538
|
);
|
|
134451
134539
|
}
|
|
134452
134540
|
async function fetchOfficialReport(config, opts) {
|
|
134541
|
+
const metrics = expandTikTokOfficialReportMetrics(opts.metrics);
|
|
134453
134542
|
const base = requireTikTokApiUrl(config);
|
|
134454
134543
|
const raw = await fetchTikTokPagedList(
|
|
134455
134544
|
config,
|
|
@@ -134461,7 +134550,7 @@ async function fetchOfficialReport(config, opts) {
|
|
|
134461
134550
|
endDate: opts.endDate,
|
|
134462
134551
|
dataLevel: opts.dataLevel,
|
|
134463
134552
|
dimensions: opts.dimensions,
|
|
134464
|
-
metrics
|
|
134553
|
+
metrics,
|
|
134465
134554
|
serviceType: opts.serviceType,
|
|
134466
134555
|
pageNum,
|
|
134467
134556
|
pageSize
|
|
@@ -134471,7 +134560,8 @@ async function fetchOfficialReport(config, opts) {
|
|
|
134471
134560
|
);
|
|
134472
134561
|
return {
|
|
134473
134562
|
items: raw.items.map(flattenTikTokReportRow),
|
|
134474
|
-
totalNumber: raw.totalNumber
|
|
134563
|
+
totalNumber: raw.totalNumber,
|
|
134564
|
+
metrics
|
|
134475
134565
|
};
|
|
134476
134566
|
}
|
|
134477
134567
|
|
|
@@ -134725,7 +134815,7 @@ async function runTikTokOfficialReport(opts) {
|
|
|
134725
134815
|
const id = assertTikTokAccountId(opts.account);
|
|
134726
134816
|
const dataLevel = opts.dataLevel.trim();
|
|
134727
134817
|
const dimensions = opts.dimensions.trim();
|
|
134728
|
-
const metrics = opts.metrics.trim();
|
|
134818
|
+
const metrics = expandTikTokOfficialReportMetrics(opts.metrics.trim());
|
|
134729
134819
|
if (!dataLevel || !dimensions || !metrics) {
|
|
134730
134820
|
console.error("\n\u274C --data-level / --dimensions / --metrics \u5747\u4E3A\u5FC5\u586B\u3002\n");
|
|
134731
134821
|
process.exit(1);
|
|
@@ -134750,6 +134840,7 @@ async function runTikTokOfficialReport(opts) {
|
|
|
134750
134840
|
} catch (err) {
|
|
134751
134841
|
fail("\u67E5\u8BE2 TikTok \u5B98\u65B9\u62A5\u8868\u5931\u8D25", err);
|
|
134752
134842
|
}
|
|
134843
|
+
const totals = summarizeTikTokOfficialReport(result.items);
|
|
134753
134844
|
const payload = {
|
|
134754
134845
|
account: id,
|
|
134755
134846
|
dataLevel,
|
|
@@ -134758,6 +134849,7 @@ async function runTikTokOfficialReport(opts) {
|
|
|
134758
134849
|
metrics,
|
|
134759
134850
|
startDate,
|
|
134760
134851
|
endDate,
|
|
134852
|
+
totals,
|
|
134761
134853
|
...wrapListJson({
|
|
134762
134854
|
page: 1,
|
|
134763
134855
|
pageSize,
|
|
@@ -134783,9 +134875,11 @@ TikTok \u5B98\u65B9\u62A5\u8868 ${dataLevel} ${startDate}~${endDate}\uFF08${id}\
|
|
|
134783
134875
|
console.log(" \u65E0\u62A5\u8868\u884C\u3002\n");
|
|
134784
134876
|
return;
|
|
134785
134877
|
}
|
|
134786
|
-
const dimKeys = dimensions
|
|
134787
|
-
const metricKeys = metrics
|
|
134788
|
-
const keys = [...dimKeys, ...metricKeys, "
|
|
134878
|
+
const dimKeys = splitTikTokCsv(dimensions);
|
|
134879
|
+
const metricKeys = splitTikTokCsv(metrics);
|
|
134880
|
+
const keys = [...dimKeys, ...metricKeys, "revenue", "revenueSource", "roas", "roasSource"].filter(
|
|
134881
|
+
(k, i, a) => a.indexOf(k) === i
|
|
134882
|
+
);
|
|
134789
134883
|
printCliTable(
|
|
134790
134884
|
result.items.map((r) => {
|
|
134791
134885
|
const row = {};
|
|
@@ -134800,7 +134894,7 @@ TikTok \u5B98\u65B9\u62A5\u8868 ${dataLevel} ${startDate}~${endDate}\uFF08${id}\
|
|
|
134800
134894
|
const sampleSpend = parseTikTokNumber(result.items[0]?.spend);
|
|
134801
134895
|
if (sampleSpend !== void 0) {
|
|
134802
134896
|
console.log(
|
|
134803
|
-
" spend \u5DF2\u8F6C\u6570\u5B57\
|
|
134897
|
+
" spend \u5DF2\u8F6C\u6570\u5B57\u3002ROAS \u4F18\u5148\u5B98\u65B9\u5217 complete_payment_roas / total_active_pay_roas\uFF0C\u5426\u5219\u7528 total_purchase_value/spend\uFF08\u89C1 roasSource\uFF09\u3002complete_payment \u662F\u7F51\u9875\u5B8C\u4ED8\u6B21\u6570\uFF0C\u4E0D\u662F\u91D1\u989D\u3002\u591A\u884C\u6C47\u603B\u89C1 totals\uFF08\u5148\u52A0\u603B\u518D\u9664\uFF0C\u52FF\u5E73\u5747\u5404\u7EC4 roas\uFF09\u3002\u52FF\u6DF7\u52A0\u4E0D\u540C DataLevel\u3002\n"
|
|
134804
134898
|
);
|
|
134805
134899
|
} else {
|
|
134806
134900
|
console.log();
|
|
@@ -134953,7 +135047,7 @@ function registerTikTokAnalysisCommands(program2) {
|
|
|
134953
135047
|
"TikTokAPI \u5B98\u65B9\u62A5\u8868\uFF08GET \u2026/query/report/Tiktok/search\uFF1B\u53EF\u542B\u4ECA\u5929/\u5C0F\u65F6\uFF1B\u4E0E `run` \u7684 TSO \u770B\u677F\u5468\u671F\u62A5\u8868\u4E0D\u540C\u3002ServiceType/DataLevel/Dimensions/Metrics/\u65E5\u671F/PageSize \u5FC5\u586B\uFF09"
|
|
134954
135048
|
).requiredOption(...ACCOUNT_OPTION2).requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u6309\u5E7F\u544A\u4E3B timezone \u81EA\u7136\u65E5\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").requiredOption("--data-level <level>", "AUCTION_CAMPAIGN / AUCTION_ADGROUP / AUCTION_AD").requiredOption("--dimensions <list>", "\u9017\u53F7\u5206\u9694\uFF0C\u5982 campaign_id \u6216 adgroup_id,stat_time_hour").requiredOption(
|
|
134955
135049
|
"--metrics <list>",
|
|
134956
|
-
"\u9017\u53F7\u5206\u9694\uFF0C\u82B1\u8D39\u987B\u7528 spend\uFF08\u4E0D\u8981 stat_cost\uFF09\
|
|
135050
|
+
"\u9017\u53F7\u5206\u9694\uFF0C\u82B1\u8D39\u987B\u7528 spend\uFF08\u4E0D\u8981 stat_cost\uFF09\u3002complete_payment \u662F\u7F51\u9875\u5B8C\u4ED8\u6B21\u6570\u3002\u542B\u5B8C\u4ED8/ROAS \u65F6\u81EA\u52A8\u9644\u5E26 total_purchase_value,complete_payment_roas,total_active_pay_roas"
|
|
134957
135051
|
).option("--service-type <type>", "AUCTION\uFF08\u9ED8\u8BA4\uFF09\u6216 RESERVATION", "AUCTION").option("--page-size <n>", "\u6BCF\u9875\u6761\u6570\uFF0C\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 1000", (v) => parseInt(v, 10)).option("-t, --token <token>", "Auth Token").option("--json-out <path>", JSON_OUT_OPTION_DESC2).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
134958
135052
|
async (opts) => {
|
|
134959
135053
|
await runTikTokOfficialReport(opts);
|
package/dist/skill/_meta.json
CHANGED
|
@@ -351,6 +351,8 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
|
|
|
351
351
|
--data-level AUCTION_CAMPAIGN --dimensions campaign_id --metrics spend
|
|
352
352
|
```
|
|
353
353
|
|
|
354
|
+
**ROAS 口径**(对齐 [网页事件](https://ads.tiktok.com/help/article/page-events?lang=zh) / [应用内事件](https://ads.tiktok.com/help/article/in-app-events?lang=zh)):`--metrics` 含完付或 ROAS 时 CLI 自动附带 `total_purchase_value,complete_payment_roas,total_active_pay_roas`。落盘行有 `revenue` / `revenueSource` / `roas` / `roasSource`,多行汇总在 `totals`(先加总 spend/revenue 再除)。`complete_payment` 是网页支付完成**次数**;金额用 `total_purchase_value`(总付费价值)。ROAS 优先官方列 `complete_payment_roas` / `total_active_pay_roas`。**禁止** `complete_payment/spend`,**禁止**把完付=0 判成「像素没回传」,**禁止**对各组 `roas` 取平均。不确定时先 `adgroup-entities` 看 `optimization_event`。
|
|
355
|
+
|
|
354
356
|
---
|
|
355
357
|
|
|
356
358
|
## Bing(BingV2)账户分析
|
|
@@ -107,7 +107,7 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
|
|
|
107
107
|
--data-level AUCTION_CAMPAIGN --dimensions campaign_id,stat_time_hour --metrics spend
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
广告效果(CPA 用 `cost_per_conversion` 或 `spend/conversion
|
|
110
|
+
广告效果(CPA 用 `cost_per_conversion` 或 `spend/conversion`;**ROAS 用落盘 `roas` / `totals.roas`**):
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
113
|
siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./snap-tt \
|
|
@@ -116,7 +116,7 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
|
|
|
116
116
|
--metrics spend,conversion,cost_per_conversion,complete_payment,ctr
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
`--metrics` 含完付/ROAS 时 CLI 自动附带 `total_purchase_value,complete_payment_roas,total_active_pay_roas`。口径对齐 Ads Manager:`complete_payment` 是网页支付完成**次数**(不是金额);ROAS 优先官方列 `complete_payment_roas`(网页)/ `total_active_pay_roas`(App 付费),否则 `total_purchase_value/spend`。多行用 `totals`(先加总再除),**禁止**对各组 `roas` 取平均。**`complete_payment=0` 不是像素坏了**——App / `ACTIVE_PAY` 看 `total_purchase_value`。不确定优化目标时先 `adgroup-entities` 看 `optimization_event`。出价在组,广告层只能关停(本批 CLI **不**关停)。
|
|
120
120
|
|
|
121
121
|
根因分解(含小时时起止仍须同一天,必须翻完页;CLI 已自动翻):
|
|
122
122
|
|
|
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
$PKG_NAME = 'siluzan-tso-cli'
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
$PKG_VERSION = '1.1.47-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.47-beta.3'
|
|
13
13
|
$CLI_BIN = 'siluzan-tso'
|
|
14
14
|
$SKILL_LABEL = 'Siluzan TSO'
|
|
15
15
|
$INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
|
|
@@ -9,7 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
readonly PKG_NAME="siluzan-tso-cli"
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
readonly PKG_VERSION="1.1.47-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.47-beta.3"
|
|
13
13
|
readonly CLI_BIN="siluzan-tso"
|
|
14
14
|
readonly SKILL_LABEL="Siluzan TSO"
|
|
15
15
|
readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
|