honoとCloudflareでLINE Botを作る
今話題(?)のhonoとCloudflare Workersで試しにLINE Botを作ってみました。
以下に全体のコードを公開しています。
まずはhonoのチュートリアルをやってみる
honoのgetting-started
のページにはベーシックなものからCloudflare、AWS Lambda、Vercelなど各デプロイ先に即したチュートリアルページが用意されています。
今回はこの中からCloudflare Workersを選択して実施しました。
詳細は公式ページを参照いただければと思いますが以下の4行でデプロイまではすんなり済んでしまいます。
yarn create hono my-app
cd my-app
yarn
yarn deploy
LINE Botに作り変える
以前作ったCloud Run x Expressでのコードを移植し、honoに書き換えていきます。
import { Hono } from 'hono';
type Bindings = {
LINE_CHANNEL_ACCESS_TOKEN: string;
LINE_CHANNEL_SECRET: string;
};
const app = new Hono<{ Bindings: Bindings }>();
Bindings
として環境変数を定義しておいて、const app = new Hono<{ Bindings: Bindings }>();
のように渡すとc.env
で値を取得できるようになります。
KV namespaces
, D1 database
, R2 bucket
などもここで定義してcontext経由で使用するようです。
個人的には少し変な感じがしますが、型もちゃんと付くので慣れれば特に問題ない気がします。
次にメインの/webhook
エンドポイントです。
import * as line from '@line/bot-sdk';
app.post('/webhook', async (c) => {
const config: line.ClientConfig = {
channelAccessToken: c.env.LINE_CHANNEL_ACCESS_TOKEN,
};
const client = new line.messagingApi.MessagingApiClient(config);
line.middleware({ channelSecret: c.env.LINE_CHANNEL_SECRET });
const events: line.WebhookEvent[] = await c.req.json().then((data) => data.events);
await Promise.all(
events.map(async (event: line.WebhookEvent) => {
try {
await textEventHandler(client, event);
} catch (err: unknown) {
if (err instanceof Error) {
console.error(err);
}
return c.status(500);
}
}),
);
return c.status(200);
});
Cloud Runにデプロイしたときのコードをほぼ流用していますが、@line/bot-sdk
の使い方が少し変わっていました。
公式によるとimport * as line from '@line/bot-sdk';
のように名前空間のimportを行い、さらにsecretはclient生成時ではなくline.middleware
を通して設定するようです。
// ES Modules or TypeScript
import * as line from '@line/bot-sdk';
new line.messagingApi.MessagingApiClient({
channelAccessToken: 'YOUR_CHANNEL_ACCESS_TOKEN',
});
line.middleware({
channelSecret: 'YOUR_CHANNEL_SECRET'
});
また、この際以下の記事にあるようなエラーが出たので、wrangler.toml
に設定を追加しています。
compatibility_flags = [ "nodejs_compat" ]
最後にtextEventHandler
という関数ですが、こちらは前回のリポジトリのコードからBigQuery
の処理を省いて、それ以外はほぼそのまま流用しています。
const textEventHandler = async (
client: line.messagingApi.MessagingApiClient,
event: line.WebhookEvent,
): Promise<line.MessageAPIResponseBase | undefined> => {
if (event.type !== 'message' || event.message.type !== 'text') {
return;
}
const { replyToken, message: { text } = {} } = event;
const response: line.TextMessage = {
type: 'text',
text: `${text}と言われましても`,
};
const replyMessageRequest: line.messagingApi.ReplyMessageRequest = {
replyToken: replyToken,
messages: [response],
};
await client.replyMessage(replyMessageRequest);
};
Secretの設定とデプロイ
Bindings
に定義している環境変数ですが、公開して良いものであればwrangler.toml
に直接記載することができます。
name = "my-worker-dev"
[vars]
API_HOST = "example.com"
API_ACCOUNT_ID = "example_user"
SERVICE_X_DATA = { URL = "service-x-api.dev.example", MY_ID = 123 }
ただしこれだとリポジトリにcommitすることになりますし、Cloudflareのコンソールでも値が見えてしまいます。
ですので、シークレットの場合は以下の様にコマンドにて設定すると良さそうです。
LINE_CHANNEL_ACCESS_TOKEN=your_value
LINE_CHANNEL_SECRET=your_value
echo $LINE_CHANNEL_ACCESS_TOKEN | yarn wrangler secret put LINE_CHANNEL_ACCESS_TOKEN
echo $LINE_CHANNEL_SECRET | yarn wrangler secret put LINE_CHANNEL_SECRET
これでオウム返しのLINE Botができました🎉
まとめ
かなり簡単にLINE Botをデプロイできました。
CloudflareだとこのままD1
などと繋げば本格的なBotの開発もスムーズにできそうでした。
この記事は以下の作業配信で経た知見をまとめたものです。試行錯誤をする姿が見たい方はぜひ御覧ください笑
Discussion