deno-linebotで画像ファイルを扱ってみる

# はじめに

[前回](https://qiita.com/bathtimefish/items/8f9d1406f043d6eb02e9)の続きです。[deno-linebot](https://github.com/WayneChang65/deno-linebot/)で画像なんかのファイルをポストした場合の扱い方がよくわからなかったので調べてみました。

# Event.message.content()

[ReadmeのEvent.message.content()](https://github.com/WayneChang65/deno-linebot/#eventmessagecontent)に `Get image, video, and audio data sent by users as a Buffer object. This is a shorthand for: LineBot.getMessageContent(event.message.messageId)` とさらっと説明されています。

```sample.ts
let content = await event.message.content();
console.log(content.toString('base64'));
```

どういうことかと思ってソースコードを覗いてみると[このへん](https://github.com/WayneChang65/deno-linebot/blob/0f2766b553801c4a8baca16a411f8e2988e8e194/deno-linebot.js#L103)とか[このへん](https://github.com/WayneChang65/deno-linebot/blob/0f2766b553801c4a8baca16a411f8e2988e8e194/deno-linebot.js#L268)がそれっぽい感じです。たしかに`LineBot.getMessageContent()`っぽく`messageId`をキーにアップされたコンテンツをfetch()してますね。取得できたbufferをhex string化して返しています。

# 検証してみる

とりあえず前回のソースコードのメッセージ受信部を以下のように変更して実行します

```src/index.ts
bot.on('message', async (event: any) => {
    try {
        console.info(`Received message from LINE Client`)
        console.info(`Type: ${event.type} ID: ${event.message.id}, Content: `)
	    let content = await event.message.content();
        console.log(content.toString('base64'));
    } catch (e) {
        console.error(e)
    }
})
```

```shell
deno run -A ./src/index.ts
```

適当な画像を投稿すると以下のようにそれっぽい文字列が出力されました

```shell
Received message fom LINE Client
Type: message ID: 458220234579902546, Content: 
ffd8ffe000104a46494600010100000100010000ffe202284943435f50524f46494c450001010000021800000000043000006d6e74725247422058595a20000000000000000000000000616...
```

ちゃんと動いている感じなので、オウム返しを作り込んでみます。

# やってみる

アップされた画像をサーバー上に保存してオウム返しするようにしてみます。まずファイルを保存するディレクトリを作成します

```
mkdir -p data
```

全ソースコードはこちら

```src/index.ts
import { opine, json, serveStatic } from "https://deno.land/x/opine@2.3.3/mod.ts";
import type { HTTPSOptions } from "https://deno.land/x/opine@2.3.3/mod.ts"
import { linebot } from "https://deno.land/x/linebot@v1.1.0/mod.ts"
import { hexToBuffer } from "https://deno.land/x/hextools/mod.ts";

const opineHttpsOptions: HTTPSOptions = {
    port: 3000,
    certFile: "cert/cert.pem",
    keyFile: "cert/key.pem",
}

const options = {
   channelId: "YOUR_CHANNEL_ID",
   channelSecret: "YOUR_CHANNEL_SECRET",
   channelAccessToken: "YOUR_CHANNEL_ACCESS_TOKEN",
   verify: true
}
const bot = linebot(options)
const app = opine()
const linebotParser = bot.parser(json)
app.use(serveStatic("data"))

app.post("/messaging/event", linebotParser)

bot.on('message', async (event: any) => {
    try {
        console.info(`Received message from LINE Client`)
        console.info(`Type: ${event.type} ID: ${event.message.id}`)
	    const content = await event.message.content()
        const buffer = hexToBuffer(content)
	    const fileName = `${Date.now()}.jpg`
	    await Deno.writeFile(`./data/${fileName}`, buffer)
	    const baseUrl = `https://line-bot.bathtimefish.com:3000`
	    const contentUrl = `${baseUrl}/${fileName}`
	    await event.reply({
	        type: "image",
	        originalContentUrl: contentUrl,
	        previewImageUrl: contentUrl
	    })
    } catch (e) {
        console.error(e)
    }
})

app.listen(opineHttpsOptions)
```

`event.message.content()`で取得したhex stringを[hextools](https://deno.land/x/hextools@v1.0.0)を使ってArray Bufferに変換しています。
opineの[serveStatic](https://deno.land/x/opine@2.3.4/mod.ts?s=serveStatic)で`data`ディレクトリを外部に公開しています

実行します

```shell
deno run -A ./src/index.ts
```

画像をアップすると`data`ディレクトリに保存された画像のパスが生成され、リプライされます。はい猫ちゃんかわいいですねかわいい。

![Screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/650ae71b-f7fb-b2ae-03a4-64446fc665b7.png)

# おわりに

同じ方法で動画や音声も扱えます。`event.reply()`のメッセージオブジェクトに関しては[Event.reply(message)](https://github.com/WayneChang65/deno-linebot/#eventreplymessage)に解説されているので参考にしてください