2018年以降の記事はGitHub Pagesに移行しました

RubyでTwitterにツイートをキメてみる

前回までのあらすじ

  1. RubyでTwitterのOAuth認証をしてみる - kk_Atakaの日記
  2. RubyでTwitterのOAuth認証をしてみる その2 - kk_Atakaの日記
  3. RubyでTwitterのタイムラインを取得してみる - kk_Atakaの日記

ターミナルからOAuth認証をキメて、タイムラインを見られるようになりました。最後にターミナルからツイートが出来れば一応Twitterの見る・書くがライブラリ無しでできたことになります。

ツイートする方法

  1. signatureを作成する。必要な値は以下のとおり。
    1. oauth_consumer_key
    2. oauth_nonce
    3. oauth_signature
    4. oauth_signature_method
    5. oauth_timestamp
    6. oauth_token
    7. oauth_version
    8. status New!

statusっていうのがツイートの本文ですね。signature作成するのにエスケープします。作成後のheaderはこんな感じ。*1statusには「確認確認」って入ってます。

{
	"oauth_nonce"=>"3c8293f9c5a0295148d428a26edb9f46", 
	"oauth_timestamp"=>"1295784908", 
	"oauth_signature_method"=>"HMAC-SHA1", 
	"oauth_consumer_key"=>"XXXXXXXXXXXXXXXXXXXXXX", 
	"oauth_token"=>"99999999-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", 
	"oauth_signature"=>"UKt1BmJ+UWNWrSmBuAyJ40iiCNY=", 
	"status"=>"%E7%A2%BA%E8%AA%8D%E7%A2%BA%E8%AA%8D", 
	"oauth_version"=>"1.0"
}

signature作成後

  • signatureを作成したら、headerからstatusを消します
  • 残ったheaderをアルファベット順に並べ、xxx=yyy,vvv=zzz……の形で連結します。連結後はこんな感じ
oauth_consumer_key=XXXXXXXXXXXXXXXXXXXXXX,oauth_nonce=341d852d61cdc3c22e3e847b0ab69d20,oauth_signature=MR5WEJ1qHuE49ozgRYduNJ%2BBiCs%3D,oauth_signature_method=HMAC-SHA1,oauth_timestamp=1295785315,oauth_token=99999999-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY,oauth_version=1.0
  • 連結した上記の値をAuthorizationヘッダに入れる。入れた後はこんな感じ。
"Authorization"=>"OAuth oauth_consumer_key=XXXXXXXXXXXXXXXXXXXXXX,oauth_nonce=341d852d61cdc3c22e3e847b0ab69d20,oauth_signature=MR5WEJ1qHuE49ozgRYduNJ%2BBiCs%3D,oauth_signature_method=HMAC-SHA1,oauth_timestamp=1295785315,oauth_token=99999999-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY,oauth_version=1.0"

結果

以上で、Twitterにツイートが完了しました! おっぱいきたーー

ソース

ソースはこんな感じです。

# -*- encoding: UTF-8 -*-

require 'openssl'
require 'uri'
require 'net/http'
require 'nkf'
require 'time'

# 署名
def sigunature(method, 
		consumer_secret, 
		oauth_token_secret, 
		url, 
		oauth_header=nil)
	# sigunature_keyの作成
	# リクエストトークン時は"CONSUMER_SECRET&"(アンドが入っている)
	# アクセストークン時は"CONSUMER_SECRET&OAUTH_TOKEN_SECRET"として使用
	sigunature_key = consumer_secret + "&"
	if !oauth_token_secret.nil? then
		sigunature_key += oauth_token_secret
	end

	param = sort_and_concat(oauth_header)

	# メソッド + URL + パラメータ
	value = method + "&" + escape(url) + "&" + escape(param)
	# hmac_sha1
	sha1 = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, sigunature_key, value)
	# base64
	base64 = [sha1].pack('m').gsub(/\n/, '')
	return base64
end

# 文字列のエスケープ(: / = %をエスケープする。. _ -はそのまま)
def escape(value)
	URI.escape(value, Regexp.new("[^a-zA-Z0-9._-]"))
end

# oauth_headerの情報をアルファベット順に並べ替え & で結合
def sort_and_concat(oauth_header)
	oauth_header_array = oauth_header.sort
	param = ""
	oauth_header_array.each do |params|
		for i in 1..params.length
			param += params[i-1]
			if i % params.length == 0
				param += "&"
			else
				param += "="
			end
		end
	end
	param = param.slice(0, param.length-1)
end

# -------------------------------------------------------------

update_url = "http://twitter.com/statuses/update.json"

url = update_url
# コマンドプロンプトはutf8変換
text = escape(ARGV[0])
# text = escape(NKF.nkf('-w', ARGV[0]))
proxy = ARGV[1]

# Twitterで登録したらもらえる
consumer_key = "XXXXXXXXXXXXXXXXXXX"
consumer_secret = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
# Twitterからもらえるリクエストトークン(最初は使わない)
oauth_token = "11111111-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
oauth_token_secret = "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"

# oauthパラメータたち(決め打ちのもの)
oauth_header = {
	# Consumer Key
	"oauth_consumer_key" => consumer_key,
	# 一意な値
	"oauth_nonce" => OpenSSL::Digest::Digest.hexdigest('MD5', "#{Time.now.to_f}#{rand}"), 
	# 署名方式(HMAC-SHA1)
	"oauth_signature_method" => "HMAC-SHA1",
	# リクエスト生成時のタイムスタンプのミリ秒
	"oauth_timestamp" => Time.now.to_i.to_s, 
	# バージョン(1.0)
	"oauth_version" => "1.0",

	# アクセストークン
	"oauth_token" => oauth_token
}

# headerにツイート追加
oauth_header["status"] = text

# 署名生成
oauth_header["oauth_signature"] = sigunature("POST",
					consumer_secret,
					oauth_token_secret,
					url,
					oauth_header)

# headerからツイート削除
oauth_header.delete("status")
# 署名エンコード
oauth_header["oauth_signature"] = escape(oauth_header["oauth_signature"])
param = sort_and_concat(oauth_header)

param = param.gsub('&', ',')

header = {
	"Authorization" => "OAuth " + param
}

# POST
uri = URI.parse(update_url)
proxy_class = Net::HTTP::Proxy(proxy, 8080)
http = proxy_class.new(uri.host)
http.start do |http|
	res = http.post(uri.path, "status=#{text}", header)

	if res.code == "200" then
		print "#{res.code} tweet: #{ARGV[0]}\n"
	else
		print "ERROR: #{res.code}\n #{res.body}\n"
	end
end

*1:実際はインデントとかなし。