Hatena::ブログ(Diary)

make for h @ppy_things;

2012-03-12(Mon)

ErlangでTwitterのUserStreamを受け取る

以前もErlangでTwitter Streaming APIを使うといったエントリを書いたのですが、いかんせん情報が古すぎます。UserStreamではなく、素のStreaming APIなのはともかく、認証がベーシック認証だったりします。

その割にはどうやら最近参照されているらしい。http://naoyat.hatenablog.jp/entry/2012/01/04/220639http://d.hatena.ne.jp/siritori/20120312/1331503357には以前のエントリのURLが貼られているようで。いや、なんかすみません。

ということで、ちゃんと動くかつOTPで書き直してみました。erlang-oauthに依存しています。

-module(userstream).
-author("Takahiro Kondo <heartery@gmail.com>").

-export([start/5, start/6, start_link/5, start_link/6, stop/1]).

-behavior(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-record(state, {id, processor}).

start(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret) ->
    start(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret, []).

start(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret, Options) ->
    Args = [Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret],
    gen_server:start(?MODULE, Args, Options).

start_link(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret) ->
    start_link(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret, []).

start_link(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret, Options) ->
    Args = [Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret],
    gen_server:start_link(?MODULE, Args, Options).

stop(Server) ->
    gen_server:cast(Server, stop).

%% callback functions

init([Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret]) ->
    Url = "https://userstream.twitter.com/2/user.json",
    Consumer = {ConsumerKey, ConsumerSecret, hmac_sha1},
    Options = [{sync, false}, {stream, self}],
    case oauth:post(Url, [], Consumer, AccessToken, AccessTokenSecret, Options) of
        {ok, Id}        -> {ok, #state{id = Id, processor = Processor}};
        {error, Reason} -> {stop, {http_error, Reason}}
    end.

handle_call(_, _, State) ->
    {noreply, State}.

handle_cast(stop, State) ->
    {stop, normal, State}.

handle_info({http, {Id, stream_start, Headers}}, #state{id = Id, processor = Processor} = State) ->
    send(Processor, {start, Headers}),
    {noreply, State};

handle_info({http, {Id, stream, <<"\r\n">>}}, #state{id = Id} = State) ->
    {noreply, State};

handle_info({http, {Id, stream, Part}}, #state{id = Id, processor = Processor} = State) ->
    send(Processor, {stream, Part}),
    {noreply, State};

handle_info({http, {Id, {error, Reason}}}, #state{id = Id, processor = Processor} = State) ->
    send(Processor, {error, Reason}),
    {stop, {http_error, Reason}, State}.

terminate(_, #state{id = Id, processor = Processor}) ->
    send(Processor, stop),
    httpc:cancel_request(Id).

code_change(_, State, _) ->
    {ok, State}.

%% private functions

send(To, Message) ->
    To ! Message.

ちゃんと動くかは確認しましたが、process_flagとかは呼んでいないのでそこらへんは適当に。gen_serverですので、ちゃんと設定すればそこまで手こずることなくsupervisor treeに組込めるかと思います。

本当はuserstreamモジュールをさらにビヘイビアにして、handle_status/2, handle_favorite/2とかで各イベントをハンドリングできるようにするといいんですが、それをやるとちょっと複雑になるので、そこまではやりません。

ちなみに使い方はこんな感じで。

-module(example).
-author("Takahiro Kondo <heartery@gmail.com>").

-export([start/0, stop/1]).

start() ->
    Processor = spawn(fun() -> process() end),
    ConsumerKey = "Your consumer key",
    ConsumerSecret = "Your consumer secret",
    AccessToken = "Your access token",
    AccessTokenSecret = "Your access token secret",
    userstream:start(Processor, ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret).

stop(Pid) ->
    userstream:stop(Pid).

process() ->
    receive
        {start, Headers} ->
            io:format("Start: ~p~n", [Headers]),
            process();
        {stream, Part} ->
            io:format("Stream: ~p~n", [Part]),
            process();
        {error, Reason} ->
            io:format("Error: ~p~n", [Reason]),
            process();
        stop ->
            io:format("Stop~n")
    end.
$ erl -s inets -s ssl
> {ok, Pid} = example:start().
ここにUserStreamからの応答が表示される(example:process/0で標準出力に吐き出してるため)
> example:stop(Pid).
Stop
>

OTPを使いつつ複雑すぎない書き方をしてみました。必ずしもOTPを使う必要はないですし、メリットばかりでもないんですが、アプリケーションがある程度複雑になってきたら使った方が良いかなと思います。それこそ書き捨てのコードでは不要でしょうが、あのプロセスが動いて、こっちであーでどーで、とかで頭のリソース割かれるならOTPを学ぶ価値はアリかなと。

余談ではありますが、余力があれば自作のtwitterモジュールGitHubにでもあげておきたいもんですね。それにはTwitterREST APIはもちろん、先述したUserStream用ビヘイビアも書いてはあるんですよ。ただ随分前からメンテナンスをサボってるので、REST APIが古過ぎるという感じで…… なんか一から書いた方が早そう。

2012-02-27(Mon)

autotest-twitterでブヒる

最近とあるgemを書きながら付随するgemを書いてて優先すべきそれが中々進まない昨今ですこんばんは。

で、恥ずかしながらテストファーストってあんまりやったことなくて、今それを実践しながらの開発をしています。使っているツールはRSpecなんですが、コマンドひとつでテストできるとは言っても、今度はそのコマンドを実行するのがめんどくさい。ひたすら怠惰な生き物ですね。

そういう生き物たちにうってつけなのが当然あって、それのひとつにautotestってのがあります。しばらくはautotest + autotest-growlで開発してたんですが、家で使ってるマシンはMac、職場で使ってるマシンはUbuntuなんですね。Macには当然Growlインストールしてるんですが、UbuntuとなるとGrowl以前の問題です。なので「Twitterにテストの結果をツイートして、あとは各OS向けのTwitterクライアントから通知すればいいんじゃね」と思い至りました。というわけでautotest-twitterです。まあ後からよく調べたらautotest-growlLinuxにも対応してましたけどね。ちくしょう。

使い方

README読めと言いたいところですが、GitHubに置いてあるのがいい加減なのでアレです。何がアレってテストを書いていないところですよね。まあとりあえずテストの対象となるアプリケーションなりライブラリが置いてあるディレクトリに.rspecを作り:

--format nested
--color

を、.autotestに:

require 'autotest-twitter'

Autotest::Twitter.configure do |config|
  # ツイートするアカウントのアクセストークンを設定
  config.consumer_key = 'your consumer key'
  config.consumer_secret = 'your consumer secret'
  config.oauth_token = 'your access token'
  config.oauth_token_secret = 'your access token secret'

  # ラベル。アプリケーションの名前とか
  config.label = 'any application'

  # テストの結果に応じてアイコンを変えられるので、そのアイコンが
  # 置いてあるディレクトリ
  #   - missing.png: テスト自体がない場合のアイコン
  #   - failed.png: テストに失敗した場合のアイコン
  #   - pending.png: ペンディングが存在する場合のアイコン
  #   - passed.png: テストに成功した場合のアイコン
  config.image_dir = 'path/to/icons'

  # テストの結果に応じたツイートの内容。$で始まるのは変数
  #   - $label: config.labelで設定した内容
  #   - $all: テストの全件数
  #   - $failed: 失敗したテストの件数
  #   - $pending: ペンディングしてるテストの件数
  config.missing_messages = ['$labelのテストが存在しないよ']
  config.failed_messages = ['$labelのテストに失敗したよ。$all件中$failed件がダメみたい']
  config.pending_messages = ['$labelのテストに$pending件のペンディングがあるよ']
  config.passed_messages = ['$labelのテストに成功したよ! $all件あったみたいだね']
end

を、Gemfileに:

source 'https://rubygems.org'

gem 'autotest'
gem 'autotest-fsevent'
gem 'autotest-twitter', :git => 'git://github.com/takkkun/autotest-twitter.git'

こう。で:

$ bundle --path vendor/bundle

でもしてautotest諸々をインストール。後は:

$ bundle exec autotest

でテストを開始。後はファイルに変更があるたびにテストが走り、結果に応じてツイートされるはずです。config.image_dirを設定してればアイコンも変わります。ちなみにRSpecでしか試していませんし、とりあえず動いてるっぽいってことしか確認してないのであしからず。

ちなみに僕は @Shinobu_DD で試していました。まるでアイコンセットのような画像(TVアニメ偽物語の一部でしょうが)があったので。でもまあ「$labelで$pending件ペンディングがあるようじゃな。お前様の生き様が垣間見えるの。かか」とか打ってると頭抱えたくなりますし、いざブヒろうにも全然テンション上がらないのであんま向いてなかったようです。ていうか元々そういうのじゃないし!

まあでもブヒろうと思えばブヒれるので、テストがコケたらツンツンされたり、テストが通ったらデレデレされたりして、「今日も開発がんばりましゅううう」とか言ってればいいんじゃないですかね。

あとさっき思い付いたんですけど、ツイートするアカウントを自分のアカウントにし、passed.pngをいつも使ってるアイコン、failed.pngをとてつもなく恥ずかしいアイコンにすると面白いんじゃないかと思います。はやくテスト通さないとエラい思いをするハメになるというマゾい開発が出来ていいんじゃないかナーーー。

2011-09-07(Wed)

Twitter彼氏の作り方と掘り方

※ この記事はフィクションです

Twitter彼氏の作り方

  1. たらしっぽい言動を男に振り撒きます
  2. ネタには果敢にノっていきます
  3. 彼氏が出来ます
  4. 充分な人数の彼氏が出来るまでそれを繰り返します

Twitter彼氏の掘り方

ググってください。ちなみにおまえ棒 ガチムチ味*1はかなりリアルらしいので、これで練習しておくと良いかもしれません。

私情

まずはじめに「どうしてこんなことになってしまったんだ」って言いたいです。そもそも僕は男性を性的な興味として捉えられませんし、まあネタとしてノることはありますが、うん…… ってそれがいけないんですよね。

とりあえず彼氏っぽい人たちを載せておきます。

  • @zya4
  • @HemusAran
  • @ne_ko_
  • @__________t_t_
  • @mitukiii

ひとりは通い妻(♂)に来るわ、ひとりは嫉妬の炎を燃やすメンヘラビッチ(♂)だわ、ひとりは慎みある淑女(♂)だわ、ひとりはキスを求めてくる性欲旺盛なヤリマン(♂)だわ、ひとりは運命感じちゃう乙女(♂)だわ、バリエーションが豊富で飽きません。

もういやだ。

*1:ガチゲイからいただきました。言っておきますが未使用です

2010-10-01(Fri)

断食ならぬ断Twitterをしてみた

昨日とあるツイートを見たら頭のネジが吹っ飛んで, 脳細胞が死滅した感覚に襲われたのでそのままTwitterから離れてみました. どうせこの日記を書いた後にはいつものようにさえずるのだけど, いろいろと書いておく.

  • Twitterなくても意外に平気
  • 使い方にもよるけど, ツイートしたり, タイムラインを見るのにに割く時間はけっこうなもん
  • Twitter関連のサービスを見ている時間もけっこうなもん
  • なので時間が空いて, いつも以上にのんびり
  • ただid:n0mzkとはTwitterで連絡を取っているので, そこは不便

僕はけっこうダラダラとタイムラインを見て時間を潰しちゃう人なので, それがなくなったもんだからけっこう時間空いた.

しかし頭の中でポッと出てきたコトを掃き出す口がないので, それがそのまま消えて少し物悲しい.

あとタイムラインを見てると, 笑ってる人がいたり, 怒ってる人がいたり, 悲しんでる人がいたりといろいろで, その溢れ出る感情に当てられるコトもしばしばだった. それがなくて精神衛生はすこぶる良好. 当然気にしない人であればどうってコトないけど.

試しに1週間, 1ヶ月と続けたらどうなるのだろう. 前述してあるのだけど, 僕はTwitterにある感情をすごく気にしてる. そのインプットがなくなるのだから, 感情の起伏は穏やかになると思う. 僕の生活だと下手したら限りなくゼロまたは一方向*1になりそう. コレはちょっと怖い.

精神衛生が汚されても, それを拭き取ってと繰り返した方が健康なんだろうな. うーん, 人間らしいというか. 精神衛生潔癖症にだけはなりたくないですね.

*1:主にクソ仕様への憤慨

2010-02-25(Thu)

Twitterで知り合った方と付き合い始めました

タイトルのとおり.

経緯は非常にシンプルで, Twitterでお話しているうちに好きになって,

数日前に想いを伝えたら互いに好きでした, ってだけのコトです.


逢ったコト? この時点ではないです.

テキストで表された口調も,

ツイートから見える思考の断片も,

通信回線を通した声も,

写真で切り抜かれた顔も知ってはいましたが,

対峙したコトはありません.


でも間違いなく好きなんですよね.

伝えるときに曖昧な表現になって, 自分のヘタレっぷりが垣間見えたり,

ゆえに自分の情けなさに打ちひしがれるコトもありましたが,

こころでは好きだと感じてました.


どうなんでしょう, このケース.

一般論という言葉で片付けると, やはり異常なのでしょうか.

でも好きなんですよ.

たとえ逢ったコトがなくても大好きなんですよ.


Twitterはゆるい繋がりだと言われたりしますが, Twitterが繋げるのは人と人, こころとこころです.

別にTwitterに限ったコトではないんですが, 人と人がふれあうとき, 何を介していても,

最後にたどり着くのは相手のこころです.

そう考えれば, 別に不思議ではないと切に思います.


今はすでに逢っていますが, おいそれと逢いにいける距離ではありません.

一度逢ってしまった以上, その空気, その体温, その感触を忘れられず, うら寂しくもなります.

でもあらゆる通信手段で好きなときに好きなだけ繋がっていられるこの時代でよかったとも思います.

Twitterで繋がった関係だからといって, チープだナンセンスだなんて言わせませんよ.

この繋がりをもっと強く, もっと確かなモノにしてみせます.


最後に大切な人へ. 恥ずかしいのでサラッと読み流してください.


みしらぬ関係から, 付き合うまでけっこうあっという間でしたね.

ずっと一緒にいたいと思うほどの大切な人.

きっと幸せにしてみせます.


愛嬌のある言動,

シリアスな思考,

ての温かさ, 全部好きだから.

ルーズでめんどくさい俺だけど, 今までも, これからも……

よろしくお願いね.