[無料][2024年版] LINE Messaging API v3 + Python(Flask) でボットを作る [その10 - リッチメニュー & LIFF 編]

## LIFF とは

https://developers.line.biz/ja/docs/liff/overview/

>LINE Front-end Framework（LIFF）は、LINEヤフー株式会社が提供するウェブアプリのプラットフォームです。このプラットフォームで動作するウェブアプリを、LIFFアプリと呼びます。

> LIFFアプリを使うと、LINEのユーザーIDなどをLINEプラットフォームから取得できます。LIFFアプリではこれらを利用して、ユーザー情報を活用した機能を提供したり、ユーザーの代わりにメッセージを送信したりできます。

要するに、**LINE上で動作するWebアプリのことをLIFFアプリ**と呼びます。"LIFF" だけだとそのプラットフォームのことですね。

これはあくまで Webアプリなので、その作成のためには Messaging API の範囲を超える知識が必要になります。HTML と 多少の JavaScript は必須です。幸いにして現在のボットは Flask で実装しており、これを使えば Webアプリも簡単に作れます（というか、そっちが一般的な使い方だと思います）。

Messaging API の範囲を超える、と書きましたが、それこそが LIFF を使うメリットです。例えば現状のボットでは `lyrics Let It Be` のように曲名の前にモード呼び出しコマンドを付けてメッセージ送信しなければなりませんが、Webアプリでテキスト入力フォームを用意し各モードに対応するボタンを押す、なんて実装もできるようになります。これならばモードコマンドの入力ミスもありませんし、より直感的な操作が可能です。

LIFFアプリはあくまで Webアプリです。普通にブラウザで開いて検索するような LINE と一切関係ない Webアプリを開発することもできますが、それだといちいち LINE を閉じてブラウザを開いて...のような作業が必要になります。その手間を省くことができるのが、LINE内で開ける Webアプリである LIFFアプリだということです。

## LIFF アプリ用チャネル登録

