Visual StudioとAzure Functionsで作るLINE BOTアプリケーション入門

以前の記事で紹介した LINE BOT開発用のAzure Functionsプロジェクトテンプレートですが、 サンプルコードを大幅に追加しました。
(詳しくはMarketplaceのRelease Notesを確認してみてください。)

marketplace.visualstudio.com

この機会に是非、使ってほしい!ということで、今回はLine.Messagingライブラリを使ったAzure FunctionsのLINE BOTアプリケーション開発について少し解説してみたいと思います。

目次

Visual Studio とAzuzre Functionsで作るLINE BOTアプリケーション入門

はじめに

ここで紹介するサンプルコードは、拙作Line.Messagingクラスライブラリを使用することを前提としています。

www.nuget.org

この記事の内容を実際に試したい方は、MarketPlaceに記載のクイックスタートガイド参考に開発環境を作っておきましょう。
(導入時の問題、質問等ありましたら、このブログのコメント欄でも、MarketplaceのQ&AでもTwitterでも何でも良いのでご連絡ください)

Webhook イベントを処理する

友だち追加やメッセージ送信などのイベントは、指定したAzure FunctionsのURLにHTTP POSTリクエストとして送信されます。 そのリクエストによって、HttpTriggerFunctionクラスのRunメソッドが実行されます。

以下は、Line.Messagingを使用したHttpTriggerFunctionの実装例です。

public static class HttpTriggerFunction
{
    //LINE Messaging API クライアントの初期化
    static LineMessagingClient lineMessagingClient;
    static HttpTriggerFunction()
    {
        lineMessagingClient = new LineMessagingClient(System.Configuration.ConfigurationManager.AppSettings["ChannelAccessToken"]);
        var sp = ServicePointManager.FindServicePoint(new Uri("https://api.line.me"));
        sp.ConnectionLeaseTimeout = 60 * 1000;
    }

    [FunctionName("LineMessagingApiSample")]
    public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
    {
        //WebhookのリクエストBodyからイベントオブジェクトを取得
        IEnumerable<WebhookEvent> events;
        try
        {
            var channelSecret = System.Configuration.ConfigurationManager.AppSettings["ChannelSecret"];
            events = await req.GetWebhookEventsAsync(channelSecret);
        }
        catch (InvalidSignatureException e)
        {
            return req.CreateResponse(HttpStatusCode.Forbidden, new { Message = e.Message });
        }

        //イベントを処理する
        try
        {
            var app = new EchoBotApp(lineMessagingClient,log);
            await app.RunAsync(events);
        }
        catch (Exception e)
        {
            log.Error(e.ToString());
        }
        return req.CreateResponse(HttpStatusCode.OK);
    }
}

LINE Messaging APIクライアントを初期化する

メッセージのリプライ、プッシュ通知などのLINEサーバーへのリクエストにはLine.Messaging.LineMessagingClientクラスを使用します。 このクラスは内部でSystem.Net.Http.HttpClientクラスを使用しています。
これをHttpTriggerFunctionクラスのStaticフィールドに1インスタンスだけ作成し、使いまわすようにします。

インスタンスを使いまわす理由は、以下を参照ください。

リクエストBodyからWebhook Event Objectを取得する

Webhookイベントの内容は、リクエストBodyにJSONで格納されています。
Line.Messagingライブラリでは、Run関数の引数HttpRequestMessage reqからEvent Objectを取得する拡張メソッドを用意しています。

static Task<IEnumerable<WebhookEvent>> GetWebhookEventsAsync(this HttpRequestMessage req,strin channelSecret);

このメソッドは以下の処理を行います。

  • リクエストの構文検証(Signature Validation)を行い、NGの場合は例外を投げる
  • リクエストBodyのJSONをパースしてWebhook Event Object(のコレクション)を返す
[FunctionName("LineMessagingApiSample")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
    IEnumerable<WebhookEvent> events;
    try
    {
        var channelSecret = System.Configuration.ConfigurationManager.AppSettings["ChannelSecret"];
        //Webhook Event Objectの取得
        events = await req.GetWebhookEventsAsync(channelSecret);
    }
    catch (InvalidSignatureException e)
    {
        //構文検証エラー
        return req.CreateResponse(HttpStatusCode.Forbidden, new { Message = e.Message });
    }

    //...
}

Webhook Event Object

Webhook Event Object はイベントのタイプ別に7種類用意されています。
Line.Messagingライブラリでは、WebhookEvent抽象クラスのサブクラスとして実装されています。

