15
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LINE Messaging API SDK(Node.js)クックブック

Last updated at Posted at 2021-06-04

はじめに

最近LINEを使って色々と作業したので、備忘録的にLINE Messaging API SDK(Node.js)
使い方を記載します。
(TypeScriptで書いていますので、JavaScriptしか分からないという方はすみません。)

基本的には以下の公式ページを参考にすれば書けると思いますが、見てもよく分からんという方の参考になればいいなと思います。

LINE MessagingAPIリファレンス

準備

この章ではLINE MessagingAPIを使用するための準備をします。
準備として、Bot機能を使用し、おうむ返しBotを作ります。

環境構築

まずはじめに環境構築から始めます。
最初にexpressを使用して、最少構成のサーバを立てます。

mkdir <適当な名前>
cd <適当な名前>
npm init -y 
npm install express
npm install -D typescript  ts-node @types/express ngrok
npx tsc --init
touch index.ts

index.ts

import express from "express";

const app = express();

const port = process.env.PORT || 8080; // port番号を指定

app.post("/webhook", (req, res) => {
  res.send("準備中");
});

app.listen(port);

console.log("listen on port " + port);

ついでにpackage.jsonscripts"start":"ts-node index.ts"を追加しましょう。

package.json

{
  "scripts": {
    "start": "ts-node index.ts"
  },
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "@types/express": "^4.17.12",
    "ngrok": "^4.0.1",
    "ts-node": "^10.0.0",
    "typescript": "^4.3.2"
  }
}

最後に確認します。

npm start
//別ターミナルを開き以下、コマンド実行
npx ngrok http 8080
// httpsのURLを取得して、以下のコマンドを実行
curl --request POST '<取得したURL>/webhook'

準備中と返信が返って来ればOKです。

LINE Messaging API SDK(Node.js)の準備

とりあえずinstallしましょう。

npm install @line/bot-sdk

次に以下のようにindex.tsを変更します。

import express from "express";
const app = express();

const port = process.env.PORT || 8080;

// ~~~次からこれより上を省略します。

import * as line from "@line/bot-sdk";

const config = {
  channelAccessToken:<ボットのアクセストークン>, // コードにベタガキしない方がいいです。 
  channelSecret: <ボットのシークレットId>, // コードにベタガキしない方がいいです。
};

app.post(
  "/webhook",
  line.middleware(config),
  (req, res) => {
    res.status(200).send("ok");
  }
);

// ~~~次からこれより下を省略します。
app.listen(port);

console.log("listen on port " + port);

これで、以下のコマンドを実行してサーバを立てましょう。

npm start
//ターミナルを新しく開いて 
npx ngrok http 8080
// 前回の処理が残っていて、8080ポートが使用できない場合は、前回の処理を止めてControl - cで終了して、再実行してください。
// 知識のある方はnodemonなどをinstallしてhotreload機能を使ってください。

LINE Developpersにログインしてngrokで取得したURL/webhookを入力して、検証を押して
成功が返ってくるか確かめてください。成功が返って来れば準備は完了です!
line.png

おうむ返しBotの作成

Botに何か話しかけると同じ言葉を返すおうむ返しBotを作成します。
すでに先ほど立てたサーバとLINEとをwebhookで連携できることが確認したので、
あとは、受け取ったデータをどのように返すのかの処理を記述します。

LINEから送ってくる処理すべきリクエストの単位をeventと呼びます。(公式な呼び方かどうかは分かりません。)
1つのhttpリクエストに複数のeventがやってくる場合があるので、まずはeventのハンドリングを行います。
そしてそのeventがユーザが発したメッセージの場合だけ、おうむ返しを実行します。
実際のコードは以下です。

index.ts

// ~~省略してます
import * as line from "@line/bot-sdk";
import { TextMessage, WebhookEvent } from "@line/bot-sdk";

const config = {
  channelAccessToken:<ボットのアクセストークン>,
  channelSecret: <ボットのシークレットId>,
};

/*
  色々な処理を行うclientを準備する。
  このclientに処理を命令することによって、Botがアクションを起こす。
*/
const client = new line.Client(config);

app.post("/webhook", line.middleware(config), (req, res, next) => {
  /*
   eventはbodyの中に格納されており
   Promise.allを使用して、複数のevent処理を行う。
  */ 
  Promise.all(req.body.events.map(handleEvent))
    .then((result) => res.json(result))
    .catch((err) => next(err));
});

const handleEvent = async (event: WebhookEvent) => {
  /*
   色々なタイプのイベント毎に処理を振り分ける関数
  */
  if (event.type === "message" && event.message.type == "text") {
    /*
      メッセージが送信されてきた時の処理
    */
    const replyToken = event.replyToken
    const message = event.message.text;
    const response: TextMessage = {
      type: "text",
      text: message,
    };
    await client.replyMessage(replyToken, response);
    return;
  }
};
// ~~省略してます