まずは LIFF用のチャネルを作らなければなりません。[LINE Developers](https://developers.line.biz/console/) から管理画面に入り、新規チャネル作成をします。

![Screenshot 2024-01-09 at 15.29.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/f32b3f7a-831c-92ec-ec02-de33f62517db.png)

ここで Messaging API ではなく、一番左の**LINEログイン**を選択します。

![Screenshot 2024-01-09 at 15.32.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/de05a9ac-2167-1758-d6f3-94f5358de3b6.png)

チャネル名などを入れます。アプリタイプは**ウェブアプリ**を選択します。2段階認証はオフでいいです。

![Screenshot 2024-01-09 at 15.37.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/034a4502-378e-e5f0-7bc2-4845d919acff.png)

チャネル作成はこれで終了です。次に LIFFアプリを作成します。

![Screenshot 2024-01-09 at 17.05.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/3a3207f9-a7e3-9486-9c53-5e9278c141ef.png)

サイズは作成するウェブアプリ次第ですが、ここでは Tall を選択しておきます。エンドポイントは、デプロイ先の Render のURLを入力します。

![Screenshot 2024-01-09 at 17.14.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/415706d0-d5fe-96f3-574a-3be5ea3428a9.png)

Scope は `chat_message.write` にチェックをつけて下さい。これがないとメッセージ送信ができません。友達追加はオプションはオフで大丈夫です。

![Screenshot 2024-01-09 at 17.16.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/402b437d-ba81-b83a-c581-becd857837c4.png)

これで LIFFアプリの作成は完了です。


## Flask 側の設定

Flask での ウェブ作成の方法に関しては、他の良記事も多いのでそちらを参考にしてもらうと良いと思います。[3年くらい前に私が書いた記事](https://qiita.com/nozomiyamada/items/8d75ff32dad5b0976738)もあります。

ディレクトリ構成は以下のような感じになります。`templates` は html ファイルを入れるためのフォルダです。今回のアプリは `index.html` 内に記述します。

`static` は cssファイルやその他データを入れるためのフォルダです。今回は [Bootstrap](https://getbootstrap.com/) を使って作成しているので、その関連ファイルを入れていますが、見栄えにこだわらないなら不要です。

~~~
linebot/
 ├ templates/
 |  └ index.html  ## LIFFアプリのHTML 
 ├ static/
 ├ venv/  ## 仮想環境
 ├ .env  ## チャンネルトークン記載
 ├ .gitignore
 ├ data/
 |  ├ beatles.csv
 |  └ xxx.csv
 ├ requirements.txt  ## ライブラリ一覧
 ├ xxx.py ## モジュール化した .py ファイル達
 └ app.py ## メインプログラム
~~~

今回の LIFFアプリでは「検索文字を入れてモードごとのボタンを押すと、チャット内に対応するメッセージが送られる」という仕様にする予定です。つまり、歌詞検索の場合には `lyrics` という prefix を入力しなければなりませんが、それを自動でやってくれるということですね。

結局受け取るメッセージは全く同じものなので、Python 側の処理自体を変える必要は一切ありません。唯一変えるのは、元々あった `Hello world!` と表示するだけの確認ページの部分です。これを、LIFFアプリのページにしてしまいましょう。`render_template` のインポートも忘れないで下さい。こうすることで、トップページにアクセスした際に `index.html` が表示されるようになります。

~~~python:app.py
from flask import Flask, request, abort, render_template

## LIFF APP
@app.route('/', methods=['GET'])
def liffpage():
	return render_template('index.html')
~~~

## リッチメニューからLIFFアプリを開く

LINEボットの中でどうやって LIFFアプリを開くかは様々な方法がありますが、やはり一番楽なのはリッチメニューをタップさせることでしょう。小サイズのリッチメニューを作り、そこに LIFFアプリへのリンクを張るということですね。

リッチメニューは Messanger API で詳細な設定もできますが、今回はただ LIFFアプリを開くだけのものなのでそこまでこだわる必要もありません。[LINE Officai Account Manager](https://manager.line.biz/) からテンプレートを使って作りましょう。

左側のリッチメニューから作成画面に入ります。

![Screenshot 2024-01-09 at 16.22.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/d118ad98-4920-3223-b06e-2257fd0e78a5.png)

表示期間を限定する必要はないので、何年も先に設定しておきます。

![Screenshot 2024-01-09 at 16.28.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/9b91bcde-eadf-4fb2-5347-501a330ce190.png)

テンプレート選択ですが、LIFF アプリを開くだけのボタンなのでコンパクトサイズにします。今回のボットでは「使い方の説明」「クイズモード」「LIFFアプリを開く」の3機能を付けます。ようにしますので、3領域のものを選びます。

![Screenshot 2024-01-09 at 16.32.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/28665065-4ac8-1589-2e90-cfa622bbe66f.png)

画像は必須要素ですが、アップロードする必要はなく、下側のテンプレートから作れます。領域ごとにテキストやアイコンを使って簡単な編集ができます。作成したリッチメニューは画像として保存しておくことも可能です。

![Screenshot 2024-01-09 at 16.33.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/37422990-b917-4f98-c79a-f9028244f0cb.png)

いい感じにできました。

![Screenshot 2024-01-09 at 16.53.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/956e181f-4682-1a19-c74d-e23c65957af5.png)


各領域にアクションを設定します。今回の場合、`How To Use` と送信すると使い方が表示される機能、`Quiz` と送信するとクイズが始まる機能はすでに実装しているので、左の2つはテキストを選択し、メッセージを入れます。LIFFアプリを開く右側は、リンクを選択します。URL は先ほど作った LIFFアプリの URL を入れます。

![Screenshot 2024-01-09 at 17.19.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/f782f0fa-8dae-8e32-696a-c90e5be99cfd.png)


一番下でラベルの設定と、初期状態でメニューを開いておくかを選択します。これはお好みで設定して下さい。

![Screenshot 2024-01-09 at 16.56.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/57752008-d9e5-3739-3257-299f1a725bf9.png)

これでリッチメニューの作成は完了です。反映には少し時間がかかります。クイズボタンを押すと、きちんとクイズがスタートします。LIFFアプリの部分はまだ作っていないのでエラーになります。

![IMG_6024.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/8c62a864-1abd-d2c8-f538-adc20a01aa2a.png)


## `index.html` 作成

ではメインの LIFFアプリを作っていきましょう。今回必要なものは

- 曲名を入れるためのインプットフォーム
- 各モードに対応したボタン

だけです。LINE内で開くだけなので、シンプルな構成でいいです（というか、そんなオシャレなページを作る能力がない）。完成品はこんな感じになります。

<img width=300 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/f94773ae-7fd1-cdac-2af8-69b6a8beed98.jpeg>


では早速ですが、html を書いていきます。LINE内でのアプリなので、viewport は必ず設定しておきましょう。Bootstrap を使っていますが、HTML 部分は単に入力フォームとボタンを配置しているだけです。

各ボタンの中に設定されている `onclick="send_message();"` がボタンを押した時の動作を決める関数です。変数として各モードのコマンド (prefix) を受け取るようになっています。

~~~html:index.html
<!DOCTYPE html>
<html>
<head>
  <title>Beatles Bot</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="static/css/bootstrap.min.css" rel="stylesheet">
  <script charset="utf-8" src="https://static.line-scdn.net/liff/edge/versions/2.22.3/sdk.js"></script>
</head>

<body>
  <section class="text-center">
    <div class="container">
      <p>
        <input type="text" class="form-control-lg mt-3" id="songtitle" placeholder="enter song title">
      <div class="d-grid gap-1 col-10 mx-auto">
        <button class="btn btn-danger btn-lg" onclick="send_message('');">YouTube</button>
        <button class="btn btn-success btn-lg" onclick="send_message('lyrics');">Lyrics</button>
        <button class="btn btn-primary btn-lg" onclick="send_message('bass');">Bass</button>
        <button class="btn btn-primary btn-lg" onclick="send_message('basstab');">Bass TAB</button>
        <button class="btn btn-primary btn-lg" onclick="send_message('info');">Song Info</button>
        <button class="btn btn-primary btn-lg" onclick="send_message('random');">Random Song</button>
      </div>
    </div>
  </section>
</body>

<script>
  liff.init({
    liffId : "<作ったLIFFのID>"
  }).catch((err) => {
      alert(err);
  });

  function send_message(prefix){
    songtitle = document.getElementById('songtitle').value.trim();
    // validate input form
    if(prefix != 'random' && songtitle == ''){
      alert('enter song title');
    }else{
      liff.sendMessages([{
        type: 'text',
        text: prefix + ' ' + songtitle
      }]).then(() => {
        liff.closeWindow();
      })
    };
  }
</script>
</html>
~~~


まず、`<head>` の中で以下のように LIFF の SDK を読み込む記述をします。

~~~html:index.html
<script charset="utf-8" src="https://static.line-scdn.net/liff/edge/versions/2.22.3/sdk.js"></script>
~~~

LIFF アプリを動かすための初期化作業が `liff.init` の部分になります。ここに作成した LIFF の ID を入力します。`catch` はエラーが起こった時に警告メッセージを表示するためのものですが、ユーザーが見ても混乱するだけなのでなくても構いません。

https://developers.line.biz/ja/docs/liff/developing-liff-apps/#initializing-liff-app

~~~html:index.html
<script>
  liff.init({
    liffId : "<作ったLIFFのID>"
  }).catch((err) => {
      alert(err);
  });
</script>
~~~

メッセージを送信するためのメイン関数がこちらです。まず入力フォームに入っている値を `document.getElementById('songtitle').value` で取得し、前後に入っている余計なスペースを `.trim()` で削除します。ランダムモード以外は曲名の入力が必須ですから、曲名が空文字の際には警告が出るようにしています。

`liff.sendMessages` がチャット内にメッセージを送るためのメソッドです。テキストメッセージなので `type:'text'` とし、各モードの prefix をつけて送信します。`.then()` は終了後の処理で、`liff.closeWindow();` によって LIFFアプリを終了して自動でメッセージ画面に戻るようにしています。エラー処理のために、`.catch()` をつけることも可能です。

~~~html:index.html
<script>
  function send_message(prefix){
    songtitle = document.getElementById('songtitle').value.trim();
    // validate input form
    if(prefix != 'random' && songtitle == ''){
      alert('enter song title');
    }else{
      liff.sendMessages([{
        type: 'text',
        text: (prefix + ' ' + songtitle).trim()
      }]).then(() => {
        liff.closeWindow();
      })
    };
  }
</script>
~~~

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

あとはこれをいつも通りいつも通りデプロイするだけです。LIFFアプリのURLは使い回しできますので、動作を確認したい場合はテスト用ボットの方で同じようにリッチメニューを作り、ngrok でトンネリングして試すといいです。

## LIFFアプリの公開

デプロイが完了したら、LIFFアプリを公開します。そのままでは自分以外のLINEアカウントからはアクセスできないようになっているからです。

<img width=300 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/9d71cda9-e560-1e34-51f6-8535732c63dd.jpeg>

[LINE Developers](https://developers.line.biz/console/) から LIFFの設定画面に入り、「開発中」のボタンを押します。そうするとチャンネルを公開するかどうか問われますので、公開を選びます。これで他のユーザーも使えるようになります。

![Screenshot 2024-01-09 at 17.54.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/86800dd8-63d4-965e-3557-5232944eb4b3.png)

## 注意点

#### 初回のみ認証が必要


画面をスクショし忘れてしまったのですが、自分・他ユーザー共に、「初めて LIFF アプリを開く時だけ」同意画面が出てきます。他ユーザーに使ってもらう時にはこれが少しめんどくさいところではあります。

#### タイムラグ

LIFFアプリを開くために、多少のロード時間が必要です。今回の場合は既にに存在する機能をGUI化したようなものなので、速さを求めるならキーボード入力を使えばいいとも言えますが、それなり気になるレベルの差です。

#### 自動でフォーカスを合わせられない

LIFFアプリを開くと同時に、入力フォームにカーソルを合わせておきたいのですが、そもそもスマホ (iPhone) だとそれはできないようです。`.focus()` を使ったり、タッチイベントを強制的に起こすなどを試してみましたがダメでした。もし裏技的なものがあればぜひ教えて頂きたいです。

## 次回予告...?

とりあえずここまでで、ボットとしてはほぼ完成し、紹介したいことは一通り紹介できました。今後も少しずつ改良していく予定ですが、今回で一段落とします。皆様のお役に立てば幸いです。ぜひ面白い LINEボットを開発して下さい。

完成品のボットはこちらからお試し下さい。

LINE BOT ID : `@711mvjit`

![711mvjit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/e5bb1e26-d1ce-8353-e1af-f274a15e2393.png)

## 目次 : [無料][2024年版] LINE Messaging API v3 + Python(Flask) でボットを作る

[GitHub レポジトリ](https://github.com/nozomiyamada/linebot/tree/main)
`older_version` 内に、各回時点での `app.py` と `requirements.txt` があります。

- [その0 - v3 での変更点](https://qiita.com/nozomiyamada/items/dff5f240e8c936d731fd)
- [その1 - 準備編](https://qiita.com/nozomiyamada/items/f6c6816e02359e08a74e)
- [その2 - テストアプリ編](https://qiita.com/nozomiyamada/items/87389aacb151b8418501)
- [その3 - 情報検索理論(前編)](https://qiita.com/nozomiyamada/items/d1bcedcc885c43794ff7)
- [その4 - 情報検索理論(後編)](https://qiita.com/nozomiyamada/items/6b0b30ebc36eeddc73d2)
- [その5 - tips 編](https://qiita.com/nozomiyamada/items/58918cd4024987ad1979)
- [その6 - モジュール化編](https://qiita.com/nozomiyamada/items/b80eaf5aef0c294c4acf)
- [その7 - クラス化編](https://qiita.com/nozomiyamada/items/c8315c8a0f0f7d19129b)
- [その8 - Flex Message 編](https://qiita.com/nozomiyamada/items/1be9ae292b01fb35273e)
- [その9 - ポストバック編](https://qiita.com/nozomiyamada/items/8a236a2d38f33827b4ce)
- その10 - リッチメニュー & LIFF 編