【GAS × LINE WORKS】今日一日の予定をまとめて教えてくれる Bot を作りました

LINE WORKS を使っていて、今日の予定を思い出すのにいちいちカレンダー見に行くのめんどくさいなー('Д') と思ったのがきっかけです。

調べてみたところ、LINE WORKS カレンダーには通知機能があるのですが、あくまで個々の予定での通知で、まとめてお知らせしてくれるような機能はないようです。
[LINE WORKS ガイド - カレンダー予定作成 10.通知](https://guide.worksmobile.com/ja/calendar/calendar-guide/create-event/create-event/#i-10)

なら作ってしまえ！ヾ(*´∀｀*)ﾉ
と思って作ったのがこちらです。

![1611198006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/3c05d98b-5a26-5e84-271c-0c2d9e565d1b.png)

- Google Apps Script で作成したので、トリガーを作成して毎朝 9時に通知してくれるようにしました。
- カレンダーへのリンクも貼り付けてあるのでワンクリックで詳細も確認しに行けます。

機能はただこれだけなんですが、意外とというか、結構便利。
自分がやっている単純な動作を一つ自動化しただけでも、何となくスムーズに仕事に入れている気がします。

こういうの、どんどん探していきたいですね！

以下、作り方です。良かったらどーぞ。

# 作るに当たって必要なもの

- LINE WORKS アカウント
- Google アカウント

# 使用するサービス

- Google Apps Script（略称 GAS）

Google が提供しているスクリプト言語。
開発環境不要で無料で Gooogle のアプリの連携サービスなどが使用できる。

- LINE WORKS API

WORKS MOBILE Japan が提供している API
今回使用するのは[トーク Bot API](https://developers.worksmobile.com/jp/document/5?lang=ja) と [カレンダー API](https://developers.worksmobile.com/jp/document/1007001?lang=ja)

# 開発手順

1. [LINE WORKS 認証情報の取得](https://qiita.com/kunihiros/items/54853e910ed1dff9ebdc)
2. [LINE WORKS Bot の登録](https://qiita.com/kunihiros/items/465e99e491b8be4cabdb)
3. [LINE WORKS Service API Token を取得](https://qiita.com/kunihiros/items/ce3a3677113426d6b75f)
4. Google Apps Script でコードを書く
5. Google Apps Script Home でトリガーを設定

1 ～ 3 は登録・取得手順のリンクを貼っておきましたのでそちらをご参考くださいませ。

## Google Apps Script でコードを書く

[Google Apps Script Home](https://script.google.com/home) にアクセスして「新しいプロジェクト」を作成します。
プロジェクト名は何でも良いのですが、私は `Concierge Bot` にしました。

![1611199810.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/8902f5ce-9831-63d0-1392-9d8426742351.png)

そう言えば、Google Apps Script の画面、新しくなりましたね。
まだ慣れていませんが、前より使いやすくなった気がします。

#### ライブラリの登録

Google Apps Script には javascript のモジュールみたいに `ライブラリ` という機能があります。これがとても便利。

LINE WORKS の API を使用するにあたり JWT の認証が必要になるのですが、ライブラリを登録しておけば勝手にやってくれます！ヾ(*´∀｀*)ﾉ

1. 左側メニューの「ライブラリ」にある「＋」をクリック
2. 「ライブラリを追加」画面でスクリプト ID に `1aLcCr3CWqfenPMyM0_FWIDUgRcTxsit9bO6BTx61NCXrCtkY2zbHBlod` を入力して検索ボタンをクリック
3. 「LINEWORKS」ライブラリが表示されるので、一番新しいバージョンを選んで追加ボタンをクリック

登録したら、コードを書いていきます。

```js:コード.gs
// LINE WORKS 認証情報
const CONSTS = {
  'apiId': 'xxxxxxxxx',
  'consumerKey': 'xxxxxxxxxxx',
  'serverId': 'xxxxxxxxxxxxxxx',
  'privateKey': '-----BEGIN PRIVATE KEY-----\nxxxxxxxxxx\n-----END PRIVATE KEY-----',
  'accountId': 'xxxxx@xxxxxxx',
  'serviceApiConsumerKey': 'xxxxxxxxxxxx',
  'serviceToken': 'xxxxxxxxxxxxxxxxxxxxxxxxx',
  'botNo': 'xxxxxxxxxxxx'
}

// メインプログラム
function main() {
  const events = getLineworksCalendar()
  LINEWORKS.sendMsg(CONSTS, CONSTS.accountId, `今日の予定は ${events.length}件です`)
  events.forEach((event) => {
    const ical = separateIcal(event.ical)
    let time
    if(ical['DTSTART;VALUE=DATE']){
      time = '終日の予定だよ'
    }　else if(ical['DTSTART;TZID=Asia/Tokyo']){
      const start = getTime(ical['DTSTART;TZID=Asia/Tokyo'])
      const end = getTime(ical['DTEND;TZID=Asia/Tokyo'])
      time = `${start.hour}時${start.minute}分から${end.hour}時${end.minute}分の予定だよ`
    }
    LINEWORKS.sendLink(CONSTS, CONSTS.accountId, `${time}\n●${ical.SUMMARY}`, '予定の詳細を確認', event.viewUrl)
  })
}

// ical データを解析してオブジェクトに変換
function separateIcal(ical){
  const splitN = ical.split('\n')
  let icalArray = {}
  splitN.forEach((e) => {
    const splitC = e.split(':')
    icalArray[splitC[0]] = splitC[1]
  })
  return icalArray
}

// ical データ内の時刻をオブジェクトに変換
function getTime(time){
  const t = time.split('T')[1]
  return {
    'hour': t.slice(0,2),
    'minute': t.slice(2,4),
    'second': t.slice(4,6)
  }
}

// LINE WORKS カレンダーから今日の予定をすべて取得
function getLineworksCalendar() {
  const today = new Date()
  let tomorrow = new Date()
  tomorrow.setDate(tomorrow.getDate() + 1)

  const startDate = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyyMMdd')
  const endDate = Utilities.formatDate(tomorrow, 'Asia/Tokyo', 'yyyyMMdd')
  const uri = `https://apis.worksmobile.com/r/${CONSTS.apiId}/calendar/v2/users/me/calendars/defaultCalendarId/events?rangeDateFrom=${startDate}&rangeDateUntil=${endDate}`
  const headers = {
    'consumerKey': CONSTS.serviceApiConsumerKey,
    'Authorization': `Bearer ${CONSTS.serviceToken}`
  }
  const options = {
    'method': 'get',
    'headers': headers
  }
  try {
    //成功時
    const res = JSON.parse(UrlFetchApp.fetch(uri, options))
    Logger.log('Success:')
    return res.returnValue    
  } catch(e) {
    //エラー時
    Logger.log('Error:')
    Logger.log(e)
  }
}
```

一番最初の LINE WORKS 認証情報のところは自分の環境での情報を入れてください(*'▽')

## Google Apps Script でトリガーを設定

画面左側の時計マークをクリックして、トリガーの画面を表示します。
右下の「トリガーを追加」をクリックしてトリガーを追加します。

![1611210433.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/57d1ced0-b504-30aa-c8d1-330fc11ceba2.png)

実行する関数は `main` で `日付ベースのタイマー` を設定します。
私は 9時までには送ってきて欲しいので `午前8時～9時` を選択しました。

![1611210596.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/f844f44c-965c-f7d4-2d5f-51c3c81f2891.png)

![1611210616.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/ae960883-08c6-9f47-a387-fb09ac891413.png)

すると、朝になると今日一日の予定を通知してくれます。

![1611198006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/f00f3699-86c6-7584-3634-ffd030ff713c.png)

これで完成です！やったね♪(*‘∀‘)

# コーディングで苦戦したところ

- ical データの解析

Node.js には [ical](https://www.npmjs.com/package/ical) パッケージがあるんですけどね。
GAS では見当たらなかったので自分で分解して必要なデータだけ取り出しました。
split ２回もしてめんどかったです。

- ical の日付の解析

これもね、[moment](https://momentjs.com/docs/) の GAS 版ライブラリを使おうかと思ったんですけどね。
トップページを和訳して読んでみたら、もう moment.js は更新しないんですって。
使わない方が良いらしいので、自前で解析しました。
良いライブラリが他にありましたら教えてください(*_ _)

ちなみに ical データ内の時刻データはこんな感じです。
EX) `20210121T170000`

RFC 基準に沿った書き方のようですが、RFC をよく読むとフォーマットが微妙に違うようなのは私のきのせいでしょうか・・・？
RFC EX) `1985-04-12T23:20:50.52Z`

兎に角。
ical データはあんまり扱ったことがなかったので割と苦戦しました。( ´Д｀)=3 ﾌｩ

# おわりに

ここまでお付き合いいただきありがとうございました。

冒頭にも書きましたが、自分がやっている単純な動作を一つ自動化しただけなのですが、スムーズに動けるというか、少し肩こりが取れた的な感じがします。

業務改善って考えると何やら難しく考えがちですが、こんな感じの積み重ねなのかもしれませんね。

ではまた！(^^)/

# 参考にさせていただきましたm(_ _)m

[LINEWORKS Developers](https://developers.worksmobile.com/jp/document?lang=ja)