LINE Trial Bot SDK ができるまで

はじめに

LINE株式会社の松野です。

先日、LINE BOT API Trial Accountが発表され、熱狂的に世界中のデベロッパーに迎えられました。今まで契約を締結した企業デベロッパーしか開発することができなかった LINEのBotアカウントを個人でも簡単に開発できるようになったからです。
そういった中で、なぜ我々がLINE BOT API Trial AccountのSDKを開発するに至ったのか、そしてどうやって1週間でリリースできたのか。その経緯について以下に解説させていただきます。

Bot SDK を作ることになったワケ

LINE BOT API Trial Accountが公開されてからまもなく社内でもたくさんのエンジニアたちが 開発にとりかかりました。 そこで、彼らは気づいてしまったのです。。
この API を使ってボット作るの大変なのでは??
と。

特に、Java などの静的言語の場合には、JSON をマッピングするためのクラスをゴリゴリと書く必要があります。LINE BOT API Trial Accountの場合には、API のエンドポイントも多く、やりとりするメッセージタイプもたくさんあるため、テキストメッセージ・画像・スタンプ・リッチメッセージ etc. と、幾つものクラスを実装する必要があります。LINE Developers に書いてある大量のテーブルからクラスを起こすのは極めて苦痛であり、
これは大変だ!
と思ったわけです。

社内チャットでそのような話をしていると、他にもそのように感じている人間が何人もいることがわかりました。

そして、いろいろ話しているうちに

LINE BOT API Trial Accountを多くの人に使ってもらうには SDK を公開するしかない!!!
ということになりました。

さて、そうなってくると、エンジニアをかき集めて一気に各言語の SDK を書こうということになります。メジャーかつ社内で優秀なエンジニアが集められる言語を中心に開発しようということで、まずは何はなくとも弊社の開発の中心であるJava と Perl の開発を行うことにしました。さらに、Ruby, PHP は手を上げるエンジニアがいたため、またそれ以外にも静的言語である golang の開発は必要だろうという話になり、それぞれ開発を行うことにしました。

開発は始まってしまうと早くて、一週間程度で各言語の実装が出揃いました。

SDK を書くにあたって、心がけたところは以下の5点です。

  • 各言語の API メソッド名などを揃える
    • 言語ごとにあまりにもインターフェースが異なっていると、ユーザーにストレスを感じさせることになります。
  • 各言語に精通したエンジニアを集めて書いてもらう
    • 各言語のコミュニティの標準的な作法に則ってリリースしてもらうことが好ましいため、各言語のモジュールレポジトリにすでにリリース経験のあるエンジニアが開発することにしました。
  • 品質を担保するために、レビューを徹底して行う
    • 各言語の lint 的なものを設定して、良い品質を保つようにしました。
  • セキュリティチェックを行う
    • 依存モジュールをできるだけ少なくする
  • 依存モジュールをできるだけ少なくする
    • 依存モジュールが増えれば学習コストが上がります。
    • Rubyの ActiveSupportのようなロードするとグローバルに組み込みクラスに影響を与えるようなライブラリは利用しないようにしました。

そういうわけで、LINE BOT API Trial Accountがリリースされました。めでたい。

Trial Bot SDK for Java の構成

以下、自分が担当した Java SDK の構成について解説します。

サポート対象の Java バージョンは 8 としました。LINE BOT API Trial Accountは Trial ということもあり、新しく環境を作るケースが多いと考えました。

Java の場合、module は細かくわけるのが一般的なので、細かくわけていきました。どの粒度に構成するかは悩みどころですが、無理のない粒度で分けました。

line-bot-model

Http でのリクエスト/レスポンス時に JSON をマッピングするクラスです。
JSON のマッピングには Jackson を利用する前提で実装しました。Java の世界では Jackson と Gson の2つの JSON ライブラリが有名ですが、弊社内では Jackson が人気があるのでJacksonを採用しました。
マッピングするための Bean は、アクセサを IDE で生成してもよかったのですが、lombok を採用しました。 lombok を利用するとアノテーションを配置するだけで以下のように Getter/Setter などを生成することができます。lombok を利用することにより、圧倒的に読みやすいコードを記述することが可能です。

記述するコード   生成されるコード
public class User {
  @Getter @Setter
  private String name;
}
 
public class User {
  private String name;
  public void setName(String name) {
    this.name = name;
  }
  public String getName() {
    return this.name;
  }
}

lombok で設定する Bean は Jackson でマッピングするためだけならば、@Data アノテーションをつけて Getter/Setter/equals/hashcode などをまとめて生成するだけで済ませても良いです。
実際、弊社で開発している通常のウェブアプリケーションではそうしているケースが多々あります。
しかし、SDK の場合には、このオブジェクト自体を Immutable にしておいたほうが、「このフィールドにセッターがあることには何か意味があるんだろうか。。」などと余計な疑念を抱かせることがなくなり、理解しやすさが向上します。
そこで、オブジェクトは Immutable にして setter メソッドを実装しないことにしました。
Jackson で Immutable なクラスを利用する場合、コンストラクタの引数に @JsonProperty アノテーションを付与してコンストラクタを利用して Jackson がオブジェクトを作成できるようにする必要があります。具体的には以下のようにすれば OK です。

