--- title: Gauche で Twitter に投稿するためのモジュール author: kazu634 date: 2010-06-05 url: /2010/06/05/_1532/ wordtwit_post_info: - 'O:8:"stdClass":13:{s:6:"manual";b:0;s:11:"tweet_times";i:1;s:5:"delay";i:0;s:7:"enabled";i:1;s:10:"separation";s:2:"60";s:7:"version";s:3:"3.7";s:14:"tweet_template";b:0;s:6:"status";i:2;s:6:"result";a:0:{}s:13:"tweet_counter";i:2;s:13:"tweet_log_ids";a:1:{i:0;i:5271;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}' categories: - gauche - Lisp ---

Gauche で OAuth – 主題のない日記」と「GaucheでOAuthを使ってTwitterに投稿する – 自称スーパーハッカーの適当なプログラミング雑記等」を丸ぱくり参考にして、モジュール化してみました:

ソース

(define-module net-twitter
(export twitter-init
twitter-post))
(select-module net-twitter)
;; =================
;; === Libraries ===
;; =================
(use rfc.http)
(use rfc.sha)
(use rfc.hmac)
(use rfc.base64)
(use www.cgi)
(use math.mt-random)
(use gauche.uvector)
;; =================
;; === Functions ===
;; =================
(define (uri-encode-string str)
(call-with-string-io str
(lambda(in out)
(while (read-byte in) (compose not eof-object?) => ch
(if (char-set-contains? #[a-zA-Z0-9.~_-] (integer->char ch))
(write-char (integer->char ch) out)
(format out "%~2,'0X" (char->integer (integer->char ch))))))))
(define (time-stamp)
(number->string (sys-time)))
(define (random-string)
(let ((random-source
(make <mersenne-twister> :seed (sys-time)))
(v (make-u32vector 10)))
(mt-random-fill-u32vector! random-source v)
(digest-hexify (sha1-digest-string (x->string v)))))
(define (query-compose query)
(string-join (map (cut string-join <> "=") query) "&"))
(define (signature method uri info consumer-secret :optional (token-secret ""))
(let* ((query-string (query-compose info))
(signature-basic-string
(string-append method "&"
(uri-encode-string uri) "&"
(uri-encode-string query-string))))
(uri-encode-string
(base64-encode-string
(hmac-digest-string signature-basic-string
:key #`",|consumer-secret|&,|token-secret|"
:hasher <sha1>)))))
;; ==================
;; === Interfaces ===
;; ==================
(define (twitter-init consumer-key consumer-secret)
(let* ((r-query `(("oauth_consumer_key" ,consumer-key)
("oauth_nonce" ,(random-string))
("oauth_signature_method" "HMAC-SHA1")
("oauth_timestamp" ,(time-stamp))
("oauth_version" "1.0")))
(r-s (signature "POST"
"http://api.twitter.com/oauth/request_token"
r-query
consumer-secret))
(r-token (receive (status header body)
(http-post "api.twitter.com"
"/oauth/request_token"
(query-compose
`(,@r-query ("oauth_signature" ,r-s))))
(cgi-parse-parameters :query-string body)))
(request-token (cadr
(assoc "oauth_token" r-token)))
(request-token-secret (cadr
(assoc "oauth_token_secret" r-token))))
(display "open this url.")
(newline)
(format #t
"https://api.twitter.com/oauth/authorize?oauth_token=~A"
request-token)
(newline)
(newline)
(display "input pin: ")
(flush)
(let* ((oauth-verifier (read-line))
(a-query `(("oauth_consumer_key" ,consumer-key)
("oauth_nonce" ,(random-string))
("oauth_signature_method" "HMAC-SHA1")
("oauth_timestamp" ,(time-stamp))
("oauth_token" ,request-token)
("oauth_verifier" ,oauth-verifier)))
(a-s (signature "GET"
"http://api.twitter.com/oauth/access_token"
a-query
request-token-secret))
(token (receive (status header body)
(http-post "api.twitter.com"
"/oauth/access_token"
(query-compose
`(,@a-query ("oauth_signature" ,a-s))))
(cgi-parse-parameters :query-string body))))
(format #t
"Consumer Key: ~A\n"
consumer-key)
(format #t
"Consumer Secret Key: ~A\n"
consumer-secret)
(format #t
"Access Token: ~A\n"
(cadr (assoc "oauth_token" token)))
(format #t
"Access Token Secret: ~A\n"
(cadr (assoc "oauth_token_secret" token))))))
(define (twitter-post consumer-key consumer-secret
access-token access-token-secret
message)
(let* ((query `(("oauth_consumer_key" ,consumer-key)
("oauth_nonce" ,(random-string))
("oauth_signature_method" "HMAC-SHA1")
("oauth_timestamp" ,(time-stamp))
("oauth_token" ,access-token)))
(s (signature "POST"
"http://api.twitter.com/statuses/update.json"
`(,@query ,`("status"
,(uri-encode-string message)))
consumer-secret
access-token-secret)))
(http-post "api.twitter.com"
"/statuses/update.json"
(format "status=~A" (uri-encode-string message))
:Authorization (format "OAuth ~A"
(string-join
(map (cut string-join <> "=")
`(,@query ("oauth_signature" ,s))) ", ")))))
(provide "net-twitter")

解説

Consumer Key と Consumer Secret Key を引数にして twitter-init を呼び出すと、認証用 URL にアクセスし、認証を求められます。認証後に表示された PIN を入力してください。すると、

が表示されます。

kazu634@kazu634% gosh temp.scm                             ~/Documents/working/tmp_lisp/test [2626]
open this url.
https://api.twitter.com/oauth/authorize?oauth_token=EP8MaCiRCZlh2XRmJrpcgoV7UxFo5lNg4Dm1dtbExV0
input pin: nnnnnnnn
Consumer Key:
Consumer Secret Key:
Access Token:
Access Token Secret:

認証完了後に表示されるこれらの情報とポスト用の文字列を引数にして、 twitter-post を呼び出します。

(twitter-post consumer key
consumer secret key
access token
access token secret
"テストです。")

使用例

(require "./net-twitter.scm")
(import net-twitter)
;; (twitter-init consumer key
;;     	         consumer secret key
(twitter-post consumer key
consumer secret key
access token
access token secret
"テストです。")
プログラミングGauche

プログラミングGauche