siluzan-tso-cli 1.1.13 → 1.1.14-beta.10
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 +2 -3
- package/assets/siluzan-ads/references/hosted-automation-user-catalog.md +40 -0
- package/dist/index.js +371 -55
- package/dist/skill/SKILL.md +78 -37
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/account-analytics.md +69 -2
- package/dist/skill/references/accounts.md +8 -5
- package/dist/skill/references/clue.md +5 -0
- package/dist/skill/references/finance.md +9 -5
- package/dist/skill/references/forewarning.md +7 -0
- package/dist/skill/references/google-ads-rules/google-ads-campaign-optimization.md +3 -2
- package/dist/skill/references/google-ads.md +123 -101
- package/dist/skill/references/hosted-automation-monitoring-json.md +94 -0
- package/dist/skill/references/hosted-automation-optimize-ab-winner.md +69 -0
- package/dist/skill/references/hosted-automation-optimize-index.md +32 -0
- package/dist/skill/references/hosted-automation-optimize-scale.md +99 -0
- package/dist/skill/references/hosted-automation-optimize-weak-downbid.md +94 -0
- package/dist/skill/references/hosted-automation-scenarios.md +23 -0
- package/dist/skill/references/hosted-automation-self-control.md +216 -0
- package/dist/skill/references/hosted-automation-user-catalog.md +40 -0
- package/dist/skill/references/open-account-google-ui.md +0 -6
- package/dist/skill/references/reporting.md +4 -2
- package/dist/skill/references/setup.md +5 -5
- package/dist/skill/references/tips.md +6 -0
- package/dist/skill/references/tso-home.md +1 -1
- package/dist/skill/references/workflows.md +14 -1
- package/dist/skill/report-templates/google-account-diagnosis-report.md +1 -1
- package/dist/skill/report-templates/google-period-report.md +1 -0
- package/dist/skill/scripts/install.ps1 +2 -2
- package/dist/skill/scripts/install.sh +2 -2
- package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +23 -0
- package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +12 -0
- package/eval/cases/accounts-single-balance-not-bulk.scenario.json +23 -0
- package/eval/cases/budget-display-not-raw-micros.scenario.json +17 -0
- package/eval/cases/clue-meta-leads-json.scenario.json +23 -0
- package/eval/cases/clue-tiktok-leads-json.scenario.json +20 -0
- package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +15 -0
- package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +15 -0
- package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +15 -0
- package/eval/cases/destructive-unshare-needs-confirm.scenario.json +9 -0
- package/eval/cases/finance-invoice-info-list.scenario.json +17 -0
- package/eval/cases/forewarning-list-google.scenario.json +20 -0
- package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +12 -0
- package/eval/cases/google-analysis-keywords-route.scenario.json +23 -0
- package/eval/cases/hosted-sop-cpa-spike-downbid.scenario.json +14 -0
- package/eval/cases/hosted-sop-daily-budget-circuit-breaker.scenario.json +13 -0
- package/eval/cases/hosted-sop-empty-spend-pause-p1.scenario.json +14 -0
- package/eval/cases/human-p1-multiturn.scenario.json +17 -0
- package/eval/cases/meta-single-balance-not-bulk.scenario.json +26 -0
- package/eval/cases/open-account-bing-noninteractive.scenario.json +13 -0
- package/eval/cases/open-account-google-noninteractive.scenario.json +12 -0
- package/eval/cases/open-account-tiktok-license-file.scenario.json +12 -0
- package/eval/cases/optimize-list-by-account.scenario.json +17 -0
- package/eval/cases/p1-single-account-profile.scenario.json +20 -0
- package/eval/cases/p2-balance-scan-bulk.scenario.json +18 -0
- package/eval/cases/p3-accounts-digest.scenario.json +20 -0
- package/eval/cases/p4-period-report-window.scenario.json +17 -0
- package/eval/cases/report-list-google.scenario.json +20 -0
- package/eval/cases/report-push-list-google.scenario.json +20 -0
- package/eval/cases/reporting-vs-account-analytics-routing.scenario.json +13 -0
- package/eval/cases/setup-login-or-env.scenario.json +12 -0
- package/eval/cases/setup-siluzan-data-permission-env.scenario.json +19 -0
- package/eval/cases/skill-async-poll-guidance.scenario.json +9 -0
- package/eval/cases/skill-optimize-vs-google-ads-distinction.scenario.json +13 -0
- package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +12 -0
- package/eval/cases/time-range-must-ask.scenario.json +16 -0
- package/eval/cases/time-range-user-delegates-default.scenario.json +17 -0
- package/eval/cases/tips-json-filtering.scenario.json +12 -0
- package/eval/cases/tips-large-json-pagination.scenario.json +19 -0
- package/eval/cases/uj-ad-bluetooth-keywords-exclude-cheap-free.scenario.json +9 -0
- package/eval/cases/uj-ad-keywords-camping-tent-outdoor-plan.scenario.json +9 -0
- package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +12 -0
- package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +29 -0
- package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +17 -0
- package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +20 -0
- package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +12 -0
- package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +20 -0
- package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +13 -0
- package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +23 -0
- package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +23 -0
- package/eval/cases/uj-ops-open-google-b2c-usd-shenzhen.scenario.json +13 -0
- package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +12 -0
- package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +23 -0
- package/eval/cases/uj-patrol-all-media-balance-stats-forewarning.scenario.json +9 -0
- package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +18 -0
- package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +12 -0
- package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +23 -0
- package/eval/cases/uj-patrol-google-balances-low.scenario.json +20 -0
- package/eval/cases/uj-roi-full-google-account-diagnosis.scenario.json +9 -0
- package/eval/cases/uj-roi-keywords-high-cpa-low-cvr-triage.scenario.json +9 -0
- package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +20 -0
- package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +23 -0
- package/eval/stub-fixtures/accounts-digest.json +33 -0
- package/eval/stub-fixtures/ad-campaigns.json +14 -0
- package/eval/stub-fixtures/balance-meta.json +6 -0
- package/eval/stub-fixtures/balance-scan.json +21 -0
- package/eval/stub-fixtures/balance.json +6 -0
- package/eval/stub-fixtures/clue-meta.json +7 -0
- package/eval/stub-fixtures/clue-tiktok.json +7 -0
- package/eval/stub-fixtures/forewarning-create-ok.json +1 -0
- package/eval/stub-fixtures/forewarning-records.json +7 -0
- package/eval/stub-fixtures/generic-ok.json +1 -0
- package/eval/stub-fixtures/google-analysis-search-terms.json +10 -0
- package/eval/stub-fixtures/google-analysis.json +7 -0
- package/eval/stub-fixtures/invoice-billable.json +7 -0
- package/eval/stub-fixtures/invoice-info-list.json +13 -0
- package/eval/stub-fixtures/invoice-list.json +10 -0
- package/eval/stub-fixtures/list-accounts.json +13 -0
- package/eval/stub-fixtures/optimize-list.json +7 -0
- package/eval/stub-fixtures/report-push-list.json +7 -0
- package/eval/stub-fixtures/stats.json +9 -0
- package/package.json +7 -2
- package/scripts/postinstall.mjs +27 -2
- package/dist/skill/references/open-account-by-media.md +0 -61
package/dist/index.js
CHANGED
|
@@ -1962,7 +1962,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
1962
1962
|
import { Command } from "commander";
|
|
1963
1963
|
|
|
1964
1964
|
// src/config/defaults.ts
|
|
1965
|
-
var DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
1965
|
+
var DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
1966
1966
|
|
|
1967
1967
|
// ../common/dist/index.js
|
|
1968
1968
|
import * as fs from "fs";
|
|
@@ -2441,19 +2441,19 @@ import * as fs3 from "fs/promises";
|
|
|
2441
2441
|
import * as path3 from "path";
|
|
2442
2442
|
async function getSkillFiles(skillDir) {
|
|
2443
2443
|
const out = {};
|
|
2444
|
-
async function
|
|
2444
|
+
async function walk2(dir, prefix) {
|
|
2445
2445
|
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
2446
2446
|
for (const ent of entries) {
|
|
2447
2447
|
const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
|
|
2448
2448
|
const full = path3.join(dir, ent.name);
|
|
2449
2449
|
if (ent.isDirectory()) {
|
|
2450
|
-
await
|
|
2450
|
+
await walk2(full, rel);
|
|
2451
2451
|
} else {
|
|
2452
2452
|
out[rel] = await fs3.readFile(full, "utf8");
|
|
2453
2453
|
}
|
|
2454
2454
|
}
|
|
2455
2455
|
}
|
|
2456
|
-
await
|
|
2456
|
+
await walk2(skillDir, "");
|
|
2457
2457
|
return out;
|
|
2458
2458
|
}
|
|
2459
2459
|
|
|
@@ -2872,8 +2872,9 @@ function fmt(d) {
|
|
|
2872
2872
|
}
|
|
2873
2873
|
function defaultDateRange() {
|
|
2874
2874
|
const end = /* @__PURE__ */ new Date();
|
|
2875
|
-
|
|
2876
|
-
start
|
|
2875
|
+
end.setDate(end.getDate() - 1);
|
|
2876
|
+
const start = new Date(end);
|
|
2877
|
+
start.setDate(start.getDate() - 6);
|
|
2877
2878
|
return { startDate: fmt(start), endDate: fmt(end) };
|
|
2878
2879
|
}
|
|
2879
2880
|
async function fetchBalanceMap(media, accountIds, config, startDate, endDate, verbose) {
|
|
@@ -3676,18 +3677,25 @@ var VALID_MEDIA_TYPES2 = ["Google", "TikTok", "Yandex", "MetaAd", "BingV2", "Kwa
|
|
|
3676
3677
|
async function runBalance(opts) {
|
|
3677
3678
|
const config = loadConfig(opts.token);
|
|
3678
3679
|
if (!VALID_MEDIA_TYPES2.includes(opts.media)) {
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3680
|
+
const msg = `\u4E0D\u652F\u6301\u7684\u5A92\u4F53\u7C7B\u578B\uFF1A${opts.media}\uFF08\u53EF\u9009\uFF1A${VALID_MEDIA_TYPES2.join(" | ")}\uFF09`;
|
|
3681
|
+
if (opts.json) {
|
|
3682
|
+
console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
|
|
3683
|
+
process.exit(1);
|
|
3684
|
+
}
|
|
3685
|
+
console.error(`
|
|
3686
|
+
\u274C ${msg}
|
|
3687
|
+
`);
|
|
3685
3688
|
process.exit(1);
|
|
3686
3689
|
}
|
|
3687
3690
|
const media = opts.media;
|
|
3688
3691
|
if (!BALANCE_SUPPORTED_MEDIA.includes(media)) {
|
|
3692
|
+
const msg = `${media} \u6682\u4E0D\u652F\u6301\u4F59\u989D\u67E5\u8BE2`;
|
|
3693
|
+
if (opts.json) {
|
|
3694
|
+
console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
|
|
3695
|
+
process.exit(1);
|
|
3696
|
+
}
|
|
3689
3697
|
console.error(`
|
|
3690
|
-
\u26A0\uFE0F ${
|
|
3698
|
+
\u26A0\uFE0F ${msg}
|
|
3691
3699
|
`);
|
|
3692
3700
|
process.exit(1);
|
|
3693
3701
|
}
|
|
@@ -3707,8 +3715,13 @@ async function runBalance(opts) {
|
|
|
3707
3715
|
opts.verbose
|
|
3708
3716
|
);
|
|
3709
3717
|
} catch (err) {
|
|
3718
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3719
|
+
if (opts.json) {
|
|
3720
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
3721
|
+
process.exit(1);
|
|
3722
|
+
}
|
|
3710
3723
|
console.error(`
|
|
3711
|
-
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${
|
|
3724
|
+
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${message}
|
|
3712
3725
|
`);
|
|
3713
3726
|
process.exit(1);
|
|
3714
3727
|
}
|
|
@@ -3948,18 +3961,54 @@ async function runBalanceScan(opts) {
|
|
|
3948
3961
|
const CHUNK = 100;
|
|
3949
3962
|
const chunks = [];
|
|
3950
3963
|
for (let i = 0; i < validIds.length; i += CHUNK) chunks.push(validIds.slice(i, i + CHUNK));
|
|
3964
|
+
process.stderr.write(
|
|
3965
|
+
`\u23F3 [balance-scan] \u6709\u6548\u8D26\u6237 ${validIds.length} \u4E2A\uFF0C\u5206 ${chunks.length} \u6279\uFF1B\u4F59\u989D\u4E0E\u8FD1 7 \u65E5\u6D88\u8017\u5E76\u884C\u8BF7\u6C42\uFF08\u5355\u8BF7\u6C42\u6700\u957F\u7EA6 10 \u5206\u949F\uFF09\u3002
|
|
3966
|
+
`
|
|
3967
|
+
);
|
|
3968
|
+
const logBalanceChunk = (idx, ids, m) => {
|
|
3969
|
+
process.stderr.write(
|
|
3970
|
+
` \u2713 [\u4F59\u989D] \u7B2C ${idx + 1}/${chunks.length} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${m.size} \u6761\uFF09
|
|
3971
|
+
`
|
|
3972
|
+
);
|
|
3973
|
+
};
|
|
3974
|
+
const logOverviewChunk = (idx, ids, m) => {
|
|
3975
|
+
process.stderr.write(
|
|
3976
|
+
` \u2713 [\u8FD17\u65E5\u6D88\u8017] \u7B2C ${idx + 1}/${chunks.length} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${m.size} \u6761\uFF09
|
|
3977
|
+
`
|
|
3978
|
+
);
|
|
3979
|
+
};
|
|
3951
3980
|
const [bMaps, oMaps] = await Promise.all([
|
|
3952
3981
|
Promise.all(
|
|
3953
|
-
chunks.map(
|
|
3954
|
-
(
|
|
3955
|
-
|
|
3982
|
+
chunks.map((ids, chunkIdx) => {
|
|
3983
|
+
process.stderr.write(
|
|
3984
|
+
` \u2192 [\u4F59\u989D] \u7B2C ${chunkIdx + 1}/${chunks.length} \u6279\u8BF7\u6C42\u4E2D\uFF08${ids.length} \u6237\uFF09\u2026
|
|
3985
|
+
`
|
|
3986
|
+
);
|
|
3987
|
+
return fetchBalanceMap(media, ids, config, void 0, void 0, opts.verbose).then(
|
|
3988
|
+
(m) => {
|
|
3989
|
+
logBalanceChunk(chunkIdx, ids, m);
|
|
3990
|
+
return m;
|
|
3991
|
+
}
|
|
3992
|
+
);
|
|
3993
|
+
})
|
|
3956
3994
|
),
|
|
3957
3995
|
Promise.all(
|
|
3958
|
-
chunks.map(
|
|
3959
|
-
(
|
|
3960
|
-
|
|
3996
|
+
chunks.map((ids, chunkIdx) => {
|
|
3997
|
+
process.stderr.write(
|
|
3998
|
+
` \u2192 [\u8FD17\u65E5\u6D88\u8017] \u7B2C ${chunkIdx + 1}/${chunks.length} \u6279\u8BF7\u6C42\u4E2D\uFF08${ids.length} \u6237\uFF09\u2026
|
|
3999
|
+
`
|
|
4000
|
+
);
|
|
4001
|
+
return fetchOverviewMap(media, ids, config, void 0, void 0, opts.verbose).then(
|
|
4002
|
+
(m) => {
|
|
4003
|
+
logOverviewChunk(chunkIdx, ids, m);
|
|
4004
|
+
return m;
|
|
4005
|
+
}
|
|
4006
|
+
);
|
|
4007
|
+
})
|
|
3961
4008
|
)
|
|
3962
4009
|
]);
|
|
4010
|
+
process.stderr.write(`\u23F3 [balance-scan] \u4F59\u989D\u4E0E\u6D88\u8017\u5DF2\u9F50\uFF0C\u6B63\u5728\u6309\u9608\u503C\u7B5B\u9009\u2026
|
|
4011
|
+
`);
|
|
3963
4012
|
for (const m of bMaps) for (const [k, v] of m) balanceMap.set(k, v);
|
|
3964
4013
|
for (const m of oMaps) for (const [k, v] of m) overviewMap.set(k, v);
|
|
3965
4014
|
}
|
|
@@ -4382,8 +4431,13 @@ async function runStats(opts) {
|
|
|
4382
4431
|
try {
|
|
4383
4432
|
raw = await apiFetch2(url, config, {}, opts.verbose);
|
|
4384
4433
|
} catch (err) {
|
|
4434
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4435
|
+
if (opts.json) {
|
|
4436
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
4437
|
+
process.exit(1);
|
|
4438
|
+
}
|
|
4385
4439
|
console.error(`
|
|
4386
|
-
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${
|
|
4440
|
+
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${message}
|
|
4387
4441
|
`);
|
|
4388
4442
|
process.exit(1);
|
|
4389
4443
|
}
|
|
@@ -5070,6 +5124,37 @@ async function runReportPushReceiveEmails(opts) {
|
|
|
5070
5124
|
console.log();
|
|
5071
5125
|
}
|
|
5072
5126
|
|
|
5127
|
+
// src/utils/strip-legacy-google-fields.ts
|
|
5128
|
+
var LEGACY_WHEN_V2_PRESENT = [
|
|
5129
|
+
["status", "statusV2"],
|
|
5130
|
+
["channelType", "channelTypeV2"],
|
|
5131
|
+
["subChannelType", "subChannelTypeV2"],
|
|
5132
|
+
["biddingStrategyType", "biddingStrategyTypeV2"],
|
|
5133
|
+
["campaignStatus", "campaignStatusV2"],
|
|
5134
|
+
["adGroupStatus", "adGroupStatusV2"],
|
|
5135
|
+
["matchType", "matchTypeV2"],
|
|
5136
|
+
["type", "typeV2"]
|
|
5137
|
+
];
|
|
5138
|
+
function stripLegacyGoogleFieldsIfV2Present(value) {
|
|
5139
|
+
return walk(value);
|
|
5140
|
+
}
|
|
5141
|
+
function walk(obj) {
|
|
5142
|
+
if (Array.isArray(obj)) return obj.map(walk);
|
|
5143
|
+
if (obj !== null && typeof obj === "object") {
|
|
5144
|
+
const o = { ...obj };
|
|
5145
|
+
for (const [legacy, modern] of LEGACY_WHEN_V2_PRESENT) {
|
|
5146
|
+
if (modern in o && legacy in o) {
|
|
5147
|
+
delete o[legacy];
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
for (const k of Object.keys(o)) {
|
|
5151
|
+
o[k] = walk(o[k]);
|
|
5152
|
+
}
|
|
5153
|
+
return o;
|
|
5154
|
+
}
|
|
5155
|
+
return obj;
|
|
5156
|
+
}
|
|
5157
|
+
|
|
5073
5158
|
// src/commands/google-analysis.ts
|
|
5074
5159
|
var SECTIONS = [
|
|
5075
5160
|
{
|
|
@@ -5100,6 +5185,12 @@ var SECTIONS = [
|
|
|
5100
5185
|
dateMode: "range",
|
|
5101
5186
|
path: (id) => `/reporting/media-account/${id}/CampaignSectionData`
|
|
5102
5187
|
},
|
|
5188
|
+
{
|
|
5189
|
+
name: "campaign-hour",
|
|
5190
|
+
description: "\u7CFB\u5217\u6309\u5C0F\u65F6 campaign-hour\uFF08Query\uFF1AstartDate\u3001endDate\uFF09",
|
|
5191
|
+
dateMode: "range",
|
|
5192
|
+
path: (id) => `/reporting/media-account/${id}/campaign-hour`
|
|
5193
|
+
},
|
|
5103
5194
|
{
|
|
5104
5195
|
name: "ads",
|
|
5105
5196
|
description: "\u5E7F\u544A\u7EA7\u5217\u8868 admanagement/v2/list",
|
|
@@ -5235,6 +5326,55 @@ async function fetchJson(config, pathWithQuery, verbose) {
|
|
|
5235
5326
|
const url = `${config.googleApiUrl}${pathWithQuery}`;
|
|
5236
5327
|
return apiFetch2(url, config, {}, verbose);
|
|
5237
5328
|
}
|
|
5329
|
+
function assertNever(x, ctx) {
|
|
5330
|
+
throw new Error(`${ctx}\uFF1A\u672A\u5904\u7406\u7684\u5206\u652F ${String(x)}`);
|
|
5331
|
+
}
|
|
5332
|
+
async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name) {
|
|
5333
|
+
switch (name) {
|
|
5334
|
+
case "overview":
|
|
5335
|
+
return fetchJson(config, fullPath, verbose);
|
|
5336
|
+
case "keywords":
|
|
5337
|
+
return fetchJson(config, fullPath, verbose);
|
|
5338
|
+
case "search-terms":
|
|
5339
|
+
return fetchJson(config, fullPath, verbose);
|
|
5340
|
+
case "campaigns":
|
|
5341
|
+
return fetchJson(config, fullPath, verbose);
|
|
5342
|
+
case "campaign-hour":
|
|
5343
|
+
return fetchJson(config, fullPath, verbose);
|
|
5344
|
+
case "ads":
|
|
5345
|
+
return fetchJson(config, fullPath, verbose);
|
|
5346
|
+
case "extensions":
|
|
5347
|
+
return fetchJson(config, fullPath, verbose);
|
|
5348
|
+
case "devices":
|
|
5349
|
+
return fetchJson(config, fullPath, verbose);
|
|
5350
|
+
case "geographic":
|
|
5351
|
+
return fetchJson(config, fullPath, verbose);
|
|
5352
|
+
case "audience":
|
|
5353
|
+
return fetchJson(config, fullPath, verbose);
|
|
5354
|
+
case "asset-images":
|
|
5355
|
+
return fetchJson(config, fullPath, verbose);
|
|
5356
|
+
case "videos":
|
|
5357
|
+
return fetchJson(config, fullPath, verbose);
|
|
5358
|
+
case "resource-counts":
|
|
5359
|
+
return fetchJson(config, fullPath, verbose);
|
|
5360
|
+
case "conversion-actions":
|
|
5361
|
+
return fetchJson(config, fullPath, verbose);
|
|
5362
|
+
case "daily-metrics":
|
|
5363
|
+
return fetchJson(config, fullPath, verbose);
|
|
5364
|
+
case "gold-account":
|
|
5365
|
+
return fetchJson(config, fullPath, verbose);
|
|
5366
|
+
case "ads-index":
|
|
5367
|
+
return fetchJson(config, fullPath, verbose);
|
|
5368
|
+
case "final-urls":
|
|
5369
|
+
return fetchJson(config, fullPath, verbose);
|
|
5370
|
+
case "dimension-summary":
|
|
5371
|
+
return fetchJson(config, fullPath, verbose);
|
|
5372
|
+
case "campaign-types":
|
|
5373
|
+
return fetchJson(config, fullPath, verbose);
|
|
5374
|
+
default:
|
|
5375
|
+
return assertNever(name, "google-analysis");
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5238
5378
|
function summarizeHuman(section, data) {
|
|
5239
5379
|
if (data === null || data === void 0) return "\u65E0\u6570\u636E";
|
|
5240
5380
|
if (Array.isArray(data)) return `\u6570\u7EC4\uFF0C\u5171 ${data.length} \u6761`;
|
|
@@ -5292,7 +5432,7 @@ async function runOneSection(def, opts) {
|
|
|
5292
5432
|
]);
|
|
5293
5433
|
const merged = { images, videos };
|
|
5294
5434
|
if (opts.json) {
|
|
5295
|
-
console.log(JSON.stringify(merged, null, 2));
|
|
5435
|
+
console.log(JSON.stringify(stripLegacyGoogleFieldsIfV2Present(merged), null, 2));
|
|
5296
5436
|
return;
|
|
5297
5437
|
}
|
|
5298
5438
|
const iLen = Array.isArray(images) ? images.length : 0;
|
|
@@ -5305,9 +5445,14 @@ async function runOneSection(def, opts) {
|
|
|
5305
5445
|
);
|
|
5306
5446
|
return;
|
|
5307
5447
|
}
|
|
5308
|
-
const data = await
|
|
5448
|
+
const data = await fetchGoogleAnalysisSectionJson(
|
|
5449
|
+
config,
|
|
5450
|
+
fullPath,
|
|
5451
|
+
!!opts.verbose,
|
|
5452
|
+
def.name
|
|
5453
|
+
);
|
|
5309
5454
|
if (opts.json) {
|
|
5310
|
-
console.log(JSON.stringify(data, null, 2));
|
|
5455
|
+
console.log(JSON.stringify(stripLegacyGoogleFieldsIfV2Present(data), null, 2));
|
|
5311
5456
|
return;
|
|
5312
5457
|
}
|
|
5313
5458
|
console.log(
|
|
@@ -6791,6 +6936,12 @@ function toDisplayMoney(raw) {
|
|
|
6791
6936
|
if (!Number.isFinite(n)) return null;
|
|
6792
6937
|
return n / 100;
|
|
6793
6938
|
}
|
|
6939
|
+
function toCentAmount(displayValue) {
|
|
6940
|
+
if (!Number.isFinite(displayValue)) {
|
|
6941
|
+
throw new Error(`toCentAmount: \u8F93\u5165\u5FC5\u987B\u662F\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${displayValue}`);
|
|
6942
|
+
}
|
|
6943
|
+
return Math.round(displayValue * 100);
|
|
6944
|
+
}
|
|
6794
6945
|
function adgroupListUrl(googleApiUrl, account, startDate, endDate) {
|
|
6795
6946
|
const params = new URLSearchParams();
|
|
6796
6947
|
params.set("startDate", toGoogleDate(startDate, -30));
|
|
@@ -6806,6 +6957,39 @@ function requireGoogleApi(config) {
|
|
|
6806
6957
|
}
|
|
6807
6958
|
return config.googleApiUrl;
|
|
6808
6959
|
}
|
|
6960
|
+
function formatGoogleCampaignListStatus(row) {
|
|
6961
|
+
let result = "-";
|
|
6962
|
+
const raw = row.statusV2;
|
|
6963
|
+
if (raw == null || String(raw).trim() === "") return result;
|
|
6964
|
+
const statusV2 = String(raw).toUpperCase();
|
|
6965
|
+
const start = parseCampaignTimeMs(row.startTime);
|
|
6966
|
+
const end = parseCampaignTimeMs(row.endTime);
|
|
6967
|
+
if (start == null || end == null) return result;
|
|
6968
|
+
const now = Date.now();
|
|
6969
|
+
if (statusV2 === "PAUSED") {
|
|
6970
|
+
if (now > start && now < end) {
|
|
6971
|
+
result = "\u5DF2\u6682\u505C";
|
|
6972
|
+
} else if (now > end) {
|
|
6973
|
+
result = "\u5DF2\u7ED3\u675F\u4F7F\u7528";
|
|
6974
|
+
} else if (now < start) {
|
|
6975
|
+
result = "\u672A\u6295\u653E";
|
|
6976
|
+
}
|
|
6977
|
+
} else if (statusV2 === "ENABLED") {
|
|
6978
|
+
if (now > start && now < end) {
|
|
6979
|
+
result = "\u6709\u6548";
|
|
6980
|
+
} else if (now > end) {
|
|
6981
|
+
result = "\u5DF2\u7ED3\u675F\u4F7F\u7528";
|
|
6982
|
+
} else if (now < start) {
|
|
6983
|
+
result = "\u672A\u6295\u653E";
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6986
|
+
return result;
|
|
6987
|
+
}
|
|
6988
|
+
function parseCampaignTimeMs(v) {
|
|
6989
|
+
if (v == null || v === "") return null;
|
|
6990
|
+
const t = new Date(v).getTime();
|
|
6991
|
+
return Number.isFinite(t) ? t : null;
|
|
6992
|
+
}
|
|
6809
6993
|
async function runAdCampaigns(opts) {
|
|
6810
6994
|
const config = loadConfig(opts.token);
|
|
6811
6995
|
const googleApiUrl = requireGoogleApi(config);
|
|
@@ -6828,18 +7012,19 @@ async function runAdCampaigns(opts) {
|
|
|
6828
7012
|
return {
|
|
6829
7013
|
...item,
|
|
6830
7014
|
budgetDisplay,
|
|
6831
|
-
budgetUnit: "display"
|
|
7015
|
+
budgetUnit: "display",
|
|
7016
|
+
statusDisplay: formatGoogleCampaignListStatus(item)
|
|
6832
7017
|
};
|
|
6833
7018
|
});
|
|
6834
7019
|
const n = items.length;
|
|
6835
7020
|
if (opts.json) {
|
|
6836
7021
|
console.log(
|
|
6837
7022
|
JSON.stringify(
|
|
6838
|
-
{
|
|
7023
|
+
stripLegacyGoogleFieldsIfV2Present({
|
|
6839
7024
|
...wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items }),
|
|
6840
7025
|
code: data.code ?? null,
|
|
6841
7026
|
message: data.message ?? null
|
|
6842
|
-
},
|
|
7027
|
+
}),
|
|
6843
7028
|
null,
|
|
6844
7029
|
2
|
|
6845
7030
|
)
|
|
@@ -6874,7 +7059,7 @@ async function runAdCampaigns(opts) {
|
|
|
6874
7059
|
const budget = item.budgetDisplay != null ? item.budgetDisplay.toFixed(2) : "\u2014";
|
|
6875
7060
|
return {
|
|
6876
7061
|
name: (item.name ?? "").slice(0, nameW),
|
|
6877
|
-
status: item.
|
|
7062
|
+
status: item.statusDisplay ?? formatGoogleCampaignListStatus(item),
|
|
6878
7063
|
channelType: item.channelTypeV2 ?? "",
|
|
6879
7064
|
bidding: String(item.biddingStrategyTypeV2 ?? ""),
|
|
6880
7065
|
budget,
|
|
@@ -6917,10 +7102,10 @@ async function runAdGroups(opts) {
|
|
|
6917
7102
|
if (opts.json) {
|
|
6918
7103
|
console.log(
|
|
6919
7104
|
JSON.stringify(
|
|
6920
|
-
{
|
|
7105
|
+
stripLegacyGoogleFieldsIfV2Present({
|
|
6921
7106
|
...wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items }),
|
|
6922
7107
|
code: data.code ?? null
|
|
6923
|
-
},
|
|
7108
|
+
}),
|
|
6924
7109
|
null,
|
|
6925
7110
|
2
|
|
6926
7111
|
)
|
|
@@ -6976,6 +7161,9 @@ async function runAdList(opts) {
|
|
|
6976
7161
|
const params = new URLSearchParams();
|
|
6977
7162
|
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
6978
7163
|
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
7164
|
+
if (opts.includeDeleted) {
|
|
7165
|
+
params.set("readDeleted", "true");
|
|
7166
|
+
}
|
|
6979
7167
|
const url = `${googleApiUrl}/admanagement/v2/list/${opts.account}?${params}`;
|
|
6980
7168
|
let data;
|
|
6981
7169
|
try {
|
|
@@ -6990,7 +7178,13 @@ async function runAdList(opts) {
|
|
|
6990
7178
|
const n = items.length;
|
|
6991
7179
|
if (opts.json) {
|
|
6992
7180
|
console.log(
|
|
6993
|
-
JSON.stringify(
|
|
7181
|
+
JSON.stringify(
|
|
7182
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
7183
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
7184
|
+
),
|
|
7185
|
+
null,
|
|
7186
|
+
2
|
|
7187
|
+
)
|
|
6994
7188
|
);
|
|
6995
7189
|
return;
|
|
6996
7190
|
}
|
|
@@ -7027,7 +7221,7 @@ async function runAdList(opts) {
|
|
|
7027
7221
|
campaign: String(item["campaign"] ?? "").slice(0, campW),
|
|
7028
7222
|
adGroup: String(item["adGroup"] ?? "").slice(0, grpW),
|
|
7029
7223
|
status: String(item["statusV2"]),
|
|
7030
|
-
type: String(item["typeV2"] ??
|
|
7224
|
+
type: String(item["typeV2"] ?? ""),
|
|
7031
7225
|
impressions: String(item["impressions"] ?? 0),
|
|
7032
7226
|
clicks: String(item["clicks"] ?? 0),
|
|
7033
7227
|
ctr,
|
|
@@ -7066,7 +7260,13 @@ async function runAdKeywords(opts) {
|
|
|
7066
7260
|
const n = items.length;
|
|
7067
7261
|
if (opts.json) {
|
|
7068
7262
|
console.log(
|
|
7069
|
-
JSON.stringify(
|
|
7263
|
+
JSON.stringify(
|
|
7264
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
7265
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
7266
|
+
),
|
|
7267
|
+
null,
|
|
7268
|
+
2
|
|
7269
|
+
)
|
|
7070
7270
|
);
|
|
7071
7271
|
return;
|
|
7072
7272
|
}
|
|
@@ -7082,7 +7282,7 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
7082
7282
|
if (opts.negative) {
|
|
7083
7283
|
items.forEach((item) => {
|
|
7084
7284
|
const kwText = Array.isArray(item["keywordText"]) ? item["keywordText"].join(", ") : String(item["text"] ?? item["keywordText"] ?? item["id"] ?? "\u2014");
|
|
7085
|
-
const matchType = item["matchTypeV2"] ??
|
|
7285
|
+
const matchType = item["matchTypeV2"] ?? "\u2014";
|
|
7086
7286
|
console.log(` [${matchType}] ${kwText} id: ${String(item["id"] ?? "")}`);
|
|
7087
7287
|
});
|
|
7088
7288
|
} else {
|
|
@@ -7113,8 +7313,8 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
7113
7313
|
const kwText = String(item["text"] ?? item["keywordText"] ?? "\u2014");
|
|
7114
7314
|
const campaign = String(item["campaignName"] ?? item["campaign"] ?? "");
|
|
7115
7315
|
const adGroup = String(item["adGroupName"] ?? item["adGroup"] ?? "");
|
|
7116
|
-
const status = String(item["
|
|
7117
|
-
const matchType = String(item["matchTypeV2"] ??
|
|
7316
|
+
const status = String(item["userStatus"] ?? "");
|
|
7317
|
+
const matchType = String(item["matchTypeV2"] ?? "");
|
|
7118
7318
|
const ctr = item["ctr"] != null ? (Number(item["ctr"]) * 100).toFixed(2) + "%" : "\u2014";
|
|
7119
7319
|
const spend = item["spend"] != null ? Number(item["spend"]).toFixed(2) : "\u2014";
|
|
7120
7320
|
return {
|
|
@@ -7211,7 +7411,8 @@ async function runAdGroupCreate(opts) {
|
|
|
7211
7411
|
campaignId: opts.campaignId,
|
|
7212
7412
|
campaignName: opts.campaignName,
|
|
7213
7413
|
manualCpc_EnhancedCpcEnabled: false,
|
|
7214
|
-
|
|
7414
|
+
// 用户传主币种金额,统一 toCentAmount 转「分」(与前端、adgroup-edit 一致)
|
|
7415
|
+
maxCPCAmount: toCentAmount(opts.maxCpc),
|
|
7215
7416
|
name: opts.name,
|
|
7216
7417
|
rotationMode: 1,
|
|
7217
7418
|
statusV2: opts.status ?? "ENABLED",
|
|
@@ -7914,6 +8115,53 @@ async function runAdGroupRename(opts) {
|
|
|
7914
8115
|
\u2705 \u5E7F\u544A\u7EC4 ${opts.id} \u5DF2\u6539\u540D\u4E3A\u300C${opts.name}\u300D
|
|
7915
8116
|
`);
|
|
7916
8117
|
}
|
|
8118
|
+
async function runAdGroupEdit(opts) {
|
|
8119
|
+
const hasName = opts.name !== void 0 && opts.name.trim() !== "";
|
|
8120
|
+
const hasMax = opts.maxCPCAmount !== void 0;
|
|
8121
|
+
const hasTcpa = opts.targetCpaAmount !== void 0;
|
|
8122
|
+
if (!hasName && !hasMax && !hasTcpa) {
|
|
8123
|
+
console.error(
|
|
8124
|
+
"\n\u274C \u8BF7\u81F3\u5C11\u6307\u5B9A\u4E00\u9879\u4FEE\u6539\uFF1A--name / --max-cpc / --target-cpa\n"
|
|
8125
|
+
);
|
|
8126
|
+
process.exit(1);
|
|
8127
|
+
}
|
|
8128
|
+
if (hasMax && (!Number.isFinite(opts.maxCPCAmount) || opts.maxCPCAmount < 0)) {
|
|
8129
|
+
console.error("\n\u274C --max-cpc \u987B\u4E3A\u975E\u8D1F\u6570\u5B57\n");
|
|
8130
|
+
process.exit(1);
|
|
8131
|
+
}
|
|
8132
|
+
if (hasTcpa && (!Number.isFinite(opts.targetCpaAmount) || opts.targetCpaAmount < 0)) {
|
|
8133
|
+
console.error("\n\u274C --target-cpa \u987B\u4E3A\u975E\u8D1F\u6570\u5B57\n");
|
|
8134
|
+
process.exit(1);
|
|
8135
|
+
}
|
|
8136
|
+
const config = loadConfig(opts.token);
|
|
8137
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
8138
|
+
const listUrl = adgroupListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate);
|
|
8139
|
+
let adgroup;
|
|
8140
|
+
try {
|
|
8141
|
+
adgroup = await findItemInList(listUrl, config, opts.id, opts.verbose);
|
|
8142
|
+
} catch (err) {
|
|
8143
|
+
console.error(`
|
|
8144
|
+
\u274C \u67E5\u8BE2\u5E7F\u544A\u7EC4\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
8145
|
+
`);
|
|
8146
|
+
process.exit(1);
|
|
8147
|
+
}
|
|
8148
|
+
const body = { ...adgroup };
|
|
8149
|
+
if (hasName) body["name"] = opts.name.trim();
|
|
8150
|
+
if (hasMax) body["maxCPCAmount"] = toCentAmount(opts.maxCPCAmount);
|
|
8151
|
+
if (hasTcpa) body["targetCpaAmount"] = toCentAmount(opts.targetCpaAmount);
|
|
8152
|
+
const putUrl = `${googleApiUrl}/adgroupnmanagement/adgroup/${opts.account}/${opts.id}`;
|
|
8153
|
+
try {
|
|
8154
|
+
await apiFetch2(putUrl, config, { method: "PUT", body: JSON.stringify(body) }, opts.verbose);
|
|
8155
|
+
} catch (err) {
|
|
8156
|
+
console.error(`
|
|
8157
|
+
\u274C \u7F16\u8F91\u5E7F\u544A\u7EC4\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
8158
|
+
`);
|
|
8159
|
+
process.exit(1);
|
|
8160
|
+
}
|
|
8161
|
+
console.log(`
|
|
8162
|
+
\u2705 \u5E7F\u544A\u7EC4 ${opts.id} \u5DF2\u66F4\u65B0
|
|
8163
|
+
`);
|
|
8164
|
+
}
|
|
7917
8165
|
async function runAdEdit(opts) {
|
|
7918
8166
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
7919
8167
|
const googleApiUrl = requireGoogleApi(config);
|
|
@@ -8101,11 +8349,12 @@ async function runAdCampaignEdit(opts) {
|
|
|
8101
8349
|
}
|
|
8102
8350
|
const body = { ...campaign };
|
|
8103
8351
|
if (opts.name !== void 0) body["name"] = opts.name;
|
|
8104
|
-
if (opts.budget !== void 0) body["budget"] = opts.budget;
|
|
8352
|
+
if (opts.budget !== void 0) body["budget"] = toCentAmount(opts.budget);
|
|
8105
8353
|
if (opts.biddingStrategy !== void 0) body["biddingStrategyTypeV2"] = opts.biddingStrategy;
|
|
8106
8354
|
if (opts.targetSpendBidCeiling !== void 0)
|
|
8107
|
-
body["targetSpend_BidCeilingAmount"] = opts.targetSpendBidCeiling;
|
|
8108
|
-
if (opts.targetCpa !== void 0)
|
|
8355
|
+
body["targetSpend_BidCeilingAmount"] = toCentAmount(opts.targetSpendBidCeiling);
|
|
8356
|
+
if (opts.targetCpa !== void 0)
|
|
8357
|
+
body["targetCpa_BidingAmount"] = toCentAmount(opts.targetCpa);
|
|
8109
8358
|
if (opts.targetSearchNetwork !== void 0)
|
|
8110
8359
|
body["targetSearchNetwork"] = opts.targetSearchNetwork;
|
|
8111
8360
|
if (opts.targetContentNetwork !== void 0)
|
|
@@ -8145,7 +8394,13 @@ async function runAdExtensionList(opts) {
|
|
|
8145
8394
|
const n = items.length;
|
|
8146
8395
|
if (opts.json) {
|
|
8147
8396
|
console.log(
|
|
8148
|
-
JSON.stringify(
|
|
8397
|
+
JSON.stringify(
|
|
8398
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
8399
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
8400
|
+
),
|
|
8401
|
+
null,
|
|
8402
|
+
2
|
|
8403
|
+
)
|
|
8149
8404
|
);
|
|
8150
8405
|
return;
|
|
8151
8406
|
}
|
|
@@ -8278,7 +8533,13 @@ async function runAdSearchTerms(opts) {
|
|
|
8278
8533
|
const n = items.length;
|
|
8279
8534
|
if (opts.json) {
|
|
8280
8535
|
console.log(
|
|
8281
|
-
JSON.stringify(
|
|
8536
|
+
JSON.stringify(
|
|
8537
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
8538
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
8539
|
+
),
|
|
8540
|
+
null,
|
|
8541
|
+
2
|
|
8542
|
+
)
|
|
8282
8543
|
);
|
|
8283
8544
|
return;
|
|
8284
8545
|
}
|
|
@@ -8321,7 +8582,7 @@ async function runAdSearchTerms(opts) {
|
|
|
8321
8582
|
term: term.slice(0, termW),
|
|
8322
8583
|
campaign: String(item["campaignName"] ?? "").slice(0, campW),
|
|
8323
8584
|
adGroup: String(item["adGroupName"] ?? "").slice(0, grpW),
|
|
8324
|
-
matchType: String(item["
|
|
8585
|
+
matchType: String(item["matchTypeV2"] ?? ""),
|
|
8325
8586
|
impressions: String(item["impressions"] ?? 0),
|
|
8326
8587
|
clicks: String(item["clicks"] ?? 0),
|
|
8327
8588
|
ctr,
|
|
@@ -8355,7 +8616,7 @@ async function runAdGeoSearch(opts) {
|
|
|
8355
8616
|
for (const item of items) {
|
|
8356
8617
|
const id = String(item["id"] ?? "");
|
|
8357
8618
|
const name = String(item["locationName"] ?? item["canonicalName"] ?? item["name"] ?? "");
|
|
8358
|
-
const type = String(item["targetType"] ?? item["typeV2"] ??
|
|
8619
|
+
const type = String(item["targetType"] ?? item["typeV2"] ?? "");
|
|
8359
8620
|
console.log(` id:${id} ${name} [${type}]`);
|
|
8360
8621
|
}
|
|
8361
8622
|
console.log();
|
|
@@ -8391,7 +8652,13 @@ async function runAdGeoList(opts) {
|
|
|
8391
8652
|
const n = items.length;
|
|
8392
8653
|
if (opts.json) {
|
|
8393
8654
|
console.log(
|
|
8394
|
-
JSON.stringify(
|
|
8655
|
+
JSON.stringify(
|
|
8656
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
8657
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
8658
|
+
),
|
|
8659
|
+
null,
|
|
8660
|
+
2
|
|
8661
|
+
)
|
|
8395
8662
|
);
|
|
8396
8663
|
return;
|
|
8397
8664
|
}
|
|
@@ -9415,8 +9682,13 @@ async function fetchTikTokClues(opts, config) {
|
|
|
9415
9682
|
);
|
|
9416
9683
|
leads = Array.isArray(res) ? res : res.data ?? [];
|
|
9417
9684
|
} catch (err) {
|
|
9685
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9686
|
+
if (opts.json) {
|
|
9687
|
+
console.log(JSON.stringify({ ok: false, error: message, items: [] }, null, 2));
|
|
9688
|
+
process.exit(1);
|
|
9689
|
+
}
|
|
9418
9690
|
console.error(`
|
|
9419
|
-
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${
|
|
9691
|
+
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${message}
|
|
9420
9692
|
`);
|
|
9421
9693
|
process.exit(1);
|
|
9422
9694
|
}
|
|
@@ -9463,8 +9735,13 @@ async function fetchMetaClues(opts, config) {
|
|
|
9463
9735
|
try {
|
|
9464
9736
|
raw = await apiFetch2(url, config, {}, opts.verbose);
|
|
9465
9737
|
} catch (err) {
|
|
9738
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9739
|
+
if (opts.json) {
|
|
9740
|
+
console.log(JSON.stringify({ ok: false, error: message, items: [] }, null, 2));
|
|
9741
|
+
process.exit(1);
|
|
9742
|
+
}
|
|
9466
9743
|
console.error(`
|
|
9467
|
-
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${
|
|
9744
|
+
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${message}
|
|
9468
9745
|
`);
|
|
9469
9746
|
process.exit(1);
|
|
9470
9747
|
}
|
|
@@ -12121,14 +12398,14 @@ program.command("accounts-digest").description(
|
|
|
12121
12398
|
program.command("stats").description("\u67E5\u8BE2\u5E7F\u544A\u6D88\u8017\u3001\u70B9\u51FB\u3001\u8F6C\u5316\u7B49\u6295\u653E\u6570\u636E\uFF08\u9ED8\u8BA4\u8FD1 7 \u5929\uFF0C\u4E0D\u542B\u4ECA\u5929\uFF09").requiredOption(
|
|
12122
12399
|
"-m, --media <type>",
|
|
12123
12400
|
"\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai"
|
|
12124
|
-
).option("-a, --accounts <ids>", "\u8D26\u6237 ID\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF08\u7559\u7A7A\u5219\u67E5\u5168\u90E8\u8D26\u6237\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F\uFF0C\u683C\u5F0F YYYY-MM-DD\uFF08\u9ED8\u8BA4 7 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF0C\u683C\u5F0F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u6628\u5929\uFF09").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA\u539F\u59CB\u54CD\u5E94", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
12401
|
+
).option("-a, --accounts <ids>", "\u8D26\u6237 ID\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF08\u7559\u7A7A\u5219\u67E5\u5168\u90E8\u8D26\u6237\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F\uFF0C\u683C\u5F0F YYYY-MM-DD\uFF08\u9ED8\u8BA4 7 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF0C\u683C\u5F0F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u6628\u5929\uFF09").option("--start-date <date>", "\u540C --start\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--end-date <date>", "\u540C --end\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA\u539F\u59CB\u54CD\u5E94", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
12125
12402
|
async (opts) => {
|
|
12126
12403
|
await runStats({
|
|
12127
12404
|
token: opts.token,
|
|
12128
12405
|
media: opts.media,
|
|
12129
12406
|
accounts: opts.accounts,
|
|
12130
|
-
startDate: opts.start,
|
|
12131
|
-
endDate: opts.end,
|
|
12407
|
+
startDate: opts.start ?? opts.startDate,
|
|
12408
|
+
endDate: opts.end ?? opts.endDate,
|
|
12132
12409
|
json: opts.json,
|
|
12133
12410
|
verbose: opts.verbose
|
|
12134
12411
|
});
|
|
@@ -12757,13 +13034,14 @@ adCmd.command("groups").description("\u67E5\u8BE2\u5E7F\u544A\u7EC4\u5217\u8868\
|
|
|
12757
13034
|
});
|
|
12758
13035
|
}
|
|
12759
13036
|
);
|
|
12760
|
-
adCmd.command("list").description("\u67E5\u8BE2\u5E7F\u544A\uFF08\u521B\u610F\uFF09\u5217\u8868\uFF08\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option("-t, --token <token>", "Auth Token").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
13037
|
+
adCmd.command("list").description("\u67E5\u8BE2\u5E7F\u544A\uFF08\u521B\u610F\uFF09\u5217\u8868\uFF08\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option("--include-deleted", "\u5305\u542B\u5DF2\u5220\u9664\u7684\u5E7F\u544A\uFF08\u7F51\u5173 readDeleted=true\uFF09", false).option("-t, --token <token>", "Auth Token").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
12761
13038
|
async (opts) => {
|
|
12762
13039
|
await runAdList({
|
|
12763
13040
|
token: opts.token,
|
|
12764
13041
|
account: opts.account,
|
|
12765
13042
|
startDate: opts.start,
|
|
12766
13043
|
endDate: opts.end,
|
|
13044
|
+
includeDeleted: opts.includeDeleted,
|
|
12767
13045
|
json: opts.json,
|
|
12768
13046
|
verbose: opts.verbose
|
|
12769
13047
|
});
|
|
@@ -12807,8 +13085,8 @@ adCmd.command("campaign-delete").description("\u5220\u9664\u5E7F\u544A\u7CFB\u52
|
|
|
12807
13085
|
});
|
|
12808
13086
|
adCmd.command("adgroup-create").description("\u5728\u5E7F\u544A\u7CFB\u5217\u4E0B\u521B\u5EFA\u641C\u7D22\u5E7F\u544A\u7EC4").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--campaign-id <id>", "\u6240\u5C5E\u5E7F\u544A\u7CFB\u5217 ID").requiredOption("--campaign-name <name>", "\u6240\u5C5E\u5E7F\u544A\u7CFB\u5217\u540D\u79F0").requiredOption("--name <name>", "\u5E7F\u544A\u7EC4\u540D\u79F0").requiredOption(
|
|
12809
13087
|
"--max-cpc <amount>",
|
|
12810
|
-
"\u6700\u9AD8\u5355\u6B21\u70B9\u51FB\u8D39\u7528\
|
|
12811
|
-
|
|
13088
|
+
"\u6700\u9AD8\u5355\u6B21\u70B9\u51FB\u8D39\u7528\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 1.5 \u8868\u793A \xA51.50\uFF1BCLI \u5185\u90E8 \xD7100 \u5199\u5165\u300C\u5206\u300D\uFF09",
|
|
13089
|
+
parseFloat
|
|
12812
13090
|
).option("--status <status>", "\u72B6\u6001\uFF1AENABLED | PAUSED\uFF08\u9ED8\u8BA4 ENABLED\uFF09", "ENABLED").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
12813
13091
|
async (opts) => {
|
|
12814
13092
|
await runAdGroupCreate({
|
|
@@ -13118,7 +13396,19 @@ adCmd.command("campaign-create").description(
|
|
|
13118
13396
|
});
|
|
13119
13397
|
}
|
|
13120
13398
|
);
|
|
13121
|
-
adCmd.command("campaign-edit").description("\u7F16\u8F91\u5E7F\u544A\u7CFB\u5217\uFF08\u540D\u79F0/\u9884\u7B97/\u51FA\u4EF7\u7B56\u7565/\u6295\u653E\u7F51\u7EDC\uFF0C\u81F3\u5C11\u4F20\u4E00\u4E2A\u4FEE\u6539\u9879\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <campaignId>", "\u5E7F\u544A\u7CFB\u5217 ID").option("--name <name>", "\u65B0\u540D\u79F0").option(
|
|
13399
|
+
adCmd.command("campaign-edit").description("\u7F16\u8F91\u5E7F\u544A\u7CFB\u5217\uFF08\u540D\u79F0/\u9884\u7B97/\u51FA\u4EF7\u7B56\u7565/\u6295\u653E\u7F51\u7EDC\uFF0C\u81F3\u5C11\u4F20\u4E00\u4E2A\u4FEE\u6539\u9879\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <campaignId>", "\u5E7F\u544A\u7CFB\u5217 ID").option("--name <name>", "\u65B0\u540D\u79F0").option(
|
|
13400
|
+
"--budget <amount>",
|
|
13401
|
+
"\u65B0\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 100 \u8868\u793A \xA5100\uFF1B\u652F\u6301\u5C0F\u6570\uFF1BCLI \u5185\u90E8\u81EA\u52A8 \xD7100 \u5199\u5165\u300C\u5206\u300D\u5B57\u6BB5\uFF09",
|
|
13402
|
+
parseFloat
|
|
13403
|
+
).option("--bidding <strategy>", "\u51FA\u4EF7\u7B56\u7565\uFF1ATARGET_SPEND | TARGET_CPA | TARGET_ROAS | MANUAL_CPC").option(
|
|
13404
|
+
"--bid-ceiling <amount>",
|
|
13405
|
+
"TARGET_SPEND \u51FA\u4EF7\u4E0A\u9650\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF080 = \u4E0D\u9650\uFF1BCLI \u5185\u90E8 \xD7100 \u5199\u5165\uFF09",
|
|
13406
|
+
parseFloat
|
|
13407
|
+
).option(
|
|
13408
|
+
"--target-cpa <amount>",
|
|
13409
|
+
"TARGET_CPA \u76EE\u6807 CPA\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08CLI \u5185\u90E8 \xD7100 \u5199\u5165\uFF09",
|
|
13410
|
+
parseFloat
|
|
13411
|
+
).option(
|
|
13122
13412
|
"--search-network <bool>",
|
|
13123
13413
|
"\u6295\u653E\u5230 Google \u641C\u7D22\uFF1Atrue | false",
|
|
13124
13414
|
(v) => v === "true"
|
|
@@ -13152,6 +13442,29 @@ adCmd.command("adgroup-rename").description("\u4FEE\u6539\u5E7F\u544A\u7EC4\u540
|
|
|
13152
13442
|
});
|
|
13153
13443
|
}
|
|
13154
13444
|
);
|
|
13445
|
+
adCmd.command("adgroup-edit").description("\u7F16\u8F91\u5E7F\u544A\u7EC4\uFF08\u540D\u79F0\u3001\u6700\u9AD8 CPC\u3001\u7EC4\u7EA7\u76EE\u6807 CPA\uFF1B\u81F3\u5C11\u4F20\u4E00\u9879\uFF1B\u4E0E\u7F51\u5173 PUT \u4E00\u81F4\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <adgroupId>", "\u5E7F\u544A\u7EC4 ID").option("--name <name>", "\u65B0\u540D\u79F0").option(
|
|
13446
|
+
"--max-cpc <n>",
|
|
13447
|
+
"\u6700\u9AD8 CPC\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 1.5 \u8868\u793A \xA51.50\uFF1BCLI \u5185\u90E8 \xD7100 \u5199\u5165\u300C\u5206\u300D\u5B57\u6BB5\uFF09",
|
|
13448
|
+
parseFloat
|
|
13449
|
+
).option(
|
|
13450
|
+
"--target-cpa <n>",
|
|
13451
|
+
"\u7EC4\u7EA7\u76EE\u6807 CPA\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08CLI \u5185\u90E8 \xD7100 \u5199\u5165\u300C\u5206\u300D\u5B57\u6BB5\uFF09",
|
|
13452
|
+
parseFloat
|
|
13453
|
+
).option("--start <date>", "\u5217\u8868\u67E5\u8BE2\u8D77\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u5217\u8868\u67E5\u8BE2\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
13454
|
+
async (opts) => {
|
|
13455
|
+
await runAdGroupEdit({
|
|
13456
|
+
token: opts.token,
|
|
13457
|
+
account: opts.account,
|
|
13458
|
+
id: opts.id,
|
|
13459
|
+
name: opts.name,
|
|
13460
|
+
maxCPCAmount: opts.maxCpc,
|
|
13461
|
+
targetCpaAmount: opts.targetCpa,
|
|
13462
|
+
startDate: opts.start,
|
|
13463
|
+
endDate: opts.end,
|
|
13464
|
+
verbose: opts.verbose
|
|
13465
|
+
});
|
|
13466
|
+
}
|
|
13467
|
+
);
|
|
13155
13468
|
adCmd.command("ad-edit").description(
|
|
13156
13469
|
"\u7F16\u8F91\u81EA\u9002\u5E94\u641C\u7D22\u5E7F\u544A\uFF08RESPONSIVE_SEARCH_AD\uFF09\uFF1A\u6807\u9898/\u63CF\u8FF0/\u843D\u5730\u9875/\u8DEF\u5F84/\u72B6\u6001\uFF1B\u4E0E\u7F51\u9875 PUT admanagement/campaign \u884C\u4E3A\u4E00\u81F4"
|
|
13157
13470
|
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <adId>", "\u5E7F\u544A ID").option(
|
|
@@ -13195,7 +13508,10 @@ adCmd.command("keyword-delete").description("\u5220\u9664\u641C\u7D22\u5173\u952
|
|
|
13195
13508
|
});
|
|
13196
13509
|
}
|
|
13197
13510
|
);
|
|
13198
|
-
adCmd.command("keyword-edit").description("\u7F16\u8F91\u641C\u7D22\u5173\u952E\u8BCD\uFF08\u6570\u7EC4 body\uFF0C\u5148 list \u518D\u5408\u5E76\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <keywordId>", "\u5173\u952E\u8BCD ID\uFF08\u6765\u81EA ad keywords --json \u2192 id\uFF09").option("--text <text>", "\u65B0\u5173\u952E\u8BCD\u6587\u672C\uFF08\u5199\u5165 keywordText \u6570\u7EC4\uFF09").option("--match-type <type>", "\u65B0\u5339\u914D\u7C7B\u578B\uFF1ABroad | Phrase | Exact\uFF08\u5199\u5165 matchTypeV2\uFF09").option(
|
|
13511
|
+
adCmd.command("keyword-edit").description("\u7F16\u8F91\u641C\u7D22\u5173\u952E\u8BCD\uFF08\u6570\u7EC4 body\uFF0C\u5148 list \u518D\u5408\u5E76\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <keywordId>", "\u5173\u952E\u8BCD ID\uFF08\u6765\u81EA ad keywords --json \u2192 id\uFF09").option("--text <text>", "\u65B0\u5173\u952E\u8BCD\u6587\u672C\uFF08\u5199\u5165 keywordText \u6570\u7EC4\uFF09").option("--match-type <type>", "\u65B0\u5339\u914D\u7C7B\u578B\uFF1ABroad | Phrase | Exact\uFF08\u5199\u5165 matchTypeV2\uFF09").option(
|
|
13512
|
+
"--max-cpc <n>",
|
|
13513
|
+
"\u6700\u9AD8\u6BCF\u6B21\u70B9\u51FB\u8D39\u7528 maxCPC\uFF0C\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 5 \u8868\u793A \xA55\uFF1B\u26A0\uFE0F \u8FD9\u4E2A\u5B57\u6BB5\u540E\u7AEF\u5355\u4F4D\u5C31\u662F\u300C\u4E3B\u5E01\u79CD\u5143\u300D\uFF0CCLI \u76F4\u63A5\u900F\u4F20\u4E0D\u505A \xD7100\uFF0C\u4E0E budget / \u7EC4 maxCPCAmount \u4E0D\u540C\uFF1B0 \u8868\u793A\u6309\u5E73\u53F0/\u8BA1\u5212\u9ED8\u8BA4\uFF09"
|
|
13514
|
+
).option("--final-url <url>", "\u5173\u952E\u8BCD\u7EA7\u6700\u7EC8\u5230\u8FBE\u7F51\u5740 finalURL").option("--start <date>", "\u5217\u8868\u67E5\u8BE2\u8D77\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u5217\u8868\u67E5\u8BE2\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
13199
13515
|
async (opts) => {
|
|
13200
13516
|
if (opts.matchType && !["Broad", "Phrase", "Exact"].includes(opts.matchType)) {
|
|
13201
13517
|
console.error("\n\u274C --match-type \u53EA\u63A5\u53D7 Broad | Phrase | Exact\n");
|