LineBotに顔画像を送信し、Face APIを使って表情判定してもらう

##はじめに
Qiita内にはNode.jsやFace APIを使って画像を認識させるLine Botの記事が少ない印象でした。

今回は下記記事を参考にしました。

https://zenn.dev/tmitsuoka0423/books/939110f3e1f533d35095/viewer

`GitHub`からソースコードを引っ張ってきて、実装しました。

##下準備

下記記事を参考にLine Botを使用できる状態にします。

https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d#2-nodejs%E3%81%A7bot%E9%96%8B%E7%99%BA

`npm init -y`
`npm i @azure/cognitiveservices-face @azure/ms-rest-azure-js`
でAzureを使えるようにします。

##全体像

```javascript
'use strict';

const fs = require("fs");
// Azure Face APIを使うための準備
const { FaceClient } = require("@azure/cognitiveservices-face");
const { CognitiveServicesCredentials } = require("@azure/ms-rest-azure-js");
const faceKey = ""; //個人のKey
const faceEndPoint = "";　//個人のEndPoint
const cognitiveServiceCredentials = new CognitiveServicesCredentials(faceKey);
const faceClient = new FaceClient(cognitiveServiceCredentials, faceEndPoint);

//APIを叩ける
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

const config = {
    channelSecret: 'Lineのシークレットチャネル',
    channelAccessToken: 'Lineのアクセストークン'
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  let message = {
    type: 'text',
    text: null
  };
  if(event.message.type === "image"){
    console.log("画像を検出しました");
    try{
      //画像を取得
      const image = await downloadContent(event.message.id);
      // 画像を取得し終わったらFace APIに送信して、顔検出を行う
      const faceList = await faceClient.face.detectWithStream(image, {
        returnFaceAttributes: ["age", "smile", "makeup", "glasses", "facialHair"], // ここにカンマ(,)区切りでパラメータを追加する
      });
      message = {
        type: 'text',
        text: ' 年齢 ：'+faceList[0].faceAttributes.age+'歳'
          +'\n 笑顔 ：'+ faceList[0].faceAttributes.smile * 100 + '点'
          +'\n ひげ ：'+ faceList[0].faceAttributes.facialHair.beard
          +'\nメイク：'+ faceList[0].faceAttributes.makeup.eyeMakeup
          +'\nメガネ：'+ faceList[0].faceAttributes.glasses

      };
      return client.replyMessage(event.replyToken, message);
    }catch (e) {
      console.error(e);

      return client.replyMessage(event.replyToken, {
        type: "text",
        text: "画像取得or画像分析中にエラーが発生しました。",
      });
    }
  }else{
    message = {
      type: 'text',
      text: '申し訳ありません。個別メッセージには対応しておりません。',
    };
    return client.replyMessage(event.replyToken, message);
  }
}

async function downloadContent(messageId, downloadPath = "./image.png") {
  const data = [];
  return client.getMessageContent(messageId).then(
    (stream) =>
      new Promise((resolve, reject) => {
        const writable = fs.createWriteStream(downloadPath);
        stream.on("data", (chunk) => data.push(Buffer.from(chunk)));
        stream.pipe(writable);
        stream.on("end", () => resolve(Buffer.concat(data)));
        stream.on("error", reject);
      })
  );
}
app.listen(PORT);
console.log(`Server running at ${PORT}`);
```

Line　Botに対して、`image`タイプのメッセージが送信されたら、動く仕様です。
それ以外のタイプには、`申し訳ありません。個別メッセージには対応しておりません。`と出力されます。

今回は「笑顔」をメインとしていますが、他にもいろいろあるみたいです。

https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/concepts/face-detection#attributes

##追記
■GitHubからソースコードをコピーしたときの注意点
・`1時間でLINE Botを作るハンズオン`のサイトを参考にBot作成をした場合、
 ソースコード115行目：
`app.post("/", line.middleware(config), (req, res) => {`
　↓
`app.post("/webhook", line.middleware(config), (req, res) => {`
へと変更が必要です。（2021/11/23）