Javascript

LIFFアプリ内でメッセージ送信をする【Create LIFF App】

LIFFアプリ内でメッセージ送信をする【Create Liff App】

こんにちは!

今回は、LINEのLIFFアプリでメッセージ送信をする手順を解説していきます。

では見ていきましょう!!

そもそもLIFFアプリとは

LINE Front-end Framework(LIFF)は、LINEが提供するウェブアプリのプラットフォームです。このプラットフォームで動作するウェブアプリを、LIFFアプリと呼びます。

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

LIFFアプリは以下のように、アプリ内で開くことができます。ChromeやSafariなどの外部ブラウザでも開くことができるようです。

今回やりたいこと

今回やりたいことは、以下になります。

  • メッセージを送信したい公式LINEアカウントで、LIFF URLを押下して、LIFFアプリを起動する
  • LIFFアプリ内の「ユーザーから管理者へ」ボタンを押したら、ユーザーから管理者へメッセージ送信する。
  • LIFFアプリ内の「管理者からユーザーへ」ボタンを押したら、管理者からユーザーへメッセージ送信する。
(リンク先にphpinfoと書いてあるのは気にしないでください笑)

LINE Developersコンソールで公式LINEアカウントを作る

LIFFアプリ用のチャネルを追加する

まずLINE Developersコンソールでプロバイダーとチャネルを作成します。

チャネルの種類は「LINEログイン」を選択して、LIFFアプリを追加しましょう。

作成方法は公式の以下の手順を踏めば、簡単に作成できます。

以下の手順で、LIFFアプリをチャネルに追加する際に、エンドポイントURLを設定する必要があります。これは、LIFF URLを利用してLIFFアプリを起動した際に、ここで設定したurlが読み込まれます。
後に「Create LIFF App」というものを使ってVueで開発環境を作りますが、ビルドしたファイルを任意のサーバーにデプロイして、エンドポイントURLとして設定する必要があります。なお、エンドポイントURLはhttpsである必要があります。

メッセージ送信用のチャネルを追加する

LIFFアプリで公式LINEアカウントにメッセージを送るには、チャネルをもう一つ作成します。

先ほどのLIFFアプリ用のチャネルと同じプロバイダー内で、チャネルの種類は「Messaging API」を選択してチャネルを追加してください。

作成方法は、以下の手順になります。

チャネルを追加したら「Messaging API設定」タブにQRコードがあるので、友達登録しておきましょう。

Create LIFF AppでLIFFアプリの開発環境をつくる

LIFFアプリの開発環境ですが、コマンド1つで構築できるCLIツールである、Create LIFF Appというものがあります。

ReactのCreate React Appや、Next.jsのCreate Next Appのように、Create LIFF Appからの質問に答えていくことで、用途に合わせたLIFFアプリのひな形を含む開発環境が生成され、すぐに開発が始められます。

動作を確認したバージョンは以下になります。

動作環境

今回の環境

  • Create LIFF App 1.1.0
  • Node.js 16.19.0
  • npm 8.19.3

使用する言語とフレームワーク

フレームワークは、以下の中から使えますが、今回はVue.jsでjavascriptを使っていこうと思います。

  • Next.js
  • Nuxt
  • React
  • Vue.js
  • Svelte

Create LIFF Appを使う

まずは、Create LIFF Appを実行していきます。

npx @line/create-liff-app


プロジェクト名を入力します。例として「my-app」にします。

? Enter your project name:  my-app


フレームワーク名と言語を選択します。今回はVueとjavascriptを選択します。

? Which template do you want to use? (Use arrow keys)
  vanilla
  react
❯ vue
  svelte
  nextjs
  nuxtjs
? JavaScript or TypeScript? (Use arrow keys)
❯ JavaScript
  TypeScript


LIFF IDを入力します。後からLIFF IDを設定することもできます。(のちに生成される.envに記述する)
LIFF IDは、LIFFアプリ用に追加したチャネル内に記述してあります。

? Please enter your LIFF ID:
Don't you have LIFF ID? Check out https://developers.line.biz/ja/docs/liff/getting-started/ (liffId)


どのパッケージマネージャを使用するかを選択します。今回はnpmを選択します。

? Which package manager do you want to use? (Use arrow keys) 
  yarn
❯ npm

そうするとLIFFアプリのひな形を含む開発環境が出来ているはずです。

LIFFアプリを起動する

npm run devコマンドを打つと、ローカルホストでLIFFアプリが起動されます。

以下の例だとhttp://localhost:5173/で起動されている状態です。

実際に遷移してみて、LIFF init succeeded.と表示されていれば成功です。

注意点!

