Help us understand the problem. What is going on with this article?

Rubyでgemを使わずに、twitterにツイート(ポスト)する

More than 5 years have passed since last update.

はじめに

ツイートに際してはユーザー別のアクセストークンとアクセストークンシークレットが必要となります。
取得方法については、以下のページのソースを参考にしてください。

Rubyでgemを使わずに、twitterのアクセストークンを取得

ソースコード

※あくまで動かすようのサンプルです。ステータスチェックはきちんとやってくださいね。
※今回は行ってませんが、投稿の際、文字長のチェックが本来必要です。
※twitterの仕様で連続して同じ内容の投稿はできません。(403が帰ってくるんじゃなかったかな?)

post_tweet.rb
  def post_tweet
    #ユーザー別のアクセストークン
    oauth_token = "194485223--oauth_token --ynck19"
    #ユーザー別のアクセストークンシークレット
    oauth_token_secret = "BopAEWj--oauth_token_secret--DKy2iei"
    res = post_time_line(oauth_token, oauth_token_secret)
  #画面に投稿文が表示される
    render text: res["text"]
  end

  def post_time_line(oauth_token, oauth_token_secret)
    api_key = 'hogehogehogehoge'
    api_secret = 'fugofugofugofugo'
    post_params = {
      ## チルダはエスケープしない方がよいらしい
      ##ERB::Utilは~をエスケープする→RFC3986だとしないのが正しい
      "status" => escape("API_TEST ~+ ちるだプラス入れたろ!").gsub("%7E", "~")
    }
    oauth_params = {
        "oauth_consumer_key" => URI.escape(api_key),
        "oauth_nonce" => URI.escape(SecureRandom.uuid),
        "oauth_signature_method" => URI.escape('HMAC-SHA1'), 
        "oauth_timestamp" => URI.escape(Time.now.to_i.to_s),
        "oauth_token" => oauth_token,
        "oauth_version" => URI.escape('1.0')
    }
    params = post_params.merge(oauth_params)
    signature_key = "#{URI.escape(api_secret)}&#{URI.escape(oauth_token_secret)}"
    method = 'POST'
    request_url = "https://api.twitter.com/1.1/statuses/update.json"
    request_params  = sort_and_concat(params, "&")
    ## チルダはエスケープしない方がよいらしい
    signature_data = "#{URI.escape(method)}&#{escape(request_url)}&#{escape(request_params).gsub("%7E", "~")}"
    hash = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, signature_key, signature_data)
    signature = escape(Base64.urlsafe_encode64(hash))
    params["oauth_signature"] = signature
    header_params = sort_and_concat(params, ',')
    twitter_uri = URI.parse(request_url)
    https = Net::HTTP.new(twitter_uri.host, twitter_uri.port)
    https.use_ssl = true
    https.verify_mode =  OpenSSL::SSL::VERIFY_NONE
    req = Net::HTTP::Post.new(twitter_uri)
    req["Authorization"] = "OAuth #{header_params}"
    https.set_debug_output $stderr
    res = https.request(req, body = sort_and_concat(post_params, '&'))
    json = JSON.parse(res.body)
    return json
  end

  def sort_and_concat(params, delimiter)
    params = params.sort
    str = params.collect{|k, v| "#{k}=#{v}"}.join(delimiter)
    return str
  end

  def escape(str)
    return ERB::Util.url_encode(str)
  end

はまったこと

投稿する文章によって、エラーが発生する。

当初、投稿内容を

"status" => "API_TEST ~+ ちるだプラス入れたろ!"

こんな感じにしていたところ、

+&=~ を投稿文に入れる
→401エラー
{"errors":[{"code":32,"message":"Could not authenticate you."}]}

% を投稿文に入れる
→:400エラー

といった具合にうまく動かない。


記号周りだからエスケープの問題だろということで

"status" => escape("API_TEST ~+ ちるだプラス入れたろ!").gsub("%7E", "~")

こんな感じにしました。

チルダ(~)以外、問題なく動くが、チルダだけはエラー

401エラー
{"errors":[{"code":32,"message":"Could not authenticate you."}]}


調べた結果、チルダのエスケープの問題らしく、以下に落ち着く

"status" => escape("API_TEST ~+ ちるだプラス入れたろ!").gsub("%7E", "~")

今回、エスケープはERB::Util.url_encodeを利用したのですが、
この子「a~z、A~Z、0~9、_(アンダースコア) -(ハイフン).(ピリオド)」以外はエスケープするらしい。

RFC3986によると

URIs that differ in the replacement of an unreserved character with
its corresponding percent-encoded US-ASCII octet are equivalent: they
identify the same resource. However, URI comparison implementations
do not always perform normalization prior to comparison (see Section
6). For consistency, percent-encoded octets in the ranges of ALPHA
(%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E),
underscore (%5F), or tilde (%7E) should not be created by URI
producers and, when found in a URI, should be decoded to their
corresponding unreserved characters by URI normalizers.

どうも「a~z、A~Z、0~9、_(アンダースコア) -(ハイフン).(ピリオド) ~(チルダ)」はエスケープすべきじゃないよと言ってるのかな?

というわけで、上記記述で問題はなさそう。

ここに書いて思ったけど、escapeメソッドに切り出してるんだから、そっちにgsubつけた方がよかったのかな…

take2isk
2010年某大学商学部卒 就職後ECサイトの開発(4年) Androidアプリ、ポータルサイトの開発(1年) 個人でのWEBサイトの開発(1年~) 基本的にスキルは低めです
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away