siluzan-tso-cli 1.1.35-beta.2 → 1.1.35-beta.4
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 +1786 -1654
- 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 +39 -4
- 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 +39 -4
- 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();
|
|
@@ -105340,12 +105346,12 @@ init_cli_json_snapshot();
|
|
|
105340
105346
|
|
|
105341
105347
|
// src/utils/list-json-envelope.ts
|
|
105342
105348
|
function wrapListJson(params) {
|
|
105343
|
-
const
|
|
105349
|
+
const itemCount2 = Array.isArray(params.items) ? params.items.length : 0;
|
|
105344
105350
|
return {
|
|
105345
105351
|
page: params.page,
|
|
105346
105352
|
pageSize: params.pageSize,
|
|
105347
105353
|
total: params.total === void 0 || params.total === null ? null : params.total,
|
|
105348
|
-
itemCount,
|
|
105354
|
+
itemCount: itemCount2,
|
|
105349
105355
|
items: params.items
|
|
105350
105356
|
};
|
|
105351
105357
|
}
|
|
@@ -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;
|
|
@@ -125502,7 +124534,7 @@ function buildGoogleAdsDiagnosisCollectAgentHint(snapDir) {
|
|
|
125502
124534
|
"Google Ads \u8BCA\u65AD collect \u5DF2\u5B8C\u6210\uFF08\u4EC5\u4E8B\u5B9E\u6570\u636E\uFF0C\u4E0D\u542B\u5EFA\u8BAE\u6027\u6587\u6848\uFF09\u3002",
|
|
125503
124535
|
`1) Read ${dir}/${GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME}\uFF08reportData + agentBrief\uFF09\uFF1B`,
|
|
125504
124536
|
"2) Read references/report-templates/google-ads-diagnosis.md\uFF0C\u7531 Agent \u64B0\u5199\u5168\u90E8 analysis/suggestions\u3001diagnosisOverview\u3001summary\u3001accountInfo.businessModel/industry\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation\u3001adCreativeOptimization.items[].suggestion \u7B49\uFF1B",
|
|
125505
|
-
`3) \u5C06 reportData \u4E0E\u53D9\u4E8B\u5B57\u6BB5\u5408\u5E76\u5199\u5165 ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME}\uFF08**\u7981\u6B62\
|
|
124537
|
+
`3) \u5C06 reportData \u4E0E\u53D9\u4E8B\u5B57\u6BB5\u5408\u5E76\u5199\u5165 ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME}\uFF08**\u7981\u6B62\u5199\u5165/\u8986\u76D6** campaigns/geographic/keywords \u7684 items \u884C\uFF0C\u53EA\u586B analysis/suggestions\uFF1Bitems \u4EE5 collect \u4E3A\u51C6\uFF0Crender \u4F1A\u81EA\u52A8\u8986\u76D6 Agent \u6539\u52A8\u7684\u5BF9\u6BD4\u8868\uFF09\uFF1B`,
|
|
125506
124538
|
`4) siluzan-tso google-ads-diagnosis render --data ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME} --out ${dir}/google-ads-diagnosis-report.html\uFF08\u9ED8\u8BA4\u540C\u76EE\u5F55 collect \u81EA\u52A8\u6062\u590D\u88AB\u6539\u574F\u7684 items\uFF09\u3002`,
|
|
125507
124539
|
"\u7981\u6B62\u5728\u672A\u7ECF\u8FC7 Agent \u64B0\u5199\u7684\u60C5\u51B5\u4E0B\u76F4\u63A5 render collect \u4EA7\u7269\uFF1B\u7F3A\u4EFB\u4E00\u5EFA\u8BAE\u5B57\u6BB5 render \u4F1A\u5931\u8D25\u3002"
|
|
125508
124540
|
].join("\n");
|
|
@@ -125555,6 +124587,20 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
|
|
|
125555
124587
|
campaigns: input.campaignHints,
|
|
125556
124588
|
keywords: input.keywordHints
|
|
125557
124589
|
},
|
|
124590
|
+
comparisonTableItemCounts: input.comparisonTableItemCounts,
|
|
124591
|
+
agentRules: {
|
|
124592
|
+
doNotModifyItemRows: [
|
|
124593
|
+
"campaigns.items",
|
|
124594
|
+
"geographic.items",
|
|
124595
|
+
"keywords.items"
|
|
124596
|
+
],
|
|
124597
|
+
renderRestoresComparisonItemsFromCollect: "render \u9ED8\u8BA4\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u4F1A\u4EE5 collect \u8986\u76D6\u4E0A\u8FF0 items\uFF08Agent \u53EA\u586B analysis/suggestions\uFF09",
|
|
124598
|
+
mayEditItemFields: [
|
|
124599
|
+
"biddingStrategy.items[].recommendationReason",
|
|
124600
|
+
"newFeatures.items[].optimizerRecommendation",
|
|
124601
|
+
"adCreativeOptimization.items[].suggestion"
|
|
124602
|
+
]
|
|
124603
|
+
},
|
|
125558
124604
|
agentDeliverable: {
|
|
125559
124605
|
outputFile: "google-ads-diagnosis.json",
|
|
125560
124606
|
structure: "\u4E0E reportData \u540C\u7ED3\u6784\u7684 sectionData\uFF1B\u987B\u586B diagnosisOverview\u3001metrics.analysis/suggestions\u3001\u5404\u6A21\u5757 analysis/suggestions\u3001summary.keyIssues\u3001summary.optimizationRoadmap[]\uFF08priority/focusArea/actionItems/expectedOutcome\uFF09\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation \u7B49",
|
|
@@ -125755,12 +124801,16 @@ function sectionRecord(env) {
|
|
|
125755
124801
|
}
|
|
125756
124802
|
|
|
125757
124803
|
// src/commands/google-ads-diagnosis/build-report-data.ts
|
|
125758
|
-
function
|
|
124804
|
+
function num(value) {
|
|
125759
124805
|
const n = Number(value);
|
|
125760
124806
|
return Number.isFinite(n) ? n : 0;
|
|
125761
124807
|
}
|
|
125762
124808
|
function rowCost(row) {
|
|
125763
|
-
return
|
|
124809
|
+
return num(row.cost ?? row.spend);
|
|
124810
|
+
}
|
|
124811
|
+
function cvrOf(row, clicks) {
|
|
124812
|
+
if (clicks > 0) return num(row.conversions) / clicks;
|
|
124813
|
+
return num(row.conversionRate ?? row.cvr);
|
|
125764
124814
|
}
|
|
125765
124815
|
function pct(value) {
|
|
125766
124816
|
return `${(value * 100).toFixed(2)}%`;
|
|
@@ -125791,11 +124841,11 @@ function asPeriodRecord(value) {
|
|
|
125791
124841
|
}
|
|
125792
124842
|
function toKeyMetricRow(title, current, previous) {
|
|
125793
124843
|
const currentCost = rowCost(current);
|
|
125794
|
-
const currentClicks =
|
|
125795
|
-
const currentCvr =
|
|
124844
|
+
const currentClicks = num(current.clicks);
|
|
124845
|
+
const currentCvr = cvrOf(current, currentClicks);
|
|
125796
124846
|
const previousCost = previous ? rowCost(previous) : null;
|
|
125797
|
-
const previousClicks = previous ?
|
|
125798
|
-
const previousCvr = previous ?
|
|
124847
|
+
const previousClicks = previous ? num(previous.clicks) : null;
|
|
124848
|
+
const previousCvr = previous ? cvrOf(previous, num(previous.clicks)) : null;
|
|
125799
124849
|
return {
|
|
125800
124850
|
title,
|
|
125801
124851
|
currentCost,
|
|
@@ -125825,13 +124875,13 @@ function buildComparisonItems(currentRows, previousRows, titleFn, keyFn) {
|
|
|
125825
124875
|
}
|
|
125826
124876
|
function toTargetingItem(row, labelKey, label) {
|
|
125827
124877
|
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 :
|
|
124878
|
+
const clicks = num(row.clicks);
|
|
124879
|
+
const impressions = num(row.impressions);
|
|
124880
|
+
const conversions = num(row.conversions);
|
|
124881
|
+
const ctr = num(row.ctr);
|
|
124882
|
+
const cvr = cvrOf(row, clicks);
|
|
124883
|
+
const cpc = num(row.averageCpc ?? row.cpc);
|
|
124884
|
+
const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
|
|
125835
124885
|
return {
|
|
125836
124886
|
[labelKey]: label,
|
|
125837
124887
|
cost,
|
|
@@ -125846,11 +124896,11 @@ function toTargetingItem(row, labelKey, label) {
|
|
|
125846
124896
|
}
|
|
125847
124897
|
function toDailyConversionItem(row) {
|
|
125848
124898
|
const cost = rowCost(row);
|
|
125849
|
-
const conversions =
|
|
124899
|
+
const conversions = num(row.conversions);
|
|
125850
124900
|
const rawDate = String(row.date ?? "");
|
|
125851
124901
|
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:
|
|
124902
|
+
const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
|
|
124903
|
+
return { date, cost, conversions, cpa, clicks: num(row.clicks), impressions: num(row.impressions) };
|
|
125854
124904
|
}
|
|
125855
124905
|
function loadCompanyNameFromSnap(snapDir) {
|
|
125856
124906
|
const file = path21.join(snapDir, "list-accounts-google.json");
|
|
@@ -125887,8 +124937,8 @@ function inferBusinessProfile(campaignRows, adRows, website) {
|
|
|
125887
124937
|
};
|
|
125888
124938
|
}
|
|
125889
124939
|
function primaryConversionLabel(rows) {
|
|
125890
|
-
const sorted = [...rows].sort((a, b) =>
|
|
125891
|
-
const top = sorted.find((r) =>
|
|
124940
|
+
const sorted = [...rows].sort((a, b) => num(b.allConversions) - num(a.allConversions));
|
|
124941
|
+
const top = sorted.find((r) => num(r.allConversions) > 0 && String(r.status) === "Enabled");
|
|
125892
124942
|
if (top) return String(top.name ?? "\u8BE2\u76D8/\u8F6C\u5316");
|
|
125893
124943
|
const enabled = rows.find((r) => String(r.status) === "Enabled");
|
|
125894
124944
|
return enabled ? String(enabled.name ?? "\u8BE2\u76D8/\u8F6C\u5316") : "\u8BE2\u76D8/\u8F6C\u5316";
|
|
@@ -125907,7 +124957,7 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125907
124957
|
const strategy = String(row.biddingStrategyTypeV2 ?? row.biddingStrategyType ?? "UNKNOWN");
|
|
125908
124958
|
const status = String(row.campaignStatus ?? row.campaignStatusV2 ?? "");
|
|
125909
124959
|
const isPaused = /paused/i.test(status);
|
|
125910
|
-
const recommended =
|
|
124960
|
+
const recommended = num(row.conversions) > 0 ? "MAXIMIZE_CONVERSIONS" : "TARGET_SPEND";
|
|
125911
124961
|
const isCorrect = policyOk && !isPaused && strategy === recommended;
|
|
125912
124962
|
return {
|
|
125913
124963
|
campaignName: String(row.campaignName ?? "\u672A\u547D\u540D\u7CFB\u5217"),
|
|
@@ -125920,16 +124970,16 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125920
124970
|
});
|
|
125921
124971
|
}
|
|
125922
124972
|
function buildStructureCounts(resourceRecord, campaignRows, keywordRows, searchRows) {
|
|
125923
|
-
const campaignCount = Math.max(
|
|
125924
|
-
const keywordCount = Math.max(
|
|
124973
|
+
const campaignCount = Math.max(num(resourceRecord?.campaignCount), campaignRows.length);
|
|
124974
|
+
const keywordCount = Math.max(num(resourceRecord?.keywordCount), keywordRows.length);
|
|
125925
124975
|
return {
|
|
125926
124976
|
campaignCount,
|
|
125927
|
-
adGroupCount:
|
|
124977
|
+
adGroupCount: num(resourceRecord?.adGroupCount),
|
|
125928
124978
|
keywordCount,
|
|
125929
|
-
adCount:
|
|
125930
|
-
extensionCount:
|
|
125931
|
-
negativeKeywordCount:
|
|
125932
|
-
countriesWithConversionsCount:
|
|
124979
|
+
adCount: num(resourceRecord?.adCount),
|
|
124980
|
+
extensionCount: num(resourceRecord?.extensionCount),
|
|
124981
|
+
negativeKeywordCount: num(resourceRecord?.negativeKeywordCount),
|
|
124982
|
+
countriesWithConversionsCount: num(resourceRecord?.countriesWithConversionsCount),
|
|
125933
124983
|
searchTermCount: searchRows.length
|
|
125934
124984
|
};
|
|
125935
124985
|
}
|
|
@@ -125943,26 +124993,26 @@ function loadWebsiteFromAds(snapDir, accountId, start, end) {
|
|
|
125943
124993
|
}
|
|
125944
124994
|
function buildKeywordShareItems(rows) {
|
|
125945
124995
|
const totalCost = rows.reduce((sum, row) => sum + rowCost(row), 0);
|
|
125946
|
-
const totalConversions = rows.reduce((sum, row) => sum +
|
|
124996
|
+
const totalConversions = rows.reduce((sum, row) => sum + num(row.conversions), 0);
|
|
125947
124997
|
return rows.map((row) => {
|
|
125948
124998
|
const cost = rowCost(row);
|
|
125949
|
-
const conversions =
|
|
124999
|
+
const conversions = num(row.conversions);
|
|
125950
125000
|
const keywordText = String(row.keywordText ?? row.keyword ?? "").trim();
|
|
125951
125001
|
return {
|
|
125952
125002
|
keyword: keywordText || "\u672A\u547D\u540D",
|
|
125953
|
-
cpc:
|
|
125003
|
+
cpc: num(row.averageCpc ?? row.cpc),
|
|
125954
125004
|
matchType: String(
|
|
125955
125005
|
row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
|
|
125956
125006
|
),
|
|
125957
|
-
impressions:
|
|
125958
|
-
clicks:
|
|
125007
|
+
impressions: num(row.impressions),
|
|
125008
|
+
clicks: num(row.clicks),
|
|
125959
125009
|
cost,
|
|
125960
125010
|
conversions,
|
|
125961
125011
|
costShare: totalCost > 0 ? cost / totalCost * 100 : 0,
|
|
125962
125012
|
conversionsShare: totalConversions > 0 ? conversions / totalConversions * 100 : 0,
|
|
125963
125013
|
cpa: conversions > 0 ? cost / conversions : 0,
|
|
125964
|
-
ctr:
|
|
125965
|
-
cvr:
|
|
125014
|
+
ctr: num(row.ctr),
|
|
125015
|
+
cvr: cvrOf(row, num(row.clicks))
|
|
125966
125016
|
};
|
|
125967
125017
|
});
|
|
125968
125018
|
}
|
|
@@ -125970,17 +125020,17 @@ function buildSearchTermItems(rows) {
|
|
|
125970
125020
|
const sorted = [...rows].sort((a, b) => rowCost(b) - rowCost(a));
|
|
125971
125021
|
return sorted.slice(0, 50).map((row) => {
|
|
125972
125022
|
const cost = rowCost(row);
|
|
125973
|
-
const conversions =
|
|
125974
|
-
const cpa = conversions > 0 ?
|
|
125023
|
+
const conversions = num(row.conversions);
|
|
125024
|
+
const cpa = conversions > 0 ? num(row.costPerConversion) || cost / conversions : void 0;
|
|
125975
125025
|
return {
|
|
125976
125026
|
searchTerm: String(row.searchTermText ?? row.searchTerm ?? "\u672A\u547D\u540D"),
|
|
125977
125027
|
keyword: String(row.keywordText ?? row.keyword ?? ""),
|
|
125978
125028
|
cost,
|
|
125979
125029
|
conversions,
|
|
125980
|
-
clicks:
|
|
125981
|
-
impressions:
|
|
125982
|
-
ctr:
|
|
125983
|
-
cvr:
|
|
125030
|
+
clicks: num(row.clicks),
|
|
125031
|
+
impressions: num(row.impressions),
|
|
125032
|
+
ctr: num(row.ctr),
|
|
125033
|
+
cvr: cvrOf(row, num(row.clicks)),
|
|
125984
125034
|
cpa
|
|
125985
125035
|
};
|
|
125986
125036
|
});
|
|
@@ -126022,18 +125072,18 @@ function buildAdCreativeItems(rows) {
|
|
|
126022
125072
|
}
|
|
126023
125073
|
function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
|
|
126024
125074
|
const current = asPeriodRecord(overviewRecord?.currentPeriod);
|
|
126025
|
-
const totalCost =
|
|
126026
|
-
const activeDays =
|
|
126027
|
-
const avgDaily =
|
|
126028
|
-
const remainingBudget =
|
|
125075
|
+
const totalCost = num(overviewRecord?.totalCost ?? current?.spend);
|
|
125076
|
+
const activeDays = num(overviewRecord?.activeDays);
|
|
125077
|
+
const avgDaily = num(overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0));
|
|
125078
|
+
const remainingBudget = num(overviewRecord?.remainingAccountBudget);
|
|
126029
125079
|
const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
|
|
126030
|
-
const sis =
|
|
125080
|
+
const sis = num(
|
|
126031
125081
|
dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
|
|
126032
125082
|
);
|
|
126033
|
-
const budgetLost =
|
|
125083
|
+
const budgetLost = num(
|
|
126034
125084
|
dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
|
|
126035
125085
|
);
|
|
126036
|
-
const rankLost =
|
|
125086
|
+
const rankLost = num(
|
|
126037
125087
|
dimensionRecord?.searchRankLostImpressionShare ?? current?.searchRankLostImpressionShare
|
|
126038
125088
|
);
|
|
126039
125089
|
return [
|
|
@@ -126119,17 +125169,17 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126119
125169
|
};
|
|
126120
125170
|
data.reportDate = end;
|
|
126121
125171
|
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 :
|
|
125172
|
+
const impressions = num(currentPeriod?.impressions);
|
|
125173
|
+
const clicks = num(currentPeriod?.clicks);
|
|
125174
|
+
const conversions = num(currentPeriod?.conversions);
|
|
125175
|
+
const ctr = num(currentPeriod?.ctr);
|
|
125176
|
+
const cvr = cvrOf(currentPeriod ?? {}, clicks);
|
|
125177
|
+
const cpa = conversions > 0 ? cost / conversions : num(currentPeriod?.costPerConversion);
|
|
126128
125178
|
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 :
|
|
125179
|
+
const prevConversions = previousPeriod ? num(previousPeriod.conversions) : 0;
|
|
125180
|
+
const prevCtr = previousPeriod ? num(previousPeriod.ctr) : 0;
|
|
125181
|
+
const prevCvr = previousPeriod ? cvrOf(previousPeriod, num(previousPeriod.clicks)) : 0;
|
|
125182
|
+
const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions : num(previousPeriod?.costPerConversion);
|
|
126133
125183
|
data.metrics = {
|
|
126134
125184
|
cost,
|
|
126135
125185
|
impressions,
|
|
@@ -126165,8 +125215,8 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126165
125215
|
items: conversionRows.map((row) => ({
|
|
126166
125216
|
name: String(row.name ?? row.eventName ?? "\u672A\u77E5\u4E8B\u4EF6"),
|
|
126167
125217
|
status: String(row.status ?? ""),
|
|
126168
|
-
allConversions:
|
|
126169
|
-
allConversionsValue:
|
|
125218
|
+
allConversions: num(row.allConversions),
|
|
125219
|
+
allConversionsValue: num(row.allConversionsValue)
|
|
126170
125220
|
}))
|
|
126171
125221
|
};
|
|
126172
125222
|
const campaignPrevious = sectionItems(
|
|
@@ -126250,16 +125300,16 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126250
125300
|
const lighthouse = opts.landingPage ?? {};
|
|
126251
125301
|
data.landingPageAnalysis = {
|
|
126252
125302
|
desktop: {
|
|
126253
|
-
score:
|
|
126254
|
-
firstContentfulPaint:
|
|
126255
|
-
firstMeaningfulPaint:
|
|
126256
|
-
speedIndex:
|
|
125303
|
+
score: num(lighthouse.desktop?.score),
|
|
125304
|
+
firstContentfulPaint: num(lighthouse.desktop?.firstContentfulPaint),
|
|
125305
|
+
firstMeaningfulPaint: num(lighthouse.desktop?.firstMeaningfulPaint),
|
|
125306
|
+
speedIndex: num(lighthouse.desktop?.speedIndex)
|
|
126257
125307
|
},
|
|
126258
125308
|
mobile: {
|
|
126259
|
-
score:
|
|
126260
|
-
firstContentfulPaint:
|
|
126261
|
-
firstMeaningfulPaint:
|
|
126262
|
-
speedIndex:
|
|
125309
|
+
score: num(lighthouse.mobile?.score),
|
|
125310
|
+
firstContentfulPaint: num(lighthouse.mobile?.firstContentfulPaint),
|
|
125311
|
+
firstMeaningfulPaint: num(lighthouse.mobile?.firstMeaningfulPaint),
|
|
125312
|
+
speedIndex: num(lighthouse.mobile?.speedIndex)
|
|
126263
125313
|
}
|
|
126264
125314
|
};
|
|
126265
125315
|
const keywordShareItems = buildKeywordShareItems(kwCurrent);
|
|
@@ -126293,7 +125343,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126293
125343
|
data.newFeatures = {
|
|
126294
125344
|
items: nfItems.map((item) => {
|
|
126295
125345
|
const strategy = String(item.strategy ?? "");
|
|
126296
|
-
const enabled = strategy === "PerformanceMax" ?
|
|
125346
|
+
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
125347
|
return {
|
|
126298
125348
|
...item,
|
|
126299
125349
|
accountStatus: enabled,
|
|
@@ -126303,7 +125353,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126303
125353
|
...emptyModuleNarrative()
|
|
126304
125354
|
};
|
|
126305
125355
|
data.summary = emptySummary();
|
|
126306
|
-
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 &&
|
|
125356
|
+
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 && num(r.conversions) === 0);
|
|
126307
125357
|
const agentBrief = buildGoogleAdsDiagnosisAgentBrief({
|
|
126308
125358
|
accountId,
|
|
126309
125359
|
period: formatPeriod(start, end),
|
|
@@ -126327,9 +125377,9 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126327
125377
|
prevCpa
|
|
126328
125378
|
},
|
|
126329
125379
|
dimension: {
|
|
126330
|
-
searchImpressionShare:
|
|
126331
|
-
searchBudgetLostImpressionShare:
|
|
126332
|
-
searchRankLostImpressionShare:
|
|
125380
|
+
searchImpressionShare: num(dimensionRecord?.searchImpressionShare),
|
|
125381
|
+
searchBudgetLostImpressionShare: num(dimensionRecord?.searchBudgetLostImpressionShare),
|
|
125382
|
+
searchRankLostImpressionShare: num(dimensionRecord?.searchRankLostImpressionShare)
|
|
126333
125383
|
},
|
|
126334
125384
|
structure: structureCounts,
|
|
126335
125385
|
goldAccount: goldRecord,
|
|
@@ -126344,11 +125394,58 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
126344
125394
|
profile.industry,
|
|
126345
125395
|
...campaignCurrent.map((r) => String(r.campaignName ?? ""))
|
|
126346
125396
|
].filter(Boolean),
|
|
126347
|
-
keywordHints: zeroConvKw.slice(0, 5).map((r) => String(r.keyword ?? ""))
|
|
125397
|
+
keywordHints: zeroConvKw.slice(0, 5).map((r) => String(r.keyword ?? "")),
|
|
125398
|
+
comparisonTableItemCounts: {
|
|
125399
|
+
campaigns: campaignItems.length,
|
|
125400
|
+
geographic: geoItems.length,
|
|
125401
|
+
keywords: kwItems.length
|
|
125402
|
+
}
|
|
126348
125403
|
});
|
|
126349
125404
|
return { reportData: data, agentBrief };
|
|
126350
125405
|
}
|
|
126351
125406
|
|
|
125407
|
+
// src/commands/google-ads-diagnosis/collect-integrity.ts
|
|
125408
|
+
function asRecord5(value) {
|
|
125409
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125410
|
+
return value;
|
|
125411
|
+
}
|
|
125412
|
+
return null;
|
|
125413
|
+
}
|
|
125414
|
+
function itemCount(block) {
|
|
125415
|
+
const items = asRecord5(block)?.items;
|
|
125416
|
+
return Array.isArray(items) ? items.length : 0;
|
|
125417
|
+
}
|
|
125418
|
+
function getCollectIntegrityWarnings(reportData) {
|
|
125419
|
+
const warnings = [];
|
|
125420
|
+
const kwItems = itemCount(reportData.keywords);
|
|
125421
|
+
const fullKwItems = itemCount(reportData.fullKeywords);
|
|
125422
|
+
const keywordCount = Number(asRecord5(reportData.structure)?.keywordCount ?? 0);
|
|
125423
|
+
if (kwItems === 0 && fullKwItems > 0) {
|
|
125424
|
+
warnings.push(
|
|
125425
|
+
`keywords.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 fullKeywords.items \u6709 ${fullKwItems} \u884C\uFF1B\u8BF7\u786E\u8BA4 collect \u662F\u5426\u6210\u529F\u62C9\u53D6\u4E0A\u4E00\u5468\u671F keywords \u5FEB\u7167\uFF08\u73AF\u6BD4\u5217\u4F9D\u8D56 prev \u5468\u671F JSON\uFF09\u3002`
|
|
125426
|
+
);
|
|
125427
|
+
} else if (kwItems === 0 && keywordCount > 0) {
|
|
125428
|
+
warnings.push(
|
|
125429
|
+
`keywords.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 structure.keywordCount=${keywordCount}\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F keywords \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
125430
|
+
);
|
|
125431
|
+
}
|
|
125432
|
+
const campaignItems = itemCount(reportData.campaigns);
|
|
125433
|
+
const campaignCount = Number(asRecord5(reportData.structure)?.campaignCount ?? 0);
|
|
125434
|
+
if (campaignItems === 0 && campaignCount > 0) {
|
|
125435
|
+
warnings.push(
|
|
125436
|
+
`campaigns.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 structure.campaignCount=${campaignCount}\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F campaigns \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
125437
|
+
);
|
|
125438
|
+
}
|
|
125439
|
+
const geoItems = itemCount(reportData.geographic);
|
|
125440
|
+
const fullGeoItems = itemCount(reportData.fullGeographic);
|
|
125441
|
+
if (geoItems === 0 && fullGeoItems > 0) {
|
|
125442
|
+
warnings.push(
|
|
125443
|
+
`geographic.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 fullGeographic.items \u6709 ${fullGeoItems} \u884C\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F geographic \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
125444
|
+
);
|
|
125445
|
+
}
|
|
125446
|
+
return warnings;
|
|
125447
|
+
}
|
|
125448
|
+
|
|
126352
125449
|
// src/commands/google-ads-diagnosis/invoke-google-analysis.ts
|
|
126353
125450
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
126354
125451
|
function invokeGoogleAnalysisCli(opts) {
|
|
@@ -126491,6 +125588,12 @@ async function runGoogleAdsDiagnosisCollect(opts) {
|
|
|
126491
125588
|
website: websiteUrl || opts.website,
|
|
126492
125589
|
landingPage
|
|
126493
125590
|
});
|
|
125591
|
+
for (const warning of getCollectIntegrityWarnings(reportData)) {
|
|
125592
|
+
console.error(`
|
|
125593
|
+
\u26A0\uFE0F collect \u5B8C\u6574\u6027\uFF1A${warning}
|
|
125594
|
+
`);
|
|
125595
|
+
degraded = true;
|
|
125596
|
+
}
|
|
126494
125597
|
const collectPayload = {
|
|
126495
125598
|
kind: "siluzan-tso-google-ads-diagnosis-collect",
|
|
126496
125599
|
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -126531,16 +125634,16 @@ import fs18 from "fs/promises";
|
|
|
126531
125634
|
import path23 from "path";
|
|
126532
125635
|
|
|
126533
125636
|
// src/commands/google-ads-diagnosis/report-content.ts
|
|
126534
|
-
function
|
|
125637
|
+
function asRecord6(value) {
|
|
126535
125638
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126536
125639
|
return value;
|
|
126537
125640
|
}
|
|
126538
125641
|
return null;
|
|
126539
125642
|
}
|
|
126540
|
-
function getNestedValue(root,
|
|
125643
|
+
function getNestedValue(root, path34) {
|
|
126541
125644
|
let cur = root;
|
|
126542
|
-
for (const key of
|
|
126543
|
-
const rec =
|
|
125645
|
+
for (const key of path34) {
|
|
125646
|
+
const rec = asRecord6(cur);
|
|
126544
125647
|
if (!rec) return void 0;
|
|
126545
125648
|
cur = rec[key];
|
|
126546
125649
|
}
|
|
@@ -126557,7 +125660,7 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
126557
125660
|
}
|
|
126558
125661
|
const warnings = [];
|
|
126559
125662
|
for (let i = 0; i < items.length; i++) {
|
|
126560
|
-
const row =
|
|
125663
|
+
const row = asRecord6(items[i]);
|
|
126561
125664
|
const name2 = String(row?.name ?? row?.metric ?? i);
|
|
126562
125665
|
if (!hasNarrativeContent(row?.strategy)) {
|
|
126563
125666
|
warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -126566,13 +125669,13 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
126566
125669
|
return warnings;
|
|
126567
125670
|
}
|
|
126568
125671
|
function validateNewFeatureRecommendations(data) {
|
|
126569
|
-
const nf =
|
|
125672
|
+
const nf = asRecord6(data.newFeatures);
|
|
126570
125673
|
if (!nf) return [];
|
|
126571
125674
|
const items = nf.items;
|
|
126572
125675
|
if (!Array.isArray(items)) return [];
|
|
126573
125676
|
const warnings = [];
|
|
126574
125677
|
for (let i = 0; i < items.length; i++) {
|
|
126575
|
-
const row =
|
|
125678
|
+
const row = asRecord6(items[i]);
|
|
126576
125679
|
const name2 = String(row?.strategyName ?? row?.feature ?? row?.strategy ?? i);
|
|
126577
125680
|
if (!hasNarrativeContent(row?.optimizerRecommendation)) {
|
|
126578
125681
|
warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -126581,13 +125684,13 @@ function validateNewFeatureRecommendations(data) {
|
|
|
126581
125684
|
return warnings;
|
|
126582
125685
|
}
|
|
126583
125686
|
function validateAdCreativeItemSuggestions(data) {
|
|
126584
|
-
const creative =
|
|
125687
|
+
const creative = asRecord6(data.adCreativeOptimization);
|
|
126585
125688
|
if (!creative) return [];
|
|
126586
125689
|
const items = creative.items;
|
|
126587
125690
|
if (!Array.isArray(items) || items.length === 0) return [];
|
|
126588
125691
|
const warnings = [];
|
|
126589
125692
|
for (let i = 0; i < items.length; i++) {
|
|
126590
|
-
const row =
|
|
125693
|
+
const row = asRecord6(items[i]);
|
|
126591
125694
|
const title = String(row?.adTitle ?? i);
|
|
126592
125695
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
126593
125696
|
warnings.push(
|
|
@@ -126602,7 +125705,7 @@ function validateGoogleAdsDiagnosisContent(data) {
|
|
|
126602
125705
|
(key) => data[key] === void 0
|
|
126603
125706
|
);
|
|
126604
125707
|
const warnings = [];
|
|
126605
|
-
const accountInfo =
|
|
125708
|
+
const accountInfo = asRecord6(data.accountInfo);
|
|
126606
125709
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
126607
125710
|
warnings.push("accountInfo \u7F3A\u5C11 companyName \u4E0E period\uFF0C\u9875\u7709\u53EF\u80FD\u4E3A\u7A7A");
|
|
126608
125711
|
}
|
|
@@ -126650,7 +125753,7 @@ var GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES = [
|
|
|
126650
125753
|
GOOGLE_ADS_DIAGNOSIS_BIDDING_STRATEGY_MODULE,
|
|
126651
125754
|
GOOGLE_ADS_DIAGNOSIS_NEW_FEATURES_MODULE
|
|
126652
125755
|
];
|
|
126653
|
-
function
|
|
125756
|
+
function asRecord7(value) {
|
|
126654
125757
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126655
125758
|
return value;
|
|
126656
125759
|
}
|
|
@@ -126661,7 +125764,7 @@ function isValidRateChange(value) {
|
|
|
126661
125764
|
return typeof value === "number" && Number.isFinite(value);
|
|
126662
125765
|
}
|
|
126663
125766
|
function isComparisonTableRowValid(row) {
|
|
126664
|
-
const rec =
|
|
125767
|
+
const rec = asRecord7(row);
|
|
126665
125768
|
if (!rec) return false;
|
|
126666
125769
|
if (!String(rec.title ?? "").trim()) return false;
|
|
126667
125770
|
if (!Number.isFinite(Number(rec.currentCost))) return false;
|
|
@@ -126673,12 +125776,12 @@ function isComparisonTableRowValid(row) {
|
|
|
126673
125776
|
return true;
|
|
126674
125777
|
}
|
|
126675
125778
|
function isNewFeatureRowValid(row) {
|
|
126676
|
-
const rec =
|
|
125779
|
+
const rec = asRecord7(row);
|
|
126677
125780
|
if (!rec) return false;
|
|
126678
125781
|
return Boolean(String(rec.strategyName ?? rec.feature ?? rec.strategy ?? "").trim());
|
|
126679
125782
|
}
|
|
126680
125783
|
function isBiddingStrategyRowValid(row) {
|
|
126681
|
-
const rec =
|
|
125784
|
+
const rec = asRecord7(row);
|
|
126682
125785
|
if (!rec) return false;
|
|
126683
125786
|
if (!String(rec.campaignName ?? "").trim()) return false;
|
|
126684
125787
|
if (!Number.isFinite(Number(rec.duration)) || Number(rec.duration) <= 0) return false;
|
|
@@ -126703,14 +125806,32 @@ function moduleItemsNeedRestore(items, validator) {
|
|
|
126703
125806
|
if (!Array.isArray(items) || items.length === 0) return false;
|
|
126704
125807
|
return items.some((row) => !validator(row));
|
|
126705
125808
|
}
|
|
125809
|
+
function isComparisonTableModule(mod) {
|
|
125810
|
+
return GOOGLE_ADS_DIAGNOSIS_COMPARISON_MODULES.includes(mod);
|
|
125811
|
+
}
|
|
125812
|
+
function comparisonItemsDifferFromCollect(finalItems, collectItems) {
|
|
125813
|
+
if (!Array.isArray(finalItems)) return true;
|
|
125814
|
+
if (finalItems.length !== collectItems.length) return true;
|
|
125815
|
+
return JSON.stringify(finalItems) !== JSON.stringify(collectItems);
|
|
125816
|
+
}
|
|
126706
125817
|
function getFactItemsErrors(data) {
|
|
126707
125818
|
const errors = [];
|
|
126708
125819
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
126709
125820
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
126710
125821
|
const hint = MODULE_ITEM_ERROR_HINT[mod];
|
|
126711
|
-
const block =
|
|
125822
|
+
const block = asRecord7(data[mod]);
|
|
126712
125823
|
const items = block?.items;
|
|
126713
|
-
if (!Array.isArray(items) || items.length === 0)
|
|
125824
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
125825
|
+
if (mod === "keywords") {
|
|
125826
|
+
const fullKw = asRecord7(data.fullKeywords)?.items;
|
|
125827
|
+
if (Array.isArray(fullKw) && fullKw.length > 0) {
|
|
125828
|
+
errors.push(
|
|
125829
|
+
"keywords.items \u4E3A\u7A7A\u4F46 fullKeywords.items \u6709\u6570\u636E\uFF1B\u8BF7\u4FDD\u7559\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u540E\u91CD\u65B0 render\uFF08\u4F1A\u81EA\u52A8\u6062\u590D\u5BF9\u6BD4\u8868\uFF09"
|
|
125830
|
+
);
|
|
125831
|
+
}
|
|
125832
|
+
}
|
|
125833
|
+
continue;
|
|
125834
|
+
}
|
|
126714
125835
|
for (let i = 0; i < items.length; i++) {
|
|
126715
125836
|
if (!validator(items[i])) {
|
|
126716
125837
|
errors.push(`${mod}.items[${i}] ${hint}`);
|
|
@@ -126719,17 +125840,47 @@ function getFactItemsErrors(data) {
|
|
|
126719
125840
|
}
|
|
126720
125841
|
return errors;
|
|
126721
125842
|
}
|
|
125843
|
+
var GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE = "landingPageAnalysis";
|
|
125844
|
+
var LANDING_PAGE_SPEED_KEYS = [
|
|
125845
|
+
"score",
|
|
125846
|
+
"firstContentfulPaint",
|
|
125847
|
+
"firstMeaningfulPaint",
|
|
125848
|
+
"speedIndex"
|
|
125849
|
+
];
|
|
125850
|
+
function isLandingPageSpeedValid(value) {
|
|
125851
|
+
const rec = asRecord7(value);
|
|
125852
|
+
if (!rec) return false;
|
|
125853
|
+
return LANDING_PAGE_SPEED_KEYS.every(
|
|
125854
|
+
(key) => typeof rec[key] === "number" && Number.isFinite(rec[key])
|
|
125855
|
+
);
|
|
125856
|
+
}
|
|
125857
|
+
function restoreLandingPageSpeedFromCollect(finalData, collectReportData) {
|
|
125858
|
+
const finalBlock = asRecord7(finalData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125859
|
+
const collectBlock = asRecord7(collectReportData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125860
|
+
if (!collectBlock) return { data: finalData, restored: false };
|
|
125861
|
+
const needsDesktop = !isLandingPageSpeedValid(finalBlock?.desktop) && isLandingPageSpeedValid(collectBlock.desktop);
|
|
125862
|
+
const needsMobile = !isLandingPageSpeedValid(finalBlock?.mobile) && isLandingPageSpeedValid(collectBlock.mobile);
|
|
125863
|
+
if (!needsDesktop && !needsMobile) return { data: finalData, restored: false };
|
|
125864
|
+
const data = { ...finalData };
|
|
125865
|
+
data[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE] = {
|
|
125866
|
+
...finalBlock ?? {},
|
|
125867
|
+
desktop: needsDesktop ? collectBlock.desktop : finalBlock?.desktop,
|
|
125868
|
+
mobile: needsMobile ? collectBlock.mobile : finalBlock?.mobile
|
|
125869
|
+
};
|
|
125870
|
+
return { data, restored: true };
|
|
125871
|
+
}
|
|
126722
125872
|
function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
126723
125873
|
const data = { ...finalData };
|
|
126724
125874
|
const restoredModules = [];
|
|
126725
125875
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
126726
125876
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
126727
|
-
const finalBlock =
|
|
126728
|
-
const collectBlock =
|
|
125877
|
+
const finalBlock = asRecord7(data[mod]);
|
|
125878
|
+
const collectBlock = asRecord7(collectReportData[mod]);
|
|
126729
125879
|
const finalItems = finalBlock?.items;
|
|
126730
125880
|
const collectItems = collectBlock?.items;
|
|
126731
|
-
if (!moduleItemsNeedRestore(finalItems, validator)) continue;
|
|
126732
125881
|
if (!Array.isArray(collectItems) || collectItems.length === 0) continue;
|
|
125882
|
+
const needsRestore = isComparisonTableModule(mod) ? comparisonItemsDifferFromCollect(finalItems, collectItems) : moduleItemsNeedRestore(finalItems, validator);
|
|
125883
|
+
if (!needsRestore) continue;
|
|
126733
125884
|
data[mod] = {
|
|
126734
125885
|
...finalBlock ?? {},
|
|
126735
125886
|
items: collectItems
|
|
@@ -126739,8 +125890,74 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
126739
125890
|
return { data, restoredModules };
|
|
126740
125891
|
}
|
|
126741
125892
|
|
|
125893
|
+
// src/commands/google-ads-diagnosis/narrative-consistency-check.ts
|
|
125894
|
+
function asRecord8(value) {
|
|
125895
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125896
|
+
return value;
|
|
125897
|
+
}
|
|
125898
|
+
return null;
|
|
125899
|
+
}
|
|
125900
|
+
function collectNarrativeTexts(data) {
|
|
125901
|
+
const texts = [];
|
|
125902
|
+
const summary = asRecord8(data.summary);
|
|
125903
|
+
if (Array.isArray(summary?.keyIssues)) {
|
|
125904
|
+
for (const item of summary.keyIssues) texts.push(String(item ?? ""));
|
|
125905
|
+
}
|
|
125906
|
+
const overview = asRecord8(data.diagnosisOverview);
|
|
125907
|
+
if (Array.isArray(overview?.disadvantages)) {
|
|
125908
|
+
for (const item of overview.disadvantages) {
|
|
125909
|
+
const rec = asRecord8(item);
|
|
125910
|
+
if (rec) {
|
|
125911
|
+
texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
125912
|
+
} else {
|
|
125913
|
+
texts.push(String(item ?? ""));
|
|
125914
|
+
}
|
|
125915
|
+
}
|
|
125916
|
+
}
|
|
125917
|
+
return texts.filter((t) => t.trim().length > 0);
|
|
125918
|
+
}
|
|
125919
|
+
function newFeatureAccountStatus(data, strategy) {
|
|
125920
|
+
const nf = asRecord8(data.newFeatures);
|
|
125921
|
+
const items = nf?.items;
|
|
125922
|
+
if (!Array.isArray(items)) return null;
|
|
125923
|
+
const row = items.find((it) => asRecord8(it)?.strategy === strategy);
|
|
125924
|
+
const rec = asRecord8(row);
|
|
125925
|
+
if (!rec || typeof rec.accountStatus !== "boolean") return null;
|
|
125926
|
+
return rec.accountStatus;
|
|
125927
|
+
}
|
|
125928
|
+
function goldAccountFlag(data, key) {
|
|
125929
|
+
const gold = asRecord8(data.goldAccount);
|
|
125930
|
+
const value = gold?.[key];
|
|
125931
|
+
return typeof value === "boolean" ? value : null;
|
|
125932
|
+
}
|
|
125933
|
+
var NARRATIVE_CONSISTENCY_RULES = [
|
|
125934
|
+
{
|
|
125935
|
+
id: "keyword-strategy-missing",
|
|
125936
|
+
textPattern: /关键词.{0,8}(完全缺失|策略缺失|从未投放|从未做过|一直没有|尚未开启)/,
|
|
125937
|
+
isContradicted: (data) => newFeatureAccountStatus(data, "KwMatch") === true,
|
|
125938
|
+
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'
|
|
125939
|
+
},
|
|
125940
|
+
{
|
|
125941
|
+
id: "conversion-tracking-incomplete",
|
|
125942
|
+
textPattern: /转化跟踪.{0,8}(不完整|不完善|未完善|不到位|缺失)/,
|
|
125943
|
+
isContradicted: (data) => goldAccountFlag(data, "conversionTrackingQualified") === true,
|
|
125944
|
+
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"
|
|
125945
|
+
}
|
|
125946
|
+
];
|
|
125947
|
+
function getNarrativeConsistencyErrors(data) {
|
|
125948
|
+
const texts = collectNarrativeTexts(data);
|
|
125949
|
+
const errors = [];
|
|
125950
|
+
for (const rule of NARRATIVE_CONSISTENCY_RULES) {
|
|
125951
|
+
const hitText = texts.find((t) => rule.textPattern.test(t));
|
|
125952
|
+
if (hitText && rule.isContradicted(data)) {
|
|
125953
|
+
errors.push(`\u53D9\u4E8B\u77DB\u76FE\uFF08${rule.id}\uFF09\uFF1A\u300C${hitText}\u300D\u2014\u2014 ${rule.hint}`);
|
|
125954
|
+
}
|
|
125955
|
+
}
|
|
125956
|
+
return errors;
|
|
125957
|
+
}
|
|
125958
|
+
|
|
126742
125959
|
// src/commands/google-ads-diagnosis/render-report.ts
|
|
126743
|
-
function
|
|
125960
|
+
function asRecord9(value) {
|
|
126744
125961
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126745
125962
|
return value;
|
|
126746
125963
|
}
|
|
@@ -126749,8 +125966,8 @@ function asRecord7(value) {
|
|
|
126749
125966
|
async function tryLoadCollectReportData(collectPath) {
|
|
126750
125967
|
try {
|
|
126751
125968
|
const raw = JSON.parse(await fs18.readFile(collectPath, "utf8"));
|
|
126752
|
-
const root =
|
|
126753
|
-
const reportData =
|
|
125969
|
+
const root = asRecord9(raw);
|
|
125970
|
+
const reportData = asRecord9(root?.reportData);
|
|
126754
125971
|
return reportData ?? root;
|
|
126755
125972
|
} catch {
|
|
126756
125973
|
return null;
|
|
@@ -126767,7 +125984,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126767
125984
|
`);
|
|
126768
125985
|
process.exit(1);
|
|
126769
125986
|
}
|
|
126770
|
-
let data =
|
|
125987
|
+
let data = asRecord9(dataRaw);
|
|
126771
125988
|
if (!opts.noMergeCollect) {
|
|
126772
125989
|
const collectPath = path23.resolve(
|
|
126773
125990
|
opts.collectFile ?? path23.join(path23.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -126784,11 +126001,32 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126784
126001
|
`
|
|
126785
126002
|
\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
126003
|
\u6765\u6E90\uFF1A${collectPath}
|
|
126004
|
+
`
|
|
126005
|
+
);
|
|
126006
|
+
}
|
|
126007
|
+
const { data: withLandingPage, restored: landingPageRestored } = restoreLandingPageSpeedFromCollect(data, collectReportData);
|
|
126008
|
+
data = withLandingPage;
|
|
126009
|
+
if (landingPageRestored) {
|
|
126010
|
+
console.log(
|
|
126011
|
+
`
|
|
126012
|
+
\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
|
|
126013
|
+
\u6765\u6E90\uFF1A${collectPath}
|
|
126787
126014
|
`
|
|
126788
126015
|
);
|
|
126789
126016
|
}
|
|
126790
126017
|
}
|
|
126791
126018
|
}
|
|
126019
|
+
const narrativeErrors = getNarrativeConsistencyErrors(data);
|
|
126020
|
+
if (narrativeErrors.length > 0) {
|
|
126021
|
+
console.error("\n\u274C \u53D9\u4E8B\u6587\u6848\u4E0E\u4E8B\u5B9E\u5B57\u6BB5\u77DB\u76FE\uFF0C\u7981\u6B62\u6E32\u67D3\uFF1A\n");
|
|
126022
|
+
for (const err of narrativeErrors) {
|
|
126023
|
+
console.error(` \xB7 ${err}`);
|
|
126024
|
+
}
|
|
126025
|
+
console.error(
|
|
126026
|
+
"\n \u8BF7\u4FEE\u6B63 summary.keyIssues / diagnosisOverview.disadvantages \u540E\u91CD\u65B0 render\u3002\n"
|
|
126027
|
+
);
|
|
126028
|
+
process.exit(1);
|
|
126029
|
+
}
|
|
126792
126030
|
const comparisonErrors = getFactItemsErrors(data);
|
|
126793
126031
|
if (comparisonErrors.length > 0) {
|
|
126794
126032
|
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 +126091,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126853
126091
|
`);
|
|
126854
126092
|
}
|
|
126855
126093
|
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\
|
|
126094
|
+
"\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
126095
|
);
|
|
126858
126096
|
}
|
|
126859
126097
|
|
|
@@ -126983,7 +126221,7 @@ var PHASE_LABEL = {
|
|
|
126983
126221
|
"find-winner": "\u627E\u8D62\u5BB6",
|
|
126984
126222
|
scale: "\u653E\u91CF"
|
|
126985
126223
|
};
|
|
126986
|
-
function
|
|
126224
|
+
function num2(value) {
|
|
126987
126225
|
const n = Number(value);
|
|
126988
126226
|
return Number.isFinite(n) ? n : void 0;
|
|
126989
126227
|
}
|
|
@@ -126994,35 +126232,35 @@ function money(value, currency = "USD") {
|
|
|
126994
126232
|
}
|
|
126995
126233
|
function adSetsWithSpend(tables) {
|
|
126996
126234
|
const rows = Array.isArray(tables?.adSets) ? tables.adSets : [];
|
|
126997
|
-
return rows.filter((r) => (
|
|
126235
|
+
return rows.filter((r) => (num2(r.spend) ?? 0) > 0);
|
|
126998
126236
|
}
|
|
126999
126237
|
function topAdSetConcentration(adSets) {
|
|
127000
126238
|
if (!adSets.length) return null;
|
|
127001
|
-
const totalSpend = adSets.reduce((s, r) => s + (
|
|
127002
|
-
const totalResults = adSets.reduce((s, r) => s + (
|
|
126239
|
+
const totalSpend = adSets.reduce((s, r) => s + (num2(r.spend) ?? 0), 0);
|
|
126240
|
+
const totalResults = adSets.reduce((s, r) => s + (num2(r.results) ?? 0), 0);
|
|
127003
126241
|
let best = adSets[0];
|
|
127004
|
-
let bestSpend =
|
|
126242
|
+
let bestSpend = num2(best.spend) ?? 0;
|
|
127005
126243
|
for (const r of adSets) {
|
|
127006
|
-
const sp =
|
|
126244
|
+
const sp = num2(r.spend) ?? 0;
|
|
127007
126245
|
if (sp > bestSpend) {
|
|
127008
126246
|
best = r;
|
|
127009
126247
|
bestSpend = sp;
|
|
127010
126248
|
}
|
|
127011
126249
|
}
|
|
127012
|
-
const results =
|
|
126250
|
+
const results = num2(best.results) ?? 0;
|
|
127013
126251
|
return {
|
|
127014
126252
|
name: String(best.adGroupName ?? best.name ?? "\u2014"),
|
|
127015
126253
|
spendShare: totalSpend > 0 ? bestSpend / totalSpend : 0,
|
|
127016
126254
|
resultsShare: totalResults > 0 ? results / totalResults : 0,
|
|
127017
|
-
cpl:
|
|
126255
|
+
cpl: num2(best.costPerResult)
|
|
127018
126256
|
};
|
|
127019
126257
|
}
|
|
127020
126258
|
function winnerSpendShare(adSets, avgCpl, totalSpend) {
|
|
127021
126259
|
if (totalSpend <= 0 || avgCpl <= 0) return 0;
|
|
127022
126260
|
const winnerSpend = adSets.filter((r) => {
|
|
127023
|
-
const cpl =
|
|
126261
|
+
const cpl = num2(r.costPerResult);
|
|
127024
126262
|
return cpl != null && cpl <= avgCpl * 0.9;
|
|
127025
|
-
}).reduce((s, r) => s + (
|
|
126263
|
+
}).reduce((s, r) => s + (num2(r.spend) ?? 0), 0);
|
|
127026
126264
|
return winnerSpend / totalSpend;
|
|
127027
126265
|
}
|
|
127028
126266
|
function countActiveDimensions(charts, tables) {
|
|
@@ -127039,7 +126277,7 @@ function asArray(value) {
|
|
|
127039
126277
|
}
|
|
127040
126278
|
function inferLifecyclePhase(input) {
|
|
127041
126279
|
const { avgCpl, currency = "USD" } = input;
|
|
127042
|
-
const spend =
|
|
126280
|
+
const spend = num2(input.spend) ?? 0;
|
|
127043
126281
|
const adSets = adSetsWithSpend(input.tables);
|
|
127044
126282
|
const activeDims = countActiveDimensions(input.charts, input.tables);
|
|
127045
126283
|
const winShare = winnerSpendShare(adSets, avgCpl, spend);
|
|
@@ -127098,7 +126336,7 @@ function ensureLifecycleFromSnapshot(healthDiagnosis, merged) {
|
|
|
127098
126336
|
}
|
|
127099
126337
|
|
|
127100
126338
|
// src/commands/facebook-analysis/scorecard-autofill.ts
|
|
127101
|
-
function
|
|
126339
|
+
function num3(value) {
|
|
127102
126340
|
const n = Number(value);
|
|
127103
126341
|
return Number.isFinite(n) ? n : void 0;
|
|
127104
126342
|
}
|
|
@@ -127138,8 +126376,8 @@ function buildScorecardFromMergedData(input) {
|
|
|
127138
126376
|
if (pLabels.length) {
|
|
127139
126377
|
const indexed = pLabels.map((label, i) => ({
|
|
127140
126378
|
label: String(label),
|
|
127141
|
-
cpl:
|
|
127142
|
-
spend:
|
|
126379
|
+
cpl: num3(pCpl[i]),
|
|
126380
|
+
spend: num3(pSpend[i]) ?? 0
|
|
127143
126381
|
})).filter((x) => x.spend > 0 || x.cpl != null && x.cpl > 0);
|
|
127144
126382
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? Infinity) - (b.cpl ?? Infinity));
|
|
127145
126383
|
if (sorted[0]) {
|
|
@@ -127164,7 +126402,7 @@ function buildScorecardFromMergedData(input) {
|
|
|
127164
126402
|
const cLabels = Array.isArray(country?.labels) ? country.labels : [];
|
|
127165
126403
|
const cCpl = Array.isArray(country?.cpl) ? country.cpl : [];
|
|
127166
126404
|
if (cLabels.length) {
|
|
127167
|
-
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl:
|
|
126405
|
+
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl: num3(cCpl[i]) })).filter((x) => x.cpl != null && x.cpl > 0);
|
|
127168
126406
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? 0) - (b.cpl ?? 0));
|
|
127169
126407
|
if (sorted[0]) {
|
|
127170
126408
|
const s = cplSignal(sorted[0].cpl, avgCpl);
|
|
@@ -127185,24 +126423,24 @@ function buildScorecardFromMergedData(input) {
|
|
|
127185
126423
|
}
|
|
127186
126424
|
}
|
|
127187
126425
|
const adSets = Array.isArray(tables.adSets) ? tables.adSets : [];
|
|
127188
|
-
const adWithSpend = adSets.filter((g) => (
|
|
126426
|
+
const adWithSpend = adSets.filter((g) => (num3(g.spend) ?? 0) > 0);
|
|
127189
126427
|
if (adWithSpend.length) {
|
|
127190
126428
|
const sorted = [...adWithSpend].sort(
|
|
127191
|
-
(a, b) => (
|
|
126429
|
+
(a, b) => (num3(a.costPerResult) ?? Infinity) - (num3(b.costPerResult) ?? Infinity)
|
|
127192
126430
|
);
|
|
127193
126431
|
const best = sorted[0];
|
|
127194
|
-
const sBest = cplSignal(
|
|
126432
|
+
const sBest = cplSignal(num3(best.costPerResult), avgCpl);
|
|
127195
126433
|
pushRow(rows, seen, {
|
|
127196
126434
|
item: String(best.name ?? "\u5E7F\u544A\u7EC4"),
|
|
127197
|
-
data: `CPL ${money2(
|
|
126435
|
+
data: `CPL ${money2(num3(best.costPerResult), currency)}`,
|
|
127198
126436
|
...sBest
|
|
127199
126437
|
});
|
|
127200
126438
|
if (sorted.length > 1) {
|
|
127201
126439
|
const worst = sorted[sorted.length - 1];
|
|
127202
|
-
const sWorst = cplSignal(
|
|
126440
|
+
const sWorst = cplSignal(num3(worst.costPerResult), avgCpl);
|
|
127203
126441
|
pushRow(rows, seen, {
|
|
127204
126442
|
item: String(worst.name ?? "\u5E7F\u544A\u7EC4"),
|
|
127205
|
-
data: `CPL ${money2(
|
|
126443
|
+
data: `CPL ${money2(num3(worst.costPerResult), currency)}`,
|
|
127206
126444
|
...sWorst
|
|
127207
126445
|
});
|
|
127208
126446
|
}
|
|
@@ -127210,18 +126448,18 @@ function buildScorecardFromMergedData(input) {
|
|
|
127210
126448
|
const topAud = Array.isArray(tables.audienceTop) ? tables.audienceTop : [];
|
|
127211
126449
|
const bottomAud = Array.isArray(tables.audienceBottom) ? tables.audienceBottom : [];
|
|
127212
126450
|
if (topAud[0]) {
|
|
127213
|
-
const s = cplSignal(
|
|
126451
|
+
const s = cplSignal(num3(topAud[0].costPerResult), avgCpl);
|
|
127214
126452
|
pushRow(rows, seen, {
|
|
127215
126453
|
item: `\u53D7\u4F17\xB7${String(topAud[0].label ?? "Top")}`,
|
|
127216
|
-
data: `CPL ${money2(
|
|
126454
|
+
data: `CPL ${money2(num3(topAud[0].costPerResult), currency)}`,
|
|
127217
126455
|
...s
|
|
127218
126456
|
});
|
|
127219
126457
|
}
|
|
127220
126458
|
if (bottomAud[0]) {
|
|
127221
|
-
const s = cplSignal(
|
|
126459
|
+
const s = cplSignal(num3(bottomAud[0].costPerResult), avgCpl);
|
|
127222
126460
|
pushRow(rows, seen, {
|
|
127223
126461
|
item: `\u53D7\u4F17\xB7${String(bottomAud[0].label ?? "Bottom")}`,
|
|
127224
|
-
data: `CPL ${money2(
|
|
126462
|
+
data: `CPL ${money2(num3(bottomAud[0].costPerResult), currency)}`,
|
|
127225
126463
|
...s
|
|
127226
126464
|
});
|
|
127227
126465
|
}
|
|
@@ -127259,13 +126497,13 @@ function ensureScorecardFromSnapshot(healthDiagnosis, merged) {
|
|
|
127259
126497
|
}
|
|
127260
126498
|
|
|
127261
126499
|
// src/commands/facebook-analysis/merge-snapshot.ts
|
|
127262
|
-
function
|
|
126500
|
+
function asRecord10(value) {
|
|
127263
126501
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127264
126502
|
return value;
|
|
127265
126503
|
}
|
|
127266
126504
|
return null;
|
|
127267
126505
|
}
|
|
127268
|
-
function
|
|
126506
|
+
function num4(value) {
|
|
127269
126507
|
const n = Number(value);
|
|
127270
126508
|
return Number.isFinite(n) ? n : void 0;
|
|
127271
126509
|
}
|
|
@@ -127308,7 +126546,7 @@ async function readManifest(snapshotDir) {
|
|
|
127308
126546
|
async function loadSectionFile(snapshotDir, file) {
|
|
127309
126547
|
try {
|
|
127310
126548
|
const raw = await fs21.readFile(path28.join(snapshotDir, file), "utf8");
|
|
127311
|
-
return
|
|
126549
|
+
return asRecord10(JSON.parse(raw));
|
|
127312
126550
|
} catch {
|
|
127313
126551
|
return null;
|
|
127314
126552
|
}
|
|
@@ -127319,8 +126557,8 @@ function aggregatePlatform(networks) {
|
|
|
127319
126557
|
const platform = String(row.publisherPlatform ?? row.network ?? "unknown");
|
|
127320
126558
|
const prev = map.get(platform) ?? { spend: 0, results: 0 };
|
|
127321
126559
|
map.set(platform, {
|
|
127322
|
-
spend: prev.spend + (
|
|
127323
|
-
results: prev.results + (
|
|
126560
|
+
spend: prev.spend + (num4(row.spend) ?? 0),
|
|
126561
|
+
results: prev.results + (num4(row.results) ?? 0)
|
|
127324
126562
|
});
|
|
127325
126563
|
}
|
|
127326
126564
|
const labels = [];
|
|
@@ -127336,10 +126574,10 @@ function aggregatePlatform(networks) {
|
|
|
127336
126574
|
function buildAudienceRows(audiences) {
|
|
127337
126575
|
return audiences.map((a) => ({
|
|
127338
126576
|
label: audienceLabel(a.age, a.gender),
|
|
127339
|
-
spend:
|
|
127340
|
-
results:
|
|
127341
|
-
costPerResult:
|
|
127342
|
-
frequency:
|
|
126577
|
+
spend: num4(a.spend) ?? 0,
|
|
126578
|
+
results: num4(a.results) ?? 0,
|
|
126579
|
+
costPerResult: num4(a.costPerResult) ?? (num4(a.results) ? (num4(a.spend) ?? 0) / (num4(a.results) ?? 1) : 0),
|
|
126580
|
+
frequency: num4(a.frequency)
|
|
127343
126581
|
})).filter((r) => r.spend > 0 || r.results > 0);
|
|
127344
126582
|
}
|
|
127345
126583
|
async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
@@ -127356,7 +126594,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127356
126594
|
if (data) sectionMap.set(art.section, data);
|
|
127357
126595
|
}
|
|
127358
126596
|
const overview = sectionMap.get("overview");
|
|
127359
|
-
const current =
|
|
126597
|
+
const current = asRecord10(overview?.currentPeriod);
|
|
127360
126598
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
127361
126599
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
127362
126600
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -127378,7 +126616,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127378
126616
|
}
|
|
127379
126617
|
if (!meta.resultType && current?.resultType) meta.resultType = String(current.resultType);
|
|
127380
126618
|
const charts = { ...payload.charts ?? {} };
|
|
127381
|
-
const avgCpl =
|
|
126619
|
+
const avgCpl = num4(kpis.costPerResult) ?? 0;
|
|
127382
126620
|
if (!charts.platform) {
|
|
127383
126621
|
const platform = sectionMap.get("platform");
|
|
127384
126622
|
const networks = Array.isArray(platform?.networks) ? platform.networks : [];
|
|
@@ -127388,10 +126626,10 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127388
126626
|
const country = sectionMap.get("country");
|
|
127389
126627
|
const countries = Array.isArray(country?.countries) ? country.countries : [];
|
|
127390
126628
|
if (countries.length) {
|
|
127391
|
-
const sorted = [...countries].sort((a, b) => (
|
|
126629
|
+
const sorted = [...countries].sort((a, b) => (num4(b.spend) ?? 0) - (num4(a.spend) ?? 0));
|
|
127392
126630
|
charts.country = {
|
|
127393
126631
|
labels: sorted.map((c) => String(c.countryOrRegion ?? "\u2014")),
|
|
127394
|
-
cpl: sorted.map((c) =>
|
|
126632
|
+
cpl: sorted.map((c) => num4(c.costPerResult) ?? 0)
|
|
127395
126633
|
};
|
|
127396
126634
|
}
|
|
127397
126635
|
}
|
|
@@ -127407,7 +126645,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127407
126645
|
};
|
|
127408
126646
|
}
|
|
127409
126647
|
}
|
|
127410
|
-
if (!charts.funnel &&
|
|
126648
|
+
if (!charts.funnel && num4(kpis.reach) != null) {
|
|
127411
126649
|
charts.funnel = { reach: kpis.reach, results: kpis.results ?? 0 };
|
|
127412
126650
|
}
|
|
127413
126651
|
const tables = { ...payload.tables ?? {} };
|
|
@@ -127416,14 +126654,14 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127416
126654
|
const groups = Array.isArray(adSets?.adGroups) ? adSets.adGroups : [];
|
|
127417
126655
|
if (groups.length) {
|
|
127418
126656
|
tables.adSets = groups.map((g) => {
|
|
127419
|
-
const cpl =
|
|
127420
|
-
const freq =
|
|
126657
|
+
const cpl = num4(g.costPerResult);
|
|
126658
|
+
const freq = num4(g.frequency);
|
|
127421
126659
|
const fatigue = fatigueFromFrequency(freq);
|
|
127422
126660
|
const status = statusFromCpl(cpl, avgCpl);
|
|
127423
126661
|
return {
|
|
127424
126662
|
name: String(g.adGroupName ?? g.campaignName ?? "\u2014"),
|
|
127425
|
-
spend:
|
|
127426
|
-
results:
|
|
126663
|
+
spend: num4(g.spend) ?? 0,
|
|
126664
|
+
results: num4(g.results) ?? 0,
|
|
127427
126665
|
costPerResult: cpl ?? 0,
|
|
127428
126666
|
frequency: freq,
|
|
127429
126667
|
fatigueLevel: fatigue.level,
|
|
@@ -127447,7 +126685,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
127447
126685
|
}
|
|
127448
126686
|
const currency = typeof meta.currency === "string" ? meta.currency : "USD";
|
|
127449
126687
|
const mergeCtx = {
|
|
127450
|
-
spend:
|
|
126688
|
+
spend: num4(kpis.spend),
|
|
127451
126689
|
avgCpl,
|
|
127452
126690
|
currency,
|
|
127453
126691
|
charts,
|
|
@@ -127478,7 +126716,7 @@ function isFiniteNumber(value) {
|
|
|
127478
126716
|
function asArray2(value) {
|
|
127479
126717
|
return Array.isArray(value) ? value : [];
|
|
127480
126718
|
}
|
|
127481
|
-
function
|
|
126719
|
+
function asRecord11(value) {
|
|
127482
126720
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127483
126721
|
return value;
|
|
127484
126722
|
}
|
|
@@ -127490,26 +126728,26 @@ function pushMissing(missing, id, chapter, dimension, hint) {
|
|
|
127490
126728
|
function countAdSetsWithSpend(tables) {
|
|
127491
126729
|
const rows = asArray2(tables?.adSets);
|
|
127492
126730
|
return rows.filter((row) => {
|
|
127493
|
-
const r =
|
|
126731
|
+
const r = asRecord11(row);
|
|
127494
126732
|
const spend = r?.spend;
|
|
127495
126733
|
return isFiniteNumber(spend) && spend > 0;
|
|
127496
126734
|
}).length;
|
|
127497
126735
|
}
|
|
127498
126736
|
function chartLabels(chart) {
|
|
127499
|
-
const c =
|
|
126737
|
+
const c = asRecord11(chart);
|
|
127500
126738
|
return asArray2(c?.labels).length;
|
|
127501
126739
|
}
|
|
127502
126740
|
function validateMetaPeriodReportContent(data) {
|
|
127503
126741
|
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 =
|
|
126742
|
+
const meta = asRecord11(data.meta) ?? {};
|
|
126743
|
+
const kpis = asRecord11(data.kpis) ?? {};
|
|
126744
|
+
const narrative = asRecord11(data.narrative) ?? {};
|
|
126745
|
+
const tables = asRecord11(data.tables);
|
|
126746
|
+
const charts = asRecord11(data.charts);
|
|
126747
|
+
const sections = asRecord11(data.sections);
|
|
126748
|
+
const health = asRecord11(data.healthDiagnosis);
|
|
126749
|
+
const priorityPlan = asRecord11(data.priorityPlan);
|
|
126750
|
+
const actionChecklist = asRecord11(data.actionChecklist);
|
|
127513
126751
|
const adSetsWithSpend2 = countAdSetsWithSpend(tables ?? void 0);
|
|
127514
126752
|
const regionalNarratives = asArray2(narrative.regional).length;
|
|
127515
126753
|
const recommendations = asArray2(narrative.recommendations);
|
|
@@ -127581,7 +126819,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127581
126819
|
"charts.audience.labels \u81F3\u5C11 1 \u9879"
|
|
127582
126820
|
);
|
|
127583
126821
|
}
|
|
127584
|
-
const funnel =
|
|
126822
|
+
const funnel = asRecord11(charts?.funnel);
|
|
127585
126823
|
if (!isFiniteNumber(funnel?.reach)) {
|
|
127586
126824
|
pushMissing(
|
|
127587
126825
|
missing,
|
|
@@ -127645,7 +126883,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127645
126883
|
);
|
|
127646
126884
|
} else {
|
|
127647
126885
|
for (let i = 0; i < regionalNarratives; i++) {
|
|
127648
|
-
const row =
|
|
126886
|
+
const row = asRecord11(asArray2(narrative.regional)[i]);
|
|
127649
126887
|
if (textLen2(row?.text) < 80) {
|
|
127650
126888
|
pushMissing(
|
|
127651
126889
|
missing,
|
|
@@ -127675,7 +126913,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127675
126913
|
"\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
126914
|
);
|
|
127677
126915
|
} else {
|
|
127678
|
-
const titles = recommendations.map((r) =>
|
|
126916
|
+
const titles = recommendations.map((r) => asRecord11(r)?.title);
|
|
127679
126917
|
for (const title of RECOMMENDATION_TITLES) {
|
|
127680
126918
|
if (!titles.includes(title)) {
|
|
127681
126919
|
pushMissing(
|
|
@@ -127688,7 +126926,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127688
126926
|
}
|
|
127689
126927
|
}
|
|
127690
126928
|
for (let i = 0; i < recommendations.length; i++) {
|
|
127691
|
-
const rec =
|
|
126929
|
+
const rec = asRecord11(recommendations[i]);
|
|
127692
126930
|
if (textLen2(rec?.content) < 150) {
|
|
127693
126931
|
pushMissing(
|
|
127694
126932
|
missing,
|
|
@@ -127710,7 +126948,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127710
126948
|
);
|
|
127711
126949
|
} else {
|
|
127712
126950
|
for (let i = 0; i < supplementary.length; i++) {
|
|
127713
|
-
const row =
|
|
126951
|
+
const row = asRecord11(supplementary[i]);
|
|
127714
126952
|
if (!textLen2(row?.dimension) || !textLen2(row?.issue)) {
|
|
127715
126953
|
pushMissing(
|
|
127716
126954
|
missing,
|
|
@@ -127792,7 +127030,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127792
127030
|
);
|
|
127793
127031
|
} else {
|
|
127794
127032
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
127795
|
-
const q =
|
|
127033
|
+
const q = asRecord11(fourQuestions[i]);
|
|
127796
127034
|
const evidence = asArray2(q?.evidence).filter((e) => textLen2(e) > 0);
|
|
127797
127035
|
if (evidence.length < 2) {
|
|
127798
127036
|
pushMissing(
|
|
@@ -127816,7 +127054,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127816
127054
|
}
|
|
127817
127055
|
let scorecardValidRows = 0;
|
|
127818
127056
|
for (let i = 0; i < scorecard.length; i++) {
|
|
127819
|
-
const row =
|
|
127057
|
+
const row = asRecord11(scorecard[i]);
|
|
127820
127058
|
const itemOk = textLen2(row?.item) > 0;
|
|
127821
127059
|
const dataOk = textLen2(row?.data) > 0;
|
|
127822
127060
|
const signalOk = SCORECARD_SIGNALS.includes(row?.signal);
|
|
@@ -127858,7 +127096,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127858
127096
|
);
|
|
127859
127097
|
}
|
|
127860
127098
|
for (const key of ["platform", "country", "adSets"]) {
|
|
127861
|
-
const insight = textLen2(
|
|
127099
|
+
const insight = textLen2(asRecord11(sections?.[key])?.insight);
|
|
127862
127100
|
if (insight < 200) {
|
|
127863
127101
|
pushMissing(
|
|
127864
127102
|
missing,
|
|
@@ -127869,7 +127107,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127869
127107
|
);
|
|
127870
127108
|
}
|
|
127871
127109
|
}
|
|
127872
|
-
const audience =
|
|
127110
|
+
const audience = asRecord11(sections?.audience);
|
|
127873
127111
|
if (asArray2(audience?.goldenProfile).filter((s) => textLen2(s) > 0).length < 3) {
|
|
127874
127112
|
pushMissing(
|
|
127875
127113
|
missing,
|
|
@@ -127897,7 +127135,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127897
127135
|
"\u2265150 \u5B57"
|
|
127898
127136
|
);
|
|
127899
127137
|
}
|
|
127900
|
-
const landingRows = asArray2(
|
|
127138
|
+
const landingRows = asArray2(asRecord11(sections?.landingPage)?.rows);
|
|
127901
127139
|
if (landingRows.length < 3) {
|
|
127902
127140
|
pushMissing(
|
|
127903
127141
|
missing,
|
|
@@ -127966,133 +127204,971 @@ function resolveSkillTemplatePath3(basename11) {
|
|
|
127966
127204
|
for (const p of candidates) {
|
|
127967
127205
|
if (fs22.existsSync(p)) return p;
|
|
127968
127206
|
}
|
|
127969
|
-
return candidates[0];
|
|
127207
|
+
return candidates[0];
|
|
127208
|
+
}
|
|
127209
|
+
function metaPeriodReportTemplatePath() {
|
|
127210
|
+
return resolveSkillTemplatePath3(TEMPLATE_BASENAMES2.html);
|
|
127211
|
+
}
|
|
127212
|
+
function metaPeriodReportRuntimePath() {
|
|
127213
|
+
return resolveSkillTemplatePath3(TEMPLATE_BASENAMES2.runtime);
|
|
127214
|
+
}
|
|
127215
|
+
async function readJsonFile2(filePath) {
|
|
127216
|
+
const text = await fsPromises2.readFile(filePath, "utf8");
|
|
127217
|
+
try {
|
|
127218
|
+
return JSON.parse(text);
|
|
127219
|
+
} catch {
|
|
127220
|
+
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
127221
|
+
}
|
|
127222
|
+
}
|
|
127223
|
+
function asRecord12(value) {
|
|
127224
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127225
|
+
return value;
|
|
127226
|
+
}
|
|
127227
|
+
throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
127228
|
+
}
|
|
127229
|
+
function injectReportData2(html, payload) {
|
|
127230
|
+
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
127231
|
+
if (!html.includes(INJECT_MARKER)) {
|
|
127232
|
+
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 meta-period-report.html");
|
|
127233
|
+
}
|
|
127234
|
+
return html.replace(INJECT_MARKER, `window.__META_PERIOD_REPORT__ = ${json};`);
|
|
127235
|
+
}
|
|
127236
|
+
async function runFacebookAnalysisRender(opts) {
|
|
127237
|
+
const dataPath = path29.resolve(opts.dataFile);
|
|
127238
|
+
let data = asRecord12(await readJsonFile2(dataPath));
|
|
127239
|
+
if (opts.snapshotDir?.trim()) {
|
|
127240
|
+
data = await mergeFacebookSnapshotIntoReport(data, path29.resolve(opts.snapshotDir));
|
|
127241
|
+
}
|
|
127242
|
+
const contentCheck = validateMetaPeriodReportContent(data);
|
|
127243
|
+
if (!contentCheck.ok) {
|
|
127244
|
+
console.error(`
|
|
127245
|
+
\u274C ${formatMetaPeriodReportErrors(contentCheck)}
|
|
127246
|
+
`);
|
|
127247
|
+
const { stats: stats2 } = contentCheck;
|
|
127248
|
+
console.error(
|
|
127249
|
+
` \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}
|
|
127250
|
+
`
|
|
127251
|
+
);
|
|
127252
|
+
process.exit(1);
|
|
127253
|
+
}
|
|
127254
|
+
const templatePath = metaPeriodReportTemplatePath();
|
|
127255
|
+
let html;
|
|
127256
|
+
try {
|
|
127257
|
+
html = await fsPromises2.readFile(templatePath, "utf8");
|
|
127258
|
+
} catch {
|
|
127259
|
+
console.error(`
|
|
127260
|
+
\u274C \u672A\u627E\u5230\u62A5\u544A\u6A21\u677F\uFF1A${templatePath}
|
|
127261
|
+
\u8BF7\u5148\u6267\u884C npm run build
|
|
127262
|
+
`);
|
|
127263
|
+
process.exit(1);
|
|
127264
|
+
}
|
|
127265
|
+
const outPath = path29.resolve(
|
|
127266
|
+
opts.out ?? path29.join(path29.dirname(dataPath), "meta-period-report.html")
|
|
127267
|
+
);
|
|
127268
|
+
const outDir = path29.dirname(outPath);
|
|
127269
|
+
await fsPromises2.mkdir(outDir, { recursive: true });
|
|
127270
|
+
await fsPromises2.writeFile(outPath, injectReportData2(html, data), "utf8");
|
|
127271
|
+
const runtimeSrc = metaPeriodReportRuntimePath();
|
|
127272
|
+
const runtimeOut = path29.join(outDir, TEMPLATE_BASENAMES2.runtime);
|
|
127273
|
+
let runtimeCopied = false;
|
|
127274
|
+
try {
|
|
127275
|
+
await fsPromises2.copyFile(runtimeSrc, runtimeOut);
|
|
127276
|
+
runtimeCopied = true;
|
|
127277
|
+
} catch {
|
|
127278
|
+
}
|
|
127279
|
+
const { stats } = contentCheck;
|
|
127280
|
+
console.log(`
|
|
127281
|
+
\u2705 Meta/Facebook \u5468\u671F\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
127282
|
+
`);
|
|
127283
|
+
console.log(
|
|
127284
|
+
` \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
|
|
127285
|
+
`
|
|
127286
|
+
);
|
|
127287
|
+
if (runtimeCopied) {
|
|
127288
|
+
console.log(` \u8FD0\u884C\u65F6\u811A\u672C\uFF1A${runtimeOut}
|
|
127289
|
+
`);
|
|
127290
|
+
}
|
|
127291
|
+
console.log(
|
|
127292
|
+
"\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"
|
|
127293
|
+
);
|
|
127294
|
+
}
|
|
127295
|
+
|
|
127296
|
+
// src/commands/facebook-analysis/run-batch.ts
|
|
127297
|
+
init_auth();
|
|
127298
|
+
import * as path30 from "path";
|
|
127299
|
+
import { performance as performance5 } from "perf_hooks";
|
|
127300
|
+
init_version();
|
|
127301
|
+
|
|
127302
|
+
// src/commands/facebook-analysis/resolve-sections.ts
|
|
127303
|
+
function normalizeSectionToken(raw) {
|
|
127304
|
+
const t = raw.trim();
|
|
127305
|
+
if (!t) return null;
|
|
127306
|
+
if (FACEBOOK_SECTION_NAMES.includes(t)) {
|
|
127307
|
+
return t;
|
|
127308
|
+
}
|
|
127309
|
+
return FACEBOOK_SECTION_ALIASES[t] ?? null;
|
|
127310
|
+
}
|
|
127311
|
+
function resolveSectionList2(sections, exclude) {
|
|
127312
|
+
const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
127313
|
+
const include = splitClean(sections);
|
|
127314
|
+
const dropRaw = splitClean(exclude);
|
|
127315
|
+
const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken(n) === null);
|
|
127316
|
+
if (unknown.length > 0) {
|
|
127317
|
+
console.error(
|
|
127318
|
+
`
|
|
127319
|
+
\u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
|
|
127320
|
+
\u53EF\u9009\uFF1A${FACEBOOK_SECTION_NAMES.join(", ")}
|
|
127321
|
+
Google \u522B\u540D\uFF1A${Object.keys(FACEBOOK_SECTION_ALIASES).join(", ")}
|
|
127322
|
+
`
|
|
127323
|
+
);
|
|
127324
|
+
process.exit(1);
|
|
127325
|
+
}
|
|
127326
|
+
const includeCanonical = new Set(include.map((n) => normalizeSectionToken(n)).filter(Boolean));
|
|
127327
|
+
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken(n)).filter(Boolean));
|
|
127328
|
+
const base = include.length > 0 ? FACEBOOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...FACEBOOK_SECTIONS];
|
|
127329
|
+
return base.filter((s) => !dropCanonical.has(s.name));
|
|
127330
|
+
}
|
|
127331
|
+
|
|
127332
|
+
// src/commands/facebook-analysis/run-batch.ts
|
|
127333
|
+
async function runWithConcurrency2(tasks, concurrency) {
|
|
127334
|
+
const results = new Array(tasks.length);
|
|
127335
|
+
let cursor = 0;
|
|
127336
|
+
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
127337
|
+
while (true) {
|
|
127338
|
+
const i = cursor++;
|
|
127339
|
+
if (i >= tasks.length) return;
|
|
127340
|
+
results[i] = await tasks[i]();
|
|
127341
|
+
}
|
|
127342
|
+
});
|
|
127343
|
+
await Promise.all(workers);
|
|
127344
|
+
return results;
|
|
127345
|
+
}
|
|
127346
|
+
async function runAllFacebookSections(opts) {
|
|
127347
|
+
const { apiId, manifestId } = normalizeFacebookAccountId(opts.account);
|
|
127348
|
+
const config = loadConfig(opts.token);
|
|
127349
|
+
if (!opts.jsonOut?.trim()) {
|
|
127350
|
+
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");
|
|
127351
|
+
process.exit(1);
|
|
127352
|
+
}
|
|
127353
|
+
const targets = resolveSectionList2(opts.sections, opts.exclude);
|
|
127354
|
+
if (targets.length === 0) {
|
|
127355
|
+
console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
|
|
127356
|
+
process.exit(1);
|
|
127357
|
+
}
|
|
127358
|
+
const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
|
|
127359
|
+
const concurrency = Math.max(
|
|
127360
|
+
1,
|
|
127361
|
+
Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
|
|
127362
|
+
);
|
|
127363
|
+
const cliVersion = getCurrentVersion2();
|
|
127364
|
+
const overallT0 = performance5.now();
|
|
127365
|
+
const tasks = targets.map(
|
|
127366
|
+
(def) => async () => {
|
|
127367
|
+
const t0 = performance5.now();
|
|
127368
|
+
const hint = endpointHintForFacebookSection(def, apiId);
|
|
127369
|
+
try {
|
|
127370
|
+
const payload = await fetchFacebookSectionPayload(def, opts, config, apiId);
|
|
127371
|
+
const summary2 = await writeReportAnalysisSnapshot({
|
|
127372
|
+
snapshotDir: opts.jsonOut,
|
|
127373
|
+
section: def.name,
|
|
127374
|
+
accountId: manifestId,
|
|
127375
|
+
dateRange: { start: startDate, end: endDate },
|
|
127376
|
+
payload,
|
|
127377
|
+
cliVersion
|
|
127378
|
+
});
|
|
127379
|
+
return {
|
|
127380
|
+
section: def.name,
|
|
127381
|
+
ok: true,
|
|
127382
|
+
elapsedMs: performance5.now() - t0,
|
|
127383
|
+
file: summary2.writtenFiles[0],
|
|
127384
|
+
endpointHint: hint
|
|
127385
|
+
};
|
|
127386
|
+
} catch (err) {
|
|
127387
|
+
return {
|
|
127388
|
+
section: def.name,
|
|
127389
|
+
ok: false,
|
|
127390
|
+
elapsedMs: performance5.now() - t0,
|
|
127391
|
+
error: err instanceof Error ? err.message : String(err),
|
|
127392
|
+
endpointHint: hint
|
|
127393
|
+
};
|
|
127394
|
+
}
|
|
127395
|
+
}
|
|
127396
|
+
);
|
|
127397
|
+
const results = await runWithConcurrency2(tasks, concurrency);
|
|
127398
|
+
const succeeded = results.filter((r) => r.ok).length;
|
|
127399
|
+
const failed = results.length - succeeded;
|
|
127400
|
+
const absoluteSnapshotDir = path30.resolve(opts.jsonOut);
|
|
127401
|
+
const summary = {
|
|
127402
|
+
kind: "siluzan-tso-facebook-analysis-snapshot-batch",
|
|
127403
|
+
absoluteSnapshotDir,
|
|
127404
|
+
manifestFile: reportManifestFile(manifestId),
|
|
127405
|
+
accountId: manifestId,
|
|
127406
|
+
totalSections: results.length,
|
|
127407
|
+
succeeded,
|
|
127408
|
+
failed,
|
|
127409
|
+
concurrency,
|
|
127410
|
+
elapsedMs: performance5.now() - overallT0,
|
|
127411
|
+
results
|
|
127412
|
+
};
|
|
127413
|
+
console.log(JSON.stringify(summary));
|
|
127414
|
+
if (failed > 0) process.exit(2);
|
|
127415
|
+
}
|
|
127416
|
+
|
|
127417
|
+
// src/commands/facebook-analysis/register-cli.ts
|
|
127418
|
+
function registerBatchCommand(sectionHelp) {
|
|
127419
|
+
return new Command("run").description(
|
|
127420
|
+
"\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"
|
|
127421
|
+
).requiredOption("-a, --account <id>", "Facebook \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09").requiredOption(
|
|
127422
|
+
"--json-out <dir>",
|
|
127423
|
+
"\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
|
|
127424
|
+
).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(
|
|
127425
|
+
"--sections <list>",
|
|
127426
|
+
`\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`
|
|
127427
|
+
).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
|
|
127428
|
+
"--limit <n>",
|
|
127429
|
+
"\u4EC5 country \u7EF4\u5EA6\uFF1A\u6309 spend \u964D\u5E8F\u53D6\u524D N \u6761\uFF08\u4E0D\u4F20\u5219\u8FD4\u56DE\u5168\u90E8\u56FD\u5BB6\uFF09",
|
|
127430
|
+
(v) => parseInt(v, 10)
|
|
127431
|
+
).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) => {
|
|
127432
|
+
await runAllFacebookSections(opts);
|
|
127433
|
+
});
|
|
127434
|
+
}
|
|
127435
|
+
function registerFacebookAnalysisCommands(program2) {
|
|
127436
|
+
const sectionHelp = FACEBOOK_SECTION_NAMES.join(", ");
|
|
127437
|
+
const root = program2.command("facebook-analysis").description(
|
|
127438
|
+
"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"
|
|
127439
|
+
);
|
|
127440
|
+
root.addCommand(registerBatchCommand(sectionHelp), { isDefault: true });
|
|
127441
|
+
root.command("render").description(
|
|
127442
|
+
"\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"
|
|
127443
|
+
).requiredOption("--data <file>", "Agent \u4EA7\u51FA\u7684 meta-period-report.json").option(
|
|
127444
|
+
"--snapshot-dir <dir>",
|
|
127445
|
+
"\u53EF\u9009\uFF1Afacebook-analysis --json-out \u76EE\u5F55\uFF0C\u81EA\u52A8\u5408\u5E76 KPI / \u56FE\u8868 / \u8868\u683C"
|
|
127446
|
+
).option("--out <file>", "\u8F93\u51FA HTML \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\uFF09").action(async (opts) => {
|
|
127447
|
+
await runFacebookAnalysisRender({
|
|
127448
|
+
dataFile: opts.data,
|
|
127449
|
+
snapshotDir: opts.snapshotDir,
|
|
127450
|
+
out: opts.out
|
|
127451
|
+
});
|
|
127452
|
+
});
|
|
127453
|
+
}
|
|
127454
|
+
|
|
127455
|
+
// src/commands/bing-analysis/sections.ts
|
|
127456
|
+
var BING_SECTIONS = [
|
|
127457
|
+
{
|
|
127458
|
+
name: "overview",
|
|
127459
|
+
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",
|
|
127460
|
+
segment: "OverviewSectionData"
|
|
127461
|
+
},
|
|
127462
|
+
{
|
|
127463
|
+
name: "device",
|
|
127464
|
+
description: "\u8BBE\u5907\u5206\u5E03 DeviceSectionData",
|
|
127465
|
+
segment: "DeviceSectionData"
|
|
127466
|
+
},
|
|
127467
|
+
{
|
|
127468
|
+
name: "geographic",
|
|
127469
|
+
description: "\u5730\u57DF\u5206\u5E03 GeographicSectionData",
|
|
127470
|
+
segment: "GeographicSectionData"
|
|
127471
|
+
},
|
|
127472
|
+
{
|
|
127473
|
+
name: "age-audience",
|
|
127474
|
+
description: "\u5E74\u9F84\u53D7\u4F17 AgeAudienceData",
|
|
127475
|
+
segment: "AgeAudienceData"
|
|
127476
|
+
},
|
|
127477
|
+
{
|
|
127478
|
+
name: "gender-audience",
|
|
127479
|
+
description: "\u6027\u522B\u53D7\u4F17 GenderAudienceData",
|
|
127480
|
+
segment: "GenderAudienceData"
|
|
127481
|
+
},
|
|
127482
|
+
{
|
|
127483
|
+
name: "audience-merged",
|
|
127484
|
+
description: "\u53D7\u4F17\u5408\u5E76\uFF08AgeAudienceData + GenderAudienceData \u5E76\u884C\u62C9\u53D6\u5408\u5E76\uFF0C\u4E0D\u505A\u524D\u7AEF filterAudienceData\uFF09",
|
|
127485
|
+
segment: "AgeAudienceData+GenderAudienceData"
|
|
127486
|
+
},
|
|
127487
|
+
{
|
|
127488
|
+
name: "campaigns",
|
|
127489
|
+
description: "\u5E7F\u544A\u7CFB\u5217\u62A5\u8868 CampaignReport",
|
|
127490
|
+
segment: "CampaignReport"
|
|
127491
|
+
},
|
|
127492
|
+
{
|
|
127493
|
+
name: "ad-groups",
|
|
127494
|
+
description: "\u5E7F\u544A\u7EC4\u62A5\u8868 AdGroupReport",
|
|
127495
|
+
segment: "AdGroupReport"
|
|
127496
|
+
},
|
|
127497
|
+
{
|
|
127498
|
+
name: "ads",
|
|
127499
|
+
description: "\u5E7F\u544A\u62A5\u8868 AdReport",
|
|
127500
|
+
segment: "AdReport"
|
|
127501
|
+
},
|
|
127502
|
+
{
|
|
127503
|
+
name: "keywords",
|
|
127504
|
+
description: "\u5173\u952E\u8BCD\u62A5\u8868 KeywordReport\uFF08\u9ED8\u8BA4 limit=100\u3001orderByCost=true\uFF09",
|
|
127505
|
+
segment: "KeywordReport",
|
|
127506
|
+
limitOption: true
|
|
127507
|
+
},
|
|
127508
|
+
{
|
|
127509
|
+
name: "search-terms",
|
|
127510
|
+
description: "\u641C\u7D22\u5B57\u8BCD\u62A5\u8868 SearchQueryReport\uFF08\u9ED8\u8BA4 limit=100\u3001orderByCost=true\uFF09",
|
|
127511
|
+
segment: "SearchQueryReport",
|
|
127512
|
+
limitOption: true
|
|
127513
|
+
}
|
|
127514
|
+
];
|
|
127515
|
+
var BING_SECTION_NAMES = BING_SECTIONS.map((s) => s.name);
|
|
127516
|
+
var BING_SECTION_ALIASES = {
|
|
127517
|
+
devices: "device",
|
|
127518
|
+
geo: "geographic",
|
|
127519
|
+
"search-queries": "search-terms"
|
|
127520
|
+
};
|
|
127521
|
+
|
|
127522
|
+
// src/commands/bing-analysis/resolve-sections.ts
|
|
127523
|
+
function normalizeSectionToken2(raw) {
|
|
127524
|
+
const t = raw.trim();
|
|
127525
|
+
if (!t) return null;
|
|
127526
|
+
if (BING_SECTION_NAMES.includes(t)) {
|
|
127527
|
+
return t;
|
|
127528
|
+
}
|
|
127529
|
+
return BING_SECTION_ALIASES[t] ?? null;
|
|
127530
|
+
}
|
|
127531
|
+
function resolveSectionList3(sections, exclude) {
|
|
127532
|
+
const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
127533
|
+
const include = splitClean(sections);
|
|
127534
|
+
const dropRaw = splitClean(exclude);
|
|
127535
|
+
const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken2(n) === null);
|
|
127536
|
+
if (unknown.length > 0) {
|
|
127537
|
+
console.error(
|
|
127538
|
+
`
|
|
127539
|
+
\u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
|
|
127540
|
+
\u53EF\u9009\uFF1A${BING_SECTION_NAMES.join(", ")}
|
|
127541
|
+
\u522B\u540D\uFF1A${Object.keys(BING_SECTION_ALIASES).join(", ")}
|
|
127542
|
+
`
|
|
127543
|
+
);
|
|
127544
|
+
process.exit(1);
|
|
127545
|
+
}
|
|
127546
|
+
const includeCanonical = new Set(include.map((n) => normalizeSectionToken2(n)).filter(Boolean));
|
|
127547
|
+
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken2(n)).filter(Boolean));
|
|
127548
|
+
const base = include.length > 0 ? BING_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...BING_SECTIONS];
|
|
127549
|
+
return base.filter((s) => !dropCanonical.has(s.name));
|
|
127550
|
+
}
|
|
127551
|
+
|
|
127552
|
+
// src/commands/bing-analysis/fetch.ts
|
|
127553
|
+
init_auth();
|
|
127554
|
+
init_balance();
|
|
127555
|
+
|
|
127556
|
+
// src/commands/bing-analysis/normalize-bing-overview.ts
|
|
127557
|
+
function num5(v) {
|
|
127558
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
127559
|
+
}
|
|
127560
|
+
function roundMoney(v) {
|
|
127561
|
+
return Math.round(v * 100) / 100;
|
|
127562
|
+
}
|
|
127563
|
+
function countInclusiveCalendarDays(startDate, endDate) {
|
|
127564
|
+
const parts = (ymd) => ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
127565
|
+
const sp = parts(startDate);
|
|
127566
|
+
const ep = parts(endDate);
|
|
127567
|
+
if (sp.length !== 3 || ep.length !== 3 || sp.some((n) => !Number.isFinite(n)) || ep.some((n) => !Number.isFinite(n))) {
|
|
127568
|
+
return 0;
|
|
127569
|
+
}
|
|
127570
|
+
const s = new Date(sp[0], sp[1] - 1, sp[2]);
|
|
127571
|
+
const e = new Date(ep[0], ep[1] - 1, ep[2]);
|
|
127572
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) return 0;
|
|
127573
|
+
const msPerDay = 24 * 60 * 60 * 1e3;
|
|
127574
|
+
return Math.floor((e.getTime() - s.getTime()) / msPerDay) + 1;
|
|
127575
|
+
}
|
|
127576
|
+
function periodSpend(period) {
|
|
127577
|
+
if (!period || typeof period !== "object") return void 0;
|
|
127578
|
+
return num5(period.spend);
|
|
127579
|
+
}
|
|
127580
|
+
function enrichBingOverviewDerivedMetrics(record, startDate, endDate) {
|
|
127581
|
+
const out = { ...record };
|
|
127582
|
+
const currentSpend = periodSpend(out.currentPeriod);
|
|
127583
|
+
let totalCost = num5(out.totalCost);
|
|
127584
|
+
if (totalCost == null || totalCost <= 0) {
|
|
127585
|
+
if (currentSpend != null && currentSpend > 0) {
|
|
127586
|
+
out.totalCost = currentSpend;
|
|
127587
|
+
totalCost = currentSpend;
|
|
127588
|
+
}
|
|
127589
|
+
}
|
|
127590
|
+
let activeDays = num5(out.activeDays);
|
|
127591
|
+
if (activeDays == null || activeDays <= 0) {
|
|
127592
|
+
const calendarDays = countInclusiveCalendarDays(startDate, endDate);
|
|
127593
|
+
if (calendarDays > 0) {
|
|
127594
|
+
out.activeDays = calendarDays;
|
|
127595
|
+
activeDays = calendarDays;
|
|
127596
|
+
}
|
|
127597
|
+
}
|
|
127598
|
+
const averageDailyCost = num5(out.averageDailyCost);
|
|
127599
|
+
if ((averageDailyCost == null || averageDailyCost <= 0) && totalCost != null && totalCost > 0 && activeDays != null && activeDays > 0) {
|
|
127600
|
+
out.averageDailyCost = roundMoney(totalCost / activeDays);
|
|
127601
|
+
}
|
|
127602
|
+
return out;
|
|
127970
127603
|
}
|
|
127971
|
-
function
|
|
127972
|
-
|
|
127604
|
+
function parseYmd(ymd) {
|
|
127605
|
+
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
127606
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) return null;
|
|
127607
|
+
const d = new Date(parts[0], parts[1] - 1, parts[2]);
|
|
127608
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
127973
127609
|
}
|
|
127974
|
-
function
|
|
127975
|
-
|
|
127610
|
+
function formatYmd(d) {
|
|
127611
|
+
const y = d.getFullYear();
|
|
127612
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
127613
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
127614
|
+
return `${y}-${m}-${day}`;
|
|
127976
127615
|
}
|
|
127977
|
-
|
|
127978
|
-
const
|
|
127979
|
-
|
|
127980
|
-
|
|
127981
|
-
|
|
127982
|
-
|
|
127616
|
+
function calcBingPreviousPeriodRange(startDate, endDate) {
|
|
127617
|
+
const diffDays = countInclusiveCalendarDays(startDate, endDate);
|
|
127618
|
+
if (diffDays <= 0) return null;
|
|
127619
|
+
const start = parseYmd(startDate);
|
|
127620
|
+
if (!start) return null;
|
|
127621
|
+
const prevEnd = new Date(start);
|
|
127622
|
+
prevEnd.setDate(prevEnd.getDate() - 1);
|
|
127623
|
+
const prevStart = new Date(prevEnd);
|
|
127624
|
+
prevStart.setDate(prevStart.getDate() - (diffDays - 1));
|
|
127625
|
+
return { startDate: formatYmd(prevStart), endDate: formatYmd(prevEnd) };
|
|
127626
|
+
}
|
|
127627
|
+
function isBingPeriodBlockEmpty(period) {
|
|
127628
|
+
if (!period || typeof period !== "object") return true;
|
|
127629
|
+
const p = period;
|
|
127630
|
+
const spend = num5(p.spend) ?? 0;
|
|
127631
|
+
const impressions = num5(p.impressions) ?? 0;
|
|
127632
|
+
const clicks = num5(p.clicks) ?? 0;
|
|
127633
|
+
return spend <= 0 && impressions <= 0 && clicks <= 0;
|
|
127634
|
+
}
|
|
127635
|
+
function syncOverviewBalanceFields(record) {
|
|
127636
|
+
const out = { ...record };
|
|
127637
|
+
const budget = num5(out.remainingAccountBudget);
|
|
127638
|
+
if (budget != null && budget >= 0) {
|
|
127639
|
+
const balanceField = num5(out.balance);
|
|
127640
|
+
if (balanceField == null || balanceField <= 0 && budget > 0) {
|
|
127641
|
+
out.balance = budget;
|
|
127642
|
+
}
|
|
127983
127643
|
}
|
|
127644
|
+
return out;
|
|
127984
127645
|
}
|
|
127985
|
-
|
|
127986
|
-
|
|
127987
|
-
|
|
127646
|
+
|
|
127647
|
+
// src/commands/bing-analysis/fetch.ts
|
|
127648
|
+
var BING_KEYWORD_LIMIT_DEFAULT = 100;
|
|
127649
|
+
function stripLocalDate(d) {
|
|
127650
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
127651
|
+
}
|
|
127652
|
+
function parseLocalYmd(ymd) {
|
|
127653
|
+
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
127654
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) {
|
|
127655
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
127988
127656
|
}
|
|
127989
|
-
|
|
127657
|
+
const [y, m, day] = parts;
|
|
127658
|
+
return new Date(y, m - 1, day);
|
|
127990
127659
|
}
|
|
127991
|
-
function
|
|
127992
|
-
const
|
|
127993
|
-
|
|
127994
|
-
|
|
127660
|
+
function formatLocalYmd(d) {
|
|
127661
|
+
const y = d.getFullYear();
|
|
127662
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
127663
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
127664
|
+
return `${y}-${m}-${day}`;
|
|
127665
|
+
}
|
|
127666
|
+
function bingRangeTouchesTodayOrYesterday(startDate, endDate) {
|
|
127667
|
+
const s = parseLocalYmd(startDate);
|
|
127668
|
+
const e = parseLocalYmd(endDate);
|
|
127669
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) {
|
|
127670
|
+
return true;
|
|
127995
127671
|
}
|
|
127996
|
-
|
|
127672
|
+
const today = stripLocalDate(/* @__PURE__ */ new Date());
|
|
127673
|
+
const yesterday = new Date(today);
|
|
127674
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
127675
|
+
for (let cur = new Date(s); cur <= e; cur.setDate(cur.getDate() + 1)) {
|
|
127676
|
+
const c = stripLocalDate(cur);
|
|
127677
|
+
if (c.getTime() === today.getTime() || c.getTime() === yesterday.getTime()) {
|
|
127678
|
+
return true;
|
|
127679
|
+
}
|
|
127680
|
+
}
|
|
127681
|
+
return false;
|
|
127997
127682
|
}
|
|
127998
|
-
|
|
127999
|
-
|
|
128000
|
-
|
|
128001
|
-
|
|
128002
|
-
|
|
127683
|
+
function resolveBingDateRange(start, end) {
|
|
127684
|
+
if (start && end) {
|
|
127685
|
+
const s = parseLocalYmd(start);
|
|
127686
|
+
const e = parseLocalYmd(end);
|
|
127687
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime())) {
|
|
127688
|
+
console.error("\n\u274C --start / --end \u987B\u4E3A\u6709\u6548\u65E5\u671F YYYY-MM-DD\u3002\n");
|
|
127689
|
+
process.exit(1);
|
|
127690
|
+
}
|
|
127691
|
+
if (s > e) {
|
|
127692
|
+
console.error("\n\u274C \u5F00\u59CB\u65E5\u671F\u4E0D\u80FD\u665A\u4E8E\u7ED3\u675F\u65E5\u671F\u3002\n");
|
|
127693
|
+
process.exit(1);
|
|
127694
|
+
}
|
|
127695
|
+
if (bingRangeTouchesTodayOrYesterday(start, end)) {
|
|
127696
|
+
console.error(
|
|
127697
|
+
"\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"
|
|
127698
|
+
);
|
|
127699
|
+
process.exit(1);
|
|
127700
|
+
}
|
|
127701
|
+
return { startDate: start, endDate: end };
|
|
128003
127702
|
}
|
|
128004
|
-
|
|
128005
|
-
|
|
128006
|
-
|
|
128007
|
-
|
|
128008
|
-
|
|
128009
|
-
|
|
128010
|
-
|
|
128011
|
-
|
|
128012
|
-
|
|
128013
|
-
|
|
127703
|
+
if (!start && !end) {
|
|
127704
|
+
const endD = stripLocalDate(/* @__PURE__ */ new Date());
|
|
127705
|
+
endD.setDate(endD.getDate() - 2);
|
|
127706
|
+
const startD = new Date(endD);
|
|
127707
|
+
startD.setDate(startD.getDate() - 6);
|
|
127708
|
+
return { startDate: formatLocalYmd(startD), endDate: formatLocalYmd(endD) };
|
|
127709
|
+
}
|
|
127710
|
+
console.error(
|
|
127711
|
+
"\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"
|
|
127712
|
+
);
|
|
127713
|
+
process.exit(1);
|
|
127714
|
+
}
|
|
127715
|
+
function assertBingAccountId(id) {
|
|
127716
|
+
const t = id.trim();
|
|
127717
|
+
if (!t) {
|
|
127718
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
128014
127719
|
process.exit(1);
|
|
128015
127720
|
}
|
|
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
|
-
`);
|
|
127721
|
+
if (!/^\d+$/.test(t)) {
|
|
127722
|
+
console.error("\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId\uFF08\u4E0E list-accounts -m BingV2 \u4E00\u81F4\uFF09\u3002\n");
|
|
128025
127723
|
process.exit(1);
|
|
128026
127724
|
}
|
|
128027
|
-
|
|
128028
|
-
|
|
127725
|
+
return t;
|
|
127726
|
+
}
|
|
127727
|
+
function reportingUrl(config, accountId, segment, query) {
|
|
127728
|
+
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
127729
|
+
return `${config.apiBaseUrl}/reporting/media-account/BingV2/${accountId}/${segment}${q}`;
|
|
127730
|
+
}
|
|
127731
|
+
function resolveLimit(raw) {
|
|
127732
|
+
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
127733
|
+
return Number.isFinite(n) && n > 0 ? n : BING_KEYWORD_LIMIT_DEFAULT;
|
|
127734
|
+
}
|
|
127735
|
+
async function fetchBingJson(config, url, verbose) {
|
|
127736
|
+
return apiFetch2(url, config, {}, verbose);
|
|
127737
|
+
}
|
|
127738
|
+
async function fetchBingOverviewPayload(config, id, startDate, endDate, verbose) {
|
|
127739
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
127740
|
+
const url = reportingUrl(config, id, "OverviewSectionData", params.toString());
|
|
127741
|
+
const raw = await fetchBingJson(config, url, verbose);
|
|
127742
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
127743
|
+
const withBalance = await enrichOverviewRecordWithRealtimeBalance(
|
|
127744
|
+
"BingV2",
|
|
127745
|
+
id,
|
|
127746
|
+
config,
|
|
127747
|
+
raw,
|
|
127748
|
+
verbose
|
|
128029
127749
|
);
|
|
128030
|
-
const
|
|
128031
|
-
|
|
128032
|
-
|
|
128033
|
-
|
|
128034
|
-
|
|
128035
|
-
|
|
127750
|
+
const withDerived = enrichBingOverviewDerivedMetrics(withBalance, startDate, endDate);
|
|
127751
|
+
const withPrevious = await enrichBingOverviewPreviousPeriod(
|
|
127752
|
+
config,
|
|
127753
|
+
id,
|
|
127754
|
+
withDerived,
|
|
127755
|
+
startDate,
|
|
127756
|
+
endDate,
|
|
127757
|
+
verbose
|
|
127758
|
+
);
|
|
127759
|
+
return syncOverviewBalanceFields(withPrevious);
|
|
127760
|
+
}
|
|
127761
|
+
async function enrichBingOverviewPreviousPeriod(config, accountId, record, startDate, endDate, verbose) {
|
|
127762
|
+
if (!isBingPeriodBlockEmpty(record.previousPeriod)) {
|
|
127763
|
+
return record;
|
|
127764
|
+
}
|
|
127765
|
+
const prevRange = calcBingPreviousPeriodRange(startDate, endDate);
|
|
127766
|
+
if (!prevRange) return record;
|
|
127767
|
+
if (bingRangeTouchesTodayOrYesterday(prevRange.startDate, prevRange.endDate)) {
|
|
127768
|
+
return record;
|
|
127769
|
+
}
|
|
128036
127770
|
try {
|
|
128037
|
-
|
|
128038
|
-
|
|
127771
|
+
const params = new URLSearchParams({
|
|
127772
|
+
startDate: prevRange.startDate,
|
|
127773
|
+
endDate: prevRange.endDate
|
|
127774
|
+
});
|
|
127775
|
+
const url = reportingUrl(config, accountId, "OverviewSectionData", params.toString());
|
|
127776
|
+
const prevRaw = await fetchBingJson(config, url, verbose);
|
|
127777
|
+
if (!prevRaw || typeof prevRaw !== "object" || Array.isArray(prevRaw)) {
|
|
127778
|
+
return record;
|
|
127779
|
+
}
|
|
127780
|
+
const prevRecord = prevRaw;
|
|
127781
|
+
const prevPeriod = prevRecord.currentPeriod;
|
|
127782
|
+
if (isBingPeriodBlockEmpty(prevPeriod)) {
|
|
127783
|
+
return record;
|
|
127784
|
+
}
|
|
127785
|
+
return {
|
|
127786
|
+
...record,
|
|
127787
|
+
previousPeriod: prevPeriod,
|
|
127788
|
+
previousPeriodDateRange: prevRange,
|
|
127789
|
+
_previousPeriodSource: "OverviewSectionData-second-fetch"
|
|
127790
|
+
};
|
|
128039
127791
|
} catch {
|
|
127792
|
+
return record;
|
|
128040
127793
|
}
|
|
128041
|
-
|
|
128042
|
-
|
|
128043
|
-
|
|
128044
|
-
|
|
128045
|
-
|
|
128046
|
-
|
|
128047
|
-
|
|
128048
|
-
|
|
128049
|
-
|
|
128050
|
-
|
|
128051
|
-
|
|
127794
|
+
}
|
|
127795
|
+
async function fetchBingAudienceMergedPayload(config, id, startDate, endDate, verbose) {
|
|
127796
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
127797
|
+
const ageUrl = reportingUrl(config, id, "AgeAudienceData", params.toString());
|
|
127798
|
+
const genderUrl = reportingUrl(config, id, "GenderAudienceData", params.toString());
|
|
127799
|
+
const [ageData, genderData] = await Promise.all([
|
|
127800
|
+
fetchBingJson(config, ageUrl, verbose),
|
|
127801
|
+
fetchBingJson(config, genderUrl, verbose)
|
|
127802
|
+
]);
|
|
127803
|
+
return {
|
|
127804
|
+
mergedParts: ["AgeAudienceData", "GenderAudienceData"],
|
|
127805
|
+
startDate,
|
|
127806
|
+
endDate,
|
|
127807
|
+
account: id,
|
|
127808
|
+
data: { ageAudience: ageData, genderAudience: genderData }
|
|
127809
|
+
};
|
|
127810
|
+
}
|
|
127811
|
+
async function fetchBingSectionPayload(def, opts, config, id) {
|
|
127812
|
+
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
127813
|
+
const verbose = !!opts.verbose;
|
|
127814
|
+
if (def.name === "overview") {
|
|
127815
|
+
return fetchBingOverviewPayload(config, id, startDate, endDate, verbose);
|
|
128052
127816
|
}
|
|
128053
|
-
|
|
128054
|
-
|
|
128055
|
-
|
|
127817
|
+
if (def.name === "audience-merged") {
|
|
127818
|
+
return fetchBingAudienceMergedPayload(config, id, startDate, endDate, verbose);
|
|
127819
|
+
}
|
|
127820
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
127821
|
+
if (def.limitOption) {
|
|
127822
|
+
params.set("limit", String(resolveLimit(opts.limit)));
|
|
127823
|
+
params.set("orderByCost", "true");
|
|
127824
|
+
}
|
|
127825
|
+
const url = reportingUrl(config, id, def.segment, params.toString());
|
|
127826
|
+
return fetchBingJson(config, url, verbose);
|
|
127827
|
+
}
|
|
127828
|
+
function endpointHintForBingSection(def) {
|
|
127829
|
+
return `GET \u2026/BingV2/{id}/${def.segment}`;
|
|
128056
127830
|
}
|
|
128057
127831
|
|
|
128058
|
-
// src/commands/
|
|
127832
|
+
// src/commands/bing-analysis/run-batch.ts
|
|
128059
127833
|
init_auth();
|
|
128060
|
-
import * as
|
|
128061
|
-
import { performance as
|
|
127834
|
+
import * as path31 from "path";
|
|
127835
|
+
import { performance as performance6 } from "perf_hooks";
|
|
128062
127836
|
init_version();
|
|
127837
|
+
async function runWithConcurrency3(tasks, concurrency) {
|
|
127838
|
+
const results = new Array(tasks.length);
|
|
127839
|
+
let cursor = 0;
|
|
127840
|
+
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
127841
|
+
while (true) {
|
|
127842
|
+
const i = cursor++;
|
|
127843
|
+
if (i >= tasks.length) return;
|
|
127844
|
+
results[i] = await tasks[i]();
|
|
127845
|
+
}
|
|
127846
|
+
});
|
|
127847
|
+
await Promise.all(workers);
|
|
127848
|
+
return results;
|
|
127849
|
+
}
|
|
127850
|
+
async function runAllBingSections(opts) {
|
|
127851
|
+
const id = assertBingAccountId(opts.account);
|
|
127852
|
+
const config = loadConfig(opts.token);
|
|
127853
|
+
if (!opts.jsonOut?.trim()) {
|
|
127854
|
+
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");
|
|
127855
|
+
process.exit(1);
|
|
127856
|
+
}
|
|
127857
|
+
const targets = resolveSectionList3(opts.sections, opts.exclude);
|
|
127858
|
+
if (targets.length === 0) {
|
|
127859
|
+
console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
|
|
127860
|
+
process.exit(1);
|
|
127861
|
+
}
|
|
127862
|
+
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
127863
|
+
const concurrency = Math.max(
|
|
127864
|
+
1,
|
|
127865
|
+
Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
|
|
127866
|
+
);
|
|
127867
|
+
const cliVersion = getCurrentVersion2();
|
|
127868
|
+
const overallT0 = performance6.now();
|
|
127869
|
+
const tasks = targets.map((def) => async () => {
|
|
127870
|
+
const t0 = performance6.now();
|
|
127871
|
+
const hint = endpointHintForBingSection(def);
|
|
127872
|
+
try {
|
|
127873
|
+
const payload = await fetchBingSectionPayload(
|
|
127874
|
+
def,
|
|
127875
|
+
{ ...opts, start: startDate, end: endDate },
|
|
127876
|
+
config,
|
|
127877
|
+
id
|
|
127878
|
+
);
|
|
127879
|
+
const summary2 = await writeReportAnalysisSnapshot({
|
|
127880
|
+
snapshotDir: opts.jsonOut,
|
|
127881
|
+
section: def.name,
|
|
127882
|
+
accountId: id,
|
|
127883
|
+
dateRange: { start: startDate, end: endDate },
|
|
127884
|
+
payload,
|
|
127885
|
+
cliVersion
|
|
127886
|
+
});
|
|
127887
|
+
return {
|
|
127888
|
+
section: def.name,
|
|
127889
|
+
ok: true,
|
|
127890
|
+
elapsedMs: performance6.now() - t0,
|
|
127891
|
+
file: summary2.writtenFiles[0],
|
|
127892
|
+
endpointHint: hint
|
|
127893
|
+
};
|
|
127894
|
+
} catch (err) {
|
|
127895
|
+
return {
|
|
127896
|
+
section: def.name,
|
|
127897
|
+
ok: false,
|
|
127898
|
+
elapsedMs: performance6.now() - t0,
|
|
127899
|
+
error: err instanceof Error ? err.message : String(err),
|
|
127900
|
+
endpointHint: hint
|
|
127901
|
+
};
|
|
127902
|
+
}
|
|
127903
|
+
});
|
|
127904
|
+
const results = await runWithConcurrency3(tasks, concurrency);
|
|
127905
|
+
const succeeded = results.filter((r) => r.ok).length;
|
|
127906
|
+
const failed = results.length - succeeded;
|
|
127907
|
+
const absoluteSnapshotDir = path31.resolve(opts.jsonOut);
|
|
127908
|
+
const summary = {
|
|
127909
|
+
kind: "siluzan-tso-bing-analysis-snapshot-batch",
|
|
127910
|
+
absoluteSnapshotDir,
|
|
127911
|
+
manifestFile: reportManifestFile(id),
|
|
127912
|
+
accountId: id,
|
|
127913
|
+
totalSections: results.length,
|
|
127914
|
+
succeeded,
|
|
127915
|
+
failed,
|
|
127916
|
+
concurrency,
|
|
127917
|
+
elapsedMs: performance6.now() - overallT0,
|
|
127918
|
+
results
|
|
127919
|
+
};
|
|
127920
|
+
console.log(JSON.stringify(summary));
|
|
127921
|
+
if (failed > 0) process.exit(2);
|
|
127922
|
+
}
|
|
128063
127923
|
|
|
128064
|
-
// src/commands/
|
|
128065
|
-
|
|
127924
|
+
// src/commands/bing-analysis/register-cli.ts
|
|
127925
|
+
import { Command as Command2 } from "commander";
|
|
127926
|
+
function registerBatchCommand2(sectionHelp, aliasHelp) {
|
|
127927
|
+
return new Command2("run").description(
|
|
127928
|
+
"\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"
|
|
127929
|
+
).requiredOption(
|
|
127930
|
+
"-a, --account <id>",
|
|
127931
|
+
"Bing mediaCustomerId\uFF08\u6570\u5B57\uFF0C\u4E0E list-accounts -m BingV2 \u4E00\u81F4\uFF09"
|
|
127932
|
+
).requiredOption(
|
|
127933
|
+
"--json-out <dir>",
|
|
127934
|
+
"\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
|
|
127935
|
+
).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(
|
|
127936
|
+
"--sections <list>",
|
|
127937
|
+
`\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}`
|
|
127938
|
+
).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
|
|
127939
|
+
"--limit <n>",
|
|
127940
|
+
"\u4EC5 keywords / search-terms\uFF1A\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF0C\u4E0E\u524D\u7AEF KeywordReport/SearchQueryReport \u4E00\u81F4\uFF09",
|
|
127941
|
+
(v) => parseInt(v, 10)
|
|
127942
|
+
).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) => {
|
|
127943
|
+
await runAllBingSections(opts);
|
|
127944
|
+
});
|
|
127945
|
+
}
|
|
127946
|
+
function registerBingAnalysisCommands(program2) {
|
|
127947
|
+
const sectionHelp = BING_SECTION_NAMES.join(", ");
|
|
127948
|
+
const aliasHelp = Object.keys(BING_SECTION_ALIASES).join(", ");
|
|
127949
|
+
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");
|
|
127950
|
+
root.addCommand(registerBatchCommand2(sectionHelp, aliasHelp), { isDefault: true });
|
|
127951
|
+
}
|
|
127952
|
+
|
|
127953
|
+
// src/commands/tiktok-analysis/sections.ts
|
|
127954
|
+
var TIKTOK_SECTIONS = [
|
|
127955
|
+
{
|
|
127956
|
+
name: "overview",
|
|
127957
|
+
description: "\u8D26\u6237\u603B\u89C8 OverviewSectionData",
|
|
127958
|
+
segment: "OverviewSectionData"
|
|
127959
|
+
},
|
|
127960
|
+
{
|
|
127961
|
+
name: "campaigns",
|
|
127962
|
+
description: "\u5E7F\u544A\u7CFB\u5217\u5206\u6BB5 CampaignSectionData\uFF08\u9ED8\u8BA4 take=100\uFF09",
|
|
127963
|
+
segment: "CampaignSectionData",
|
|
127964
|
+
takeOption: true
|
|
127965
|
+
},
|
|
127966
|
+
{
|
|
127967
|
+
name: "ad-groups",
|
|
127968
|
+
description: "\u5E7F\u544A\u7EC4\u62A5\u8868 AdGroupReport",
|
|
127969
|
+
segment: "AdGroupReport",
|
|
127970
|
+
takeOption: true
|
|
127971
|
+
},
|
|
127972
|
+
{
|
|
127973
|
+
name: "ads",
|
|
127974
|
+
description: "\u5E7F\u544A\u62A5\u8868 AdReport",
|
|
127975
|
+
segment: "AdReport",
|
|
127976
|
+
takeOption: true
|
|
127977
|
+
},
|
|
127978
|
+
{
|
|
127979
|
+
name: "videos",
|
|
127980
|
+
description: "\u89C6\u9891\u7D20\u6750\u62A5\u8868 VideoReport",
|
|
127981
|
+
segment: "VideoReport",
|
|
127982
|
+
takeOption: true
|
|
127983
|
+
},
|
|
127984
|
+
{
|
|
127985
|
+
name: "audience-gender",
|
|
127986
|
+
description: "\u53D7\u4F17\uFF08\u6027\u522B\uFF09AudienceReport dimensions=gender",
|
|
127987
|
+
segment: "AudienceReport",
|
|
127988
|
+
audienceDimension: "gender",
|
|
127989
|
+
takeOption: true
|
|
127990
|
+
},
|
|
127991
|
+
{
|
|
127992
|
+
name: "audience-age",
|
|
127993
|
+
description: "\u53D7\u4F17\uFF08\u5E74\u9F84\uFF09AudienceReport dimensions=age",
|
|
127994
|
+
segment: "AudienceReport",
|
|
127995
|
+
audienceDimension: "age",
|
|
127996
|
+
takeOption: true
|
|
127997
|
+
},
|
|
127998
|
+
{
|
|
127999
|
+
name: "audience-interest-category",
|
|
128000
|
+
description: "\u53D7\u4F17\uFF08\u5174\u8DA3\u7C7B\u76EE\uFF09AudienceReport dimensions=interest_category",
|
|
128001
|
+
segment: "AudienceReport",
|
|
128002
|
+
audienceDimension: "interest_category",
|
|
128003
|
+
takeOption: true
|
|
128004
|
+
},
|
|
128005
|
+
{
|
|
128006
|
+
name: "audience-country-code",
|
|
128007
|
+
description: "\u53D7\u4F17\uFF08\u56FD\u5BB6/\u5730\u533A\uFF09AudienceReport dimensions=country_code",
|
|
128008
|
+
segment: "AudienceReport",
|
|
128009
|
+
audienceDimension: "country_code",
|
|
128010
|
+
takeOption: true
|
|
128011
|
+
},
|
|
128012
|
+
{
|
|
128013
|
+
name: "audience-platform",
|
|
128014
|
+
description: "\u53D7\u4F17\uFF08\u5E73\u53F0\uFF09AudienceReport dimensions=platform",
|
|
128015
|
+
segment: "AudienceReport",
|
|
128016
|
+
audienceDimension: "platform",
|
|
128017
|
+
takeOption: true
|
|
128018
|
+
},
|
|
128019
|
+
{
|
|
128020
|
+
name: "audience-language",
|
|
128021
|
+
description: "\u53D7\u4F17\uFF08\u8BED\u8A00\uFF09AudienceReport dimensions=language",
|
|
128022
|
+
segment: "AudienceReport",
|
|
128023
|
+
audienceDimension: "language",
|
|
128024
|
+
takeOption: true
|
|
128025
|
+
},
|
|
128026
|
+
{
|
|
128027
|
+
name: "audience-merged",
|
|
128028
|
+
description: "\u53D7\u4F17\u5408\u5E76\uFF08gender + age + interest_category \u4E09\u6B21 AudienceReport \u5408\u5E76\uFF0C\u4E0E getTikTokAudienceApi \u4E00\u81F4\uFF09",
|
|
128029
|
+
segment: "AudienceReport(gender+age+interest_category)",
|
|
128030
|
+
takeOption: true
|
|
128031
|
+
}
|
|
128032
|
+
];
|
|
128033
|
+
var TIKTOK_SECTION_NAMES = TIKTOK_SECTIONS.map((s) => s.name);
|
|
128034
|
+
var TIKTOK_SECTION_ALIASES = {
|
|
128035
|
+
"ad-groups-report": "ad-groups",
|
|
128036
|
+
audience: "audience-merged"
|
|
128037
|
+
};
|
|
128038
|
+
|
|
128039
|
+
// src/commands/tiktok-analysis/resolve-sections.ts
|
|
128040
|
+
function normalizeSectionToken3(raw) {
|
|
128066
128041
|
const t = raw.trim();
|
|
128067
128042
|
if (!t) return null;
|
|
128068
|
-
if (
|
|
128043
|
+
if (TIKTOK_SECTION_NAMES.includes(t)) {
|
|
128069
128044
|
return t;
|
|
128070
128045
|
}
|
|
128071
|
-
return
|
|
128046
|
+
return TIKTOK_SECTION_ALIASES[t] ?? null;
|
|
128072
128047
|
}
|
|
128073
|
-
function
|
|
128048
|
+
function resolveSectionList4(sections, exclude) {
|
|
128074
128049
|
const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
|
|
128075
128050
|
const include = splitClean(sections);
|
|
128076
128051
|
const dropRaw = splitClean(exclude);
|
|
128077
|
-
const unknown = [...include, ...dropRaw].filter((n) =>
|
|
128052
|
+
const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken3(n) === null);
|
|
128078
128053
|
if (unknown.length > 0) {
|
|
128079
128054
|
console.error(
|
|
128080
128055
|
`
|
|
128081
128056
|
\u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
|
|
128082
|
-
\u53EF\u9009\uFF1A${
|
|
128083
|
-
|
|
128057
|
+
\u53EF\u9009\uFF1A${TIKTOK_SECTION_NAMES.join(", ")}
|
|
128058
|
+
\u522B\u540D\uFF1A${Object.keys(TIKTOK_SECTION_ALIASES).join(", ")}
|
|
128084
128059
|
`
|
|
128085
128060
|
);
|
|
128086
128061
|
process.exit(1);
|
|
128087
128062
|
}
|
|
128088
|
-
const includeCanonical = new Set(include.map((n) =>
|
|
128089
|
-
const dropCanonical = new Set(dropRaw.map((n) =>
|
|
128090
|
-
const base = include.length > 0 ?
|
|
128063
|
+
const includeCanonical = new Set(include.map((n) => normalizeSectionToken3(n)).filter(Boolean));
|
|
128064
|
+
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken3(n)).filter(Boolean));
|
|
128065
|
+
const base = include.length > 0 ? TIKTOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...TIKTOK_SECTIONS];
|
|
128091
128066
|
return base.filter((s) => !dropCanonical.has(s.name));
|
|
128092
128067
|
}
|
|
128093
128068
|
|
|
128094
|
-
// src/commands/
|
|
128095
|
-
|
|
128069
|
+
// src/commands/tiktok-analysis/fetch.ts
|
|
128070
|
+
init_auth();
|
|
128071
|
+
var TIKTOK_TAKE_DEFAULT = 100;
|
|
128072
|
+
function resolveTake(raw) {
|
|
128073
|
+
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
128074
|
+
return Number.isFinite(n) && n > 0 ? n : TIKTOK_TAKE_DEFAULT;
|
|
128075
|
+
}
|
|
128076
|
+
function resolveDateRange2(start, end) {
|
|
128077
|
+
if (start && end) return { startDate: start, endDate: end };
|
|
128078
|
+
if (!start && !end) {
|
|
128079
|
+
const endD = /* @__PURE__ */ new Date();
|
|
128080
|
+
endD.setDate(endD.getDate() - 1);
|
|
128081
|
+
const startD = new Date(endD);
|
|
128082
|
+
startD.setDate(startD.getDate() - 6);
|
|
128083
|
+
const fmt2 = (d) => d.toISOString().slice(0, 10);
|
|
128084
|
+
return { startDate: fmt2(startD), endDate: fmt2(endD) };
|
|
128085
|
+
}
|
|
128086
|
+
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");
|
|
128087
|
+
process.exit(1);
|
|
128088
|
+
}
|
|
128089
|
+
function assertTikTokAccountId(id) {
|
|
128090
|
+
const t = id.trim();
|
|
128091
|
+
if (!t) {
|
|
128092
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
128093
|
+
process.exit(1);
|
|
128094
|
+
}
|
|
128095
|
+
if (!/^\d+$/.test(t)) {
|
|
128096
|
+
console.error(
|
|
128097
|
+
"\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"
|
|
128098
|
+
);
|
|
128099
|
+
process.exit(1);
|
|
128100
|
+
}
|
|
128101
|
+
return t;
|
|
128102
|
+
}
|
|
128103
|
+
function reportingUrl2(config, accountId, segment, query) {
|
|
128104
|
+
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
128105
|
+
return `${config.apiBaseUrl}/reporting/media-account/TikTok/${accountId}/${segment}${q}`;
|
|
128106
|
+
}
|
|
128107
|
+
async function fetchTikTokJson(config, url, verbose) {
|
|
128108
|
+
return apiFetch2(url, config, {}, verbose);
|
|
128109
|
+
}
|
|
128110
|
+
async function fetchTikTokAudienceMergedPayload(config, id, startDate, endDate, take, verbose) {
|
|
128111
|
+
const dims = ["gender", "age", "interest_category"];
|
|
128112
|
+
const parts = {};
|
|
128113
|
+
for (const dimension of dims) {
|
|
128114
|
+
const params = new URLSearchParams({
|
|
128115
|
+
startDate,
|
|
128116
|
+
endDate,
|
|
128117
|
+
take: String(take),
|
|
128118
|
+
dimensions: dimension
|
|
128119
|
+
});
|
|
128120
|
+
const url = reportingUrl2(config, id, "AudienceReport", params.toString());
|
|
128121
|
+
parts[dimension] = await fetchTikTokJson(config, url, verbose);
|
|
128122
|
+
}
|
|
128123
|
+
return { mergedDimensions: dims, startDate, endDate, account: id, data: parts };
|
|
128124
|
+
}
|
|
128125
|
+
async function fetchTikTokSectionPayload(def, opts, config, id) {
|
|
128126
|
+
const { startDate, endDate } = resolveDateRange2(opts.start, opts.end);
|
|
128127
|
+
const verbose = !!opts.verbose;
|
|
128128
|
+
const take = resolveTake(opts.take);
|
|
128129
|
+
if (def.name === "audience-merged") {
|
|
128130
|
+
return fetchTikTokAudienceMergedPayload(config, id, startDate, endDate, take, verbose);
|
|
128131
|
+
}
|
|
128132
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
128133
|
+
if (def.takeOption) params.set("take", String(take));
|
|
128134
|
+
if (def.audienceDimension) params.set("dimensions", def.audienceDimension);
|
|
128135
|
+
const url = reportingUrl2(config, id, def.segment, params.toString());
|
|
128136
|
+
return fetchTikTokJson(config, url, verbose);
|
|
128137
|
+
}
|
|
128138
|
+
function endpointHintForTikTokSection(def) {
|
|
128139
|
+
return `GET \u2026/TikTok/{id}/${def.segment}`;
|
|
128140
|
+
}
|
|
128141
|
+
|
|
128142
|
+
// src/commands/tiktok-analysis/auxiliary.ts
|
|
128143
|
+
init_auth();
|
|
128144
|
+
async function fetchTikTokAreacode(config, verbose) {
|
|
128145
|
+
const base = config.mainApiUrl?.trim();
|
|
128146
|
+
if (!base) {
|
|
128147
|
+
console.error("\n\u274C \u65E0\u6CD5\u63A8\u5BFC mainApiUrl\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
|
|
128148
|
+
process.exit(1);
|
|
128149
|
+
}
|
|
128150
|
+
const url = `${base}/query/media-account/tiktok/TikTokAreacode/Read`;
|
|
128151
|
+
return apiFetch2(url, config, {}, verbose);
|
|
128152
|
+
}
|
|
128153
|
+
async function fetchTikTokInterestList(config, account, verbose) {
|
|
128154
|
+
const id = assertTikTokAccountId(account);
|
|
128155
|
+
const tt = config.tiktokApiUrl?.trim();
|
|
128156
|
+
if (!tt) {
|
|
128157
|
+
console.error("\n\u274C \u672A\u914D\u7F6E tiktokApiUrl\u3002\n");
|
|
128158
|
+
process.exit(1);
|
|
128159
|
+
}
|
|
128160
|
+
const params = new URLSearchParams({ mediaCustomerId: id });
|
|
128161
|
+
const url = `${tt}/tiktokadgroupmanagement/adgroup/tiktok/GetInterestList?${params}`;
|
|
128162
|
+
const data = await apiFetch2(url, config, {}, verbose);
|
|
128163
|
+
return { id, data };
|
|
128164
|
+
}
|
|
128165
|
+
|
|
128166
|
+
// src/commands/tiktok-analysis/run-batch.ts
|
|
128167
|
+
init_auth();
|
|
128168
|
+
import * as path32 from "path";
|
|
128169
|
+
import { performance as performance7 } from "perf_hooks";
|
|
128170
|
+
init_version();
|
|
128171
|
+
async function runWithConcurrency4(tasks, concurrency) {
|
|
128096
128172
|
const results = new Array(tasks.length);
|
|
128097
128173
|
let cursor = 0;
|
|
128098
128174
|
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
@@ -128105,35 +128181,40 @@ async function runWithConcurrency2(tasks, concurrency) {
|
|
|
128105
128181
|
await Promise.all(workers);
|
|
128106
128182
|
return results;
|
|
128107
128183
|
}
|
|
128108
|
-
async function
|
|
128109
|
-
const
|
|
128184
|
+
async function runAllTikTokSections(opts) {
|
|
128185
|
+
const id = assertTikTokAccountId(opts.account);
|
|
128110
128186
|
const config = loadConfig(opts.token);
|
|
128111
128187
|
if (!opts.jsonOut?.trim()) {
|
|
128112
128188
|
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
128189
|
process.exit(1);
|
|
128114
128190
|
}
|
|
128115
|
-
const targets =
|
|
128191
|
+
const targets = resolveSectionList4(opts.sections, opts.exclude);
|
|
128116
128192
|
if (targets.length === 0) {
|
|
128117
128193
|
console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
|
|
128118
128194
|
process.exit(1);
|
|
128119
128195
|
}
|
|
128120
|
-
const { startDate, endDate } =
|
|
128196
|
+
const { startDate, endDate } = resolveDateRange2(opts.start, opts.end);
|
|
128121
128197
|
const concurrency = Math.max(
|
|
128122
128198
|
1,
|
|
128123
128199
|
Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
|
|
128124
128200
|
);
|
|
128125
128201
|
const cliVersion = getCurrentVersion2();
|
|
128126
|
-
const overallT0 =
|
|
128202
|
+
const overallT0 = performance7.now();
|
|
128127
128203
|
const tasks = targets.map(
|
|
128128
128204
|
(def) => async () => {
|
|
128129
|
-
const t0 =
|
|
128130
|
-
const hint =
|
|
128205
|
+
const t0 = performance7.now();
|
|
128206
|
+
const hint = endpointHintForTikTokSection(def);
|
|
128131
128207
|
try {
|
|
128132
|
-
const payload = await
|
|
128208
|
+
const payload = await fetchTikTokSectionPayload(
|
|
128209
|
+
def,
|
|
128210
|
+
{ ...opts, start: startDate, end: endDate },
|
|
128211
|
+
config,
|
|
128212
|
+
id
|
|
128213
|
+
);
|
|
128133
128214
|
const summary2 = await writeReportAnalysisSnapshot({
|
|
128134
128215
|
snapshotDir: opts.jsonOut,
|
|
128135
128216
|
section: def.name,
|
|
128136
|
-
accountId:
|
|
128217
|
+
accountId: id,
|
|
128137
128218
|
dateRange: { start: startDate, end: endDate },
|
|
128138
128219
|
payload,
|
|
128139
128220
|
cliVersion
|
|
@@ -128141,7 +128222,7 @@ async function runAllFacebookSections(opts) {
|
|
|
128141
128222
|
return {
|
|
128142
128223
|
section: def.name,
|
|
128143
128224
|
ok: true,
|
|
128144
|
-
elapsedMs:
|
|
128225
|
+
elapsedMs: performance7.now() - t0,
|
|
128145
128226
|
file: summary2.writtenFiles[0],
|
|
128146
128227
|
endpointHint: hint
|
|
128147
128228
|
};
|
|
@@ -128149,69 +128230,118 @@ async function runAllFacebookSections(opts) {
|
|
|
128149
128230
|
return {
|
|
128150
128231
|
section: def.name,
|
|
128151
128232
|
ok: false,
|
|
128152
|
-
elapsedMs:
|
|
128233
|
+
elapsedMs: performance7.now() - t0,
|
|
128153
128234
|
error: err instanceof Error ? err.message : String(err),
|
|
128154
128235
|
endpointHint: hint
|
|
128155
128236
|
};
|
|
128156
128237
|
}
|
|
128157
128238
|
}
|
|
128158
128239
|
);
|
|
128159
|
-
const results = await
|
|
128240
|
+
const results = await runWithConcurrency4(tasks, concurrency);
|
|
128160
128241
|
const succeeded = results.filter((r) => r.ok).length;
|
|
128161
128242
|
const failed = results.length - succeeded;
|
|
128162
|
-
const absoluteSnapshotDir =
|
|
128243
|
+
const absoluteSnapshotDir = path32.resolve(opts.jsonOut);
|
|
128163
128244
|
const summary = {
|
|
128164
|
-
kind: "siluzan-tso-
|
|
128245
|
+
kind: "siluzan-tso-tiktok-analysis-snapshot-batch",
|
|
128165
128246
|
absoluteSnapshotDir,
|
|
128166
|
-
manifestFile: reportManifestFile(
|
|
128167
|
-
accountId:
|
|
128247
|
+
manifestFile: reportManifestFile(id),
|
|
128248
|
+
accountId: id,
|
|
128168
128249
|
totalSections: results.length,
|
|
128169
128250
|
succeeded,
|
|
128170
128251
|
failed,
|
|
128171
128252
|
concurrency,
|
|
128172
|
-
elapsedMs:
|
|
128253
|
+
elapsedMs: performance7.now() - overallT0,
|
|
128173
128254
|
results
|
|
128174
128255
|
};
|
|
128175
128256
|
console.log(JSON.stringify(summary));
|
|
128176
128257
|
if (failed > 0) process.exit(2);
|
|
128177
128258
|
}
|
|
128178
128259
|
|
|
128179
|
-
// src/commands/
|
|
128180
|
-
|
|
128181
|
-
|
|
128182
|
-
|
|
128183
|
-
|
|
128260
|
+
// src/commands/tiktok-analysis/register-cli.ts
|
|
128261
|
+
init_version();
|
|
128262
|
+
init_auth();
|
|
128263
|
+
import { Command as Command3 } from "commander";
|
|
128264
|
+
function registerBatchCommand3(sectionHelp, aliasHelp) {
|
|
128265
|
+
return new Command3("run").description(
|
|
128266
|
+
"\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"
|
|
128267
|
+
).requiredOption("-a, --account <id>", "TikTok \u5E7F\u544A\u4E3B mediaCustomerId\uFF08\u6570\u5B57\uFF09").requiredOption(
|
|
128184
128268
|
"--json-out <dir>",
|
|
128185
128269
|
"\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
|
|
128186
128270
|
).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
128271
|
"--sections <list>",
|
|
128188
|
-
`\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,
|
|
128272
|
+
`\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
128273
|
).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
|
|
128190
|
-
"--
|
|
128191
|
-
"\
|
|
128274
|
+
"--take <n>",
|
|
128275
|
+
"\u6D88\u8017/\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF0C\u4E0E\u524D\u7AEF take \u4E00\u81F4\uFF1Boverview \u4E0D\u9002\u7528\uFF09",
|
|
128192
128276
|
(v) => parseInt(v, 10)
|
|
128193
128277
|
).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
|
|
128278
|
+
await runAllTikTokSections(opts);
|
|
128195
128279
|
});
|
|
128196
128280
|
}
|
|
128197
|
-
function
|
|
128198
|
-
const sectionHelp =
|
|
128199
|
-
const
|
|
128200
|
-
|
|
128201
|
-
);
|
|
128202
|
-
root.
|
|
128203
|
-
|
|
128204
|
-
"\
|
|
128205
|
-
).
|
|
128206
|
-
|
|
128207
|
-
|
|
128208
|
-
|
|
128209
|
-
|
|
128210
|
-
|
|
128211
|
-
|
|
128212
|
-
|
|
128213
|
-
|
|
128281
|
+
function registerTikTokAnalysisCommands(program2) {
|
|
128282
|
+
const sectionHelp = TIKTOK_SECTION_NAMES.join(", ");
|
|
128283
|
+
const aliasHelp = Object.keys(TIKTOK_SECTION_ALIASES).join(", ");
|
|
128284
|
+
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");
|
|
128285
|
+
root.addCommand(registerBatchCommand3(sectionHelp, aliasHelp), { isDefault: true });
|
|
128286
|
+
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(
|
|
128287
|
+
"--json-out <dir>",
|
|
128288
|
+
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
128289
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
128290
|
+
const config = loadConfig(opts.token);
|
|
128291
|
+
try {
|
|
128292
|
+
const data = await fetchTikTokAreacode(config, !!opts.verbose);
|
|
128293
|
+
if (opts.jsonOut) {
|
|
128294
|
+
const summary = await writeReportAnalysisSnapshot({
|
|
128295
|
+
snapshotDir: opts.jsonOut,
|
|
128296
|
+
section: "areacode",
|
|
128297
|
+
dateRange: null,
|
|
128298
|
+
payload: data,
|
|
128299
|
+
cliVersion: getCurrentVersion2()
|
|
128300
|
+
});
|
|
128301
|
+
console.log(JSON.stringify(summary));
|
|
128302
|
+
return;
|
|
128303
|
+
}
|
|
128304
|
+
console.log("\n\u2705 TikTok \u5730\u533A\u4EE3\u7801\u8868\uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09\n");
|
|
128305
|
+
} catch (err) {
|
|
128306
|
+
console.error(`
|
|
128307
|
+
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
128308
|
+
`);
|
|
128309
|
+
process.exit(1);
|
|
128310
|
+
}
|
|
128214
128311
|
});
|
|
128312
|
+
root.command("interest-list").description(
|
|
128313
|
+
"TikTok \u5174\u8DA3\u7C7B\u76EE\uFF08tiktokApiUrl GetInterestList\uFF0C\u89E3\u6790 country_code/interest \u62A5\u8868\u65F6\u5E38\u7528\uFF09"
|
|
128314
|
+
).requiredOption("-a, --account <id>", "TikTok mediaCustomerId").option("-t, --token <token>", "Auth Token").option(
|
|
128315
|
+
"--json-out <dir>",
|
|
128316
|
+
"\u5C06 JSON \u5199\u5165\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest-<accountId>.json\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
|
|
128317
|
+
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
128318
|
+
async (opts) => {
|
|
128319
|
+
const config = loadConfig(opts.token);
|
|
128320
|
+
try {
|
|
128321
|
+
const { id, data } = await fetchTikTokInterestList(config, opts.account, !!opts.verbose);
|
|
128322
|
+
if (opts.jsonOut) {
|
|
128323
|
+
const summary = await writeReportAnalysisSnapshot({
|
|
128324
|
+
snapshotDir: opts.jsonOut,
|
|
128325
|
+
section: "interest-list",
|
|
128326
|
+
accountId: id,
|
|
128327
|
+
dateRange: null,
|
|
128328
|
+
payload: data,
|
|
128329
|
+
cliVersion: getCurrentVersion2()
|
|
128330
|
+
});
|
|
128331
|
+
console.log(JSON.stringify(summary));
|
|
128332
|
+
return;
|
|
128333
|
+
}
|
|
128334
|
+
console.log(`
|
|
128335
|
+
\u2705 TikTok \u5174\u8DA3\u7C7B\u76EE ${id}\uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09
|
|
128336
|
+
`);
|
|
128337
|
+
} catch (err) {
|
|
128338
|
+
console.error(`
|
|
128339
|
+
\u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
128340
|
+
`);
|
|
128341
|
+
process.exit(1);
|
|
128342
|
+
}
|
|
128343
|
+
}
|
|
128344
|
+
);
|
|
128215
128345
|
}
|
|
128216
128346
|
|
|
128217
128347
|
// src/index.ts
|
|
@@ -128220,8 +128350,8 @@ init_cli_json_snapshot();
|
|
|
128220
128350
|
installProcessHandlers();
|
|
128221
128351
|
function getVersion() {
|
|
128222
128352
|
try {
|
|
128223
|
-
const __dirname3 =
|
|
128224
|
-
const pkgPath =
|
|
128353
|
+
const __dirname3 = path33.dirname(fileURLToPath7(import.meta.url));
|
|
128354
|
+
const pkgPath = path33.join(__dirname3, "..", "package.json");
|
|
128225
128355
|
const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf8"));
|
|
128226
128356
|
return pkg.version ?? "0.0.0";
|
|
128227
128357
|
} catch {
|
|
@@ -128237,7 +128367,7 @@ configureCliJsonSnapshot({
|
|
|
128237
128367
|
"references/accounts/accounts.md"
|
|
128238
128368
|
]
|
|
128239
128369
|
});
|
|
128240
|
-
var program = new
|
|
128370
|
+
var program = new Command4();
|
|
128241
128371
|
program.name("siluzan-tso").description(
|
|
128242
128372
|
"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
128373
|
).version(getVersion());
|
|
@@ -128271,6 +128401,8 @@ var REGISTRARS = [
|
|
|
128271
128401
|
registerGoogleAnalysisCommands,
|
|
128272
128402
|
registerGoogleAnalysisBatchCommands,
|
|
128273
128403
|
registerFacebookAnalysisCommands,
|
|
128404
|
+
registerBingAnalysisCommands,
|
|
128405
|
+
registerTikTokAnalysisCommands,
|
|
128274
128406
|
register14,
|
|
128275
128407
|
register15,
|
|
128276
128408
|
register16,
|