LIFF IDを設定していない場合は、以下のように LIFF init failed. というメッセージを含む画面が表示されます。
生成されたmy-appのディレクトリ内にある.envファイルにLIFF IDを書いて、LIFFアプリを再起動してください。
LIFF IDは、LINE DevelopersコンソールのLIFFアプリ用のチャネルに書いてあります。

任意のサーバーにデプロイする

これでLIFFアプリを開発する準備ができました。

なおLIFFアプリはLINE上で動かしていくことになるので、動作を確認するにはnpm run buildでビルドした後に、生成されたファイルを任意のサーバーにデプロイした上で、URLをLINE DevelopersコンソールのLIFFアプリ用のチャネルにある[エンドポイントURL]に設定しなければいけません。エンドポイントURLはhttpsである必要があります。

任意のサーバーへのデプロイ方法は、解説しません。


サーバーにデプロイして、エンドポイントurlを設定したら、LIFF URLを友達登録した公式LINEアカウントにメッセージ送信して、以下のようにLIFFアプリを起動します。「Create LIFF App」の初期画面が表示すれば、成功です。

(リンク先にphpinfoと書いてあるのは気にしないでください笑)

ユーザーから管理者へメッセージを送る

src/App.vueを確認する

まず、生成されたmy-appディレクトリ内にある、src/App.vueの中身を見ていきましょう。

<template>
  <div>
    <h1>create-liff-app</h1>
    <p v-if="message">{{ message }}</p>
    <p v-if="error">
      <code>{{ error }}</code>
    </p>
    <a href="https://developers.line.biz/ja/docs/liff/" target="_blank" rel="noreferrer">
      LIFF Documentation
    </a>
  </div>
</template>

<script>
import liff from "@line/liff";

