適材適所

PowerShellを中心にプログラミングやシステム管理の備忘録的なブログ

MENU

【PowerShell】Google APIを使ってGoolgeカレンダーを操作する

Googleカレンダー、便利ですよね。

私はGoogleカレンダーでスケジュール管理をしています。

そんなGoogleカレンダーをPowerShellを使って操作できたらいいなと思いませんか?

そんなことできるわけ・・・いえ、できるんです。

そう、Google Calendar APIならね。

Google様は私みたいな愚民のために、色々なAPIを用意してくれています。

しかも無料で使えるものも多数!!

どんなAPIがあるか知りたい人は、Google APIのページをご覧ください。

っと、なんだかGoogleの宣伝みたいになってしまいましたが、決してGoogleの回し者ではありません。

Googleカレンダーに話を戻しましょう。

Google APIを使ってPowerShellからGoogleカレンダーを操作する方法を紹介したいと思います。

Googleカレンダー APIのページを見てみると、JavaやPythonなど、主要な言語でGoogleカレンダーAPIを使うためのサンプルが載っています。

サンプルは、Googleカレンダーから予定を10個参照し、コンソールに書き出すという単純なものです。

サンプルは単純なものですが、これさえ理解できれば、これを足掛かりにPowerShellでGoogleカレンダーを扱うちょっとしたアプリを作ることも可能です。

PowerShellの場合は、.NETの例を参考にします。

事前準備

Google APIを使う場合、まず、Google APIのキーを取得する必要があります。

先ほどの.NETの例のページの「Enable the Google Calendar API」をクリックします。

f:id:shinmai_papa:20200630060140p:plain

保存したキーは、ここでは「credentials.json」という名前で、これから作るスクリプトと同じフォルダに保存します。

カレンダーから予定を取得する

先ほどの.NETの例を見てもらうと、コードはC#で書かれていますね。

そのため、PowerShellではこのサンプルにひと手間加え、PowerShell用に改造してあげる必要があります。

やらなければならいいことは次の3つ。

  1. Google API用のdllをダウンロードする
  2. ダウンロードしたdllをインポートする
  3. サンプルをPowerShellの文法で書き直す

ひとつずつ見ていきましょう。

環境

  • OS Windows10 pro (10.0.18362)
  • PSVersion 5.1.18362.145
  • Google API Version 1.46.0(この記事を書いた時点(2020/6/29)の最新Version)
  • .NET Version 4.5

Google API用のdllをダウンロードする

Google APIはパッケージマネージャのNugetからダウンロードします。

現時点(2020/6/29)の最新のGoogleカレンダー APIはv3になります。

Nuget GallaryのGoogle.Apis.Calendar.V3のページを見てみます。

インストールコマンドが載っていますが、これをそのまま実行してみても依存関係の解決で失敗するので、自分で依存関係を解決してある必要があります。

依存関係は先ほどのページのDependenciesに書いてます。

.NET4.5だとGoogle.ApisとGoogle Apis.Authに依存していることがわかります。

f:id:shinmai_papa:20200629140356p:plain

このようにどんどん辿っていくと、

  • Newtonsoft.Json
  • Google.Apis.Core
  • Google.Apis
  • Google.Apis.Auth
  • Google.Apis.Calendar.v3

という順番で依存していることがわかります。

あとは、先ほどの5つをInstall-Package コマンドでインストールすればOKです。

その際、バージョンの指定を忘れないでください。ここではさらにユーザーとダウンロード先のフォルダを指定しています。

管理者権限がある場合は、-Scope CurrentUser オプションは外しても大丈夫かと思います。

また、-SkipDependencies オプションについては、依存関係がうまく解決されないため加えています。

 
$dir="任意のディレクトリ"
Install-Package Newtonsoft.Json -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion "12.0.3"
Install-Package Google.Apis.Core -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion "1.46.0"
Install-Package Google.Apis -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion "1.46.0"
Install-Package Google.Apis.Auth -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion "1.46.0"
Install-Package Google.Apis.Calendar.v3 -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion "1.46.0.1986"

ダウンロードしたdllをインポートする

先ほどのdllたちをインポートしてあげます。

インポートには、Add-Typeコマンドを使います。

.NETのバージョンは4.5なので、ダウンロードされたフォルダの中のnet45のものを使います。

