#!/usr/bin/env perl ## Copyright © 2009,2010 by Daniel Friesel ## License: WTFPL ## 0. You just DO WHAT THE FUCK YOU WANT TO. use strict; use warnings; use 5.010; use Net::Travel::DE::VRR; use Getopt::Long qw/:config no_ignore_case/; our $VERSION = '1.3'; my $ignore_info = 'Fahrradmitnahme'; my $efa; my ( @from, @to, @via, $from_type, $to_type, $via_type ); my $opt = { 'help' => sub { exec( 'perldoc', '-F', $0 ) }, 'ignore-info' => \$ignore_info, 'from' => \@from, 'to' => \@to, 'version' => sub { say "efa version $VERSION"; exit 0 }, 'via' => \@via, }; binmode( STDOUT, ':encoding(utf-8)' ); binmode( STDERR, ':encoding(utf-8)' ); #<<< GetOptions( $opt, qw{ arrive|a=s bike|b date|d=s depart=s exclude|e=s@ from=s@{2} help|h ignore-info|I=s max-change|m=i prefer|P=s proximity|p include|i=s test-dump test-parse time|t=s timeout=i to=s@{2} version|v via=s@{2} walk-speed|w=s }, ) or die("Please see perldoc -F $0\n"); #>>> if ( not( @from and @to ) ) { if ( @ARGV == 4 ) { ( @from[ 0, 1 ], @to[ 0, 1 ] ) = @ARGV; } elsif ( @ARGV == 6 ) { ( @from[ 0, 1 ], @via[ 0, 1 ], @to[ 0, 1 ] ) = @ARGV; } } for my $pair ( [ \@from, \$from_type ], [ \@via, \$via_type ], [ \@to, \$to_type ], ) { next if ( not defined $pair->[0]->[1] ); if ( $pair->[0]->[1] =~ s{ ^ (? [^:]+ ) : \s* (? .+ ) $ } {$+{target}}x ) { given ( $+{type} ) { when ('addr') { ${ $pair->[1] } = 'address' } default { ${ $pair->[1] } = $+{type} } } } } $efa = Net::Travel::DE::VRR->new( from => [ @from, $from_type ], to => [ @to, $to_type ], via => ( @via ? [ @via, $via_type ] : undef ), arrive => $opt->{'arrive'}, depart => $opt->{'depart'} // $opt->{'time'}, date => $opt->{'date'}, exclude => $opt->{'exclude'}, prefer => $opt->{'prefer'}, include => $opt->{'include'}, bike => $opt->{'bike'}, proximity => $opt->{'proximity'}, walk_speed => $opt->{'walk-speed'}, max_interchanges => $opt->{'max-change'}, ); $efa->submit( timeout => $opt->{'timeout'} ); $efa->parse(); my @connections = $efa->connections(); for my $i ( 0 .. $#connections ) { for my $c ( @{ $connections[$i] } ) { for my $extra ( @{ $c->{'extra'} } ) { if ( not( length $ignore_info and $extra =~ /$ignore_info/i ) ) { say "# $extra"; } } printf( "%-5s ab %-30s %-20s %s\n%-5s an %-30s\n\n", $c->{'dep_time'}, $c->{'dep_stop'}, $c->{'train_line'}, $c->{'train_dest'}, $c->{'arr_time'}, $c->{'arr_stop'}, ); } if ( $i != $#connections ) { print "------\n\n"; } } __END__ =head1 NAME efa - unofficial efa.vrr.de command line client =head1 SYNOPSIS =over =item B B<--from> I I B<--to> I I [ I ] =item B [ I ] I I [ I I ] I I =back =head1 VERSION version 1.3 =head1 DESCRIPTION B is a command line client for the L web interface. It sends the specified information to the online form and displays the results. =head1 OPTIONS =over =item B<--from> I I Departure place =item B<--to> I I Arrival place =item B<--via> I I Travel via this place In case you want I to be an address or "point of interest", you can set it to 'addr:something' or 'poi:something'. =item B<-t>|B<--time>|B<--depart> I:I Journey start time =item B<-a>|B<--arrive> I:I Journey end time (overrides --time/--depart) =item B<-d>|B<--date> I
.I.[I] Journey date =item B<-b>|B<--bike> Choose connections allowing to carry a bike =item B<-e>|B<--exclude> I Exclude I (comma separated list). Possible transports: zug, s-bahn, u-bahn, stadtbahn, tram, stadtbus, regionalbus, schnellbus, seilbahn, schiff, ast, sonstige =item B<-m>|B<--max-change> I Print connections with at most I interchanges =item B<-P>|B<--prefer> I Prefer connections of I: =over =item * speed (default) The faster, the better =item * nowait Prefer connections with less interchanges =item * nowalk Prefer connections with less walking (at interchanges) =back =item B<-p>|B<--proximity> Take stops close to the stop/start into account and possibly use them instead =item B<-i>|B<--include> I Include connections using trains of type I, where I may be: =over =item * local (default) only take local trains ("Verbund-/Nahverkehrslinien"). Slow, but the cheapest method if you're not travelling long distance =item * ic Local trains + IC =item * ice All trains (local + IC + ICE) =back =item B<-w>|B<--walk-speed> I Set your walking speed to I. Accepted values: normal (default), fast, slow =item B<-I>|B<--ignore-info> [ I ] Ignore additional information matching I (default: /Fahrradmitnahme/) If I is not supplied, removes the default regex (-E nothing will be ignored) =item B<--timeout> I Set timeout for HTTP requests. Default: 60 seconds. =item B<--post> I=I Add I with I to the HTTP POST request sent to the EFA server. This can be used to use setting B does not yet cover, like C<--post lineRestriction=400> to also show IC and ICE trains. Note that B<--post> will be overridden by the standard efa options, such as B<--time> =item B<-v>|B<--version> Print version information =back =head1 EXIT STATUS 0 Everything went well 1 Ambiguous input, re-run efa with different arguments 2 efa.vrr.de error (i.e. unable to find matching connections) 3 efa.vrr.de error (usually invalid input data) 255 Any other kind of error =head1 CONFIGURATION None. =head1 DEPENDENCIES This script requires perl 5.10 (or higher) with the modules WWW::Mechanize and XML::LibXML. =head1 BUGS AND LIMITATIONS B cannot handle Unicode in its arguments, so use plain ASCII. The Parser is quite new and may not yet cover all corner cases, use with caution. =head1 AUTHOR Copyright (C) 2009,2010 by Daniel Friesel Ederf@derf.homelinux.orgE =head1 LICENSE 0. You just DO WHAT THE FUCK YOU WANT TO.