UNPKG

@wickr-sample-integrations/wickrio-rekognition-bot

Version:

WickrIO Rekognition Bot

432 lines (376 loc) 12.9 kB
const WickrIOBotAPI = require('wickrio-bot-api'); const util = require('util') const logger = require('wickrio-bot-api').logger const FileReader = require('filereader') const Stream = require('stream') //var FileAPI = require('file-api') // , File = FileAPI.File const bot = new WickrIOBotAPI.WickrIOBot(); const WickrIOAPI = bot.apiService().WickrIOAPI; const fs = require('fs') const path = require('path') const { RecognizeCelebritiesCommand, DetectFacesCommand, RekognitionClient } = require('@aws-sdk/client-rekognition'); const { fromIni } = require('@aws-sdk/credential-providers'); const { DetectLabelsCommand } = require("@aws-sdk/client-rekognition"); // snippet-start:[s3.JavaScript.buckets.uploadV3] const { PutObjectCommand, S3Client } = require("@aws-sdk/client-s3"); const { fileURLToPath } = require("url"); console.log = function () { logger.info(util.format.apply(null, arguments)) } console.error = function () { logger.error(util.format.apply(null, arguments)) } /* * Set the AWS Region and the profile from the .aws/credentials file * TODO These values should be configured */ var REGION = "" var profileName = "" const s3Bucket = "rekognize4bots"; module.exports = WickrIOAPI; process.stdin.resume(); //so the program will not close instantly async function exitHandler(options, err) { try { var closed = await bot.close(); console.log(closed); if (err) { console.log("Exit Error:", err); process.exit(); } if (options.exit) { process.exit(); } else if (options.pid) { process.kill(process.pid); } } catch (err) { console.log(err); } } //catches ctrl+c and stop.sh events process.on('SIGINT', exitHandler.bind(null, { exit: true })); // catches "kill pid" (for example: nodemon restart) process.on('SIGUSR1', exitHandler.bind(null, { pid: true })); process.on('SIGUSR2', exitHandler.bind(null, { pid: true })); //catches uncaught exceptions process.on('uncaughtException', exitHandler.bind(null, { exit: true })); function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function main() { logger.info('entering main') try { var status; if (process.argv[2] === undefined) { var bot_username = fs.readFileSync('client_bot_username.txt', 'utf-8'); bot_username = bot_username.trim(); status = await bot.start(bot_username) } else { status = await bot.start(process.argv[2]) } // set the avatar const avatarPath=path.join(process.cwd(), 'AWS_Rekognition.png') logger.info(`avatar path:${avatarPath}`) if (await WickrIOAPI.cmdSetAvatar(avatarPath)) { logger.info('avatar set') } else { logger.info('avatar NOT set') } bot.processesJsonToProcessEnv() var tokens = JSON.parse(process.env.tokens) // Set the global configuration values REGION = tokens.AWS_REGION.value profileName = tokens.AWS_PROFILENAME.value if (!status) { exitHandler(null, { exit: true, reason: 'Client not able to start' }); } await bot.startListening(listen); //Passes a callback function that will receive incoming messages into the bot client } catch (err) { logger.error(err); } } // Calls DetectFaces API and shows estimated ages of detected faces. async function DetectFaces(imageData, vGroupID) { // Set the parameters. var params = { Image: { Bytes: imageData, }, Attributes: ["ALL"], }; try { /* const rekogClient = new RekognitionClient({region: REGION, credentials: fromIni({profile: profileName,}), }); const data = await rekogClient.send(new DetectFacesCommand(params)); // show each face and build out estimated age table for (var i = 0; i < data.FaceDetails.length; i++) { table += "<tr><td>" + data.FaceDetails[i].AgeRange.Low + "</td><td>" + data.FaceDetails[i].AgeRange.High + "</td></tr>"; } table += "</table>"; document.getElementById("opResult").innerHTML = table; return data; // For unit tests. */ // const paramrstring = JSON.stringify(params) // logger.info(`params=${paramrstring}`) const detect_labels = async () => { var rekogClient try { console.log('calling Rekognitionclient') rekogClient = new RekognitionClient({region: REGION, credentials: fromIni({profile: profileName,}), }); } catch(err) { console.error(`Error from new RekognitionClient ${err}`); return; } var response try { console.log('calling rekogClient.send') response = await rekogClient.send(new RecognizeCelebritiesCommand(params)); if (response=== undefined) { console.error('RekognitionClient send returned undefined!') return; } } catch (err) { console.error(`Error from rekogClient.send ${err}`); return; } try { const responsestring = JSON.stringify(response) logger.info(`response=${responsestring}`) if (response.CelebrityFaces === undefined) { logger.info('response.CelebrityFaces == undefined') var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, "Did not detect a celebrity face") return } // logger.info(`response.Labels=${response.Labels}`) response.CelebrityFaces.forEach(async celebrity => { logger.info(`Name: ${celebrity.Name}`) logger.info(`MatchConfidence: ${celebrity.MatchConfidence}`) logger.info("-------") const responseString = `*Celebrity*: ${celebrity.Name}\n*Confidence*: ${celebrity.MatchConfidence}`; var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, responseString) }) if (response.UnrecognizedFaces !== undefined && response.UnrecognizedFaces.length > 0) { const responseString = "There are " + response.UnrecognizedFaces.length + " faces that are unrecognized"; var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, responseString) } return response; // For unit tests. } catch (err) { console.error(`Error ${err}`); } }; detect_labels(); } catch (err) { console.log("Error", err); } } async function File2Base64(filename, vGroupID) { console.log('Entered File2Base64: filename=', filename) var base64ImageStr try { // read image file var data = fs.readFileSync(filename) // get image file extension name const extensionName = path.extname(filename); // convert image file to base64-encoded string const base64Image = Buffer.from(data, 'binary').toString('base64'); // combine all strings // base64ImageStr = `${base64Image}`; base64ImageStr = `data:image/jpeg;base64,${base64Image}`; // base64ImageStr = `data:image/${extensionName.split('.').pop()};base64,${base64Image}`; return base64ImageStr } catch (err) { console.error(err) return undefined } } // Loads selected image and unencodes image bytes for Rekognition DetectFaces API. async function ProcessImage(filename, vGroupID) { console.log('Entered ProcessImage: filename=', filename) try { // var base64image = await File2Base64(filename, vGroupID) var data try { // read image file data = fs.readFileSync(filename) } catch (err) { console.error(err) return undefined } if (data === undefined) { console.error('failed to get file data!') } else { /* const length = data.length; const imageBytes = new ArrayBuffer(length); const ua = new Uint8Array(imageBytes); for (var i = 0; i < length; i++) { ua[i] = data.charCodeAt(i); } */ // Call Rekognition. DetectFaces(data, vGroupID); } } catch (err) { console.error(err) } } async function listen(rMessage) { logger.info('entering listen') rMessage = JSON.parse(rMessage); var sender = rMessage.sender; var vGroupID = rMessage.vgroupid; var userArr = []; userArr.push(sender); if (rMessage.message) { var request = rMessage.message; var command = '', argument = ''; var parsedData = request.match(/(\/[a-zA-Z]+)(@[a-zA-Z0-9_-]+)?(\s+)?(.*)$/); if (parsedData !== null) { command = parsedData[1]; if (parsedData[4] !== '') { argument = parsedData[4]; } } /* * LIST Command */ if (command === '/list') { const header = 'List of files in the given directory:' let messagemeta = { table: { name: header, firstcolname: 'File', actioncolname: 'Select', rows: [], }, textcut: [] } let fileArr = []; fileArr.push(header); fs.readdirSync('files/').forEach(file => { const fileName = file.toString() fileArr.push(fileName); // Create entry in table const row = { firstcolvalue: fileName, response: fileName, } messagemeta.table.rows.push(row) }); // Generate the string to send const msg2send = fileArr.join('\n'); if (messagemeta.table.rows.length > 0) { const textcut = { startindex: header.length, endindex: msg2send.length, } messagemeta.textcut.push(textcut) } const messagemetastring = JSON.stringify(messagemeta) logger.info('messageMetaString=', messagemetastring) try { var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, msg2send, "", "", "", [], messagemetastring); logger.info(sMessage); } catch (err) { logger.error(err); } } /* * HELP Command */ else if (command === '/help') { const help = "Send me a picture of a celebrity and I will try to recognize which celebrities are in the picture. I will respond with a list of celebrities and a number of the faces I could not recognize." var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, help); logger.info(sMessage); } else if (command === '/rekognize') { var attachment = argument; logger.info(`attachment=${attachment}`) const words = attachment.split(' '); if (words.length !== 2) { var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, 'Invalid usage: /rekognize <S3 bucket> <S3 filename>'); } else { const bucket = words[0] const photo = words[1] logger.info(`bucket=${bucket}`) logger.info(`photo=${photo}`) // Set params const params = { Image: { S3Object: { Bucket: bucket, Name: photo }, }, } const paramrstring = JSON.stringify(params) logger.info(`params=${paramrstring}`) const detect_labels = async () => { try { const rekogClient = new RekognitionClient({region: REGION, credentials: fromIni({profile: profileName,}), }); const response = await rekogClient.send(new DetectLabelsCommand(params)); logger.info(`response.Labels=${response.Labels}`) response.Labels.forEach(async label =>{ const labelString = JSON.stringify(label) var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, labelString) logger.info(`Confidence: ${label.Confidence}`) logger.info(`Name: ${label.Name}`) logger.info('Instances:') label.Instances.forEach(instance => { logger.info(instance) }) logger.info('Parents:') label.Parents.forEach(name => { logger.info(name) }) logger.info("-------") }) return response; // For unit tests. } catch (err) { logger.error(`Error ${err}`); } }; detect_labels(); } } else { logger.error('bad command'); var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, 'Invalid command'); } } else if (rMessage.file) { logger.info('processing file') const filename=rMessage.file.localfilename.toString() logger.info(`filename=${filename}`) await ProcessImage(filename, vGroupID) fs.unlinkSync(filename) } else { logger.error('bad command'); var sMessage = await WickrIOAPI.cmdSendRoomMessage(vGroupID, 'Invalid command'); } } main();