kokoni

クラウドおじさんの備忘録

LineSimulatorというイカしたツールを試してみた(ローカル実行編)

前回のブログでLineSimulatorをローカル環境で実行する方法について記載しました。

今回はAzure Functionをローカルでデバッグ実行してLineSimulatorとデータ通信する方法について記載していきます。

 

※本記事は2017年12月8日時点の情報となります。

 

前回の記事は下記を参照してください。

https://cdn.kokoni.jp/2017/12/06/linebotsimulator%E3%81%A8%E3%81%84%E3%81%86%E3%82%A4%E3%82%AB%E3%81%97%E3%81%9F%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F%EF%BC%88%E8%A8%AD%E5%AE%9A%E7%B7%A8/cdn.kokoni.jp

 

ローカル実行について

今回は前提としてVisualSutdio 2017を利用してAzure Functionsをローカル実行します。Azure Functionsの作成方法や開発環境構築については本記事では割愛しますので下記のリンクを参考にしてください。

docs.microsoft.com

 

とりあえずVisualStudio2017を起動してAzure Functionsのテンプレートからプロジェクトを作成します。プロジェクトを作成したらプロジェクトを右クリックして追加からFunctionをHttpTriggerで作成しましょう。HttpTriggerでFunctionを作成したら下記のソースを参考にLineBotを作成してください。

 

// 必要なusingは下記の通り
//***********************************************
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
//***********************************************

[FunctionName("****")] //←FunctionNameは作成したFunctionNameに合わせること
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
    log.Info("Start");

    // リクエスJSONをパース
    var jsonContent = await req.Content.ReadAsStringAsync();
    var data = JsonConvert.DeserializeObject<Request>(jsonContent);

    string replyToken = null;
    string messageType = null;
    string message = null;

    string ChannelAccessToken = "dummyToken";

    // リクエストデータからデータを取得
    foreach (var item in data.Events)
    {
        // リプライデータ送付時の認証トークンを取得
        replyToken = item.ReplyToken;
        if (item.Message == null) continue;
        // メッセージタイプを取得、テキスト以外はエラー文言を返却
        messageType = item.Message.type;
        message = item.Message.text;
    }

    // リプライデータの作成
    var content = CreateResponse(replyToken, message, messageType);

    // JSON形式に変換
    var reqData = JsonConvert.SerializeObject(content);

    // レスポンスの作成
    using (var client = new HttpClient())
    {
        // リクエストデータを作成
        HttpRequestMessage request =
            new HttpRequestMessage(HttpMethod.Post, "http://localhost:8080/v2/bot/message/reply")
            {
                Content = new StringContent(reqData, Encoding.UTF8, "application/json")
            };

        // 認証ヘッダーを追加
        client.DefaultRequestHeaders.Add("Authorization", $"Bearer {ChannelAccessToken}");

        // 非同期でPOST
        var res = await client.SendAsync(request);

        return req.CreateResponse(res.StatusCode);
    }
}

/// <summary>
/// リプライ情報の作成
/// </summary>
private static Response CreateResponse(string token, string message, string messageType = "")
{
    var res = new Response();
    var msg = new Messages();

    // リプライトークンはリクエストに含まれるリプライトークンを使う
    res.replyToken = token;
    res.messages = new List<Messages>();

    // メッセージタイプがtext以外は単一のレスポンス情報とする
    if (messageType == "text")
    {
        msg.type = "text";
        msg.text = message;
        res.messages.Add(msg);

    }
    else
    {
        msg.type = "text";
        msg.text = "その形式は対応していません。";
        res.messages.Add(msg);
    }

    return res;
}

// リクエスト
public class Request
{
    public List<Event> Events { get; set; }
}

// イベント
public class Event
{
    public string ReplyToken { get; set; }
    public string Type { get; set; }
    public object Timestamp { get; set; }
    public Source Source { get; set; }
    public Message Message { get; set; }
}

// ソース
public class Source
{
    public string type { get; set; }
    public string userId { get; set; }
}

// リクエストメッセージ
public class Message
{
    public string id { get; set; }
    public string type { get; set; }
    public string text { get; set; }
}


// レスポンス
public class Response
{
    public string replyToken { get; set; }
    public List<Messages> messages { get; set; }
}

// レスポンスメッセージ
public class Messages
{
    public string type { get; set; }
    public string text { get; set; }
}

 

コーディングが完了したらビルドしてエラーがなければF5で起動しましょう。Function向けのデバッグCLIが起動したらLineSimulatorを起動します。LineSimulatorの起動方法はWindowsであればコマンドプロンプトを管理者で立ち上げてLineSimulatorのフォルダに移動して"npm start"とコマンドを叩けば実行されます。※環境構築済みであることが前提です。

 

LineSimulatorを起動したら設定の「Bot API Server Address」にローカルで実行されているFunctionsのURLを設定します。ローカルで実行されているFunctionsのURLはFunctionsCliが起動したら下のほうに表示されるのでそこを参考にしてください。

 

設定が完了したらLineSimulatorで文章を入力して送信してみましょう。上記のサンプルソースはオウム返しするコードなので入力した文字がオウム返しされれば成功です。

※私は下記の画像のように向かって左側にLineSimulatorを配置し、右側上段にFunctionsCliを右側下段にnpmを配置してデバッグしています。

 

LineSimulatorを使えばローカル環境のみでLineBotの作成が可能になるので非常に便利だと思います。ただ、LineSimulatorを利用するとChannelAccessTokenがdummyTokenになるので、そこら辺の設定をちゃんと実装しないと実際のLineAPIに接続するときにエラーになるので注意が必要です。

上記のサンプルソースで言うとChannelAccessToken の”dummyToken”の部分をLineDeveloperCenterで取得できるアクセストークンに置き換えてあげる必要があります。また、API呼び出しのURLの部分を「http://localhost:8080/v2/bot/message/reply」から本番のURL「https://api.line.me/v2/bot/message/reply」に変更する必要もあります。

今回はあくまでローカル環境での実行に特化してますので、作成したLineBotをクラウドにデプロイするときは設定周りに注意してください。

ということで年始年末の暇な時間はLineSimulatorとAzure Functionsを使ってLineBotの作成に勤しむのも一興かと思います。