siluzan-tso-cli 1.1.34-beta.2 → 1.1.34-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +589 -267
- package/dist/skill/SKILL.md +3 -3
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/accounts/accounts.md +21 -0
- package/dist/skill/references/analytics/account-analytics.md +1 -1
- package/dist/skill/references/analytics/keyword-planner-workflows.md +25 -0
- package/dist/skill/references/core/agent-conventions.md +1 -1
- package/dist/skill/references/core/intent-routing.md +55 -3
- package/dist/skill/references/core/workflows.md +5 -5
- package/dist/skill/references/report-templates/bing-period-report.md +151 -17
- package/dist/skill/references/report-templates/okki-weekly-google-client.md +44 -39
- package/dist/skill/report-templates/bing-period-report.md +151 -17
- package/dist/skill/report-templates/okki-weekly-google-client.md +44 -39
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/dist/skill/snippets/handoff-p6-okki.md +1 -1
- package/eval/cases/ir-p8-network-diagnosis-with-url.scenario.json +36 -0
- package/eval/cases/ir-w5-keyword-planner-url-core-terms.scenario.json +35 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -258,9 +258,9 @@ var require_semver = __commonJS({
|
|
|
258
258
|
} else {
|
|
259
259
|
this.prerelease = m[4].split(".").map((id) => {
|
|
260
260
|
if (/^[0-9]+$/.test(id)) {
|
|
261
|
-
const
|
|
262
|
-
if (
|
|
263
|
-
return
|
|
261
|
+
const num6 = +id;
|
|
262
|
+
if (num6 >= 0 && num6 < MAX_SAFE_INTEGER) {
|
|
263
|
+
return num6;
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
return id;
|
|
@@ -4361,7 +4361,15 @@ async function enrichOverviewRecordWithRealtimeBalance(media, accountId, config,
|
|
|
4361
4361
|
const bal = balanceMap.get(accountId);
|
|
4362
4362
|
if (bal == null) return record;
|
|
4363
4363
|
const out = { ...record };
|
|
4364
|
-
|
|
4364
|
+
const overviewBudget = typeof out.remainingAccountBudget === "number" ? out.remainingAccountBudget : void 0;
|
|
4365
|
+
const balanceTarget = out;
|
|
4366
|
+
applyRealtimeBalanceIfNeeded(balanceTarget, bal);
|
|
4367
|
+
if (typeof balanceTarget.remainingAccountBudget === "number") {
|
|
4368
|
+
out.balance = balanceTarget.remainingAccountBudget;
|
|
4369
|
+
if (overviewBudget === 0 && bal.remainingAccountBudget != null && bal.remainingAccountBudget > 0) {
|
|
4370
|
+
out._balanceSource = "GetMediaAccountInfo";
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4365
4373
|
if (bal.name && out["accountName"] == null && out["mediaCustomerName"] == null) {
|
|
4366
4374
|
out["mediaCustomerName"] = bal.name;
|
|
4367
4375
|
}
|
|
@@ -107794,6 +107802,133 @@ async function fetchBingJson(config, url, verbose) {
|
|
|
107794
107802
|
// src/commands/report-bing-analysis/run.ts
|
|
107795
107803
|
init_auth();
|
|
107796
107804
|
init_balance();
|
|
107805
|
+
|
|
107806
|
+
// src/commands/report-bing-analysis/normalize-bing-overview.ts
|
|
107807
|
+
function num(v) {
|
|
107808
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
107809
|
+
}
|
|
107810
|
+
function roundMoney(v) {
|
|
107811
|
+
return Math.round(v * 100) / 100;
|
|
107812
|
+
}
|
|
107813
|
+
function countInclusiveCalendarDays(startDate, endDate) {
|
|
107814
|
+
const parts = (ymd) => ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
107815
|
+
const sp = parts(startDate);
|
|
107816
|
+
const ep = parts(endDate);
|
|
107817
|
+
if (sp.length !== 3 || ep.length !== 3 || sp.some((n) => !Number.isFinite(n)) || ep.some((n) => !Number.isFinite(n))) {
|
|
107818
|
+
return 0;
|
|
107819
|
+
}
|
|
107820
|
+
const s = new Date(sp[0], sp[1] - 1, sp[2]);
|
|
107821
|
+
const e = new Date(ep[0], ep[1] - 1, ep[2]);
|
|
107822
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) return 0;
|
|
107823
|
+
const msPerDay = 24 * 60 * 60 * 1e3;
|
|
107824
|
+
return Math.floor((e.getTime() - s.getTime()) / msPerDay) + 1;
|
|
107825
|
+
}
|
|
107826
|
+
function periodSpend(period) {
|
|
107827
|
+
if (!period || typeof period !== "object") return void 0;
|
|
107828
|
+
return num(period.spend);
|
|
107829
|
+
}
|
|
107830
|
+
function enrichBingOverviewDerivedMetrics(record, startDate, endDate) {
|
|
107831
|
+
const out = { ...record };
|
|
107832
|
+
const currentSpend = periodSpend(out.currentPeriod);
|
|
107833
|
+
let totalCost = num(out.totalCost);
|
|
107834
|
+
if (totalCost == null || totalCost <= 0) {
|
|
107835
|
+
if (currentSpend != null && currentSpend > 0) {
|
|
107836
|
+
out.totalCost = currentSpend;
|
|
107837
|
+
totalCost = currentSpend;
|
|
107838
|
+
}
|
|
107839
|
+
}
|
|
107840
|
+
let activeDays = num(out.activeDays);
|
|
107841
|
+
if (activeDays == null || activeDays <= 0) {
|
|
107842
|
+
const calendarDays = countInclusiveCalendarDays(startDate, endDate);
|
|
107843
|
+
if (calendarDays > 0) {
|
|
107844
|
+
out.activeDays = calendarDays;
|
|
107845
|
+
activeDays = calendarDays;
|
|
107846
|
+
}
|
|
107847
|
+
}
|
|
107848
|
+
const averageDailyCost = num(out.averageDailyCost);
|
|
107849
|
+
if ((averageDailyCost == null || averageDailyCost <= 0) && totalCost != null && totalCost > 0 && activeDays != null && activeDays > 0) {
|
|
107850
|
+
out.averageDailyCost = roundMoney(totalCost / activeDays);
|
|
107851
|
+
}
|
|
107852
|
+
return out;
|
|
107853
|
+
}
|
|
107854
|
+
function parseYmd(ymd) {
|
|
107855
|
+
const parts = ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
107856
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n))) return null;
|
|
107857
|
+
const d = new Date(parts[0], parts[1] - 1, parts[2]);
|
|
107858
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
107859
|
+
}
|
|
107860
|
+
function formatYmd(d) {
|
|
107861
|
+
const y = d.getFullYear();
|
|
107862
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
107863
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
107864
|
+
return `${y}-${m}-${day}`;
|
|
107865
|
+
}
|
|
107866
|
+
function calcBingPreviousPeriodRange(startDate, endDate) {
|
|
107867
|
+
const diffDays = countInclusiveCalendarDays(startDate, endDate);
|
|
107868
|
+
if (diffDays <= 0) return null;
|
|
107869
|
+
const start = parseYmd(startDate);
|
|
107870
|
+
if (!start) return null;
|
|
107871
|
+
const prevEnd = new Date(start);
|
|
107872
|
+
prevEnd.setDate(prevEnd.getDate() - 1);
|
|
107873
|
+
const prevStart = new Date(prevEnd);
|
|
107874
|
+
prevStart.setDate(prevStart.getDate() - (diffDays - 1));
|
|
107875
|
+
return { startDate: formatYmd(prevStart), endDate: formatYmd(prevEnd) };
|
|
107876
|
+
}
|
|
107877
|
+
function isBingPeriodBlockEmpty(period) {
|
|
107878
|
+
if (!period || typeof period !== "object") return true;
|
|
107879
|
+
const p = period;
|
|
107880
|
+
const spend = num(p.spend) ?? 0;
|
|
107881
|
+
const impressions = num(p.impressions) ?? 0;
|
|
107882
|
+
const clicks = num(p.clicks) ?? 0;
|
|
107883
|
+
return spend <= 0 && impressions <= 0 && clicks <= 0;
|
|
107884
|
+
}
|
|
107885
|
+
function syncOverviewBalanceFields(record) {
|
|
107886
|
+
const out = { ...record };
|
|
107887
|
+
const budget = num(out.remainingAccountBudget);
|
|
107888
|
+
if (budget != null && budget >= 0) {
|
|
107889
|
+
const balanceField = num(out.balance);
|
|
107890
|
+
if (balanceField == null || balanceField <= 0 && budget > 0) {
|
|
107891
|
+
out.balance = budget;
|
|
107892
|
+
}
|
|
107893
|
+
}
|
|
107894
|
+
return out;
|
|
107895
|
+
}
|
|
107896
|
+
|
|
107897
|
+
// src/commands/report-bing-analysis/run.ts
|
|
107898
|
+
async function enrichBingOverviewPreviousPeriod(config, accountId, record, startDate, endDate, verbose) {
|
|
107899
|
+
if (!isBingPeriodBlockEmpty(record.previousPeriod)) {
|
|
107900
|
+
return record;
|
|
107901
|
+
}
|
|
107902
|
+
const prevRange = calcBingPreviousPeriodRange(startDate, endDate);
|
|
107903
|
+
if (!prevRange) return record;
|
|
107904
|
+
if (bingRangeTouchesTodayOrYesterday(prevRange.startDate, prevRange.endDate)) {
|
|
107905
|
+
return record;
|
|
107906
|
+
}
|
|
107907
|
+
try {
|
|
107908
|
+
const params = new URLSearchParams({
|
|
107909
|
+
startDate: prevRange.startDate,
|
|
107910
|
+
endDate: prevRange.endDate
|
|
107911
|
+
});
|
|
107912
|
+
const url = reportingUrl2(config, accountId, "OverviewSectionData", params.toString());
|
|
107913
|
+
const prevRaw = await fetchBingJson(config, url, verbose);
|
|
107914
|
+
if (!prevRaw || typeof prevRaw !== "object" || Array.isArray(prevRaw)) {
|
|
107915
|
+
return record;
|
|
107916
|
+
}
|
|
107917
|
+
const prevRecord = prevRaw;
|
|
107918
|
+
const prevPeriod = prevRecord.currentPeriod;
|
|
107919
|
+
if (isBingPeriodBlockEmpty(prevPeriod)) {
|
|
107920
|
+
return record;
|
|
107921
|
+
}
|
|
107922
|
+
return {
|
|
107923
|
+
...record,
|
|
107924
|
+
previousPeriod: prevPeriod,
|
|
107925
|
+
previousPeriodDateRange: prevRange,
|
|
107926
|
+
_previousPeriodSource: "OverviewSectionData-second-fetch"
|
|
107927
|
+
};
|
|
107928
|
+
} catch {
|
|
107929
|
+
return record;
|
|
107930
|
+
}
|
|
107931
|
+
}
|
|
107797
107932
|
async function runReportBingOverview(opts) {
|
|
107798
107933
|
const id = assertBingAccountId(opts.account);
|
|
107799
107934
|
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
@@ -107802,13 +107937,26 @@ async function runReportBingOverview(opts) {
|
|
|
107802
107937
|
const url = reportingUrl2(config, id, "OverviewSectionData", params.toString());
|
|
107803
107938
|
try {
|
|
107804
107939
|
const raw = await fetchBingJson(config, url, !!opts.verbose);
|
|
107805
|
-
|
|
107806
|
-
|
|
107807
|
-
|
|
107808
|
-
|
|
107809
|
-
|
|
107810
|
-
|
|
107811
|
-
|
|
107940
|
+
let data = raw;
|
|
107941
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
107942
|
+
const withBalance = await enrichOverviewRecordWithRealtimeBalance(
|
|
107943
|
+
"BingV2",
|
|
107944
|
+
id,
|
|
107945
|
+
config,
|
|
107946
|
+
raw,
|
|
107947
|
+
opts.verbose
|
|
107948
|
+
);
|
|
107949
|
+
const withDerived = enrichBingOverviewDerivedMetrics(withBalance, startDate, endDate);
|
|
107950
|
+
const withPrevious = await enrichBingOverviewPreviousPeriod(
|
|
107951
|
+
config,
|
|
107952
|
+
id,
|
|
107953
|
+
withDerived,
|
|
107954
|
+
startDate,
|
|
107955
|
+
endDate,
|
|
107956
|
+
!!opts.verbose
|
|
107957
|
+
);
|
|
107958
|
+
data = syncOverviewBalanceFields(withPrevious);
|
|
107959
|
+
}
|
|
107812
107960
|
if (opts.jsonOut) {
|
|
107813
107961
|
await emitBingSnapshot(opts, "bing-overview", id, startDate, endDate, data);
|
|
107814
107962
|
return;
|
|
@@ -120851,6 +120999,309 @@ async function runAccountMe(opts) {
|
|
|
120851
120999
|
}
|
|
120852
121000
|
}
|
|
120853
121001
|
|
|
121002
|
+
// src/commands/account-manage/google-access-permission.ts
|
|
121003
|
+
init_dist();
|
|
121004
|
+
init_auth();
|
|
121005
|
+
init_cli_json_snapshot();
|
|
121006
|
+
|
|
121007
|
+
// src/commands/account-manage/google-mcc.ts
|
|
121008
|
+
init_auth();
|
|
121009
|
+
init_cli_json_snapshot();
|
|
121010
|
+
function parseMccManagerIds(input) {
|
|
121011
|
+
const normalized = input.replace(/[,,;、]/g, ";");
|
|
121012
|
+
return normalized.split(";").map((part) => {
|
|
121013
|
+
const digits = part.replace(/\D/g, "");
|
|
121014
|
+
return digits ? +digits : NaN;
|
|
121015
|
+
}).filter((n) => Number.isFinite(n) && n > 0);
|
|
121016
|
+
}
|
|
121017
|
+
var MCC_AGENT_TYPES = ["ChengGongYi", "SiGeUS", "SiGeCN", "Dee"];
|
|
121018
|
+
function googleGatewayBase(config) {
|
|
121019
|
+
const raw = (config.googleApiUrl ?? "").replace(/\/$/, "");
|
|
121020
|
+
if (!raw) {
|
|
121021
|
+
console.error("\n\u274C Google \u7F51\u5173\u5730\u5740\u672A\u80FD\u63A8\u5BFC\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
|
|
121022
|
+
process.exit(1);
|
|
121023
|
+
}
|
|
121024
|
+
const err = validateBaseUrl(raw);
|
|
121025
|
+
if (err) {
|
|
121026
|
+
console.error(`
|
|
121027
|
+
\u274C googleApiUrl \u4E0D\u5408\u6CD5\uFF1A${err}
|
|
121028
|
+
`);
|
|
121029
|
+
process.exit(1);
|
|
121030
|
+
}
|
|
121031
|
+
return raw;
|
|
121032
|
+
}
|
|
121033
|
+
function mccResponseHasSuccess(res, managerId) {
|
|
121034
|
+
if (!Array.isArray(res)) return false;
|
|
121035
|
+
return res.some((item) => String(item) === String(managerId));
|
|
121036
|
+
}
|
|
121037
|
+
async function runAccountMccBind(opts) {
|
|
121038
|
+
const config = loadConfig(opts.token);
|
|
121039
|
+
const base = googleGatewayBase(config);
|
|
121040
|
+
const managerIds = parseMccManagerIds(opts.mcc);
|
|
121041
|
+
if (managerIds.length === 0) {
|
|
121042
|
+
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
121043
|
+
process.exit(1);
|
|
121044
|
+
}
|
|
121045
|
+
if (opts.customers.length === 0) {
|
|
121046
|
+
console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
|
|
121047
|
+
process.exit(1);
|
|
121048
|
+
}
|
|
121049
|
+
const body = { agentTypes: [...MCC_AGENT_TYPES], managerIds };
|
|
121050
|
+
const results = [];
|
|
121051
|
+
for (const customerId of opts.customers) {
|
|
121052
|
+
const url = `${base}/command/media-account/${customerId}/CustomerLinks`;
|
|
121053
|
+
try {
|
|
121054
|
+
const response = await apiFetch2(
|
|
121055
|
+
url,
|
|
121056
|
+
config,
|
|
121057
|
+
{ method: "POST", body: JSON.stringify(body) },
|
|
121058
|
+
opts.verbose
|
|
121059
|
+
);
|
|
121060
|
+
results.push({ customerId, response });
|
|
121061
|
+
} catch (err) {
|
|
121062
|
+
console.error(
|
|
121063
|
+
`
|
|
121064
|
+
\u274C \u8D26\u6237 ${customerId} MCC \u7ED1\u5B9A\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
121065
|
+
`
|
|
121066
|
+
);
|
|
121067
|
+
process.exit(1);
|
|
121068
|
+
}
|
|
121069
|
+
}
|
|
121070
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
121071
|
+
section: "account-mcc-bind",
|
|
121072
|
+
commandLabel: "account mcc-bind",
|
|
121073
|
+
payload: results,
|
|
121074
|
+
idSuffix: opts.mcc
|
|
121075
|
+
})) {
|
|
121076
|
+
return;
|
|
121077
|
+
}
|
|
121078
|
+
let hasFailure = false;
|
|
121079
|
+
for (const { customerId, response } of results) {
|
|
121080
|
+
const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
|
|
121081
|
+
if (failed.length > 0) {
|
|
121082
|
+
hasFailure = true;
|
|
121083
|
+
console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
|
|
121084
|
+
} else {
|
|
121085
|
+
console.log(
|
|
121086
|
+
` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u7ED1\u5B9A\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
|
|
121087
|
+
);
|
|
121088
|
+
}
|
|
121089
|
+
}
|
|
121090
|
+
console.log(
|
|
121091
|
+
hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u7ED1\u5B9A\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u7ED1\u5B9A\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
|
|
121092
|
+
);
|
|
121093
|
+
}
|
|
121094
|
+
async function runAccountMccUnbind(opts) {
|
|
121095
|
+
const config = loadConfig(opts.token);
|
|
121096
|
+
const base = googleGatewayBase(config);
|
|
121097
|
+
const managerIds = parseMccManagerIds(opts.mcc);
|
|
121098
|
+
if (managerIds.length === 0) {
|
|
121099
|
+
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
121100
|
+
process.exit(1);
|
|
121101
|
+
}
|
|
121102
|
+
if (opts.customers.length === 0) {
|
|
121103
|
+
console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
|
|
121104
|
+
process.exit(1);
|
|
121105
|
+
}
|
|
121106
|
+
const results = [];
|
|
121107
|
+
for (const customerId of opts.customers) {
|
|
121108
|
+
const url = `${base}/command/media-account/${customerId}/CustomerUnlinks`;
|
|
121109
|
+
try {
|
|
121110
|
+
const response = await apiFetch2(
|
|
121111
|
+
url,
|
|
121112
|
+
config,
|
|
121113
|
+
{ method: "POST", body: JSON.stringify(managerIds) },
|
|
121114
|
+
opts.verbose
|
|
121115
|
+
);
|
|
121116
|
+
results.push({ customerId, response });
|
|
121117
|
+
} catch (err) {
|
|
121118
|
+
console.error(
|
|
121119
|
+
`
|
|
121120
|
+
\u274C \u8D26\u6237 ${customerId} MCC \u89E3\u7ED1\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
121121
|
+
`
|
|
121122
|
+
);
|
|
121123
|
+
process.exit(1);
|
|
121124
|
+
}
|
|
121125
|
+
}
|
|
121126
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
121127
|
+
section: "account-mcc-unbind",
|
|
121128
|
+
commandLabel: "account mcc-unbind",
|
|
121129
|
+
payload: results,
|
|
121130
|
+
idSuffix: opts.mcc
|
|
121131
|
+
})) {
|
|
121132
|
+
return;
|
|
121133
|
+
}
|
|
121134
|
+
let hasFailure = false;
|
|
121135
|
+
for (const { customerId, response } of results) {
|
|
121136
|
+
const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
|
|
121137
|
+
if (failed.length > 0) {
|
|
121138
|
+
hasFailure = true;
|
|
121139
|
+
console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u89E3\u7ED1\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
|
|
121140
|
+
} else {
|
|
121141
|
+
console.log(
|
|
121142
|
+
` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u89E3\u7ED1\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
|
|
121143
|
+
);
|
|
121144
|
+
}
|
|
121145
|
+
}
|
|
121146
|
+
console.log(
|
|
121147
|
+
hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u89E3\u7ED1\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u89E3\u7ED1\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
|
|
121148
|
+
);
|
|
121149
|
+
}
|
|
121150
|
+
|
|
121151
|
+
// src/commands/account-manage/google-access-permission.ts
|
|
121152
|
+
function assertGoogleMediaCustomerId(raw) {
|
|
121153
|
+
const id = raw.trim();
|
|
121154
|
+
if (!id) {
|
|
121155
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
121156
|
+
process.exit(1);
|
|
121157
|
+
}
|
|
121158
|
+
if (!/^\d+$/.test(id)) {
|
|
121159
|
+
console.error("\n\u274C --account \u987B\u4E3A Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09\u3002\n");
|
|
121160
|
+
process.exit(1);
|
|
121161
|
+
}
|
|
121162
|
+
return id;
|
|
121163
|
+
}
|
|
121164
|
+
function parseCustomerAccessPermissionBody(text) {
|
|
121165
|
+
const trimmed = text.trim();
|
|
121166
|
+
if (!trimmed) return null;
|
|
121167
|
+
try {
|
|
121168
|
+
const parsed = JSON.parse(trimmed);
|
|
121169
|
+
if (typeof parsed === "boolean") return parsed;
|
|
121170
|
+
if (typeof parsed === "string") return parsed;
|
|
121171
|
+
} catch {
|
|
121172
|
+
}
|
|
121173
|
+
return trimmed;
|
|
121174
|
+
}
|
|
121175
|
+
function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody) {
|
|
121176
|
+
const base = {
|
|
121177
|
+
mediaCustomerId,
|
|
121178
|
+
httpStatus,
|
|
121179
|
+
rawBody
|
|
121180
|
+
};
|
|
121181
|
+
if (httpStatus === 200) {
|
|
121182
|
+
if (rawBody === true) {
|
|
121183
|
+
return {
|
|
121184
|
+
...base,
|
|
121185
|
+
status: "accessible",
|
|
121186
|
+
accessible: true,
|
|
121187
|
+
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u53EF\u8BBF\u95EE\u8BE5 Google \u5E7F\u544A\u8D26\u6237\u3002"
|
|
121188
|
+
};
|
|
121189
|
+
}
|
|
121190
|
+
if (rawBody === false) {
|
|
121191
|
+
return {
|
|
121192
|
+
...base,
|
|
121193
|
+
status: "reauth_required",
|
|
121194
|
+
accessible: false,
|
|
121195
|
+
message: "\u8D26\u6237\u5DF2\u7ED1\u5B9A\u4F46 Google OAuth \u4E0D\u53EF\u7528\uFF0C\u9700\u91CD\u65B0\u6388\u6743\u3002",
|
|
121196
|
+
hint: "list-accounts --json-out \u53D6 ma.entityId \u540E\u6267\u884C\uFF1Asiluzan-tso account reauth -m Google --id <entityId>"
|
|
121197
|
+
};
|
|
121198
|
+
}
|
|
121199
|
+
return {
|
|
121200
|
+
...base,
|
|
121201
|
+
status: "unknown",
|
|
121202
|
+
accessible: false,
|
|
121203
|
+
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`
|
|
121204
|
+
};
|
|
121205
|
+
}
|
|
121206
|
+
if (httpStatus === 403) {
|
|
121207
|
+
const text = typeof rawBody === "string" ? rawBody : rawBody == null ? "" : String(rawBody);
|
|
121208
|
+
if (text.includes("token\u4E0D\u80FD\u4E3A\u7A7A") || text.toLowerCase().includes("token") && text.includes("\u7A7A")) {
|
|
121209
|
+
return {
|
|
121210
|
+
...base,
|
|
121211
|
+
status: "google_not_bound",
|
|
121212
|
+
accessible: false,
|
|
121213
|
+
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u672A\u7ED1\u5B9A Google \u5A92\u4F53\u6388\u6743\u3002",
|
|
121214
|
+
hint: "\u6267\u884C siluzan-tso account auth -m Google \u5B8C\u6210 OAuth \u7ED1\u5B9A\u3002"
|
|
121215
|
+
};
|
|
121216
|
+
}
|
|
121217
|
+
return {
|
|
121218
|
+
...base,
|
|
121219
|
+
status: "no_permission",
|
|
121220
|
+
accessible: false,
|
|
121221
|
+
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",
|
|
121222
|
+
hint: "\u8BF7\u786E\u8BA4 mediaCustomerId \u662F\u5426\u6B63\u786E\uFF0C\u6216\u5207\u6362\u5230\u62E5\u6709\u8BE5\u8D26\u6237\u7684\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u767B\u5F55\uFF08send-login-code + login\uFF09\u3002"
|
|
121223
|
+
};
|
|
121224
|
+
}
|
|
121225
|
+
if (httpStatus === 401) {
|
|
121226
|
+
return {
|
|
121227
|
+
...base,
|
|
121228
|
+
status: "siluzan_token_invalid",
|
|
121229
|
+
accessible: false,
|
|
121230
|
+
message: "\u4E1D\u8DEF\u8D5E\u767B\u5F55\u51ED\u636E\u65E0\u6548\u6216\u5DF2\u8FC7\u671F\u3002",
|
|
121231
|
+
hint: "\u6267\u884C send-login-code + login --phone --code \u91CD\u65B0\u767B\u5F55\uFF0C\u6216 siluzan-tso config show \u68C0\u67E5 Token\u3002"
|
|
121232
|
+
};
|
|
121233
|
+
}
|
|
121234
|
+
return {
|
|
121235
|
+
...base,
|
|
121236
|
+
status: "unknown",
|
|
121237
|
+
accessible: false,
|
|
121238
|
+
message: `\u672A\u9884\u671F\u7684 HTTP ${httpStatus}\uFF1B\u8BF7\u7528 --verbose \u67E5\u770B\u539F\u59CB\u54CD\u5E94\u3002`
|
|
121239
|
+
};
|
|
121240
|
+
}
|
|
121241
|
+
function buildCustomerAccessPermissionUrl(googleApiUrl, mediaCustomerId) {
|
|
121242
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
121243
|
+
return `${base}/query/media-account/${mediaCustomerId}/customer-access-permission`;
|
|
121244
|
+
}
|
|
121245
|
+
async function fetchGoogleCustomerAccessPermission(config, mediaCustomerId, verbose) {
|
|
121246
|
+
await ensureDataPermission(config);
|
|
121247
|
+
const url = buildCustomerAccessPermissionUrl(googleGatewayBase(config), mediaCustomerId);
|
|
121248
|
+
const res = await rawRequest(url, {
|
|
121249
|
+
method: "GET",
|
|
121250
|
+
headers: {
|
|
121251
|
+
"Content-Type": "application/json",
|
|
121252
|
+
"Accept-Language": "zh-CN",
|
|
121253
|
+
Accept: "application/json",
|
|
121254
|
+
...buildSiluzanAuthHeaders(config),
|
|
121255
|
+
Datapermission: config.dataPermission ?? ""
|
|
121256
|
+
}
|
|
121257
|
+
});
|
|
121258
|
+
if (verbose && res.text) {
|
|
121259
|
+
console.error(` [verbose] HTTP ${res.status} body: ${res.text.slice(0, 300)}`);
|
|
121260
|
+
}
|
|
121261
|
+
const rawBody = parseCustomerAccessPermissionBody(res.text);
|
|
121262
|
+
return interpretCustomerAccessPermission(mediaCustomerId, res.status, rawBody);
|
|
121263
|
+
}
|
|
121264
|
+
async function runAccountCheckAccess(opts) {
|
|
121265
|
+
const mediaCustomerId = assertGoogleMediaCustomerId(opts.account);
|
|
121266
|
+
const config = loadConfig(opts.token);
|
|
121267
|
+
const payload = await fetchGoogleCustomerAccessPermission(
|
|
121268
|
+
config,
|
|
121269
|
+
mediaCustomerId,
|
|
121270
|
+
opts.verbose
|
|
121271
|
+
);
|
|
121272
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
121273
|
+
section: "account-check-access",
|
|
121274
|
+
commandLabel: "account check-access",
|
|
121275
|
+
payload
|
|
121276
|
+
})) {
|
|
121277
|
+
if (!payload.accessible) {
|
|
121278
|
+
process.exit(1);
|
|
121279
|
+
}
|
|
121280
|
+
return;
|
|
121281
|
+
}
|
|
121282
|
+
const statusLabel = {
|
|
121283
|
+
accessible: "\u53EF\u8BBF\u95EE",
|
|
121284
|
+
reauth_required: "\u9700\u91CD\u65B0\u6388\u6743",
|
|
121285
|
+
no_permission: "\u65E0\u6743\u9650",
|
|
121286
|
+
google_not_bound: "\u672A\u7ED1\u5B9A Google",
|
|
121287
|
+
siluzan_token_invalid: "\u4E1D\u8DEF\u8D5E\u51ED\u636E\u5931\u6548",
|
|
121288
|
+
unknown: "\u672A\u77E5"
|
|
121289
|
+
};
|
|
121290
|
+
console.log(`
|
|
121291
|
+
Google \u8D26\u6237\u8BBF\u95EE\u6743\u9650\u6821\u9A8C ${mediaCustomerId}`);
|
|
121292
|
+
console.log(` \u72B6\u6001 : ${statusLabel[payload.status]} (${payload.status})`);
|
|
121293
|
+
console.log(` HTTP : ${payload.httpStatus}`);
|
|
121294
|
+
console.log(` \u53EF\u8BBF\u95EE : ${payload.accessible ? "\u662F" : "\u5426"}`);
|
|
121295
|
+
console.log(` \u8BF4\u660E : ${payload.message}`);
|
|
121296
|
+
if (payload.hint) {
|
|
121297
|
+
console.log(` \u5EFA\u8BAE : ${payload.hint}`);
|
|
121298
|
+
}
|
|
121299
|
+
console.log();
|
|
121300
|
+
if (!payload.accessible) {
|
|
121301
|
+
process.exit(1);
|
|
121302
|
+
}
|
|
121303
|
+
}
|
|
121304
|
+
|
|
120854
121305
|
// src/commands/account-manage/share.ts
|
|
120855
121306
|
init_auth();
|
|
120856
121307
|
init_cli_json_snapshot();
|
|
@@ -121075,150 +121526,6 @@ async function runAccountReauth(opts) {
|
|
|
121075
121526
|
});
|
|
121076
121527
|
}
|
|
121077
121528
|
|
|
121078
|
-
// src/commands/account-manage/google-mcc.ts
|
|
121079
|
-
init_auth();
|
|
121080
|
-
init_cli_json_snapshot();
|
|
121081
|
-
function parseMccManagerIds(input) {
|
|
121082
|
-
const normalized = input.replace(/[,,;、]/g, ";");
|
|
121083
|
-
return normalized.split(";").map((part) => {
|
|
121084
|
-
const digits = part.replace(/\D/g, "");
|
|
121085
|
-
return digits ? +digits : NaN;
|
|
121086
|
-
}).filter((n) => Number.isFinite(n) && n > 0);
|
|
121087
|
-
}
|
|
121088
|
-
var MCC_AGENT_TYPES = ["ChengGongYi", "SiGeUS", "SiGeCN", "Dee"];
|
|
121089
|
-
function googleGatewayBase(config) {
|
|
121090
|
-
const raw = (config.googleApiUrl ?? "").replace(/\/$/, "");
|
|
121091
|
-
if (!raw) {
|
|
121092
|
-
console.error("\n\u274C Google \u7F51\u5173\u5730\u5740\u672A\u80FD\u63A8\u5BFC\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
|
|
121093
|
-
process.exit(1);
|
|
121094
|
-
}
|
|
121095
|
-
const err = validateBaseUrl(raw);
|
|
121096
|
-
if (err) {
|
|
121097
|
-
console.error(`
|
|
121098
|
-
\u274C googleApiUrl \u4E0D\u5408\u6CD5\uFF1A${err}
|
|
121099
|
-
`);
|
|
121100
|
-
process.exit(1);
|
|
121101
|
-
}
|
|
121102
|
-
return raw;
|
|
121103
|
-
}
|
|
121104
|
-
function mccResponseHasSuccess(res, managerId) {
|
|
121105
|
-
if (!Array.isArray(res)) return false;
|
|
121106
|
-
return res.some((item) => String(item) === String(managerId));
|
|
121107
|
-
}
|
|
121108
|
-
async function runAccountMccBind(opts) {
|
|
121109
|
-
const config = loadConfig(opts.token);
|
|
121110
|
-
const base = googleGatewayBase(config);
|
|
121111
|
-
const managerIds = parseMccManagerIds(opts.mcc);
|
|
121112
|
-
if (managerIds.length === 0) {
|
|
121113
|
-
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
121114
|
-
process.exit(1);
|
|
121115
|
-
}
|
|
121116
|
-
if (opts.customers.length === 0) {
|
|
121117
|
-
console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
|
|
121118
|
-
process.exit(1);
|
|
121119
|
-
}
|
|
121120
|
-
const body = { agentTypes: [...MCC_AGENT_TYPES], managerIds };
|
|
121121
|
-
const results = [];
|
|
121122
|
-
for (const customerId of opts.customers) {
|
|
121123
|
-
const url = `${base}/command/media-account/${customerId}/CustomerLinks`;
|
|
121124
|
-
try {
|
|
121125
|
-
const response = await apiFetch2(
|
|
121126
|
-
url,
|
|
121127
|
-
config,
|
|
121128
|
-
{ method: "POST", body: JSON.stringify(body) },
|
|
121129
|
-
opts.verbose
|
|
121130
|
-
);
|
|
121131
|
-
results.push({ customerId, response });
|
|
121132
|
-
} catch (err) {
|
|
121133
|
-
console.error(
|
|
121134
|
-
`
|
|
121135
|
-
\u274C \u8D26\u6237 ${customerId} MCC \u7ED1\u5B9A\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
121136
|
-
`
|
|
121137
|
-
);
|
|
121138
|
-
process.exit(1);
|
|
121139
|
-
}
|
|
121140
|
-
}
|
|
121141
|
-
if (await emitCliJsonOrSnapshot(opts, {
|
|
121142
|
-
section: "account-mcc-bind",
|
|
121143
|
-
commandLabel: "account mcc-bind",
|
|
121144
|
-
payload: results,
|
|
121145
|
-
idSuffix: opts.mcc
|
|
121146
|
-
})) {
|
|
121147
|
-
return;
|
|
121148
|
-
}
|
|
121149
|
-
let hasFailure = false;
|
|
121150
|
-
for (const { customerId, response } of results) {
|
|
121151
|
-
const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
|
|
121152
|
-
if (failed.length > 0) {
|
|
121153
|
-
hasFailure = true;
|
|
121154
|
-
console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
|
|
121155
|
-
} else {
|
|
121156
|
-
console.log(
|
|
121157
|
-
` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u7ED1\u5B9A\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
|
|
121158
|
-
);
|
|
121159
|
-
}
|
|
121160
|
-
}
|
|
121161
|
-
console.log(
|
|
121162
|
-
hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u7ED1\u5B9A\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u7ED1\u5B9A\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
|
|
121163
|
-
);
|
|
121164
|
-
}
|
|
121165
|
-
async function runAccountMccUnbind(opts) {
|
|
121166
|
-
const config = loadConfig(opts.token);
|
|
121167
|
-
const base = googleGatewayBase(config);
|
|
121168
|
-
const managerIds = parseMccManagerIds(opts.mcc);
|
|
121169
|
-
if (managerIds.length === 0) {
|
|
121170
|
-
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
121171
|
-
process.exit(1);
|
|
121172
|
-
}
|
|
121173
|
-
if (opts.customers.length === 0) {
|
|
121174
|
-
console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
|
|
121175
|
-
process.exit(1);
|
|
121176
|
-
}
|
|
121177
|
-
const results = [];
|
|
121178
|
-
for (const customerId of opts.customers) {
|
|
121179
|
-
const url = `${base}/command/media-account/${customerId}/CustomerUnlinks`;
|
|
121180
|
-
try {
|
|
121181
|
-
const response = await apiFetch2(
|
|
121182
|
-
url,
|
|
121183
|
-
config,
|
|
121184
|
-
{ method: "POST", body: JSON.stringify(managerIds) },
|
|
121185
|
-
opts.verbose
|
|
121186
|
-
);
|
|
121187
|
-
results.push({ customerId, response });
|
|
121188
|
-
} catch (err) {
|
|
121189
|
-
console.error(
|
|
121190
|
-
`
|
|
121191
|
-
\u274C \u8D26\u6237 ${customerId} MCC \u89E3\u7ED1\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
121192
|
-
`
|
|
121193
|
-
);
|
|
121194
|
-
process.exit(1);
|
|
121195
|
-
}
|
|
121196
|
-
}
|
|
121197
|
-
if (await emitCliJsonOrSnapshot(opts, {
|
|
121198
|
-
section: "account-mcc-unbind",
|
|
121199
|
-
commandLabel: "account mcc-unbind",
|
|
121200
|
-
payload: results,
|
|
121201
|
-
idSuffix: opts.mcc
|
|
121202
|
-
})) {
|
|
121203
|
-
return;
|
|
121204
|
-
}
|
|
121205
|
-
let hasFailure = false;
|
|
121206
|
-
for (const { customerId, response } of results) {
|
|
121207
|
-
const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
|
|
121208
|
-
if (failed.length > 0) {
|
|
121209
|
-
hasFailure = true;
|
|
121210
|
-
console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u89E3\u7ED1\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
|
|
121211
|
-
} else {
|
|
121212
|
-
console.log(
|
|
121213
|
-
` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u89E3\u7ED1\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
|
|
121214
|
-
);
|
|
121215
|
-
}
|
|
121216
|
-
}
|
|
121217
|
-
console.log(
|
|
121218
|
-
hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u89E3\u7ED1\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u89E3\u7ED1\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
|
|
121219
|
-
);
|
|
121220
|
-
}
|
|
121221
|
-
|
|
121222
121529
|
// src/commands/account-manage/tiktok-close.ts
|
|
121223
121530
|
init_auth();
|
|
121224
121531
|
init_cli_json_snapshot();
|
|
@@ -121988,6 +122295,21 @@ function register23(program2) {
|
|
|
121988
122295
|
});
|
|
121989
122296
|
}
|
|
121990
122297
|
);
|
|
122298
|
+
accountCmd.command("check-access").description(
|
|
122299
|
+
"\u6821\u9A8C Google \u5E7F\u544A\u8D26\u6237\u8BBF\u95EE\u6743\u9650\uFF08GET googleapi \u2026/customer-access-permission\uFF09\uFF1B403 \u901A\u5E38\u8868\u793A\u8D26\u6237\u4E0D\u5728\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u4E0B"
|
|
122300
|
+
).requiredOption(
|
|
122301
|
+
"-a, --account <mediaCustomerId>",
|
|
122302
|
+
"Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09"
|
|
122303
|
+
).option("-t, --token <token>", "Auth Token").option("--json-out <path>", "\u843D\u76D8 JSON\uFF1Bstdout \u4E00\u884C\u6458\u8981\uFF1B\u4E0D\u53EF\u8BBF\u95EE\u65F6 exit 1").option("--verbose", "\u6253\u5370\u539F\u59CB HTTP \u54CD\u5E94\u7247\u6BB5", false).action(
|
|
122304
|
+
async (opts) => {
|
|
122305
|
+
await runAccountCheckAccess({
|
|
122306
|
+
account: opts.account,
|
|
122307
|
+
token: opts.token,
|
|
122308
|
+
jsonOut: opts.jsonOut,
|
|
122309
|
+
verbose: opts.verbose
|
|
122310
|
+
});
|
|
122311
|
+
}
|
|
122312
|
+
);
|
|
121991
122313
|
accountCmd.command("delink").description(
|
|
121992
122314
|
"\u89E3\u9664\u6388\u6743\uFF1A\u65AD\u5F00\u5E7F\u544A\u8D26\u6237\u4E0E\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u7684\u5173\u8054\uFF08\u7F51\u9875 manageAccounts\u300C\u89E3\u9664\u6388\u6743\u300D\uFF1B--id \u6216 --ids\uFF09"
|
|
121993
122315
|
).option("--id <entityId>", "\u5355\u4E2A\u8D26\u6237 entityId\uFF08\u6765\u81EA list-accounts \u7684 ma.entityId \u5B57\u6BB5\uFF09").option("--ids <entityIds>", "\u6279\u91CF entityId\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u4E0E --id \u4E8C\u9009\u4E00\uFF0C\u53EF\u540C\u65F6\u4F20\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
@@ -125282,12 +125604,12 @@ function sectionRecord(env) {
|
|
|
125282
125604
|
}
|
|
125283
125605
|
|
|
125284
125606
|
// src/commands/google-ads-diagnosis/build-report-data.ts
|
|
125285
|
-
function
|
|
125607
|
+
function num2(value) {
|
|
125286
125608
|
const n = Number(value);
|
|
125287
125609
|
return Number.isFinite(n) ? n : 0;
|
|
125288
125610
|
}
|
|
125289
125611
|
function rowCost(row) {
|
|
125290
|
-
return
|
|
125612
|
+
return num2(row.cost ?? row.spend);
|
|
125291
125613
|
}
|
|
125292
125614
|
function pct(value) {
|
|
125293
125615
|
return `${(value * 100).toFixed(2)}%`;
|
|
@@ -125318,11 +125640,11 @@ function asPeriodRecord(value) {
|
|
|
125318
125640
|
}
|
|
125319
125641
|
function toKeyMetricRow(title, current, previous) {
|
|
125320
125642
|
const currentCost = rowCost(current);
|
|
125321
|
-
const currentClicks =
|
|
125322
|
-
const currentCvr =
|
|
125643
|
+
const currentClicks = num2(current.clicks);
|
|
125644
|
+
const currentCvr = num2(current.conversionRate ?? current.cvr);
|
|
125323
125645
|
const previousCost = previous ? rowCost(previous) : null;
|
|
125324
|
-
const previousClicks = previous ?
|
|
125325
|
-
const previousCvr = previous ?
|
|
125646
|
+
const previousClicks = previous ? num2(previous.clicks) : null;
|
|
125647
|
+
const previousCvr = previous ? num2(previous.conversionRate ?? previous.cvr) : null;
|
|
125326
125648
|
return {
|
|
125327
125649
|
title,
|
|
125328
125650
|
currentCost,
|
|
@@ -125352,13 +125674,13 @@ function buildComparisonItems(currentRows, previousRows, titleFn, keyFn) {
|
|
|
125352
125674
|
}
|
|
125353
125675
|
function toTargetingItem(row, labelKey, label) {
|
|
125354
125676
|
const cost = rowCost(row);
|
|
125355
|
-
const clicks =
|
|
125356
|
-
const impressions =
|
|
125357
|
-
const conversions =
|
|
125358
|
-
const ctr =
|
|
125359
|
-
const cvr =
|
|
125360
|
-
const cpc =
|
|
125361
|
-
const cpa = conversions > 0 ? cost / conversions :
|
|
125677
|
+
const clicks = num2(row.clicks);
|
|
125678
|
+
const impressions = num2(row.impressions);
|
|
125679
|
+
const conversions = num2(row.conversions);
|
|
125680
|
+
const ctr = num2(row.ctr);
|
|
125681
|
+
const cvr = num2(row.conversionRate ?? row.cvr);
|
|
125682
|
+
const cpc = num2(row.averageCpc ?? row.cpc);
|
|
125683
|
+
const cpa = conversions > 0 ? cost / conversions : num2(row.costPerConversion ?? row.cpa);
|
|
125362
125684
|
return {
|
|
125363
125685
|
[labelKey]: label,
|
|
125364
125686
|
cost,
|
|
@@ -125373,11 +125695,11 @@ function toTargetingItem(row, labelKey, label) {
|
|
|
125373
125695
|
}
|
|
125374
125696
|
function toDailyConversionItem(row) {
|
|
125375
125697
|
const cost = rowCost(row);
|
|
125376
|
-
const conversions =
|
|
125698
|
+
const conversions = num2(row.conversions);
|
|
125377
125699
|
const rawDate = String(row.date ?? "");
|
|
125378
125700
|
const date = rawDate.includes("T") ? rawDate.slice(0, 10) : rawDate.slice(0, 10);
|
|
125379
|
-
const cpa = conversions > 0 ? cost / conversions :
|
|
125380
|
-
return { date, cost, conversions, cpa, clicks:
|
|
125701
|
+
const cpa = conversions > 0 ? cost / conversions : num2(row.costPerConversion ?? row.cpa);
|
|
125702
|
+
return { date, cost, conversions, cpa, clicks: num2(row.clicks), impressions: num2(row.impressions) };
|
|
125381
125703
|
}
|
|
125382
125704
|
function loadCompanyNameFromSnap(snapDir) {
|
|
125383
125705
|
const file = path21.join(snapDir, "list-accounts-google.json");
|
|
@@ -125414,8 +125736,8 @@ function inferBusinessProfile(campaignRows, adRows, website) {
|
|
|
125414
125736
|
};
|
|
125415
125737
|
}
|
|
125416
125738
|
function primaryConversionLabel(rows) {
|
|
125417
|
-
const sorted = [...rows].sort((a, b) =>
|
|
125418
|
-
const top = sorted.find((r) =>
|
|
125739
|
+
const sorted = [...rows].sort((a, b) => num2(b.allConversions) - num2(a.allConversions));
|
|
125740
|
+
const top = sorted.find((r) => num2(r.allConversions) > 0 && String(r.status) === "Enabled");
|
|
125419
125741
|
if (top) return String(top.name ?? "\u8BE2\u76D8/\u8F6C\u5316");
|
|
125420
125742
|
const enabled = rows.find((r) => String(r.status) === "Enabled");
|
|
125421
125743
|
return enabled ? String(enabled.name ?? "\u8BE2\u76D8/\u8F6C\u5316") : "\u8BE2\u76D8/\u8F6C\u5316";
|
|
@@ -125434,7 +125756,7 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125434
125756
|
const strategy = String(row.biddingStrategyTypeV2 ?? row.biddingStrategyType ?? "UNKNOWN");
|
|
125435
125757
|
const status = String(row.campaignStatus ?? row.campaignStatusV2 ?? "");
|
|
125436
125758
|
const isPaused = /paused/i.test(status);
|
|
125437
|
-
const recommended =
|
|
125759
|
+
const recommended = num2(row.conversions) > 0 ? "MAXIMIZE_CONVERSIONS" : "TARGET_SPEND";
|
|
125438
125760
|
const isCorrect = policyOk && !isPaused && strategy === recommended;
|
|
125439
125761
|
return {
|
|
125440
125762
|
campaignName: String(row.campaignName ?? "\u672A\u547D\u540D\u7CFB\u5217"),
|
|
@@ -125447,16 +125769,16 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125447
125769
|
});
|
|
125448
125770
|
}
|
|
125449
125771
|
function buildStructureCounts(resourceRecord, campaignRows, keywordRows, searchRows) {
|
|
125450
|
-
const campaignCount = Math.max(
|
|
125451
|
-
const keywordCount = Math.max(
|
|
125772
|
+
const campaignCount = Math.max(num2(resourceRecord?.campaignCount), campaignRows.length);
|
|
125773
|
+
const keywordCount = Math.max(num2(resourceRecord?.keywordCount), keywordRows.length);
|
|
125452
125774
|
return {
|
|
125453
125775
|
campaignCount,
|
|
125454
|
-
adGroupCount:
|
|
125776
|
+
adGroupCount: num2(resourceRecord?.adGroupCount),
|
|
125455
125777
|
keywordCount,
|
|
125456
|
-
adCount:
|
|
125457
|
-
extensionCount:
|
|
125458
|
-
negativeKeywordCount:
|
|
125459
|
-
countriesWithConversionsCount:
|
|
125778
|
+
adCount: num2(resourceRecord?.adCount),
|
|
125779
|
+
extensionCount: num2(resourceRecord?.extensionCount),
|
|
125780
|
+
negativeKeywordCount: num2(resourceRecord?.negativeKeywordCount),
|
|
125781
|
+
countriesWithConversionsCount: num2(resourceRecord?.countriesWithConversionsCount),
|
|
125460
125782
|
searchTermCount: searchRows.length
|
|
125461
125783
|
};
|
|
125462
125784
|
}
|
|
@@ -125470,26 +125792,26 @@ function loadWebsiteFromAds(snapDir, accountId, start, end) {
|
|
|
125470
125792
|
}
|
|
125471
125793
|
function buildKeywordShareItems(rows) {
|
|
125472
125794
|
const totalCost = rows.reduce((sum, row) => sum + rowCost(row), 0);
|
|
125473
|
-
const totalConversions = rows.reduce((sum, row) => sum +
|
|
125795
|
+
const totalConversions = rows.reduce((sum, row) => sum + num2(row.conversions), 0);
|
|
125474
125796
|
return rows.map((row) => {
|
|
125475
125797
|
const cost = rowCost(row);
|
|
125476
|
-
const conversions =
|
|
125798
|
+
const conversions = num2(row.conversions);
|
|
125477
125799
|
const keywordText = String(row.keywordText ?? row.keyword ?? "").trim();
|
|
125478
125800
|
return {
|
|
125479
125801
|
keyword: keywordText || "\u672A\u547D\u540D",
|
|
125480
|
-
cpc:
|
|
125802
|
+
cpc: num2(row.averageCpc ?? row.cpc),
|
|
125481
125803
|
matchType: String(
|
|
125482
125804
|
row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
|
|
125483
125805
|
),
|
|
125484
|
-
impressions:
|
|
125485
|
-
clicks:
|
|
125806
|
+
impressions: num2(row.impressions),
|
|
125807
|
+
clicks: num2(row.clicks),
|
|
125486
125808
|
cost,
|
|
125487
125809
|
conversions,
|
|
125488
125810
|
costShare: totalCost > 0 ? cost / totalCost * 100 : 0,
|
|
125489
125811
|
conversionsShare: totalConversions > 0 ? conversions / totalConversions * 100 : 0,
|
|
125490
125812
|
cpa: conversions > 0 ? cost / conversions : 0,
|
|
125491
|
-
ctr:
|
|
125492
|
-
cvr:
|
|
125813
|
+
ctr: num2(row.ctr),
|
|
125814
|
+
cvr: num2(row.conversionRate ?? row.cvr)
|
|
125493
125815
|
};
|
|
125494
125816
|
});
|
|
125495
125817
|
}
|
|
@@ -125497,17 +125819,17 @@ function buildSearchTermItems(rows) {
|
|
|
125497
125819
|
const sorted = [...rows].sort((a, b) => rowCost(b) - rowCost(a));
|
|
125498
125820
|
return sorted.slice(0, 50).map((row) => {
|
|
125499
125821
|
const cost = rowCost(row);
|
|
125500
|
-
const conversions =
|
|
125501
|
-
const cpa = conversions > 0 ?
|
|
125822
|
+
const conversions = num2(row.conversions);
|
|
125823
|
+
const cpa = conversions > 0 ? num2(row.costPerConversion) || cost / conversions : void 0;
|
|
125502
125824
|
return {
|
|
125503
125825
|
searchTerm: String(row.searchTermText ?? row.searchTerm ?? "\u672A\u547D\u540D"),
|
|
125504
125826
|
keyword: String(row.keywordText ?? row.keyword ?? ""),
|
|
125505
125827
|
cost,
|
|
125506
125828
|
conversions,
|
|
125507
|
-
clicks:
|
|
125508
|
-
impressions:
|
|
125509
|
-
ctr:
|
|
125510
|
-
cvr:
|
|
125829
|
+
clicks: num2(row.clicks),
|
|
125830
|
+
impressions: num2(row.impressions),
|
|
125831
|
+
ctr: num2(row.ctr),
|
|
125832
|
+
cvr: num2(row.conversionRate ?? row.cvr),
|
|
125511
125833
|
cpa
|
|
125512
125834
|
};
|
|
125513
125835
|
});
|
|
@@ -125549,18 +125871,18 @@ function buildAdCreativeItems(rows) {
|
|
|
125549
125871
|
}
|
|
125550
125872
|
function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
|
|
125551
125873
|
const current = asPeriodRecord(overviewRecord?.currentPeriod);
|
|
125552
|
-
const totalCost =
|
|
125553
|
-
const activeDays =
|
|
125554
|
-
const avgDaily =
|
|
125555
|
-
const remainingBudget =
|
|
125874
|
+
const totalCost = num2(overviewRecord?.totalCost ?? current?.spend);
|
|
125875
|
+
const activeDays = num2(overviewRecord?.activeDays);
|
|
125876
|
+
const avgDaily = num2(overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0));
|
|
125877
|
+
const remainingBudget = num2(overviewRecord?.remainingAccountBudget);
|
|
125556
125878
|
const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
|
|
125557
|
-
const sis =
|
|
125879
|
+
const sis = num2(
|
|
125558
125880
|
dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
|
|
125559
125881
|
);
|
|
125560
|
-
const budgetLost =
|
|
125882
|
+
const budgetLost = num2(
|
|
125561
125883
|
dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
|
|
125562
125884
|
);
|
|
125563
|
-
const rankLost =
|
|
125885
|
+
const rankLost = num2(
|
|
125564
125886
|
dimensionRecord?.searchRankLostImpressionShare ?? current?.searchRankLostImpressionShare
|
|
125565
125887
|
);
|
|
125566
125888
|
return [
|
|
@@ -125646,17 +125968,17 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125646
125968
|
};
|
|
125647
125969
|
data.reportDate = end;
|
|
125648
125970
|
const cost = rowCost(currentPeriod ?? overviewRecord ?? {});
|
|
125649
|
-
const impressions =
|
|
125650
|
-
const clicks =
|
|
125651
|
-
const conversions =
|
|
125652
|
-
const ctr =
|
|
125653
|
-
const cvr =
|
|
125654
|
-
const cpa = conversions > 0 ? cost / conversions :
|
|
125971
|
+
const impressions = num2(currentPeriod?.impressions);
|
|
125972
|
+
const clicks = num2(currentPeriod?.clicks);
|
|
125973
|
+
const conversions = num2(currentPeriod?.conversions);
|
|
125974
|
+
const ctr = num2(currentPeriod?.ctr);
|
|
125975
|
+
const cvr = num2(currentPeriod?.conversionRate ?? currentPeriod?.cvr);
|
|
125976
|
+
const cpa = conversions > 0 ? cost / conversions : num2(currentPeriod?.costPerConversion);
|
|
125655
125977
|
const prevCost = previousPeriod ? rowCost(previousPeriod) : 0;
|
|
125656
|
-
const prevConversions = previousPeriod ?
|
|
125657
|
-
const prevCtr = previousPeriod ?
|
|
125658
|
-
const prevCvr = previousPeriod ?
|
|
125659
|
-
const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions :
|
|
125978
|
+
const prevConversions = previousPeriod ? num2(previousPeriod.conversions) : 0;
|
|
125979
|
+
const prevCtr = previousPeriod ? num2(previousPeriod.ctr) : 0;
|
|
125980
|
+
const prevCvr = previousPeriod ? num2(previousPeriod.conversionRate ?? previousPeriod.cvr) : 0;
|
|
125981
|
+
const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions : num2(previousPeriod?.costPerConversion);
|
|
125660
125982
|
data.metrics = {
|
|
125661
125983
|
cost,
|
|
125662
125984
|
impressions,
|
|
@@ -125692,8 +126014,8 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125692
126014
|
items: conversionRows.map((row) => ({
|
|
125693
126015
|
name: String(row.name ?? row.eventName ?? "\u672A\u77E5\u4E8B\u4EF6"),
|
|
125694
126016
|
status: String(row.status ?? ""),
|
|
125695
|
-
allConversions:
|
|
125696
|
-
allConversionsValue:
|
|
126017
|
+
allConversions: num2(row.allConversions),
|
|
126018
|
+
allConversionsValue: num2(row.allConversionsValue)
|
|
125697
126019
|
}))
|
|
125698
126020
|
};
|
|
125699
126021
|
const campaignPrevious = sectionItems(
|
|
@@ -125777,16 +126099,16 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125777
126099
|
const lighthouse = opts.landingPage ?? {};
|
|
125778
126100
|
data.landingPageAnalysis = {
|
|
125779
126101
|
desktop: {
|
|
125780
|
-
score:
|
|
125781
|
-
firstContentfulPaint:
|
|
125782
|
-
firstMeaningfulPaint:
|
|
125783
|
-
speedIndex:
|
|
126102
|
+
score: num2(lighthouse.desktop?.score),
|
|
126103
|
+
firstContentfulPaint: num2(lighthouse.desktop?.firstContentfulPaint),
|
|
126104
|
+
firstMeaningfulPaint: num2(lighthouse.desktop?.firstMeaningfulPaint),
|
|
126105
|
+
speedIndex: num2(lighthouse.desktop?.speedIndex)
|
|
125784
126106
|
},
|
|
125785
126107
|
mobile: {
|
|
125786
|
-
score:
|
|
125787
|
-
firstContentfulPaint:
|
|
125788
|
-
firstMeaningfulPaint:
|
|
125789
|
-
speedIndex:
|
|
126108
|
+
score: num2(lighthouse.mobile?.score),
|
|
126109
|
+
firstContentfulPaint: num2(lighthouse.mobile?.firstContentfulPaint),
|
|
126110
|
+
firstMeaningfulPaint: num2(lighthouse.mobile?.firstMeaningfulPaint),
|
|
126111
|
+
speedIndex: num2(lighthouse.mobile?.speedIndex)
|
|
125790
126112
|
}
|
|
125791
126113
|
};
|
|
125792
126114
|
const keywordShareItems = buildKeywordShareItems(kwCurrent);
|
|
@@ -125820,7 +126142,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125820
126142
|
data.newFeatures = {
|
|
125821
126143
|
items: nfItems.map((item) => {
|
|
125822
126144
|
const strategy = String(item.strategy ?? "");
|
|
125823
|
-
const enabled = strategy === "PerformanceMax" ?
|
|
126145
|
+
const enabled = strategy === "PerformanceMax" ? num2(campaignTypes?.PerformanceMax) > 0 : strategy === "KwMatch" ? num2(campaignTypes?.Search) > 0 : strategy === "DemandGen" ? num2(campaignTypes?.DemandGen) > 0 : strategy === "Youtube" ? num2(campaignTypes?.Video) > 0 || num2(campaignTypes?.Youtube) > 0 : false;
|
|
125824
126146
|
return {
|
|
125825
126147
|
...item,
|
|
125826
126148
|
accountStatus: enabled,
|
|
@@ -125830,7 +126152,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125830
126152
|
...emptyModuleNarrative()
|
|
125831
126153
|
};
|
|
125832
126154
|
data.summary = emptySummary();
|
|
125833
|
-
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 &&
|
|
126155
|
+
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 && num2(r.conversions) === 0);
|
|
125834
126156
|
const agentBrief = buildGoogleAdsDiagnosisAgentBrief({
|
|
125835
126157
|
accountId,
|
|
125836
126158
|
period: formatPeriod(start, end),
|
|
@@ -125854,9 +126176,9 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125854
126176
|
prevCpa
|
|
125855
126177
|
},
|
|
125856
126178
|
dimension: {
|
|
125857
|
-
searchImpressionShare:
|
|
125858
|
-
searchBudgetLostImpressionShare:
|
|
125859
|
-
searchRankLostImpressionShare:
|
|
126179
|
+
searchImpressionShare: num2(dimensionRecord?.searchImpressionShare),
|
|
126180
|
+
searchBudgetLostImpressionShare: num2(dimensionRecord?.searchBudgetLostImpressionShare),
|
|
126181
|
+
searchRankLostImpressionShare: num2(dimensionRecord?.searchRankLostImpressionShare)
|
|
125860
126182
|
},
|
|
125861
126183
|
structure: structureCounts,
|
|
125862
126184
|
goldAccount: goldRecord,
|
|
@@ -126502,7 +126824,7 @@ var PHASE_LABEL = {
|
|
|
126502
126824
|
"find-winner": "\u627E\u8D62\u5BB6",
|
|
126503
126825
|
scale: "\u653E\u91CF"
|
|
126504
126826
|
};
|
|
126505
|
-
function
|
|
126827
|
+
function num3(value) {
|
|
126506
126828
|
const n = Number(value);
|
|
126507
126829
|
return Number.isFinite(n) ? n : void 0;
|
|
126508
126830
|
}
|
|
@@ -126513,35 +126835,35 @@ function money(value, currency = "USD") {
|
|
|
126513
126835
|
}
|
|
126514
126836
|
function adSetsWithSpend(tables) {
|
|
126515
126837
|
const rows = Array.isArray(tables?.adSets) ? tables.adSets : [];
|
|
126516
|
-
return rows.filter((r) => (
|
|
126838
|
+
return rows.filter((r) => (num3(r.spend) ?? 0) > 0);
|
|
126517
126839
|
}
|
|
126518
126840
|
function topAdSetConcentration(adSets) {
|
|
126519
126841
|
if (!adSets.length) return null;
|
|
126520
|
-
const totalSpend = adSets.reduce((s, r) => s + (
|
|
126521
|
-
const totalResults = adSets.reduce((s, r) => s + (
|
|
126842
|
+
const totalSpend = adSets.reduce((s, r) => s + (num3(r.spend) ?? 0), 0);
|
|
126843
|
+
const totalResults = adSets.reduce((s, r) => s + (num3(r.results) ?? 0), 0);
|
|
126522
126844
|
let best = adSets[0];
|
|
126523
|
-
let bestSpend =
|
|
126845
|
+
let bestSpend = num3(best.spend) ?? 0;
|
|
126524
126846
|
for (const r of adSets) {
|
|
126525
|
-
const sp =
|
|
126847
|
+
const sp = num3(r.spend) ?? 0;
|
|
126526
126848
|
if (sp > bestSpend) {
|
|
126527
126849
|
best = r;
|
|
126528
126850
|
bestSpend = sp;
|
|
126529
126851
|
}
|
|
126530
126852
|
}
|
|
126531
|
-
const results =
|
|
126853
|
+
const results = num3(best.results) ?? 0;
|
|
126532
126854
|
return {
|
|
126533
126855
|
name: String(best.adGroupName ?? best.name ?? "\u2014"),
|
|
126534
126856
|
spendShare: totalSpend > 0 ? bestSpend / totalSpend : 0,
|
|
126535
126857
|
resultsShare: totalResults > 0 ? results / totalResults : 0,
|
|
126536
|
-
cpl:
|
|
126858
|
+
cpl: num3(best.costPerResult)
|
|
126537
126859
|
};
|
|
126538
126860
|
}
|
|
126539
126861
|
function winnerSpendShare(adSets, avgCpl, totalSpend) {
|
|
126540
126862
|
if (totalSpend <= 0 || avgCpl <= 0) return 0;
|
|
126541
126863
|
const winnerSpend = adSets.filter((r) => {
|
|
126542
|
-
const cpl =
|
|
126864
|
+
const cpl = num3(r.costPerResult);
|
|
126543
126865
|
return cpl != null && cpl <= avgCpl * 0.9;
|
|
126544
|
-
}).reduce((s, r) => s + (
|
|
126866
|
+
}).reduce((s, r) => s + (num3(r.spend) ?? 0), 0);
|
|
126545
126867
|
return winnerSpend / totalSpend;
|
|
126546
126868
|
}
|
|
126547
126869
|
function countActiveDimensions(charts, tables) {
|
|
@@ -126558,7 +126880,7 @@ function asArray(value) {
|
|
|
126558
126880
|
}
|
|
126559
126881
|
function inferLifecyclePhase(input) {
|
|
126560
126882
|
const { avgCpl, currency = "USD" } = input;
|
|
126561
|
-
const spend =
|
|
126883
|
+
const spend = num3(input.spend) ?? 0;
|
|
126562
126884
|
const adSets = adSetsWithSpend(input.tables);
|
|
126563
126885
|
const activeDims = countActiveDimensions(input.charts, input.tables);
|
|
126564
126886
|
const winShare = winnerSpendShare(adSets, avgCpl, spend);
|
|
@@ -126617,7 +126939,7 @@ function ensureLifecycleFromSnapshot(healthDiagnosis, merged) {
|
|
|
126617
126939
|
}
|
|
126618
126940
|
|
|
126619
126941
|
// src/commands/facebook-analysis/scorecard-autofill.ts
|
|
126620
|
-
function
|
|
126942
|
+
function num4(value) {
|
|
126621
126943
|
const n = Number(value);
|
|
126622
126944
|
return Number.isFinite(n) ? n : void 0;
|
|
126623
126945
|
}
|
|
@@ -126657,8 +126979,8 @@ function buildScorecardFromMergedData(input) {
|
|
|
126657
126979
|
if (pLabels.length) {
|
|
126658
126980
|
const indexed = pLabels.map((label, i) => ({
|
|
126659
126981
|
label: String(label),
|
|
126660
|
-
cpl:
|
|
126661
|
-
spend:
|
|
126982
|
+
cpl: num4(pCpl[i]),
|
|
126983
|
+
spend: num4(pSpend[i]) ?? 0
|
|
126662
126984
|
})).filter((x) => x.spend > 0 || x.cpl != null && x.cpl > 0);
|
|
126663
126985
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? Infinity) - (b.cpl ?? Infinity));
|
|
126664
126986
|
if (sorted[0]) {
|
|
@@ -126683,7 +127005,7 @@ function buildScorecardFromMergedData(input) {
|
|
|
126683
127005
|
const cLabels = Array.isArray(country?.labels) ? country.labels : [];
|
|
126684
127006
|
const cCpl = Array.isArray(country?.cpl) ? country.cpl : [];
|
|
126685
127007
|
if (cLabels.length) {
|
|
126686
|
-
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl:
|
|
127008
|
+
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl: num4(cCpl[i]) })).filter((x) => x.cpl != null && x.cpl > 0);
|
|
126687
127009
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? 0) - (b.cpl ?? 0));
|
|
126688
127010
|
if (sorted[0]) {
|
|
126689
127011
|
const s = cplSignal(sorted[0].cpl, avgCpl);
|
|
@@ -126704,24 +127026,24 @@ function buildScorecardFromMergedData(input) {
|
|
|
126704
127026
|
}
|
|
126705
127027
|
}
|
|
126706
127028
|
const adSets = Array.isArray(tables.adSets) ? tables.adSets : [];
|
|
126707
|
-
const adWithSpend = adSets.filter((g) => (
|
|
127029
|
+
const adWithSpend = adSets.filter((g) => (num4(g.spend) ?? 0) > 0);
|
|
126708
127030
|
if (adWithSpend.length) {
|
|
126709
127031
|
const sorted = [...adWithSpend].sort(
|
|
126710
|
-
(a, b) => (
|
|
127032
|
+
(a, b) => (num4(a.costPerResult) ?? Infinity) - (num4(b.costPerResult) ?? Infinity)
|
|
126711
127033
|
);
|
|
126712
127034
|
const best = sorted[0];
|
|
126713
|
-
const sBest = cplSignal(
|
|
127035
|
+
const sBest = cplSignal(num4(best.costPerResult), avgCpl);
|
|
126714
127036
|
pushRow(rows, seen, {
|
|
126715
127037
|
item: String(best.name ?? "\u5E7F\u544A\u7EC4"),
|
|
126716
|
-
data: `CPL ${money2(
|
|
127038
|
+
data: `CPL ${money2(num4(best.costPerResult), currency)}`,
|
|
126717
127039
|
...sBest
|
|
126718
127040
|
});
|
|
126719
127041
|
if (sorted.length > 1) {
|
|
126720
127042
|
const worst = sorted[sorted.length - 1];
|
|
126721
|
-
const sWorst = cplSignal(
|
|
127043
|
+
const sWorst = cplSignal(num4(worst.costPerResult), avgCpl);
|
|
126722
127044
|
pushRow(rows, seen, {
|
|
126723
127045
|
item: String(worst.name ?? "\u5E7F\u544A\u7EC4"),
|
|
126724
|
-
data: `CPL ${money2(
|
|
127046
|
+
data: `CPL ${money2(num4(worst.costPerResult), currency)}`,
|
|
126725
127047
|
...sWorst
|
|
126726
127048
|
});
|
|
126727
127049
|
}
|
|
@@ -126729,18 +127051,18 @@ function buildScorecardFromMergedData(input) {
|
|
|
126729
127051
|
const topAud = Array.isArray(tables.audienceTop) ? tables.audienceTop : [];
|
|
126730
127052
|
const bottomAud = Array.isArray(tables.audienceBottom) ? tables.audienceBottom : [];
|
|
126731
127053
|
if (topAud[0]) {
|
|
126732
|
-
const s = cplSignal(
|
|
127054
|
+
const s = cplSignal(num4(topAud[0].costPerResult), avgCpl);
|
|
126733
127055
|
pushRow(rows, seen, {
|
|
126734
127056
|
item: `\u53D7\u4F17\xB7${String(topAud[0].label ?? "Top")}`,
|
|
126735
|
-
data: `CPL ${money2(
|
|
127057
|
+
data: `CPL ${money2(num4(topAud[0].costPerResult), currency)}`,
|
|
126736
127058
|
...s
|
|
126737
127059
|
});
|
|
126738
127060
|
}
|
|
126739
127061
|
if (bottomAud[0]) {
|
|
126740
|
-
const s = cplSignal(
|
|
127062
|
+
const s = cplSignal(num4(bottomAud[0].costPerResult), avgCpl);
|
|
126741
127063
|
pushRow(rows, seen, {
|
|
126742
127064
|
item: `\u53D7\u4F17\xB7${String(bottomAud[0].label ?? "Bottom")}`,
|
|
126743
|
-
data: `CPL ${money2(
|
|
127065
|
+
data: `CPL ${money2(num4(bottomAud[0].costPerResult), currency)}`,
|
|
126744
127066
|
...s
|
|
126745
127067
|
});
|
|
126746
127068
|
}
|
|
@@ -126784,7 +127106,7 @@ function asRecord8(value) {
|
|
|
126784
127106
|
}
|
|
126785
127107
|
return null;
|
|
126786
127108
|
}
|
|
126787
|
-
function
|
|
127109
|
+
function num5(value) {
|
|
126788
127110
|
const n = Number(value);
|
|
126789
127111
|
return Number.isFinite(n) ? n : void 0;
|
|
126790
127112
|
}
|
|
@@ -126838,8 +127160,8 @@ function aggregatePlatform(networks) {
|
|
|
126838
127160
|
const platform = String(row.publisherPlatform ?? row.network ?? "unknown");
|
|
126839
127161
|
const prev = map.get(platform) ?? { spend: 0, results: 0 };
|
|
126840
127162
|
map.set(platform, {
|
|
126841
|
-
spend: prev.spend + (
|
|
126842
|
-
results: prev.results + (
|
|
127163
|
+
spend: prev.spend + (num5(row.spend) ?? 0),
|
|
127164
|
+
results: prev.results + (num5(row.results) ?? 0)
|
|
126843
127165
|
});
|
|
126844
127166
|
}
|
|
126845
127167
|
const labels = [];
|
|
@@ -126855,10 +127177,10 @@ function aggregatePlatform(networks) {
|
|
|
126855
127177
|
function buildAudienceRows(audiences) {
|
|
126856
127178
|
return audiences.map((a) => ({
|
|
126857
127179
|
label: audienceLabel(a.age, a.gender),
|
|
126858
|
-
spend:
|
|
126859
|
-
results:
|
|
126860
|
-
costPerResult:
|
|
126861
|
-
frequency:
|
|
127180
|
+
spend: num5(a.spend) ?? 0,
|
|
127181
|
+
results: num5(a.results) ?? 0,
|
|
127182
|
+
costPerResult: num5(a.costPerResult) ?? (num5(a.results) ? (num5(a.spend) ?? 0) / (num5(a.results) ?? 1) : 0),
|
|
127183
|
+
frequency: num5(a.frequency)
|
|
126862
127184
|
})).filter((r) => r.spend > 0 || r.results > 0);
|
|
126863
127185
|
}
|
|
126864
127186
|
async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
@@ -126897,7 +127219,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126897
127219
|
}
|
|
126898
127220
|
if (!meta.resultType && current?.resultType) meta.resultType = String(current.resultType);
|
|
126899
127221
|
const charts = { ...payload.charts ?? {} };
|
|
126900
|
-
const avgCpl =
|
|
127222
|
+
const avgCpl = num5(kpis.costPerResult) ?? 0;
|
|
126901
127223
|
if (!charts.platform) {
|
|
126902
127224
|
const platform = sectionMap.get("platform");
|
|
126903
127225
|
const networks = Array.isArray(platform?.networks) ? platform.networks : [];
|
|
@@ -126907,10 +127229,10 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126907
127229
|
const country = sectionMap.get("country");
|
|
126908
127230
|
const countries = Array.isArray(country?.countries) ? country.countries : [];
|
|
126909
127231
|
if (countries.length) {
|
|
126910
|
-
const sorted = [...countries].sort((a, b) => (
|
|
127232
|
+
const sorted = [...countries].sort((a, b) => (num5(b.spend) ?? 0) - (num5(a.spend) ?? 0));
|
|
126911
127233
|
charts.country = {
|
|
126912
127234
|
labels: sorted.map((c) => String(c.countryOrRegion ?? "\u2014")),
|
|
126913
|
-
cpl: sorted.map((c) =>
|
|
127235
|
+
cpl: sorted.map((c) => num5(c.costPerResult) ?? 0)
|
|
126914
127236
|
};
|
|
126915
127237
|
}
|
|
126916
127238
|
}
|
|
@@ -126926,7 +127248,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126926
127248
|
};
|
|
126927
127249
|
}
|
|
126928
127250
|
}
|
|
126929
|
-
if (!charts.funnel &&
|
|
127251
|
+
if (!charts.funnel && num5(kpis.reach) != null) {
|
|
126930
127252
|
charts.funnel = { reach: kpis.reach, results: kpis.results ?? 0 };
|
|
126931
127253
|
}
|
|
126932
127254
|
const tables = { ...payload.tables ?? {} };
|
|
@@ -126935,14 +127257,14 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126935
127257
|
const groups = Array.isArray(adSets?.adGroups) ? adSets.adGroups : [];
|
|
126936
127258
|
if (groups.length) {
|
|
126937
127259
|
tables.adSets = groups.map((g) => {
|
|
126938
|
-
const cpl =
|
|
126939
|
-
const freq =
|
|
127260
|
+
const cpl = num5(g.costPerResult);
|
|
127261
|
+
const freq = num5(g.frequency);
|
|
126940
127262
|
const fatigue = fatigueFromFrequency(freq);
|
|
126941
127263
|
const status = statusFromCpl(cpl, avgCpl);
|
|
126942
127264
|
return {
|
|
126943
127265
|
name: String(g.adGroupName ?? g.campaignName ?? "\u2014"),
|
|
126944
|
-
spend:
|
|
126945
|
-
results:
|
|
127266
|
+
spend: num5(g.spend) ?? 0,
|
|
127267
|
+
results: num5(g.results) ?? 0,
|
|
126946
127268
|
costPerResult: cpl ?? 0,
|
|
126947
127269
|
frequency: freq,
|
|
126948
127270
|
fatigueLevel: fatigue.level,
|
|
@@ -126966,7 +127288,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126966
127288
|
}
|
|
126967
127289
|
const currency = typeof meta.currency === "string" ? meta.currency : "USD";
|
|
126968
127290
|
const mergeCtx = {
|
|
126969
|
-
spend:
|
|
127291
|
+
spend: num5(kpis.spend),
|
|
126970
127292
|
avgCpl,
|
|
126971
127293
|
currency,
|
|
126972
127294
|
charts,
|