また、定数を使うことになるので、-ReferencedAssembliesオプションで、dllと同じフォルダにあるxmlも追加しておきます。

 
Add-Type -path (Join-Path $dir "Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll") -ReferencedAssemblies (Join-Path $dir "Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.xml")
Add-Type -path (Join-Path $dir "Google.Apis.Core.1.46.0\lib\net45\Google.Apis.Core.dll") -ReferencedAssemblies (Join-Path $dir "Google.Apis.Core.1.46.0\lib\net45\Google.Apis.Core.xml")
Add-Type -path (Join-Path $dir "Google.Apis.1.46.0\lib\net45\Google.Apis.dll") -ReferencedAssemblies (Join-Path $dir "Google.Apis.1.46.0\lib\net45\Google.Apis.xml")
Add-Type -path (Join-Path $dir "Google.Apis.Auth.1.46.0\lib\net45\Google.Apis.Auth.dll") -ReferencedAssemblies (Join-Path $dir "Google.Apis.Auth.1.46.0\lib\net45\Google.Apis.Auth.xml")
Add-Type -path (Join-Path $dir "Google.Apis.Calendar.v3.1.46.0.1986\lib\net45\Google.Apis.Calendar.v3.dll") -ReferencedAssemblies (Join-Path $dir "Google.Apis.Calendar.v3.1.46.0.1986\lib\net45\Google.Apis.Calendar.v3.xml")

PowerShellの文法で書き直す

ここまで来れば、あとはサンプルをPowerShellっぽく書き直すだけです。

.NETの例とにらめっこしながら書き直します。

コードの全体

そして完成したのがこちら。

一部エラーの挙動や、dllのダウンロード、インポートを簡略化しています。

これをテキストエディタに貼って、~~.ps1という名前で保存すれば完成です。

 
#packageの読み込み準備
$dir="$PSScriptRoot\"
$package=@(
   ("Newtonsoft.Json","12.0.3"),
   ("Google.Apis.Core","1.46.0"),
   ("Google.Apis","1.46.0"),
   ("Google.Apis.Auth","1.46.0"),
   ("Google.Apis.Calendar.v3","1.46.0.1986")
)

#dllのインストール
for($i=0;$i -lt $package.Count;$i++){
   $name=$package[$i][0]
   $version=$package[$i][1]
   if(!(Test-Path "$dir$name.$version")){
      Install-Package $name -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion $version|out-null
   }
}

##アセンブリの読み込み
$target="net45"
for($i=0;$i -lt $package.Count;$i++){
   $name=$package[$i][0]
   $version=$package[$i][1]
   Add-Type -path (Join-Path "$dir$name.$version" "lib\$target\$name.dll") -ReferencedAssemblies (Join-Path $dir "$name.$version\lib\$target\$name.xml")|Out-Null
}
##エラーを無視する
$defaultErroAction=$ErrorActionPreference
$ErrorActionPreference="SilentlyContinue"

