docomoの対話APIについてです.
私はこの素晴らしいAPIのおかげで, 現在製作中のちょまどbot(@chomado_bot)に対話機能を付けることが出来, 毎日楽しんでいます!
でも, 文字コードを(仕様に書いてある) UTF-8 にしても, Shift-JIS で収まらないものを与えると 400 Bad Request でしぬようなので, ここに書いておきます.(2015/1/28現在)
問題が起こった時, 私は自力で考えても全然原因が分からなくて, 詳しい人に教えていただくまでずっと詰んでいたので,
他の人の役に立てばいいなという思いで, ここに, 記しておきます.
【重要 後記2015/3/1】(注) このバグは2015/2/24のdocomo対話APIのアップデートで修正されたっぽいです. でも記念にこの記事は残しておきます.
目次
- まとめ/結論
- 具体的な修正例
- 何があったか時系列順にまとめ
- ちょまどbotに対話機能付けたい!(((o(*゚▽゚*)o)))
- docomo雑談対話APIを利用したら, botに対話機能実装できた!楽しい!
- ところが, ちょまどbotのツイート見てると, たまに, 対話部が空白になっているリプライを返していることがある.
- 調べてみたら, どうやら, APIに渡している文字列(ユーザ名とリプライ本文)のどこかに € とか ✗ とか特殊文字が含まれていると, HTTP/1.1 400 Bad Request が返ってくる模様.
- API仕様通りにUTF-8で渡していてもダメ. なんで?
- 詳しい人が検証してくださった. どうやら Shift-JIS で収まらないものを与えると 400 Bad Request でしぬよう
- SJIS で収まらないものは下駄記号『〓』で置換してAPIへ送る修正.
- その後の話
まとめ/結論
仕様書.pdf には 『共通仕様』の文字コードとして UTF-8 が指定してあるのですが, 実際は, 内部のどこかが CP932(Windows の Shift-JIS) で動いているようで, € とか ✗ とか特殊な文字を渡すとしにます. (なのでもちろん明示的に UTF-8 変換してもしにます)
APIへ送る文字列(JSON)は, UTF-8にした上で, すべて, SJIS で収まらないものは何か安全な文字(SJISに収まるもの)で置換しましょう.
「とても嫌な予感がする」というのは「今日日まさかSJISで表せるような範囲しか通らないようなシステムなんじゃ」という
— 連合艦隊旗艦 軽巡ひな改二 (@fetus_hina) January 27, 2015
いや10000歩譲ってSJISのみ対応でもいい.だったらなぜインターフェースをUTF8にしてる(´・_・`)
— くぃ@世界でいちばんお姫さま(ぺちぱー) (@qittu) January 27, 2015
@chomado 元のシステムはSJISで実装されていて、インタフェース公開するときに偉い人の一言でUTF-8にしちゃったんだろうね。元システムが変換エラーを起こすと苦肉の策で400返すようにしたんだろうな~
— ムチャ@うつ病だけど求職中 (@mutoj_rdm821) 2015, 2月 1
具体的な修正例
GitHub: class/chat.php
@fetus_hina さんからプルリクくださいました: https://github.com/chomado/chomado_bot/commit/1d2b3494da27b15d1ff2936c7d45e8f5a94ce0d4
この例に使っている,
$user_data は API に渡すデータです
変更前 (= 私が書いていたコード):
private function GetData($context, $nickname, $text) { $user_data = array( 'utt' => (string)$text, 'context' => (string)$context, 'nickname' => (string)$nickname, );
変更後:
private function GetData($context, $nickname, $text) { $user_data = array( 'utt' => self::sjisSafe((string)$text), 'context' => (string)$context, 'nickname' => self::sjisSafe((string)$nickname), );
/** * 与えられた文字列を Shift-JIS で収まる範囲に変換する * * docomo APIがどうも Shift-JIS で収まらないものを与えると Bad Request で死ぬようなので * とりあえず SJIS で収まるもののみを送るようにしてみる * * @param string $text 変換対象文字列 * @return string */ private static function sjisSafe($text) { mb_substitute_character(0x3013); // 変換できない文字をゲタにする return mb_convert_encoding( mb_convert_encoding($text, 'CP932', 'UTF-8'), 'UTF-8', 'CP932' ); }
何があったか時系列順にまとめ
1.ちょまどbotに対話機能付けたい!(((o(*゚▽゚*)o)))
↓ ask.fmで来た
音声でぼっちの話し相手になってくれるちょまどbotがほしいです。10年後くらいにはできますか? — 音声は無理ですが、分かりました、対話機能を付けます。docomoの対話APIを使います。明日には完成します http://t.co/l4BPkDDS9v
— ちょまど@bot開発楽しい.php (@chomado) January 23, 2015
2.docomo雑談対話APIを利用したら, botに対話機能実装できた!楽しい!
@tbpgr_bot tbpgr_botさん げんき!₍⁽⁽(ી(*゚▽゚*)ʃ)₎₎⁾⁾ ノリノリ! 楽しそうですね!
— ちょまど習作bot@対話機能実装 (@chomado_bot) January 24, 2015
↓
① 名前,
② 対話部(from docomo対話API),
③ 顔文字
という順序のフォーマットにした.
@WeatherSparrow 雀さん 最近何か読みましたか? ┌(┌ *゚▽゚*)┐
— ちょまど習作bot@対話機能実装 (@chomado_bot) January 27, 2015
@progrommer ろまーさん パソコンは便利ですね ₍₍⁽⁽(ી(*゚▽゚*)ʃ)₎₎⁾⁾ ノリノリ!
— ちょまど習作bot@対話機能実装 (@chomado_bot) January 27, 2015
↓ 煽り性能も高い
@WiMAX2plus 葛城 玲 @ ガジェ鉄垢さん
寝ろや
(((o(*゚▽゚*)o)))
— ちょまど習作bot@対話機能実装 (@chomado_bot) 2015, 1月 31
3.ところが, ちょまどbotのツイート見てると, たまに, 対話部が空白になっているリプライを返していることがある.
つまり, なんらかの条件で, docomo対話APIから取ってきた対話部がNULLになっているみたい. (対話APIから何も返ってきていないということ.)
こんな感じで, リプライは " @スクリーンネーム 名前さん 対話部(docomo対話API) 顔文字部( https://t.co/u5NuxMxCPl からランダムに1つ) " という構成なのですが, たまに対話部がNULLになっているのです
— ちょまど@bot開発楽しい.php (@chomado) January 27, 2015
↓ 対話部が何もない. (下の顔文字(((o(*゚▽゚*)o)))は埋め込みでAPIのじゃない)
@310reo ✗レオ @ チョコ待ってる✗さん (((o(*゚▽゚*)o)))
— ちょまど習作bot@対話機能実装 (@chomado_bot) January 27, 2015
@uenoakihiko oʞᴉɥᴉʞɐouǝnさん ┌(┌ *゚▽゚*)┐
— ちょまど習作bot@対話機能実装 (@chomado_bot) January 27, 2015
@chomado ちょまど @ bot開発楽しい . phpさん ₍₍⁽⁽(ી(*゚▽゚*)ʃ)₎₎⁾⁾ ノリノリ!
— ちょまど習作bot@対話機能実装 (@chomado_bot) January 27, 2015
4.調べてみたら, どうやら, APIに渡している文字列(ユーザ名とリプライ本文)のどこかに € とか ✗ とか特殊文字が含まれていると, HTTP/1.1 400 Bad Request が返ってくる模様.
@fetus_hina コンソール見ると Warning: file_get_contents(https:~?APIKEY= ~~): failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request
— ちょまど@bot開発楽しい.php (@chomado) January 27, 2015
やはり > []{}#%^*+=_\;|<>"'$€.,?!・:@()「」¥&(≧∇≦)(^ω^) を送ると HTTP request failed! HTTP/1.1 400 Bad Request が帰って来る
— ちょまど@bot開発楽しい.php (@chomado) January 27, 2015
あきらめたときの応答、情報量ねえなあ pic.twitter.com/e4krFahvGS
— 連合艦隊旗艦 軽巡ひな改二 (@fetus_hina) January 27, 2015
5.API仕様通りにUTF-8で渡していてもダメ. なんで?
わかったこと:特殊記号が含まれるとなんかばっどりくえすとって怒られる UTF-8に明示的に変換してもダメ urlencodeすればちゃんと返ってくるけど, ‘こんにちは’とurlencode(‘こんにちは’)で返ってくる結果が明らかに異なる
— じゅんじ (@junjiru) January 27, 2015
6.詳しい人(@fetus_hina)が検証してくださった. どうやら Shift-JIS で収まらないものを与えると 400 Bad Request でしぬよう
やっぱりCP932範囲外を除いてやればいけるんじゃね?(これで応答返ってきた) pic.twitter.com/xmnt0bHAoO
— 連合艦隊旗艦 軽巡ひな改二 (@fetus_hina) January 27, 2015
(後から注: @fetus_hina さん「※このコードにはバグがあり正しく対応できていません」)
仕様書にはUTF-8って書いてあるのに, どうして CP932 ( Windows の Shift-JIS )に収まるものじゃないとダメなんだろう? 文字コードよく分からない><
— ちょまど@bot開発楽しい.php (@chomado) January 27, 2015
@chomado たとえば、裏でなんかレガシーなシステムが動いててその辺の範囲外の文字が取り扱えない(裏で変換して取り扱ってるだけとか)とか
— 連合艦隊旗艦 軽巡ひな改二 (@fetus_hina) January 27, 2015
docomoの会話API,まさかの内部処理がSJISだった案件か..(´・_・`)
— くぃ@世界でいちばんお姫さま(ぺちぱー) (@qittu) January 27, 2015
7. SJIS で収まらないものは下駄記号『〓』で置換してAPIへ送る修正.
@fetus_hina さんからプルリクくださいました: https://github.com/chomado/chomado_bot/commit/1d2b3494da27b15d1ff2936c7d45e8f5a94ce0d4
その後の話
これら一連のことを, ベテランのエンジニアさんに話したら,
「ああ, 文字コードのゴタゴタね. よくある話だから, まあ, いい経験になったんじゃない?(^o^)」
よくある話らしい!!
[…] 【docomo雑談対話API】【UTF-8/SJIS】文字コードの扱いについて | ちょまど帳 docomoの対話APIについてです. 私はこの素晴らしいAPIのおかげで, 現在製作中のちょまどbot(@chomado_bot)に対話機能を […]
※ごちゅうい:
2015-02-24 以降、この問題に対する修正が行われており、現在はこの対応は必要ありません。
ただし、この対応は「CP932で表せない文字を消す」ことで行われているように推測できる挙動を行うため、「変な文字」を挟む位置によっては文意が変わる可能性がありますし、「変な文字」のみで構成されるテキストを送信すると空の応答が返ってきたりします。
ちょまどbotからもこの問題に対応するコードは削除されています。
https://github.com/chomado/chomado_bot/commit/dbc00b6c286b17e3194d9365ab3e0df7ef291807
わあ! ひなさんいつもありがとうございます!