Loading .gitignore +2 −0 Original line number Diff line number Diff line Loading @@ -4,3 +4,5 @@ /cover_db /MANIFEST* /MYMETA.* lib/Travel/Status/DE/HAFAS/Services.pm .gitmodules 0 → 100644 +3 −0 Original line number Diff line number Diff line [submodule "ext/transport-apis"] path = ext/transport-apis url = https://github.com/public-transport/transport-apis.git transport-apis @ 5222788b Original line number Diff line number Diff line Subproject commit 5222788b43582523ebd91390f6fb9416406fee78 lib/Travel/Status/DE/HAFAS.pm +31 −444 Original line number Diff line number Diff line Loading @@ -14,436 +14,23 @@ use Digest::MD5 qw(md5_hex); use Encode qw(decode encode); use JSON; use LWP::UserAgent; use Math::Polygon; use Travel::Status::DE::HAFAS::Journey; use Travel::Status::DE::HAFAS::Location; use Travel::Status::DE::HAFAS::Message; use Travel::Status::DE::HAFAS::Polyline qw(decode_polyline); use Travel::Status::DE::HAFAS::Product; use Travel::Status::DE::HAFAS::Services; use Travel::Status::DE::HAFAS::StopFinder; our $VERSION = '6.04'; # {{{ Endpoint Definition # Most of these have been adapted from # <https://github.com/public-transport/transport-apis> and # <https://github.com/public-transport/hafas-client/tree/main/p>. # Many thanks to Jannis R / @derhuerst and all contributors for maintaining # these resources. my %hafas_instance = ( AVV => { stopfinder => 'https://auskunft.avv.de/bin/ajax-getstop.exe', mgate => 'https://auskunft.avv.de/bin/mgate.exe', name => 'Aachener Verkehrsverbund', productbits => [ [ regio => 'region trains' ], [ ic_ec => 'long distance trains' ], [ ice => 'long distance trains' ], [ bus => 'long distance busses' ], [ s => 'sububrban trains' ], [ u => 'underground trains' ], [ tram => 'trams' ], [ bus => 'busses' ], [ bus => 'additional busses' ], [ ondemand => 'on-demand services' ], [ ferry => 'maritime transit' ] ], languages => [qw[de]], request => { client => { id => 'AVV_AACHEN', type => 'WEB', name => 'webapp', l => 'vs_avv', }, ver => '1.26', auth => { type => 'AID', aid => '4vV1AcH3' . 'N511icH', }, lang => 'deu', }, }, BART => { stopfinder => 'https://planner.bart.gov/bin/ajax-getstop.exe', mgate => 'https://planner.bart.gov/bin/mgate.exe', name => 'Bay Area Rapid Transit', time_zone => 'America/Los_Angeles', productbits => [ [ _ => undef ], [ _ => undef ], [ cc => 'cable cars' ], [ regio => 'regional trains' ], [ _ => undef ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ bart => 'BART trains' ], [ tram => 'trams' ], ], languages => [qw[en]], request => { client => { id => 'BART', type => 'WEB', name => 'webapp', }, ver => '1.40', auth => { type => 'AID', aid => 'kEwHkFUC' . 'IL500dym', }, lang => 'en', }, }, BLS => { mgate => 'https://bls.hafas.de/bin/mgate.exe', stopfinder => 'https://bls.hafas.de/bin/ajax-stopfinder.exe', name => 'BLS AG', time_zone => 'Europe/Zurich', productbits => [ [ ice => 'long distance trains' ], [ ic_ec => 'long distance trains' ], [ ir => 'inter-regio trains' ], [ regio => 'regional trains' ], [ ferry => 'maritime transit' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ fun => 'funicular / gondola' ], [ _ => undef ], [ tram => 'trams' ], [ _ => undef ], [ _ => undef ], [ car => 'Autoverlad' ] ], languages => [qw[de fr it en]], request => { client => { id => 'HAFAS', type => 'WEB', name => 'webapp', }, ver => '1.46', auth => { type => 'AID', aid => '3jkAncud78HSo' . 'qclmN54812A', }, lang => 'deu', }, }, BVG => { stopfinder => 'https://bvg-apps-ext.hafas.de/bin/ajax-getstop.exe', mgate => 'https://bvg-apps-ext.hafas.de/bin/mgate.exe', name => 'Berliner Verkehrsbetriebe', productbits => [qw[s u tram bus]], languages => [qw[de en]], request => { client => { type => 'WEB', id => 'VBB', v => 10002, name => 'webapp', l => 'vs_webapp', }, ext => 'BVG.1', ver => '1.72', auth => { type => 'AID', aid => 'dVg4TZbW8anjx9z' . 'tPwe2uk4LVRi9wO', }, lang => 'deu', }, }, CMTA => { stopfinder => 'https://capmetro.hafas.cloud/bin/ajax-getstop.exe', mgate => 'https://capmetro.hafas.cloud/bin/mgate.exe', name => 'Capital Metropolitan Transportation Authority', time_zone => 'America/Chicago', productbits => [ [ _ => undef ], [ _ => undef ], [ _ => undef ], [ regio => 'MetroRail' ], [ _ => undef ], [ bus => 'MetroBus' ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ rapid => 'MetroRapid' ], ], languages => [qw[en]], request => { client => { id => 'CMTA', type => 'IPH', name => 'CapMetro', v => 2, }, ver => '1.40', auth => { type => 'AID', aid => 'ioslaskd' . 'cndrjcmlsd', }, lang => 'en', }, }, DB => { stopfinder => 'https://reiseauskunft.bahn.de/bin/ajax-getstop.exe', mgate => 'https://reiseauskunft.bahn.de/bin/mgate.exe', name => 'Deutsche Bahn', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], productgroups => [ [qw[ice ic_ec d]], [qw[regio s]], [qw[bus ferry u tram ondemand]] ], salt => 'bdI8UVj4' . '0K5fvxwf', languages => [qw[de en fr es]], request => { client => { id => 'DB', v => '20100000', type => 'IPH', name => 'DB Navigator', }, ext => 'DB.R21.12.a', ver => '1.15', auth => { type => 'AID', aid => 'n91dB8Z77' . 'MLdoR0K' }, }, }, IE => { stopfinder => 'https://journeyplanner.irishrail.ie/bin/ajax-getstop.exe', mgate => 'https://journeyplanner.irishrail.ie/bin/mgate.exe', name => 'Iarnród Éireann', time_zone => 'Europe/Dublin', productbits => [ [ _ => undef ], [ ic => 'national trains' ], [ _ => undef ], [ regio => 'regional trains' ], [ dart => 'DART trains' ], [ _ => undef ], [ luas => 'LUAS trams' ], ], languages => [qw[en ga]], request => { client => { id => 'IRISHRAIL', type => 'IPA', name => 'IrishRailPROD-APPSTORE', v => '4000100', os => 'iOS 12.4.8', }, ver => '1.33', auth => { type => 'AID', aid => 'P9bplgVCG' . 'nozdgQE', }, lang => 'en', }, salt => 'i5s7m3q9' . 'z6b4k1c2', micmac => 1, }, KVB => { mgate => 'https://auskunft.kvb.koeln/gate', name => 'Kölner Verkehrs-Betriebe', productbits => [ [ s => 'sub-urban trains' ], [ tram => 'trams' ], [ _ => undef ], [ bus => 'buses' ], [ regio => 'regional trains' ], [ ic => 'national trains' ], [ _ => undef ], [ _ => undef ], [ ondemand => 'taxi buses' ] ], request => { client => { id => 'HAFAS', type => 'WEB', name => 'webapp', l => 'vs_webapp', v => '154', }, ver => '1.58', auth => { type => 'AID', aid => 'Rt6foY5' . 'zcTTRXMQs', }, lang => 'deu', }, }, NAHSH => { mgate => 'https://nah.sh.hafas.de/bin/mgate.exe', stopfinder => 'https://nah.sh.hafas.de/bin/ajax-getstop.exe', name => 'Nahverkehrsverbund Schleswig-Holstein', productbits => [qw[ice ice ice regio s bus ferry u tram ondemand]], request => { client => { id => 'NAHSH', v => '3000700', type => 'IPH', name => 'NAHSHPROD', }, ver => '1.16', auth => { type => 'AID', aid => 'r0Ot9FLF' . 'NAFxijLW' }, }, }, NASA => { mgate => 'https://reiseauskunft.insa.de/bin/mgate.exe', stopfinder => 'https://reiseauskunft.insa.de/bin/ajax-getstop.exe', name => 'Nahverkehrsservice Sachsen-Anhalt', productbits => [qw[ice ice regio regio regio tram bus ondemand]], languages => [qw[de en]], request => { client => { id => 'NASA', v => '4000200', type => 'IPH', name => 'nasaPROD', os => 'iPhone OS 13.1.2', }, ver => '1.18', auth => { type => 'AID', aid => 'nasa-' . 'apps', }, lang => 'deu', }, }, NVV => { mgate => 'https://auskunft.nvv.de/auskunft/bin/app/mgate.exe', stopfinder => 'https://auskunft.nvv.de/auskunft/bin/jp/ajax-getstop.exe', name => 'Nordhessischer VerkehrsVerbund', productbits => [qw[ice ic_ec regio s u tram bus bus ferry ondemand regio regio]], request => { client => { id => 'NVV', v => '5000300', type => 'IPH', name => 'NVVMobilPROD_APPSTORE', os => 'iOS 13.1.2', }, ext => 'NVV.6.0', ver => '1.18', auth => { type => 'AID', aid => 'Kt8eNOH7' . 'qjVeSxNA', }, lang => 'deu', }, }, 'ÖBB' => { mgate => 'https://fahrplan.oebb.at/bin/mgate.exe', stopfinder => 'https://fahrplan.oebb.at/bin/ajax-getstop.exe', name => 'Österreichische Bundesbahnen', time_zone => 'Europe/Vienna', productbits => [ [ ice_rj => 'long distance trains' ], [ sev => 'rail replacement service' ], [ ic_ec => 'long distance trains' ], [ d_n => 'night trains and rapid trains' ], [ regio => 'regional trains' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ u => 'underground' ], [ tram => 'trams' ], [ other => 'other transit services' ] ], productgroups => [ qw[ice_rj ic_ec d_n], qw[regio s sev], qw[bus ferry u tram other] ], request => { client => { id => 'OEBB', v => '6030600', type => 'IPH', name => 'oebbPROD-ADHOC', }, ver => '1.57', auth => { type => 'AID', aid => 'OWDL4fE4' . 'ixNiPBBm', }, lang => 'deu', }, }, VBB => { mgate => 'https://fahrinfo.vbb.de/bin/mgate.exe', stopfinder => 'https://fahrinfo.vbb.de/bin/ajax-getstop.exe', name => 'Verkehrsverbund Berlin-Brandenburg', productbits => [qw[s u tram bus ferry ice regio]], languages => [qw[de en]], request => { client => { id => 'VBB', type => 'WEB', name => 'VBB WebApp', l => 'vs_webapp_vbb', }, ext => 'VBB.1', ver => '1.33', auth => { type => 'AID', aid => 'hafas-vb' . 'b-webapp', }, lang => 'deu', }, }, VBN => { mgate => 'https://fahrplaner.vbn.de/bin/mgate.exe', stopfinder => 'https://fahrplaner.vbn.de/hafas/ajax-getstop.exe', name => 'Verkehrsverbund Bremen/Niedersachsen', productbits => [qw[ice ice regio regio s bus ferry u tram ondemand]], salt => 'SP31mBu' . 'fSyCLmNxp', micmac => 1, languages => [qw[de en]], request => { client => { id => 'VBN', v => '6000000', type => 'IPH', name => 'vbn', }, ver => '1.42', auth => { type => 'AID', aid => 'kaoxIXLn' . '03zCr2KR', }, lang => 'deu', }, }, VOS => { stopfinder => 'https://fahrplan.vos.info/bin/ajax-getstop.exe', mgate => 'https://fahrplan.vos.info/bin/mgate.exe', name => 'Verkehrsgemeinschaft Osnabrück', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], languages => [qw[de]], request => { client => { id => 'SWO', type => 'WEB', name => 'webapp', l => 'vs_swo', }, ver => '1.72', auth => { type => 'AID', aid => 'PnYowCQ' . 'P7Tp1V' }, lang => 'deu', }, }, ); # Data sources: <https://github.com/public-transport/transport-apis> and # <https://github.com/public-transport/hafas-client/tree/main/p>. Thanks to # Jannis R / @derhuerst and all contributors for maintaining these. my $hafas_instance = Travel::Status::DE::HAFAS::Services::get_service_ref(); # }}} # {{{ Constructors Loading Loading @@ -477,11 +64,11 @@ sub new { $service = $conf{service} = 'DB'; } if ( defined $service and not exists $hafas_instance{$service} ) { if ( defined $service and not exists $hafas_instance->{$service} ) { confess("The service '$service' is not supported"); } my $now = DateTime->now( time_zone => $hafas_instance{$service}{time_zone} my $now = DateTime->now( time_zone => $hafas_instance->{$service}{time_zone} // 'Europe/Berlin' ); my $self = { active_service => $service, Loading Loading @@ -514,7 +101,7 @@ sub new { }, } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } elsif ( $conf{journeyMatch} ) { Loading @@ -535,7 +122,7 @@ sub new { }, } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } elsif ( $conf{geoSearch} ) { Loading Loading @@ -566,7 +153,7 @@ sub new { } } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } elsif ( $conf{locationSearch} ) { Loading @@ -587,7 +174,7 @@ sub new { } } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } else { Loading Loading @@ -627,7 +214,7 @@ sub new { }, }, ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } Loading @@ -637,7 +224,7 @@ sub new { $self->{strptime_obj} //= DateTime::Format::Strptime->new( pattern => '%Y%m%dT%H%M%S', time_zone => $hafas_instance{$service}{time_zone} // 'Europe/Berlin', time_zone => $hafas_instance->{$service}{time_zone} // 'Europe/Berlin', ); my $json = $self->{json} = JSON->new->utf8; Loading @@ -651,10 +238,10 @@ sub new { $req = $json->encode($req); $self->{post} = $req; my $url = $conf{url} // $hafas_instance{$service}{mgate}; my $url = $conf{url} // $hafas_instance->{$service}{mgate}; if ( my $salt = $hafas_instance{$service}{salt} ) { if ( $hafas_instance{$service}{micmac} ) { if ( my $salt = $hafas_instance->{$service}{salt} ) { if ( $hafas_instance->{$service}{micmac} ) { my $mic = md5_hex( $self->{post} ); my $mac = md5_hex( $mic . $salt ); $url .= "?mic=$mic&mac=$mac"; Loading Loading @@ -772,15 +359,15 @@ sub mot_mask { my ($self) = @_; my $service = $self->{active_service}; my $mot_mask = 2**@{ $hafas_instance{$service}{productbits} } - 1; my $mot_mask = 2**@{ $hafas_instance->{$service}{productbits} } - 1; my %mot_pos; for my $i ( 0 .. $#{ $hafas_instance{$service}{productbits} } ) { if ( ref( $hafas_instance{$service}{productbits}[$i] ) eq 'ARRAY' ) { $mot_pos{ $hafas_instance{$service}{productbits}[$i][0] } = $i; for my $i ( 0 .. $#{ $hafas_instance->{$service}{productbits} } ) { if ( ref( $hafas_instance->{$service}{productbits}[$i] ) eq 'ARRAY' ) { $mot_pos{ $hafas_instance->{$service}{productbits}[$i][0] } = $i; } else { $mot_pos{ $hafas_instance{$service}{productbits}[$i] } = $i; $mot_pos{ $hafas_instance->{$service}{productbits}[$i] } = $i; } } Loading Loading @@ -1129,10 +716,10 @@ sub similar_stops { my $service = $self->{active_service}; if ( $service and exists $hafas_instance{$service}{stopfinder} ) { if ( $service and exists $hafas_instance->{$service}{stopfinder} ) { my $sf = Travel::Status::DE::HAFAS::StopFinder->new( url => $hafas_instance{$service}{stopfinder}, url => $hafas_instance->{$service}{stopfinder}, input => $self->{station}, ua => $self->{ua}, developer_mode => $self->{developer_mode}, Loading @@ -1151,11 +738,11 @@ sub similar_stops_p { my $service = $self->{active_service}; if ( $service and exists $hafas_instance{$service}{stopfinder} ) { if ( $service and exists $hafas_instance->{$service}{stopfinder} ) { $opt{user_agent} //= $self->{ua}; $opt{promise} //= $self->{promise}; return Travel::Status::DE::HAFAS::StopFinder->new_p( url => $hafas_instance{$service}{stopfinder}, url => $hafas_instance->{$service}{stopfinder}, input => $self->{station}, user_agent => $opt{user_agent}, developer_mode => $self->{developer_mode}, Loading Loading @@ -1237,8 +824,8 @@ sub result { # static sub get_services { my @services; for my $service ( sort keys %hafas_instance ) { my %desc = %{ $hafas_instance{$service} }; for my $service ( sort keys %{$hafas_instance} ) { my %desc = %{ $hafas_instance->{$service} }; $desc{shortname} = $service; push( @services, \%desc ); } Loading @@ -1249,8 +836,8 @@ sub get_services { sub get_service { my ($service) = @_; if ( defined $service and exists $hafas_instance{$service} ) { return $hafas_instance{$service}; if ( defined $service and exists $hafas_instance->{$service} ) { return $hafas_instance->{$service}; } return; } Loading @@ -1259,7 +846,7 @@ sub get_active_service { my ($self) = @_; if ( defined $self->{active_service} ) { return $hafas_instance{ $self->{active_service} }; return $hafas_instance->{ $self->{active_service} }; } return; } Loading lib/Travel/Status/DE/HAFAS/Services.pm.PL 0 → 100644 +317 −0 Original line number Diff line number Diff line #!/usr/bin/env perl use strict; use warnings; use 5.014; use utf8; use Data::Dumper; use Encode qw(encode); use File::Slurp qw(read_file write_file); use JSON; my $json = JSON->new->utf8; sub load_instance { my ( $path, %opt ) = @_; my $data = $json->decode( scalar read_file("ext/transport-apis/data/${path}-hafas-mgate.json") ); my %ret = ( name => $data->{name} =~ s{ *[(][^)]+[)]}{}r, homepage => $data->{attribution}{homepage}, mgate => $data->{options}{endpoint}, time_zone => $data->{timezone}, languages => $data->{supportedLanguages}, request => { client => $data->{options}{client}, auth => $data->{options}{auth}, }, #coverage => { # area => $data->{coverage}{realtimeCoverage}{area}, # regions => $data->{coverage}{realtimeCoverage}{region}, #} ); if ( $opt{lang} ) { $ret{request}{lang} = $opt{lang}; } if ( $opt{ver} ) { $ret{request}{ver} = $opt{ver}; } if ( $data->{options}{ext} ) { $ret{request}{ext} = $data->{options}{ext}; } if ( $data->{options}{ver} ) { $ret{request}{ver} = $data->{options}{ver}; } return %ret; } my %hafas_instance = ( AVV => { load_instance( 'de/avv', lang => 'deu', ver => '1.26' ), stopfinder => 'https://auskunft.avv.de/bin/ajax-getstop.exe', productbits => [ [ regio => 'regional trains' ], [ ic_ec => 'long distance trains' ], [ ice => 'long distance trains' ], [ bus => 'long distance busses' ], [ s => 'sububrban trains' ], [ u => 'underground trains' ], [ tram => 'trams' ], [ bus => 'busses' ], [ bus => 'additional busses' ], [ ondemand => 'on-demand services' ], [ ferry => 'maritime transit' ] ], }, BART => { load_instance( 'us/bart', lang => 'en', ver => '1.40' ), stopfinder => 'https://planner.bart.gov/bin/ajax-getstop.exe', productbits => [ [ _ => undef ], [ _ => undef ], [ cc => 'cable cars' ], [ regio => 'regional trains' ], [ _ => undef ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ bart => 'BART trains' ], [ tram => 'trams' ], ], }, BLS => { load_instance( 'ch/bls', lang => 'deu', ver => '1.46' ), languages => [qw[de fr it en]], stopfinder => 'https://bls.hafas.de/bin/ajax-stopfinder.exe', productbits => [ [ ice => 'long distance trains' ], [ ic_ec => 'long distance trains' ], [ ir => 'inter-regio trains' ], [ regio => 'regional trains' ], [ ferry => 'maritime transit' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ fun => 'funicular / gondola' ], [ _ => undef ], [ tram => 'trams' ], [ _ => undef ], [ _ => undef ], [ car => 'Autoverlad' ] ], }, BVG => { load_instance( 'de/bvg', lang => 'deu' ), stopfinder => 'https://bvg-apps-ext.hafas.de/bin/ajax-getstop.exe', productbits => [qw[s u tram bus]], }, CMTA => { load_instance( 'us/cmta', lang => 'en', ver => '1.40' ), stopfinder => 'https://capmetro.hafas.cloud/bin/ajax-getstop.exe', productbits => [ [ _ => undef ], [ _ => undef ], [ _ => undef ], [ regio => 'MetroRail' ], [ _ => undef ], [ bus => 'MetroBus' ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ rapid => 'MetroRapid' ], ], }, DB => { load_instance('de/db'), stopfinder => 'https://reiseauskunft.bahn.de/bin/ajax-getstop.exe', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], productgroups => [ [qw[ice ic_ec d]], [qw[regio s]], [qw[bus ferry u tram ondemand]] ], salt => 'bdI8UVj40K5fvxwf', languages => [qw[de en fr es]], request => { client => { id => 'DB', v => '20100000', type => 'IPH', name => 'DB Navigator', }, ext => 'DB.R21.12.a', ver => '1.18', auth => { type => 'AID', aid => 'n91dB8Z77MLdoR0K' }, lang => 'deu' }, }, IE => { load_instance( 'ie/iarnrod-eireann', lang => 'en', ver => '1.33' ), stopfinder => 'https://journeyplanner.irishrail.ie/bin/ajax-getstop.exe', productbits => [ [ _ => undef ], [ ic => 'national trains' ], [ _ => undef ], [ regio => 'regional trains' ], [ dart => 'DART trains' ], [ _ => undef ], [ luas => 'LUAS trams' ], ], salt => 'i5s7m3q9z6b4k1c2', micmac => 1, }, KVB => { load_instance( 'de/kvb', lang => 'deu' ), productbits => [ [ s => 'sub-urban trains' ], [ tram => 'trams' ], [ _ => undef ], [ bus => 'buses' ], [ regio => 'regional trains' ], [ ic => 'national trains' ], [ _ => undef ], [ _ => undef ], [ ondemand => 'taxi buses' ] ], }, NAHSH => { load_instance('de/nahsh'), stopfinder => 'https://nah.sh.hafas.de/bin/ajax-getstop.exe', productbits => [qw[ice ice ice regio s bus ferry u tram ondemand]], }, NASA => { load_instance( 'de/nasa', lang => 'deu' ), stopfinder => 'https://reiseauskunft.insa.de/bin/ajax-getstop.exe', productbits => [qw[ice ice regio regio regio tram bus ondemand]], }, NVV => { load_instance( 'de/nvv', lang => 'deu' ), stopfinder => 'https://auskunft.nvv.de/auskunft/bin/jp/ajax-getstop.exe', productbits => [qw[ice ic_ec regio s u tram bus bus ferry ondemand regio regio]], }, 'ÖBB' => { load_instance( 'at/oebb', lang => 'deu', ver => '1.57' ), stopfinder => 'https://fahrplan.oebb.at/bin/ajax-getstop.exe', productbits => [ [ ice_rj => 'long distance trains' ], [ sev => 'rail replacement service' ], [ ic_ec => 'long distance trains' ], [ d_n => 'night trains and rapid trains' ], [ regio => 'regional trains' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ u => 'underground' ], [ tram => 'trams' ], [ other => 'other transit services' ] ], productgroups => [ qw[ice_rj ic_ec d_n], qw[regio s sev], qw[bus ferry u tram other] ], }, VBB => { load_instance( 'de/vbb', lang => 'deu' ), stopfinder => 'https://fahrinfo.vbb.de/bin/ajax-getstop.exe', productbits => [qw[s u tram bus ferry ice regio]], }, VBN => { load_instance( 'de/vbn', lang => 'deu', ver => '1.42' ), stopfinder => 'https://fahrplaner.vbn.de/hafas/ajax-getstop.exe', productbits => [qw[ice ice regio regio s bus ferry u tram ondemand]], salt => 'SP31mBufSyCLmNxp', micmac => 1, }, VOS => { load_instance( 'de/vos', lang => 'deu', ver => '1.72' ), stopfinder => 'https://fahrplan.vos.info/bin/ajax-getstop.exe', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], }, ); my $perlobj = Data::Dumper->new( [ \%hafas_instance ], ['hafas_instance'] ); my $buf = <<'__EOF__'; package Travel::Status::DE::HAFAS::Services; # vim:readonly # This module has been automatically generated # by lib/Travel/Status/DE/HAFAS/Services.pm.PL. # Do not edit, changes will be lost. use strict; use warnings; use 5.014; use utf8; our $VERSION = '6.04'; # Most of these have been adapted from # <https://github.com/public-transport/transport-apis> and # <https://github.com/public-transport/hafas-client/tree/main/p>. # Many thanks to Jannis R / @derhuerst and all contributors for maintaining # these resources. __EOF__ $buf .= 'my ' . $perlobj->Sortkeys(1)->Indent(1)->Dump; $buf =~ s{\Q\x{d6}\E}{Ö}g; $buf =~ s{\Q\x{c9}\E}{É}g; $buf =~ s{\Q\x{f3}\E}{ó}g; $buf =~ s{\Q\x{f6}\E}{ö}g; $buf =~ s{\Q\x{fc}\E}{ü}g; $buf .= <<'__EOF__'; sub get_service_ref { return $hafas_instance; } sub get_service_map { return %{$hafas_instance}; } 1; __EOF__ write_file( $ARGV[0], { binmode => ':utf8' }, $buf ); Loading
.gitignore +2 −0 Original line number Diff line number Diff line Loading @@ -4,3 +4,5 @@ /cover_db /MANIFEST* /MYMETA.* lib/Travel/Status/DE/HAFAS/Services.pm
.gitmodules 0 → 100644 +3 −0 Original line number Diff line number Diff line [submodule "ext/transport-apis"] path = ext/transport-apis url = https://github.com/public-transport/transport-apis.git
transport-apis @ 5222788b Original line number Diff line number Diff line Subproject commit 5222788b43582523ebd91390f6fb9416406fee78
lib/Travel/Status/DE/HAFAS.pm +31 −444 Original line number Diff line number Diff line Loading @@ -14,436 +14,23 @@ use Digest::MD5 qw(md5_hex); use Encode qw(decode encode); use JSON; use LWP::UserAgent; use Math::Polygon; use Travel::Status::DE::HAFAS::Journey; use Travel::Status::DE::HAFAS::Location; use Travel::Status::DE::HAFAS::Message; use Travel::Status::DE::HAFAS::Polyline qw(decode_polyline); use Travel::Status::DE::HAFAS::Product; use Travel::Status::DE::HAFAS::Services; use Travel::Status::DE::HAFAS::StopFinder; our $VERSION = '6.04'; # {{{ Endpoint Definition # Most of these have been adapted from # <https://github.com/public-transport/transport-apis> and # <https://github.com/public-transport/hafas-client/tree/main/p>. # Many thanks to Jannis R / @derhuerst and all contributors for maintaining # these resources. my %hafas_instance = ( AVV => { stopfinder => 'https://auskunft.avv.de/bin/ajax-getstop.exe', mgate => 'https://auskunft.avv.de/bin/mgate.exe', name => 'Aachener Verkehrsverbund', productbits => [ [ regio => 'region trains' ], [ ic_ec => 'long distance trains' ], [ ice => 'long distance trains' ], [ bus => 'long distance busses' ], [ s => 'sububrban trains' ], [ u => 'underground trains' ], [ tram => 'trams' ], [ bus => 'busses' ], [ bus => 'additional busses' ], [ ondemand => 'on-demand services' ], [ ferry => 'maritime transit' ] ], languages => [qw[de]], request => { client => { id => 'AVV_AACHEN', type => 'WEB', name => 'webapp', l => 'vs_avv', }, ver => '1.26', auth => { type => 'AID', aid => '4vV1AcH3' . 'N511icH', }, lang => 'deu', }, }, BART => { stopfinder => 'https://planner.bart.gov/bin/ajax-getstop.exe', mgate => 'https://planner.bart.gov/bin/mgate.exe', name => 'Bay Area Rapid Transit', time_zone => 'America/Los_Angeles', productbits => [ [ _ => undef ], [ _ => undef ], [ cc => 'cable cars' ], [ regio => 'regional trains' ], [ _ => undef ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ bart => 'BART trains' ], [ tram => 'trams' ], ], languages => [qw[en]], request => { client => { id => 'BART', type => 'WEB', name => 'webapp', }, ver => '1.40', auth => { type => 'AID', aid => 'kEwHkFUC' . 'IL500dym', }, lang => 'en', }, }, BLS => { mgate => 'https://bls.hafas.de/bin/mgate.exe', stopfinder => 'https://bls.hafas.de/bin/ajax-stopfinder.exe', name => 'BLS AG', time_zone => 'Europe/Zurich', productbits => [ [ ice => 'long distance trains' ], [ ic_ec => 'long distance trains' ], [ ir => 'inter-regio trains' ], [ regio => 'regional trains' ], [ ferry => 'maritime transit' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ fun => 'funicular / gondola' ], [ _ => undef ], [ tram => 'trams' ], [ _ => undef ], [ _ => undef ], [ car => 'Autoverlad' ] ], languages => [qw[de fr it en]], request => { client => { id => 'HAFAS', type => 'WEB', name => 'webapp', }, ver => '1.46', auth => { type => 'AID', aid => '3jkAncud78HSo' . 'qclmN54812A', }, lang => 'deu', }, }, BVG => { stopfinder => 'https://bvg-apps-ext.hafas.de/bin/ajax-getstop.exe', mgate => 'https://bvg-apps-ext.hafas.de/bin/mgate.exe', name => 'Berliner Verkehrsbetriebe', productbits => [qw[s u tram bus]], languages => [qw[de en]], request => { client => { type => 'WEB', id => 'VBB', v => 10002, name => 'webapp', l => 'vs_webapp', }, ext => 'BVG.1', ver => '1.72', auth => { type => 'AID', aid => 'dVg4TZbW8anjx9z' . 'tPwe2uk4LVRi9wO', }, lang => 'deu', }, }, CMTA => { stopfinder => 'https://capmetro.hafas.cloud/bin/ajax-getstop.exe', mgate => 'https://capmetro.hafas.cloud/bin/mgate.exe', name => 'Capital Metropolitan Transportation Authority', time_zone => 'America/Chicago', productbits => [ [ _ => undef ], [ _ => undef ], [ _ => undef ], [ regio => 'MetroRail' ], [ _ => undef ], [ bus => 'MetroBus' ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ rapid => 'MetroRapid' ], ], languages => [qw[en]], request => { client => { id => 'CMTA', type => 'IPH', name => 'CapMetro', v => 2, }, ver => '1.40', auth => { type => 'AID', aid => 'ioslaskd' . 'cndrjcmlsd', }, lang => 'en', }, }, DB => { stopfinder => 'https://reiseauskunft.bahn.de/bin/ajax-getstop.exe', mgate => 'https://reiseauskunft.bahn.de/bin/mgate.exe', name => 'Deutsche Bahn', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], productgroups => [ [qw[ice ic_ec d]], [qw[regio s]], [qw[bus ferry u tram ondemand]] ], salt => 'bdI8UVj4' . '0K5fvxwf', languages => [qw[de en fr es]], request => { client => { id => 'DB', v => '20100000', type => 'IPH', name => 'DB Navigator', }, ext => 'DB.R21.12.a', ver => '1.15', auth => { type => 'AID', aid => 'n91dB8Z77' . 'MLdoR0K' }, }, }, IE => { stopfinder => 'https://journeyplanner.irishrail.ie/bin/ajax-getstop.exe', mgate => 'https://journeyplanner.irishrail.ie/bin/mgate.exe', name => 'Iarnród Éireann', time_zone => 'Europe/Dublin', productbits => [ [ _ => undef ], [ ic => 'national trains' ], [ _ => undef ], [ regio => 'regional trains' ], [ dart => 'DART trains' ], [ _ => undef ], [ luas => 'LUAS trams' ], ], languages => [qw[en ga]], request => { client => { id => 'IRISHRAIL', type => 'IPA', name => 'IrishRailPROD-APPSTORE', v => '4000100', os => 'iOS 12.4.8', }, ver => '1.33', auth => { type => 'AID', aid => 'P9bplgVCG' . 'nozdgQE', }, lang => 'en', }, salt => 'i5s7m3q9' . 'z6b4k1c2', micmac => 1, }, KVB => { mgate => 'https://auskunft.kvb.koeln/gate', name => 'Kölner Verkehrs-Betriebe', productbits => [ [ s => 'sub-urban trains' ], [ tram => 'trams' ], [ _ => undef ], [ bus => 'buses' ], [ regio => 'regional trains' ], [ ic => 'national trains' ], [ _ => undef ], [ _ => undef ], [ ondemand => 'taxi buses' ] ], request => { client => { id => 'HAFAS', type => 'WEB', name => 'webapp', l => 'vs_webapp', v => '154', }, ver => '1.58', auth => { type => 'AID', aid => 'Rt6foY5' . 'zcTTRXMQs', }, lang => 'deu', }, }, NAHSH => { mgate => 'https://nah.sh.hafas.de/bin/mgate.exe', stopfinder => 'https://nah.sh.hafas.de/bin/ajax-getstop.exe', name => 'Nahverkehrsverbund Schleswig-Holstein', productbits => [qw[ice ice ice regio s bus ferry u tram ondemand]], request => { client => { id => 'NAHSH', v => '3000700', type => 'IPH', name => 'NAHSHPROD', }, ver => '1.16', auth => { type => 'AID', aid => 'r0Ot9FLF' . 'NAFxijLW' }, }, }, NASA => { mgate => 'https://reiseauskunft.insa.de/bin/mgate.exe', stopfinder => 'https://reiseauskunft.insa.de/bin/ajax-getstop.exe', name => 'Nahverkehrsservice Sachsen-Anhalt', productbits => [qw[ice ice regio regio regio tram bus ondemand]], languages => [qw[de en]], request => { client => { id => 'NASA', v => '4000200', type => 'IPH', name => 'nasaPROD', os => 'iPhone OS 13.1.2', }, ver => '1.18', auth => { type => 'AID', aid => 'nasa-' . 'apps', }, lang => 'deu', }, }, NVV => { mgate => 'https://auskunft.nvv.de/auskunft/bin/app/mgate.exe', stopfinder => 'https://auskunft.nvv.de/auskunft/bin/jp/ajax-getstop.exe', name => 'Nordhessischer VerkehrsVerbund', productbits => [qw[ice ic_ec regio s u tram bus bus ferry ondemand regio regio]], request => { client => { id => 'NVV', v => '5000300', type => 'IPH', name => 'NVVMobilPROD_APPSTORE', os => 'iOS 13.1.2', }, ext => 'NVV.6.0', ver => '1.18', auth => { type => 'AID', aid => 'Kt8eNOH7' . 'qjVeSxNA', }, lang => 'deu', }, }, 'ÖBB' => { mgate => 'https://fahrplan.oebb.at/bin/mgate.exe', stopfinder => 'https://fahrplan.oebb.at/bin/ajax-getstop.exe', name => 'Österreichische Bundesbahnen', time_zone => 'Europe/Vienna', productbits => [ [ ice_rj => 'long distance trains' ], [ sev => 'rail replacement service' ], [ ic_ec => 'long distance trains' ], [ d_n => 'night trains and rapid trains' ], [ regio => 'regional trains' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ u => 'underground' ], [ tram => 'trams' ], [ other => 'other transit services' ] ], productgroups => [ qw[ice_rj ic_ec d_n], qw[regio s sev], qw[bus ferry u tram other] ], request => { client => { id => 'OEBB', v => '6030600', type => 'IPH', name => 'oebbPROD-ADHOC', }, ver => '1.57', auth => { type => 'AID', aid => 'OWDL4fE4' . 'ixNiPBBm', }, lang => 'deu', }, }, VBB => { mgate => 'https://fahrinfo.vbb.de/bin/mgate.exe', stopfinder => 'https://fahrinfo.vbb.de/bin/ajax-getstop.exe', name => 'Verkehrsverbund Berlin-Brandenburg', productbits => [qw[s u tram bus ferry ice regio]], languages => [qw[de en]], request => { client => { id => 'VBB', type => 'WEB', name => 'VBB WebApp', l => 'vs_webapp_vbb', }, ext => 'VBB.1', ver => '1.33', auth => { type => 'AID', aid => 'hafas-vb' . 'b-webapp', }, lang => 'deu', }, }, VBN => { mgate => 'https://fahrplaner.vbn.de/bin/mgate.exe', stopfinder => 'https://fahrplaner.vbn.de/hafas/ajax-getstop.exe', name => 'Verkehrsverbund Bremen/Niedersachsen', productbits => [qw[ice ice regio regio s bus ferry u tram ondemand]], salt => 'SP31mBu' . 'fSyCLmNxp', micmac => 1, languages => [qw[de en]], request => { client => { id => 'VBN', v => '6000000', type => 'IPH', name => 'vbn', }, ver => '1.42', auth => { type => 'AID', aid => 'kaoxIXLn' . '03zCr2KR', }, lang => 'deu', }, }, VOS => { stopfinder => 'https://fahrplan.vos.info/bin/ajax-getstop.exe', mgate => 'https://fahrplan.vos.info/bin/mgate.exe', name => 'Verkehrsgemeinschaft Osnabrück', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], languages => [qw[de]], request => { client => { id => 'SWO', type => 'WEB', name => 'webapp', l => 'vs_swo', }, ver => '1.72', auth => { type => 'AID', aid => 'PnYowCQ' . 'P7Tp1V' }, lang => 'deu', }, }, ); # Data sources: <https://github.com/public-transport/transport-apis> and # <https://github.com/public-transport/hafas-client/tree/main/p>. Thanks to # Jannis R / @derhuerst and all contributors for maintaining these. my $hafas_instance = Travel::Status::DE::HAFAS::Services::get_service_ref(); # }}} # {{{ Constructors Loading Loading @@ -477,11 +64,11 @@ sub new { $service = $conf{service} = 'DB'; } if ( defined $service and not exists $hafas_instance{$service} ) { if ( defined $service and not exists $hafas_instance->{$service} ) { confess("The service '$service' is not supported"); } my $now = DateTime->now( time_zone => $hafas_instance{$service}{time_zone} my $now = DateTime->now( time_zone => $hafas_instance->{$service}{time_zone} // 'Europe/Berlin' ); my $self = { active_service => $service, Loading Loading @@ -514,7 +101,7 @@ sub new { }, } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } elsif ( $conf{journeyMatch} ) { Loading @@ -535,7 +122,7 @@ sub new { }, } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } elsif ( $conf{geoSearch} ) { Loading Loading @@ -566,7 +153,7 @@ sub new { } } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } elsif ( $conf{locationSearch} ) { Loading @@ -587,7 +174,7 @@ sub new { } } ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } else { Loading Loading @@ -627,7 +214,7 @@ sub new { }, }, ], %{ $hafas_instance{$service}{request} } %{ $hafas_instance->{$service}{request} } }; } Loading @@ -637,7 +224,7 @@ sub new { $self->{strptime_obj} //= DateTime::Format::Strptime->new( pattern => '%Y%m%dT%H%M%S', time_zone => $hafas_instance{$service}{time_zone} // 'Europe/Berlin', time_zone => $hafas_instance->{$service}{time_zone} // 'Europe/Berlin', ); my $json = $self->{json} = JSON->new->utf8; Loading @@ -651,10 +238,10 @@ sub new { $req = $json->encode($req); $self->{post} = $req; my $url = $conf{url} // $hafas_instance{$service}{mgate}; my $url = $conf{url} // $hafas_instance->{$service}{mgate}; if ( my $salt = $hafas_instance{$service}{salt} ) { if ( $hafas_instance{$service}{micmac} ) { if ( my $salt = $hafas_instance->{$service}{salt} ) { if ( $hafas_instance->{$service}{micmac} ) { my $mic = md5_hex( $self->{post} ); my $mac = md5_hex( $mic . $salt ); $url .= "?mic=$mic&mac=$mac"; Loading Loading @@ -772,15 +359,15 @@ sub mot_mask { my ($self) = @_; my $service = $self->{active_service}; my $mot_mask = 2**@{ $hafas_instance{$service}{productbits} } - 1; my $mot_mask = 2**@{ $hafas_instance->{$service}{productbits} } - 1; my %mot_pos; for my $i ( 0 .. $#{ $hafas_instance{$service}{productbits} } ) { if ( ref( $hafas_instance{$service}{productbits}[$i] ) eq 'ARRAY' ) { $mot_pos{ $hafas_instance{$service}{productbits}[$i][0] } = $i; for my $i ( 0 .. $#{ $hafas_instance->{$service}{productbits} } ) { if ( ref( $hafas_instance->{$service}{productbits}[$i] ) eq 'ARRAY' ) { $mot_pos{ $hafas_instance->{$service}{productbits}[$i][0] } = $i; } else { $mot_pos{ $hafas_instance{$service}{productbits}[$i] } = $i; $mot_pos{ $hafas_instance->{$service}{productbits}[$i] } = $i; } } Loading Loading @@ -1129,10 +716,10 @@ sub similar_stops { my $service = $self->{active_service}; if ( $service and exists $hafas_instance{$service}{stopfinder} ) { if ( $service and exists $hafas_instance->{$service}{stopfinder} ) { my $sf = Travel::Status::DE::HAFAS::StopFinder->new( url => $hafas_instance{$service}{stopfinder}, url => $hafas_instance->{$service}{stopfinder}, input => $self->{station}, ua => $self->{ua}, developer_mode => $self->{developer_mode}, Loading @@ -1151,11 +738,11 @@ sub similar_stops_p { my $service = $self->{active_service}; if ( $service and exists $hafas_instance{$service}{stopfinder} ) { if ( $service and exists $hafas_instance->{$service}{stopfinder} ) { $opt{user_agent} //= $self->{ua}; $opt{promise} //= $self->{promise}; return Travel::Status::DE::HAFAS::StopFinder->new_p( url => $hafas_instance{$service}{stopfinder}, url => $hafas_instance->{$service}{stopfinder}, input => $self->{station}, user_agent => $opt{user_agent}, developer_mode => $self->{developer_mode}, Loading Loading @@ -1237,8 +824,8 @@ sub result { # static sub get_services { my @services; for my $service ( sort keys %hafas_instance ) { my %desc = %{ $hafas_instance{$service} }; for my $service ( sort keys %{$hafas_instance} ) { my %desc = %{ $hafas_instance->{$service} }; $desc{shortname} = $service; push( @services, \%desc ); } Loading @@ -1249,8 +836,8 @@ sub get_services { sub get_service { my ($service) = @_; if ( defined $service and exists $hafas_instance{$service} ) { return $hafas_instance{$service}; if ( defined $service and exists $hafas_instance->{$service} ) { return $hafas_instance->{$service}; } return; } Loading @@ -1259,7 +846,7 @@ sub get_active_service { my ($self) = @_; if ( defined $self->{active_service} ) { return $hafas_instance{ $self->{active_service} }; return $hafas_instance->{ $self->{active_service} }; } return; } Loading
lib/Travel/Status/DE/HAFAS/Services.pm.PL 0 → 100644 +317 −0 Original line number Diff line number Diff line #!/usr/bin/env perl use strict; use warnings; use 5.014; use utf8; use Data::Dumper; use Encode qw(encode); use File::Slurp qw(read_file write_file); use JSON; my $json = JSON->new->utf8; sub load_instance { my ( $path, %opt ) = @_; my $data = $json->decode( scalar read_file("ext/transport-apis/data/${path}-hafas-mgate.json") ); my %ret = ( name => $data->{name} =~ s{ *[(][^)]+[)]}{}r, homepage => $data->{attribution}{homepage}, mgate => $data->{options}{endpoint}, time_zone => $data->{timezone}, languages => $data->{supportedLanguages}, request => { client => $data->{options}{client}, auth => $data->{options}{auth}, }, #coverage => { # area => $data->{coverage}{realtimeCoverage}{area}, # regions => $data->{coverage}{realtimeCoverage}{region}, #} ); if ( $opt{lang} ) { $ret{request}{lang} = $opt{lang}; } if ( $opt{ver} ) { $ret{request}{ver} = $opt{ver}; } if ( $data->{options}{ext} ) { $ret{request}{ext} = $data->{options}{ext}; } if ( $data->{options}{ver} ) { $ret{request}{ver} = $data->{options}{ver}; } return %ret; } my %hafas_instance = ( AVV => { load_instance( 'de/avv', lang => 'deu', ver => '1.26' ), stopfinder => 'https://auskunft.avv.de/bin/ajax-getstop.exe', productbits => [ [ regio => 'regional trains' ], [ ic_ec => 'long distance trains' ], [ ice => 'long distance trains' ], [ bus => 'long distance busses' ], [ s => 'sububrban trains' ], [ u => 'underground trains' ], [ tram => 'trams' ], [ bus => 'busses' ], [ bus => 'additional busses' ], [ ondemand => 'on-demand services' ], [ ferry => 'maritime transit' ] ], }, BART => { load_instance( 'us/bart', lang => 'en', ver => '1.40' ), stopfinder => 'https://planner.bart.gov/bin/ajax-getstop.exe', productbits => [ [ _ => undef ], [ _ => undef ], [ cc => 'cable cars' ], [ regio => 'regional trains' ], [ _ => undef ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ bart => 'BART trains' ], [ tram => 'trams' ], ], }, BLS => { load_instance( 'ch/bls', lang => 'deu', ver => '1.46' ), languages => [qw[de fr it en]], stopfinder => 'https://bls.hafas.de/bin/ajax-stopfinder.exe', productbits => [ [ ice => 'long distance trains' ], [ ic_ec => 'long distance trains' ], [ ir => 'inter-regio trains' ], [ regio => 'regional trains' ], [ ferry => 'maritime transit' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ fun => 'funicular / gondola' ], [ _ => undef ], [ tram => 'trams' ], [ _ => undef ], [ _ => undef ], [ car => 'Autoverlad' ] ], }, BVG => { load_instance( 'de/bvg', lang => 'deu' ), stopfinder => 'https://bvg-apps-ext.hafas.de/bin/ajax-getstop.exe', productbits => [qw[s u tram bus]], }, CMTA => { load_instance( 'us/cmta', lang => 'en', ver => '1.40' ), stopfinder => 'https://capmetro.hafas.cloud/bin/ajax-getstop.exe', productbits => [ [ _ => undef ], [ _ => undef ], [ _ => undef ], [ regio => 'MetroRail' ], [ _ => undef ], [ bus => 'MetroBus' ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ _ => undef ], [ rapid => 'MetroRapid' ], ], }, DB => { load_instance('de/db'), stopfinder => 'https://reiseauskunft.bahn.de/bin/ajax-getstop.exe', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], productgroups => [ [qw[ice ic_ec d]], [qw[regio s]], [qw[bus ferry u tram ondemand]] ], salt => 'bdI8UVj40K5fvxwf', languages => [qw[de en fr es]], request => { client => { id => 'DB', v => '20100000', type => 'IPH', name => 'DB Navigator', }, ext => 'DB.R21.12.a', ver => '1.18', auth => { type => 'AID', aid => 'n91dB8Z77MLdoR0K' }, lang => 'deu' }, }, IE => { load_instance( 'ie/iarnrod-eireann', lang => 'en', ver => '1.33' ), stopfinder => 'https://journeyplanner.irishrail.ie/bin/ajax-getstop.exe', productbits => [ [ _ => undef ], [ ic => 'national trains' ], [ _ => undef ], [ regio => 'regional trains' ], [ dart => 'DART trains' ], [ _ => undef ], [ luas => 'LUAS trams' ], ], salt => 'i5s7m3q9z6b4k1c2', micmac => 1, }, KVB => { load_instance( 'de/kvb', lang => 'deu' ), productbits => [ [ s => 'sub-urban trains' ], [ tram => 'trams' ], [ _ => undef ], [ bus => 'buses' ], [ regio => 'regional trains' ], [ ic => 'national trains' ], [ _ => undef ], [ _ => undef ], [ ondemand => 'taxi buses' ] ], }, NAHSH => { load_instance('de/nahsh'), stopfinder => 'https://nah.sh.hafas.de/bin/ajax-getstop.exe', productbits => [qw[ice ice ice regio s bus ferry u tram ondemand]], }, NASA => { load_instance( 'de/nasa', lang => 'deu' ), stopfinder => 'https://reiseauskunft.insa.de/bin/ajax-getstop.exe', productbits => [qw[ice ice regio regio regio tram bus ondemand]], }, NVV => { load_instance( 'de/nvv', lang => 'deu' ), stopfinder => 'https://auskunft.nvv.de/auskunft/bin/jp/ajax-getstop.exe', productbits => [qw[ice ic_ec regio s u tram bus bus ferry ondemand regio regio]], }, 'ÖBB' => { load_instance( 'at/oebb', lang => 'deu', ver => '1.57' ), stopfinder => 'https://fahrplan.oebb.at/bin/ajax-getstop.exe', productbits => [ [ ice_rj => 'long distance trains' ], [ sev => 'rail replacement service' ], [ ic_ec => 'long distance trains' ], [ d_n => 'night trains and rapid trains' ], [ regio => 'regional trains' ], [ s => 'suburban trains' ], [ bus => 'busses' ], [ ferry => 'maritime transit' ], [ u => 'underground' ], [ tram => 'trams' ], [ other => 'other transit services' ] ], productgroups => [ qw[ice_rj ic_ec d_n], qw[regio s sev], qw[bus ferry u tram other] ], }, VBB => { load_instance( 'de/vbb', lang => 'deu' ), stopfinder => 'https://fahrinfo.vbb.de/bin/ajax-getstop.exe', productbits => [qw[s u tram bus ferry ice regio]], }, VBN => { load_instance( 'de/vbn', lang => 'deu', ver => '1.42' ), stopfinder => 'https://fahrplaner.vbn.de/hafas/ajax-getstop.exe', productbits => [qw[ice ice regio regio s bus ferry u tram ondemand]], salt => 'SP31mBufSyCLmNxp', micmac => 1, }, VOS => { load_instance( 'de/vos', lang => 'deu', ver => '1.72' ), stopfinder => 'https://fahrplan.vos.info/bin/ajax-getstop.exe', productbits => [qw[ice ic_ec d regio s bus ferry u tram ondemand]], }, ); my $perlobj = Data::Dumper->new( [ \%hafas_instance ], ['hafas_instance'] ); my $buf = <<'__EOF__'; package Travel::Status::DE::HAFAS::Services; # vim:readonly # This module has been automatically generated # by lib/Travel/Status/DE/HAFAS/Services.pm.PL. # Do not edit, changes will be lost. use strict; use warnings; use 5.014; use utf8; our $VERSION = '6.04'; # Most of these have been adapted from # <https://github.com/public-transport/transport-apis> and # <https://github.com/public-transport/hafas-client/tree/main/p>. # Many thanks to Jannis R / @derhuerst and all contributors for maintaining # these resources. __EOF__ $buf .= 'my ' . $perlobj->Sortkeys(1)->Indent(1)->Dump; $buf =~ s{\Q\x{d6}\E}{Ö}g; $buf =~ s{\Q\x{c9}\E}{É}g; $buf =~ s{\Q\x{f3}\E}{ó}g; $buf =~ s{\Q\x{f6}\E}{ö}g; $buf =~ s{\Q\x{fc}\E}{ü}g; $buf .= <<'__EOF__'; sub get_service_ref { return $hafas_instance; } sub get_service_map { return %{$hafas_instance}; } 1; __EOF__ write_file( $ARGV[0], { binmode => ':utf8' }, $buf );