234 lines
6.6 KiB
Perl
234 lines
6.6 KiB
Perl
package GoogleCalendarApi;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use JSON;
|
|
use JSON::WebToken;
|
|
use LWP::UserAgent;
|
|
use HTML::Entities;
|
|
use URI::Escape;
|
|
use Data::Dumper;
|
|
use DateTime;
|
|
|
|
|
|
sub new {
|
|
my $class = shift;
|
|
my $params = shift;
|
|
|
|
my $self={};
|
|
for my $attr ('calendarId','debug'){
|
|
$self->{$attr}=$params->{$attr} if defined $params->{$attr};
|
|
}
|
|
|
|
my $instance=bless $self, $class;
|
|
if ((defined $params->{serviceAccount}) && (defined $params->{privateKey})){
|
|
$instance->login($params->{serviceAccount}, $params->{privateKey});
|
|
}
|
|
|
|
return $instance;
|
|
}
|
|
|
|
sub setCalendar{
|
|
my $self=shift;
|
|
my $calendarId=shift;
|
|
$self->{calendarId}=$calendarId;
|
|
}
|
|
|
|
sub getBasicUrl{
|
|
my $self=shift;
|
|
return 'https://www.googleapis.com/calendar/v3/calendars/'.encode_entities($self->{calendarId});
|
|
}
|
|
|
|
#https://developers.google.com/google-apps/calendar/v3/reference/events/list
|
|
|
|
#returns {
|
|
# 'timeZone' => 'Europe/Berlin',
|
|
# 'description' => "Radioprogramm von Pi Radio f\x{fc}r 88vier.de",
|
|
# 'defaultReminders' => [],
|
|
# 'accessRole' => 'owner',
|
|
# 'etag' => '"1415821582086000"',
|
|
# 'kind' => 'calendar#events',
|
|
# 'summary' => '88vier.de Pi Radio (Programm)',
|
|
# 'updated' => '2014-11-12T19:46:22.086Z',
|
|
# 'items' => [...]
|
|
# }
|
|
sub getEvents{
|
|
my $self=shift;
|
|
my $params=shift;
|
|
|
|
my $url='/events?';
|
|
for my $param ('iCalUID','alwaysIncludeEmail','maxAttendees','maxResults','orderBy','pageToken','privateExtendedProperty',
|
|
'q','sharedExtendedProperty','showDeleted','showHiddenInvitations','singleEvents','syncToken','timeZone'
|
|
){
|
|
$url.='&'.$param.'='.uri_escape($params->{$param}) if defined $params->{$param};
|
|
}
|
|
for my $param ('timeMin','timeMax','updatedMin'){
|
|
$url.='&'.$param.'='.uri_escape($self->formatDateTime($params->{$param})) if defined $params->{$param};
|
|
}
|
|
my $result=$self->httpRequest('GET', $url);
|
|
return $result;
|
|
}
|
|
|
|
# sleep 0.25 seconds to prevent hitting the 5.0 requests/second/user rate
|
|
sub sleep{
|
|
my $this=shift;
|
|
my $duration=shift || 0.25;
|
|
select(undef, undef, undef, $duration);
|
|
}
|
|
|
|
#https://developers.google.com/google-apps/calendar/v3/reference/events/delete
|
|
sub deleteEvent{
|
|
my $self=shift;
|
|
my $eventId=shift;
|
|
my $url='/events/'.$eventId;
|
|
#DELETE https://www.googleapis.com/calendar/v3/calendars/calendarId/events/eventId
|
|
my $result=$self->httpRequest('DELETE', $url);
|
|
$self->sleep();
|
|
return $result;
|
|
}
|
|
|
|
#https://developers.google.com/google-apps/calendar/v3/reference/events/insert
|
|
sub insertEvent{
|
|
my $self=shift;
|
|
my $params=shift;
|
|
|
|
my $event={
|
|
start => {
|
|
dateTime => $self->formatDateTime($params->{start})
|
|
},
|
|
end => {
|
|
dateTime => $self->formatDateTime($params->{end})
|
|
},
|
|
summary => $params->{summary}||'',
|
|
description => $params->{description}||'',
|
|
location => $params->{location}||'',
|
|
status => $params->{confirmed}||'confirmed'
|
|
};
|
|
$event= encode_json $event;
|
|
|
|
#POST https://www.googleapis.com/calendar/v3/calendars/calendarId/events
|
|
my $url='/events';
|
|
my $result=$self->httpRequest('POST', $url, $event);
|
|
$self->sleep();
|
|
return $result;
|
|
}
|
|
|
|
# send a HTTP request
|
|
sub httpRequest{
|
|
my $self=shift;
|
|
my $method=shift;
|
|
my $url=shift;
|
|
my $content=shift||'';
|
|
|
|
print STDERR "$method ".$url."\n" if $self->{debug};
|
|
|
|
die ("missing url") unless defined $url;
|
|
die ("calendarId not set") unless defined $self->{calendarId};
|
|
die ("not logged in ") unless defined $self->{api};
|
|
|
|
#prepend basic url including calendar id
|
|
$url=$self->getBasicUrl().$url;
|
|
print STDERR "$method ".$url."\n" if $self->{debug};
|
|
|
|
my $response=undef;
|
|
if($method eq 'GET'){
|
|
$response = $self->{api}->get($url);
|
|
}elsif(($method eq 'POST')||($method eq 'PUT')){
|
|
print STDERR $content."\n" if $self->{debug};
|
|
my $request = HTTP::Request->new( $method, $url );
|
|
$request->header( 'Content-Type' => 'application/json' );
|
|
$request->content( $content );
|
|
$response=$self->{api}->request( $request );
|
|
}elsif($method eq 'DELETE'){
|
|
$response = $self->{api}->delete($url);
|
|
}
|
|
|
|
if($response->is_success) {
|
|
my $content = $response->content;
|
|
return {} if $content eq '';
|
|
return decode_json($content);
|
|
} else {
|
|
print "ERROR:\n";
|
|
print "Code: ".$response->code."\n";
|
|
print "Message: ".$response->message."\n";
|
|
print $response->content."\n";
|
|
die;
|
|
}
|
|
}
|
|
|
|
# write datetime object to string
|
|
sub formatDateTime{
|
|
my $self=shift;
|
|
my $dt=shift;
|
|
|
|
my $datetime= $dt->format_cldr("yyyy-MM-ddTHH:mm:ssZZZZZ");
|
|
print STDERR "$dt -> $datetime\n" if $self->{debug};
|
|
return $datetime;
|
|
}
|
|
|
|
# parse datetime from string to object
|
|
sub getDateTime{
|
|
my $self=shift;
|
|
my $datetime=shift;
|
|
my $timezone=shift;
|
|
|
|
return if((!defined $datetime) or ($datetime eq ''));
|
|
my @l=split /[\-\;T\s\:\+\.]/,$datetime;
|
|
|
|
$datetime=DateTime->new(
|
|
year => $l[0],
|
|
month => $l[1],
|
|
day => $l[2],
|
|
hour => $l[3],
|
|
minute => $l[4],
|
|
second => $l[5],
|
|
time_zone => $timezone
|
|
);
|
|
return $datetime;
|
|
}
|
|
|
|
# login with serviceAccount and webToken (from privateKey)
|
|
sub login{
|
|
my $self=shift;
|
|
my $serviceAccount=shift;
|
|
my $privateKey=shift;
|
|
|
|
# https://developers.google.com/accounts/docs/OAuth2ServiceAccount
|
|
my $time = time;
|
|
#create JSON Web Token
|
|
my $jwt = JSON::WebToken->encode(
|
|
{
|
|
iss => $serviceAccount,
|
|
scope => 'https://www.googleapis.com/auth/calendar',
|
|
aud => 'https://accounts.google.com/o/oauth2/token',
|
|
exp => $time + 3600,
|
|
iat => $time,
|
|
},
|
|
$privateKey,
|
|
'RS256',
|
|
{typ => 'JWT'}
|
|
);
|
|
|
|
#send JSON web token to authentication service
|
|
$self->{auth} = LWP::UserAgent->new();
|
|
my $response = $self->{auth}->post(
|
|
'https://accounts.google.com/o/oauth2/token',
|
|
{
|
|
grant_type => encode_entities('urn:ietf:params:oauth:grant-type:jwt-bearer'),
|
|
assertion => $jwt
|
|
}
|
|
);
|
|
|
|
die($response->code, "\n", $response->content, "\n") unless $response->is_success();
|
|
my $data= decode_json($response->content);
|
|
|
|
#create a new user agent and set token to bearer
|
|
$self->{api} = LWP::UserAgent->new();
|
|
$self->{api}->default_header(Authorization => 'Bearer ' . $data->{access_token});
|
|
|
|
print STDERR "login successful\n" if $self->{debug};
|
|
return $data;
|
|
}
|
|
|
|
1;
|