Jabber IRC クライアント
Net::XMPP2はめんどくさい
せっかくサーバを作ったので,今度はクライアントを作ってみました.なんとか動いたものの,ものすごくきたないです.Net::XMPP2がRPCをサポートしてくれていれば何の問題もなかったのですが,それがないので苦労しました.サーバを作るのに使ったJabber::RPCは外部コンポーネント用としては使えるのですが,TLSをサポートしていないので,普通のクライアントとしては使えません.
request
今回はNet::XMPP2::IM::Connectionを使っています.このモジュールでは,send_iqというメソッドでiqを送受信できます.
send_iq ($type, $create_cb, $result_cb, %attrs)
$typeはJabber RPCの場合"set"になります.$create_cbはNet::XMPP2::Writerがネットに書き出すための情報をノードの形式で渡します.$result_cbはサーバからレスポンスが返って来た時の処理を書き,%attrsはメッセージを送信するために必要なtoやfromの情報を書きます.簡単な方から書いて行くと,まず%attrsはこんな感じ.
to => 'jrpc.xmpp.foo.co.jp', from => 'bot@xmpp.foo.co.jp',
次に$result_cbはこんな感じ.find_allの使い方がわからなくて,ここでえらく時間を食いました.現状は文字列(string)を出力するようにハードコードしていますが,これは後で修正するつもりです.
sub { my ( $n, $e ) = @_; die "iq error : " . $e->string . "\n" if ($e); my ($q) = $n->find_all( ['jabber:iq:rpc','query'], ['jabber:iq:rpc','methodResponse'], ['jabber:iq:rpc','params'], ['jabber:iq:rpc','param'], ['jabber:iq:rpc','value'], ['jabber:iq:rpc','string'] ); print $q->text,"\n"; },
問題なのは,$create_cbです.こんな物手動で書くなんて絶対変です.なんとかしたいと思うんだけど,Jabber rpcだけを考えると,パラメータの所をテンプレートにすればいいだけかも.
sub { my ($w) = @_; simxml( $w, defns => 'jabber:iq:rpc', node => { name => 'query', ns => 'jabber:iq:rpc', childs => [ { name => 'methodCall', childs => [ { name => 'methodName', childs => [ 'examples.getStateName' ], }, { name => 'params', childs => [ { name => 'param', childs => [ { name => 'value', childs => [ { name => 'i4', childs => [ 6 ], } ], } ], } ], }, ], } ], } ); },
複雑なんだけど,やってることは大したことなくて,
<iq type='set' from='bot@xmpp.foo.co.jp' to='jrpc.xmpp.foo.co.jp' id='rpc1'> <query xmlns='jabber:iq:rpc'> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>6</i4></value> </param> </params> </methodCall> </query> </iq>
を送ろうとしてるだけです.
source
全体はこんな感じになりました.
#!/opt/perl/bin/perl use strict; use utf8; use AnyEvent; use Net::XMPP2::Util qw/simxml/; use Net::XMPP2::IM::Connection; my $j = AnyEvent->condvar; my $con = Net::XMPP2::IM::Connection->new( jid => 'bot@xmpp.foo.co.jp', password => 'secret', initial_presence => -10, debug => 1 ); $con->reg_cb( session_ready => sub { my ($con) = @_; print "Connected as " . $con->jid . "\n"; $con->send_iq( 'set', # type sub { my ($w) = @_; simxml( $w, defns => 'jabber:iq:rpc', node => { name => 'query', ns => 'jabber:iq:rpc', childs => [ { name => 'methodCall', childs => [ { name => 'methodName', childs => [ 'examples.getStateName' ], }, { name => 'params', childs => [ { name => 'param', childs => [ { name => 'value', childs => [ { name => 'i4', childs => [ 6 ], } ], } ], } ], }, ], } ], } ); }, sub { my ( $n, $e ) = @_; die "iq error : " . $e->string . "\n" if ($e); my ($q) = $n->find_all( ['jabber:iq:rpc','query'], ['jabber:iq:rpc','methodResponse'], ['jabber:iq:rpc','params'], ['jabber:iq:rpc','param'], ['jabber:iq:rpc','value'], ['jabber:iq:rpc','string'] ); print $q->text,"\n"; }, to => 'jrpc.xmpp.foo.co.jp', from => 'bot@xmpp.foo.co.jp', ); }, error => sub { my ( $con, $error ) = @_; warn "Error: " . $error->string . "\n"; }, disconnect => sub { my ( $con, $h, $p, $reason ) = @_; warn "Disconnected from $h:$p: $reason\n"; $j->broadcast; } ); print "Trying to connect...\n"; $con->connect(); $j->wait;
かなり作り込まないときれいなコードにはならない感じです.