くろごま語録

くろごまが何か綴るとこ

Twitterにたまに投稿できなくてハマったとこ

今でしょbot@itsu_ima)に30分おきにGoogle Apps Scriptから自動的につぶやかせようとしていたのですが、毎日のようにある時 突然認証エラーを起こしてbotがつぶやかなくなり*1Googleからこういう感じのメールが送られてきていたのですが、何が原因なのかサッパリ分からなくて困っていました。

概要:

エラー メッセージ カウント
その操作を実行するには承認が必要です。 46
サービスの認証に失敗しました: twitter(行 37、ファイル「twitter」) 1

で、その時の問題のJavaScriptコード(一部)はこれ。

var encodedContent = encodeURIComponent(content);
var options = {
  "oAuthServiceName" : "twitter",
  "oAuthUseToken" : "always",
  "method" : "post",
  "payload" : "status=" + encodedContent
};
var result = UrlFetchApp.fetch("https://api.twitter.com/1.1/statuses/update.json", options);

その時は手の施しようが無くて、とりあえず放置していろいろと機能を追加していました。140字を超えた時に「~(続く」「続き)~」と分割してつぶやく機能を実装した時に、それが必ず認証失敗することに気づき、いろいろ疑って調べてみたところこういうことが分かりました。

  • OAuthの仕様では、0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~ 以外の文字は %xx の形にエンコードしなくてはならない。(RFC3986)
  • JavaScript(ECMAScript)のencodeURIComponentは、0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!'()*-._~ 以外の文字しか %xx の形にエンコードしない。(RFC2396)

つまりは、!'()* の5文字は、OAuthの仕様ではエンコードしなくてはならないのに、encodeURIComponentがエンコードしてくれないということになります。

さっきの例で「~(続く」「続き)~」がつぶやけなかったのは、この半角丸括弧が原因だったことが分かりました。なるほど。

じゃあ、どうする?

答えは簡単です。!'()* の5文字を自前でエンコードしましょう。というわけで修正後のコードはこちら。

var encodedContent = encodeURIComponent(content).replace(/[!'()*]/g, function(c) {
  return "%" + c.charCodeAt(0).toString(16);
});

こうしたら、もう1週間以上認証に失敗しないようになりました。やったー。

*1:編集画面から実行して認証すればつぶやくようになるが、しばらく経つとまたエラーになる