金沢のレンタサイクル「まちのり」の空き状況を教えてくれるLINE Botを作った

![IMG_3004.JPG](https://qiita-image-store.s3.amazonaws.com/0/38478/7f4bf472-3ae4-0092-fb45-79376f4a6e8f.jpeg)

金沢には「まちのり」というレンタサイクルサービスがあります。市内22箇所のポートで貸出・返却ができ、30分以内に返せば何度乗っても200円で済むので、観光の移動手段に持って来い[^1]のサービスです。

上述したとおり非常に便利なサービスですが、近年では北陸新幹線が開通し観光客増加によってポートまで行ったのに自転車がないという場面に遭遇することが何度かありました。[^2]

[^1]: 弁当忘れても傘忘れるなという言葉があるくらい雨が多い地域なので突然の雨にはご注意を
[^2]: [まちのり公式サイト](http://www.machi-nori.jp/)を見れば空き状況が分かるので回避できる

そこで今回はLINE Botの開発の勉強も兼ねて、まちのりの空き状況を教えてくるBotを作ってみました。

## デモ

![usage.png](https://qiita-image-store.s3.amazonaws.com/0/38478/ffc118dd-4915-ac7a-abe5-d9ce7855f5a5.png)
LINE Botに対して位置情報を投げると、現在地周辺で1台以上の自転車が貸し出しできるポートを3件教えてくれます。

![usage2.png](https://qiita-image-store.s3.amazonaws.com/0/38478/03a15d4e-ceb1-093f-3c17-3ed13dca3bc4.png)
また、現在地からポートまでの道順をGoogle Mapsを使用して案内してくれる機能もあわせてつけています。

<img width="512" alt="20171201002300.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/b3a5267d-f31d-e28c-c431-e3c098285e43.png">
今回作ったBotは金沢空きチャリBotという名前でLINEに登録しており [^3]、上記QRコードをLINEアプリで撮影すると簡単に試すことができます。

[^3]: まちのりAPIの公開中止やAWSのリソース整理で予告なしで削除することがありますのでご了承ください

## 構成
![architecture.png](https://qiita-image-store.s3.amazonaws.com/0/38478/450d840d-830f-cb24-897d-6daad4a1d313.png)

Botが動作する仕組みとしてはLINEアプリから位置情報を送信するイベントをトリガーにLambdaが動作して、まちのりAPIから取得した情報を加工して現在地周辺の貸出可能ポート情報をメッセージとして送り返す形となっています。

API GatewayとLambdaを組み合わせて実装し、AWS SAMを使って構成をテンプレート化しました。

## まちのりAPI
金沢市では公共データを二次利用可能の形式で提供するオープンデータの取り組みに力を入れているようです。今回利用したまちのりAPIはオープンデータを使用したアプリコンテスト用に提供されており、各ポートごとの位置情報や自転車の貸出・返却可能台数が取得できます。

[KANAZAWA Civic Tech Contest 2017 | KANAZAWA-AIR](https://www.kanazawa-air.com/kanazawa-civic-tech-contest-2017/)

```bash
$ curl https://interstreet.jp/kanazawa/api/get_info | jq
{
  "port": [
    {
      "number": "00",
      "name": "事務局",
      "name_english": "Machinori Office",
      "latitude": 36.576959,
      "longitude": 136.651474,
      "available_bikes": 2,
      "racks": 0,
      "empty_racks": 0
    },
    {
      "number": "01",
      "name": "金沢駅Ａ",
      "name_english": "Kanazawa Station A",
      "latitude": 36.576393,
      "longitude": 136.649853,
      "available_bikes": 6,
      "racks": 20,
      "empty_racks": 13
    },
    ...
}
```

緯度・経度の情報を取得できるので、ポートまでの徒歩でかかる時間や道順の案内機能に利用しています。

```javascript
const distance = geolib.getDistance(
    { latitude: 51.5103, longitude: 7.49347 },
    { latitude: "51° 31' N", longitude: "7° 28' E" }
);
```

[Geolib]([https://github.com/manuelbieh/Geolib)という地理情報を取り扱うパッケージを使用すれば、2点の緯度・経度を渡すだけで距離を返してくれるので簡単に実装できました。

## LINE Message API
[LINE Message API](https://developers.line.me/ja/services/messaging-api/)はBotとしての応答メッセージを送信するときに使用しています。Webhookのイベント情報にreplyTokenという値が含まれ、これがどのユーザーに返信するかの識別子となっています。送信できるメッセージのタイプにはテキストや画像の他にも種類あり、今回はカルーセルを選択しました。

カルーセルの実装は下記サイトが詳しいです。

[近くの神社を教えてくれる LINE Bot を作ってみました。 - 鷹楠日記](https://github.com/kentaro-m/kanazawa-avaiable-bike-line-bot)

```javascript
import request from 'request-promise';

const MESSAGE_API_ENDPOINT = 'https://api.line.me/v2/bot/message/reply';

export default class Line {
  constructor(token) {
    this.token = token;
  }

  async postMessage(replyToken, messages) {
    try {
      const options = {
        method: 'POST',
        uri: MESSAGE_API_ENDPOINT,
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${this.token}`,
        },
        body: {
          replyToken,
          messages,
        },
        json: true,
      };

      await request(options);
    } catch (error) {
      throw new Error(error.message);
    }
  }
}
```

## さいごに
はじめてのLINE Bot開発でしたが、ネット上を探すと分かりやすいドキュメントが多く悩むことなく実装することができました。アイデア次第で日常のちょっと困っていることを解決することに役立てそうです。

また、オープンデータを使用することも初めてで事前にどんなデータが使えるか眺めていたのですが全体的に加工が必要なデータが多そうで使用のハードルが上がっている感じがしました。

## リポジトリ
[kentaro-m/kanazawa-avaiable-bike-line-bot: 金沢のレンタサイクル「まちのり」の貸出可能なポートを案内してくれるLINE Botです。](https://github.com/kentaro-m/kanazawa-avaiable-bike-line-bot)