メッセージeventにはreplyTokenというものが設定されており、このreplyTokenを使用することによって
発言者に対して、返信することができます。
(event全てにreplyTokenがあるわけではなく、eventのタイプによってはreplyTokenがないものもあります。)

上記のコードで、おうむ返しのBotが出来上がったと思います。実際に試して確認してみてください。
※ 上手くいかない人はnpm startnpx ngrok http 8080をやり直して、LINE Developpersのwebhook設定をやり直してみてください。

クックブック

ここからは、まとまった内容というよりは、ユースケースに合わせた内容を記載していきます。
困っている方の助けになればいいなと思います。

新規に公式Botアカウントがフォローされた時の処理

Botが新しいユーザにフォローされた時に、特別な処理を行いたい時があると思います。
Botが新しいユーザにフォローされたタイミングでもeventが発行され、webhookを通じて
サーバが知ることができます。

index.ts

const handleEvent = async (event: WebhookEvent) => {
  if (event.type === 'follow') {
    /*
      友達登録された時の処理
    */
    const userId = event.source.userId;
    const profile = await client.getProfile(userId!);
    console.log(profile.displayName);
    return;
  }
 // ~~~他の処理 
};

eventを処理するhandleEvent関数内で、event.typefollowのものを
検知して、フォローされた際のアクションを行うことができます。
userIdevent.source.userIdから得ることができ、userIdからユーザの詳細情報の
profileを得ることもできます。

クイックリプライを送る

クイックリプライの詳細についてはリンク先を参照してください。
ざっくりいうとユーザが簡単に返信できるようにするための機能です。
ここでは、テキストメッセージにクイックリプライを設定する方法を示します。
以下は、おうむ返しを行うBotに「yes」,「no」とクイックリプライを設定しています。
クイックリプライは色々なアクションに対応しているのですが、今回はpostbackアクションというものを設定しています。

const handleEvent = async (event: WebhookEvent) => {
  if (event.type === "message" && event.message.type == "text") {
    /*
      メッセージが送信されてきた時の処理
    */
    const message = event.message.text;
    const quickReplys: QuickReplyItem[] = [
      {
        type: "action",
        action: {
          type: "postback",
          label: "yes",
          data: '{"action":"yes"}',
        },
      },
      {
        type: "action",
        action: {
          type: "postback",
          label: "no",
          data: '{"action":"no"}',
        },
      },
    ];
    const response: TextMessage = {
      type: "text",
      text: message,
      quickReply:{
       items :quickReplys
      }
    };
    await client.replyMessage(event.replyToken, response);
    return;
  }
};

postbackアクションはかなり有用なアクションで、ボタンを押すとdataに記載されている情報とともに
サーバに送信されます。
※ postbackアクションのdataには好きな文字列を設定することができます。私的におすすめなのはJSON形式で書くとbackendで使いやすくなります。

postbackの受け取りについては次の節で説明します。

postbackアクションを受け取る

postbackアクションを設定しているボタンなどがクリックされるとpostbackeventが発行されます。
その受け取り方について示します。

const handleEvent = async (event: WebhookEvent) => {
  /*
    postBackアクションが送られてきた時の処理
  */
  if (event.type === "postback") {
    const data = event.postback.data;
    const parseData = JSON.parse(data);
    console.log(parseData);
    return;
  }
 // ~~~他の処理は省略
}

他の処理と同様にevent.typeでeventをハンドリングします。
postbackアクションのtypeは、"postback"になります。
data自体は文字列型なのですが、JSON形式の文字列型に設定することで、parseして扱いやすくなります。

pushMessgeを送信する

Botから特定のユーザに向けてメッセージを送信する。
この処理は送りたいuserのIDが分かっている場合に使用します。

pushMessage.ts

import * as line from "@line/bot-sdk";
import { TextMessage } from "@line/bot-sdk";
const config = {
  channelAccessToken:<ボットのアクセストークン>,
  channelSecret: <ボットのシークレットId>,
};
// ~~~ 次回以降省略します。

const client = new line.Client(config);

const pushMessage = async (userId: string) => {
  // userIdを引数にとって、pushMessageを送信する関数
  const message: TextMessage = {
    type: "text",
    text: "pushメッセージです。",
  };
  await client.pushMessage(userId, message);
};

pushMessage("U95b0ce9db0b4cb73ec243a56e8b78nou");

サーバなどは関係ないため、

npx ts-node pushMessage.ts

で特定ユーザにメッセージを送ることができます。

broadCastMessageを送信する

BotからBotと友達になっている全ユーザに向けてメッセージを送信します。

broadCastMessage.ts

const broadCastMessage = async () => {
  const message: TextMessage = {
    type: "text",
    text: "broadCastメッセージです。全てのユーザに届きます。",
  };
  await client.broadcast( message);
};

broadCastMessage();

リッチメニューを設定する

Botにリッチメニューを登録することで、ユーザがどのように使えば良いかや
Botが提供する機能について、分かりやすくなります。
ここではリッチメニューの設定方法を示します。