public abstract class WebhookEvent
{
    public WebhookEventType Type { get; }
    public WebhookEventSource Source { get; }
    public long Timestamp { get; }
}
  • MessageEvent
    ユーザーからメッセージが送られた際に発行されるイベント。
    メッセージには、送られてくるデータの種類によって異なる以下のタイプがあります。

    • text (文字列メッセージ)
    • image(画像データ)
    • audio(オーディオデータ)
    • video(ビデオデータ)
    • file(ファイル)
    • location(位置情報)
    • sticker(スタンプ
  • FollowEvent
    ユーザーがBOTアカウントをフォローした際に発行されるイベント

  • UnfollowEvent
    ユーザーにBTOアカウントをブロックされた際に発行されるイベント
  • JoinEvent
    BOTアカウントがグループやトークルームに参加した際に発行されるイベント
  • LeaveEvent
    BOTアカウントがグループやトークルームから退室させられた際に発行されるイベント
  • PostbackEvent
    テンプレートメッセージに設定したPostbackアクションが実行された際に発行されるイベント
  • BeaconEvent
    LINE Beaconデバイスの受信圏内に入った(出た)際に発行されるイベント

※各イベントの詳細はLINE公式のAPIリファレンスを参照ください。

WebhookEventSource

WebhookEventクラスは、イベントの送信元を表すSourceプロパティを持ちます。

Line.MessagingライブラリではWebhookEventSourceクラスとして実装しています。

public class WebhookEventSource
{
    public EventSourceType Type { get; }
    public string Id { get; }        
    public string UserId { get; }
}

送信元のタイプは以下の3種類です。

  • User (ユーザーと1対1のトークで発生するイベントに付与されます)
  • Group (トークグループで発生するイベントに付与されます)
  • Room (トークルーム(複数ユーザー間でのトーク)で発生するイベントに付与されます)

また、Idプロパティには、各送信元を示すIDが格納されます(User ID、Group ID、Room ID)。
UserIdには、各送信元から発信したユーザーのIDが入ります。

イベントを処理する

あとは、リクエストBodyから取得したWebhookEventオブジェクトの内容を見てそれに応じた処理を記述すればOKです。
これをHttpTriggerFunctionクラスに直接記述しても良いのですが、Line.Messagingライブラリではイベントの種類に応じて処理を振り分けて実行するためのクラスが用意されているので、これを使います。

WebhookApplicationクラスを使用する

WebhookApplicationクラスは、RunAsyncというメソッドでWebhookEventのコレクションを受け取り、イベントの種類に応じて各イベントの処理(On~ 仮想メソッド)を呼ぶだけのクラスです。

public abstract class WebhookApplication
{
    protected virtual Task OnMessageAsync(MessageEvent ev) => Task.CompletedTask;
    protected virtual Task OnJoinAsync(JoinEvent ev) => Task.CompletedTask;
    protected virtual Task OnLeaveAsync(LeaveEvent ev) => Task.CompletedTask;
    protected virtual Task OnFollowAsync(FollowEvent ev) => Task.CompletedTask;
    protected virtual Task OnUnfollowAsync(UnfollowEvent ev) => Task.CompletedTask;
    protected virtual Task OnBeaconAsync(BeaconEvent ev) => Task.CompletedTask;
    protected virtual Task OnPostbackAsync(PostbackEvent ev) => Task.CompletedTask;

    public async Task RunAsync(IEnumerable<WebhookEvent> events)
    {
        foreach (var ev in events)
        {
            switch (ev.Type)
            {
                case WebhookEventType.Message:
                    await OnMessageAsync((MessageEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Join:
                    await OnJoinAsync((JoinEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Leave:
                    await OnLeaveAsync((LeaveEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Follow:
                    await OnFollowAsync((FollowEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Unfollow:
                    await OnUnfollowAsync((UnfollowEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Postback:
                    await OnPostbackAsync((PostbackEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Beacon:
                    await OnBeaconAsync((BeaconEvent)ev).ConfigureAwait(false);
                    break;
            }
        }
    }
}

このクラスを継承したクラスを作成し、必要な仮想メソッドをオーバーライドして処理を記述します。
以下に、ユーザーのメッセージをオウム返しするだけの処理の実装例を示します。

//オウム返しBOTアプリケーション
public class EchoBotApp:WebhookApplication
{
    private LineMessagingClient MessagingClient {get;}
    private TraceWriter Log{get;}

    //コンストラクタでLineMessagingClientのインスタンスを渡す
    public EchoBotApp(LineMessagingClient messagingClient, TraceWriter log)
    {
        MessagingClient = messagingClient;
        Log = log;
    } 

    //メッセージEventを処理する
    protected override async Task OnMessageAsync(MessageEvent ev)
    {
        //Textメッセージにだけ返信する
        if(ev.Type != EventMessageType.Text){return;}

        return MessagingClient.ReplyMessageAsync(ev.ReplyToken, ((TextEventMessage)ev.Message).Text);
    }
}

あとは、HttpTriggerFunctionのRunメソッドで以下の様に記述するだけです。

//WebhookApplicationを継承したクラスでイベントを処理する
var app = new EchoBotApp(lineMessagingClient,log);
await app.RunAsync(events);

まとめ

基本的な使い方は以上です。
次回からは、もう少し具体的な実装例を解説してみたいと思います。