blog/content/post/1970/01/01/1970-01-01-00000004.md

322 lines
22 KiB
Markdown

---
title: GaucheでMixi Voiceに投稿する
author: kazu634
date: 1969-12-31
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:5433;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}'
categories:
- gauche
---
<div class="section">
<p>
Gauche で Mixi Voice に投稿できるところまで書きました。まだモジュールにはしていないのですが、とりあえずここにひっそりと書いておきます。
</p>
<h4>
はじめに
</h4>
<p>
Twitter から任意のキーワードにマッチするものだけを Mixi Voice に投稿したい、という希望がありました。Mixi には大学の時のサークル関係の方が多くいて、その人達向けに Runkeeper のつぶやきを見せたかったのです。
</p>
<p>
Mixi Voice の OAuth の取得などに関しては、次のページが詳しかったので、まずはこのページをご覧ください:
</p>
<ul>
<li>
<a href="http://d.hatena.ne.jp/isseium/20110112/1294849747" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/isseium/20110112/1294849747', '先日公開された mixi の Voice API (Graph API ) をブラウザとターミナルで試してみた &#8211; 田舎を感じてみんないい仲');" target="_blank">先日公開された mixi の Voice API (Graph API ) をブラウザとターミナルで試してみた &#8211; 田舎を感じてみんないい仲</a>
</li>
</ul>
<h4>
ソース
</h4>
<p>
つぎのソースを適当な名前で保存してください:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>use rfc.http<span class="synSpecial">)</span>
<span class="synSpecial">(</span>use rfc.json<span class="synSpecial">)</span>
<span class="synComment">;;; class definition</span>
<span class="synSpecial">(</span>define-class &#60;mixi-token&#62; <span class="synSpecial">()</span>
<span class="synSpecial">((</span>consumer-key :init-keyword :consumer-key
:init-value #f
:accessor consumer-key-of<span class="synSpecial">)</span>
<span class="synSpecial">(</span>consumer-secret :init-keyword :consumer-secret
:init-value #f
:accessor consumer-secret-of<span class="synSpecial">)</span>
<span class="synSpecial">(</span>authorization-code :init-keyword :authorization-code
:init-value #f
:accessor authorization-code-of<span class="synSpecial">)</span>
<span class="synSpecial">(</span>refresh-token :init-keyword :refresh-token
:init-value #f
:accessor refresh-token-of<span class="synSpecial">)</span>
<span class="synSpecial">(</span>access-token :init-keyword :access-token
:init-value #f
:accessor access-token-of<span class="synSpecial">)))</span>
<span class="synComment">;; /* _\|/_</span>
<span class="synComment">;; (o o)</span>
<span class="synComment">;; +----oOO-{_}-OOo--------------+</span>
<span class="synComment">;; |Wrapper macro for HTTP access|</span>
<span class="synComment">;; +----------------------------*/</span>
<span class="synSpecial">(</span>define-macro <span class="synSpecial">(</span>wrap-http http-method proc<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>g!status <span class="synSpecial">(</span><span class="synStatement">gensym</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>g!header <span class="synSpecial">(</span><span class="synStatement">gensym</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>g!body <span class="synSpecial">(</span><span class="synStatement">gensym</span><span class="synSpecial">)))</span>
<span class="synPreProc">`(</span>receive <span class="synSpecial">(</span>,g!status ,g!header ,g!body<span class="synSpecial">)</span>
,http-method
<span class="synSpecial">(</span><span class="synStatement">cond</span>
<span class="synSpecial">((</span><span class="synStatement">equal</span>? <span class="synConstant">&#34;200&#34;</span> ,g!status<span class="synSpecial">)</span> <span class="synSpecial">(</span>,proc ,g!body<span class="synSpecial">))</span>
<span class="synSpecial">(</span>else <span class="synSpecial">(</span><span class="synStatement">print</span> ,g!status ,g!header ,g!body<span class="synSpecial">)))</span><span class="synPreProc">)</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>define-macro <span class="synSpecial">(</span>wrap-http/debug http-method proc debug-flag<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>g!status <span class="synSpecial">(</span><span class="synStatement">gensym</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>g!header <span class="synSpecial">(</span><span class="synStatement">gensym</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>g!body <span class="synSpecial">(</span><span class="synStatement">gensym</span><span class="synSpecial">)))</span>
<span class="synPreProc">`(</span>receive <span class="synSpecial">(</span>,g!status ,g!header ,g!body<span class="synSpecial">)</span>
,http-method
<span class="synSpecial">(</span><span class="synStatement">cond</span>
<span class="synSpecial">((</span><span class="synStatement">equal</span>? <span class="synConstant">&#34;200&#34;</span> ,g!status<span class="synSpecial">)</span> <span class="synSpecial">(</span>,proc ,g!body<span class="synSpecial">))</span>
<span class="synSpecial">(</span>else ,<span class="synSpecial">(</span><span class="synStatement">if</span> debug-flag
<span class="synPreProc">`(</span>else <span class="synSpecial">(</span><span class="synStatement">print</span> ,g!status ,g!header ,g!body<span class="synSpecial">)</span><span class="synPreProc">)</span>
#f<span class="synSpecial">)))</span><span class="synPreProc">)</span><span class="synSpecial">))</span>
<span class="synComment">;;; &#60;mixi-token&#62;インスタンスから、</span>
<span class="synComment">;;; アクセストークンを取得するためのクエリーを生成</span>
<span class="synSpecial">(</span>define-method compose-access-token-query <span class="synSpecial">((</span>token &#60;mixi-token&#62;<span class="synSpecial">))</span>
<span class="synPreProc">`(</span><span class="synSpecial">(</span><span class="synConstant">&#34;grant_type&#34;</span> <span class="synConstant">&#34;authorization_code&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;client_id&#34;</span> ,<span class="synSpecial">(</span>consumer-key-of token<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;client_secret&#34;</span> ,<span class="synSpecial">(</span>consumer-secret-of token<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;code&#34;</span> ,<span class="synSpecial">(</span>authorization-code-of token<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;redirect_uri&#34;</span> <span class="synConstant">&#34;http://mixi.jp/connect_authorize_success.html&#34;</span><span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">)</span>
<span class="synComment">;;; アクセストークン取得のための手続き</span>
<span class="synSpecial">(</span>define-method acquire-access-token! <span class="synSpecial">((</span>token &#60;mixi-token&#62;<span class="synSpecial">))</span>
<span class="synSpecial">(</span>wrap-http/debug <span class="synSpecial">(</span>http-post <span class="synConstant">&#34;secure.mixi-platform.com&#34;</span>
<span class="synConstant">&#34;/2/token&#34;</span>
<span class="synSpecial">(</span>compose-access-token-query token<span class="synSpecial">)</span>
:secure #t<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>response<span class="synSpecial">)</span>
<span class="synSpecial">(</span>update-token! response token<span class="synSpecial">))</span>
#f<span class="synSpecial">))</span>
<span class="synComment">;;; &#60;mixi-token&#62;を更新するための手続き</span>
<span class="synSpecial">(</span>define-method update-token! <span class="synSpecial">((</span>response &#60;string&#62;<span class="synSpecial">)</span>
<span class="synSpecial">(</span>mixi-instance &#60;mixi-token&#62;<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>refresh-token response<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">equal</span>? #f response<span class="synSpecial">)</span>
response
<span class="synSpecial">(</span><span class="synStatement">cdr</span> <span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">&#34;refresh_token&#34;</span>
<span class="synSpecial">(</span>parse-json-string response<span class="synSpecial">)))))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>access-token response<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">equal</span>? #f response<span class="synSpecial">)</span>
response
<span class="synSpecial">(</span><span class="synStatement">cdr</span> <span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">&#34;access_token&#34;</span>
<span class="synSpecial">(</span>parse-json-string response<span class="synSpecial">)))))</span>
<span class="synSpecial">(</span><span class="synStatement">set</span>! <span class="synSpecial">(</span>access-token-of mixi-instance<span class="synSpecial">)</span> <span class="synSpecial">(</span>access-token response<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">set</span>! <span class="synSpecial">(</span>refresh-token-of mixi-instance<span class="synSpecial">)</span> <span class="synSpecial">(</span>refresh-token response<span class="synSpecial">))</span>
mixi-instance<span class="synSpecial">)</span>
<span class="synComment">;;; mixi voiceに投稿するための手続き</span>
<span class="synSpecial">(</span>define-method post-voice <span class="synSpecial">((</span>mixi-instance &#60;mixi-token&#62;<span class="synSpecial">)</span>
<span class="synSpecial">(</span>voice &#60;string&#62;<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>post-id response<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cdr</span> <span class="synSpecial">(</span><span class="synStatement">assoc</span> <span class="synConstant">&#34;id&#34;</span> <span class="synSpecial">(</span>parse-json-string response<span class="synSpecial">))))</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>post-status <span class="synSpecial">(</span>http-compose-query #f <span class="synPreProc">`(</span><span class="synSpecial">(</span>status ,voice<span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>authorization-key <span class="synSpecial">(</span>string-append <span class="synConstant">&#34;OAuth &#34;</span>
<span class="synSpecial">(</span>access-token-of mixi-instance<span class="synSpecial">))))</span>
<span class="synSpecial">(</span>wrap-http/debug <span class="synSpecial">(</span>http-post <span class="synConstant">&#34;api.mixi-platform.com&#34;</span>
<span class="synConstant">&#34;/2/voice/statuses/update&#34;</span>
post-status
:content-type <span class="synConstant">&#34;application/x-www-form-urlencoded&#34;</span>
:Authorization authorization-key<span class="synSpecial">)</span>
post-id
#f<span class="synSpecial">)))</span>
<span class="synComment">;;; OAuth tokenの更新</span>
<span class="synSpecial">(</span>define-method update-access-token! <span class="synSpecial">((</span>mixi-instance &#60;mixi-token&#62;<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>token-update! response<span class="synSpecial">)</span>
<span class="synSpecial">(</span>update-token! response mixi-instance<span class="synSpecial">))</span>
<span class="synSpecial">(</span>wrap-http/debug <span class="synSpecial">(</span>http-post <span class="synConstant">&#34;secure.mixi-platform.com&#34;</span>
<span class="synConstant">&#34;/2/token&#34;</span>
<span class="synPreProc">`(</span><span class="synSpecial">(</span><span class="synConstant">&#34;grant_type&#34;</span> <span class="synConstant">&#34;refresh_token&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;client_id&#34;</span> ,<span class="synSpecial">(</span>consumer-key-of mixi-instance<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;client_secret&#34;</span> ,<span class="synSpecial">(</span>consumer-secret-of mixi-instance<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synConstant">&#34;refresh_token&#34;</span> ,<span class="synSpecial">(</span>refresh-token-of mixi-instance<span class="synSpecial">))</span><span class="synPreProc">)</span>
:secure #t<span class="synSpecial">)</span>
token-update!
#f<span class="synSpecial">))</span>
<span class="synComment">;;; 投稿の削除</span>
<span class="synSpecial">(</span>define-method delete-voice <span class="synSpecial">((</span>mixi-instance &#60;mixi-token&#62;<span class="synSpecial">)</span>
<span class="synSpecial">(</span>id &#60;string&#62;<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>authorization-key <span class="synSpecial">(</span>string-append <span class="synConstant">&#34;OAuth &#34;</span>
<span class="synSpecial">(</span>access-token-of mixi-instance<span class="synSpecial">)))</span>
<span class="synSpecial">(</span>uri <span class="synSpecial">(</span>string-append <span class="synConstant">&#34;/2/voice/statuses/&#34;</span>
id<span class="synSpecial">)))</span>
<span class="synSpecial">(</span>wrap-http/debug <span class="synSpecial">(</span>http-delete <span class="synConstant">&#34;api.mixi-platform.com&#34;</span>
uri
:Authorization authorization-key<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>response<span class="synSpecial">)</span> #t<span class="synSpecial">)</span>
#f<span class="synSpecial">)))</span>
<span class="synComment">;;; Voice一覧の取得</span>
<span class="synSpecial">(</span>define-method friend-voices <span class="synSpecial">((</span>mixi-instance &#60;mixi-token&#62;<span class="synSpecial">))</span>
<span class="synSpecial">(</span>wrap-http/debug <span class="synSpecial">(</span>http-get <span class="synConstant">&#34;api.mixi-platform.com&#34;</span>
<span class="synPreProc">`(</span><span class="synConstant">&#34;/2/voice/statuses/friends_timeline/&#34;</span>
<span class="synSpecial">(</span>oauth_token ,<span class="synSpecial">(</span>access-token-of mixi-instance<span class="synSpecial">))</span>
<span class="synSpecial">(</span>trim_user <span class="synConstant">&#34;1&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>attach_photo <span class="synConstant">&#34;1&#34;</span><span class="synSpecial">)</span><span class="synPreProc">)</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>response<span class="synSpecial">)</span> <span class="synSpecial">(</span>parse-json-string response<span class="synSpecial">))</span>
#f<span class="synSpecial">))</span>
</pre>
<h4>
使い方
</h4>
<p>
ソースコードを適当な名前で保存します (たとえば「foo.scm」とか)。その後、loadしてください:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span><span class="synStatement">load</span> <span class="synConstant">&#34;foo&#34;</span><span class="synSpecial">)</span>
</pre>
<h4>
Consumer key, Consumer secret の取得
</h4>
<p>
参考ページを参考にしてください。
</p>
<h4>
Authorization Code の取得
</h4>
<p>
参考ページを参考にして、次の URL にアクセスしてください:
</p>
<blockquote>
<p>
<a href="https://mixi.jp/connect_authorize.pl?client_id=" onclick="__gaTracker('send', 'event', 'outbound-article', 'https://mixi.jp/connect_authorize.pl?client_id=', 'https://mixi.jp/connect_authorize.pl?client_id=');" target="_blank">https://mixi.jp/connect_authorize.pl?client_id=</a><コンシューマキー>&response_type=code&scope=r_voice%20w_voice&display=pc
</p>
</blockquote>
<p>
[同意する]ボタンをクリックすると、次の URL のページにリダイレクトします:
</p>
<blockquote>
<p>
<a href="http://mixi.jp/connect_authorize_success.html?code=" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://mixi.jp/connect_authorize_success.html?code=', 'http://mixi.jp/connect_authorize_success.html?code=');" target="_blank">http://mixi.jp/connect_authorize_success.html?code=</a><英数字>
</p>
</blockquote>
<p>
上の「<英数字>」の部分が Authorization Code です。
</p>
<h5>
アクセストークンの取得
</h5>
<p>
次のようにしてコードを実行してください:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span>define <span class="synType">*mixi-obj*</span>
<span class="synSpecial">(</span>make &#60;mixi-token&#62;
:consumer-key <span class="synConstant">&#34;コンシューマーキー&#34;</span>
:consumer-secret <span class="synConstant">&#34;コンシューマーシークレット&#34;</span>
:authorization-code <span class="synConstant">&#34;authorization code&#34;</span><span class="synSpecial">))</span>
<span class="synType">*mixi-obj*</span>
gosh&#62; <span class="synSpecial">(</span>acquire-access-token! <span class="synType">*mixi-obj*</span><span class="synSpecial">)</span>
#&#60;&#60;mixi-token&#62; <span class="synConstant"></span>x<span class="synConstant">1011521e0</span>&#62;
</pre>
<p>
acquire-access-token! 手続きは、リフレッシュトークン・アクセストークンを取得し、 *mixi-obj* を上書きします。
</p>
<h5>
Voice の投稿
</h5>
<p>
アクセストークンを取得してから、次のようにして投稿してください:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span>post-voice <span class="synType">*mixi-obj*</span> <span class="synConstant">&#34;今日はゆっくりと、だらだら過ごすのだ!&#34;</span><span class="synSpecial">)</span>
<span class="synConstant">&#34;4ncdm68fwm56j-20110619113448&#34;</span>
</pre>
<p>
post-voice 手続きは成功すると、投稿したボイスの ID を戻り値としてます。
</p>
<p>
なお、 取得したアクセストークンの有効期限は 15 分と短いため、期限が切れた場合にはアクセストークンを再取得する必要があります。その場合には、update-access-token!手続きを使えば再取得してくれます:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span>define <span class="synSpecial">(</span>wrapper-post-voice mixi-instance voice<span class="synSpecial">)</span>
<span class="synSpecial">(</span>let1 result <span class="synSpecial">(</span>post-voice mixi-instance voice<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> result
result
<span class="synSpecial">(</span>post-voice <span class="synSpecial">(</span>update-access-token! mixi-instance<span class="synSpecial">)</span> voice<span class="synSpecial">))))</span>
wrapper-post-voice
gosh&#62; <span class="synSpecial">(</span>wrapper-post-voice <span class="synType">*mixi-obj*</span> <span class="synConstant">&#34;test&#34;</span><span class="synSpecial">)</span>
</pre>
<p>
実行結果はこんな感じです:
</p>
<p>
<a href="http://f.hatena.ne.jp/sirocco634/20110619114337" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://f.hatena.ne.jp/sirocco634/20110619114337', '');" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/sirocco634/20110619/20110619114337.jpg" alt="f:id:sirocco634:20110619114337j:image" title="f:id:sirocco634:20110619114337j:image" class="hatena-fotolife" /></a>
</p>
<h5>
Voice の削除
</h5>
<p>
次のようにすると、 Voice を削除できます。 Voice の ID が必要です。
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span>delete-voice <span class="synType">*mixi-obj*</span> <span class="synConstant">&#34;4ncdm68fwm56j-20110619114011&#34;</span><span class="synSpecial">)</span>
#<span class="synStatement">t</span>
</pre>
<p>
成功すると、#tが戻って来ます。失敗すると#fが戻って来ます。#fが戻ってくるときは、「(update-access-token! *mixi-obj*)」をしてください。
</p>
<h4>
友人の Voice 一覧の取得
</h4>
<p>
次のようにすると、自分と友人を含めた Voice の一覧を取得します。
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span>friend-voices <span class="synType">*mixi-obj*</span><span class="synSpecial">)</span>
</pre>
<p>
勝手に公開するのもどうかと思うので、実行結果は省略します。成功すると、#tが戻って来ます。失敗すると#fが戻って来ます。#fが戻ってくるときは、「(update-access-token! *mixi-obj*)」をしてください。
</p>
<h4>
後は
</h4>
<p>
Twitter から Runkeeper のデータを取得するだけですね。
</p>
</div>