Skip to content

API-009 おすすめ台 REST ​

← API-一覧

概要 ​

おすすめ台担当 bot が、ホールの台データを読んでおすすめ台の案を作り、下書きとして保存するための REST。おすすめ台担当ロール(recommend_picker、Capability read_recommend_data / write_recommend_draft)のユーザーがアプリケーションパスワードで呼ぶ。

  • 対象ホールは HallEnum::get_ana_slo_halls()(island / espasu / bigapple)。どのホールも案は最大 20 台
  • bot が書き込めるのは案の下書き(db_recommendation_draft)だけ。記事への反映・公開はしない
  • 案の承認・却下は管理画面(ADM-037、manage_options)だけで行う。管理画面は開いたときの revision と一致する draft の行だけを書き換えるため、その間に bot が上書きした案は承認されない。承認・却下済みの案は bot から上書きできない
  • BB / RB は返さない。台データは差枚(samai)と G 数(g)だけを使う
  • 運用確認・日別記事編集・X告知用の REST とは namespace を分ける。アカウントとアプリケーションパスワードも分ける

案の項目(draft) ​

field型説明
idint案の ID
hallstringホール(HallEnum::value)
target_datestringおすすめ対象日(Y-m-d)
method_versionstring選定ロジックの版
itemsarrayおすすめ台(下記)。rank の昇順
notestringbot のメモ
statusstringdraft(下書き)/ approved(承認済み)/ rejected(却下)
revisionint版。作成時は 1 で、上書きするたびに 1 増える
created_byint最後に保存したユーザー ID
approved_byint|null承認・却下したユーザー ID。未判断は null
created_atstring作成日時(DB の created_at)
updated_atstring更新日時(DB の updated_at)
results_availablebool対象日の台データがあるか。API-009-1 だけが付ける

おすすめ台の項目(items[]) ​

field型説明
rankint順位(1〜20)
dainumstring台番号(数字 1〜6 桁)
kishu_idint機種 ID(db_kishu_master.id)
scorefloatbot が付けた点数(小数 4 桁に丸める)
reason_codestring選んだ理由の種類(英小文字・数字・_、32 文字以内。値の一覧は bot 側で決める)
reason_textstring選んだ理由(200 文字以内)
resultobject|null対象日の結果 {kishu_id, samai, g}。API-009-1 だけが付ける。台データが無い・台が無いときは null

API-009-1 GET /drafts ​

保存済みの案と、対象日の台データがあればその結果を返す。

入力(リクエスト) ​

param必須型・制約説明
hallはいisland / espasu / bigappleホール
target_dateはいY-m-dおすすめ対象日

出力(レスポンス) ​

field型説明
successbooltrue
hallstringホール
target_datestring対象日
draftobject|null案の項目。保存されていなければ null

HTTP ステータス: 200。items[].result は db2023 の対象日・ホールの行を台番号で引く。台番号が同じでも機種が入れ替わっている場合があるため、result.kishu_id と案の kishu_id を比べて判断すること。

失敗・エラー条件 ​

条件HTTPmessage
hall が無い・対象ホール以外400RecommendRestCopy::HALL_INVALID
target_date が無い・Y-m-d でない・存在しない日付400RecommendRestCopy::TARGET_DATE_INVALID
DB の読み込みで例外が起きた503RecommendRestCopy::READ_FAILED

API-009-2 POST /drafts ​

案を保存する。ホール×対象日ごとに 1 件で、draft の間は何度でも上書きできる。

入力(リクエスト) ​

JSON ボディ。

param必須型・制約説明
hallはいisland / espasu / bigappleホール
target_dateはいY-m-d。サイトのタイムゾーンで今日の 31 日前から 31 日後までおすすめ対象日
method_versionはい英数字と . _ -、1〜32 文字選定ロジックの版
itemsはい1〜20 件の配列(下記)おすすめ台
noteいいえ文字列、200 文字以内bot のメモ。省略時は空文字

items[]:

param必須型・制約
rankはい1〜20 の整数
dainumはい数字 1〜6 桁の文字列(正の整数も受け付けて文字列にする)
kishu_idはい正の整数
scoreはい数値(JSON の number。絶対値 1,000,000 以下)
reason_codeはい英小文字・数字・_、1〜32 文字
reason_textいいえ文字列、200 文字以内

