247 lines
21 KiB
Markdown
247 lines
21 KiB
Markdown
---
|
|
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
|
|
|
|
---
|
|
<div class="section">
|
|
<p>
|
|
「<a href="http://d.hatena.ne.jp/SaitoAtsushi/20100429/1272545442" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/SaitoAtsushi/20100429/1272545442', 'Gauche で OAuth – 主題のない日記');" target="_blank">Gauche で OAuth – 主題のない日記</a>」と「<a href="http://d.hatena.ne.jp/tana-laevatein/20100505/1273025284" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/tana-laevatein/20100505/1273025284', 'GaucheでOAuthを使ってTwitterに投稿する – 自称スーパーハッカーの適当なプログラミング雑記等');" target="_blank">GaucheでOAuthを使ってTwitterに投稿する – 自称スーパーハッカーの適当なプログラミング雑記等</a>」を<strike>丸ぱくり</strike>参考にして、モジュール化してみました:
|
|
</p>
|
|
|
|
<h4>
|
|
ソース
|
|
</h4>
|
|
|
|
<pre class="syntax-highlight">
|
|
<span class="synSpecial">(</span>define-module net-twitter
|
|
<span class="synSpecial">(</span><span class="synStatement">export</span> twitter-init
|
|
twitter-post<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>select-module net-twitter<span class="synSpecial">)</span>
|
|
<span class="synComment">;; =================</span>
|
|
<span class="synComment">;; === Libraries ===</span>
|
|
<span class="synComment">;; =================</span>
|
|
<span class="synSpecial">(</span>use rfc.http<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>use rfc.sha<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>use rfc.hmac<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>use rfc.base64<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>use www.cgi<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>use math.mt-random<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>use gauche.uvector<span class="synSpecial">)</span>
|
|
<span class="synComment">;; =================</span>
|
|
<span class="synComment">;; === Functions ===</span>
|
|
<span class="synComment">;; =================</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>uri-encode-string str<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>call-with-string-io str
|
|
<span class="synSpecial">(</span><span class="synStatement">lambda</span><span class="synSpecial">(</span>in out<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>while <span class="synSpecial">(</span><span class="synStatement">read-byte</span> in<span class="synSpecial">)</span> <span class="synSpecial">(</span>compose <span class="synStatement">not</span> eof-object?<span class="synSpecial">)</span> => ch
|
|
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span>char-set-contains? #[a-zA-Z0-9.~_-] <span class="synSpecial">(</span>integer->char ch<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">write-char</span> <span class="synSpecial">(</span>integer->char ch<span class="synSpecial">)</span> out<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> out <span class="synConstant">"%~2,'0X"</span> <span class="synSpecial">(</span>char->integer <span class="synSpecial">(</span>integer->char ch<span class="synSpecial">))))))))</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>time-stamp<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>number->string <span class="synSpecial">(</span>sys-time<span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>random-string<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>random-source
|
|
<span class="synSpecial">(</span>make <mersenne-twister> :seed <span class="synSpecial">(</span>sys-time<span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span>v <span class="synSpecial">(</span>make-u32vector <span class="synConstant">10</span><span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span>mt-random-fill-u32vector! random-source v<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>digest-hexify <span class="synSpecial">(</span>sha1-digest-string <span class="synSpecial">(</span>x->string v<span class="synSpecial">)))))</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>query-compose query<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>string-join <span class="synSpecial">(</span><span class="synStatement">map</span> <span class="synSpecial">(</span>cut string-join <> <span class="synConstant">"="</span><span class="synSpecial">)</span> query<span class="synSpecial">)</span> <span class="synConstant">"&"</span><span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>signature <span class="synStatement">method</span> uri info consumer-secret :optional <span class="synSpecial">(</span>token-secret <span class="synConstant">""</span><span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">let*</span> <span class="synSpecial">((</span>query-string <span class="synSpecial">(</span>query-compose info<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>signature-basic-string
|
|
<span class="synSpecial">(</span>string-append <span class="synStatement">method</span> <span class="synConstant">"&"</span>
|
|
<span class="synSpecial">(</span>uri-encode-string uri<span class="synSpecial">)</span> <span class="synConstant">"&"</span>
|
|
<span class="synSpecial">(</span>uri-encode-string query-string<span class="synSpecial">))))</span>
|
|
<span class="synSpecial">(</span>uri-encode-string
|
|
<span class="synSpecial">(</span>base64-encode-string
|
|
<span class="synSpecial">(</span>hmac-digest-string signature-basic-string
|
|
<span class="synType">:key</span> #`<span class="synConstant">",|consumer-secret|&,|token-secret|"</span>
|
|
:hasher <sha1><span class="synSpecial">)))))</span>
|
|
<span class="synComment">;; ==================</span>
|
|
<span class="synComment">;; === Interfaces ===</span>
|
|
<span class="synComment">;; ==================</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>twitter-init consumer-key consumer-secret<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">let*</span> <span class="synSpecial">((</span>r-query <span class="synPreProc">`(</span><span class="synSpecial">(</span><span class="synConstant">"oauth_consumer_key"</span> ,consumer-key<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_nonce"</span> ,<span class="synSpecial">(</span>random-string<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_signature_method"</span> <span class="synConstant">"HMAC-SHA1"</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_timestamp"</span> ,<span class="synSpecial">(</span>time-stamp<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_version"</span> <span class="synConstant">"1.0"</span><span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>r-s <span class="synSpecial">(</span>signature <span class="synConstant">"POST"</span>
|
|
<span class="synConstant">"http://api.twitter.com/oauth/request_token"</span>
|
|
r-query
|
|
consumer-secret<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>r-token <span class="synSpecial">(</span>receive <span class="synSpecial">(</span>status header body<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>http-post <span class="synConstant">"api.twitter.com"</span>
|
|
<span class="synConstant">"/oauth/request_token"</span>
|
|
<span class="synSpecial">(</span>query-compose
|
|
<span class="synPreProc">`(</span>,@r-query <span class="synSpecial">(</span><span class="synConstant">"oauth_signature"</span> ,r-s<span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>cgi-parse-parameters :query-string body<span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span>request-token <span class="synSpecial">(</span><span class="synStatement">cadr</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">"oauth_token"</span> r-token<span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span>request-token-secret <span class="synSpecial">(</span><span class="synStatement">cadr</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">"oauth_token_secret"</span> r-token<span class="synSpecial">))))</span>
|
|
<span class="synSpecial">(</span>display <span class="synConstant">"open this url."</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>newline<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> #t
|
|
<span class="synConstant">"https://api.twitter.com/oauth/authorize?oauth_token=~A"</span>
|
|
request-token<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>newline<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>newline<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>display <span class="synConstant">"input pin: "</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>flush<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">let*</span> <span class="synSpecial">((</span>oauth-verifier <span class="synSpecial">(</span><span class="synStatement">read-line</span><span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>a-query <span class="synPreProc">`(</span><span class="synSpecial">(</span><span class="synConstant">"oauth_consumer_key"</span> ,consumer-key<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_nonce"</span> ,<span class="synSpecial">(</span>random-string<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_signature_method"</span> <span class="synConstant">"HMAC-SHA1"</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_timestamp"</span> ,<span class="synSpecial">(</span>time-stamp<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_token"</span> ,request-token<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_verifier"</span> ,oauth-verifier<span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>a-s <span class="synSpecial">(</span>signature <span class="synConstant">"GET"</span>
|
|
<span class="synConstant">"http://api.twitter.com/oauth/access_token"</span>
|
|
a-query
|
|
request-token-secret<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>token <span class="synSpecial">(</span>receive <span class="synSpecial">(</span>status header body<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>http-post <span class="synConstant">"api.twitter.com"</span>
|
|
<span class="synConstant">"/oauth/access_token"</span>
|
|
<span class="synSpecial">(</span>query-compose
|
|
<span class="synPreProc">`(</span>,@a-query <span class="synSpecial">(</span><span class="synConstant">"oauth_signature"</span> ,a-s<span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span>cgi-parse-parameters :query-string body<span class="synSpecial">))))</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> #t
|
|
<span class="synConstant">"Consumer Key: ~A\n"</span>
|
|
consumer-key<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> #t
|
|
<span class="synConstant">"Consumer Secret Key: ~A\n"</span>
|
|
consumer-secret<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> #t
|
|
<span class="synConstant">"Access Token: ~A\n"</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">cadr</span> <span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">"oauth_token"</span> token<span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> #t
|
|
<span class="synConstant">"Access Token Secret: ~A\n"</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">cadr</span> <span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">"oauth_token_secret"</span> token<span class="synSpecial">))))))</span>
|
|
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>twitter-post consumer-key consumer-secret
|
|
access-token access-token-secret
|
|
message<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">let*</span> <span class="synSpecial">((</span>query <span class="synPreProc">`(</span><span class="synSpecial">(</span><span class="synConstant">"oauth_consumer_key"</span> ,consumer-key<span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_nonce"</span> ,<span class="synSpecial">(</span>random-string<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_signature_method"</span> <span class="synConstant">"HMAC-SHA1"</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_timestamp"</span> ,<span class="synSpecial">(</span>time-stamp<span class="synSpecial">))</span>
|
|
<span class="synSpecial">(</span><span class="synConstant">"oauth_token"</span> ,access-token<span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span>s <span class="synSpecial">(</span>signature <span class="synConstant">"POST"</span>
|
|
<span class="synConstant">"http://api.twitter.com/statuses/update.json"</span>
|
|
<span class="synPreProc">`(</span>,@query ,<span class="synPreProc">`(</span><span class="synConstant">"status"</span>
|
|
,<span class="synSpecial">(</span>uri-encode-string message<span class="synSpecial">)</span><span class="synPreProc">))</span>
|
|
consumer-secret
|
|
access-token-secret<span class="synSpecial">)))</span>
|
|
<span class="synSpecial">(</span>http-post <span class="synConstant">"api.twitter.com"</span>
|
|
<span class="synConstant">"/statuses/update.json"</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">format</span> <span class="synConstant">"status=~A"</span> <span class="synSpecial">(</span>uri-encode-string message<span class="synSpecial">))</span>
|
|
:Authorization <span class="synSpecial">(</span><span class="synStatement">format</span> <span class="synConstant">"OAuth ~A"</span>
|
|
<span class="synSpecial">(</span>string-join
|
|
<span class="synSpecial">(</span><span class="synStatement">map</span> <span class="synSpecial">(</span>cut string-join <> <span class="synConstant">"="</span><span class="synSpecial">)</span>
|
|
<span class="synPreProc">`(</span>,@query <span class="synSpecial">(</span><span class="synConstant">"oauth_signature"</span> ,s<span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">)</span> <span class="synConstant">", "</span><span class="synSpecial">)))))</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">provide</span> <span class="synConstant">"net-twitter"</span><span class="synSpecial">)</span>
|
|
</pre>
|
|
|
|
<h4>
|
|
解説
|
|
</h4>
|
|
|
|
<p>
|
|
Consumer Key と Consumer Secret Key を引数にして twitter-init を呼び出すと、認証用 URL にアクセスし、認証を求められます。認証後に表示された PIN を入力してください。すると、
|
|
</p>
|
|
|
|
<ul>
|
|
<li>
|
|
Access Token
|
|
</li>
|
|
<li>
|
|
Access Token Secret
|
|
</li>
|
|
</ul>
|
|
|
|
<p>
|
|
が表示されます。
|
|
</p>
|
|
|
|
<pre class="syntax-highlight">
|
|
kazu634@kazu634% gosh temp.scm ~/Documents/working/tmp_lisp/<span class="synStatement">test</span> <span class="synStatement">[</span><span class="synConstant">2626</span><span class="synStatement">]</span>
|
|
open this url.
|
|
https://api.twitter.com/oauth/authorize?<span class="synIdentifier">oauth_token</span>=EP8MaCiRCZlh2XRmJrpcgoV7UxFo5lNg4Dm1dtbExV0
|
|
input pin: nnnnnnnn
|
|
Consumer Key:
|
|
Consumer Secret Key:
|
|
Access Token:
|
|
Access Token Secret:
|
|
</pre>
|
|
|
|
<p>
|
|
認証完了後に表示されるこれらの情報とポスト用の文字列を引数にして、 twitter-post を呼び出します。
|
|
</p>
|
|
|
|
<pre class="syntax-highlight">
|
|
<span class="synSpecial">(</span>twitter-post consumer key
|
|
consumer secret key
|
|
<span class="synStatement">access</span> token
|
|
<span class="synStatement">access</span> token secret
|
|
<span class="synConstant">"テストです。"</span><span class="synSpecial">)</span>
|
|
</pre>
|
|
|
|
<h4>
|
|
使用例
|
|
</h4>
|
|
|
|
<pre class="syntax-highlight">
|
|
<span class="synSpecial">(</span><span class="synStatement">require</span> <span class="synConstant">"./net-twitter.scm"</span><span class="synSpecial">)</span>
|
|
<span class="synSpecial">(</span><span class="synStatement">import</span> net-twitter<span class="synSpecial">)</span>
|
|
<span class="synComment">;; (twitter-init consumer key</span>
|
|
<span class="synComment">;; consumer secret key</span>
|
|
<span class="synSpecial">(</span>twitter-post consumer key
|
|
consumer secret key
|
|
<span class="synStatement">access</span> token
|
|
<span class="synStatement">access</span> token secret
|
|
<span class="synConstant">"テストです。"</span><span class="synSpecial">)</span>
|
|
</pre>
|
|
|
|
<div class="hatena-asin-detail">
|
|
<a href="http://www.amazon.co.jp/dp/4873113482/?tag=hatena_st1-22&ascsubtag=d-7ibv" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://www.amazon.co.jp/dp/4873113482/?tag=hatena_st1-22&ascsubtag=d-7ibv', '');"><img src="https://images-na.ssl-images-amazon.com/images/I/51Exg14b4uL._SL160_.jpg" class="hatena-asin-detail-image" alt="プログラミングGauche" title="プログラミングGauche" /></a></p>
|
|
|
|
<div class="hatena-asin-detail-info">
|
|
<p class="hatena-asin-detail-title">
|
|
<a href="http://www.amazon.co.jp/dp/4873113482/?tag=hatena_st1-22&ascsubtag=d-7ibv" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://www.amazon.co.jp/dp/4873113482/?tag=hatena_st1-22&ascsubtag=d-7ibv', 'プログラミングGauche');">プログラミングGauche</a>
|
|
</p>
|
|
|
|
<ul>
|
|
<li>
|
|
<span class="hatena-asin-detail-label">作者:</span> <a href="http://d.hatena.ne.jp/keyword/Kahua%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/keyword/Kahua%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8', 'Kahuaプロジェクト');" class="keyword">Kahuaプロジェクト</a>,<a href="http://d.hatena.ne.jp/keyword/%C0%EE%B9%E7%BB%CB%CF%AF" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/keyword/%C0%EE%B9%E7%BB%CB%CF%AF', '川合史朗');" class="keyword">川合史朗</a>
|
|
</li>
|
|
<li>
|
|
<span class="hatena-asin-detail-label">出版社/メーカー:</span> <a href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%E9%A5%A4%A5%EA%A1%BC%A5%B8%A5%E3%A5%D1%A5%F3" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/keyword/%A5%AA%A5%E9%A5%A4%A5%EA%A1%BC%A5%B8%A5%E3%A5%D1%A5%F3', 'オライリージャパン');" class="keyword">オライリージャパン</a>
|
|
</li>
|
|
<li>
|
|
<span class="hatena-asin-detail-label">発売日:</span> 2008/03/14
|
|
</li>
|
|
<li>
|
|
<span class="hatena-asin-detail-label">メディア:</span> 大型本
|
|
</li>
|
|
<li>
|
|
<span class="hatena-asin-detail-label">購入</span>: 22人 <span class="hatena-asin-detail-label">クリック</span>: 713回
|
|
</li>
|
|
<li>
|
|
<a href="http://d.hatena.ne.jp/asin/4873113482" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/asin/4873113482', 'この商品を含むブログ (272件) を見る');" target="_blank">この商品を含むブログ (272件) を見る</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="hatena-asin-detail-foot">
|
|
</div>
|
|
</div>
|
|
</div>
|