export default {
  data() {
    return {
      message: "",
      error: ""
    };
  },
  mounted() {
    liff
      .init({
        liffId: import.meta.env.VITE_LIFF_ID
      })
      .then(() => {
        this.message = "LIFF init succeeded.";
      })
      .catch((e) => {
        this.message = "LIFF init failed.";
        this.error = `${e}`;
      });
  }
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

最初にmounted()の中身が読み込まれまれ、liff.init() が実行されます。

このメソッドを実行することによって、のちに記述しますが、そのユーザーのアクセストークンや、メッセージを送信できるメソッドを実行できるようになります。

メッセージ送信する

それでは、ユーザーから管理者へメッセージを送っていきます。

まずmethodsを追加して、その中にsendUsertoAdminMessege()を作成します。

mounted() {
    liff
      .init({
        liffId: import.meta.env.VITE_LIFF_ID
      })
      .then(() => {
        this.message = "LIFF init succeeded.";
      })
      .catch((e) => {
        this.message = "LIFF init failed.";
        this.error = `${e}`;
      });
  },
 // ここから追加 
  methods: {  
    sendUsertoAdminMessege() {
      // メッセージ送信する
      liff.sendMessages([{
          type: "text",
          text: "ユーザーからメッセージ送信したしん!",
      }])
      .then(() => {
          console.log('Message sent');
      })
      .catch((error) => {
          console.log('Error sending message: ' + error);
      });
    }
  }

sendUsertoAdminMessege()の中で実行されている liff.sendMessages() は、メッセージ送信を送信するメソッドになります。

次にテンプレート部分に、メッセージ送信するボタンを追加します。

<template>
  <div>
    <h1>create-liff-app</h1>
    <p v-if="message">{{ message }}</p>
    <p v-if="error">
      <code>{{ error }}</code>
    </p>
    <a href="https://developers.line.biz/ja/docs/liff/" target="_blank" rel="noreferrer">
      LIFF Documentation
    </a>
    <!-- ここから追加 -->
    <div style="padding-top: 10px">
      <button @click="sendUsertoAdminMessege()">ユーザーから管理者へ</button>
    </div>
    <!-- ここまで -->
  </div>
</template>

そしたら、npm run build でビルドしたファイルをデプロイして、以下の動画のようにLIFFアプリを起動し、「ユーザーから管理者へ」ボタンを押すと、メッセージ送信ができるようになっていると思います。

(リンク先にphpinfoと書いてあるのは気にしないでください笑)

管理者からユーザーへメッセージを送る

次に、管理者からユーザーへメッセージを送ります。

管理者からユーザーへメッセージを送るには、LINEのMessage APIを使っていきます。

そのためにまずは、ユーザー情報を取得していきます。

ユーザー情報を取得する

ユーザーが、LINEでLIFFアプリを起動した後、liff.init()  を実行してログイン処理を行うと、ユーザーのプロフィール(ユーザーID、表示名、プロフィール画像、メールアドレスなど)を取得できるようになります。

ユーザー情報を取得する方法は、以下の2通りあります。

  • IDトークンを送信してユーザー情報を取得する
  • アクセストークンを送信してユーザー情報を取得する

今回は、「アクセストークンを送信してユーザー情報を取得する」方法でやっていきます。


また、LIFFアプリへのログイン処理や、アクセストークン取得は、フロント側でやりますが、実際にユーザーへメッセージを送るのは、サーバーサイド側でNode.jsを使って実装していきます。

具体的な手順としては以下のようになります。

  1. liff.init() でログインする。
  2. liff.getAccessToken() でアクセストークンを取得。
  3. サーバーサイド側にapiで2で受け取ったアクセストークンを送信する。
  4. サーバーサイド側で受け取ったアクセストークンを GET /oauth2/v2.1/verify で検証する。
  5. サーバーサイド側でGET /v2/profile でユーザーのプロフィール情報を安全に取得する。
  6. サーバーサイド側で POST v2/bot/message/push で管理者からユーザーへメッセージ送信する。

フロントエンド側

以下が全体のコードです。LIFFアプリを起動したら、ログイン処理が完了するまで、「ログイン中」と表示させるようにしています。

ログインが完了すると、「ログイン成功」とボタンが2つ表示されます。

また、ブラウザからアクセスした場合は、「LINEアプリから実行してください」と表示するようにしています。

全体のソースコードが変わっているので、コピペした方がいいかもしれません。

axiosを使ってapiを叩いているので、npm install axiosでインストールしてください

<template>
  <div>
    <h1>create-liff-app</h1>
    <div>
        {{ message }}
    </div>
    <template v-if="isBrowserCheck">
      <div>
        <div>
          <button @click="sendUsertoAdminMessege()">ユーザーから管理者へ</button>
        </div>
        <div class="kankaku">
          <button @click="sendAdmintoUserMessege()">管理者からユーザーへ</button>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
import liff from "@line/liff";
import axios from 'axios'

export default {
  data() {
    return {
      message: "ログイン中",
      error: "",
      accessToken: "",
      isBrowserCheck: false,
    };
  },
  mounted() {
    liff
      .init({
        liffId: import.meta.env.VITE_LIFF_ID
      })
      .then(() => {
        this.accessToken = liff.getAccessToken();
        if (this.accessToken == null) {
          this.isBrowserCheck = false;
          this.message = "LINEアプリから実行してください";
          return false;
        }
        this.isBrowserCheck = true;
        this.message = "ログイン成功";
      })
      .catch((e) => {
        this.message = "ログイン失敗";
        this.error = `${e}`;
      });
  },
  methods: {
    sendUsertoAdminMessege() {
      liff.sendMessages([{
          type: "text",
          text: "ユーザーからメッセージ送信したしん!",
      }]);
    },
    async sendAdmintoUserMessege() {
      const apiClient = axios.create({
        baseURL: "https://hogehogehogehogehoge",
        headers: {
          "Content-type": "application/json",
        },
      });
      await apiClient.post("/api/backend/sendMessage", {
        accessToken: this.accessToken,
      })
    }
  },
};
</script>

<style>

.kankaku {
  padding-top: 10px;
}

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

まず以下の部分で、liff.init() でログインした後に、 liff.getAccessToken()でアクセストークンを取得しています。

返り値がnullの場合は、ブラウザからアクセスしているので、LINEアプリから実行してください。

this.accessToken = liff.getAccessToken();



ログインが成功したら、表示される「管理者からユーザーへ」ボタンを押します。

そうすると、sendAdmintoUserMessege()メソッドが実行されて、以下の部分でapiでサーバーサイド側にアクセストークンを渡します。

以下の例だと、https://ghogehogehogehogehoge/api/backend/sendMessageというapiに送信しています。

const apiClient = axios.create({
  baseURL: "https://hogehogehogehogehoge",
  headers: {
    "Content-type": "application/json",
  },
});
await apiClient.post("/api/backend/sendMessage", {
  accessToken: this.accessToken
})

フロントエンド側は以上になります。

npm run buildでビルドしたファイルを、任意のサーバへデプロイしてください。

サーバーサイド側

ここからサーバサイド側での処理でNode.jsを使っています。

以下が全体のコードです。

const axiosBase  = require("axios");
const express = require("express");
const app  = express();
app.use(express.json())
app.use(express.urlencoded({
  extended: true
}))
app.use(cors())

//----------------------------------------------------------
// アクセストークンを受けとって、メッセージを送信するapi
//----------------------------------------------------------
app.post("/api/backend/token", async function(req, res) {
  // アクセストークンを検証する
  const response1 = await verifyAcessToken(req.body['accessToken'])
  if (response1.status !== 200) {
    console.log("検証エラー");
    return false
  }
  // ユーザー情報取得する
  const response2 = await getProfile(req.body['accessToken'])
  if (response2.status !== 200) {
    console.log("プロフィール取得エラー");
    return false
  }
  // 管理者からユーザーへメッセージ送信
  const response3 = await liffLineMessagePush(response2.data.userId)
  if (response3.status !== 200) {
    console.log("メッセージ送信エラー");
    return false
  }
  console.log("送信完了");
})


/**
 * アクセストークンを検証
 */
const verifyAcessToken = async (id_token) => {
  const axios= axiosBase.create({
    baseURL: 'https://api.line.me',
    headers: {
      "Content-type": "application/x-www-form-urlencoded",
    },
    timeout: 5000,
  });
  return axios.get(`oauth2/v2.1/verify?access_token=${id_token}`)
}

/**
 * ユーザープロフィール情報取得
 */
const getProfile = async (accessToken) => {
  const axios= axiosBase.create({
    baseURL: 'https://api.line.me',
    headers: {
      "Content-type": "application/x-www-form-urlencoded",
      Authorization:
      "Bearer " + accessToken,
    },
    timeout: 5000,
  });
  return axios.get(`/v2/profile`)
}

/**
 * 管理者からユーザーへメッセージ送信
 */
const liffLineMessagePush = async (userId) => {
  const axios= axiosBase.create({
    baseURL: 'https://api.line.me',
    headers: {
      "Content-type": "application/json",
      Authorization:
        "Bearer " + "チャネルアクセストークン",
    },
    timeout: 5000,
  });
  return axios.post('v2/bot/message/push', {
    to: userId,
    messages: [
      {
        "type":"text",
        "text":"管理者からメッセージ送信したしん!"
      }
    ]
  })
}

サーバーサイド側でアクセストークンを受け取り、まずは以下のようにアクセストークンを検証( GET /oauth2/v2.1/verify)していきます。

const response1 = await verifyAcessToken(req.body['accessToken'])
  if (response1.status !== 200) {
    console.log("検証エラー");
    return false
}
・・・・・・
・・・・・・
/**
 * アクセストークンを検証
 */
const verifyAcessToken = async (accessToken) => {
  const axios = axiosBase.create({
    baseURL: 'https://api.line.me',
    headers: {
      "Content-type": "application/x-www-form-urlencoded",
    },
    timeout: 5000,
  });
  return axios.get(`oauth2/v2.1/verify?access_token=${accessToken}`)
}

アクセストークンが有効である場合は、HTTPステータスコード 200 OK と、以下の情報を含むJSONオブジェクトが返されます。

{
  "scope": "profile",
  "client_id": "1440057261",
  "expires_in": 2591659
}


アクセストークンが有効の場合は、アクセストークンをAuthorization に指定して、ユーザープロフィール (GET /v2/profile)を取得します。

const response2 = await getProfile(req.body['accessToken'])
if (response2.status !== 200) {
  console.log("プロフィール取得エラー");
  return false
}
・・・・・・
・・・・・・
/**
 * ユーザープロフィール情報取得
 */
const getProfile = async (accessToken) => {
  const axios = axiosBase.create({
    baseURL: 'https://api.line.me',
    headers: {
      "Content-type": "application/x-www-form-urlencoded",
      Authorization:
      "Bearer " + accessToken,
    },
    timeout: 5000,
  });
  return axios.get(`/v2/profile`)
}

成功の場合は、HTTPステータスコード 200 OK と、以下の情報を含むJSONオブジェクトが返されます。

userIdが、メッセージ送信時に使うユーザーidになります。

{
  "userId": "U4af4980629...",
  "displayName": "Brown",
  "pictureUrl": "https://profile.line-scdn.net/abcdefghijklmn",
  "statusMessage": "Hello, LINE!"
}


先ほど取得した、ユーザーidで、Message API(POST v2/bot/message/push)を叩いて、ユーザーへメッセージ送信を行います。

ここでは、AuthorizationLINE Developersのメッセージ送信用のチャネルから取得できる、チャネルアクセストークンを指定します。

// 管理者からユーザーへメッセージ送信
const response3 = await liffLineMessagePush(response2.data.userId)
if (response3.status !== 200) {
  console.log("メッセージ送信エラー");
  return false
}
  console.log("送信完了");
・・・・・・
・・・・・・
/**
 * 管理者からユーザーへメッセージ送信
 */
const liffLineMessagePush = async (userId) => {
  const axios = axiosBase.create({
    baseURL: 'https://api.line.me',
    headers: {
      "Content-type": "application/json",
      Authorization:
        "Bearer " + "チャネルアクセストークン",
    },
    timeout: 5000,
  });
  return axios.post('v2/bot/message/push', {
    to: userId,
    messages: [
      {
        "type":"text",
        "text":"管理者からメッセージ送信したしん!"
      }
    ]
  })
}

以上になります!!

LIFFアプリから「管理者からユーザーへ」ボタンを押して、管理者からメッセージが送信されれば成功です!!

参考

-Javascript
-, , , , ,