12
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 Bot 開発入門] LINE Bot Serverを作ってみる その2

Posted at

はじめに

LINE Bot Serverを作ってみる その1のつづきです。
前回はMessaging APIチャンネルを開設してEC2インスタンス上にサンプルのBotサーバーをセットアップして使ってみました。今回はサンプルのソースコードを説明するかたちでBotサーバーの作り方を解説していきます

サンプルのソースコードはこちらです
https://github.com/bathtimefish/line-bot-server-example

Botサーバーの基本

Botサーバーは要するにWebサーバーです。WebサーバーはLINE PlatformからのWebhookを受信してLINE Messaging API SDKを使ってLINE Platformにレスポンスを返すのが主要な機能です

messaging-api-architecture

ユーザーがLINEアプリで入力したテキストや画像等のメッセージはMessaging APIチャンネルを介してWebサーバーにPOSTリクエストされます。POSTリクエストにはメッセージデータとともにreply tokenなど返信のための情報等が含まれます。Webサーバーは受信したメッセージに応じた処理を返すように作ります。レスポンスを返すために必要な処理はLINE Messaging API SDKにラッピングされているのでSDKの各プログラミング言語バインディングを利用すると簡単に開発できます

つまりBotサーバーは以下の2点が特長を持つWebサーバーと言えます

  • LINE PlatformのWebhookを受け付けるためのPOST method APIを持つ
  • LINE Messaging SDKを利用している

サンプルのBotサーバーはnode.jsで動作するWebサーバーでWebサーバーFrameworkとしてFastifyを利用しています。開発言語はTypeScriptです

Fastify

Fastifyはexpress等に比べて軽量高速で知られるWeb server frameworkです。ルーターや認証等の拡張機能はプラグインとして開発するよう設計されており、リクエストデータのvalidationをJSON Schemaで記述できるなどの特長を持っています

軽量で拡張しやすいWebサーバーを開発できるのでぼくは最近ほとんどの案件でFastifyを採用しているんですが、まだあまり日本語ドキュメントが見当たらないので、ここでは少しFastifyにも触れておこうと思います

まず、index.tsを見てみます

index.ts
import server from './server';

try {
  const port = process.env.APPSERVER_PORT || 443;
  server.listen(port, '0.0.0.0', (err, address) => {
    if (err) {
      console.error(err);
      process.exit(1);
    }
    console.info(`Starting LINE Bot Example Server listening at ${address}`);
  });
} catch (e) {
  console.error(e);
  process.exit(1);
}

ここではserver.tsで作成したFastify ServerオブジェクトにIPアドレスとPort番号を与えて起動することだけをやっています

server.ts
import fastify from 'fastify';
import fastifyCors from 'fastify-cors';
import fastifyStatic from 'fastify-static';
import { readFileSync } from 'fs';
import path from 'path';
import config from './config';

const options = {
  logger: false,
  https: {
    key: readFileSync(path.join(__dirname, config.certFilePath.key)),
    cert: readFileSync(path.join(__dirname, config.certFilePath.cert)),
  },
};
const server = fastify(options);
server.register(fastifyCors, {
  exposedHeaders: ['X-Total-Count'],
});
server.register(fastifyStatic, {
  root: path.join(__dirname, '../public'),
  prefix: '/',
});

server.get('/', async (_, rep) => {
  rep.sendFile('index.html');
});

// routers
server.register(import('./routers/messaging'));

export default server;

server.tsではserverオブジェクトを生成し必要なプラグインを登録しています。まずオプションでhttpsを有効にしサーバー証明書を読み込んでいます。次にfastify-corsでCORSを設定しfastify-staticで静的ファイルを置くディレクトリを指定しています

ルーターはプラグインとして作成したrouters/messaging/index.tsをインポートして登録しています。このrouters/messaging/がこのBotサーバーの要です

routers/messaging/index.ts
import { FastifyInstance } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
import { postHandler } from './post';

const name = 'messaging';

const router =  function (server: FastifyInstance, _: any, next: any) {

  server.post(`/${name}/event`,  {
    handler: postHandler,
  });

  next();
};

export default fastifyPlugin(router, {
  name,
  fastify: '3.x',
});

このルータープラグインでPOSTメソッドを定義しています。ハンドラは次のpost.tsに記述しています

line-bot-sdk-nodejs

routers/messaging/post.ts
import { FastifySchema, RouteHandlerMethod } from 'fastify';
import line from '@line/bot-sdk';
import { dispatchMessageEvent } from '../../lib/event';

export const postSchema: FastifySchema = {
  body: {
    type: 'object',
    required: ['destination', 'events'],
    properties: {
      destination: { type: 'string' },
      events: { type: 'array' },
    },
  },
};

export const postHandler: RouteHandlerMethod = async (request, reply) => {
    const body: line.WebhookRequestBody = request.body as any;
    const destination: string = body.destination;
    const events: line.WebhookEvent[] = body.events;
    console.log(`Destination: ${destination}`);
    console.log(`Events: ${JSON.stringify(events)}`);
    if (events.length < 1) reply.send({}); // for webhook test in developer console
    const event = events[0];
    let response;
    if (event.type === 'message') { // request body is a message event
      [response] = await dispatchMessageEvent(event);
    }
    if (!response) throw 'Server could not have a valid response message';
    console.log(`LINE Request ID: ${JSON.stringify(await response)}`);
    reply.send(response);
};

ここでLINE Messaging API SDKが登場します。node.js向けのline-bot-sdk-nodejsをインポートしています

このpostHandlerがLINE Platformから送信されたWebhook POSTリクエストを受けるための関数です。リクエストボディはdestinationeventsというオブジェクトを持ちます。ドキュメントによるとdestinationはWebhookイベントを受信すべきボットのユーザーIDでeventsはユーザーからのテキストメッセージ等Message Eventのリストです。

eventsには複数のMessage Eventが含まれる可能性があるので複数のオブジェクトが処理できるようにする必要がありますが、このサンプルでは簡単のためにeventsの先頭のMessage Eventのみをピックアップして処理するようにしています。

ピックアップしたMessage EventをdispatchMessageEventで解析し、返信用のMessageを作成してLINE Platformに返信しています。

そのあとdispatchMessageEventが返すresponseオブジェクトをサーバーのレスポンスとして返します。

続きは次回に

最後にふれたdispatchMessageEventが実質Botのビジネスロジックを実装している関数でLINE Massaging API SDKの具体的な使い方に関係する部分なんですが長くなったので続きは次回に書きたいと思います。

本記事ではLINE Botサーバーの基本的なアーキテクチャをFastifyを使って開発する方法を紹介しました。Botサーバー開発のご参考になれば幸いです。

12
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
12
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?