かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Azure Bot Service の LINE Connector でスタンプみたいな LINE 固有機能を使おう

先日書いた Azure Bot Service の LINE 対応ですが、スタンプみたいな LINE 固有機能使いたいんだけどどうするの?という疑問がわいてくるような気がします。

こういう複数のプラットフォームに対応するときは、機能が最大公約数になってしまうのですが、ちゃんと Azure Bot Service の SDK にはチャンネル固有機能への対応方法が用意されています。

チャンネル固有の機能の実装というドキュメントが用意されていて、なんとそこにちゃんと LINE のことも書いてありました。

docs.microsoft.com

ドキュメントから引用すると以下のように結構いける気がする。

次の LINE メッセージのタイプがサポートされています。
- スタンプ
- イメージマップ
- テンプレート (ボタン、確認、カルーセル)
- Flex

対応早いね!!って思ったのでドキュメントのコミットログを見てみたら…

Release 'live' 2-21-2019 (#1320) と書いてあるコミットで、LINE の追記がされてたので、私が前に試したのが 2019/02/22 なので、その時にはドキュメントも整理されてたっぽい。気づかなかった。

github.com

では、実際に話しかけるとスタンプを返すボットを作ってみようと思います。

プロジェクトの作成

とりあえず Bot Builder SDK のテンプレートにある EchoBot を土台にします。

f:id:okazuki:20190311173841p:plain

あ、余談ですが英語だと Sticker ってなってるスタンプが日本語ドキュメントでステッカーとなってないあたりは、機械翻訳オンリーのドキュメントではなく、ちゃんとされてるドキュメントなんだなぁって思いました。

プロジェクトが作成されたら プロジェクト名Bot.cs というファイルにエコーボットのロジックが書かれてるのでそこを編集します。

チャンネル固有のデータを突っ込むには Activity (Bot でやりとりするメッセージを表すクラス) を作って、そこの ChannelData プロパティに所定の JSON のオブジェクトを突っ込めばいいと書いてあります。 .NET で作る場合には JSON.NET の JObject を突っ込めば OK とあるのでさくっとやりましょう。

スタンプを送るためのメッセージは LINE のドキュメントによると typesticker を指定して packageIdstickerId を指定してやればいいみたいです。

developers.line.biz

スタンプのリストも何故か PDF 形式で公開されてます(個人的には、普通に Web ページでいいのでは…と思いました)

https://developers.line.biz/media/messaging-api/sticker_list.pdf

ということで素直に実装すると…こんな感じ?

public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    // Handle Message activity type, which is the main activity type for shown within a conversational interface
    // Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
    // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
    if (turnContext.Activity.Type == ActivityTypes.Message)
    {
        // Get the conversation state from the turn context.
        var state = await _accessors.CounterState.GetAsync(turnContext, () => new CounterState());

        // Bump the turn count for this conversation.
        state.TurnCount++;

        // Set the property using the accessor.
        await _accessors.CounterState.SetAsync(turnContext, state);

        // Save the new turn count into the conversation state.
        await _accessors.ConversationState.SaveChangesAsync(turnContext);

        if (turnContext.Activity.ChannelId == "line")
        {
            var reply = new Activity();
            reply.ChannelData = new JObject(
                new JProperty("type", "sticker"),
                new JProperty("packageId", "1"),
                new JProperty("stickerId", "1"));
        }
        else
        {
            var responseMessage = $"Turn {state.TurnCount}: You sent '{turnContext.Activity.Text}'\n";
            await turnContext.SendActivityAsync(responseMessage);
        }
    }
    else
    {
        await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
    }
}

でも、

一応 ChannelId が LINE のときだけスタンプにしました。

Azure で WebApp と Bot Channel Registration を作成して諸々設定します。ここらへんのデプロイ手順とかはドキュメント見るか、私の以下の記事とかが LINE に繋いだ時のものなので参考にしてください。

blog.okazuki.jp

動かしてみると…

f:id:okazuki:20190311183611p:plain

動いた!!因みに JObject で JSON くみたてるのだるいので試してみたら、普通にクラスを突っ込んでも JSON.NET でシリアライズしてくれるみたいでしたので、自分で JSON.NET で意図したとおりにシリアライズされるクラスを定義するか、以下のようにさくっとその場で匿名クラス作っても OK です。

if (turnContext.Activity.ChannelId == "line")
{
    var reply = turnContext.Activity.CreateReply();
    // これでも OK
    reply.ChannelData = new
    {
        type = "sticker",
        packageId = "1",
        stickerId = "1",
    };
    //reply.ChannelData = new JObject(
    //    new JProperty("type", "sticker"),
    //    new JProperty("packageId", "1"),
    //    new JProperty("stickerId", "1"));
    await turnContext.SendActivityAsync(reply);
}

もう一つくらいやってみよう

同じ要領で画像を出してみたいと思います。 先ほどの LINE Developers のドキュメントのスタンプの下に画像の場合の JSON がありました。

{
    "type": "image",
    "originalContentUrl": "https://example.com/original.jpg",
    "previewImageUrl": "https://example.com/preview.jpg"
}

簡単ですね。ということで Twitter の自分のヘッダー画像の URL をぺたっと貼るようにしてみました。

if (turnContext.Activity.ChannelId == "line")
{
    var reply = turnContext.Activity.CreateReply();
    reply.ChannelData = new
    {
        type = "image",
        originalContentUrl = "https://pbs.twimg.com/profile_banners/3272341/1552195235/1500x500",
        previewImageUrl = "https://pbs.twimg.com/profile_banners/3272341/1552195235/1500x500",
    };
    await turnContext.SendActivityAsync(reply);
}

動かしてみると…

f:id:okazuki:20190311184348p:plain

ばっちり!!

まとめ

ということで、Bot Framework SDK で提供されている ChannelData プロパティを使うとチャンネル固有の機能が使えます。 Flex とかにも対応しているみたいなので本格的な LINE ボットを使いたい人は是非見てみてね!!