まず、最初にリッチメニューの背景画像を用意します。
リッチメニューの背景画像は以下の要件をみたいしている必要があります。

画像フォーマット:JPEGまたはPNG
画像の幅サイズ(ピクセル):800以上、2500以下
画像の高さサイズ(ピクセル):250以上
画像のアスペクト比(幅/高さ):1.45以上
最大ファイルサイズ:1MB

※上記のように記載されていますが、私は画像サイズが横2500px,縦1686pxの画像でのみ設定したことがありますが、
1pxでもズレるとエラーになるため、エラーで進まない場合は、横2500px縦1686pxで画像を作成してみてください。

次に、リッチメニューの画像のどこを押せば何を行うなどの情報を作成するのですが、
その作成には、LINE Bot Designerで作成するのが簡単だと思います。
※Designerの話はここでは詳しくやりません。また、別の機会に書きます。割と直感的に使えると思います。

LINE Bot Designerからリッチメニューの設定を行うと
Designerの下の方からJSON形式の情報を取得できます。(やってみたらわかるはず。適当ですみません。)

画像とJSON形式の情報を作成したら以下のスクリプトに入力してください。

richMenu.ts

import fs from 'fs';
import { RichMenu } from '@line/bot-sdk';

// LINE Designerを使用して作成
const richMenu: RichMenu = <JSONをここに貼り付け>

const registRichMenu = async () => {
  const richMenuId = await client.createRichMenu(richMenu);
  await client.setRichMenuImage(
    richMenuId,
    fs.createReadStream('<リッチメニューの背景画像へのパスを入力>')
  );
  await client.setDefaultRichMenu(richMenuId);
};

registRichMenu();
npx ts-node richMenu.ts

上記を実行することによってリッチメニューが登録できます。
client.setDefaultRichMenuはデフォルトのリッチメニューを設定します。
ユーザ毎にリッチメニューを分けたい場合は、client.linkRichMenuToUser(<userId>,<richMenuId>)
で設定することができます。

アクセストークンの検証(LINEログインとの連携)

LINEログイン(LIFF,LINE ログイン SDK)を使用したアプリケーションとLINEBotを連携させたいという要望もあったりするかなと思います。
同じプロバイダーでLINEログインのアプリとBotを作成すると、同じユーザには同じuserIDが割り振られるので、簡単に連携することが可能です。

LINEでログインを行うアプリケーションでは、ログインが行われるとLINEからアクセストークンが配布されます。
そのアクセストークンをBotが動いているサーバに送ることによって、Botが連携してメッセージを送信することができるようになります。
詳しくはこちらをご覧ください。

この時のアクセストークンの検証を方法を記載します。
少し複雑になってしまいました。後で、ちゃんと修正します。

validation.ts

import fetch from 'node-fetch';
import express from 'express';

const LOGIN_APP_ID = <LINEログインアプリケーションのID>

const LINE_AUTH_API = 'https://api.line.me/oauth2/v2.1/';
const LINE_API = 'https://api.line.me/v2/';


interface ValidationSuccess {
  client_id: string;
  expires_in: number;
  scope: string;
}
interface ValidationError {
  error: string;
  error_description: string;
}

function isValidationError(arg: any): arg is ValidationError {
  return arg.error !== undefined;
}
export const accsessTokenValidation = async (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  try {
    const token = req.headers.authorization?.replace('Bearer ', '');
    if (typeof token !== 'string') {
      throw new Error('バリデーションエラー');
    }
    const res = await fetch(`${LINE_AUTH_API}verify?access_token=${token}`);

    const data = (await res.json()) as ValidationSuccess | ValidationError;

    if (isValidationError(data)) {
      // バリデーションエラー
      console.log(data.error);
      throw new Error('バリデーションエラー');
    }

    if ( data.client_id === LOGIN_APP_ID) {
      // 認証成功
      try {
        const res = await fetch(`${LINE_API}/profile`, {
          headers: { Authorization: `Bearer ${token}` },
        });
        const profile = (await res.json()) as { userId: string };

        // userId取得後にheaderに記録、後の処理で使用する
        req.headers.userId = profile.userId;

        next();
      } catch (e) {
        next(new Error('lineのユーザ情報の取得に失敗しました。'));
      }
    } else {
      throw new Error('認証エラー');
    }
  } catch (e) {
    next(e);
  }
};

accessTokenの検証が終了すると、accsessTokenからprofileを取得して、userIdを知ることで
さまざまな処理ができるようになります。

index.ts

app.post('/api',accsessTokenValidation,(req,res,next) => {
 const userId = req.headers.userId
 const message: TextMessage = {
    type: "text",
    text: "LINEログインと連携されてメッセージが送られました。",
  };
  await client.pushMessage(userId, message);
  res.send("ok");
})

middlewareとして導入することで、好きなところでaccsessTokenの検証ができるようになります。
その後の処理でuserIdを使用してBotからメッセージを送っています。

15
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?