#!/usr/bin/env perl use warnings; use strict; use JSON; use Data::Dumper; use POE qw(Component::Client::TCP Filter::Stream); # Load HTTP::Request for forming the initial request. use HTTP::Request; # Create a JSON object my $json = new JSON; # A very simple 'read only' mtgox streaming api client # This is for those that want to do there own ticker or just watch prices fly by # on MTGox without implementing a full SocketIO or WebSocket client in POE, # It should be noted that though this works it is not exactly and elegant way to # handle websockets. # Follow the comments in the code for what is going on its pretty self # explanatory :) # Author: Paul G Webster # Email: 'daemon' <daemon@cpan.org> # IRC: irc.perl.org#poe irc.freenode.net#perl POE::Component::Client::TCP->new( RemoteAddress => "websocket.mtgox.com", RemotePort => 80, Filter => POE::Filter::Stream->new(), Connected => sub { my $request = HTTP::Request->new(GET => '/mtgox'); $request->protocol('HTTP/1.1'); $request->header( Upgrade => 'WebSocket', Connection => 'Upgrade', Host => 'websocket.mtgox.com', Origin => 'http://websocket.mtgox.com', Sec_WebSocket_Key => '+HCD64gytTSbCbBTdvduPw==', Sec_WebSocket_Version => 13, ); $_[HEAP]->{server}->put($request->as_string); }, ServerInput => sub { my ($kernel, $heap, $input) = @_[KERNEL, HEAP, ARG0]; # if no rcvbuf set, set it to an empty string. $heap->{rcvbuf} = "" if (!defined $heap->{rcvbuf}); # This is a stream (POE::Filter::Stream) we may or may not get a # complete response in one go, so lets read whatever we get into # a buffer. $heap->{rcvbuf} .= $input; # Return from processing if there is nothing in the buffer return if (!$heap->{rcvbuf}); # Ok all requests start with {"channel" so lets match upto that # per request... # This regex says ... match from the start of the buffer to # {"channel upto the next occurance of {"channel, the .{4} # is because in the stream there is a seperator between each # block of data that is exactly 4 characters wide. my ($request, $rest) = $heap->{rcvbuf} =~ m#^.*?({"channel".*?).{4}({"channel".*)#s; # Above we not only matched the packet but the start of the next # packet. # We really should have BOTH so lets check for the second match # $rest if it does not exist return and stop processing till we # have more data in the buffer. return if (!$rest); # We now have our request in $request, so lets overwrite our # buffer with what was left $rest ($heap->{rcvbuf}) = $rest; # $request should be a complete JSON object encoded in UTF8, so # lets unencode it to a perl hash. $request = $json->utf8->decode($request); # Extract the channel handler (This is a mandatory key) my $handler = $request->{channel_name}; # Convert the .'s to _'s (more POE compatible) $handler =~ s#\.#_#g; # We could now post it off to that handler in this session print "Posting to: $handler\n"; $kernel->yield($handler, $request); }, InlineStates => { ticker_BTCUSD => sub { my ($input) = $_[ARG0]; warn "Got: " . Dumper($input); }, }, ); POE::Kernel->run(); exit;