rank と dainum はそれぞれ重複できない。保存時は rank の昇順に並べ替える。

reason_text と note は受け取った文字列をそのまま保存する(HTML の除去はしない)。管理画面(ADM-037)など表示する側で必ずエスケープすること。

台番号と機種は、ホールの db2023 最新取込日のデータと照合する。最新取込日にその台番号が無い、または機種 ID が違う台が 1 件でもあれば保存しない(入れ替え前の台番号で案を作るのを防ぐため)。

出力(レスポンス) ​

field型説明
successbooltrue
resultstringcreated(新規保存)/ updated(下書きを上書き)
draftobject保存後の案の項目(results_available と result は付けない)

HTTP ステータス: created は 201、updated は 200。

失敗・エラー条件 ​

条件HTTPmessage
hall が無い・対象ホール以外400RecommendRestCopy::HALL_INVALID
target_date が無い・Y-m-d でない・存在しない日付400RecommendRestCopy::TARGET_DATE_INVALID
target_date が今日の 31 日前より前・31 日後より後400RecommendRestCopy::TARGET_DATE_OUT_OF_RANGE
method_version が上記の形式でない400RecommendRestCopy::METHOD_VERSION_INVALID
note が文字列でない・200 文字を超える400RecommendRestCopy::NOTE_INVALID
items が無い・配列でない・空・21 件以上・キー付きの連想配列400RecommendRestCopy::ITEMS_INVALID
items[] のどれかが上記の形式でない(メッセージに添字が入る)400RecommendRestCopy::ITEM_INVALID
rank または dainum が重複400RecommendRestCopy::ITEM_DUPLICATE
最新取込日に無い台番号・機種 ID が違う台がある(メッセージに順位が入る)400RecommendRestCopy::UNITS_INVALID
ホールの台データが 1 日分も無い409RecommendRestCopy::NO_UNIT_DATA
案が承認済み・却下済み409RecommendRestCopy::DRAFT_LOCKED
DB に保存できなかった・DB の読み書きで例外が起きた503RecommendRestCopy::SAVE_FAILED

上書きは status = 'draft' の行だけを対象にし、上書きするたびに revision を 1 増やす。保存の直前に管理画面で承認・却下された場合も 409 になる。同じホール・対象日を同時に新規保存して INSERT が重複キーで失敗したときは、読み直して draft なら上書きする(結果は updated)。例外は error_log に 1 行(RecommendRestCopy::LOG_ERROR_FORMAT)残し、監査ログの outcome は failed になる。

監査ログ ​

BotRestAuditLogger で error_log に 1 行 JSON(接頭辞 [bot-rest-audit])を書く。記録するのはルート・ユーザー ID・hall・target_date・件数(item_count)・method_version・結果(outcome: created / updated / locked / invalid_units / no_unit_data / failed)だけ。reason_text と note は記録しない。入力検証で 400 になったリクエスト(台番号の照合で 400 になったものを除く)は記録しない。

台データの項目(items[]) ​

API-009-4・5・6 で返す db2023 の 1 行。BB / RB は返さない。

field型説明
datestring日付(Y-m-d)
dainumstring台番号
kishu_idint機種 ID(db_kishu_master.id)
kishustring機種名(マスタの正規名称。マスタに無いときは 未紐づけ)
samaiint差枚
gintG 数(db2023 の kaiten)
samai_stepintその日・ホールの差枚の刻み(150 / 50 / 1。下記)

samai_step は、その日・ホールの台データのうち差枚が 0 でない行がすべて 150 の倍数なら 150、50 の倍数なら 50、それ以外は 1(判定に使える行が無い日も 1)。差枚が -4900 以下の行は下限への張り付き(アイランドの -5000 / -4988 / -4954 / -4919 など。丸めの刻みに揃わない)とみなし、判定に使わない。差枚が丸められて取り込まれている日があり、刻みは 1 台の行からは決まらないため、ホール全体の行から求める(API-009-5 の 1 台分の履歴でも同じ値)。刻みが 50 や 150 の日の差枚は丸めた値なので、samai が 0 の行も「0 前後」の意味になる。取り込み途中の日は、残りの行が入ると値が変わることがある。値はクエリのたびに DAY の範囲とホールで集計する(RecommendUnitDataRepositoryInterface::list_samai_steps)。

