package Finance::Bitcoin::Feed::Site::BtcChina; use strict; use Mojo::Base 'Finance::Bitcoin::Feed::Site'; use Mojo::UserAgent; our $VERSION = '0.02'; has ws_url => 'wss://websocket.btcchina.com/socket.io/?transport=websocket'; has 'ua'; has 'site' => 'BTCCHINA'; sub go { my $self = shift; $self->SUPER::go; $self->ua( Mojo::UserAgent->new() ); $self->debug( 'connecting...', $self->ws_url ); $self->ua->websocket( $self->ws_url => sub { my ( $ua, $tx ) = @_; $self->debug('connected!'); unless ( $tx->is_websocket ) { $self->error("Site BtcChina WebSocket handshake failed!"); # set timeout; $self->set_timeout; return; } bless $tx, 'Mojo::Transaction::WebSocket::ForBtcChina'; $tx->configure($self); } ); } package Mojo::Transaction::WebSocket::ForBtcChina; # hidden from PAUSE use JSON; use Mojo::Base 'Mojo::Transaction::WebSocket'; use Scalar::Util qw(weaken); has 'owner'; has 'ping_interval'; has 'ping_timeout'; has 'last_ping_at'; has 'last_pong_at'; has 'timer'; sub configure { my $self = shift; my $owner = shift; $self->owner($owner); weaken( $self->{owner} ); # call parse when receive text event $self->on( text => sub { my ( $self, $message ) = @_; $self->parse($message); } ); ################################################ # setup events $self->on( subscribe => sub { my ( $self, $channel ) = @_; $self->on( 'setup', sub { $self->send( { text => qq(42["subscribe","$channel"]) } ); } ); } ); $self->emit( 'subscribe', 'marketdata_cnybtc' ); $self->emit( 'subscribe', 'marketdata_cnyltc' ); $self->emit( 'subscribe', 'marketdata_btcltc' ); #receive trade vent $self->on( trade => sub { my ( $self, $data ) = @_; $self->owner->emit( 'data_out', $data->{date} * 1000, # the unit of timestamp is ms uc( $data->{market} ), $data->{price} ); } ); $self->on( 'ping', sub { $self->send( { text => '2' } ); } ); # ping ping! my $timer = AnyEvent->timer( after => 10, interval => 1, cb => sub { if ( time() - $self->last_ping_at > $self->ping_interval / 1000 ) { $self->emit('ping'); $self->last_ping_at( time() ); } } ); $self->timer($timer); } #socket.io v2.2.2 sub parse { my ( $self, $data ) = @_; $self->owner->last_activity_at( time() ); return unless $data =~ /^\d+/; my ( $code, $body ) = $data =~ /^(\d+)(.*)$/; # connect, setup if ( $code == 0 ) { my $json_data = decode_json($body); #session_id useless ? $self->ping_interval( $json_data->{pingInterval} ) if $json_data->{pingInterval}; $self->ping_timeout( $json_data->{pingTimeout} ) if $json_data->{pingTimeout}; $self->last_pong_at( time() ); $self->last_ping_at( time() ); $self->emit('setup'); } # pong elsif ( $code == 3 ) { $self->last_pong_at( time() ); } #disconnect ? reconnect! elsif ( $code == 41 ) { $self->owner->debug('disconnected by server'); #set timeout $self->owner->set_timeout(); } elsif ( $code == 42 ) { my $json_data = decode_json($body); $self->emit( $json_data->[0], $json_data->[1] ); } } 1; __END__ =head1 NAME Finance::Bitcoin::Feed::Site::BtcChina -- the class that connect and fetch the bitcoin price data from site btcchina =head1 SYNOPSIS use Finance::Bitcoin::Feed::Site::BtcChina; use AnyEvent; my $obj = Finance::Bitcoin::Feed::Site::BtcChina->new(); # listen on the event 'output' to get the adata $obj->on('output', sub { shift; say @_ }); $obj->go(); # dont forget this AnyEvent->condvar->recv; =head1 DESCRIPTION Connect to site BitStamp by protocol socket.io v2.2.2 and fetch the bitcoin price data. =head1 EVENTS This class inherits all events from L<Finance::Bitcoin::Feed::Site> and add some new ones. The most important event is 'output'. =head2 output It will be emit by its parent class when print out the data. You can listen on this event to get the output. =head2 subscribe It will subscribe channel from the source site. You can subscribe more channels in the method L</configure> =head1 SEE ALSO L<Finance::Bitcoin::Feed::Site> L<btcchina api|http://btcchina.org/websocket-api-market-data-documentation-en> L<socket.io-parse|https://github.com/Automattic/socket.io-parser> L<Mojo::UserAgent> =head1 AUTHOR Chylli C<< <chylli@binary.com> >>