LINE BOTをJava Servletでお手軽に作る

LINE BOTをJavaのServletを利用してお手軽に作る方法を紹介します
紹介したソースコード一式は https://github.com/riversun/line-bot-servlet-examples.git にあります

# LINE BOTをつくる準備
Messaging APIを使える状態になっている前提でかきます。
登録方法などは別の記事に委ねます。

登録はこちらから
https://business.line.me/ja/services/bot 

# ServletでLINE BOTを作る
**HttpServlet**でLINE BOTを作ります。

LINE公式Github(https://github.com/line/line-bot-sdk-java)ではSpring Bootを利用した例が紹介されています(※)。

今回はSpring Bootではなくて、**スッピンのHttpServlet**でLINE BOTを作りたいと思います。

ご存知のとおりSpring Bootを使うとかなりお手軽にLINE BOTをつくれますが、
**HttpServlet**でもSpring Bootの例と同じくらいのお手軽感をめざします。

そのために、ヘルパーライブラリを用意しました。ライブラリといっても、内部で https://github.com/line/line-bot-sdk-java を利用している薄いラッパーですのでソースを自分のServletにとりこんで利用しても良いとおもいます。メソッド名もSpring Bootの例を参考にしました。

**ヘルパーライブラリのソースはこちら**
https://github.com/riversun/line-bot-helper


　　_※公式SDK（現時点でv1.6.0)にもline-bot-servletというものがあり、
　　　中身をみるとHttpServlet用の簡易ヘルパークラス（署名検証など）は準備してくれているようですが
　　　SpringBootのようなevent mappingはしてくれない模様。_


# メッセージへのお返事BOT　をつくる

ユーザーがBOTにむかってテキストメッセージを送ると、BOTがテキストでお返事する、というサンプルです。

コードは以下のようになります。

```java:LineBotExample01Servlet.java
@SuppressWarnings("serial")
public class LineBotExample01Servlet extends LineBotServlet {

	// CHANNEL_SECRETとCHANNEL_ACCESS_TOKENはそのまま文字列で指定可能
	private static final String CHANNEL_SECRET ="取得したものをいれる" ;
	private static final String CHANNEL_ACCESS_TOKEN ="取得したものをいれる";
	@Override
	protected ReplyMessage handleTextMessageEvent(MessageEvent<TextMessageContent> event) throws IOException {

		// ユーザーがBOTに送信したメッセージ
		TextMessageContent userMessage = event.getMessage();

		// ユーザーのProfileを取得する
		UserProfileResponse userProfile = getUserProfile(event.getSource().getUserId());

		// BOTからの返信メッセージ
		String botResponseText = userProfile.getDisplayName() + "さん、"
				+ "「" + userMessage.getText() + "」って言いましたね";

		TextMessage textMessage = new TextMessage(botResponseText);

		return new ReplyMessage(event.getReplyToken(), Arrays.asList(textMessage));
	}

	@Override
	protected ReplyMessage handleDefaultMessageEvent(Event event) {
		// overrideしていないメッセージを受信した場合は何もしない(nullを返す)
		return null;
	}

	@Override
	public String getChannelSecret() {
		return CHANNEL_SECRET;
	}

	@Override
	public String getChannelAccessToken() {
		return CHANNEL_ACCESS_TOKEN;
	}
```
### 動作イメージ

