Go x Google Calendar API x Copilot 、簡易なチーム日程調整を作ってみる


この記事は [Wano Group Advent Calendar 2023](https://qiita.com/advent-calendar/2023/wano-group) の16日目の記事です。

## 概要

最近Copilotの使い方を学び始めました。
ちょうどチームメンバーの日程調整機能を作りたいので、Google Calendar APIとCopilotを使って作ってみました。

## 先ずはgolangでGoogle Calendar APIを使う

https://developers.google.com/calendar/api/quickstart/go?hl=ja

google提供のクイックスタートを通りにやっていくだけで、個別のカレンダー情報を取得できます。さらに、[calendar/v3/freeBusy](https://developers.google.com/calendar/api/v3/reference/freebusy/query?hl=ja) API は複数メンバーのカレンダーの予定時間情報を返します：

```go

	jst, _ := time.LoadLocation("Asia/Tokyo")
	events, err := srv.Freebusy.Query(&calendar.FreeBusyRequest{
		Items: []*calendar.FreeBusyRequestItem{
			{
				Id: "XXXXX1@gmail.com",  // メンバー1カレンダーのメールアドレス
			},
			{
				Id: "XXXXX2@gmail.com",  // メンバー2カレンダーのメールアドレス
			},
		},
        // クエリの間隔の終了時刻
		TimeMax:  time.Date(2023, month, days, 24, 0, 0, 0, jst).Format(time.RFC3339),
        // クエリの間隔の開始時刻
		TimeMin:  time.Date(2023, month, 1, 0, 0, 0, 0, jst).Format(time.RFC3339),
        // レスポンスで使用されるタイムゾーン
		TimeZone: "Asia/Tokyo",
	}).Do()

```
しかし、複数対象の共通空き時間を取得するAPIがあるかどうかを探しましたが、
残念ですが、そのような機能は提供されていないようですので、この関数を自分で実装してみる。

```golang
	// 1ヶ月の分を確認
	for day := 1; day < days; day++ {

		// 確認したいの時間帯 12:00-13:00 / 12:30-13:30 / 13:00-14:00
		checkTimeStructs := make([]CheckTimeStruct, 0)
		checkTimeStructs = append(checkTimeStructs, CheckTimeStruct{
			StartTime: time.Date(2023, month, day, 12, 0, 0, 0, jst),
			EndTime:   time.Date(2023, month, day, 13, 0, 0, 0, jst),
			IsUsable:  true,
		})
		checkTimeStructs = append(checkTimeStructs, CheckTimeStruct{
			StartTime: time.Date(2023, month, day, 12, 30, 0, 0, jst),
			EndTime:   time.Date(2023, month, day, 13, 30, 0, 0, jst),
			IsUsable:  true,
		})
		checkTimeStructs = append(checkTimeStructs, CheckTimeStruct{
			StartTime: time.Date(2023, month, day, 13, 0, 0, 0, jst),
			EndTime:   time.Date(2023, month, day, 14, 0, 0, 0, jst),
			IsUsable:  true,
		})

		// 土日は除外
		if checkTimeStructs[0].StartTime.Weekday() == 0 || checkTimeStructs[0].StartTime.Weekday() == 6 {
			continue
		}

		// メンバーの予定を確認
		for _, item := range events.Calendars {
			for index, _ := range checkTimeStructs {
                // 日付が範囲内にあるかどうかの確認関数
				if checkDateInPeriod(item.Busy, checkTimeStructs[index].StartTime, checkTimeStructs[index].EndTime) {
					checkTimeStructs[index].IsUsable = false
				}
			}
		}

		//結果を出力
		for index, single := range checkTimeStructs {

			if index == 0 && single.IsUsable {
				fmt.Printf("%d日の12:00-13:00は空いています。\n", day)
			}
			if index == 1 && single.IsUsable {
				fmt.Printf("%d日の12:30-13:30は空いています。\n", day)
			}
			if index == 2 && single.IsUsable {
				fmt.Printf("%d日の13:00-14:00は空いています。\n", day)
			}
		}
	}

}

```

## よく使うの関数はCopilotに補完

https://docs.github.com/ja/copilot/using-github-copilot/getting-started-with-github-copilot

補完の機能について、基本の操作はこの感じ：
Command + → -> ワードごとの適用
Tab -> サジェスト内容を適用する 
Esc -> サジェスト内容を棄却出来る

実際に試したら、すごく便利と思います
例えば：
![nb3bw-fr6kt.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153285/f3dbd9ba-0711-2118-73ac-8a6b4696810b.gif)


それを使って時間帯確認周りの関数が簡単に作られた

```go
// 時間帯が範囲内にあるかどうか
func checkDateDuplicated(inStart time.Time, inEnd time.Time, targetStart time.Time, targetEnd time.Time) bool {
	if inStart.Before(targetEnd) && targetStart.Before(inEnd) {
		return true
	}
	return false
}

// 日付が範囲内にあるかどうか
func checkDateInPeriod(in []*calendar.TimePeriod, targetStart time.Time, targetEnd time.Time) bool {

	if len(in) == 0 {
		return false
	}
	for _, v := range in {
		start, _ := time.Parse(time.RFC3339, v.Start)
		end, _ := time.Parse(time.RFC3339, v.End)
		if checkDateDuplicated(start, end, targetStart, targetEnd) {
			return true
		}
	}
	return false
}

// うるう年の判定
func isLeapYear(year int) bool {
	if year%400 == 0 {
		return true
	} else if year%100 == 0 {
		return false
	} else {
		return year%4 == 0
	}
}

// 月の日数を返す
func daysInMonth(year int, month time.Month) int {
	switch month {
	case time.February:
		if isLeapYear(year) {
			return 29
		} else {
			return 28
		}
	case time.April, time.June, time.September, time.November:
		return 30
	default:
		return 31
	}
}
```
以上の関数は全部Copilotで生成された、結構楽ですね！

## 最後に
出力した結果がこちらです。

![スクリーンショット 2023-12-11 0.20.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153285/b5d6d6a1-a723-8173-46ac-c944503f47ba.png)

いい感じでみんなの空き日時を抽出することを出来ました。

今回はGoogle Calendar API x Copilotを活用して簡易な日程調整を作成しました。
こういう組み合わせると意外と面白いことができると思います。


現在、Wanoグループでは人材募集をしています。興味のある方は下記を参照してください。
[JOBS | Wano Group](https://group.wano.co.jp/jobs/)