Line.Messaging 1.4.1 でQuick ReplyとFlex Messageに対応しました

Line.Messaging v1.4.1 をリリースしました

www.nuget.org

Quick Replyと、(長らくお待たせしておりましたがようやく)Flex Message に対応しました!

各機能の詳細は以下のドキュメントをご確認ください。

Quick Reply の使い方

Quick Replyの使い方は簡単で、各種送信メッセージに追加されたQuick ReplyプロパティにQuickReplyオブジェクトを設定してあげるだけです。

new TextMessage("text", new QuickReply()
{
    Items = new[] 
    {
        new QuickReplyButtonObject(new CameraTemplateAction("カメラ起動")),
        new QuickReplyButtonObject(new CameraRollTemplateAction("カメラロール")),
        new QuickReplyButtonObject(new LocationTemplateAction("位置を指定")),
    }
});

QuickReply ので表示するボタンは、QuickReplyButtonObjectItemsプロパティに配列で設定します。
QuickReplyButtonObject には、ボタンをタップした際の動作をActionプロパティで指定します。 Actionには、これまでテンプレートメッセージ等で使用していたITemplateActionインターフェースを実装するクラスに加え、今回追加されたカメラや、カメラロールを起動するアクションが使用できます。

  • MessageTemplateAction
  • UriTemplateAction
  • PostbackTemplateAction
  • LocationTemplateAction
  • DateTimePickerTemplateAction
  • CameraTemplateAction (New!)
  • CameraRollTemplateAction (New!)

Flex Message の使い方

Flex Messageの実装例を以下に書きましたので、こちらを参考に試してみてください。

LineMessagingApi/FlexMessageSampleApp.cs at master · pierre3/LineMessagingApi · GitHub

なお、ライブラリでは、以下の3種類の実装方法をサポートしています。

1. Flex Message のJSON文字列をそのまま利用する

LineMessagingClientに追加された以下のメソッドを 利用することでJSON文字列を直接指定してメッセージを送れるようになりました。

  • ReplyMessageWithJsonAsync
  • PushMessageWithJsonAsync
  • MulticastMessageWithJsonAsync

この方法は、LINE Developers で公開されている Flex Message Simulatorで生成したJSONをコピーして利用する場合に便利です。

