LINE BotをNext.js + TypeScript + Netlify Functionsで作る
後でLIFFアプリも作ることを考えてNext.jsで、チャットボットのベースを作ります。
おうむ返しでは面白くないので、getProfile()
でユーザーの名前を取得するところまでやってみたいと思います。
プロジェクトを作成
$ yarn create next-app --typescript sampleapp
$ cd sampleapp
まずは動作確認。
$ yarn dev
で起動させて別のターミナルからアクセスすると
$ curl http://localhost:3000/api/hello
{"name":"John Doe"}
OKですね。
tsconfig.jsonにbaseUrl
とpaths
を追加しておきます。
"compilerOptions": {
〜
"baseUrl": "./",
"paths": {
"~/*": ["./*"]
},
〜
デプロイ
GitHubにpushして、NetlifyでAdd new site
>Import an existing project
します。
設定は特にいじらなくてOK。Functions directoryも空欄でOK。
デプロイできたらアクセスしてみます。
$ curl https://<your-domain>.netlify.app/api/hello
{"name":"John Doe"}
OKですね。
LINE Bot SDKを組み込む
$ yarn add @line/bot-sdk
$ mkdir lib
$ touch .env.local lib/line.ts pages/api/webhook.ts
↓は https://github.com/line/line-bot-sdk-nodejs/blob/next/examples/echo-bot-ts/index.ts を参考に。
import { ClientConfig, Client, middleware as lineMiddleware, MiddlewareConfig } from '@line/bot-sdk';
// Setup all LINE client and Express configurations.
const clientConfig: ClientConfig = {
channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN || '',
channelSecret: process.env.CHANNEL_SECRET,
};
const middlewareConfig: MiddlewareConfig = {
channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
channelSecret: process.env.CHANNEL_SECRET || '',
};
export const client = new Client(clientConfig);
export const middleware = lineMiddleware(middlewareConfig);
↓値はLINE Developersコンソールからコピーします。
CHANNEL_ACCESS_TOKEN=********
CHANNEL_SECRET=******
↓のconfig
とrunMiddleware
は https://nextjs.org/docs/api-routes/api-middlewares を参考に。
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { WebhookRequestBody } from '@line/bot-sdk';
import { Middleware } from '@line/bot-sdk/lib/middleware';
import * as line from '~/lib/line';
export const config = {
api: {
bodyParser: false, // Necessary for line.middleware
},
};
async function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Middleware) {
return new Promise((resolve, reject) => {
fn(req, res, (result) =>
result instanceof Error
? reject(result)
: resolve(result)
)
});
}
const handler: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
if (req.method === 'POST') {
// Validate request
await runMiddleware(req, res, line.middleware);
// Handle events
const body: WebhookRequestBody = req.body;
await Promise.all(body.events.map(event => (async () => {
if (event.mode === 'active') {
switch(event.type) {
case 'message': {
const name = event.source.userId
? (await line.client.getProfile(event.source.userId)).displayName
: 'User';
await line.client.replyMessage(event.replyToken, {
type: 'text',
text: `Hi, ${name}!`
});
break;
}
case 'follow': {
// Do something.
break;
}
}
}
})()));
res.status(200).end();
} else {
res.status(405).end();
}
} catch(e) {
if (e instanceof Error) {
res.status(500).json({ name: e.name, message: e.message });
} else {
res.status(500).end();
}
}
};
export default handler;
ビルドしてみる。
$ yarn build
yarn run v1.22.17
$ next build
info - Loaded env from ***/sampleapp/.env.local
Failed to compile.
./node_modules/@line/bot-sdk/lib/middleware.ts:69:31
Type error: Object is of type 'unknown'.
67 | next();
68 | } catch (err) {
> 69 | next(new JSONParseError(err.message, strBody));
| ^
70 | }
71 | };
72 | return (req, res, next): void => {
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
まぢか。
とりあえずtsconfig.jsonに設定を追加してエラー回避😑
{
"compilerOptions": {
...,
"useUnknownInCatchVariables": false
},
...
}
$ yarn build
yarn run v1.22.17
〜
✨ Done in 5.02s.
ということでビルドが通ったので、git commit
しておきます。
Netlifyに環境変数を設定
.env.local ファイルに設定していたのと同じ内容を NetlifyのSite settings
>Build & deploy
>Environment
で設定します。
デプロイ
先ほどcommitしたソースをGitHubにpushします。
Netlify上でPublishedになったら、追加したAPIを叩いてみます。
$ curl -X POST -H "x-line-signature: dummy-signature" -H "Content-Type: application/json" -d '{"destination":"dummy","events":[]}' https://<your-domain>.netlify.app/api/webhook
{"name":"Error","message":"signature validation failed"}
想定通りのエラーが返ってきたのでOK。
ということで、LINEのチャネルに登録します。
そしてLINEからこのチャネル当てにメッセージを送ってみると
動きました😄
Discussion