API-009-3 GET /coverage ​

ホールごとに、台データがある最初の日・最新日と、最新日までの直近 days 日で台データが無い日を返す。bot はデータが揃っているかをここで確かめてから案を作る。

入力(リクエスト) ​

param必須型・制約説明
hallいいえisland / espasu / bigapple省略時は 3 ホールすべて
daysいいえ1〜92 の整数欠けている日を探す日数(最新日を含む)。既定は 31

出力(レスポンス) ​

field型説明
successbooltrue
daysint受け付けた days
items[].hallstringホール
items[].first_daystring|null台データがある最初の日。データが無いホールは null
items[].latest_daystring|null台データがある最新日
items[].checked_fromstring|null欠けている日を探した最初の日(最初の日より前にはさかのぼらない)
items[].missing_datesarraychecked_from から latest_day までで台データが無い日(Y-m-d、昇順)

HTTP ステータス: 200。休業日も missing_dates に入る(休業日かどうかは判定しない)。

失敗・エラー条件 ​

条件HTTPmessage
hall が対象ホール以外400RecommendRestCopy::HALL_INVALID
days が 1〜92 の整数でない400RecommendRestCopy::DAYS_INVALID
DB の読み込みに失敗した503RecommendRestCopy::READ_FAILED

API-009-4 GET /units ​

期間内の台データをページ単位で返す。

入力(リクエスト) ​

param必須型・制約説明
hallはいisland / espasu / bigappleホール
date_fromはいY-m-d開始日(含む)
date_toはいY-m-d終了日(含む)
pageいいえ正の整数既定は 1
per_pageいいえ1〜2000 の整数既定は 500

期間は両端を含めて最大 31 日(RecommendUnitsRestController::UNITS_MAX_RANGE_DAYS)。

出力(レスポンス) ​

field型説明
successbooltrue
hallstringホール
date_fromstring開始日
date_tostring終了日
pageintページ
per_pageint1 ページの件数
totalint期間内の全件数
total_pagesint全ページ数
itemsarray台データの項目。日付・台番号(数値順)の昇順

HTTP ステータス: 200。total_pages を超えたページは items が空になる。

失敗・エラー条件 ​

条件HTTPmessage
hall が無い・対象ホール以外400RecommendRestCopy::HALL_INVALID
日付が無い・Y-m-d でない・存在しない日付・date_from が後400RecommendRestCopy::DATE_RANGE_INVALID
期間が 31 日を超える400RecommendRestCopy::DATE_RANGE_TOO_LONG
page が正の整数でない・per_page が 1〜2000 の整数でない400RecommendRestCopy::PAGE_INVALID
DB の読み込みに失敗した503RecommendRestCopy::READ_FAILED

ページは OFFSET で切るため、取り込み中の日を含む期間をページ送りすると行がずれることがある。取り込みが終わった日(/coverage の latest_day まで)を指定する。

API-009-5 GET /units/{dainum}/history ​

1 台の直近 days 日(ホールの最新日まで)の台データを返す。台番号が同じでも途中で機種が入れ替わることがあるため、前の行から機種 ID が変わった行に kishu_changed: true を付ける。

入力(リクエスト) ​

param必須型・制約説明
dainumはい数字 1〜6 桁(パス)台番号
hallはいisland / espasu / bigappleホール
daysいいえ1〜180 の整数最新日を含む日数。既定は 60

台番号はパスの値だけを使う(クエリ・ボディの dainum は無視する)。

出力(レスポンス) ​

field型説明
successbooltrue
hallstringホール
dainumstring台番号
date_fromstring|null開始日。ホールに台データが無いときは null
date_tostring|nullホールの最新日。ホールに台データが無いときは null
countintitems の件数
kishu_changesintkishu_changed が true の行の数
itemsarray台データの項目に kishu_changed(bool)を足したもの。日付の昇順

HTTP ステータス: 200。台番号が無い・期間内にデータが無いときも 200 で items が空。

失敗・エラー条件 ​