private static readonly string FlexJson =
@"{
  ""type"": ""flex"",
  ""contents"": {
    ""type"": ""bubble"",
    ""direction"": ""ltr"",
    ""hero"": {
      ""type"": ""image"",
      ""url"": ""https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png"",
      ""size"": ""full"",
      ""aspectRatio"": ""20:13"",
      ""aspectMode"": "
...(省略)
};
private static readonly string TextMessageJson = "{ \"type\" : \"text\", \"text\" : \"I Sent a flex message with json string.\" }";

private async Task ReplyFlexWithJson(MessageEvent ev)
{
    await MessagingClient.ReplyMessageWithJsonAsync(ev.ReplyToken, FlexJson, TextMessageJson);
}

2. オブジェクト初期化子を使ってFlex Messageのオブジェクトを組み立てる

以下の様に、Flexの各オブジェクトのプロパティ値をオブジェクト初期化子内で設定してオブジェクトを組み立てます。
この方法で実装した場合、コードの構造が生成されるJSONと一致するため、JSONを見慣れている人には結果がイメージしやすいかもしれません。

private async Task ReplyFlexWithObjectInitializer(MessageEvent ev)
{

    var restrant = CreateRestrantWithObjectInitializer();
    var news = CreateNewsWithExtensions();
    var receipt = CreateReceiptWithExtensions();

    //バブルメッセージ
    var bubble = new FlexMessage("Bubble Message")
    {
        Contents = restrant
    };

    //カルーセルメッセージ
    var carousel = new FlexMessage("Carousel Message")
    {
        Contents = new CarouselContainer()
        {
            Contents = new BubbleContainer[]
            {
                restrant,
                news,
                receipt
            }
        },
        QuickReply = new QuickReply(new[]
        {
            new QuickReplyButtonObject(new CameraRollTemplateAction("CameraRoll")),
            new QuickReplyButtonObject(new CameraTemplateAction("Camera")),
            new QuickReplyButtonObject(new LocationTemplateAction("Location"))
        })
    };

    await MessagingClient.ReplyMessageAsync(ev.ReplyToken, new FlexMessage[] { bubble, carousel });
}

//バブルコンテナの作成
private static BubbleContainer CreateRestrantWithObjectInitializer()
    {
        return new BubbleContainer()
        {
            Hero = new ImageComponent()
            {
                Url = "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
                Size = ComponentSize.Full,
                AspectRatio = AspectRatio._20_13,
                AspectMode = AspectMode.Cover,
                Action = new UriTemplateAction(null, "http://linecorp.com/")
            },
            Body = new BoxComponent()
            {
                Layout = BoxLayout.Vertical,
                Contents = new IFlexComponent[]
                {
                    new TextComponent()
                    {
                        Text = "Broun Cafe",
                        Weight = Weight.Bold,
                        Size = ComponentSize.Xl
                    },
                    new BoxComponent()
                    {
                        Layout = BoxLayout.Baseline,
                        Margin = Spacing.Md,
                        Contents = new IFlexComponent[]
                        {
                            new IconComponent()
                            {
                                Url = "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
                                Size = ComponentSize.Sm
                            },
                            new IconComponent()
                            {
                                Url = "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
                                Size = ComponentSize.Sm
                            },
                            new IconComponent()
                            {
                                Url = "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
                                Size = ComponentSize.Sm
                            },
                            new IconComponent()
                            {
                                Url = "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
                                Size = ComponentSize.Sm
                            },
                            new IconComponent()
                            {
                                Url = "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png",
                                Size = ComponentSize.Sm
                            },
                            new TextComponent()
                            {
                                Text = "4.0",
                                Size = ComponentSize.Sm,
                                Margin = Spacing.Md,
                                Flex = 0,
                                Color = "#999999"
                            }
                        }
                    },
                    new BoxComponent()
                    {
                        Layout = BoxLayout.Vertical,
                        Margin = Spacing.Lg,
                        Spacing = Spacing.Sm,
                        Contents = new IFlexComponent[]
                        {
                            new BoxComponent()
                            {
                                Layout = BoxLayout.Baseline,
                                Spacing = Spacing.Sm,
                                Contents = new IFlexComponent[]
                                {
                                    new TextComponent()
                                    {
                                        Text = "Place",
                                        Size = ComponentSize.Sm,
                                        Color = "#aaaaaa",
                                        Flex = 1
                                    },
                                    new TextComponent()
                                    {
                                        Text = "Miraina Tower, 4-1-6 Shinjuku, Tokyo",
                                        Size = ComponentSize.Sm,
                                        Wrap = true,
                                        Color = "#666666",
                                        Flex = 5
                                    }
                                }
                            }
                        }
                    },
                    new BoxComponent(BoxLayout.Baseline)
                    {
                        Spacing = Spacing.Sm,
                        Contents = new IFlexComponent[]
                        {
                            new TextComponent()
                            {
                                Text = "Time",
                                Size = ComponentSize.Sm,
                                Color = "#aaaaaa",
                                Flex = 1
                            },
                            new TextComponent()
                            {
                                Text = "10:00 - 23:00",
                                Size = ComponentSize.Sm,
                                Wrap = true,
                                Color = "#666666",
                                Flex=5
                            }
                        }
                    }
                }
            },
            Footer = new BoxComponent()
            {
                Layout = BoxLayout.Vertical,
                Spacing = Spacing.Sm,
                Flex = 0,
                Contents = new IFlexComponent[]
                    {
                        new ButtonComponent()
                        {
                            Action = new UriTemplateAction("Call", "https://linecorp.com"),
                            Style = ButtonStyle.Link,
                            Height = ButtonHeight.Sm
                        },
                        new ButtonComponent()
                        {
                            Action = new UriTemplateAction("WEBSITE", "https://linecorp.com"),
                            Style = ButtonStyle.Link,
                            Height = ButtonHeight.Sm
                        },
                        new SpacerComponent()
                        {
                            Size = ComponentSize.Sm
                        }
                    }
            },
            Styles = new BubbleStyles()
            {
                Body = new BlockStyle()
                {
                    BackgroundColor = ColorCode.FromRgb(192, 200, 200),
                    Separator = true,
                    SeparatorColor = ColorCode.DarkViolet
                },
                Footer = new BlockStyle()
                {
                    BackgroundColor = ColorCode.Ivory
                }
            }
        };
    }

}

