Skip to content

API-008 誕生日 REST ​

← API-一覧

概要 ​

誕生日紐付け担当 bot が、誕生日データ(db_birthday)・作品名マスタ・作品名と機種の紐付け(db_birthday_kishu)を wp-admin を開かずに検索・追加・更新するための REST。誕生日紐付け担当ロール(birthday_linker)のユーザーがアプリケーションパスワードで呼ぶ。Issue #3892。

  • 書き込みはすべて人間の確認後に行う(bot はプレビューや対象を人間に見せ、了承を得てから書き込みのルートを呼ぶ)。REST 側では確認の有無を判定しない。MCP の書き込みツールの説明に明記する(利用手順)
  • 紐付けと誕生日の追加・作品名の変更では、作品名・機種が有効(is_active = 1)でなければ 400。無効な作品名・機種へは新しく紐付けない
  • 機種マスタの編集・作品名の統合・全件シード(ADM-013 の「シードを実行」)はできない。管理画面(ADM-013 等)で管理者が行う
  • 書き込みは BotRestAuditLogger で監査ログに残す(下記「監査ログ」)
  • 運用確認・日別記事編集・取込・X告知用の REST とは namespace を分ける。アカウントとアプリケーションパスワードも分ける
  • 状況の件数だけなら運用確認bot が API-006-6 で読める
  • 日別記事 bot 向けのホール別・前後 N 日の誕生日(紐付いた機種の設置台数・去年の結果つき)と声優名・キャラ名の検索は、日別記事側の API-001-26 / 27(daily-article/v1、edit_daily_articles。Issue #3995)。本 API の権限(read_birthday_data)は日別記事 bot に付けなくてよい

項目 ​

作品名

field型説明
idint作品名 ID
namestring作品名
is_activebool有効か
birthday_countintこの作品名を参照する誕生日データの件数(一覧のときだけ)
link_countintこの作品名の機種紐付けの件数(一覧のときだけ)

機種: id(機種 ID)・name(機種名)。アクティブな機種だけを返す。

誕生日データ

field型説明
idint誕生日データ ID
monthint月
dayint日
divistringキャラ誕 / 声優誕
actorstring声優名(声優誕のときだけ。それ以外は空文字)
charastringキャラ名
title_idint作品名 ID(作品名なしは空の作品名の ID)
titlestring作品名

紐付け: title_id・title・kishu_id・kishu_name。

一覧の共通入出力 ​

一覧(API-008-1〜5)は limit(1〜200、既定 50。200 を超える値は 200、不正値は既定値)と offset(0 以上、既定 0)を受け付け、success・total(絞り込み後の全件数)・count・limit・offset・items を返す。

エラー応答 ​

コントローラーが返すエラーはすべて {"success": false, "code": "<code>", "message": "<文言>"}。bot は code で次の操作を分ける(message は人間に見せる文言で、変わることがある)。code は BirthdayRestErrorCodes の定数(snake_case)で、値を変えるときは本書と MCP 利用手順 も合わせる。Issue #3969。

codeHTTP意味・bot の次の操作
invalid_parameter400パラメータの形式・値が不正、作品名・機種がマスタに無い。入力を直す
title_inactive400作品名はマスタにあるが無効。人間の確認後に API-008-15 で有効にしてから再実行する
kishu_inactive400機種はマスタにあるが無効。bot からは有効にできない(管理画面で管理者が扱う)
no_change400変更前と同じ内容(紐付け・誕生日データ・作品名・有効/無効)。書き込みは不要
title_protected400作品名なし用の行は改名・無効化できない
not_found404対象(紐付け・誕生日データ・作品名)が無い。一覧で探し直す
duplicate409同じデータ(紐付け・誕生日データ・作品名)が既にある
sulocale_run_in_progress409他の誕生日取込が実行中(何も書き込んでいない)。時間をおいて再実行する
sulocale_preview_mismatch409プレビュー後にページ・登録状況が変わった(何も書き込んでいない)。API-008-16 からやり直す
sulocale_page_invalid422スロカレの作品ページとして読み取れない。URL を確認する
sulocale_too_many_rows422スロカレの抽出件数が上限を超えた
unexpected_error500想定外の例外(例外のメッセージは返さない)
sulocale_fetch_failed502スロカレのページを取得できなかった。時間をおいて再実行する
write_failed503DB 書き込みの失敗。時間をおいて再実行する

以下の各節で「400(MONTH_DAY_INVALID)」のように括弧内に書いたのは message の定数名(BirthdayRestCopy 等)。code は上の表のとおり。

権限チェックで拒否した 403・429 と、WordPress 側で返るエラー(存在しないルートの 404 rest_no_route、BotWriteGuardHooks の 403 birthday_write_forbidden、認証失敗の 401 等)はコントローラーを通らないため、WordPress の WP_Error 形式({"code", "message", "data": {"status"}}、success なし)で返る。権限チェックの 403・429 はどちらも code: rest_forbidden のため、HTTP ステータスで分ける(MCP 利用手順 の「トラブルシューティング」)。

読み取り(GET) ​

API-008-1 GET /titles ​

param必須説明
searchいいえ作品名の部分一致
activeいいえ1=有効のみ / 0=無効のみ / それ以外=すべて

作品名の昇順。items は作品名の項目。

API-008-2 GET /kishu ​

param必須説明
searchいいえ機種名の部分一致

API-008-3 GET /birthdays ​

param必須説明
charaいいえキャラ名の部分一致
titleいいえ作品名の部分一致
diviいいえキャラ誕 / 声優誕
monthいいえ1〜12
dayいいえ1〜31

月日の昇順。divi が不正なら 400(BirthdayRestCopy::DIVI_FILTER_INVALID)、month / day が範囲外・存在しない月日なら 400(MONTH_DAY_INVALID)。

param必須説明
titleいいえ作品名の部分一致
kishuいいえ機種名の部分一致

作品名の昇順。

API-008-5 GET /unlinked-titles ​

誕生日データがあり、機種が 1 件も紐付いていない有効な作品名(空の作品名は除く)。items[] は id・name・birthday_count(BirthdayLinkStatusServiceInterface::list_unlinked_titles)。

API-008-6 GET /upcoming ​

param必須説明
daysいいえ1〜60(既定 14、今日を含む)

今日(サイトのタイムゾーン)から days 日間の誕生日。success・days・count・items を返し、items[] は date(Y-m-d)・linked(その作品名に機種が紐付いているか)と誕生日データの項目。2/29 は平年には出さない。days が範囲外なら 400(DAYS_INVALID)。

紐付け ​

API-008-7 POST /links ​

param必須説明
title_idはい作品名 ID
kishu_idはい機種 ID

成功時 201 で link(紐付けの項目)を返す。

param必須説明
old_title_idはい今の作品名 ID
old_kishu_idはい今の機種 ID
title_idはい変更後の作品名 ID
kishu_idはい変更後の機種 ID

成功時 200 で link を返す。

title_id・kishu_id(クエリまたはボディ)。成功時 200 で deleted: {title_id, kishu_id}。

紐付けの失敗・エラー条件 ​

条件HTTPcodemessage
ID が正の整数でない400invalid_parameterBirthdayRestCopy::TITLE_ID_INVALID / KISHU_ID_INVALID
作品名・機種がマスタに無い(追加・変更先)400invalid_parameterMessages::BIRTHDAY_KISHU_ADMIN_*
作品名が無効(追加・変更先)400title_inactiveMessages::BIRTHDAY_KISHU_ADMIN_TITLE_NOT_ACTIVE
機種が無効(追加・変更先)400kishu_inactiveMessages::BIRTHDAY_KISHU_ADMIN_KISHU_NOT_ACTIVE
変更前と同じ(PATCH)400no_changeLINK_NO_CHANGE
対象の紐付けが無い(PATCH・DELETE)404not_foundLINK_NOT_FOUND
既にある(POST・PATCH の変更先)409duplicateLINK_DUPLICATE
DB 書き込みの失敗503write_failedWRITE_FAILED

誕生日データ ​

API-008-10 POST /birthdays ​

param必須説明
monthはい1〜12
dayはい存在する日
diviはいキャラ誕 / 声優誕
charaはいキャラ名
actor声優誕声優名
title_idいいえ作品名 ID(有効なもの)。省略・0 は作品名なし

検証は管理画面の手入力(BirthDayAdminServiceInterface::validate_birthday_form)と同じで、作品名は有効必須(無効なら 400、code: title_inactive、BirthdaySeedMessages::TITLE_NOT_ACTIVE。その他の検証エラーは invalid_parameter)。成功時 201 で birthday(誕生日データの項目)。同じ月日・区分・キャラ・作品名があれば 409(BIRTHDAY_DUPLICATE)。

API-008-11 PATCH /birthdays/{id} ​

API-008-10 と同じ項目のうち、指定したものだけを変える(指定しない項目は今の値)。title_id は API-008-10 と同じく 0 を作品名なしとして受け付ける(name が空の作品名の ID を直接渡してもよい)。変えるときは変更先が有効であること(変えないときは今の作品名が無効でもよい)。成功時 200 で birthday。何も変わらなければ 400(BIRTHDAY_NO_CHANGE)、変更後が他の行と同じなら 409(BIRTHDAY_DUPLICATE。事前の判定で見つからなくても、DB の一意キー(大文字小文字を区別しない照合順序)で重複になった場合も 409)、無ければ 404(BIRTHDAY_NOT_FOUND)。

API-008-12 DELETE /birthdays/{id} ​

成功時 200 で deleted: {id}。無ければ 404。

{id} は URL パスの値だけを使う(include_url_params)。クエリ・ボディの id は見ない。

作品名マスタ ​

API-008-13 POST /titles ​

name(必須)。有効な状態で作る。成功時 201 で title: {id, name, is_active}。同じ作品名(無効なものを含む)があれば 409(TITLE_DUPLICATE)、空なら 400(TITLE_NAME_REQUIRED)、長すぎれば 400(TitleMasterAdminMessages::NAME_MAX)。

API-008-14 PATCH /titles/{id} ​

name(必須)で改名する。成功時 200 で title: {id, name}。無ければ 404(TITLE_NOT_FOUND)、同じ名前なら 400(TITLE_NAME_NO_CHANGE)、他の作品名と重なれば 409(TITLE_DUPLICATE)。

API-008-15 POST /titles/{id}/active ​

is_active(true / false、1 / 0 も可)で有効/無効を切り替える。成功時 200 で title: {id, is_active}。既にその状態なら 400(TITLE_ACTIVE_NO_CHANGE)、不正値は 400(TITLE_ACTIVE_INVALID)。無効にしても既存の誕生日データ・紐付けは残る。

作品名なし用の行(name が空)は改名・無効化できない(400、TITLE_EMPTY_PROTECTED。TitleMasterAdminService で止めるため、管理画面の改名・無効化でも同じ)。作品名の統合はできない(ADM の作品名マスタ画面で行う)。

スロカレ取込 ​

API-008-16 POST /sulocale/preview ​

url(必須、https://sulocale.sulopachinews.com/archives/<数字>。末尾の / は取り除いて取得する)のページを取得・解析し、取り込んだ場合の結果を返す。DB・作品名マスタには書き込まない。

field型説明
processedint抽出した件数(重複を除く)
new_countint新規に登録される件数
existing_countint登録済みでスキップされる件数
new_titlesstring[]作品名マスタに無く、取込で新規作成される作品名
entries[]arraymonth・day・divi・actor_display・chara・title・status(new / existing)
preview_hashstringAPI-008-17 の expected_preview_hash に渡す照合用の値(SHA-256、16 進 64 文字)

preview_hash は URL のアーカイブ番号・new_titles・entries[](status を含む全行の並び)から作る。別の URL のプレビューの値は一致しない(末尾の / の有無は同じ扱い)。ページの内容が同じでも、その間に誕生日データや作品名マスタへ登録されて status や new_titles が変われば値が変わる。

API-008-17 POST /sulocale/import ​

同じ url を取り込む。作品名マスタに無い作品名は新規作成する。bot は直前に API-008-16 の結果(特に new_titles)を人間に見せ、了承を得てから呼ぶ。Issue #3901。

param必須説明
url○API-008-16 と同じ URL
expected_preview_hash任意人間に見せた API-008-16 の preview_hash(16 進 64 文字)

expected_preview_hash を指定したときは、ページを取得し直して API-008-16 と同じ方法で preview_hash を作り、一致しなければ 誕生日データ・作品名マスタのどちらにも書き込まずに 409(SULOCALE_PREVIEW_MISMATCH)を返す。プレビューから取込までの間にページが更新された・他の経路で登録されたなど、人間が確認していない内容を登録しないためのもの。409 のときは API-008-16 からやり直し、結果を人間に見せ直す。

照合は取込のロック(BirthdaySeedRunLock)の中で書き込み直前に行うが、このロックは誕生日取込どうし(本 API・管理画面の URL 取込/全件シード)しか排他しない。照合から書き込みまでのごく短い間に、ロックを取らない経路(API-008-10・13、管理画面の作品名マスタ・誕生日の編集)で登録されることまでは防がない。その場合も作品名の新規作成が減る・行が重複スキップになる方向にしかずれず、人間が確認していない作品名や誕生日は登録されない。

未指定(または空文字)のときは照合せずに取り込む(Issue #3901 以前の呼び出しとの互換のため)。MCP の birthday_sulocale_import は expected_preview_hash を必須にしているため、bot からの取込は常に照合される。

field型説明
processedint抽出した件数
insertedint登録した件数
skippedint登録済みでスキップした件数
created_titlesstring[]新規作成した作品名
inserted_entriesarray登録した行(entries[] と同じ形、status なし)
skipped_entriesarrayスキップした行

管理画面のシード・URL 取込と同じロック(BirthdaySeedRunLock)を取る。他の取込が実行中なら 409(SULOCALE_RUN_IN_PROGRESS)。成功したら最終取得日時(API-006-6 の last_sulocale_import_at)を更新する。全件シードの 1 時間クールダウンは URL 取込にはかからない(外部取得の回数はレート制限で抑える)。

スロカレの失敗・エラー条件 ​

条件HTTPcodemessage
URL がスロカレのアーカイブ URL でない400invalid_parameterBirthdayRestCopy::URL_INVALID(取得はしない)
作品ページとして読み取れない422sulocale_page_invalidSULOCALE_PAGE_INVALID
抽出件数が上限(500 件)を超えた422sulocale_too_many_rowsSULOCALE_TOO_MANY_ROWS
取得に失敗した(200 以外・HTML 以外・2MB 超等)502sulocale_fetch_failedSULOCALE_FETCH_FAILED
他の取込が実行中(import のみ)409sulocale_run_in_progressSULOCALE_RUN_IN_PROGRESS
expected_preview_hash が 16 進 64 文字でない(import のみ。取得はしない)400invalid_parameterSULOCALE_PREVIEW_HASH_INVALID
取得し直した結果が expected_preview_hash と一致しない(import のみ。何も書き込まない)409sulocale_preview_mismatchSULOCALE_PREVIEW_MISMATCH
想定外の例外500unexpected_errorUNEXPECTED_ERROR

レート制限は権限チェックで先に数えるため、URL の形式が不正で 400 になったリクエストも 1 時間 10 回の枠を 1 回使う。例外のメッセージはレスポンスに載せない。取得は wp_safe_remote_get(リダイレクトを追わない・2MB・15 秒)で、取得先は URL の検証を通ったスロカレのアーカイブだけ。

権限・nonce・レート制限 ​

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

actionルートCapabilityレート制限(ユーザー単位)カウンタ不可時
readAPI-008-1〜6read_birthday_data1 分 120 回通す
linkAPI-008-7〜9link_birthday_kishu1 分 30 回拒否(429)
addAPI-008-10・13add_birthday_data1 分 30 回拒否(429)
sulocaleAPI-008-16・17add_birthday_data1 時間 10 回(合算)拒否(429)
updateAPI-008-11・12・14・15update_birthday_data1 分 30 回拒否(429)
項目内容
匿名アクセス403
nonce検証しない(アプリケーションパスワード前提)
レート制限BotRestRateLimiter(transient、固定ウィンドウ)。バケットは birthday:read / birthday:link / birthday:add / birthday:sulocale / birthday:update
ログログイン済みユーザーの 403 と、429 のときだけ error_log に 1 行(BirthdayRestCopy::LOG_DENIED_FORMAT)
Local バイパスAPI-001 の WordPressPermissionChecker と同じ条件(レート制限もかけない)

誕生日紐付け担当ロールは wp-admin を開けない(MemberAdminAccessGuardHooks)。誕生日の管理画面(ADM-013 等、manage_options)は開放しない。

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

birthday_linker を持ち、他には REST 用 bot ロール(ops_status_reader / x_announcer)しか持たないユーザーには BotWriteGuardHooks(MemberServiceProvider で登録)で、X告知担当(API-007)と同じ制限をかける。違いは通す書き込みが BotWriteGuardHooks::BOT_ROLES の birthday_linker の allowed_writes(上の API-008-7〜17 のメソッドとパス)であること。他の bot ロールを併せ持つ場合は、そのロールの許可ルートも合わせて通す。

経路フック内容
RESTrest_pre_dispatchGET / HEAD / OPTIONS と allowed_writes 以外は 403(birthday_write_forbidden、BirthdayRestCopy::WRITE_FORBIDDEN)
権限map_meta_cap自分自身に対する edit_user とアプリケーションパスワードの発行・編集・削除を do_not_allow
XML-RPCauthenticate(優先度 100)XML-RPC リクエスト中は bot のログインを拒否する(birthday_xmlrpc_forbidden)

監査ログ ​

書き込み(API-008-7〜17)は BotRestAuditLogger で error_log に 1 行 JSON(接頭辞 [bot-rest-audit])を書く。記録するのはルート(birthday/v1/<パス>)・ユーザー ID・操作(action)・対象の ID・結果(outcome: created / updated / deleted / duplicate / not_found / no_change / failed 等)・件数だけ。スロカレはアーカイブ番号と件数だけを残し、URL 全体・キャラ名・作品名は残さない(取込はプレビューと照合したかを preview_checked に、不一致で中止したときは outcome: preview_mismatch を残す。ハッシュの値は残さない)。ID・必須項目の形式や作品名・機種の有効性の検証で 400 になったリクエストは記録しない(作品名の作成・改名・有効/無効の切り替えはサービス側で検証するため、失敗も invalid / no_change 等の outcome で記録する)。