LoginSignup
2
1

More than 5 years have passed since last update.

FaceAPIを使った年齢判定LINE BotをNode.jsで作ってみた

Last updated at Posted at 2018-06-13

最新のコードと手順書は、Githubにあります

linefacebot

Messege API

LINE Developers

アクセストークンをコピーしておく

Face API

1分あたり20トランザクションまで、できるため、とりあえずはFree価格で十分足りる

価格 - Face API | Microsoft Azure

KEYをコピーしておく

Function App

  1. 新規作成

HttpTriggerの関数を作成

LINEから来たメッセージを処理する関数

  1. [新しい関数]を選択
  2. HttpTriggerのテンプレートを選択
  3. 言語:JavaScript、承認レベルはFunctionの関数を作成
  4. [開発]タブを選択し、LINEからHTTP送信されたBodyをAzure Queue Storageに設定する処理を追加(index.js)
  5. Azure Queue Storageへのバインド設定を追加する(function.js)

関数名: HttpTriggerJS1
Azure Queue Storage: outputQueueItem
キュー名: js-queue-items
ストレージアカウント接続: AzureWebJobsDashboard

index.js
module.exports = function (context, req) {
  context.bindings.outputQueueItem = req.body;
  res = { body : "" };
  context.done();
};
function.js
{
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    },
    {
      "type": "queue",
      "name": "outputQueueItem",
      "queueName": "js-queue-items",
      "connection": "AzureWebJobsDashboard",
      "direction": "out"
    }
  ],
  "disabled": false
}

QueueTriggerの関数を作成

キューに設定された内容を処理する関数を作成

  1. [新しい関数]を選択
  2. QueueTriggerのテンプレートを選択
  3. 言語:JavaScript、関数HttpTriggerJS1と一致するキュー名ストレージアカウント接続を入力し関数を作成
  4. [開発]タブを選択し、Azure Queue Storageに設定された内容を取得し、画像ならFaceAPIに渡し、年齢をLINEに送信する処理を追加(index.js)

関数名: QueueTriggerJS1
キュー名: js-queue-items (関数HttpTriggerJS1と一致)
ストレージアカウント接続: AzureWebJobsDashboard (関数HttpTriggerJS1と一致)

index.js
const https = require('https');
const url = require('url');

const FACE_API = 'https://australiaeast.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender,smile';

/**
 * JavaScript queue trigger function processed work item
 * @param {*} context
 * @param {*} myQueueItem
 */
module.exports = function(context, myQueueItem) {
  myQueueItem.events.forEach((event) => postMessage(context, event));
  context.done();
};

/**
 * Determining the message type
 * @param {*} context
 * @param {*} event
 */
function postMessage(context, event) {
  var messageType = event.message.type;
  if (messageType === 'text') {
    postLineMessage(context, event, '画像をおくってください');
    // postCognitiveUrl(context, event);
  } else if (messageType === 'image') {
    getImageData(context, event)
    .then((postData) =>{
        postCognitiveImage(context, event, postData);
    })
    .catch((err) => {
      context.log(err);
      postCognitiveImage(context, event, err);
    });
  } else if (messageType === 'sticker') {
    postLineMessage(context, event, '画像をおくってください');
  } else {
    postLineMessage(context, event, '画像をおくってください');
  }
}

/**
 *  Send image data to Face API
 * @param {*} context
 * @param {*} event
 * @param {*} postData
 */
function postCognitiveImage(context, event, postData) {
  var parseUrl = url.parse(FACE_API);
  var postOptions = {
      host: parseUrl.host,
      path: parseUrl.path,
      method: 'POST',
      headers: {
          'Content-Type': 'application/octet-stream',
          'Content-Length': postData.length,
          'Ocp-Apim-Subscription-Key': process.env.COGNITIVE_KEY,
      },
  };

  var bodyString = null;

  var req = https.request(postOptions, function(res) {
     context.log('STATUS: ' + res.statusCode);
    res.setEncoding('utf8');
    res.on('data', function(chunk) {
      bodyString = chunk;
    }).on('end', function() {
      if (res.statusCode !== 200 || bodyString === '[]') {
        postLineMessage(context, event, '顔が認識できませんでした');
      } else {
        var result = JSON.parse(bodyString);
        var age = result[0].faceAttributes.age;
        postLineMessage(context, event, age + '');
      }
    });
  });
  req.write(postData);
  req.end();
}

/**
 * Send message to LINE
 * @param {*} context
 * @param {*} event
 * @param {*} msg
 */
function postLineMessage(context, event, msg) {
  var jObj = {};
  jObj.type = 'text';
  jObj.id = event.message.id;
  jObj.text = msg;

  var postData = JSON.stringify({
    'replyToken': event.replyToken,
    'messages': [jObj],
  });

  var parseUrl = url.parse('https://api.line.me/v2/bot/message/reply');
  var postOptions = {
    host: parseUrl.host,
    path: parseUrl.path,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer {' + process.env.LINE_CHANNEL_ACCESS_TOKEN + '}',
      'Content-Length': Buffer.byteLength(postData),
    },
  };

  var req = https.request(postOptions);
  req.write(postData);
  req.end();
}

/**
 * Retrieve image data from LINE
 * @param {*} context
 * @param {*} event
 */
function getImageData(context, event) {
    return new Promise((resolve, reject) => {
        var messageId = event.message.id;
        var parseUrl = url.parse('https://api.line.me/v2/bot/message/' + messageId + '/content');
        var postOptions = {
        host: parseUrl.host,
        path: parseUrl.path,
        method: 'GET',
            headers: {
                'Content-type': 'application/json',
                'Authorization': 'Bearer {' + process.env.LINE_CHANNEL_ACCESS_TOKEN + '}',
            },
        };
        var req = https.request(postOptions, function(res) {
            var data = [];
            res.on('data', function(chunk) {
                data.push(new Buffer(chunk));
            }).on('error', function(err) {
                context.log(err);
                postLineMessage(context, event, err);
                reject(err);
            }).on('end', function() {
                var postData = Buffer.concat(data);
                resolve(postData);
            });
            });
        req.end();
    });
}
function.js
{
  "bindings": [
    {
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in",
      "queueName": "js-queue-items",
      "connection": "AzureWebJobsDashboard"
    }
  ],
  "disabled": false
}

※ コードはエラー処理も足りないと思うので自己責任で、コピーまたは、編集してください

それぞれのアプリケーションを接続

  1. Function APIのアプリケーション設定を開く
  2. アプリ設定にFace APIのKEY、とLINEのMessaging APIのKEYを環境変数に設定
  3. HttpTriggerの関数のURL(エンドポイント)をコピーする
  4. LINEのMessaging APIのWebhookにFunction Appのエンドポイントを指定する
環境変数 KEY
COGNITIVE_KEY FaceAPIのKEY
LINE_CHANNEL_ACCESS_TOKE Messaging APIのアクセストークン

画像をLINEBoTに送信してみる

年齢が返ってくれば成功
以下は、フリー画像の美少女さんを使わせて頂いています

faceBot-test.jpg

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1