himanago

Azure・C#などのMS系技術やLINE関連技術など、好きな技術について書くブログ

【GitHub Codespaces + LINE OpenAPI + azd】最新の機能をサポートした C# SDK で LINE Bot 開発が始められるテンプレートを改良しました

昨年末、アドベントカレンダー記事用に実験的に「GitHub Codespaces + LINE OpenAPI による C# での LINE Bot 開発がすぐ始められるテンプレート」を作っていました。 himanago.hatenablog.com

明日このハンズオンイベントをやるので(準備がギリギリすぎる)

linedevelopercommunity.connpass.com

ここで使えるように以前の課題を克服してある程度使えるように改良してみました!

今回作ったテンプレートリポジトリ

こちら。使い方は README に簡単に書きました。

最新の LINE Messaging API の機能をサポートした SDK で開発が始められます。

github.com

改善点

Azure Developer CLI (azd) & Bicep 対応

今回一番やりたかったことですが、最初に azd up すれば一発でオウム返ししてくれる LINE Bot のバックエンド(Azure Functions)が Azure 上にデプロイされます!

LINE のチャネルアクセストークンも azd up 実行時のパラメータとして受け取って、Key Vault に格納するように組んでいるので、使い勝手もよいかと思います。

.NET 8 isolated → .NET 6 in-process への変更

以前作ったものでは .NET 8 isolated になってしまっていました。

Azure Functions の .NET 8 isolated のコードはいままでの書き方とだいぶ違っていたりして作りにくい感じだったのですが、今回は .NET 6 in-process に変更1。だいぶ作りやすくなったかと思います。

もうすぐ .NET 8 in-process が出るとのことなので、そちらが出たらアップグレードしようと思います。

関数コードの改良

DI で MessagingApiApi をもらってくるようにしたので、関数のクラスのコードがちょっとすっきりできたかなと思います。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using LineOpenApi.MessagingApi.Api;
using LineOpenApi.MessagingApi.Model;
using LineOpenApi.Webhook.Model;
using System.Collections.Generic;

namespace LineBotFunctions;
public class WebhookEndpoint
{
    private IMessagingApiApiAsync Api { get; }

    public WebhookEndpoint(IMessagingApiApiAsync api)
    {
        Api = api;
    }

    [FunctionName("WebhookEndpoint")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        ILogger log)
    {
        log.LogInformation("C# webhook endpoint function processed a request.");

        try
        {
            var body = await new StreamReader(req.Body).ReadToEndAsync();
            var callbackRequest = JsonConvert.DeserializeObject<CallbackRequest>(body);

            foreach (var ev in callbackRequest.Events)
            {
                // echo when receive text message
                if (ev is MessageEvent messageEvent && messageEvent.Message is TextMessageContent textMessageContent)
                {
                    var replyMessageRequest = new ReplyMessageRequest(messageEvent.ReplyToken, new List<Message>
                    {
                        new TextMessage(textMessageContent.Text)
                        {
                            Type = "text"
                        }
                    });
                    await Api.ReplyMessageAsync(replyMessageRequest);
                }
            }
        }
        catch (Exception e)
        {
            log.LogError($"Error: {e.Message}");
            log.LogError($"Error: {e.StackTrace}");
        }
        
        return new OkObjectResult("OK");
    }
}

Webhook で受け取る JSON のデシリアライズがうまくいかない件の対応&最新化対応

以前の記事にも書いたのですが、不要な行が OpenAPI Generator で生成した SDK の一部ファイルに含まれています。

そこで、シェルスクリプトでその行を一気に削除するようにしました。

この処理は SDK の生成とあわせてひとつのファイルにまとめ、再実行により常に最新のものにアップデートできるようにしています。

if [ -d "./sdk" ]; then
    rm -rf ./sdk
fi

npx @openapitools/openapi-generator-cli generate -i https://raw.githubusercontent.com/line/line-openapi/main/messaging-api.yml -o sdk -g csharp --additional-properties=netCoreProjectFile=true,targetFramework=netstandard2.1,library=httpclient,packageName=LineOpenApi.MessagingApi

npx @openapitools/openapi-generator-cli generate -i https://raw.githubusercontent.com/line/line-openapi/main/webhook.yml -o sdk -g csharp --additional-properties=netCoreProjectFile=true,targetFramework=netstandard2.1,library=httpclient,packageName=LineOpenApi.Webhook

find ./sdk/src/LineOpenApi.Webhook/Model/ -type f | while read file; do
    if ! grep -q ": IEquatable<" "$file"; then
        sed -i '/\[JsonConverter(typeof(JsonSubtypes), "Type")\]/d' "$file"
    fi
done

まとめ

まだオウム返ししか試せてないですが、明日のハンズオンでやりたい機能なども検証しつつハンズオン用のテンプレートも作っていこうと思います。

今回のテンプレートはハンズオンでもわりと使いやすいと思うので、今後ハンズオンする際に活用していきたいなと思います。


  1. .NET 6 に落とした際、OpenAPI Generator で生成したコードで使用している RestSharp でビルドエラーになったので、生成コマンドのオプションでこのライブラリを使わないように指定して回避しました。