siluzan-tso-cli 1.1.35-beta.2 → 1.1.35-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 +1679 -1633
- package/dist/skill/AGENTS.md +11 -10
- package/dist/skill/SKILL.md +1 -1
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/campaign-create-template.md +15 -15
- package/dist/skill/assets/market-analysis-rules.md +14 -14
- package/dist/skill/assets/pmax-create-template.md +6 -6
- package/dist/skill/assets/pmax-lead-form-template.md +3 -3
- package/dist/skill/references/README.md +21 -21
- package/dist/skill/references/accounts/accounts.md +42 -42
- package/dist/skill/references/analytics/account-analytics.md +46 -39
- package/dist/skill/references/analytics/keyword-planner-workflows.md +14 -13
- package/dist/skill/references/analytics/rag.md +7 -7
- package/dist/skill/references/analytics/reporting.md +1 -1
- package/dist/skill/references/core/agent-conventions.md +20 -20
- package/dist/skill/references/core/intent-routing.md +116 -117
- package/dist/skill/references/core/subagent-orchestration.md +1 -1
- package/dist/skill/references/core/workflows.md +12 -12
- package/dist/skill/references/google-ads/google-ads-campaign-plan.md +29 -31
- package/dist/skill/references/google-ads/google-ads.md +7 -7
- package/dist/skill/references/google-ads/rules/google-ads-keyword-taxonomy.md +14 -14
- package/dist/skill/references/operations/hosted-automation-optimize-index.md +2 -2
- package/dist/skill/references/operations/hosted-automation-scenarios.md +2 -2
- package/dist/skill/references/operations/hosted-automation-self-control.md +6 -6
- package/dist/skill/references/operations/hosted-automation-user-catalog.md +17 -17
- package/dist/skill/references/report-templates/README.md +68 -0
- package/dist/skill/references/report-templates/REPORT-WORKFLOW.md +2 -2
- package/dist/skill/references/report-templates/bing-period-report.md +53 -53
- package/dist/skill/references/report-templates/google-ads-diagnosis.md +28 -2
- package/dist/skill/references/report-templates/google-inquiry-analysis.md +8 -8
- package/dist/skill/references/report-templates/google-period-report-excel.md +2 -2
- package/dist/skill/references/report-templates/okki-weekly-google-client.md +55 -55
- package/dist/skill/references/report-templates/tiktok-period-report.md +22 -21
- package/dist/skill/report-templates/GoogleAdsDiagnosisReport.html +390 -892
- package/dist/skill/report-templates/REPORT-WORKFLOW.md +2 -2
- package/dist/skill/report-templates/bing-period-report.md +53 -53
- package/dist/skill/report-templates/google-ads-diagnosis.md +28 -2
- package/dist/skill/report-templates/google-inquiry-analysis.md +8 -8
- package/dist/skill/report-templates/google-period-report-excel.md +2 -2
- package/dist/skill/report-templates/meta-period-report.html +1 -4
- package/dist/skill/report-templates/okki-weekly-google-client.md +55 -55
- package/dist/skill/report-templates/report-template-academic.html +0 -2
- package/dist/skill/report-templates/report-template.html +0 -2
- package/dist/skill/report-templates/tiktok-period-report.md +22 -21
- package/dist/skill/report-templates/website-diagnosis-report.html +1 -5
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +2 -14
- package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +1 -3
- package/eval/cases/accounts-single-balance-not-bulk.scenario.json +3 -14
- package/eval/cases/budget-display-not-raw-micros.scenario.json +1 -8
- package/eval/cases/clue-meta-leads-json.scenario.json +2 -14
- package/eval/cases/clue-tiktok-leads-json.scenario.json +2 -11
- package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +3 -9
- package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +3 -9
- package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +3 -9
- package/eval/cases/facebook-analysis-google-section-aliases.scenario.json +2 -11
- package/eval/cases/facebook-analysis-not-google-keywords.scenario.json +3 -10
- package/eval/cases/facebook-analysis-period-default.scenario.json +2 -14
- package/eval/cases/finance-invoice-info-list.scenario.json +3 -11
- package/eval/cases/forewarning-list-google.scenario.json +3 -14
- package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +2 -6
- package/eval/cases/google-analysis-keywords-route.scenario.json +2 -14
- package/eval/cases/human-p1-multiturn.scenario.json +1 -5
- package/eval/cases/ir-p1-vs-p8-account-vs-url.scenario.json +5 -17
- package/eval/cases/ir-p4-period-not-diagnosis.scenario.json +3 -16
- package/eval/cases/ir-p8-network-diagnosis-with-url.scenario.json +3 -25
- package/eval/cases/ir-p8-website-monitor-report-with-url.scenario.json +3 -28
- package/eval/cases/ir-p8-website-test-report-no-url.scenario.json +3 -14
- package/eval/cases/ir-p9-industry-not-p8.scenario.json +3 -19
- package/eval/cases/ir-vague-report-clarify.scenario.json +3 -14
- package/eval/cases/ir-w3-vs-p8-from-website.scenario.json +2 -8
- package/eval/cases/ir-w5-keyword-planner-url-core-terms.scenario.json +2 -22
- package/eval/cases/meta-single-balance-not-bulk.scenario.json +3 -17
- package/eval/cases/no-legacy-json-flag.scenario.json +2 -6
- package/eval/cases/open-account-google-noninteractive.scenario.json +1 -3
- package/eval/cases/open-account-tiktok-license-file.scenario.json +1 -3
- package/eval/cases/optimize-list-by-account.scenario.json +3 -11
- package/eval/cases/p1-single-account-profile.scenario.json +1 -11
- package/eval/cases/p2-balance-scan-bulk.scenario.json +2 -9
- package/eval/cases/p3-accounts-digest.scenario.json +1 -5
- package/eval/cases/p4-fb-meta-period-report.scenario.json +2 -12
- package/eval/cases/p4-period-report-window.scenario.json +1 -8
- package/eval/cases/pmax-asset-group-create-with-bg.scenario.json +2 -13
- package/eval/cases/pmax-brand-edit-routing.scenario.json +3 -16
- package/eval/cases/pmax-edit-not-campaign-edit.scenario.json +2 -12
- package/eval/cases/pmax-enable-brand-guidelines.scenario.json +2 -12
- package/eval/cases/pmax-no-assets-update-brand-fields.scenario.json +3 -14
- package/eval/cases/rag-before-keyword-expand.scenario.json +1 -11
- package/eval/cases/rag-list-then-query.scenario.json +2 -14
- package/eval/cases/report-list-google.scenario.json +2 -11
- package/eval/cases/report-push-list-google.scenario.json +2 -11
- package/eval/cases/setup-login-or-env.scenario.json +1 -3
- package/eval/cases/setup-siluzan-data-permission-env.scenario.json +1 -3
- package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +2 -6
- package/eval/cases/time-range-user-delegates-default.scenario.json +1 -8
- package/eval/cases/tips-json-out-filtering.scenario.json +1 -3
- package/eval/cases/tips-large-json-pagination.scenario.json +1 -3
- package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +2 -11
- package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +1 -3
- package/eval/cases/uj-ad-website-search-plan-table-format.scenario.json +1 -3
- package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +6 -18
- package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +1 -8
- package/eval/cases/uj-analytics-ecommerce-industry-report.scenario.json +2 -14
- package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +2 -11
- package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +1 -3
- package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +2 -11
- package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +1 -4
- package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +2 -17
- package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +2 -14
- package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +2 -6
- package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +3 -17
- package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +1 -5
- package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +1 -3
- package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +3 -17
- package/eval/cases/uj-patrol-google-balances-low.scenario.json +2 -11
- package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +3 -14
- package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +2 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4357,7 +4357,14 @@ async function enrichOverviewRecordWithRealtimeBalance(media, accountId, config,
|
|
|
4357
4357
|
if (!BALANCE_SUPPORTED_MEDIA.includes(media)) {
|
|
4358
4358
|
return record;
|
|
4359
4359
|
}
|
|
4360
|
-
const balanceMap = await fetchBalanceMap(
|
|
4360
|
+
const balanceMap = await fetchBalanceMap(
|
|
4361
|
+
media,
|
|
4362
|
+
[accountId],
|
|
4363
|
+
config,
|
|
4364
|
+
void 0,
|
|
4365
|
+
void 0,
|
|
4366
|
+
verbose
|
|
4367
|
+
);
|
|
4361
4368
|
const bal = balanceMap.get(accountId);
|
|
4362
4369
|
if (bal == null) return record;
|
|
4363
4370
|
const out = { ...record };
|
|
@@ -4630,11 +4637,7 @@ async function fetchBalanceAndOverviewSequential(opts) {
|
|
|
4630
4637
|
const ids = chunks[chunkIdx];
|
|
4631
4638
|
const oneBased = chunkIdx + 1;
|
|
4632
4639
|
opts.onChunkStart?.(oneBased, chunks.length, ids);
|
|
4633
|
-
const routeGroups = groupAccountIdsByOverviewRoute(
|
|
4634
|
-
media,
|
|
4635
|
-
ids,
|
|
4636
|
-
opts.accountMediaTypes
|
|
4637
|
-
);
|
|
4640
|
+
const routeGroups = groupAccountIdsByOverviewRoute(media, ids, opts.accountMediaTypes);
|
|
4638
4641
|
let chunkOverviewSize = 0;
|
|
4639
4642
|
for (const [route, routeIds] of routeGroups) {
|
|
4640
4643
|
const overviewChunk = await fetchOverviewMap(
|
|
@@ -4649,14 +4652,7 @@ async function fetchBalanceAndOverviewSequential(opts) {
|
|
|
4649
4652
|
for (const [k, v] of overviewChunk) overviewMap.set(k, v);
|
|
4650
4653
|
}
|
|
4651
4654
|
opts.onOverviewDone?.(oneBased, chunks.length, ids, chunkOverviewSize);
|
|
4652
|
-
const balanceChunk = await fetchBalanceMap(
|
|
4653
|
-
media,
|
|
4654
|
-
ids,
|
|
4655
|
-
config,
|
|
4656
|
-
startDate,
|
|
4657
|
-
endDate,
|
|
4658
|
-
verbose
|
|
4659
|
-
);
|
|
4655
|
+
const balanceChunk = await fetchBalanceMap(media, ids, config, startDate, endDate, verbose);
|
|
4660
4656
|
opts.onBalanceDone?.(oneBased, chunks.length, ids, balanceChunk.size);
|
|
4661
4657
|
for (const [k, v] of balanceChunk) balanceMap.set(k, v);
|
|
4662
4658
|
}
|
|
@@ -4986,20 +4982,16 @@ async function runAccountsDigest(opts) {
|
|
|
4986
4982
|
verbose: opts.verbose,
|
|
4987
4983
|
accountMediaTypes: buildAccountMediaTypesMap(accountsList),
|
|
4988
4984
|
onChunkStart: (chunkIdx, totalChunks, ids) => {
|
|
4989
|
-
process.stderr.write(
|
|
4990
|
-
|
|
4991
|
-
`
|
|
4992
|
-
);
|
|
4985
|
+
process.stderr.write(` \u2192 [\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\uFF08${ids.length} \u6237\uFF09\u2026
|
|
4986
|
+
`);
|
|
4993
4987
|
},
|
|
4994
4988
|
onOverviewDone: (chunkIdx, totalChunks, ids, size) => {
|
|
4995
4989
|
process.stderr.write(
|
|
4996
4990
|
` \u2713 [\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${size} \u6761\uFF09
|
|
4997
4991
|
`
|
|
4998
4992
|
);
|
|
4999
|
-
process.stderr.write(
|
|
5000
|
-
|
|
5001
|
-
`
|
|
5002
|
-
);
|
|
4993
|
+
process.stderr.write(` \u2192 [\u4F59\u989D] \u7B2C ${chunkIdx}/${totalChunks} \u6279\uFF08${ids.length} \u6237\uFF09\u2026
|
|
4994
|
+
`);
|
|
5003
4995
|
},
|
|
5004
4996
|
onBalanceDone: (chunkIdx, totalChunks, ids, size) => {
|
|
5005
4997
|
process.stderr.write(
|
|
@@ -5773,14 +5765,14 @@ function computeBackoffMs(attempt, policy) {
|
|
|
5773
5765
|
return capped + jitter;
|
|
5774
5766
|
}
|
|
5775
5767
|
function sleep3(ms, signal) {
|
|
5776
|
-
return new Promise((
|
|
5768
|
+
return new Promise((resolve17, reject) => {
|
|
5777
5769
|
if (signal?.aborted) {
|
|
5778
5770
|
reject(new Error("retry sleep aborted"));
|
|
5779
5771
|
return;
|
|
5780
5772
|
}
|
|
5781
5773
|
const timer = setTimeout(() => {
|
|
5782
5774
|
signal?.removeEventListener("abort", onAbort);
|
|
5783
|
-
|
|
5775
|
+
resolve17();
|
|
5784
5776
|
}, ms);
|
|
5785
5777
|
const onAbort = () => {
|
|
5786
5778
|
clearTimeout(timer);
|
|
@@ -101574,7 +101566,7 @@ function defaultDateRange3() {
|
|
|
101574
101566
|
const fmt2 = (d) => d.toISOString().slice(0, 10);
|
|
101575
101567
|
return { startDate: fmt2(start), endDate: fmt2(end) };
|
|
101576
101568
|
}
|
|
101577
|
-
function
|
|
101569
|
+
function resolveDateRange(start, end) {
|
|
101578
101570
|
if (start && end) return { startDate: start, endDate: end };
|
|
101579
101571
|
if (!start && !end) return defaultDateRange3();
|
|
101580
101572
|
console.error("\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF09\u3002\n");
|
|
@@ -101583,7 +101575,7 @@ function resolveDateRange2(start, end) {
|
|
|
101583
101575
|
function buildSearchParams(def, start, end, extras) {
|
|
101584
101576
|
const params = new URLSearchParams();
|
|
101585
101577
|
if (def.dateMode === "range") {
|
|
101586
|
-
const { startDate, endDate } =
|
|
101578
|
+
const { startDate, endDate } = resolveDateRange(start, end);
|
|
101587
101579
|
params.set("startDate", startDate);
|
|
101588
101580
|
params.set("endDate", endDate);
|
|
101589
101581
|
}
|
|
@@ -101718,7 +101710,7 @@ function endpointHintFrom(def) {
|
|
|
101718
101710
|
}
|
|
101719
101711
|
function manifestDateRange(def, start, end) {
|
|
101720
101712
|
if (def.dateMode === "none") return null;
|
|
101721
|
-
const { startDate, endDate } =
|
|
101713
|
+
const { startDate, endDate } = resolveDateRange(start, end);
|
|
101722
101714
|
return { mode: "range", start: startDate, end: endDate };
|
|
101723
101715
|
}
|
|
101724
101716
|
async function fetchSectionPayload(def, opts, config, id, http2) {
|
|
@@ -101744,7 +101736,7 @@ async function fetchSectionPayload(def, opts, config, id, http2) {
|
|
|
101744
101736
|
if (n(opts.conversionsGreater)) extras.conversionsGreater = opts.conversionsGreater;
|
|
101745
101737
|
}
|
|
101746
101738
|
if (def.name === "materials") {
|
|
101747
|
-
const { startDate, endDate } =
|
|
101739
|
+
const { startDate, endDate } = resolveDateRange(opts.start, opts.end);
|
|
101748
101740
|
const q = `?${new URLSearchParams({ startDate, endDate }).toString()}`;
|
|
101749
101741
|
const videoPath = `/reporting/media-account/${id}/Videos${q}`;
|
|
101750
101742
|
const imagePath = `/reporting/media-account/${id}/CampaignAssetView${q}`;
|
|
@@ -101792,7 +101784,7 @@ async function fetchSectionPayload(def, opts, config, id, http2) {
|
|
|
101792
101784
|
);
|
|
101793
101785
|
}
|
|
101794
101786
|
if (def.name === "daily-metrics") {
|
|
101795
|
-
const { startDate, endDate } =
|
|
101787
|
+
const { startDate, endDate } = resolveDateRange(opts.start, opts.end);
|
|
101796
101788
|
const { startInstant, endInstant } = resolveDailyMetricsQueryTimes(startDate, endDate);
|
|
101797
101789
|
const params = new URLSearchParams({
|
|
101798
101790
|
mediaCustomerIds: id,
|
|
@@ -102360,8 +102352,8 @@ async function runBatch(opts) {
|
|
|
102360
102352
|
return;
|
|
102361
102353
|
}
|
|
102362
102354
|
if (raw instanceof HttpRetryError) {
|
|
102363
|
-
const
|
|
102364
|
-
await recordTaskTransition(opts.paths, inner, task,
|
|
102355
|
+
const next2 = raw.class === "permanent" ? "failed_permanent" : "failed_retryable";
|
|
102356
|
+
await recordTaskTransition(opts.paths, inner, task, next2, {
|
|
102365
102357
|
attempts: raw.attempts,
|
|
102366
102358
|
error: raw.message,
|
|
102367
102359
|
status: raw.status,
|
|
@@ -102371,12 +102363,28 @@ async function runBatch(opts) {
|
|
|
102371
102363
|
await onTaskFinish(false);
|
|
102372
102364
|
return;
|
|
102373
102365
|
}
|
|
102374
|
-
|
|
102366
|
+
const classified = classifyError(err);
|
|
102367
|
+
if (classified.class === "abort") {
|
|
102368
|
+
tokenInvalidated = (classified.status ?? 0) === 401;
|
|
102369
|
+
await recordTaskTransition(opts.paths, inner, task, "failed_permanent", {
|
|
102370
|
+
attempts: 1,
|
|
102371
|
+
error: err.message,
|
|
102372
|
+
status: classified.status,
|
|
102373
|
+
durationMs
|
|
102374
|
+
});
|
|
102375
|
+
await writeTaskError(opts.paths, errorsDir, task, err, "abort");
|
|
102376
|
+
await onTaskFinish(false);
|
|
102377
|
+
aborterToken.abort();
|
|
102378
|
+
return;
|
|
102379
|
+
}
|
|
102380
|
+
const next = classified.class === "permanent" ? "failed_permanent" : "failed_retryable";
|
|
102381
|
+
await recordTaskTransition(opts.paths, inner, task, next, {
|
|
102375
102382
|
attempts: 1,
|
|
102376
102383
|
error: err.message,
|
|
102384
|
+
status: classified.status,
|
|
102377
102385
|
durationMs
|
|
102378
102386
|
});
|
|
102379
|
-
await writeTaskError(opts.paths, errorsDir, task, err,
|
|
102387
|
+
await writeTaskError(opts.paths, errorsDir, task, err, classified.class);
|
|
102380
102388
|
await onTaskFinish(false);
|
|
102381
102389
|
}
|
|
102382
102390
|
},
|
|
@@ -103165,9 +103173,7 @@ async function runAllSections(opts) {
|
|
|
103165
103173
|
);
|
|
103166
103174
|
const cliVersion = getCurrentVersion2();
|
|
103167
103175
|
const overallT0 = performance4.now();
|
|
103168
|
-
const tasks = targets.map(
|
|
103169
|
-
(def) => () => fetchAndWriteSection(def, opts, config, id, cliVersion)
|
|
103170
|
-
);
|
|
103176
|
+
const tasks = targets.map((def) => () => fetchAndWriteSection(def, opts, config, id, cliVersion));
|
|
103171
103177
|
let results = await runWithConcurrency(tasks, concurrency);
|
|
103172
103178
|
const failedIndices = results.map((r, i) => r.ok ? -1 : i).filter((i) => i >= 0);
|
|
103173
103179
|
if (failedIndices.length > 0) {
|
|
@@ -103415,9 +103421,9 @@ var init_google_analysis3 = __esm({
|
|
|
103415
103421
|
init_dist();
|
|
103416
103422
|
init_dist();
|
|
103417
103423
|
import * as fs23 from "fs";
|
|
103418
|
-
import * as
|
|
103424
|
+
import * as path33 from "path";
|
|
103419
103425
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
103420
|
-
import { Command as
|
|
103426
|
+
import { Command as Command4 } from "commander";
|
|
103421
103427
|
|
|
103422
103428
|
// src/commands/login/urls.ts
|
|
103423
103429
|
init_defaults();
|
|
@@ -103902,8 +103908,8 @@ function deriveWebUrl(apiBaseUrl) {
|
|
|
103902
103908
|
}
|
|
103903
103909
|
var TSO_UMI_ROUTE_PREFIX = "/v3umijs/tso";
|
|
103904
103910
|
function buildTsoPageWebUrl(webUrl, tsoSubRoute, extraQuery) {
|
|
103905
|
-
const
|
|
103906
|
-
const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${
|
|
103911
|
+
const path34 = tsoSubRoute.replace(/^\/+/, "");
|
|
103912
|
+
const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${path34}`;
|
|
103907
103913
|
const q = new URLSearchParams({ tso: umiPath });
|
|
103908
103914
|
if (extraQuery) {
|
|
103909
103915
|
for (const [key, value] of Object.entries(extraQuery)) {
|
|
@@ -103911,7 +103917,7 @@ function buildTsoPageWebUrl(webUrl, tsoSubRoute, extraQuery) {
|
|
|
103911
103917
|
if (v) q.set(key, v);
|
|
103912
103918
|
}
|
|
103913
103919
|
}
|
|
103914
|
-
return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${
|
|
103920
|
+
return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${path34}?${q.toString()}`;
|
|
103915
103921
|
}
|
|
103916
103922
|
function cmdConfigShow() {
|
|
103917
103923
|
const shared = readSharedConfig();
|
|
@@ -107382,1304 +107388,334 @@ async function runReportPushReceiveEmails(opts) {
|
|
|
107382
107388
|
// src/commands/report/register-commander-types.ts
|
|
107383
107389
|
var splitCsv = (v) => v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
107384
107390
|
|
|
107385
|
-
// src/commands/report
|
|
107386
|
-
|
|
107387
|
-
|
|
107388
|
-
|
|
107389
|
-
|
|
107390
|
-
|
|
107391
|
-
|
|
107392
|
-
|
|
107393
|
-
|
|
107394
|
-
|
|
107395
|
-
|
|
107396
|
-
|
|
107397
|
-
|
|
107398
|
-
|
|
107399
|
-
|
|
107400
|
-
|
|
107401
|
-
|
|
107402
|
-
|
|
107403
|
-
|
|
107404
|
-
|
|
107405
|
-
}
|
|
107406
|
-
|
|
107407
|
-
|
|
107408
|
-
|
|
107409
|
-
|
|
107410
|
-
|
|
107411
|
-
|
|
107412
|
-
|
|
107413
|
-
|
|
107414
|
-
|
|
107415
|
-
|
|
107416
|
-
|
|
107417
|
-
|
|
107418
|
-
|
|
107419
|
-
|
|
107420
|
-
|
|
107421
|
-
|
|
107422
|
-
|
|
107423
|
-
|
|
107424
|
-
|
|
107425
|
-
|
|
107426
|
-
|
|
107427
|
-
|
|
107428
|
-
|
|
107429
|
-
|
|
107430
|
-
|
|
107431
|
-
|
|
107432
|
-
|
|
107433
|
-
|
|
107434
|
-
|
|
107435
|
-
section
|
|
107436
|
-
|
|
107437
|
-
|
|
107438
|
-
|
|
107439
|
-
|
|
107391
|
+
// src/commands/report/register.ts
|
|
107392
|
+
function register13(program2) {
|
|
107393
|
+
const reportCmd = program2.command("report").description("\u4F18\u5316\u62A5\u544A\u4E0E\u63A8\u9001\u7BA1\u7406");
|
|
107394
|
+
reportCmd.command("list").description("\u67E5\u8BE2\u4F18\u5316\u62A5\u544A\u5217\u8868").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").option("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B\uFF08\u5982 Daily / Hourly\uFF09").option("-s, --status <ready>", "\u751F\u6210\u72B6\u6001\uFF08true/false\uFF09").option("-k, --keyword <text>", "\u62A5\u544A\u540D\u79F0\u5173\u952E\u5B57").option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--token <token>", "Auth Token").option(
|
|
107395
|
+
"--json-out <path>",
|
|
107396
|
+
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
107397
|
+
void 0
|
|
107398
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107399
|
+
await runReportList({
|
|
107400
|
+
token: opts.token,
|
|
107401
|
+
media: opts.media,
|
|
107402
|
+
type: opts.type,
|
|
107403
|
+
status: opts.status,
|
|
107404
|
+
keyword: opts.keyword,
|
|
107405
|
+
startDate: opts.start,
|
|
107406
|
+
endDate: opts.end,
|
|
107407
|
+
page: opts.page,
|
|
107408
|
+
pageSize: opts.pageSize,
|
|
107409
|
+
jsonOut: opts.jsonOut,
|
|
107410
|
+
verbose: opts.verbose
|
|
107411
|
+
});
|
|
107412
|
+
});
|
|
107413
|
+
reportCmd.command("create").description("\u6279\u91CF\u751F\u6210\u4F18\u5316\u62A5\u544A").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("-a, --accounts <ids>", "\u8D26\u6237 ID\uFF0C\u9017\u53F7\u5206\u9694").requiredOption("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B\uFF0C\u5982 Daily").requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
|
|
107414
|
+
"--json-out <path>",
|
|
107415
|
+
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
107416
|
+
void 0
|
|
107417
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107418
|
+
await runReportCreate({
|
|
107419
|
+
token: opts.token,
|
|
107420
|
+
media: opts.media,
|
|
107421
|
+
accounts: opts.accounts.split(",").map((s) => s.trim()),
|
|
107422
|
+
type: opts.type,
|
|
107423
|
+
startDate: opts.start,
|
|
107424
|
+
endDate: opts.end,
|
|
107425
|
+
jsonOut: opts.jsonOut,
|
|
107426
|
+
verbose: opts.verbose
|
|
107427
|
+
});
|
|
107428
|
+
});
|
|
107429
|
+
reportCmd.command("delete").description("\u5220\u9664\u4F18\u5316\u62A5\u544A\uFF08--id \u6216 --ids \u81F3\u5C11\u4F20\u4E00\u4E2A\uFF09").option("--id <id>", "\u5355\u6761\u62A5\u544A entityId\uFF08\u4E0E --ids \u4E8C\u9009\u4E00\uFF09").option("--ids <ids>", "\u6279\u91CF\u5220\u9664\uFF0C\u9017\u53F7\u5206\u9694 entityId\uFF08\u4E0E --id \u4E8C\u9009\u4E00\uFF09").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107430
|
+
await runReportDelete({
|
|
107431
|
+
token: opts.token,
|
|
107432
|
+
id: opts.id,
|
|
107433
|
+
ids: opts.ids ? opts.ids.split(",").map((s) => s.trim()) : void 0,
|
|
107434
|
+
verbose: opts.verbose
|
|
107435
|
+
});
|
|
107436
|
+
});
|
|
107437
|
+
reportCmd.command("meta-overview").description(
|
|
107438
|
+
"Meta \u8D26\u6237\u5206\u6790\u603B\u89C8\uFF08OverviewSectionData\uFF1BFacebookAds \u6388\u6743\u6237\u8D70 FacebookAds/act_\uFF0CMetaAd \u5F00\u6237\u8D70 MetaAd\uFF09"
|
|
107439
|
+
).requiredOption("-a, --account <id>", "Meta \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
|
|
107440
|
+
"--json-out <path>",
|
|
107441
|
+
"\u843D\u76D8\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest[-<accountId>].json\uFF1B\u6587\u4EF6\u540D\u4E3A `<section>[-<accountId>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09"
|
|
107442
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107443
|
+
await runReportMetaOverview({
|
|
107444
|
+
token: opts.token,
|
|
107445
|
+
account: opts.account,
|
|
107446
|
+
startDate: opts.start,
|
|
107447
|
+
endDate: opts.end,
|
|
107448
|
+
jsonOut: opts.jsonOut,
|
|
107449
|
+
verbose: opts.verbose
|
|
107450
|
+
});
|
|
107451
|
+
});
|
|
107452
|
+
const pushCmd = reportCmd.command("push").description("\u63A8\u9001\u7BA1\u7406\uFF08\u90AE\u4EF6\u62A5\u544A\u63A8\u9001\u4EFB\u52A1\uFF09");
|
|
107453
|
+
pushCmd.command("list").description("\u67E5\u8BE2\u63A8\u9001\u4EFB\u52A1\u5217\u8868").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").option("-k, --keyword <text>", "\u63A8\u9001\u540D\u79F0\u5173\u952E\u5B57").option("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B").option("--stopped", "\u53EA\u663E\u793A\u5DF2\u505C\u7528\u7684\u63A8\u9001", false).option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--token <token>", "Auth Token").option(
|
|
107454
|
+
"--json-out <path>",
|
|
107455
|
+
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
107456
|
+
void 0
|
|
107457
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107458
|
+
await runReportPushList({
|
|
107459
|
+
token: opts.token,
|
|
107460
|
+
media: opts.media,
|
|
107461
|
+
keyword: opts.keyword,
|
|
107462
|
+
type: opts.type,
|
|
107463
|
+
stopped: opts.stopped,
|
|
107464
|
+
startDate: opts.start,
|
|
107465
|
+
endDate: opts.end,
|
|
107466
|
+
page: opts.page,
|
|
107467
|
+
pageSize: opts.pageSize,
|
|
107468
|
+
jsonOut: opts.jsonOut,
|
|
107469
|
+
verbose: opts.verbose
|
|
107470
|
+
});
|
|
107471
|
+
});
|
|
107472
|
+
pushCmd.command("create").description(
|
|
107473
|
+
"\u65B0\u5EFA\u63A8\u9001\u914D\u7F6E\uFF08POST /command/report-push/settings/{\u5A92\u4F53}\uFF0C\u4E0E\u7F51\u9875\u300C\u6DFB\u52A0\u63A8\u9001\u300D\u4E00\u81F4\uFF09"
|
|
107474
|
+
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").requiredOption("--name <name>", "\u63A8\u9001\u540D\u79F0\uFF08\u5BF9\u5E94 body.Name\uFF09").requiredOption(
|
|
107475
|
+
"--media-accounts <ids>",
|
|
107476
|
+
"\u5E7F\u544A\u8D26\u6237 entityId\uFF0C\u9017\u53F7\u5206\u9694\uFF08list-accounts --json-out \u2192 ma.entityId\uFF09",
|
|
107477
|
+
splitCsv
|
|
107478
|
+
).requiredOption("--emails <list>", "\u6536\u4EF6\u90AE\u7BB1\uFF0C\u9017\u53F7\u5206\u9694", splitCsv).requiredOption("--types <list>", "\u62A5\u544A\u7C7B\u578B\uFF0C\u9017\u53F7\u5206\u9694\uFF1ADaily | Weekly | Monthly", splitCsv).option("--title <text>", "\u90AE\u4EF6\u6807\u9898\uFF08ReceiveTitle\uFF0C\u53EF\u7A7A\uFF09", "").option("--ai-suggest", "\u4EC5 Google\uFF1AAiSuggest=true", false).option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107479
|
+
await runReportPushCreate({
|
|
107480
|
+
token: opts.token,
|
|
107481
|
+
media: opts.media,
|
|
107482
|
+
name: opts.name,
|
|
107483
|
+
mediaAccounts: opts.mediaAccounts,
|
|
107484
|
+
receiveEmails: opts.emails,
|
|
107485
|
+
reportTypes: opts.types,
|
|
107486
|
+
receiveTitle: opts.title ?? "",
|
|
107487
|
+
aiSuggest: opts.aiSuggest,
|
|
107488
|
+
verbose: opts.verbose
|
|
107489
|
+
});
|
|
107490
|
+
});
|
|
107491
|
+
pushCmd.command("update").description(
|
|
107492
|
+
"\u7F16\u8F91\u63A8\u9001\u914D\u7F6E\uFF08PUT /command/report-push/settings/{\u5A92\u4F53}/{id}\uFF1Bid \u4E0E\u7F51\u9875\u7F16\u8F91\u7528 row.id \u4E00\u81F4\uFF0C\u901A\u5E38\u540C entityId\uFF09"
|
|
107493
|
+
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u914D\u7F6E id\uFF08push list --json-out \u2192 items[].id \u6216 entityId\uFF09").requiredOption("--name <name>", "\u63A8\u9001\u540D\u79F0").requiredOption("--media-accounts <ids>", "\u5E7F\u544A\u8D26\u6237 entityId\uFF0C\u9017\u53F7\u5206\u9694", splitCsv).requiredOption("--emails <list>", "\u6536\u4EF6\u90AE\u7BB1\uFF0C\u9017\u53F7\u5206\u9694", splitCsv).requiredOption("--types <list>", "\u62A5\u544A\u7C7B\u578B\uFF0C\u9017\u53F7\u5206\u9694\uFF1ADaily | Weekly | Monthly", splitCsv).option("--title <text>", "\u90AE\u4EF6\u6807\u9898", "").option("--ai-suggest", "\u4EC5 Google\uFF1AAiSuggest=true", false).option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107494
|
+
await runReportPushUpdate({
|
|
107495
|
+
token: opts.token,
|
|
107496
|
+
media: opts.media,
|
|
107497
|
+
id: opts.id,
|
|
107498
|
+
name: opts.name,
|
|
107499
|
+
mediaAccounts: opts.mediaAccounts,
|
|
107500
|
+
receiveEmails: opts.emails,
|
|
107501
|
+
reportTypes: opts.types,
|
|
107502
|
+
receiveTitle: opts.title ?? "",
|
|
107503
|
+
aiSuggest: opts.aiSuggest,
|
|
107504
|
+
verbose: opts.verbose
|
|
107505
|
+
});
|
|
107506
|
+
});
|
|
107507
|
+
pushCmd.command("history").description(
|
|
107508
|
+
"\u63A8\u9001\u8BB0\u5F55\u5217\u8868\uFF08GET /query/report-push/settings/{\u5A92\u4F53}/{settingId}/push-history\uFF0C\u5BF9\u5E94\u7F51\u9875 /dataReport/pushRecord\uFF09"
|
|
107509
|
+
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").requiredOption(
|
|
107510
|
+
"--setting-id <id>",
|
|
107511
|
+
"\u63A8\u9001\u89C4\u5219 id\uFF08\u4E0E push list \u7684 entityId \u4E00\u81F4\uFF1B\u7F51\u9875\u8DEF\u7531 entityId\uFF09"
|
|
107512
|
+
).option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--start <date>", "\u7B5B\u9009\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u7B5B\u9009\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--receive-email <email>", "\u7B5B\u9009\u6536\u4EF6\u90AE\u7BB1").option("--report-type <type>", "\u7B5B\u9009\u62A5\u544A\u7C7B\u578B").option("--media-account-id <entityId>", "\u7B5B\u9009\u8D26\u6237\uFF08\u4F20\u8D26\u6237 entityId\uFF09").option("--token <token>", "Auth Token").option(
|
|
107513
|
+
"--json-out <path>",
|
|
107514
|
+
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
107515
|
+
void 0
|
|
107516
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107517
|
+
await runReportPushHistory({
|
|
107518
|
+
token: opts.token,
|
|
107519
|
+
media: opts.media,
|
|
107520
|
+
settingId: opts.settingId,
|
|
107521
|
+
page: opts.page,
|
|
107522
|
+
pageSize: opts.pageSize,
|
|
107523
|
+
startDate: opts.start,
|
|
107524
|
+
endDate: opts.end,
|
|
107525
|
+
receiveEmail: opts.receiveEmail,
|
|
107526
|
+
reportType: opts.reportType,
|
|
107527
|
+
mediaAccountId: opts.mediaAccountId,
|
|
107528
|
+
jsonOut: opts.jsonOut,
|
|
107529
|
+
verbose: opts.verbose
|
|
107530
|
+
});
|
|
107531
|
+
});
|
|
107532
|
+
pushCmd.command("receive-emails").description(
|
|
107533
|
+
"\u67E5\u8BE2\u63A8\u9001\u66FE\u7528\u8FC7\u7684\u6536\u4EF6\u90AE\u7BB1\u5217\u8868\uFF08GET /query/report-push/settings/{\u5A92\u4F53}/all/receive-emails\uFF0C\u4E0E\u7F51\u9875\u4E0B\u62C9\u4E00\u81F4\uFF09"
|
|
107534
|
+
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").option("--token <token>", "Auth Token").option(
|
|
107535
|
+
"--json-out <path>",
|
|
107536
|
+
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
107537
|
+
void 0
|
|
107538
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107539
|
+
await runReportPushReceiveEmails({
|
|
107540
|
+
token: opts.token,
|
|
107541
|
+
media: opts.media,
|
|
107542
|
+
jsonOut: opts.jsonOut,
|
|
107543
|
+
verbose: opts.verbose
|
|
107544
|
+
});
|
|
107545
|
+
});
|
|
107546
|
+
pushCmd.command("start").description("\u542F\u52A8\u63A8\u9001\u4EFB\u52A1").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u4EFB\u52A1 entityId").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107547
|
+
await runReportPushStart({
|
|
107548
|
+
token: opts.token,
|
|
107549
|
+
media: opts.media,
|
|
107550
|
+
id: opts.id,
|
|
107551
|
+
verbose: opts.verbose
|
|
107552
|
+
});
|
|
107553
|
+
});
|
|
107554
|
+
pushCmd.command("stop").description("\u505C\u6B62\u63A8\u9001\u4EFB\u52A1").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u4EFB\u52A1 entityId").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107555
|
+
await runReportPushStop({
|
|
107556
|
+
token: opts.token,
|
|
107557
|
+
media: opts.media,
|
|
107558
|
+
id: opts.id,
|
|
107559
|
+
verbose: opts.verbose
|
|
107560
|
+
});
|
|
107561
|
+
});
|
|
107562
|
+
pushCmd.command("delete").description("\u5220\u9664\u63A8\u9001\u4EFB\u52A1").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u4EFB\u52A1 entityId").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107563
|
+
await runReportPushDelete({
|
|
107564
|
+
token: opts.token,
|
|
107565
|
+
media: opts.media,
|
|
107566
|
+
id: opts.id,
|
|
107567
|
+
verbose: opts.verbose
|
|
107568
|
+
});
|
|
107440
107569
|
});
|
|
107441
|
-
console.log(JSON.stringify(summary));
|
|
107442
|
-
}
|
|
107443
|
-
async function fetchTikTokJson(config, url, verbose) {
|
|
107444
|
-
return apiFetch2(url, config, {}, verbose);
|
|
107445
107570
|
}
|
|
107446
107571
|
|
|
107447
|
-
// src/commands/
|
|
107572
|
+
// src/commands/transfer.ts
|
|
107448
107573
|
init_auth();
|
|
107449
|
-
|
|
107450
|
-
|
|
107451
|
-
|
|
107452
|
-
|
|
107453
|
-
|
|
107454
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
107455
|
-
const url = reportingUrl(config, id, "OverviewSectionData", params.toString());
|
|
107456
|
-
try {
|
|
107457
|
-
const data = await fetchTikTokJson(config, url, !!opts.verbose);
|
|
107458
|
-
if (opts.jsonOut) {
|
|
107459
|
-
await emitTiktokSnapshot(opts, "tiktok-overview", id, startDate, endDate, data);
|
|
107460
|
-
return;
|
|
107461
|
-
}
|
|
107462
|
-
console.log(`
|
|
107463
|
-
\u2705 TikTok \u8D26\u6237\u603B\u89C8 ${id} ${startDate} ~ ${endDate}`);
|
|
107464
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107465
|
-
} catch (err) {
|
|
107466
|
-
console.error(`
|
|
107467
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107468
|
-
`);
|
|
107469
|
-
process.exit(1);
|
|
107470
|
-
}
|
|
107574
|
+
init_cli_json_snapshot();
|
|
107575
|
+
init_cli_table();
|
|
107576
|
+
var VALID_MEDIA_TYPES6 = ["Google", "TikTok", "MetaAd", "BingV2"];
|
|
107577
|
+
function mediaToPath(media) {
|
|
107578
|
+
return media.toLowerCase();
|
|
107471
107579
|
}
|
|
107472
|
-
async function
|
|
107473
|
-
|
|
107474
|
-
|
|
107475
|
-
|
|
107476
|
-
|
|
107477
|
-
|
|
107478
|
-
|
|
107479
|
-
|
|
107480
|
-
take: String(take)
|
|
107481
|
-
});
|
|
107482
|
-
const url = reportingUrl(config, id, "CampaignSectionData", params.toString());
|
|
107483
|
-
try {
|
|
107484
|
-
const data = await fetchTikTokJson(config, url, !!opts.verbose);
|
|
107485
|
-
if (opts.jsonOut) {
|
|
107486
|
-
await emitTiktokSnapshot(opts, "tiktok-campaigns", id, startDate, endDate, data);
|
|
107487
|
-
return;
|
|
107488
|
-
}
|
|
107489
|
-
console.log(`
|
|
107490
|
-
\u2705 TikTok \u5E7F\u544A\u7CFB\u5217\u5206\u6BB5 ${id} take=${take}`);
|
|
107491
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107492
|
-
} catch (err) {
|
|
107493
|
-
console.error(`
|
|
107494
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107495
|
-
`);
|
|
107580
|
+
async function runTransferCreate(opts) {
|
|
107581
|
+
if (!VALID_MEDIA_TYPES6.includes(opts.media)) {
|
|
107582
|
+
console.error(
|
|
107583
|
+
`
|
|
107584
|
+
\u274C \u4E0D\u652F\u6301\u7684\u5A92\u4F53\u7C7B\u578B\uFF1A${opts.media}
|
|
107585
|
+
\u53EF\u9009\uFF1A${VALID_MEDIA_TYPES6.join(" | ")}
|
|
107586
|
+
`
|
|
107587
|
+
);
|
|
107496
107588
|
process.exit(1);
|
|
107497
107589
|
}
|
|
107498
|
-
}
|
|
107499
|
-
async function runReportTikTokAdGroups(opts) {
|
|
107500
|
-
const id = assertTikTokAccountId(opts.account);
|
|
107501
|
-
const { startDate, endDate } = resolveDateRange(opts.start, opts.end);
|
|
107502
|
-
const take = resolveTake(opts.take);
|
|
107503
107590
|
const config = loadConfig(opts.token);
|
|
107504
|
-
const
|
|
107505
|
-
|
|
107506
|
-
|
|
107507
|
-
|
|
107508
|
-
|
|
107509
|
-
|
|
107510
|
-
|
|
107511
|
-
|
|
107512
|
-
|
|
107513
|
-
|
|
107514
|
-
|
|
107591
|
+
const url = `${config.apiBaseUrl}/command/accounttransfer/${mediaToPath(opts.media)}/createaccounttransfers`;
|
|
107592
|
+
const body = [
|
|
107593
|
+
{
|
|
107594
|
+
exchangeRate: 1,
|
|
107595
|
+
accountTransferOutData: {
|
|
107596
|
+
mediaCustomerId: opts.outAccount,
|
|
107597
|
+
shortId: null,
|
|
107598
|
+
transferOutAmounts: opts.amount
|
|
107599
|
+
},
|
|
107600
|
+
accountTransferInData: {
|
|
107601
|
+
mediaCustomerId: opts.inAccount,
|
|
107602
|
+
shortId: null
|
|
107603
|
+
},
|
|
107604
|
+
customerName: opts.customerName ?? ""
|
|
107515
107605
|
}
|
|
107516
|
-
|
|
107517
|
-
|
|
107518
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107519
|
-
} catch (err) {
|
|
107520
|
-
console.error(`
|
|
107521
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107522
|
-
`);
|
|
107523
|
-
process.exit(1);
|
|
107524
|
-
}
|
|
107525
|
-
}
|
|
107526
|
-
async function runReportTikTokAds(opts) {
|
|
107527
|
-
const id = assertTikTokAccountId(opts.account);
|
|
107528
|
-
const { startDate, endDate } = resolveDateRange(opts.start, opts.end);
|
|
107529
|
-
const take = resolveTake(opts.take);
|
|
107530
|
-
const config = loadConfig(opts.token);
|
|
107531
|
-
const params = new URLSearchParams({
|
|
107532
|
-
startDate,
|
|
107533
|
-
endDate,
|
|
107534
|
-
take: String(take)
|
|
107535
|
-
});
|
|
107536
|
-
const url = reportingUrl(config, id, "AdReport", params.toString());
|
|
107606
|
+
];
|
|
107607
|
+
let result;
|
|
107537
107608
|
try {
|
|
107538
|
-
|
|
107539
|
-
|
|
107540
|
-
|
|
107541
|
-
|
|
107542
|
-
|
|
107543
|
-
|
|
107544
|
-
\u2705 TikTok \u5E7F\u544A\u62A5\u8868 ${id} take=${take}`);
|
|
107545
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107609
|
+
result = await apiFetch2(
|
|
107610
|
+
url,
|
|
107611
|
+
config,
|
|
107612
|
+
{ method: "POST", body: JSON.stringify(body) },
|
|
107613
|
+
opts.verbose
|
|
107614
|
+
);
|
|
107546
107615
|
} catch (err) {
|
|
107547
107616
|
console.error(`
|
|
107548
|
-
\u274C \u8BF7\
|
|
107617
|
+
\u274C \u8F6C\u8D26\u7533\u8BF7\u63D0\u4EA4\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107549
107618
|
`);
|
|
107550
107619
|
process.exit(1);
|
|
107551
107620
|
}
|
|
107552
|
-
|
|
107553
|
-
|
|
107554
|
-
|
|
107555
|
-
|
|
107556
|
-
|
|
107557
|
-
|
|
107558
|
-
|
|
107559
|
-
|
|
107560
|
-
endDate,
|
|
107561
|
-
take: String(take),
|
|
107562
|
-
dimensions: opts.dimension
|
|
107563
|
-
});
|
|
107564
|
-
const url = reportingUrl(config, id, "AudienceReport", params.toString());
|
|
107565
|
-
try {
|
|
107566
|
-
const data = await fetchTikTokJson(config, url, !!opts.verbose);
|
|
107567
|
-
if (opts.jsonOut) {
|
|
107568
|
-
await emitTiktokSnapshot(
|
|
107569
|
-
opts,
|
|
107570
|
-
`tiktok-audience-${opts.dimension}`,
|
|
107571
|
-
id,
|
|
107572
|
-
startDate,
|
|
107573
|
-
endDate,
|
|
107574
|
-
data
|
|
107575
|
-
);
|
|
107576
|
-
return;
|
|
107577
|
-
}
|
|
107578
|
-
console.log(`
|
|
107579
|
-
\u2705 TikTok \u53D7\u4F17\u62A5\u8868 ${id} dimensions=${opts.dimension} take=${take}`);
|
|
107580
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107581
|
-
} catch (err) {
|
|
107582
|
-
console.error(`
|
|
107583
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107584
|
-
`);
|
|
107585
|
-
process.exit(1);
|
|
107621
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
107622
|
+
section: `transfer-create-${mediaToPath(opts.media)}`,
|
|
107623
|
+
commandLabel: "transfer create",
|
|
107624
|
+
commandHint: opts.media,
|
|
107625
|
+
payload: result,
|
|
107626
|
+
idSuffix: opts.media
|
|
107627
|
+
})) {
|
|
107628
|
+
return;
|
|
107586
107629
|
}
|
|
107630
|
+
console.log(
|
|
107631
|
+
`
|
|
107632
|
+
\u2705 \u8F6C\u8D26\u7533\u8BF7\u5DF2\u63D0\u4EA4\uFF1A${opts.outAccount} \u2192 ${opts.inAccount}\uFF0C\u91D1\u989D ${opts.amount}\u3002
|
|
107633
|
+
`
|
|
107634
|
+
);
|
|
107635
|
+
console.log(" \u4F7F\u7528 transfer list \u67E5\u770B\u8F6C\u8D26\u8BB0\u5F55\u53CA\u5230\u8D26\u72B6\u6001\u3002\n");
|
|
107587
107636
|
}
|
|
107588
|
-
async function
|
|
107589
|
-
|
|
107590
|
-
|
|
107591
|
-
|
|
107592
|
-
|
|
107593
|
-
|
|
107594
|
-
|
|
107595
|
-
|
|
107596
|
-
for (const dimension of dims) {
|
|
107597
|
-
const params = new URLSearchParams({
|
|
107598
|
-
startDate,
|
|
107599
|
-
endDate,
|
|
107600
|
-
take: String(take),
|
|
107601
|
-
dimensions: dimension
|
|
107602
|
-
});
|
|
107603
|
-
const url = reportingUrl(config, id, "AudienceReport", params.toString());
|
|
107604
|
-
parts[dimension] = await fetchTikTokJson(config, url, !!opts.verbose);
|
|
107605
|
-
}
|
|
107606
|
-
const merged = { mergedDimensions: dims, startDate, endDate, account: id, data: parts };
|
|
107607
|
-
if (opts.jsonOut) {
|
|
107608
|
-
await emitTiktokSnapshot(opts, "tiktok-audience-merged", id, startDate, endDate, merged);
|
|
107609
|
-
return;
|
|
107610
|
-
}
|
|
107611
|
-
console.log(JSON.stringify(merged, null, 2));
|
|
107612
|
-
} catch (err) {
|
|
107613
|
-
console.error(`
|
|
107614
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107615
|
-
`);
|
|
107637
|
+
async function runTransferList(opts) {
|
|
107638
|
+
if (!VALID_MEDIA_TYPES6.includes(opts.media)) {
|
|
107639
|
+
console.error(
|
|
107640
|
+
`
|
|
107641
|
+
\u274C \u4E0D\u652F\u6301\u7684\u5A92\u4F53\u7C7B\u578B\uFF1A${opts.media}
|
|
107642
|
+
\u53EF\u9009\uFF1A${VALID_MEDIA_TYPES6.join(" | ")}
|
|
107643
|
+
`
|
|
107644
|
+
);
|
|
107616
107645
|
process.exit(1);
|
|
107617
107646
|
}
|
|
107618
|
-
}
|
|
107619
|
-
async function runReportTikTokVideos(opts) {
|
|
107620
|
-
const id = assertTikTokAccountId(opts.account);
|
|
107621
|
-
const { startDate, endDate } = resolveDateRange(opts.start, opts.end);
|
|
107622
|
-
const take = resolveTake(opts.take);
|
|
107623
107647
|
const config = loadConfig(opts.token);
|
|
107624
|
-
const params = new URLSearchParams(
|
|
107625
|
-
|
|
107626
|
-
|
|
107627
|
-
|
|
107628
|
-
|
|
107629
|
-
|
|
107648
|
+
const params = new URLSearchParams();
|
|
107649
|
+
params.set("pageNumber", String(opts.page ?? 1));
|
|
107650
|
+
params.set("pageSize", String(opts.pageSize ?? 20));
|
|
107651
|
+
if (opts.number) params.set("transferNumber", opts.number);
|
|
107652
|
+
if (opts.status) params.set("transferStatus", opts.status);
|
|
107653
|
+
if (opts.startDate) params.set("startDate", opts.startDate);
|
|
107654
|
+
if (opts.endDate) params.set("endDate", opts.endDate);
|
|
107655
|
+
const url = `${config.apiBaseUrl}/query/accounttransfer/${opts.media}/search?${params}`;
|
|
107656
|
+
let data;
|
|
107630
107657
|
try {
|
|
107631
|
-
|
|
107632
|
-
if (opts.jsonOut) {
|
|
107633
|
-
await emitTiktokSnapshot(opts, "tiktok-videos", id, startDate, endDate, data);
|
|
107634
|
-
return;
|
|
107635
|
-
}
|
|
107636
|
-
console.log(`
|
|
107637
|
-
\u2705 TikTok \u89C6\u9891\u7D20\u6750\u62A5\u8868 ${id} take=${take}`);
|
|
107638
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107658
|
+
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
107639
107659
|
} catch (err) {
|
|
107640
107660
|
console.error(`
|
|
107641
|
-
\u274C \
|
|
107661
|
+
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107642
107662
|
`);
|
|
107643
107663
|
process.exit(1);
|
|
107644
107664
|
}
|
|
107645
|
-
|
|
107646
|
-
|
|
107647
|
-
const
|
|
107648
|
-
const
|
|
107649
|
-
|
|
107650
|
-
|
|
107651
|
-
|
|
107665
|
+
const items = data.list ?? data.results ?? [];
|
|
107666
|
+
const total = data.total ?? data.totalResultCount ?? 0;
|
|
107667
|
+
const page = opts.page ?? 1;
|
|
107668
|
+
const pageSize = opts.pageSize ?? 20;
|
|
107669
|
+
const listPayload = wrapListJson({ page, pageSize, total, items });
|
|
107670
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
107671
|
+
section: `transfer-list-${mediaToPath(opts.media)}`,
|
|
107672
|
+
commandLabel: "transfer list",
|
|
107673
|
+
commandHint: opts.media,
|
|
107674
|
+
payload: listPayload,
|
|
107675
|
+
idSuffix: opts.media
|
|
107676
|
+
})) {
|
|
107677
|
+
return;
|
|
107652
107678
|
}
|
|
107653
|
-
|
|
107654
|
-
|
|
107655
|
-
|
|
107656
|
-
|
|
107657
|
-
|
|
107658
|
-
|
|
107659
|
-
|
|
107660
|
-
|
|
107661
|
-
payload: data,
|
|
107662
|
-
cliVersion: getCurrentVersion2()
|
|
107663
|
-
});
|
|
107664
|
-
console.log(JSON.stringify(summary));
|
|
107665
|
-
return;
|
|
107666
|
-
}
|
|
107667
|
-
console.log("\n\u2705 TikTok \u5730\u533A\u4EE3\u7801\u8868\uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
107668
|
-
} catch (err) {
|
|
107669
|
-
console.error(`
|
|
107670
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
107671
|
-
`);
|
|
107672
|
-
process.exit(1);
|
|
107679
|
+
console.log(
|
|
107680
|
+
`
|
|
107681
|
+
\u8F6C\u8D26\u8BB0\u5F55\uFF08\u7B2C ${page}/${Math.max(1, Math.ceil(total / pageSize))} \u9875\uFF0C\u672C\u9875 ${items.length} \u6761\uFF0C\u5171 ${total} \u6761\uFF09
|
|
107682
|
+
`
|
|
107683
|
+
);
|
|
107684
|
+
if (items.length === 0) {
|
|
107685
|
+
console.log(" \u6682\u65E0\u6570\u636E\u3002\n");
|
|
107686
|
+
return;
|
|
107673
107687
|
}
|
|
107674
|
-
|
|
107675
|
-
|
|
107676
|
-
|
|
107677
|
-
|
|
107678
|
-
|
|
107679
|
-
|
|
107680
|
-
|
|
107681
|
-
|
|
107682
|
-
|
|
107683
|
-
|
|
107684
|
-
const
|
|
107685
|
-
|
|
107686
|
-
const
|
|
107687
|
-
|
|
107688
|
-
|
|
107689
|
-
|
|
107690
|
-
|
|
107691
|
-
|
|
107692
|
-
|
|
107693
|
-
|
|
107694
|
-
|
|
107695
|
-
|
|
107696
|
-
|
|
107697
|
-
|
|
107698
|
-
|
|
107699
|
-
|
|
107700
|
-
|
|
107701
|
-
|
|
107702
|
-
}
|
|
107703
|
-
|
|
107704
|
-
|
|
107705
|
-
`);
|
|
107706
|
-
process.exit(1);
|
|
107707
|
-
}
|
|
107708
|
-
}
|
|
107709
|
-
|
|
107710
|
-
// src/commands/report-tiktok-analysis/_register.ts
|
|
107711
|
-
function addDateAndFetchOptions(c) {
|
|
107712
|
-
return c.requiredOption("-a, --account <id>", "TikTok \u5E7F\u544A\u4E3B mediaCustomerId\uFF08\u6570\u5B57\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--take <n>", "\u6D88\u8017/\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF0C\u4E0E\u524D\u7AEF take \u4E00\u81F4\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Auth Token").option(
|
|
107713
|
-
"--json-out <dir>",
|
|
107714
|
-
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
107715
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false);
|
|
107716
|
-
}
|
|
107717
|
-
function registerReportTiktokCommands(reportCmd) {
|
|
107718
|
-
const desc = "TikTok \u8D26\u6237\u5206\u6790\uFF08TSO reporting/media-account/TikTok/...\uFF0C\u4E0E\u5468\u671F\u62A5\u544A\u6570\u636E\u5757\u4E00\u81F4\uFF09";
|
|
107719
|
-
reportCmd.command("tiktok-overview").description(`${desc} \u2014 \u603B\u89C8 OverviewSectionData`).requiredOption("-a, --account <id>", "TikTok mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-t, --token <token>", "Auth Token").option(
|
|
107720
|
-
"--json-out <dir>",
|
|
107721
|
-
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
107722
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107723
|
-
await runReportTikTokOverview(opts);
|
|
107724
|
-
});
|
|
107725
|
-
const bindTake = (name2, segmentLabel, runner) => {
|
|
107726
|
-
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${segmentLabel}`);
|
|
107727
|
-
addDateAndFetchOptions(cmd);
|
|
107728
|
-
cmd.action(async (opts) => {
|
|
107729
|
-
await runner(opts);
|
|
107730
|
-
});
|
|
107731
|
-
};
|
|
107732
|
-
bindTake("tiktok-campaigns", "CampaignSectionData", runReportTikTokCampaigns);
|
|
107733
|
-
bindTake("tiktok-ad-groups", "AdGroupReport", runReportTikTokAdGroups);
|
|
107734
|
-
bindTake("tiktok-ads", "AdReport", runReportTikTokAds);
|
|
107735
|
-
bindTake("tiktok-videos", "VideoReport", runReportTikTokVideos);
|
|
107736
|
-
reportCmd.command("tiktok-audience").description(`${desc} \u2014 AudienceReport\uFF08\u5355\u7EF4\u5EA6 dimensions\uFF09`).requiredOption("-a, --account <id>", "TikTok mediaCustomerId").requiredOption("-d, --dimension <dim>", `\u7EF4\u5EA6\uFF1A${AUDIENCE_DIMENSIONS.join(" | ")}`).option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("--take <n>", "\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Auth Token").option(
|
|
107737
|
-
"--json-out <dir>",
|
|
107738
|
-
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
107739
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107740
|
-
const d = opts.dimension;
|
|
107741
|
-
if (!AUDIENCE_DIMENSIONS.includes(d)) {
|
|
107742
|
-
console.error(`
|
|
107743
|
-
\u274C --dimension \u975E\u6CD5\uFF0C\u53EF\u9009\uFF1A${AUDIENCE_DIMENSIONS.join(" | ")}
|
|
107744
|
-
`);
|
|
107745
|
-
process.exit(1);
|
|
107746
|
-
}
|
|
107747
|
-
await runReportTikTokAudience({ ...opts, dimension: d });
|
|
107748
|
-
});
|
|
107749
|
-
reportCmd.command("tiktok-audience-merged").description(
|
|
107750
|
-
`${desc} \u2014 \u5408\u5E76 gender + age + interest_category \u4E09\u6B21 AudienceReport\uFF08\u9ED8\u8BA4 stdout \u4E3A JSON\uFF09`
|
|
107751
|
-
).requiredOption("-a, --account <id>", "TikTok mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("--take <n>", "\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Auth Token").option(
|
|
107752
|
-
"--json-out <dir>",
|
|
107753
|
-
"\u5C06\u5408\u5E76 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
107754
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107755
|
-
await runReportTikTokAudienceMerged(opts);
|
|
107756
|
-
});
|
|
107757
|
-
reportCmd.command("tiktok-areacode").description("TikTok \u5730\u533A\u4EE3\u7801\u679A\u4E3E\uFF08\u4E3B\u5E73\u53F0 query/media-account/tiktok/TikTokAreacode/Read\uFF09").option("-t, --token <token>", "Auth Token").option(
|
|
107758
|
-
"--json-out <dir>",
|
|
107759
|
-
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
107760
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
107761
|
-
async (opts) => {
|
|
107762
|
-
await runReportTikTokAreacode(opts);
|
|
107763
|
-
}
|
|
107764
|
-
);
|
|
107765
|
-
reportCmd.command("tiktok-interest-list").description(
|
|
107766
|
-
"TikTok \u5174\u8DA3\u7C7B\u76EE\uFF08tiktokApiUrl GetInterestList\uFF0C\u89E3\u6790 country_code/interest \u62A5\u8868\u65F6\u5E38\u7528\uFF09"
|
|
107767
|
-
).requiredOption("-a, --account <id>", "TikTok mediaCustomerId").option("-t, --token <token>", "Auth Token").option(
|
|
107768
|
-
"--json-out <dir>",
|
|
107769
|
-
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
107770
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
107771
|
-
async (opts) => {
|
|
107772
|
-
await runReportTikTokInterestList(opts);
|
|
107773
|
-
}
|
|
107774
|
-
);
|
|
107775
|
-
}
|
|
107776
|
-
|
|
107777
|
-
// src/commands/report-bing-analysis/_shared.ts
|
|
107778
|
-
init_auth();
|
|
107779
|
-
init_version();
|
|
107780
|
-
var BING_KEYWORD_LIMIT_DEFAULT = 100;
|
|
107781
|
-
function stripLocalDate(d) {
|
|
107782
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
107783
|
-
}
|
|
107784
|
-
function parseLocalYmd(ymd) {
|
|
107785
|
-
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
107786
|
-
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) {
|
|
107787
|
-
return /* @__PURE__ */ new Date(NaN);
|
|
107788
|
-
}
|
|
107789
|
-
const [y, m, day] = parts;
|
|
107790
|
-
return new Date(y, m - 1, day);
|
|
107791
|
-
}
|
|
107792
|
-
function formatLocalYmd(d) {
|
|
107793
|
-
const y = d.getFullYear();
|
|
107794
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
107795
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
107796
|
-
return `${y}-${m}-${day}`;
|
|
107797
|
-
}
|
|
107798
|
-
function bingRangeTouchesTodayOrYesterday(startDate, endDate) {
|
|
107799
|
-
const s = parseLocalYmd(startDate);
|
|
107800
|
-
const e = parseLocalYmd(endDate);
|
|
107801
|
-
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) {
|
|
107802
|
-
return true;
|
|
107803
|
-
}
|
|
107804
|
-
const today = stripLocalDate(/* @__PURE__ */ new Date());
|
|
107805
|
-
const yesterday = new Date(today);
|
|
107806
|
-
yesterday.setDate(yesterday.getDate() - 1);
|
|
107807
|
-
for (let cur = new Date(s); cur <= e; cur.setDate(cur.getDate() + 1)) {
|
|
107808
|
-
const c = stripLocalDate(cur);
|
|
107809
|
-
if (c.getTime() === today.getTime() || c.getTime() === yesterday.getTime()) {
|
|
107810
|
-
return true;
|
|
107811
|
-
}
|
|
107812
|
-
}
|
|
107813
|
-
return false;
|
|
107814
|
-
}
|
|
107815
|
-
function resolveBingDateRange(start, end) {
|
|
107816
|
-
if (start && end) {
|
|
107817
|
-
const s = parseLocalYmd(start);
|
|
107818
|
-
const e = parseLocalYmd(end);
|
|
107819
|
-
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime())) {
|
|
107820
|
-
console.error("\n\u274C --start / --end \u987B\u4E3A\u6709\u6548\u65E5\u671F YYYY-MM-DD\u3002\n");
|
|
107821
|
-
process.exit(1);
|
|
107822
|
-
}
|
|
107823
|
-
if (s > e) {
|
|
107824
|
-
console.error("\n\u274C \u5F00\u59CB\u65E5\u671F\u4E0D\u80FD\u665A\u4E8E\u7ED3\u675F\u65E5\u671F\u3002\n");
|
|
107825
|
-
process.exit(1);
|
|
107826
|
-
}
|
|
107827
|
-
if (bingRangeTouchesTodayOrYesterday(start, end)) {
|
|
107828
|
-
console.error(
|
|
107829
|
-
"\n\u274C Bing \u62A5\u8868\u65F6\u95F4\u8303\u56F4\u4E0D\u80FD\u5305\u542B\u300C\u4ECA\u5929\u300D\u6216\u300C\u6628\u5929\u300D\uFF08\u63A5\u53E3\u9650\u5236\uFF09\u3002\u8BF7\u6539\u7528\u66F4\u65E9\u7684\u7ED3\u675F\u65E5\u671F\uFF0C\u4F8B\u5982\u524D\u5929\u53CA\u4EE5\u524D\u3002\n"
|
|
107830
|
-
);
|
|
107831
|
-
process.exit(1);
|
|
107832
|
-
}
|
|
107833
|
-
return { startDate: start, endDate: end };
|
|
107834
|
-
}
|
|
107835
|
-
if (!start && !end) {
|
|
107836
|
-
const endD = stripLocalDate(/* @__PURE__ */ new Date());
|
|
107837
|
-
endD.setDate(endD.getDate() - 2);
|
|
107838
|
-
const startD = new Date(endD);
|
|
107839
|
-
startD.setDate(startD.getDate() - 6);
|
|
107840
|
-
return { startDate: formatLocalYmd(startD), endDate: formatLocalYmd(endD) };
|
|
107841
|
-
}
|
|
107842
|
-
console.error(
|
|
107843
|
-
"\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u533A\u95F4\uFF08\u622A\u81F3\u524D\u5929\u5171 7 \u5929\uFF09\u3002\n"
|
|
107844
|
-
);
|
|
107845
|
-
process.exit(1);
|
|
107846
|
-
}
|
|
107847
|
-
function assertBingAccountId(id) {
|
|
107848
|
-
const t = id.trim();
|
|
107849
|
-
if (!t) {
|
|
107850
|
-
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
107851
|
-
process.exit(1);
|
|
107852
|
-
}
|
|
107853
|
-
if (!/^\d+$/.test(t)) {
|
|
107854
|
-
console.error("\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId\uFF08\u4E0E list-accounts -m BingV2 \u4E00\u81F4\uFF09\u3002\n");
|
|
107855
|
-
process.exit(1);
|
|
107856
|
-
}
|
|
107857
|
-
return t;
|
|
107858
|
-
}
|
|
107859
|
-
function reportingUrl2(config, accountId, segment, query) {
|
|
107860
|
-
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
107861
|
-
return `${config.apiBaseUrl}/reporting/media-account/BingV2/${accountId}/${segment}${q}`;
|
|
107862
|
-
}
|
|
107863
|
-
function resolveLimit(raw) {
|
|
107864
|
-
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
107865
|
-
return Number.isFinite(n) && n > 0 ? n : BING_KEYWORD_LIMIT_DEFAULT;
|
|
107866
|
-
}
|
|
107867
|
-
async function emitBingSnapshot(opts, section, accountId, startDate, endDate, data) {
|
|
107868
|
-
const summary = await writeReportAnalysisSnapshot({
|
|
107869
|
-
snapshotDir: opts.jsonOut,
|
|
107870
|
-
section,
|
|
107871
|
-
accountId,
|
|
107872
|
-
dateRange: { start: startDate, end: endDate },
|
|
107873
|
-
payload: data,
|
|
107874
|
-
cliVersion: getCurrentVersion2()
|
|
107875
|
-
});
|
|
107876
|
-
console.log(JSON.stringify(summary));
|
|
107877
|
-
}
|
|
107878
|
-
async function fetchBingJson(config, url, verbose) {
|
|
107879
|
-
return apiFetch2(url, config, {}, verbose);
|
|
107880
|
-
}
|
|
107881
|
-
|
|
107882
|
-
// src/commands/report-bing-analysis/run.ts
|
|
107883
|
-
init_auth();
|
|
107884
|
-
init_balance();
|
|
107885
|
-
|
|
107886
|
-
// src/commands/report-bing-analysis/normalize-bing-overview.ts
|
|
107887
|
-
function num(v) {
|
|
107888
|
-
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
107889
|
-
}
|
|
107890
|
-
function roundMoney(v) {
|
|
107891
|
-
return Math.round(v * 100) / 100;
|
|
107892
|
-
}
|
|
107893
|
-
function countInclusiveCalendarDays(startDate, endDate) {
|
|
107894
|
-
const parts = (ymd) => ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
107895
|
-
const sp = parts(startDate);
|
|
107896
|
-
const ep = parts(endDate);
|
|
107897
|
-
if (sp.length !== 3 || ep.length !== 3 || sp.some((n) => !Number.isFinite(n)) || ep.some((n) => !Number.isFinite(n))) {
|
|
107898
|
-
return 0;
|
|
107899
|
-
}
|
|
107900
|
-
const s = new Date(sp[0], sp[1] - 1, sp[2]);
|
|
107901
|
-
const e = new Date(ep[0], ep[1] - 1, ep[2]);
|
|
107902
|
-
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) return 0;
|
|
107903
|
-
const msPerDay = 24 * 60 * 60 * 1e3;
|
|
107904
|
-
return Math.floor((e.getTime() - s.getTime()) / msPerDay) + 1;
|
|
107905
|
-
}
|
|
107906
|
-
function periodSpend(period) {
|
|
107907
|
-
if (!period || typeof period !== "object") return void 0;
|
|
107908
|
-
return num(period.spend);
|
|
107909
|
-
}
|
|
107910
|
-
function enrichBingOverviewDerivedMetrics(record, startDate, endDate) {
|
|
107911
|
-
const out = { ...record };
|
|
107912
|
-
const currentSpend = periodSpend(out.currentPeriod);
|
|
107913
|
-
let totalCost = num(out.totalCost);
|
|
107914
|
-
if (totalCost == null || totalCost <= 0) {
|
|
107915
|
-
if (currentSpend != null && currentSpend > 0) {
|
|
107916
|
-
out.totalCost = currentSpend;
|
|
107917
|
-
totalCost = currentSpend;
|
|
107918
|
-
}
|
|
107919
|
-
}
|
|
107920
|
-
let activeDays = num(out.activeDays);
|
|
107921
|
-
if (activeDays == null || activeDays <= 0) {
|
|
107922
|
-
const calendarDays = countInclusiveCalendarDays(startDate, endDate);
|
|
107923
|
-
if (calendarDays > 0) {
|
|
107924
|
-
out.activeDays = calendarDays;
|
|
107925
|
-
activeDays = calendarDays;
|
|
107926
|
-
}
|
|
107927
|
-
}
|
|
107928
|
-
const averageDailyCost = num(out.averageDailyCost);
|
|
107929
|
-
if ((averageDailyCost == null || averageDailyCost <= 0) && totalCost != null && totalCost > 0 && activeDays != null && activeDays > 0) {
|
|
107930
|
-
out.averageDailyCost = roundMoney(totalCost / activeDays);
|
|
107931
|
-
}
|
|
107932
|
-
return out;
|
|
107933
|
-
}
|
|
107934
|
-
function parseYmd(ymd) {
|
|
107935
|
-
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
107936
|
-
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) return null;
|
|
107937
|
-
const d = new Date(parts[0], parts[1] - 1, parts[2]);
|
|
107938
|
-
return Number.isNaN(d.getTime()) ? null : d;
|
|
107939
|
-
}
|
|
107940
|
-
function formatYmd(d) {
|
|
107941
|
-
const y = d.getFullYear();
|
|
107942
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
107943
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
107944
|
-
return `${y}-${m}-${day}`;
|
|
107945
|
-
}
|
|
107946
|
-
function calcBingPreviousPeriodRange(startDate, endDate) {
|
|
107947
|
-
const diffDays = countInclusiveCalendarDays(startDate, endDate);
|
|
107948
|
-
if (diffDays <= 0) return null;
|
|
107949
|
-
const start = parseYmd(startDate);
|
|
107950
|
-
if (!start) return null;
|
|
107951
|
-
const prevEnd = new Date(start);
|
|
107952
|
-
prevEnd.setDate(prevEnd.getDate() - 1);
|
|
107953
|
-
const prevStart = new Date(prevEnd);
|
|
107954
|
-
prevStart.setDate(prevStart.getDate() - (diffDays - 1));
|
|
107955
|
-
return { startDate: formatYmd(prevStart), endDate: formatYmd(prevEnd) };
|
|
107956
|
-
}
|
|
107957
|
-
function isBingPeriodBlockEmpty(period) {
|
|
107958
|
-
if (!period || typeof period !== "object") return true;
|
|
107959
|
-
const p = period;
|
|
107960
|
-
const spend = num(p.spend) ?? 0;
|
|
107961
|
-
const impressions = num(p.impressions) ?? 0;
|
|
107962
|
-
const clicks = num(p.clicks) ?? 0;
|
|
107963
|
-
return spend <= 0 && impressions <= 0 && clicks <= 0;
|
|
107964
|
-
}
|
|
107965
|
-
function syncOverviewBalanceFields(record) {
|
|
107966
|
-
const out = { ...record };
|
|
107967
|
-
const budget = num(out.remainingAccountBudget);
|
|
107968
|
-
if (budget != null && budget >= 0) {
|
|
107969
|
-
const balanceField = num(out.balance);
|
|
107970
|
-
if (balanceField == null || balanceField <= 0 && budget > 0) {
|
|
107971
|
-
out.balance = budget;
|
|
107972
|
-
}
|
|
107973
|
-
}
|
|
107974
|
-
return out;
|
|
107975
|
-
}
|
|
107976
|
-
|
|
107977
|
-
// src/commands/report-bing-analysis/run.ts
|
|
107978
|
-
async function enrichBingOverviewPreviousPeriod(config, accountId, record, startDate, endDate, verbose) {
|
|
107979
|
-
if (!isBingPeriodBlockEmpty(record.previousPeriod)) {
|
|
107980
|
-
return record;
|
|
107981
|
-
}
|
|
107982
|
-
const prevRange = calcBingPreviousPeriodRange(startDate, endDate);
|
|
107983
|
-
if (!prevRange) return record;
|
|
107984
|
-
if (bingRangeTouchesTodayOrYesterday(prevRange.startDate, prevRange.endDate)) {
|
|
107985
|
-
return record;
|
|
107986
|
-
}
|
|
107987
|
-
try {
|
|
107988
|
-
const params = new URLSearchParams({
|
|
107989
|
-
startDate: prevRange.startDate,
|
|
107990
|
-
endDate: prevRange.endDate
|
|
107991
|
-
});
|
|
107992
|
-
const url = reportingUrl2(config, accountId, "OverviewSectionData", params.toString());
|
|
107993
|
-
const prevRaw = await fetchBingJson(config, url, verbose);
|
|
107994
|
-
if (!prevRaw || typeof prevRaw !== "object" || Array.isArray(prevRaw)) {
|
|
107995
|
-
return record;
|
|
107996
|
-
}
|
|
107997
|
-
const prevRecord = prevRaw;
|
|
107998
|
-
const prevPeriod = prevRecord.currentPeriod;
|
|
107999
|
-
if (isBingPeriodBlockEmpty(prevPeriod)) {
|
|
108000
|
-
return record;
|
|
108001
|
-
}
|
|
108002
|
-
return {
|
|
108003
|
-
...record,
|
|
108004
|
-
previousPeriod: prevPeriod,
|
|
108005
|
-
previousPeriodDateRange: prevRange,
|
|
108006
|
-
_previousPeriodSource: "OverviewSectionData-second-fetch"
|
|
108007
|
-
};
|
|
108008
|
-
} catch {
|
|
108009
|
-
return record;
|
|
108010
|
-
}
|
|
108011
|
-
}
|
|
108012
|
-
async function runReportBingOverview(opts) {
|
|
108013
|
-
const id = assertBingAccountId(opts.account);
|
|
108014
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108015
|
-
const config = loadConfig(opts.token);
|
|
108016
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108017
|
-
const url = reportingUrl2(config, id, "OverviewSectionData", params.toString());
|
|
108018
|
-
try {
|
|
108019
|
-
const raw = await fetchBingJson(config, url, !!opts.verbose);
|
|
108020
|
-
let data = raw;
|
|
108021
|
-
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
108022
|
-
const withBalance = await enrichOverviewRecordWithRealtimeBalance(
|
|
108023
|
-
"BingV2",
|
|
108024
|
-
id,
|
|
108025
|
-
config,
|
|
108026
|
-
raw,
|
|
108027
|
-
opts.verbose
|
|
108028
|
-
);
|
|
108029
|
-
const withDerived = enrichBingOverviewDerivedMetrics(withBalance, startDate, endDate);
|
|
108030
|
-
const withPrevious = await enrichBingOverviewPreviousPeriod(
|
|
108031
|
-
config,
|
|
108032
|
-
id,
|
|
108033
|
-
withDerived,
|
|
108034
|
-
startDate,
|
|
108035
|
-
endDate,
|
|
108036
|
-
!!opts.verbose
|
|
108037
|
-
);
|
|
108038
|
-
data = syncOverviewBalanceFields(withPrevious);
|
|
108039
|
-
}
|
|
108040
|
-
if (opts.jsonOut) {
|
|
108041
|
-
await emitBingSnapshot(opts, "bing-overview", id, startDate, endDate, data);
|
|
108042
|
-
return;
|
|
108043
|
-
}
|
|
108044
|
-
console.log(`
|
|
108045
|
-
\u2705 Bing \u8D26\u6237\u603B\u89C8 ${id} ${startDate} ~ ${endDate}`);
|
|
108046
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF1BBing \u533A\u95F4\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF09\n");
|
|
108047
|
-
} catch (err) {
|
|
108048
|
-
console.error(`
|
|
108049
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108050
|
-
`);
|
|
108051
|
-
process.exit(1);
|
|
108052
|
-
}
|
|
108053
|
-
}
|
|
108054
|
-
async function runReportBingDevice(opts) {
|
|
108055
|
-
const id = assertBingAccountId(opts.account);
|
|
108056
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108057
|
-
const config = loadConfig(opts.token);
|
|
108058
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108059
|
-
const url = reportingUrl2(config, id, "DeviceSectionData", params.toString());
|
|
108060
|
-
try {
|
|
108061
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108062
|
-
if (opts.jsonOut) {
|
|
108063
|
-
await emitBingSnapshot(opts, "bing-device", id, startDate, endDate, data);
|
|
108064
|
-
return;
|
|
108065
|
-
}
|
|
108066
|
-
console.log(`
|
|
108067
|
-
\u2705 Bing \u8BBE\u5907\u5206\u5E03 ${id} ${startDate} ~ ${endDate}`);
|
|
108068
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108069
|
-
} catch (err) {
|
|
108070
|
-
console.error(`
|
|
108071
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108072
|
-
`);
|
|
108073
|
-
process.exit(1);
|
|
108074
|
-
}
|
|
108075
|
-
}
|
|
108076
|
-
async function runReportBingGeographic(opts) {
|
|
108077
|
-
const id = assertBingAccountId(opts.account);
|
|
108078
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108079
|
-
const config = loadConfig(opts.token);
|
|
108080
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108081
|
-
const url = reportingUrl2(config, id, "GeographicSectionData", params.toString());
|
|
108082
|
-
try {
|
|
108083
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108084
|
-
if (opts.jsonOut) {
|
|
108085
|
-
await emitBingSnapshot(opts, "bing-geographic", id, startDate, endDate, data);
|
|
108086
|
-
return;
|
|
108087
|
-
}
|
|
108088
|
-
console.log(`
|
|
108089
|
-
\u2705 Bing \u5730\u57DF\u5206\u5E03 ${id} ${startDate} ~ ${endDate}`);
|
|
108090
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108091
|
-
} catch (err) {
|
|
108092
|
-
console.error(`
|
|
108093
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108094
|
-
`);
|
|
108095
|
-
process.exit(1);
|
|
108096
|
-
}
|
|
108097
|
-
}
|
|
108098
|
-
async function runReportBingAgeAudience(opts) {
|
|
108099
|
-
const id = assertBingAccountId(opts.account);
|
|
108100
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108101
|
-
const config = loadConfig(opts.token);
|
|
108102
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108103
|
-
const url = reportingUrl2(config, id, "AgeAudienceData", params.toString());
|
|
108104
|
-
try {
|
|
108105
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108106
|
-
if (opts.jsonOut) {
|
|
108107
|
-
await emitBingSnapshot(opts, "bing-age-audience", id, startDate, endDate, data);
|
|
108108
|
-
return;
|
|
108109
|
-
}
|
|
108110
|
-
console.log(`
|
|
108111
|
-
\u2705 Bing \u5E74\u9F84\u53D7\u4F17 ${id} ${startDate} ~ ${endDate}`);
|
|
108112
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108113
|
-
} catch (err) {
|
|
108114
|
-
console.error(`
|
|
108115
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108116
|
-
`);
|
|
108117
|
-
process.exit(1);
|
|
108118
|
-
}
|
|
108119
|
-
}
|
|
108120
|
-
async function runReportBingGenderAudience(opts) {
|
|
108121
|
-
const id = assertBingAccountId(opts.account);
|
|
108122
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108123
|
-
const config = loadConfig(opts.token);
|
|
108124
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108125
|
-
const url = reportingUrl2(config, id, "GenderAudienceData", params.toString());
|
|
108126
|
-
try {
|
|
108127
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108128
|
-
if (opts.jsonOut) {
|
|
108129
|
-
await emitBingSnapshot(opts, "bing-gender-audience", id, startDate, endDate, data);
|
|
108130
|
-
return;
|
|
108131
|
-
}
|
|
108132
|
-
console.log(`
|
|
108133
|
-
\u2705 Bing \u6027\u522B\u53D7\u4F17 ${id} ${startDate} ~ ${endDate}`);
|
|
108134
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108135
|
-
} catch (err) {
|
|
108136
|
-
console.error(`
|
|
108137
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108138
|
-
`);
|
|
108139
|
-
process.exit(1);
|
|
108140
|
-
}
|
|
108141
|
-
}
|
|
108142
|
-
async function runReportBingAudienceMerged(opts) {
|
|
108143
|
-
const id = assertBingAccountId(opts.account);
|
|
108144
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108145
|
-
const config = loadConfig(opts.token);
|
|
108146
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108147
|
-
try {
|
|
108148
|
-
const ageUrl = reportingUrl2(config, id, "AgeAudienceData", params.toString());
|
|
108149
|
-
const genderUrl = reportingUrl2(config, id, "GenderAudienceData", params.toString());
|
|
108150
|
-
const [ageData, genderData] = await Promise.all([
|
|
108151
|
-
fetchBingJson(config, ageUrl, !!opts.verbose),
|
|
108152
|
-
fetchBingJson(config, genderUrl, !!opts.verbose)
|
|
108153
|
-
]);
|
|
108154
|
-
const merged = {
|
|
108155
|
-
mergedParts: ["AgeAudienceData", "GenderAudienceData"],
|
|
108156
|
-
startDate,
|
|
108157
|
-
endDate,
|
|
108158
|
-
account: id,
|
|
108159
|
-
data: { ageAudience: ageData, genderAudience: genderData }
|
|
108160
|
-
};
|
|
108161
|
-
if (opts.jsonOut) {
|
|
108162
|
-
await emitBingSnapshot(opts, "bing-audience-merged", id, startDate, endDate, merged);
|
|
108163
|
-
return;
|
|
108164
|
-
}
|
|
108165
|
-
console.log(JSON.stringify(merged, null, 2));
|
|
108166
|
-
} catch (err) {
|
|
108167
|
-
console.error(`
|
|
108168
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108169
|
-
`);
|
|
108170
|
-
process.exit(1);
|
|
108171
|
-
}
|
|
108172
|
-
}
|
|
108173
|
-
async function runReportBingCampaigns(opts) {
|
|
108174
|
-
const id = assertBingAccountId(opts.account);
|
|
108175
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108176
|
-
const config = loadConfig(opts.token);
|
|
108177
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108178
|
-
const url = reportingUrl2(config, id, "CampaignReport", params.toString());
|
|
108179
|
-
try {
|
|
108180
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108181
|
-
if (opts.jsonOut) {
|
|
108182
|
-
await emitBingSnapshot(opts, "bing-campaigns", id, startDate, endDate, data);
|
|
108183
|
-
return;
|
|
108184
|
-
}
|
|
108185
|
-
console.log(`
|
|
108186
|
-
\u2705 Bing \u5E7F\u544A\u7CFB\u5217\u62A5\u8868 ${id} ${startDate} ~ ${endDate}`);
|
|
108187
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108188
|
-
} catch (err) {
|
|
108189
|
-
console.error(`
|
|
108190
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108191
|
-
`);
|
|
108192
|
-
process.exit(1);
|
|
108193
|
-
}
|
|
108194
|
-
}
|
|
108195
|
-
async function runReportBingAdGroups(opts) {
|
|
108196
|
-
const id = assertBingAccountId(opts.account);
|
|
108197
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108198
|
-
const config = loadConfig(opts.token);
|
|
108199
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108200
|
-
const url = reportingUrl2(config, id, "AdGroupReport", params.toString());
|
|
108201
|
-
try {
|
|
108202
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108203
|
-
if (opts.jsonOut) {
|
|
108204
|
-
await emitBingSnapshot(opts, "bing-ad-groups", id, startDate, endDate, data);
|
|
108205
|
-
return;
|
|
108206
|
-
}
|
|
108207
|
-
console.log(`
|
|
108208
|
-
\u2705 Bing \u5E7F\u544A\u7EC4\u62A5\u8868 ${id} ${startDate} ~ ${endDate}`);
|
|
108209
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108210
|
-
} catch (err) {
|
|
108211
|
-
console.error(`
|
|
108212
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108213
|
-
`);
|
|
108214
|
-
process.exit(1);
|
|
108215
|
-
}
|
|
108216
|
-
}
|
|
108217
|
-
async function runReportBingAds(opts) {
|
|
108218
|
-
const id = assertBingAccountId(opts.account);
|
|
108219
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108220
|
-
const config = loadConfig(opts.token);
|
|
108221
|
-
const params = new URLSearchParams({ startDate, endDate });
|
|
108222
|
-
const url = reportingUrl2(config, id, "AdReport", params.toString());
|
|
108223
|
-
try {
|
|
108224
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108225
|
-
if (opts.jsonOut) {
|
|
108226
|
-
await emitBingSnapshot(opts, "bing-ads", id, startDate, endDate, data);
|
|
108227
|
-
return;
|
|
108228
|
-
}
|
|
108229
|
-
console.log(`
|
|
108230
|
-
\u2705 Bing \u5E7F\u544A\u62A5\u8868 ${id} ${startDate} ~ ${endDate}`);
|
|
108231
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108232
|
-
} catch (err) {
|
|
108233
|
-
console.error(`
|
|
108234
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108235
|
-
`);
|
|
108236
|
-
process.exit(1);
|
|
108237
|
-
}
|
|
108238
|
-
}
|
|
108239
|
-
async function runReportBingKeywords(opts) {
|
|
108240
|
-
const id = assertBingAccountId(opts.account);
|
|
108241
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108242
|
-
const limit = resolveLimit(opts.limit);
|
|
108243
|
-
const config = loadConfig(opts.token);
|
|
108244
|
-
const params = new URLSearchParams({
|
|
108245
|
-
startDate,
|
|
108246
|
-
endDate,
|
|
108247
|
-
limit: String(limit),
|
|
108248
|
-
orderByCost: "true"
|
|
108249
|
-
});
|
|
108250
|
-
const url = reportingUrl2(config, id, "KeywordReport", params.toString());
|
|
108251
|
-
try {
|
|
108252
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108253
|
-
if (opts.jsonOut) {
|
|
108254
|
-
await emitBingSnapshot(opts, "bing-keywords", id, startDate, endDate, data);
|
|
108255
|
-
return;
|
|
108256
|
-
}
|
|
108257
|
-
console.log(`
|
|
108258
|
-
\u2705 Bing \u5173\u952E\u8BCD\u62A5\u8868 ${id} limit=${limit} orderByCost=true`);
|
|
108259
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108260
|
-
} catch (err) {
|
|
108261
|
-
console.error(`
|
|
108262
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108263
|
-
`);
|
|
108264
|
-
process.exit(1);
|
|
108265
|
-
}
|
|
108266
|
-
}
|
|
108267
|
-
async function runReportBingSearchTerms(opts) {
|
|
108268
|
-
const id = assertBingAccountId(opts.account);
|
|
108269
|
-
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
108270
|
-
const limit = resolveLimit(opts.limit);
|
|
108271
|
-
const config = loadConfig(opts.token);
|
|
108272
|
-
const params = new URLSearchParams({
|
|
108273
|
-
startDate,
|
|
108274
|
-
endDate,
|
|
108275
|
-
limit: String(limit),
|
|
108276
|
-
orderByCost: "true"
|
|
108277
|
-
});
|
|
108278
|
-
const url = reportingUrl2(config, id, "SearchQueryReport", params.toString());
|
|
108279
|
-
try {
|
|
108280
|
-
const data = await fetchBingJson(config, url, !!opts.verbose);
|
|
108281
|
-
if (opts.jsonOut) {
|
|
108282
|
-
await emitBingSnapshot(opts, "bing-search-terms", id, startDate, endDate, data);
|
|
108283
|
-
return;
|
|
108284
|
-
}
|
|
108285
|
-
console.log(`
|
|
108286
|
-
\u2705 Bing \u641C\u7D22\u5B57\u8BCD\u62A5\u8868 ${id} limit=${limit} orderByCost=true`);
|
|
108287
|
-
console.log(" \uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
108288
|
-
} catch (err) {
|
|
108289
|
-
console.error(`
|
|
108290
|
-
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108291
|
-
`);
|
|
108292
|
-
process.exit(1);
|
|
108293
|
-
}
|
|
108294
|
-
}
|
|
108295
|
-
|
|
108296
|
-
// src/commands/report-bing-analysis/_register.ts
|
|
108297
|
-
function addBingDateOptions(c) {
|
|
108298
|
-
return c.requiredOption(
|
|
108299
|
-
"-a, --account <id>",
|
|
108300
|
-
"Bing mediaCustomerId\uFF08\u6570\u5B57\uFF0C\u4E0E list-accounts -m BingV2 \u4E00\u81F4\uFF09"
|
|
108301
|
-
).option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-t, --token <token>", "Auth Token").option(
|
|
108302
|
-
"--json-out <dir>",
|
|
108303
|
-
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
108304
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false);
|
|
108305
|
-
}
|
|
108306
|
-
function registerReportBingCommands(reportCmd) {
|
|
108307
|
-
const desc = "Bing\uFF08BingV2\uFF09\u8D26\u6237\u5206\u6790\uFF08TSO reporting/media-account/BingV2/...\uFF1B\u65E5\u671F\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF09";
|
|
108308
|
-
const bindSimple = (name2, label, runner) => {
|
|
108309
|
-
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${label}`);
|
|
108310
|
-
addBingDateOptions(cmd);
|
|
108311
|
-
cmd.action(async (opts) => {
|
|
108312
|
-
await runner(opts);
|
|
108313
|
-
});
|
|
108314
|
-
};
|
|
108315
|
-
bindSimple("bing-overview", "\u603B\u89C8 OverviewSectionData", runReportBingOverview);
|
|
108316
|
-
bindSimple("bing-device", "\u8BBE\u5907 DeviceSectionData", runReportBingDevice);
|
|
108317
|
-
bindSimple("bing-geographic", "\u5730\u57DF GeographicSectionData", runReportBingGeographic);
|
|
108318
|
-
bindSimple("bing-age-audience", "\u5E74\u9F84\u53D7\u4F17 AgeAudienceData", runReportBingAgeAudience);
|
|
108319
|
-
bindSimple("bing-gender-audience", "\u6027\u522B\u53D7\u4F17 GenderAudienceData", runReportBingGenderAudience);
|
|
108320
|
-
bindSimple("bing-campaigns", "\u5E7F\u544A\u7CFB\u5217 CampaignReport", runReportBingCampaigns);
|
|
108321
|
-
bindSimple("bing-ad-groups", "\u5E7F\u544A\u7EC4 AdGroupReport", runReportBingAdGroups);
|
|
108322
|
-
bindSimple("bing-ads", "\u5E7F\u544A AdReport", runReportBingAds);
|
|
108323
|
-
reportCmd.command("bing-audience-merged").description(`${desc} \u2014 \u5408\u5E76 AgeAudienceData + GenderAudienceData\uFF08\u9ED8\u8BA4 stdout \u4E3A JSON\uFF09`).requiredOption("-a, --account <id>", "Bing mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-t, --token <token>", "Auth Token").option(
|
|
108324
|
-
"--json-out <dir>",
|
|
108325
|
-
"\u5C06\u5408\u5E76 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
108326
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108327
|
-
await runReportBingAudienceMerged(opts);
|
|
108328
|
-
});
|
|
108329
|
-
const bindLimit = (name2, label, runner) => {
|
|
108330
|
-
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${label}`);
|
|
108331
|
-
addBingDateOptions(cmd);
|
|
108332
|
-
cmd.option(
|
|
108333
|
-
"--limit <n>",
|
|
108334
|
-
"\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF0C\u4E0E\u524D\u7AEF KeywordReport/SearchQueryReport \u4E00\u81F4\uFF09",
|
|
108335
|
-
(v) => parseInt(v, 10)
|
|
108336
|
-
);
|
|
108337
|
-
cmd.action(async (opts) => {
|
|
108338
|
-
await runner(opts);
|
|
108339
|
-
});
|
|
108340
|
-
};
|
|
108341
|
-
bindLimit(
|
|
108342
|
-
"bing-keywords",
|
|
108343
|
-
"\u5173\u952E\u8BCD KeywordReport\uFF08limit + orderByCost=true\uFF09",
|
|
108344
|
-
runReportBingKeywords
|
|
108345
|
-
);
|
|
108346
|
-
bindLimit(
|
|
108347
|
-
"bing-search-terms",
|
|
108348
|
-
"\u641C\u7D22\u5B57\u8BCD SearchQueryReport\uFF08limit + orderByCost=true\uFF09",
|
|
108349
|
-
runReportBingSearchTerms
|
|
108350
|
-
);
|
|
108351
|
-
}
|
|
108352
|
-
|
|
108353
|
-
// src/commands/report/register.ts
|
|
108354
|
-
function register13(program2) {
|
|
108355
|
-
const reportCmd = program2.command("report").description("\u4F18\u5316\u62A5\u544A\u4E0E\u63A8\u9001\u7BA1\u7406");
|
|
108356
|
-
reportCmd.command("list").description("\u67E5\u8BE2\u4F18\u5316\u62A5\u544A\u5217\u8868").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").option("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B\uFF08\u5982 Daily / Hourly\uFF09").option("-s, --status <ready>", "\u751F\u6210\u72B6\u6001\uFF08true/false\uFF09").option("-k, --keyword <text>", "\u62A5\u544A\u540D\u79F0\u5173\u952E\u5B57").option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--token <token>", "Auth Token").option(
|
|
108357
|
-
"--json-out <path>",
|
|
108358
|
-
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
108359
|
-
void 0
|
|
108360
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108361
|
-
await runReportList({
|
|
108362
|
-
token: opts.token,
|
|
108363
|
-
media: opts.media,
|
|
108364
|
-
type: opts.type,
|
|
108365
|
-
status: opts.status,
|
|
108366
|
-
keyword: opts.keyword,
|
|
108367
|
-
startDate: opts.start,
|
|
108368
|
-
endDate: opts.end,
|
|
108369
|
-
page: opts.page,
|
|
108370
|
-
pageSize: opts.pageSize,
|
|
108371
|
-
jsonOut: opts.jsonOut,
|
|
108372
|
-
verbose: opts.verbose
|
|
108373
|
-
});
|
|
108374
|
-
});
|
|
108375
|
-
reportCmd.command("create").description("\u6279\u91CF\u751F\u6210\u4F18\u5316\u62A5\u544A").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("-a, --accounts <ids>", "\u8D26\u6237 ID\uFF0C\u9017\u53F7\u5206\u9694").requiredOption("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B\uFF0C\u5982 Daily").requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
|
|
108376
|
-
"--json-out <path>",
|
|
108377
|
-
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
108378
|
-
void 0
|
|
108379
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108380
|
-
await runReportCreate({
|
|
108381
|
-
token: opts.token,
|
|
108382
|
-
media: opts.media,
|
|
108383
|
-
accounts: opts.accounts.split(",").map((s) => s.trim()),
|
|
108384
|
-
type: opts.type,
|
|
108385
|
-
startDate: opts.start,
|
|
108386
|
-
endDate: opts.end,
|
|
108387
|
-
jsonOut: opts.jsonOut,
|
|
108388
|
-
verbose: opts.verbose
|
|
108389
|
-
});
|
|
108390
|
-
});
|
|
108391
|
-
reportCmd.command("delete").description("\u5220\u9664\u4F18\u5316\u62A5\u544A\uFF08--id \u6216 --ids \u81F3\u5C11\u4F20\u4E00\u4E2A\uFF09").option("--id <id>", "\u5355\u6761\u62A5\u544A entityId\uFF08\u4E0E --ids \u4E8C\u9009\u4E00\uFF09").option("--ids <ids>", "\u6279\u91CF\u5220\u9664\uFF0C\u9017\u53F7\u5206\u9694 entityId\uFF08\u4E0E --id \u4E8C\u9009\u4E00\uFF09").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108392
|
-
await runReportDelete({
|
|
108393
|
-
token: opts.token,
|
|
108394
|
-
id: opts.id,
|
|
108395
|
-
ids: opts.ids ? opts.ids.split(",").map((s) => s.trim()) : void 0,
|
|
108396
|
-
verbose: opts.verbose
|
|
108397
|
-
});
|
|
108398
|
-
});
|
|
108399
|
-
reportCmd.command("meta-overview").description(
|
|
108400
|
-
"Meta \u8D26\u6237\u5206\u6790\u603B\u89C8\uFF08OverviewSectionData\uFF1BFacebookAds \u6388\u6743\u6237\u8D70 FacebookAds/act_\uFF0CMetaAd \u5F00\u6237\u8D70 MetaAd\uFF09"
|
|
108401
|
-
).requiredOption("-a, --account <id>", "Meta \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
|
|
108402
|
-
"--json-out <path>",
|
|
108403
|
-
"\u843D\u76D8\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest[-<accountId>].json\uFF1B\u6587\u4EF6\u540D\u4E3A `<section>[-<accountId>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09"
|
|
108404
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108405
|
-
await runReportMetaOverview({
|
|
108406
|
-
token: opts.token,
|
|
108407
|
-
account: opts.account,
|
|
108408
|
-
startDate: opts.start,
|
|
108409
|
-
endDate: opts.end,
|
|
108410
|
-
jsonOut: opts.jsonOut,
|
|
108411
|
-
verbose: opts.verbose
|
|
108412
|
-
});
|
|
108413
|
-
});
|
|
108414
|
-
registerReportTiktokCommands(reportCmd);
|
|
108415
|
-
registerReportBingCommands(reportCmd);
|
|
108416
|
-
const pushCmd = reportCmd.command("push").description("\u63A8\u9001\u7BA1\u7406\uFF08\u90AE\u4EF6\u62A5\u544A\u63A8\u9001\u4EFB\u52A1\uFF09");
|
|
108417
|
-
pushCmd.command("list").description("\u67E5\u8BE2\u63A8\u9001\u4EFB\u52A1\u5217\u8868").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").option("-k, --keyword <text>", "\u63A8\u9001\u540D\u79F0\u5173\u952E\u5B57").option("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B").option("--stopped", "\u53EA\u663E\u793A\u5DF2\u505C\u7528\u7684\u63A8\u9001", false).option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--token <token>", "Auth Token").option(
|
|
108418
|
-
"--json-out <path>",
|
|
108419
|
-
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
108420
|
-
void 0
|
|
108421
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108422
|
-
await runReportPushList({
|
|
108423
|
-
token: opts.token,
|
|
108424
|
-
media: opts.media,
|
|
108425
|
-
keyword: opts.keyword,
|
|
108426
|
-
type: opts.type,
|
|
108427
|
-
stopped: opts.stopped,
|
|
108428
|
-
startDate: opts.start,
|
|
108429
|
-
endDate: opts.end,
|
|
108430
|
-
page: opts.page,
|
|
108431
|
-
pageSize: opts.pageSize,
|
|
108432
|
-
jsonOut: opts.jsonOut,
|
|
108433
|
-
verbose: opts.verbose
|
|
108434
|
-
});
|
|
108435
|
-
});
|
|
108436
|
-
pushCmd.command("create").description(
|
|
108437
|
-
"\u65B0\u5EFA\u63A8\u9001\u914D\u7F6E\uFF08POST /command/report-push/settings/{\u5A92\u4F53}\uFF0C\u4E0E\u7F51\u9875\u300C\u6DFB\u52A0\u63A8\u9001\u300D\u4E00\u81F4\uFF09"
|
|
108438
|
-
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").requiredOption("--name <name>", "\u63A8\u9001\u540D\u79F0\uFF08\u5BF9\u5E94 body.Name\uFF09").requiredOption(
|
|
108439
|
-
"--media-accounts <ids>",
|
|
108440
|
-
"\u5E7F\u544A\u8D26\u6237 entityId\uFF0C\u9017\u53F7\u5206\u9694\uFF08list-accounts --json-out \u2192 ma.entityId\uFF09",
|
|
108441
|
-
splitCsv
|
|
108442
|
-
).requiredOption("--emails <list>", "\u6536\u4EF6\u90AE\u7BB1\uFF0C\u9017\u53F7\u5206\u9694", splitCsv).requiredOption("--types <list>", "\u62A5\u544A\u7C7B\u578B\uFF0C\u9017\u53F7\u5206\u9694\uFF1ADaily | Weekly | Monthly", splitCsv).option("--title <text>", "\u90AE\u4EF6\u6807\u9898\uFF08ReceiveTitle\uFF0C\u53EF\u7A7A\uFF09", "").option("--ai-suggest", "\u4EC5 Google\uFF1AAiSuggest=true", false).option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108443
|
-
await runReportPushCreate({
|
|
108444
|
-
token: opts.token,
|
|
108445
|
-
media: opts.media,
|
|
108446
|
-
name: opts.name,
|
|
108447
|
-
mediaAccounts: opts.mediaAccounts,
|
|
108448
|
-
receiveEmails: opts.emails,
|
|
108449
|
-
reportTypes: opts.types,
|
|
108450
|
-
receiveTitle: opts.title ?? "",
|
|
108451
|
-
aiSuggest: opts.aiSuggest,
|
|
108452
|
-
verbose: opts.verbose
|
|
108453
|
-
});
|
|
108454
|
-
});
|
|
108455
|
-
pushCmd.command("update").description(
|
|
108456
|
-
"\u7F16\u8F91\u63A8\u9001\u914D\u7F6E\uFF08PUT /command/report-push/settings/{\u5A92\u4F53}/{id}\uFF1Bid \u4E0E\u7F51\u9875\u7F16\u8F91\u7528 row.id \u4E00\u81F4\uFF0C\u901A\u5E38\u540C entityId\uFF09"
|
|
108457
|
-
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u914D\u7F6E id\uFF08push list --json-out \u2192 items[].id \u6216 entityId\uFF09").requiredOption("--name <name>", "\u63A8\u9001\u540D\u79F0").requiredOption("--media-accounts <ids>", "\u5E7F\u544A\u8D26\u6237 entityId\uFF0C\u9017\u53F7\u5206\u9694", splitCsv).requiredOption("--emails <list>", "\u6536\u4EF6\u90AE\u7BB1\uFF0C\u9017\u53F7\u5206\u9694", splitCsv).requiredOption("--types <list>", "\u62A5\u544A\u7C7B\u578B\uFF0C\u9017\u53F7\u5206\u9694\uFF1ADaily | Weekly | Monthly", splitCsv).option("--title <text>", "\u90AE\u4EF6\u6807\u9898", "").option("--ai-suggest", "\u4EC5 Google\uFF1AAiSuggest=true", false).option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108458
|
-
await runReportPushUpdate({
|
|
108459
|
-
token: opts.token,
|
|
108460
|
-
media: opts.media,
|
|
108461
|
-
id: opts.id,
|
|
108462
|
-
name: opts.name,
|
|
108463
|
-
mediaAccounts: opts.mediaAccounts,
|
|
108464
|
-
receiveEmails: opts.emails,
|
|
108465
|
-
reportTypes: opts.types,
|
|
108466
|
-
receiveTitle: opts.title ?? "",
|
|
108467
|
-
aiSuggest: opts.aiSuggest,
|
|
108468
|
-
verbose: opts.verbose
|
|
108469
|
-
});
|
|
108470
|
-
});
|
|
108471
|
-
pushCmd.command("history").description(
|
|
108472
|
-
"\u63A8\u9001\u8BB0\u5F55\u5217\u8868\uFF08GET /query/report-push/settings/{\u5A92\u4F53}/{settingId}/push-history\uFF0C\u5BF9\u5E94\u7F51\u9875 /dataReport/pushRecord\uFF09"
|
|
108473
|
-
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").requiredOption(
|
|
108474
|
-
"--setting-id <id>",
|
|
108475
|
-
"\u63A8\u9001\u89C4\u5219 id\uFF08\u4E0E push list \u7684 entityId \u4E00\u81F4\uFF1B\u7F51\u9875\u8DEF\u7531 entityId\uFF09"
|
|
108476
|
-
).option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--start <date>", "\u7B5B\u9009\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u7B5B\u9009\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--receive-email <email>", "\u7B5B\u9009\u6536\u4EF6\u90AE\u7BB1").option("--report-type <type>", "\u7B5B\u9009\u62A5\u544A\u7C7B\u578B").option("--media-account-id <entityId>", "\u7B5B\u9009\u8D26\u6237\uFF08\u4F20\u8D26\u6237 entityId\uFF09").option("--token <token>", "Auth Token").option(
|
|
108477
|
-
"--json-out <path>",
|
|
108478
|
-
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
108479
|
-
void 0
|
|
108480
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108481
|
-
await runReportPushHistory({
|
|
108482
|
-
token: opts.token,
|
|
108483
|
-
media: opts.media,
|
|
108484
|
-
settingId: opts.settingId,
|
|
108485
|
-
page: opts.page,
|
|
108486
|
-
pageSize: opts.pageSize,
|
|
108487
|
-
startDate: opts.start,
|
|
108488
|
-
endDate: opts.end,
|
|
108489
|
-
receiveEmail: opts.receiveEmail,
|
|
108490
|
-
reportType: opts.reportType,
|
|
108491
|
-
mediaAccountId: opts.mediaAccountId,
|
|
108492
|
-
jsonOut: opts.jsonOut,
|
|
108493
|
-
verbose: opts.verbose
|
|
108494
|
-
});
|
|
108495
|
-
});
|
|
108496
|
-
pushCmd.command("receive-emails").description(
|
|
108497
|
-
"\u67E5\u8BE2\u63A8\u9001\u66FE\u7528\u8FC7\u7684\u6536\u4EF6\u90AE\u7BB1\u5217\u8868\uFF08GET /query/report-push/settings/{\u5A92\u4F53}/all/receive-emails\uFF0C\u4E0E\u7F51\u9875\u4E0B\u62C9\u4E00\u81F4\uFF09"
|
|
108498
|
-
).requiredOption("-m, --media <type>", "\u5A92\u4F53\uFF1AGoogle | TikTok").option("--token <token>", "Auth Token").option(
|
|
108499
|
-
"--json-out <path>",
|
|
108500
|
-
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
108501
|
-
void 0
|
|
108502
|
-
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108503
|
-
await runReportPushReceiveEmails({
|
|
108504
|
-
token: opts.token,
|
|
108505
|
-
media: opts.media,
|
|
108506
|
-
jsonOut: opts.jsonOut,
|
|
108507
|
-
verbose: opts.verbose
|
|
108508
|
-
});
|
|
108509
|
-
});
|
|
108510
|
-
pushCmd.command("start").description("\u542F\u52A8\u63A8\u9001\u4EFB\u52A1").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u4EFB\u52A1 entityId").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108511
|
-
await runReportPushStart({
|
|
108512
|
-
token: opts.token,
|
|
108513
|
-
media: opts.media,
|
|
108514
|
-
id: opts.id,
|
|
108515
|
-
verbose: opts.verbose
|
|
108516
|
-
});
|
|
108517
|
-
});
|
|
108518
|
-
pushCmd.command("stop").description("\u505C\u6B62\u63A8\u9001\u4EFB\u52A1").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u4EFB\u52A1 entityId").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108519
|
-
await runReportPushStop({
|
|
108520
|
-
token: opts.token,
|
|
108521
|
-
media: opts.media,
|
|
108522
|
-
id: opts.id,
|
|
108523
|
-
verbose: opts.verbose
|
|
108524
|
-
});
|
|
108525
|
-
});
|
|
108526
|
-
pushCmd.command("delete").description("\u5220\u9664\u63A8\u9001\u4EFB\u52A1").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").requiredOption("--id <id>", "\u63A8\u9001\u4EFB\u52A1 entityId").option("--token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
108527
|
-
await runReportPushDelete({
|
|
108528
|
-
token: opts.token,
|
|
108529
|
-
media: opts.media,
|
|
108530
|
-
id: opts.id,
|
|
108531
|
-
verbose: opts.verbose
|
|
108532
|
-
});
|
|
108533
|
-
});
|
|
108534
|
-
}
|
|
108535
|
-
|
|
108536
|
-
// src/commands/transfer.ts
|
|
108537
|
-
init_auth();
|
|
108538
|
-
init_cli_json_snapshot();
|
|
108539
|
-
init_cli_table();
|
|
108540
|
-
var VALID_MEDIA_TYPES6 = ["Google", "TikTok", "MetaAd", "BingV2"];
|
|
108541
|
-
function mediaToPath(media) {
|
|
108542
|
-
return media.toLowerCase();
|
|
108543
|
-
}
|
|
108544
|
-
async function runTransferCreate(opts) {
|
|
108545
|
-
if (!VALID_MEDIA_TYPES6.includes(opts.media)) {
|
|
108546
|
-
console.error(
|
|
108547
|
-
`
|
|
108548
|
-
\u274C \u4E0D\u652F\u6301\u7684\u5A92\u4F53\u7C7B\u578B\uFF1A${opts.media}
|
|
108549
|
-
\u53EF\u9009\uFF1A${VALID_MEDIA_TYPES6.join(" | ")}
|
|
108550
|
-
`
|
|
108551
|
-
);
|
|
108552
|
-
process.exit(1);
|
|
108553
|
-
}
|
|
108554
|
-
const config = loadConfig(opts.token);
|
|
108555
|
-
const url = `${config.apiBaseUrl}/command/accounttransfer/${mediaToPath(opts.media)}/createaccounttransfers`;
|
|
108556
|
-
const body = [
|
|
108557
|
-
{
|
|
108558
|
-
exchangeRate: 1,
|
|
108559
|
-
accountTransferOutData: {
|
|
108560
|
-
mediaCustomerId: opts.outAccount,
|
|
108561
|
-
shortId: null,
|
|
108562
|
-
transferOutAmounts: opts.amount
|
|
108563
|
-
},
|
|
108564
|
-
accountTransferInData: {
|
|
108565
|
-
mediaCustomerId: opts.inAccount,
|
|
108566
|
-
shortId: null
|
|
108567
|
-
},
|
|
108568
|
-
customerName: opts.customerName ?? ""
|
|
108569
|
-
}
|
|
108570
|
-
];
|
|
108571
|
-
let result;
|
|
108572
|
-
try {
|
|
108573
|
-
result = await apiFetch2(
|
|
108574
|
-
url,
|
|
108575
|
-
config,
|
|
108576
|
-
{ method: "POST", body: JSON.stringify(body) },
|
|
108577
|
-
opts.verbose
|
|
108578
|
-
);
|
|
108579
|
-
} catch (err) {
|
|
108580
|
-
console.error(`
|
|
108581
|
-
\u274C \u8F6C\u8D26\u7533\u8BF7\u63D0\u4EA4\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108582
|
-
`);
|
|
108583
|
-
process.exit(1);
|
|
108584
|
-
}
|
|
108585
|
-
if (await emitCliJsonOrSnapshot(opts, {
|
|
108586
|
-
section: `transfer-create-${mediaToPath(opts.media)}`,
|
|
108587
|
-
commandLabel: "transfer create",
|
|
108588
|
-
commandHint: opts.media,
|
|
108589
|
-
payload: result,
|
|
108590
|
-
idSuffix: opts.media
|
|
108591
|
-
})) {
|
|
108592
|
-
return;
|
|
108593
|
-
}
|
|
108594
|
-
console.log(
|
|
108595
|
-
`
|
|
108596
|
-
\u2705 \u8F6C\u8D26\u7533\u8BF7\u5DF2\u63D0\u4EA4\uFF1A${opts.outAccount} \u2192 ${opts.inAccount}\uFF0C\u91D1\u989D ${opts.amount}\u3002
|
|
108597
|
-
`
|
|
108598
|
-
);
|
|
108599
|
-
console.log(" \u4F7F\u7528 transfer list \u67E5\u770B\u8F6C\u8D26\u8BB0\u5F55\u53CA\u5230\u8D26\u72B6\u6001\u3002\n");
|
|
108600
|
-
}
|
|
108601
|
-
async function runTransferList(opts) {
|
|
108602
|
-
if (!VALID_MEDIA_TYPES6.includes(opts.media)) {
|
|
108603
|
-
console.error(
|
|
108604
|
-
`
|
|
108605
|
-
\u274C \u4E0D\u652F\u6301\u7684\u5A92\u4F53\u7C7B\u578B\uFF1A${opts.media}
|
|
108606
|
-
\u53EF\u9009\uFF1A${VALID_MEDIA_TYPES6.join(" | ")}
|
|
108607
|
-
`
|
|
108608
|
-
);
|
|
108609
|
-
process.exit(1);
|
|
108610
|
-
}
|
|
108611
|
-
const config = loadConfig(opts.token);
|
|
108612
|
-
const params = new URLSearchParams();
|
|
108613
|
-
params.set("pageNumber", String(opts.page ?? 1));
|
|
108614
|
-
params.set("pageSize", String(opts.pageSize ?? 20));
|
|
108615
|
-
if (opts.number) params.set("transferNumber", opts.number);
|
|
108616
|
-
if (opts.status) params.set("transferStatus", opts.status);
|
|
108617
|
-
if (opts.startDate) params.set("startDate", opts.startDate);
|
|
108618
|
-
if (opts.endDate) params.set("endDate", opts.endDate);
|
|
108619
|
-
const url = `${config.apiBaseUrl}/query/accounttransfer/${opts.media}/search?${params}`;
|
|
108620
|
-
let data;
|
|
108621
|
-
try {
|
|
108622
|
-
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
108623
|
-
} catch (err) {
|
|
108624
|
-
console.error(`
|
|
108625
|
-
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
108626
|
-
`);
|
|
108627
|
-
process.exit(1);
|
|
108628
|
-
}
|
|
108629
|
-
const items = data.list ?? data.results ?? [];
|
|
108630
|
-
const total = data.total ?? data.totalResultCount ?? 0;
|
|
108631
|
-
const page = opts.page ?? 1;
|
|
108632
|
-
const pageSize = opts.pageSize ?? 20;
|
|
108633
|
-
const listPayload = wrapListJson({ page, pageSize, total, items });
|
|
108634
|
-
if (await emitCliJsonOrSnapshot(opts, {
|
|
108635
|
-
section: `transfer-list-${mediaToPath(opts.media)}`,
|
|
108636
|
-
commandLabel: "transfer list",
|
|
108637
|
-
commandHint: opts.media,
|
|
108638
|
-
payload: listPayload,
|
|
108639
|
-
idSuffix: opts.media
|
|
108640
|
-
})) {
|
|
108641
|
-
return;
|
|
108642
|
-
}
|
|
108643
|
-
console.log(
|
|
108644
|
-
`
|
|
108645
|
-
\u8F6C\u8D26\u8BB0\u5F55\uFF08\u7B2C ${page}/${Math.max(1, Math.ceil(total / pageSize))} \u9875\uFF0C\u672C\u9875 ${items.length} \u6761\uFF0C\u5171 ${total} \u6761\uFF09
|
|
108646
|
-
`
|
|
108647
|
-
);
|
|
108648
|
-
if (items.length === 0) {
|
|
108649
|
-
console.log(" \u6682\u65E0\u6570\u636E\u3002\n");
|
|
108650
|
-
return;
|
|
108651
|
-
}
|
|
108652
|
-
const cols = [
|
|
108653
|
-
{ key: "transferNumber", header: "\u8BA2\u5355\u53F7" },
|
|
108654
|
-
{ key: "outAcct", header: "\u8F6C\u51FA\u8D26\u6237" },
|
|
108655
|
-
{ key: "inAcct", header: "\u8F6C\u5165\u8D26\u6237" },
|
|
108656
|
-
{ key: "amount", header: "\u91D1\u989D" },
|
|
108657
|
-
{ key: "currency", header: "\u5E01\u79CD" },
|
|
108658
|
-
{ key: "customerName", header: "\u5BA2\u6237\u540D\u79F0" },
|
|
108659
|
-
{ key: "transferStatus", header: "\u8BA2\u5355\u72B6\u6001" },
|
|
108660
|
-
{ key: "created", header: "\u521B\u5EFA\u65F6\u95F4" }
|
|
108661
|
-
];
|
|
108662
|
-
const rows = items.map((item) => {
|
|
108663
|
-
const out = item.accountTransferOutData;
|
|
108664
|
-
const inp = item.accountTransferInData;
|
|
108665
|
-
const outAcct = out ? `${out.mediaCustomerId ?? ""}(${out.mediaCustomerName ?? "\u2014"})` : "\u2014";
|
|
108666
|
-
const inAcct = inp ? `${inp.mediaCustomerId ?? ""}(${inp.mediaCustomerName ?? "\u2014"})` : "\u2014";
|
|
108667
|
-
const amount = out?.transferOutAmounts ?? inp?.transferInAmounts ?? "\u2014";
|
|
108668
|
-
const currency = out?.currencyCode ?? "";
|
|
108669
|
-
const created = item.createdDateTime ? String(item.createdDateTime).slice(0, 10) : "\u2014";
|
|
108670
|
-
return {
|
|
108671
|
-
transferNumber: item.transferNumber ?? "",
|
|
108672
|
-
outAcct: outAcct.slice(0, 22),
|
|
108673
|
-
inAcct: inAcct.slice(0, 22),
|
|
108674
|
-
amount: String(amount),
|
|
108675
|
-
currency,
|
|
108676
|
-
customerName: (item.customerName ?? "").slice(0, 14),
|
|
108677
|
-
transferStatus: item.transferStatus ?? "",
|
|
108678
|
-
created
|
|
108679
|
-
};
|
|
108680
|
-
});
|
|
108681
|
-
printCliTable(rows, cols);
|
|
108682
|
-
console.log();
|
|
107688
|
+
const cols = [
|
|
107689
|
+
{ key: "transferNumber", header: "\u8BA2\u5355\u53F7" },
|
|
107690
|
+
{ key: "outAcct", header: "\u8F6C\u51FA\u8D26\u6237" },
|
|
107691
|
+
{ key: "inAcct", header: "\u8F6C\u5165\u8D26\u6237" },
|
|
107692
|
+
{ key: "amount", header: "\u91D1\u989D" },
|
|
107693
|
+
{ key: "currency", header: "\u5E01\u79CD" },
|
|
107694
|
+
{ key: "customerName", header: "\u5BA2\u6237\u540D\u79F0" },
|
|
107695
|
+
{ key: "transferStatus", header: "\u8BA2\u5355\u72B6\u6001" },
|
|
107696
|
+
{ key: "created", header: "\u521B\u5EFA\u65F6\u95F4" }
|
|
107697
|
+
];
|
|
107698
|
+
const rows = items.map((item) => {
|
|
107699
|
+
const out = item.accountTransferOutData;
|
|
107700
|
+
const inp = item.accountTransferInData;
|
|
107701
|
+
const outAcct = out ? `${out.mediaCustomerId ?? ""}(${out.mediaCustomerName ?? "\u2014"})` : "\u2014";
|
|
107702
|
+
const inAcct = inp ? `${inp.mediaCustomerId ?? ""}(${inp.mediaCustomerName ?? "\u2014"})` : "\u2014";
|
|
107703
|
+
const amount = out?.transferOutAmounts ?? inp?.transferInAmounts ?? "\u2014";
|
|
107704
|
+
const currency = out?.currencyCode ?? "";
|
|
107705
|
+
const created = item.createdDateTime ? String(item.createdDateTime).slice(0, 10) : "\u2014";
|
|
107706
|
+
return {
|
|
107707
|
+
transferNumber: item.transferNumber ?? "",
|
|
107708
|
+
outAcct: outAcct.slice(0, 22),
|
|
107709
|
+
inAcct: inAcct.slice(0, 22),
|
|
107710
|
+
amount: String(amount),
|
|
107711
|
+
currency,
|
|
107712
|
+
customerName: (item.customerName ?? "").slice(0, 14),
|
|
107713
|
+
transferStatus: item.transferStatus ?? "",
|
|
107714
|
+
created
|
|
107715
|
+
};
|
|
107716
|
+
});
|
|
107717
|
+
printCliTable(rows, cols);
|
|
107718
|
+
console.log();
|
|
108683
107719
|
}
|
|
108684
107720
|
function register14(program2) {
|
|
108685
107721
|
const transferCmd = program2.command("transfer").description("\u5A92\u4F53\u8D26\u6237\u8F6C\u8D26\uFF1A\u63D0\u4EA4\u8F6C\u8D26\u7533\u8BF7\u6216\u67E5\u8BE2\u8F6C\u8D26\u8BB0\u5F55");
|
|
@@ -111341,7 +110377,7 @@ function cloneKeywordBlockShell(block) {
|
|
|
111341
110377
|
delete shell["matchTypeV2"];
|
|
111342
110378
|
return shell;
|
|
111343
110379
|
}
|
|
111344
|
-
function splitKeywordsForBatchJobBlockIfMixed(block,
|
|
110380
|
+
function splitKeywordsForBatchJobBlockIfMixed(block, path34, warnings) {
|
|
111345
110381
|
const declaredUi = matchTypeV2ToUi(readBlockMatchTypeRaw(block));
|
|
111346
110382
|
const entries = collectKeywordEntriesFromBlock(block);
|
|
111347
110383
|
if (entries.length === 0) return [block];
|
|
@@ -111360,7 +110396,7 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path32, warnings) {
|
|
|
111360
110396
|
(ui) => matchTypeUiToV2(ui)
|
|
111361
110397
|
);
|
|
111362
110398
|
warnings.push(
|
|
111363
|
-
`${
|
|
110399
|
+
`${path34} \u542B\u591A\u79CD\u5339\u914D\u7C7B\u578B\uFF0C\u5DF2\u81EA\u52A8\u62C6\u5206\u4E3A ${groups.size} \u4E2A KeywordsForBatchJob \u5757\uFF08${labels.join("\u3001")}\uFF09`
|
|
111364
110400
|
);
|
|
111365
110401
|
const shell = cloneKeywordBlockShell(block);
|
|
111366
110402
|
const splitBlocks = [];
|
|
@@ -111380,16 +110416,16 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path32, warnings) {
|
|
|
111380
110416
|
}
|
|
111381
110417
|
return splitBlocks;
|
|
111382
110418
|
}
|
|
111383
|
-
function validateKeywordCore(core,
|
|
110419
|
+
function validateKeywordCore(core, path34, errors, lengthViolations) {
|
|
111384
110420
|
const trimmed = core.trim();
|
|
111385
110421
|
if (!trimmed) {
|
|
111386
|
-
errors.push(`${
|
|
110422
|
+
errors.push(`${path34} \u5173\u952E\u8BCD\u8BCD\u5E72\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
111387
110423
|
return false;
|
|
111388
110424
|
}
|
|
111389
110425
|
if (trimmed.length > GOOGLE_KEYWORD_MAX_CORE_LENGTH) {
|
|
111390
110426
|
if (lengthViolations) {
|
|
111391
110427
|
pushLengthViolation(lengthViolations, {
|
|
111392
|
-
path:
|
|
110428
|
+
path: path34,
|
|
111393
110429
|
field: "KeywordText",
|
|
111394
110430
|
kind: "keyword_core",
|
|
111395
110431
|
limit: GOOGLE_KEYWORD_MAX_CORE_LENGTH,
|
|
@@ -111399,13 +110435,13 @@ function validateKeywordCore(core, path32, errors, lengthViolations) {
|
|
|
111399
110435
|
});
|
|
111400
110436
|
}
|
|
111401
110437
|
errors.push(
|
|
111402
|
-
`${
|
|
110438
|
+
`${path34} \u5173\u952E\u8BCD\u8BCD\u5E72\u8D85\u8FC7 ${GOOGLE_KEYWORD_MAX_CORE_LENGTH} \u5B57\u7B26\uFF08\u5F53\u524D ${trimmed.length}\uFF09\uFF1A"${trimmed}"`
|
|
111403
110439
|
);
|
|
111404
110440
|
return false;
|
|
111405
110441
|
}
|
|
111406
110442
|
if (!VALID_CORE_REGEX.test(trimmed)) {
|
|
111407
110443
|
errors.push(
|
|
111408
|
-
`${
|
|
110444
|
+
`${path34} \u542B Google \u4E0D\u5141\u8BB8\u7684\u5B57\u7B26\uFF08\u8BCD\u5E72\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u7A7A\u683C/\u8FDE\u5B57\u7B26/\u53E5\u70B9\uFF09\uFF1A"${trimmed.slice(0, 40)}${trimmed.length > 40 ? "\u2026" : ""}"`
|
|
111409
110445
|
);
|
|
111410
110446
|
return false;
|
|
111411
110447
|
}
|
|
@@ -111462,24 +110498,24 @@ function canonicalizeKeywordBatchBlock(block, resolvedMatchV2, normalizedTexts,
|
|
|
111462
110498
|
}
|
|
111463
110499
|
}
|
|
111464
110500
|
}
|
|
111465
|
-
function pushKeywordAutoFixWarning(warnings,
|
|
110501
|
+
function pushKeywordAutoFixWarning(warnings, path34, fieldLabel, trimmedRaw, formatted, declaredUi, beforeUi, matchType, inferredMatchType) {
|
|
111466
110502
|
if (inferredMatchType) {
|
|
111467
110503
|
warnings.push(
|
|
111468
|
-
`${
|
|
110504
|
+
`${path34}.${fieldLabel} \u672A\u6307\u5B9A MatchTypeV2\uFF0C\u5DF2\u6309\u8BCD\u9762\u63A8\u65AD\u4E3A ${matchTypeUiToV2(matchType)}`
|
|
111469
110505
|
);
|
|
111470
110506
|
return;
|
|
111471
110507
|
}
|
|
111472
110508
|
if (declaredUi && beforeUi !== declaredUi) {
|
|
111473
110509
|
warnings.push(
|
|
111474
|
-
`${
|
|
110510
|
+
`${path34}.${fieldLabel} MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u4E0E\u8BCD\u9762\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`
|
|
111475
110511
|
);
|
|
111476
110512
|
return;
|
|
111477
110513
|
}
|
|
111478
110514
|
if (formatted !== trimmedRaw) {
|
|
111479
|
-
warnings.push(`${
|
|
110515
|
+
warnings.push(`${path34}.${fieldLabel} \u5DF2\u81EA\u52A8\u4FEE\u590D\u8BCD\u9762\uFF1A${trimmedRaw} \u2192 ${formatted}`);
|
|
111480
110516
|
}
|
|
111481
110517
|
}
|
|
111482
|
-
function normalizeKeywordTextList(texts, matchTypeRaw,
|
|
110518
|
+
function normalizeKeywordTextList(texts, matchTypeRaw, path34, fieldLabel, errors, warnings, lengthViolations) {
|
|
111483
110519
|
const declaredUi = matchTypeV2ToUi(matchTypeRaw);
|
|
111484
110520
|
const normalized = [];
|
|
111485
110521
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -111487,7 +110523,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path32, fieldLabel, error
|
|
|
111487
110523
|
for (let k = 0; k < texts.length; k++) {
|
|
111488
110524
|
const raw = texts[k];
|
|
111489
110525
|
if (typeof raw !== "string" || !raw.trim()) {
|
|
111490
|
-
errors.push(`${
|
|
110526
|
+
errors.push(`${path34}.${fieldLabel}[${k}] \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
111491
110527
|
continue;
|
|
111492
110528
|
}
|
|
111493
110529
|
const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
|
|
@@ -111498,7 +110534,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path32, fieldLabel, error
|
|
|
111498
110534
|
);
|
|
111499
110535
|
pushKeywordAutoFixWarning(
|
|
111500
110536
|
warnings,
|
|
111501
|
-
|
|
110537
|
+
path34,
|
|
111502
110538
|
fieldLabel,
|
|
111503
110539
|
trimmedRaw,
|
|
111504
110540
|
formatted,
|
|
@@ -111508,11 +110544,11 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path32, fieldLabel, error
|
|
|
111508
110544
|
inferredMatchType
|
|
111509
110545
|
);
|
|
111510
110546
|
const core = unwrapKeywordDisplayTextForEdit(formatted);
|
|
111511
|
-
if (!validateKeywordCore(core, `${
|
|
110547
|
+
if (!validateKeywordCore(core, `${path34}.${fieldLabel}[${k}]`, errors, lengthViolations))
|
|
111512
110548
|
continue;
|
|
111513
110549
|
const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
|
|
111514
110550
|
if (seen.has(dedupeKey)) {
|
|
111515
|
-
warnings.push(`${
|
|
110551
|
+
warnings.push(`${path34}.${fieldLabel}[${k}] \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
|
|
111516
110552
|
continue;
|
|
111517
110553
|
}
|
|
111518
110554
|
seen.add(dedupeKey);
|
|
@@ -111521,12 +110557,12 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path32, fieldLabel, error
|
|
|
111521
110557
|
}
|
|
111522
110558
|
return { normalized, resolvedUi };
|
|
111523
110559
|
}
|
|
111524
|
-
function normalizeKeywordsForBatchJobBlock(block,
|
|
110560
|
+
function normalizeKeywordsForBatchJobBlock(block, path34, errors, warnings, lengthViolations) {
|
|
111525
110561
|
const matchTypeRaw = readBlockMatchTypeRaw(block);
|
|
111526
110562
|
const declaredUi = matchTypeV2ToUi(matchTypeRaw);
|
|
111527
110563
|
if (matchTypeRaw !== void 0 && declaredUi === null) {
|
|
111528
110564
|
errors.push(
|
|
111529
|
-
`${
|
|
110565
|
+
`${path34}.MatchTypeV2 \u65E0\u6548\uFF08${String(matchTypeRaw)}\uFF09\uFF0C\u5408\u6CD5\u503C\uFF1ABROAD | PHRASE | EXACT`
|
|
111530
110566
|
);
|
|
111531
110567
|
return;
|
|
111532
110568
|
}
|
|
@@ -111541,7 +110577,7 @@ function normalizeKeywordsForBatchJobBlock(block, path32, errors, warnings, leng
|
|
|
111541
110577
|
const result = normalizeKeywordTextList(
|
|
111542
110578
|
texts,
|
|
111543
110579
|
matchTypeRaw,
|
|
111544
|
-
|
|
110580
|
+
path34,
|
|
111545
110581
|
"KeywordText",
|
|
111546
110582
|
errors,
|
|
111547
110583
|
warnings,
|
|
@@ -111550,7 +110586,7 @@ function normalizeKeywordsForBatchJobBlock(block, path32, errors, warnings, leng
|
|
|
111550
110586
|
normalizedTexts = result.normalized;
|
|
111551
110587
|
if (result.resolvedUi) resolvedUi = result.resolvedUi;
|
|
111552
110588
|
if (normalizedTexts.length === 0 && texts.length > 0) {
|
|
111553
|
-
errors.push(`${
|
|
110589
|
+
errors.push(`${path34}.KeywordText \u7ECF\u6821\u9A8C\u540E\u65E0\u6709\u6548\u5173\u952E\u8BCD\uFF0C\u8BF7\u4FEE\u6B63\u8BCD\u9762\u6216 MatchTypeV2`);
|
|
111554
110590
|
}
|
|
111555
110591
|
}
|
|
111556
110592
|
if (hasItems && items) {
|
|
@@ -111558,12 +110594,12 @@ function normalizeKeywordsForBatchJobBlock(block, path32, errors, warnings, leng
|
|
|
111558
110594
|
for (let k = 0; k < items.length; k++) {
|
|
111559
110595
|
const item = items[k];
|
|
111560
110596
|
if (!item || typeof item !== "object") {
|
|
111561
|
-
errors.push(`${
|
|
110597
|
+
errors.push(`${path34}.Items[${k}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
111562
110598
|
continue;
|
|
111563
110599
|
}
|
|
111564
110600
|
const raw = readItemText(item);
|
|
111565
110601
|
if (raw === null || !raw.trim()) {
|
|
111566
|
-
errors.push(`${
|
|
110602
|
+
errors.push(`${path34}.Items[${k}].Text \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
111567
110603
|
continue;
|
|
111568
110604
|
}
|
|
111569
110605
|
const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
|
|
@@ -111574,7 +110610,7 @@ function normalizeKeywordsForBatchJobBlock(block, path32, errors, warnings, leng
|
|
|
111574
110610
|
);
|
|
111575
110611
|
pushKeywordAutoFixWarning(
|
|
111576
110612
|
warnings,
|
|
111577
|
-
|
|
110613
|
+
path34,
|
|
111578
110614
|
`Items[${k}].Text`,
|
|
111579
110615
|
trimmedRaw,
|
|
111580
110616
|
formatted,
|
|
@@ -111584,11 +110620,11 @@ function normalizeKeywordsForBatchJobBlock(block, path32, errors, warnings, leng
|
|
|
111584
110620
|
inferredMatchType
|
|
111585
110621
|
);
|
|
111586
110622
|
const core = unwrapKeywordDisplayTextForEdit(formatted);
|
|
111587
|
-
if (!validateKeywordCore(core, `${
|
|
110623
|
+
if (!validateKeywordCore(core, `${path34}.Items[${k}].Text`, errors, lengthViolations))
|
|
111588
110624
|
continue;
|
|
111589
110625
|
const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
|
|
111590
110626
|
if (seen.has(dedupeKey)) {
|
|
111591
|
-
warnings.push(`${
|
|
110627
|
+
warnings.push(`${path34}.Items[${k}].Text \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
|
|
111592
110628
|
continue;
|
|
111593
110629
|
}
|
|
111594
110630
|
seen.add(dedupeKey);
|
|
@@ -111733,16 +110769,16 @@ function warnIfNegativeKeywordsInPositiveKeywords(campaign, warnings) {
|
|
|
111733
110769
|
const raw = texts[ki];
|
|
111734
110770
|
if (typeof raw !== "string" || !raw.trim()) continue;
|
|
111735
110771
|
const core = normalizeKeywordCoreForNegativeCheck(raw);
|
|
111736
|
-
const
|
|
110772
|
+
const path34 = `campaign.AdGroupsForBatchJob[${gi}].KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
|
|
111737
110773
|
if (plannedNegCores.has(core)) {
|
|
111738
110774
|
warnings.push(
|
|
111739
|
-
`${
|
|
110775
|
+
`${path34} \u8BCD\u9762\u300C${raw}\u300D\u5DF2\u51FA\u73B0\u5728 campaign.NegativeKeywordsForBatchJob\uFF0C\u4E0D\u5E94\u91CD\u590D\u5199\u5165\u6B63\u5411 KeywordsForBatchJob\uFF1B\u5426\u8BCD\u53EA\u653E NegativeKeywordsForBatchJob`
|
|
111740
110776
|
);
|
|
111741
110777
|
continue;
|
|
111742
110778
|
}
|
|
111743
110779
|
if (looksLikeStandaloneNegativeKeyword(core)) {
|
|
111744
110780
|
warnings.push(
|
|
111745
|
-
`${
|
|
110781
|
+
`${path34} \u8BCD\u9762\u300C${raw}\u300D\u7591\u4F3C\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u5E38\u89C1\u5426\u8BCD\u8BCD\u6839\uFF09\uFF0C\u5E94\u5199\u5165 campaign.NegativeKeywordsForBatchJob\uFF08MatchTypeV2: BROAD\uFF09\uFF0C\u52FF\u653E\u5165 KeywordsForBatchJob`
|
|
111746
110782
|
);
|
|
111747
110783
|
}
|
|
111748
110784
|
}
|
|
@@ -114768,12 +113804,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
114768
113804
|
for (let ki = 0; ki < texts.length; ki++) {
|
|
114769
113805
|
const t = texts[ki];
|
|
114770
113806
|
if (typeof t !== "string" || !t.trim()) continue;
|
|
114771
|
-
const
|
|
113807
|
+
const path34 = `${groupPath}.KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
|
|
114772
113808
|
const key = keywordKey(t, matchTypeV2);
|
|
114773
113809
|
if (!liveKwKeys.has(key)) {
|
|
114774
113810
|
missing.push({
|
|
114775
113811
|
layer: "keyword",
|
|
114776
|
-
path:
|
|
113812
|
+
path: path34,
|
|
114777
113813
|
adGroupName: groupName,
|
|
114778
113814
|
plannedContent: `\u8BCD\u9762: ${t} | \u5339\u914D: ${matchTypeV2}`,
|
|
114779
113815
|
liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveKwInGroup.length} \u6761\u5173\u952E\u8BCD\uFF0C\u65E0\u952E ${key}`,
|
|
@@ -114791,12 +113827,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
114791
113827
|
for (let ai = 0; ai < ads.length; ai++) {
|
|
114792
113828
|
const ad = asRecord2(ads[ai]);
|
|
114793
113829
|
if (!ad) continue;
|
|
114794
|
-
const
|
|
113830
|
+
const path34 = `${groupPath}.AdsForBatchJob[${ai}]`;
|
|
114795
113831
|
const primary = pickString(ad["headlinePart1"], ad["AdTitle"]);
|
|
114796
113832
|
if (!primary) {
|
|
114797
113833
|
missing.push({
|
|
114798
113834
|
layer: "ad",
|
|
114799
|
-
path:
|
|
113835
|
+
path: path34,
|
|
114800
113836
|
adGroupName: groupName,
|
|
114801
113837
|
plannedContent: `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\uFF09`,
|
|
114802
113838
|
liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761\u521B\u610F`,
|
|
@@ -114809,7 +113845,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
114809
113845
|
if (!found) {
|
|
114810
113846
|
missing.push({
|
|
114811
113847
|
layer: "ad",
|
|
114812
|
-
path:
|
|
113848
|
+
path: path34,
|
|
114813
113849
|
adGroupName: groupName,
|
|
114814
113850
|
plannedContent: `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}`,
|
|
114815
113851
|
liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761 RSA\uFF0C\u65E0\u6B64\u9996\u6807\u9898`,
|
|
@@ -116056,8 +115092,8 @@ function pmaxChannelTypesUrl(googleApiUrl) {
|
|
|
116056
115092
|
}
|
|
116057
115093
|
function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
116058
115094
|
const base = googleApiUrl.replace(/\/$/, "");
|
|
116059
|
-
const
|
|
116060
|
-
return campaignId ? `${
|
|
115095
|
+
const path34 = `${base}/accounts/${accountId}/campaign/pmax`;
|
|
115096
|
+
return campaignId ? `${path34}/${campaignId}` : path34;
|
|
116061
115097
|
}
|
|
116062
115098
|
function pmaxBrandAssetsUrl(googleApiUrl, accountId, campaignId) {
|
|
116063
115099
|
return `${pmaxCampaignUrl(googleApiUrl, accountId, campaignId)}/brand-assets`;
|
|
@@ -116067,8 +115103,8 @@ function pmaxBrandGuidelinesEnableUrl(googleApiUrl, accountId, campaignId) {
|
|
|
116067
115103
|
}
|
|
116068
115104
|
function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
|
|
116069
115105
|
const base = googleApiUrl.replace(/\/$/, "");
|
|
116070
|
-
const
|
|
116071
|
-
return suffix ? `${
|
|
115106
|
+
const path34 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
|
|
115107
|
+
return suffix ? `${path34}/${suffix.replace(/^\//, "")}` : path34;
|
|
116072
115108
|
}
|
|
116073
115109
|
function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
|
|
116074
115110
|
const base = googleApiUrl.replace(/\/$/, "");
|
|
@@ -118723,8 +117759,8 @@ async function runAdPmaxImageConvert(opts) {
|
|
|
118723
117759
|
}
|
|
118724
117760
|
|
|
118725
117761
|
// src/commands/ad/pmax-brand-mgmt.ts
|
|
118726
|
-
function loadBrandConfig(
|
|
118727
|
-
const cfg = loadPmaxJsonFile(
|
|
117762
|
+
function loadBrandConfig(path34, accountId, campaignId) {
|
|
117763
|
+
const cfg = loadPmaxJsonFile(path34);
|
|
118728
117764
|
return {
|
|
118729
117765
|
...cfg,
|
|
118730
117766
|
account: String(cfg["account"] ?? accountId).trim(),
|
|
@@ -121236,7 +120272,9 @@ function assertGoogleMediaCustomerId(raw) {
|
|
|
121236
120272
|
process.exit(1);
|
|
121237
120273
|
}
|
|
121238
120274
|
if (!/^\d+$/.test(id)) {
|
|
121239
|
-
console.error(
|
|
120275
|
+
console.error(
|
|
120276
|
+
"\n\u274C --account \u987B\u4E3A Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09\u3002\n"
|
|
120277
|
+
);
|
|
121240
120278
|
process.exit(1);
|
|
121241
120279
|
}
|
|
121242
120280
|
return id;
|
|
@@ -121344,11 +120382,7 @@ async function fetchGoogleCustomerAccessPermission(config, mediaCustomerId, verb
|
|
|
121344
120382
|
async function runAccountCheckAccess(opts) {
|
|
121345
120383
|
const mediaCustomerId = assertGoogleMediaCustomerId(opts.account);
|
|
121346
120384
|
const config = loadConfig(opts.token);
|
|
121347
|
-
const payload = await fetchGoogleCustomerAccessPermission(
|
|
121348
|
-
config,
|
|
121349
|
-
mediaCustomerId,
|
|
121350
|
-
opts.verbose
|
|
121351
|
-
);
|
|
120385
|
+
const payload = await fetchGoogleCustomerAccessPermission(config, mediaCustomerId, opts.verbose);
|
|
121352
120386
|
if (await emitCliJsonOrSnapshot(opts, {
|
|
121353
120387
|
section: "account-check-access",
|
|
121354
120388
|
commandLabel: "account check-access",
|
|
@@ -125166,9 +124200,7 @@ function countCompetitorMentions(html) {
|
|
|
125166
124200
|
}
|
|
125167
124201
|
}
|
|
125168
124202
|
const globalPlain = stripHtmlTags(html);
|
|
125169
|
-
const named = globalPlain.match(
|
|
125170
|
-
/(?:竞品|竞争对手)[::][^\n。;;]{10,400}/
|
|
125171
|
-
)?.[0] ?? "";
|
|
124203
|
+
const named = globalPlain.match(/(?:竞品|竞争对手)[::][^\n。;;]{10,400}/)?.[0] ?? "";
|
|
125172
124204
|
if (named) {
|
|
125173
124205
|
const parts = named.replace(/^[^::]+[::]/, "").split(/[、,,;;|]/).map((s) => s.trim()).filter((s) => s.length >= 2);
|
|
125174
124206
|
if (parts.length >= 3) return parts.length;
|
|
@@ -125755,12 +124787,16 @@ function sectionRecord(env) {
|
|
|
125755
124787
|
}
|
|
125756
124788
|
|
|
125757
124789
|
// src/commands/google-ads-diagnosis/build-report-data.ts
|
|
125758
|
-
function
|
|
124790
|
+
function num(value) {
|
|
125759
124791
|
const n = Number(value);
|
|
125760
124792
|
return Number.isFinite(n) ? n : 0;
|
|
125761
124793
|
}
|
|
125762
124794
|
function rowCost(row) {
|
|
125763
|
-
return
|
|
124795
|
+
return num(row.cost ?? row.spend);
|
|
124796
|
+
}
|
|
124797
|
+
function cvrOf(row, clicks) {
|
|
124798
|
+
if (clicks > 0) return num(row.conversions) / clicks;
|
|
124799
|
+
return num(row.conversionRate ?? row.cvr);
|
|
125764
124800
|
}
|
|
125765
124801
|
function pct(value) {
|
|
125766
124802
|
return `${(value * 100).toFixed(2)}%`;
|
|
@@ -125791,11 +124827,11 @@ function asPeriodRecord(value) {
|
|
|
125791
124827
|
}
|
|
125792
124828
|
function toKeyMetricRow(title, current, previous) {
|
|
125793
124829
|
const currentCost = rowCost(current);
|
|
125794
|
-
const currentClicks =
|
|
125795
|
-
const currentCvr =
|
|
124830
|
+
const currentClicks = num(current.clicks);
|
|
124831
|
+
const currentCvr = cvrOf(current, currentClicks);
|
|
125796
124832
|
const previousCost = previous ? rowCost(previous) : null;
|
|
125797
|
-
const previousClicks = previous ?
|
|
125798
|
-
const previousCvr = previous ?
|
|
124833
|
+
const previousClicks = previous ? num(previous.clicks) : null;
|
|
124834
|
+
const previousCvr = previous ? cvrOf(previous, num(previous.clicks)) : null;
|
|
125799
124835
|
return {
|
|
125800
124836
|
title,
|
|
125801
124837
|
currentCost,
|
|
@@ -125825,13 +124861,13 @@ function buildComparisonItems(currentRows, previousRows, titleFn, keyFn) {
|
|
|
125825
124861
|
}
|
|
125826
124862
|
function toTargetingItem(row, labelKey, label) {
|
|
125827
124863
|
const cost = rowCost(row);
|
|
125828
|
-
const clicks =
|
|
125829
|
-
const impressions =
|
|
125830
|
-
const conversions =
|
|
125831
|
-
const ctr =
|
|
125832
|
-
const cvr =
|
|
125833
|
-
const cpc =
|
|
125834
|
-
const cpa = conversions > 0 ? cost / conversions :
|
|
124864
|
+
const clicks = num(row.clicks);
|
|
124865
|
+
const impressions = num(row.impressions);
|
|
124866
|
+
const conversions = num(row.conversions);
|
|
124867
|
+
const ctr = num(row.ctr);
|
|
124868
|
+
const cvr = cvrOf(row, clicks);
|
|
124869
|
+
const cpc = num(row.averageCpc ?? row.cpc);
|
|
124870
|
+
const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
|
|
125835
124871
|
return {
|
|
125836
124872
|
[labelKey]: label,
|
|
125837
124873
|
cost,
|
|
@@ -125846,11 +124882,11 @@ function toTargetingItem(row, labelKey, label) {
|
|
|
125846
124882
|
}
|
|
125847
124883
|
function toDailyConversionItem(row) {
|
|
125848
124884
|
const cost = rowCost(row);
|
|
125849
|
-
const conversions =
|
|
124885
|
+
const conversions = num(row.conversions);
|
|
125850
124886
|
const rawDate = String(row.date ?? "");
|
|
125851
124887
|
const date = rawDate.includes("T") ? rawDate.slice(0, 10) : rawDate.slice(0, 10);
|
|
125852
|
-
const cpa = conversions > 0 ? cost / conversions :
|
|
125853
|
-
return { date, cost, conversions, cpa, clicks:
|
|
124888
|
+
const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
|
|
124889
|
+
return { date, cost, conversions, cpa, clicks: num(row.clicks), impressions: num(row.impressions) };
|
|
125854
124890
|
}
|
|
125855
124891
|
function loadCompanyNameFromSnap(snapDir) {
|
|
125856
124892
|
const file = path21.join(snapDir, "list-accounts-google.json");
|
|
@@ -125887,8 +124923,8 @@ function inferBusinessProfile(campaignRows, adRows, website) {
|
|
|
125887
124923
|
};
|
|
125888
124924
|
}
|
|
125889
124925
|
function primaryConversionLabel(rows) {
|
|
125890
|
-
const sorted = [...rows].sort((a, b) =>
|
|
125891
|
-
const top = sorted.find((r) =>
|
|
124926
|
+
const sorted = [...rows].sort((a, b) => num(b.allConversions) - num(a.allConversions));
|
|
124927
|
+
const top = sorted.find((r) => num(r.allConversions) > 0 && String(r.status) === "Enabled");
|
|
125892
124928
|
if (top) return String(top.name ?? "\u8BE2\u76D8/\u8F6C\u5316");
|
|
125893
124929
|
const enabled = rows.find((r) => String(r.status) === "Enabled");
|
|
125894
124930
|
return enabled ? String(enabled.name ?? "\u8BE2\u76D8/\u8F6C\u5316") : "\u8BE2\u76D8/\u8F6C\u5316";
|
|
@@ -125907,7 +124943,7 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125907
124943
|
const strategy = String(row.biddingStrategyTypeV2 ?? row.biddingStrategyType ?? "UNKNOWN");
|
|
125908
124944
|
const status = String(row.campaignStatus ?? row.campaignStatusV2 ?? "");
|
|
125909
124945
|
const isPaused = /paused/i.test(status);
|
|
125910
|
-
const recommended =
|
|
124946
|
+
const recommended = num(row.conversions) > 0 ? "MAXIMIZE_CONVERSIONS" : "TARGET_SPEND";
|
|
125911
124947
|
const isCorrect = policyOk && !isPaused && strategy === recommended;
|
|
125912
124948
|
return {
|
|
125913
124949
|
campaignName: String(row.campaignName ?? "\u672A\u547D\u540D\u7CFB\u5217"),
|
|
@@ -125920,16 +124956,16 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125920
124956
|
});
|
|
125921
124957
|
}
|
|
125922
124958
|
function buildStructureCounts(resourceRecord, campaignRows, keywordRows, searchRows) {
|
|
125923
|
-
const campaignCount = Math.max(
|
|
125924
|
-
const keywordCount = Math.max(
|
|
124959
|
+
const campaignCount = Math.max(num(resourceRecord?.campaignCount), campaignRows.length);
|
|
124960
|
+
const keywordCount = Math.max(num(resourceRecord?.keywordCount), keywordRows.length);
|
|
125925
124961
|
return {
|
|
125926
124962
|
campaignCount,
|
|
125927
|
-
adGroupCount:
|
|
124963
|
+
adGroupCount: num(resourceRecord?.adGroupCount),
|
|
125928
124964
|
keywordCount,
|
|
125929
|
-
adCount:
|
|
125930
|
-
extensionCount:
|
|
125931
|
-
negativeKeywordCount:
|
|
125932
|
-
countriesWithConversionsCount:
|
|
124965
|
+
adCount: num(resourceRecord?.adCount),
|
|
124966
|
+
extensionCount: num(resourceRecord?.extensionCount),
|
|
124967
|
+
negativeKeywordCount: num(resourceRecord?.negativeKeywordCount),
|
|
124968
|
+
countriesWithConversionsCount: num(resourceRecord?.countriesWithConversionsCount),
|
|
125933
124969
|
searchTermCount: searchRows.length
|
|
125934
124970
|
};
|
|
125935
124971
|
}
|
|
@@ -125943,26 +124979,26 @@ function loadWebsiteFromAds(snapDir, accountId, start, end) {
|
|
|
125943
124979
|
}
|
|
125944
124980
|
function buildKeywordShareItems(rows) {
|
|
125945
124981
|
const totalCost = rows.reduce((sum, row) => sum + rowCost(row), 0);
|
|
125946
|
-
const totalConversions = rows.reduce((sum, row) => sum +
|
|
124982
|
+
const totalConversions = rows.reduce((sum, row) => sum + num(row.conversions), 0);
|
|
125947
124983
|
return rows.map((row) => {
|
|
125948
124984
|
const cost = rowCost(row);
|
|
125949
|
-
const conversions =
|
|
124985
|
+
const conversions = num(row.conversions);
|
|
125950
124986
|
const keywordText = String(row.keywordText ?? row.keyword ?? "").trim();
|
|
125951
124987
|
return {
|
|
125952
124988
|
keyword: keywordText || "\u672A\u547D\u540D",
|
|
125953
|
-
cpc:
|
|
124989
|
+
cpc: num(row.averageCpc ?? row.cpc),
|
|
125954
124990
|
matchType: String(
|
|
125955
124991
|
row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
|
|
125956
124992
|
),
|
|
125957
|
-
impressions:
|
|
125958
|
-
clicks:
|
|
124993
|
+
impressions: num(row.impressions),
|
|
124994
|
+
clicks: num(row.clicks),
|
|
125959
124995
|
cost,
|
|
125960
124996
|
conversions,
|
|
125961
124997
|
costShare: totalCost > 0 ? cost / totalCost * 100 : 0,
|
|
125962
124998
|
conversionsShare: totalConversions > 0 ? conversions / totalConversions * 100 : 0,
|
|
125963
124999
|
cpa: conversions > 0 ? cost / conversions : 0,
|
|
125964
|
-
ctr:
|
|
125965
|
-
cvr:
|
|
125000
|
+
ctr: num(row.ctr),
|
|
125001
|
+
cvr: cvrOf(row, num(row.clicks))
|
|
125966
125002
|
};
|
|
125967
125003
|
});
|
|
125968
125004
|
}
|
|
@@ -125970,17 +125006,17 @@ function buildSearchTermItems(rows) {
|
|
|
125970
125006
|
const sorted = [...rows].sort((a, b) => rowCost(b) - rowCost(a));
|
|
125971
125007
|
return sorted.slice(0, 50).map((row) => {
|
|
125972
125008
|
const cost = rowCost(row);
|
|
125973
|
-
const conversions =
|
|
125974
|
-
const cpa = conversions > 0 ?
|
|
125009
|
+
const conversions = num(row.conversions);
|
|
125010
|
+
const cpa = conversions > 0 ? num(row.costPerConversion) || cost / conversions : void 0;
|
|
125975
125011
|
return {
|
|
125976
125012
|
searchTerm: String(row.searchTermText ?? row.searchTerm ?? "\u672A\u547D\u540D"),
|
|
125977
125013
|
keyword: String(row.keywordText ?? row.keyword ?? ""),
|
|
125978
125014
|
cost,
|
|
125979
125015
|
conversions,
|
|
125980
|
-
clicks:
|
|
125981
|
-
impressions:
|
|
125982
|
-
ctr:
|
|
125983
|
-
cvr:
|
|
125016
|
+
clicks: num(row.clicks),
|
|
125017
|
+
impressions: num(row.impressions),
|
|
125018
|
+
ctr: num(row.ctr),
|
|
125019
|
+
cvr: cvrOf(row, num(row.clicks)),
|
|
125984
125020
|
cpa
|
|
125985
125021
|
};
|
|
125986
125022
|
});
|
|
@@ -126022,18 +125058,18 @@ function buildAdCreativeItems(rows) {
|
|
|
126022
125058
|
}
|
|
126023
125059
|
function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
|
|
126024
125060
|
const current = asPeriodRecord(overviewRecord?.currentPeriod);
|
|
126025
|
-
const totalCost =
|
|
126026
|
-
const activeDays =
|
|
126027
|
-
const avgDaily =
|
|
126028
|
-
const remainingBudget =
|
|
125061
|
+
const totalCost = num(overviewRecord?.totalCost ?? current?.spend);
|
|
125062
|
+
const activeDays = num(overviewRecord?.activeDays);
|
|
125063
|
+
const avgDaily = num(overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0));
|
|
125064
|
+
const remainingBudget = num(overviewRecord?.remainingAccountBudget);
|
|
126029
125065
|
const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
|
|
126030
|
-
const sis =
|
|
125066
|
+
const sis = num(
|
|
126031
125067
|
dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
|
|
126032
125068
|
);
|
|
126033
|
-
const budgetLost =
|
|
125069
|
+
const budgetLost = num(
|
|
126034
125070
|
dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
|
|
126035
125071
|
);
|
|
126036
|
-
const rankLost =
|
|
125072
|
+
const rankLost = num(
|
|
126037
125073
|
dimensionRecord?.searchRankLostImpressionShare ?? current?.searchRankLostImpressionShare
|
|
126038
125074
|
);
|
|
126039
125075
|
return [
|
|
@@ -126119,17 +125155,17 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126119
125155
|
};
|
|
126120
125156
|
data.reportDate = end;
|
|
126121
125157
|
const cost = rowCost(currentPeriod ?? overviewRecord ?? {});
|
|
126122
|
-
const impressions =
|
|
126123
|
-
const clicks =
|
|
126124
|
-
const conversions =
|
|
126125
|
-
const ctr =
|
|
126126
|
-
const cvr =
|
|
126127
|
-
const cpa = conversions > 0 ? cost / conversions :
|
|
125158
|
+
const impressions = num(currentPeriod?.impressions);
|
|
125159
|
+
const clicks = num(currentPeriod?.clicks);
|
|
125160
|
+
const conversions = num(currentPeriod?.conversions);
|
|
125161
|
+
const ctr = num(currentPeriod?.ctr);
|
|
125162
|
+
const cvr = cvrOf(currentPeriod ?? {}, clicks);
|
|
125163
|
+
const cpa = conversions > 0 ? cost / conversions : num(currentPeriod?.costPerConversion);
|
|
126128
125164
|
const prevCost = previousPeriod ? rowCost(previousPeriod) : 0;
|
|
126129
|
-
const prevConversions = previousPeriod ?
|
|
126130
|
-
const prevCtr = previousPeriod ?
|
|
126131
|
-
const prevCvr = previousPeriod ?
|
|
126132
|
-
const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions :
|
|
125165
|
+
const prevConversions = previousPeriod ? num(previousPeriod.conversions) : 0;
|
|
125166
|
+
const prevCtr = previousPeriod ? num(previousPeriod.ctr) : 0;
|
|
125167
|
+
const prevCvr = previousPeriod ? cvrOf(previousPeriod, num(previousPeriod.clicks)) : 0;
|
|
125168
|
+
const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions : num(previousPeriod?.costPerConversion);
|
|
126133
125169
|
data.metrics = {
|
|
126134
125170
|
cost,
|
|
126135
125171
|
impressions,
|
|
@@ -126165,8 +125201,8 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126165
125201
|
items: conversionRows.map((row) => ({
|
|
126166
125202
|
name: String(row.name ?? row.eventName ?? "\u672A\u77E5\u4E8B\u4EF6"),
|
|
126167
125203
|
status: String(row.status ?? ""),
|
|
126168
|
-
allConversions:
|
|
126169
|
-
allConversionsValue:
|
|
125204
|
+
allConversions: num(row.allConversions),
|
|
125205
|
+
allConversionsValue: num(row.allConversionsValue)
|
|
126170
125206
|
}))
|
|
126171
125207
|
};
|
|
126172
125208
|
const campaignPrevious = sectionItems(
|
|
@@ -126250,16 +125286,16 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126250
125286
|
const lighthouse = opts.landingPage ?? {};
|
|
126251
125287
|
data.landingPageAnalysis = {
|
|
126252
125288
|
desktop: {
|
|
126253
|
-
score:
|
|
126254
|
-
firstContentfulPaint:
|
|
126255
|
-
firstMeaningfulPaint:
|
|
126256
|
-
speedIndex:
|
|
125289
|
+
score: num(lighthouse.desktop?.score),
|
|
125290
|
+
firstContentfulPaint: num(lighthouse.desktop?.firstContentfulPaint),
|
|
125291
|
+
firstMeaningfulPaint: num(lighthouse.desktop?.firstMeaningfulPaint),
|
|
125292
|
+
speedIndex: num(lighthouse.desktop?.speedIndex)
|
|
126257
125293
|
},
|
|
126258
125294
|
mobile: {
|
|
126259
|
-
score:
|
|
126260
|
-
firstContentfulPaint:
|
|
126261
|
-
firstMeaningfulPaint:
|
|
126262
|
-
speedIndex:
|
|
125295
|
+
score: num(lighthouse.mobile?.score),
|
|
125296
|
+
firstContentfulPaint: num(lighthouse.mobile?.firstContentfulPaint),
|
|
125297
|
+
firstMeaningfulPaint: num(lighthouse.mobile?.firstMeaningfulPaint),
|
|
125298
|
+
speedIndex: num(lighthouse.mobile?.speedIndex)
|
|
126263
125299
|
}
|
|
126264
125300
|
};
|
|
126265
125301
|
const keywordShareItems = buildKeywordShareItems(kwCurrent);
|
|
@@ -126293,7 +125329,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126293
125329
|
data.newFeatures = {
|
|
126294
125330
|
items: nfItems.map((item) => {
|
|
126295
125331
|
const strategy = String(item.strategy ?? "");
|
|
126296
|
-
const enabled = strategy === "PerformanceMax" ?
|
|
125332
|
+
const enabled = strategy === "PerformanceMax" ? num(campaignTypes?.PerformanceMax) > 0 : strategy === "KwMatch" ? num(campaignTypes?.Search) > 0 : strategy === "DemandGen" ? num(campaignTypes?.DemandGen) > 0 : strategy === "Youtube" ? num(campaignTypes?.Video) > 0 || num(campaignTypes?.Youtube) > 0 : false;
|
|
126297
125333
|
return {
|
|
126298
125334
|
...item,
|
|
126299
125335
|
accountStatus: enabled,
|
|
@@ -126303,7 +125339,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126303
125339
|
...emptyModuleNarrative()
|
|
126304
125340
|
};
|
|
126305
125341
|
data.summary = emptySummary();
|
|
126306
|
-
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 &&
|
|
125342
|
+
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 && num(r.conversions) === 0);
|
|
126307
125343
|
const agentBrief = buildGoogleAdsDiagnosisAgentBrief({
|
|
126308
125344
|
accountId,
|
|
126309
125345
|
period: formatPeriod(start, end),
|
|
@@ -126327,9 +125363,9 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126327
125363
|
prevCpa
|
|
126328
125364
|
},
|
|
126329
125365
|
dimension: {
|
|
126330
|
-
searchImpressionShare:
|
|
126331
|
-
searchBudgetLostImpressionShare:
|
|
126332
|
-
searchRankLostImpressionShare:
|
|
125366
|
+
searchImpressionShare: num(dimensionRecord?.searchImpressionShare),
|
|
125367
|
+
searchBudgetLostImpressionShare: num(dimensionRecord?.searchBudgetLostImpressionShare),
|
|
125368
|
+
searchRankLostImpressionShare: num(dimensionRecord?.searchRankLostImpressionShare)
|
|
126333
125369
|
},
|
|
126334
125370
|
structure: structureCounts,
|
|
126335
125371
|
goldAccount: goldRecord,
|
|
@@ -126537,9 +125573,9 @@ function asRecord5(value) {
|
|
|
126537
125573
|
}
|
|
126538
125574
|
return null;
|
|
126539
125575
|
}
|
|
126540
|
-
function getNestedValue(root,
|
|
125576
|
+
function getNestedValue(root, path34) {
|
|
126541
125577
|
let cur = root;
|
|
126542
|
-
for (const key of
|
|
125578
|
+
for (const key of path34) {
|
|
126543
125579
|
const rec = asRecord5(cur);
|
|
126544
125580
|
if (!rec) return void 0;
|
|
126545
125581
|
cur = rec[key];
|
|
@@ -126719,6 +125755,35 @@ function getFactItemsErrors(data) {
|
|
|
126719
125755
|
}
|
|
126720
125756
|
return errors;
|
|
126721
125757
|
}
|
|
125758
|
+
var GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE = "landingPageAnalysis";
|
|
125759
|
+
var LANDING_PAGE_SPEED_KEYS = [
|
|
125760
|
+
"score",
|
|
125761
|
+
"firstContentfulPaint",
|
|
125762
|
+
"firstMeaningfulPaint",
|
|
125763
|
+
"speedIndex"
|
|
125764
|
+
];
|
|
125765
|
+
function isLandingPageSpeedValid(value) {
|
|
125766
|
+
const rec = asRecord6(value);
|
|
125767
|
+
if (!rec) return false;
|
|
125768
|
+
return LANDING_PAGE_SPEED_KEYS.every(
|
|
125769
|
+
(key) => typeof rec[key] === "number" && Number.isFinite(rec[key])
|
|
125770
|
+
);
|
|
125771
|
+
}
|
|
125772
|
+
function restoreLandingPageSpeedFromCollect(finalData, collectReportData) {
|
|
125773
|
+
const finalBlock = asRecord6(finalData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125774
|
+
const collectBlock = asRecord6(collectReportData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125775
|
+
if (!collectBlock) return { data: finalData, restored: false };
|
|
125776
|
+
const needsDesktop = !isLandingPageSpeedValid(finalBlock?.desktop) && isLandingPageSpeedValid(collectBlock.desktop);
|
|
125777
|
+
const needsMobile = !isLandingPageSpeedValid(finalBlock?.mobile) && isLandingPageSpeedValid(collectBlock.mobile);
|
|
125778
|
+
if (!needsDesktop && !needsMobile) return { data: finalData, restored: false };
|
|
125779
|
+
const data = { ...finalData };
|
|
125780
|
+
data[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE] = {
|
|
125781
|
+
...finalBlock ?? {},
|
|
125782
|
+
desktop: needsDesktop ? collectBlock.desktop : finalBlock?.desktop,
|
|
125783
|
+
mobile: needsMobile ? collectBlock.mobile : finalBlock?.mobile
|
|
125784
|
+
};
|
|
125785
|
+
return { data, restored: true };
|
|
125786
|
+
}
|
|
126722
125787
|
function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
126723
125788
|
const data = { ...finalData };
|
|
126724
125789
|
const restoredModules = [];
|
|
@@ -126739,8 +125804,74 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
126739
125804
|
return { data, restoredModules };
|
|
126740
125805
|
}
|
|
126741
125806
|
|
|
126742
|
-
// src/commands/google-ads-diagnosis/
|
|
125807
|
+
// src/commands/google-ads-diagnosis/narrative-consistency-check.ts
|
|
126743
125808
|
function asRecord7(value) {
|
|
125809
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125810
|
+
return value;
|
|
125811
|
+
}
|
|
125812
|
+
return null;
|
|
125813
|
+
}
|
|
125814
|
+
function collectNarrativeTexts(data) {
|
|
125815
|
+
const texts = [];
|
|
125816
|
+
const summary = asRecord7(data.summary);
|
|
125817
|
+
if (Array.isArray(summary?.keyIssues)) {
|
|
125818
|
+
for (const item of summary.keyIssues) texts.push(String(item ?? ""));
|
|
125819
|
+
}
|
|
125820
|
+
const overview = asRecord7(data.diagnosisOverview);
|
|
125821
|
+
if (Array.isArray(overview?.disadvantages)) {
|
|
125822
|
+
for (const item of overview.disadvantages) {
|
|
125823
|
+
const rec = asRecord7(item);
|
|
125824
|
+
if (rec) {
|
|
125825
|
+
texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
125826
|
+
} else {
|
|
125827
|
+
texts.push(String(item ?? ""));
|
|
125828
|
+
}
|
|
125829
|
+
}
|
|
125830
|
+
}
|
|
125831
|
+
return texts.filter((t) => t.trim().length > 0);
|
|
125832
|
+
}
|
|
125833
|
+
function newFeatureAccountStatus(data, strategy) {
|
|
125834
|
+
const nf = asRecord7(data.newFeatures);
|
|
125835
|
+
const items = nf?.items;
|
|
125836
|
+
if (!Array.isArray(items)) return null;
|
|
125837
|
+
const row = items.find((it) => asRecord7(it)?.strategy === strategy);
|
|
125838
|
+
const rec = asRecord7(row);
|
|
125839
|
+
if (!rec || typeof rec.accountStatus !== "boolean") return null;
|
|
125840
|
+
return rec.accountStatus;
|
|
125841
|
+
}
|
|
125842
|
+
function goldAccountFlag(data, key) {
|
|
125843
|
+
const gold = asRecord7(data.goldAccount);
|
|
125844
|
+
const value = gold?.[key];
|
|
125845
|
+
return typeof value === "boolean" ? value : null;
|
|
125846
|
+
}
|
|
125847
|
+
var NARRATIVE_CONSISTENCY_RULES = [
|
|
125848
|
+
{
|
|
125849
|
+
id: "keyword-strategy-missing",
|
|
125850
|
+
textPattern: /关键词.{0,8}(完全缺失|策略缺失|从未投放|从未做过|一直没有|尚未开启)/,
|
|
125851
|
+
isContradicted: (data) => newFeatureAccountStatus(data, "KwMatch") === true,
|
|
125852
|
+
hint: 'newFeatures \u91CC KwMatch.accountStatus=true\uFF0C\u8BF4\u660E\u8D26\u6237\u5386\u53F2\u4E0A\u6295\u653E\u8FC7 Search/\u5173\u952E\u8BCD\u5E7F\u6CDB\u5339\u914D\uFF08\u53EA\u662F\u5F53\u524D\u7CFB\u5217\u5DF2\u6682\u505C\uFF09\uFF0C\u4E0D\u662F"\u4ECE\u672A\u6295\u653E"\u3002\u8BF7\u6838\u5B9E\u5386\u53F2\u7CFB\u5217\uFF08channelTypeV2=SEARCH \u4E14\u5DF2 Paused\uFF09\u540E\u6539\u5199\u4E3A"\u5173\u952E\u8BCD\u7B56\u7565\u5DF2\u88AB\u6401\u7F6E/\u8FC7\u5EA6\u4F9D\u8D56 PMax \u7B49\u5176\u4ED6\u7CFB\u5217\u7C7B\u578B"\uFF0C\u5E76\u7ED9\u51FA\u5177\u4F53\u5386\u53F2\u7CFB\u5217\u540D\u79F0\u4E0E\u82B1\u8D39/\u8F6C\u5316\u6570\u636E\u3002'
|
|
125853
|
+
},
|
|
125854
|
+
{
|
|
125855
|
+
id: "conversion-tracking-incomplete",
|
|
125856
|
+
textPattern: /转化跟踪.{0,8}(不完整|不完善|未完善|不到位|缺失)/,
|
|
125857
|
+
isContradicted: (data) => goldAccountFlag(data, "conversionTrackingQualified") === true,
|
|
125858
|
+
hint: "goldAccount.conversionTrackingQualified=true \u8868\u793A\u8BE5\u9879\u5DF2\u8FBE\u6807\uFF0C\u4E0E\u6587\u6848\u7ED3\u8BBA\u76F8\u53CD\uFF08true=\u5DF2\u8FBE\u6807\u3001false=\u672A\u8FBE\u6807\uFF0C\u4E0D\u53EF\u98A0\u5012\uFF09\u3002\u8BF7\u91CD\u65B0\u6838\u5BF9\u5177\u4F53\u662F\u54EA\u4E2A\u8F6C\u5316\u52A8\u4F5C/\u54EA\u4E2A\u5E73\u53F0\u7684\u95EE\u9898\uFF0C\u6216\u5220\u9664\u8BE5\u6761\u7ED3\u8BBA\u3002"
|
|
125859
|
+
}
|
|
125860
|
+
];
|
|
125861
|
+
function getNarrativeConsistencyErrors(data) {
|
|
125862
|
+
const texts = collectNarrativeTexts(data);
|
|
125863
|
+
const errors = [];
|
|
125864
|
+
for (const rule of NARRATIVE_CONSISTENCY_RULES) {
|
|
125865
|
+
const hitText = texts.find((t) => rule.textPattern.test(t));
|
|
125866
|
+
if (hitText && rule.isContradicted(data)) {
|
|
125867
|
+
errors.push(`\u53D9\u4E8B\u77DB\u76FE\uFF08${rule.id}\uFF09\uFF1A\u300C${hitText}\u300D\u2014\u2014 ${rule.hint}`);
|
|
125868
|
+
}
|
|
125869
|
+
}
|
|
125870
|
+
return errors;
|
|
125871
|
+
}
|
|
125872
|
+
|
|
125873
|
+
// src/commands/google-ads-diagnosis/render-report.ts
|
|
125874
|
+
function asRecord8(value) {
|
|
126744
125875
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126745
125876
|
return value;
|
|
126746
125877
|
}
|
|
@@ -126749,8 +125880,8 @@ function asRecord7(value) {
|
|
|
126749
125880
|
async function tryLoadCollectReportData(collectPath) {
|
|
126750
125881
|
try {
|
|
126751
125882
|
const raw = JSON.parse(await fs18.readFile(collectPath, "utf8"));
|
|
126752
|
-
const root =
|
|
126753
|
-
const reportData =
|
|
125883
|
+
const root = asRecord8(raw);
|
|
125884
|
+
const reportData = asRecord8(root?.reportData);
|
|
126754
125885
|
return reportData ?? root;
|
|
126755
125886
|
} catch {
|
|
126756
125887
|
return null;
|
|
@@ -126767,7 +125898,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126767
125898
|
`);
|
|
126768
125899
|
process.exit(1);
|
|
126769
125900
|
}
|
|
126770
|
-
let data =
|
|
125901
|
+
let data = asRecord8(dataRaw);
|
|
126771
125902
|
if (!opts.noMergeCollect) {
|
|
126772
125903
|
const collectPath = path23.resolve(
|
|
126773
125904
|
opts.collectFile ?? path23.join(path23.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -126784,11 +125915,32 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126784
125915
|
`
|
|
126785
125916
|
\u2139\uFE0F \u5DF2\u4ECE collect \u6062\u590D\u4E8B\u5B9E items\uFF08${restoredModules.join("\u3001")}\uFF09\uFF1BAgent \u53EA\u5E94\u586B\u5199 analysis/suggestions\uFF0C\u52FF\u6539\u5199 items \u884C\uFF08\u542B biddingStrategy.duration \u5929\u6570\uFF09\u3002
|
|
126786
125917
|
\u6765\u6E90\uFF1A${collectPath}
|
|
125918
|
+
`
|
|
125919
|
+
);
|
|
125920
|
+
}
|
|
125921
|
+
const { data: withLandingPage, restored: landingPageRestored } = restoreLandingPageSpeedFromCollect(data, collectReportData);
|
|
125922
|
+
data = withLandingPage;
|
|
125923
|
+
if (landingPageRestored) {
|
|
125924
|
+
console.log(
|
|
125925
|
+
`
|
|
125926
|
+
\u2139\uFE0F \u5DF2\u4ECE collect \u6062\u590D landingPageAnalysis.desktop/mobile \u6D4B\u901F\u6570\u636E\uFF08Agent \u53EA\u5E94\u8FFD\u52A0 analysis/suggestions\uFF0C\u7981\u6B62\u6574\u4F53\u8986\u76D6\u8BE5\u5BF9\u8C61\uFF09\u3002
|
|
125927
|
+
\u6765\u6E90\uFF1A${collectPath}
|
|
126787
125928
|
`
|
|
126788
125929
|
);
|
|
126789
125930
|
}
|
|
126790
125931
|
}
|
|
126791
125932
|
}
|
|
125933
|
+
const narrativeErrors = getNarrativeConsistencyErrors(data);
|
|
125934
|
+
if (narrativeErrors.length > 0) {
|
|
125935
|
+
console.error("\n\u274C \u53D9\u4E8B\u6587\u6848\u4E0E\u4E8B\u5B9E\u5B57\u6BB5\u77DB\u76FE\uFF0C\u7981\u6B62\u6E32\u67D3\uFF1A\n");
|
|
125936
|
+
for (const err of narrativeErrors) {
|
|
125937
|
+
console.error(` \xB7 ${err}`);
|
|
125938
|
+
}
|
|
125939
|
+
console.error(
|
|
125940
|
+
"\n \u8BF7\u4FEE\u6B63 summary.keyIssues / diagnosisOverview.disadvantages \u540E\u91CD\u65B0 render\u3002\n"
|
|
125941
|
+
);
|
|
125942
|
+
process.exit(1);
|
|
125943
|
+
}
|
|
126792
125944
|
const comparisonErrors = getFactItemsErrors(data);
|
|
126793
125945
|
if (comparisonErrors.length > 0) {
|
|
126794
125946
|
console.error("\n\u274C \u4E8B\u5B9E items \u4E0D\u5B8C\u6574\uFF0CHTML \u4E2D\u90E8\u5206\u8868\u683C\u4F1A\u663E\u793A\u4E3A\u7A7A\uFF1A\n");
|
|
@@ -126853,7 +126005,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126853
126005
|
`);
|
|
126854
126006
|
}
|
|
126855
126007
|
console.log(
|
|
126856
|
-
"\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 HTML \u6587\u4EF6\u5373\u53EF\u67E5\u770B\u5B8C\u6574\u62A5\u544A\uFF08\u6837\u5F0F\u4E0E MarkAI GoogleAdsDiagnosisReport.html \u4E00\u81F4\uFF1B\u56FE\u8868\
|
|
126008
|
+
"\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 HTML \u6587\u4EF6\u5373\u53EF\u67E5\u770B\u5B8C\u6574\u62A5\u544A\uFF08\u6837\u5F0F\u4E0E MarkAI GoogleAdsDiagnosisReport.html \u4E00\u81F4\uFF1B\u56FE\u8868\uFF1AECharts CDN\uFF09\u3002\n"
|
|
126857
126009
|
);
|
|
126858
126010
|
}
|
|
126859
126011
|
|
|
@@ -126983,7 +126135,7 @@ var PHASE_LABEL = {
|
|
|
126983
126135
|
"find-winner": "\u627E\u8D62\u5BB6",
|
|
126984
126136
|
scale: "\u653E\u91CF"
|
|
126985
126137
|
};
|
|
126986
|
-
function
|
|
126138
|
+
function num2(value) {
|
|
126987
126139
|
const n = Number(value);
|
|
126988
126140
|
return Number.isFinite(n) ? n : void 0;
|
|
126989
126141
|
}
|
|
@@ -126994,35 +126146,35 @@ function money(value, currency = "USD") {
|
|
|
126994
126146
|
}
|
|
126995
126147
|
function adSetsWithSpend(tables) {
|
|
126996
126148
|
const rows = Array.isArray(tables?.adSets) ? tables.adSets : [];
|
|
126997
|
-
return rows.filter((r) => (
|
|
126149
|
+
return rows.filter((r) => (num2(r.spend) ?? 0) > 0);
|
|
126998
126150
|
}
|
|
126999
126151
|
function topAdSetConcentration(adSets) {
|
|
127000
126152
|
if (!adSets.length) return null;
|
|
127001
|
-
const totalSpend = adSets.reduce((s, r) => s + (
|
|
127002
|
-
const totalResults = adSets.reduce((s, r) => s + (
|
|
126153
|
+
const totalSpend = adSets.reduce((s, r) => s + (num2(r.spend) ?? 0), 0);
|
|
126154
|
+
const totalResults = adSets.reduce((s, r) => s + (num2(r.results) ?? 0), 0);
|
|
127003
126155
|
let best = adSets[0];
|
|
127004
|
-
let bestSpend =
|
|
126156
|
+
let bestSpend = num2(best.spend) ?? 0;
|
|
127005
126157
|
for (const r of adSets) {
|
|
127006
|
-
const sp =
|
|
126158
|
+
const sp = num2(r.spend) ?? 0;
|
|
127007
126159
|
if (sp > bestSpend) {
|
|
127008
126160
|
best = r;
|
|
127009
126161
|
bestSpend = sp;
|
|
127010
126162
|
}
|
|
127011
126163
|
}
|
|
127012
|
-
const results =
|
|
126164
|
+
const results = num2(best.results) ?? 0;
|
|
127013
126165
|
return {
|
|
127014
126166
|
name: String(best.adGroupName ?? best.name ?? "\u2014"),
|
|
127015
126167
|
spendShare: totalSpend > 0 ? bestSpend / totalSpend : 0,
|
|
127016
126168
|
resultsShare: totalResults > 0 ? results / totalResults : 0,
|
|
127017
|
-
cpl:
|
|
126169
|
+
cpl: num2(best.costPerResult)
|
|
127018
126170
|
};
|
|
127019
126171
|
}
|
|
127020
126172
|
function winnerSpendShare(adSets, avgCpl, totalSpend) {
|
|
127021
126173
|
if (totalSpend <= 0 || avgCpl <= 0) return 0;
|
|
127022
126174
|
const winnerSpend = adSets.filter((r) => {
|
|
127023
|
-
const cpl =
|
|
126175
|
+
const cpl = num2(r.costPerResult);
|
|
127024
126176
|
return cpl != null && cpl <= avgCpl * 0.9;
|
|
127025
|
-
}).reduce((s, r) => s + (
|
|
126177
|
+
}).reduce((s, r) => s + (num2(r.spend) ?? 0), 0);
|
|
127026
126178
|
return winnerSpend / totalSpend;
|
|
127027
126179
|
}
|
|
127028
126180
|
function countActiveDimensions(charts, tables) {
|
|
@@ -127039,7 +126191,7 @@ function asArray(value) {
|
|
|
127039
126191
|
}
|
|
127040
126192
|
function inferLifecyclePhase(input) {
|
|
127041
126193
|
const { avgCpl, currency = "USD" } = input;
|
|
127042
|
-
const spend =
|
|
126194
|
+
const spend = num2(input.spend) ?? 0;
|
|
127043
126195
|
const adSets = adSetsWithSpend(input.tables);
|
|
127044
126196
|
const activeDims = countActiveDimensions(input.charts, input.tables);
|
|
127045
126197
|
const winShare = winnerSpendShare(adSets, avgCpl, spend);
|
|
@@ -127098,7 +126250,7 @@ function ensureLifecycleFromSnapshot(healthDiagnosis, merged) {
|
|
|
127098
126250
|
}
|
|
127099
126251
|
|
|
127100
126252
|
// src/commands/facebook-analysis/scorecard-autofill.ts
|
|
127101
|
-
function
|
|
126253
|
+
function num3(value) {
|
|
127102
126254
|
const n = Number(value);
|
|
127103
126255
|
return Number.isFinite(n) ? n : void 0;
|
|
127104
126256
|
}
|
|
@@ -127138,8 +126290,8 @@ function buildScorecardFromMergedData(input) {
|
|
|
127138
126290
|
if (pLabels.length) {
|
|
127139
126291
|
const indexed = pLabels.map((label, i) => ({
|
|
127140
126292
|
label: String(label),
|
|
127141
|
-
cpl:
|
|
127142
|
-
spend:
|
|
126293
|
+
cpl: num3(pCpl[i]),
|
|
126294
|
+
spend: num3(pSpend[i]) ?? 0
|
|
127143
126295
|
})).filter((x) => x.spend > 0 || x.cpl != null && x.cpl > 0);
|
|
127144
126296
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? Infinity) - (b.cpl ?? Infinity));
|
|
127145
126297
|
if (sorted[0]) {
|
|
@@ -127164,7 +126316,7 @@ function buildScorecardFromMergedData(input) {
|
|
|
127164
126316
|
const cLabels = Array.isArray(country?.labels) ? country.labels : [];
|
|
127165
126317
|
const cCpl = Array.isArray(country?.cpl) ? country.cpl : [];
|
|
127166
126318
|
if (cLabels.length) {
|
|
127167
|
-
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl:
|
|
126319
|
+
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl: num3(cCpl[i]) })).filter((x) => x.cpl != null && x.cpl > 0);
|
|
127168
126320
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? 0) - (b.cpl ?? 0));
|
|
127169
126321
|
if (sorted[0]) {
|
|
127170
126322
|
const s = cplSignal(sorted[0].cpl, avgCpl);
|
|
@@ -127185,24 +126337,24 @@ function buildScorecardFromMergedData(input) {
|
|
|
127185
126337
|
}
|
|
127186
126338
|
}
|
|
127187
126339
|
const adSets = Array.isArray(tables.adSets) ? tables.adSets : [];
|
|
127188
|
-
const adWithSpend = adSets.filter((g) => (
|
|
126340
|
+
const adWithSpend = adSets.filter((g) => (num3(g.spend) ?? 0) > 0);
|
|
127189
126341
|
if (adWithSpend.length) {
|
|
127190
126342
|
const sorted = [...adWithSpend].sort(
|
|
127191
|
-
(a, b) => (
|
|
126343
|
+
(a, b) => (num3(a.costPerResult) ?? Infinity) - (num3(b.costPerResult) ?? Infinity)
|
|
127192
126344
|
);
|
|
127193
126345
|
const best = sorted[0];
|
|
127194
|
-
const sBest = cplSignal(
|
|
126346
|
+
const sBest = cplSignal(num3(best.costPerResult), avgCpl);
|
|
127195
126347
|
pushRow(rows, seen, {
|
|
127196
126348
|
item: String(best.name ?? "\u5E7F\u544A\u7EC4"),
|
|
127197
|
-
data: `CPL ${money2(
|
|
126349
|
+
data: `CPL ${money2(num3(best.costPerResult), currency)}`,
|
|
127198
126350
|
...sBest
|
|
127199
126351
|
});
|
|
127200
126352
|
if (sorted.length > 1) {
|
|
127201
126353
|
const worst = sorted[sorted.length - 1];
|
|
127202
|
-
const sWorst = cplSignal(
|
|
126354
|
+
const sWorst = cplSignal(num3(worst.costPerResult), avgCpl);
|
|
127203
126355
|
pushRow(rows, seen, {
|
|
127204
126356
|
item: String(worst.name ?? "\u5E7F\u544A\u7EC4"),
|
|
127205
|
-
data: `CPL ${money2(
|
|
126357
|
+
data: `CPL ${money2(num3(worst.costPerResult), currency)}`,
|
|
127206
126358
|
...sWorst
|
|
127207
126359
|
});
|
|
127208
126360
|
}
|
|
@@ -127210,18 +126362,18 @@ function buildScorecardFromMergedData(input) {
|
|
|
127210
126362
|
const topAud = Array.isArray(tables.audienceTop) ? tables.audienceTop : [];
|
|
127211
126363
|
const bottomAud = Array.isArray(tables.audienceBottom) ? tables.audienceBottom : [];
|
|
127212
126364
|
if (topAud[0]) {
|
|
127213
|
-
const s = cplSignal(
|
|
126365
|
+
const s = cplSignal(num3(topAud[0].costPerResult), avgCpl);
|
|
127214
126366
|
pushRow(rows, seen, {
|
|
127215
126367
|
item: `\u53D7\u4F17\xB7${String(topAud[0].label ?? "Top")}`,
|
|
127216
|
-
data: `CPL ${money2(
|
|
126368
|
+
data: `CPL ${money2(num3(topAud[0].costPerResult), currency)}`,
|
|
127217
126369
|
...s
|
|
127218
126370
|
});
|
|
127219
126371
|
}
|
|
127220
126372
|
if (bottomAud[0]) {
|
|
127221
|
-
const s = cplSignal(
|
|
126373
|
+
const s = cplSignal(num3(bottomAud[0].costPerResult), avgCpl);
|
|
127222
126374
|
pushRow(rows, seen, {
|
|
127223
126375
|
item: `\u53D7\u4F17\xB7${String(bottomAud[0].label ?? "Bottom")}`,
|
|
127224
|
-
data: `CPL ${money2(
|
|
126376
|
+
data: `CPL ${money2(num3(bottomAud[0].costPerResult), currency)}`,
|
|
127225
126377
|
...s
|
|
127226
126378
|
});
|
|
127227
126379
|
}
|
|
@@ -127259,13 +126411,13 @@ function ensureScorecardFromSnapshot(healthDiagnosis, merged) {
|
|
|
127259
126411
|
}
|
|
127260
126412
|
|
|
127261
126413
|
// src/commands/facebook-analysis/merge-snapshot.ts
|
|
127262
|
-
function
|
|
126414
|
+
function asRecord9(value) {
|
|
127263
126415
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127264
126416
|
return value;
|
|
127265
126417
|
}
|
|
127266
126418
|
return null;
|
|
127267
126419
|
}
|
|
127268
|
-
function
|
|
126420
|
+
function num4(value) {
|
|
127269
126421
|
const n = Number(value);
|
|
127270
126422
|
return Number.isFinite(n) ? n : void 0;
|
|
127271
126423
|
}
|
|
@@ -127308,7 +126460,7 @@ async function readManifest(snapshotDir) {
|
|
|
127308
126460
|
async function loadSectionFile(snapshotDir, file) {
|
|
127309
126461
|
try {
|
|
127310
126462
|
const raw = await fs21.readFile(path28.join(snapshotDir, file), "utf8");
|
|
127311
|
-
return
|
|
126463
|
+
return asRecord9(JSON.parse(raw));
|
|
127312
126464
|
} catch {
|
|
127313
126465
|
return null;
|
|
127314
126466
|
}
|
|
@@ -127319,8 +126471,8 @@ function aggregatePlatform(networks) {
|
|
|
127319
126471
|
const platform = String(row.publisherPlatform ?? row.network ?? "unknown");
|
|
127320
126472
|
const prev = map.get(platform) ?? { spend: 0, results: 0 };
|
|
127321
126473
|
map.set(platform, {
|
|
127322
|
-
spend: prev.spend + (
|
|
127323
|
-
results: prev.results + (
|
|
126474
|
+
spend: prev.spend + (num4(row.spend) ?? 0),
|
|
126475
|
+
results: prev.results + (num4(row.results) ?? 0)
|
|
127324
126476
|
});
|
|
127325
126477
|
}
|
|
127326
126478
|
const labels = [];
|
|
@@ -127336,10 +126488,10 @@ function aggregatePlatform(networks) {
|
|
|
127336
126488
|
function buildAudienceRows(audiences) {
|
|
127337
126489
|
return audiences.map((a) => ({
|
|
127338
126490
|
label: audienceLabel(a.age, a.gender),
|
|
127339
|
-
spend:
|
|
127340
|
-
results:
|
|
127341
|
-
costPerResult:
|
|
127342
|
-
frequency:
|
|
126491
|
+
spend: num4(a.spend) ?? 0,
|
|
126492
|
+
results: num4(a.results) ?? 0,
|
|
126493
|
+
costPerResult: num4(a.costPerResult) ?? (num4(a.results) ? (num4(a.spend) ?? 0) / (num4(a.results) ?? 1) : 0),
|
|
126494
|
+
frequency: num4(a.frequency)
|
|
127343
126495
|
})).filter((r) => r.spend > 0 || r.results > 0);
|
|
127344
126496
|
}
|
|
127345
126497
|
async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
@@ -127356,7 +126508,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127356
126508
|
if (data) sectionMap.set(art.section, data);
|
|
127357
126509
|
}
|
|
127358
126510
|
const overview = sectionMap.get("overview");
|
|
127359
|
-
const current =
|
|
126511
|
+
const current = asRecord9(overview?.currentPeriod);
|
|
127360
126512
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
127361
126513
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
127362
126514
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -127378,7 +126530,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127378
126530
|
}
|
|
127379
126531
|
if (!meta.resultType && current?.resultType) meta.resultType = String(current.resultType);
|
|
127380
126532
|
const charts = { ...payload.charts ?? {} };
|
|
127381
|
-
const avgCpl =
|
|
126533
|
+
const avgCpl = num4(kpis.costPerResult) ?? 0;
|
|
127382
126534
|
if (!charts.platform) {
|
|
127383
126535
|
const platform = sectionMap.get("platform");
|
|
127384
126536
|
const networks = Array.isArray(platform?.networks) ? platform.networks : [];
|
|
@@ -127388,10 +126540,10 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127388
126540
|
const country = sectionMap.get("country");
|
|
127389
126541
|
const countries = Array.isArray(country?.countries) ? country.countries : [];
|
|
127390
126542
|
if (countries.length) {
|
|
127391
|
-
const sorted = [...countries].sort((a, b) => (
|
|
126543
|
+
const sorted = [...countries].sort((a, b) => (num4(b.spend) ?? 0) - (num4(a.spend) ?? 0));
|
|
127392
126544
|
charts.country = {
|
|
127393
126545
|
labels: sorted.map((c) => String(c.countryOrRegion ?? "\u2014")),
|
|
127394
|
-
cpl: sorted.map((c) =>
|
|
126546
|
+
cpl: sorted.map((c) => num4(c.costPerResult) ?? 0)
|
|
127395
126547
|
};
|
|
127396
126548
|
}
|
|
127397
126549
|
}
|
|
@@ -127407,7 +126559,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127407
126559
|
};
|
|
127408
126560
|
}
|
|
127409
126561
|
}
|
|
127410
|
-
if (!charts.funnel &&
|
|
126562
|
+
if (!charts.funnel && num4(kpis.reach) != null) {
|
|
127411
126563
|
charts.funnel = { reach: kpis.reach, results: kpis.results ?? 0 };
|
|
127412
126564
|
}
|
|
127413
126565
|
const tables = { ...payload.tables ?? {} };
|
|
@@ -127416,14 +126568,14 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127416
126568
|
const groups = Array.isArray(adSets?.adGroups) ? adSets.adGroups : [];
|
|
127417
126569
|
if (groups.length) {
|
|
127418
126570
|
tables.adSets = groups.map((g) => {
|
|
127419
|
-
const cpl =
|
|
127420
|
-
const freq =
|
|
126571
|
+
const cpl = num4(g.costPerResult);
|
|
126572
|
+
const freq = num4(g.frequency);
|
|
127421
126573
|
const fatigue = fatigueFromFrequency(freq);
|
|
127422
126574
|
const status = statusFromCpl(cpl, avgCpl);
|
|
127423
126575
|
return {
|
|
127424
126576
|
name: String(g.adGroupName ?? g.campaignName ?? "\u2014"),
|
|
127425
|
-
spend:
|
|
127426
|
-
results:
|
|
126577
|
+
spend: num4(g.spend) ?? 0,
|
|
126578
|
+
results: num4(g.results) ?? 0,
|
|
127427
126579
|
costPerResult: cpl ?? 0,
|
|
127428
126580
|
frequency: freq,
|
|
127429
126581
|
fatigueLevel: fatigue.level,
|
|
@@ -127447,7 +126599,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127447
126599
|
}
|
|
127448
126600
|
const currency = typeof meta.currency === "string" ? meta.currency : "USD";
|
|
127449
126601
|
const mergeCtx = {
|
|
127450
|
-
spend:
|
|
126602
|
+
spend: num4(kpis.spend),
|
|
127451
126603
|
avgCpl,
|
|
127452
126604
|
currency,
|
|
127453
126605
|
charts,
|
|
@@ -127478,7 +126630,7 @@ function isFiniteNumber(value) {
|
|
|
127478
126630
|
function asArray2(value) {
|
|
127479
126631
|
return Array.isArray(value) ? value : [];
|
|
127480
126632
|
}
|
|
127481
|
-
function
|
|
126633
|
+
function asRecord10(value) {
|
|
127482
126634
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127483
126635
|
return value;
|
|
127484
126636
|
}
|
|
@@ -127490,26 +126642,26 @@ function pushMissing(missing, id, chapter, dimension, hint) {
|
|
|
127490
126642
|
function countAdSetsWithSpend(tables) {
|
|
127491
126643
|
const rows = asArray2(tables?.adSets);
|
|
127492
126644
|
return rows.filter((row) => {
|
|
127493
|
-
const r =
|
|
126645
|
+
const r = asRecord10(row);
|
|
127494
126646
|
const spend = r?.spend;
|
|
127495
126647
|
return isFiniteNumber(spend) && spend > 0;
|
|
127496
126648
|
}).length;
|
|
127497
126649
|
}
|
|
127498
126650
|
function chartLabels(chart) {
|
|
127499
|
-
const c =
|
|
126651
|
+
const c = asRecord10(chart);
|
|
127500
126652
|
return asArray2(c?.labels).length;
|
|
127501
126653
|
}
|
|
127502
126654
|
function validateMetaPeriodReportContent(data) {
|
|
127503
126655
|
const missing = [];
|
|
127504
|
-
const meta =
|
|
127505
|
-
const kpis =
|
|
127506
|
-
const narrative =
|
|
127507
|
-
const tables =
|
|
127508
|
-
const charts =
|
|
127509
|
-
const sections =
|
|
127510
|
-
const health =
|
|
127511
|
-
const priorityPlan =
|
|
127512
|
-
const actionChecklist =
|
|
126656
|
+
const meta = asRecord10(data.meta) ?? {};
|
|
126657
|
+
const kpis = asRecord10(data.kpis) ?? {};
|
|
126658
|
+
const narrative = asRecord10(data.narrative) ?? {};
|
|
126659
|
+
const tables = asRecord10(data.tables);
|
|
126660
|
+
const charts = asRecord10(data.charts);
|
|
126661
|
+
const sections = asRecord10(data.sections);
|
|
126662
|
+
const health = asRecord10(data.healthDiagnosis);
|
|
126663
|
+
const priorityPlan = asRecord10(data.priorityPlan);
|
|
126664
|
+
const actionChecklist = asRecord10(data.actionChecklist);
|
|
127513
126665
|
const adSetsWithSpend2 = countAdSetsWithSpend(tables ?? void 0);
|
|
127514
126666
|
const regionalNarratives = asArray2(narrative.regional).length;
|
|
127515
126667
|
const recommendations = asArray2(narrative.recommendations);
|
|
@@ -127581,7 +126733,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127581
126733
|
"charts.audience.labels \u81F3\u5C11 1 \u9879"
|
|
127582
126734
|
);
|
|
127583
126735
|
}
|
|
127584
|
-
const funnel =
|
|
126736
|
+
const funnel = asRecord10(charts?.funnel);
|
|
127585
126737
|
if (!isFiniteNumber(funnel?.reach)) {
|
|
127586
126738
|
pushMissing(
|
|
127587
126739
|
missing,
|
|
@@ -127645,7 +126797,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127645
126797
|
);
|
|
127646
126798
|
} else {
|
|
127647
126799
|
for (let i = 0; i < regionalNarratives; i++) {
|
|
127648
|
-
const row =
|
|
126800
|
+
const row = asRecord10(asArray2(narrative.regional)[i]);
|
|
127649
126801
|
if (textLen2(row?.text) < 80) {
|
|
127650
126802
|
pushMissing(
|
|
127651
126803
|
missing,
|
|
@@ -127675,7 +126827,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127675
126827
|
"\u5FC5\u987B\u6070\u597D 4 \u6761\uFF08\u7B80\u5316\u8868\u5355/\u533A\u57DF\u8C03\u6574/\u9884\u7B97\u91CD\u6784/\u7D20\u6750\u5EFA\u8BAE\uFF09"
|
|
127676
126828
|
);
|
|
127677
126829
|
} else {
|
|
127678
|
-
const titles = recommendations.map((r) =>
|
|
126830
|
+
const titles = recommendations.map((r) => asRecord10(r)?.title);
|
|
127679
126831
|
for (const title of RECOMMENDATION_TITLES) {
|
|
127680
126832
|
if (!titles.includes(title)) {
|
|
127681
126833
|
pushMissing(
|
|
@@ -127688,7 +126840,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127688
126840
|
}
|
|
127689
126841
|
}
|
|
127690
126842
|
for (let i = 0; i < recommendations.length; i++) {
|
|
127691
|
-
const rec =
|
|
126843
|
+
const rec = asRecord10(recommendations[i]);
|
|
127692
126844
|
if (textLen2(rec?.content) < 150) {
|
|
127693
126845
|
pushMissing(
|
|
127694
126846
|
missing,
|
|
@@ -127710,7 +126862,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127710
126862
|
);
|
|
127711
126863
|
} else {
|
|
127712
126864
|
for (let i = 0; i < supplementary.length; i++) {
|
|
127713
|
-
const row =
|
|
126865
|
+
const row = asRecord10(supplementary[i]);
|
|
127714
126866
|
if (!textLen2(row?.dimension) || !textLen2(row?.issue)) {
|
|
127715
126867
|
pushMissing(
|
|
127716
126868
|
missing,
|
|
@@ -127792,7 +126944,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127792
126944
|
);
|
|
127793
126945
|
} else {
|
|
127794
126946
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
127795
|
-
const q =
|
|
126947
|
+
const q = asRecord10(fourQuestions[i]);
|
|
127796
126948
|
const evidence = asArray2(q?.evidence).filter((e) => textLen2(e) > 0);
|
|
127797
126949
|
if (evidence.length < 2) {
|
|
127798
126950
|
pushMissing(
|
|
@@ -127816,7 +126968,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127816
126968
|
}
|
|
127817
126969
|
let scorecardValidRows = 0;
|
|
127818
126970
|
for (let i = 0; i < scorecard.length; i++) {
|
|
127819
|
-
const row =
|
|
126971
|
+
const row = asRecord10(scorecard[i]);
|
|
127820
126972
|
const itemOk = textLen2(row?.item) > 0;
|
|
127821
126973
|
const dataOk = textLen2(row?.data) > 0;
|
|
127822
126974
|
const signalOk = SCORECARD_SIGNALS.includes(row?.signal);
|
|
@@ -127858,7 +127010,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127858
127010
|
);
|
|
127859
127011
|
}
|
|
127860
127012
|
for (const key of ["platform", "country", "adSets"]) {
|
|
127861
|
-
const insight = textLen2(
|
|
127013
|
+
const insight = textLen2(asRecord10(sections?.[key])?.insight);
|
|
127862
127014
|
if (insight < 200) {
|
|
127863
127015
|
pushMissing(
|
|
127864
127016
|
missing,
|
|
@@ -127869,7 +127021,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127869
127021
|
);
|
|
127870
127022
|
}
|
|
127871
127023
|
}
|
|
127872
|
-
const audience =
|
|
127024
|
+
const audience = asRecord10(sections?.audience);
|
|
127873
127025
|
if (asArray2(audience?.goldenProfile).filter((s) => textLen2(s) > 0).length < 3) {
|
|
127874
127026
|
pushMissing(
|
|
127875
127027
|
missing,
|
|
@@ -127897,7 +127049,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127897
127049
|
"\u2265150 \u5B57"
|
|
127898
127050
|
);
|
|
127899
127051
|
}
|
|
127900
|
-
const landingRows = asArray2(
|
|
127052
|
+
const landingRows = asArray2(asRecord10(sections?.landingPage)?.rows);
|
|
127901
127053
|
if (landingRows.length < 3) {
|
|
127902
127054
|
pushMissing(
|
|
127903
127055
|
missing,
|
|
@@ -127966,133 +127118,971 @@ function resolveSkillTemplatePath3(basename11) {
|
|
|
127966
127118
|
for (const p of candidates) {
|
|
127967
127119
|
if (fs22.existsSync(p)) return p;
|
|
127968
127120
|
}
|
|
127969
|
-
return candidates[0];
|
|
127121
|
+
return candidates[0];
|
|
127122
|
+
}
|
|
127123
|
+
function metaPeriodReportTemplatePath() {
|
|
127124
|
+
return resolveSkillTemplatePath3(TEMPLATE_BASENAMES2.html);
|
|
127125
|
+
}
|
|
127126
|
+
function metaPeriodReportRuntimePath() {
|
|
127127
|
+
return resolveSkillTemplatePath3(TEMPLATE_BASENAMES2.runtime);
|
|
127128
|
+
}
|
|
127129
|
+
async function readJsonFile2(filePath) {
|
|
127130
|
+
const text = await fsPromises2.readFile(filePath, "utf8");
|
|
127131
|
+
try {
|
|
127132
|
+
return JSON.parse(text);
|
|
127133
|
+
} catch {
|
|
127134
|
+
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
127135
|
+
}
|
|
127136
|
+
}
|
|
127137
|
+
function asRecord11(value) {
|
|
127138
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127139
|
+
return value;
|
|
127140
|
+
}
|
|
127141
|
+
throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
127142
|
+
}
|
|
127143
|
+
function injectReportData2(html, payload) {
|
|
127144
|
+
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
127145
|
+
if (!html.includes(INJECT_MARKER)) {
|
|
127146
|
+
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 meta-period-report.html");
|
|
127147
|
+
}
|
|
127148
|
+
return html.replace(INJECT_MARKER, `window.__META_PERIOD_REPORT__ = ${json};`);
|
|
127149
|
+
}
|
|
127150
|
+
async function runFacebookAnalysisRender(opts) {
|
|
127151
|
+
const dataPath = path29.resolve(opts.dataFile);
|
|
127152
|
+
let data = asRecord11(await readJsonFile2(dataPath));
|
|
127153
|
+
if (opts.snapshotDir?.trim()) {
|
|
127154
|
+
data = await mergeFacebookSnapshotIntoReport(data, path29.resolve(opts.snapshotDir));
|
|
127155
|
+
}
|
|
127156
|
+
const contentCheck = validateMetaPeriodReportContent(data);
|
|
127157
|
+
if (!contentCheck.ok) {
|
|
127158
|
+
console.error(`
|
|
127159
|
+
\u274C ${formatMetaPeriodReportErrors(contentCheck)}
|
|
127160
|
+
`);
|
|
127161
|
+
const { stats: stats2 } = contentCheck;
|
|
127162
|
+
console.error(
|
|
127163
|
+
` \u7EDF\u8BA1\uFF1A\u5EFA\u8BAE ${stats2.recommendationCount}/4\uFF5C\u8865\u5145 ${stats2.supplementaryCount}/7\uFF5C\u56DB\u95EE ${stats2.fourQuestionsCount}/4\uFF5C\u9636\u6BB5 ${stats2.lifecyclePhase ?? "\u2014"}${stats2.lifecyclePhaseInferred ? `\uFF08\u63A8\u65AD ${stats2.lifecyclePhaseInferred}\uFF09` : ""}\uFF5C\u7EA2\u7EFF\u706F ${stats2.scorecardValidRows}/${stats2.scorecardRows} \u6709\u6548\u884C\uFF5C\u533A\u57DF\u53D9\u4E8B ${stats2.regionalNarratives}/${stats2.adSetsWithSpend}
|
|
127164
|
+
`
|
|
127165
|
+
);
|
|
127166
|
+
process.exit(1);
|
|
127167
|
+
}
|
|
127168
|
+
const templatePath = metaPeriodReportTemplatePath();
|
|
127169
|
+
let html;
|
|
127170
|
+
try {
|
|
127171
|
+
html = await fsPromises2.readFile(templatePath, "utf8");
|
|
127172
|
+
} catch {
|
|
127173
|
+
console.error(`
|
|
127174
|
+
\u274C \u672A\u627E\u5230\u62A5\u544A\u6A21\u677F\uFF1A${templatePath}
|
|
127175
|
+
\u8BF7\u5148\u6267\u884C npm run build
|
|
127176
|
+
`);
|
|
127177
|
+
process.exit(1);
|
|
127178
|
+
}
|
|
127179
|
+
const outPath = path29.resolve(
|
|
127180
|
+
opts.out ?? path29.join(path29.dirname(dataPath), "meta-period-report.html")
|
|
127181
|
+
);
|
|
127182
|
+
const outDir = path29.dirname(outPath);
|
|
127183
|
+
await fsPromises2.mkdir(outDir, { recursive: true });
|
|
127184
|
+
await fsPromises2.writeFile(outPath, injectReportData2(html, data), "utf8");
|
|
127185
|
+
const runtimeSrc = metaPeriodReportRuntimePath();
|
|
127186
|
+
const runtimeOut = path29.join(outDir, TEMPLATE_BASENAMES2.runtime);
|
|
127187
|
+
let runtimeCopied = false;
|
|
127188
|
+
try {
|
|
127189
|
+
await fsPromises2.copyFile(runtimeSrc, runtimeOut);
|
|
127190
|
+
runtimeCopied = true;
|
|
127191
|
+
} catch {
|
|
127192
|
+
}
|
|
127193
|
+
const { stats } = contentCheck;
|
|
127194
|
+
console.log(`
|
|
127195
|
+
\u2705 Meta/Facebook \u5468\u671F\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
127196
|
+
`);
|
|
127197
|
+
console.log(
|
|
127198
|
+
` \u5FC5\u542B\u5B57\u6BB5\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C\u5EFA\u8BAE ${stats.recommendationCount}\uFF5C\u8865\u5145 ${stats.supplementaryCount}\uFF5C\u56DB\u95EE ${stats.fourQuestionsCount}\uFF5C\u9636\u6BB5 ${stats.lifecyclePhase ?? "\u2014"}\uFF5C\u7EA2\u7EFF\u706F ${stats.scorecardValidRows} \u6709\u6548\u884C
|
|
127199
|
+
`
|
|
127200
|
+
);
|
|
127201
|
+
if (runtimeCopied) {
|
|
127202
|
+
console.log(` \u8FD0\u884C\u65F6\u811A\u672C\uFF1A${runtimeOut}
|
|
127203
|
+
`);
|
|
127204
|
+
}
|
|
127205
|
+
console.log(
|
|
127206
|
+
"\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 HTML \u6587\u4EF6\u5373\u53EF\u67E5\u770B\u56FE\u8868\u4E0E\u5B8C\u6574\u7AE0\u8282\uFF08ECharts\uFF1Astaticpn.siluzan.com/homeCDN/echarts.js\uFF09\u3002\n"
|
|
127207
|
+
);
|
|
127208
|
+
}
|
|
127209
|
+
|
|
127210
|
+
// src/commands/facebook-analysis/run-batch.ts
|
|
127211
|
+
init_auth();
|
|
127212
|
+
import * as path30 from "path";
|
|
127213
|
+
import { performance as performance5 } from "perf_hooks";
|
|
127214
|
+
init_version();
|
|
127215
|
+
|
|
127216
|
+
// src/commands/facebook-analysis/resolve-sections.ts
|
|
127217
|
+
function normalizeSectionToken(raw) {
|
|
127218
|
+
const t = raw.trim();
|
|
127219
|
+
if (!t) return null;
|
|
127220
|
+
if (FACEBOOK_SECTION_NAMES.includes(t)) {
|
|
127221
|
+
return t;
|
|
127222
|
+
}
|
|
127223
|
+
return FACEBOOK_SECTION_ALIASES[t] ?? null;
|
|
127224
|
+
}
|
|
127225
|
+
function resolveSectionList2(sections, exclude) {
|
|
127226
|
+
const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
127227
|
+
const include = splitClean(sections);
|
|
127228
|
+
const dropRaw = splitClean(exclude);
|
|
127229
|
+
const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken(n) === null);
|
|
127230
|
+
if (unknown.length > 0) {
|
|
127231
|
+
console.error(
|
|
127232
|
+
`
|
|
127233
|
+
\u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
|
|
127234
|
+
\u53EF\u9009\uFF1A${FACEBOOK_SECTION_NAMES.join(", ")}
|
|
127235
|
+
Google \u522B\u540D\uFF1A${Object.keys(FACEBOOK_SECTION_ALIASES).join(", ")}
|
|
127236
|
+
`
|
|
127237
|
+
);
|
|
127238
|
+
process.exit(1);
|
|
127239
|
+
}
|
|
127240
|
+
const includeCanonical = new Set(include.map((n) => normalizeSectionToken(n)).filter(Boolean));
|
|
127241
|
+
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken(n)).filter(Boolean));
|
|
127242
|
+
const base = include.length > 0 ? FACEBOOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...FACEBOOK_SECTIONS];
|
|
127243
|
+
return base.filter((s) => !dropCanonical.has(s.name));
|
|
127244
|
+
}
|
|
127245
|
+
|
|
127246
|
+
// src/commands/facebook-analysis/run-batch.ts
|
|
127247
|
+
async function runWithConcurrency2(tasks, concurrency) {
|
|
127248
|
+
const results = new Array(tasks.length);
|
|
127249
|
+
let cursor = 0;
|
|
127250
|
+
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
127251
|
+
while (true) {
|
|
127252
|
+
const i = cursor++;
|
|
127253
|
+
if (i >= tasks.length) return;
|
|
127254
|
+
results[i] = await tasks[i]();
|
|
127255
|
+
}
|
|
127256
|
+
});
|
|
127257
|
+
await Promise.all(workers);
|
|
127258
|
+
return results;
|
|
127259
|
+
}
|
|
127260
|
+
async function runAllFacebookSections(opts) {
|
|
127261
|
+
const { apiId, manifestId } = normalizeFacebookAccountId(opts.account);
|
|
127262
|
+
const config = loadConfig(opts.token);
|
|
127263
|
+
if (!opts.jsonOut?.trim()) {
|
|
127264
|
+
console.error("\n\u274C \u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF08\u5404\u7EF4\u5EA6\u843D\u76D8\u540E\u7531 report-manifest \u7D22\u5F15\uFF09\u3002\n");
|
|
127265
|
+
process.exit(1);
|
|
127266
|
+
}
|
|
127267
|
+
const targets = resolveSectionList2(opts.sections, opts.exclude);
|
|
127268
|
+
if (targets.length === 0) {
|
|
127269
|
+
console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
|
|
127270
|
+
process.exit(1);
|
|
127271
|
+
}
|
|
127272
|
+
const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
|
|
127273
|
+
const concurrency = Math.max(
|
|
127274
|
+
1,
|
|
127275
|
+
Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
|
|
127276
|
+
);
|
|
127277
|
+
const cliVersion = getCurrentVersion2();
|
|
127278
|
+
const overallT0 = performance5.now();
|
|
127279
|
+
const tasks = targets.map(
|
|
127280
|
+
(def) => async () => {
|
|
127281
|
+
const t0 = performance5.now();
|
|
127282
|
+
const hint = endpointHintForFacebookSection(def, apiId);
|
|
127283
|
+
try {
|
|
127284
|
+
const payload = await fetchFacebookSectionPayload(def, opts, config, apiId);
|
|
127285
|
+
const summary2 = await writeReportAnalysisSnapshot({
|
|
127286
|
+
snapshotDir: opts.jsonOut,
|
|
127287
|
+
section: def.name,
|
|
127288
|
+
accountId: manifestId,
|
|
127289
|
+
dateRange: { start: startDate, end: endDate },
|
|
127290
|
+
payload,
|
|
127291
|
+
cliVersion
|
|
127292
|
+
});
|
|
127293
|
+
return {
|
|
127294
|
+
section: def.name,
|
|
127295
|
+
ok: true,
|
|
127296
|
+
elapsedMs: performance5.now() - t0,
|
|
127297
|
+
file: summary2.writtenFiles[0],
|
|
127298
|
+
endpointHint: hint
|
|
127299
|
+
};
|
|
127300
|
+
} catch (err) {
|
|
127301
|
+
return {
|
|
127302
|
+
section: def.name,
|
|
127303
|
+
ok: false,
|
|
127304
|
+
elapsedMs: performance5.now() - t0,
|
|
127305
|
+
error: err instanceof Error ? err.message : String(err),
|
|
127306
|
+
endpointHint: hint
|
|
127307
|
+
};
|
|
127308
|
+
}
|
|
127309
|
+
}
|
|
127310
|
+
);
|
|
127311
|
+
const results = await runWithConcurrency2(tasks, concurrency);
|
|
127312
|
+
const succeeded = results.filter((r) => r.ok).length;
|
|
127313
|
+
const failed = results.length - succeeded;
|
|
127314
|
+
const absoluteSnapshotDir = path30.resolve(opts.jsonOut);
|
|
127315
|
+
const summary = {
|
|
127316
|
+
kind: "siluzan-tso-facebook-analysis-snapshot-batch",
|
|
127317
|
+
absoluteSnapshotDir,
|
|
127318
|
+
manifestFile: reportManifestFile(manifestId),
|
|
127319
|
+
accountId: manifestId,
|
|
127320
|
+
totalSections: results.length,
|
|
127321
|
+
succeeded,
|
|
127322
|
+
failed,
|
|
127323
|
+
concurrency,
|
|
127324
|
+
elapsedMs: performance5.now() - overallT0,
|
|
127325
|
+
results
|
|
127326
|
+
};
|
|
127327
|
+
console.log(JSON.stringify(summary));
|
|
127328
|
+
if (failed > 0) process.exit(2);
|
|
127329
|
+
}
|
|
127330
|
+
|
|
127331
|
+
// src/commands/facebook-analysis/register-cli.ts
|
|
127332
|
+
function registerBatchCommand(sectionHelp) {
|
|
127333
|
+
return new Command("run").description(
|
|
127334
|
+
"\u6279\u91CF\u62C9\u53D6 TSO reporting/media-account/FacebookAds Section \u6570\u636E\uFF08\u7701\u7565\u5B50\u547D\u4EE4\u540D\u65F6\u9ED8\u8BA4\u6267\u884C\u672C\u547D\u4EE4\uFF09"
|
|
127335
|
+
).requiredOption("-a, --account <id>", "Facebook \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09").requiredOption(
|
|
127336
|
+
"--json-out <dir>",
|
|
127337
|
+
"\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
|
|
127338
|
+
).option("-t, --token <token>", "Auth Token").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u7701\u7565=\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
|
|
127339
|
+
"--sections <list>",
|
|
127340
|
+
`\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,ad-sets,platform\uFF1B\u7701\u7565=\u5168\u90E8 7 \u4E2A\u3002\u53EF\u9009\uFF1A${sectionHelp}\uFF1BGoogle \u522B\u540D\uFF1Acampaigns,geographic,devices,ads,materials`
|
|
127341
|
+
).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
|
|
127342
|
+
"--limit <n>",
|
|
127343
|
+
"\u4EC5 country \u7EF4\u5EA6\uFF1A\u6309 spend \u964D\u5E8F\u53D6\u524D N \u6761\uFF08\u4E0D\u4F20\u5219\u8FD4\u56DE\u5168\u90E8\u56FD\u5BB6\uFF09",
|
|
127344
|
+
(v) => parseInt(v, 10)
|
|
127345
|
+
).option("--concurrency <n>", "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 16", (v) => parseInt(v, 10)).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
127346
|
+
await runAllFacebookSections(opts);
|
|
127347
|
+
});
|
|
127348
|
+
}
|
|
127349
|
+
function registerFacebookAnalysisCommands(program2) {
|
|
127350
|
+
const sectionHelp = FACEBOOK_SECTION_NAMES.join(", ");
|
|
127351
|
+
const root = program2.command("facebook-analysis").description(
|
|
127352
|
+
"Facebook Ads \u8D26\u6237\u5206\u6790\uFF1A\u6279\u91CF\u62C9\u53D6 Section \u6570\u636E\uFF08\u9ED8\u8BA4\uFF09\uFF0C\u6216\u7531 render \u751F\u6210 HTML \u5468\u671F\u62A5\u544A"
|
|
127353
|
+
);
|
|
127354
|
+
root.addCommand(registerBatchCommand(sectionHelp), { isDefault: true });
|
|
127355
|
+
root.command("render").description(
|
|
127356
|
+
"\u6839\u636E Agent \u64B0\u5199\u7684\u62A5\u544A JSON \u751F\u6210 Meta/Facebook \u5468\u671F\u5206\u6790 HTML \u7EC8\u7A3F\uFF08\u6821\u9A8C\u5FC5\u542B\u5B57\u6BB5\uFF0C\u7F3A\u9879\u62D2\u7EDD\u6E32\u67D3\uFF09"
|
|
127357
|
+
).requiredOption("--data <file>", "Agent \u4EA7\u51FA\u7684 meta-period-report.json").option(
|
|
127358
|
+
"--snapshot-dir <dir>",
|
|
127359
|
+
"\u53EF\u9009\uFF1Afacebook-analysis --json-out \u76EE\u5F55\uFF0C\u81EA\u52A8\u5408\u5E76 KPI / \u56FE\u8868 / \u8868\u683C"
|
|
127360
|
+
).option("--out <file>", "\u8F93\u51FA HTML \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\uFF09").action(async (opts) => {
|
|
127361
|
+
await runFacebookAnalysisRender({
|
|
127362
|
+
dataFile: opts.data,
|
|
127363
|
+
snapshotDir: opts.snapshotDir,
|
|
127364
|
+
out: opts.out
|
|
127365
|
+
});
|
|
127366
|
+
});
|
|
127367
|
+
}
|
|
127368
|
+
|
|
127369
|
+
// src/commands/bing-analysis/sections.ts
|
|
127370
|
+
var BING_SECTIONS = [
|
|
127371
|
+
{
|
|
127372
|
+
name: "overview",
|
|
127373
|
+
description: "\u8D26\u6237\u603B\u89C8 OverviewSectionData\uFF08\u542B\u4F59\u989D\u5B9E\u65F6\u6821\u6B63\u3001\u65E5\u5747\u6D88\u8017\u56DE\u586B\u3001\u4E0A\u4E00\u5468\u671F\u73AF\u6BD4\u4E8C\u6B21\u62C9\u6570\uFF09",
|
|
127374
|
+
segment: "OverviewSectionData"
|
|
127375
|
+
},
|
|
127376
|
+
{
|
|
127377
|
+
name: "device",
|
|
127378
|
+
description: "\u8BBE\u5907\u5206\u5E03 DeviceSectionData",
|
|
127379
|
+
segment: "DeviceSectionData"
|
|
127380
|
+
},
|
|
127381
|
+
{
|
|
127382
|
+
name: "geographic",
|
|
127383
|
+
description: "\u5730\u57DF\u5206\u5E03 GeographicSectionData",
|
|
127384
|
+
segment: "GeographicSectionData"
|
|
127385
|
+
},
|
|
127386
|
+
{
|
|
127387
|
+
name: "age-audience",
|
|
127388
|
+
description: "\u5E74\u9F84\u53D7\u4F17 AgeAudienceData",
|
|
127389
|
+
segment: "AgeAudienceData"
|
|
127390
|
+
},
|
|
127391
|
+
{
|
|
127392
|
+
name: "gender-audience",
|
|
127393
|
+
description: "\u6027\u522B\u53D7\u4F17 GenderAudienceData",
|
|
127394
|
+
segment: "GenderAudienceData"
|
|
127395
|
+
},
|
|
127396
|
+
{
|
|
127397
|
+
name: "audience-merged",
|
|
127398
|
+
description: "\u53D7\u4F17\u5408\u5E76\uFF08AgeAudienceData + GenderAudienceData \u5E76\u884C\u62C9\u53D6\u5408\u5E76\uFF0C\u4E0D\u505A\u524D\u7AEF filterAudienceData\uFF09",
|
|
127399
|
+
segment: "AgeAudienceData+GenderAudienceData"
|
|
127400
|
+
},
|
|
127401
|
+
{
|
|
127402
|
+
name: "campaigns",
|
|
127403
|
+
description: "\u5E7F\u544A\u7CFB\u5217\u62A5\u8868 CampaignReport",
|
|
127404
|
+
segment: "CampaignReport"
|
|
127405
|
+
},
|
|
127406
|
+
{
|
|
127407
|
+
name: "ad-groups",
|
|
127408
|
+
description: "\u5E7F\u544A\u7EC4\u62A5\u8868 AdGroupReport",
|
|
127409
|
+
segment: "AdGroupReport"
|
|
127410
|
+
},
|
|
127411
|
+
{
|
|
127412
|
+
name: "ads",
|
|
127413
|
+
description: "\u5E7F\u544A\u62A5\u8868 AdReport",
|
|
127414
|
+
segment: "AdReport"
|
|
127415
|
+
},
|
|
127416
|
+
{
|
|
127417
|
+
name: "keywords",
|
|
127418
|
+
description: "\u5173\u952E\u8BCD\u62A5\u8868 KeywordReport\uFF08\u9ED8\u8BA4 limit=100\u3001orderByCost=true\uFF09",
|
|
127419
|
+
segment: "KeywordReport",
|
|
127420
|
+
limitOption: true
|
|
127421
|
+
},
|
|
127422
|
+
{
|
|
127423
|
+
name: "search-terms",
|
|
127424
|
+
description: "\u641C\u7D22\u5B57\u8BCD\u62A5\u8868 SearchQueryReport\uFF08\u9ED8\u8BA4 limit=100\u3001orderByCost=true\uFF09",
|
|
127425
|
+
segment: "SearchQueryReport",
|
|
127426
|
+
limitOption: true
|
|
127427
|
+
}
|
|
127428
|
+
];
|
|
127429
|
+
var BING_SECTION_NAMES = BING_SECTIONS.map((s) => s.name);
|
|
127430
|
+
var BING_SECTION_ALIASES = {
|
|
127431
|
+
devices: "device",
|
|
127432
|
+
geo: "geographic",
|
|
127433
|
+
"search-queries": "search-terms"
|
|
127434
|
+
};
|
|
127435
|
+
|
|
127436
|
+
// src/commands/bing-analysis/resolve-sections.ts
|
|
127437
|
+
function normalizeSectionToken2(raw) {
|
|
127438
|
+
const t = raw.trim();
|
|
127439
|
+
if (!t) return null;
|
|
127440
|
+
if (BING_SECTION_NAMES.includes(t)) {
|
|
127441
|
+
return t;
|
|
127442
|
+
}
|
|
127443
|
+
return BING_SECTION_ALIASES[t] ?? null;
|
|
127444
|
+
}
|
|
127445
|
+
function resolveSectionList3(sections, exclude) {
|
|
127446
|
+
const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
127447
|
+
const include = splitClean(sections);
|
|
127448
|
+
const dropRaw = splitClean(exclude);
|
|
127449
|
+
const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken2(n) === null);
|
|
127450
|
+
if (unknown.length > 0) {
|
|
127451
|
+
console.error(
|
|
127452
|
+
`
|
|
127453
|
+
\u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
|
|
127454
|
+
\u53EF\u9009\uFF1A${BING_SECTION_NAMES.join(", ")}
|
|
127455
|
+
\u522B\u540D\uFF1A${Object.keys(BING_SECTION_ALIASES).join(", ")}
|
|
127456
|
+
`
|
|
127457
|
+
);
|
|
127458
|
+
process.exit(1);
|
|
127459
|
+
}
|
|
127460
|
+
const includeCanonical = new Set(include.map((n) => normalizeSectionToken2(n)).filter(Boolean));
|
|
127461
|
+
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken2(n)).filter(Boolean));
|
|
127462
|
+
const base = include.length > 0 ? BING_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...BING_SECTIONS];
|
|
127463
|
+
return base.filter((s) => !dropCanonical.has(s.name));
|
|
127464
|
+
}
|
|
127465
|
+
|
|
127466
|
+
// src/commands/bing-analysis/fetch.ts
|
|
127467
|
+
init_auth();
|
|
127468
|
+
init_balance();
|
|
127469
|
+
|
|
127470
|
+
// src/commands/bing-analysis/normalize-bing-overview.ts
|
|
127471
|
+
function num5(v) {
|
|
127472
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
127473
|
+
}
|
|
127474
|
+
function roundMoney(v) {
|
|
127475
|
+
return Math.round(v * 100) / 100;
|
|
127476
|
+
}
|
|
127477
|
+
function countInclusiveCalendarDays(startDate, endDate) {
|
|
127478
|
+
const parts = (ymd) => ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
127479
|
+
const sp = parts(startDate);
|
|
127480
|
+
const ep = parts(endDate);
|
|
127481
|
+
if (sp.length !== 3 || ep.length !== 3 || sp.some((n) => !Number.isFinite(n)) || ep.some((n) => !Number.isFinite(n))) {
|
|
127482
|
+
return 0;
|
|
127483
|
+
}
|
|
127484
|
+
const s = new Date(sp[0], sp[1] - 1, sp[2]);
|
|
127485
|
+
const e = new Date(ep[0], ep[1] - 1, ep[2]);
|
|
127486
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) return 0;
|
|
127487
|
+
const msPerDay = 24 * 60 * 60 * 1e3;
|
|
127488
|
+
return Math.floor((e.getTime() - s.getTime()) / msPerDay) + 1;
|
|
127489
|
+
}
|
|
127490
|
+
function periodSpend(period) {
|
|
127491
|
+
if (!period || typeof period !== "object") return void 0;
|
|
127492
|
+
return num5(period.spend);
|
|
127493
|
+
}
|
|
127494
|
+
function enrichBingOverviewDerivedMetrics(record, startDate, endDate) {
|
|
127495
|
+
const out = { ...record };
|
|
127496
|
+
const currentSpend = periodSpend(out.currentPeriod);
|
|
127497
|
+
let totalCost = num5(out.totalCost);
|
|
127498
|
+
if (totalCost == null || totalCost <= 0) {
|
|
127499
|
+
if (currentSpend != null && currentSpend > 0) {
|
|
127500
|
+
out.totalCost = currentSpend;
|
|
127501
|
+
totalCost = currentSpend;
|
|
127502
|
+
}
|
|
127503
|
+
}
|
|
127504
|
+
let activeDays = num5(out.activeDays);
|
|
127505
|
+
if (activeDays == null || activeDays <= 0) {
|
|
127506
|
+
const calendarDays = countInclusiveCalendarDays(startDate, endDate);
|
|
127507
|
+
if (calendarDays > 0) {
|
|
127508
|
+
out.activeDays = calendarDays;
|
|
127509
|
+
activeDays = calendarDays;
|
|
127510
|
+
}
|
|
127511
|
+
}
|
|
127512
|
+
const averageDailyCost = num5(out.averageDailyCost);
|
|
127513
|
+
if ((averageDailyCost == null || averageDailyCost <= 0) && totalCost != null && totalCost > 0 && activeDays != null && activeDays > 0) {
|
|
127514
|
+
out.averageDailyCost = roundMoney(totalCost / activeDays);
|
|
127515
|
+
}
|
|
127516
|
+
return out;
|
|
127970
127517
|
}
|
|
127971
|
-
function
|
|
127972
|
-
|
|
127518
|
+
function parseYmd(ymd) {
|
|
127519
|
+
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
127520
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) return null;
|
|
127521
|
+
const d = new Date(parts[0], parts[1] - 1, parts[2]);
|
|
127522
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
127973
127523
|
}
|
|
127974
|
-
function
|
|
127975
|
-
|
|
127524
|
+
function formatYmd(d) {
|
|
127525
|
+
const y = d.getFullYear();
|
|
127526
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
127527
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
127528
|
+
return `${y}-${m}-${day}`;
|
|
127976
127529
|
}
|
|
127977
|
-
|
|
127978
|
-
const
|
|
127979
|
-
|
|
127980
|
-
|
|
127981
|
-
|
|
127982
|
-
|
|
127530
|
+
function calcBingPreviousPeriodRange(startDate, endDate) {
|
|
127531
|
+
const diffDays = countInclusiveCalendarDays(startDate, endDate);
|
|
127532
|
+
if (diffDays <= 0) return null;
|
|
127533
|
+
const start = parseYmd(startDate);
|
|
127534
|
+
if (!start) return null;
|
|
127535
|
+
const prevEnd = new Date(start);
|
|
127536
|
+
prevEnd.setDate(prevEnd.getDate() - 1);
|
|
127537
|
+
const prevStart = new Date(prevEnd);
|
|
127538
|
+
prevStart.setDate(prevStart.getDate() - (diffDays - 1));
|
|
127539
|
+
return { startDate: formatYmd(prevStart), endDate: formatYmd(prevEnd) };
|
|
127540
|
+
}
|
|
127541
|
+
function isBingPeriodBlockEmpty(period) {
|
|
127542
|
+
if (!period || typeof period !== "object") return true;
|
|
127543
|
+
const p = period;
|
|
127544
|
+
const spend = num5(p.spend) ?? 0;
|
|
127545
|
+
const impressions = num5(p.impressions) ?? 0;
|
|
127546
|
+
const clicks = num5(p.clicks) ?? 0;
|
|
127547
|
+
return spend <= 0 && impressions <= 0 && clicks <= 0;
|
|
127548
|
+
}
|
|
127549
|
+
function syncOverviewBalanceFields(record) {
|
|
127550
|
+
const out = { ...record };
|
|
127551
|
+
const budget = num5(out.remainingAccountBudget);
|
|
127552
|
+
if (budget != null && budget >= 0) {
|
|
127553
|
+
const balanceField = num5(out.balance);
|
|
127554
|
+
if (balanceField == null || balanceField <= 0 && budget > 0) {
|
|
127555
|
+
out.balance = budget;
|
|
127556
|
+
}
|
|
127983
127557
|
}
|
|
127558
|
+
return out;
|
|
127984
127559
|
}
|
|
127985
|
-
|
|
127986
|
-
|
|
127987
|
-
|
|
127560
|
+
|
|
127561
|
+
// src/commands/bing-analysis/fetch.ts
|
|
127562
|
+
var BING_KEYWORD_LIMIT_DEFAULT = 100;
|
|
127563
|
+
function stripLocalDate(d) {
|
|
127564
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
127565
|
+
}
|
|
127566
|
+
function parseLocalYmd(ymd) {
|
|
127567
|
+
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
127568
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) {
|
|
127569
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
127988
127570
|
}
|
|
127989
|
-
|
|
127571
|
+
const [y, m, day] = parts;
|
|
127572
|
+
return new Date(y, m - 1, day);
|
|
127990
127573
|
}
|
|
127991
|
-
function
|
|
127992
|
-
const
|
|
127993
|
-
|
|
127994
|
-
|
|
127574
|
+
function formatLocalYmd(d) {
|
|
127575
|
+
const y = d.getFullYear();
|
|
127576
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
127577
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
127578
|
+
return `${y}-${m}-${day}`;
|
|
127579
|
+
}
|
|
127580
|
+
function bingRangeTouchesTodayOrYesterday(startDate, endDate) {
|
|
127581
|
+
const s = parseLocalYmd(startDate);
|
|
127582
|
+
const e = parseLocalYmd(endDate);
|
|
127583
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) {
|
|
127584
|
+
return true;
|
|
127995
127585
|
}
|
|
127996
|
-
|
|
127586
|
+
const today = stripLocalDate(/* @__PURE__ */ new Date());
|
|
127587
|
+
const yesterday = new Date(today);
|
|
127588
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
127589
|
+
for (let cur = new Date(s); cur <= e; cur.setDate(cur.getDate() + 1)) {
|
|
127590
|
+
const c = stripLocalDate(cur);
|
|
127591
|
+
if (c.getTime() === today.getTime() || c.getTime() === yesterday.getTime()) {
|
|
127592
|
+
return true;
|
|
127593
|
+
}
|
|
127594
|
+
}
|
|
127595
|
+
return false;
|
|
127997
127596
|
}
|
|
127998
|
-
|
|
127999
|
-
|
|
128000
|
-
|
|
128001
|
-
|
|
128002
|
-
|
|
127597
|
+
function resolveBingDateRange(start, end) {
|
|
127598
|
+
if (start && end) {
|
|
127599
|
+
const s = parseLocalYmd(start);
|
|
127600
|
+
const e = parseLocalYmd(end);
|
|
127601
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime())) {
|
|
127602
|
+
console.error("\n\u274C --start / --end \u987B\u4E3A\u6709\u6548\u65E5\u671F YYYY-MM-DD\u3002\n");
|
|
127603
|
+
process.exit(1);
|
|
127604
|
+
}
|
|
127605
|
+
if (s > e) {
|
|
127606
|
+
console.error("\n\u274C \u5F00\u59CB\u65E5\u671F\u4E0D\u80FD\u665A\u4E8E\u7ED3\u675F\u65E5\u671F\u3002\n");
|
|
127607
|
+
process.exit(1);
|
|
127608
|
+
}
|
|
127609
|
+
if (bingRangeTouchesTodayOrYesterday(start, end)) {
|
|
127610
|
+
console.error(
|
|
127611
|
+
"\n\u274C Bing \u62A5\u8868\u65F6\u95F4\u8303\u56F4\u4E0D\u80FD\u5305\u542B\u300C\u4ECA\u5929\u300D\u6216\u300C\u6628\u5929\u300D\uFF08\u63A5\u53E3\u9650\u5236\uFF09\u3002\u8BF7\u6539\u7528\u66F4\u65E9\u7684\u7ED3\u675F\u65E5\u671F\uFF0C\u4F8B\u5982\u524D\u5929\u53CA\u4EE5\u524D\u3002\n"
|
|
127612
|
+
);
|
|
127613
|
+
process.exit(1);
|
|
127614
|
+
}
|
|
127615
|
+
return { startDate: start, endDate: end };
|
|
128003
127616
|
}
|
|
128004
|
-
|
|
128005
|
-
|
|
128006
|
-
|
|
128007
|
-
|
|
128008
|
-
|
|
128009
|
-
|
|
128010
|
-
|
|
128011
|
-
|
|
128012
|
-
|
|
128013
|
-
|
|
127617
|
+
if (!start && !end) {
|
|
127618
|
+
const endD = stripLocalDate(/* @__PURE__ */ new Date());
|
|
127619
|
+
endD.setDate(endD.getDate() - 2);
|
|
127620
|
+
const startD = new Date(endD);
|
|
127621
|
+
startD.setDate(startD.getDate() - 6);
|
|
127622
|
+
return { startDate: formatLocalYmd(startD), endDate: formatLocalYmd(endD) };
|
|
127623
|
+
}
|
|
127624
|
+
console.error(
|
|
127625
|
+
"\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u533A\u95F4\uFF08\u622A\u81F3\u524D\u5929\u5171 7 \u5929\uFF09\u3002\n"
|
|
127626
|
+
);
|
|
127627
|
+
process.exit(1);
|
|
127628
|
+
}
|
|
127629
|
+
function assertBingAccountId(id) {
|
|
127630
|
+
const t = id.trim();
|
|
127631
|
+
if (!t) {
|
|
127632
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
128014
127633
|
process.exit(1);
|
|
128015
127634
|
}
|
|
128016
|
-
|
|
128017
|
-
|
|
128018
|
-
try {
|
|
128019
|
-
html = await fsPromises2.readFile(templatePath, "utf8");
|
|
128020
|
-
} catch {
|
|
128021
|
-
console.error(`
|
|
128022
|
-
\u274C \u672A\u627E\u5230\u62A5\u544A\u6A21\u677F\uFF1A${templatePath}
|
|
128023
|
-
\u8BF7\u5148\u6267\u884C npm run build
|
|
128024
|
-
`);
|
|
127635
|
+
if (!/^\d+$/.test(t)) {
|
|
127636
|
+
console.error("\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId\uFF08\u4E0E list-accounts -m BingV2 \u4E00\u81F4\uFF09\u3002\n");
|
|
128025
127637
|
process.exit(1);
|
|
128026
127638
|
}
|
|
128027
|
-
|
|
128028
|
-
|
|
127639
|
+
return t;
|
|
127640
|
+
}
|
|
127641
|
+
function reportingUrl(config, accountId, segment, query) {
|
|
127642
|
+
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
127643
|
+
return `${config.apiBaseUrl}/reporting/media-account/BingV2/${accountId}/${segment}${q}`;
|
|
127644
|
+
}
|
|
127645
|
+
function resolveLimit(raw) {
|
|
127646
|
+
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
127647
|
+
return Number.isFinite(n) && n > 0 ? n : BING_KEYWORD_LIMIT_DEFAULT;
|
|
127648
|
+
}
|
|
127649
|
+
async function fetchBingJson(config, url, verbose) {
|
|
127650
|
+
return apiFetch2(url, config, {}, verbose);
|
|
127651
|
+
}
|
|
127652
|
+
async function fetchBingOverviewPayload(config, id, startDate, endDate, verbose) {
|
|
127653
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
127654
|
+
const url = reportingUrl(config, id, "OverviewSectionData", params.toString());
|
|
127655
|
+
const raw = await fetchBingJson(config, url, verbose);
|
|
127656
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
127657
|
+
const withBalance = await enrichOverviewRecordWithRealtimeBalance(
|
|
127658
|
+
"BingV2",
|
|
127659
|
+
id,
|
|
127660
|
+
config,
|
|
127661
|
+
raw,
|
|
127662
|
+
verbose
|
|
128029
127663
|
);
|
|
128030
|
-
const
|
|
128031
|
-
|
|
128032
|
-
|
|
128033
|
-
|
|
128034
|
-
|
|
128035
|
-
|
|
127664
|
+
const withDerived = enrichBingOverviewDerivedMetrics(withBalance, startDate, endDate);
|
|
127665
|
+
const withPrevious = await enrichBingOverviewPreviousPeriod(
|
|
127666
|
+
config,
|
|
127667
|
+
id,
|
|
127668
|
+
withDerived,
|
|
127669
|
+
startDate,
|
|
127670
|
+
endDate,
|
|
127671
|
+
verbose
|
|
127672
|
+
);
|
|
127673
|
+
return syncOverviewBalanceFields(withPrevious);
|
|
127674
|
+
}
|
|
127675
|
+
async function enrichBingOverviewPreviousPeriod(config, accountId, record, startDate, endDate, verbose) {
|
|
127676
|
+
if (!isBingPeriodBlockEmpty(record.previousPeriod)) {
|
|
127677
|
+
return record;
|
|
127678
|
+
}
|
|
127679
|
+
const prevRange = calcBingPreviousPeriodRange(startDate, endDate);
|
|
127680
|
+
if (!prevRange) return record;
|
|
127681
|
+
if (bingRangeTouchesTodayOrYesterday(prevRange.startDate, prevRange.endDate)) {
|
|
127682
|
+
return record;
|
|
127683
|
+
}
|
|
128036
127684
|
try {
|
|
128037
|
-
|
|
128038
|
-
|
|
127685
|
+
const params = new URLSearchParams({
|
|
127686
|
+
startDate: prevRange.startDate,
|
|
127687
|
+
endDate: prevRange.endDate
|
|
127688
|
+
});
|
|
127689
|
+
const url = reportingUrl(config, accountId, "OverviewSectionData", params.toString());
|
|
127690
|
+
const prevRaw = await fetchBingJson(config, url, verbose);
|
|
127691
|
+
if (!prevRaw || typeof prevRaw !== "object" || Array.isArray(prevRaw)) {
|
|
127692
|
+
return record;
|
|
127693
|
+
}
|
|
127694
|
+
const prevRecord = prevRaw;
|
|
127695
|
+
const prevPeriod = prevRecord.currentPeriod;
|
|
127696
|
+
if (isBingPeriodBlockEmpty(prevPeriod)) {
|
|
127697
|
+
return record;
|
|
127698
|
+
}
|
|
127699
|
+
return {
|
|
127700
|
+
...record,
|
|
127701
|
+
previousPeriod: prevPeriod,
|
|
127702
|
+
previousPeriodDateRange: prevRange,
|
|
127703
|
+
_previousPeriodSource: "OverviewSectionData-second-fetch"
|
|
127704
|
+
};
|
|
128039
127705
|
} catch {
|
|
127706
|
+
return record;
|
|
128040
127707
|
}
|
|
128041
|
-
|
|
128042
|
-
|
|
128043
|
-
|
|
128044
|
-
|
|
128045
|
-
|
|
128046
|
-
|
|
128047
|
-
|
|
128048
|
-
|
|
128049
|
-
|
|
128050
|
-
|
|
128051
|
-
|
|
127708
|
+
}
|
|
127709
|
+
async function fetchBingAudienceMergedPayload(config, id, startDate, endDate, verbose) {
|
|
127710
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
127711
|
+
const ageUrl = reportingUrl(config, id, "AgeAudienceData", params.toString());
|
|
127712
|
+
const genderUrl = reportingUrl(config, id, "GenderAudienceData", params.toString());
|
|
127713
|
+
const [ageData, genderData] = await Promise.all([
|
|
127714
|
+
fetchBingJson(config, ageUrl, verbose),
|
|
127715
|
+
fetchBingJson(config, genderUrl, verbose)
|
|
127716
|
+
]);
|
|
127717
|
+
return {
|
|
127718
|
+
mergedParts: ["AgeAudienceData", "GenderAudienceData"],
|
|
127719
|
+
startDate,
|
|
127720
|
+
endDate,
|
|
127721
|
+
account: id,
|
|
127722
|
+
data: { ageAudience: ageData, genderAudience: genderData }
|
|
127723
|
+
};
|
|
127724
|
+
}
|
|
127725
|
+
async function fetchBingSectionPayload(def, opts, config, id) {
|
|
127726
|
+
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
127727
|
+
const verbose = !!opts.verbose;
|
|
127728
|
+
if (def.name === "overview") {
|
|
127729
|
+
return fetchBingOverviewPayload(config, id, startDate, endDate, verbose);
|
|
128052
127730
|
}
|
|
128053
|
-
|
|
128054
|
-
|
|
128055
|
-
|
|
127731
|
+
if (def.name === "audience-merged") {
|
|
127732
|
+
return fetchBingAudienceMergedPayload(config, id, startDate, endDate, verbose);
|
|
127733
|
+
}
|
|
127734
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
127735
|
+
if (def.limitOption) {
|
|
127736
|
+
params.set("limit", String(resolveLimit(opts.limit)));
|
|
127737
|
+
params.set("orderByCost", "true");
|
|
127738
|
+
}
|
|
127739
|
+
const url = reportingUrl(config, id, def.segment, params.toString());
|
|
127740
|
+
return fetchBingJson(config, url, verbose);
|
|
127741
|
+
}
|
|
127742
|
+
function endpointHintForBingSection(def) {
|
|
127743
|
+
return `GET \u2026/BingV2/{id}/${def.segment}`;
|
|
128056
127744
|
}
|
|
128057
127745
|
|
|
128058
|
-
// src/commands/
|
|
127746
|
+
// src/commands/bing-analysis/run-batch.ts
|
|
128059
127747
|
init_auth();
|
|
128060
|
-
import * as
|
|
128061
|
-
import { performance as
|
|
127748
|
+
import * as path31 from "path";
|
|
127749
|
+
import { performance as performance6 } from "perf_hooks";
|
|
128062
127750
|
init_version();
|
|
127751
|
+
async function runWithConcurrency3(tasks, concurrency) {
|
|
127752
|
+
const results = new Array(tasks.length);
|
|
127753
|
+
let cursor = 0;
|
|
127754
|
+
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
127755
|
+
while (true) {
|
|
127756
|
+
const i = cursor++;
|
|
127757
|
+
if (i >= tasks.length) return;
|
|
127758
|
+
results[i] = await tasks[i]();
|
|
127759
|
+
}
|
|
127760
|
+
});
|
|
127761
|
+
await Promise.all(workers);
|
|
127762
|
+
return results;
|
|
127763
|
+
}
|
|
127764
|
+
async function runAllBingSections(opts) {
|
|
127765
|
+
const id = assertBingAccountId(opts.account);
|
|
127766
|
+
const config = loadConfig(opts.token);
|
|
127767
|
+
if (!opts.jsonOut?.trim()) {
|
|
127768
|
+
console.error("\n\u274C \u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF08\u5404\u7EF4\u5EA6\u843D\u76D8\u540E\u7531 report-manifest \u7D22\u5F15\uFF09\u3002\n");
|
|
127769
|
+
process.exit(1);
|
|
127770
|
+
}
|
|
127771
|
+
const targets = resolveSectionList3(opts.sections, opts.exclude);
|
|
127772
|
+
if (targets.length === 0) {
|
|
127773
|
+
console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
|
|
127774
|
+
process.exit(1);
|
|
127775
|
+
}
|
|
127776
|
+
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
127777
|
+
const concurrency = Math.max(
|
|
127778
|
+
1,
|
|
127779
|
+
Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
|
|
127780
|
+
);
|
|
127781
|
+
const cliVersion = getCurrentVersion2();
|
|
127782
|
+
const overallT0 = performance6.now();
|
|
127783
|
+
const tasks = targets.map((def) => async () => {
|
|
127784
|
+
const t0 = performance6.now();
|
|
127785
|
+
const hint = endpointHintForBingSection(def);
|
|
127786
|
+
try {
|
|
127787
|
+
const payload = await fetchBingSectionPayload(
|
|
127788
|
+
def,
|
|
127789
|
+
{ ...opts, start: startDate, end: endDate },
|
|
127790
|
+
config,
|
|
127791
|
+
id
|
|
127792
|
+
);
|
|
127793
|
+
const summary2 = await writeReportAnalysisSnapshot({
|
|
127794
|
+
snapshotDir: opts.jsonOut,
|
|
127795
|
+
section: def.name,
|
|
127796
|
+
accountId: id,
|
|
127797
|
+
dateRange: { start: startDate, end: endDate },
|
|
127798
|
+
payload,
|
|
127799
|
+
cliVersion
|
|
127800
|
+
});
|
|
127801
|
+
return {
|
|
127802
|
+
section: def.name,
|
|
127803
|
+
ok: true,
|
|
127804
|
+
elapsedMs: performance6.now() - t0,
|
|
127805
|
+
file: summary2.writtenFiles[0],
|
|
127806
|
+
endpointHint: hint
|
|
127807
|
+
};
|
|
127808
|
+
} catch (err) {
|
|
127809
|
+
return {
|
|
127810
|
+
section: def.name,
|
|
127811
|
+
ok: false,
|
|
127812
|
+
elapsedMs: performance6.now() - t0,
|
|
127813
|
+
error: err instanceof Error ? err.message : String(err),
|
|
127814
|
+
endpointHint: hint
|
|
127815
|
+
};
|
|
127816
|
+
}
|
|
127817
|
+
});
|
|
127818
|
+
const results = await runWithConcurrency3(tasks, concurrency);
|
|
127819
|
+
const succeeded = results.filter((r) => r.ok).length;
|
|
127820
|
+
const failed = results.length - succeeded;
|
|
127821
|
+
const absoluteSnapshotDir = path31.resolve(opts.jsonOut);
|
|
127822
|
+
const summary = {
|
|
127823
|
+
kind: "siluzan-tso-bing-analysis-snapshot-batch",
|
|
127824
|
+
absoluteSnapshotDir,
|
|
127825
|
+
manifestFile: reportManifestFile(id),
|
|
127826
|
+
accountId: id,
|
|
127827
|
+
totalSections: results.length,
|
|
127828
|
+
succeeded,
|
|
127829
|
+
failed,
|
|
127830
|
+
concurrency,
|
|
127831
|
+
elapsedMs: performance6.now() - overallT0,
|
|
127832
|
+
results
|
|
127833
|
+
};
|
|
127834
|
+
console.log(JSON.stringify(summary));
|
|
127835
|
+
if (failed > 0) process.exit(2);
|
|
127836
|
+
}
|
|
128063
127837
|
|
|
128064
|
-
// src/commands/
|
|
128065
|
-
|
|
127838
|
+
// src/commands/bing-analysis/register-cli.ts
|
|
127839
|
+
import { Command as Command2 } from "commander";
|
|
127840
|
+
function registerBatchCommand2(sectionHelp, aliasHelp) {
|
|
127841
|
+
return new Command2("run").description(
|
|
127842
|
+
"\u6279\u91CF\u62C9\u53D6 TSO reporting/media-account/BingV2 Section \u6570\u636E\uFF08\u7701\u7565\u5B50\u547D\u4EE4\u540D\u65F6\u9ED8\u8BA4\u6267\u884C\u672C\u547D\u4EE4\uFF09"
|
|
127843
|
+
).requiredOption(
|
|
127844
|
+
"-a, --account <id>",
|
|
127845
|
+
"Bing mediaCustomerId\uFF08\u6570\u5B57\uFF0C\u4E0E list-accounts -m BingV2 \u4E00\u81F4\uFF09"
|
|
127846
|
+
).requiredOption(
|
|
127847
|
+
"--json-out <dir>",
|
|
127848
|
+
"\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
|
|
127849
|
+
).option("-t, --token <token>", "Auth Token").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
|
|
127850
|
+
"--sections <list>",
|
|
127851
|
+
`\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,campaigns,keywords\uFF1B\u7701\u7565=\u5168\u90E8 11 \u4E2A\u3002\u53EF\u9009\uFF1A${sectionHelp}\uFF1B\u522B\u540D\uFF1A${aliasHelp}`
|
|
127852
|
+
).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
|
|
127853
|
+
"--limit <n>",
|
|
127854
|
+
"\u4EC5 keywords / search-terms\uFF1A\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF0C\u4E0E\u524D\u7AEF KeywordReport/SearchQueryReport \u4E00\u81F4\uFF09",
|
|
127855
|
+
(v) => parseInt(v, 10)
|
|
127856
|
+
).option("--concurrency <n>", "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 16", (v) => parseInt(v, 10)).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
127857
|
+
await runAllBingSections(opts);
|
|
127858
|
+
});
|
|
127859
|
+
}
|
|
127860
|
+
function registerBingAnalysisCommands(program2) {
|
|
127861
|
+
const sectionHelp = BING_SECTION_NAMES.join(", ");
|
|
127862
|
+
const aliasHelp = Object.keys(BING_SECTION_ALIASES).join(", ");
|
|
127863
|
+
const root = program2.command("bing-analysis").description("Bing\uFF08BingV2\uFF09\u8D26\u6237\u5206\u6790\uFF1A\u6279\u91CF\u62C9\u53D6 Section \u6570\u636E\uFF08\u9ED8\u8BA4\uFF09\uFF1B\u65E5\u671F\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929");
|
|
127864
|
+
root.addCommand(registerBatchCommand2(sectionHelp, aliasHelp), { isDefault: true });
|
|
127865
|
+
}
|
|
127866
|
+
|
|
127867
|
+
// src/commands/tiktok-analysis/sections.ts
|
|
127868
|
+
var TIKTOK_SECTIONS = [
|
|
127869
|
+
{
|
|
127870
|
+
name: "overview",
|
|
127871
|
+
description: "\u8D26\u6237\u603B\u89C8 OverviewSectionData",
|
|
127872
|
+
segment: "OverviewSectionData"
|
|
127873
|
+
},
|
|
127874
|
+
{
|
|
127875
|
+
name: "campaigns",
|
|
127876
|
+
description: "\u5E7F\u544A\u7CFB\u5217\u5206\u6BB5 CampaignSectionData\uFF08\u9ED8\u8BA4 take=100\uFF09",
|
|
127877
|
+
segment: "CampaignSectionData",
|
|
127878
|
+
takeOption: true
|
|
127879
|
+
},
|
|
127880
|
+
{
|
|
127881
|
+
name: "ad-groups",
|
|
127882
|
+
description: "\u5E7F\u544A\u7EC4\u62A5\u8868 AdGroupReport",
|
|
127883
|
+
segment: "AdGroupReport",
|
|
127884
|
+
takeOption: true
|
|
127885
|
+
},
|
|
127886
|
+
{
|
|
127887
|
+
name: "ads",
|
|
127888
|
+
description: "\u5E7F\u544A\u62A5\u8868 AdReport",
|
|
127889
|
+
segment: "AdReport",
|
|
127890
|
+
takeOption: true
|
|
127891
|
+
},
|
|
127892
|
+
{
|
|
127893
|
+
name: "videos",
|
|
127894
|
+
description: "\u89C6\u9891\u7D20\u6750\u62A5\u8868 VideoReport",
|
|
127895
|
+
segment: "VideoReport",
|
|
127896
|
+
takeOption: true
|
|
127897
|
+
},
|
|
127898
|
+
{
|
|
127899
|
+
name: "audience-gender",
|
|
127900
|
+
description: "\u53D7\u4F17\uFF08\u6027\u522B\uFF09AudienceReport dimensions=gender",
|
|
127901
|
+
segment: "AudienceReport",
|
|
127902
|
+
audienceDimension: "gender",
|
|
127903
|
+
takeOption: true
|
|
127904
|
+
},
|
|
127905
|
+
{
|
|
127906
|
+
name: "audience-age",
|
|
127907
|
+
description: "\u53D7\u4F17\uFF08\u5E74\u9F84\uFF09AudienceReport dimensions=age",
|
|
127908
|
+
segment: "AudienceReport",
|
|
127909
|
+
audienceDimension: "age",
|
|
127910
|
+
takeOption: true
|
|
127911
|
+
},
|
|
127912
|
+
{
|
|
127913
|
+
name: "audience-interest-category",
|
|
127914
|
+
description: "\u53D7\u4F17\uFF08\u5174\u8DA3\u7C7B\u76EE\uFF09AudienceReport dimensions=interest_category",
|
|
127915
|
+
segment: "AudienceReport",
|
|
127916
|
+
audienceDimension: "interest_category",
|
|
127917
|
+
takeOption: true
|
|
127918
|
+
},
|
|
127919
|
+
{
|
|
127920
|
+
name: "audience-country-code",
|
|
127921
|
+
description: "\u53D7\u4F17\uFF08\u56FD\u5BB6/\u5730\u533A\uFF09AudienceReport dimensions=country_code",
|
|
127922
|
+
segment: "AudienceReport",
|
|
127923
|
+
audienceDimension: "country_code",
|
|
127924
|
+
takeOption: true
|
|
127925
|
+
},
|
|
127926
|
+
{
|
|
127927
|
+
name: "audience-platform",
|
|
127928
|
+
description: "\u53D7\u4F17\uFF08\u5E73\u53F0\uFF09AudienceReport dimensions=platform",
|
|
127929
|
+
segment: "AudienceReport",
|
|
127930
|
+
audienceDimension: "platform",
|
|
127931
|
+
takeOption: true
|
|
127932
|
+
},
|
|
127933
|
+
{
|
|
127934
|
+
name: "audience-language",
|
|
127935
|
+
description: "\u53D7\u4F17\uFF08\u8BED\u8A00\uFF09AudienceReport dimensions=language",
|
|
127936
|
+
segment: "AudienceReport",
|
|
127937
|
+
audienceDimension: "language",
|
|
127938
|
+
takeOption: true
|
|
127939
|
+
},
|
|
127940
|
+
{
|
|
127941
|
+
name: "audience-merged",
|
|
127942
|
+
description: "\u53D7\u4F17\u5408\u5E76\uFF08gender + age + interest_category \u4E09\u6B21 AudienceReport \u5408\u5E76\uFF0C\u4E0E getTikTokAudienceApi \u4E00\u81F4\uFF09",
|
|
127943
|
+
segment: "AudienceReport(gender+age+interest_category)",
|
|
127944
|
+
takeOption: true
|
|
127945
|
+
}
|
|
127946
|
+
];
|
|
127947
|
+
var TIKTOK_SECTION_NAMES = TIKTOK_SECTIONS.map((s) => s.name);
|
|
127948
|
+
var TIKTOK_SECTION_ALIASES = {
|
|
127949
|
+
"ad-groups-report": "ad-groups",
|
|
127950
|
+
audience: "audience-merged"
|
|
127951
|
+
};
|
|
127952
|
+
|
|
127953
|
+
// src/commands/tiktok-analysis/resolve-sections.ts
|
|
127954
|
+
function normalizeSectionToken3(raw) {
|
|
128066
127955
|
const t = raw.trim();
|
|
128067
127956
|
if (!t) return null;
|
|
128068
|
-
if (
|
|
127957
|
+
if (TIKTOK_SECTION_NAMES.includes(t)) {
|
|
128069
127958
|
return t;
|
|
128070
127959
|
}
|
|
128071
|
-
return
|
|
127960
|
+
return TIKTOK_SECTION_ALIASES[t] ?? null;
|
|
128072
127961
|
}
|
|
128073
|
-
function
|
|
127962
|
+
function resolveSectionList4(sections, exclude) {
|
|
128074
127963
|
const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
128075
127964
|
const include = splitClean(sections);
|
|
128076
127965
|
const dropRaw = splitClean(exclude);
|
|
128077
|
-
const unknown = [...include, ...dropRaw].filter((n) =>
|
|
127966
|
+
const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken3(n) === null);
|
|
128078
127967
|
if (unknown.length > 0) {
|
|
128079
127968
|
console.error(
|
|
128080
127969
|
`
|
|
128081
127970
|
\u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
|
|
128082
|
-
\u53EF\u9009\uFF1A${
|
|
128083
|
-
|
|
127971
|
+
\u53EF\u9009\uFF1A${TIKTOK_SECTION_NAMES.join(", ")}
|
|
127972
|
+
\u522B\u540D\uFF1A${Object.keys(TIKTOK_SECTION_ALIASES).join(", ")}
|
|
128084
127973
|
`
|
|
128085
127974
|
);
|
|
128086
127975
|
process.exit(1);
|
|
128087
127976
|
}
|
|
128088
|
-
const includeCanonical = new Set(include.map((n) =>
|
|
128089
|
-
const dropCanonical = new Set(dropRaw.map((n) =>
|
|
128090
|
-
const base = include.length > 0 ?
|
|
127977
|
+
const includeCanonical = new Set(include.map((n) => normalizeSectionToken3(n)).filter(Boolean));
|
|
127978
|
+
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken3(n)).filter(Boolean));
|
|
127979
|
+
const base = include.length > 0 ? TIKTOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...TIKTOK_SECTIONS];
|
|
128091
127980
|
return base.filter((s) => !dropCanonical.has(s.name));
|
|
128092
127981
|
}
|
|
128093
127982
|
|
|
128094
|
-
// src/commands/
|
|
128095
|
-
|
|
127983
|
+
// src/commands/tiktok-analysis/fetch.ts
|
|
127984
|
+
init_auth();
|
|
127985
|
+
var TIKTOK_TAKE_DEFAULT = 100;
|
|
127986
|
+
function resolveTake(raw) {
|
|
127987
|
+
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
127988
|
+
return Number.isFinite(n) && n > 0 ? n : TIKTOK_TAKE_DEFAULT;
|
|
127989
|
+
}
|
|
127990
|
+
function resolveDateRange2(start, end) {
|
|
127991
|
+
if (start && end) return { startDate: start, endDate: end };
|
|
127992
|
+
if (!start && !end) {
|
|
127993
|
+
const endD = /* @__PURE__ */ new Date();
|
|
127994
|
+
endD.setDate(endD.getDate() - 1);
|
|
127995
|
+
const startD = new Date(endD);
|
|
127996
|
+
startD.setDate(startD.getDate() - 6);
|
|
127997
|
+
const fmt2 = (d) => d.toISOString().slice(0, 10);
|
|
127998
|
+
return { startDate: fmt2(startD), endDate: fmt2(endD) };
|
|
127999
|
+
}
|
|
128000
|
+
console.error("\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF09\u3002\n");
|
|
128001
|
+
process.exit(1);
|
|
128002
|
+
}
|
|
128003
|
+
function assertTikTokAccountId(id) {
|
|
128004
|
+
const t = id.trim();
|
|
128005
|
+
if (!t) {
|
|
128006
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
128007
|
+
process.exit(1);
|
|
128008
|
+
}
|
|
128009
|
+
if (!/^\d+$/.test(t)) {
|
|
128010
|
+
console.error(
|
|
128011
|
+
"\n\u274C --account \u987B\u4E3A TikTok \u5E7F\u544A\u4E3B ID\uFF08\u6570\u5B57 mediaCustomerId\uFF0C\u4E0E list-accounts -m TikTok \u4E00\u81F4\uFF09\u3002\n"
|
|
128012
|
+
);
|
|
128013
|
+
process.exit(1);
|
|
128014
|
+
}
|
|
128015
|
+
return t;
|
|
128016
|
+
}
|
|
128017
|
+
function reportingUrl2(config, accountId, segment, query) {
|
|
128018
|
+
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
128019
|
+
return `${config.apiBaseUrl}/reporting/media-account/TikTok/${accountId}/${segment}${q}`;
|
|
128020
|
+
}
|
|
128021
|
+
async function fetchTikTokJson(config, url, verbose) {
|
|
128022
|
+
return apiFetch2(url, config, {}, verbose);
|
|
128023
|
+
}
|
|
128024
|
+
async function fetchTikTokAudienceMergedPayload(config, id, startDate, endDate, take, verbose) {
|
|
128025
|
+
const dims = ["gender", "age", "interest_category"];
|
|
128026
|
+
const parts = {};
|
|
128027
|
+
for (const dimension of dims) {
|
|
128028
|
+
const params = new URLSearchParams({
|
|
128029
|
+
startDate,
|
|
128030
|
+
endDate,
|
|
128031
|
+
take: String(take),
|
|
128032
|
+
dimensions: dimension
|
|
128033
|
+
});
|
|
128034
|
+
const url = reportingUrl2(config, id, "AudienceReport", params.toString());
|
|
128035
|
+
parts[dimension] = await fetchTikTokJson(config, url, verbose);
|
|
128036
|
+
}
|
|
128037
|
+
return { mergedDimensions: dims, startDate, endDate, account: id, data: parts };
|
|
128038
|
+
}
|
|
128039
|
+
async function fetchTikTokSectionPayload(def, opts, config, id) {
|
|
128040
|
+
const { startDate, endDate } = resolveDateRange2(opts.start, opts.end);
|
|
128041
|
+
const verbose = !!opts.verbose;
|
|
128042
|
+
const take = resolveTake(opts.take);
|
|
128043
|
+
if (def.name === "audience-merged") {
|
|
128044
|
+
return fetchTikTokAudienceMergedPayload(config, id, startDate, endDate, take, verbose);
|
|
128045
|
+
}
|
|
128046
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
128047
|
+
if (def.takeOption) params.set("take", String(take));
|
|
128048
|
+
if (def.audienceDimension) params.set("dimensions", def.audienceDimension);
|
|
128049
|
+
const url = reportingUrl2(config, id, def.segment, params.toString());
|
|
128050
|
+
return fetchTikTokJson(config, url, verbose);
|
|
128051
|
+
}
|
|
128052
|
+
function endpointHintForTikTokSection(def) {
|
|
128053
|
+
return `GET \u2026/TikTok/{id}/${def.segment}`;
|
|
128054
|
+
}
|
|
128055
|
+
|
|
128056
|
+
// src/commands/tiktok-analysis/auxiliary.ts
|
|
128057
|
+
init_auth();
|
|
128058
|
+
async function fetchTikTokAreacode(config, verbose) {
|
|
128059
|
+
const base = config.mainApiUrl?.trim();
|
|
128060
|
+
if (!base) {
|
|
128061
|
+
console.error("\n\u274C \u65E0\u6CD5\u63A8\u5BFC mainApiUrl\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
|
|
128062
|
+
process.exit(1);
|
|
128063
|
+
}
|
|
128064
|
+
const url = `${base}/query/media-account/tiktok/TikTokAreacode/Read`;
|
|
128065
|
+
return apiFetch2(url, config, {}, verbose);
|
|
128066
|
+
}
|
|
128067
|
+
async function fetchTikTokInterestList(config, account, verbose) {
|
|
128068
|
+
const id = assertTikTokAccountId(account);
|
|
128069
|
+
const tt = config.tiktokApiUrl?.trim();
|
|
128070
|
+
if (!tt) {
|
|
128071
|
+
console.error("\n\u274C \u672A\u914D\u7F6E tiktokApiUrl\u3002\n");
|
|
128072
|
+
process.exit(1);
|
|
128073
|
+
}
|
|
128074
|
+
const params = new URLSearchParams({ mediaCustomerId: id });
|
|
128075
|
+
const url = `${tt}/tiktokadgroupmanagement/adgroup/tiktok/GetInterestList?${params}`;
|
|
128076
|
+
const data = await apiFetch2(url, config, {}, verbose);
|
|
128077
|
+
return { id, data };
|
|
128078
|
+
}
|
|
128079
|
+
|
|
128080
|
+
// src/commands/tiktok-analysis/run-batch.ts
|
|
128081
|
+
init_auth();
|
|
128082
|
+
import * as path32 from "path";
|
|
128083
|
+
import { performance as performance7 } from "perf_hooks";
|
|
128084
|
+
init_version();
|
|
128085
|
+
async function runWithConcurrency4(tasks, concurrency) {
|
|
128096
128086
|
const results = new Array(tasks.length);
|
|
128097
128087
|
let cursor = 0;
|
|
128098
128088
|
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
@@ -128105,35 +128095,40 @@ async function runWithConcurrency2(tasks, concurrency) {
|
|
|
128105
128095
|
await Promise.all(workers);
|
|
128106
128096
|
return results;
|
|
128107
128097
|
}
|
|
128108
|
-
async function
|
|
128109
|
-
const
|
|
128098
|
+
async function runAllTikTokSections(opts) {
|
|
128099
|
+
const id = assertTikTokAccountId(opts.account);
|
|
128110
128100
|
const config = loadConfig(opts.token);
|
|
128111
128101
|
if (!opts.jsonOut?.trim()) {
|
|
128112
128102
|
console.error("\n\u274C \u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF08\u5404\u7EF4\u5EA6\u843D\u76D8\u540E\u7531 report-manifest \u7D22\u5F15\uFF09\u3002\n");
|
|
128113
128103
|
process.exit(1);
|
|
128114
128104
|
}
|
|
128115
|
-
const targets =
|
|
128105
|
+
const targets = resolveSectionList4(opts.sections, opts.exclude);
|
|
128116
128106
|
if (targets.length === 0) {
|
|
128117
128107
|
console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
|
|
128118
128108
|
process.exit(1);
|
|
128119
128109
|
}
|
|
128120
|
-
const { startDate, endDate } =
|
|
128110
|
+
const { startDate, endDate } = resolveDateRange2(opts.start, opts.end);
|
|
128121
128111
|
const concurrency = Math.max(
|
|
128122
128112
|
1,
|
|
128123
128113
|
Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
|
|
128124
128114
|
);
|
|
128125
128115
|
const cliVersion = getCurrentVersion2();
|
|
128126
|
-
const overallT0 =
|
|
128116
|
+
const overallT0 = performance7.now();
|
|
128127
128117
|
const tasks = targets.map(
|
|
128128
128118
|
(def) => async () => {
|
|
128129
|
-
const t0 =
|
|
128130
|
-
const hint =
|
|
128119
|
+
const t0 = performance7.now();
|
|
128120
|
+
const hint = endpointHintForTikTokSection(def);
|
|
128131
128121
|
try {
|
|
128132
|
-
const payload = await
|
|
128122
|
+
const payload = await fetchTikTokSectionPayload(
|
|
128123
|
+
def,
|
|
128124
|
+
{ ...opts, start: startDate, end: endDate },
|
|
128125
|
+
config,
|
|
128126
|
+
id
|
|
128127
|
+
);
|
|
128133
128128
|
const summary2 = await writeReportAnalysisSnapshot({
|
|
128134
128129
|
snapshotDir: opts.jsonOut,
|
|
128135
128130
|
section: def.name,
|
|
128136
|
-
accountId:
|
|
128131
|
+
accountId: id,
|
|
128137
128132
|
dateRange: { start: startDate, end: endDate },
|
|
128138
128133
|
payload,
|
|
128139
128134
|
cliVersion
|
|
@@ -128141,7 +128136,7 @@ async function runAllFacebookSections(opts) {
|
|
|
128141
128136
|
return {
|
|
128142
128137
|
section: def.name,
|
|
128143
128138
|
ok: true,
|
|
128144
|
-
elapsedMs:
|
|
128139
|
+
elapsedMs: performance7.now() - t0,
|
|
128145
128140
|
file: summary2.writtenFiles[0],
|
|
128146
128141
|
endpointHint: hint
|
|
128147
128142
|
};
|
|
@@ -128149,69 +128144,118 @@ async function runAllFacebookSections(opts) {
|
|
|
128149
128144
|
return {
|
|
128150
128145
|
section: def.name,
|
|
128151
128146
|
ok: false,
|
|
128152
|
-
elapsedMs:
|
|
128147
|
+
elapsedMs: performance7.now() - t0,
|
|
128153
128148
|
error: err instanceof Error ? err.message : String(err),
|
|
128154
128149
|
endpointHint: hint
|
|
128155
128150
|
};
|
|
128156
128151
|
}
|
|
128157
128152
|
}
|
|
128158
128153
|
);
|
|
128159
|
-
const results = await
|
|
128154
|
+
const results = await runWithConcurrency4(tasks, concurrency);
|
|
128160
128155
|
const succeeded = results.filter((r) => r.ok).length;
|
|
128161
128156
|
const failed = results.length - succeeded;
|
|
128162
|
-
const absoluteSnapshotDir =
|
|
128157
|
+
const absoluteSnapshotDir = path32.resolve(opts.jsonOut);
|
|
128163
128158
|
const summary = {
|
|
128164
|
-
kind: "siluzan-tso-
|
|
128159
|
+
kind: "siluzan-tso-tiktok-analysis-snapshot-batch",
|
|
128165
128160
|
absoluteSnapshotDir,
|
|
128166
|
-
manifestFile: reportManifestFile(
|
|
128167
|
-
accountId:
|
|
128161
|
+
manifestFile: reportManifestFile(id),
|
|
128162
|
+
accountId: id,
|
|
128168
128163
|
totalSections: results.length,
|
|
128169
128164
|
succeeded,
|
|
128170
128165
|
failed,
|
|
128171
128166
|
concurrency,
|
|
128172
|
-
elapsedMs:
|
|
128167
|
+
elapsedMs: performance7.now() - overallT0,
|
|
128173
128168
|
results
|
|
128174
128169
|
};
|
|
128175
128170
|
console.log(JSON.stringify(summary));
|
|
128176
128171
|
if (failed > 0) process.exit(2);
|
|
128177
128172
|
}
|
|
128178
128173
|
|
|
128179
|
-
// src/commands/
|
|
128180
|
-
|
|
128181
|
-
|
|
128182
|
-
|
|
128183
|
-
|
|
128174
|
+
// src/commands/tiktok-analysis/register-cli.ts
|
|
128175
|
+
init_version();
|
|
128176
|
+
init_auth();
|
|
128177
|
+
import { Command as Command3 } from "commander";
|
|
128178
|
+
function registerBatchCommand3(sectionHelp, aliasHelp) {
|
|
128179
|
+
return new Command3("run").description(
|
|
128180
|
+
"\u6279\u91CF\u62C9\u53D6 TSO reporting/media-account/TikTok Section \u6570\u636E\uFF08\u7701\u7565\u5B50\u547D\u4EE4\u540D\u65F6\u9ED8\u8BA4\u6267\u884C\u672C\u547D\u4EE4\uFF09"
|
|
128181
|
+
).requiredOption("-a, --account <id>", "TikTok \u5E7F\u544A\u4E3B mediaCustomerId\uFF08\u6570\u5B57\uFF09").requiredOption(
|
|
128184
128182
|
"--json-out <dir>",
|
|
128185
128183
|
"\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
|
|
128186
128184
|
).option("-t, --token <token>", "Auth Token").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u7701\u7565=\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
|
|
128187
128185
|
"--sections <list>",
|
|
128188
|
-
`\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,
|
|
128186
|
+
`\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,campaigns,audience-merged\uFF1B\u7701\u7565=\u5168\u90E8 12 \u4E2A\u3002\u53EF\u9009\uFF1A${sectionHelp}\uFF1B\u522B\u540D\uFF1A${aliasHelp}`
|
|
128189
128187
|
).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
|
|
128190
|
-
"--
|
|
128191
|
-
"\
|
|
128188
|
+
"--take <n>",
|
|
128189
|
+
"\u6D88\u8017/\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF0C\u4E0E\u524D\u7AEF take \u4E00\u81F4\uFF1Boverview \u4E0D\u9002\u7528\uFF09",
|
|
128192
128190
|
(v) => parseInt(v, 10)
|
|
128193
128191
|
).option("--concurrency <n>", "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 16", (v) => parseInt(v, 10)).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
128194
|
-
await
|
|
128192
|
+
await runAllTikTokSections(opts);
|
|
128195
128193
|
});
|
|
128196
128194
|
}
|
|
128197
|
-
function
|
|
128198
|
-
const sectionHelp =
|
|
128199
|
-
const
|
|
128200
|
-
|
|
128201
|
-
);
|
|
128202
|
-
root.
|
|
128203
|
-
|
|
128204
|
-
"\
|
|
128205
|
-
).
|
|
128206
|
-
|
|
128207
|
-
|
|
128208
|
-
|
|
128209
|
-
|
|
128210
|
-
|
|
128211
|
-
|
|
128212
|
-
|
|
128213
|
-
|
|
128195
|
+
function registerTikTokAnalysisCommands(program2) {
|
|
128196
|
+
const sectionHelp = TIKTOK_SECTION_NAMES.join(", ");
|
|
128197
|
+
const aliasHelp = Object.keys(TIKTOK_SECTION_ALIASES).join(", ");
|
|
128198
|
+
const root = program2.command("tiktok-analysis").description("TikTok \u8D26\u6237\u5206\u6790\uFF1A\u6279\u91CF\u62C9\u53D6 Section \u6570\u636E\uFF08\u9ED8\u8BA4\uFF09\uFF0C\u6216\u67E5\u8BE2\u5730\u533A\u4EE3\u7801/\u5174\u8DA3\u7C7B\u76EE");
|
|
128199
|
+
root.addCommand(registerBatchCommand3(sectionHelp, aliasHelp), { isDefault: true });
|
|
128200
|
+
root.command("areacode").description("TikTok \u5730\u533A\u4EE3\u7801\u679A\u4E3E\uFF08\u4E3B\u5E73\u53F0 query/media-account/tiktok/TikTokAreacode/Read\uFF09").option("-t, --token <token>", "Auth Token").option(
|
|
128201
|
+
"--json-out <dir>",
|
|
128202
|
+
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
128203
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
128204
|
+
const config = loadConfig(opts.token);
|
|
128205
|
+
try {
|
|
128206
|
+
const data = await fetchTikTokAreacode(config, !!opts.verbose);
|
|
128207
|
+
if (opts.jsonOut) {
|
|
128208
|
+
const summary = await writeReportAnalysisSnapshot({
|
|
128209
|
+
snapshotDir: opts.jsonOut,
|
|
128210
|
+
section: "areacode",
|
|
128211
|
+
dateRange: null,
|
|
128212
|
+
payload: data,
|
|
128213
|
+
cliVersion: getCurrentVersion2()
|
|
128214
|
+
});
|
|
128215
|
+
console.log(JSON.stringify(summary));
|
|
128216
|
+
return;
|
|
128217
|
+
}
|
|
128218
|
+
console.log("\n\u2705 TikTok \u5730\u533A\u4EE3\u7801\u8868\uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
128219
|
+
} catch (err) {
|
|
128220
|
+
console.error(`
|
|
128221
|
+
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
128222
|
+
`);
|
|
128223
|
+
process.exit(1);
|
|
128224
|
+
}
|
|
128214
128225
|
});
|
|
128226
|
+
root.command("interest-list").description(
|
|
128227
|
+
"TikTok \u5174\u8DA3\u7C7B\u76EE\uFF08tiktokApiUrl GetInterestList\uFF0C\u89E3\u6790 country_code/interest \u62A5\u8868\u65F6\u5E38\u7528\uFF09"
|
|
128228
|
+
).requiredOption("-a, --account <id>", "TikTok mediaCustomerId").option("-t, --token <token>", "Auth Token").option(
|
|
128229
|
+
"--json-out <dir>",
|
|
128230
|
+
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
128231
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
128232
|
+
async (opts) => {
|
|
128233
|
+
const config = loadConfig(opts.token);
|
|
128234
|
+
try {
|
|
128235
|
+
const { id, data } = await fetchTikTokInterestList(config, opts.account, !!opts.verbose);
|
|
128236
|
+
if (opts.jsonOut) {
|
|
128237
|
+
const summary = await writeReportAnalysisSnapshot({
|
|
128238
|
+
snapshotDir: opts.jsonOut,
|
|
128239
|
+
section: "interest-list",
|
|
128240
|
+
accountId: id,
|
|
128241
|
+
dateRange: null,
|
|
128242
|
+
payload: data,
|
|
128243
|
+
cliVersion: getCurrentVersion2()
|
|
128244
|
+
});
|
|
128245
|
+
console.log(JSON.stringify(summary));
|
|
128246
|
+
return;
|
|
128247
|
+
}
|
|
128248
|
+
console.log(`
|
|
128249
|
+
\u2705 TikTok \u5174\u8DA3\u7C7B\u76EE ${id}\uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09
|
|
128250
|
+
`);
|
|
128251
|
+
} catch (err) {
|
|
128252
|
+
console.error(`
|
|
128253
|
+
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
128254
|
+
`);
|
|
128255
|
+
process.exit(1);
|
|
128256
|
+
}
|
|
128257
|
+
}
|
|
128258
|
+
);
|
|
128215
128259
|
}
|
|
128216
128260
|
|
|
128217
128261
|
// src/index.ts
|
|
@@ -128220,8 +128264,8 @@ init_cli_json_snapshot();
|
|
|
128220
128264
|
installProcessHandlers();
|
|
128221
128265
|
function getVersion() {
|
|
128222
128266
|
try {
|
|
128223
|
-
const __dirname3 =
|
|
128224
|
-
const pkgPath =
|
|
128267
|
+
const __dirname3 = path33.dirname(fileURLToPath7(import.meta.url));
|
|
128268
|
+
const pkgPath = path33.join(__dirname3, "..", "package.json");
|
|
128225
128269
|
const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf8"));
|
|
128226
128270
|
return pkg.version ?? "0.0.0";
|
|
128227
128271
|
} catch {
|
|
@@ -128237,7 +128281,7 @@ configureCliJsonSnapshot({
|
|
|
128237
128281
|
"references/accounts/accounts.md"
|
|
128238
128282
|
]
|
|
128239
128283
|
});
|
|
128240
|
-
var program = new
|
|
128284
|
+
var program = new Command4();
|
|
128241
128285
|
program.name("siluzan-tso").description(
|
|
128242
128286
|
"Siluzan \u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1A\u8D26\u6237\u67E5\u8BE2\u3001\u4F59\u989D\u3001\u6295\u653E\u6570\u636E\u3001\u5F00\u6237\u7533\u8BF7\uFF08Google/TikTok/Yandex/Bing/Kwai\uFF09\u3001\n\u8D26\u53F7\u5206\u4EAB/\u89E3\u7ED1\u3001\u4F18\u5316\u62A5\u544A\u3001Google \u8D26\u6237\u5206\u6790\u7F51\u5173\uFF08google-analysis\uFF09\u3001Google \u5E7F\u544A\u8BCA\u65AD\u62A5\u544A\uFF08google-ads-diagnosis render\uFF09\u3001\u7F51\u7AD9\u8BCA\u65AD\uFF08website-diagnosis\uFF09\u3001\u6218\u7565\u5E02\u573A\u5206\u6790\uFF08market-analysis\uFF0CAgent \u751F\u6210\u62A5\u544A\uFF09\u3001\u5145\u503C\u8F6C\u8D26\u3001\u5F00\u7968\u3001\u667A\u80FD\u9884\u8B66\u3001Google \u5E7F\u544A\u7BA1\u7406\uFF08\u542B\u5F02\u6B65\u6279\u91CF\uFF09\u3002"
|
|
128243
128287
|
).version(getVersion());
|
|
@@ -128271,6 +128315,8 @@ var REGISTRARS = [
|
|
|
128271
128315
|
registerGoogleAnalysisCommands,
|
|
128272
128316
|
registerGoogleAnalysisBatchCommands,
|
|
128273
128317
|
registerFacebookAnalysisCommands,
|
|
128318
|
+
registerBingAnalysisCommands,
|
|
128319
|
+
registerTikTokAnalysisCommands,
|
|
128274
128320
|
register14,
|
|
128275
128321
|
register15,
|
|
128276
128322
|
register16,
|