iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 12
0
AI & Machine Learning

利用 MS Bot framework 與 Cognitive Service 建構自用智慧小秘書系列 第 12

12.應用:自用 Google calendar 查詢機器人 TL;DR

前言

在這一篇,我們將嘗試擷取個人 Google Calendar 行事曆。 Google 相關 API 介接有許多方式,自己在撰寫這篇的時候最先透過 Oauth 2.0 的方式介接,卻因為機器人程式是透過 Server to Server 的方式取得資料,故無法使用教學文件範本中的方式盡情使用者授權 → 取得 Authorization Code,當然也取不到Token,也拿不到資料。

資料來源:https://developers.google.com/identity/protocols/OAuth2
資料來源:https://developers.google.com/identity/protocols/OAuth2

撰寫這篇文章前閱讀了許多的文件、論壇與進行大量的測試、最後採取了使用 Service Account (服務帳戶)的方式 + 處理憑證在 Azure Web Application 的問題後得以解決,獲益良多。本篇文章若有錯誤或更好的建議,歡迎各位先進不吝指導與討論,謝謝。


Google Calendar API 啟用與註冊

Step 1. 開啟啟用網站 (https://console.developers.google.com/flows/enableapi?apiid=calendar&hl=zh-TW),選擇應用程式要註冊的專案,如果你沒有建立過 Google console 專案,可以在此建立新的。
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513265249268_image.png

Step 2. 點選前往憑證
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513265289070_image.png

Step 3. 填寫內容,會推薦你適合的方式取用資料
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513501342085_image.png

Step 4. 選擇你的服務帳戶,若沒有服務帳戶則建立新的服務帳戶

註:請記住電子郵件與金鑰 ID

選擇現有服務帳戶

建立新的服務帳戶

Step 5. 建立金鑰
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513502166381_image.png

Step 6. 建立並下載金鑰,這個金鑰等等我們要放入專案內,請謹慎保存
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513502207717_image.png

Step 7. 開啟你要讀取的 Google 日曆 → 設定。到這邊,我們完成撰寫程式前的準備工作。
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513501742749_image.png

Step 8. 與特定使用者共用日曆 → 新增邀請對象 →傳送
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513501847829_image.png

Step 9. 往下拉,找到整合日曆,複製日曆ID,程式會需要這個參數。
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513503237172_image.png


程式

Step 1. 在開始撰寫程式之前,我們先安裝 RestSharp。 工具 → NuGet 封裝管理員 → 管理方案的 NuGet 套件..
https://d2mxuefqeaa7sj.cloudfront.net/s_9215B5A5D5CCDA6255EDBD445F7D2649CDA38884351ABF37A54060ED64892C8E_1512982562880_image.png

Step 2. 輸入Google.Apis.Calendar → 進行安裝

註:個人測試的時候安裝1.31.1.1074版本,最新版本似乎有相依性問題

https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513267644774_image.png

Step 2. 對專案點選右鍵 → 建立Resources 資料夾
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513502412189_image.png

Step 3. 我們建
Resources 資料夾 → 右鍵 → 加入 → 新增項目 → Resources Files (未來有機會可以延伸作為多國語系)

註:因為我在我的2017找不到 Resources Files。若您與我相同,嘗試建立文字檔案並更改附檔名為res,並確定性質自訂工具為 ResXFileCodeGenerator 與建置動作為內嵌資源。

https://d2mxuefqeaa7sj.cloudfront.net/s_4BD31EC08DAC6681F125ECB2893F87311C2A9E8644E7E0D9FAA1622819FD5464_1512916453508_image.png
https://d2mxuefqeaa7sj.cloudfront.net/s_4BD31EC08DAC6681F125ECB2893F87311C2A9E8644E7E0D9FAA1622819FD5464_1512916840625_image.png

Step 4. 點開資源檔案 → 加入現有檔案
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513502764149_image.png

Step 5.選擇我們剛剛前置作業下載的憑證檔案 → xxxx.p12
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513502926199_image.png

Step 6. 首先我們得先組裝驗證資料,包含:

  • 字串陣列 Scope,內容為服務範圍參數,我們只需要 CalendarService.Scope.CalendarReadonly
  • 透過 Service Account 取得的頻證,再初始化 CalendarService,開始使用 Google Calendar 服務

程式碼如下:

    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. 開啟機器人模擬器進行測試
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513270597114_image.png

Step 7. 右鍵點選專案 → 發行,上傳 Azure Web Application,透過Skype 嘗試,成功!
https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513503521664_image.png

https://d2mxuefqeaa7sj.cloudfront.net/s_B9EED07F3E11AFAEDC6053200152C8B39C159B1C4ED17D3977D88BE521AACD72_1513503596833_image.png


參考資料

Using OAuth 2.0 to Access Google APIs - https://developers.google.com/identity/protocols/OAuth2


上一篇
11.應用:自用笑話機器人
下一篇
13. Azure 排程器
系列文
利用 MS Bot framework 與 Cognitive Service 建構自用智慧小秘書31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言