条件HTTPmessage
dainum が数字 1〜6 桁でない400RecommendRestCopy::DAINUM_INVALID
hall が無い・対象ホール以外400RecommendRestCopy::HALL_INVALID
days が 1〜180 の整数でない400RecommendRestCopy::DAYS_INVALID
DB の読み込みに失敗した503RecommendRestCopy::READ_FAILED

API-009-6 GET /export ​

1 年分の台データを 1 か月ずつ NDJSON(1 行 1 JSON)で返す。bot が手元で分析するための一括取得で、他の GET より厳しいレート制限をかける。毎日の取り直しは since でその日以降だけを取る(Issue #3918)。

入力(リクエスト) ​

param必須型・制約説明
hallはいisland / espasu / bigappleホール
yearはい2020〜今年の整数年
cursorいいえYYYY-MM(year と同じ年)取得する月。省略時は 1 月(since があれば since の月)。前回の next_cursor をそのまま渡す。since があるときは since の月以降
sinceいいえY-m-d(year と同じ年、今日まで)この日以降の行だけを返す。since の月より後の月(cursor)では月全体を返す

出力(レスポンス) ​

field型説明
successbooltrue
hallstringホール
yearint年
monthstring返した月(YYYY-MM)
sincestring|null受け付けた since(Y-m-d)。指定なしは null
formatstringndjson
countintndjson の行数
ndjsonstring台データの項目を 1 行ずつ JSON にして改行でつないだ文字列(末尾も改行)。0 件は空文字
next_cursorstring|null次の月。12 月、または今年の今月を返したときは null

HTTP ステータス: 200。今月は今日までを返す。メモリを抑えるため 1 日ずつ読み、JSON にできない行は飛ばす(飛ばした数は監査ログの skipped)。REST のレスポンスは JSON に限られるため、NDJSON は ndjson フィールドの文字列で返す。データが無い月も 200(count は 0)。1 年分は 12 回の呼び出しになる。レート制限(1 分あたり 20 回)を超えて 429 になったら、Retry-After の秒数だけ待ってから呼ぶ。

毎日の差分は ?hall=island&year=2026&since=2026-10-06 のように、前回の取得時点で取り込みが終わっていた日(/coverage の latest_day)の翌日を since に渡す。今日など取り込み途中の日を返したときは、その日の行と samai_step が途中の値なので、次回もう一度取り直す。since の月から始まり、月をまたぐときは next_cursor を since と一緒に渡して続きを取る(since より後の月は月全体)。年をまたぐときは新しい year の 1 月から since なしで取る。

失敗・エラー条件 ​

条件HTTPmessage
hall が無い・対象ホール以外400RecommendRestCopy::HALL_INVALID
year が 2020〜今年の整数でない400RecommendRestCopy::YEAR_INVALID
cursor が YYYY-MM でない・year と年が違う・月が不正400RecommendRestCopy::CURSOR_INVALID
since が Y-m-d の実在する日でない・year と年が違う・今日より後400RecommendRestCopy::SINCE_INVALID
cursor が since の月より前400RecommendRestCopy::CURSOR_BEFORE_SINCE
DB の読み込みに失敗した503RecommendRestCopy::READ_FAILED

監査ログ ​

BotRestAuditLogger で error_log に 1 行 JSON(接頭辞 [bot-rest-audit])を書く。記録するのはルート・ユーザー ID・hall・month・行数(count)・飛ばした行数(skipped)だけ。入力検証で 400、DB の失敗で 503 になったリクエストは記録しない。

DB 読み込みの失敗(API-009-3〜6 共通) ​

$wpdb->last_error が空でなければ 0 件と区別して 503 にし、error_log に 1 行(RecommendRestCopy::LOG_ERROR_FORMAT)残す。db2023 のインデックスは UNIQUE (DAY, dainum, hall) だけなので、最初の日・最新日は ORDER BY DAY ASC/DESC LIMIT 1 でインデックスの端から読み、それ以外のクエリは必ず DAY の範囲で絞る。

API-009-7・8・9 共通の入力 ​

param必須型・制約説明
hallはいisland / espasu / bigappleホール
date_fromはいY-m-d開始日(含む)
date_toはいY-m-d終了日(含む)

出力はどれも {success: true, hall, date_from, date_to, count, items}(count は items の件数)。HTTP ステータスは 200。

条件HTTPmessage
hall が無い・対象ホール以外400RecommendRestCopy::HALL_INVALID
日付が無い・Y-m-d でない・存在しない日付・date_from が後400RecommendRestCopy::DATE_RANGE_INVALID
期間が上限(各 API の項)を超える400RecommendRestCopy::DATE_RANGE_TOO_LONG
DB の読み込みに失敗した503RecommendRestCopy::READ_FAILED

機種別サマリ・〇〇の日マスタ・日別リンク・台数変化・日別記事の既存 Repository は DB の失敗を空配列にして返すため、RecommendSummaryService は Repository を呼ぶたびに DatabaseErrorProbeInterface($wpdb->last_error)で失敗を確かめ、0 件と区別して 503 にする。失敗したクエリの結果は BaseRepository がキャッシュしない。

API-009-7 GET /kishu-daily ​

日別記事の機種別サマリ(db_daily_article_kishu_single_day_summary)を機種・日ごとに返す。期間は最大 92 日(RecommendSummaryRestController::KISHU_DAILY_MAX_DAYS)。サマリを集計していない日は行が無い。

param必須型・制約説明
kishu_idsいいえ機種 ID(正の整数)のカンマ区切り、50 件以内指定した機種だけ返す。省略時はすべての機種
field型説明
items[].datestring日付
items[].kishu_idint機種 ID
items[].kishustring機種名
items[].countint台数
items[].total_samaiint総差枚
items[].avg_samaifloat平均差枚(小数 1 桁)
items[].avg_gfloat平均 G 数(小数 1 桁)
items[].total_samai_rankintその日のホール内の総差枚順位
items[].avg_samai_rankintその日のホール内の平均差枚順位

並びは日付の昇順、同じ日は total_samai_rank の昇順。kishu_ids が不正なときは 400(RecommendRestCopy::KISHU_IDS_INVALID)。

すべての機種を 92 日分取ると 1 万行を超える(数 MB)ことがある。必要な機種だけ kishu_ids で絞るか、期間を短くして呼ぶ。

API-009-8 GET /end-number ​

台データがある日ごとに、末尾 0〜9 とゾロ目の集計を返す。期間は最大 31 日(RecommendSummaryRestController::END_NUMBER_MAX_DAYS)。数え方は日別記事の末尾データと同じで、末尾は台番号の最後の数字(数字でなければ 0 として数える)、ゾロ目は台番号の最後の 2 文字(バイト単位)が同じ台(通常は下 2 桁が同じ数字。英字など数字以外でも同じならゾロ目になる。末尾の集計にも入る)。db2023 は 1 回のクエリで「日付 × 末尾 × ゾロ目かどうか」ごとの台数・差枚と G 数の合計・勝ち台数を SQL で集計して受け取り(RecommendUnitDataRepositoryInterface::aggregate_end_digits。機種マスタとは JOIN しない)、PHP では日ごとの末尾 0〜9 とゾロ目の合計にまとめるだけにする(台ごとの行は PHP に読み込まない)。

field型説明
items[].datestring日付(台データが無い日は含まない)
items[].unitsintその日の台数
items[].digits[].digitstring0〜9 または zoro(この順で 11 件)
items[].digits[].unitsint台数
items[].digits[].avg_samaifloat|null平均差枚(小数 1 桁)。台が無いときは null
items[].digits[].avg_gfloat|null平均 G 数(小数 1 桁)
items[].digits[].win_unitsint勝ち台数(差枚がプラスの台)

API-009-9 GET /calendar ​

期間内のすべての日について、曜日・〇〇の日・ホールのイベント・機種の台数変化(入替)を返す。期間は最大 92 日(RecommendSummaryRestController::CALENDAR_MAX_DAYS)で、先の日付も指定できる。

field型説明
items[].datestring日付
items[].weekdaystringsun〜sat
items[].weekday_jastring日〜土
items[].what_daysarray〇〇の日マスタ(db_what_day_master の表示中のもの)で日付が当てはまる名前
items[].espasu_what_daysarrayエスパスの日別記事に手入力で掲載した〇〇の日(考察日ごと。公開済みの記事だけ)。hall に関係なくエスパスの値
items[].eventsarray日別リンク(db_link_day.event)に登録したそのホールのイベント名
items[].replacements[].kishustring機種名
items[].replacements[].count_deltaint前日からの台数の増減(db_daily_article_kishu_count_delta)
items[].replacements[].is_new_kishubool新台情報(db_new_machine_info)の導入日が date_to の前月 1 日〜当月末にある機種か。期間内のどの日の行も date_to の月で判定する

〇〇の日とイベントは有料記事の AI ツール(fetch_what_days_by_period / fetch_event_info)と同じデータ。入替は台データを取り込んだ日にしか無いので、先の日付は空になる。

what_days は〇〇の日マスタ(ADM-004)だけを見る。マスタに表示中の行が 1 件も無いと全期間で空になる(エラーにはしない)。エスパスの日別記事に手入力した〇〇の日は espasu_what_days で返す。events は日別リンクに今登録されているイベント名で、日別リンクは登録した日時を持たない(事前に告知されたものか、あとから付いたものかは区別できない)。

権限・nonce・レート制限 ​

全エンドポイント共通。RecommendRestHandler + WordPressRecommendPermissionChecker。Handler がルートごとに action(read / export / write)を付け、Checker は action に応じて Capability とレート制限を変える。action が無い・不明なときは 403。

項目GET(API-009-1・3〜5・7〜9)GET /export(API-009-6)POST(API-009-2)
actionreadexportwrite
Capabilityread_recommend_dataread_recommend_datawrite_recommend_draft
レート制限ユーザー単位で 1 分あたり 120 回ユーザー単位で 1 分あたり 20 回ユーザー単位で 1 分あたり 20 回
カウンタ不可時通す(fail-open)拒否する(fail-closed、429)拒否する(fail-closed、429)
項目内容
匿名アクセス403
nonce検証しない(アプリケーションパスワード前提)。Cookie 認証で呼ぶ場合の REST nonce は WordPress コア側の挙動に従う
レート制限BotRestRateLimiter(transient、60 秒の固定ウィンドウ。最初の呼び出しから 60 秒)。バケットは recommend:read / recommend:export / recommend:write。超過時は 429
応答ヘッダー数えた呼び出しには X-RateLimit-Limit(上限)・X-RateLimit-Remaining(残り回数)・X-RateLimit-Reset(ウィンドウが切り替わる時刻の Unix タイムスタンプ)を付ける。429 には Retry-After(秒)も付ける。Local バイパス時・未ログイン時・カウンタを保存できなかったとき(read の fail-open で通したときと、export・write の fail-closed で 429 にしたとき)は付けない
ログログイン済みユーザーの 403 と、429 のときだけ error_log に 1 行(ステータス・user_id・ルート)。未ログインの 403 と許可したアクセスは記録しない
Local バイパスAPI-001 の WordPressPermissionChecker と同じ条件(レート制限もかけない)

WordPress は応答の Allow ヘッダーを作るため、同じリクエストで同じルートの全ハンドラーの permission_callback をもう一度呼ぶ(rest_send_allow_header)。WordPressRestApiAdapter が 1 リクエストにつき結果を 1 回だけ求めて使い回し、リクエストのメソッドを受け付けないハンドラー(GET /drafts のときの POST など)ではドメイン側を呼ばないため、レート制限は 1 リクエスト 1 回だけ数える(Issue #3918。以前は 1 回の呼び出しで 2 回数え、GET /drafts で recommend:write も減っていた)。このため応答の Allow にはリクエストのメソッドだけが載り(GET /drafts は Allow: GET)、OPTIONS の応答には Allow を付けない。レート制限を数えずに他のメソッドの可否を調べる手段が無いための意図した挙動。

おすすめ台担当ロールは wp-admin を開けない(MemberAdminAccessGuardHooks がサイトトップへリダイレクトし、管理バーも出さない)。記事の Capability は持たない。

WordPress コア経由の書き込みの遮断 ​

recommend_picker を持ち、他には REST 用 bot ロールしか持たないユーザーには BotWriteGuardHooks で、X告知担当(API-007)と同じ制限をかける。違いは REST で通す書き込みが POST /recommend/v1/drafts だけであること。エラーコードは recommend_write_forbidden(RecommendRestCopy::WRITE_FORBIDDEN)/ recommend_xmlrpc_forbidden(RecommendRestCopy::XMLRPC_FORBIDDEN)。