siluzan-tso-cli 1.1.62 → 1.1.63
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/dist/index.js +1412 -420
- package/dist/skill/SKILL.md +6 -5
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/demand-gen-create-template.json +21 -0
- package/dist/skill/references/core/intent-routing.md +2 -1
- package/dist/skill/references/core/workflows.md +3 -2
- package/dist/skill/references/google-ads/demand-gen-api.md +74 -0
- package/dist/skill/references/google-ads/google-ads-write.md +18 -2
- package/dist/skill/references/google-ads/google-ads.md +4 -2
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -438,9 +438,9 @@ var require_semver = __commonJS({
|
|
|
438
438
|
// This probably shouldn't be used publicly.
|
|
439
439
|
// 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
|
|
440
440
|
case "pre": {
|
|
441
|
-
const
|
|
441
|
+
const base2 = Number(identifierBase) ? 1 : 0;
|
|
442
442
|
if (this.prerelease.length === 0) {
|
|
443
|
-
this.prerelease = [
|
|
443
|
+
this.prerelease = [base2];
|
|
444
444
|
} else {
|
|
445
445
|
let i = this.prerelease.length;
|
|
446
446
|
while (--i >= 0) {
|
|
@@ -453,11 +453,11 @@ var require_semver = __commonJS({
|
|
|
453
453
|
if (identifier === this.prerelease.join(".") && identifierBase === false) {
|
|
454
454
|
throw new Error("invalid increment argument: identifier already exists");
|
|
455
455
|
}
|
|
456
|
-
this.prerelease.push(
|
|
456
|
+
this.prerelease.push(base2);
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
459
|
if (identifier) {
|
|
460
|
-
let prerelease = [identifier,
|
|
460
|
+
let prerelease = [identifier, base2];
|
|
461
461
|
if (identifierBase === false) {
|
|
462
462
|
prerelease = [identifier];
|
|
463
463
|
}
|
|
@@ -3058,8 +3058,8 @@ async function writeCliJsonSnapshot(params) {
|
|
|
3058
3058
|
if (fileName.toLowerCase() === manifestFile.toLowerCase()) {
|
|
3059
3059
|
throw new Error(`\u8F93\u51FA\u6587\u4EF6\u4E0D\u80FD\u547D\u540D\u4E3A ${manifestFile}\uFF0C\u4E0E\u6E05\u5355\u6587\u4EF6\u51B2\u7A81`);
|
|
3060
3060
|
}
|
|
3061
|
-
const
|
|
3062
|
-
section = normalizeCliSnapshotSection(
|
|
3061
|
+
const base2 = fileName.slice(0, -".json".length);
|
|
3062
|
+
section = normalizeCliSnapshotSection(base2 || params.section);
|
|
3063
3063
|
} else {
|
|
3064
3064
|
absDir = await ensureSnapshotDir(trimmed);
|
|
3065
3065
|
section = normalizeCliSnapshotSection(params.section);
|
|
@@ -3374,8 +3374,8 @@ function sanitizeNamespace(ns) {
|
|
|
3374
3374
|
}
|
|
3375
3375
|
function getWriteAuditRootDir(namespace) {
|
|
3376
3376
|
const ns = sanitizeNamespace(namespace);
|
|
3377
|
-
const
|
|
3378
|
-
if (
|
|
3377
|
+
const base2 = process.env.SILUZAN_WRITE_AUDIT_BASE?.trim();
|
|
3378
|
+
if (base2) return path5.join(path5.resolve(base2), ns);
|
|
3379
3379
|
if (ns === "tso") {
|
|
3380
3380
|
const legacyOverride = process.env.SILUZAN_TSO_AUDIT_DIR?.trim();
|
|
3381
3381
|
if (legacyOverride) return path5.resolve(legacyOverride);
|
|
@@ -8607,7 +8607,7 @@ var init_normalize_overview = __esm({
|
|
|
8607
8607
|
function isRecord3(value) {
|
|
8608
8608
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
8609
8609
|
}
|
|
8610
|
-
function
|
|
8610
|
+
function finiteNumber2(value) {
|
|
8611
8611
|
const n = typeof value === "number" ? value : Number(value);
|
|
8612
8612
|
return Number.isFinite(n) ? n : 0;
|
|
8613
8613
|
}
|
|
@@ -8624,16 +8624,16 @@ function targetedLocationsToGeographic(raw) {
|
|
|
8624
8624
|
countryOrRegion: typeof row.locationName === "string" ? row.locationName : "",
|
|
8625
8625
|
campaignId: row.campaignId == null ? void 0 : String(row.campaignId),
|
|
8626
8626
|
campaignName: typeof row.campaignName === "string" ? row.campaignName : "",
|
|
8627
|
-
impressions:
|
|
8628
|
-
clicks:
|
|
8629
|
-
ctr:
|
|
8630
|
-
spend:
|
|
8631
|
-
conversions:
|
|
8632
|
-
allConversions:
|
|
8633
|
-
averageCpc:
|
|
8634
|
-
conversionRate:
|
|
8635
|
-
costPerConversion:
|
|
8636
|
-
interactions:
|
|
8627
|
+
impressions: finiteNumber2(row.impressions),
|
|
8628
|
+
clicks: finiteNumber2(row.clicks),
|
|
8629
|
+
ctr: finiteNumber2(row.ctr) / 100,
|
|
8630
|
+
spend: finiteNumber2(row.spend),
|
|
8631
|
+
conversions: finiteNumber2(row.conversions),
|
|
8632
|
+
allConversions: finiteNumber2(row.allConversions),
|
|
8633
|
+
averageCpc: finiteNumber2(row.averageCpc),
|
|
8634
|
+
conversionRate: finiteNumber2(row.conversionRate),
|
|
8635
|
+
costPerConversion: finiteNumber2(row.costPerConversion),
|
|
8636
|
+
interactions: finiteNumber2(row.interactions),
|
|
8637
8637
|
interactionRate: row.interactionRate ?? null
|
|
8638
8638
|
}));
|
|
8639
8639
|
return { countries };
|
|
@@ -104250,7 +104250,7 @@ function narrativeClaimsMobileSpendLeads(text3) {
|
|
|
104250
104250
|
if (!t.includes("\u79FB\u52A8")) return false;
|
|
104251
104251
|
return /移动(?:端|设备)?(?:为|是)主要消耗/.test(t) || /移动(?:端|设备)?为主要(?:消耗)?设备/.test(t) || /移动(?:端|设备)?(?:消耗|花费|费用|流量)?占比高/.test(t);
|
|
104252
104252
|
}
|
|
104253
|
-
function
|
|
104253
|
+
function asRecord20(value) {
|
|
104254
104254
|
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
104255
104255
|
return null;
|
|
104256
104256
|
}
|
|
@@ -104258,16 +104258,16 @@ function stringList(value) {
|
|
|
104258
104258
|
return (Array.isArray(value) ? value : []).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
104259
104259
|
}
|
|
104260
104260
|
function collectOkkiNarrativeText(data) {
|
|
104261
|
-
const narrative =
|
|
104261
|
+
const narrative = asRecord20(data.narrative) ?? {};
|
|
104262
104262
|
const parts = [];
|
|
104263
|
-
const sheetAnalysis =
|
|
104263
|
+
const sheetAnalysis = asRecord20(narrative.sheetAnalysis) ?? {};
|
|
104264
104264
|
for (const block of Object.values(sheetAnalysis)) {
|
|
104265
|
-
const rec =
|
|
104265
|
+
const rec = asRecord20(block);
|
|
104266
104266
|
parts.push(...stringList(rec?.summary), ...stringList(rec?.suggestions));
|
|
104267
104267
|
}
|
|
104268
|
-
const review =
|
|
104268
|
+
const review = asRecord20(narrative.review) ?? {};
|
|
104269
104269
|
for (const block of Object.values(review)) {
|
|
104270
|
-
const rec =
|
|
104270
|
+
const rec = asRecord20(block);
|
|
104271
104271
|
for (const key of ["overview", "summary", "suggestion"]) {
|
|
104272
104272
|
const value = rec?.[key];
|
|
104273
104273
|
if (typeof value === "string" && value.trim()) parts.push(value.trim());
|
|
@@ -104284,7 +104284,7 @@ var init_okki_display = __esm({
|
|
|
104284
104284
|
// src/commands/google-analysis/merge-snapshot.ts
|
|
104285
104285
|
import * as fs28 from "fs/promises";
|
|
104286
104286
|
import * as path33 from "path";
|
|
104287
|
-
function
|
|
104287
|
+
function asRecord21(value) {
|
|
104288
104288
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
104289
104289
|
return value;
|
|
104290
104290
|
}
|
|
@@ -104411,13 +104411,13 @@ function periodMetricsFromRow(row) {
|
|
|
104411
104411
|
}
|
|
104412
104412
|
function items(envelope) {
|
|
104413
104413
|
if (!envelope || !Array.isArray(envelope.items)) return [];
|
|
104414
|
-
return envelope.items.filter((row) =>
|
|
104414
|
+
return envelope.items.filter((row) => asRecord21(row) !== null);
|
|
104415
104415
|
}
|
|
104416
104416
|
function record(envelope) {
|
|
104417
104417
|
return envelope?.record ?? null;
|
|
104418
104418
|
}
|
|
104419
104419
|
function pickMetricRow(row, extraFields) {
|
|
104420
|
-
const
|
|
104420
|
+
const base2 = {
|
|
104421
104421
|
spend: num4(row.spend) ?? 0,
|
|
104422
104422
|
impressions: num4(row.impressions) ?? 0,
|
|
104423
104423
|
clicks: num4(row.clicks) ?? 0,
|
|
@@ -104427,9 +104427,9 @@ function pickMetricRow(row, extraFields) {
|
|
|
104427
104427
|
costPerConversion: num4(row.costPerConversion)
|
|
104428
104428
|
};
|
|
104429
104429
|
for (const f of extraFields) {
|
|
104430
|
-
if (row[f] !== void 0)
|
|
104430
|
+
if (row[f] !== void 0) base2[f] = row[f];
|
|
104431
104431
|
}
|
|
104432
|
-
return
|
|
104432
|
+
return base2;
|
|
104433
104433
|
}
|
|
104434
104434
|
function sumCampaignRowsToPeriodKpis(rows) {
|
|
104435
104435
|
if (rows.length === 0) return null;
|
|
@@ -104458,14 +104458,14 @@ function sumCampaignRowsToPeriodKpis(rows) {
|
|
|
104458
104458
|
}
|
|
104459
104459
|
async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
104460
104460
|
const absDir = path33.resolve(snapshotDir);
|
|
104461
|
-
const metaAccountId = typeof
|
|
104461
|
+
const metaAccountId = typeof asRecord21(payload.meta)?.accountId === "string" ? asRecord21(payload.meta)?.accountId : void 0;
|
|
104462
104462
|
const manifest = await readManifest(snapshotDir, metaAccountId);
|
|
104463
104463
|
if (!manifest) {
|
|
104464
104464
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C google-analysis --json-out`);
|
|
104465
104465
|
}
|
|
104466
104466
|
const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
|
|
104467
104467
|
const dateRange = manifest.googleAnalysis?.dateRange;
|
|
104468
|
-
const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(
|
|
104468
|
+
const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(asRecord21(payload.meta) ?? void 0);
|
|
104469
104469
|
const sectionMap = /* @__PURE__ */ new Map();
|
|
104470
104470
|
for (const art of manifest.artifacts ?? []) {
|
|
104471
104471
|
const data = await loadSectionFile(absDir, art.file);
|
|
@@ -104473,8 +104473,8 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
104473
104473
|
}
|
|
104474
104474
|
await recoverMissingReportSections(sectionMap, absDir, accountId, preferredDateTag);
|
|
104475
104475
|
const overviewRecord = record(sectionMap.get("overview"));
|
|
104476
|
-
const currentPeriod =
|
|
104477
|
-
const previousPeriod =
|
|
104476
|
+
const currentPeriod = asRecord21(overviewRecord?.currentPeriod);
|
|
104477
|
+
const previousPeriod = asRecord21(overviewRecord?.previousPeriod);
|
|
104478
104478
|
const meta = { ...payload.meta ?? {} };
|
|
104479
104479
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
104480
104480
|
if (!meta.currency) {
|
|
@@ -104630,7 +104630,7 @@ function textLen(value) {
|
|
|
104630
104630
|
function asArray(value) {
|
|
104631
104631
|
return Array.isArray(value) ? value : [];
|
|
104632
104632
|
}
|
|
104633
|
-
function
|
|
104633
|
+
function asRecord22(value) {
|
|
104634
104634
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
104635
104635
|
return value;
|
|
104636
104636
|
}
|
|
@@ -104644,7 +104644,7 @@ function pushMissing(missing, id, chapter, dimension, hint) {
|
|
|
104644
104644
|
}
|
|
104645
104645
|
function validateOkkiWeeklyReportContent(data) {
|
|
104646
104646
|
const missing = [];
|
|
104647
|
-
const meta =
|
|
104647
|
+
const meta = asRecord22(data.meta) ?? {};
|
|
104648
104648
|
if (!textLen(meta.accountId)) {
|
|
104649
104649
|
pushMissing(
|
|
104650
104650
|
missing,
|
|
@@ -104654,7 +104654,7 @@ function validateOkkiWeeklyReportContent(data) {
|
|
|
104654
104654
|
"meta.accountId \u5FC5\u586B\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 overview/manifest"
|
|
104655
104655
|
);
|
|
104656
104656
|
}
|
|
104657
|
-
const snapshotSections =
|
|
104657
|
+
const snapshotSections = asRecord22(data.snapshot?.sections) ?? {};
|
|
104658
104658
|
const snapshotPresent = {};
|
|
104659
104659
|
for (const section of OKKI_SNAPSHOT_SECTIONS) {
|
|
104660
104660
|
const present = snapshotSections[section] === true;
|
|
@@ -104669,12 +104669,12 @@ function validateOkkiWeeklyReportContent(data) {
|
|
|
104669
104669
|
);
|
|
104670
104670
|
}
|
|
104671
104671
|
}
|
|
104672
|
-
const narrative =
|
|
104673
|
-
const sheetAnalysis =
|
|
104674
|
-
const review =
|
|
104672
|
+
const narrative = asRecord22(data.narrative) ?? {};
|
|
104673
|
+
const sheetAnalysis = asRecord22(narrative.sheetAnalysis) ?? {};
|
|
104674
|
+
const review = asRecord22(narrative.review) ?? {};
|
|
104675
104675
|
const sheetAnalysisCounts = {};
|
|
104676
104676
|
for (const key of OKKI_SHEET_KEYS) {
|
|
104677
|
-
const block =
|
|
104677
|
+
const block = asRecord22(sheetAnalysis[key]);
|
|
104678
104678
|
const summary = stringList2(block?.summary);
|
|
104679
104679
|
const suggestions = stringList2(block?.suggestions);
|
|
104680
104680
|
sheetAnalysisCounts[key] = { summary: summary.length, suggestions: suggestions.length };
|
|
@@ -104700,7 +104700,7 @@ function validateOkkiWeeklyReportContent(data) {
|
|
|
104700
104700
|
}
|
|
104701
104701
|
const reviewFilled = {};
|
|
104702
104702
|
for (const key of OKKI_REVIEW_KEYS) {
|
|
104703
|
-
const block =
|
|
104703
|
+
const block = asRecord22(review[key]);
|
|
104704
104704
|
const overview = textLen(block?.overview);
|
|
104705
104705
|
const summary = textLen(block?.summary);
|
|
104706
104706
|
const suggestion = textLen(block?.suggestion);
|
|
@@ -104735,7 +104735,7 @@ function validateOkkiWeeklyReportContent(data) {
|
|
|
104735
104735
|
}
|
|
104736
104736
|
}
|
|
104737
104737
|
const deviceRows2 = Array.isArray(data.tables?.devices) ? data.tables.devices : [];
|
|
104738
|
-
const deviceSpend = summarizeOkkiDeviceSpend(deviceRows2.filter((row) =>
|
|
104738
|
+
const deviceSpend = summarizeOkkiDeviceSpend(deviceRows2.filter((row) => asRecord22(row) !== null));
|
|
104739
104739
|
if (deviceSpend.total > 0 && deviceSpend.desktop >= deviceSpend.mobile && narrativeClaimsMobileSpendLeads(collectOkkiNarrativeText(data))) {
|
|
104740
104740
|
pushMissing(
|
|
104741
104741
|
missing,
|
|
@@ -104824,7 +104824,7 @@ function round25(value) {
|
|
|
104824
104824
|
}
|
|
104825
104825
|
function items2(envelope) {
|
|
104826
104826
|
if (!envelope || !Array.isArray(envelope.items)) return [];
|
|
104827
|
-
return envelope.items.filter((row) =>
|
|
104827
|
+
return envelope.items.filter((row) => asRecord22(row) !== null);
|
|
104828
104828
|
}
|
|
104829
104829
|
function dateTagFromRange2(dateRange) {
|
|
104830
104830
|
if (!dateRange || dateRange.mode !== "range") return void 0;
|
|
@@ -104868,14 +104868,14 @@ async function loadSectionFile2(snapshotDir, file) {
|
|
|
104868
104868
|
}
|
|
104869
104869
|
async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
|
|
104870
104870
|
const absDir = path34.resolve(snapshotDir);
|
|
104871
|
-
const metaAccountId = typeof
|
|
104871
|
+
const metaAccountId = typeof asRecord22(payload.meta)?.accountId === "string" ? asRecord22(payload.meta)?.accountId : void 0;
|
|
104872
104872
|
const manifest = await readManifest2(absDir, metaAccountId);
|
|
104873
104873
|
if (!manifest) {
|
|
104874
104874
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C google-analysis --json-out`);
|
|
104875
104875
|
}
|
|
104876
104876
|
const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
|
|
104877
104877
|
const dateRange = manifest.googleAnalysis?.dateRange;
|
|
104878
|
-
const preferredDateTag = dateTagFromRange2(dateRange) ?? dateTagFromMeta2(
|
|
104878
|
+
const preferredDateTag = dateTagFromRange2(dateRange) ?? dateTagFromMeta2(asRecord22(payload.meta) ?? void 0);
|
|
104879
104879
|
const sectionMap = /* @__PURE__ */ new Map();
|
|
104880
104880
|
for (const art of manifest.artifacts ?? []) {
|
|
104881
104881
|
const data = await loadSectionFile2(absDir, art.file);
|
|
@@ -104901,7 +104901,7 @@ async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
|
|
|
104901
104901
|
snapshotPresent[section] = sectionMap.has(section);
|
|
104902
104902
|
}
|
|
104903
104903
|
const overviewRecord = sectionMap.get("overview")?.record ?? null;
|
|
104904
|
-
const currentPeriod =
|
|
104904
|
+
const currentPeriod = asRecord22(overviewRecord?.currentPeriod);
|
|
104905
104905
|
const meta = { ...payload.meta ?? {} };
|
|
104906
104906
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
104907
104907
|
if (!meta.currency) {
|
|
@@ -105058,18 +105058,18 @@ function prefixLines(items3, firstPrefix) {
|
|
|
105058
105058
|
});
|
|
105059
105059
|
}
|
|
105060
105060
|
function analysisLines(data, key) {
|
|
105061
|
-
const block =
|
|
105062
|
-
const rec =
|
|
105061
|
+
const block = asRecord22(asRecord22(data.narrative)?.sheetAnalysis)?.[key];
|
|
105062
|
+
const rec = asRecord22(block);
|
|
105063
105063
|
return [
|
|
105064
105064
|
...prefixLines(stringList2(rec?.summary), "\u603B\u7ED3\uFF1A"),
|
|
105065
105065
|
...prefixLines(stringList2(rec?.suggestions), "\u5EFA\u8BAE\uFF1A")
|
|
105066
105066
|
];
|
|
105067
105067
|
}
|
|
105068
105068
|
function reviewLines(data) {
|
|
105069
|
-
const review =
|
|
105069
|
+
const review = asRecord22(asRecord22(data.narrative)?.review) ?? {};
|
|
105070
105070
|
const lines = [];
|
|
105071
105071
|
OKKI_REVIEW_KEYS.forEach((key, idx) => {
|
|
105072
|
-
const block =
|
|
105072
|
+
const block = asRecord22(review[key]) ?? {};
|
|
105073
105073
|
lines.push(`${idx + 1}\u3001${REVIEW_LABELS2[key]}\uFF1A${str(block.overview, "")}`);
|
|
105074
105074
|
lines.push(` \u603B\u7ED3\uFF1A${str(block.summary, "")}`);
|
|
105075
105075
|
lines.push(` \u5EFA\u8BAE\uFF1A${str(block.suggestion, "")}`);
|
|
@@ -105204,7 +105204,7 @@ function geoRows(data, lang = "zh") {
|
|
|
105204
105204
|
});
|
|
105205
105205
|
}
|
|
105206
105206
|
function buildOkkiWeeklySheetDrafts(data, lang = "zh") {
|
|
105207
|
-
const meta =
|
|
105207
|
+
const meta = asRecord22(data.meta) ?? {};
|
|
105208
105208
|
const period = formatOkkiDateRange(str(meta.startDate, ""), str(meta.endDate, ""), lang);
|
|
105209
105209
|
const campaigns = campaignRows(data);
|
|
105210
105210
|
const drafts = {
|
|
@@ -105479,7 +105479,7 @@ function textLen2(value) {
|
|
|
105479
105479
|
function asArray2(value) {
|
|
105480
105480
|
return Array.isArray(value) ? value : [];
|
|
105481
105481
|
}
|
|
105482
|
-
function
|
|
105482
|
+
function asRecord23(value) {
|
|
105483
105483
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
105484
105484
|
return value;
|
|
105485
105485
|
}
|
|
@@ -105490,9 +105490,9 @@ function pushMissing2(missing, id, chapter, dimension, hint) {
|
|
|
105490
105490
|
}
|
|
105491
105491
|
function validateGooglePeriodReportContent(data) {
|
|
105492
105492
|
const missing = [];
|
|
105493
|
-
const meta =
|
|
105494
|
-
const narrative =
|
|
105495
|
-
const sections =
|
|
105493
|
+
const meta = asRecord23(data.meta) ?? {};
|
|
105494
|
+
const narrative = asRecord23(data.narrative) ?? {};
|
|
105495
|
+
const sections = asRecord23(narrative.sections) ?? {};
|
|
105496
105496
|
if (!textLen2(meta.accountId)) {
|
|
105497
105497
|
pushMissing2(
|
|
105498
105498
|
missing,
|
|
@@ -105512,7 +105512,7 @@ function validateGooglePeriodReportContent(data) {
|
|
|
105512
105512
|
"\u22651 \u6BB5\u6267\u884C\u6458\u8981\uFF0C\u6982\u8FF0\u672C\u671F\u6D88\u8017/\u8F6C\u5316/CTR/CPA \u8D70\u52BF"
|
|
105513
105513
|
);
|
|
105514
105514
|
}
|
|
105515
|
-
const kpis =
|
|
105515
|
+
const kpis = asRecord23(data.kpis);
|
|
105516
105516
|
const hasCoreKpi = ["cost", "impressions", "clicks"].some(
|
|
105517
105517
|
(key) => Number.isFinite(Number(kpis?.[key]))
|
|
105518
105518
|
);
|
|
@@ -105525,7 +105525,7 @@ function validateGooglePeriodReportContent(data) {
|
|
|
105525
105525
|
"\u81F3\u5C11\u4E00\u9879\u987B\u4E3A\u6709\u6548\u6570\u5B57\uFF1B\u8BF7\u52A0 --snapshot-dir \u4ECE google-analysis \u5FEB\u7167\u5408\u5E76\uFF08\u52FF\u624B\u586B HTML\uFF09"
|
|
105526
105526
|
);
|
|
105527
105527
|
}
|
|
105528
|
-
const negativeRows =
|
|
105528
|
+
const negativeRows = asRecord23(data.tables)?.negativeKeywords;
|
|
105529
105529
|
if (!Array.isArray(negativeRows)) {
|
|
105530
105530
|
pushMissing2(
|
|
105531
105531
|
missing,
|
|
@@ -105538,7 +105538,7 @@ function validateGooglePeriodReportContent(data) {
|
|
|
105538
105538
|
const sectionAnalysisCounts = {};
|
|
105539
105539
|
const sectionSuggestionCounts = {};
|
|
105540
105540
|
for (const key of SECTION_KEYS) {
|
|
105541
|
-
const section =
|
|
105541
|
+
const section = asRecord23(sections[key]);
|
|
105542
105542
|
const analysis = asArray2(section?.analysis).filter((s) => textLen2(s) > 0);
|
|
105543
105543
|
const suggestions = asArray2(section?.suggestions).filter((s) => textLen2(s) > 0);
|
|
105544
105544
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -105564,7 +105564,7 @@ function validateGooglePeriodReportContent(data) {
|
|
|
105564
105564
|
}
|
|
105565
105565
|
const recommendations = asArray2(narrative.recommendations).filter((r) => {
|
|
105566
105566
|
if (typeof r === "string") return textLen2(r) > 0;
|
|
105567
|
-
const rec =
|
|
105567
|
+
const rec = asRecord23(r);
|
|
105568
105568
|
return textLen2(rec?.content) > 0;
|
|
105569
105569
|
});
|
|
105570
105570
|
if (recommendations.length < 3) {
|
|
@@ -105655,7 +105655,7 @@ async function readJsonFile3(filePath) {
|
|
|
105655
105655
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
105656
105656
|
}
|
|
105657
105657
|
}
|
|
105658
|
-
function
|
|
105658
|
+
function asRecord24(value) {
|
|
105659
105659
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
105660
105660
|
return value;
|
|
105661
105661
|
}
|
|
@@ -105671,7 +105671,7 @@ function injectReportData2(html, payload, lang) {
|
|
|
105671
105671
|
}
|
|
105672
105672
|
async function runGoogleAnalysisRender(opts) {
|
|
105673
105673
|
const dataPath = path36.resolve(opts.dataFile);
|
|
105674
|
-
let data =
|
|
105674
|
+
let data = asRecord24(await readJsonFile3(dataPath));
|
|
105675
105675
|
if (opts.snapshotDir?.trim()) {
|
|
105676
105676
|
data = await mergeGoogleAnalysisSnapshotIntoReport(data, path36.resolve(opts.snapshotDir));
|
|
105677
105677
|
}
|
|
@@ -106416,8 +106416,8 @@ var init_batch_runner = __esm({
|
|
|
106416
106416
|
shouldDowngrade(now = Date.now()) {
|
|
106417
106417
|
if (this.window.length < this.opts.windowSize) return false;
|
|
106418
106418
|
if (now - this.lastDowngradeAt < this.opts.cooldownMs) return false;
|
|
106419
|
-
const
|
|
106420
|
-
const rate2 =
|
|
106419
|
+
const fail5 = this.window.filter((s) => !s).length;
|
|
106420
|
+
const rate2 = fail5 / this.window.length;
|
|
106421
106421
|
if (rate2 >= this.opts.errorRateThreshold) {
|
|
106422
106422
|
this.lastDowngradeAt = now;
|
|
106423
106423
|
this.window.length = 0;
|
|
@@ -106998,8 +106998,8 @@ function resolveSectionList(sections, exclude) {
|
|
|
106998
106998
|
);
|
|
106999
106999
|
process.exit(1);
|
|
107000
107000
|
}
|
|
107001
|
-
const
|
|
107002
|
-
return
|
|
107001
|
+
const base2 = include.length > 0 ? SECTIONS.filter((s) => include.includes(s.name)) : SECTIONS;
|
|
107002
|
+
return base2.filter((s) => !drop.has(s.name));
|
|
107003
107003
|
}
|
|
107004
107004
|
async function runWithConcurrency2(tasks, concurrency) {
|
|
107005
107005
|
const results = new Array(tasks.length);
|
|
@@ -111258,7 +111258,7 @@ ${opts.media} \u6295\u653E\u6570\u636E\uFF08${rangeLabel}\uFF09\uFF08\u7B2C 1 \u
|
|
|
111258
111258
|
totalImpressions += item.impressions ?? 0;
|
|
111259
111259
|
totalClicks += item.clicks ?? 0;
|
|
111260
111260
|
totalConversions += item.conversions ?? 0;
|
|
111261
|
-
const
|
|
111261
|
+
const base2 = {
|
|
111262
111262
|
id: item.mediaAccountId ?? "",
|
|
111263
111263
|
name: item.mediaCustomerName ?? "",
|
|
111264
111264
|
spend: item.spend?.toFixed(2) ?? "0.00",
|
|
@@ -111267,7 +111267,7 @@ ${opts.media} \u6295\u653E\u6570\u636E\uFF08${rangeLabel}\uFF09\uFF08\u7B2C 1 \u
|
|
|
111267
111267
|
conversions: String(item.conversions ?? 0),
|
|
111268
111268
|
cpc: item.costPerClick?.toFixed(4) ?? "0.0000"
|
|
111269
111269
|
};
|
|
111270
|
-
return opts.byDay ? { date: item.date ?? "", ...
|
|
111270
|
+
return opts.byDay ? { date: item.date ?? "", ...base2 } : base2;
|
|
111271
111271
|
});
|
|
111272
111272
|
if (items3.length > 1) {
|
|
111273
111273
|
const avgCpc = totalClicks > 0 ? (totalSpend / totalClicks).toFixed(4) : "0.0000";
|
|
@@ -114102,9 +114102,9 @@ async function runForewarningCreate(opts) {
|
|
|
114102
114102
|
process.exit(1);
|
|
114103
114103
|
}
|
|
114104
114104
|
const config = loadConfig(opts.token);
|
|
114105
|
-
const
|
|
114105
|
+
const base2 = `${config.apiBaseUrl}/command/smart-strategy/settings/${opts.media}`;
|
|
114106
114106
|
const isUpdate = Boolean(opts.id);
|
|
114107
|
-
const url = isUpdate ? `${
|
|
114107
|
+
const url = isUpdate ? `${base2}/${opts.id}` : base2;
|
|
114108
114108
|
const method = isUpdate ? "PUT" : "POST";
|
|
114109
114109
|
const body = buildRuleBody(opts);
|
|
114110
114110
|
try {
|
|
@@ -115659,12 +115659,12 @@ async function runAdKeywordEdit(opts) {
|
|
|
115659
115659
|
}
|
|
115660
115660
|
const body = { ...keyword };
|
|
115661
115661
|
if (opts.matchType !== void 0) {
|
|
115662
|
-
const
|
|
115663
|
-
if (!String(
|
|
115662
|
+
const base2 = opts.text !== void 0 ? opts.text : firstKeywordTextFromRecord(keyword);
|
|
115663
|
+
if (!String(base2).trim()) {
|
|
115664
115664
|
console.error("\n\u274C \u65E0\u6CD5\u89E3\u6790\u5F53\u524D\u5173\u952E\u8BCD\u6587\u6848\uFF0C\u8BF7\u540C\u65F6\u4F20 --text <\u8BCD\u5E72>\n");
|
|
115665
115665
|
process.exit(1);
|
|
115666
115666
|
}
|
|
115667
|
-
body["keywordText"] = [formatKeywordTextForMatchType(
|
|
115667
|
+
body["keywordText"] = [formatKeywordTextForMatchType(base2, opts.matchType)];
|
|
115668
115668
|
body["matchTypeV2"] = opts.matchType;
|
|
115669
115669
|
} else if (opts.text !== void 0) {
|
|
115670
115670
|
body["keywordText"] = [opts.text];
|
|
@@ -115812,12 +115812,12 @@ async function runAdNegativeKeywordEdit(opts) {
|
|
|
115812
115812
|
}
|
|
115813
115813
|
const body = { ...keyword };
|
|
115814
115814
|
if (opts.matchType !== void 0) {
|
|
115815
|
-
const
|
|
115816
|
-
if (!String(
|
|
115815
|
+
const base2 = opts.text !== void 0 ? opts.text : firstKeywordTextFromRecord(keyword);
|
|
115816
|
+
if (!String(base2).trim()) {
|
|
115817
115817
|
console.error("\n\u274C \u65E0\u6CD5\u89E3\u6790\u5F53\u524D\u5426\u5B9A\u5173\u952E\u8BCD\u6587\u6848\uFF0C\u8BF7\u540C\u65F6\u4F20 --text <\u8BCD\u5E72>\n");
|
|
115818
115818
|
process.exit(1);
|
|
115819
115819
|
}
|
|
115820
|
-
body["keywordText"] = [formatKeywordTextForMatchType(
|
|
115820
|
+
body["keywordText"] = [formatKeywordTextForMatchType(base2, opts.matchType)];
|
|
115821
115821
|
body["matchTypeV2"] = opts.matchType;
|
|
115822
115822
|
} else if (opts.text !== void 0) {
|
|
115823
115823
|
body["keywordText"] = [opts.text];
|
|
@@ -119652,8 +119652,8 @@ function pickGeoCandidate(query, items3) {
|
|
|
119652
119652
|
const key = geoQueryKey(query);
|
|
119653
119653
|
const unserved = GEO_UNSERVED[normGeoName(query)];
|
|
119654
119654
|
const parsed = items3.map((it) => {
|
|
119655
|
-
const { base, level } = splitGeoName(geoItemDisplayName(it));
|
|
119656
|
-
return { it, base, level: level || geoItemType(it) };
|
|
119655
|
+
const { base: base2, level } = splitGeoName(geoItemDisplayName(it));
|
|
119656
|
+
return { it, base: base2, level: level || geoItemType(it) };
|
|
119657
119657
|
});
|
|
119658
119658
|
const same = parsed.filter((p) => normGeoName(p.base) === key || normGeoName(geoItemDisplayName(p.it)) === key);
|
|
119659
119659
|
const rank = (lvl) => {
|
|
@@ -120160,13 +120160,13 @@ function buildExtensionRemediateCommandLine(opts) {
|
|
|
120160
120160
|
const props = asRecord2(ext["Properties"]) ?? {};
|
|
120161
120161
|
const levelFlag = `--level ${level}`;
|
|
120162
120162
|
const campaignFlag = level === "Campaign" ? `--campaign-id ${campaignId}` : "";
|
|
120163
|
-
const
|
|
120163
|
+
const base2 = ["siluzan-tso", "ad", "extension"];
|
|
120164
120164
|
if (type === "SITELINK") {
|
|
120165
120165
|
const text3 = pickString(props["Text"], props["LinkText"]);
|
|
120166
120166
|
const url = pickString(props["DestinationUrl"]);
|
|
120167
120167
|
if (!text3 || !url) return null;
|
|
120168
120168
|
const parts = [
|
|
120169
|
-
...
|
|
120169
|
+
...base2,
|
|
120170
120170
|
"sitelink",
|
|
120171
120171
|
`-a ${accountId}`,
|
|
120172
120172
|
`--text ${shellArg(text3)}`,
|
|
@@ -120185,7 +120185,7 @@ function buildExtensionRemediateCommandLine(opts) {
|
|
|
120185
120185
|
if (type === "CALLOUT") {
|
|
120186
120186
|
const text3 = pickString(props["Text"], props["CalloutText"]);
|
|
120187
120187
|
if (!text3) return null;
|
|
120188
|
-
const parts = [...
|
|
120188
|
+
const parts = [...base2, "callout", `-a ${accountId}`, `--text ${shellArg(text3)}`, levelFlag];
|
|
120189
120189
|
if (campaignFlag) parts.push(campaignFlag);
|
|
120190
120190
|
parts.push(`--json-out ${jsonOut}`);
|
|
120191
120191
|
parts.push(`--commit ${shellArg("batch-diff remediate extension")}`);
|
|
@@ -120196,7 +120196,7 @@ function buildExtensionRemediateCommandLine(opts) {
|
|
|
120196
120196
|
const phone = pickString(props["PhoneNumber"], props["phoneNumber"]);
|
|
120197
120197
|
if (!code || !phone) return null;
|
|
120198
120198
|
const parts = [
|
|
120199
|
-
...
|
|
120199
|
+
...base2,
|
|
120200
120200
|
"call",
|
|
120201
120201
|
`-a ${accountId}`,
|
|
120202
120202
|
`--country-code ${shellArg(code)}`,
|
|
@@ -120216,7 +120216,7 @@ function buildExtensionRemediateCommandLine(opts) {
|
|
|
120216
120216
|
const valueStr = values.filter((v) => typeof v === "string" && v.trim().length > 0).join(",");
|
|
120217
120217
|
if (!valueStr) return null;
|
|
120218
120218
|
const parts = [
|
|
120219
|
-
...
|
|
120219
|
+
...base2,
|
|
120220
120220
|
"snippet",
|
|
120221
120221
|
`-a ${accountId}`,
|
|
120222
120222
|
`--header ${shellArg(header)}`,
|
|
@@ -120546,12 +120546,12 @@ function livePoolHasText(pool, text3) {
|
|
|
120546
120546
|
return t.length > 0 && pool.has(t);
|
|
120547
120547
|
}
|
|
120548
120548
|
function listPlannedRsaAssets(ad, groupPath, adIndex, adGroupName) {
|
|
120549
|
-
const
|
|
120549
|
+
const base2 = `${groupPath}.AdsForBatchJob[${adIndex}]`;
|
|
120550
120550
|
const out = [];
|
|
120551
120551
|
const push = (kind, field, raw) => {
|
|
120552
120552
|
if (typeof raw !== "string" || !raw.trim()) return;
|
|
120553
120553
|
out.push({
|
|
120554
|
-
path: `${
|
|
120554
|
+
path: `${base2}.${field}`,
|
|
120555
120555
|
adGroupName,
|
|
120556
120556
|
kind,
|
|
120557
120557
|
field,
|
|
@@ -122709,12 +122709,12 @@ import { existsSync as existsSync4, readFileSync as readFileSync10 } from "fs";
|
|
|
122709
122709
|
|
|
122710
122710
|
// src/commands/ad/pmax-urls.ts
|
|
122711
122711
|
function pmaxChannelTypesUrl(googleApiUrl) {
|
|
122712
|
-
const
|
|
122713
|
-
return `${
|
|
122712
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122713
|
+
return `${base2}/campaign/pmax/channel-types`;
|
|
122714
122714
|
}
|
|
122715
122715
|
function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
122716
|
-
const
|
|
122717
|
-
const path56 = `${
|
|
122716
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122717
|
+
const path56 = `${base2}/accounts/${accountId}/campaign/pmax`;
|
|
122718
122718
|
return campaignId ? `${path56}/${campaignId}` : path56;
|
|
122719
122719
|
}
|
|
122720
122720
|
function pmaxBrandAssetsUrl(googleApiUrl, accountId, campaignId) {
|
|
@@ -122724,53 +122724,53 @@ function pmaxBrandGuidelinesEnableUrl(googleApiUrl, accountId, campaignId) {
|
|
|
122724
122724
|
return `${pmaxCampaignUrl(googleApiUrl, accountId, campaignId)}/brand-guidelines/enable`;
|
|
122725
122725
|
}
|
|
122726
122726
|
function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
|
|
122727
|
-
const
|
|
122728
|
-
const path56 = `${
|
|
122727
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122728
|
+
const path56 = `${base2}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
|
|
122729
122729
|
return suffix ? `${path56}/${suffix.replace(/^\//, "")}` : path56;
|
|
122730
122730
|
}
|
|
122731
122731
|
function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
|
|
122732
|
-
const
|
|
122733
|
-
return `${
|
|
122732
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122733
|
+
return `${base2}/accounts/${accountId}/campaign/pmax/${campaignId}/asset-group`;
|
|
122734
122734
|
}
|
|
122735
122735
|
function pmaxUserlistsUrl(googleApiUrl, accountId) {
|
|
122736
|
-
const
|
|
122737
|
-
return `${
|
|
122736
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122737
|
+
return `${base2}/campaignmanagement/userlists/${accountId}`;
|
|
122738
122738
|
}
|
|
122739
122739
|
function pmaxAudiencesUrl(googleApiUrl, accountId, limit) {
|
|
122740
|
-
const
|
|
122741
|
-
const url = `${
|
|
122740
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122741
|
+
const url = `${base2}/campaignmanagement/audiences/${accountId}`;
|
|
122742
122742
|
if (limit != null && Number.isFinite(limit)) {
|
|
122743
122743
|
return `${url}?limit=${Math.floor(limit)}`;
|
|
122744
122744
|
}
|
|
122745
122745
|
return url;
|
|
122746
122746
|
}
|
|
122747
122747
|
function pmaxImageAssetUrl(googleApiUrl, accountId, name2) {
|
|
122748
|
-
const
|
|
122749
|
-
const url = `${
|
|
122748
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122749
|
+
const url = `${base2}/mediamanagement/imageasset/${accountId}`;
|
|
122750
122750
|
if (name2?.trim()) {
|
|
122751
122751
|
return `${url}?name=${encodeURIComponent(name2.trim())}`;
|
|
122752
122752
|
}
|
|
122753
122753
|
return url;
|
|
122754
122754
|
}
|
|
122755
122755
|
function pmaxAssetGroupReportUrl(googleApiUrl, accountId, query) {
|
|
122756
|
-
const
|
|
122756
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122757
122757
|
const qs = query.toString();
|
|
122758
|
-
return `${
|
|
122758
|
+
return `${base2}/reporting/media-account/${accountId}/pmax/asset-groups/reports${qs ? `?${qs}` : ""}`;
|
|
122759
122759
|
}
|
|
122760
122760
|
function pmaxAssetReportUrl(googleApiUrl, accountId, query) {
|
|
122761
|
-
const
|
|
122761
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122762
122762
|
const qs = query.toString();
|
|
122763
|
-
return `${
|
|
122763
|
+
return `${base2}/reporting/media-account/${accountId}/pmax/asset-groups/reports/assets${qs ? `?${qs}` : ""}`;
|
|
122764
122764
|
}
|
|
122765
122765
|
function pmaxChannelReportUrl(googleApiUrl, accountId, query) {
|
|
122766
|
-
const
|
|
122766
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122767
122767
|
const qs = query.toString();
|
|
122768
|
-
return `${
|
|
122768
|
+
return `${base2}/reporting/media-account/${accountId}/pmax/campaigns/reports/channels${qs ? `?${qs}` : ""}`;
|
|
122769
122769
|
}
|
|
122770
122770
|
function pmaxGeoReportUrl(googleApiUrl, accountId, query) {
|
|
122771
|
-
const
|
|
122771
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
122772
122772
|
const qs = query.toString();
|
|
122773
|
-
return `${
|
|
122773
|
+
return `${base2}/reporting/media-account/${accountId}/pmax/campaigns/reports/geo${qs ? `?${qs}` : ""}`;
|
|
122774
122774
|
}
|
|
122775
122775
|
|
|
122776
122776
|
// src/commands/ad/pmax-image-resolve.ts
|
|
@@ -123719,8 +123719,8 @@ function buildPmaxCreateApiBody(cfg, imageSlots) {
|
|
|
123719
123719
|
return body;
|
|
123720
123720
|
}
|
|
123721
123721
|
function buildPmaxCreateUrl(googleApiUrl, accountId) {
|
|
123722
|
-
const
|
|
123723
|
-
return `${
|
|
123722
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
123723
|
+
return `${base2}/accounts/${accountId}/campaign/pmax`;
|
|
123724
123724
|
}
|
|
123725
123725
|
|
|
123726
123726
|
// src/commands/ad/pmax-video-upload.ts
|
|
@@ -123860,16 +123860,16 @@ var FAILED_VIDEO_STATES = /* @__PURE__ */ new Set(["FAILED", "REJECTED", "UNAVAI
|
|
|
123860
123860
|
var PMAX_VIDEO_POLL_INTERVAL_MS = 1e4;
|
|
123861
123861
|
var PMAX_VIDEO_POLL_MAX_ATTEMPTS = 120;
|
|
123862
123862
|
function pyapiVideoUploadUrl(googleApiUrl) {
|
|
123863
|
-
const
|
|
123864
|
-
return `${
|
|
123863
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
123864
|
+
return `${base2}/pyapi/video/upload`;
|
|
123865
123865
|
}
|
|
123866
123866
|
function pyapiVideoStatusUrl(googleApiUrl, mediaAccountId, resourceName) {
|
|
123867
|
-
const
|
|
123867
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
123868
123868
|
const params = new URLSearchParams({
|
|
123869
123869
|
media_account_id: mediaAccountId.replace(/-/g, ""),
|
|
123870
123870
|
resource_name: resourceName
|
|
123871
123871
|
});
|
|
123872
|
-
return `${
|
|
123872
|
+
return `${base2}/pyapi/video/upload/status?${params.toString()}`;
|
|
123873
123873
|
}
|
|
123874
123874
|
function resolveVideoPath(configFile, relOrAbs) {
|
|
123875
123875
|
const trimmed = relOrAbs.trim();
|
|
@@ -125842,6 +125842,997 @@ async function runAdPmaxBrandGuidelinesEnable(opts) {
|
|
|
125842
125842
|
});
|
|
125843
125843
|
}
|
|
125844
125844
|
|
|
125845
|
+
// src/commands/ad/demand-gen-body.ts
|
|
125846
|
+
init_shared();
|
|
125847
|
+
var WRITE_CENT_KEYS = ["budget", "targetCpa_BidingAmount", "currencyMinimumBudget"];
|
|
125848
|
+
var YUAN_TO_CENT = {
|
|
125849
|
+
budgetYuan: "budget",
|
|
125850
|
+
targetCpaAmountYuan: "targetCpa_BidingAmount",
|
|
125851
|
+
currencyMinimumBudgetYuan: "currencyMinimumBudget"
|
|
125852
|
+
};
|
|
125853
|
+
var READ_CENT_TO_YUAN = {
|
|
125854
|
+
budget: "budgetYuan",
|
|
125855
|
+
targetCpa_BidingAmount: "targetCpaAmountYuan",
|
|
125856
|
+
minimumDailyBudget: "minimumDailyBudgetYuan",
|
|
125857
|
+
currencyMinimumBudget: "currencyMinimumBudgetYuan",
|
|
125858
|
+
currencyMinimum: "currencyMinimumYuan",
|
|
125859
|
+
tenTimesTargetCpa: "tenTimesTargetCpaYuan",
|
|
125860
|
+
fifteenTimesTargetCpa: "fifteenTimesTargetCpaYuan"
|
|
125861
|
+
};
|
|
125862
|
+
var DISPLAY_ONLY_KEYS = [
|
|
125863
|
+
...Object.keys(YUAN_TO_CENT),
|
|
125864
|
+
"minimumDailyBudgetYuan",
|
|
125865
|
+
"currencyMinimumYuan",
|
|
125866
|
+
"tenTimesTargetCpaYuan",
|
|
125867
|
+
"fifteenTimesTargetCpaYuan",
|
|
125868
|
+
"_agentMoneyHint",
|
|
125869
|
+
"_cliMoney"
|
|
125870
|
+
];
|
|
125871
|
+
var DEMAND_GEN_CHANNEL_KEYS = [
|
|
125872
|
+
"youtubeInStream",
|
|
125873
|
+
"youtubeInFeed",
|
|
125874
|
+
"youtubeShorts",
|
|
125875
|
+
"discover",
|
|
125876
|
+
"gmail",
|
|
125877
|
+
"display",
|
|
125878
|
+
"maps"
|
|
125879
|
+
];
|
|
125880
|
+
function finiteNumber(value) {
|
|
125881
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
125882
|
+
if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
|
|
125883
|
+
return Number(value);
|
|
125884
|
+
}
|
|
125885
|
+
return null;
|
|
125886
|
+
}
|
|
125887
|
+
function quoteLongResumeIds2(rawBody) {
|
|
125888
|
+
return rawBody.replace(
|
|
125889
|
+
/"(budgetId|campaignId|adGroupId)"\s*:\s*(-?\d+)/g,
|
|
125890
|
+
'"$1":"$2"'
|
|
125891
|
+
);
|
|
125892
|
+
}
|
|
125893
|
+
function normalizeResumeId(value) {
|
|
125894
|
+
if (value == null) return null;
|
|
125895
|
+
const text3 = String(value).trim();
|
|
125896
|
+
if (!text3 || text3.toLowerCase() === "null") return null;
|
|
125897
|
+
return text3;
|
|
125898
|
+
}
|
|
125899
|
+
function parseDemandGenCreateFailure(rawBody) {
|
|
125900
|
+
const text3 = rawBody.trim();
|
|
125901
|
+
if (!text3.startsWith("{")) return null;
|
|
125902
|
+
let parsed;
|
|
125903
|
+
try {
|
|
125904
|
+
parsed = JSON.parse(quoteLongResumeIds2(text3));
|
|
125905
|
+
} catch {
|
|
125906
|
+
return null;
|
|
125907
|
+
}
|
|
125908
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
125909
|
+
const rec = parsed;
|
|
125910
|
+
if (typeof rec["message"] !== "string" || !rec["message"].trim()) return null;
|
|
125911
|
+
if (!("budgetId" in rec) && !("campaignId" in rec) && !("adGroupId" in rec)) return null;
|
|
125912
|
+
return {
|
|
125913
|
+
message: rec["message"].trim(),
|
|
125914
|
+
budgetId: normalizeResumeId(rec["budgetId"]),
|
|
125915
|
+
campaignId: normalizeResumeId(rec["campaignId"]),
|
|
125916
|
+
adGroupId: normalizeResumeId(rec["adGroupId"])
|
|
125917
|
+
};
|
|
125918
|
+
}
|
|
125919
|
+
function readHttpRawBody2(err) {
|
|
125920
|
+
if (!err || typeof err !== "object" || !("rawBody" in err)) return "";
|
|
125921
|
+
const raw = err.rawBody;
|
|
125922
|
+
return typeof raw === "string" ? raw : "";
|
|
125923
|
+
}
|
|
125924
|
+
function unwrapDemandGenCreateBody(raw) {
|
|
125925
|
+
const nested = raw["createRequest"];
|
|
125926
|
+
const hasOwnPayload = raw["name"] != null || raw["budget"] != null || raw["budgetYuan"] != null;
|
|
125927
|
+
if (!hasOwnPayload && nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
125928
|
+
return { ...nested };
|
|
125929
|
+
}
|
|
125930
|
+
return { ...raw };
|
|
125931
|
+
}
|
|
125932
|
+
function normalizeDemandGenMoneyForWrite(raw) {
|
|
125933
|
+
const out = { ...raw };
|
|
125934
|
+
for (const [yuanKey, centKey] of Object.entries(YUAN_TO_CENT)) {
|
|
125935
|
+
const yuan = finiteNumber(out[yuanKey]);
|
|
125936
|
+
if (yuan != null) out[centKey] = yuan;
|
|
125937
|
+
}
|
|
125938
|
+
for (const key of DISPLAY_ONLY_KEYS) delete out[key];
|
|
125939
|
+
for (const key of WRITE_CENT_KEYS) {
|
|
125940
|
+
const amount = finiteNumber(out[key]);
|
|
125941
|
+
if (amount != null) out[key] = toCentAmount(amount);
|
|
125942
|
+
}
|
|
125943
|
+
return out;
|
|
125944
|
+
}
|
|
125945
|
+
function decorateDemandGenRead(value) {
|
|
125946
|
+
if (Array.isArray(value)) return value.map(decorateDemandGenRead);
|
|
125947
|
+
if (!value || typeof value !== "object") return value;
|
|
125948
|
+
const out = {};
|
|
125949
|
+
for (const [key, child] of Object.entries(value)) {
|
|
125950
|
+
out[key] = decorateDemandGenRead(child);
|
|
125951
|
+
}
|
|
125952
|
+
for (const [centKey, yuanKey] of Object.entries(READ_CENT_TO_YUAN)) {
|
|
125953
|
+
const amount = finiteNumber(out[centKey]);
|
|
125954
|
+
if (amount != null) out[yuanKey] = toDisplayMoney(amount);
|
|
125955
|
+
}
|
|
125956
|
+
return out;
|
|
125957
|
+
}
|
|
125958
|
+
function normalizeCampaignStatus(raw) {
|
|
125959
|
+
if (raw == null || String(raw).trim() === "") return null;
|
|
125960
|
+
const text3 = String(raw).trim().toLowerCase();
|
|
125961
|
+
if (text3 === "enabled" || text3 === "2") return "Enabled";
|
|
125962
|
+
if (text3 === "paused" || text3 === "3") return "Paused";
|
|
125963
|
+
if (text3 === "removed" || text3 === "4") return "Removed";
|
|
125964
|
+
return null;
|
|
125965
|
+
}
|
|
125966
|
+
function normalizeAdStatus(raw) {
|
|
125967
|
+
const status = normalizeCampaignStatus(raw);
|
|
125968
|
+
if (status === "Enabled" || status === "Paused") return status;
|
|
125969
|
+
return null;
|
|
125970
|
+
}
|
|
125971
|
+
function buildSelectedChannels(names) {
|
|
125972
|
+
const picked = new Set(names.map((name2) => name2.trim()).filter(Boolean));
|
|
125973
|
+
const unknown = [...picked].filter(
|
|
125974
|
+
(name2) => !DEMAND_GEN_CHANNEL_KEYS.includes(name2)
|
|
125975
|
+
);
|
|
125976
|
+
if (unknown.length > 0) {
|
|
125977
|
+
throw new Error(
|
|
125978
|
+
`\u672A\u77E5\u7248\u4F4D\uFF1A${unknown.join(", ")}\u3002\u53EF\u9009 ${DEMAND_GEN_CHANNEL_KEYS.join(", ")}`
|
|
125979
|
+
);
|
|
125980
|
+
}
|
|
125981
|
+
const out = {};
|
|
125982
|
+
let any = false;
|
|
125983
|
+
for (const key of DEMAND_GEN_CHANNEL_KEYS) {
|
|
125984
|
+
const on = picked.has(key);
|
|
125985
|
+
out[key] = on;
|
|
125986
|
+
if (on) any = true;
|
|
125987
|
+
}
|
|
125988
|
+
if (!any) throw new Error("SelectedChannels requires at least one channel.");
|
|
125989
|
+
return out;
|
|
125990
|
+
}
|
|
125991
|
+
function hasText(value) {
|
|
125992
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
125993
|
+
}
|
|
125994
|
+
function hasKeywords(value) {
|
|
125995
|
+
return Array.isArray(value) && value.some((item) => hasText(item));
|
|
125996
|
+
}
|
|
125997
|
+
function demandGenCreateIssues(body) {
|
|
125998
|
+
const issues = [];
|
|
125999
|
+
if (body["automatic"] === true) {
|
|
126000
|
+
issues.push("\u4EBA\u5DE5\u64CD\u4F5C\u4E0D\u8981\u5E26 automatic\u3002\u5B66\u4E60\u671F\u5185\u81EA\u52A8\u6539\u4EF7\u3001\u6539\u8F6C\u5316\u76EE\u6807\u6216\u6362\u53D7\u4F17\u4F1A\u88AB\u62D2\u7EDD\u3002");
|
|
126001
|
+
}
|
|
126002
|
+
const bidding = String(body["biddingStrategyTypeV2"] ?? "").trim();
|
|
126003
|
+
if (!bidding || bidding.toUpperCase() === "UNSPECIFIED") {
|
|
126004
|
+
issues.push("Bidding strategy is required.");
|
|
126005
|
+
}
|
|
126006
|
+
if (hasText(body["channelStrategy"]) && body["selectedChannels"]) {
|
|
126007
|
+
issues.push("ChannelStrategy and SelectedChannels cannot both be set.");
|
|
126008
|
+
}
|
|
126009
|
+
if (hasText(body["audienceResourceName"]) && hasKeywords(body["audienceKeywords"])) {
|
|
126010
|
+
issues.push("AudienceResourceName and AudienceKeywords cannot both be set.");
|
|
126011
|
+
}
|
|
126012
|
+
return issues;
|
|
126013
|
+
}
|
|
126014
|
+
function demandGenEditIssues(body) {
|
|
126015
|
+
const issues = [];
|
|
126016
|
+
if (body["automatic"] === true) {
|
|
126017
|
+
issues.push("\u4EBA\u5DE5\u64CD\u4F5C\u4E0D\u8981\u5E26 automatic\u3002\u5B66\u4E60\u671F\u5185\u81EA\u52A8\u6539\u4EF7\u3001\u6539\u8F6C\u5316\u76EE\u6807\u6216\u6362\u53D7\u4F17\u4F1A\u88AB\u62D2\u7EDD\u3002");
|
|
126018
|
+
}
|
|
126019
|
+
if (body["statusV2"] != null && normalizeCampaignStatus(body["statusV2"]) == null) {
|
|
126020
|
+
issues.push("Campaign status must be Enabled, Paused, or Removed.");
|
|
126021
|
+
}
|
|
126022
|
+
if (normalizeCampaignStatus(body["statusV2"]) === "Enabled" && body["confirmEnable"] !== true) {
|
|
126023
|
+
issues.push("Enabling a campaign spends money. Set confirmEnable true.");
|
|
126024
|
+
}
|
|
126025
|
+
if (body["budget"] != null && !hasText(body["budgetId"])) {
|
|
126026
|
+
issues.push("\u4FEE\u6539\u9884\u7B97\u987B\u540C\u65F6\u5E26 budgetId\uFF08\u8FD9\u6761\u7CFB\u5217\u81EA\u5DF1\u7684\u672A\u5171\u4EAB\u9884\u7B97\uFF09\u3002");
|
|
126027
|
+
}
|
|
126028
|
+
return issues;
|
|
126029
|
+
}
|
|
126030
|
+
function demandGenResumeHint(failure) {
|
|
126031
|
+
const lines = [
|
|
126032
|
+
failure.message,
|
|
126033
|
+
"\u521B\u5EFA\u505C\u5728\u534A\u622A\u3002\u628A\u4E0B\u9762\u975E\u7A7A\u7F16\u53F7\u539F\u6837\u5199\u56DE JSON \u518D\u8DD1\u4E00\u6B21\uFF0C\u5176\u5B83\u5B57\u6BB5\u5148\u4E0D\u8981\u6539\u3002",
|
|
126034
|
+
"\u987A\u5E8F\u662F\u9884\u7B97 \u2192 \u7CFB\u5217 \u2192 \u5E7F\u544A\u7EC4\u3002\u53EA\u5E26\u7CFB\u5217\u3001\u4E0D\u5E26\u9884\u7B97\u4F1A\u88AB\u62D2\u3002\u5E7F\u544A\u7EC4\u91CC\u5DF2\u6709\u5E7F\u544A\u65F6\u4E0D\u4F1A\u518D\u63D2\u4E00\u5957\u3002"
|
|
126035
|
+
];
|
|
126036
|
+
if (failure.budgetId) lines.push(`budgetId: ${failure.budgetId}`);
|
|
126037
|
+
if (failure.campaignId) lines.push(`campaignId: ${failure.campaignId}`);
|
|
126038
|
+
if (failure.adGroupId) lines.push(`adGroupId: ${failure.adGroupId}`);
|
|
126039
|
+
if (!failure.budgetId && !failure.campaignId && !failure.adGroupId) {
|
|
126040
|
+
return failure.message;
|
|
126041
|
+
}
|
|
126042
|
+
return lines.join("\n");
|
|
126043
|
+
}
|
|
126044
|
+
|
|
126045
|
+
// src/commands/ad/demand-gen-urls.ts
|
|
126046
|
+
function base(googleApiUrl) {
|
|
126047
|
+
return googleApiUrl.replace(/\/$/, "");
|
|
126048
|
+
}
|
|
126049
|
+
function demandGenCollectionUrl(googleApiUrl, accountId) {
|
|
126050
|
+
return `${base(googleApiUrl)}/accounts/${accountId}/campaign/demand-gen`;
|
|
126051
|
+
}
|
|
126052
|
+
function demandGenCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
126053
|
+
return `${demandGenCollectionUrl(googleApiUrl, accountId)}/${campaignId}`;
|
|
126054
|
+
}
|
|
126055
|
+
function demandGenMinimumBudgetUrl(googleApiUrl, accountId, currencyCode) {
|
|
126056
|
+
const url = `${demandGenCollectionUrl(googleApiUrl, accountId)}/minimum-daily-budget`;
|
|
126057
|
+
const code = currencyCode?.trim();
|
|
126058
|
+
return code ? `${url}?currencyCode=${encodeURIComponent(code)}` : url;
|
|
126059
|
+
}
|
|
126060
|
+
function demandGenBiddableConversionsUrl(googleApiUrl, accountId) {
|
|
126061
|
+
return `${demandGenCollectionUrl(googleApiUrl, accountId)}/conversions/biddable`;
|
|
126062
|
+
}
|
|
126063
|
+
function demandGenConversionsValidateUrl(googleApiUrl, accountId) {
|
|
126064
|
+
return `${demandGenCollectionUrl(googleApiUrl, accountId)}/conversions/validate`;
|
|
126065
|
+
}
|
|
126066
|
+
function demandGenAdGroupUrl(googleApiUrl, accountId, campaignId, adGroupId, suffix) {
|
|
126067
|
+
const path56 = `${demandGenCampaignUrl(googleApiUrl, accountId, campaignId)}/ad-groups/${adGroupId}`;
|
|
126068
|
+
return suffix ? `${path56}/${suffix.replace(/^\//, "")}` : path56;
|
|
126069
|
+
}
|
|
126070
|
+
function demandGenAdUrl(googleApiUrl, accountId, campaignId, adGroupId, adId, suffix) {
|
|
126071
|
+
const path56 = `${demandGenAdGroupUrl(googleApiUrl, accountId, campaignId, adGroupId)}/ads/${adId}`;
|
|
126072
|
+
return suffix ? `${path56}/${suffix.replace(/^\//, "")}` : path56;
|
|
126073
|
+
}
|
|
126074
|
+
function demandGenChannelReportUrl(googleApiUrl, accountId, campaignId, startDate, endDate) {
|
|
126075
|
+
const params = new URLSearchParams({ startDate, endDate });
|
|
126076
|
+
return `${demandGenCampaignUrl(googleApiUrl, accountId, campaignId)}/reports/channels?${params}`;
|
|
126077
|
+
}
|
|
126078
|
+
|
|
126079
|
+
// src/commands/ad/demand-gen.ts
|
|
126080
|
+
init_shared();
|
|
126081
|
+
var MONEY_HINT = "budget / targetCpa_BidingAmount / minimumDailyBudget \u662F\u5206\uFF08500 = 5 \u5143\uFF09\u3002\u770B\u65C1\u8FB9\u7684 *Yuan\u3002\u5199\u5165 --budget / JSON \u586B\u5143\uFF1B\u82E5\u540C\u65F6\u6709 *Yuan\uFF0C\u4EE5 Yuan \u4E3A\u51C6\u3002targetRoas \u662F\u6BD4\u7387\u3002\u62A5\u8868 spend \u5DF2\u662F\u8D26\u6237\u5E01\u79CD\uFF0C\u4E0D\u8981\u62FF\u53BB\u5F53\u9884\u7B97\u3002";
|
|
126082
|
+
function fail(message) {
|
|
126083
|
+
console.error(`
|
|
126084
|
+
\u274C ${message}
|
|
126085
|
+
`);
|
|
126086
|
+
process.exit(1);
|
|
126087
|
+
}
|
|
126088
|
+
function asRecord5(value) {
|
|
126089
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : { data: value };
|
|
126090
|
+
}
|
|
126091
|
+
async function emit(opts, section, commandLabel, payload, idSuffix) {
|
|
126092
|
+
return emitPmaxResult(
|
|
126093
|
+
{ ...opts, account: opts.account },
|
|
126094
|
+
section,
|
|
126095
|
+
commandLabel,
|
|
126096
|
+
payload,
|
|
126097
|
+
idSuffix ?? opts.account
|
|
126098
|
+
);
|
|
126099
|
+
}
|
|
126100
|
+
function printIssues(issues) {
|
|
126101
|
+
if (issues.length === 0) return;
|
|
126102
|
+
fail(issues.join("\n"));
|
|
126103
|
+
}
|
|
126104
|
+
function loadBody(configFile) {
|
|
126105
|
+
return stripMetaKeys(loadPmaxJsonFile(configFile));
|
|
126106
|
+
}
|
|
126107
|
+
function imageKinds(body) {
|
|
126108
|
+
const paths = body["imagePaths"];
|
|
126109
|
+
const kinds = [];
|
|
126110
|
+
const specs = [
|
|
126111
|
+
["marketing", "marketingImageAssetId", "marketingImageBase64"],
|
|
126112
|
+
["square", "squareMarketingImageAssetId", "squareMarketingImageBase64"],
|
|
126113
|
+
["logo", "logoImageAssetId", "logoImageBase64"]
|
|
126114
|
+
];
|
|
126115
|
+
for (const [kind, idKey, b64Key] of specs) {
|
|
126116
|
+
const hasId = typeof body[idKey] === "string" && body[idKey].trim();
|
|
126117
|
+
const hasB64 = typeof body[b64Key] === "string" && body[b64Key].trim();
|
|
126118
|
+
const hasPath = Boolean(paths?.[kind]?.trim());
|
|
126119
|
+
if (hasId || hasB64 || hasPath) kinds.push(kind);
|
|
126120
|
+
}
|
|
126121
|
+
return kinds;
|
|
126122
|
+
}
|
|
126123
|
+
async function attachImages(configFile, body, accountId, config, googleApiUrl, verbose) {
|
|
126124
|
+
const kinds = imageKinds(body);
|
|
126125
|
+
if (kinds.length === 0) {
|
|
126126
|
+
const next2 = { ...body };
|
|
126127
|
+
delete next2["imagePaths"];
|
|
126128
|
+
return next2;
|
|
126129
|
+
}
|
|
126130
|
+
const paths = body["imagePaths"];
|
|
126131
|
+
if (kinds.some((kind) => Boolean(paths?.[kind]?.trim()))) {
|
|
126132
|
+
console.log("\n\u{1F4E4} \u6B63\u5728\u4E0A\u4F20 Demand Gen \u56FE\u7247\uFF08imagePaths \u2192 assetId\uFF09\u2026");
|
|
126133
|
+
}
|
|
126134
|
+
const slots = await resolvePmaxImageSlots(
|
|
126135
|
+
configFile,
|
|
126136
|
+
{
|
|
126137
|
+
imagePaths: body["imagePaths"],
|
|
126138
|
+
marketingImageAssetId: body["marketingImageAssetId"],
|
|
126139
|
+
squareMarketingImageAssetId: body["squareMarketingImageAssetId"],
|
|
126140
|
+
logoImageAssetId: body["logoImageAssetId"],
|
|
126141
|
+
marketingImageBase64: body["marketingImageBase64"],
|
|
126142
|
+
squareMarketingImageBase64: body["squareMarketingImageBase64"],
|
|
126143
|
+
logoImageBase64: body["logoImageBase64"]
|
|
126144
|
+
},
|
|
126145
|
+
accountId,
|
|
126146
|
+
config,
|
|
126147
|
+
googleApiUrl,
|
|
126148
|
+
{ verbose, logUploads: false, requiredKinds: kinds }
|
|
126149
|
+
);
|
|
126150
|
+
const next = { ...body, ...slots };
|
|
126151
|
+
delete next["imagePaths"];
|
|
126152
|
+
return next;
|
|
126153
|
+
}
|
|
126154
|
+
function prepareCreateBody(raw) {
|
|
126155
|
+
const unwrapped = unwrapDemandGenCreateBody(raw);
|
|
126156
|
+
if (unwrapped["statusV2"] != null) {
|
|
126157
|
+
console.error(
|
|
126158
|
+
"\n\u26A0\uFE0F \u5DF2\u5FFD\u7565 statusV2\u3002Demand Gen \u521B\u5EFA\u56FA\u5B9A\u6682\u505C\u3002\u542F\u7528\u4F1A\u82B1\u94B1\uFF0C\u521B\u5EFA\u540E\u518D demand-gen edit --status Enabled --confirm-enable --i-confirm\u3002\n"
|
|
126159
|
+
);
|
|
126160
|
+
}
|
|
126161
|
+
const body = normalizeDemandGenMoneyForWrite(unwrapped);
|
|
126162
|
+
delete body["account"];
|
|
126163
|
+
delete body["imagePaths"];
|
|
126164
|
+
delete body["statusV2"];
|
|
126165
|
+
delete body["hasBiddableConversion"];
|
|
126166
|
+
if (typeof body["biddingStrategyTypeV2"] === "string") {
|
|
126167
|
+
body["biddingStrategyTypeV2"] = body["biddingStrategyTypeV2"].trim().toUpperCase();
|
|
126168
|
+
}
|
|
126169
|
+
return body;
|
|
126170
|
+
}
|
|
126171
|
+
function explainCreateError(err) {
|
|
126172
|
+
const failure = parseDemandGenCreateFailure(readHttpRawBody2(err));
|
|
126173
|
+
if (failure) return demandGenResumeHint(failure);
|
|
126174
|
+
return err instanceof Error ? err.message : String(err);
|
|
126175
|
+
}
|
|
126176
|
+
async function postCreate(opts, path56, commandLabel) {
|
|
126177
|
+
const accountId = requireAccountId(opts.account);
|
|
126178
|
+
const raw = loadBody(opts.configFile);
|
|
126179
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126180
|
+
let body;
|
|
126181
|
+
try {
|
|
126182
|
+
body = await attachImages(opts.configFile, raw, accountId, config, googleApiUrl, opts.verbose);
|
|
126183
|
+
body = prepareCreateBody(body);
|
|
126184
|
+
} catch (err) {
|
|
126185
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
126186
|
+
}
|
|
126187
|
+
printIssues(demandGenCreateIssues(body));
|
|
126188
|
+
const url = path56 ? `${demandGenCollectionUrl(googleApiUrl, accountId)}/${path56}` : demandGenCollectionUrl(googleApiUrl, accountId);
|
|
126189
|
+
let data;
|
|
126190
|
+
try {
|
|
126191
|
+
data = await pmaxApiFetch(
|
|
126192
|
+
url,
|
|
126193
|
+
config,
|
|
126194
|
+
{ method: "POST", body: JSON.stringify(body) },
|
|
126195
|
+
opts.verbose
|
|
126196
|
+
);
|
|
126197
|
+
} catch (err) {
|
|
126198
|
+
fail(explainCreateError(err));
|
|
126199
|
+
}
|
|
126200
|
+
const payload = decorateDemandGenRead(data);
|
|
126201
|
+
if (await emit({ ...opts, account: accountId }, `ad-demand-gen-${path56 || "create"}`, commandLabel, payload, accountId)) {
|
|
126202
|
+
return;
|
|
126203
|
+
}
|
|
126204
|
+
console.log(`
|
|
126205
|
+
\u2705 ${commandLabel}
|
|
126206
|
+
`);
|
|
126207
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
126208
|
+
console.log();
|
|
126209
|
+
});
|
|
126210
|
+
}
|
|
126211
|
+
async function runDemandGenList(opts) {
|
|
126212
|
+
const accountId = requireAccountId(opts.account);
|
|
126213
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126214
|
+
let data;
|
|
126215
|
+
try {
|
|
126216
|
+
data = await pmaxApiFetch(demandGenCollectionUrl(googleApiUrl, accountId), config, {}, opts.verbose);
|
|
126217
|
+
} catch (err) {
|
|
126218
|
+
fail(`\u5217\u51FA Demand Gen \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126219
|
+
}
|
|
126220
|
+
const payload = {
|
|
126221
|
+
items: decorateDemandGenRead(data),
|
|
126222
|
+
agentHint: MONEY_HINT
|
|
126223
|
+
};
|
|
126224
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-list", "ad demand-gen list", payload, accountId)) {
|
|
126225
|
+
return;
|
|
126226
|
+
}
|
|
126227
|
+
const items3 = Array.isArray(payload.items) ? payload.items : [];
|
|
126228
|
+
console.log(`
|
|
126229
|
+
Demand Gen \u7CFB\u5217 ${items3.length} \u6761\uFF08\u8D26\u6237 ${accountId}\uFF09`);
|
|
126230
|
+
console.log(` ${MONEY_HINT}
|
|
126231
|
+
`);
|
|
126232
|
+
for (const row of items3) {
|
|
126233
|
+
if (!row || typeof row !== "object") continue;
|
|
126234
|
+
const item = row;
|
|
126235
|
+
console.log(
|
|
126236
|
+
` ${item["id"] ?? "\u2014"} ${item["name"] ?? "\u2014"} ${item["statusV2"] ?? "\u2014"} \u9884\u7B97 ${item["budgetYuan"] ?? "\u2014"} \u5143`
|
|
126237
|
+
);
|
|
126238
|
+
}
|
|
126239
|
+
console.log();
|
|
126240
|
+
});
|
|
126241
|
+
}
|
|
126242
|
+
async function runDemandGenGet(opts) {
|
|
126243
|
+
const accountId = requireAccountId(opts.account);
|
|
126244
|
+
const campaignId = opts.campaignId.trim();
|
|
126245
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126246
|
+
let data;
|
|
126247
|
+
try {
|
|
126248
|
+
data = await pmaxApiFetch(
|
|
126249
|
+
demandGenCampaignUrl(googleApiUrl, accountId, campaignId),
|
|
126250
|
+
config,
|
|
126251
|
+
{},
|
|
126252
|
+
opts.verbose
|
|
126253
|
+
);
|
|
126254
|
+
} catch (err) {
|
|
126255
|
+
fail(`\u8BFB\u53D6 Demand Gen \u8BE6\u60C5\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126256
|
+
}
|
|
126257
|
+
const payload = asRecord5(decorateDemandGenRead(data));
|
|
126258
|
+
payload["agentHint"] = MONEY_HINT;
|
|
126259
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-get", "ad demand-gen get", payload, campaignId)) {
|
|
126260
|
+
return;
|
|
126261
|
+
}
|
|
126262
|
+
console.log(`
|
|
126263
|
+
\u2705 Demand Gen ${campaignId}`);
|
|
126264
|
+
console.log(` \u540D\u79F0\uFF1A${payload["name"] ?? "\u2014"} \u72B6\u6001\uFF1A${payload["statusV2"] ?? "\u2014"}`);
|
|
126265
|
+
console.log(` \u9884\u7B97\uFF1A${payload["budgetYuan"] ?? "\u2014"} \u5143\uFF08\u7F51\u5173\u5206 ${payload["budget"] ?? "\u2014"}\uFF09`);
|
|
126266
|
+
console.log(` \u51FA\u4EF7\uFF1A${payload["biddingStrategyTypeV2"] ?? "\u2014"}`);
|
|
126267
|
+
console.log(` ${MONEY_HINT}`);
|
|
126268
|
+
console.log(` \u5B8C\u6574\u7ED3\u6784\u8BF7\u4F7F\u7528 --json-out
|
|
126269
|
+
`);
|
|
126270
|
+
});
|
|
126271
|
+
}
|
|
126272
|
+
async function runDemandGenMinimumBudget(opts) {
|
|
126273
|
+
const accountId = requireAccountId(opts.account);
|
|
126274
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126275
|
+
let data;
|
|
126276
|
+
try {
|
|
126277
|
+
data = await pmaxApiFetch(
|
|
126278
|
+
demandGenMinimumBudgetUrl(googleApiUrl, accountId, opts.currency),
|
|
126279
|
+
config,
|
|
126280
|
+
{},
|
|
126281
|
+
opts.verbose
|
|
126282
|
+
);
|
|
126283
|
+
} catch (err) {
|
|
126284
|
+
fail(`\u8BFB\u53D6\u6700\u4F4E\u65E5\u9884\u7B97\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126285
|
+
}
|
|
126286
|
+
const payload = decorateDemandGenRead(data);
|
|
126287
|
+
if (await emit(
|
|
126288
|
+
{ ...opts, account: accountId },
|
|
126289
|
+
"ad-demand-gen-minimum-budget",
|
|
126290
|
+
"ad demand-gen minimum-budget",
|
|
126291
|
+
payload,
|
|
126292
|
+
accountId
|
|
126293
|
+
)) {
|
|
126294
|
+
return;
|
|
126295
|
+
}
|
|
126296
|
+
const row = asRecord5(payload);
|
|
126297
|
+
console.log(
|
|
126298
|
+
`
|
|
126299
|
+
\u6700\u4F4E\u65E5\u9884\u7B97 ${row["minimumDailyBudgetYuan"] ?? "\u2014"} \u5143\uFF08${row["currencyCode"] ?? "\u8D26\u6237\u5E01\u79CD"}\uFF0C\u7F51\u5173\u5206 ${row["minimumDailyBudget"] ?? "\u2014"}\uFF09
|
|
126300
|
+
`
|
|
126301
|
+
);
|
|
126302
|
+
});
|
|
126303
|
+
}
|
|
126304
|
+
async function runDemandGenConversions(opts) {
|
|
126305
|
+
const accountId = requireAccountId(opts.account);
|
|
126306
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126307
|
+
let data;
|
|
126308
|
+
try {
|
|
126309
|
+
data = await pmaxApiFetch(
|
|
126310
|
+
demandGenBiddableConversionsUrl(googleApiUrl, accountId),
|
|
126311
|
+
config,
|
|
126312
|
+
{},
|
|
126313
|
+
opts.verbose
|
|
126314
|
+
);
|
|
126315
|
+
} catch (err) {
|
|
126316
|
+
fail(`\u8BFB\u53D6\u53EF\u51FA\u4EF7\u8F6C\u5316\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126317
|
+
}
|
|
126318
|
+
if (await emit(
|
|
126319
|
+
{ ...opts, account: accountId },
|
|
126320
|
+
"ad-demand-gen-conversions",
|
|
126321
|
+
"ad demand-gen conversions",
|
|
126322
|
+
data,
|
|
126323
|
+
accountId
|
|
126324
|
+
)) {
|
|
126325
|
+
return;
|
|
126326
|
+
}
|
|
126327
|
+
const row = asRecord5(data);
|
|
126328
|
+
console.log(
|
|
126329
|
+
`
|
|
126330
|
+
\u53EF\u51FA\u4EF7\u8F6C\u5316\uFF1A${row["hasBiddableConversion"] === true ? "\u6709" : "\u65E0"}\u3002\u6CA1\u6709\u65F6\u521B\u5EFA\u7528 TARGET_SPEND\uFF0C\u4E0D\u8981\u7528 MAXIMIZE_CONVERSIONS\u3002
|
|
126331
|
+
`
|
|
126332
|
+
);
|
|
126333
|
+
});
|
|
126334
|
+
}
|
|
126335
|
+
async function runDemandGenDraft(opts) {
|
|
126336
|
+
const accountId = requireAccountId(opts.account);
|
|
126337
|
+
const body = normalizeDemandGenMoneyForWrite(loadBody(opts.configFile));
|
|
126338
|
+
delete body["account"];
|
|
126339
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126340
|
+
let data;
|
|
126341
|
+
try {
|
|
126342
|
+
data = await pmaxApiFetch(
|
|
126343
|
+
`${demandGenCollectionUrl(googleApiUrl, accountId)}/draft`,
|
|
126344
|
+
config,
|
|
126345
|
+
{ method: "POST", body: JSON.stringify(body) },
|
|
126346
|
+
opts.verbose
|
|
126347
|
+
);
|
|
126348
|
+
} catch (err) {
|
|
126349
|
+
fail(`\u751F\u6210\u8349\u7A3F\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126350
|
+
}
|
|
126351
|
+
const payload = decorateDemandGenRead(data);
|
|
126352
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-draft", "ad demand-gen draft", payload, accountId)) {
|
|
126353
|
+
return;
|
|
126354
|
+
}
|
|
126355
|
+
const row = asRecord5(payload);
|
|
126356
|
+
console.log(`
|
|
126357
|
+
\u2705 \u8349\u7A3F\u5DF2\u751F\u6210\uFF08\u672A\u5199\u5165 Google\uFF09`);
|
|
126358
|
+
console.log(` \u9700\u4EBA\u5DE5\u770B\uFF1A${row["reviewRequired"] === true ? "\u662F" : "\u5426"}`);
|
|
126359
|
+
console.log(` createRequest \u91CC\u7684\u91D1\u989D\u5DF2\u9644\u5E26 *Yuan\u3002\u786E\u8BA4\u524D\u8865\u89C6\u9891\u3001\u56FE\u7247\u3001\u5730\u57DF\u548C\u8BED\u8A00\u3002
|
|
126360
|
+
`);
|
|
126361
|
+
});
|
|
126362
|
+
}
|
|
126363
|
+
async function runDemandGenValidate(opts) {
|
|
126364
|
+
await postCreate(opts, "validate", "ad demand-gen validate");
|
|
126365
|
+
}
|
|
126366
|
+
async function runDemandGenCreate(opts) {
|
|
126367
|
+
await postCreate(opts, "", "ad demand-gen create");
|
|
126368
|
+
}
|
|
126369
|
+
async function runDemandGenConfirm(opts) {
|
|
126370
|
+
await postCreate(opts, "draft/confirm", "ad demand-gen confirm");
|
|
126371
|
+
}
|
|
126372
|
+
function readFlagNumber(value, label2) {
|
|
126373
|
+
const amount = typeof value === "number" ? value : Number(String(value).trim());
|
|
126374
|
+
if (!Number.isFinite(amount)) fail(`${label2}\u987B\u4E3A\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${String(value)}`);
|
|
126375
|
+
return amount;
|
|
126376
|
+
}
|
|
126377
|
+
async function runDemandGenEdit(opts) {
|
|
126378
|
+
const accountId = requireAccountId(opts.account);
|
|
126379
|
+
const campaignId = opts.campaignId.trim();
|
|
126380
|
+
let body;
|
|
126381
|
+
if (opts.patchFile) {
|
|
126382
|
+
body = normalizeDemandGenMoneyForWrite(loadBody(opts.patchFile));
|
|
126383
|
+
delete body["account"];
|
|
126384
|
+
delete body["id"];
|
|
126385
|
+
delete body["agentHint"];
|
|
126386
|
+
} else {
|
|
126387
|
+
body = {};
|
|
126388
|
+
if (opts.name !== void 0) body["name"] = opts.name.trim();
|
|
126389
|
+
if (opts.status !== void 0) {
|
|
126390
|
+
const status2 = normalizeCampaignStatus(opts.status);
|
|
126391
|
+
if (!status2) fail("Campaign status must be Enabled, Paused, or Removed.");
|
|
126392
|
+
body["statusV2"] = status2;
|
|
126393
|
+
}
|
|
126394
|
+
if (opts.budget !== void 0) body["budget"] = readFlagNumber(opts.budget, "\u9884\u7B97");
|
|
126395
|
+
if (opts.budgetId !== void 0) body["budgetId"] = opts.budgetId.trim();
|
|
126396
|
+
if (opts.bidding !== void 0) body["biddingStrategyTypeV2"] = opts.bidding.trim().toUpperCase();
|
|
126397
|
+
if (opts.targetCpa !== void 0) {
|
|
126398
|
+
body["targetCpa_BidingAmount"] = readFlagNumber(opts.targetCpa, "\u76EE\u6807 CPA");
|
|
126399
|
+
}
|
|
126400
|
+
if (opts.targetRoas !== void 0) body["targetRoas"] = readFlagNumber(opts.targetRoas, "\u76EE\u6807 ROAS");
|
|
126401
|
+
if (opts.confirmLargeChange) body["confirmLargeChange"] = true;
|
|
126402
|
+
if (opts.confirmEnable) body["confirmEnable"] = true;
|
|
126403
|
+
body = normalizeDemandGenMoneyForWrite(body);
|
|
126404
|
+
}
|
|
126405
|
+
if (Object.keys(body).length === 0) {
|
|
126406
|
+
fail("\u8BF7\u6307\u5B9A --patch-file\uFF0C\u6216\u81F3\u5C11\u4E00\u9879\u4FEE\u6539\uFF08--name / --status / --budget / --bidding / \u2026\uFF09");
|
|
126407
|
+
}
|
|
126408
|
+
const status = normalizeCampaignStatus(body["statusV2"]);
|
|
126409
|
+
if (status) body["statusV2"] = status;
|
|
126410
|
+
printIssues(demandGenEditIssues(body));
|
|
126411
|
+
if (status === "Enabled") {
|
|
126412
|
+
ensureIConfirm({
|
|
126413
|
+
iConfirm: opts.iConfirm,
|
|
126414
|
+
actionLabel: `ad demand-gen edit --status Enabled\uFF08\u542F\u7528\u7CFB\u5217 ${campaignId} \u4F1A\u5F00\u59CB\u82B1\u94B1\uFF09`
|
|
126415
|
+
});
|
|
126416
|
+
}
|
|
126417
|
+
if (status === "Removed") {
|
|
126418
|
+
ensureIConfirm({
|
|
126419
|
+
iConfirm: opts.iConfirm,
|
|
126420
|
+
actionLabel: `ad demand-gen edit --status Removed\uFF08\u79FB\u9664 Demand Gen \u7CFB\u5217 ${campaignId}\uFF0C\u4E0D\u53EF\u6062\u590D\uFF09`
|
|
126421
|
+
});
|
|
126422
|
+
}
|
|
126423
|
+
if (body["automatic"] === true) {
|
|
126424
|
+
fail("\u4EBA\u5DE5\u64CD\u4F5C\u4E0D\u8981\u5E26 automatic\u3002");
|
|
126425
|
+
}
|
|
126426
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126427
|
+
try {
|
|
126428
|
+
await pmaxApiFetch(
|
|
126429
|
+
demandGenCampaignUrl(googleApiUrl, accountId, campaignId),
|
|
126430
|
+
config,
|
|
126431
|
+
{ method: "PATCH", body: JSON.stringify(body) },
|
|
126432
|
+
opts.verbose
|
|
126433
|
+
);
|
|
126434
|
+
} catch (err) {
|
|
126435
|
+
fail(`\u66F4\u65B0 Demand Gen \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126436
|
+
}
|
|
126437
|
+
const payload = { ok: true, campaignId, patch: body };
|
|
126438
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-edit", "ad demand-gen edit", payload, campaignId)) {
|
|
126439
|
+
return;
|
|
126440
|
+
}
|
|
126441
|
+
console.log(`
|
|
126442
|
+
\u2705 Demand Gen \u7CFB\u5217 ${campaignId} \u5DF2\u66F4\u65B0
|
|
126443
|
+
`);
|
|
126444
|
+
});
|
|
126445
|
+
}
|
|
126446
|
+
async function runDemandGenContent(opts) {
|
|
126447
|
+
const accountId = requireAccountId(opts.account);
|
|
126448
|
+
const body = loadBody(opts.configFile);
|
|
126449
|
+
delete body["account"];
|
|
126450
|
+
if (opts.adId?.trim()) {
|
|
126451
|
+
delete body["targetedLocations"];
|
|
126452
|
+
delete body["targetedLanguages"];
|
|
126453
|
+
}
|
|
126454
|
+
if (Object.keys(body).length === 0) fail("content JSON \u6CA1\u6709\u8981\u6539\u7684\u5B57\u6BB5");
|
|
126455
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126456
|
+
let next = body;
|
|
126457
|
+
try {
|
|
126458
|
+
next = await attachImages(opts.configFile, body, accountId, config, googleApiUrl, opts.verbose);
|
|
126459
|
+
} catch (err) {
|
|
126460
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
126461
|
+
}
|
|
126462
|
+
delete next["imagePaths"];
|
|
126463
|
+
const url = opts.adId?.trim() ? demandGenAdUrl(googleApiUrl, accountId, opts.campaignId.trim(), opts.adGroupId.trim(), opts.adId.trim(), "content") : demandGenAdGroupUrl(googleApiUrl, accountId, opts.campaignId.trim(), opts.adGroupId.trim(), "content");
|
|
126464
|
+
try {
|
|
126465
|
+
await pmaxApiFetch(url, config, { method: "PUT", body: JSON.stringify(next) }, opts.verbose);
|
|
126466
|
+
} catch (err) {
|
|
126467
|
+
fail(`\u66F4\u65B0\u5E7F\u544A\u5185\u5BB9\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126468
|
+
}
|
|
126469
|
+
const payload = { ok: true, campaignId: opts.campaignId, adGroupId: opts.adGroupId, adId: opts.adId ?? null };
|
|
126470
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-content", "ad demand-gen content", payload)) return;
|
|
126471
|
+
console.log("\n\u2705 \u5E7F\u544A\u5185\u5BB9\u5DF2\u66F4\u65B0\u3002\u6539\u5B8C\u7528 demand-gen get \u8BFB\u56DE\u3002waitForReview \u65F6\u4E0D\u8981\u518D\u6539\u6587\u6848\u3002\n");
|
|
126472
|
+
});
|
|
126473
|
+
}
|
|
126474
|
+
async function runDemandGenChannels(opts) {
|
|
126475
|
+
const accountId = requireAccountId(opts.account);
|
|
126476
|
+
const hasStrategy = Boolean(opts.strategy?.trim());
|
|
126477
|
+
const hasSelected = Boolean(opts.selected?.trim());
|
|
126478
|
+
if (hasStrategy === hasSelected) {
|
|
126479
|
+
fail("channelStrategy \u548C selectedChannels \u4E8C\u9009\u4E00\uFF1A\u53EA\u4F20 --strategy\uFF0C\u6216\u53EA\u4F20 --selected\u3002");
|
|
126480
|
+
}
|
|
126481
|
+
const body = {};
|
|
126482
|
+
if (hasStrategy) body["channelStrategy"] = opts.strategy.trim();
|
|
126483
|
+
if (hasSelected) {
|
|
126484
|
+
try {
|
|
126485
|
+
body["selectedChannels"] = buildSelectedChannels(opts.selected.split(","));
|
|
126486
|
+
} catch (err) {
|
|
126487
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
126488
|
+
}
|
|
126489
|
+
}
|
|
126490
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126491
|
+
const url = demandGenAdGroupUrl(
|
|
126492
|
+
googleApiUrl,
|
|
126493
|
+
accountId,
|
|
126494
|
+
opts.campaignId.trim(),
|
|
126495
|
+
opts.adGroupId.trim(),
|
|
126496
|
+
"channels"
|
|
126497
|
+
);
|
|
126498
|
+
try {
|
|
126499
|
+
await pmaxApiFetch(url, config, { method: "PATCH", body: JSON.stringify(body) }, opts.verbose);
|
|
126500
|
+
} catch (err) {
|
|
126501
|
+
fail(`\u66F4\u65B0\u7248\u4F4D\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126502
|
+
}
|
|
126503
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-channels", "ad demand-gen channels", { ok: true, patch: body })) {
|
|
126504
|
+
return;
|
|
126505
|
+
}
|
|
126506
|
+
console.log("\n\u2705 \u7248\u4F4D\u5DF2\u66F4\u65B0\n");
|
|
126507
|
+
});
|
|
126508
|
+
}
|
|
126509
|
+
async function putStatus(opts, kind) {
|
|
126510
|
+
const status = normalizeAdStatus(opts.status);
|
|
126511
|
+
if (!status) {
|
|
126512
|
+
fail("\u5E7F\u544A\u548C\u5E7F\u544A\u7EC4\u53EA\u63A5\u53D7 Enabled \u6216 Paused\u3002\u4E0D\u8981\u7528\u5B83\u542F\u7528\u7CFB\u5217\uFF1B\u5220\u5149\u4F1A 400\uFF0C\u53EA\u60F3\u505C\u6295\u7528 Paused\u3002");
|
|
126513
|
+
}
|
|
126514
|
+
const accountId = requireAccountId(opts.account);
|
|
126515
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126516
|
+
const url = kind === "ad" ? demandGenAdUrl(
|
|
126517
|
+
googleApiUrl,
|
|
126518
|
+
accountId,
|
|
126519
|
+
opts.campaignId.trim(),
|
|
126520
|
+
opts.adGroupId.trim(),
|
|
126521
|
+
opts.adId.trim(),
|
|
126522
|
+
"status"
|
|
126523
|
+
) : demandGenAdGroupUrl(
|
|
126524
|
+
googleApiUrl,
|
|
126525
|
+
accountId,
|
|
126526
|
+
opts.campaignId.trim(),
|
|
126527
|
+
opts.adGroupId.trim(),
|
|
126528
|
+
"status"
|
|
126529
|
+
);
|
|
126530
|
+
try {
|
|
126531
|
+
await pmaxApiFetch(
|
|
126532
|
+
url,
|
|
126533
|
+
config,
|
|
126534
|
+
{ method: "PUT", body: JSON.stringify({ status }) },
|
|
126535
|
+
opts.verbose
|
|
126536
|
+
);
|
|
126537
|
+
} catch (err) {
|
|
126538
|
+
fail(`\u66F4\u65B0\u72B6\u6001\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126539
|
+
}
|
|
126540
|
+
console.log(`
|
|
126541
|
+
\u2705 \u5DF2\u8BBE\u4E3A ${status}\u3002\u7CFB\u5217\u72B6\u6001\u4E0D\u4F1A\u8DDF\u7740\u53D8\u3002
|
|
126542
|
+
`);
|
|
126543
|
+
});
|
|
126544
|
+
}
|
|
126545
|
+
async function runDemandGenAdGroupStatus(opts) {
|
|
126546
|
+
await putStatus(opts, "adgroup");
|
|
126547
|
+
}
|
|
126548
|
+
async function runDemandGenAdStatus(opts) {
|
|
126549
|
+
await putStatus(opts, "ad");
|
|
126550
|
+
}
|
|
126551
|
+
async function runDemandGenAdGroupRename(opts) {
|
|
126552
|
+
const name2 = opts.name.trim();
|
|
126553
|
+
if (!name2 || name2.length > 255) fail("\u5E7F\u544A\u7EC4\u540D\u987B\u4E3A 1\uFF5E255 \u4E2A\u5B57\u7B26\u3002");
|
|
126554
|
+
const accountId = requireAccountId(opts.account);
|
|
126555
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126556
|
+
const url = demandGenAdGroupUrl(
|
|
126557
|
+
googleApiUrl,
|
|
126558
|
+
accountId,
|
|
126559
|
+
opts.campaignId.trim(),
|
|
126560
|
+
opts.adGroupId.trim(),
|
|
126561
|
+
"name"
|
|
126562
|
+
);
|
|
126563
|
+
try {
|
|
126564
|
+
await pmaxApiFetch(url, config, { method: "PUT", body: JSON.stringify({ name: name2 }) }, opts.verbose);
|
|
126565
|
+
} catch (err) {
|
|
126566
|
+
fail(`\u5E7F\u544A\u7EC4\u6539\u540D\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126567
|
+
}
|
|
126568
|
+
console.log(`
|
|
126569
|
+
\u2705 \u5E7F\u544A\u7EC4\u5DF2\u6539\u540D\u4E3A ${name2}
|
|
126570
|
+
`);
|
|
126571
|
+
});
|
|
126572
|
+
}
|
|
126573
|
+
async function runDemandGenAudience(opts) {
|
|
126574
|
+
const audienceResourceName = opts.resourceName.trim();
|
|
126575
|
+
if (!audienceResourceName.includes("/audiences/")) {
|
|
126576
|
+
fail("Audience resource name must be customers/{id}/audiences/{audienceId}.");
|
|
126577
|
+
}
|
|
126578
|
+
const accountId = requireAccountId(opts.account);
|
|
126579
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126580
|
+
const url = demandGenAdGroupUrl(
|
|
126581
|
+
googleApiUrl,
|
|
126582
|
+
accountId,
|
|
126583
|
+
opts.campaignId.trim(),
|
|
126584
|
+
opts.adGroupId.trim(),
|
|
126585
|
+
"audience"
|
|
126586
|
+
);
|
|
126587
|
+
try {
|
|
126588
|
+
await pmaxApiFetch(
|
|
126589
|
+
url,
|
|
126590
|
+
config,
|
|
126591
|
+
{ method: "PUT", body: JSON.stringify({ audienceResourceName }) },
|
|
126592
|
+
opts.verbose
|
|
126593
|
+
);
|
|
126594
|
+
} catch (err) {
|
|
126595
|
+
fail(`\u66FF\u6362\u53D7\u4F17\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126596
|
+
}
|
|
126597
|
+
console.log("\n\u2705 \u5E7F\u544A\u7EC4\u53D7\u4F17\u5DF2\u66FF\u6362\u3002\u4E00\u4E2A\u5E7F\u544A\u7EC4\u53EA\u80FD\u7ED1\u4E00\u4E2A Audience\u3002\n");
|
|
126598
|
+
});
|
|
126599
|
+
}
|
|
126600
|
+
async function runDemandGenAudienceKeywords(opts) {
|
|
126601
|
+
const audienceKeywords = opts.keywords.split(",").map((item) => item.trim()).filter(Boolean);
|
|
126602
|
+
if (audienceKeywords.length < 1 || audienceKeywords.length > 10) {
|
|
126603
|
+
fail("Audience keyword count must be between 1 and 10.");
|
|
126604
|
+
}
|
|
126605
|
+
const accountId = requireAccountId(opts.account);
|
|
126606
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126607
|
+
const url = demandGenAdGroupUrl(
|
|
126608
|
+
googleApiUrl,
|
|
126609
|
+
accountId,
|
|
126610
|
+
opts.campaignId.trim(),
|
|
126611
|
+
opts.adGroupId.trim(),
|
|
126612
|
+
"audience/keywords"
|
|
126613
|
+
);
|
|
126614
|
+
try {
|
|
126615
|
+
await pmaxApiFetch(
|
|
126616
|
+
url,
|
|
126617
|
+
config,
|
|
126618
|
+
{ method: "PUT", body: JSON.stringify({ audienceKeywords }) },
|
|
126619
|
+
opts.verbose
|
|
126620
|
+
);
|
|
126621
|
+
} catch (err) {
|
|
126622
|
+
fail(`\u66FF\u6362\u53D7\u4F17\u8BCD\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126623
|
+
}
|
|
126624
|
+
console.log("\n\u2705 \u641C\u7D22\u81EA\u5B9A\u4E49\u53D7\u4F17\u7684\u8BCD\u5DF2\u6574\u4EFD\u66FF\u6362\n");
|
|
126625
|
+
});
|
|
126626
|
+
}
|
|
126627
|
+
async function postChild(opts, kind) {
|
|
126628
|
+
const accountId = requireAccountId(opts.account);
|
|
126629
|
+
const raw = loadBody(opts.configFile);
|
|
126630
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126631
|
+
let body;
|
|
126632
|
+
try {
|
|
126633
|
+
body = await attachImages(opts.configFile, raw, accountId, config, googleApiUrl, opts.verbose);
|
|
126634
|
+
} catch (err) {
|
|
126635
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
126636
|
+
}
|
|
126637
|
+
delete body["account"];
|
|
126638
|
+
delete body["imagePaths"];
|
|
126639
|
+
const url = kind === "adgroup" ? `${demandGenCampaignUrl(googleApiUrl, accountId, opts.campaignId.trim())}/ad-groups` : `${demandGenAdGroupUrl(googleApiUrl, accountId, opts.campaignId.trim(), opts.adGroupId.trim())}/ads`;
|
|
126640
|
+
let data;
|
|
126641
|
+
try {
|
|
126642
|
+
data = await pmaxApiFetch(url, config, { method: "POST", body: JSON.stringify(body) }, opts.verbose);
|
|
126643
|
+
} catch (err) {
|
|
126644
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
126645
|
+
}
|
|
126646
|
+
if (await emit({ ...opts, account: accountId }, `ad-demand-gen-add-${kind}`, `ad demand-gen add-${kind}`, data)) {
|
|
126647
|
+
return;
|
|
126648
|
+
}
|
|
126649
|
+
console.log(`
|
|
126650
|
+
\u2705 \u5DF2\u6DFB\u52A0${kind === "adgroup" ? "\u5E7F\u544A\u7EC4" : "\u5E7F\u544A"}
|
|
126651
|
+
`);
|
|
126652
|
+
console.log(JSON.stringify(data, null, 2));
|
|
126653
|
+
console.log();
|
|
126654
|
+
});
|
|
126655
|
+
}
|
|
126656
|
+
async function runDemandGenAddAdGroup(opts) {
|
|
126657
|
+
await postChild(opts, "adgroup");
|
|
126658
|
+
}
|
|
126659
|
+
async function runDemandGenAddAd(opts) {
|
|
126660
|
+
await postChild(opts, "ad");
|
|
126661
|
+
}
|
|
126662
|
+
async function runDemandGenDeleteAd(opts) {
|
|
126663
|
+
ensureIConfirm({
|
|
126664
|
+
iConfirm: opts.iConfirm,
|
|
126665
|
+
actionLabel: `ad demand-gen delete-ad\uFF08\u5E7F\u544A\u7EC4\u81F3\u5C11\u7559 1 \u6761\u5E7F\u544A\uFF0C\u5426\u5219\u4F1A 400\uFF09`
|
|
126666
|
+
});
|
|
126667
|
+
const accountId = requireAccountId(opts.account);
|
|
126668
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126669
|
+
const url = demandGenAdUrl(
|
|
126670
|
+
googleApiUrl,
|
|
126671
|
+
accountId,
|
|
126672
|
+
opts.campaignId.trim(),
|
|
126673
|
+
opts.adGroupId.trim(),
|
|
126674
|
+
opts.adId.trim()
|
|
126675
|
+
);
|
|
126676
|
+
try {
|
|
126677
|
+
await pmaxApiFetch(url, config, { method: "DELETE" }, opts.verbose);
|
|
126678
|
+
} catch (err) {
|
|
126679
|
+
fail(`\u5220\u9664\u5E7F\u544A\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126680
|
+
}
|
|
126681
|
+
console.log("\n\u2705 \u5E7F\u544A\u5DF2\u5220\u9664\n");
|
|
126682
|
+
});
|
|
126683
|
+
}
|
|
126684
|
+
async function runDemandGenDeleteAdGroup(opts) {
|
|
126685
|
+
ensureIConfirm({
|
|
126686
|
+
iConfirm: opts.iConfirm,
|
|
126687
|
+
actionLabel: `ad demand-gen delete-adgroup\uFF08\u7CFB\u5217\u81F3\u5C11\u7559 1 \u4E2A\u5E7F\u544A\u7EC4\uFF0C\u5426\u5219\u4F1A 400\uFF09`
|
|
126688
|
+
});
|
|
126689
|
+
const accountId = requireAccountId(opts.account);
|
|
126690
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126691
|
+
const url = demandGenAdGroupUrl(
|
|
126692
|
+
googleApiUrl,
|
|
126693
|
+
accountId,
|
|
126694
|
+
opts.campaignId.trim(),
|
|
126695
|
+
opts.adGroupId.trim()
|
|
126696
|
+
);
|
|
126697
|
+
try {
|
|
126698
|
+
await pmaxApiFetch(url, config, { method: "DELETE" }, opts.verbose);
|
|
126699
|
+
} catch (err) {
|
|
126700
|
+
fail(`\u5220\u9664\u5E7F\u544A\u7EC4\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126701
|
+
}
|
|
126702
|
+
console.log("\n\u2705 \u5E7F\u544A\u7EC4\u5DF2\u5220\u9664\n");
|
|
126703
|
+
});
|
|
126704
|
+
}
|
|
126705
|
+
async function runDemandGenReport(opts) {
|
|
126706
|
+
const accountId = requireAccountId(opts.account);
|
|
126707
|
+
const startDate = toGoogleDate(opts.startDate, -7).replace(/\//g, "-");
|
|
126708
|
+
const endDate = toGoogleDate(opts.endDate, 0).replace(/\//g, "-");
|
|
126709
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126710
|
+
let data;
|
|
126711
|
+
try {
|
|
126712
|
+
data = await pmaxApiFetch(
|
|
126713
|
+
demandGenChannelReportUrl(googleApiUrl, accountId, opts.campaignId.trim(), startDate, endDate),
|
|
126714
|
+
config,
|
|
126715
|
+
{},
|
|
126716
|
+
opts.verbose
|
|
126717
|
+
);
|
|
126718
|
+
} catch (err) {
|
|
126719
|
+
fail(`\u8BFB\u53D6\u7248\u4F4D\u62A5\u8868\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126720
|
+
}
|
|
126721
|
+
if (await emit({ ...opts, account: accountId }, "ad-demand-gen-report", "ad demand-gen report", data, opts.campaignId)) {
|
|
126722
|
+
return;
|
|
126723
|
+
}
|
|
126724
|
+
console.log(`
|
|
126725
|
+
\u2705 \u7248\u4F4D\u62A5\u8868 ${startDate} ~ ${endDate}`);
|
|
126726
|
+
console.log(" spend \u662F\u8D26\u6237\u5E01\u79CD\u6D6E\u70B9\uFF0C\u4E0D\u8981\u62FF\u53BB PATCH budget\u3002\u6682\u505C\u7CFB\u5217\u5168 0 \u662F\u6B63\u5E38\u7684\u3002\n");
|
|
126727
|
+
});
|
|
126728
|
+
}
|
|
126729
|
+
async function runDemandGenConversionsValidate(opts) {
|
|
126730
|
+
const accountId = requireAccountId(opts.account);
|
|
126731
|
+
const body = loadBody(opts.configFile);
|
|
126732
|
+
delete body["account"];
|
|
126733
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
126734
|
+
let data;
|
|
126735
|
+
try {
|
|
126736
|
+
data = await pmaxApiFetch(
|
|
126737
|
+
demandGenConversionsValidateUrl(googleApiUrl, accountId),
|
|
126738
|
+
config,
|
|
126739
|
+
{ method: "POST", body: JSON.stringify(body) },
|
|
126740
|
+
opts.verbose
|
|
126741
|
+
);
|
|
126742
|
+
} catch (err) {
|
|
126743
|
+
fail(`\u79BB\u7EBF\u8F6C\u5316\u6821\u9A8C\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
126744
|
+
}
|
|
126745
|
+
if (await emit(
|
|
126746
|
+
{ ...opts, account: accountId },
|
|
126747
|
+
"ad-demand-gen-conversions-validate",
|
|
126748
|
+
"ad demand-gen conversions-validate",
|
|
126749
|
+
data,
|
|
126750
|
+
accountId
|
|
126751
|
+
)) {
|
|
126752
|
+
return;
|
|
126753
|
+
}
|
|
126754
|
+
console.log("\n\u2705 \u79BB\u7EBF\u8F6C\u5316\u53EA\u505A\u4E86\u6821\u9A8C\uFF0C\u6CA1\u6709\u5199\u5165\u3002validatedOnly \u5E94\u4E3A true\u3002\n");
|
|
126755
|
+
});
|
|
126756
|
+
}
|
|
126757
|
+
|
|
126758
|
+
// src/commands/ad/demand-gen-register.ts
|
|
126759
|
+
function withCommon(cmd) {
|
|
126760
|
+
return cmd.option("-t, --token <token>", "Auth Token").option("--json-out <path>", "\u843D\u76D8\u54CD\u5E94\u5E76\u66F4\u65B0 cli-manifest").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false);
|
|
126761
|
+
}
|
|
126762
|
+
function registerDemandGen(adCmd) {
|
|
126763
|
+
const dg = adCmd.command("demand-gen").description(
|
|
126764
|
+
"Google Demand Gen\uFF08\u9700\u6C42\u5F00\u53D1\uFF09\u3002\u4E0D\u8981\u7528 campaign-edit / pmax-edit / campaign-create\u3002\n \u521B\u5EFA\u56FA\u5B9A\u6682\u505C\u3002\u542F\u7528\u7CFB\u5217\u4F1A\u82B1\u94B1\uFF1Aedit --status Enabled --confirm-enable --i-confirm\u3002"
|
|
126765
|
+
);
|
|
126766
|
+
withCommon(
|
|
126767
|
+
dg.command("list").description("\u672A\u5220\u9664\u7684 Demand Gen \u5217\u8868\u3002budget \u662F\u5206\uFF0C\u770B budgetYuan\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID")
|
|
126768
|
+
).action(runDemandGenList);
|
|
126769
|
+
withCommon(
|
|
126770
|
+
dg.command("get").description("\u7CFB\u5217\u8BE6\u60C5\uFF1A\u5E7F\u544A\u7EC4\u3001\u5E7F\u544A\u3001\u53D7\u4F17\u3001\u80FD\u5426\u6295\u653E\u3002\u4E0D\u8981\u628A\u6574\u5305 PATCH \u56DE\u53BB\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID")
|
|
126771
|
+
).action(runDemandGenGet);
|
|
126772
|
+
withCommon(
|
|
126773
|
+
dg.command("minimum-budget").description("\u5E01\u79CD\u6700\u4F4E\u65E5\u9884\u7B97\u3002\u4E0D\u4F20 --currency \u7528\u8D26\u6237\u5E01\u79CD\u3002minimumDailyBudget \u662F\u5206\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").option("--currency <code>", "\u5982 USD")
|
|
126774
|
+
).action(runDemandGenMinimumBudget);
|
|
126775
|
+
withCommon(
|
|
126776
|
+
dg.command("conversions").description("\u53EF\u51FA\u4EF7\u7684\u8BE2\u76D8/\u6210\u4EA4\u8F6C\u5316\u3002\u6CA1\u6709\u65F6\u521B\u5EFA\u7528 TARGET_SPEND\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID")
|
|
126777
|
+
).action(runDemandGenConversions);
|
|
126778
|
+
withCommon(
|
|
126779
|
+
dg.command("draft").description("\u6309\u843D\u5730\u9875\u51FA\u8349\u7A3F\uFF0C\u4E0D\u5199 Google\u3002\u91D1\u989D\u586B\u5143\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--config-file <path>", "\u8349\u7A3F JSON\uFF08pageUrl\u3001mainKeyword\uFF09")
|
|
126780
|
+
).action(runDemandGenDraft);
|
|
126781
|
+
withCommon(
|
|
126782
|
+
dg.command("validate").description("\u6574\u5355\u6821\u9A8C\uFF0C\u4E0D\u843D\u5E93\u3002JSON \u91D1\u989D\u586B\u5143\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--config-file <path>", "\u521B\u5EFA JSON")
|
|
126783
|
+
).action(runDemandGenValidate);
|
|
126784
|
+
withCommon(
|
|
126785
|
+
dg.command("create").description("\u521B\u5EFA\u5E76\u56FA\u5B9A\u6682\u505C\u3002\u5931\u8D25\u65F6\u628A budgetId/campaignId/adGroupId \u5199\u56DE JSON \u518D\u8DD1\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--config-file <path>", "\u521B\u5EFA JSON\uFF0C\u6A21\u677F\u89C1 assets/demand-gen-create-template.json")
|
|
126786
|
+
).action(runDemandGenCreate);
|
|
126787
|
+
withCommon(
|
|
126788
|
+
dg.command("confirm").description("\u653F\u7B56\u9884\u5BA1\u540E\u521B\u5EFA\uFF0C\u4ECD\u7136\u6682\u505C\u3002\u53EF\u76F4\u63A5\u4F20\u8349\u7A3F JSON\uFF08\u7528\u91CC\u9762\u7684 createRequest\uFF09\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--config-file <path>", "\u8865\u5B8C\u7D20\u6750\u540E\u7684\u521B\u5EFA JSON\uFF0C\u6216\u8349\u7A3F\u54CD\u5E94")
|
|
126789
|
+
).action(runDemandGenConfirm);
|
|
126790
|
+
withCommon(
|
|
126791
|
+
dg.command("edit").description(
|
|
126792
|
+
"\u53EA\u6539\u7CFB\u5217\u540D\u3001\u9884\u7B97\u3001\u51FA\u4EF7\u6216\u72B6\u6001\u3002\u9884\u7B97\u586B\u5143\uFF0C\u4E14\u5FC5\u987B\u5E26 --budget-id\u3002\n \u542F\u7528\uFF1A--status Enabled --confirm-enable --i-confirm\u3002\u4E0D\u8981\u5E26 automatic\u3002"
|
|
126793
|
+
).requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").option("--patch-file <path>", "\u53EA\u542B\u8981\u6539\u7684\u5B57\u6BB5\uFF1B\u91D1\u989D\u586B\u5143").option("--name <name>", "\u7CFB\u5217\u540D").option("--status <status>", "Enabled | Paused | Removed").option("--budget <amount>", "\u65E5\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143").option("--budget-id <id>", "\u8FD9\u6761\u7CFB\u5217\u81EA\u5DF1\u7684\u9884\u7B97 ID").option("--bidding <strategy>", "TARGET_CPA | TARGET_ROAS | \u2026").option("--target-cpa <amount>", "\u76EE\u6807 CPA\uFF0C\u5143\u3002\u5B57\u6BB5\u540D\u4ECD\u662F targetCpa_BidingAmount").option("--target-roas <ratio>", "\u76EE\u6807 ROAS \u6BD4\u7387\uFF0C2.5 = 250%").option("--confirm-large-change", "\u9884\u7B97\u6216\u51FA\u4EF7\u53D8\u52A8\u8D85\u8FC7 15% \u65F6\u5FC5\u5E26").option("--confirm-enable", "\u628A\u7CFB\u5217\u6539\u6210 Enabled \u65F6\u5FC5\u5E26").option("--i-confirm", "\u7528\u6237\u5DF2\u660E\u786E\u540C\u610F\u542F\u7528\u6216\u79FB\u9664")
|
|
126794
|
+
).action(runDemandGenEdit);
|
|
126795
|
+
withCommon(
|
|
126796
|
+
dg.command("content").description("\u6539\u5E7F\u544A\u6587\u6848\u548C\u7D20\u6750\u3002\u4E0D\u4F20 --ad-id \u65F6\u6539\u6574\u7EC4\uFF0C\u5E76\u53EF\u4EE5\u6539\u5730\u57DF\u8BED\u8A00\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").option("--ad-id <id>", "\u53EA\u6539\u8FD9\u4E00\u6761\u5E7F\u544A\uFF1B\u4E0D\u8981\u5728 JSON \u91CC\u5E26\u5730\u57DF\u8BED\u8A00").requiredOption("--config-file <path>", "\u8981\u6539\u7684\u5B57\u6BB5\u3002\u7A7A\u5B57\u6BB5\u4FDD\u6301\u4E0D\u52A8")
|
|
126797
|
+
).action(runDemandGenContent);
|
|
126798
|
+
withCommon(
|
|
126799
|
+
dg.command("channels").description("\u6539\u7248\u4F4D\u3002--strategy \u4E0E --selected \u4E8C\u9009\u4E00\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").option("--strategy <name>", "ALL_OWNED_AND_OPERATED_CHANNELS \u6216 ALL_CHANNELS").option("--selected <list>", "\u9017\u53F7\u5206\u9694\uFF1AyoutubeInStream,discover,gmail,maps,display,youtubeInFeed,youtubeShorts")
|
|
126800
|
+
).action(runDemandGenChannels);
|
|
126801
|
+
withCommon(
|
|
126802
|
+
dg.command("adgroup-status").description("\u505C\u6216\u5F00\u5E7F\u544A\u7EC4\u3002\u7CFB\u5217\u72B6\u6001\u4E0D\u53D8\u3002\u53EA\u63A5\u53D7 Enabled \u6216 Paused\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--status <status>", "Enabled | Paused")
|
|
126803
|
+
).action(runDemandGenAdGroupStatus);
|
|
126804
|
+
withCommon(
|
|
126805
|
+
dg.command("ad-status").description("\u505C\u6216\u5F00\u4E00\u6761\u5E7F\u544A\u3002\u7CFB\u5217\u72B6\u6001\u4E0D\u53D8\u3002\u53EA\u63A5\u53D7 Enabled \u6216 Paused\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--ad-id <id>", "\u5E7F\u544A ID").requiredOption("--status <status>", "Enabled | Paused")
|
|
126806
|
+
).action(runDemandGenAdStatus);
|
|
126807
|
+
withCommon(
|
|
126808
|
+
dg.command("adgroup-rename").description("\u53EA\u6539\u5E7F\u544A\u7EC4\u540D\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--name <name>", "1\uFF5E255 \u5B57\u7B26")
|
|
126809
|
+
).action(runDemandGenAdGroupRename);
|
|
126810
|
+
withCommon(
|
|
126811
|
+
dg.command("audience").description("\u628A\u5E7F\u544A\u7EC4\u7684\u552F\u4E00\u4EBA\u7FA4\u6362\u6210\u8FD9\u4E2A Audience\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--resource-name <name>", "customers/{id}/audiences/{audienceId}")
|
|
126812
|
+
).action(runDemandGenAudience);
|
|
126813
|
+
withCommon(
|
|
126814
|
+
dg.command("audience-keywords").description("\u6574\u4EFD\u66FF\u6362\u641C\u7D22\u81EA\u5B9A\u4E49\u53D7\u4F17\u7684\u8BCD\uFF0C1\uFF5E10 \u4E2A\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--keywords <list>", "\u9017\u53F7\u5206\u9694")
|
|
126815
|
+
).action(runDemandGenAudienceKeywords);
|
|
126816
|
+
withCommon(
|
|
126817
|
+
dg.command("add-adgroup").description("\u518D\u52A0\u4E00\u4E2A\u5E7F\u544A\u7EC4\uFF1A\u4E00\u6761\u89C6\u9891\u5E7F\u544A + \u4E00\u6761\u56FE\u7247\u5E7F\u544A\u3002\u4E0D\u6539\u7CFB\u5217\u9884\u7B97\u548C\u51FA\u4EF7\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--config-file <path>", "\u5E7F\u544A\u7EC4 JSON\uFF0CadGroupName \u5FC5\u586B")
|
|
126818
|
+
).action(runDemandGenAddAdGroup);
|
|
126819
|
+
withCommon(
|
|
126820
|
+
dg.command("add-ad").description("\u518D\u52A0\u5E7F\u544A\u3002\u6709\u89C6\u9891\u52A0\u89C6\u9891\uFF0C\u6709\u56FE\u7247\u52A0\u56FE\u7247\uFF0C\u53EF\u4EE5\u53EA\u52A0\u4E00\u79CD\u3002Logo \u5FC5\u586B\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--config-file <path>", "\u5E7F\u544A JSON")
|
|
126821
|
+
).action(runDemandGenAddAd);
|
|
126822
|
+
withCommon(
|
|
126823
|
+
dg.command("delete-ad").description("\u5220\u9664\u4E00\u6761\u5E7F\u544A\u3002\u7EC4\u5185\u81F3\u5C11\u7559\u4E00\u6761\uFF0C\u5426\u5219 400\u3002\u987B --i-confirm\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--ad-id <id>", "\u5E7F\u544A ID").option("--i-confirm", "\u7528\u6237\u5DF2\u660E\u786E\u540C\u610F\u5220\u9664")
|
|
126824
|
+
).action(runDemandGenDeleteAd);
|
|
126825
|
+
withCommon(
|
|
126826
|
+
dg.command("delete-adgroup").description("\u5220\u9664\u4E00\u4E2A\u5E7F\u544A\u7EC4\u3002\u7CFB\u5217\u81F3\u5C11\u7559\u4E00\u4E2A\u7EC4\uFF0C\u5426\u5219 400\u3002\u987B --i-confirm\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID").option("--i-confirm", "\u7528\u6237\u5DF2\u660E\u786E\u540C\u610F\u5220\u9664")
|
|
126827
|
+
).action(runDemandGenDeleteAdGroup);
|
|
126828
|
+
withCommon(
|
|
126829
|
+
dg.command("report").description("\u6309\u7248\u4F4D\u3001\u5929\u3001\u5E7F\u544A\u770B\u6570\u3002spend \u662F\u8D26\u6237\u5E01\u79CD\u6D6E\u70B9\u3002\u6682\u505C\u7CFB\u5217\u5168 0 \u4ECD\u662F\u6210\u529F\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").option("--start-date <date>", "\u9ED8\u8BA4 7 \u5929\u524D\uFF0CYYYY-MM-DD").option("--end-date <date>", "\u9ED8\u8BA4\u4ECA\u5929")
|
|
126830
|
+
).action(runDemandGenReport);
|
|
126831
|
+
withCommon(
|
|
126832
|
+
dg.command("conversions-validate").description("\u79BB\u7EBF\u8F6C\u5316\u53EA\u6821\u9A8C\uFF0C\u4E0D\u5199\u5165\u3002leadBiddable \u5FC5\u987B true\uFF0CpageViewBiddable \u5FC5\u987B false\u3002").requiredOption("-a, --account <id>", "Google \u5A92\u4F53\u5BA2\u6237 ID").requiredOption("--config-file <path>", "\u8F6C\u5316 JSON\u3002conversionValue \u662F\u8D26\u6237\u5E01\u79CD\uFF0C\u4E0D\u8981 \xD7100")
|
|
126833
|
+
).action(runDemandGenConversionsValidate);
|
|
126834
|
+
}
|
|
126835
|
+
|
|
125845
126836
|
// src/commands/ad/_register.ts
|
|
125846
126837
|
function parseOptionalMatchType(raw) {
|
|
125847
126838
|
if (!raw?.trim()) return void 0;
|
|
@@ -127170,6 +128161,7 @@ function register21(program2) {
|
|
|
127170
128161
|
});
|
|
127171
128162
|
}
|
|
127172
128163
|
);
|
|
128164
|
+
registerDemandGen(adCmd);
|
|
127173
128165
|
}
|
|
127174
128166
|
|
|
127175
128167
|
// src/commands/meta-ad/account.ts
|
|
@@ -127404,9 +128396,9 @@ function metaAdContext(token) {
|
|
|
127404
128396
|
return { config, facebookApiUrl: requireFacebookApi(config) };
|
|
127405
128397
|
}
|
|
127406
128398
|
function adsUrl(facebookApiUrl, account, path56) {
|
|
127407
|
-
const
|
|
128399
|
+
const base2 = metaAdsBaseUrl(facebookApiUrl, account);
|
|
127408
128400
|
const p = path56.startsWith("/") ? path56 : `/${path56}`;
|
|
127409
|
-
return `${
|
|
128401
|
+
return `${base2}${p}`;
|
|
127410
128402
|
}
|
|
127411
128403
|
async function metaAdGet(url, config, verbose, action) {
|
|
127412
128404
|
try {
|
|
@@ -130198,13 +131190,13 @@ function appendGeoTargetConstantIdsQuery(baseUrl, geoTargetConstantIds) {
|
|
|
130198
131190
|
return `${baseUrl}?${qs}`;
|
|
130199
131191
|
}
|
|
130200
131192
|
function buildKeywordIdeaGoogleUrl(googleApiUrl, geoTargetConstantIds) {
|
|
130201
|
-
const
|
|
130202
|
-
return appendGeoTargetConstantIdsQuery(
|
|
131193
|
+
const base2 = `${googleApiUrl.replace(/\/$/, "")}/keywordidea/google`;
|
|
131194
|
+
return appendGeoTargetConstantIdsQuery(base2, geoTargetConstantIds);
|
|
130203
131195
|
}
|
|
130204
131196
|
function buildKeywordRecommendationGoogleUrl(googleApiUrl, mediaCustomerId, geoTargetConstantIds) {
|
|
130205
131197
|
const id = encodeURIComponent(mediaCustomerId.trim());
|
|
130206
|
-
const
|
|
130207
|
-
return appendGeoTargetConstantIdsQuery(
|
|
131198
|
+
const base2 = `${googleApiUrl.replace(/\/$/, "")}/keywordrecommendation/recommend/${id}/google`;
|
|
131199
|
+
return appendGeoTargetConstantIdsQuery(base2, geoTargetConstantIds);
|
|
130208
131200
|
}
|
|
130209
131201
|
function geoTargetConstantsListPath(googleApiUrl) {
|
|
130210
131202
|
return `${googleApiUrl.replace(/\/$/, "")}/geotargetmanagement/GeoTargetConstans`;
|
|
@@ -131248,7 +132240,7 @@ function mccResponseHasSuccess(res, managerId) {
|
|
|
131248
132240
|
}
|
|
131249
132241
|
async function runAccountMccBind(opts) {
|
|
131250
132242
|
const config = loadConfig(opts.token);
|
|
131251
|
-
const
|
|
132243
|
+
const base2 = googleGatewayBase(config);
|
|
131252
132244
|
const managerIds = parseMccManagerIds(opts.mcc);
|
|
131253
132245
|
if (managerIds.length === 0) {
|
|
131254
132246
|
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
@@ -131261,7 +132253,7 @@ async function runAccountMccBind(opts) {
|
|
|
131261
132253
|
const body = { agentTypes: [...MCC_AGENT_TYPES], managerIds };
|
|
131262
132254
|
const results = [];
|
|
131263
132255
|
for (const customerId of opts.customers) {
|
|
131264
|
-
const url = `${
|
|
132256
|
+
const url = `${base2}/command/media-account/${customerId}/CustomerLinks`;
|
|
131265
132257
|
try {
|
|
131266
132258
|
const response = await apiFetch2(
|
|
131267
132259
|
url,
|
|
@@ -131306,7 +132298,7 @@ async function runAccountMccBind(opts) {
|
|
|
131306
132298
|
async function runAccountMccUnbind(opts) {
|
|
131307
132299
|
ensureIConfirm({ iConfirm: opts.iConfirm, actionLabel: "account mcc-unbind\uFF08Google MCC \u89E3\u7ED1\uFF09" });
|
|
131308
132300
|
const config = loadConfig(opts.token);
|
|
131309
|
-
const
|
|
132301
|
+
const base2 = googleGatewayBase(config);
|
|
131310
132302
|
const managerIds = parseMccManagerIds(opts.mcc);
|
|
131311
132303
|
if (managerIds.length === 0) {
|
|
131312
132304
|
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
@@ -131318,7 +132310,7 @@ async function runAccountMccUnbind(opts) {
|
|
|
131318
132310
|
}
|
|
131319
132311
|
const results = [];
|
|
131320
132312
|
for (const customerId of opts.customers) {
|
|
131321
|
-
const url = `${
|
|
132313
|
+
const url = `${base2}/command/media-account/${customerId}/CustomerUnlinks`;
|
|
131322
132314
|
try {
|
|
131323
132315
|
const response = await apiFetch2(
|
|
131324
132316
|
url,
|
|
@@ -131396,7 +132388,7 @@ function parseCustomerAccessPermissionBody(text3) {
|
|
|
131396
132388
|
return trimmed;
|
|
131397
132389
|
}
|
|
131398
132390
|
function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody) {
|
|
131399
|
-
const
|
|
132391
|
+
const base2 = {
|
|
131400
132392
|
mediaCustomerId,
|
|
131401
132393
|
httpStatus,
|
|
131402
132394
|
rawBody
|
|
@@ -131404,7 +132396,7 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131404
132396
|
if (httpStatus === 200) {
|
|
131405
132397
|
if (rawBody === true) {
|
|
131406
132398
|
return {
|
|
131407
|
-
...
|
|
132399
|
+
...base2,
|
|
131408
132400
|
status: "accessible",
|
|
131409
132401
|
accessible: true,
|
|
131410
132402
|
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u53EF\u8BBF\u95EE\u8BE5 Google \u5E7F\u544A\u8D26\u6237\u3002"
|
|
@@ -131412,7 +132404,7 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131412
132404
|
}
|
|
131413
132405
|
if (rawBody === false) {
|
|
131414
132406
|
return {
|
|
131415
|
-
...
|
|
132407
|
+
...base2,
|
|
131416
132408
|
status: "reauth_required",
|
|
131417
132409
|
accessible: false,
|
|
131418
132410
|
message: "\u8D26\u6237\u5DF2\u7ED1\u5B9A\u4F46 Google OAuth \u4E0D\u53EF\u7528\uFF0C\u9700\u91CD\u65B0\u6388\u6743\u3002",
|
|
@@ -131420,7 +132412,7 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131420
132412
|
};
|
|
131421
132413
|
}
|
|
131422
132414
|
return {
|
|
131423
|
-
...
|
|
132415
|
+
...base2,
|
|
131424
132416
|
status: "unknown",
|
|
131425
132417
|
accessible: false,
|
|
131426
132418
|
message: `HTTP 200 \u4F46\u54CD\u5E94\u4F53\u975E\u5E38\u89C4\uFF08${String(rawBody)}\uFF09\uFF0C\u8BF7\u7528 --verbose \u67E5\u770B\u539F\u59CB\u54CD\u5E94\u3002`
|
|
@@ -131430,7 +132422,7 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131430
132422
|
const text3 = typeof rawBody === "string" ? rawBody : rawBody == null ? "" : String(rawBody);
|
|
131431
132423
|
if (text3.includes("token\u4E0D\u80FD\u4E3A\u7A7A") || text3.toLowerCase().includes("token") && text3.includes("\u7A7A")) {
|
|
131432
132424
|
return {
|
|
131433
|
-
...
|
|
132425
|
+
...base2,
|
|
131434
132426
|
status: "google_not_bound",
|
|
131435
132427
|
accessible: false,
|
|
131436
132428
|
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u672A\u7ED1\u5B9A Google \u5A92\u4F53\u6388\u6743\u3002",
|
|
@@ -131438,7 +132430,7 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131438
132430
|
};
|
|
131439
132431
|
}
|
|
131440
132432
|
return {
|
|
131441
|
-
...
|
|
132433
|
+
...base2,
|
|
131442
132434
|
status: "no_permission",
|
|
131443
132435
|
accessible: false,
|
|
131444
132436
|
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u5BF9\u8BE5 Google \u5E7F\u544A\u8D26\u6237\u65E0\u8BBF\u95EE\u6743\u9650\uFF1B\u901A\u5E38\u8868\u793A\u8BE5\u8D26\u6237\u4E0D\u5728\u672C\u8D26\u53F7\u4E0B\uFF08\u6216\u672A\u5206\u4EAB\u7ED9\u4F60\uFF09\u3002",
|
|
@@ -131447,7 +132439,7 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131447
132439
|
}
|
|
131448
132440
|
if (httpStatus === 401) {
|
|
131449
132441
|
return {
|
|
131450
|
-
...
|
|
132442
|
+
...base2,
|
|
131451
132443
|
status: "siluzan_token_invalid",
|
|
131452
132444
|
accessible: false,
|
|
131453
132445
|
message: "\u4E1D\u8DEF\u8D5E\u767B\u5F55\u51ED\u636E\u65E0\u6548\u6216\u5DF2\u8FC7\u671F\u3002",
|
|
@@ -131455,15 +132447,15 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
|
|
|
131455
132447
|
};
|
|
131456
132448
|
}
|
|
131457
132449
|
return {
|
|
131458
|
-
...
|
|
132450
|
+
...base2,
|
|
131459
132451
|
status: "unknown",
|
|
131460
132452
|
accessible: false,
|
|
131461
132453
|
message: `\u672A\u9884\u671F\u7684 HTTP ${httpStatus}\uFF1B\u8BF7\u7528 --verbose \u67E5\u770B\u539F\u59CB\u54CD\u5E94\u3002`
|
|
131462
132454
|
};
|
|
131463
132455
|
}
|
|
131464
132456
|
function buildCustomerAccessPermissionUrl(googleApiUrl, mediaCustomerId) {
|
|
131465
|
-
const
|
|
131466
|
-
return `${
|
|
132457
|
+
const base2 = googleApiUrl.replace(/\/$/, "");
|
|
132458
|
+
return `${base2}/query/media-account/${mediaCustomerId}/customer-access-permission`;
|
|
131467
132459
|
}
|
|
131468
132460
|
async function fetchGoogleCustomerAccessPermission(config, mediaCustomerId, verbose) {
|
|
131469
132461
|
await ensureDataPermission(config);
|
|
@@ -132151,8 +133143,8 @@ async function runAccountBcBind(opts) {
|
|
|
132151
133143
|
process.exit(1);
|
|
132152
133144
|
}
|
|
132153
133145
|
const config = loadConfig(opts.token);
|
|
132154
|
-
const
|
|
132155
|
-
const url = `${
|
|
133146
|
+
const base2 = tiktokGatewayBase(config);
|
|
133147
|
+
const url = `${base2}/bcmanagement/AddPartnerList`;
|
|
132156
133148
|
const body = { bcIds: opts.bcIds, mediacustomerIds: opts.customers };
|
|
132157
133149
|
let result;
|
|
132158
133150
|
try {
|
|
@@ -132202,8 +133194,8 @@ async function runAccountBcUnbind(opts) {
|
|
|
132202
133194
|
process.exit(1);
|
|
132203
133195
|
}
|
|
132204
133196
|
const config = loadConfig(opts.token);
|
|
132205
|
-
const
|
|
132206
|
-
const url = `${
|
|
133197
|
+
const base2 = tiktokGatewayBase(config);
|
|
133198
|
+
const url = `${base2}/bcmanagement/DeletePartnerAssetList`;
|
|
132207
133199
|
const body = [{ bcId: opts.bcId, mediacustomerIds: opts.customers }];
|
|
132208
133200
|
let result;
|
|
132209
133201
|
try {
|
|
@@ -132249,13 +133241,13 @@ function isLikelyPendingInvitationStatus(status) {
|
|
|
132249
133241
|
return status.trim().toUpperCase().includes("PENDING");
|
|
132250
133242
|
}
|
|
132251
133243
|
async function fetchCustomerUserAccessInvitationList(config, customerId, verbose, agentType) {
|
|
132252
|
-
const
|
|
133244
|
+
const base2 = googleGatewayBase(config);
|
|
132253
133245
|
const params = new URLSearchParams({ customerId });
|
|
132254
133246
|
const at = agentType?.trim();
|
|
132255
133247
|
if (at) {
|
|
132256
133248
|
params.set("AgentType", at);
|
|
132257
133249
|
}
|
|
132258
|
-
const url = `${
|
|
133250
|
+
const url = `${base2}/query/media-account/GetCustomerUserAccessInvitation?${params}`;
|
|
132259
133251
|
const data = await apiFetch2(url, config, {}, verbose ?? false);
|
|
132260
133252
|
return Array.isArray(data) ? data : [];
|
|
132261
133253
|
}
|
|
@@ -132359,8 +133351,8 @@ async function runAccountEmailAuthList(opts) {
|
|
|
132359
133351
|
}
|
|
132360
133352
|
async function runAccountEmailAuth(opts) {
|
|
132361
133353
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
132362
|
-
const
|
|
132363
|
-
const url = `${
|
|
133354
|
+
const base2 = googleGatewayBase(config);
|
|
133355
|
+
const url = `${base2}/command/media-account/MutateCustomerUserAccessInvitation`;
|
|
132364
133356
|
const body = {
|
|
132365
133357
|
CustomerId: opts.customerId,
|
|
132366
133358
|
EmailAddress: opts.email,
|
|
@@ -132445,9 +133437,9 @@ async function runAccountEmailAuth(opts) {
|
|
|
132445
133437
|
}
|
|
132446
133438
|
async function runAccountEmailDeauth(opts) {
|
|
132447
133439
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
132448
|
-
const
|
|
133440
|
+
const base2 = googleGatewayBase(config);
|
|
132449
133441
|
const endpoint = opts.accepted !== false ? "RemoveCustomerUserAccess" : "RemoveCustomerUserAccessInvitation";
|
|
132450
|
-
const url = `${
|
|
133442
|
+
const url = `${base2}/command/media-account/${endpoint}`;
|
|
132451
133443
|
const resourceName = opts.resourceName?.trim() || `customers/${opts.customerId}/customerUserAccessInvitations/${opts.invitationId}`;
|
|
132452
133444
|
const body = {
|
|
132453
133445
|
CustomerId: opts.customerId,
|
|
@@ -133760,7 +134752,7 @@ async function runOpenAccountGoogle(opts) {
|
|
|
133760
134752
|
const promotionLinkNorm = normalizePromotionLinkForGoogle(opts.promotionLink);
|
|
133761
134753
|
const body = {
|
|
133762
134754
|
accountInfo: Array.from({ length: counts }, (_, i) => {
|
|
133763
|
-
const
|
|
134755
|
+
const base2 = buildGoogleAccountInfoItem({
|
|
133764
134756
|
accountName: opts.accountName,
|
|
133765
134757
|
currency: opts.currency,
|
|
133766
134758
|
timezone,
|
|
@@ -133773,9 +134765,9 @@ async function runOpenAccountGoogle(opts) {
|
|
|
133773
134765
|
promotionType: opts.promotionType,
|
|
133774
134766
|
autoAllotMailbox: opts.autoAllotMailbox
|
|
133775
134767
|
});
|
|
133776
|
-
const adv =
|
|
134768
|
+
const adv = base2.advertiser_info;
|
|
133777
134769
|
adv.name = counts > 1 ? `${opts.accountName}-${i + 1}` : opts.accountName;
|
|
133778
|
-
return
|
|
134770
|
+
return base2;
|
|
133779
134771
|
}),
|
|
133780
134772
|
MediaAccountGroupId: magKey,
|
|
133781
134773
|
IsSaltAdd: false,
|
|
@@ -134225,8 +135217,8 @@ function parseMetaOpenAccountLinkResponse(raw) {
|
|
|
134225
135217
|
throw new Error("\u63A5\u53E3\u672A\u8FD4\u56DE\u6709\u6548\u7684 Meta/Facebook \u5F00\u6237\u94FE\u63A5\uFF08\u987B\u4E3A https URL\uFF09");
|
|
134226
135218
|
}
|
|
134227
135219
|
function metaOpenAccountLinkApiUrl(apiBaseUrl) {
|
|
134228
|
-
const
|
|
134229
|
-
return `${
|
|
135220
|
+
const base2 = apiBaseUrl.replace(/\/$/, "");
|
|
135221
|
+
return `${base2}${META_OPEN_ACCOUNT_LINK_PATH}`;
|
|
134230
135222
|
}
|
|
134231
135223
|
async function fetchMetaOpenAccountLink(config, verbose) {
|
|
134232
135224
|
const url = metaOpenAccountLinkApiUrl(config.apiBaseUrl);
|
|
@@ -134888,8 +135880,8 @@ init_auth();
|
|
|
134888
135880
|
function resolveAgentApiBase(tsoApiBaseUrl) {
|
|
134889
135881
|
const fromEnv = process.env.SILUZAN_AGENT_BASE?.trim();
|
|
134890
135882
|
if (fromEnv) return requireWhitelistedBase("SILUZAN_AGENT_BASE", fromEnv);
|
|
134891
|
-
const
|
|
134892
|
-
if (
|
|
135883
|
+
const base2 = tsoApiBaseUrl ?? "";
|
|
135884
|
+
if (base2.includes("-ci") || base2.includes("ci.")) {
|
|
134893
135885
|
return "https://agent-ci.mysiluzan.com";
|
|
134894
135886
|
}
|
|
134895
135887
|
return "https://agent.mysiluzan.com";
|
|
@@ -134918,8 +135910,8 @@ function pickDownloadedHtml(settled) {
|
|
|
134918
135910
|
return { ok: true, value };
|
|
134919
135911
|
}
|
|
134920
135912
|
async function fetchWebsiteHtml(config, websiteUrl, verbose) {
|
|
134921
|
-
const
|
|
134922
|
-
const url = `${
|
|
135913
|
+
const base2 = resolveAgentApiBase(config.apiBaseUrl);
|
|
135914
|
+
const url = `${base2}/download-assets`;
|
|
134923
135915
|
const bodyStr = JSON.stringify({ urls: [websiteUrl] });
|
|
134924
135916
|
const headers = {
|
|
134925
135917
|
"Content-Type": "application/json; charset=utf-8",
|
|
@@ -135213,7 +136205,7 @@ function readJsonFile(filePath) {
|
|
|
135213
136205
|
}
|
|
135214
136206
|
});
|
|
135215
136207
|
}
|
|
135216
|
-
function
|
|
136208
|
+
function asRecord6(value) {
|
|
135217
136209
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
135218
136210
|
return value;
|
|
135219
136211
|
}
|
|
@@ -135276,10 +136268,10 @@ function injectReportData(html, payload, lang) {
|
|
|
135276
136268
|
async function runWebsiteDiagnosisRender(opts) {
|
|
135277
136269
|
const dataPath = path21.resolve(opts.dataFile);
|
|
135278
136270
|
const dataRaw = await readJsonFile(dataPath);
|
|
135279
|
-
let data =
|
|
136271
|
+
let data = asRecord6(dataRaw);
|
|
135280
136272
|
if (opts.collectFile) {
|
|
135281
136273
|
const collectRaw = await readJsonFile(path21.resolve(opts.collectFile));
|
|
135282
|
-
data = mergeCollectLighthouse(data,
|
|
136274
|
+
data = mergeCollectLighthouse(data, asRecord6(collectRaw));
|
|
135283
136275
|
}
|
|
135284
136276
|
const planError = priorityPlanError(data);
|
|
135285
136277
|
if (planError) {
|
|
@@ -135951,7 +136943,7 @@ function formatMarketReportContentErrors(result) {
|
|
|
135951
136943
|
|
|
135952
136944
|
// src/commands/market-analysis/render-report.ts
|
|
135953
136945
|
init_lang();
|
|
135954
|
-
function
|
|
136946
|
+
function asRecord7(value) {
|
|
135955
136947
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
135956
136948
|
return value;
|
|
135957
136949
|
}
|
|
@@ -135981,7 +136973,7 @@ async function runMarketAnalysisRender(opts) {
|
|
|
135981
136973
|
}
|
|
135982
136974
|
let html;
|
|
135983
136975
|
try {
|
|
135984
|
-
html = resolveMarketAnalysisHtml(
|
|
136976
|
+
html = resolveMarketAnalysisHtml(asRecord7(dataRaw));
|
|
135985
136977
|
} catch (err) {
|
|
135986
136978
|
const message = err instanceof Error ? err.message : String(err);
|
|
135987
136979
|
console.error(`
|
|
@@ -135993,7 +136985,7 @@ async function runMarketAnalysisRender(opts) {
|
|
|
135993
136985
|
console.error("\n\u274C \u62A5\u544A HTML \u5185\u5BB9\u4E3A\u7A7A\n");
|
|
135994
136986
|
process.exit(1);
|
|
135995
136987
|
}
|
|
135996
|
-
const reportData =
|
|
136988
|
+
const reportData = asRecord7(dataRaw);
|
|
135997
136989
|
const lang = readReportLangOrExit({ flag: opts.lang, meta: reportData.meta });
|
|
135998
136990
|
try {
|
|
135999
136991
|
assertHtmlLangMatches(html, lang);
|
|
@@ -136276,7 +137268,7 @@ function sectionRecord(env) {
|
|
|
136276
137268
|
}
|
|
136277
137269
|
|
|
136278
137270
|
// src/commands/google-ads-diagnosis/gold-text-ads.ts
|
|
136279
|
-
function
|
|
137271
|
+
function asRecord8(value) {
|
|
136280
137272
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
136281
137273
|
return value;
|
|
136282
137274
|
}
|
|
@@ -136330,7 +137322,7 @@ function noteUnqualified(gold, suggestions, flag, itemName, present, sentence) {
|
|
|
136330
137322
|
}
|
|
136331
137323
|
function reconcileGoldAccount(gold, evidence = {}) {
|
|
136332
137324
|
const next = { ...gold };
|
|
136333
|
-
const suggestions = { ...
|
|
137325
|
+
const suggestions = { ...asRecord8(gold.goldItemSuggestions) ?? {} };
|
|
136334
137326
|
const notes = {};
|
|
136335
137327
|
const conversions = evidence.conversionActions ?? [];
|
|
136336
137328
|
const extensions = evidence.extensions ?? [];
|
|
@@ -136437,8 +137429,8 @@ function loadEvidence(snapDir, accountId, start, end) {
|
|
|
136437
137429
|
};
|
|
136438
137430
|
}
|
|
136439
137431
|
function reconcileGoldAccountInReport(data, snapDir) {
|
|
136440
|
-
const gold =
|
|
136441
|
-
const account =
|
|
137432
|
+
const gold = asRecord8(data.goldAccount) ?? {};
|
|
137433
|
+
const account = asRecord8(data.accountInfo);
|
|
136442
137434
|
const accountId = String(account?.companyId ?? "").replace(/\D/g, "");
|
|
136443
137435
|
const { start, end } = parsePeriod(String(account?.period ?? ""));
|
|
136444
137436
|
const evidence = snapDir && accountId ? loadEvidence(snapDir, accountId, start, end) : {};
|
|
@@ -137199,14 +138191,14 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
137199
138191
|
}
|
|
137200
138192
|
|
|
137201
138193
|
// src/commands/google-ads-diagnosis/collect-digest.ts
|
|
137202
|
-
function
|
|
138194
|
+
function asRecord9(value) {
|
|
137203
138195
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137204
138196
|
return value;
|
|
137205
138197
|
}
|
|
137206
138198
|
return null;
|
|
137207
138199
|
}
|
|
137208
138200
|
function fieldsOfFirstRow(rows) {
|
|
137209
|
-
const first =
|
|
138201
|
+
const first = asRecord9(rows[0]);
|
|
137210
138202
|
return first ? Object.keys(first) : [];
|
|
137211
138203
|
}
|
|
137212
138204
|
function describeSectionShapes(reportData) {
|
|
@@ -137216,7 +138208,7 @@ function describeSectionShapes(reportData) {
|
|
|
137216
138208
|
if (Array.isArray(value)) {
|
|
137217
138209
|
return { section, itemCount: value.length, fields: fieldsOfFirstRow(value), needsNarrative };
|
|
137218
138210
|
}
|
|
137219
|
-
const record2 =
|
|
138211
|
+
const record2 = asRecord9(value);
|
|
137220
138212
|
if (!record2) {
|
|
137221
138213
|
return { section, itemCount: null, fields: [], needsNarrative };
|
|
137222
138214
|
}
|
|
@@ -137282,21 +138274,21 @@ function buildCollectDigest(reportData, meta) {
|
|
|
137282
138274
|
}
|
|
137283
138275
|
|
|
137284
138276
|
// src/commands/google-ads-diagnosis/collect-integrity.ts
|
|
137285
|
-
function
|
|
138277
|
+
function asRecord10(value) {
|
|
137286
138278
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137287
138279
|
return value;
|
|
137288
138280
|
}
|
|
137289
138281
|
return null;
|
|
137290
138282
|
}
|
|
137291
138283
|
function itemCount(block) {
|
|
137292
|
-
const items3 =
|
|
138284
|
+
const items3 = asRecord10(block)?.items;
|
|
137293
138285
|
return Array.isArray(items3) ? items3.length : 0;
|
|
137294
138286
|
}
|
|
137295
138287
|
function getCollectIntegrityWarnings(reportData) {
|
|
137296
138288
|
const warnings = [];
|
|
137297
138289
|
const kwItems = itemCount(reportData.keywords);
|
|
137298
138290
|
const fullKwItems = itemCount(reportData.fullKeywords);
|
|
137299
|
-
const keywordCount = Number(
|
|
138291
|
+
const keywordCount = Number(asRecord10(reportData.structure)?.keywordCount ?? 0);
|
|
137300
138292
|
if (kwItems === 0 && fullKwItems > 0) {
|
|
137301
138293
|
warnings.push(
|
|
137302
138294
|
`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`
|
|
@@ -137307,7 +138299,7 @@ function getCollectIntegrityWarnings(reportData) {
|
|
|
137307
138299
|
);
|
|
137308
138300
|
}
|
|
137309
138301
|
const campaignItems = itemCount(reportData.campaigns);
|
|
137310
|
-
const campaignCount = Number(
|
|
138302
|
+
const campaignCount = Number(asRecord10(reportData.structure)?.campaignCount ?? 0);
|
|
137311
138303
|
if (campaignItems === 0 && campaignCount > 0) {
|
|
137312
138304
|
warnings.push(
|
|
137313
138305
|
`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`
|
|
@@ -137365,7 +138357,7 @@ function invokeGoogleAnalysisCli(opts) {
|
|
|
137365
138357
|
|
|
137366
138358
|
// src/commands/google-ads-diagnosis/write-draft.ts
|
|
137367
138359
|
import fs21 from "fs/promises";
|
|
137368
|
-
function
|
|
138360
|
+
function asRecord11(value) {
|
|
137369
138361
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137370
138362
|
return value;
|
|
137371
138363
|
}
|
|
@@ -137374,7 +138366,7 @@ function asRecord10(value) {
|
|
|
137374
138366
|
function valueAtPath(data, keys) {
|
|
137375
138367
|
let cursor = data;
|
|
137376
138368
|
for (const key of keys) {
|
|
137377
|
-
const record2 =
|
|
138369
|
+
const record2 = asRecord11(cursor);
|
|
137378
138370
|
if (!record2) return void 0;
|
|
137379
138371
|
cursor = record2[key];
|
|
137380
138372
|
}
|
|
@@ -137393,7 +138385,7 @@ function hasAgentNarrative(data) {
|
|
|
137393
138385
|
async function writeDiagnosisDraft(opts) {
|
|
137394
138386
|
try {
|
|
137395
138387
|
const existing = JSON.parse(await fs21.readFile(opts.draftPath, "utf8"));
|
|
137396
|
-
const record2 =
|
|
138388
|
+
const record2 = asRecord11(existing);
|
|
137397
138389
|
if (record2 && hasAgentNarrative(record2)) return "skipped-existing-narrative";
|
|
137398
138390
|
} catch {
|
|
137399
138391
|
}
|
|
@@ -137583,7 +138575,7 @@ import fs23 from "fs/promises";
|
|
|
137583
138575
|
import path27 from "path";
|
|
137584
138576
|
|
|
137585
138577
|
// src/commands/google-ads-diagnosis/report-content.ts
|
|
137586
|
-
function
|
|
138578
|
+
function asRecord12(value) {
|
|
137587
138579
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137588
138580
|
return value;
|
|
137589
138581
|
}
|
|
@@ -137592,7 +138584,7 @@ function asRecord11(value) {
|
|
|
137592
138584
|
function getNestedValue(root, path56) {
|
|
137593
138585
|
let cur = root;
|
|
137594
138586
|
for (const key of path56) {
|
|
137595
|
-
const rec =
|
|
138587
|
+
const rec = asRecord12(cur);
|
|
137596
138588
|
if (!rec) return void 0;
|
|
137597
138589
|
cur = rec[key];
|
|
137598
138590
|
}
|
|
@@ -137609,7 +138601,7 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
137609
138601
|
}
|
|
137610
138602
|
const warnings = [];
|
|
137611
138603
|
for (let i = 0; i < items3.length; i++) {
|
|
137612
|
-
const row =
|
|
138604
|
+
const row = asRecord12(items3[i]);
|
|
137613
138605
|
const name2 = String(row?.name ?? row?.metric ?? i);
|
|
137614
138606
|
if (!hasNarrativeContent(row?.strategy)) {
|
|
137615
138607
|
warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08render \u4F1A\u6309\u672C\u671F\u6570\u503C\u4E0E\u6807\u51C6\u751F\u6210\uFF0C\u4E0D\u5E94\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -137618,13 +138610,13 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
137618
138610
|
return warnings;
|
|
137619
138611
|
}
|
|
137620
138612
|
function validateNewFeatureRecommendations(data) {
|
|
137621
|
-
const nf =
|
|
138613
|
+
const nf = asRecord12(data.newFeatures);
|
|
137622
138614
|
if (!nf) return [];
|
|
137623
138615
|
const items3 = nf.items;
|
|
137624
138616
|
if (!Array.isArray(items3)) return [];
|
|
137625
138617
|
const warnings = [];
|
|
137626
138618
|
for (let i = 0; i < items3.length; i++) {
|
|
137627
|
-
const row =
|
|
138619
|
+
const row = asRecord12(items3[i]);
|
|
137628
138620
|
const name2 = String(row?.strategyName ?? row?.feature ?? row?.strategy ?? i);
|
|
137629
138621
|
if (!hasNarrativeContent(row?.optimizerRecommendation)) {
|
|
137630
138622
|
warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08render \u4F1A\u6309\u542F\u7528\u72B6\u6001\u751F\u6210\uFF0C\u4E0D\u5E94\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -137633,14 +138625,14 @@ function validateNewFeatureRecommendations(data) {
|
|
|
137633
138625
|
return warnings;
|
|
137634
138626
|
}
|
|
137635
138627
|
function validateDiagnosisOverviewItems(data) {
|
|
137636
|
-
const overview =
|
|
138628
|
+
const overview = asRecord12(data.diagnosisOverview);
|
|
137637
138629
|
if (!overview) return [];
|
|
137638
138630
|
const warnings = [];
|
|
137639
138631
|
for (const key of ["advantages", "disadvantages"]) {
|
|
137640
138632
|
const items3 = overview[key];
|
|
137641
138633
|
if (!Array.isArray(items3)) continue;
|
|
137642
138634
|
items3.forEach((item, index) => {
|
|
137643
|
-
const row =
|
|
138635
|
+
const row = asRecord12(item);
|
|
137644
138636
|
const title = String(row?.title ?? "").trim();
|
|
137645
138637
|
const description = String(row?.description ?? "").trim();
|
|
137646
138638
|
if (!title || !description) {
|
|
@@ -137653,13 +138645,13 @@ function validateDiagnosisOverviewItems(data) {
|
|
|
137653
138645
|
return warnings;
|
|
137654
138646
|
}
|
|
137655
138647
|
function validateAdCreativeItemSuggestions(data) {
|
|
137656
|
-
const creative =
|
|
138648
|
+
const creative = asRecord12(data.adCreativeOptimization);
|
|
137657
138649
|
if (!creative) return [];
|
|
137658
138650
|
const items3 = creative.items;
|
|
137659
138651
|
if (!Array.isArray(items3) || items3.length === 0) return [];
|
|
137660
138652
|
const warnings = [];
|
|
137661
138653
|
for (let i = 0; i < items3.length; i++) {
|
|
137662
|
-
const row =
|
|
138654
|
+
const row = asRecord12(items3[i]);
|
|
137663
138655
|
const title = String(row?.adTitle ?? i);
|
|
137664
138656
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
137665
138657
|
warnings.push(`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08render \u4F1A\u6309\u6807\u9898/\u63CF\u8FF0\u6761\u6570\u751F\u6210\uFF0C\u4E0D\u5E94\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -137670,7 +138662,7 @@ function validateAdCreativeItemSuggestions(data) {
|
|
|
137670
138662
|
function validateGoogleAdsDiagnosisContent(data) {
|
|
137671
138663
|
const missingKeys = GOOGLE_ADS_DIAGNOSIS_TOP_LEVEL_KEYS.filter((key) => data[key] === void 0);
|
|
137672
138664
|
const warnings = [];
|
|
137673
|
-
const accountInfo =
|
|
138665
|
+
const accountInfo = asRecord12(data.accountInfo);
|
|
137674
138666
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
137675
138667
|
warnings.push("accountInfo \u7F3A\u5C11 companyName \u4E0E period\uFF0C\u9875\u7709\u53EF\u80FD\u4E3A\u7A7A");
|
|
137676
138668
|
}
|
|
@@ -137719,7 +138711,7 @@ var GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES = [
|
|
|
137719
138711
|
GOOGLE_ADS_DIAGNOSIS_BIDDING_STRATEGY_MODULE,
|
|
137720
138712
|
GOOGLE_ADS_DIAGNOSIS_NEW_FEATURES_MODULE
|
|
137721
138713
|
];
|
|
137722
|
-
function
|
|
138714
|
+
function asRecord13(value) {
|
|
137723
138715
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137724
138716
|
return value;
|
|
137725
138717
|
}
|
|
@@ -137730,7 +138722,7 @@ function isValidRateChange(value) {
|
|
|
137730
138722
|
return typeof value === "number" && Number.isFinite(value);
|
|
137731
138723
|
}
|
|
137732
138724
|
function isComparisonTableRowValid(row) {
|
|
137733
|
-
const rec =
|
|
138725
|
+
const rec = asRecord13(row);
|
|
137734
138726
|
if (!rec) return false;
|
|
137735
138727
|
if (!String(rec.title ?? "").trim()) return false;
|
|
137736
138728
|
if (!Number.isFinite(Number(rec.currentCost))) return false;
|
|
@@ -137742,12 +138734,12 @@ function isComparisonTableRowValid(row) {
|
|
|
137742
138734
|
return true;
|
|
137743
138735
|
}
|
|
137744
138736
|
function isNewFeatureRowValid(row) {
|
|
137745
|
-
const rec =
|
|
138737
|
+
const rec = asRecord13(row);
|
|
137746
138738
|
if (!rec) return false;
|
|
137747
138739
|
return Boolean(String(rec.strategyName ?? rec.feature ?? rec.strategy ?? "").trim());
|
|
137748
138740
|
}
|
|
137749
138741
|
function isBiddingStrategyRowValid(row) {
|
|
137750
|
-
const rec =
|
|
138742
|
+
const rec = asRecord13(row);
|
|
137751
138743
|
if (!rec) return false;
|
|
137752
138744
|
if (!String(rec.campaignName ?? "").trim()) return false;
|
|
137753
138745
|
if (!Number.isFinite(Number(rec.duration)) || Number(rec.duration) <= 0) return false;
|
|
@@ -137785,11 +138777,11 @@ function getFactItemsErrors(data) {
|
|
|
137785
138777
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
137786
138778
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
137787
138779
|
const hint = MODULE_ITEM_ERROR_HINT[mod];
|
|
137788
|
-
const block =
|
|
138780
|
+
const block = asRecord13(data[mod]);
|
|
137789
138781
|
const items3 = block?.items;
|
|
137790
138782
|
if (!Array.isArray(items3) || items3.length === 0) {
|
|
137791
138783
|
if (mod === "keywords") {
|
|
137792
|
-
const fullKw =
|
|
138784
|
+
const fullKw = asRecord13(data.fullKeywords)?.items;
|
|
137793
138785
|
if (Array.isArray(fullKw) && fullKw.length > 0) {
|
|
137794
138786
|
errors.push(
|
|
137795
138787
|
"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"
|
|
@@ -137814,15 +138806,15 @@ var LANDING_PAGE_SPEED_KEYS = [
|
|
|
137814
138806
|
"speedIndex"
|
|
137815
138807
|
];
|
|
137816
138808
|
function isLandingPageSpeedValid(value) {
|
|
137817
|
-
const rec =
|
|
138809
|
+
const rec = asRecord13(value);
|
|
137818
138810
|
if (!rec) return false;
|
|
137819
138811
|
return LANDING_PAGE_SPEED_KEYS.every(
|
|
137820
138812
|
(key) => typeof rec[key] === "number" && Number.isFinite(rec[key])
|
|
137821
138813
|
);
|
|
137822
138814
|
}
|
|
137823
138815
|
function restoreLandingPageSpeedFromCollect(finalData, collectReportData) {
|
|
137824
|
-
const finalBlock =
|
|
137825
|
-
const collectBlock =
|
|
138816
|
+
const finalBlock = asRecord13(finalData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
138817
|
+
const collectBlock = asRecord13(collectReportData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
137826
138818
|
if (!collectBlock) return { data: finalData, restored: false };
|
|
137827
138819
|
const needsDesktop = !isLandingPageSpeedValid(finalBlock?.desktop) && isLandingPageSpeedValid(collectBlock.desktop);
|
|
137828
138820
|
const needsMobile = !isLandingPageSpeedValid(finalBlock?.mobile) && isLandingPageSpeedValid(collectBlock.mobile);
|
|
@@ -137845,8 +138837,8 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
137845
138837
|
const restoredModules = [];
|
|
137846
138838
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
137847
138839
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
137848
|
-
const finalBlock =
|
|
137849
|
-
const collectBlock =
|
|
138840
|
+
const finalBlock = asRecord13(data[mod]);
|
|
138841
|
+
const collectBlock = asRecord13(collectReportData[mod]);
|
|
137850
138842
|
const finalItems = finalBlock?.items;
|
|
137851
138843
|
const collectItems = collectBlock?.items;
|
|
137852
138844
|
if (!Array.isArray(collectItems) || collectItems.length === 0) continue;
|
|
@@ -137862,7 +138854,7 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
137862
138854
|
}
|
|
137863
138855
|
|
|
137864
138856
|
// src/commands/google-ads-diagnosis/narrative-consistency-check.ts
|
|
137865
|
-
function
|
|
138857
|
+
function asRecord14(value) {
|
|
137866
138858
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137867
138859
|
return value;
|
|
137868
138860
|
}
|
|
@@ -137873,14 +138865,14 @@ function normalizeSuggestionText(value) {
|
|
|
137873
138865
|
}
|
|
137874
138866
|
function collectNarrativeTexts(data) {
|
|
137875
138867
|
const texts = [];
|
|
137876
|
-
const summary =
|
|
138868
|
+
const summary = asRecord14(data.summary);
|
|
137877
138869
|
if (Array.isArray(summary?.keyIssues)) {
|
|
137878
138870
|
for (const item of summary.keyIssues) texts.push(String(item ?? ""));
|
|
137879
138871
|
}
|
|
137880
|
-
const overview =
|
|
138872
|
+
const overview = asRecord14(data.diagnosisOverview);
|
|
137881
138873
|
if (Array.isArray(overview?.disadvantages)) {
|
|
137882
138874
|
for (const item of overview.disadvantages) {
|
|
137883
|
-
const rec =
|
|
138875
|
+
const rec = asRecord14(item);
|
|
137884
138876
|
if (rec) {
|
|
137885
138877
|
texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
137886
138878
|
} else {
|
|
@@ -137891,16 +138883,16 @@ function collectNarrativeTexts(data) {
|
|
|
137891
138883
|
return texts.filter((t) => t.trim().length > 0);
|
|
137892
138884
|
}
|
|
137893
138885
|
function newFeatureAccountStatus(data, strategy) {
|
|
137894
|
-
const nf =
|
|
138886
|
+
const nf = asRecord14(data.newFeatures);
|
|
137895
138887
|
const items3 = nf?.items;
|
|
137896
138888
|
if (!Array.isArray(items3)) return null;
|
|
137897
|
-
const row = items3.find((it) =>
|
|
137898
|
-
const rec =
|
|
138889
|
+
const row = items3.find((it) => asRecord14(it)?.strategy === strategy);
|
|
138890
|
+
const rec = asRecord14(row);
|
|
137899
138891
|
if (!rec || typeof rec.accountStatus !== "boolean") return null;
|
|
137900
138892
|
return rec.accountStatus;
|
|
137901
138893
|
}
|
|
137902
138894
|
function goldAccountFlag(data, key) {
|
|
137903
|
-
const gold =
|
|
138895
|
+
const gold = asRecord14(data.goldAccount);
|
|
137904
138896
|
const value = gold?.[key];
|
|
137905
138897
|
return typeof value === "boolean" ? value : null;
|
|
137906
138898
|
}
|
|
@@ -137931,7 +138923,7 @@ var NARRATIVE_CONSISTENCY_RULES = [
|
|
|
137931
138923
|
}
|
|
137932
138924
|
];
|
|
137933
138925
|
function goldAssetNoted(data, key) {
|
|
137934
|
-
const notes =
|
|
138926
|
+
const notes = asRecord14(asRecord14(data.goldAccount)?.goldAssetNotes);
|
|
137935
138927
|
return notes?.[key] === true;
|
|
137936
138928
|
}
|
|
137937
138929
|
var GOLD_CLAIM_RULES = [
|
|
@@ -137974,12 +138966,12 @@ var GOLD_CLAIM_RULES = [
|
|
|
137974
138966
|
];
|
|
137975
138967
|
function collectGoldClaimTexts(data) {
|
|
137976
138968
|
const texts = collectNarrativeTexts(data);
|
|
137977
|
-
const overview =
|
|
138969
|
+
const overview = asRecord14(data.diagnosisOverview);
|
|
137978
138970
|
for (const key of ["advantages", "disadvantages"]) {
|
|
137979
138971
|
const items3 = overview?.[key];
|
|
137980
138972
|
if (!Array.isArray(items3)) continue;
|
|
137981
138973
|
for (const item of items3) {
|
|
137982
|
-
const rec =
|
|
138974
|
+
const rec = asRecord14(item);
|
|
137983
138975
|
if (rec) texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
137984
138976
|
}
|
|
137985
138977
|
}
|
|
@@ -138000,7 +138992,7 @@ function getRepetitiveNarrativeErrors(data) {
|
|
|
138000
138992
|
const errors = [];
|
|
138001
138993
|
const budget = data.budgetCompetitiveness;
|
|
138002
138994
|
if (Array.isArray(budget) && budget.length >= 3) {
|
|
138003
|
-
const strategies = budget.map((row) => normalizeSuggestionText(
|
|
138995
|
+
const strategies = budget.map((row) => normalizeSuggestionText(asRecord14(row)?.strategy)).filter((s) => s.length > 0);
|
|
138004
138996
|
const dup = findIdenticalTexts(strategies, 3);
|
|
138005
138997
|
if (dup) {
|
|
138006
138998
|
errors.push(
|
|
@@ -138008,10 +139000,10 @@ function getRepetitiveNarrativeErrors(data) {
|
|
|
138008
139000
|
);
|
|
138009
139001
|
}
|
|
138010
139002
|
}
|
|
138011
|
-
const creative =
|
|
139003
|
+
const creative = asRecord14(data.adCreativeOptimization);
|
|
138012
139004
|
const creativeItems = creative?.items;
|
|
138013
139005
|
if (Array.isArray(creativeItems) && creativeItems.length >= 3) {
|
|
138014
|
-
const suggestions = creativeItems.map((row) => normalizeSuggestionText(
|
|
139006
|
+
const suggestions = creativeItems.map((row) => normalizeSuggestionText(asRecord14(row)?.suggestion)).filter((s) => s.length > 0);
|
|
138015
139007
|
const dup = findIdenticalTexts(suggestions, 3);
|
|
138016
139008
|
if (dup) {
|
|
138017
139009
|
errors.push(
|
|
@@ -138022,7 +139014,7 @@ function getRepetitiveNarrativeErrors(data) {
|
|
|
138022
139014
|
return errors;
|
|
138023
139015
|
}
|
|
138024
139016
|
function getLandingPageTopicErrors(data) {
|
|
138025
|
-
const lp =
|
|
139017
|
+
const lp = asRecord14(data.landingPageAnalysis);
|
|
138026
139018
|
if (!lp) return [];
|
|
138027
139019
|
const analysis = String(lp.analysis ?? "");
|
|
138028
139020
|
if (!analysis.trim()) return [];
|
|
@@ -138059,7 +139051,7 @@ function getNarrativeConsistencyErrors(data) {
|
|
|
138059
139051
|
}
|
|
138060
139052
|
|
|
138061
139053
|
// src/commands/google-ads-diagnosis/overview-items.ts
|
|
138062
|
-
function
|
|
139054
|
+
function asRecord15(value) {
|
|
138063
139055
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
138064
139056
|
return value;
|
|
138065
139057
|
}
|
|
@@ -138093,7 +139085,7 @@ function normalizeOverviewItem(item) {
|
|
|
138093
139085
|
if (!text3) return null;
|
|
138094
139086
|
return splitOverviewProse(text3);
|
|
138095
139087
|
}
|
|
138096
|
-
const rec =
|
|
139088
|
+
const rec = asRecord15(item);
|
|
138097
139089
|
if (!rec) return null;
|
|
138098
139090
|
const title = String(rec.title ?? "").trim();
|
|
138099
139091
|
const description = String(rec.description ?? "").trim();
|
|
@@ -138107,7 +139099,7 @@ function normalizeOverviewItem(item) {
|
|
|
138107
139099
|
};
|
|
138108
139100
|
}
|
|
138109
139101
|
function normalizeDiagnosisOverview(data) {
|
|
138110
|
-
const overview =
|
|
139102
|
+
const overview = asRecord15(data.diagnosisOverview);
|
|
138111
139103
|
if (!overview) return { data, coerced: [] };
|
|
138112
139104
|
const coerced = [];
|
|
138113
139105
|
const nextOverview = { ...overview };
|
|
@@ -138126,7 +139118,7 @@ function normalizeDiagnosisOverview(data) {
|
|
|
138126
139118
|
}
|
|
138127
139119
|
|
|
138128
139120
|
// src/commands/google-ads-diagnosis/data-rendered-copy.ts
|
|
138129
|
-
function
|
|
139121
|
+
function asRecord16(value) {
|
|
138130
139122
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
138131
139123
|
return value;
|
|
138132
139124
|
}
|
|
@@ -138187,7 +139179,7 @@ function distinctCopies(rows, key) {
|
|
|
138187
139179
|
function mapRows(rows, write) {
|
|
138188
139180
|
if (!Array.isArray(rows)) return rows;
|
|
138189
139181
|
const written = rows.map((item) => {
|
|
138190
|
-
const row =
|
|
139182
|
+
const row = asRecord16(item);
|
|
138191
139183
|
return row ? write(row) : item;
|
|
138192
139184
|
});
|
|
138193
139185
|
return written;
|
|
@@ -138201,7 +139193,7 @@ function applyDataRenderedCopy(data) {
|
|
|
138201
139193
|
if (Array.isArray(budgetRows)) {
|
|
138202
139194
|
next.budgetCompetitiveness = distinctCopies(budgetRows, "strategy");
|
|
138203
139195
|
}
|
|
138204
|
-
const bidding =
|
|
139196
|
+
const bidding = asRecord16(data.biddingStrategy);
|
|
138205
139197
|
if (bidding) {
|
|
138206
139198
|
next.biddingStrategy = {
|
|
138207
139199
|
...bidding,
|
|
@@ -138211,7 +139203,7 @@ function applyDataRenderedCopy(data) {
|
|
|
138211
139203
|
}))
|
|
138212
139204
|
};
|
|
138213
139205
|
}
|
|
138214
|
-
const features =
|
|
139206
|
+
const features = asRecord16(data.newFeatures);
|
|
138215
139207
|
if (features) {
|
|
138216
139208
|
next.newFeatures = {
|
|
138217
139209
|
...features,
|
|
@@ -138221,7 +139213,7 @@ function applyDataRenderedCopy(data) {
|
|
|
138221
139213
|
}))
|
|
138222
139214
|
};
|
|
138223
139215
|
}
|
|
138224
|
-
const creative =
|
|
139216
|
+
const creative = asRecord16(data.adCreativeOptimization);
|
|
138225
139217
|
if (creative) {
|
|
138226
139218
|
const creativeRows = mapRows(creative.items, (row) => ({
|
|
138227
139219
|
...row,
|
|
@@ -138238,7 +139230,7 @@ function applyDataRenderedCopy(data) {
|
|
|
138238
139230
|
// src/commands/google-ads-diagnosis/render-report.ts
|
|
138239
139231
|
init_lang();
|
|
138240
139232
|
init_html();
|
|
138241
|
-
function
|
|
139233
|
+
function asRecord17(value) {
|
|
138242
139234
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
138243
139235
|
return value;
|
|
138244
139236
|
}
|
|
@@ -138247,8 +139239,8 @@ function asRecord16(value) {
|
|
|
138247
139239
|
async function tryLoadCollectReportData(collectPath) {
|
|
138248
139240
|
try {
|
|
138249
139241
|
const raw = JSON.parse(await fs23.readFile(collectPath, "utf8"));
|
|
138250
|
-
const root =
|
|
138251
|
-
const reportData =
|
|
139242
|
+
const root = asRecord17(raw);
|
|
139243
|
+
const reportData = asRecord17(root?.reportData);
|
|
138252
139244
|
return reportData ?? root;
|
|
138253
139245
|
} catch {
|
|
138254
139246
|
return null;
|
|
@@ -138265,7 +139257,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
138265
139257
|
`);
|
|
138266
139258
|
process.exit(1);
|
|
138267
139259
|
}
|
|
138268
|
-
let data =
|
|
139260
|
+
let data = asRecord17(dataRaw);
|
|
138269
139261
|
if (!opts.noMergeCollect) {
|
|
138270
139262
|
const collectPath = path27.resolve(
|
|
138271
139263
|
opts.collectFile ?? path27.join(path27.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -138471,13 +139463,13 @@ function sumCampaigns(rows) {
|
|
|
138471
139463
|
}
|
|
138472
139464
|
return withRates({ spend, impressions, clicks, conversions });
|
|
138473
139465
|
}
|
|
138474
|
-
function withRates(
|
|
139466
|
+
function withRates(base2) {
|
|
138475
139467
|
return {
|
|
138476
|
-
...
|
|
138477
|
-
ctr:
|
|
138478
|
-
cpc:
|
|
138479
|
-
cpa:
|
|
138480
|
-
cvr:
|
|
139468
|
+
...base2,
|
|
139469
|
+
ctr: base2.impressions > 0 ? base2.clicks / base2.impressions : 0,
|
|
139470
|
+
cpc: base2.clicks > 0 ? base2.spend / base2.clicks : 0,
|
|
139471
|
+
cpa: base2.conversions > 0 ? base2.spend / base2.conversions : 0,
|
|
139472
|
+
cvr: base2.clicks > 0 ? base2.conversions / base2.clicks : 0
|
|
138481
139473
|
};
|
|
138482
139474
|
}
|
|
138483
139475
|
function pctChange(current, baseline) {
|
|
@@ -138638,8 +139630,8 @@ function parseKeywordSeedsFlag(raw) {
|
|
|
138638
139630
|
// src/commands/google-quarterly/fetch-subdivisions.ts
|
|
138639
139631
|
init_auth();
|
|
138640
139632
|
async function fetchSubdivisions(opts) {
|
|
138641
|
-
const
|
|
138642
|
-
if (!
|
|
139633
|
+
const base2 = opts.config.googleApiUrl?.replace(/\/$/, "");
|
|
139634
|
+
if (!base2) return null;
|
|
138643
139635
|
const qs = new URLSearchParams({
|
|
138644
139636
|
startDate: opts.start,
|
|
138645
139637
|
endDate: opts.end,
|
|
@@ -138648,7 +139640,7 @@ async function fetchSubdivisions(opts) {
|
|
|
138648
139640
|
if (opts.countryCriterionId && opts.countryCriterionId > 0) {
|
|
138649
139641
|
qs.set("countryCriterionId", String(opts.countryCriterionId));
|
|
138650
139642
|
}
|
|
138651
|
-
const url = `${
|
|
139643
|
+
const url = `${base2}/reporting/media-account/${opts.accountId}/reports/geo/subdivisions?${qs}`;
|
|
138652
139644
|
try {
|
|
138653
139645
|
const payload = await apiFetch2(url, opts.config, { verbose: opts.verbose });
|
|
138654
139646
|
const rows = Array.isArray(payload) ? payload : payload && typeof payload === "object" && Array.isArray(payload.countries) ? payload.countries : payload && typeof payload === "object" && Array.isArray(payload.items) ? payload.items : [];
|
|
@@ -138687,8 +139679,8 @@ function pickMetric(headers, row, name2) {
|
|
|
138687
139679
|
return Number.isFinite(n) ? n : null;
|
|
138688
139680
|
}
|
|
138689
139681
|
async function fetchYoutubeChannel(opts) {
|
|
138690
|
-
const
|
|
138691
|
-
if (!
|
|
139682
|
+
const base2 = opts.config.youtubeApiUrl?.replace(/\/$/, "");
|
|
139683
|
+
if (!base2 || !opts.externalTokenId.trim()) {
|
|
138692
139684
|
return {
|
|
138693
139685
|
source: "unbound",
|
|
138694
139686
|
subscribersGained: null,
|
|
@@ -138703,7 +139695,7 @@ async function fetchYoutubeChannel(opts) {
|
|
|
138703
139695
|
endDate: opts.end
|
|
138704
139696
|
});
|
|
138705
139697
|
if (opts.channelId?.trim()) qs.set("ids", `channel==${opts.channelId.trim()}`);
|
|
138706
|
-
const url = `${
|
|
139698
|
+
const url = `${base2}/query/report/YouTube/channel-kpi?${qs}`;
|
|
138707
139699
|
try {
|
|
138708
139700
|
const payload = await apiFetch2(url, opts.config, { verbose: opts.verbose });
|
|
138709
139701
|
const headers = Array.isArray(payload.columnHeaders) ? payload.columnHeaders : [];
|
|
@@ -139171,15 +140163,15 @@ function pickSpecialAds(videos, campaigns) {
|
|
|
139171
140163
|
function countryName(row) {
|
|
139172
140164
|
return String(row.countryNameZh ?? row.countryOrRegionZh ?? row.countryOrRegion ?? "").trim();
|
|
139173
140165
|
}
|
|
139174
|
-
function kpiDelta(cur,
|
|
140166
|
+
function kpiDelta(cur, base2) {
|
|
139175
140167
|
return {
|
|
139176
|
-
spend: pctChange(cur.spend,
|
|
139177
|
-
impressions: pctChange(cur.impressions,
|
|
139178
|
-
clicks: pctChange(cur.clicks,
|
|
139179
|
-
conversions: pctChange(cur.conversions,
|
|
139180
|
-
ctr: pctChange(cur.ctr,
|
|
139181
|
-
cpc: pctChange(cur.cpc,
|
|
139182
|
-
cpa: pctChange(cur.cpa,
|
|
140168
|
+
spend: pctChange(cur.spend, base2.spend),
|
|
140169
|
+
impressions: pctChange(cur.impressions, base2.impressions),
|
|
140170
|
+
clicks: pctChange(cur.clicks, base2.clicks),
|
|
140171
|
+
conversions: pctChange(cur.conversions, base2.conversions),
|
|
140172
|
+
ctr: pctChange(cur.ctr, base2.ctr),
|
|
140173
|
+
cpc: pctChange(cur.cpc, base2.cpc),
|
|
140174
|
+
cpa: pctChange(cur.cpa, base2.cpa)
|
|
139183
140175
|
};
|
|
139184
140176
|
}
|
|
139185
140177
|
function matchConversionAction(actions, name2) {
|
|
@@ -139632,26 +140624,26 @@ import fs27 from "fs/promises";
|
|
|
139632
140624
|
import path32 from "path";
|
|
139633
140625
|
|
|
139634
140626
|
// src/commands/google-quarterly/report-content.ts
|
|
139635
|
-
function
|
|
140627
|
+
function asRecord18(value) {
|
|
139636
140628
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
139637
140629
|
}
|
|
139638
140630
|
function asStringArray(value) {
|
|
139639
140631
|
return Array.isArray(value) ? value.map((x) => String(x).trim()).filter(Boolean) : [];
|
|
139640
140632
|
}
|
|
139641
140633
|
function validateQuarterlyNarrative(data) {
|
|
139642
|
-
const root =
|
|
139643
|
-
const narrative =
|
|
140634
|
+
const root = asRecord18(data);
|
|
140635
|
+
const narrative = asRecord18(root?.narrative);
|
|
139644
140636
|
const missing = [];
|
|
139645
140637
|
if (!narrative) {
|
|
139646
140638
|
return { ok: false, missing: ["narrative"] };
|
|
139647
140639
|
}
|
|
139648
140640
|
if (asStringArray(narrative.executiveSummary).length < 1) missing.push("narrative.executiveSummary");
|
|
139649
|
-
const sections =
|
|
140641
|
+
const sections = asRecord18(narrative.sections);
|
|
139650
140642
|
for (const key of ["efficiency", "adType", "region", "summary"]) {
|
|
139651
|
-
const block =
|
|
140643
|
+
const block = asRecord18(sections?.[key]);
|
|
139652
140644
|
if (asStringArray(block?.analysis).length < 1) missing.push(`narrative.sections.${key}.analysis`);
|
|
139653
140645
|
}
|
|
139654
|
-
const plan =
|
|
140646
|
+
const plan = asRecord18(narrative.plan);
|
|
139655
140647
|
if (asStringArray(plan?.analysis ?? plan?.goals).length < 1 && !String(plan?.text ?? "").trim()) {
|
|
139656
140648
|
missing.push("narrative.plan");
|
|
139657
140649
|
}
|
|
@@ -139664,8 +140656,8 @@ function mergeCollectFacts(agentData, collect) {
|
|
|
139664
140656
|
...facts,
|
|
139665
140657
|
narrative: agentData.narrative ?? collect.narrative,
|
|
139666
140658
|
meta: {
|
|
139667
|
-
...
|
|
139668
|
-
...
|
|
140659
|
+
...asRecord18(collect.meta) ?? {},
|
|
140660
|
+
...asRecord18(agentData.meta) ?? {}
|
|
139669
140661
|
}
|
|
139670
140662
|
};
|
|
139671
140663
|
}
|
|
@@ -139679,7 +140671,7 @@ var TSO_CLI_ROOT = path31.join(path31.dirname(fileURLToPath8(import.meta.url)),
|
|
|
139679
140671
|
// src/commands/google-quarterly/render-report.ts
|
|
139680
140672
|
init_lang();
|
|
139681
140673
|
init_html();
|
|
139682
|
-
function
|
|
140674
|
+
function asRecord19(value) {
|
|
139683
140675
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
139684
140676
|
return value;
|
|
139685
140677
|
}
|
|
@@ -139708,7 +140700,7 @@ async function runGoogleQuarterlyRender(opts) {
|
|
|
139708
140700
|
`);
|
|
139709
140701
|
process.exit(1);
|
|
139710
140702
|
}
|
|
139711
|
-
const merged = mergeCollectFacts(
|
|
140703
|
+
const merged = mergeCollectFacts(asRecord19(agentRaw), asRecord19(collectRaw));
|
|
139712
140704
|
const check = validateQuarterlyNarrative(merged);
|
|
139713
140705
|
if (!check.ok && !opts.lenient) {
|
|
139714
140706
|
console.error("\n\u274C narrative \u4E0D\u5B8C\u6574\uFF0C\u62D2\u7EDD\u51FA HTML\uFF1A\n");
|
|
@@ -139892,7 +140884,7 @@ import * as fs34 from "fs/promises";
|
|
|
139892
140884
|
import * as path41 from "path";
|
|
139893
140885
|
|
|
139894
140886
|
// src/commands/facebook-analysis/report-metrics.ts
|
|
139895
|
-
function
|
|
140887
|
+
function asRecord25(value) {
|
|
139896
140888
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
139897
140889
|
return value;
|
|
139898
140890
|
}
|
|
@@ -140029,7 +141021,7 @@ function localizeFacebookReportLabels(payload) {
|
|
|
140029
141021
|
for (const rows of Object.values(tables)) {
|
|
140030
141022
|
if (!Array.isArray(rows)) continue;
|
|
140031
141023
|
for (const row of rows) {
|
|
140032
|
-
localizeMetricLabels(
|
|
141024
|
+
localizeMetricLabels(asRecord25(row));
|
|
140033
141025
|
}
|
|
140034
141026
|
}
|
|
140035
141027
|
return payload;
|
|
@@ -140062,7 +141054,7 @@ async function readManifest3(snapshotDir) {
|
|
|
140062
141054
|
async function loadSectionFile3(snapshotDir, file) {
|
|
140063
141055
|
try {
|
|
140064
141056
|
const raw = await fs34.readFile(path41.join(snapshotDir, file), "utf8");
|
|
140065
|
-
return
|
|
141057
|
+
return asRecord25(JSON.parse(raw));
|
|
140066
141058
|
} catch {
|
|
140067
141059
|
return null;
|
|
140068
141060
|
}
|
|
@@ -140118,7 +141110,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
140118
141110
|
if (data) sectionMap.set(art.section, data);
|
|
140119
141111
|
}
|
|
140120
141112
|
const overview = sectionMap.get("overview");
|
|
140121
|
-
const current =
|
|
141113
|
+
const current = asRecord25(overview?.currentPeriod);
|
|
140122
141114
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
140123
141115
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
140124
141116
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -140258,7 +141250,7 @@ function writeSimpleXlsx(filePath, sheets, opts = {}) {
|
|
|
140258
141250
|
}
|
|
140259
141251
|
|
|
140260
141252
|
// src/commands/facebook-analysis/render-excel.ts
|
|
140261
|
-
function
|
|
141253
|
+
function asRecord26(value) {
|
|
140262
141254
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140263
141255
|
return value;
|
|
140264
141256
|
}
|
|
@@ -140306,8 +141298,8 @@ function bullets(value) {
|
|
|
140306
141298
|
return asArray3(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
140307
141299
|
}
|
|
140308
141300
|
function insightLines(section) {
|
|
140309
|
-
const rec =
|
|
140310
|
-
const insight =
|
|
141301
|
+
const rec = asRecord26(section);
|
|
141302
|
+
const insight = asRecord26(rec?.insight) ?? rec;
|
|
140311
141303
|
return [...bullets(insight?.analysis), ...bullets(insight?.advice)];
|
|
140312
141304
|
}
|
|
140313
141305
|
function numberedSection(index, title, lines) {
|
|
@@ -140316,15 +141308,15 @@ function numberedSection(index, title, lines) {
|
|
|
140316
141308
|
${body}`;
|
|
140317
141309
|
}
|
|
140318
141310
|
function buildSummaryNarrative(data) {
|
|
140319
|
-
const kpis =
|
|
140320
|
-
const meta =
|
|
140321
|
-
const sections =
|
|
141311
|
+
const kpis = asRecord26(data.kpis) ?? {};
|
|
141312
|
+
const meta = asRecord26(data.meta) ?? {};
|
|
141313
|
+
const sections = asRecord26(data.sections) ?? {};
|
|
140322
141314
|
const period = str2(meta.periodLabel) || `${str2(meta.startDate)} ~ ${str2(meta.endDate)}`.trim();
|
|
140323
141315
|
const account = str2(meta.accountName) || "Facebook \u5E7F\u544A\u8D26\u6237";
|
|
140324
141316
|
const campaign = str2(meta.campaignName);
|
|
140325
141317
|
const resultLabel = displayResultType(kpis.resultType ?? meta.resultType) || "\u6210\u6548";
|
|
140326
141318
|
const resultNoun = /潜在客户|lead/i.test(resultLabel) ? "\u4E2A\u6F5C\u5728\u5BA2\u6237\u8868\u5355" : `\u4E2A${resultLabel}`;
|
|
140327
|
-
const firstQuestion =
|
|
141319
|
+
const firstQuestion = asRecord26(asArray3(data.fourQuestions)[0]);
|
|
140328
141320
|
const extra = str2(firstQuestion?.verdict);
|
|
140329
141321
|
const spendLine = [
|
|
140330
141322
|
campaign ? `\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${campaign}\uFF09` : "",
|
|
@@ -140344,7 +141336,7 @@ function buildSummaryNarrative(data) {
|
|
|
140344
141336
|
numberedSection(3, "\u5E7F\u544A\u9891\u6B21\u4E0E\u65E5\u8D8B\u52BF", insightLines(sections.daily))
|
|
140345
141337
|
].join("\n");
|
|
140346
141338
|
const recBlocks = asArray3(data.recommendations).map((raw, i) => {
|
|
140347
|
-
const rec =
|
|
141339
|
+
const rec = asRecord26(raw);
|
|
140348
141340
|
if (!rec) return "";
|
|
140349
141341
|
const title = [str2(rec.title), str2(rec.tag)].filter(Boolean).join(" \xB7 ");
|
|
140350
141342
|
return numberedSection(i + 1, title || "\u4F18\u5316\u5EFA\u8BAE", bullets(rec.items));
|
|
@@ -140355,9 +141347,9 @@ function buildSummaryNarrative(data) {
|
|
|
140355
141347
|
return [partOne, partTwo, partThree];
|
|
140356
141348
|
}
|
|
140357
141349
|
function buildMetaPeriodExcelSheets(data) {
|
|
140358
|
-
const meta =
|
|
140359
|
-
const kpis =
|
|
140360
|
-
const tables =
|
|
141350
|
+
const meta = asRecord26(data.meta) ?? {};
|
|
141351
|
+
const kpis = asRecord26(data.kpis) ?? {};
|
|
141352
|
+
const tables = asRecord26(data.tables) ?? {};
|
|
140361
141353
|
const currency = str2(meta.currency) || "USD";
|
|
140362
141354
|
const resultType = displayResultType(kpis.resultType ?? meta.resultType);
|
|
140363
141355
|
const attribution = displayAttribution(kpis.attributionSetting ?? meta.attributionSetting);
|
|
@@ -140376,19 +141368,19 @@ function buildMetaPeriodExcelSheets(data) {
|
|
|
140376
141368
|
];
|
|
140377
141369
|
const dailyRows = [metricHeaders(currency, "\u5355\u65E5")];
|
|
140378
141370
|
for (const raw of asArray3(tables.daily)) {
|
|
140379
|
-
const row =
|
|
141371
|
+
const row = asRecord26(raw);
|
|
140380
141372
|
if (!row) continue;
|
|
140381
141373
|
dailyRows.push([normalizeInsightDate(row.date), ...metricCells(row)]);
|
|
140382
141374
|
}
|
|
140383
141375
|
const countryRows = [metricHeaders(currency, "\u56FD\u5BB6/\u5730\u533A")];
|
|
140384
141376
|
for (const raw of asArray3(tables.countries)) {
|
|
140385
|
-
const row =
|
|
141377
|
+
const row = asRecord26(raw);
|
|
140386
141378
|
if (!row) continue;
|
|
140387
141379
|
countryRows.push([str2(row.countryOrRegion), ...metricCells(row)]);
|
|
140388
141380
|
}
|
|
140389
141381
|
const campaignRows3 = [metricHeaders(currency, "\u5E7F\u544A\u7CFB\u5217\u540D\u79F0")];
|
|
140390
141382
|
for (const raw of asArray3(tables.campaigns)) {
|
|
140391
|
-
const row =
|
|
141383
|
+
const row = asRecord26(raw);
|
|
140392
141384
|
if (!row) continue;
|
|
140393
141385
|
campaignRows3.push([str2(row.campaignName), ...metricCells(row)]);
|
|
140394
141386
|
}
|
|
@@ -140407,7 +141399,7 @@ function buildMetaPeriodExcelSheets(data) {
|
|
|
140407
141399
|
]
|
|
140408
141400
|
];
|
|
140409
141401
|
for (const raw of asArray3(tables.audiences)) {
|
|
140410
|
-
const row =
|
|
141402
|
+
const row = asRecord26(raw);
|
|
140411
141403
|
if (!row) continue;
|
|
140412
141404
|
audienceRows.push([str2(row.age), str2(row.gender), ...metricCells(row)]);
|
|
140413
141405
|
}
|
|
@@ -140449,7 +141441,7 @@ function isFiniteNumber(value) {
|
|
|
140449
141441
|
function asArray4(value) {
|
|
140450
141442
|
return Array.isArray(value) ? value : [];
|
|
140451
141443
|
}
|
|
140452
|
-
function
|
|
141444
|
+
function asRecord27(value) {
|
|
140453
141445
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140454
141446
|
return value;
|
|
140455
141447
|
}
|
|
@@ -140462,8 +141454,8 @@ function stringList3(value) {
|
|
|
140462
141454
|
return asArray4(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
140463
141455
|
}
|
|
140464
141456
|
function insightLists(section) {
|
|
140465
|
-
const rec =
|
|
140466
|
-
const insight =
|
|
141457
|
+
const rec = asRecord27(section);
|
|
141458
|
+
const insight = asRecord27(rec?.insight) ?? rec;
|
|
140467
141459
|
return {
|
|
140468
141460
|
analysis: stringList3(insight?.analysis),
|
|
140469
141461
|
advice: stringList3(insight?.advice)
|
|
@@ -140493,11 +141485,11 @@ function requireInsight(missing, key, chapter, section, required) {
|
|
|
140493
141485
|
}
|
|
140494
141486
|
function validateMetaPeriodReportContent(data) {
|
|
140495
141487
|
const missing = [];
|
|
140496
|
-
const meta =
|
|
140497
|
-
const kpis =
|
|
140498
|
-
const tables =
|
|
140499
|
-
const charts =
|
|
140500
|
-
const sections =
|
|
141488
|
+
const meta = asRecord27(data.meta) ?? {};
|
|
141489
|
+
const kpis = asRecord27(data.kpis) ?? {};
|
|
141490
|
+
const tables = asRecord27(data.tables) ?? {};
|
|
141491
|
+
const charts = asRecord27(data.charts) ?? {};
|
|
141492
|
+
const sections = asRecord27(data.sections) ?? {};
|
|
140501
141493
|
const fourQuestions = asArray4(data.fourQuestions);
|
|
140502
141494
|
const recommendations = asArray4(data.recommendations);
|
|
140503
141495
|
if (!textLen3(meta.accountName) && !textLen3(meta.accountId)) {
|
|
@@ -140576,7 +141568,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
140576
141568
|
"\u987B\u6709 tables.audiences \u6570\u7EC4\uFF1B\u62C9\u6570\u52A0 --sections audience\uFF0Crender \u65F6\u4F20 --snapshot-dir\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
140577
141569
|
);
|
|
140578
141570
|
}
|
|
140579
|
-
const dailyChart =
|
|
141571
|
+
const dailyChart = asRecord27(charts.daily);
|
|
140580
141572
|
if (!Array.isArray(dailyChart?.labels)) {
|
|
140581
141573
|
pushMissing3(
|
|
140582
141574
|
missing,
|
|
@@ -140586,7 +141578,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
140586
141578
|
"\u987B\u6709 charts.daily.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 daily\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
140587
141579
|
);
|
|
140588
141580
|
}
|
|
140589
|
-
const countryChart =
|
|
141581
|
+
const countryChart = asRecord27(charts.country);
|
|
140590
141582
|
if (!Array.isArray(countryChart?.labels)) {
|
|
140591
141583
|
pushMissing3(
|
|
140592
141584
|
missing,
|
|
@@ -140596,7 +141588,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
140596
141588
|
"\u987B\u6709 charts.country.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 country\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
140597
141589
|
);
|
|
140598
141590
|
}
|
|
140599
|
-
const audienceChart =
|
|
141591
|
+
const audienceChart = asRecord27(charts.audience);
|
|
140600
141592
|
if (!Array.isArray(audienceChart?.labels)) {
|
|
140601
141593
|
pushMissing3(
|
|
140602
141594
|
missing,
|
|
@@ -140615,7 +141607,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
140615
141607
|
"\u5FC5\u987B\u6070\u597D 4 \u95EE\uFF1A\u94B1\u82B1\u5F97\u503C\u4E0D\u503C / \u8C01\u771F\u7684\u60F3\u4E70 / \u5E7F\u544A\u8FD8\u65B0\u9C9C\u5417 / \u7528\u6237\u4E3A\u4EC0\u4E48\u4E0D\u7559\u8D44"
|
|
140616
141608
|
);
|
|
140617
141609
|
} else {
|
|
140618
|
-
const titles = fourQuestions.map((q) =>
|
|
141610
|
+
const titles = fourQuestions.map((q) => asRecord27(q)?.title);
|
|
140619
141611
|
for (const title of FOUR_QUESTION_TITLES) {
|
|
140620
141612
|
if (!titles.includes(title)) {
|
|
140621
141613
|
pushMissing3(
|
|
@@ -140628,7 +141620,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
140628
141620
|
}
|
|
140629
141621
|
}
|
|
140630
141622
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
140631
|
-
const q =
|
|
141623
|
+
const q = asRecord27(fourQuestions[i]);
|
|
140632
141624
|
if (textLen3(q?.verdict) < 8) {
|
|
140633
141625
|
pushMissing3(
|
|
140634
141626
|
missing,
|
|
@@ -140668,7 +141660,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
140668
141660
|
);
|
|
140669
141661
|
} else {
|
|
140670
141662
|
for (let i = 0; i < recommendations.length; i++) {
|
|
140671
|
-
const rec =
|
|
141663
|
+
const rec = asRecord27(recommendations[i]);
|
|
140672
141664
|
if (textLen3(rec?.title) < 2) {
|
|
140673
141665
|
pushMissing3(
|
|
140674
141666
|
missing,
|
|
@@ -140764,7 +141756,7 @@ async function readJsonFile4(filePath) {
|
|
|
140764
141756
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
140765
141757
|
}
|
|
140766
141758
|
}
|
|
140767
|
-
function
|
|
141759
|
+
function asRecord28(value) {
|
|
140768
141760
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140769
141761
|
return value;
|
|
140770
141762
|
}
|
|
@@ -140794,7 +141786,7 @@ function defaultOutPath(dataPath, format, out) {
|
|
|
140794
141786
|
}
|
|
140795
141787
|
async function runFacebookAnalysisRender(opts) {
|
|
140796
141788
|
const dataPath = path42.resolve(opts.dataFile);
|
|
140797
|
-
let data =
|
|
141789
|
+
let data = asRecord28(await readJsonFile4(dataPath));
|
|
140798
141790
|
if (opts.snapshotDir?.trim()) {
|
|
140799
141791
|
data = await mergeFacebookSnapshotIntoReport(data, path42.resolve(opts.snapshotDir));
|
|
140800
141792
|
}
|
|
@@ -140860,7 +141852,7 @@ import { fileURLToPath as fileURLToPath11 } from "url";
|
|
|
140860
141852
|
// src/commands/facebook-analysis/diagnosis-merge-snapshot.ts
|
|
140861
141853
|
import * as fs36 from "fs/promises";
|
|
140862
141854
|
import * as path43 from "path";
|
|
140863
|
-
function
|
|
141855
|
+
function asRecord29(value) {
|
|
140864
141856
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140865
141857
|
return value;
|
|
140866
141858
|
}
|
|
@@ -140871,7 +141863,7 @@ function num8(value) {
|
|
|
140871
141863
|
return Number.isFinite(n) ? n : void 0;
|
|
140872
141864
|
}
|
|
140873
141865
|
function asRows(value) {
|
|
140874
|
-
return Array.isArray(value) ? value.filter((r) =>
|
|
141866
|
+
return Array.isArray(value) ? value.filter((r) => asRecord29(r) !== null) : [];
|
|
140875
141867
|
}
|
|
140876
141868
|
function pickMetricExtras(row, extras) {
|
|
140877
141869
|
const out = {
|
|
@@ -140920,14 +141912,14 @@ async function readManifest4(snapshotDir, accountId) {
|
|
|
140920
141912
|
async function loadSectionFile4(snapshotDir, file) {
|
|
140921
141913
|
try {
|
|
140922
141914
|
const raw = await fs36.readFile(path43.join(snapshotDir, file), "utf8");
|
|
140923
|
-
return
|
|
141915
|
+
return asRecord29(JSON.parse(raw));
|
|
140924
141916
|
} catch {
|
|
140925
141917
|
return null;
|
|
140926
141918
|
}
|
|
140927
141919
|
}
|
|
140928
141920
|
async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
140929
141921
|
const absDir = path43.resolve(snapshotDir);
|
|
140930
|
-
const metaAccountId = typeof
|
|
141922
|
+
const metaAccountId = typeof asRecord29(payload.meta)?.accountId === "string" ? asRecord29(payload.meta)?.accountId : void 0;
|
|
140931
141923
|
const manifest = await readManifest4(snapshotDir, metaAccountId);
|
|
140932
141924
|
if (!manifest) {
|
|
140933
141925
|
throw new Error(
|
|
@@ -140940,8 +141932,8 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
140940
141932
|
if (data) sectionMap.set(art.section, data);
|
|
140941
141933
|
}
|
|
140942
141934
|
const overview = sectionMap.get("overview");
|
|
140943
|
-
const currentPeriod =
|
|
140944
|
-
const previousPeriod =
|
|
141935
|
+
const currentPeriod = asRecord29(overview?.currentPeriod);
|
|
141936
|
+
const previousPeriod = asRecord29(overview?.previousPeriod);
|
|
140945
141937
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
140946
141938
|
const meta = { ...payload.meta ?? {} };
|
|
140947
141939
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
@@ -141075,7 +142067,7 @@ function textLen4(value) {
|
|
|
141075
142067
|
function asArray5(value) {
|
|
141076
142068
|
return Array.isArray(value) ? value : [];
|
|
141077
142069
|
}
|
|
141078
|
-
function
|
|
142070
|
+
function asRecord30(value) {
|
|
141079
142071
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
141080
142072
|
return value;
|
|
141081
142073
|
}
|
|
@@ -141086,9 +142078,9 @@ function pushMissing4(missing, id, chapter, dimension, hint) {
|
|
|
141086
142078
|
}
|
|
141087
142079
|
function validateMetaDiagnosisReportContent(data) {
|
|
141088
142080
|
const missing = [];
|
|
141089
|
-
const meta =
|
|
141090
|
-
const narrative =
|
|
141091
|
-
const sections =
|
|
142081
|
+
const meta = asRecord30(data.meta) ?? {};
|
|
142082
|
+
const narrative = asRecord30(data.narrative) ?? {};
|
|
142083
|
+
const sections = asRecord30(narrative.sections) ?? {};
|
|
141092
142084
|
if (!textLen4(meta.accountId)) {
|
|
141093
142085
|
pushMissing4(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
141094
142086
|
}
|
|
@@ -141106,7 +142098,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
141106
142098
|
const sectionSuggestionCounts = {};
|
|
141107
142099
|
const sectionNotAvailable = {};
|
|
141108
142100
|
for (const key of NORMAL_SECTION_KEYS) {
|
|
141109
|
-
const section =
|
|
142101
|
+
const section = asRecord30(sections[key]);
|
|
141110
142102
|
const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
|
|
141111
142103
|
const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
|
|
141112
142104
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -141132,7 +142124,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
141132
142124
|
}
|
|
141133
142125
|
}
|
|
141134
142126
|
for (const key of NA_SECTION_KEYS) {
|
|
141135
|
-
const section =
|
|
142127
|
+
const section = asRecord30(sections[key]);
|
|
141136
142128
|
const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
|
|
141137
142129
|
const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
|
|
141138
142130
|
const notAvailable = section?.notAvailable === true;
|
|
@@ -141154,7 +142146,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
141154
142146
|
}
|
|
141155
142147
|
const recommendations = asArray5(narrative.recommendations).filter((r) => {
|
|
141156
142148
|
if (typeof r === "string") return textLen4(r) > 0;
|
|
141157
|
-
const rec =
|
|
142149
|
+
const rec = asRecord30(r);
|
|
141158
142150
|
return textLen4(rec?.content) > 0;
|
|
141159
142151
|
});
|
|
141160
142152
|
if (recommendations.length < 3) {
|
|
@@ -141229,7 +142221,7 @@ async function readJsonFile5(filePath) {
|
|
|
141229
142221
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
141230
142222
|
}
|
|
141231
142223
|
}
|
|
141232
|
-
function
|
|
142224
|
+
function asRecord31(value) {
|
|
141233
142225
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
141234
142226
|
return value;
|
|
141235
142227
|
}
|
|
@@ -141245,7 +142237,7 @@ function injectReportData4(html, payload, lang) {
|
|
|
141245
142237
|
}
|
|
141246
142238
|
async function runFacebookAnalysisDiagnosisRender(opts) {
|
|
141247
142239
|
const dataPath = path44.resolve(opts.dataFile);
|
|
141248
|
-
let data =
|
|
142240
|
+
let data = asRecord31(await readJsonFile5(dataPath));
|
|
141249
142241
|
if (opts.snapshotDir?.trim()) {
|
|
141250
142242
|
data = await mergeMetaDiagnosisSnapshotIntoReport(data, path44.resolve(opts.snapshotDir));
|
|
141251
142243
|
}
|
|
@@ -141325,8 +142317,8 @@ function resolveSectionList2(sections, exclude) {
|
|
|
141325
142317
|
}
|
|
141326
142318
|
const includeCanonical = new Set(include.map((n) => normalizeSectionToken(n)).filter(Boolean));
|
|
141327
142319
|
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken(n)).filter(Boolean));
|
|
141328
|
-
const
|
|
141329
|
-
return
|
|
142320
|
+
const base2 = include.length > 0 ? FACEBOOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...FACEBOOK_SECTIONS];
|
|
142321
|
+
return base2.filter((s) => !dropCanonical.has(s.name));
|
|
141330
142322
|
}
|
|
141331
142323
|
|
|
141332
142324
|
// src/commands/facebook-analysis/run-batch.ts
|
|
@@ -141445,7 +142437,7 @@ function resolveFacebookPageSize(raw, fallback = FB_ENTITY_PAGE_SIZE_DEFAULT) {
|
|
|
141445
142437
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
141446
142438
|
return Math.min(Math.max(Math.floor(n), 1), FB_ENTITY_PAGE_SIZE_MAX);
|
|
141447
142439
|
}
|
|
141448
|
-
function
|
|
142440
|
+
function asRecord32(value) {
|
|
141449
142441
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
141450
142442
|
}
|
|
141451
142443
|
function pickProp(obj, ...keys) {
|
|
@@ -141455,12 +142447,12 @@ function pickProp(obj, ...keys) {
|
|
|
141455
142447
|
return void 0;
|
|
141456
142448
|
}
|
|
141457
142449
|
function unwrapFacebookPaged(raw) {
|
|
141458
|
-
const rec =
|
|
142450
|
+
const rec = asRecord32(raw);
|
|
141459
142451
|
if (!rec) return { items: [] };
|
|
141460
142452
|
const data = pickProp(rec, "data", "Data");
|
|
141461
142453
|
const items3 = Array.isArray(data) ? data : [];
|
|
141462
|
-
const paging =
|
|
141463
|
-
const cursors = paging ?
|
|
142454
|
+
const paging = asRecord32(pickProp(rec, "paging", "Paging"));
|
|
142455
|
+
const cursors = paging ? asRecord32(pickProp(paging, "cursors", "Cursors")) : void 0;
|
|
141464
142456
|
const afterRaw = cursors ? pickProp(cursors, "after", "After") : void 0;
|
|
141465
142457
|
const after = typeof afterRaw === "string" && afterRaw.trim() ? afterRaw.trim() : void 0;
|
|
141466
142458
|
return { items: items3, after };
|
|
@@ -141522,14 +142514,14 @@ function parseEffectiveStatusFilter(raw) {
|
|
|
141522
142514
|
return canonical.length ? canonical.join(",") : void 0;
|
|
141523
142515
|
}
|
|
141524
142516
|
function facebookAdsUrl(facebookApiUrl, digits, path56) {
|
|
141525
|
-
const
|
|
142517
|
+
const base2 = facebookApiUrl.replace(/\/$/, "");
|
|
141526
142518
|
const p = path56.startsWith("/") ? path56 : `/${path56}`;
|
|
141527
|
-
return `${
|
|
142519
|
+
return `${base2}/api/fb/ads/${digits}${p}`;
|
|
141528
142520
|
}
|
|
141529
142521
|
function facebookInsightsUrl(facebookApiUrl, digits, level) {
|
|
141530
|
-
const
|
|
142522
|
+
const base2 = facebookApiUrl.replace(/\/$/, "");
|
|
141531
142523
|
const suffix = level === "account" ? "" : `/${level}`;
|
|
141532
|
-
return `${
|
|
142524
|
+
return `${base2}/api/fb/ads/insights/accounts/${digits}${suffix}`;
|
|
141533
142525
|
}
|
|
141534
142526
|
function buildEntityQuery(opts) {
|
|
141535
142527
|
const q = new URLSearchParams();
|
|
@@ -141552,16 +142544,16 @@ function buildInsightsQuery(opts) {
|
|
|
141552
142544
|
return q;
|
|
141553
142545
|
}
|
|
141554
142546
|
async function fetchFacebookAccount(config, digits, verbose) {
|
|
141555
|
-
const
|
|
142547
|
+
const base2 = requireFacebookApi(config);
|
|
141556
142548
|
return apiFetch2(
|
|
141557
|
-
facebookAdsUrl(
|
|
142549
|
+
facebookAdsUrl(base2, digits, "/account"),
|
|
141558
142550
|
config,
|
|
141559
142551
|
{},
|
|
141560
142552
|
verbose
|
|
141561
142553
|
);
|
|
141562
142554
|
}
|
|
141563
142555
|
async function fetchFacebookPagedList(config, digits, path56, opts) {
|
|
141564
|
-
const
|
|
142556
|
+
const base2 = requireFacebookApi(config);
|
|
141565
142557
|
const items3 = [];
|
|
141566
142558
|
let after;
|
|
141567
142559
|
let pagesFetched = 0;
|
|
@@ -141575,7 +142567,7 @@ async function fetchFacebookPagedList(config, digits, path56, opts) {
|
|
|
141575
142567
|
adsetId: opts.adsetId
|
|
141576
142568
|
});
|
|
141577
142569
|
const raw = await apiFetch2(
|
|
141578
|
-
`${facebookAdsUrl(
|
|
142570
|
+
`${facebookAdsUrl(base2, digits, path56)}?${query}`,
|
|
141579
142571
|
config,
|
|
141580
142572
|
{},
|
|
141581
142573
|
opts.verbose
|
|
@@ -141591,7 +142583,7 @@ async function fetchFacebookPagedList(config, digits, path56, opts) {
|
|
|
141591
142583
|
}
|
|
141592
142584
|
async function fetchFacebookInsights(config, digits, level, opts) {
|
|
141593
142585
|
assertFacebookInsightsQuery({ breakdown: opts.breakdown, timeIncrement: opts.timeIncrement });
|
|
141594
|
-
const
|
|
142586
|
+
const base2 = requireFacebookApi(config);
|
|
141595
142587
|
const query = buildInsightsQuery({
|
|
141596
142588
|
startDate: opts.startDate,
|
|
141597
142589
|
endDate: opts.endDate,
|
|
@@ -141602,13 +142594,13 @@ async function fetchFacebookInsights(config, digits, level, opts) {
|
|
|
141602
142594
|
adIds: opts.adIds
|
|
141603
142595
|
});
|
|
141604
142596
|
const raw = await apiFetch2(
|
|
141605
|
-
`${facebookInsightsUrl(
|
|
142597
|
+
`${facebookInsightsUrl(base2, digits, level)}?${query}`,
|
|
141606
142598
|
config,
|
|
141607
142599
|
{},
|
|
141608
142600
|
opts.verbose
|
|
141609
142601
|
);
|
|
141610
142602
|
if (Array.isArray(raw)) return raw;
|
|
141611
|
-
const rec =
|
|
142603
|
+
const rec = asRecord32(raw);
|
|
141612
142604
|
const data = rec ? pickProp(rec, "data", "Data") : void 0;
|
|
141613
142605
|
return Array.isArray(data) ? data : [];
|
|
141614
142606
|
}
|
|
@@ -141617,7 +142609,7 @@ function sumActionArray(raw) {
|
|
|
141617
142609
|
let sum = 0;
|
|
141618
142610
|
let hit = false;
|
|
141619
142611
|
for (const item of raw) {
|
|
141620
|
-
const rec =
|
|
142612
|
+
const rec = asRecord32(item);
|
|
141621
142613
|
if (!rec) continue;
|
|
141622
142614
|
const n = Number(rec.value);
|
|
141623
142615
|
if (!Number.isFinite(n)) continue;
|
|
@@ -141628,7 +142620,7 @@ function sumActionArray(raw) {
|
|
|
141628
142620
|
}
|
|
141629
142621
|
|
|
141630
142622
|
// src/commands/facebook-analysis/entities.ts
|
|
141631
|
-
function
|
|
142623
|
+
function fail2(label2, err) {
|
|
141632
142624
|
const msg = err instanceof Error ? err.message : String(err);
|
|
141633
142625
|
console.error(`
|
|
141634
142626
|
\u274C ${label2}\uFF1A${msg}
|
|
@@ -141656,7 +142648,7 @@ async function runFacebookAccountStatus(opts) {
|
|
|
141656
142648
|
try {
|
|
141657
142649
|
row = await fetchFacebookAccount(config, manifestId, !!opts.verbose);
|
|
141658
142650
|
} catch (err) {
|
|
141659
|
-
|
|
142651
|
+
fail2("\u67E5\u8BE2 Facebook \u8D26\u6237\u5931\u8D25", err);
|
|
141660
142652
|
}
|
|
141661
142653
|
const payload = { account: manifestId, ...row };
|
|
141662
142654
|
if (await emitCliJsonOrSnapshot(opts, {
|
|
@@ -141706,7 +142698,7 @@ async function runFacebookEntityList(opts, kind) {
|
|
|
141706
142698
|
try {
|
|
141707
142699
|
effectiveStatus = parseEffectiveStatusFilter(opts.effectiveStatus);
|
|
141708
142700
|
} catch (err) {
|
|
141709
|
-
|
|
142701
|
+
fail2("\u53C2\u6570\u9519\u8BEF", err);
|
|
141710
142702
|
}
|
|
141711
142703
|
const pageSize = resolveFacebookPageSize(opts.pageSize);
|
|
141712
142704
|
const config = loadConfig(opts.token);
|
|
@@ -141731,7 +142723,7 @@ async function runFacebookEntityList(opts, kind) {
|
|
|
141731
142723
|
adsetId: opts.adsetId?.trim() || void 0
|
|
141732
142724
|
});
|
|
141733
142725
|
} catch (err) {
|
|
141734
|
-
|
|
142726
|
+
fail2(`\u67E5\u8BE2 ${meta.title} \u5931\u8D25`, err);
|
|
141735
142727
|
}
|
|
141736
142728
|
const payload = wrapListJson({
|
|
141737
142729
|
page: 1,
|
|
@@ -141852,7 +142844,7 @@ async function runFacebookInsights(opts) {
|
|
|
141852
142844
|
breakdown = resolveFacebookBreakdown(opts.breakdown);
|
|
141853
142845
|
timeIncrement = parseOptionalNumber2(opts.timeIncrement);
|
|
141854
142846
|
} catch (err) {
|
|
141855
|
-
|
|
142847
|
+
fail2("\u53C2\u6570\u9519\u8BEF", err);
|
|
141856
142848
|
}
|
|
141857
142849
|
const config = loadConfig(opts.token);
|
|
141858
142850
|
let items3;
|
|
@@ -141868,7 +142860,7 @@ async function runFacebookInsights(opts) {
|
|
|
141868
142860
|
adIds: opts.adIds?.trim() || void 0
|
|
141869
142861
|
});
|
|
141870
142862
|
} catch (err) {
|
|
141871
|
-
|
|
142863
|
+
fail2("\u67E5\u8BE2 Facebook Insights \u5931\u8D25", err);
|
|
141872
142864
|
}
|
|
141873
142865
|
const payload = {
|
|
141874
142866
|
account: manifestId,
|
|
@@ -142146,8 +143138,8 @@ function resolveSectionList3(sections, exclude) {
|
|
|
142146
143138
|
const includeCanonical = new Set(include.map((n) => normalizeSectionToken2(n)).filter(Boolean));
|
|
142147
143139
|
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken2(n)).filter(Boolean));
|
|
142148
143140
|
const defaultSet = new Set(BING_DEFAULT_SECTION_NAMES);
|
|
142149
|
-
const
|
|
142150
|
-
return dropMergedWhenSplitPresent(
|
|
143141
|
+
const base2 = include.length > 0 ? BING_SECTIONS.filter((s) => includeCanonical.has(s.name)) : BING_SECTIONS.filter((s) => defaultSet.has(s.name));
|
|
143142
|
+
return dropMergedWhenSplitPresent(base2.filter((s) => !dropCanonical.has(s.name)));
|
|
142151
143143
|
}
|
|
142152
143144
|
|
|
142153
143145
|
// src/commands/bing-analysis/fetch.ts
|
|
@@ -142898,7 +143890,7 @@ import { fileURLToPath as fileURLToPath12 } from "url";
|
|
|
142898
143890
|
// src/commands/bing-analysis/merge-snapshot.ts
|
|
142899
143891
|
import * as fs38 from "fs/promises";
|
|
142900
143892
|
import * as path47 from "path";
|
|
142901
|
-
function
|
|
143893
|
+
function asRecord33(value) {
|
|
142902
143894
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142903
143895
|
return value;
|
|
142904
143896
|
}
|
|
@@ -142913,7 +143905,7 @@ function round27(value) {
|
|
|
142913
143905
|
}
|
|
142914
143906
|
function asRows2(value) {
|
|
142915
143907
|
if (Array.isArray(value)) {
|
|
142916
|
-
return value.filter((r) =>
|
|
143908
|
+
return value.filter((r) => asRecord33(r) !== null);
|
|
142917
143909
|
}
|
|
142918
143910
|
return [];
|
|
142919
143911
|
}
|
|
@@ -142995,17 +143987,17 @@ function campaignStatusDisplay(status) {
|
|
|
142995
143987
|
return status == null || status === "" ? "\u2014" : String(status);
|
|
142996
143988
|
}
|
|
142997
143989
|
function mapDeviceRows(payload) {
|
|
142998
|
-
const obj =
|
|
143990
|
+
const obj = asRecord33(payload);
|
|
142999
143991
|
return asRows2(obj?.devices).map((r) => pickMetrics(r, ["deviceType"]));
|
|
143000
143992
|
}
|
|
143001
143993
|
function mapGeographicRows(payload) {
|
|
143002
|
-
const obj =
|
|
143994
|
+
const obj = asRecord33(payload);
|
|
143003
143995
|
return asRows2(obj?.countries).map(
|
|
143004
143996
|
(r) => pickMetrics(r, ["countryOrRegion", "countryCriteriaId", "regionName", "cityName"])
|
|
143005
143997
|
);
|
|
143006
143998
|
}
|
|
143007
143999
|
function mapAudienceLabelRows(payload, labelKey) {
|
|
143008
|
-
const obj =
|
|
144000
|
+
const obj = asRecord33(payload);
|
|
143009
144001
|
return asRows2(obj?.audience).map((r) => {
|
|
143010
144002
|
const m = pickMetrics(r, ["audience", "bidModifier"]);
|
|
143011
144003
|
m[labelKey] = r.audience;
|
|
@@ -143013,7 +144005,7 @@ function mapAudienceLabelRows(payload, labelKey) {
|
|
|
143013
144005
|
});
|
|
143014
144006
|
}
|
|
143015
144007
|
function mapAudienceFromMerged(payload) {
|
|
143016
|
-
const data =
|
|
144008
|
+
const data = asRecord33(asRecord33(payload)?.data);
|
|
143017
144009
|
return {
|
|
143018
144010
|
audienceAge: mapAudienceLabelRows(data?.ageAudience, "ageRange"),
|
|
143019
144011
|
audienceGender: mapAudienceLabelRows(data?.genderAudience, "gender")
|
|
@@ -143108,7 +144100,7 @@ function mapSearchTermRows(payload) {
|
|
|
143108
144100
|
}
|
|
143109
144101
|
async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
143110
144102
|
const absDir = path47.resolve(snapshotDir);
|
|
143111
|
-
const metaAccountId = typeof
|
|
144103
|
+
const metaAccountId = typeof asRecord33(payload.meta)?.accountId === "string" ? asRecord33(payload.meta)?.accountId : void 0;
|
|
143112
144104
|
const manifest = await readManifest5(snapshotDir, metaAccountId);
|
|
143113
144105
|
if (!manifest) {
|
|
143114
144106
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 report-manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C bing-analysis --json-out`);
|
|
@@ -143118,9 +144110,9 @@ async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
143118
144110
|
const data = await loadSectionPayload(absDir, art.file);
|
|
143119
144111
|
if (data != null) sectionMap.set(art.section, data);
|
|
143120
144112
|
}
|
|
143121
|
-
const overview =
|
|
143122
|
-
const currentPeriod =
|
|
143123
|
-
const previousPeriod =
|
|
144113
|
+
const overview = asRecord33(sectionMap.get("overview"));
|
|
144114
|
+
const currentPeriod = asRecord33(overview?.currentPeriod);
|
|
144115
|
+
const previousPeriod = asRecord33(overview?.previousPeriod);
|
|
143124
144116
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
143125
144117
|
const campaignRows3 = mapCampaignRows2(sectionMap.get("campaigns"));
|
|
143126
144118
|
const campaignKpis = sumBingCampaignRowsToPeriodKpis(campaignRows3);
|
|
@@ -143247,7 +144239,7 @@ function textLen5(value) {
|
|
|
143247
144239
|
function asArray6(value) {
|
|
143248
144240
|
return Array.isArray(value) ? value : [];
|
|
143249
144241
|
}
|
|
143250
|
-
function
|
|
144242
|
+
function asRecord34(value) {
|
|
143251
144243
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
143252
144244
|
return value;
|
|
143253
144245
|
}
|
|
@@ -143258,9 +144250,9 @@ function pushMissing5(missing, id, chapter, dimension, hint) {
|
|
|
143258
144250
|
}
|
|
143259
144251
|
function validateBingPeriodReportContent(data) {
|
|
143260
144252
|
const missing = [];
|
|
143261
|
-
const meta =
|
|
143262
|
-
const narrative =
|
|
143263
|
-
const sections =
|
|
144253
|
+
const meta = asRecord34(data.meta) ?? {};
|
|
144254
|
+
const narrative = asRecord34(data.narrative) ?? {};
|
|
144255
|
+
const sections = asRecord34(narrative.sections) ?? {};
|
|
143264
144256
|
if (!textLen5(meta.accountId)) {
|
|
143265
144257
|
pushMissing5(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
143266
144258
|
}
|
|
@@ -143277,7 +144269,7 @@ function validateBingPeriodReportContent(data) {
|
|
|
143277
144269
|
const sectionAnalysisCounts = {};
|
|
143278
144270
|
const sectionSuggestionCounts = {};
|
|
143279
144271
|
for (const key of SECTION_KEYS2) {
|
|
143280
|
-
const section =
|
|
144272
|
+
const section = asRecord34(sections[key]);
|
|
143281
144273
|
const analysis = asArray6(section?.analysis).filter((s) => textLen5(s) > 0);
|
|
143282
144274
|
const suggestions = asArray6(section?.suggestions).filter((s) => textLen5(s) > 0);
|
|
143283
144275
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -143303,7 +144295,7 @@ function validateBingPeriodReportContent(data) {
|
|
|
143303
144295
|
}
|
|
143304
144296
|
const recommendations = asArray6(narrative.recommendations).filter((r) => {
|
|
143305
144297
|
if (typeof r === "string") return textLen5(r) > 0;
|
|
143306
|
-
const rec =
|
|
144298
|
+
const rec = asRecord34(r);
|
|
143307
144299
|
return textLen5(rec?.content) > 0;
|
|
143308
144300
|
});
|
|
143309
144301
|
if (recommendations.length < 3) {
|
|
@@ -143367,7 +144359,7 @@ async function readJsonFile6(filePath) {
|
|
|
143367
144359
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
143368
144360
|
}
|
|
143369
144361
|
}
|
|
143370
|
-
function
|
|
144362
|
+
function asRecord35(value) {
|
|
143371
144363
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
143372
144364
|
return value;
|
|
143373
144365
|
}
|
|
@@ -143383,7 +144375,7 @@ function injectReportData5(html, payload, lang) {
|
|
|
143383
144375
|
}
|
|
143384
144376
|
async function runBingAnalysisRender(opts) {
|
|
143385
144377
|
const dataPath = path48.resolve(opts.dataFile);
|
|
143386
|
-
let data =
|
|
144378
|
+
let data = asRecord35(await readJsonFile6(dataPath));
|
|
143387
144379
|
if (opts.snapshotDir?.trim()) {
|
|
143388
144380
|
data = await mergeBingAnalysisSnapshotIntoReport(data, path48.resolve(opts.snapshotDir));
|
|
143389
144381
|
}
|
|
@@ -143650,8 +144642,8 @@ function resolveSectionList4(sections, exclude) {
|
|
|
143650
144642
|
}
|
|
143651
144643
|
const includeCanonical = new Set(include.map((n) => normalizeSectionToken3(n)).filter(Boolean));
|
|
143652
144644
|
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken3(n)).filter(Boolean));
|
|
143653
|
-
const
|
|
143654
|
-
return
|
|
144645
|
+
const base2 = include.length > 0 ? TIKTOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...TIKTOK_SECTIONS];
|
|
144646
|
+
return base2.filter((s) => !dropCanonical.has(s.name));
|
|
143655
144647
|
}
|
|
143656
144648
|
|
|
143657
144649
|
// src/commands/tiktok-analysis/fetch.ts
|
|
@@ -143730,12 +144722,12 @@ function endpointHintForTikTokSection(def) {
|
|
|
143730
144722
|
// src/commands/tiktok-analysis/auxiliary.ts
|
|
143731
144723
|
init_auth();
|
|
143732
144724
|
async function fetchTikTokAreacode(config, verbose) {
|
|
143733
|
-
const
|
|
143734
|
-
if (!
|
|
144725
|
+
const base2 = config.mainApiUrl?.trim();
|
|
144726
|
+
if (!base2) {
|
|
143735
144727
|
console.error("\n\u274C \u65E0\u6CD5\u63A8\u5BFC mainApiUrl\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
|
|
143736
144728
|
process.exit(1);
|
|
143737
144729
|
}
|
|
143738
|
-
const url = `${
|
|
144730
|
+
const url = `${base2}/query/media-account/tiktok/TikTokAreacode/Read`;
|
|
143739
144731
|
return apiFetch2(url, config, {}, verbose);
|
|
143740
144732
|
}
|
|
143741
144733
|
async function fetchTikTokInterestList(config, account, verbose) {
|
|
@@ -143859,7 +144851,7 @@ import { fileURLToPath as fileURLToPath13 } from "url";
|
|
|
143859
144851
|
// src/commands/tiktok-analysis/merge-snapshot.ts
|
|
143860
144852
|
import * as fs40 from "fs/promises";
|
|
143861
144853
|
import * as path50 from "path";
|
|
143862
|
-
function
|
|
144854
|
+
function asRecord36(value) {
|
|
143863
144855
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
143864
144856
|
return value;
|
|
143865
144857
|
}
|
|
@@ -144037,7 +145029,7 @@ function mapAudienceMerged(payload) {
|
|
|
144037
145029
|
}
|
|
144038
145030
|
async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
144039
145031
|
const absDir = path50.resolve(snapshotDir);
|
|
144040
|
-
const metaAccountId = typeof
|
|
145032
|
+
const metaAccountId = typeof asRecord36(payload.meta)?.accountId === "string" ? asRecord36(payload.meta)?.accountId : void 0;
|
|
144041
145033
|
const manifest = await readManifest6(snapshotDir, metaAccountId);
|
|
144042
145034
|
if (!manifest) {
|
|
144043
145035
|
throw new Error(
|
|
@@ -144151,7 +145143,7 @@ function textLen6(value) {
|
|
|
144151
145143
|
function asArray7(value) {
|
|
144152
145144
|
return Array.isArray(value) ? value : [];
|
|
144153
145145
|
}
|
|
144154
|
-
function
|
|
145146
|
+
function asRecord37(value) {
|
|
144155
145147
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
144156
145148
|
return value;
|
|
144157
145149
|
}
|
|
@@ -144162,9 +145154,9 @@ function pushMissing6(missing, id, chapter, dimension, hint) {
|
|
|
144162
145154
|
}
|
|
144163
145155
|
function validateTikTokPeriodReportContent(data) {
|
|
144164
145156
|
const missing = [];
|
|
144165
|
-
const meta =
|
|
144166
|
-
const narrative =
|
|
144167
|
-
const sections =
|
|
145157
|
+
const meta = asRecord37(data.meta) ?? {};
|
|
145158
|
+
const narrative = asRecord37(data.narrative) ?? {};
|
|
145159
|
+
const sections = asRecord37(narrative.sections) ?? {};
|
|
144168
145160
|
if (!textLen6(meta.accountId)) {
|
|
144169
145161
|
pushMissing6(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
144170
145162
|
}
|
|
@@ -144181,7 +145173,7 @@ function validateTikTokPeriodReportContent(data) {
|
|
|
144181
145173
|
const sectionAnalysisCounts = {};
|
|
144182
145174
|
const sectionSuggestionCounts = {};
|
|
144183
145175
|
for (const key of SECTION_KEYS3) {
|
|
144184
|
-
const section =
|
|
145176
|
+
const section = asRecord37(sections[key]);
|
|
144185
145177
|
const analysis = asArray7(section?.analysis).filter((s) => textLen6(s) > 0);
|
|
144186
145178
|
const suggestions = asArray7(section?.suggestions).filter((s) => textLen6(s) > 0);
|
|
144187
145179
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -144207,7 +145199,7 @@ function validateTikTokPeriodReportContent(data) {
|
|
|
144207
145199
|
}
|
|
144208
145200
|
const recommendations = asArray7(narrative.recommendations).filter((r) => {
|
|
144209
145201
|
if (typeof r === "string") return textLen6(r) > 0;
|
|
144210
|
-
const rec =
|
|
145202
|
+
const rec = asRecord37(r);
|
|
144211
145203
|
return textLen6(rec?.content) > 0;
|
|
144212
145204
|
});
|
|
144213
145205
|
if (recommendations.length < 3) {
|
|
@@ -144271,7 +145263,7 @@ async function readJsonFile7(filePath) {
|
|
|
144271
145263
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
144272
145264
|
}
|
|
144273
145265
|
}
|
|
144274
|
-
function
|
|
145266
|
+
function asRecord38(value) {
|
|
144275
145267
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
144276
145268
|
return value;
|
|
144277
145269
|
}
|
|
@@ -144287,7 +145279,7 @@ function injectReportData6(html, payload, lang) {
|
|
|
144287
145279
|
}
|
|
144288
145280
|
async function runTikTokAnalysisRender(opts) {
|
|
144289
145281
|
const dataPath = path51.resolve(opts.dataFile);
|
|
144290
|
-
let data =
|
|
145282
|
+
let data = asRecord38(await readJsonFile7(dataPath));
|
|
144291
145283
|
if (opts.snapshotDir?.trim()) {
|
|
144292
145284
|
data = await mergeTikTokAnalysisSnapshotIntoReport(data, path51.resolve(opts.snapshotDir));
|
|
144293
145285
|
}
|
|
@@ -144571,16 +145563,16 @@ async function fetchTikTokPagedList(config, buildUrl, opts) {
|
|
|
144571
145563
|
}
|
|
144572
145564
|
return { items: items3, totalNumber, pagesFetched };
|
|
144573
145565
|
}
|
|
144574
|
-
function advertiserInfoUrl(
|
|
145566
|
+
function advertiserInfoUrl(base2, advertiserIds) {
|
|
144575
145567
|
const q = new URLSearchParams({ advertiserids: advertiserIds });
|
|
144576
|
-
return `${
|
|
145568
|
+
return `${base2}/tiktokadvquery/GetAdvertiserInfo/${TIKTOK_MEDIA_TYPE}?${q}`;
|
|
144577
145569
|
}
|
|
144578
|
-
function entityListUrl(
|
|
145570
|
+
function entityListUrl(base2, kind, query) {
|
|
144579
145571
|
const prefix = kind === "campaign" ? "tiktokcampaignmanagement/campaign" : kind === "adgroup" ? "tiktokadgroupmanagement/adgroup" : "tiktokadmanagement/ad";
|
|
144580
|
-
return `${
|
|
145572
|
+
return `${base2}/${prefix}/${TIKTOK_MEDIA_TYPE}?${query}`;
|
|
144581
145573
|
}
|
|
144582
|
-
function officialReportUrl(
|
|
144583
|
-
return `${
|
|
145574
|
+
function officialReportUrl(base2, query) {
|
|
145575
|
+
return `${base2}/query/report/${TIKTOK_MEDIA_TYPE}/search?${query}`;
|
|
144584
145576
|
}
|
|
144585
145577
|
function buildEntityQuery2(opts) {
|
|
144586
145578
|
const q = new URLSearchParams({
|
|
@@ -144609,16 +145601,16 @@ function buildOfficialReportQuery(opts) {
|
|
|
144609
145601
|
});
|
|
144610
145602
|
}
|
|
144611
145603
|
async function fetchAdvertiserInfoList(config, advertiserId, verbose) {
|
|
144612
|
-
const
|
|
144613
|
-
const raw = await apiFetch2(advertiserInfoUrl(
|
|
145604
|
+
const base2 = requireTikTokApiUrl(config);
|
|
145605
|
+
const raw = await apiFetch2(advertiserInfoUrl(base2, advertiserId), config, {}, verbose);
|
|
144614
145606
|
return unwrapTikTokEnvelope(raw, "GetAdvertiserInfo").list;
|
|
144615
145607
|
}
|
|
144616
145608
|
async function fetchCampaignEntities(config, opts) {
|
|
144617
|
-
const
|
|
145609
|
+
const base2 = requireTikTokApiUrl(config);
|
|
144618
145610
|
return fetchTikTokPagedList(
|
|
144619
145611
|
config,
|
|
144620
145612
|
(pageNum, pageSize) => entityListUrl(
|
|
144621
|
-
|
|
145613
|
+
base2,
|
|
144622
145614
|
"campaign",
|
|
144623
145615
|
buildEntityQuery2({
|
|
144624
145616
|
advertiserId: opts.advertiserId,
|
|
@@ -144631,11 +145623,11 @@ async function fetchCampaignEntities(config, opts) {
|
|
|
144631
145623
|
);
|
|
144632
145624
|
}
|
|
144633
145625
|
async function fetchAdGroupEntities(config, opts) {
|
|
144634
|
-
const
|
|
145626
|
+
const base2 = requireTikTokApiUrl(config);
|
|
144635
145627
|
return fetchTikTokPagedList(
|
|
144636
145628
|
config,
|
|
144637
145629
|
(pageNum, pageSize) => entityListUrl(
|
|
144638
|
-
|
|
145630
|
+
base2,
|
|
144639
145631
|
"adgroup",
|
|
144640
145632
|
buildEntityQuery2({
|
|
144641
145633
|
advertiserId: opts.advertiserId,
|
|
@@ -144649,11 +145641,11 @@ async function fetchAdGroupEntities(config, opts) {
|
|
|
144649
145641
|
);
|
|
144650
145642
|
}
|
|
144651
145643
|
async function fetchAdEntities(config, opts) {
|
|
144652
|
-
const
|
|
145644
|
+
const base2 = requireTikTokApiUrl(config);
|
|
144653
145645
|
return fetchTikTokPagedList(
|
|
144654
145646
|
config,
|
|
144655
145647
|
(pageNum, pageSize) => entityListUrl(
|
|
144656
|
-
|
|
145648
|
+
base2,
|
|
144657
145649
|
"ad",
|
|
144658
145650
|
buildEntityQuery2({
|
|
144659
145651
|
advertiserId: opts.advertiserId,
|
|
@@ -144670,11 +145662,11 @@ async function fetchAdEntities(config, opts) {
|
|
|
144670
145662
|
}
|
|
144671
145663
|
async function fetchOfficialReport(config, opts) {
|
|
144672
145664
|
const metrics = expandTikTokOfficialReportMetrics(opts.metrics);
|
|
144673
|
-
const
|
|
145665
|
+
const base2 = requireTikTokApiUrl(config);
|
|
144674
145666
|
const raw = await fetchTikTokPagedList(
|
|
144675
145667
|
config,
|
|
144676
145668
|
(pageNum, pageSize) => officialReportUrl(
|
|
144677
|
-
|
|
145669
|
+
base2,
|
|
144678
145670
|
buildOfficialReportQuery({
|
|
144679
145671
|
advertiserId: opts.advertiserId,
|
|
144680
145672
|
startDate: opts.startDate,
|
|
@@ -144697,7 +145689,7 @@ async function fetchOfficialReport(config, opts) {
|
|
|
144697
145689
|
}
|
|
144698
145690
|
|
|
144699
145691
|
// src/commands/tiktok-analysis/entities.ts
|
|
144700
|
-
function
|
|
145692
|
+
function fail3(label2, err) {
|
|
144701
145693
|
const msg = err instanceof Error ? err.message : String(err);
|
|
144702
145694
|
console.error(`
|
|
144703
145695
|
\u274C ${label2}\uFF1A${msg}
|
|
@@ -144711,7 +145703,7 @@ async function runTikTokAccountStatus(opts) {
|
|
|
144711
145703
|
try {
|
|
144712
145704
|
rows = await fetchAdvertiserInfoList(config, id, !!opts.verbose);
|
|
144713
145705
|
} catch (err) {
|
|
144714
|
-
|
|
145706
|
+
fail3("\u67E5\u8BE2 TikTok \u8D26\u6237\u4FE1\u606F\u5931\u8D25\uFF08\u987B\u62E5\u6709\u8BE5\u5E7F\u544A\u4E3B\uFF0C\u5217\u8868\u53EF\u89C1\u4E0D\u591F\uFF09", err);
|
|
144715
145707
|
}
|
|
144716
145708
|
const payload = wrapListJson({
|
|
144717
145709
|
page: 1,
|
|
@@ -144766,7 +145758,7 @@ async function runTikTokCampaignEntities(opts) {
|
|
|
144766
145758
|
verbose: !!opts.verbose
|
|
144767
145759
|
});
|
|
144768
145760
|
} catch (err) {
|
|
144769
|
-
|
|
145761
|
+
fail3("\u67E5\u8BE2 TikTok \u7CFB\u5217\u5931\u8D25", err);
|
|
144770
145762
|
}
|
|
144771
145763
|
const payload = wrapListJson({
|
|
144772
145764
|
page: 1,
|
|
@@ -144828,7 +145820,7 @@ async function runTikTokAdGroupEntities(opts) {
|
|
|
144828
145820
|
verbose: !!opts.verbose
|
|
144829
145821
|
});
|
|
144830
145822
|
} catch (err) {
|
|
144831
|
-
|
|
145823
|
+
fail3("\u67E5\u8BE2 TikTok \u5E7F\u544A\u7EC4\u5931\u8D25", err);
|
|
144832
145824
|
}
|
|
144833
145825
|
const payload = wrapListJson({
|
|
144834
145826
|
page: 1,
|
|
@@ -144895,7 +145887,7 @@ async function runTikTokAdEntities(opts) {
|
|
|
144895
145887
|
verbose: !!opts.verbose
|
|
144896
145888
|
});
|
|
144897
145889
|
} catch (err) {
|
|
144898
|
-
|
|
145890
|
+
fail3("\u67E5\u8BE2 TikTok \u5E7F\u544A\u5931\u8D25", err);
|
|
144899
145891
|
}
|
|
144900
145892
|
const payload = wrapListJson({
|
|
144901
145893
|
page: 1,
|
|
@@ -144969,7 +145961,7 @@ async function runTikTokOfficialReport(opts) {
|
|
|
144969
145961
|
verbose: !!opts.verbose
|
|
144970
145962
|
});
|
|
144971
145963
|
} catch (err) {
|
|
144972
|
-
|
|
145964
|
+
fail3("\u67E5\u8BE2 TikTok \u5B98\u65B9\u62A5\u8868\u5931\u8D25", err);
|
|
144973
145965
|
}
|
|
144974
145966
|
const totals = summarizeTikTokOfficialReport(result.items);
|
|
144975
145967
|
const payload = {
|
|
@@ -145346,14 +146338,14 @@ function assertYandexAccountId(id) {
|
|
|
145346
146338
|
return t;
|
|
145347
146339
|
}
|
|
145348
146340
|
function requireYandexApi(config) {
|
|
145349
|
-
const
|
|
145350
|
-
if (!
|
|
146341
|
+
const base2 = (config.yandexApiUrl ?? "").replace(/\/$/, "");
|
|
146342
|
+
if (!base2) {
|
|
145351
146343
|
console.error(
|
|
145352
146344
|
"\n\u274C \u672A\u627E\u5230 Yandex API \u5730\u5740\uFF08yandexApiUrl\uFF09\u3002\n \u4ECE TSO API \u5730\u5740\u81EA\u52A8\u63A8\u5BFC\uFF0C\u4E5F\u53EF\u901A\u8FC7\u73AF\u5883\u53D8\u91CF SILUZAN_YANDEX_API \u8986\u76D6\u3002\n"
|
|
145353
146345
|
);
|
|
145354
146346
|
process.exit(1);
|
|
145355
146347
|
}
|
|
145356
|
-
return
|
|
146348
|
+
return base2;
|
|
145357
146349
|
}
|
|
145358
146350
|
function resolveYandexLimit(raw) {
|
|
145359
146351
|
const n = typeof raw === "number" ? raw : parseInt(String(raw ?? ""), 10);
|
|
@@ -145361,9 +146353,9 @@ function resolveYandexLimit(raw) {
|
|
|
145361
146353
|
return Math.min(Math.floor(n), YANDEX_KEYWORD_LIMIT_MAX);
|
|
145362
146354
|
}
|
|
145363
146355
|
function analysisUrl(yandexApiUrl, pathSeg, query) {
|
|
145364
|
-
const
|
|
146356
|
+
const base2 = yandexApiUrl.replace(/\/$/, "");
|
|
145365
146357
|
const q = query ? query.startsWith("?") ? query : `?${query}` : "";
|
|
145366
|
-
return `${
|
|
146358
|
+
return `${base2}/yandex-analysis/${pathSeg}${q}`;
|
|
145367
146359
|
}
|
|
145368
146360
|
async function fetchYandexSectionPayload(def, opts, config, accountId) {
|
|
145369
146361
|
const { startDate, endDate } = resolveYandexDateRange(opts.start, opts.end);
|
|
@@ -145389,7 +146381,7 @@ function endpointHintForYandexSection(def) {
|
|
|
145389
146381
|
// src/commands/yandex-analysis/merge-snapshot.ts
|
|
145390
146382
|
import * as fs42 from "fs/promises";
|
|
145391
146383
|
import * as path52 from "path";
|
|
145392
|
-
function
|
|
146384
|
+
function asRecord39(value) {
|
|
145393
146385
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
145394
146386
|
return value;
|
|
145395
146387
|
}
|
|
@@ -145409,11 +146401,11 @@ function normalizeYandexRoi(value) {
|
|
|
145409
146401
|
return r === -1 ? void 0 : r;
|
|
145410
146402
|
}
|
|
145411
146403
|
function asItems(payload) {
|
|
145412
|
-
const obj =
|
|
146404
|
+
const obj = asRecord39(payload);
|
|
145413
146405
|
if (!obj) return [];
|
|
145414
146406
|
const items3 = obj.items;
|
|
145415
146407
|
if (!Array.isArray(items3)) return [];
|
|
145416
|
-
return items3.filter((r) =>
|
|
146408
|
+
return items3.filter((r) => asRecord39(r) !== null);
|
|
145417
146409
|
}
|
|
145418
146410
|
function pickMetrics2(row, extras = []) {
|
|
145419
146411
|
const spend = num12(row.spend) ?? 0;
|
|
@@ -145549,7 +146541,7 @@ function aggregateDailyToWeekly(dailyRows) {
|
|
|
145549
146541
|
});
|
|
145550
146542
|
}
|
|
145551
146543
|
function prepareYandexDailyPayload(payload) {
|
|
145552
|
-
const obj =
|
|
146544
|
+
const obj = asRecord39(payload) ?? {};
|
|
145553
146545
|
const items3 = asItems(obj).map((r) => {
|
|
145554
146546
|
const date = normalizeYandexDailyDate(r.date);
|
|
145555
146547
|
return date ? { ...r, date } : r;
|
|
@@ -145618,7 +146610,7 @@ function mapDeviceRows2(payload) {
|
|
|
145618
146610
|
}
|
|
145619
146611
|
async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
145620
146612
|
const absDir = path52.resolve(snapshotDir);
|
|
145621
|
-
const metaAccountId = typeof
|
|
146613
|
+
const metaAccountId = typeof asRecord39(payload.meta)?.accountId === "string" ? asRecord39(payload.meta)?.accountId : void 0;
|
|
145622
146614
|
const manifest = await readManifest7(snapshotDir, metaAccountId);
|
|
145623
146615
|
if (!manifest) {
|
|
145624
146616
|
throw new Error(
|
|
@@ -145630,8 +146622,8 @@ async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
145630
146622
|
const data = await loadSectionPayload3(absDir, art.file);
|
|
145631
146623
|
if (data != null) sectionMap.set(art.section, data);
|
|
145632
146624
|
}
|
|
145633
|
-
const overview =
|
|
145634
|
-
const totals =
|
|
146625
|
+
const overview = asRecord39(sectionMap.get("overview"));
|
|
146626
|
+
const totals = asRecord39(overview?.totals);
|
|
145635
146627
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
145636
146628
|
const meta = { ...payload.meta ?? {} };
|
|
145637
146629
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
@@ -145724,8 +146716,8 @@ function resolveSectionList5(sections, exclude) {
|
|
|
145724
146716
|
}
|
|
145725
146717
|
const includeCanonical = new Set(include.map((n) => normalizeSectionToken4(n)).filter(Boolean));
|
|
145726
146718
|
const dropCanonical = new Set(dropRaw.map((n) => normalizeSectionToken4(n)).filter(Boolean));
|
|
145727
|
-
const
|
|
145728
|
-
return
|
|
146719
|
+
const base2 = include.length > 0 ? YANDEX_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...YANDEX_SECTIONS];
|
|
146720
|
+
return base2.filter((s) => !dropCanonical.has(s.name));
|
|
145729
146721
|
}
|
|
145730
146722
|
|
|
145731
146723
|
// src/commands/yandex-analysis/run-batch.ts
|
|
@@ -145853,7 +146845,7 @@ function textLen7(value) {
|
|
|
145853
146845
|
function asArray8(value) {
|
|
145854
146846
|
return Array.isArray(value) ? value : [];
|
|
145855
146847
|
}
|
|
145856
|
-
function
|
|
146848
|
+
function asRecord40(value) {
|
|
145857
146849
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
145858
146850
|
return value;
|
|
145859
146851
|
}
|
|
@@ -145866,23 +146858,23 @@ function normKey(value) {
|
|
|
145866
146858
|
return String(value ?? "").trim().toLowerCase();
|
|
145867
146859
|
}
|
|
145868
146860
|
function insightMatchRecord(item) {
|
|
145869
|
-
const rec =
|
|
146861
|
+
const rec = asRecord40(item);
|
|
145870
146862
|
if (!rec) return {};
|
|
145871
|
-
return
|
|
146863
|
+
return asRecord40(rec.match) ?? rec;
|
|
145872
146864
|
}
|
|
145873
146865
|
function findRowInsight(list, primaryField, primaryValue, row) {
|
|
145874
146866
|
const target = normKey(primaryValue);
|
|
145875
146867
|
if (!target) return null;
|
|
145876
146868
|
const candidates = list.filter((it) => normKey(insightMatchRecord(it)[primaryField]) === target);
|
|
145877
|
-
if (candidates.length <= 1) return
|
|
146869
|
+
if (candidates.length <= 1) return asRecord40(candidates[0]);
|
|
145878
146870
|
const campaign = normKey(row.campaignName);
|
|
145879
146871
|
const narrowed = candidates.find(
|
|
145880
146872
|
(it) => normKey(insightMatchRecord(it).campaignName) === campaign
|
|
145881
146873
|
);
|
|
145882
|
-
return
|
|
146874
|
+
return asRecord40(narrowed ?? candidates[0]);
|
|
145883
146875
|
}
|
|
145884
146876
|
function topRowsBySpend(rows, limit) {
|
|
145885
|
-
return rows.map((r) =>
|
|
146877
|
+
return rows.map((r) => asRecord40(r)).filter((r) => r != null).sort((a, b) => (Number(b.spend) || 0) - (Number(a.spend) || 0)).slice(0, limit);
|
|
145886
146878
|
}
|
|
145887
146879
|
var ROW_INSIGHT_SPECS = [
|
|
145888
146880
|
{
|
|
@@ -145897,17 +146889,17 @@ var ROW_INSIGHT_SPECS = [
|
|
|
145897
146889
|
];
|
|
145898
146890
|
function validateYandexPeriodReportContent(data) {
|
|
145899
146891
|
const missing = [];
|
|
145900
|
-
const meta =
|
|
145901
|
-
const narrative =
|
|
145902
|
-
const sections =
|
|
145903
|
-
const kpis =
|
|
146892
|
+
const meta = asRecord40(data.meta) ?? {};
|
|
146893
|
+
const narrative = asRecord40(data.narrative) ?? {};
|
|
146894
|
+
const sections = asRecord40(narrative.sections) ?? {};
|
|
146895
|
+
const kpis = asRecord40(data.kpis) ?? {};
|
|
145904
146896
|
if (!textLen7(meta.accountId)) {
|
|
145905
146897
|
pushMissing7(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
145906
146898
|
}
|
|
145907
146899
|
const sectionAnalysisCounts = {};
|
|
145908
146900
|
const sectionSuggestionCounts = {};
|
|
145909
146901
|
for (const key of SECTION_KEYS4) {
|
|
145910
|
-
const section =
|
|
146902
|
+
const section = asRecord40(sections[key]);
|
|
145911
146903
|
const analysis = asArray8(section?.analysis).filter((s) => textLen7(s) > 0);
|
|
145912
146904
|
const suggestions = asArray8(section?.suggestions).filter((s) => textLen7(s) > 0);
|
|
145913
146905
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -145933,7 +146925,7 @@ function validateYandexPeriodReportContent(data) {
|
|
|
145933
146925
|
}
|
|
145934
146926
|
const recommendations = asArray8(narrative.recommendations).filter((r) => {
|
|
145935
146927
|
if (typeof r === "string") return textLen7(r) > 0;
|
|
145936
|
-
const rec =
|
|
146928
|
+
const rec = asRecord40(r);
|
|
145937
146929
|
return textLen7(rec?.content) > 0 || textLen7(rec?.text) > 0;
|
|
145938
146930
|
});
|
|
145939
146931
|
if (recommendations.length < 3) {
|
|
@@ -145955,8 +146947,8 @@ function validateYandexPeriodReportContent(data) {
|
|
|
145955
146947
|
"\u81F3\u5C11\u4E00\u9879\u6709\u6548\u6570\u5B57\uFF08\u8BF7\u4F20 --snapshot-dir \u7531 CLI \u4ECE overview \u5408\u5E76\uFF09"
|
|
145956
146948
|
);
|
|
145957
146949
|
}
|
|
145958
|
-
const tables =
|
|
145959
|
-
const weeklyRows = asArray8(tables.weekly).map((r) =>
|
|
146950
|
+
const tables = asRecord40(data.tables) ?? {};
|
|
146951
|
+
const weeklyRows = asArray8(tables.weekly).map((r) => asRecord40(r)).filter((r) => r != null);
|
|
145960
146952
|
for (const [i, row] of weeklyRows.entries()) {
|
|
145961
146953
|
const label2 = `${row.weekLabel ?? ""} ${row.startDate ?? ""} ${row.endDate ?? ""}`;
|
|
145962
146954
|
if (/nan/i.test(label2) || !normalizeYandexDailyDate(row.startDate) || !normalizeYandexDailyDate(row.endDate)) {
|
|
@@ -145969,7 +146961,7 @@ function validateYandexPeriodReportContent(data) {
|
|
|
145969
146961
|
);
|
|
145970
146962
|
}
|
|
145971
146963
|
}
|
|
145972
|
-
const rowInsights =
|
|
146964
|
+
const rowInsights = asRecord40(narrative.rowInsights) ?? {};
|
|
145973
146965
|
const rowInsightCoverage = {};
|
|
145974
146966
|
for (const spec of ROW_INSIGHT_SPECS) {
|
|
145975
146967
|
const shown = topRowsBySpend(asArray8(tables[spec.tableKey]), spec.limit).filter(
|
|
@@ -146052,7 +147044,7 @@ async function readJsonFile8(filePath) {
|
|
|
146052
147044
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
146053
147045
|
}
|
|
146054
147046
|
}
|
|
146055
|
-
function
|
|
147047
|
+
function asRecord41(value) {
|
|
146056
147048
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
146057
147049
|
return value;
|
|
146058
147050
|
}
|
|
@@ -146081,7 +147073,7 @@ function injectReportData7(html, payload, lang) {
|
|
|
146081
147073
|
}
|
|
146082
147074
|
async function runYandexAnalysisRender(opts) {
|
|
146083
147075
|
const dataPath = path54.resolve(opts.dataFile);
|
|
146084
|
-
let data =
|
|
147076
|
+
let data = asRecord41(await readJsonFile8(dataPath));
|
|
146085
147077
|
if (opts.snapshotDir?.trim()) {
|
|
146086
147078
|
data = await mergeYandexAnalysisSnapshotIntoReport(data, path54.resolve(opts.snapshotDir));
|
|
146087
147079
|
}
|
|
@@ -146279,12 +147271,12 @@ function unwrapYandexItems(raw) {
|
|
|
146279
147271
|
}
|
|
146280
147272
|
return [];
|
|
146281
147273
|
}
|
|
146282
|
-
function
|
|
147274
|
+
function asRecord42(raw) {
|
|
146283
147275
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
146284
147276
|
}
|
|
146285
147277
|
|
|
146286
147278
|
// src/commands/yandex-analysis/entities.ts
|
|
146287
|
-
function
|
|
147279
|
+
function fail4(label2, err) {
|
|
146288
147280
|
const msg = err instanceof Error ? err.message : String(err);
|
|
146289
147281
|
console.error(`
|
|
146290
147282
|
\u274C ${label2}\uFF1A${msg}
|
|
@@ -146356,9 +147348,9 @@ async function runYandexAccountStatus(opts) {
|
|
|
146356
147348
|
try {
|
|
146357
147349
|
result = await fetchPath(opts, "overview", {});
|
|
146358
147350
|
} catch (err) {
|
|
146359
|
-
|
|
147351
|
+
fail4("\u67E5\u8BE2 Yandex \u8D26\u6237\u603B\u89C8\u5931\u8D25", err);
|
|
146360
147352
|
}
|
|
146361
|
-
const row =
|
|
147353
|
+
const row = asRecord42(result.raw);
|
|
146362
147354
|
await emitOrTable(opts, {
|
|
146363
147355
|
section: "yandex-account-status",
|
|
146364
147356
|
commandLabel: "yandex-analysis account-status",
|
|
@@ -146398,7 +147390,7 @@ async function runYandexCampaignEntities(opts) {
|
|
|
146398
147390
|
network: flags.network
|
|
146399
147391
|
});
|
|
146400
147392
|
} catch (err) {
|
|
146401
|
-
|
|
147393
|
+
fail4("\u67E5\u8BE2 Yandex \u7CFB\u5217\u5931\u8D25", err);
|
|
146402
147394
|
}
|
|
146403
147395
|
const items3 = unwrapYandexItems(result.raw);
|
|
146404
147396
|
await emitOrTable(opts, {
|
|
@@ -146444,7 +147436,7 @@ async function runYandexAdGroupEntities(opts) {
|
|
|
146444
147436
|
campaignId: flags.campaignId
|
|
146445
147437
|
});
|
|
146446
147438
|
} catch (err) {
|
|
146447
|
-
|
|
147439
|
+
fail4("\u67E5\u8BE2 Yandex \u5E7F\u544A\u7EC4\u5931\u8D25", err);
|
|
146448
147440
|
}
|
|
146449
147441
|
const items3 = unwrapYandexItems(result.raw);
|
|
146450
147442
|
await emitOrTable(opts, {
|
|
@@ -146494,7 +147486,7 @@ async function runYandexAdEntities(opts) {
|
|
|
146494
147486
|
adGroupId: flags.adGroupId
|
|
146495
147487
|
});
|
|
146496
147488
|
} catch (err) {
|
|
146497
|
-
|
|
147489
|
+
fail4("\u67E5\u8BE2 Yandex \u5E7F\u544A\u5931\u8D25", err);
|
|
146498
147490
|
}
|
|
146499
147491
|
const items3 = unwrapYandexItems(result.raw);
|
|
146500
147492
|
const filtered = flags.status != null;
|
|
@@ -146546,7 +147538,7 @@ async function runYandexInsights(opts) {
|
|
|
146546
147538
|
adGroupId: flags.adGroupId
|
|
146547
147539
|
});
|
|
146548
147540
|
} catch (err) {
|
|
146549
|
-
|
|
147541
|
+
fail4("\u67E5\u8BE2 Yandex insights \u5931\u8D25", err);
|
|
146550
147542
|
}
|
|
146551
147543
|
const items3 = unwrapYandexItems(result.raw);
|
|
146552
147544
|
await emitOrTable(opts, {
|