siluzan-tso-cli 1.1.34-beta.2 → 1.1.34-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +495 -266
- 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/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 +1 -0
- package/dist/skill/report-templates/bing-period-report.md +1 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +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;
|
|
@@ -107794,6 +107794,57 @@ async function fetchBingJson(config, url, verbose) {
|
|
|
107794
107794
|
// src/commands/report-bing-analysis/run.ts
|
|
107795
107795
|
init_auth();
|
|
107796
107796
|
init_balance();
|
|
107797
|
+
|
|
107798
|
+
// src/commands/report-bing-analysis/normalize-bing-overview.ts
|
|
107799
|
+
function num(v) {
|
|
107800
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
107801
|
+
}
|
|
107802
|
+
function roundMoney(v) {
|
|
107803
|
+
return Math.round(v * 100) / 100;
|
|
107804
|
+
}
|
|
107805
|
+
function countInclusiveCalendarDays(startDate, endDate) {
|
|
107806
|
+
const parts = (ymd) => ymd.trim().split("-").map((x) => parseInt(x, 10));
|
|
107807
|
+
const sp = parts(startDate);
|
|
107808
|
+
const ep = parts(endDate);
|
|
107809
|
+
if (sp.length !== 3 || ep.length !== 3 || sp.some((n) => !Number.isFinite(n)) || ep.some((n) => !Number.isFinite(n))) {
|
|
107810
|
+
return 0;
|
|
107811
|
+
}
|
|
107812
|
+
const s = new Date(sp[0], sp[1] - 1, sp[2]);
|
|
107813
|
+
const e = new Date(ep[0], ep[1] - 1, ep[2]);
|
|
107814
|
+
if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) return 0;
|
|
107815
|
+
const msPerDay = 24 * 60 * 60 * 1e3;
|
|
107816
|
+
return Math.floor((e.getTime() - s.getTime()) / msPerDay) + 1;
|
|
107817
|
+
}
|
|
107818
|
+
function periodSpend(period) {
|
|
107819
|
+
if (!period || typeof period !== "object") return void 0;
|
|
107820
|
+
return num(period.spend);
|
|
107821
|
+
}
|
|
107822
|
+
function enrichBingOverviewDerivedMetrics(record, startDate, endDate) {
|
|
107823
|
+
const out = { ...record };
|
|
107824
|
+
const currentSpend = periodSpend(out.currentPeriod);
|
|
107825
|
+
let totalCost = num(out.totalCost);
|
|
107826
|
+
if (totalCost == null || totalCost <= 0) {
|
|
107827
|
+
if (currentSpend != null && currentSpend > 0) {
|
|
107828
|
+
out.totalCost = currentSpend;
|
|
107829
|
+
totalCost = currentSpend;
|
|
107830
|
+
}
|
|
107831
|
+
}
|
|
107832
|
+
let activeDays = num(out.activeDays);
|
|
107833
|
+
if (activeDays == null || activeDays <= 0) {
|
|
107834
|
+
const calendarDays = countInclusiveCalendarDays(startDate, endDate);
|
|
107835
|
+
if (calendarDays > 0) {
|
|
107836
|
+
out.activeDays = calendarDays;
|
|
107837
|
+
activeDays = calendarDays;
|
|
107838
|
+
}
|
|
107839
|
+
}
|
|
107840
|
+
const averageDailyCost = num(out.averageDailyCost);
|
|
107841
|
+
if ((averageDailyCost == null || averageDailyCost <= 0) && totalCost != null && totalCost > 0 && activeDays != null && activeDays > 0) {
|
|
107842
|
+
out.averageDailyCost = roundMoney(totalCost / activeDays);
|
|
107843
|
+
}
|
|
107844
|
+
return out;
|
|
107845
|
+
}
|
|
107846
|
+
|
|
107847
|
+
// src/commands/report-bing-analysis/run.ts
|
|
107797
107848
|
async function runReportBingOverview(opts) {
|
|
107798
107849
|
const id = assertBingAccountId(opts.account);
|
|
107799
107850
|
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
@@ -107802,13 +107853,17 @@ async function runReportBingOverview(opts) {
|
|
|
107802
107853
|
const url = reportingUrl2(config, id, "OverviewSectionData", params.toString());
|
|
107803
107854
|
try {
|
|
107804
107855
|
const raw = await fetchBingJson(config, url, !!opts.verbose);
|
|
107805
|
-
|
|
107806
|
-
|
|
107807
|
-
|
|
107808
|
-
|
|
107809
|
-
|
|
107810
|
-
|
|
107811
|
-
|
|
107856
|
+
let data = raw;
|
|
107857
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
107858
|
+
const withBalance = await enrichOverviewRecordWithRealtimeBalance(
|
|
107859
|
+
"BingV2",
|
|
107860
|
+
id,
|
|
107861
|
+
config,
|
|
107862
|
+
raw,
|
|
107863
|
+
opts.verbose
|
|
107864
|
+
);
|
|
107865
|
+
data = enrichBingOverviewDerivedMetrics(withBalance, startDate, endDate);
|
|
107866
|
+
}
|
|
107812
107867
|
if (opts.jsonOut) {
|
|
107813
107868
|
await emitBingSnapshot(opts, "bing-overview", id, startDate, endDate, data);
|
|
107814
107869
|
return;
|
|
@@ -120851,6 +120906,309 @@ async function runAccountMe(opts) {
|
|
|
120851
120906
|
}
|
|
120852
120907
|
}
|
|
120853
120908
|
|
|
120909
|
+
// src/commands/account-manage/google-access-permission.ts
|
|
120910
|
+
init_dist();
|
|
120911
|
+
init_auth();
|
|
120912
|
+
init_cli_json_snapshot();
|
|
120913
|
+
|
|
120914
|
+
// src/commands/account-manage/google-mcc.ts
|
|
120915
|
+
init_auth();
|
|
120916
|
+
init_cli_json_snapshot();
|
|
120917
|
+
function parseMccManagerIds(input) {
|
|
120918
|
+
const normalized = input.replace(/[,,;、]/g, ";");
|
|
120919
|
+
return normalized.split(";").map((part) => {
|
|
120920
|
+
const digits = part.replace(/\D/g, "");
|
|
120921
|
+
return digits ? +digits : NaN;
|
|
120922
|
+
}).filter((n) => Number.isFinite(n) && n > 0);
|
|
120923
|
+
}
|
|
120924
|
+
var MCC_AGENT_TYPES = ["ChengGongYi", "SiGeUS", "SiGeCN", "Dee"];
|
|
120925
|
+
function googleGatewayBase(config) {
|
|
120926
|
+
const raw = (config.googleApiUrl ?? "").replace(/\/$/, "");
|
|
120927
|
+
if (!raw) {
|
|
120928
|
+
console.error("\n\u274C Google \u7F51\u5173\u5730\u5740\u672A\u80FD\u63A8\u5BFC\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
|
|
120929
|
+
process.exit(1);
|
|
120930
|
+
}
|
|
120931
|
+
const err = validateBaseUrl(raw);
|
|
120932
|
+
if (err) {
|
|
120933
|
+
console.error(`
|
|
120934
|
+
\u274C googleApiUrl \u4E0D\u5408\u6CD5\uFF1A${err}
|
|
120935
|
+
`);
|
|
120936
|
+
process.exit(1);
|
|
120937
|
+
}
|
|
120938
|
+
return raw;
|
|
120939
|
+
}
|
|
120940
|
+
function mccResponseHasSuccess(res, managerId) {
|
|
120941
|
+
if (!Array.isArray(res)) return false;
|
|
120942
|
+
return res.some((item) => String(item) === String(managerId));
|
|
120943
|
+
}
|
|
120944
|
+
async function runAccountMccBind(opts) {
|
|
120945
|
+
const config = loadConfig(opts.token);
|
|
120946
|
+
const base = googleGatewayBase(config);
|
|
120947
|
+
const managerIds = parseMccManagerIds(opts.mcc);
|
|
120948
|
+
if (managerIds.length === 0) {
|
|
120949
|
+
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
120950
|
+
process.exit(1);
|
|
120951
|
+
}
|
|
120952
|
+
if (opts.customers.length === 0) {
|
|
120953
|
+
console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
|
|
120954
|
+
process.exit(1);
|
|
120955
|
+
}
|
|
120956
|
+
const body = { agentTypes: [...MCC_AGENT_TYPES], managerIds };
|
|
120957
|
+
const results = [];
|
|
120958
|
+
for (const customerId of opts.customers) {
|
|
120959
|
+
const url = `${base}/command/media-account/${customerId}/CustomerLinks`;
|
|
120960
|
+
try {
|
|
120961
|
+
const response = await apiFetch2(
|
|
120962
|
+
url,
|
|
120963
|
+
config,
|
|
120964
|
+
{ method: "POST", body: JSON.stringify(body) },
|
|
120965
|
+
opts.verbose
|
|
120966
|
+
);
|
|
120967
|
+
results.push({ customerId, response });
|
|
120968
|
+
} catch (err) {
|
|
120969
|
+
console.error(
|
|
120970
|
+
`
|
|
120971
|
+
\u274C \u8D26\u6237 ${customerId} MCC \u7ED1\u5B9A\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
120972
|
+
`
|
|
120973
|
+
);
|
|
120974
|
+
process.exit(1);
|
|
120975
|
+
}
|
|
120976
|
+
}
|
|
120977
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
120978
|
+
section: "account-mcc-bind",
|
|
120979
|
+
commandLabel: "account mcc-bind",
|
|
120980
|
+
payload: results,
|
|
120981
|
+
idSuffix: opts.mcc
|
|
120982
|
+
})) {
|
|
120983
|
+
return;
|
|
120984
|
+
}
|
|
120985
|
+
let hasFailure = false;
|
|
120986
|
+
for (const { customerId, response } of results) {
|
|
120987
|
+
const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
|
|
120988
|
+
if (failed.length > 0) {
|
|
120989
|
+
hasFailure = true;
|
|
120990
|
+
console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
|
|
120991
|
+
} else {
|
|
120992
|
+
console.log(
|
|
120993
|
+
` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u7ED1\u5B9A\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
|
|
120994
|
+
);
|
|
120995
|
+
}
|
|
120996
|
+
}
|
|
120997
|
+
console.log(
|
|
120998
|
+
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"
|
|
120999
|
+
);
|
|
121000
|
+
}
|
|
121001
|
+
async function runAccountMccUnbind(opts) {
|
|
121002
|
+
const config = loadConfig(opts.token);
|
|
121003
|
+
const base = googleGatewayBase(config);
|
|
121004
|
+
const managerIds = parseMccManagerIds(opts.mcc);
|
|
121005
|
+
if (managerIds.length === 0) {
|
|
121006
|
+
console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
|
|
121007
|
+
process.exit(1);
|
|
121008
|
+
}
|
|
121009
|
+
if (opts.customers.length === 0) {
|
|
121010
|
+
console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
|
|
121011
|
+
process.exit(1);
|
|
121012
|
+
}
|
|
121013
|
+
const results = [];
|
|
121014
|
+
for (const customerId of opts.customers) {
|
|
121015
|
+
const url = `${base}/command/media-account/${customerId}/CustomerUnlinks`;
|
|
121016
|
+
try {
|
|
121017
|
+
const response = await apiFetch2(
|
|
121018
|
+
url,
|
|
121019
|
+
config,
|
|
121020
|
+
{ method: "POST", body: JSON.stringify(managerIds) },
|
|
121021
|
+
opts.verbose
|
|
121022
|
+
);
|
|
121023
|
+
results.push({ customerId, response });
|
|
121024
|
+
} catch (err) {
|
|
121025
|
+
console.error(
|
|
121026
|
+
`
|
|
121027
|
+
\u274C \u8D26\u6237 ${customerId} MCC \u89E3\u7ED1\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
121028
|
+
`
|
|
121029
|
+
);
|
|
121030
|
+
process.exit(1);
|
|
121031
|
+
}
|
|
121032
|
+
}
|
|
121033
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
121034
|
+
section: "account-mcc-unbind",
|
|
121035
|
+
commandLabel: "account mcc-unbind",
|
|
121036
|
+
payload: results,
|
|
121037
|
+
idSuffix: opts.mcc
|
|
121038
|
+
})) {
|
|
121039
|
+
return;
|
|
121040
|
+
}
|
|
121041
|
+
let hasFailure = false;
|
|
121042
|
+
for (const { customerId, response } of results) {
|
|
121043
|
+
const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
|
|
121044
|
+
if (failed.length > 0) {
|
|
121045
|
+
hasFailure = true;
|
|
121046
|
+
console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u89E3\u7ED1\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
|
|
121047
|
+
} else {
|
|
121048
|
+
console.log(
|
|
121049
|
+
` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u89E3\u7ED1\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
|
|
121050
|
+
);
|
|
121051
|
+
}
|
|
121052
|
+
}
|
|
121053
|
+
console.log(
|
|
121054
|
+
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"
|
|
121055
|
+
);
|
|
121056
|
+
}
|
|
121057
|
+
|
|
121058
|
+
// src/commands/account-manage/google-access-permission.ts
|
|
121059
|
+
function assertGoogleMediaCustomerId(raw) {
|
|
121060
|
+
const id = raw.trim();
|
|
121061
|
+
if (!id) {
|
|
121062
|
+
console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
|
|
121063
|
+
process.exit(1);
|
|
121064
|
+
}
|
|
121065
|
+
if (!/^\d+$/.test(id)) {
|
|
121066
|
+
console.error("\n\u274C --account \u987B\u4E3A Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09\u3002\n");
|
|
121067
|
+
process.exit(1);
|
|
121068
|
+
}
|
|
121069
|
+
return id;
|
|
121070
|
+
}
|
|
121071
|
+
function parseCustomerAccessPermissionBody(text) {
|
|
121072
|
+
const trimmed = text.trim();
|
|
121073
|
+
if (!trimmed) return null;
|
|
121074
|
+
try {
|
|
121075
|
+
const parsed = JSON.parse(trimmed);
|
|
121076
|
+
if (typeof parsed === "boolean") return parsed;
|
|
121077
|
+
if (typeof parsed === "string") return parsed;
|
|
121078
|
+
} catch {
|
|
121079
|
+
}
|
|
121080
|
+
return trimmed;
|
|
121081
|
+
}
|
|
121082
|
+
function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody) {
|
|
121083
|
+
const base = {
|
|
121084
|
+
mediaCustomerId,
|
|
121085
|
+
httpStatus,
|
|
121086
|
+
rawBody
|
|
121087
|
+
};
|
|
121088
|
+
if (httpStatus === 200) {
|
|
121089
|
+
if (rawBody === true) {
|
|
121090
|
+
return {
|
|
121091
|
+
...base,
|
|
121092
|
+
status: "accessible",
|
|
121093
|
+
accessible: true,
|
|
121094
|
+
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u53EF\u8BBF\u95EE\u8BE5 Google \u5E7F\u544A\u8D26\u6237\u3002"
|
|
121095
|
+
};
|
|
121096
|
+
}
|
|
121097
|
+
if (rawBody === false) {
|
|
121098
|
+
return {
|
|
121099
|
+
...base,
|
|
121100
|
+
status: "reauth_required",
|
|
121101
|
+
accessible: false,
|
|
121102
|
+
message: "\u8D26\u6237\u5DF2\u7ED1\u5B9A\u4F46 Google OAuth \u4E0D\u53EF\u7528\uFF0C\u9700\u91CD\u65B0\u6388\u6743\u3002",
|
|
121103
|
+
hint: "list-accounts --json-out \u53D6 ma.entityId \u540E\u6267\u884C\uFF1Asiluzan-tso account reauth -m Google --id <entityId>"
|
|
121104
|
+
};
|
|
121105
|
+
}
|
|
121106
|
+
return {
|
|
121107
|
+
...base,
|
|
121108
|
+
status: "unknown",
|
|
121109
|
+
accessible: false,
|
|
121110
|
+
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`
|
|
121111
|
+
};
|
|
121112
|
+
}
|
|
121113
|
+
if (httpStatus === 403) {
|
|
121114
|
+
const text = typeof rawBody === "string" ? rawBody : rawBody == null ? "" : String(rawBody);
|
|
121115
|
+
if (text.includes("token\u4E0D\u80FD\u4E3A\u7A7A") || text.toLowerCase().includes("token") && text.includes("\u7A7A")) {
|
|
121116
|
+
return {
|
|
121117
|
+
...base,
|
|
121118
|
+
status: "google_not_bound",
|
|
121119
|
+
accessible: false,
|
|
121120
|
+
message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u672A\u7ED1\u5B9A Google \u5A92\u4F53\u6388\u6743\u3002",
|
|
121121
|
+
hint: "\u6267\u884C siluzan-tso account auth -m Google \u5B8C\u6210 OAuth \u7ED1\u5B9A\u3002"
|
|
121122
|
+
};
|
|
121123
|
+
}
|
|
121124
|
+
return {
|
|
121125
|
+
...base,
|
|
121126
|
+
status: "no_permission",
|
|
121127
|
+
accessible: false,
|
|
121128
|
+
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",
|
|
121129
|
+
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"
|
|
121130
|
+
};
|
|
121131
|
+
}
|
|
121132
|
+
if (httpStatus === 401) {
|
|
121133
|
+
return {
|
|
121134
|
+
...base,
|
|
121135
|
+
status: "siluzan_token_invalid",
|
|
121136
|
+
accessible: false,
|
|
121137
|
+
message: "\u4E1D\u8DEF\u8D5E\u767B\u5F55\u51ED\u636E\u65E0\u6548\u6216\u5DF2\u8FC7\u671F\u3002",
|
|
121138
|
+
hint: "\u6267\u884C send-login-code + login --phone --code \u91CD\u65B0\u767B\u5F55\uFF0C\u6216 siluzan-tso config show \u68C0\u67E5 Token\u3002"
|
|
121139
|
+
};
|
|
121140
|
+
}
|
|
121141
|
+
return {
|
|
121142
|
+
...base,
|
|
121143
|
+
status: "unknown",
|
|
121144
|
+
accessible: false,
|
|
121145
|
+
message: `\u672A\u9884\u671F\u7684 HTTP ${httpStatus}\uFF1B\u8BF7\u7528 --verbose \u67E5\u770B\u539F\u59CB\u54CD\u5E94\u3002`
|
|
121146
|
+
};
|
|
121147
|
+
}
|
|
121148
|
+
function buildCustomerAccessPermissionUrl(googleApiUrl, mediaCustomerId) {
|
|
121149
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
121150
|
+
return `${base}/query/media-account/${mediaCustomerId}/customer-access-permission`;
|
|
121151
|
+
}
|
|
121152
|
+
async function fetchGoogleCustomerAccessPermission(config, mediaCustomerId, verbose) {
|
|
121153
|
+
await ensureDataPermission(config);
|
|
121154
|
+
const url = buildCustomerAccessPermissionUrl(googleGatewayBase(config), mediaCustomerId);
|
|
121155
|
+
const res = await rawRequest(url, {
|
|
121156
|
+
method: "GET",
|
|
121157
|
+
headers: {
|
|
121158
|
+
"Content-Type": "application/json",
|
|
121159
|
+
"Accept-Language": "zh-CN",
|
|
121160
|
+
Accept: "application/json",
|
|
121161
|
+
...buildSiluzanAuthHeaders(config),
|
|
121162
|
+
Datapermission: config.dataPermission ?? ""
|
|
121163
|
+
}
|
|
121164
|
+
});
|
|
121165
|
+
if (verbose && res.text) {
|
|
121166
|
+
console.error(` [verbose] HTTP ${res.status} body: ${res.text.slice(0, 300)}`);
|
|
121167
|
+
}
|
|
121168
|
+
const rawBody = parseCustomerAccessPermissionBody(res.text);
|
|
121169
|
+
return interpretCustomerAccessPermission(mediaCustomerId, res.status, rawBody);
|
|
121170
|
+
}
|
|
121171
|
+
async function runAccountCheckAccess(opts) {
|
|
121172
|
+
const mediaCustomerId = assertGoogleMediaCustomerId(opts.account);
|
|
121173
|
+
const config = loadConfig(opts.token);
|
|
121174
|
+
const payload = await fetchGoogleCustomerAccessPermission(
|
|
121175
|
+
config,
|
|
121176
|
+
mediaCustomerId,
|
|
121177
|
+
opts.verbose
|
|
121178
|
+
);
|
|
121179
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
121180
|
+
section: "account-check-access",
|
|
121181
|
+
commandLabel: "account check-access",
|
|
121182
|
+
payload
|
|
121183
|
+
})) {
|
|
121184
|
+
if (!payload.accessible) {
|
|
121185
|
+
process.exit(1);
|
|
121186
|
+
}
|
|
121187
|
+
return;
|
|
121188
|
+
}
|
|
121189
|
+
const statusLabel = {
|
|
121190
|
+
accessible: "\u53EF\u8BBF\u95EE",
|
|
121191
|
+
reauth_required: "\u9700\u91CD\u65B0\u6388\u6743",
|
|
121192
|
+
no_permission: "\u65E0\u6743\u9650",
|
|
121193
|
+
google_not_bound: "\u672A\u7ED1\u5B9A Google",
|
|
121194
|
+
siluzan_token_invalid: "\u4E1D\u8DEF\u8D5E\u51ED\u636E\u5931\u6548",
|
|
121195
|
+
unknown: "\u672A\u77E5"
|
|
121196
|
+
};
|
|
121197
|
+
console.log(`
|
|
121198
|
+
Google \u8D26\u6237\u8BBF\u95EE\u6743\u9650\u6821\u9A8C ${mediaCustomerId}`);
|
|
121199
|
+
console.log(` \u72B6\u6001 : ${statusLabel[payload.status]} (${payload.status})`);
|
|
121200
|
+
console.log(` HTTP : ${payload.httpStatus}`);
|
|
121201
|
+
console.log(` \u53EF\u8BBF\u95EE : ${payload.accessible ? "\u662F" : "\u5426"}`);
|
|
121202
|
+
console.log(` \u8BF4\u660E : ${payload.message}`);
|
|
121203
|
+
if (payload.hint) {
|
|
121204
|
+
console.log(` \u5EFA\u8BAE : ${payload.hint}`);
|
|
121205
|
+
}
|
|
121206
|
+
console.log();
|
|
121207
|
+
if (!payload.accessible) {
|
|
121208
|
+
process.exit(1);
|
|
121209
|
+
}
|
|
121210
|
+
}
|
|
121211
|
+
|
|
120854
121212
|
// src/commands/account-manage/share.ts
|
|
120855
121213
|
init_auth();
|
|
120856
121214
|
init_cli_json_snapshot();
|
|
@@ -121075,150 +121433,6 @@ async function runAccountReauth(opts) {
|
|
|
121075
121433
|
});
|
|
121076
121434
|
}
|
|
121077
121435
|
|
|
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
121436
|
// src/commands/account-manage/tiktok-close.ts
|
|
121223
121437
|
init_auth();
|
|
121224
121438
|
init_cli_json_snapshot();
|
|
@@ -121988,6 +122202,21 @@ function register23(program2) {
|
|
|
121988
122202
|
});
|
|
121989
122203
|
}
|
|
121990
122204
|
);
|
|
122205
|
+
accountCmd.command("check-access").description(
|
|
122206
|
+
"\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"
|
|
122207
|
+
).requiredOption(
|
|
122208
|
+
"-a, --account <mediaCustomerId>",
|
|
122209
|
+
"Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09"
|
|
122210
|
+
).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(
|
|
122211
|
+
async (opts) => {
|
|
122212
|
+
await runAccountCheckAccess({
|
|
122213
|
+
account: opts.account,
|
|
122214
|
+
token: opts.token,
|
|
122215
|
+
jsonOut: opts.jsonOut,
|
|
122216
|
+
verbose: opts.verbose
|
|
122217
|
+
});
|
|
122218
|
+
}
|
|
122219
|
+
);
|
|
121991
122220
|
accountCmd.command("delink").description(
|
|
121992
122221
|
"\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
122222
|
).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 +125511,12 @@ function sectionRecord(env) {
|
|
|
125282
125511
|
}
|
|
125283
125512
|
|
|
125284
125513
|
// src/commands/google-ads-diagnosis/build-report-data.ts
|
|
125285
|
-
function
|
|
125514
|
+
function num2(value) {
|
|
125286
125515
|
const n = Number(value);
|
|
125287
125516
|
return Number.isFinite(n) ? n : 0;
|
|
125288
125517
|
}
|
|
125289
125518
|
function rowCost(row) {
|
|
125290
|
-
return
|
|
125519
|
+
return num2(row.cost ?? row.spend);
|
|
125291
125520
|
}
|
|
125292
125521
|
function pct(value) {
|
|
125293
125522
|
return `${(value * 100).toFixed(2)}%`;
|
|
@@ -125318,11 +125547,11 @@ function asPeriodRecord(value) {
|
|
|
125318
125547
|
}
|
|
125319
125548
|
function toKeyMetricRow(title, current, previous) {
|
|
125320
125549
|
const currentCost = rowCost(current);
|
|
125321
|
-
const currentClicks =
|
|
125322
|
-
const currentCvr =
|
|
125550
|
+
const currentClicks = num2(current.clicks);
|
|
125551
|
+
const currentCvr = num2(current.conversionRate ?? current.cvr);
|
|
125323
125552
|
const previousCost = previous ? rowCost(previous) : null;
|
|
125324
|
-
const previousClicks = previous ?
|
|
125325
|
-
const previousCvr = previous ?
|
|
125553
|
+
const previousClicks = previous ? num2(previous.clicks) : null;
|
|
125554
|
+
const previousCvr = previous ? num2(previous.conversionRate ?? previous.cvr) : null;
|
|
125326
125555
|
return {
|
|
125327
125556
|
title,
|
|
125328
125557
|
currentCost,
|
|
@@ -125352,13 +125581,13 @@ function buildComparisonItems(currentRows, previousRows, titleFn, keyFn) {
|
|
|
125352
125581
|
}
|
|
125353
125582
|
function toTargetingItem(row, labelKey, label) {
|
|
125354
125583
|
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 :
|
|
125584
|
+
const clicks = num2(row.clicks);
|
|
125585
|
+
const impressions = num2(row.impressions);
|
|
125586
|
+
const conversions = num2(row.conversions);
|
|
125587
|
+
const ctr = num2(row.ctr);
|
|
125588
|
+
const cvr = num2(row.conversionRate ?? row.cvr);
|
|
125589
|
+
const cpc = num2(row.averageCpc ?? row.cpc);
|
|
125590
|
+
const cpa = conversions > 0 ? cost / conversions : num2(row.costPerConversion ?? row.cpa);
|
|
125362
125591
|
return {
|
|
125363
125592
|
[labelKey]: label,
|
|
125364
125593
|
cost,
|
|
@@ -125373,11 +125602,11 @@ function toTargetingItem(row, labelKey, label) {
|
|
|
125373
125602
|
}
|
|
125374
125603
|
function toDailyConversionItem(row) {
|
|
125375
125604
|
const cost = rowCost(row);
|
|
125376
|
-
const conversions =
|
|
125605
|
+
const conversions = num2(row.conversions);
|
|
125377
125606
|
const rawDate = String(row.date ?? "");
|
|
125378
125607
|
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:
|
|
125608
|
+
const cpa = conversions > 0 ? cost / conversions : num2(row.costPerConversion ?? row.cpa);
|
|
125609
|
+
return { date, cost, conversions, cpa, clicks: num2(row.clicks), impressions: num2(row.impressions) };
|
|
125381
125610
|
}
|
|
125382
125611
|
function loadCompanyNameFromSnap(snapDir) {
|
|
125383
125612
|
const file = path21.join(snapDir, "list-accounts-google.json");
|
|
@@ -125414,8 +125643,8 @@ function inferBusinessProfile(campaignRows, adRows, website) {
|
|
|
125414
125643
|
};
|
|
125415
125644
|
}
|
|
125416
125645
|
function primaryConversionLabel(rows) {
|
|
125417
|
-
const sorted = [...rows].sort((a, b) =>
|
|
125418
|
-
const top = sorted.find((r) =>
|
|
125646
|
+
const sorted = [...rows].sort((a, b) => num2(b.allConversions) - num2(a.allConversions));
|
|
125647
|
+
const top = sorted.find((r) => num2(r.allConversions) > 0 && String(r.status) === "Enabled");
|
|
125419
125648
|
if (top) return String(top.name ?? "\u8BE2\u76D8/\u8F6C\u5316");
|
|
125420
125649
|
const enabled = rows.find((r) => String(r.status) === "Enabled");
|
|
125421
125650
|
return enabled ? String(enabled.name ?? "\u8BE2\u76D8/\u8F6C\u5316") : "\u8BE2\u76D8/\u8F6C\u5316";
|
|
@@ -125434,7 +125663,7 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125434
125663
|
const strategy = String(row.biddingStrategyTypeV2 ?? row.biddingStrategyType ?? "UNKNOWN");
|
|
125435
125664
|
const status = String(row.campaignStatus ?? row.campaignStatusV2 ?? "");
|
|
125436
125665
|
const isPaused = /paused/i.test(status);
|
|
125437
|
-
const recommended =
|
|
125666
|
+
const recommended = num2(row.conversions) > 0 ? "MAXIMIZE_CONVERSIONS" : "TARGET_SPEND";
|
|
125438
125667
|
const isCorrect = policyOk && !isPaused && strategy === recommended;
|
|
125439
125668
|
return {
|
|
125440
125669
|
campaignName: String(row.campaignName ?? "\u672A\u547D\u540D\u7CFB\u5217"),
|
|
@@ -125447,16 +125676,16 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
|
|
|
125447
125676
|
});
|
|
125448
125677
|
}
|
|
125449
125678
|
function buildStructureCounts(resourceRecord, campaignRows, keywordRows, searchRows) {
|
|
125450
|
-
const campaignCount = Math.max(
|
|
125451
|
-
const keywordCount = Math.max(
|
|
125679
|
+
const campaignCount = Math.max(num2(resourceRecord?.campaignCount), campaignRows.length);
|
|
125680
|
+
const keywordCount = Math.max(num2(resourceRecord?.keywordCount), keywordRows.length);
|
|
125452
125681
|
return {
|
|
125453
125682
|
campaignCount,
|
|
125454
|
-
adGroupCount:
|
|
125683
|
+
adGroupCount: num2(resourceRecord?.adGroupCount),
|
|
125455
125684
|
keywordCount,
|
|
125456
|
-
adCount:
|
|
125457
|
-
extensionCount:
|
|
125458
|
-
negativeKeywordCount:
|
|
125459
|
-
countriesWithConversionsCount:
|
|
125685
|
+
adCount: num2(resourceRecord?.adCount),
|
|
125686
|
+
extensionCount: num2(resourceRecord?.extensionCount),
|
|
125687
|
+
negativeKeywordCount: num2(resourceRecord?.negativeKeywordCount),
|
|
125688
|
+
countriesWithConversionsCount: num2(resourceRecord?.countriesWithConversionsCount),
|
|
125460
125689
|
searchTermCount: searchRows.length
|
|
125461
125690
|
};
|
|
125462
125691
|
}
|
|
@@ -125470,26 +125699,26 @@ function loadWebsiteFromAds(snapDir, accountId, start, end) {
|
|
|
125470
125699
|
}
|
|
125471
125700
|
function buildKeywordShareItems(rows) {
|
|
125472
125701
|
const totalCost = rows.reduce((sum, row) => sum + rowCost(row), 0);
|
|
125473
|
-
const totalConversions = rows.reduce((sum, row) => sum +
|
|
125702
|
+
const totalConversions = rows.reduce((sum, row) => sum + num2(row.conversions), 0);
|
|
125474
125703
|
return rows.map((row) => {
|
|
125475
125704
|
const cost = rowCost(row);
|
|
125476
|
-
const conversions =
|
|
125705
|
+
const conversions = num2(row.conversions);
|
|
125477
125706
|
const keywordText = String(row.keywordText ?? row.keyword ?? "").trim();
|
|
125478
125707
|
return {
|
|
125479
125708
|
keyword: keywordText || "\u672A\u547D\u540D",
|
|
125480
|
-
cpc:
|
|
125709
|
+
cpc: num2(row.averageCpc ?? row.cpc),
|
|
125481
125710
|
matchType: String(
|
|
125482
125711
|
row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
|
|
125483
125712
|
),
|
|
125484
|
-
impressions:
|
|
125485
|
-
clicks:
|
|
125713
|
+
impressions: num2(row.impressions),
|
|
125714
|
+
clicks: num2(row.clicks),
|
|
125486
125715
|
cost,
|
|
125487
125716
|
conversions,
|
|
125488
125717
|
costShare: totalCost > 0 ? cost / totalCost * 100 : 0,
|
|
125489
125718
|
conversionsShare: totalConversions > 0 ? conversions / totalConversions * 100 : 0,
|
|
125490
125719
|
cpa: conversions > 0 ? cost / conversions : 0,
|
|
125491
|
-
ctr:
|
|
125492
|
-
cvr:
|
|
125720
|
+
ctr: num2(row.ctr),
|
|
125721
|
+
cvr: num2(row.conversionRate ?? row.cvr)
|
|
125493
125722
|
};
|
|
125494
125723
|
});
|
|
125495
125724
|
}
|
|
@@ -125497,17 +125726,17 @@ function buildSearchTermItems(rows) {
|
|
|
125497
125726
|
const sorted = [...rows].sort((a, b) => rowCost(b) - rowCost(a));
|
|
125498
125727
|
return sorted.slice(0, 50).map((row) => {
|
|
125499
125728
|
const cost = rowCost(row);
|
|
125500
|
-
const conversions =
|
|
125501
|
-
const cpa = conversions > 0 ?
|
|
125729
|
+
const conversions = num2(row.conversions);
|
|
125730
|
+
const cpa = conversions > 0 ? num2(row.costPerConversion) || cost / conversions : void 0;
|
|
125502
125731
|
return {
|
|
125503
125732
|
searchTerm: String(row.searchTermText ?? row.searchTerm ?? "\u672A\u547D\u540D"),
|
|
125504
125733
|
keyword: String(row.keywordText ?? row.keyword ?? ""),
|
|
125505
125734
|
cost,
|
|
125506
125735
|
conversions,
|
|
125507
|
-
clicks:
|
|
125508
|
-
impressions:
|
|
125509
|
-
ctr:
|
|
125510
|
-
cvr:
|
|
125736
|
+
clicks: num2(row.clicks),
|
|
125737
|
+
impressions: num2(row.impressions),
|
|
125738
|
+
ctr: num2(row.ctr),
|
|
125739
|
+
cvr: num2(row.conversionRate ?? row.cvr),
|
|
125511
125740
|
cpa
|
|
125512
125741
|
};
|
|
125513
125742
|
});
|
|
@@ -125549,18 +125778,18 @@ function buildAdCreativeItems(rows) {
|
|
|
125549
125778
|
}
|
|
125550
125779
|
function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
|
|
125551
125780
|
const current = asPeriodRecord(overviewRecord?.currentPeriod);
|
|
125552
|
-
const totalCost =
|
|
125553
|
-
const activeDays =
|
|
125554
|
-
const avgDaily =
|
|
125555
|
-
const remainingBudget =
|
|
125781
|
+
const totalCost = num2(overviewRecord?.totalCost ?? current?.spend);
|
|
125782
|
+
const activeDays = num2(overviewRecord?.activeDays);
|
|
125783
|
+
const avgDaily = num2(overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0));
|
|
125784
|
+
const remainingBudget = num2(overviewRecord?.remainingAccountBudget);
|
|
125556
125785
|
const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
|
|
125557
|
-
const sis =
|
|
125786
|
+
const sis = num2(
|
|
125558
125787
|
dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
|
|
125559
125788
|
);
|
|
125560
|
-
const budgetLost =
|
|
125789
|
+
const budgetLost = num2(
|
|
125561
125790
|
dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
|
|
125562
125791
|
);
|
|
125563
|
-
const rankLost =
|
|
125792
|
+
const rankLost = num2(
|
|
125564
125793
|
dimensionRecord?.searchRankLostImpressionShare ?? current?.searchRankLostImpressionShare
|
|
125565
125794
|
);
|
|
125566
125795
|
return [
|
|
@@ -125646,17 +125875,17 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125646
125875
|
};
|
|
125647
125876
|
data.reportDate = end;
|
|
125648
125877
|
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 :
|
|
125878
|
+
const impressions = num2(currentPeriod?.impressions);
|
|
125879
|
+
const clicks = num2(currentPeriod?.clicks);
|
|
125880
|
+
const conversions = num2(currentPeriod?.conversions);
|
|
125881
|
+
const ctr = num2(currentPeriod?.ctr);
|
|
125882
|
+
const cvr = num2(currentPeriod?.conversionRate ?? currentPeriod?.cvr);
|
|
125883
|
+
const cpa = conversions > 0 ? cost / conversions : num2(currentPeriod?.costPerConversion);
|
|
125655
125884
|
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 :
|
|
125885
|
+
const prevConversions = previousPeriod ? num2(previousPeriod.conversions) : 0;
|
|
125886
|
+
const prevCtr = previousPeriod ? num2(previousPeriod.ctr) : 0;
|
|
125887
|
+
const prevCvr = previousPeriod ? num2(previousPeriod.conversionRate ?? previousPeriod.cvr) : 0;
|
|
125888
|
+
const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions : num2(previousPeriod?.costPerConversion);
|
|
125660
125889
|
data.metrics = {
|
|
125661
125890
|
cost,
|
|
125662
125891
|
impressions,
|
|
@@ -125692,8 +125921,8 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125692
125921
|
items: conversionRows.map((row) => ({
|
|
125693
125922
|
name: String(row.name ?? row.eventName ?? "\u672A\u77E5\u4E8B\u4EF6"),
|
|
125694
125923
|
status: String(row.status ?? ""),
|
|
125695
|
-
allConversions:
|
|
125696
|
-
allConversionsValue:
|
|
125924
|
+
allConversions: num2(row.allConversions),
|
|
125925
|
+
allConversionsValue: num2(row.allConversionsValue)
|
|
125697
125926
|
}))
|
|
125698
125927
|
};
|
|
125699
125928
|
const campaignPrevious = sectionItems(
|
|
@@ -125777,16 +126006,16 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125777
126006
|
const lighthouse = opts.landingPage ?? {};
|
|
125778
126007
|
data.landingPageAnalysis = {
|
|
125779
126008
|
desktop: {
|
|
125780
|
-
score:
|
|
125781
|
-
firstContentfulPaint:
|
|
125782
|
-
firstMeaningfulPaint:
|
|
125783
|
-
speedIndex:
|
|
126009
|
+
score: num2(lighthouse.desktop?.score),
|
|
126010
|
+
firstContentfulPaint: num2(lighthouse.desktop?.firstContentfulPaint),
|
|
126011
|
+
firstMeaningfulPaint: num2(lighthouse.desktop?.firstMeaningfulPaint),
|
|
126012
|
+
speedIndex: num2(lighthouse.desktop?.speedIndex)
|
|
125784
126013
|
},
|
|
125785
126014
|
mobile: {
|
|
125786
|
-
score:
|
|
125787
|
-
firstContentfulPaint:
|
|
125788
|
-
firstMeaningfulPaint:
|
|
125789
|
-
speedIndex:
|
|
126015
|
+
score: num2(lighthouse.mobile?.score),
|
|
126016
|
+
firstContentfulPaint: num2(lighthouse.mobile?.firstContentfulPaint),
|
|
126017
|
+
firstMeaningfulPaint: num2(lighthouse.mobile?.firstMeaningfulPaint),
|
|
126018
|
+
speedIndex: num2(lighthouse.mobile?.speedIndex)
|
|
125790
126019
|
}
|
|
125791
126020
|
};
|
|
125792
126021
|
const keywordShareItems = buildKeywordShareItems(kwCurrent);
|
|
@@ -125820,7 +126049,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125820
126049
|
data.newFeatures = {
|
|
125821
126050
|
items: nfItems.map((item) => {
|
|
125822
126051
|
const strategy = String(item.strategy ?? "");
|
|
125823
|
-
const enabled = strategy === "PerformanceMax" ?
|
|
126052
|
+
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
126053
|
return {
|
|
125825
126054
|
...item,
|
|
125826
126055
|
accountStatus: enabled,
|
|
@@ -125830,7 +126059,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125830
126059
|
...emptyModuleNarrative()
|
|
125831
126060
|
};
|
|
125832
126061
|
data.summary = emptySummary();
|
|
125833
|
-
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 &&
|
|
126062
|
+
const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 && num2(r.conversions) === 0);
|
|
125834
126063
|
const agentBrief = buildGoogleAdsDiagnosisAgentBrief({
|
|
125835
126064
|
accountId,
|
|
125836
126065
|
period: formatPeriod(start, end),
|
|
@@ -125854,9 +126083,9 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125854
126083
|
prevCpa
|
|
125855
126084
|
},
|
|
125856
126085
|
dimension: {
|
|
125857
|
-
searchImpressionShare:
|
|
125858
|
-
searchBudgetLostImpressionShare:
|
|
125859
|
-
searchRankLostImpressionShare:
|
|
126086
|
+
searchImpressionShare: num2(dimensionRecord?.searchImpressionShare),
|
|
126087
|
+
searchBudgetLostImpressionShare: num2(dimensionRecord?.searchBudgetLostImpressionShare),
|
|
126088
|
+
searchRankLostImpressionShare: num2(dimensionRecord?.searchRankLostImpressionShare)
|
|
125860
126089
|
},
|
|
125861
126090
|
structure: structureCounts,
|
|
125862
126091
|
goldAccount: goldRecord,
|
|
@@ -126502,7 +126731,7 @@ var PHASE_LABEL = {
|
|
|
126502
126731
|
"find-winner": "\u627E\u8D62\u5BB6",
|
|
126503
126732
|
scale: "\u653E\u91CF"
|
|
126504
126733
|
};
|
|
126505
|
-
function
|
|
126734
|
+
function num3(value) {
|
|
126506
126735
|
const n = Number(value);
|
|
126507
126736
|
return Number.isFinite(n) ? n : void 0;
|
|
126508
126737
|
}
|
|
@@ -126513,35 +126742,35 @@ function money(value, currency = "USD") {
|
|
|
126513
126742
|
}
|
|
126514
126743
|
function adSetsWithSpend(tables) {
|
|
126515
126744
|
const rows = Array.isArray(tables?.adSets) ? tables.adSets : [];
|
|
126516
|
-
return rows.filter((r) => (
|
|
126745
|
+
return rows.filter((r) => (num3(r.spend) ?? 0) > 0);
|
|
126517
126746
|
}
|
|
126518
126747
|
function topAdSetConcentration(adSets) {
|
|
126519
126748
|
if (!adSets.length) return null;
|
|
126520
|
-
const totalSpend = adSets.reduce((s, r) => s + (
|
|
126521
|
-
const totalResults = adSets.reduce((s, r) => s + (
|
|
126749
|
+
const totalSpend = adSets.reduce((s, r) => s + (num3(r.spend) ?? 0), 0);
|
|
126750
|
+
const totalResults = adSets.reduce((s, r) => s + (num3(r.results) ?? 0), 0);
|
|
126522
126751
|
let best = adSets[0];
|
|
126523
|
-
let bestSpend =
|
|
126752
|
+
let bestSpend = num3(best.spend) ?? 0;
|
|
126524
126753
|
for (const r of adSets) {
|
|
126525
|
-
const sp =
|
|
126754
|
+
const sp = num3(r.spend) ?? 0;
|
|
126526
126755
|
if (sp > bestSpend) {
|
|
126527
126756
|
best = r;
|
|
126528
126757
|
bestSpend = sp;
|
|
126529
126758
|
}
|
|
126530
126759
|
}
|
|
126531
|
-
const results =
|
|
126760
|
+
const results = num3(best.results) ?? 0;
|
|
126532
126761
|
return {
|
|
126533
126762
|
name: String(best.adGroupName ?? best.name ?? "\u2014"),
|
|
126534
126763
|
spendShare: totalSpend > 0 ? bestSpend / totalSpend : 0,
|
|
126535
126764
|
resultsShare: totalResults > 0 ? results / totalResults : 0,
|
|
126536
|
-
cpl:
|
|
126765
|
+
cpl: num3(best.costPerResult)
|
|
126537
126766
|
};
|
|
126538
126767
|
}
|
|
126539
126768
|
function winnerSpendShare(adSets, avgCpl, totalSpend) {
|
|
126540
126769
|
if (totalSpend <= 0 || avgCpl <= 0) return 0;
|
|
126541
126770
|
const winnerSpend = adSets.filter((r) => {
|
|
126542
|
-
const cpl =
|
|
126771
|
+
const cpl = num3(r.costPerResult);
|
|
126543
126772
|
return cpl != null && cpl <= avgCpl * 0.9;
|
|
126544
|
-
}).reduce((s, r) => s + (
|
|
126773
|
+
}).reduce((s, r) => s + (num3(r.spend) ?? 0), 0);
|
|
126545
126774
|
return winnerSpend / totalSpend;
|
|
126546
126775
|
}
|
|
126547
126776
|
function countActiveDimensions(charts, tables) {
|
|
@@ -126558,7 +126787,7 @@ function asArray(value) {
|
|
|
126558
126787
|
}
|
|
126559
126788
|
function inferLifecyclePhase(input) {
|
|
126560
126789
|
const { avgCpl, currency = "USD" } = input;
|
|
126561
|
-
const spend =
|
|
126790
|
+
const spend = num3(input.spend) ?? 0;
|
|
126562
126791
|
const adSets = adSetsWithSpend(input.tables);
|
|
126563
126792
|
const activeDims = countActiveDimensions(input.charts, input.tables);
|
|
126564
126793
|
const winShare = winnerSpendShare(adSets, avgCpl, spend);
|
|
@@ -126617,7 +126846,7 @@ function ensureLifecycleFromSnapshot(healthDiagnosis, merged) {
|
|
|
126617
126846
|
}
|
|
126618
126847
|
|
|
126619
126848
|
// src/commands/facebook-analysis/scorecard-autofill.ts
|
|
126620
|
-
function
|
|
126849
|
+
function num4(value) {
|
|
126621
126850
|
const n = Number(value);
|
|
126622
126851
|
return Number.isFinite(n) ? n : void 0;
|
|
126623
126852
|
}
|
|
@@ -126657,8 +126886,8 @@ function buildScorecardFromMergedData(input) {
|
|
|
126657
126886
|
if (pLabels.length) {
|
|
126658
126887
|
const indexed = pLabels.map((label, i) => ({
|
|
126659
126888
|
label: String(label),
|
|
126660
|
-
cpl:
|
|
126661
|
-
spend:
|
|
126889
|
+
cpl: num4(pCpl[i]),
|
|
126890
|
+
spend: num4(pSpend[i]) ?? 0
|
|
126662
126891
|
})).filter((x) => x.spend > 0 || x.cpl != null && x.cpl > 0);
|
|
126663
126892
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? Infinity) - (b.cpl ?? Infinity));
|
|
126664
126893
|
if (sorted[0]) {
|
|
@@ -126683,7 +126912,7 @@ function buildScorecardFromMergedData(input) {
|
|
|
126683
126912
|
const cLabels = Array.isArray(country?.labels) ? country.labels : [];
|
|
126684
126913
|
const cCpl = Array.isArray(country?.cpl) ? country.cpl : [];
|
|
126685
126914
|
if (cLabels.length) {
|
|
126686
|
-
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl:
|
|
126915
|
+
const indexed = cLabels.map((label, i) => ({ label: String(label), cpl: num4(cCpl[i]) })).filter((x) => x.cpl != null && x.cpl > 0);
|
|
126687
126916
|
const sorted = [...indexed].sort((a, b) => (a.cpl ?? 0) - (b.cpl ?? 0));
|
|
126688
126917
|
if (sorted[0]) {
|
|
126689
126918
|
const s = cplSignal(sorted[0].cpl, avgCpl);
|
|
@@ -126704,24 +126933,24 @@ function buildScorecardFromMergedData(input) {
|
|
|
126704
126933
|
}
|
|
126705
126934
|
}
|
|
126706
126935
|
const adSets = Array.isArray(tables.adSets) ? tables.adSets : [];
|
|
126707
|
-
const adWithSpend = adSets.filter((g) => (
|
|
126936
|
+
const adWithSpend = adSets.filter((g) => (num4(g.spend) ?? 0) > 0);
|
|
126708
126937
|
if (adWithSpend.length) {
|
|
126709
126938
|
const sorted = [...adWithSpend].sort(
|
|
126710
|
-
(a, b) => (
|
|
126939
|
+
(a, b) => (num4(a.costPerResult) ?? Infinity) - (num4(b.costPerResult) ?? Infinity)
|
|
126711
126940
|
);
|
|
126712
126941
|
const best = sorted[0];
|
|
126713
|
-
const sBest = cplSignal(
|
|
126942
|
+
const sBest = cplSignal(num4(best.costPerResult), avgCpl);
|
|
126714
126943
|
pushRow(rows, seen, {
|
|
126715
126944
|
item: String(best.name ?? "\u5E7F\u544A\u7EC4"),
|
|
126716
|
-
data: `CPL ${money2(
|
|
126945
|
+
data: `CPL ${money2(num4(best.costPerResult), currency)}`,
|
|
126717
126946
|
...sBest
|
|
126718
126947
|
});
|
|
126719
126948
|
if (sorted.length > 1) {
|
|
126720
126949
|
const worst = sorted[sorted.length - 1];
|
|
126721
|
-
const sWorst = cplSignal(
|
|
126950
|
+
const sWorst = cplSignal(num4(worst.costPerResult), avgCpl);
|
|
126722
126951
|
pushRow(rows, seen, {
|
|
126723
126952
|
item: String(worst.name ?? "\u5E7F\u544A\u7EC4"),
|
|
126724
|
-
data: `CPL ${money2(
|
|
126953
|
+
data: `CPL ${money2(num4(worst.costPerResult), currency)}`,
|
|
126725
126954
|
...sWorst
|
|
126726
126955
|
});
|
|
126727
126956
|
}
|
|
@@ -126729,18 +126958,18 @@ function buildScorecardFromMergedData(input) {
|
|
|
126729
126958
|
const topAud = Array.isArray(tables.audienceTop) ? tables.audienceTop : [];
|
|
126730
126959
|
const bottomAud = Array.isArray(tables.audienceBottom) ? tables.audienceBottom : [];
|
|
126731
126960
|
if (topAud[0]) {
|
|
126732
|
-
const s = cplSignal(
|
|
126961
|
+
const s = cplSignal(num4(topAud[0].costPerResult), avgCpl);
|
|
126733
126962
|
pushRow(rows, seen, {
|
|
126734
126963
|
item: `\u53D7\u4F17\xB7${String(topAud[0].label ?? "Top")}`,
|
|
126735
|
-
data: `CPL ${money2(
|
|
126964
|
+
data: `CPL ${money2(num4(topAud[0].costPerResult), currency)}`,
|
|
126736
126965
|
...s
|
|
126737
126966
|
});
|
|
126738
126967
|
}
|
|
126739
126968
|
if (bottomAud[0]) {
|
|
126740
|
-
const s = cplSignal(
|
|
126969
|
+
const s = cplSignal(num4(bottomAud[0].costPerResult), avgCpl);
|
|
126741
126970
|
pushRow(rows, seen, {
|
|
126742
126971
|
item: `\u53D7\u4F17\xB7${String(bottomAud[0].label ?? "Bottom")}`,
|
|
126743
|
-
data: `CPL ${money2(
|
|
126972
|
+
data: `CPL ${money2(num4(bottomAud[0].costPerResult), currency)}`,
|
|
126744
126973
|
...s
|
|
126745
126974
|
});
|
|
126746
126975
|
}
|
|
@@ -126784,7 +127013,7 @@ function asRecord8(value) {
|
|
|
126784
127013
|
}
|
|
126785
127014
|
return null;
|
|
126786
127015
|
}
|
|
126787
|
-
function
|
|
127016
|
+
function num5(value) {
|
|
126788
127017
|
const n = Number(value);
|
|
126789
127018
|
return Number.isFinite(n) ? n : void 0;
|
|
126790
127019
|
}
|
|
@@ -126838,8 +127067,8 @@ function aggregatePlatform(networks) {
|
|
|
126838
127067
|
const platform = String(row.publisherPlatform ?? row.network ?? "unknown");
|
|
126839
127068
|
const prev = map.get(platform) ?? { spend: 0, results: 0 };
|
|
126840
127069
|
map.set(platform, {
|
|
126841
|
-
spend: prev.spend + (
|
|
126842
|
-
results: prev.results + (
|
|
127070
|
+
spend: prev.spend + (num5(row.spend) ?? 0),
|
|
127071
|
+
results: prev.results + (num5(row.results) ?? 0)
|
|
126843
127072
|
});
|
|
126844
127073
|
}
|
|
126845
127074
|
const labels = [];
|
|
@@ -126855,10 +127084,10 @@ function aggregatePlatform(networks) {
|
|
|
126855
127084
|
function buildAudienceRows(audiences) {
|
|
126856
127085
|
return audiences.map((a) => ({
|
|
126857
127086
|
label: audienceLabel(a.age, a.gender),
|
|
126858
|
-
spend:
|
|
126859
|
-
results:
|
|
126860
|
-
costPerResult:
|
|
126861
|
-
frequency:
|
|
127087
|
+
spend: num5(a.spend) ?? 0,
|
|
127088
|
+
results: num5(a.results) ?? 0,
|
|
127089
|
+
costPerResult: num5(a.costPerResult) ?? (num5(a.results) ? (num5(a.spend) ?? 0) / (num5(a.results) ?? 1) : 0),
|
|
127090
|
+
frequency: num5(a.frequency)
|
|
126862
127091
|
})).filter((r) => r.spend > 0 || r.results > 0);
|
|
126863
127092
|
}
|
|
126864
127093
|
async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
@@ -126897,7 +127126,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126897
127126
|
}
|
|
126898
127127
|
if (!meta.resultType && current?.resultType) meta.resultType = String(current.resultType);
|
|
126899
127128
|
const charts = { ...payload.charts ?? {} };
|
|
126900
|
-
const avgCpl =
|
|
127129
|
+
const avgCpl = num5(kpis.costPerResult) ?? 0;
|
|
126901
127130
|
if (!charts.platform) {
|
|
126902
127131
|
const platform = sectionMap.get("platform");
|
|
126903
127132
|
const networks = Array.isArray(platform?.networks) ? platform.networks : [];
|
|
@@ -126907,10 +127136,10 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126907
127136
|
const country = sectionMap.get("country");
|
|
126908
127137
|
const countries = Array.isArray(country?.countries) ? country.countries : [];
|
|
126909
127138
|
if (countries.length) {
|
|
126910
|
-
const sorted = [...countries].sort((a, b) => (
|
|
127139
|
+
const sorted = [...countries].sort((a, b) => (num5(b.spend) ?? 0) - (num5(a.spend) ?? 0));
|
|
126911
127140
|
charts.country = {
|
|
126912
127141
|
labels: sorted.map((c) => String(c.countryOrRegion ?? "\u2014")),
|
|
126913
|
-
cpl: sorted.map((c) =>
|
|
127142
|
+
cpl: sorted.map((c) => num5(c.costPerResult) ?? 0)
|
|
126914
127143
|
};
|
|
126915
127144
|
}
|
|
126916
127145
|
}
|
|
@@ -126926,7 +127155,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126926
127155
|
};
|
|
126927
127156
|
}
|
|
126928
127157
|
}
|
|
126929
|
-
if (!charts.funnel &&
|
|
127158
|
+
if (!charts.funnel && num5(kpis.reach) != null) {
|
|
126930
127159
|
charts.funnel = { reach: kpis.reach, results: kpis.results ?? 0 };
|
|
126931
127160
|
}
|
|
126932
127161
|
const tables = { ...payload.tables ?? {} };
|
|
@@ -126935,14 +127164,14 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126935
127164
|
const groups = Array.isArray(adSets?.adGroups) ? adSets.adGroups : [];
|
|
126936
127165
|
if (groups.length) {
|
|
126937
127166
|
tables.adSets = groups.map((g) => {
|
|
126938
|
-
const cpl =
|
|
126939
|
-
const freq =
|
|
127167
|
+
const cpl = num5(g.costPerResult);
|
|
127168
|
+
const freq = num5(g.frequency);
|
|
126940
127169
|
const fatigue = fatigueFromFrequency(freq);
|
|
126941
127170
|
const status = statusFromCpl(cpl, avgCpl);
|
|
126942
127171
|
return {
|
|
126943
127172
|
name: String(g.adGroupName ?? g.campaignName ?? "\u2014"),
|
|
126944
|
-
spend:
|
|
126945
|
-
results:
|
|
127173
|
+
spend: num5(g.spend) ?? 0,
|
|
127174
|
+
results: num5(g.results) ?? 0,
|
|
126946
127175
|
costPerResult: cpl ?? 0,
|
|
126947
127176
|
frequency: freq,
|
|
126948
127177
|
fatigueLevel: fatigue.level,
|
|
@@ -126966,7 +127195,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126966
127195
|
}
|
|
126967
127196
|
const currency = typeof meta.currency === "string" ? meta.currency : "USD";
|
|
126968
127197
|
const mergeCtx = {
|
|
126969
|
-
spend:
|
|
127198
|
+
spend: num5(kpis.spend),
|
|
126970
127199
|
avgCpl,
|
|
126971
127200
|
currency,
|
|
126972
127201
|
charts,
|