siluzan-tso-cli 1.1.30-beta.6 → 1.1.30-beta.7
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
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.30-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.30-beta.7),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -4170,11 +4170,27 @@ function defaultDateRange() {
|
|
|
4170
4170
|
start.setDate(start.getDate() - 6);
|
|
4171
4171
|
return { startDate: fmt(start), endDate: fmt(end) };
|
|
4172
4172
|
}
|
|
4173
|
+
function resolveAccountOverviewMediaRoute(cliMedia, accountMediaAccountType) {
|
|
4174
|
+
if (cliMedia === "MetaAd" && accountMediaAccountType === "FacebookAds") {
|
|
4175
|
+
return "FacebookAds";
|
|
4176
|
+
}
|
|
4177
|
+
return cliMedia;
|
|
4178
|
+
}
|
|
4179
|
+
function groupAccountIdsByOverviewRoute(cliMedia, accountIds, accountMediaTypes) {
|
|
4180
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4181
|
+
for (const id of accountIds) {
|
|
4182
|
+
const route = resolveAccountOverviewMediaRoute(cliMedia, accountMediaTypes?.get(id));
|
|
4183
|
+
const list = groups.get(route) ?? [];
|
|
4184
|
+
list.push(id);
|
|
4185
|
+
groups.set(route, list);
|
|
4186
|
+
}
|
|
4187
|
+
return groups;
|
|
4188
|
+
}
|
|
4173
4189
|
async function fetchBalanceMap(media, accountIds, config, startDate, endDate, verbose) {
|
|
4174
4190
|
const result = /* @__PURE__ */ new Map();
|
|
4175
|
-
if (accountIds.length === 0
|
|
4191
|
+
if (accountIds.length === 0) return result;
|
|
4176
4192
|
try {
|
|
4177
|
-
if (["Google", "Yandex", "TikTok", "BingV2"].includes(media)) {
|
|
4193
|
+
if (["Google", "Yandex", "TikTok", "BingV2", "MetaAd"].includes(media)) {
|
|
4178
4194
|
const params = new URLSearchParams({ ids: accountIds.join("|") });
|
|
4179
4195
|
const url = `${config.apiBaseUrl}/query/media-account/${media}/GetMediaAccountInfo?${params}`;
|
|
4180
4196
|
const raw = await apiFetch2(
|
|
@@ -4284,39 +4300,87 @@ function parseGoogleAccountSpendOverviewRows(raw) {
|
|
|
4284
4300
|
const items = Array.isArray(r.items) ? r.items : [];
|
|
4285
4301
|
return items.map(fromDbItem).filter((row) => row !== null);
|
|
4286
4302
|
}
|
|
4287
|
-
async function fetchOverviewMap(
|
|
4303
|
+
async function fetchOverviewMap(route, accountIds, config, startDate, endDate, verbose) {
|
|
4288
4304
|
const result = /* @__PURE__ */ new Map();
|
|
4289
|
-
if (accountIds.length === 0
|
|
4305
|
+
if (accountIds.length === 0) return result;
|
|
4290
4306
|
const range = defaultDateRange();
|
|
4291
4307
|
const start = startDate ?? range.startDate;
|
|
4292
4308
|
const end = endDate ?? range.endDate;
|
|
4293
4309
|
try {
|
|
4294
|
-
if (
|
|
4295
|
-
const
|
|
4310
|
+
if (route === "Google") {
|
|
4311
|
+
const params = new URLSearchParams({
|
|
4296
4312
|
startDate: start,
|
|
4297
4313
|
endDate: end,
|
|
4298
4314
|
mediaCustomerIds: accountIds.join("|")
|
|
4299
4315
|
});
|
|
4300
|
-
const
|
|
4301
|
-
const
|
|
4302
|
-
const rows = parseGoogleAccountSpendOverviewRows(
|
|
4316
|
+
const url = `${config.apiBaseUrl}/report/media-account/google/account-spend-overview?${params}`;
|
|
4317
|
+
const raw = await apiFetch2(url, config, {}, verbose);
|
|
4318
|
+
const rows = parseGoogleAccountSpendOverviewRows(raw);
|
|
4303
4319
|
for (const row of rows) {
|
|
4304
4320
|
result.set(row.mediaAccountId, row);
|
|
4305
4321
|
}
|
|
4306
4322
|
return result;
|
|
4307
4323
|
}
|
|
4308
|
-
const
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4324
|
+
const fetchLegacyBatch = async (ids) => {
|
|
4325
|
+
const batch = /* @__PURE__ */ new Map();
|
|
4326
|
+
const params = new URLSearchParams({
|
|
4327
|
+
period: "true",
|
|
4328
|
+
startDate: start,
|
|
4329
|
+
endDate: end,
|
|
4330
|
+
mediaCustomerIds: ids.join(",")
|
|
4331
|
+
});
|
|
4332
|
+
const url = `${config.apiBaseUrl}/report/media-account/${route}/accountsoverview?${params}`;
|
|
4333
|
+
const raw = await apiFetch2(url, config, {}, verbose);
|
|
4334
|
+
const items = Array.isArray(raw) ? raw : [];
|
|
4335
|
+
for (const item of items) {
|
|
4336
|
+
if (item.mediaAccountId != null) {
|
|
4337
|
+
batch.set(String(item.mediaAccountId), item);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
return batch;
|
|
4341
|
+
};
|
|
4342
|
+
const mergeOverview = (batch) => {
|
|
4343
|
+
for (const [k, v] of batch) result.set(k, v);
|
|
4344
|
+
};
|
|
4345
|
+
if (accountIds.length === 1) {
|
|
4346
|
+
mergeOverview(await fetchLegacyBatch(accountIds));
|
|
4347
|
+
return result;
|
|
4348
|
+
}
|
|
4349
|
+
try {
|
|
4350
|
+
const batch = await fetchLegacyBatch(accountIds);
|
|
4351
|
+
mergeOverview(batch);
|
|
4352
|
+
if (batch.size === 0) {
|
|
4353
|
+
for (const id of accountIds) {
|
|
4354
|
+
try {
|
|
4355
|
+
mergeOverview(await fetchLegacyBatch([id]));
|
|
4356
|
+
} catch (singleErr) {
|
|
4357
|
+
if (verbose) {
|
|
4358
|
+
process.stderr.write(
|
|
4359
|
+
`[fetchOverviewMap] \u9010\u6237\u91CD\u8BD5\u5931\u8D25 ${id}\uFF1A${singleErr instanceof Error ? singleErr.message : String(singleErr)}
|
|
4360
|
+
`
|
|
4361
|
+
);
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
} catch (err) {
|
|
4367
|
+
if (verbose) {
|
|
4368
|
+
process.stderr.write(
|
|
4369
|
+
`[fetchOverviewMap] \u6279\u91CF\u5F02\u5E38\uFF0C\u9010\u6237\u91CD\u8BD5\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
4370
|
+
`
|
|
4371
|
+
);
|
|
4372
|
+
}
|
|
4373
|
+
for (const id of accountIds) {
|
|
4374
|
+
try {
|
|
4375
|
+
mergeOverview(await fetchLegacyBatch([id]));
|
|
4376
|
+
} catch (singleErr) {
|
|
4377
|
+
if (verbose) {
|
|
4378
|
+
process.stderr.write(
|
|
4379
|
+
`[fetchOverviewMap] \u9010\u6237\u91CD\u8BD5\u5931\u8D25 ${id}\uFF1A${singleErr instanceof Error ? singleErr.message : String(singleErr)}
|
|
4380
|
+
`
|
|
4381
|
+
);
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4320
4384
|
}
|
|
4321
4385
|
}
|
|
4322
4386
|
} catch (err) {
|
|
@@ -4339,7 +4403,8 @@ var init_balance = __esm({
|
|
|
4339
4403
|
"Yandex",
|
|
4340
4404
|
"TikTok",
|
|
4341
4405
|
"BingV2",
|
|
4342
|
-
"Kwai"
|
|
4406
|
+
"Kwai",
|
|
4407
|
+
"MetaAd"
|
|
4343
4408
|
];
|
|
4344
4409
|
}
|
|
4345
4410
|
});
|
|
@@ -4373,16 +4438,25 @@ async function fetchBalanceAndOverviewSequential(opts) {
|
|
|
4373
4438
|
const ids = chunks[chunkIdx];
|
|
4374
4439
|
const oneBased = chunkIdx + 1;
|
|
4375
4440
|
opts.onChunkStart?.(oneBased, chunks.length, ids);
|
|
4376
|
-
const
|
|
4441
|
+
const routeGroups = groupAccountIdsByOverviewRoute(
|
|
4377
4442
|
media,
|
|
4378
4443
|
ids,
|
|
4379
|
-
|
|
4380
|
-
startDate,
|
|
4381
|
-
endDate,
|
|
4382
|
-
verbose
|
|
4444
|
+
opts.accountMediaTypes
|
|
4383
4445
|
);
|
|
4384
|
-
|
|
4385
|
-
for (const [
|
|
4446
|
+
let chunkOverviewSize = 0;
|
|
4447
|
+
for (const [route, routeIds] of routeGroups) {
|
|
4448
|
+
const overviewChunk = await fetchOverviewMap(
|
|
4449
|
+
route,
|
|
4450
|
+
routeIds,
|
|
4451
|
+
config,
|
|
4452
|
+
startDate,
|
|
4453
|
+
endDate,
|
|
4454
|
+
verbose
|
|
4455
|
+
);
|
|
4456
|
+
chunkOverviewSize += overviewChunk.size;
|
|
4457
|
+
for (const [k, v] of overviewChunk) overviewMap.set(k, v);
|
|
4458
|
+
}
|
|
4459
|
+
opts.onOverviewDone?.(oneBased, chunks.length, ids, chunkOverviewSize);
|
|
4386
4460
|
const balanceChunk = await fetchBalanceMap(
|
|
4387
4461
|
media,
|
|
4388
4462
|
ids,
|
|
@@ -4403,6 +4477,143 @@ var init_media_account_batch = __esm({
|
|
|
4403
4477
|
}
|
|
4404
4478
|
});
|
|
4405
4479
|
|
|
4480
|
+
// src/utils/media-account-lookup.ts
|
|
4481
|
+
function pickMediaCustomerName(items, mediaCustomerId) {
|
|
4482
|
+
const list = Array.isArray(items) ? items : [];
|
|
4483
|
+
const target = mediaCustomerId.trim();
|
|
4484
|
+
for (const item of list) {
|
|
4485
|
+
const cid = String(item.ma?.mediaCustomerId ?? "");
|
|
4486
|
+
if (cid === target) {
|
|
4487
|
+
const name2 = String(item.ma?.mediaCustomerName ?? "").trim();
|
|
4488
|
+
return name2 || null;
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
return null;
|
|
4492
|
+
}
|
|
4493
|
+
function metaAccountIdDigits(raw) {
|
|
4494
|
+
const t = raw.trim();
|
|
4495
|
+
if (t.startsWith("act_")) return t.slice(4);
|
|
4496
|
+
return t;
|
|
4497
|
+
}
|
|
4498
|
+
function idsMatch(a, b) {
|
|
4499
|
+
return metaAccountIdDigits(a) === metaAccountIdDigits(b);
|
|
4500
|
+
}
|
|
4501
|
+
async function lookupFromMediaAccountQuery(apiBaseUrl, config, mediaType, mediaCustomerId, verbose) {
|
|
4502
|
+
const params = new URLSearchParams({
|
|
4503
|
+
MediaType: mediaType,
|
|
4504
|
+
mediaAccountState: "Approved,Linked",
|
|
4505
|
+
pageNo: "1",
|
|
4506
|
+
pageSize: "20",
|
|
4507
|
+
mediaCustomerId: mediaCustomerId.trim()
|
|
4508
|
+
});
|
|
4509
|
+
const data = await apiFetch2(
|
|
4510
|
+
`${apiBaseUrl}/query/media-account/?${params}`,
|
|
4511
|
+
config,
|
|
4512
|
+
{},
|
|
4513
|
+
verbose
|
|
4514
|
+
);
|
|
4515
|
+
return pickMediaCustomerName(data, mediaCustomerId);
|
|
4516
|
+
}
|
|
4517
|
+
async function lookupMediaCustomerName(apiBaseUrl, config, mediaType, mediaCustomerId, verbose = false) {
|
|
4518
|
+
const id = mediaCustomerId.trim();
|
|
4519
|
+
if (!id) return null;
|
|
4520
|
+
if (mediaType === "Google" || mediaType === "Yandex") {
|
|
4521
|
+
return lookupFromMediaAccountQuery(apiBaseUrl, config, mediaType, id, verbose);
|
|
4522
|
+
}
|
|
4523
|
+
return null;
|
|
4524
|
+
}
|
|
4525
|
+
async function lookupMetaMediaAccountTypes(config, accountIds, verbose = false) {
|
|
4526
|
+
const result = /* @__PURE__ */ new Map();
|
|
4527
|
+
const pending = new Set(accountIds.map((id) => id.trim()).filter(Boolean));
|
|
4528
|
+
if (pending.size === 0) return result;
|
|
4529
|
+
const CHUNK = 50;
|
|
4530
|
+
const ids = [...pending];
|
|
4531
|
+
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
4532
|
+
const chunk = ids.slice(i, i + CHUNK);
|
|
4533
|
+
const params = new URLSearchParams({
|
|
4534
|
+
MediaTypes: "MetaAd|FacebookAds",
|
|
4535
|
+
advStatus: "",
|
|
4536
|
+
mediaAccountState: "Approved,Linked",
|
|
4537
|
+
isForce: "false",
|
|
4538
|
+
pageNum: "1",
|
|
4539
|
+
pageSize: String(Math.max(20, chunk.length)),
|
|
4540
|
+
mediaCustomerIds: chunk.join(",")
|
|
4541
|
+
});
|
|
4542
|
+
try {
|
|
4543
|
+
const data = await apiFetch2(
|
|
4544
|
+
`${config.apiBaseUrl}/query/media-account/SearchMediaAcountByCriteria?${params}`,
|
|
4545
|
+
config,
|
|
4546
|
+
{},
|
|
4547
|
+
verbose
|
|
4548
|
+
);
|
|
4549
|
+
for (const item of data?.mas ?? []) {
|
|
4550
|
+
const ma = item.ma ?? {};
|
|
4551
|
+
const cid = String(ma.mediaCustomerId ?? "");
|
|
4552
|
+
const typ = String(ma.mediaAccountType ?? "").trim();
|
|
4553
|
+
if (!cid || !typ) continue;
|
|
4554
|
+
for (const inputId of chunk) {
|
|
4555
|
+
if (idsMatch(inputId, cid)) {
|
|
4556
|
+
result.set(inputId, typ);
|
|
4557
|
+
pending.delete(inputId);
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4561
|
+
} catch {
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
for (const inputId of [...pending]) {
|
|
4565
|
+
for (const idCandidate of [inputId, `act_${metaAccountIdDigits(inputId)}`]) {
|
|
4566
|
+
const params = new URLSearchParams({
|
|
4567
|
+
MediaTypes: "MetaAd|FacebookAds",
|
|
4568
|
+
advStatus: "",
|
|
4569
|
+
mediaAccountState: "Approved,Linked",
|
|
4570
|
+
isForce: "false",
|
|
4571
|
+
pageNum: "1",
|
|
4572
|
+
pageSize: "20",
|
|
4573
|
+
mediaCustomerIds: idCandidate
|
|
4574
|
+
});
|
|
4575
|
+
try {
|
|
4576
|
+
const data = await apiFetch2(
|
|
4577
|
+
`${config.apiBaseUrl}/query/media-account/SearchMediaAcountByCriteria?${params}`,
|
|
4578
|
+
config,
|
|
4579
|
+
{},
|
|
4580
|
+
verbose
|
|
4581
|
+
);
|
|
4582
|
+
for (const item of data?.mas ?? []) {
|
|
4583
|
+
const ma = item.ma ?? {};
|
|
4584
|
+
const cid = String(ma.mediaCustomerId ?? "");
|
|
4585
|
+
if (idsMatch(inputId, cid)) {
|
|
4586
|
+
result.set(inputId, String(ma.mediaAccountType ?? "FacebookAds"));
|
|
4587
|
+
pending.delete(inputId);
|
|
4588
|
+
break;
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
} catch {
|
|
4592
|
+
}
|
|
4593
|
+
if (!pending.has(inputId)) break;
|
|
4594
|
+
}
|
|
4595
|
+
if (!result.has(inputId)) {
|
|
4596
|
+
result.set(inputId, "FacebookAds");
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
return result;
|
|
4600
|
+
}
|
|
4601
|
+
function buildAccountMediaTypesMap(items) {
|
|
4602
|
+
const map = /* @__PURE__ */ new Map();
|
|
4603
|
+
for (const item of items) {
|
|
4604
|
+
const id = item.ma?.mediaCustomerId;
|
|
4605
|
+
const typ = item.ma?.mediaAccountType;
|
|
4606
|
+
if (id && typ) map.set(String(id), String(typ));
|
|
4607
|
+
}
|
|
4608
|
+
return map;
|
|
4609
|
+
}
|
|
4610
|
+
var init_media_account_lookup = __esm({
|
|
4611
|
+
"src/utils/media-account-lookup.ts"() {
|
|
4612
|
+
"use strict";
|
|
4613
|
+
init_auth();
|
|
4614
|
+
}
|
|
4615
|
+
});
|
|
4616
|
+
|
|
4406
4617
|
// src/commands/accounts-digest/list-fetch.ts
|
|
4407
4618
|
async function fetchAccountsByMedia(media, config, opts) {
|
|
4408
4619
|
const cfg = DIGEST_PLATFORM_CONFIG[media];
|
|
@@ -4491,6 +4702,17 @@ var init_list_fetch = __esm({
|
|
|
4491
4702
|
isForce: false
|
|
4492
4703
|
},
|
|
4493
4704
|
responseType: "mas"
|
|
4705
|
+
},
|
|
4706
|
+
MetaAd: {
|
|
4707
|
+
path: "/query/media-account/SearchMediaAcountByCriteria",
|
|
4708
|
+
pageParam: "pageNum",
|
|
4709
|
+
fixedParams: {
|
|
4710
|
+
MediaTypes: "MetaAd|FacebookAds",
|
|
4711
|
+
advStatus: "",
|
|
4712
|
+
mediaAccountState: "Approved,Linked",
|
|
4713
|
+
isForce: false
|
|
4714
|
+
},
|
|
4715
|
+
responseType: "mas"
|
|
4494
4716
|
}
|
|
4495
4717
|
};
|
|
4496
4718
|
}
|
|
@@ -4509,7 +4731,7 @@ async function runAccountsDigest(opts) {
|
|
|
4509
4731
|
console.error(
|
|
4510
4732
|
`
|
|
4511
4733
|
\u274C accounts-digest \u6682\u4E0D\u652F\u6301\u5A92\u4F53\uFF1A${opts.media}
|
|
4512
|
-
\u53EF\u9009\uFF1A${BALANCE_SUPPORTED_MEDIA.join(" | ")}
|
|
4734
|
+
\u53EF\u9009\uFF1A${BALANCE_SUPPORTED_MEDIA.join(" | ")}
|
|
4513
4735
|
`
|
|
4514
4736
|
);
|
|
4515
4737
|
process.exit(1);
|
|
@@ -4523,12 +4745,16 @@ async function runAccountsDigest(opts) {
|
|
|
4523
4745
|
let accountsList;
|
|
4524
4746
|
let scannedFromList = false;
|
|
4525
4747
|
if (filterSet.size > 0) {
|
|
4748
|
+
let mediaTypes = /* @__PURE__ */ new Map();
|
|
4749
|
+
if (media === "MetaAd") {
|
|
4750
|
+
mediaTypes = await lookupMetaMediaAccountTypes(config, filterIds, opts.verbose);
|
|
4751
|
+
}
|
|
4526
4752
|
accountsList = filterIds.map((id) => ({
|
|
4527
4753
|
ma: {
|
|
4528
4754
|
entityId: "",
|
|
4529
4755
|
mediaCustomerId: id,
|
|
4530
4756
|
mediaCustomerName: null,
|
|
4531
|
-
mediaAccountType: media,
|
|
4757
|
+
mediaAccountType: mediaTypes.get(id) ?? media,
|
|
4532
4758
|
invalidOAuthToken: false
|
|
4533
4759
|
},
|
|
4534
4760
|
accepted: true,
|
|
@@ -4566,6 +4792,7 @@ async function runAccountsDigest(opts) {
|
|
|
4566
4792
|
startDate: opts.startDate,
|
|
4567
4793
|
endDate: opts.endDate,
|
|
4568
4794
|
verbose: opts.verbose,
|
|
4795
|
+
accountMediaTypes: buildAccountMediaTypesMap(accountsList),
|
|
4569
4796
|
onChunkStart: (chunkIdx, totalChunks, ids) => {
|
|
4570
4797
|
process.stderr.write(
|
|
4571
4798
|
` \u2192 [\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\uFF08${ids.length} \u6237\uFF09\u2026
|
|
@@ -4725,6 +4952,7 @@ var init_run = __esm({
|
|
|
4725
4952
|
init_cli_table();
|
|
4726
4953
|
init_currency_display();
|
|
4727
4954
|
init_media_account_batch();
|
|
4955
|
+
init_media_account_lookup();
|
|
4728
4956
|
init_list_fetch();
|
|
4729
4957
|
}
|
|
4730
4958
|
});
|
|
@@ -4735,7 +4963,7 @@ function register10(program2) {
|
|
|
4735
4963
|
"\u591A\u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u4E00\u6761\u547D\u4EE4\u62FF\u9F50\u6D88\u8017/\u70B9\u51FB/\u8F6C\u5316/\u4F59\u989D/\u6D3E\u751F\u6307\u6807\uFF08CTR/CPC/CPA\uFF09\uFF0C\u66FF\u4EE3 AI \u9010\u8D26\u6237 `list-accounts` + `stats` \u7684\u7F16\u6392\u3002\u5178\u578B\u573A\u666F\uFF1A\u300C\u8FD9 10 \u4E2A\u8D26\u6237\uFF08\u6216\u67D0\u5A92\u4F53\u5168\u90E8\u8D26\u6237\uFF09\u5E2E\u6211\u6574\u7406\u4E00\u4E0B\u300D\u3002"
|
|
4736
4964
|
).requiredOption(
|
|
4737
4965
|
"-m, --media <type>",
|
|
4738
|
-
"\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | BingV2 | Kwai
|
|
4966
|
+
"\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai"
|
|
4739
4967
|
).option(
|
|
4740
4968
|
"-a, --accounts <ids>",
|
|
4741
4969
|
"\u6307\u5B9A\u8D26\u6237 mediaCustomerId\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A\u5219\u626B\u63CF\u8BE5\u5A92\u4F53\u4E0B\u5168\u90E8\u8D26\u6237"
|
|
@@ -105320,6 +105548,7 @@ init_balance();
|
|
|
105320
105548
|
init_cli_table();
|
|
105321
105549
|
init_currency_display();
|
|
105322
105550
|
init_media_account_batch();
|
|
105551
|
+
init_media_account_lookup();
|
|
105323
105552
|
|
|
105324
105553
|
// src/commands/balance-scan/list-fetch.ts
|
|
105325
105554
|
init_auth();
|
|
@@ -105369,6 +105598,17 @@ var SCAN_PLATFORM_CONFIG = {
|
|
|
105369
105598
|
isForce: false
|
|
105370
105599
|
},
|
|
105371
105600
|
responseType: "mas"
|
|
105601
|
+
},
|
|
105602
|
+
MetaAd: {
|
|
105603
|
+
path: "/query/media-account/SearchMediaAcountByCriteria",
|
|
105604
|
+
pageParam: "pageNum",
|
|
105605
|
+
fixedParams: {
|
|
105606
|
+
MediaTypes: "MetaAd|FacebookAds",
|
|
105607
|
+
advStatus: "",
|
|
105608
|
+
mediaAccountState: "Approved,Linked",
|
|
105609
|
+
isForce: false
|
|
105610
|
+
},
|
|
105611
|
+
responseType: "mas"
|
|
105372
105612
|
}
|
|
105373
105613
|
};
|
|
105374
105614
|
async function fetchOnePage(media, page, pageSize, config, verbose) {
|
|
@@ -105472,7 +105712,7 @@ async function runBalanceScan(opts) {
|
|
|
105472
105712
|
console.error(
|
|
105473
105713
|
`
|
|
105474
105714
|
\u274C balance scan \u6682\u4E0D\u652F\u6301\u5A92\u4F53\uFF1A${opts.media}
|
|
105475
|
-
\u53EF\u9009\uFF1A${BALANCE_SUPPORTED_MEDIA.join(" | ")}
|
|
105715
|
+
\u53EF\u9009\uFF1A${BALANCE_SUPPORTED_MEDIA.join(" | ")}
|
|
105476
105716
|
`
|
|
105477
105717
|
);
|
|
105478
105718
|
process.exit(1);
|
|
@@ -105489,12 +105729,16 @@ async function runBalanceScan(opts) {
|
|
|
105489
105729
|
let allItems;
|
|
105490
105730
|
let total;
|
|
105491
105731
|
if (isSubset) {
|
|
105732
|
+
let mediaTypes = /* @__PURE__ */ new Map();
|
|
105733
|
+
if (media === "MetaAd") {
|
|
105734
|
+
mediaTypes = await lookupMetaMediaAccountTypes(config, filterIds, opts.verbose);
|
|
105735
|
+
}
|
|
105492
105736
|
allItems = filterIds.map((id) => ({
|
|
105493
105737
|
ma: {
|
|
105494
105738
|
entityId: "",
|
|
105495
105739
|
mediaCustomerId: id,
|
|
105496
105740
|
mediaCustomerName: null,
|
|
105497
|
-
mediaAccountType: media,
|
|
105741
|
+
mediaAccountType: mediaTypes.get(id) ?? media,
|
|
105498
105742
|
invalidOAuthToken: false
|
|
105499
105743
|
},
|
|
105500
105744
|
accepted: true,
|
|
@@ -105532,6 +105776,7 @@ async function runBalanceScan(opts) {
|
|
|
105532
105776
|
accountIds: validIds,
|
|
105533
105777
|
config,
|
|
105534
105778
|
verbose: opts.verbose,
|
|
105779
|
+
accountMediaTypes: buildAccountMediaTypesMap(allItems),
|
|
105535
105780
|
onChunkStart: (chunkIdx, totalChunks, ids) => {
|
|
105536
105781
|
process.stderr.write(
|
|
105537
105782
|
` \u2192 [\u8FD17\u65E5\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u8BF7\u6C42\u4E2D\uFF08${ids.length} \u6237\uFF09\u2026
|
|
@@ -105691,7 +105936,7 @@ function register9(program2) {
|
|
|
105691
105936
|
"\u4E00\u952E\u626B\u63CF\u67D0\u5A92\u4F53\u4E0B\u6240\u6709\u8D26\u6237\u7684\u4F59\u989D\uFF0C\u7B5B\u51FA\u7EED\u822A\u5929\u6570\u4E0D\u8DB3\u7684\u8D26\u6237\u3002\u66FF\u4EE3\u5FAA\u73AF\u8C03\u7528 balance \u7684\u4F20\u7EDF\u505A\u6CD5\uFF0C\u5178\u578B\u573A\u666F\uFF1A\u300C\u5E2E\u6211\u627E\u51FA 117 \u4E2A Bing \u8D26\u6237\u91CC\u4F59\u989D\u4E0D\u8DB3 7 \u5929\u7684\u300D\u3002"
|
|
105692
105937
|
).requiredOption(
|
|
105693
105938
|
"-m, --media <type>",
|
|
105694
|
-
"\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | BingV2 | Kwai
|
|
105939
|
+
"\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai"
|
|
105695
105940
|
).option(
|
|
105696
105941
|
"-a, --accounts <ids>",
|
|
105697
105942
|
"\u6307\u5B9A mediaCustomerId\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF1B\u8DF3\u8FC7\u6E05\u5355\u7FFB\u9875\uFF0C\u62C9\u53D6\u540E\u5168\u90E8\u8F93\u51FA\uFF08\u4E0D\u6309\u9608\u503C\u8FC7\u6EE4\uFF09"
|
|
@@ -111664,46 +111909,8 @@ function tryLoadCampaignCreateConfig(configFile) {
|
|
|
111664
111909
|
return stripMetaKeys(raw);
|
|
111665
111910
|
}
|
|
111666
111911
|
|
|
111667
|
-
// src/utils/media-account-lookup.ts
|
|
111668
|
-
init_auth();
|
|
111669
|
-
function pickMediaCustomerName(items, mediaCustomerId) {
|
|
111670
|
-
const list = Array.isArray(items) ? items : [];
|
|
111671
|
-
const target = mediaCustomerId.trim();
|
|
111672
|
-
for (const item of list) {
|
|
111673
|
-
const cid = String(item.ma?.mediaCustomerId ?? "");
|
|
111674
|
-
if (cid === target) {
|
|
111675
|
-
const name2 = String(item.ma?.mediaCustomerName ?? "").trim();
|
|
111676
|
-
return name2 || null;
|
|
111677
|
-
}
|
|
111678
|
-
}
|
|
111679
|
-
return null;
|
|
111680
|
-
}
|
|
111681
|
-
async function lookupFromMediaAccountQuery(apiBaseUrl, config, mediaType, mediaCustomerId, verbose) {
|
|
111682
|
-
const params = new URLSearchParams({
|
|
111683
|
-
MediaType: mediaType,
|
|
111684
|
-
mediaAccountState: "Approved,Linked",
|
|
111685
|
-
pageNo: "1",
|
|
111686
|
-
pageSize: "20",
|
|
111687
|
-
mediaCustomerId: mediaCustomerId.trim()
|
|
111688
|
-
});
|
|
111689
|
-
const data = await apiFetch2(
|
|
111690
|
-
`${apiBaseUrl}/query/media-account/?${params}`,
|
|
111691
|
-
config,
|
|
111692
|
-
{},
|
|
111693
|
-
verbose
|
|
111694
|
-
);
|
|
111695
|
-
return pickMediaCustomerName(data, mediaCustomerId);
|
|
111696
|
-
}
|
|
111697
|
-
async function lookupMediaCustomerName(apiBaseUrl, config, mediaType, mediaCustomerId, verbose = false) {
|
|
111698
|
-
const id = mediaCustomerId.trim();
|
|
111699
|
-
if (!id) return null;
|
|
111700
|
-
if (mediaType === "Google" || mediaType === "Yandex") {
|
|
111701
|
-
return lookupFromMediaAccountQuery(apiBaseUrl, config, mediaType, id, verbose);
|
|
111702
|
-
}
|
|
111703
|
-
return null;
|
|
111704
|
-
}
|
|
111705
|
-
|
|
111706
111912
|
// src/commands/ad/campaign-customer-name.ts
|
|
111913
|
+
init_media_account_lookup();
|
|
111707
111914
|
var CampaignCustomerNameResolveError = class extends Error {
|
|
111708
111915
|
constructor(accountId) {
|
|
111709
111916
|
super(
|
|
@@ -113932,6 +114139,7 @@ async function runAdGeoSetBid(opts) {
|
|
|
113932
114139
|
|
|
113933
114140
|
// src/commands/ad/ai-creation.ts
|
|
113934
114141
|
init_auth();
|
|
114142
|
+
init_media_account_lookup();
|
|
113935
114143
|
init_cli_json_snapshot();
|
|
113936
114144
|
init_cli_table();
|
|
113937
114145
|
|
package/dist/skill/_meta.json
CHANGED
|
@@ -128,7 +128,7 @@ siluzan-tso balance -m <媒体类型> -a <账户ID列表>
|
|
|
128
128
|
|
|
129
129
|
| 选项 | 说明 |
|
|
130
130
|
| ---------------------- | ------------------------------------------------------------------------------------ |
|
|
131
|
-
| `-m, --media <type>` |
|
|
131
|
+
| `-m, --media <type>` | 媒体类型(必填):`Google \| TikTok \| Yandex \| MetaAd \| BingV2 \| Kwai`(MetaAd 走 `GetMediaAccountInfo`,余额字段为 `spend_cap`) |
|
|
132
132
|
| `-a, --accounts <ids>` | 账户 `mediaCustomerId`(数字 ID),多个用逗号分隔(必填)。**注意:不是 `entityId`** |
|
|
133
133
|
| `--json-out` | 输出原始 JSON;不支持或查询失败时 stdout 为 `{"ok":false,"error":"..."}` |
|
|
134
134
|
|
|
@@ -141,6 +141,9 @@ siluzan-tso balance -m Google -a 6326027735
|
|
|
141
141
|
# 查询多个 TikTok 账户余额
|
|
142
142
|
siluzan-tso balance -m TikTok -a 1234567890,9876543210
|
|
143
143
|
|
|
144
|
+
# 查询 Meta 广告账户余额
|
|
145
|
+
siluzan-tso balance -m MetaAd -a <mediaCustomerId>
|
|
146
|
+
|
|
144
147
|
# JSON 输出,供脚本使用
|
|
145
148
|
siluzan-tso balance -m Google -a 6326027735 --json-out ./snap
|
|
146
149
|
```
|
|
@@ -153,7 +156,7 @@ siluzan-tso balance -m Google -a 6326027735 --json-out ./snap
|
|
|
153
156
|
|
|
154
157
|
一条命令替代 AI 对每个账户循环 `list-accounts -k` + `stats`。**多账户汇总表、对比消耗、跨账户巡检** 应优先本命令,禁止外层 for-loop 逐户 `stats`。
|
|
155
158
|
|
|
156
|
-
> **数据时效性**:与 `stats` / `balance-scan` 相同(Google `account-spend-overview` 分流;TikTok/Yandex/BingV2/Kwai 为截至昨天的 `accountsoverview`)。完整表见 `references/analytics/account-analytics.md` 顶部。
|
|
159
|
+
> **数据时效性**:与 `stats` / `balance-scan` 相同(Google `account-spend-overview` 分流;TikTok/Yandex/BingV2/Kwai/**MetaAd** 为截至昨天的 `accountsoverview`)。完整表见 `references/analytics/account-analytics.md` 顶部。
|
|
157
160
|
|
|
158
161
|
```bash
|
|
159
162
|
siluzan-tso accounts-digest -m <媒体类型> [选项]
|
|
@@ -161,7 +164,7 @@ siluzan-tso accounts-digest -m <媒体类型> [选项]
|
|
|
161
164
|
|
|
162
165
|
| 选项 | 说明 | 默认 |
|
|
163
166
|
| ---------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
|
|
164
|
-
| `-m, --media <type>` | 媒体类型(必填):`Google \| TikTok \| Yandex \| BingV2 \| Kwai
|
|
167
|
+
| `-m, --media <type>` | 媒体类型(必填):`Google \| TikTok \| Yandex \| MetaAd \| BingV2 \| Kwai` | — |
|
|
165
168
|
| `-a, --accounts <ids>` | 指定 `mediaCustomerId`,逗号分隔;**留空**则翻页拉该媒体全部账户 | — |
|
|
166
169
|
| `--start <YYYY-MM-DD>` | 统计开始日期(SKILL 要求 AI 先与用户确认区间) | 近 7 天 |
|
|
167
170
|
| `--end <YYYY-MM-DD>` | 统计结束日期 | 昨天 |
|
|
@@ -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.30-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.30-beta.7'
|
|
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.30-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.30-beta.7"
|
|
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"
|