siluzan-tso-cli 1.1.20-beta.22 → 1.1.20-beta.23
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 +242 -136
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/google-ads-rules/google-ads-campaign-optimization.md +1 -0
- package/dist/skill/references/google-ads.md +19 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
51
51
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
> **注意**:当前为测试版(1.1.20-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.20-beta.23),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -111956,141 +111956,6 @@ async function runAdSearchTerms(opts) {
|
|
|
111956
111956
|
init_auth();
|
|
111957
111957
|
init_cli_json_snapshot();
|
|
111958
111958
|
init_strip_legacy_google_fields();
|
|
111959
|
-
async function runAdGeoSearch(opts) {
|
|
111960
|
-
const config = loadConfig(opts.token);
|
|
111961
|
-
const googleApiUrl = requireGoogleApi(config);
|
|
111962
|
-
const url = `${googleApiUrl}/campaignmanagement/geolocations/${opts.account}/${encodeURIComponent(opts.query)}`;
|
|
111963
|
-
let data;
|
|
111964
|
-
try {
|
|
111965
|
-
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
111966
|
-
} catch (err) {
|
|
111967
|
-
console.error(`
|
|
111968
|
-
\u274C \u641C\u7D22\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
111969
|
-
`);
|
|
111970
|
-
process.exit(1);
|
|
111971
|
-
}
|
|
111972
|
-
const items = Array.isArray(data) ? data : data.data ?? [];
|
|
111973
|
-
console.log(`
|
|
111974
|
-
\u5730\u7406\u4F4D\u7F6E\u641C\u7D22\u7ED3\u679C\u300C${opts.query}\u300D\uFF08\u5171 ${items.length} \u6761\uFF09
|
|
111975
|
-
`);
|
|
111976
|
-
if (items.length === 0) {
|
|
111977
|
-
console.log(" \u672A\u627E\u5230\u5339\u914D\u4F4D\u7F6E\u3002\n");
|
|
111978
|
-
return;
|
|
111979
|
-
}
|
|
111980
|
-
for (const item of items) {
|
|
111981
|
-
const id = String(item["id"] ?? "");
|
|
111982
|
-
const name = String(item["locationName"] ?? item["canonicalName"] ?? item["name"] ?? "");
|
|
111983
|
-
const type = String(item["targetType"] ?? item["typeV2"] ?? "");
|
|
111984
|
-
console.log(` id:${id} ${name} [${type}]`);
|
|
111985
|
-
}
|
|
111986
|
-
console.log();
|
|
111987
|
-
}
|
|
111988
|
-
async function runAdGeoList(opts) {
|
|
111989
|
-
const config = loadConfig(opts.token);
|
|
111990
|
-
const googleApiUrl = requireGoogleApi(config);
|
|
111991
|
-
let url;
|
|
111992
|
-
const params = new URLSearchParams();
|
|
111993
|
-
if (opts.mode === "report") {
|
|
111994
|
-
url = `${googleApiUrl}/geotargetmanagement/v2/list/${opts.account}?${params}`;
|
|
111995
|
-
} else {
|
|
111996
|
-
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
111997
|
-
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
111998
|
-
url = `${googleApiUrl}/campaignmanagement/v2/${opts.mode}locations/${opts.account}?${params}`;
|
|
111999
|
-
}
|
|
112000
|
-
let data;
|
|
112001
|
-
try {
|
|
112002
|
-
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
112003
|
-
} catch (err) {
|
|
112004
|
-
console.error(`
|
|
112005
|
-
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112006
|
-
`);
|
|
112007
|
-
process.exit(1);
|
|
112008
|
-
}
|
|
112009
|
-
const rawData = data.data;
|
|
112010
|
-
let items = Array.isArray(rawData) ? rawData : rawData?.countries ?? [];
|
|
112011
|
-
if (opts.campaignId) {
|
|
112012
|
-
const targetId = opts.campaignId;
|
|
112013
|
-
items = items.filter((item) => String(item["campaignId"] ?? "") === targetId);
|
|
112014
|
-
}
|
|
112015
|
-
const modeLabel = { targeted: "\u5DF2\u5B9A\u4F4D", excluded: "\u5DF2\u6392\u9664", report: "\u6D88\u8017\u62A5\u544A" }[opts.mode];
|
|
112016
|
-
const n = items.length;
|
|
112017
|
-
const geoPayload = stripLegacyGoogleFieldsIfV2Present(
|
|
112018
|
-
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
112019
|
-
);
|
|
112020
|
-
if (await emitCliJsonOrSnapshot(opts, {
|
|
112021
|
-
section: `ad-geo-${opts.mode}-${opts.account}`,
|
|
112022
|
-
commandLabel: "ad geo",
|
|
112023
|
-
commandHint: `${opts.mode}:${opts.account}`,
|
|
112024
|
-
payload: geoPayload,
|
|
112025
|
-
idSuffix: opts.account
|
|
112026
|
-
})) {
|
|
112027
|
-
return;
|
|
112028
|
-
}
|
|
112029
|
-
console.log(
|
|
112030
|
-
`
|
|
112031
|
-
\u5730\u7406\u4F4D\u7F6E\uFF08${modeLabel}\uFF0C\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${items.length} \u6761\uFF0C\u5171 ${items.length} \u6761\uFF09
|
|
112032
|
-
`
|
|
112033
|
-
);
|
|
112034
|
-
if (items.length === 0) {
|
|
112035
|
-
console.log(" \u6682\u65E0\u6570\u636E\u3002\n");
|
|
112036
|
-
return;
|
|
112037
|
-
}
|
|
112038
|
-
for (const item of items) {
|
|
112039
|
-
const id = String(item["id"] ?? item["countryCriteriaId"] ?? "");
|
|
112040
|
-
const name = String(
|
|
112041
|
-
item["name"] ?? item["countryOrRegion"] ?? item["country"] ?? item["location"] ?? item["canonicalName"] ?? ""
|
|
112042
|
-
);
|
|
112043
|
-
const campaign = item["campaignName"] ? ` campaign:${item["campaignName"]}` : "";
|
|
112044
|
-
const bid = item["bidModifier"] != null ? ` \u51FA\u4EF7\u500D\u7387:${item["bidModifier"]}` : "";
|
|
112045
|
-
const spend = item["spend"] != null ? ` \u6D88\u8017:${item["spend"]}` : "";
|
|
112046
|
-
const clicks = item["clicks"] != null ? ` \u70B9\u51FB:${item["clicks"]}` : "";
|
|
112047
|
-
console.log(` id:${id} ${name}${campaign}${bid}${spend}${clicks}`);
|
|
112048
|
-
}
|
|
112049
|
-
console.log();
|
|
112050
|
-
}
|
|
112051
|
-
async function runAdGeoAdd(opts) {
|
|
112052
|
-
const config = loadConfig(opts.token);
|
|
112053
|
-
const googleApiUrl = requireGoogleApi(config);
|
|
112054
|
-
const body = {
|
|
112055
|
-
CampaignId: opts.campaignId,
|
|
112056
|
-
Id: opts.locationId,
|
|
112057
|
-
TypeV2: "Location",
|
|
112058
|
-
BidModifier: opts.bidModifier ?? 0,
|
|
112059
|
-
BidModifierSpecified: opts.bidModifier !== void 0
|
|
112060
|
-
};
|
|
112061
|
-
let url = `${googleApiUrl}/campaignmanagement/criterion/${opts.account}`;
|
|
112062
|
-
const method = opts.exclude ? "POST" : "PUT";
|
|
112063
|
-
if (opts.exclude) url += "?isNegative=true";
|
|
112064
|
-
try {
|
|
112065
|
-
await apiFetch2(url, config, { method, body: JSON.stringify(body) }, opts.verbose);
|
|
112066
|
-
} catch (err) {
|
|
112067
|
-
console.error(`
|
|
112068
|
-
\u274C \u6DFB\u52A0\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112069
|
-
`);
|
|
112070
|
-
process.exit(1);
|
|
112071
|
-
}
|
|
112072
|
-
const label = opts.exclude ? "\u5DF2\u6392\u9664" : "\u5DF2\u6DFB\u52A0\u5B9A\u4F4D";
|
|
112073
|
-
console.log(`
|
|
112074
|
-
\u2705 \u5730\u7406\u4F4D\u7F6E ${opts.locationId} ${label}\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${opts.campaignId}\uFF09
|
|
112075
|
-
`);
|
|
112076
|
-
}
|
|
112077
|
-
async function runAdGeoRemove(opts) {
|
|
112078
|
-
const config = loadConfig(opts.token);
|
|
112079
|
-
const googleApiUrl = requireGoogleApi(config);
|
|
112080
|
-
const body = { campaignId: opts.campaignId, id: opts.locationId };
|
|
112081
|
-
const url = `${googleApiUrl}/campaignmanagement/criterion/${opts.account}?campaignId=${opts.campaignId}`;
|
|
112082
|
-
try {
|
|
112083
|
-
await apiDeleteRaw(url, config, { body: JSON.stringify(body), verbose: opts.verbose });
|
|
112084
|
-
} catch (err) {
|
|
112085
|
-
console.error(`
|
|
112086
|
-
\u274C \u5220\u9664\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112087
|
-
`);
|
|
112088
|
-
process.exit(1);
|
|
112089
|
-
}
|
|
112090
|
-
console.log(`
|
|
112091
|
-
\u2705 \u5730\u7406\u4F4D\u7F6E ${opts.locationId} \u5DF2\u4ECE\u5E7F\u544A\u7CFB\u5217 ${opts.campaignId} \u4E2D\u79FB\u9664
|
|
112092
|
-
`);
|
|
112093
|
-
}
|
|
112094
111959
|
|
|
112095
111960
|
// src/commands/ad/device-bid.ts
|
|
112096
111961
|
init_auth();
|
|
@@ -112323,6 +112188,220 @@ async function runAdDeviceBidSet(opts) {
|
|
|
112323
112188
|
);
|
|
112324
112189
|
}
|
|
112325
112190
|
|
|
112191
|
+
// src/commands/ad/geo.ts
|
|
112192
|
+
async function runAdGeoSearch(opts) {
|
|
112193
|
+
const config = loadConfig(opts.token);
|
|
112194
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
112195
|
+
const url = `${googleApiUrl}/campaignmanagement/geolocations/${opts.account}/${encodeURIComponent(opts.query)}`;
|
|
112196
|
+
let data;
|
|
112197
|
+
try {
|
|
112198
|
+
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
112199
|
+
} catch (err) {
|
|
112200
|
+
console.error(`
|
|
112201
|
+
\u274C \u641C\u7D22\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112202
|
+
`);
|
|
112203
|
+
process.exit(1);
|
|
112204
|
+
}
|
|
112205
|
+
const items = Array.isArray(data) ? data : data.data ?? [];
|
|
112206
|
+
console.log(`
|
|
112207
|
+
\u5730\u7406\u4F4D\u7F6E\u641C\u7D22\u7ED3\u679C\u300C${opts.query}\u300D\uFF08\u5171 ${items.length} \u6761\uFF09
|
|
112208
|
+
`);
|
|
112209
|
+
if (items.length === 0) {
|
|
112210
|
+
console.log(" \u672A\u627E\u5230\u5339\u914D\u4F4D\u7F6E\u3002\n");
|
|
112211
|
+
return;
|
|
112212
|
+
}
|
|
112213
|
+
for (const item of items) {
|
|
112214
|
+
const id = String(item["id"] ?? "");
|
|
112215
|
+
const name = String(item["locationName"] ?? item["canonicalName"] ?? item["name"] ?? "");
|
|
112216
|
+
const type = String(item["targetType"] ?? item["typeV2"] ?? "");
|
|
112217
|
+
console.log(` id:${id} ${name} [${type}]`);
|
|
112218
|
+
}
|
|
112219
|
+
console.log();
|
|
112220
|
+
}
|
|
112221
|
+
async function runAdGeoList(opts) {
|
|
112222
|
+
const config = loadConfig(opts.token);
|
|
112223
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
112224
|
+
let url;
|
|
112225
|
+
const params = new URLSearchParams();
|
|
112226
|
+
if (opts.mode === "report") {
|
|
112227
|
+
url = `${googleApiUrl}/geotargetmanagement/v2/list/${opts.account}?${params}`;
|
|
112228
|
+
} else {
|
|
112229
|
+
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
112230
|
+
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
112231
|
+
url = `${googleApiUrl}/campaignmanagement/v2/${opts.mode}locations/${opts.account}?${params}`;
|
|
112232
|
+
}
|
|
112233
|
+
let data;
|
|
112234
|
+
try {
|
|
112235
|
+
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
112236
|
+
} catch (err) {
|
|
112237
|
+
console.error(`
|
|
112238
|
+
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112239
|
+
`);
|
|
112240
|
+
process.exit(1);
|
|
112241
|
+
}
|
|
112242
|
+
const rawData = data.data;
|
|
112243
|
+
let items = Array.isArray(rawData) ? rawData : rawData?.countries ?? [];
|
|
112244
|
+
if (opts.campaignId) {
|
|
112245
|
+
const targetId = opts.campaignId;
|
|
112246
|
+
items = items.filter((item) => String(item["campaignId"] ?? "") === targetId);
|
|
112247
|
+
}
|
|
112248
|
+
const modeLabel = { targeted: "\u5DF2\u5B9A\u4F4D", excluded: "\u5DF2\u6392\u9664", report: "\u6D88\u8017\u62A5\u544A" }[opts.mode];
|
|
112249
|
+
const n = items.length;
|
|
112250
|
+
const geoPayload = stripLegacyGoogleFieldsIfV2Present(
|
|
112251
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
112252
|
+
);
|
|
112253
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
112254
|
+
section: `ad-geo-${opts.mode}-${opts.account}`,
|
|
112255
|
+
commandLabel: "ad geo",
|
|
112256
|
+
commandHint: `${opts.mode}:${opts.account}`,
|
|
112257
|
+
payload: geoPayload,
|
|
112258
|
+
idSuffix: opts.account
|
|
112259
|
+
})) {
|
|
112260
|
+
return;
|
|
112261
|
+
}
|
|
112262
|
+
console.log(
|
|
112263
|
+
`
|
|
112264
|
+
\u5730\u7406\u4F4D\u7F6E\uFF08${modeLabel}\uFF0C\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${items.length} \u6761\uFF0C\u5171 ${items.length} \u6761\uFF09
|
|
112265
|
+
`
|
|
112266
|
+
);
|
|
112267
|
+
if (items.length === 0) {
|
|
112268
|
+
console.log(" \u6682\u65E0\u6570\u636E\u3002\n");
|
|
112269
|
+
return;
|
|
112270
|
+
}
|
|
112271
|
+
for (const item of items) {
|
|
112272
|
+
const id = String(item["id"] ?? item["countryCriteriaId"] ?? "");
|
|
112273
|
+
const name = String(
|
|
112274
|
+
item["name"] ?? item["countryOrRegion"] ?? item["country"] ?? item["location"] ?? item["canonicalName"] ?? ""
|
|
112275
|
+
);
|
|
112276
|
+
const campaign = item["campaignName"] ? ` campaign:${item["campaignName"]}` : "";
|
|
112277
|
+
const bid = item["bidModifier"] != null && typeof item["bidModifier"] === "number" ? ` \u51FA\u4EF7\u8C03\u6574:${formatBidModifierPercent(item["bidModifier"])}` : item["bidModifier"] != null ? ` \u51FA\u4EF7\u8C03\u6574:${item["bidModifier"]}` : "";
|
|
112278
|
+
const spend = item["spend"] != null ? ` \u6D88\u8017:${item["spend"]}` : "";
|
|
112279
|
+
const clicks = item["clicks"] != null ? ` \u70B9\u51FB:${item["clicks"]}` : "";
|
|
112280
|
+
console.log(` id:${id} ${name}${campaign}${bid}${spend}${clicks}`);
|
|
112281
|
+
}
|
|
112282
|
+
console.log();
|
|
112283
|
+
}
|
|
112284
|
+
async function runAdGeoAdd(opts) {
|
|
112285
|
+
const config = loadConfig(opts.token);
|
|
112286
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
112287
|
+
const body = {
|
|
112288
|
+
CampaignId: opts.campaignId,
|
|
112289
|
+
Id: opts.locationId,
|
|
112290
|
+
TypeV2: "Location",
|
|
112291
|
+
BidModifier: opts.bidModifier !== void 0 ? multiplierToAdGroupBidPercent(opts.bidModifier) : 0,
|
|
112292
|
+
BidModifierSpecified: opts.bidModifier !== void 0
|
|
112293
|
+
};
|
|
112294
|
+
let url = `${googleApiUrl}/campaignmanagement/criterion/${opts.account}`;
|
|
112295
|
+
const method = opts.exclude ? "POST" : "PUT";
|
|
112296
|
+
if (opts.exclude) url += "?isNegative=true";
|
|
112297
|
+
try {
|
|
112298
|
+
await apiFetch2(url, config, { method, body: JSON.stringify(body) }, opts.verbose);
|
|
112299
|
+
} catch (err) {
|
|
112300
|
+
console.error(`
|
|
112301
|
+
\u274C \u6DFB\u52A0\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112302
|
+
`);
|
|
112303
|
+
process.exit(1);
|
|
112304
|
+
}
|
|
112305
|
+
const label = opts.exclude ? "\u5DF2\u6392\u9664" : "\u5DF2\u6DFB\u52A0\u5B9A\u4F4D";
|
|
112306
|
+
console.log(`
|
|
112307
|
+
\u2705 \u5730\u7406\u4F4D\u7F6E ${opts.locationId} ${label}\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${opts.campaignId}\uFF09
|
|
112308
|
+
`);
|
|
112309
|
+
}
|
|
112310
|
+
async function runAdGeoRemove(opts) {
|
|
112311
|
+
const config = loadConfig(opts.token);
|
|
112312
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
112313
|
+
const body = { campaignId: opts.campaignId, id: opts.locationId };
|
|
112314
|
+
const url = `${googleApiUrl}/campaignmanagement/criterion/${opts.account}?campaignId=${opts.campaignId}`;
|
|
112315
|
+
try {
|
|
112316
|
+
await apiDeleteRaw(url, config, { body: JSON.stringify(body), verbose: opts.verbose });
|
|
112317
|
+
} catch (err) {
|
|
112318
|
+
console.error(`
|
|
112319
|
+
\u274C \u5220\u9664\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112320
|
+
`);
|
|
112321
|
+
process.exit(1);
|
|
112322
|
+
}
|
|
112323
|
+
console.log(`
|
|
112324
|
+
\u2705 \u5730\u7406\u4F4D\u7F6E ${opts.locationId} \u5DF2\u4ECE\u5E7F\u544A\u7CFB\u5217 ${opts.campaignId} \u4E2D\u79FB\u9664
|
|
112325
|
+
`);
|
|
112326
|
+
}
|
|
112327
|
+
async function fetchTargetedGeoRows(config, googleApiUrl, account, campaignId, verbose) {
|
|
112328
|
+
const params = new URLSearchParams({
|
|
112329
|
+
startDate: toGoogleDate(void 0, -30),
|
|
112330
|
+
endDate: toGoogleDate(void 0, 0)
|
|
112331
|
+
});
|
|
112332
|
+
const url = `${googleApiUrl}/campaignmanagement/v2/targetedlocations/${account}?${params}`;
|
|
112333
|
+
const data = await apiFetch2(url, config, {}, verbose);
|
|
112334
|
+
const items = Array.isArray(data.data) ? data.data : [];
|
|
112335
|
+
return items.filter((item) => String(item["campaignId"] ?? "") === campaignId);
|
|
112336
|
+
}
|
|
112337
|
+
function resolveGeoCriterionId(rows, opts) {
|
|
112338
|
+
if (opts.criterionId) return opts.criterionId;
|
|
112339
|
+
if (!opts.locationId) {
|
|
112340
|
+
throw new Error("\u8BF7\u6307\u5B9A --criterion-id\uFF0C\u6216\u914D\u5408 --location-id \u4ECE\u5DF2\u5B9A\u5411\u5217\u8868\u81EA\u52A8\u5339\u914D");
|
|
112341
|
+
}
|
|
112342
|
+
const match = rows.find((r) => String(r["id"] ?? "") === opts.locationId);
|
|
112343
|
+
if (!match?.["id"]) {
|
|
112344
|
+
throw new Error(
|
|
112345
|
+
`\u672A\u627E\u5230\u5DF2\u5B9A\u5411\u5730\u7406\u4F4D\u7F6E id=${opts.locationId}\uFF08\u7CFB\u5217 ${opts.campaignId}\uFF09\uFF0C\u8BF7\u5148\u6267\u884C ad geo list --mode targeted`
|
|
112346
|
+
);
|
|
112347
|
+
}
|
|
112348
|
+
return String(match["id"]);
|
|
112349
|
+
}
|
|
112350
|
+
async function runAdGeoSetBid(opts) {
|
|
112351
|
+
const config = loadConfig(opts.token);
|
|
112352
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
112353
|
+
if (opts.bidModifier < 0) {
|
|
112354
|
+
console.error("\n\u274C --bid-modifier \u4E0D\u80FD\u4E3A\u8D1F\u6570\n");
|
|
112355
|
+
process.exit(1);
|
|
112356
|
+
}
|
|
112357
|
+
let rows;
|
|
112358
|
+
try {
|
|
112359
|
+
rows = await fetchTargetedGeoRows(
|
|
112360
|
+
config,
|
|
112361
|
+
googleApiUrl,
|
|
112362
|
+
opts.account,
|
|
112363
|
+
opts.campaignId,
|
|
112364
|
+
opts.verbose
|
|
112365
|
+
);
|
|
112366
|
+
} catch (err) {
|
|
112367
|
+
console.error(`
|
|
112368
|
+
\u274C \u62C9\u53D6\u5DF2\u5B9A\u5411\u5730\u7406\u4F4D\u7F6E\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112369
|
+
`);
|
|
112370
|
+
process.exit(1);
|
|
112371
|
+
}
|
|
112372
|
+
let criterionId;
|
|
112373
|
+
try {
|
|
112374
|
+
criterionId = resolveGeoCriterionId(rows, {
|
|
112375
|
+
criterionId: opts.criterionId,
|
|
112376
|
+
locationId: opts.locationId,
|
|
112377
|
+
campaignId: opts.campaignId
|
|
112378
|
+
});
|
|
112379
|
+
} catch (err) {
|
|
112380
|
+
console.error(`
|
|
112381
|
+
\u274C ${err instanceof Error ? err.message : String(err)}
|
|
112382
|
+
`);
|
|
112383
|
+
process.exit(1);
|
|
112384
|
+
}
|
|
112385
|
+
const putUrl = `${googleApiUrl}/campaignmanagement/${opts.account}/campaigns/${opts.campaignId}/Criteria/${criterionId}/BidModifier/${opts.bidModifier}`;
|
|
112386
|
+
try {
|
|
112387
|
+
await apiFetch2(putUrl, config, { method: "PUT" }, opts.verbose);
|
|
112388
|
+
} catch (err) {
|
|
112389
|
+
console.error(`
|
|
112390
|
+
\u274C \u4FEE\u6539\u5730\u7406\u4F4D\u7F6E\u51FA\u4EF7\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
112391
|
+
`);
|
|
112392
|
+
process.exit(1);
|
|
112393
|
+
}
|
|
112394
|
+
const row = rows.find((r) => String(r["id"] ?? "") === criterionId);
|
|
112395
|
+
const name = String(
|
|
112396
|
+
row?.["locationName"] ?? row?.["name"] ?? row?.["countryOrRegion"] ?? criterionId
|
|
112397
|
+
);
|
|
112398
|
+
console.log(
|
|
112399
|
+
`
|
|
112400
|
+
\u2705 \u5DF2\u66F4\u65B0\u5730\u7406\u4F4D\u7F6E\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / ${name} / id ${criterionId} \u2192 \u500D\u7387 ${opts.bidModifier}\uFF0C${formatBidModifierPercent(opts.bidModifier)}\uFF09
|
|
112401
|
+
`
|
|
112402
|
+
);
|
|
112403
|
+
}
|
|
112404
|
+
|
|
112326
112405
|
// src/commands/ad/ai-creation.ts
|
|
112327
112406
|
init_auth();
|
|
112328
112407
|
init_cli_json_snapshot();
|
|
@@ -114033,7 +114112,7 @@ function register20(program2) {
|
|
|
114033
114112
|
});
|
|
114034
114113
|
}
|
|
114035
114114
|
);
|
|
114036
|
-
const geoCmd = adCmd.command("geo").description("\u5730\u7406\u4F4D\u7F6E\u5B9A\u5411\u7BA1\u7406\uFF08\u641C\u7D22/\u5217\u8868/\u6DFB\u52A0/\u5220\u9664\uFF09");
|
|
114115
|
+
const geoCmd = adCmd.command("geo").description("\u5730\u7406\u4F4D\u7F6E\u5B9A\u5411\u7BA1\u7406\uFF08\u641C\u7D22/\u5217\u8868/\u6DFB\u52A0/\u5220\u9664/\u51FA\u4EF7\u8C03\u6574\uFF09");
|
|
114037
114116
|
geoCmd.command("search").description("\u641C\u7D22\u53EF\u7528\u7684\u5730\u7406\u4F4D\u7F6E\uFF08\u83B7\u53D6 locationId \u7528\u4E8E\u6DFB\u52A0\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("-q, --query <text>", "\u5730\u7406\u4F4D\u7F6E\u540D\u79F0\uFF0C\u5982 Beijing | United States").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
114038
114117
|
await runAdGeoSearch({
|
|
114039
114118
|
token: opts.token,
|
|
@@ -114091,6 +114170,33 @@ function register20(program2) {
|
|
|
114091
114170
|
});
|
|
114092
114171
|
}
|
|
114093
114172
|
);
|
|
114173
|
+
geoCmd.command("set-bid").description(
|
|
114174
|
+
"\u4FEE\u6539\u5DF2\u5B9A\u5411\u5730\u7406\u4F4D\u7F6E\u7684\u51FA\u4EF7\u8C03\u6574\uFF08PUT Criteria/\u2026/BidModifier\uFF1B\u500D\u7387\u53E3\u5F84\u540C ad device-bid set\uFF09"
|
|
114175
|
+
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--campaign-id <id>", "\u5E7F\u544A\u7CFB\u5217 ID").requiredOption(
|
|
114176
|
+
"--bid-modifier <n>",
|
|
114177
|
+
"Google \u51FA\u4EF7\u500D\u7387\uFF1A1.0=\u4E0D\u8C03\u6574\uFF0C1.2=+20%\uFF0C0.8=-20%",
|
|
114178
|
+
parseFloat
|
|
114179
|
+
).option("--criterion-id <id>", "campaign_criterion id\uFF08geo list --mode targeted --json \u2192 id\uFF09").option("--location-id <id>", "\u5730\u7406\u4F4D\u7F6E ID\uFF08\u4E0E list/search \u7684 id \u76F8\u540C\uFF1B\u4E0E --criterion-id \u4E8C\u9009\u4E00\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
114180
|
+
async (opts) => {
|
|
114181
|
+
if (!opts.criterionId && !opts.locationId) {
|
|
114182
|
+
console.error("\n\u274C \u987B\u6307\u5B9A --criterion-id \u6216 --location-id\n");
|
|
114183
|
+
process.exit(1);
|
|
114184
|
+
}
|
|
114185
|
+
if (Number.isNaN(opts.bidModifier)) {
|
|
114186
|
+
console.error("\n\u274C --bid-modifier \u987B\u4E3A\u6709\u6548\u6570\u5B57\n");
|
|
114187
|
+
process.exit(1);
|
|
114188
|
+
}
|
|
114189
|
+
await runAdGeoSetBid({
|
|
114190
|
+
token: opts.token,
|
|
114191
|
+
account: opts.account,
|
|
114192
|
+
campaignId: opts.campaignId,
|
|
114193
|
+
bidModifier: opts.bidModifier,
|
|
114194
|
+
criterionId: opts.criterionId,
|
|
114195
|
+
locationId: opts.locationId,
|
|
114196
|
+
verbose: opts.verbose
|
|
114197
|
+
});
|
|
114198
|
+
}
|
|
114199
|
+
);
|
|
114094
114200
|
const deviceBidCmd = adCmd.command("device-bid").description("\u8BBE\u5907\u51FA\u4EF7\u8C03\u6574\uFF08\u7CFB\u5217\u7EA7 / \u5E7F\u544A\u7EC4\u7EA7\uFF1B\u4E0E\u524D\u7AEF updateDeviceAndAddress \u540C\u6E90\uFF09");
|
|
114095
114201
|
deviceBidCmd.command("list").description("\u67E5\u8BE2\u8BBE\u5907\u51FA\u4EF7\u8C03\u6574\u5217\u8868\uFF08\u7CFB\u5217\u7EA7\u9ED8\u8BA4\uFF1B\u5E7F\u544A\u7EC4\u7EA7\u9700 --level adgroup\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--level <level>", "campaign | adgroup\uFF08\u9ED8\u8BA4 campaign\uFF09", "campaign").option("--campaign-id <id>", "\u53EF\u9009\uFF1A\u6309\u5E7F\u544A\u7CFB\u5217\u8FC7\u6EE4\uFF08\u7CFB\u5217\u7EA7\uFF09\u6216\u5FC5\u586B\u4E0A\u4E0B\u6587\uFF08\u5E7F\u544A\u7EC4\u7EA7\uFF09").option("--ad-group-id <id>", "\u5E7F\u544A\u7EC4 ID\uFF08--level adgroup \u65F6\u5FC5\u586B\uFF09").option("-t, --token <token>", "Auth Token").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).option(
|
|
114096
114202
|
"--json-out <path>",
|
package/dist/skill/_meta.json
CHANGED
|
@@ -249,6 +249,7 @@ siluzan-tso ad geo add -a <CID> --campaign-id <ID> --location-id <ID> --exclude
|
|
|
249
249
|
|
|
250
250
|
# 添加出价调整(+20%)
|
|
251
251
|
siluzan-tso ad geo add -a <CID> --campaign-id <ID> --location-id <ID> --bid-modifier 1.2
|
|
252
|
+
siluzan-tso ad geo set-bid -a <CID> --campaign-id <ID> --location-id <ID> --bid-modifier 1.2
|
|
252
253
|
|
|
253
254
|
# 查看地域效果报告
|
|
254
255
|
siluzan-tso google-analysis -a <CID> --sections geographic
|
|
@@ -521,10 +521,29 @@ siluzan-tso ad geo list -a <accountId> --mode targeted|excluded|report [--start/
|
|
|
521
521
|
# 添加定向
|
|
522
522
|
siluzan-tso ad geo add -a <accountId> --campaign-id <id> --location-id <id> [--bid-modifier 1.2] [--exclude]
|
|
523
523
|
|
|
524
|
+
# 修改已定向地区的出价调整(系列级 campaign_criterion)
|
|
525
|
+
siluzan-tso ad geo set-bid -a <accountId> --campaign-id <id> --location-id <id> --bid-modifier 1.2
|
|
526
|
+
# 或使用 list 返回的 criterion id
|
|
527
|
+
siluzan-tso ad geo set-bid -a <accountId> --campaign-id <id> --criterion-id <id> --bid-modifier 0.8
|
|
528
|
+
|
|
524
529
|
# 删除
|
|
525
530
|
siluzan-tso ad geo remove -a <accountId> --campaign-id <id> --location-id <id>
|
|
526
531
|
```
|
|
527
532
|
|
|
533
|
+
**`--bid-modifier` 口径(`add` / `set-bid` 均为 Google 倍率)**
|
|
534
|
+
|
|
535
|
+
| 倍率 | 含义 |
|
|
536
|
+
|------|------|
|
|
537
|
+
| `1.0` | 不调整 |
|
|
538
|
+
| `1.2` | 提高 20% |
|
|
539
|
+
| `0.8` | 降低 20% |
|
|
540
|
+
|
|
541
|
+
- `add`:写入 `PUT …/criterion/{account}` 时 CLI 会换算为后端百分比。
|
|
542
|
+
- `set-bid`:直接 `PUT …/campaigns/{campaignId}/Criteria/{criterionId}/BidModifier/{bidModifier}`,与 `ad device-bid set`(系列级)、AI 优化 `updateDeviceAndAddress` 同源。
|
|
543
|
+
- `list` 返回的 `bidModifier` 为 Google 倍率(非网页编辑弹窗里的 `+10` 百分比)。
|
|
544
|
+
|
|
545
|
+
**Sammamish 落点**:`ModifyCampaignCriteriaBidModifier` → `GoogleAdsAcctMgmtServiceProviderV2.ModifyCampaignCriteriaBidModifier`。
|
|
546
|
+
|
|
528
547
|
---
|
|
529
548
|
|
|
530
549
|
## ad device-bid — 设备出价调整
|
|
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
$PKG_NAME = 'siluzan-tso-cli'
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
$PKG_VERSION = '1.1.20-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.20-beta.23'
|
|
13
13
|
$CLI_BIN = 'siluzan-tso'
|
|
14
14
|
$SKILL_LABEL = 'Siluzan TSO'
|
|
15
15
|
$INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
|
|
@@ -9,7 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
readonly PKG_NAME="siluzan-tso-cli"
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
readonly PKG_VERSION="1.1.20-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.20-beta.23"
|
|
13
13
|
readonly CLI_BIN="siluzan-tso"
|
|
14
14
|
readonly SKILL_LABEL="Siluzan TSO"
|
|
15
15
|
readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
|