addashboard-cli 0.17.0 → 0.18.0
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 +2 -1
- package/dist/commands/report.js +55 -1
- package/dist/commands/report.js.map +1 -1
- package/dist/lib/apiError.js +25 -1
- package/dist/lib/apiError.js.map +1 -1
- package/package.json +1 -1
- package/skills/ad-ops/SKILL.md +2 -2
- package/skills/ad-ops/ad-planner.html +16 -7
- package/skills/ad-ops/scripts/plan_lint.py +12 -0
- package/skills/ad-ops/scripts/test_plan_lint.sh +37 -0
package/README.md
CHANGED
|
@@ -39,13 +39,14 @@ webgram pl projects [--month YYYY-MM] [--status <s>] [--platform <p>] # 案件
|
|
|
39
39
|
webgram report yda --account-id <id> [--level <l>] [--from --to] [--format csv] # YDA の日別実績(DB の値)。列の意味・比率の出し方は --help
|
|
40
40
|
webgram report smartnews --account-id <id> [--level <l>] [--from --to] [--format csv] # SmartNews の日別実績(DB の値)。列の意味・数え方は --help
|
|
41
41
|
webgram report google --account-id <id> [--level <l>] [--from --to] [--format csv] # Google の日別実績(DB の値)。列の意味・制約は --help
|
|
42
|
+
webgram report bytedance --account-id <id> [--level <l>] [--from --to] [--format csv] # TikTok の日別実績(DB の値)。列の意味・制約は --help
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
`<p>` = 対応媒体 (`yda` / `meta` / `google` / `smartnews` / `bytedance`)。`submit-plan` は **`yda` / `meta`** 対応(入稿計画JSON の `plan.platform` で自動判別、未記載は `yda` 扱い)。
|
|
45
46
|
`--resource` の媒体制約: `resources` は smartnews 非対応 / `media` は yda のみ(登録画像 mediaId)/ `image-library`・`video-library` は meta のみ(imageHash / videoId。`--account-id` は `act_` 付き)。
|
|
46
47
|
`pl summary` / `pl projects` は JSON をそのまま出力する(要約は呼び出し側の Claude Code が行う)。
|
|
47
48
|
|
|
48
|
-
`report` は DB に貯めた実績を日別の行で返す(期間の合計や比率は返さない)。列の意味・既定の期間・比率の出し方・行数の上限は `webgram report <媒体> --help`(`yda` / `smartnews` / `google`)に書いてあり、ここには書き写さない。
|
|
49
|
+
`report` は DB に貯めた実績を日別の行で返す(期間の合計や比率は返さない)。列の意味・既定の期間・比率の出し方・行数の上限は `webgram report <媒体> --help`(`yda` / `smartnews` / `google` / `bytedance`)に書いてあり、ここには書き写さない。
|
|
49
50
|
|
|
50
51
|
`review` はチーム × 週で全案件の週次レビュー(課題・今週のアクションプラン)を 1 回で取得する (JSON 出力)。`--week` 省略時は先週の月曜 (JST) を自動採用。要約は Claude Code / Codex (LLM) 側で行う。
|
|
51
52
|
|
package/dist/commands/report.js
CHANGED
|
@@ -16,7 +16,7 @@ const apiError_1 = require("../lib/apiError");
|
|
|
16
16
|
*/
|
|
17
17
|
exports.REPORT_LEVELS = ['account', 'campaign', 'adgroup', 'ad'];
|
|
18
18
|
exports.REPORT_FORMATS = ['json', 'csv'];
|
|
19
|
-
exports.REPORT_PLATFORMS = ['yda', 'smartnews', 'google'];
|
|
19
|
+
exports.REPORT_PLATFORMS = ['yda', 'smartnews', 'google', 'bytedance'];
|
|
20
20
|
/** 長い期間はサーバーの集計に時間がかかるので、既定の 15 秒より長く待つ */
|
|
21
21
|
const REPORT_TIMEOUT_MS = 120000;
|
|
22
22
|
async function runReport(input) {
|
|
@@ -226,6 +226,59 @@ const GOOGLE_HELP = `
|
|
|
226
226
|
# 今月のキャンペーン別(日別)
|
|
227
227
|
webgram report google --level campaign --account-id 8265446505 --from 2026-10-01 --to 2026-10-16
|
|
228
228
|
`;
|
|
229
|
+
const BYTEDANCE_HELP = `
|
|
230
|
+
行の単位:
|
|
231
|
+
1行=1日×--level の単位。実績のない日・単位の行は出ない(0 の行で埋めない)
|
|
232
|
+
account・campaign・adgroup Campaign テーブルの行(広告グループ×日)を合計した値
|
|
233
|
+
ad Creative テーブルの行(広告×日)
|
|
234
|
+
名前の列は、その日の行に入っている値。期間でまとめるときは ID(campaignId・adGroupId・adId)で束ねる
|
|
235
|
+
|
|
236
|
+
出力の列:
|
|
237
|
+
date 日付。広告アカウントのタイムゾーンでの日付(日本のアカウントは JST)
|
|
238
|
+
platform bytedance
|
|
239
|
+
accountId〜adName account・campaign・adGroup・ad の ID と名前。--level の階層とその上の列を出す
|
|
240
|
+
impressions 表示回数
|
|
241
|
+
clicks TikTok API の clicks。管理画面の「クリック数(遷移先)」と「クリック数(すべて)」の
|
|
242
|
+
どちらと同じかは確かめていない。CTR が他の媒体より高く出ることがある
|
|
243
|
+
cost 媒体の費用(円)。代理店の手数料(マージン)は含まない
|
|
244
|
+
conversions CV(媒体)。TikTok ピクセルで計測した CV(MCV ではない)
|
|
245
|
+
mcv 常に null。遷移はアドエビスで計測する値で、TikTok のデータにはない
|
|
246
|
+
crNumber --level ad のときだけ。CR番号(広告名に入れた MVT と数字。例 MVT5558)。
|
|
247
|
+
CR番号ごとの集計はこの列で束ねる。「MVT11修正2」のような版違いも同じ MVT11 になる。
|
|
248
|
+
広告名に MVT 番号が無い広告は null
|
|
249
|
+
CV(全体)・遷移数はアドエビスの値なので返さない
|
|
250
|
+
|
|
251
|
+
既知の制約:
|
|
252
|
+
広告文・リンク先・画像は返さない(TikTok の API が広告文・リンク先を返さず、画像は数時間で切れる URL のため)
|
|
253
|
+
配信ステータスは返さない。webgram list --platform bytedance --resource campaigns の operationStatus
|
|
254
|
+
(ENABLE=配信中 / DISABLE=停止)で確認し、campaignId で突き合わせる
|
|
255
|
+
管理画面の値と明らかに違うときや、値に違和感があるときは、ユーザーに開発者への報告を勧める
|
|
256
|
+
|
|
257
|
+
値の鮮度:
|
|
258
|
+
直近8日分は30分ごとに取り直すので、CV などが後から増えることがある。それより前の日は更新しない
|
|
259
|
+
|
|
260
|
+
比率の出し方:
|
|
261
|
+
期間の比率は合計どうしで割る(日ごとの比率を平均しない)。分母が 0 のときは計算しない
|
|
262
|
+
CTR=clicks/impressions CPC=cost/clicks CPA=cost/conversions
|
|
263
|
+
CVR=conversions/impressions(TikTok の管理画面の CVR と同じ) CVR(対クリック)=conversions/clicks
|
|
264
|
+
グロスCPA=CPA×係数(係数は案件で違う。1.2 や 1.25)
|
|
265
|
+
|
|
266
|
+
上限と長い期間:
|
|
267
|
+
10万行を超えるとエラー(途中までの行は返さない)。期間を短くするか、--campaign-id か上の --level で絞る
|
|
268
|
+
行が多いときは --format csv でファイルに保存し、スクリプトで集計する(行を文脈に読み込まない)
|
|
269
|
+
|
|
270
|
+
出力の形:
|
|
271
|
+
JSON:{ platform, level, from, to, rowCount, rows: [...] }。from / to は既定値を使ったときも実際の期間が入る
|
|
272
|
+
CSV:rows だけ。UTF-8、1行目がヘッダー、null は空欄
|
|
273
|
+
Windows PowerShell 5.1 の > は UTF-16 で書くので、| Out-File -Encoding utf8 ads.csv で保存する
|
|
274
|
+
エラーは stderr に「✗ 理由」を出して終了コード 1
|
|
275
|
+
|
|
276
|
+
例:
|
|
277
|
+
# 直近7日の広告別(CR番号ごとの集計は crNumber で束ねる)
|
|
278
|
+
webgram report bytedance --account-id 7631042580838563847 --format csv > ads.csv
|
|
279
|
+
# 期間を決めたキャンペーン別(日別)
|
|
280
|
+
webgram report bytedance --level campaign --account-id 7631042580838563847 --from 2026-10-05 --to 2026-10-12
|
|
281
|
+
`;
|
|
229
282
|
const parseYmd = (v) => {
|
|
230
283
|
// 2026-02-30 のような実在しない日付もここで止める(サーバーまで送らない)
|
|
231
284
|
const d = new Date(v + 'T00:00:00Z');
|
|
@@ -239,6 +292,7 @@ const PLATFORM_COMMANDS = {
|
|
|
239
292
|
yda: { label: 'YDA', help: YDA_HELP },
|
|
240
293
|
smartnews: { label: 'SmartNews', help: SMARTNEWS_HELP },
|
|
241
294
|
google: { label: 'Google', help: GOOGLE_HELP, accountIdHint: '(ハイフンなし。例 8265446505)' },
|
|
295
|
+
bytedance: { label: 'TikTok', help: BYTEDANCE_HELP },
|
|
242
296
|
};
|
|
243
297
|
/** report コマンドを program に登録する。help の文面をテストで確かめられるよう index.ts から分けている */
|
|
244
298
|
function registerReportCommand(program) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":";;;AAkDA,8BAgCC;AAGD,sBAUC;
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/apiError.js
CHANGED
|
@@ -43,7 +43,11 @@ function describeErrorBody(data) {
|
|
|
43
43
|
`formatGoogleAdsError()` が組み立てた本文が CLI に届かず、
|
|
44
44
|
運用者は `✗ google_ads_error` しか見られない。両方あるなら両方出す。 */
|
|
45
45
|
if (typeof e === 'string' && e) {
|
|
46
|
-
|
|
46
|
+
const reasons = [
|
|
47
|
+
...(typeof d.message === 'string' && d.message ? [d.message] : []),
|
|
48
|
+
...describeIssues(d.issues),
|
|
49
|
+
];
|
|
50
|
+
return reasons.length > 0 ? `${e}: ${reasons.join(' / ')}` : e;
|
|
47
51
|
}
|
|
48
52
|
if (e && typeof e === 'object') {
|
|
49
53
|
const m = e.message;
|
|
@@ -56,6 +60,26 @@ function describeErrorBody(data) {
|
|
|
56
60
|
// error / message どちらも無い形(想定外)は握り潰さず中身を見せる
|
|
57
61
|
return safeJson(d);
|
|
58
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* zod の検証エラー (`zodErrorBody()` の `issues: [{ path, message }]`) を
|
|
65
|
+
* `campaigns.0.status: 理由` の形に並べる。読まないと CLI には `validation_error` しか出ず、
|
|
66
|
+
* どのフィールドが何の理由で弾かれたか分からない (#396)。
|
|
67
|
+
*/
|
|
68
|
+
function describeIssues(issues) {
|
|
69
|
+
if (!Array.isArray(issues))
|
|
70
|
+
return [];
|
|
71
|
+
const lines = issues.flatMap((i) => {
|
|
72
|
+
const { path, message } = (i ?? {});
|
|
73
|
+
if (typeof message !== 'string' || !message)
|
|
74
|
+
return [];
|
|
75
|
+
return [typeof path === 'string' && path ? `${path}: ${message}` : message];
|
|
76
|
+
});
|
|
77
|
+
// 大きな計画で全件並べると1行が数KBになり読めない。先頭だけ出して残りは件数で示す
|
|
78
|
+
return lines.length > MAX_ISSUES
|
|
79
|
+
? [...lines.slice(0, MAX_ISSUES), `ほか ${lines.length - MAX_ISSUES} 件`]
|
|
80
|
+
: lines;
|
|
81
|
+
}
|
|
82
|
+
const MAX_ISSUES = 10;
|
|
59
83
|
function safeJson(v) {
|
|
60
84
|
try {
|
|
61
85
|
const s = JSON.stringify(v);
|
package/dist/lib/apiError.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiError.js","sourceRoot":"","sources":["../../src/lib/apiError.ts"],"names":[],"mappings":";;AAaA,4CASC;AAGD,
|
|
1
|
+
{"version":3,"file":"apiError.js","sourceRoot":"","sources":["../../src/lib/apiError.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skills/ad-ops/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ Google Drive から手動 DL 済みの素材を各媒体に半自動入稿する
|
|
|
9
9
|
|
|
10
10
|
- **動画の API 入稿フロー(`submit`)**: 現状 **YDA のみ**。
|
|
11
11
|
- **入稿プランナー(`ad-planner.html` → 入稿計画JSON → `submit-plan`)**: **YDA / Meta / Google / TikTok 対応**(ヘッダーの「案件 × 媒体」統合プルダウンで切替)。
|
|
12
|
-
- **Google (Demand Gen)**: プランナー・dry-run・本実行に対応。**2026-08-23 に実 API での入稿を確認済み**(PAUSED・1
|
|
12
|
+
- **Google (Demand Gen)**: プランナー・dry-run・本実行に対応。**2026-08-23 に実 API での入稿を確認済み**(PAUSED・1キャンペーン)。ただし配信実績はまだ無いので、初回は最小予算から始めて管理画面で目視確認する。**キャンペーンは常に PAUSED で入り、広告グループ・広告は既定で ENABLED**(#396)。キャンペーンをオンにした時点で配信が始まるので、オンにする前に検品を済ませる。詳細は下の「Google (Demand Gen) の対応範囲」を参照。
|
|
13
13
|
- **TikTok / ByteDance**: プランナー・dry-run・本実行に対応(**単一動画のみ**)。**2026-08-23 に実 API を初めて叩き、キャンペーン作成と自動ロールバックまで確認**。広告グループ以降は地域IDの取得手段が無くて未達だったため `regions` を追加した。**配信オン (ENABLE) は backend が拒否する**。詳細は下の「TikTok (単一動画) の対応範囲」を参照。
|
|
14
14
|
|
|
15
15
|
配置・発火方法・ログイン手順は [cli/README.md](../../README.md) の「初期設定: Skill として配置する」章を参照。
|
|
@@ -405,7 +405,7 @@ skills/ad-ops/scripts/adops.sh open yda <accountId> campaigns
|
|
|
405
405
|
|---|---|
|
|
406
406
|
| プランナーで計画を組む | ✅ できる(統合プルダウンで「Google」を選ぶ) |
|
|
407
407
|
| dry-run で不備を潰す | ✅ できる |
|
|
408
|
-
| **本実行(実際の入稿)** | ✅ **できる**(2026-08-23 に実 API
|
|
408
|
+
| **本実行(実際の入稿)** | ✅ **できる**(2026-08-23 に実 API で確認済み)。ただし**キャンペーンは配信オン (ENABLED) では入稿できない** — backend が 400 で拒否し、lint も ERROR で止める。**広告グループ・広告は ENABLED で入稿できる**(プランナーの既定。キャンペーンは常に PAUSED で書き出す)。**入稿 → 管理画面で検品 → キャンペーンをオンにするだけで配信が始まる**運用(#396)。配下が PAUSED だと lint が WARN を出す(キャンペーンをオンにしても配信されない)。広告が1件も作れなかった CP は自動削除される |
|
|
409
409
|
| 既存キャンペーン・広告グループへの広告追加 | ❌ 未対応。毎回あたらしいキャンペーンを作る形になる |
|
|
410
410
|
| 動画ファイルのアップロード | ✅ **対応**(#246)。プランナーの広告行「動画パス」にローカル動画を指定すると、本実行の直前に CLI がアップロードして YouTube 動画IDに解決する(dry-run では上げない)。動画IDとパスを同時に載せない。⚠ **上がり先は Google 管理チャンネルの限定公開で、広告主のチャンネルには置けない**(手上げ運用とは置き場所が変わる)。⚠ **実 API での検証は未了** |
|
|
411
411
|
| 1 広告に動画を複数本(縦・横など) | ✅ **対応**(#347)。動画広告の「クリエイティブ番号」セル → 右サイドバーで**押すたびに追加/解除**(最大 5 本・選んだ順。セルに `542, 543` と打っても同じ)。**先頭が代表**で、広告名はその番号から付く。縦横の区別は不要(Google が配信面ごとに選ぶ)。2本目以降は登録済みの番号に限る。入稿計画は 2 本以上のときだけ `videos: [{youtubeVideoId}\|{videoPath}]`、1 本なら旧形式。backend / CLI は 5 本まで受ける(公式ヘルプ「Upload 1-5 videos per ad」。API リファレンスには本数の記載が無い)。⚠ **実 API での検証は未了** |
|
|
@@ -748,7 +748,7 @@ finalUrl = baseURL + paramCode + "?ad=" + paramCode + "&creative=" + …
|
|
|
748
748
|
<div id="mt-google" style="display:none">
|
|
749
749
|
<div class="eyebrow">Google Ads (Demand Gen) — 対応範囲・要件一覧</div>
|
|
750
750
|
<h1 class="title">Google 広告入稿 <span class="pill green" style="vertical-align:middle">実 API 確認済み</span></h1>
|
|
751
|
-
<p class="sub">2026-08-23 更新。<b>実 API での入稿を確認済み</b>(PAUSED で 1 キャンペーン)。<b
|
|
751
|
+
<p class="sub">2026-08-23 更新。<b>実 API での入稿を確認済み</b>(PAUSED で 1 キャンペーン)。<b>キャンペーンは配信オフ(PAUSED)でしか入稿できない</b> — 意図しない配信・課金を防ぐため backend が 400 で拒否する。<b>広告グループ・広告は配信オン(ENABLED)で入稿でき、プランナーの既定もオン</b>。入稿 → 管理画面で検品 → <b>キャンペーンをオンにするだけで配信が始まる</b>運用(2026-10-29・#396)。Google Ads API v23 の proto 定義に照合して実装。</p>
|
|
752
752
|
|
|
753
753
|
<details class="sec" open><summary><span class="num">1</span><span class="stitle">概要 — なぜ Demand Gen なのか</span><span class="chev"></span></summary>
|
|
754
754
|
<div class="body">
|
|
@@ -756,7 +756,7 @@ finalUrl = baseURL + paramCode + "?ad=" + paramCode + "&creative=" + …
|
|
|
756
756
|
<p>Demand Gen は YouTube(インストリーム / インフィード / ショート)・Discover・Gmail・ディスプレイに配信できる。画像広告と動画広告を同じキャンペーンで扱えるため、広告行の「形式」列で切り替える(Meta と同じ作り)。</p>
|
|
757
757
|
<table class="g"><thead><tr><th>レイヤ</th><th>状態</th><th>内容</th></tr></thead><tbody>
|
|
758
758
|
<tr><td>backend</td><td><span class="pill green">実装済み</span></td><td><code>/api/submission/google</code> + <code>GoogleSubmissionConnector</code>。Demand Gen キャンペーン / 広告グループ / 動画・画像広告 / 目標CPA入札 / 地域・言語ターゲティング / 配信面の制御</td></tr>
|
|
759
|
-
<tr><td>CLI</td><td><span class="pill green">実 API 確認済み</span></td><td><code>webgram submit-plan</code> が Google に対応(dry-run / 本実行とも)。2026-08-23 に PAUSED で入稿を確認した。初回は <b>
|
|
759
|
+
<tr><td>CLI</td><td><span class="pill green">実 API 確認済み</span></td><td><code>webgram submit-plan</code> が Google に対応(dry-run / 本実行とも)。2026-08-23 に PAUSED で入稿を確認した。初回は <b>1キャンペーン・最小予算</b>で流し、管理画面で目視確認する。<b>キャンペーンは常に PAUSED で入り、広告グループ・広告は既定で ENABLED</b>(#396)。キャンペーンをオンにした時点で配信が始まるので、オンにする前に検品を済ませる。広告が1件も作れなかったキャンペーンは自動で削除される</td></tr>
|
|
760
760
|
<tr><td>プランナーGUI</td><td><span class="pill green">実装済み</span></td><td>ヘッダーの統合プルダウンで Google に切替。シート(広告 / 広告グループ / キャンペーン)・ツリー・参照データ・入稿JSON書き出しに対応</td></tr>
|
|
761
761
|
<tr><td>実 API 検証</td><td><span class="pill green">実施済み</span></td><td>2026-08-23 に本番アカウントで Demand Gen 画像広告を PAUSED 入稿し、作成を確認した。developer token のアクセスレベルは Basic 以上(読み取りが通っているため)。<b>ただし配信オン(ENABLED)での実配信は未検証</b>。また<b>アカウントによって書き込み可否が違う</b>(停止・未有効化のものがある)</td></tr>
|
|
762
762
|
</tbody></table>
|
|
@@ -918,7 +918,7 @@ finalUrl = baseURL + paramCode + "?ad=" + paramCode + "&creative=" + …
|
|
|
918
918
|
(cli/test/unit/plannerVersion.test.ts が package.json との一致を検証するので、
|
|
919
919
|
手で書き換えて放置するとテストが落ちる)。
|
|
920
920
|
利用者が「どの版のプランナーを開いているか」を言えないと、不具合報告が噛み合わない。 */
|
|
921
|
-
const APP_VERSION = '0.
|
|
921
|
+
const APP_VERSION = '0.18.0'; /* version:sync */
|
|
922
922
|
/* 案A: embed廃止。参照データ(refs)・初期プラン・共通設定は外部JSONから起動時に読み込む。
|
|
923
923
|
同梱デフォルト: アカウント/<媒体>/index.json の先頭アカウント (refs+principal+projectName+baseArticleUrl) と サンプルプラン.json (project+campaigns)。
|
|
924
924
|
固有名詞はハードコードせず、すべて外部JSON由来にする。 */
|
|
@@ -3938,7 +3938,8 @@ const GOOGLE_COLS = {
|
|
|
3938
3938
|
{field:'tg_channel', label:'配信面', kind:'tgpick', tgKey:'tg_channel', readonlyOnExisting:true, roEmptyDash:true,
|
|
3939
3939
|
title:'YouTube だけに出したい場合はここで絞る。「すべて」なら Google の既定に任せる'},
|
|
3940
3940
|
{field:'deliveryStatus', label:'配信', kind:'select', options:['','オフ','オン'], minWidth:'66px',
|
|
3941
|
-
readonlyOnExisting:true, roEmptyDash:true, coerce:v=>{const s=String(v).trim(); return (s==='オフ'||s==='オン')?s:'';}
|
|
3941
|
+
readonlyOnExisting:true, roEmptyDash:true, coerce:v=>{const s=String(v).trim(); return (s==='オフ'||s==='オン')?s:'';},
|
|
3942
|
+
title:'この広告グループと配下の広告の配信。空欄なら案件設定の「配信」に従う。キャンペーンは常にオフで入稿します'}
|
|
3942
3943
|
],
|
|
3943
3944
|
campaigns:[
|
|
3944
3945
|
{field:'campaignId', label:'キャンペーンID', kind:'badge', alwaysReadonly:true},
|
|
@@ -4053,7 +4054,10 @@ PLATFORMS.google = {
|
|
|
4053
4054
|
options:['詳しくはこちら','購入する','登録する','ダウンロード','お問い合わせ','今すぐ申し込む',
|
|
4054
4055
|
'予約する','見積もりを取得','購読する','インストール','サイトへアクセス','もっと見る','ボタンなし'],
|
|
4055
4056
|
title:'画像広告のボタン文言。使える値はアカウントによって違い、弾かれると入稿時にエラーが出ます。動画広告ではアセット登録が要るため今は送りません'},
|
|
4056
|
-
|
|
4057
|
+
/* キャンペーンは常に PAUSED で書き出す(backend が CP の ENABLED を拒否する)。
|
|
4058
|
+
この「配信」は広告グループ・広告にだけ効く (#396)。 */
|
|
4059
|
+
{key:'deliveryStatus', label:'配信(広告グループ・広告)', kind:'select', options:['オフ','オン'],
|
|
4060
|
+
title:'キャンペーンは常にオフ(PAUSED)で入稿します。ここは広告グループ・広告の配信です。オンにしておくと、検品後にキャンペーンをオンにするだけで配信が始まります'},
|
|
4057
4061
|
{key:'baseArticleUrl', label:'記事URL', kind:'text', width:'16ch', fallback:''},
|
|
4058
4062
|
{key:'urlParams', label:'URLパラメータ', kind:'toggle', fallback:true,
|
|
4059
4063
|
title:'OFF にすると最終リンク先URLに ?ad= を付けません(案件単位)'}
|
|
@@ -4121,7 +4125,10 @@ PLATFORMS.google = {
|
|
|
4121
4125
|
targetCpa:8500,
|
|
4122
4126
|
project:{
|
|
4123
4127
|
businessName:'', logoAsset:'', logoImagePath:'', cta:'詳しくはこちら', startDate:'',
|
|
4124
|
-
|
|
4128
|
+
/* 広告グループ・広告の配信。キャンペーンは常に PAUSED なので、既定をオンにして
|
|
4129
|
+
「検品後にキャンペーンをオンにするだけで配信」にする (#396)。
|
|
4130
|
+
保存済みのプランに入っている値(オフ)はそのまま使う。 */
|
|
4131
|
+
deliveryStatus:'オン'
|
|
4125
4132
|
},
|
|
4126
4133
|
ad:{format:'VIDEO', imageNo:'', moreVideoNos:'', titleNo:'', longNo:'', descNo:'', videoPath:'', adName:''},
|
|
4127
4134
|
adGroup:{name:'', paramCode:'', isExisting:false,
|
|
@@ -4428,7 +4435,9 @@ PLATFORMS.google = {
|
|
|
4428
4435
|
const P=PLATFORMS.google, p=ctx.project;
|
|
4429
4436
|
const cp={
|
|
4430
4437
|
name:c.name,
|
|
4431
|
-
|
|
4438
|
+
/* キャンペーンは常に PAUSED。backend が CP の ENABLED を 400 で拒否する(実配信が未検証)。
|
|
4439
|
+
案件設定の「配信」は広告グループ・広告にだけ効く (#396)。 */
|
|
4440
|
+
status:P.enums.statusOff,
|
|
4432
4441
|
budgetAmountYen:Number(c.dailyBudget)||0,
|
|
4433
4442
|
advertisingChannelType:P.enums.advertisingChannelType
|
|
4434
4443
|
};
|
|
@@ -516,6 +516,18 @@ def lint(plan: dict, plan_path: str = "") -> None:
|
|
|
516
516
|
" まとめて入稿すると2件目以降が落ちます")
|
|
517
517
|
if key:
|
|
518
518
|
plan_cp_names.add(key)
|
|
519
|
+
# Google はキャンペーンを PAUSED でしか入稿できない(backend が ENABLED を 400)。
|
|
520
|
+
# 配下まで PAUSED だと、検品後にキャンペーンだけオンにしても配信されない
|
|
521
|
+
# (2026-10-28 桑の葉69 の5CPが実績0)。配下の ENABLED は backend が通す (#396)。
|
|
522
|
+
paused = [f"広告グループ「{g.get('name', '')}」" for g in ad_groups(c)
|
|
523
|
+
if status_of(g) not in ON_STATUSES]
|
|
524
|
+
paused += [f"広告「{a.get('adName') or a.get('name') or '?'}」"
|
|
525
|
+
for g in ad_groups(c) for a in g.get("ads") or []
|
|
526
|
+
if status_of(a) not in ON_STATUSES]
|
|
527
|
+
if paused and cp_status not in ON_STATUSES:
|
|
528
|
+
more = f" ほか{len(paused) - 3}件" if len(paused) > 3 else ""
|
|
529
|
+
warn(f"CP「{name}」の配下に PAUSED があります({'、'.join(paused[:3])}{more})。"
|
|
530
|
+
" キャンペーンをオンにしても、PAUSED の広告グループ・広告は配信されません")
|
|
519
531
|
|
|
520
532
|
for g in ad_groups(c):
|
|
521
533
|
gname = g.get("name", "")
|
|
@@ -120,6 +120,43 @@ python3 "$SCRIPT" --wait --timeout 8 >/dev/null 2>&1; rc=$?
|
|
|
120
120
|
check "timeout した(サブディレクトリは対象外)" 124 "$rc"
|
|
121
121
|
wait
|
|
122
122
|
|
|
123
|
+
echo "=== 8. Google: CP は PAUSED 必須・配下の PAUSED は WARN (#396) ==="
|
|
124
|
+
rm -rf "$ADOPS_PLAN_DIR"/* "$ADOPS_DOWNLOADS"/*.json
|
|
125
|
+
mkdir -p "$SKILL/アカウント/google"
|
|
126
|
+
cat > "$SKILL/アカウント/google/テスト案件G.json" <<'EOF'
|
|
127
|
+
{"accountJsonVersion":2,"platform":"google","accountId":"1234567890","name":"テスト案件G",
|
|
128
|
+
"baseArticleUrl":"https://example.com/ab/","urlParamsEnabled":false,"refs":{}}
|
|
129
|
+
EOF
|
|
130
|
+
# $1=出力先 $2=CP status $3=AG status $4=広告 status
|
|
131
|
+
google_plan_json() {
|
|
132
|
+
cat > "$1" <<EOF
|
|
133
|
+
{"planVersion":1,"platform":"google","accountId":"1234567890","customerId":"1234567890",
|
|
134
|
+
"campaigns":[{"name":"CP_G","status":"$2","budgetAmountYen":5000,
|
|
135
|
+
"adGroups":[{"name":"AG1","status":"$3","ads":[
|
|
136
|
+
{"type":"DEMAND_GEN_VIDEO_RESPONSIVE_AD","adName":"AD1","status":"$4","youtubeVideoId":"abc",
|
|
137
|
+
"headlines":["見出し"],"descriptions":["説明"],"businessName":"テスト",
|
|
138
|
+
"finalUrls":["https://example.com/ab/x"]}]}]}]}
|
|
139
|
+
EOF
|
|
140
|
+
}
|
|
141
|
+
google_plan_json "$ADOPS_PLAN_DIR/g1.json" PAUSED ENABLED ENABLED
|
|
142
|
+
out=$(python3 "$SCRIPT" "$ADOPS_PLAN_DIR/g1.json" 2>&1); rc=$?
|
|
143
|
+
# exit 0 も見る(異常終了で指摘が出なかっただけ、を通さない)
|
|
144
|
+
[ "$rc" = "0" ] && ! echo "$out" | grep -q "ENABLED(配信オン)\|配下に PAUSED" \
|
|
145
|
+
&& { echo " ✓ CP PAUSED +配下 ENABLED は通る"; pass=$((pass+1)); } \
|
|
146
|
+
|| { echo " ✗ CP PAUSED +配下 ENABLED が通らない (exit=$rc)"; echo "$out" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
147
|
+
|
|
148
|
+
google_plan_json "$ADOPS_PLAN_DIR/g2.json" PAUSED PAUSED PAUSED
|
|
149
|
+
out=$(python3 "$SCRIPT" "$ADOPS_PLAN_DIR/g2.json" 2>&1)
|
|
150
|
+
echo "$out" | grep -q "配下に PAUSED があります" \
|
|
151
|
+
&& { echo " ✓ 配下が PAUSED なら WARN"; pass=$((pass+1)); } \
|
|
152
|
+
|| { echo " ✗ 配下 PAUSED の WARN が出ない"; echo "$out" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
153
|
+
|
|
154
|
+
google_plan_json "$ADOPS_PLAN_DIR/g3.json" ENABLED ENABLED ENABLED
|
|
155
|
+
out=$(python3 "$SCRIPT" "$ADOPS_PLAN_DIR/g3.json" 2>&1); rc=$?
|
|
156
|
+
echo "$out" | grep -q "CP「CP_G」が ENABLED(配信オン)" && [ "$rc" != "0" ] \
|
|
157
|
+
&& { echo " ✓ CP ENABLED は ERROR (exit=$rc)"; pass=$((pass+1)); } \
|
|
158
|
+
|| { echo " ✗ CP ENABLED が ERROR にならない (exit=$rc)"; echo "$out" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
159
|
+
|
|
123
160
|
echo
|
|
124
161
|
echo "══ 結果: ${pass} passed / ${fail} failed ══"
|
|
125
162
|
rm -rf "$ROOT"
|