File Coverage

blib/lib/Finance/Bitcoin/Feed/Site/BtcChina.pm
Criterion Covered Total %
statement 67 71 94.3
branch 12 18 66.6
condition n/a
subroutine 13 15 86.6
pod 1 1 100.0
total 93 105 88.5


line stmt bran cond sub pod time code
1             package Finance::Bitcoin::Feed::Site::BtcChina;
2 1     1   25142 use strict;
  1         1  
  1         32  
3 1     1   405 use Mojo::Base 'Finance::Bitcoin::Feed::Site';
  1         7174  
  1         6  
4 1     1   674 use Mojo::UserAgent;
  1         216818  
  1         10  
5              
6             our $VERSION = '0.03';
7              
8             has ws_url => 'wss://websocket.btcchina.com/socket.io/?transport=websocket';
9             has 'ua';
10             has 'site' => 'BTCCHINA';
11              
12             sub go {
13 2     2 1 724 my $self = shift;
14 2         12 $self->SUPER::go;
15              
16 2         14 $self->ua(Mojo::UserAgent->new());
17 2         66 $self->debug('connecting...', $self->ws_url);
18             $self->ua->websocket(
19             $self->ws_url => sub {
20 2     2   158 my ($ua, $tx) = @_;
21 2         7 $self->debug('connected!');
22 2 100       5 unless ($tx->is_websocket) {
23 1         62 $self->error("Site BtcChina WebSocket handshake failed!");
24              
25             # set timeout;
26 1         8 $self->set_timeout;
27 1         27 return;
28             }
29              
30 1         55 bless $tx, 'Mojo::Transaction::WebSocket::ForBtcChina';
31 1         4 $tx->configure($self);
32 2         28 });
33              
34             }
35              
36             package Mojo::Transaction::WebSocket::ForBtcChina; # hidden from PAUSE
37              
38 1     1   897 use JSON;
  1         8283  
  1         14  
39 1     1   194 use Mojo::Base 'Mojo::Transaction::WebSocket';
  1         2  
  1         5  
40 1     1   156 use Scalar::Util qw(weaken);
  1         1  
  1         559  
41             has 'owner';
42             has 'ping_interval';
43             has 'ping_timeout';
44             has 'last_ping_at';
45             has 'last_pong_at';
46             has 'timer';
47              
48             sub configure {
49 1     1   1 my $self = shift;
50 1         2 my $owner = shift;
51 1         20 $self->owner($owner);
52 1         8 weaken($self->{owner});
53              
54             # call parse when receive text event
55             $self->on(
56             text => sub {
57 4     4   4645 my ($self, $message) = @_;
58 4         11 $self->parse($message);
59 1         9 });
60              
61             ################################################
62             # setup events
63             $self->on(
64             subscribe => sub {
65 3     3   17 my ($self, $channel) = @_;
66             $self->on(
67             'setup',
68             sub {
69 3         181 $self->send({text => qq(42["subscribe","$channel"])});
70 3         8 });
71 1         9 });
72 1         10 $self->emit('subscribe', 'marketdata_cnybtc');
73 1         12 $self->emit('subscribe', 'marketdata_cnyltc');
74 1         7 $self->emit('subscribe', 'marketdata_btcltc');
75              
76             #receive trade vent
77             $self->on(
78             trade => sub {
79 1     1   6 my ($self, $data) = @_;
80 1         17 $self->owner->emit(
81             'data_out',
82             $data->{date} * 1000, # the unit of timestamp is ms
83             uc($data->{market}),
84             $data->{price});
85              
86 1         9 });
87              
88             $self->on(
89             'ping',
90             sub {
91 0     0   0 $self->send({text => '2'});
92 1         7 });
93              
94             # ping ping!
95             my $timer = AnyEvent->timer(
96             after => 10,
97             interval => 1,
98             cb => sub {
99 0 0   0   0 if (time() - $self->last_ping_at > $self->ping_interval / 1000) {
100 0         0 $self->emit('ping');
101 0         0 $self->last_ping_at(time());
102             }
103 1         20 });
104 1         25 $self->timer($timer);
105              
106             }
107              
108             #socket.io v2.2.2
109             sub parse {
110 4     4   3 my ($self, $data) = @_;
111 4         84 $self->owner->last_activity_at(time());
112 4 50       96 return unless $data =~ /^\d+/;
113 4         13 my ($code, $body) = $data =~ /^(\d+)(.*)$/;
114              
115             # connect, setup
116 4 100       21 if ($code == 0) {
    100          
    100          
    50          
117 1         8 my $json_data = decode_json($body);
118              
119             #session_id useless ?
120              
121 1 50       22 $self->ping_interval($json_data->{pingInterval})
122             if $json_data->{pingInterval};
123 1 50       22 $self->ping_timeout($json_data->{pingTimeout})
124             if $json_data->{pingTimeout};
125 1         19 $self->last_pong_at(time());
126 1         19 $self->last_ping_at(time());
127 1         9 $self->emit('setup');
128             }
129              
130             # pong
131             elsif ($code == 3) {
132 1         17 $self->last_pong_at(time());
133             }
134              
135             #disconnect ? reconnect!
136             elsif ($code == 41) {
137 1         16 $self->owner->debug('disconnected by server');
138              
139             #set timeout
140 1         15 $self->owner->set_timeout();
141             } elsif ($code == 42) {
142 1         11 my $json_data = decode_json($body);
143 1         5 $self->emit($json_data->[0], $json_data->[1]);
144             }
145              
146             }
147              
148             1;
149              
150             __END__
151              
152             =head1 NAME
153              
154             Finance::Bitcoin::Feed::Site::BtcChina -- the class that connect and fetch the bitcoin price data from site btcchina
155              
156             =head1 SYNOPSIS
157              
158             use Finance::Bitcoin::Feed::Site::BtcChina;
159             use AnyEvent;
160              
161             my $obj = Finance::Bitcoin::Feed::Site::BtcChina->new();
162             # listen on the event 'output' to get the adata
163             $obj->on('output', sub { shift; say @_ });
164             $obj->go();
165              
166             # dont forget this
167             AnyEvent->condvar->recv;
168              
169             =head1 DESCRIPTION
170              
171             Connect to site BitStamp by protocol socket.io v2.2.2 and fetch the bitcoin price data.
172              
173             =head1 EVENTS
174              
175             This class inherits all events from L<Finance::Bitcoin::Feed::Site> and add some new ones.
176             The most important event is 'output'.
177              
178             =head2 output
179              
180             It will be emit by its parent class when print out the data. You can listen on this event to get the output.
181              
182             =head2 subscribe
183              
184             It will subscribe channel from the source site. You can subscribe more channels in the method L</configure>
185              
186             =head1 SEE ALSO
187              
188             L<Finance::Bitcoin::Feed::Site>
189              
190             L<btcchina api|http://btcchina.org/websocket-api-market-data-documentation-en>
191              
192             L<socket.io-parse|https://github.com/Automattic/socket.io-parser>
193              
194             L<Mojo::UserAgent>
195              
196             =head1 AUTHOR
197              
198             Chylli C<< <chylli@binary.com> >>
199