![line_bot_small.png](https://qiita-image-store.s3.amazonaws.com/0/170905/1e4dadb8-c7e4-3f07-a82f-d92f33788028.png)


### 解説
ユーザーからのテキストメッセージを受信すると、

```java
ReplyMessage handleTextMessageEvent(MessageEvent<TextMessageContent> event)
```
がコールされますので、このメソッドの中で必要な処理を行います。
この例ではユーザーの発言（テキスト）を取得して、少し加工して返事をします。

以下のよう ReplyMessage を返すと、ユーザー側に返信がおくられます。

```java
return new ReplyMessage(event.getReplyToken(), Arrays.asList(textMessage));
```
ここを

```java
return null;
```
にすると、返信はしません。

ちなみに、

```java
Arrays.asList(textMessage)
```
としている理由は、メッセージは同時に複数返すことができるためです。
たとえば、画像とテキストを同時に返したい場合には、

```java
Arrays.asList(imageMessage,textMessage)
```
のようにします。

今回の例では、handleTextMessageEventをオーバーライドしましたが、他にも以下のメソッドをオーバーライドすれば、各イベントを受信することができます。

```java
protected ReplyMessage handleImageMessageEvent(MessageEvent<ImageMessageContent> event)
protected ReplyMessage handleLocationMessageEvent(MessageEvent<LocationMessageContent> event)  
protected ReplyMessage handleStickerMessageEvent(MessageEvent<StickerMessageContent> event)  
protected ReplyMessage handleAudioMessageEvent(MessageEvent<AudioMessageContent> event)  
protected ReplyMessage handleVideoMessageEvent(MessageEvent<VideoMessageContent> event)  
protected void handleUnfollowEvent(UnfollowEvent event) 
protected ReplyMessage handleFollowEvent(FollowEvent event) 
protected ReplyMessage handleJoinEvent(JoinEvent event) 
protected void handleLeaveEvent(LeaveEvent event) 
protected ReplyMessage handlePostbackEvent(PostbackEvent event) 
protected ReplyMessage handleBeaconEvent(BeaconEvent event) 
```

最後に、

```java

@Override
    protected ReplyMessage handleDefaultMessageEvent(Event event) {
        // overrideしていないメッセージを受信した場合は何もしない(nullを返す)
        return null;
    }
```
ですが、オーバーライドしていないイベントを受信した場合にコールされます。

上記**LineBotServlet**が含まれているヘルパーライブラリを取り込むために、Gradle/Mavenでは以下のように指定します。

### Gradle
**compile 'org.riversun:line-bot-helper:1.0.0'**

### Maven
```xml
<dependency>
	<groupId>org.riversun</groupId>
	<artifactId>line-bot-helper</artifactId>
	<version>1.0.0</version>
</dependency>
```

# ローカルPCでお手軽に試す
Servletをつくるところまでがこの記事の目的ですがおまけで、ローカルPCで試す方法についても書いておきます。

Servletをつくるところまではだいぶお手軽になりましたが、すぐ試せる、というところだとまだ弱いですね。

わざわざTomcat等立ち上げDeployするのはめんどうですので、servletをお手軽にためせるコンテナ**Jetty**を利用しましょう。

Jettyの利用はとっても簡単です。

まずGradle/Mavenに以下を追加します。

### Gradle
**compile 'org.eclipse.jetty:jetty-server:9.4.0.v20161208'**
**compile 'org.eclipse.jetty:jetty-webapp:9.4.0.v20161208'**

### Maven
```xml

<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-server</artifactId>
	<version>9.4.0.v20161208</version>
</dependency>
<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-webapp</artifactId>
	<version>9.4.0.v20161208</version>
</dependency>
```

### Jettyを起動して、servletにアクセスする
Gradle/Mavenに記述したら、以下のようにコードを書いて実行しましょう。

```java:AppMain.java
public class AppMain {

	public static void main(String[] args) throws Exception {

		ServletHandler handler = new ServletHandler();

		handler.addServletWithMapping(LineBotExample01Servlet.class, "/callback");

		// loclahost:3000でJettyを起動
		Server jetty = new Server(3000);

		jetty.setHandler(handler);
		jetty.start();
		jetty.join();

	}

}
```

```java

handler.addServletWithMapping(LineBotExample01Servlet.class, "/callback");
```
にあるとおり、先ほど作成した**LineBotExample01Servlet**を**/callback**というパスで受け付けます。

ローカルPCで実行すれば  http://127.0.0.1:3000/callback でLINE BOT Servletにアクセスすることができるようになります。

### つくったLINE BOT Servletに外部からアクセスできるようにする
せっかくローカルで立ち上げても、LINE BOTとして振舞うには、サーバーにDeployして外部に公開する必要があります。
テスト用途でお手軽にためすため、さきほどJettyをつかってつくったサーバーを外部からアクセスできるようにしましょう。

ここでは、こうした用途にぴったりな ngrok というサービスを使います。

以下から ngrok をダウンロードしておきます
https://ngrok.com/download

コマンドラインで
``ngrok http -region=ap 127.0.0.1:3000``

とすると、ngrokが開始して以下のような画面が表示されます
![ngrok.png](https://qiita-image-store.s3.amazonaws.com/0/170905/261552c2-314e-85da-2a84-0cabd9751c0d.png)

たったこれだけで、
https://xxxxx.ap.ngrok.io というurlが https://127.0.0.1:3000 にマップされました！

_（xxxxxの部分はngrokを起動するたびにランダムに変わります。sign upすれば固定することも可能です)_

LINE BOTのweb hook urlにはhttpsが求められますのでこれは便利ですね。

ここまでくれば、あとは、以下のurlをWeb Hook URLに登録するだけです。
``https://xxxxx.ap.ngrok.io/callback``

**ngrokのTIPS**
**-region=ap**と指定するとAsia Pacificリージョンが選択されますので
日本からアクセスする場合はデフォルトのUSリージョンよりもレイテンシーの面で有利です。

# 実行します
スマホのLINEアプリからメッセージを入れると
![line_bot_small.png](https://qiita-image-store.s3.amazonaws.com/0/170905/1e4dadb8-c7e4-3f07-a82f-d92f33788028.png)

うまくいきました

# まとめ
- 今回は、シンプルなLINE BOTをHttpServletで作る例を紹介しました。
- ServletをJettyでホストして、ngrokをつかって外部からアクセス可能にしたことで、実際にスマホのLINEアプリからBOTにメッセージを送るテストができました
- 紹介したソースコード一式は https://github.com/riversun/line-bot-servlet-examples.git にあります

[次回](http://qiita.com/riversun/items/c28f116440bd8de8ac88)は、画像の送受信など、もうすこしハンドリングする素材をふやす例を試したいとおもいます。