#!/usr/bin/env perl -w
#
# This is a translation of facebook-chat.rb into Perl, by geofft@mit.edu.

use strict;

use IO::Uncompress::Gunzip;
use JSON;
use Time::HiRes;
use WWW::Mechanize;

package Net::Facebook;

# Net::Facebook->new(email => ..., pass => ...)

sub new {
  my $class = shift;
  return bless {@_}, $class;
}

sub login {
  my $self = shift;
  my $agent = $self->{agent} = WWW::Mechanize->new();
  $agent->agent_alias('Windows IE 6');
  $agent->get("http://facebook.com/login.php");
  $agent->submit_form(
    form_number => 0,
    fields => {
      email => $self->{email},
      pass => $self->{pass}
    }
  );
  my $body;
  my $zbody = $agent->get("http://www.facebook.com/home.php")->content;
  IO::Uncompress::Gunzip::gunzip(\$zbody, \$body);

  # parse info out of facebook home page
  $body =~ m{<a href=".+?/profile.php\?id=(\d+)&amp;ref=profile" .*?>Profile</a>};
  $self->{uid} = $1;
  $body =~ m{"channel(\d+)"};
  $self->{channel} = $1;
  $body =~ m{<input type="hidden" id="post_form_id" name="post_form_id" value="([^"]+)};
  $self->{post_form_id} = $1;
}

sub wait_for_messages {
  my $self = shift;
  $self->determine_initial_seq_number unless $self->{seq};
  my $json;

  do {
    $json = parse_json($self->{agent}->get($self->get_message_url($self->{seq}))->content);
  } while ($json->{t} eq "continue");
  $self->{seq}++;

  my @messages;
  foreach my $msg ($json->{ms}) {
    next unless $msg->{type} eq "message";
    push @messages, $msg;
  }
  return @messages;
}

sub send_message {
  my ($self, $uid, $text) = @_;
  $self->{agent}->post("http://www.facebook.com/ajax/chat/send.php",
      'msg_text' => $text,
      'msg_id' => rand(999999999),
      'client_time' => int(Time::HiRes::time * 1000),
      'to' => $uid,
      'post_form_id' => $self->{post_form_id});
}

sub buddy_list {
  my $self = shift;
  my $response = $self->{agent}->post("http://www.facebook.com/ajax/presence/update.php",
                        ['buddy_list' => 1, 'force_render' => 1,
                         'post_form_id' => $self->{post_form_id}, 'user' => $self->{uid}]);
  $self->{json} = parse_json($response->content);
  my %ret = ();
  while (my ($key, $value) = each %{$self->{json}->{payload}->{buddy_list}->{userInfos}}) {
    $ret{$key} = $value->{name};
  }
  return {%ret};
};

sub determine_initial_seq_number {
  # -1 will always be a bad seq number so fb will tell us what the correct one is
  my $self = shift;
  my $json = parse_json($self->{agent}->get($self->get_message_url(-1))->content);
  $self->{seq} = $json->{seq};
}

sub get_message_url {
  my $self = shift;
  my $seq = shift;
  "http://0.channel$self->{channel}.facebook.com/x/0/false/p_$self->{uid}=$seq";
}

# get rid of initial js junk, like 'for(;;);'
sub parse_json {
  my $zinput = shift;
  my $input;
  IO::Uncompress::Gunzip::gunzip(\$zinput, \$input);
  $input =~ s/^[^{]+//;
  ::decode_json($input);
}

1;
