昨年末、アドベントカレンダー記事用に実験的に「GitHub Codespaces + LINE OpenAPI による C# での LINE Bot 開発がすぐ始められるテンプレート」を作っていました。 himanago.hatenablog.com
明日このハンズオンイベントをやるので(準備がギリギリすぎる)
linedevelopercommunity.connpass.com
ここで使えるように以前の課題を克服してある程度使えるように改良してみました!
今回作ったテンプレートリポジトリ
こちら。使い方は README に簡単に書きました。
最新の LINE Messaging API の機能をサポートした SDK で開発が始められます。
改善点
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
まとめ
まだオウム返ししか試せてないですが、明日のハンズオンでやりたい機能なども検証しつつハンズオン用のテンプレートも作っていこうと思います。
今回のテンプレートはハンズオンでもわりと使いやすいと思うので、今後ハンズオンする際に活用していきたいなと思います。
- .NET 6 に落とした際、OpenAPI Generator で生成したコードで使用している RestSharp でビルドエラーになったので、生成コマンドのオプションでこのライブラリを使わないように指定して回避しました。↩