はじめに
最近LINEを使って色々と作業したので、備忘録的にLINE Messaging API SDK(Node.js)の
使い方を記載します。
(TypeScriptで書いていますので、JavaScriptしか分からないという方はすみません。)
基本的には以下の公式ページを参考にすれば書けると思いますが、見てもよく分からんという方の参考になればいいなと思います。
準備
この章では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.json
のscripts
に"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
を入力して、検証を押して
成功が返ってくるか確かめてください。成功が返って来れば準備は完了です!
おうむ返し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 start
とnpx 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.type
がfollow
のものを
検知して、フォローされた際のアクションを行うことができます。
userId
はevent.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からメッセージを送っています。