3. ヘルパーメソッドを使ってメソッドチェーンでFlex Messageオブジェクトを生成す

BubbleContainerクラスやBoxComponentクラスに付加された拡張メソッドを利用することで、以下の様にメソッドチェーンで記述することも可能です。
こちらの方が多少すっきりと記述できると思います。

private async Task ReplyFlexWithExtensions(MessageEvent ev)
{
    var restrant = CreateRestrantWithObjectInitializer();
    var news = CreateNewsWithExtensions();
    var receipt = CreateReceiptWithExtensions();

    //バブルメッセージ
    var bubble = FlexMessage.CreateBubbleMessage("Bubble Message")
        .SetBubbleContainer(restrant);

    //カルーセルメッセージ
    var carousel = FlexMessage.CreateCarouselMessage("Carousel Message")
        .AddBubbleContainer(restrant)
        .AddBubbleContainer(news)
        .AddBubbleContainer(receipt)
        .SetQuickReply(new QuickReply(new[]
        {
            new QuickReplyButtonObject(new CameraTemplateAction("Camera")),
            new QuickReplyButtonObject(new LocationTemplateAction("Location"))
        }));

    await MessagingClient.ReplyMessageAsync(ev.ReplyToken, new FlexMessage[] { bubble, carousel });
}

//バブルコンテナの生成
private static BubbleContainer CreateNewsWithExtensions()
{
    return new BubbleContainer()
        .SetHeader(BoxLayout.Horizontal)
            .AddHeaderContents(new TextComponent("NEWS DIGEST") { Weight = Weight.Bold, Color = "#aaaaaa", Size = ComponentSize.Sm })
        .SetHero(imageUrl: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_4_news.png",
                size: ComponentSize.Full, aspectRatio: AspectRatio._20_13, aspectMode: AspectMode.Cover)
            .SetHeroAction(new UriTemplateAction(null, "http://linecorp.com/"))
        .SetBody(boxLayout: BoxLayout.Horizontal, spacing: Spacing.Md)
            .AddBodyContents(new BoxComponent(BoxLayout.Vertical) { Flex = 1 }
                .AddContents(new ImageComponent("https://scdn.line-apps.com/n/channel_devcenter/img/fx/02_1_news_thumbnail_1.png")
                { AspectMode = AspectMode.Cover, AspectRatio = AspectRatio._4_3, Size = ComponentSize.Sm, Gravity = Gravity.Bottom })
                .AddContents(new ImageComponent("https://scdn.line-apps.com/n/channel_devcenter/img/fx/02_1_news_thumbnail_2.png")
                { AspectMode = AspectMode.Cover, AspectRatio = AspectRatio._4_3, Size = ComponentSize.Sm, Gravity = Gravity.Bottom }))
            .AddBodyContents(new BoxComponent(BoxLayout.Vertical) { Flex = 2 }
                .AddContents(new TextComponent("7 Things to Know for Today") { Gravity = Gravity.Top, Size = ComponentSize.Xs, Flex = 1 })
                .AddContents(new SeparatorComponent())
                .AddContents(new TextComponent("Hay fever goes wild") { Gravity = Gravity.Center, Size = ComponentSize.Xs, Flex = 2 })
                .AddContents(new SeparatorComponent())
                .AddContents(new TextComponent("LINE Pay Begins Barcode Payment Service") { Gravity = Gravity.Center, Size = ComponentSize.Xs, Flex = 2 })
                .AddContents(new SeparatorComponent())
                .AddContents(new TextComponent("LINE Adds LINE Wallet") { Gravity = Gravity.Bottom, Size = ComponentSize.Xs, Flex = 1 }))
        .SetFooter(BoxLayout.Horizontal)
            .AddFooterContents(new ButtonComponent() { Action = new UriTemplateAction("More", "https://linecorp.com") });
}