diff options
author | Daniel Friesel <derf@finalrewind.org> | 2020-09-30 19:12:29 +0200 |
---|---|---|
committer | Daniel Friesel <derf@finalrewind.org> | 2020-09-30 19:12:29 +0200 |
commit | 89e709d8d593939ab528b81d125fd37d303c4fa9 (patch) | |
tree | 574a0fbe6b6b0849d878c228e1f6b84e2c29d878 /lib/Travelynx/Helper/Traewelling.pm | |
parent | 952740969ca9fa74c893dfe0961d3ae55ec9e85b (diff) |
Allow linking a Träwelling account, auto-sync Träwelling→travelynx
travelynx→Träwelling is still work-in-progress
Squashed commit of the following:
commit 97faa6e2e6c8d20fba30f2d0f6e78187ceeb72e6
Author: Daniel Friesel <derf@finalrewind.org>
Date: Wed Sep 30 18:50:05 2020 +0200
improve traewelling log and tx handling
commit 487d7dd728b9d45b731bdc7098cf3358ea2e206e
Author: Daniel Friesel <derf@finalrewind.org>
Date: Wed Sep 30 18:02:41 2020 +0200
add missing traewelling template
commit 0148da2f48d9a52dcddc0ab81f83d8f8ac3062ab
Author: Daniel Friesel <derf@finalrewind.org>
Date: Wed Sep 30 18:02:35 2020 +0200
improve traewelling pull sync
commit 4861a9750f9f2d7621043361d0af6b0a8869a0df
Author: Daniel Friesel <derf@finalrewind.org>
Date: Tue Sep 29 22:14:24 2020 +0200
wip checkin from traewelling
commit f6aeb6f06998a2a7a80f63a7b1b688b1a26b66bd
Author: Daniel Friesel <derf@finalrewind.org>
Date: Tue Sep 29 18:37:53 2020 +0200
refactor traewelling integration. login and logout are less of a hack now.
checkin and checkout are not supported at the moment.
Diffstat (limited to 'lib/Travelynx/Helper/Traewelling.pm')
-rw-r--r-- | lib/Travelynx/Helper/Traewelling.pm | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/lib/Travelynx/Helper/Traewelling.pm b/lib/Travelynx/Helper/Traewelling.pm new file mode 100644 index 0000000..3c7bec2 --- /dev/null +++ b/lib/Travelynx/Helper/Traewelling.pm @@ -0,0 +1,332 @@ +package Travelynx::Helper::Traewelling; + +use strict; +use warnings; +use 5.020; + +use Mojo::Promise; + +sub new { + my ( $class, %opt ) = @_; + + my $version = $opt{version}; + + $opt{header} + = { 'User-Agent' => +"travelynx/${version} on $opt{root_url} +https://finalrewind.org/projects/travelynx" + }; + + return bless( \%opt, $class ); +} + +sub get_status_p { + my ( $self, %opt ) = @_; + + my $username = $opt{username}; + my $token = $opt{token}; + my $promise = Mojo::Promise->new; + + my $header = { + 'User-Agent' => $self->{header}{'User-Agent'}, + 'Authorization' => "Bearer $token", + }; + + $self->{user_agent}->request_timeout(20) + ->get_p( "https://traewelling.de/api/v0/user/${username}" => $header ) + ->then( + sub { + my ($tx) = @_; + if ( my $err = $tx->error ) { + my $err_msg = "HTTP $err->{code} $err->{message}"; + $promise->reject($err_msg); + return; + } + else { + if ( my $status = $tx->result->json->{statuses}{data}[0] ) { + my $strp = DateTime::Format::Strptime->new( + pattern => '%Y-%m-%dT%H:%M:%S.000000Z', + time_zone => 'UTC', + ); + my $status_id = $status->{id}; + my $message = $status->{body}; + my $checkin_at + = $strp->parse_datetime( $status->{created_at} ); + + my $dep_dt = $strp->parse_datetime( + $status->{train_checkin}{departure} ); + my $arr_dt = $strp->parse_datetime( + $status->{train_checkin}{arrival} ); + + my $dep_eva + = $status->{train_checkin}{origin}{ibnr}; + my $arr_eva + = $status->{train_checkin}{destination}{ibnr}; + + my $dep_name + = $status->{train_checkin}{origin}{name}; + my $arr_name + = $status->{train_checkin}{destination}{name}; + + my $category + = $status->{train_checkin}{hafas_trip}{category}; + my $trip_id + = $status->{train_checkin}{hafas_trip}{trip_id}; + my $linename + = $status->{train_checkin}{hafas_trip}{linename}; + my ( $train_type, $train_line ) = split( qr{ }, $linename ); + $promise->resolve( + { + status_id => $status_id, + message => $message, + checkin => $checkin_at, + dep_dt => $dep_dt, + dep_eva => $dep_eva, + dep_name => $dep_name, + arr_dt => $arr_dt, + arr_eva => $arr_eva, + arr_name => $arr_name, + trip_id => $trip_id, + train_type => $train_type, + line => $linename, + line_no => $train_line, + category => $category, + } + ); + return; + } + else { + $promise->reject("unknown error"); + return; + } + } + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + + return $promise; +} + +sub get_user_p { + my ( $self, $uid, $token ) = @_; + my $ua = $self->{user_agent}->request_timeout(20); + + my $header = { + 'User-Agent' => $self->{header}{'User-Agent'}, + 'Authorization' => "Bearer $token", + }; + my $promise = Mojo::Promise->new; + + $ua->get_p( "https://traewelling.de/api/v0/getuser" => $header )->then( + sub { + my ($tx) = @_; + if ( my $err = $tx->error ) { + my $err_msg + = "HTTP $err->{code} $err->{message} bei Abfrage der Nutzerdaten"; + $promise->reject($err_msg); + return; + } + else { + my $user_data = $tx->result->json; + $self->{model}->set_user( + uid => $uid, + trwl_id => $user_data->{id}, + screen_name => $user_data->{name}, + user_name => $user_data->{username}, + ); + $promise->resolve; + return; + } + } + )->catch( + sub { + my ($err) = @_; + $promise->reject("$err bei Abfrage der Nutzerdaten"); + return; + } + )->wait; + + return $promise; +} + +sub login_p { + my ( $self, %opt ) = @_; + + my $uid = $opt{uid}; + my $email = $opt{email}; + my $password = $opt{password}; + + my $ua = $self->{user_agent}->request_timeout(20); + + my $request = { + email => $email, + password => $password, + }; + + my $promise = Mojo::Promise->new; + my $token; + + $ua->post_p( + "https://traewelling.de/api/v0/auth/login" => $self->{header} => + json => $request )->then( + sub { + my ($tx) = @_; + if ( my $err = $tx->error ) { + my $err_msg = "HTTP $err->{code} $err->{message} bei Login"; + $promise->reject($err_msg); + return; + } + else { + $token = $tx->result->json->{token}; + $self->{model}->link( + uid => $uid, + email => $email, + token => $token + ); + return $self->get_user_p( $uid, $token ); + } + } + )->then( + sub { + $promise->resolve; + return; + } + )->catch( + sub { + my ($err) = @_; + if ($token) { + + # We have a token, but couldn't complete the login. For now, we + # solve this by logging out and invalidating the token. + $self->logout_p( + uid => $uid, + token => $token + )->finally( + sub { + $promise->reject($err); + return; + } + ); + } + else { + $promise->reject($err); + } + return; + } + )->wait; + + return $promise; +} + +sub logout_p { + my ( $self, %opt ) = @_; + + my $uid = $opt{uid}; + my $token = $opt{token}; + + my $ua = $self->{user_agent}->request_timeout(20); + + my $header = { + 'User-Agent' => $self->{header}{'User-Agent'}, + 'Authorization' => "Bearer $token", + }; + my $request = {}; + + $self->{model}->unlink( uid => $uid ); + + my $promise = Mojo::Promise->new; + + $ua->post_p( + "https://traewelling.de/api/v0/auth/logout" => $header => json => + $request )->then( + sub { + my ($tx) = @_; + if ( my $err = $tx->error ) { + my $err_msg = "HTTP $err->{code} $err->{message}"; + $promise->reject($err_msg); + return; + } + else { + $promise->resolve; + return; + } + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + + return $promise; +} + +sub checkin { + my ( $self, $uid ) = @_; + if ( my $token = $self->get_traewelling_push_token($uid) ) { + my $user = $self->get_user_status; + +# TODO delete previous traewelling status if the train's destination has been changed +# TODO delete traewelling status when undoing a travelynx checkin + if ( $user->{checked_in} and $user->{extra_data}{trip_id} ) { + my $traewelling = $self->{model}->get($uid); + if ( $traewelling->{data}{trip_id} eq $user->{extra_data}{trip_id} ) + { + return; + } + my $header = { + 'User-Agent' => 'travelynx/' . $self->{version}, + 'Authorization' => "Bearer $token", + }; + + my $request = { + tripID => $user->{extra_data}{trip_id}, + start => q{} . $user->{dep_eva}, + destination => q{} . $user->{arr_eva}, + }; + my $trip_req = sprintf( + "tripID=%s&lineName=%s%%20%s&start=%s", + $user->{extra_data}{trip_id}, $user->{train_type}, + $user->{train_line} // $user->{train_no}, $user->{dep_eva} + ); + $self->{user_agent}->request_timeout(20) + ->get_p( + "https://traewelling.de/api/v0/trains/trip?$trip_req" => + $header )->then( + sub { + return $self->{user_agent}->request_timeout(20) + ->post_p( + "https://traewelling.de/api/v0/trains/checkin" => + $header => json => $request ); + } + )->then( + sub { + my ($tx) = @_; + if ( my $err = $tx->error ) { + my $err_msg = "HTTP $err->{code} $err->{message}"; + $self->mark_trwl_checkin_error( $uid, $user, $err_msg ); + } + else { + # TODO check for traewelling error ("error" key in response) + # TODO store ID of resulting status (request /user/{name} and store status ID) + $self->mark_trwl_checkin_success( $uid, $user ); + + # mark success: checked into (trip_id, start, destination) + } + } + )->catch( + sub { + my ($err) = @_; + $self->mark_trwl_checkin_error( $uid, $user, $err ); + } + )->wait; + } + } +} + +1; |