[string[]]$scope=[Google.Apis.Calendar.v3.CalendarService+Scope]::CalendarReadonly
$ApplicationName = "Google Calendar API .NET Quickstart";
$open=[System.IO.FileMode]::Open
$read=[System.IO.FileAccess]::Read
$stream= New-Object System.IO.FileStream -ArgumentList (Join-Path $dir "credentials.json"),$open,$read
$secrets=([Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream)).Secrets
$credPath=(Join-Path $dir "token.json")
$dataStore=[Google.Apis.Util.Store.FileDataStore]::new($credPath,$true)
$credential=
([Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync(
$secrets,$scope,"user",[System.Threading.CancellationToken]::None,$dataStore)).Result;

Write-Host ("Credential file saved to: " + $credPath)

##Create Google Calendar API service.
$baseClient=New-Object Google.Apis.Services.BaseClientService+Initializer
$baseClient.HttpClientInitializer =$credential
$baseClient.ApplicationName=$ApplicationName
$service=New-Object Google.Apis.Calendar.v3.CalendarService($baseClient)

##Define parameters of request.
$request=$service.Events.List("primary")
$request.TimeMin=[System.DateTime]::Now
$request.ShowDeleted=$false
$request.SingleEvents=$true
$request.MaxResults=10
$request.OrderBy=[Google.Apis.Calendar.v3.EventsResource+ListRequest+OrderByEnum]::StartTime

##List events.
$events=$request.Execute()
Write-Host "Upcoming events:"
if($events.Items -ne $null -and $events.Items.Count -gt 0){
foreach($item in $events.Items)
{
   $when=$null
   $when=$item.Start.DateTime.ToString()
   if([System.String]::IsNullOrEmpty($when)){$when=$item.Start.Date}
      Write-Host ("{0} {1}" -f $item.Summary,$when)
   }
}

$ErrorActionPreference=$defaultErroAction

新しい予定を追加する

.NETの予定追加の例を見ると、C#で予定を追加する例が載っています。

基本的には、これをPowerShell風に書き直していきます。

よく見ると、コードの上になにやら注意書きが書いてありますね。

// Refer to the .NET quickstart on how to setup the environment:
//https://developers.google.com/calendar/quickstart/dotnet
// Change the scope to CalendarService.Scope.Calendar and delete any stored
// credentials.

.QuickStartを参考に環境を構築しておいてね、あとスコープを変更して、他の認証情報は全部消しておいてね

とGoogle様が仰せのようなので、「token.json」フォルダを削除し、credentials.jsonはそのままにしておきます。

これで準備完了です。

流れ

  1. Nugetから下記をダウンロード
    ・Newtonsoft.Json
    ・Google.Apis.Core
    ・Google.Apis
    ・Google.Apis.Auth
    ・Google.Apis.Calendar.v3
  2. インストール
  3. 認証
  4. カレンダーへの追加処理

予定を参照するときと、予定を追加するときの違いは認証のスコープです。

コード

.NETの予定追加の例を参考に、 コード全体をPowerShell風に書き直したものがこちら。

#packageの読み込み準備
$dir="$PSScriptRoot\"
$package=@(
("Newtonsoft.Json","12.0.3"),
("Google.Apis.Core","1.46.0"),
("Google.Apis","1.46.0"),
("Google.Apis.Auth","1.46.0"),
("Google.Apis.Calendar.v3","1.46.0.1986")
)

#dllのインストール
for($i=0;$i -lt $package.Count;$i++){
$name=$package[$i][0]
$version=$package[$i][1]
if(!(Test-Path "$dir$name.$version")){
Install-Package $name -Source nuget -Scope CurrentUser -SkipDependencies -Destination $dir -RequiredVersion $version|out-null
}
}

##アセンブリの読み込み
$target="net45"
for($i=0;$i -lt $package.Count;$i++){
$name=$package[$i][0]
$version=$package[$i][1]
Add-Type -path (Join-Path "$dir$name.$version" "lib\$target\$name.dll") -ReferencedAssemblies (Join-Path $dir "$name.$version\lib\$target\$name.xml")|Out-Null
}
#ここのスコープが前回と異なります。
[string[]]$scope=[Google.Apis.Calendar.v3.CalendarService+Scope]::Calendar
$ApplicationName = "Google Calendar API .NET Quickstart";
$open=[System.IO.FileMode]::Open
$read=[System.IO.FileAccess]::Read
$stream= New-Object System.IO.FileStream -ArgumentList (Join-Path $dir "sclient_secret.json"),$open,$read
$secrets=([Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream)).Secrets
$credPath=(Join-Path $dir "token.json")
$dataStore=[Google.Apis.Util.Store.FileDataStore]::new($credPath,$true)
$credential=
([Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync(
$secrets,$scope,"user",[System.Threading.CancellationToken]::None,$dataStore)).Result;

Write-Host ("Credential file saved to: " + $credPath)

##Create Google Calendar API service.
$baseClient=New-Object Google.Apis.Services.BaseClientService+Initializer
$baseClient.HttpClientInitializer =$credential
$baseClient.ApplicationName=$ApplicationName
$service=New-Object Google.Apis.Calendar.v3.CalendarService($baseClient)

#新しいイベントを作成します
$newEvent=New-Object Google.Apis.Calendar.v3.Data.Event
$newEvent.Summary="Google Api Test"
$newEvent.Location="東京都中央区"
$newEvent.Description="meeting"

#開始日
$start=New-Object Google.Apis.Calendar.v3.Data.EventDateTime
$start.DateTime=[System.DateTime]::Parse("2020/7/1 10:00:00")
$start.TimeZone="Asia/Tokyo"

#終了日
$end=New-Object Google.Apis.Calendar.v3.Data.EventDateTime
$end.DateTime=[System.DateTime]::Parse("2020/7/1 12:00:00")
$end.TimeZone="Asia/Tokyo"

$newEvent.Start=$start
$newEvent.End=$end

#2日連続
[string[]]$recurrence="RRULE:FREQ=DAILY;COUNT=2"
$newEvent.Recurrence=$recurrence

#他の参加者(ここで設定したメールアドレスにメールが飛びます)
$attendees1=New-Object Google.Apis.Calendar.v3.Data.EventAttendee
$attendees1.Email="large@example.com"

$attendees2=New-Object Google.Apis.Calendar.v3.Data.EventAttendee
$attendees2.Email="sbrin@example.com"

$attendeesList=New-Object System.Collections.Generic.List[Google.Apis.Calendar.v3.Data.EventAttendee]
$attendeesList.Add($attendees1)
$attendeesList.Add($attendees2)
$newEvent.Attendees=$attendeesList

#通知の設定
$remainders=New-Object Google.Apis.Calendar.v3.Data.Event+RemindersData
$remainders.UseDefault=$false
$newRemainder1=New-Object Google.Apis.Calendar.v3.Data.EventReminder
$newRemainder1.Method="email"
$newRemainder1.Minutes=24*60

$newRemainder2=New-Object Google.Apis.Calendar.v3.Data.EventReminder
$newRemainder2.Method="sms"
$newRemainder2.Minutes=10

$remainderList=New-Object System.Collections.Generic.List[Google.Apis.Calendar.v3.Data.EventReminder]
$remainderList.Add($newRemainder1)
$remainderList.Add($newRemainder2)
$remainders.Overrides=$remainderList

$calendarId="primary"
$request=$service.Events.Insert($newEvent,$calendarId)
$createdEvent=$request.Execute()
Write-Host ("Created:{0}" -f $createdEvent.HtmlLink)

注意

参加者に架空のアドレスを指定しているので、これを実行すると、Delivery Status Notification (Failure)メールが飛んできます。

私はテストで何回も実行したので、受信トレイが埋め尽くされました。

テストされるときはご留意ください。

終日の予定を追加

Googleカレンダーでは時間指定の予定と終日の予定があります。

終日の予定を追加するサンプル

予定を追加するコードの一部を書き換えるだけです。具体的には下記の部分になります。

$start=New-Object Google.Apis.Calendar.v3.Data.EventDateTime
$start.DateTime=[System.DateTime]::Parse("2020/7/1 10:00:00")

ここを次のように書き換えます。

$start=New-Object Google.Apis.Calendar.v3.Data.EventDateTime
$start.Date=(New-Object System.DateTime -ArgumentList 2020,7,10).ToString("yyyy-MM-dd")

先述のコードではGoogle.Apis.Calendar.v3.Data.EventDateTime型のDateTimeプロパティを設定しましたが、終日の予定の場合は、Dateプロパティを設定します。

開始日だけでなく、終了日も同様に書き換えます。

終日の予定を追加する時のどハマりポイント

私がどハマりしたのは、「yyyy-MM-dd」のところ。

Dateプロパティを設定するときの、日付の形式です。

こちらをご覧ください。

##正常に動かなかったコード
$start.Date=(New-Object System.DateTime -ArgumentList 2020,7,10).ToString("yyyy/MM/dd")           
##正しいコード
$start.Date=(New-Object System.DateTime -ArgumentList 2020,7,10).ToString("yyyy-MM-dd")

上は私が書いた間違ったコードです。下は正しく動作するコードです。

どこが違うかわかますか?

そうです、書式設定が"yyyy/MM/dd"になっております・・・。

"yyyy/MM/dd"だと、うごかないんですね。

これぐらいいいじゃん!!って思う私はプログラムを書く資格がないんでしょうか。

厳密に言うと、このコードを実行した時点ではエラーになりません。

これを開始日や終了日に設定したリクエストをGoogleに投げるとエラーが返ってきます。

エラーの内容を見ても、何が間違っているのかわらず、2時間を無駄にしました泣。

2時間格闘した末、やっぱり最後は基本に帰って解決しました。

公式リファレンスにしっかり書いてありました。

The date, in the format "yyyy-mm-dd", if this is an all-day event.

さらっと書いてございました。

やっぱり公式リファレンスはしっかり読まないとだめですね。

さらに簡単に予定を追加

Googleカレンダーに予定を追加する方法はもう一つあります。

それはQuickAddという書き方です。

他の参加者や、予定の繰り返しなど、全ての項目を細かいものも含めて隈なく設定できるのですが、多くの場合、そこまで細かい設定はせず、ただ開始日と終了日、件名くらいしか登録しないのではないでしょうか。

Google先生は、そんなこともお見通しです。

そして、そんな面倒臭がり屋さんのためにちゃんと簡単に予定を追加する方法を提供してくれているです。

QuickAddという魔法

なんとEventsオブジェクトのQuickAddメソッドを使うことで、簡単に予定を追加することができます。

公式リファレンスに

Creates an event based on a simple text string

とあるように、こちらのQuickAddメソッドは、簡単な文字列からEventを追加することができます。

Appointment at Somewhere on June 3rd 10am-10:25am

こんな英語のテストに出てきそうなみ文で予定を追加できるなんて素敵!!

リンクの公式リファレンスには.NETの例はありませんが、他の言語のサンプルから、なんとなく雰囲気がわかりますね。

ちなみに.NETのQuickAddの構文はこうなっています。

OverloadDefinitions
-------------------
Google.Apis.Calendar.v3.EventsResource+QuickAddRequest QuickAdd(string calendarId, string text)

これを踏まえて、PowerShell(.NET)で

QuickAddを使って予定を追加してみます。

 
#dllのロード、OAuth認証は省略

##Create Google Calendar API service.
$baseClient=New-Object Google.Apis.Services.BaseClientService+Initializer
$baseClient.HttpClientInitializer =$credential
$baseClient.ApplicationName=$ApplicationName
$service=New-Object Google.Apis.Calendar.v3.CalendarService($baseClient)
$service.Events.QuickAdd("primary",'2020年6月3日 10am-10:25am 約束').execute()

先述のInsertの例と比べると、追加の処理が1行で済んでおり、予定の追加コードがとても簡素化されています。

日本語の場合は引数のTextに注意

引数のTextについては、どのように書いたらどのように動作するのか、

公式リファレンスにはあまり詳しく書いておりません。

公式リファレンスでは、Textについて、

The text describing the event to be created.

とだけ書かれています。

ちょっと素っ気ない気がしますが・・・。

もう少し、どんな風に書いたらいいか等の説明が欲しいところですね・・・。

ということで、日本語ではどんなケースは登録できて、どんなケースが登録できないのか、少し実験してみました。

QuickAddの実験

Version

Google.Apis.Calendar.v3 1.46.0.1986

これはOK

2020/6/3 10am-10:25am 約束 2020年6月3日 10am-10:25am 約束

これはNG

6月3日 10am-10:25am 約束
⇒「6月3日約束」という名前で、明日(コマンドを実行した日の次の日)の日付で登録されてしまいました。

2020年6月3日 10:00-10:25 約束
⇒「約束」という名前で、6月4日の終日の予定として登録されてしまいました。

2020年6月3日 午前10時-午前10時25分 約束
⇒「約束」という名前で、6月4日の終日の予定として登録されてしまいました。

2020年6月3日 約束 東京にて
⇒「約束 東京にて」という名前で6月4日の終日の予定として登録されてしまいました。

2020年6月3日 約束 at 東京
⇒「約束 at 東京」という名前で6月4日の終日の予定として登録されてしまいました。

ここから言えること

・日付はyyyy/M/dでも、yyyy年M月d日のどちらでも大丈夫 ・時間指定は、「10am-10:25am」の書式に従う必要があること ・時間指定の書式が正しくなかったり、指定しないと指定した日の翌日になってしまう? ・場所の指定はできない?

⇒どうも本当に簡単な予定(開始日、終了日、サマリーくらい)しか登録できなさそうです。

また、日本語でうまく場所を指定することができませんでした。

うーん。

アプローチが悪いのでしょうか。

もっと調査が必要ですね。

予定を削除する

最後に予定を削除する方法についても述べておきます。

Googleカレンダーから個々の予定を削除するには、Google.Apis.Calendar.v3.CalendarServiceオブジェクトEventsプロパティのDeleteメソッドを呼び出します。

 
#アセンブリのロード、認証については省略

##Create Google Calendar API service.
$baseClient=New-Object Google.Apis.Services.BaseClientService+Initializer
$baseClient.HttpClientInitializer =$credential
$baseClient.ApplicationName=$ApplicationName
$service=New-Object Google.Apis.Calendar.v3.CalendarService($baseClient)

#削除したい予定を取得
$calendarId="primary"
$delReq=$service.Events.List($calendarId)
$delReq.ShowDeleted=$false
$delReq.SingleEvents=$true
$delReq.OrderBy=[Google.Apis.Calendar.v3.EventsResource+ListRequest+OrderByEnum]::StartTime
#削除したい予定を$delEvents変数に格納
$delEvents=$delReq.Execute()
#ひとつずつ予定を削除
foreach($d in $delEvents.Items){
     [void]$service.Events.Delete($calendarId,$d.id).Execute()
}

Deleteも比較的簡単にできます。

簡単なDeleteなのにハマる

しかし、私が開発していたアプリでは、そうは問屋が卸しませんでした。

しっかり落とし穴、ありました。

Deleteは関係なく、EventsResource+ListRequestのExcecuteメソッドにハマる

私が削除しようとしたのは、なんと5年先までの予定が入ったカレンダーのすべての予定でした。

なんでそんな先まで予定入ってんねんって感じですよね。

お前は売れっ子かって感じですね。

真相はただ単純に毎週の予定を何も考えずに繰り返しで入れていただけです。

そのカレンダーから予定を全て削除すべく、上述のコードを実行したところ、予定は削除されたかに見えました。

しかし、カレンダーを未来に進めていくと、なぜか残っている予定が。

何度やっても、残る。

なぜか、消えない。

おかしいなぁ、おかしいなぁと思って一行ずつステップ実行してみました。

そうすると、$delEventsの中の要素数(予定の数)が250しかないことに気が付きました。

あれあれ?もっと削除したい予定はあるはずなのに・・・?

それもそのはず、5年先予定は全部で800も登録されていました。

何度やっても250個しか取得できません。

おかしい、おかしい・・・

といって、ここで貴重な時間を費やしてしまいました。

そう、賢い読者の方はもうお気づきだと思いますが、一度のリクエストで取得できる予定数に制限があったんですね。

この部分です。

#削除したい予定を$delEvents変数に格納
$delEvents=$delReq.Execute()

Excecuteメソッドで取得できるイベント数の

最大値のデフォルト数は250とのことでした。

こちらの公式リファレンスにも記載されていました。

Maximum number of events returned on one result page. The number of events in the resulting page may be less than this value, or none at all, even if there are more events matching the query. Incomplete pages can be detected by a non-empty nextPageToken field in the response. By default the value is250 events. The page size can never be larger than 2500 events. Optional.

デフォルトでは250ですが、maxResultsプロパティを指定することでその数を増やすことができます。

最大で2500個まで指定できるようです。

上記のコードに付け加えるならば、

$delReq.MaxResults=2500

と指定してあげれば2500個まで予定を取得できます。

最初からリファレンスを読めよ、って話ですが・・・。

毎度のことながら、壁にぶち当たって読み始める・・・

学習しないですね。

では2500個以上の予定を削除したいときは?

今回ハマった8,000個以上の予定を削除したいときはどうすればいいでしょうか。

これは、もうスマートな方法はありません。

力業で何度もリクエストを送ってあげます。

2500個以上の削除したい予定がある場合はこんな風にしてみます。

 
$calendarId="Primary"
$delReq=$service.Events.List($calendarId)
$delReq.ShowDeleted=$false
$delReq.SingleEvents=$true
#リクエストの最大数を2500に指定
$delReq.MaxResults=2500
$delReq.OrderBy=[Google.Apis.Calendar.v3.EventsResource+ListRequest+OrderByEnum]::StartTime
$delEvents=$delReq.Execute()
#ひたすら予定を取得し続ける
while($delEvents.Items.Count -ne 0){
    foreach($d in $delEvents.Items){
        [void]$service.Events.Delete($calendarId,$d.id).Execute()
    }
    $delEvents=$delReq.Execute()
}

ゴリ押しで$delEvents.Items.Countが0になるまで、延々と予定を取得し続けます。

おわりに

PowershellからGoogle Calendar APIを使う方法でした。

認証やスコープといったところが少し面倒ではありますが、そこをクリアできると比較的簡単に操作できます。

私がハマってしまった、ちょっとした落とし穴についても触れましたが、外部APIを使うときは、とにかく動かしてみたい気持ちが先行してしまいがちですが、はやる気持ちも抑えつつ、ちゃんとドキュメントを読みましょう(自戒を込めて)。

あんまり暴挙を犯すと、APIの提供元にもご迷惑をおかけしてしまいますからね・・・。

というわけで、ここまでお読みいただき、ありがとうございました。