在這一篇,我們將嘗試擷取個人 Google Calendar 行事曆。 Google 相關 API 介接有許多方式,自己在撰寫這篇的時候最先透過 Oauth 2.0 的方式介接,卻因為機器人程式是透過 Server to Server 的方式取得資料,故無法使用教學文件範本中的方式盡情使用者授權 → 取得 Authorization Code,當然也取不到Token,也拿不到資料。
資料來源:https://developers.google.com/identity/protocols/OAuth2
撰寫這篇文章前閱讀了許多的文件、論壇與進行大量的測試、最後採取了使用 Service Account (服務帳戶)的方式 + 處理憑證在 Azure Web Application 的問題後得以解決,獲益良多。本篇文章若有錯誤或更好的建議,歡迎各位先進不吝指導與討論,謝謝。
Step 1. 開啟啟用網站 (https://console.developers.google.com/flows/enableapi?apiid=calendar&hl=zh-TW),選擇應用程式要註冊的專案,如果你沒有建立過 Google console 專案,可以在此建立新的。
Step 2. 點選前往憑證
Step 3. 填寫內容,會推薦你適合的方式取用資料
Step 4. 選擇你的服務帳戶,若沒有服務帳戶則建立新的服務帳戶
註:請記住電子郵件與金鑰 ID
Step 5. 建立金鑰
Step 6. 建立並下載金鑰,這個金鑰等等我們要放入專案內,請謹慎保存
Step 7. 開啟你要讀取的 Google 日曆 → 設定。到這邊,我們完成撰寫程式前的準備工作。
Step 8. 與特定使用者共用日曆 → 新增邀請對象 →傳送
Step 9. 往下拉,找到整合日曆,複製日曆ID,程式會需要這個參數。
Step 1. 在開始撰寫程式之前,我們先安裝 RestSharp。 工具 → NuGet 封裝管理員 → 管理方案的 NuGet 套件..
Step 2. 輸入Google.Apis.Calendar → 進行安裝
註:個人測試的時候安裝1.31.1.1074版本,最新版本似乎有相依性問題
Step 2. 對專案點選右鍵 → 建立Resources 資料夾
Step 3. 我們建
Resources 資料夾 → 右鍵 → 加入 → 新增項目 → Resources Files (未來有機會可以延伸作為多國語系)
註:因為我在我的2017找不到 Resources Files。若您與我相同,嘗試建立文字檔案並更改附檔名為res,並確定性質自訂工具為 ResXFileCodeGenerator 與建置動作為內嵌資源。
Step 4. 點開資源檔案 → 加入現有檔案
Step 5.選擇我們剛剛前置作業下載的憑證檔案 → xxxx.p12
Step 6. 首先我們得先組裝驗證資料,包含:
程式碼如下:
string[] Scopes = { CalendarService.Scope.CalendarReadonly };
var certificate = new X509Certificate2(Resources.Resources.API_Project_xxxxx, "notasecret", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("dog0416@api-project-xxxxxxxxx.iam.gserviceaccount.com")
{
Scopes = Scopes
}.FromCertificate(certificate));
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Test",
});
Step 7. 接下來你可以選擇你要的日曆與先關參數測定,若使用 primary ,表示這個登入人員主要的日曆。執行後 request.Execute() 會回傳行事曆內容 ,你可以在events.Items 找到近10比行事曆資料(若有資料)。
註:參數 xxxxxxxxx@group.calendar.google.com 為日曆ID,我們在日曆設定時可以取得。
EventsResource.ListRequest request = service.Events.List("xxxxxxxxx@group.calendar.google.com");
request.TimeMin = DateTime.Now;
request.ShowDeleted = false;
request.SingleEvents = true;
request.MaxResults = 10;
request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
Events events = request.Execute();
string response = "Upcoming events:";
if (events.Items != null && events.Items.Count > 0)
{
foreach (var eventItem in events.Items)
{
string when = eventItem.Start.DateTime.ToString();
if (String.IsNullOrEmpty(when))
{
when = eventItem.Start.Date;
}
response += when + ":" + eventItem.Summary + "<br/>";
}
}
Step 8. RootDialog.cs 程式碼應該如下
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Calendar.v3;
using System.Threading;
using Google.Apis.Services;
using Google.Apis.Calendar.v3.Data;
using System.Security.Cryptography.X509Certificates;
namespace GoogleCalendarApplication.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
if(activity.Text.IndexOf("行事曆") >= 0)
{
string[] Scopes = { CalendarService.Scope.CalendarReadonly };
var certificate = new X509Certificate2(Resources.Resources.API_Project_xxxxxxx, "notasecret", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("dog0416@api-project-xxxxxxxxx.iam.gserviceaccount.com")
{
Scopes = Scopes
}.FromCertificate(certificate));
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Test",
});
EventsResource.ListRequest request = service.Events.List("xxxxxxxxx@group.calendar.google.com");
request.TimeMin = DateTime.Now;
request.ShowDeleted = false;
request.SingleEvents = true;
request.MaxResults = 10;
request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
Events events = request.Execute();
string response = "Upcoming events:";
if (events.Items != null && events.Items.Count > 0)
{
foreach (var eventItem in events.Items)
{
string when = eventItem.Start.DateTime.ToString();
if (String.IsNullOrEmpty(when))
{
when = eventItem.Start.Date;
}
response += when + ":" + eventItem.Summary + "<br/>";
}
}
else
{
response += "No upcoming events found.";
}
// return our reply to the user
await context.PostAsync($"{response}");
}
context.Wait(MessageReceivedAsync);
}
}
}
Step 6. 開啟機器人模擬器進行測試
Step 7. 右鍵點選專案 → 發行,上傳 Azure Web Application,透過Skype 嘗試,成功!
Using OAuth 2.0 to Access Google APIs - https://developers.google.com/identity/protocols/OAuth2