File Coverage

blib/lib/WebService/EveOnline/Base.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package WebService::EveOnline::Base;
2              
3             our $VERSION = "0.62";
4              
5 10     10   22083 use LWP::UserAgent;
  10         821648  
  10         414  
6 10     10   130 use HTTP::Request;
  10         24  
  10         250  
7 10     10   24616 use XML::Simple;
  0            
  0            
8             use Data::Dumper;
9              
10             use WebService::EveOnline::Cache;
11              
12             use WebService::EveOnline::API::Character;
13             use WebService::EveOnline::API::Corporation;
14             use WebService::EveOnline::API::Skills;
15             use WebService::EveOnline::API::Transactions;
16             use WebService::EveOnline::API::Journal;
17             use WebService::EveOnline::API::Account;
18             use WebService::EveOnline::API::Map;
19              
20             # U.G.L.Y. You ain't got no alibi (this is where we set up the API mappings, sort out the internal symbol conversion and set max cache times)
21             # max_cache overrides the cache time set in the default EVE webservice response XML (e.g. shorter for wallet, longer for bloodline which
22             # probably won't update every hour...)
23              
24             our $API_MAP = {
25             # Character
26             skills => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], max_cache => 900 },
27             balance => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], max_cache => 60 },
28             race => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], max_cache => 604800 },
29             bloodline => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], max_cache => 604800 },
30             attributes => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], },
31             enhancers => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], },
32             gender => { endpoint => 'char/CharacterSheet', params => [ [ 'character_id', 'characterID' ] ], max_cache => 604800 },
33             training => { endpoint => 'char/SkillInTraining', params => [ [ 'character_id', 'characterID' ] ], },
34             accounts => { endpoint => 'char/AccountBalance', params => [ [ 'character_id', 'characterID' ] ], max_cache => 60 },
35             transactions => { endpoint => 'char/WalletTransactions', params => [
36             [ 'character_id', 'characterID' ],
37             [ 'before_trans_id', 'beforeTransID' ],
38             [ 'account_key', 'accountKey' ],
39             ], max_cache => 3600 },
40             kills => { endpoint => 'char/Killlog', params => [ [ 'character_id', 'characterID' ] ], },
41             orders => { endpoint => 'char/MarketOrders', params => [ [ 'character_id', 'characterID' ] ], },
42             assets => { endpoint => 'char/AssetList', params => [ [ 'character_id', 'characterID' ] ], },
43              
44             # Corporation
45             corp_accounts => { endpoint => 'corp/AccountBalance', params => [ [ 'character_id', 'characterID' ] ], max_cache => 60 },
46             corp_members => { endpoint => 'corp/MemberTracking', params => [ [ 'character_id', 'characterID' ] ], },
47             corp_assets => { endpoint => 'corp/AssetList', params => [ [ 'character_id', 'characterID' ] ], },
48             corp_sheet => { endpoint => 'corp/CorporationSheet', params => [ [ 'character_id', 'characterID' ] ], },
49             corp_transactions => { endpoint => 'corp/WalletTransactions', params => [
50             [ 'character_id', 'characterID' ],
51             [ 'before_trans_id', 'beforeTransID' ],
52             [ 'account_key', 'accountKey' ],
53             ], max_cache => 3600 },
54             corp_kills => { endpoint => 'corp/Killlog', params => [ [ 'character_id', 'characterID' ] ], },
55             corp_orders => { endpoint => 'corp/MarketOrders', params => [ [ 'character_id', 'characterID' ] ], },
56             corp_baselist => { endpoint => 'corp/StarbaseList', params => [ [ 'character_id', 'characterID' ] ], },
57             corp_base => { endpoint => 'corp/StarbaseDetail', params => [ [ 'character_id', 'characterID' ] ], },
58              
59             # Map
60             map_jumps => { endpoint => 'map/Jumps', params => [ [ 'character_id', 'characterID' ] ], },
61             map_kills => { endpoint => 'map/Kills', params => [ [ 'character_id', 'characterID' ] ], },
62             map => { endpoint => 'map/Sovereignty', params => undef , },
63              
64             # Global/Misc
65             character => { endpoint => 'account/Characters', params => undef, max_cache => 3600 },
66             all_skills => { endpoint => 'eve/SkillTree', params => undef, max_cache => 86400 },
67             all_reftypes => { endpoint => 'eve/RefTypes', params => undef, },
68             };
69              
70             =head2 new
71              
72             Called by WebService::EveOnline->new -- sets things up at the backend without cluttering things up.
73             Doesn't die if not passed an api_key/user_id combination, unlike the latter.
74              
75             =cut
76              
77             sub new {
78             my ($class, $params) = @_;
79              
80             $params ||= {};
81             $params->{cache_type} ||= "SQLite";
82             $params->{cache_user} ||= "";
83             $params->{cache_pass} ||= "";
84             $params->{cache_dbname} ||= ($^O =~ /MSWin/) ? "c:/windows/temp/webservice_eveonline.db" : "/tmp/webservice_eveonline.db";
85             $params->{cache_init} ||= "yes";
86             $params->{cache_maxage} ||= (86400 * 7 * 4); # time (s) between cache rebuilds. 28 days, for now.
87            
88             my $evecache = WebService::EveOnline::Cache->new( { eve_user_id => $params->{user_id}, cache_type => $params->{cache_type}, cache_dbname => $params->{cache_dbname} } ) if $params->{cache_init} eq "yes";
89             if ($evecache && $evecache->cache_age >= $params->{cache_maxage}) {
90             $evecache->repopulate( { skills => call_api('all_skills'), map => call_api('map') } );
91             } else {
92             $evecache ||= WebService::EveOnline::Cache->new( { cache_type => "no_cache" } );
93             }
94            
95             return bless({ _user_id => $params->{user_id}, _api_key => $params->{api_key}, _evecache => $evecache }, $class);
96             }
97              
98             =head2 character, characters
99              
100             Pull back character objects based on your API key -- see examples/show_characters
101              
102             Singlar and plural are provided so as to allow grammatically correct usage given
103             the appropriate context (they both do exactly the same thing under the hood and
104             can be used interchangeably -- handy for contractors... ;-) )
105              
106             =cut
107              
108             sub characters {
109             return WebService::EveOnline::API::Character->new(@_);
110             }
111              
112             sub character {
113             return WebService::EveOnline::API::Character->new(@_);
114             }
115              
116             =head2 corporation
117              
118             Pull back a corporation information object -- use on a character object for best effect.
119             See examples/show_corporation
120              
121             =cut
122              
123             sub corporation {
124             return WebService::EveOnline::API::Corporation->new(@_);
125             }
126              
127             =head2 skill, skills
128              
129             Pull back skill objects on a character. See examples/skills_overview for more
130             details.
131              
132             Singlar and plural are provided so as to allow grammatically correct usage given
133             the appropriate context (they both do exactly the same thing under the hood and
134             can be used interchangeably).
135              
136             =cut
137              
138             sub skill {
139             return WebService::EveOnline::API::Skills->new(@_);
140             }
141              
142             sub skills {
143             return WebService::EveOnline::API::Skills->new(@_);
144             }
145              
146             =head2 transaction, transactions
147              
148             Returns transaction objects for a particular character/corporation. Singular/plural as above;
149             See examples/show_transactions for more details.
150              
151             =cut
152              
153             sub transaction {
154             return WebService::EveOnline::API::Transactions->new(@_);
155             }
156              
157             sub transactions {
158             return WebService::EveOnline::API::Transactions->new(@_);
159             }
160              
161             =head2 journal
162              
163             Placeholder, for the moment.
164              
165             =cut
166              
167             sub journal {
168             return WebService::EveOnline::API::Journal->new(@_);
169             }
170              
171             =head2 account, accounts
172              
173             Return detailed account objects for a particular character, including corporate
174             account info. The first member of the array ALWAYS returns the selected character's
175             personal account object -- subsequent accounts are from the corporation the
176             character belongs to. See examples/show_character for an example of how to use this.
177              
178             =cut
179              
180             sub account {
181             return WebService::EveOnline::API::Account->new(@_);
182             }
183              
184             sub accounts {
185             return WebService::EveOnline::API::Account->new(@_);
186             }
187              
188             =head2 map
189              
190             Another placeholder.
191              
192             =cut
193              
194             sub map {
195             return WebService::EveOnline::API::Map->new(@_);
196             }
197              
198              
199             =head2 $eve->user_id
200              
201             Returns the current user_id.
202              
203             =cut
204              
205             sub user_id {
206             my ($self, $user_id) = @_;
207             $self->{_user_id} = $user_id if $user_id;
208             return $self->{_user_id};
209             }
210              
211             =head2 $eve->api_key
212              
213             Returns the current api_key.
214              
215             =cut
216              
217             sub api_key {
218             my ($self, $api_key) = @_;
219             $self->{_api_key} = $api_key if $api_key;
220             return $self->{_api_key};
221             }
222              
223             =head2 $eve->call_api(, )
224              
225             Call the Eve API and retrieve the results. Look in the cache first. Cache results according to API map settings.
226              
227             =cut
228              
229             sub call_api {
230             my ($self, $command, $params, $base) = @_;
231            
232             my $auth = { user_id => "", api_key => "" };
233              
234             if (ref($base)) {
235             $auth = { user_id => $base->user_id, api_key => $base->api_key };
236             } else {
237             $command = $self;
238             }
239              
240             if ( defined($API_MAP->{$command}) ) {
241             my $cache = ref($self) ? $self->{_evecache} : $base->{_evecache};
242            
243             my $gen_params = _gen_params($self, $API_MAP->{$command}->{params}, $params);
244            
245             my $cached_response = $cache->retrieve( { command => "$command", params => $gen_params } ) if ref($cache);
246             return $cached_response if $cached_response;
247            
248             my $ua = LWP::UserAgent->new;
249             $ua->agent("$WebService::EveOnline::AGENT/$WebService::EveOnline::VERSION");
250              
251             my $req = HTTP::Request->new( POST => $WebService::EveOnline::EVE_API . $API_MAP->{$command}->{endpoint} . '.xml.aspx' );
252             $req->content_type("application/x-www-form-urlencoded");
253              
254             my $content = 'userid=' . $auth->{user_id} . '&apikey=' . $auth->{api_key} . $gen_params;
255            
256             $req->content($content) ;
257            
258             my $res = $ua->request($req);
259             if ($res->is_success) {
260             my $xs = XML::Simple->new();
261             my $xml = $res->content;
262              
263             warn "RAW XML is:\n$xml\n" if $ENV{EVE_DEBUG} =~ m/xml/i;
264              
265             my $pre = $xs->XMLin($xml);
266             my $data = {};
267             my $in_error_state = undef;
268              
269             # print out any error content if it's set.
270             if ($pre->{error}) {
271             # error 206 is returned on characters without corp permissions. ignore. FIXME: nasty hack
272             if ($pre->{error}->{code} != "206") {
273             $in_error_state = 1;
274             $data->{error} = "EVE API Error: " . $pre->{error}->{content} . " (" . $pre->{error}->{code} . ")";
275             }
276             }
277              
278             # at the moment, we deal in hashrefs. one day, these will be objects (like everything else will be ;-P)
279             unless ($in_error_state) {
280             if ($command eq "character") {
281             $data = $pre->{result}->{rowset}->{row};
282             } elsif ($command eq "skills") {
283             $data->{skills} = $pre->{result}->{rowset}->{skills}->{row} if $pre->{result}->{rowset}->{skills}->{row};
284             } elsif ($command eq "attributes") {
285             $data = $pre->{result}->{attributes};
286             } elsif ($command eq "enhancers") {
287             $data = $pre->{result}->{attributeEnhancers};
288             } elsif ($command eq "gender") {
289             $data = $pre->{result};
290             } elsif ($command eq "race") {
291             $data = $pre->{result};
292             } elsif ($command eq "bloodline") {
293             $data = $pre->{result};
294             } elsif ($command eq "balance") {
295             $data = $pre->{result};
296             } elsif ($command eq "training") {
297             $data = $pre->{result};
298             } elsif ($command eq "kills") {
299             $data = $pre->{result}->{rowset}->{row};
300             } elsif ($command eq "orders") {
301             $data = $pre->{result}->{rowset}->{row};
302             } elsif ($command eq "corp_kills") {
303             $data = $pre->{result}->{rowset}->{row};
304             } elsif ($command eq "corp_members") {
305             $data = $pre->{result}->{rowset}->{row};
306             } elsif ($command eq "corp_orders") {
307             $data = $pre->{result}->{rowset}->{row};
308             } elsif ($command eq "assets") {
309             $data = $pre->{result}->{rowset}->{row};
310             } elsif ($command eq "transactions") {
311             $data->{transactions} = $pre->{result}->{rowset}->{row} if $pre->{result}->{rowset}->{row};
312             } elsif ($command =~ /accounts/) {
313             my $acc = $pre->{result}->{rowset}->{row};
314             $data->{accounts} = ref($acc) eq "HASH" ? [ $acc ] : $acc;
315             } else {
316             $data = $pre;
317             return $data;
318             }
319             }
320              
321             $data->{_status} ||= "ok";
322             $data->{_xml} = $xml;
323             $data->{_parsed_as} = $pre;
324              
325             my $stripped_data = undef;
326            
327             unless ($WebService::EveOnline::DEBUG_MODE) {
328             $stripped_data = {};
329             foreach my $strip_debug (keys %{$data}) {
330             next if $strip_debug =~ /^_/; # skip meta keys
331             $stripped_data->{$strip_debug} = $data->{$strip_debug};
332             }
333             }
334              
335              
336             if ($cache && ($stripped_data || $data) && !$in_error_state) {
337             # error results are not cached
338             return $cache->store( { command => $command, obj => $self, data => $stripped_data || $data, params => $gen_params, cache_until => $pre->{cachedUntil}, max_cache => $API_MAP->{$command}->{max_cache} } );
339             } elsif ($in_error_state) {
340             warn $data->{error} . "\n";
341             return undef; # better error handling is required...;
342             } else {
343             return $stripped_data || $data;
344             }
345             } else {
346             warn "Error code received: " . $res->status_line . "\n" if $ENV{EVE_DEBUG};
347             return { _status => "error", message => $res->status_line, _raw => undef };
348             }
349             } else {
350             return { _status => "error", message => "Bad command", _raw => undef };
351             }
352            
353             }
354              
355             =head2 $character->before_trans_id
356              
357             Set to return transactions older than a particular trans id for character/corp transactions.
358              
359             =cut
360              
361             sub before_trans_id {
362             my ($self, $before_trans_id) = @_;
363             $self->{_before_trans_id} = $before_trans_id if $before_trans_id;
364             return $self->{_before_trans_id} || undef;
365             }
366              
367             =head2 id
368              
369             This will not return anything useful on the base class; call id on characters, accounts, transactions, etc.
370             where appropriate.
371              
372             =cut
373              
374             sub id {
375             return undef;
376             }
377              
378             sub _gen_params {
379             my ($self, $keys, $passed) = @_;
380             return "" unless defined $keys;
381              
382             my @kvp = ();
383             foreach my $param (@{$keys}) {
384             my ($intkey, $evekey) = @{$param};
385             if ($self->can($intkey)) {
386             push(@kvp, "$evekey=" . ($self->$intkey || $passed->{$intkey})) if ($self->$intkey || $passed->{$intkey});
387             } else {
388             push(@kvp, "$evekey=" . ($passed->{$evekey} || $passed->{$intkey})) if ($passed->{$evekey} || $passed->{$intkey});
389             }
390             }
391              
392             return '&' . (join('&', @kvp));
393             }
394              
395             1;