LIFF v2でLINEログイン、QRスキャン、LIFFからメッセージ送信などを試すハンズオン #ヒーローズリーグ


(もしかしたらLIFF v2ハンズオンは国内初かもしれない。)

<img src="https://i.gyazo.com/135ed1eaecdeade8185a0f79ac8b6ccd.gif" width="200px">

## この資料について

[福井ハッカソン@ #ヒーローズリーグ 2019 by MA](https://mashupawards.connpass.com/event/147977/)のハンズオンで実施した内容になります。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/42f9fd70-2d9a-8478-30f0-0485b18de8a7.png" width="400px">

## 自己紹介

* 菅原のびすけ (@n0bisuke)
	* dotstudio, inc
	* https://www.facebook.com/dotstud10/ <- いいねしてね！
* プロトタイピング専門スクール「[プロトアウトスタジオ](https://protoout.studio)」
	* https://www.facebook.com/protoout/ <- いいねしてね！
* 国内最大のIoTコミュニティ[IoTLT](https://iotlt.connpass.com)主催
* LINE API Experts
	* [LINE API Expert第一期メンバーを発表します](https://engineering.linecorp.com/ja/blog/announcing-the-line-api-experts-2018-q1/)
* Microsoft MVP (Node.js)

## LINEのAPI群

https://speakerdeck.com/n0bisuke/line-apishao-jie

## LIFFとは

LINE Frontend Frameworkの略で、LINEアプリの内部ブラウザ上でLINEの情報を活用したWebアプリケーションを動作させることができるプラットフォームの事を指します。
LIFF環境上で動作するWebアプリケーションのことを`LIFFアプリ`と呼びます。

> 公式文面: https://developers.line.biz/ja/docs/liff/overview/

LIFFアプリはこんな雰囲気です。↓

> <img src="https://i.gyazo.com/793259987a386421a1ef281c68dfdebf.png" width="100px">

> [Messaging APIの新機能LIFFの使い方を解説します。](https://engineering.linecorp.com/ja/blog/liff-our-latest-product-for-third-party-developers/)

## これまでのLIFFで出来たこと

既存のLIFFでは大まかに以下の機能が扱えます。

* ユーザーの情報取得
* LINEにメッセージ送信
* 外部のBLEデバイスにアクセス(ただしこれは[LINE Things](https://developers.line.biz/ja/docs/line-things/)という機能になり、ちょっと別カテゴリ感なので割愛)

現状見ている限りだと、v1とv2で特段何かが出来なくなったって感じではなさそうです。
なのでこの辺の機能は

## LIFF v2のアップデート

[2019/10/16のリリース](https://developers.line.biz/ja/news/2019/10/)で`LIFF v2`がリリースされました。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/4e39e786-9d81-84bb-2612-b05f0d387026.png" width="500px">

大まかにアップデートされた内容は

### 1. 外部ブラウザでもLIFFアプリを扱うことが出来るようになった

→ 今までのLIFFアプリはLINEのブラウザ上からしかアクセス出来ませんでした。

### 2. LINEログインが扱えるようになった

→ 今までのLIFFアプリはLINEのブラウザ上からしかアクセス出来なかったので、ログインはしている前提だったのですが、v2から外部ブラウザでも扱えるようになったことにより、 **外部ブラウザからのアクセス時のみ** `LINEログイン`を扱うことができます。

### 3. QRコードスキャンが扱えるようになった

→ LIFF上からLINEアプリのQRコードスキャン機能を呼び出せるようになりました。

### 4. LIFFアプリを動作環境を細かく取得できるようになった

→ 外部ブラウザでもLIFFアプリが動くようになったので、外部ブラウザでの動作なのか、LINEアプリ内での動作なのかなど、動作環境を細かく取得できるようになりました。

## ハンズオン

今回は **これらの機能を丸っと触れてみるハンズオンになります。**
また、管理のしやすさなどを加味してVue.jsを利用しています。

### 事前準備

1. LINE BOTを作ったことがある
2. Node.jsが動く環境がある
3. ngrokが動作する環境がある
4. ローカルサーバーを起動する環境がある
	* VSCodeのLive Serverプラグインが便利です

1~3までは「[1時間でLINE BOTを作るハンズオン](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)」をやっておくと良いです。

### 今回の到達目標

1 or 2で選んで行きましょう。前での紹介としては2で進んで行きますが、1のフォローもします。

1. LINE BOTを作ったことが無い人 -> 「[1時間でLINE BOTを作るハンズオン](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)」を試してLINE BOTを作ってみるのもOK(STEP3まで)
2. LINE BOTを作ったことがある人 -> このページをこのままお進み下さい。

### 1. ngrokなどでトンネリング

`ngrok http <利用するポート名>`という形で起動させておきます。

```bash
$ ngrok http 5500
```

生成される`https://xxxxxx.ngrok.io`のアドレスをコピーしておきましょう。次の手順で利用します。

#### 補足

ngrokがインストール出来なさそうな人はserveoを利用すると良さそうです。特にインストールなどせずにsshでトンネリングが出来ます。Node.jsやnpmがそもそも入ってないよーって人はこちらの手段でもOKです。

> [servioの利用](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d#%E8%A3%9C%E8%B6%B3-serveo%E3%81%AE%E5%88%A9%E7%94%A8---ngrok%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%8C%E3%81%86%E3%81%BE%E3%81%8F%E3%81%84%E3%81%8B%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88%E3%81%AA%E3%81%A9)

```bash
$ ssh -o ServerAliveInterval=60 -R <利用したいドメイン名>:80:localhost:<ポート> serveo.net
```

実際はこんな感じ

```bash
$ ssh -o ServerAliveInterval=60 -R n0bisuke:80:localhost:3000 serveo.net
```

```
Forwarding HTTP traffic from https://n0bisuke.serveo.net
Press g to start a GUI session and ctrl-c to quit
```

この場合は次の手順で設定するエンドポイントURLは`https://xxxxxx.serveo.net`を設定しましょう。

### 2. LIFFアプリの登録

LINE DevelopersのLINE BOTの管理画面から`LIFFのタブ`を選択し、`追加ボタン`を押しましょう。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/46293995-bd53-9284-d2be-3b61edcab89b.png" width="400px">

`エンドポイントURL`に **先ほどのngrokのアドレスを指定**し、キャプチャのようにチェックをして`追加する`を押しましょう。

> <img src="https://i.gyazo.com/cba103dd28192ff1072fb4824fa51950.png" width="400px">

作成したLIFFアプリケーションを確認し、`LIFF URL`の **line://app/xxxxxxxxのxxxxxxxxをメモ**しておきましょう。

> <img src="https://i.gyazo.com/9642974cc76b10775f666d5db19c8a87.png" width="400px">

### 3. 外部ブラウザでの挙動とLINEログインを試す

まずは外部ブラウザでの挙動とLINEログインを試していきます。

#### 3.1 コードを書いていく

任意のフォルダを作成し、その中に`index.html`と`script.js`を作成しましょう。

```index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
        <title>LIFF v2 ハンズオン</title>
    </head>
    <body>
        <div id="app">
            <button @click=logout>ログアウトする</button>

            <h1>{{displayName}}</h1>
            <p>{{userId}}</p>
            <p>{{statusMessage}}</p>
            <img :src=pictureUrl alt="profile" width="200px" />
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="https://static.line-scdn.net/liff/edge/2.1/sdk.js"></script>
        <script src="script.js"></script>
    </body>
</html>
```

```script.js
'use strict';

const app = new Vue({
    el: '#app',
    data: {
        displayName: '',
        userId: '',
        statusMessage: '',
        pictureUrl: '',
    },

    methods: {
        //プロフィール取得関数
        getProfile: async function(){
            const accessToken = liff.getAccessToken();
            const profile = await liff.getProfile();
            this.displayName = profile.displayName; //LINEの名前
            this.userId = profile.userId; //LINEのID
            this.pictureUrl = profile.pictureUrl; //LINEのアイコン画像
            this.statusMessage = profile.statusMessage; //LINEのステータスメッセージ
        },

        //ログアウト処理の関数
        logout: async function(){
            if (liff.isLoggedIn()){
                alert('ログアウトします。');
                liff.logout();
                window.location.reload();
            }
        },

    },

    //ページを開いた時に実行される
    mounted: async function(){
        await liff.init({
            liffId: 'xxxxxxxxxxx' // ! 先ほどメモしたものを入力してください。
        });

        //LINE内のブラウザかどうか
        if(liff.isInClient()){
            alert('LINE内のブラウザ');
            this.getProfile(); //LINE内で開いた場合は特にログイン処理なしで大丈夫
        }else{
        //外部ブラウザかどうか
            if(liff.isLoggedIn()){
                alert('外部ブラウザ');
                this.getProfile();
            }else{
                liff.login();
            }
        }
    }
});
```

この状態でindex.htmlでローカルサーバーを起動させましょう。

最初の手順でngrokでトンネリングさせているポート(5500)を指定して起動してください。

#### 3.2 Webサイトにアクセスして試してみる

`https://xxxxx.ngrok.io`のアドレスをPC上のブラウザなどから開いてみましょう。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/e5f02963-25eb-21ba-26db-ce2d6e6f9a20.png" width="200px"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/ac329289-c85c-7957-e814-3a3ef5a4836e.png" width="200px">

こんな感じでログイン画面に遷移します。

無事にログインできるとこんな感じでアラートが出ます。
> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/b0a2d377-d56e-e163-5bc2-e42d13f540e7.png" width="400px">

#### 3.3 ここで繋がらない人FAQ

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/71c71030-6950-6b35-772d-fa1e126d2a6f.png" width="400px">

* LIFFのIDちゃんとありますか？
	* ソースコード上で一箇所だけ書き換えがあるので注意
* `http://localhost:5500`みたいなアクセスしてませんか?
	* `https://xxxx.ngrok.io`のアドレスで

#### 3.4 LINEアプリ内からもアクセスしてみる 

LINEアプリから`line://app/xxxxxxxxxxx`のアドレスにアクセスしてみましょう。
何かしらの方法でLINEのBOTや会話などにこのアドレスを投稿して試してみるのが良いです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/7caa87d0-1d49-8aaf-048c-dddd6458c911.png" width="200px">

### 4. QRスキャンとメッセージ送信を試してみる

QRとメッセージ送信を試します。

#### 4.1 コードを書く

`index.html`と`script.js`を更新します。

```index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
        <title>LIFF v2 ハンズオン</title>
    </head>
    <body>
        <div id="app">
            <button @click=logout>ログアウトする</button>

            <button @click=sendMessage>メッセージ送信</button>
            <button @click=QR>QRスキャン</button>

            <h1>{{displayName}}</h1>
            <p>{{userId}}</p>
            <p>{{statusMessage}}</p>
            <img :src=pictureUrl alt="profile" width="200px" />
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="https://static.line-scdn.net/liff/edge/2.1/sdk.js"></script>
        <script src="script.js"></script>
    </body>
</html>
```

```script.js
'use strict';

const app = new Vue({
    el: '#app',
    data: {
        displayName: '',
        userId: '',
        statusMessage: '',
        pictureUrl: '',
    },

    methods: {
        //プロフィール取得関数
        getProfile: async function(){
            const accessToken = liff.getAccessToken();
            const profile = await liff.getProfile();
            this.displayName = profile.displayName; //LINEの名前
            this.userId = profile.userId; //LINEのID
            this.pictureUrl = profile.pictureUrl; //LINEのアイコン画像
            this.statusMessage = profile.statusMessage; //LINEのステータスメッセージ
        },

        //ログアウト処理の関数
        logout: async function(){
            if (liff.isLoggedIn()){
                alert('ログアウトします。');
                liff.logout();
                window.location.reload();
            }
        },

        //QRコードの利用の関数
        QR: async function(){
            if(!liff.isInClient()) {
                alert('LINEから開いて下さい');
                return;
            }
            //QR読み込み
            const res = await liff.scanCode();
            const msg = `読み取ったコードの中身は「${res.value}」です`;
            alert(msg);
        },

        //LINEにメッセージ送信の関数
        sendMessage: async function(){
            if(!liff.isInClient()) {
                alert('LINEから開いて下さい');
                return;
            }
            //メッセージ送信
            await liff.sendMessages([
                {
                  type:'text',
                  text:'Hello, World!'
                }
            ]);
            
            alert('メッセージを送信しました。');
        }
    },

    //ページを開いた時に実行される
    mounted: async function(){
        // alert(liff.getOS());
        await liff.init({
            liffId: 'xxxxxxxxxx' // ! 先ほどメモしたものを入力してください。
        });

        //LINE内のブラウザかどうか
        if(liff.isInClient()){
            console.log('LINE内のブラウザ');
            this.getProfile(); //LINE内で開いた場合は特にログイン処理なしで大丈夫
        }else{
        //外部ブラウザかどうか
            if(liff.isLoggedIn()){
                console.log('外部ブラウザ');
                this.getProfile();
            }else{
                liff.login();
            }
        }
    }
});
```

#### 4.2 LINEアプリからアクセスして試してみる

この機能は外部ブラウザからのアクセスでは利用できないので、LINEアプリから`line://app/xxxxxxxxxxx`のアドレスにアクセスして試してみましょう。

<img src="https://i.gyazo.com/135ed1eaecdeade8185a0f79ac8b6ccd.gif" width="200px">

### 5 チャレンジ課題

時間が余った人はチャレンジしてみましょ！

#### 5.1 公式ドキュメントを参考に送信するメッセージをカスタマイズしてみよう

> 参考: https://developers.line.biz/ja/reference/liff/#send-messages

#### 5.2 QRコードで読み込んだ文字列をLINEに送信してみよう

組み合わせチャレンジです。

---

## まとめ

お疲れ様でした。LIFF v2の機能を試すハンズオンを行いました。
目玉機能はLINEログインとQRスキャンだと思うのですが、個人的には外部ブラウザでLIFFの活用が出来るようになったというところだと思ってます。

既存のWebサービスにもLIFFアプリの機能を活用できるポイントも増えてくるかもしれないので、LINE Thingsのように、LINEがスマートフォンアプリやチャットプラットホームからさらに活用したユースケースが出てくると思います。

みんなで活用考えてみましょう！