--- 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 "テストです。")