| 1 | #!/usr/bin/env perl |
|---|
| 2 | package WassrPod; |
|---|
| 3 | use Moose; |
|---|
| 4 | use HTTP::Engine; |
|---|
| 5 | use HTTP::Headers; |
|---|
| 6 | use LWP::UserAgent; |
|---|
| 7 | use JSON 2.11; |
|---|
| 8 | use DateTime; |
|---|
| 9 | use XML::Simple; |
|---|
| 10 | use Encode; |
|---|
| 11 | |
|---|
| 12 | our $VERSION = '0.0.1'; |
|---|
| 13 | |
|---|
| 14 | use Data::Dumper; |
|---|
| 15 | |
|---|
| 16 | with 'MooseX::Getopt'; |
|---|
| 17 | |
|---|
| 18 | has port => |
|---|
| 19 | is => 'ro', |
|---|
| 20 | isa => 'Int', |
|---|
| 21 | default => 9277; # 9277 = wassr |
|---|
| 22 | |
|---|
| 23 | sub run { |
|---|
| 24 | my $self = shift; |
|---|
| 25 | my $engine = HTTP::Engine->new( |
|---|
| 26 | interface => { |
|---|
| 27 | module => 'ServerSimple', |
|---|
| 28 | args => { |
|---|
| 29 | host => '127.0.0.1', |
|---|
| 30 | port => $self->port, |
|---|
| 31 | }, |
|---|
| 32 | request_handler => sub { $self->handler(@_) }, |
|---|
| 33 | }, |
|---|
| 34 | ); |
|---|
| 35 | $engine->run; |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | sub handler { |
|---|
| 39 | my($self, $c) = @_; |
|---|
| 40 | my $req = $c->req; |
|---|
| 41 | |
|---|
| 42 | my $body = ''; |
|---|
| 43 | if ($req->path eq 'http://twitter.com/statuses/friends_timeline.xml') { |
|---|
| 44 | $body = $self->friends_timeline($req); |
|---|
| 45 | $c->res->header('Content-Type' => 'application/xml'); |
|---|
| 46 | } elsif ($req->path eq 'http://twitter.com/statuses/update.xml') { |
|---|
| 47 | $body = $self->update($req); |
|---|
| 48 | $c->res->header('Content-Type' => 'application/xml'); |
|---|
| 49 | } elsif ($req->path =~ '^http://wassr.jp/.*$') { |
|---|
| 50 | my $ua = ua($req, 'wassr.jp'); |
|---|
| 51 | my $res = $ua->get($req->path); |
|---|
| 52 | $c->res->header('Content-Type' => $res->header('Content-Type')); |
|---|
| 53 | $body = $res->content; |
|---|
| 54 | } else { |
|---|
| 55 | warn $req->path; |
|---|
| 56 | } |
|---|
| 57 | $c->res->body($body); |
|---|
| 58 | }; |
|---|
| 59 | |
|---|
| 60 | sub ua { |
|---|
| 61 | my $req = shift; |
|---|
| 62 | my $host = shift || 'api.wassr.jp'; |
|---|
| 63 | |
|---|
| 64 | my $headers = $req->headers->clone; |
|---|
| 65 | $headers->remove_header('host'); |
|---|
| 66 | $headers->header( Host => $host ); |
|---|
| 67 | $headers->remove_header('user-agent'); |
|---|
| 68 | $headers->header( 'User-Agent' => sprintf('%s/%s', __PACKAGE__, $VERSION) ); |
|---|
| 69 | |
|---|
| 70 | my $ua = LWP::UserAgent->new( |
|---|
| 71 | default_headers => $headers, |
|---|
| 72 | ); |
|---|
| 73 | $ua->default_headers($headers); |
|---|
| 74 | $ua; |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | sub friends_timeline { |
|---|
| 78 | my($self, $req) = @_; |
|---|
| 79 | |
|---|
| 80 | my $ua = ua $req; |
|---|
| 81 | |
|---|
| 82 | my $ret = $ua->get('http://api.wassr.jp/statuses/friends_timeline.json'); |
|---|
| 83 | my $json = eval { decode_json($ret->content) }; |
|---|
| 84 | return '<statuses></statuses>' if $@ || !ref($json); |
|---|
| 85 | |
|---|
| 86 | my $data = { |
|---|
| 87 | statuses => { |
|---|
| 88 | status => [ |
|---|
| 89 | ] |
|---|
| 90 | } |
|---|
| 91 | }; |
|---|
| 92 | |
|---|
| 93 | for my $status (@{ $json }) { |
|---|
| 94 | my $dt = DateTime->from_epoch( epoch => $status->{epoch} ); |
|---|
| 95 | # $dt->set_time_zone( 'Asia/Tokyo' ); |
|---|
| 96 | my $text = $status->{text}; |
|---|
| 97 | if ($status->{reply_user_login_id}) { |
|---|
| 98 | $text .= sprintf '[Re:%s/%s]', $status->{reply_user_login_id}, $status->{reply_message}; |
|---|
| 99 | } |
|---|
| 100 | my $tmp = { |
|---|
| 101 | created_at => $dt->strftime('%a %b %d %T %z %Y'), |
|---|
| 102 | id => $status->{rid}, |
|---|
| 103 | text => $text, |
|---|
| 104 | source => 'web', |
|---|
| 105 | truncated => 'false', |
|---|
| 106 | in_reply_to_status_id => undef, |
|---|
| 107 | in_reply_to_user_id => undef, |
|---|
| 108 | favorited => undef, |
|---|
| 109 | |
|---|
| 110 | user => { |
|---|
| 111 | id => $status->{user_login_id}, |
|---|
| 112 | name => $status->{user}->{screen_name}, |
|---|
| 113 | screen_name => $status->{user_login_id}, |
|---|
| 114 | location => undef, |
|---|
| 115 | description => undef, |
|---|
| 116 | profile_image_url => $status->{user}->{profile_image_url}, |
|---|
| 117 | url => undef, |
|---|
| 118 | protected => ''.$status->{user}->{protected}, |
|---|
| 119 | followers_count => 1, |
|---|
| 120 | }, |
|---|
| 121 | }; |
|---|
| 122 | push @{ $data->{statuses}->{status} }, $tmp; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | my $xml = XMLout($data, NoAttr => 1, KeepRoot => 1, NumericEscape => 3 ); |
|---|
| 126 | $xml =~ s/<statuses>/<statuses type="array">/; |
|---|
| 127 | $xml = qq{<?xml version="1.0" encoding="UTF-8"?>\n$xml}; |
|---|
| 128 | $xml; |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | sub update { |
|---|
| 132 | my($self, $req) = @_; |
|---|
| 133 | |
|---|
| 134 | my $status = $req->param('status'); |
|---|
| 135 | my $source = "WassrPod(@{[ $req->param('source') ]})"; |
|---|
| 136 | |
|---|
| 137 | my $ua = ua $req; |
|---|
| 138 | if ($status =~ /^#([a-z0-9_]+)\s+(.+)$/) { |
|---|
| 139 | my $ret = $ua->post('http://api.wassr.jp/channel_message/update.json', { |
|---|
| 140 | name_en => $1, |
|---|
| 141 | body => $2, |
|---|
| 142 | }); |
|---|
| 143 | return $ret->content; |
|---|
| 144 | } else { |
|---|
| 145 | my $ret = $ua->post('http://api.wassr.jp/statuses/update.json', { |
|---|
| 146 | status => $status, |
|---|
| 147 | source => $source, |
|---|
| 148 | }); |
|---|
| 149 | return $ret->content; |
|---|
| 150 | } |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | package main; |
|---|
| 154 | WassrPod->new_with_options->run; |
|---|
| 155 | |
|---|