Closed7
Hono + Line Messaging API + Cloudflare Workers + R2トライアル
参考
wrangler.jsonc
/**
* Bindings
* Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
* databases, object storage, AI inference, real-time communication and more.
* https://developers.cloudflare.com/workers/runtime-apis/bindings/
*/
"r2_buckets": [
{
"binding": "R2_BUCKET",
"bucket_name": "[bucket_name]",
"preview_bucket_name": "[bucket_name]"
}
]
こちらは開発用途なので適当に
LINE_CHANNEL_ID=[random number]
LINE_CHANNEL_SECRET=[secret string]
index.ts
type Bindings = {
R2_BUCKET: R2Bucket;
LINE_CHANNEL_ID: string;
LINE_CHANNEL_SECRET: string;
}
const app = new Hono<{ Bindings: Bindings }>();
CFの Workers & Pages
> 設定
> 変数とシークレット
に以下をシークレットとして登録する
- LINE_CHANNEL_ID: LINE Messaging API の Channel ID
- LINE_CHANNEL_SECRET: LINE Messaging API の Channel secret
R2 にbucketを作成する
wrangler r2 bucket create [bucket_name]
やりたいことはこんな感じ。
- Lineで画像をアップロード
- Messaging APIのwebhookを使ってメッセージの情報を取得
- BearerTokenを生成
- BearerTokenを使って画像データを取得
- 画像データをR2にアップロード
BearerTokenを生成
ちょっと型は雑に...
ステートレスチャネルアクセストークンを利用する
- チャネルIDとチャネルシークレットから発行する
- Content-Type は
application/x-www-form-urlencoded
index.ts
async function fetchBearerToken(channelId: string, channelSecret: string) : Promise<string> {
const params = new URLSearchParams();
params.append('grant_type', 'client_credentials');
params.append('client_id', channelId);
params.append('client_secret', channelSecret);
const res = await fetch('https://api.line.me/oauth2/v3/token', {
method: 'POST',
body: params,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}); if (res.status !== 200) {
throw new Error(`Failed to fetch bearer token: ${res.status} ${res.statusText}`);
}
const data: any = await res.json();
return data.access_token;
}
app.post("/api/webhook", async (c) => {
const j = await c.req.json();
if (j.events[0].message.type !== 'text') {
const token = await fetchBearerToken(c.env.LINE_CHANNEL_ID, c.env.LINE_CHANNEL_SECRET);
}
return c.json({ message: "Hello World!" });
})
BearerTokenを使って画像データを取得
- responseはblobとして扱う
index.ts
async function fetchContent(token: string, messageId: string) : Promise<Blob> {
const res = await fetch(`https://api-data.line.me/v2/bot/message/${messageId}/content`, {
headers: { 'Authorization': `Bearer ${token}` }
})
if (res.status !== 200) {
throw new Error(`Failed to fetch bearer token: ${res.status} ${res.statusText}`);
}
const data = await res.blob()
return data;
}
app.post("/api/webhook", async (c) => {
const j = await c.req.json();
if (j.events[0].message.type !== 'text') {
const token = await fetchBearerToken(c.env.LINE_CHANNEL_ID, c.env.LINE_CHANNEL_SECRET);
const messageId = j.events[0].message.id;
const data = await fetchContent(token, messageId)
}
return c.json({ message: "Hello World!" });
});
- blob.type で mime-type を取得する
- mime-typeのスラッシュ以降の文字列をオブジェクトの拡張子として扱う
index.ts
async function uploadContent(bucket: R2Bucket, messageId: string, data: Blob) : Promise<void> {
const type = data.type;
const suffix = type.split("/")[1];
const uploaded = await bucket.put(messageId + "." + suffix, data, { httpMetadata: { contentType: type } });
console.log(JSON.stringify(uploaded))
}
app.post("/api/webhook", async (c) => {
const j = await c.req.json();
if (j.events[0].message.type !== 'text') {
const token = await fetchBearerToken(c.env.LINE_CHANNEL_ID, c.env.LINE_CHANNEL_SECRET);
const messageId = j.events[0].message.id;
const data = await fetchContent(token, messageId)
await uploadContent(c.env.R2_BUCKET, messageId, data)
}
return c.json({ message: "Hello World!" });
});
deployして、ログを確認という流れで便利
- npm run deploy
- npm run tail
package.json
{
"name": "line-bot-cf-worker-sample",
...
"scripts": {
"deploy": "wrangler deploy",
"tail": "wrangler tail",
...
},
このスクラップは2025/02/22にクローズされました