@Getter
@ToString
public class EventResponse {
    private final Integer version;
    private final Long timestamp;
    private final String messageId;
    private final int statusCode;
    private final String statusMessage;
    private final List<String> failed;
public EventResponse(
        @JsonProperty("version") Integer version,
        @JsonProperty("timestamp") Long timestamp,
        @JsonProperty("messageId") String messageId,
        @JsonProperty("statusCode") int statusCode,
        @JsonProperty("statusMessage") String statusMessage,
        @JsonProperty("failed") List&lt;String> failed) {
    this.version = version;
    this.timestamp = timestamp;
    this.messageId = messageId;
    this.statusCode = statusCode;
    this.statusMessage = statusMessage;
    this.failed = failed;
}

}

line-bot-api-client

HTTP API のクライアントモジュールです。これはこの SDKのキモとなる部分です。
HTTP クライアントライブラリとしては Apache HttpClient を採用しました。コミュニティでも利用者が多く、安定している印象です。
しかし、HTTP クライアントライブラリの世界は栄枯盛衰。トレンドが変化する可能性もありますので、interface を定義して、HTTP クライアントライブラリの実装が別のものになっても大幅に書き換えなくてもいいように設計しました。

line-bot-servlet

LINEのBotアカウントは web application として動作します。
Java なのでウェブアプリケーションの記述はやはり servlet で記述するのが基本なので servlet として簡単に利用できるようにする必要があります。
Servlet は、古臭い API だと揶揄されるときもありますが、Servlet API は Lightweight Language でいう Rack/WSGI/PSGI などに相当するものです。
Servlet API をおさえておくことはやはり重要なことです。
特定のフレームワークに依存したサンプルコード、SDKなどを用意しても、その SDK に詳しくない人にとってとっつきにくくなってしまいますので、やはり Servlet による例が含まれている必要があるでしょう。

line-bot-spring-boot

servlet のレイヤで Bot を記述できれば十分といえば十分なのですが、世間的には web application framework などを利用して記述したいという層もいらっしゃるだろうというところで、spring boot でさっくりと作れるような SDK も用意してみました。

依存を設定すれば、以下のように記述するだけで、簡単にエコーサーバーを起動可能です。極めて簡単です。

package com.example.bot.spring.echo;

import com.linecorp.bot.client.LineBotClient; import com.linecorp.bot.client.exception.LineBotAPIException; import com.linecorp.bot.model.callback.Message; import com.linecorp.bot.model.content.Content; import com.linecorp.bot.model.content.TextContent; import com.linecorp.bot.spring.boot.annotation.LineBotMessages; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@SpringBootApplication public class EchoApplication { public static void main(String[] args) { SpringApplication.run(EchoApplication.class, args); }

@RestController
public static class MyController {
    @Autowired
    private LineBotClient lineBotClient;

    @RequestMapping("/callback")
    public void callback(@LineBotMessages List&lt;Message> messages) 
      throws LineBotAPIException {
        for (Message message : messages) {
            Content content = message.getContent();
            if (content instanceof TextContent) {
                TextContent textContent = (TextContent) content;
                lineBotClient.sendText(textContent.getFrom(),
                        textContent.getText());
            }
        }
    }
}

}

sample-spring-boot-echo

echo Botのサンプルです。単純に話しかけられたことをオウム返しするボットの実装のサンプルになっています。
ボットの形式としてはもっとも簡単なものです。各言語の SDK にはそれぞれ echo ボットのサンプルコードがレポジトリに含まれています。
簡単にボットを実装できるんだということを実感していただくために、できるだけ記述量を減らしています(そして、記述量を減らせるように SDK を設計しています)。

sample-spring-boot-kitchensink

echo Botのようなシンプルなサンプルコードも重要ですが、実際どんな機能が利用可能なのかを網羅的に確認できるサンプルコードも重要です。
このモジュールでは、SDK の全ての機能を網羅的に利用するサンプルを記述しています。具体的には、全てのイベントタイプについてオウム返しする実装を用意するだけです。
SDK 自体の動作確認のためにもこういった全機能を試せるサンプルコードを実装したわけです。ドキュメントを元に実装しても、実際に動くという保証は実機を使ってテストしてみないと得られませんからね!

まとめ

以上のような感じで、LINE BOT API Trial Accountは開発されました。
GitHub: https://github.com/line

LINE では、自発的に SDK 開発に参加できるようなエンジニアを募集しています。
サーバサイドエンジニア【LINE GAME】【ファミリーアプリ】【LINE Pay】
サーバサイドエンジニア【LINEプラットフォーム】

Related Post