Appearance
API-006 運用確認 REST
概要
運用 bot が毎日確認する状態(当日・前日の日別記事の有無とステータス、考察フィールドの入力有無、毎朝の取込状況、新台情報取得の最終実行結果、誕生日紐付けの件数、P-WORLD メールと日別記事メール欄キャッシュの状態)を、wp-admin にログインせずに読むための読み取り専用 REST。運用確認bot ロール(ops_status_reader、Capability read_ops_status)のユーザーがアプリケーションパスワードで呼ぶ。
- GET のみ。どのルートも書き込みはしない
- 返す項目は許可リストで固定する。
post_content・任意の post meta・有料記事・会員情報・設定値(API キー等)は返さない - 考察フィールドの本文は
GET /daily-articles/{id}?include_body=1のときだけ返す - 日別記事の投稿タイプは
show_in_rest => falseのまま。この REST 経由でだけ決めた項目を読める - 日別記事編集用・取込用の REST とは namespace を分ける。アカウントとアプリケーションパスワードも分ける
日別記事の対象ステータスは publish / future / draft / pending(private / trash / auto-draft は含めない)。
日別記事の項目(共通)
| field | 型 | 説明 |
|---|---|---|
id | int | 投稿 ID |
kousatsu_date | string | 考察日(Y-m-d。Ymd で保存されている場合も Y-m-d に揃える) |
status | string | publish / future / draft / pending |
author.id | int | 作成者のユーザー ID |
author.display_name | string | 作成者の表示名 |
modified_gmt | string | 更新日時(GMT、Y-m-d H:i:s) |
considerations.{key}.filled | bool | タグを除いて空白以外の文字があるか |
considerations.{key}.length | int | 保存値そのまま(HTML タグを含む)の文字数(mb_strlen) |
considerations.{key}.body | string | 考察本文(HTML)。/daily-articles/{id} で include_body=1 のときだけ |
{key} は DailyArticleConsiderationMetaKeys::META_KEYS の 6 つ(island_pre / island_after / espasu_pre / espasu_after / bigapple_pre / bigapple_after)。
API-006-1 GET /summary
運用 bot の日次チェック用。指定日と前日の日別記事、取込状況、新台情報取得、指定日と翌日のおすすめ案の状態を 1 回で返す。
入力(リクエスト)
| param | 必須 | 型・制約 | 説明 |
|---|---|---|---|
date | いいえ | Y-m-d | 基準日。省略時はサイトのタイムゾーンの今日(current_time) |
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
date | string | 基準日(Y-m-d) |
daily_articles.dates | string[] | [前日, 基準日] |
daily_articles.missing_dates | string[] | dates のうち日別記事が 1 件も無い日(truncated が true のときは不正確になり得る) |
daily_articles.truncated | bool | 上限(200 件)で打ち切ったか |
daily_articles.items | array | 日別記事の項目(本文なし)。考察日・ID の昇順 |
imports | object | API-006-4 の latest_days / history と同じ。date には左右されず、常に実行日基準 |
new_machine | object | API-006-5 の last_run / unlinked_count と同じ |
recommendation_drafts | array | [基準日, 翌日] を対象日とするおすすめ案(db_recommendation_draft)。対象日ごとに HallEnum::get_ana_slo_halls() の順 |
recommendation_drafts[].target_date | string | 対象日(Y-m-d) |
recommendation_drafts[].hall | string | ホールキー |
recommendation_drafts[].hall_name | string | ホール名(日本語) |
recommendation_drafts[].id | int|null | 案の ID。案が無い・取得失敗時は null |
recommendation_drafts[].status | string|null | draft / approved / rejected。案が無い・取得失敗時、それ以外の値のときは null |
recommendation_drafts[].read_failed | bool | 取得に失敗したか(失敗時は error_log に 1 行残す) |
おすすめ案は ID と status だけを返し、台番号・理由・メモは返さない。中身はおすすめ台担当 bot の API-009 か管理画面 ADM-037 で確認する。
HTTP ステータス: 200。date が Y-m-d でない・存在しない日付のときは 400(OpsStatusRestCopy::DATE_INVALID)。
API-006-2 GET /daily-articles
入力(リクエスト)
| param | 必須 | 型・制約 | 説明 |
|---|---|---|---|
date_from | はい | Y-m-d | 考察日の開始日(含む) |
date_to | はい | Y-m-d | 考察日の終了日(含む) |
期間は両端を含めて最大 31 日。
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
date_from | string | 開始日 |
date_to | string | 終了日 |
count | int | items の件数 |
truncated | bool | 上限で打ち切ったか |
items | array | 日別記事の項目(本文なし)。考察日・ID の昇順 |
HTTP ステータス: 200。1 回の取得は最大 200 件(OpsStatusDailyArticleReaderInterface::LIST_LIMIT)。超えるときは投稿 ID の小さい順に 200 件を返し、truncated を true にする。期間を分けて取り直すこと。
失敗・エラー条件
| 条件 | HTTP | message |
|---|---|---|
| どちらかが無い・Y-m-d でない・存在しない日付 | 400 | OpsStatusRestCopy::DATE_RANGE_REQUIRED |
date_from が date_to より後 | 400 | OpsStatusRestCopy::DATE_RANGE_ORDER_INVALID |
| 期間が 31 日を超える | 400 | OpsStatusRestCopy::DATE_RANGE_TOO_LONG |
API-006-3 GET /daily-articles/{id}
入力(リクエスト)
| param | 必須 | 型・制約 | 説明 |
|---|---|---|---|
id | はい | 正の整数(パス) | 日別記事の投稿 ID |
include_body | いいえ | 1 / true / yes / on で有効 | 有効なとき考察フィールドの本文を含める |
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
item | object | 日別記事の項目。include_body 有効時だけ considerations.{key}.body を含む |
HTTP ステータス: 200
失敗・エラー条件
| 条件 | HTTP | message |
|---|---|---|
id 不正 | 400 | Messages::REST_INVALID_ID_MESSAGE |
| 日別記事でない・対象ステータス外・存在しない | 404 | OpsStatusRestCopy::ARTICLE_NOT_FOUND |
API-006-4 GET /imports
入力(リクエスト)
なし。
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
latest_days | array | HallEnum::get_ana_slo_halls() の順(island / espasu / bigapple) |
latest_days[].hall | string | ホールキー |
latest_days[].hall_name | string | ホール名(日本語) |
latest_days[].latest_day | string|null | db2023 のそのホールの最新日(DailyDataPerUnitRepositoryInterface::select_latest_day_by_hall)。取得失敗時は null |
latest_days[].missing_dates | string[]|null | 昨日までの直近 14 日(OpsStatusRestController::MISSING_LOOKBACK_DAYS)のうち、db2023 にそのホールの行が無い日(古い順)。今日はまだ取り込み前のため含めない。定休日・臨時休業日も含まれる。取得失敗時は null |
history | array | MinRepoImportHistory::list_recent(20) の新しい順 |
history[].date | string | 取込対象日 |
history[].hall_keys | string[] | 取込ホール |
history[].imported_at | string | 取込日時(サイトのタイムゾーン) |
history[].row_count | int | 取込件数 |
history[].source | string | 取込元。履歴に記録されている場合だけ含む |
HTTP ステータス: 200
API-006-5 GET /new-machine
入力(リクエスト)
なし。
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
last_run | object|null | 最終実行結果(NewMachineFetchOptions::get_last_run_log)。未実行なら null |
last_run.ran_at | string|null | 実行日時(UTC、ISO 8601)。保存値に無い・不正なときは null |
last_run.inserted | int | 新規件数 |
last_run.updated | int | 更新件数 |
last_run.fetched | int | パース件数 |
last_run.skipped_sites | string[] | クールダウン等でスキップしたサイト |
last_run.failed_sites | string[] | 取得に失敗したサイト |
last_run.unregistered_sites | string[] | Scraper 未登録のサイト |
unlinked_count | int | 機種マスタ未紐付けの新台情報の件数(非表示も含む) |
実行ログの各行(log)は取得元 URL やエラー詳細を含み得るため返さない。保存値に項目が欠けているときは、数値は 0、一覧は空配列、ran_at は null にする。unlinked_count は NewMachineInfoRepositoryInterface::count_unlinked()(kishu_id が NULL または 0 以下)で数える。HTTP ステータス: 200
API-006-6 GET /birthday
誕生日紐付けの状況を件数だけで返す。作品名・誕生日・機種の中身は返さない(中身の確認と紐付けは誕生日紐付け担当 bot が API-008 で行う)。
入力(リクエスト)
なし。
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
unlinked_title_count | int | 機種が 1 件も紐付いていない有効な作品名の数(BirthdayLinkStatusServiceInterface::summarize) |
upcoming_days | int | 近日として数える日数(OpsStatusRestController::BIRTHDAY_UPCOMING_DAYS = 14、今日を含む) |
upcoming_unlinked_count | int | 今日から upcoming_days 日以内の誕生日のうち、作品名に機種が紐付いていないものの数 |
last_sulocale_import_at | string|null | sulocale から最後に取り込んだ日時(UTC、ISO 8601、BirthdaySeedRunLock::get_last_import_at)。未取得なら null |
「今日」はサイトのタイムゾーン(wp_timezone())で決める。HTTP ステータス: 200
API-006-7 GET /pworld-mails
記事対象日が date の P-WORLD メールと、公開側の日別記事メール欄キャッシュの状態を返す。DB にメールがあるのに記事にメール画像が出ないときの切り分けに使う(Issue #4051)。メール本文(processed_html)は返さない。
入力(リクエスト)
| param | 必須 | 型・制約 | 説明 |
|---|---|---|---|
date | いいえ | Y-m-d | 記事対象日。省略時はサイトのタイムゾーンの今日(current_time) |
出力(レスポンス)
| field | 型 | 説明 |
|---|---|---|
success | bool | true |
date | string | 記事対象日(Y-m-d) |
cache_generation | string | 日別記事キャッシュの世代(DailyArticleFrontendCacheGeneration::token、g{全体}.{月}.{日})。取込やメール保存の後に日の値が増えていれば無効化が届いている |
count | int | items の件数 |
items | array | article_target_date が date のメール行(pworld_mail_archive)。ID の昇順 |
items[].id | int | メール ID |
items[].hall_name | string | ホール名(空なら未紐付け) |
items[].article_target_date | string | 記事対象日 |
items[].show_in_article | bool | 記事に表示するフラグ |
items[].displayed | bool | 記事のメール欄に出る行か(ホール名あり・記事対象日あり・表示フラグ on。PworldMailArchiveEntity::is_displayed_in_article)。本文の有無は has_processed_html で見る |
items[].received_date | string | 受信日時 |
items[].created_at | string | 取り込み日時 |
items[].has_processed_html | bool | 加工済み本文があるか |
items[].img_count | int | 加工済み本文の <img> の数 |
mail_image_cache | array | 日別記事に出すホールごとのメール欄ブロックキャッシュ(DailyArticleBlockHtmlCacheService、現在の世代) |
mail_image_cache[].hall | string | ホールキー |
mail_image_cache[].hall_name | string | ホール名(日本語) |
mail_image_cache[].state | string | miss(キャッシュ無し。次の表示で作る)/ empty(「画像なし」をキャッシュ中。記事対象日が今日・明日なら最長 5 分、それより前は通常の TTL)/ images(画像ありをキャッシュ中) |
displayed: true の行があるのに state が empty のままなら、キャッシュの無効化が届いていない。HTTP ステータス: 200。date が Y-m-d でない・存在しない日付のときは 400(OpsStatusRestCopy::DATE_INVALID)。
権限・nonce・レート制限
全エンドポイント共通。OpsStatusRestHandler + WordPressOpsStatusPermissionChecker。
| 項目 | 内容 |
|---|---|
| Capability | read_ops_status。持たないときは 403(匿名アクセスも 403) |
| nonce | 検証しない(アプリケーションパスワード前提)。Cookie 認証で呼ぶ場合の REST nonce は WordPress コア側の挙動に従う |
| レート制限 | ユーザー単位で 1 分あたり 120 回(分ごとの固定ウィンドウ、transient ops_status_rl_{user_id}_{分})。超過時は 429 |
| fail-open | 読み取り専用のため、transient が使えずカウンタを取れないときは通す |
| ログ | ログイン済みユーザーの 403 と、429 のときだけ error_log に 1 行(ステータス・user_id・ルート)。未ログインの 403 と許可したアクセスは記録しない |
| Local バイパス | API-001 の WordPressPermissionChecker と同じ条件(レート制限もかけない) |
運用確認bot ロールは wp-admin を開けない(MemberAdminAccessGuardHooks がサイトトップへリダイレクトし、管理バーも出さない)。記事の Capability(edit_daily_articles / read_private_daily_articles 等)は持たない。
WordPress コア経由の書き込みの遮断
WordPress は read を持つユーザーに自分のプロフィール編集とアプリケーションパスワードの発行を許す。BotWriteGuardHooks(MemberServiceProvider で登録)で、REST 用 bot ロール(ops_status_reader / x_announcer(API-007)/ birthday_linker(API-008))だけを持つユーザーに次をかける。bot ロールを複数持つユーザーも対象にし、REST は持っている bot ロールそれぞれに許した書き込みを合わせたものだけ通す。エラーコードは誕生日紐付け担当 → X告知担当 → 運用確認bot の順で最初に持っているロールのものを返す。bot ロール以外のロールを併せ持つユーザーは対象外。
対象はアプリケーションパスワードでのアクセス(REST・XML-RPC)と wp-admin。ログインパスワードで wp-login からブラウザにログインした場合、フロントの会員プロフィール・退会フォームやコメント投稿は止めない。そのため、運用確認bot のログインパスワードは長いランダムな値にして保存・使用せず、アプリケーションパスワードだけで運用する(利用手順)。
| 経路 | フック | 内容 |
|---|---|---|
| REST | rest_pre_dispatch | GET / HEAD / OPTIONS 以外は 403(ops_status_read_only、OpsStatusRestCopy::WRITE_FORBIDDEN)。/wp/v2/users/me の更新やアプリケーションパスワードの API も含む。他の bot ロールを併せ持つ場合はそのロールの許可ルートは通し、エラーコードも上の順で決まる(例: x_announcer 併用なら x_announce_write_forbidden) |
| 権限 | map_meta_cap | 自分自身に対する edit_user / create_app_password / edit_app_password / delete_app_password / delete_app_passwords を do_not_allow。管理者が bot に対して行う操作は対象外。edit_user を外すため、自分のアプリケーションパスワードの一覧・取得(GET)もできない |
| XML-RPC | authenticate(優先度 100) | XML-RPC リクエスト中は bot のログインを拒否する(wp.editProfile 等すべて使えない)。xmlrpc_methods は全ユーザー共通のため使わない。アプリケーションパスワード認証(優先度 20)の後で判定する |