Confirm output of spell checker before proceeding in bot framework - botframework

I am trying to accomplish this:
I have a spell checker in the middleware. In case the user's input text does not match the corrected text, I would like to confirm the corrected text with the user before proceeding. I tried several approaches but have not been able to achieve this. Here's the code I wrote:
app.js:
var builder = require('botbuilder');
var restify = require('restify');
var spellService = require('./spell-service')
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create connector and listen for messages
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector, function (session) {
session.beginDialog('search');
});
bot.dialog('search', [
function (session, args, next) {
builder.Prompts.text(session, 'Type the name of a company you would like to search');
}
]);
//=========================================================
// Spell checker
//=========================================================
const dontCorrectIdentifier = "dontCorrect";
bot.use({
botbuilder: function (session, next) {
if (session.message.text.startsWith(dontCorrectIdentifier)) {
session.message.text = session.message.text.replace(dontCorrectIdentifier, "");
next();
} else {
spellService
.getCorrectedText(session.message.text)
.then(function (text) {
if (session.message.text !== text) {
var msg = new builder.Message(session)
.text("Did you mean \"" + text + "\"?")
.suggestedActions(builder.SuggestedActions.create(session, [
builder.CardAction.postBack(session, dontCorrectIdentifier + text, "Yes"),
builder.CardAction.postBack(session, dontCorrectIdentifier + session.message.text, "No")
]
));
builder.Prompts.text(session, msg);
}
next();
})
.catch(function (error) {
console.error(error);
next();
});
}
}
});
spell-service.js
// from https://dev.cognitive.microsoft.com/docs/services/56e73033cf5ff80c2008c679/operations/56e73036cf5ff81048ee6727
var request = require('request');
var SPELL_CHECK_API_URL = 'https://api.cognitive.microsoft.com/bing/v7.0/SpellCheck?mkt=en-US&mode=proof',
SPELL_CHECK_API_KEY = 'MY KEY';
/**
* Gets the correct spelling for the given text
* #param {string} text The text to be corrected
* #returns {Promise} Promise with corrected text if succeeded, error otherwise.
*/
exports.getCorrectedText = function (text) {
return new Promise(function (resolve, reject) {
if (text) {
var requestData = {
url: SPELL_CHECK_API_URL,
headers: {
"Ocp-Apim-Subscription-Key": SPELL_CHECK_API_KEY
},
form: {
text: text
},
json: true
};
request.post(requestData, function (error, response, body) {
if (error) {
reject(error);
} else if (response.statusCode != 200) {
reject(body);
} else {
var previousOffset = 0;
var result = '';
for (var i = 0; i < body.flaggedTokens.length; i++) {
var element = body.flaggedTokens[i];
// append the text from the previous offset to the current misspelled word offset
result += text.substring(previousOffset, element.offset);
// append the first suggested correction instead of the misspelled word
result += element.suggestions[0].suggestion;
// Increment the offset by the length of the misspelled word
previousOffset = element.offset + element.token.length;
}
// Append the text after the last misspelled word.
if (previousOffset < text.length) {
result += text.substring(previousOffset);
}
resolve(result);
}
});
} else {
resolve(text);
}
})
};
Based on Gary's suggestion, here's the right way to do this:
bot.use({
botbuilder: function (session, next) {
if (session.message.text.startsWith(dontCorrectIdentifier)) {
session.message.text = session.message.text.replace(dontCorrectIdentifier, "");
next();
} else {
spellService
.getCorrectedText(session.message.text)
.then(function (text) {
if (session.message.text !== text) {
var msg = new builder.Message(session)
.text("Did you mean \"" + text + "\"?")
.suggestedActions(builder.SuggestedActions.create(session, [
builder.CardAction.postBack(session, dontCorrectIdentifier + text, "Yes"),
builder.CardAction.postBack(session, dontCorrectIdentifier + session.message.text, "No")
]
));
session.send(msg);
} else {
next();
}
})
.catch(function (error) {
console.error(error);
next();
});
}
}
});

Modify the logic flow in the builder middle, which works fine on my side:
bot.use({
botbuilder: (session, next) => {
if (session.message.text.startsWith(dontCorrectIdentifier)) {
session.message.text = session.message.text.replace(dontCorrectIdentifier, "");
next();
} else {
var text = session.message.text;
var msg = new builder.Message(session)
.text("Did you mean \"" + text + "\"?")
.suggestedActions(builder.SuggestedActions.create(session, [
builder.CardAction.postBack(session, dontCorrectIdentifier + text, "Yes"),
builder.CardAction.postBack(session, dontCorrectIdentifier + session.message.text, "No")
]));
session.send(msg);
}
},
})
Use session.send(msg); replace builder.Prompts.text(session, msg).
And remove next() in the else condition.

You need to append text parameter in URL. I tried this in curl:
curl -v -X GET https://api.cognitive.microsoft.com/bing/v7.0/SpellCheck?mkt=en-US&mode=proof&text=hollo%2Cwrld
The other thing is hyphen between spell and service in your .js file. You can probably try to check json response first to see if you are getting correction back.

Related

Service Worker "notificationclick" not firing

The notification is showing fine, but when I click on it, or any of the actions, nothing happens. I see no logging, no error messages, but the notification does close (although it closes even when I comment out the event.notification.close()).
I've tried using the Chrome debugger, and I can set a break point in the code that shows the notification, but all breakpoints within the notificationclick handler fail to pause execution.
I've spent days trying to get this to work and I'm at my wits' end.
const auth = firebase.auth();
const functions = firebase.functions();
const done = functions.httpsCallable("done");
const snooze = functions.httpsCallable("snooze");
self.addEventListener("notificationclick", event => {
console.log("notificationclick", event);
const uid = auth.currentUser.uid;
const { id, url } = event.notification.data;
event.notification.close();
event.waitUntil(() => {
switch (event.action) {
case "done":
console.log("Done");
return done({ uid, id });
case "snooze1":
console.log("Snooze 1 Hour");
return snooze({ uid, id, hours: 1 });
case "snooze24":
console.log("Snooze 1 Day");
return snooze({ uid, id, hours: 24 });
default:
console.log("Open App");
return clients
.matchAll({
includeUncontrolled: true,
type: "window"
})
.then(clientList => {
for (let i = 0; i < clientList.length; i++) {
let client = clientList[i];
if (url[0] === "#") {
if (client.url.endsWith(url) && "focus" in client) {
return client.focus();
}
} else {
if (
client.url.replace(/#.*$/, "") === url &&
"focus" in client
) {
return client.focus();
}
}
}
if (clients.openWindow) {
return clients.openWindow(location.origin + url);
}
});
}
});
});
firebase
.messaging()
.setBackgroundMessageHandler(({ data: { title, options } }) => {
options = JSON.parse(options);
options.actions = [
{ action: "done", title: "Done" },
{ action: "snooze1", title: "Snooze 1 Hour" },
{ action: "snooze24", title: "Snooze 1 Day" }
];
return self.registration.showNotification(title, options);
});
Hi Could you try below code and see if this is getting called-
self.addEventListener('notificationclick', function (event) {
event.notification.close();
var redirectUrl = null;
var tag = event.notification.tag;
if (event.action) {
redirectUrl = event.action
}
if (redirectUrl) {
event.waitUntil(async function () {
var allClients = await clients.matchAll({
includeUncontrolled: !0
});
var chatClient;
for (const client of allClients) {
if (redirectUrl != '/' && client.url.indexOf(redirectUrl) >= 0) {
client.focus();
chatClient = client;
break
}
}
if (chatClient == null || chatClient == 'undefined') {
chatClient = clients.openWindow(redirectUrl);
return chatClient
}
}().then(result => {
if (tag) {
//PostAction(tag, "click")
}
}))
}
});
Edited-
Attaching both js files. it is working at my end.
firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');
var config = {
apiKey: "your api key",
authDomain: "you firebase domain",
databaseURL: "your firbase db url",
projectId: "your project id",
storageBucket: "",
messagingSenderId: "sender id"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload.data);
var notificationTitle = payload.data.Title;
var notificationOptions = {
body: payload.data.Body,
icon: payload.data.Icon,
image: payload.data.Image,
action: payload.data.ClickAction
};
console.log("strated sending msg" + notificationOptions);
return self.registration.showNotification(notificationTitle,notificationOptions);
});
self.addEventListener('notificationclick', function (event) {
console.log('On notification click: ', event.notification);
event.notification.close();
var redirectUrl = null;
if (event.notification.data) {
if (event.notification.data.FCM_MSG) {
redirectUrl = event.notification.data.FCM_MSG.data ? event.notification.data.FCM_MSG.data.click_action : null
} else {
redirectUrl = event.notification.data ? event.notification.data.click_action : null
}
}
console.log("redirect url is : " + redirectUrl);
if (redirectUrl) {
event.waitUntil(async function () {
var allClients = await clients.matchAll({
includeUncontrolled: true
});
var chatClient;
for (var i = 0; i < allClients.length; i++) {
var client = allClients[i];
if (client['url'].indexOf(redirectUrl) >= 0) {
client.focus();
chatClient = client;
break;
}
}
if (chatClient == null || chatClient == 'undefined') {
chatClient = clients.openWindow(redirectUrl);
return chatClient;
}
}());
}
});
self.addEventListener("notificationclose", function (event) {
event.notification.close();
console.log('user has clicked notification close');
});
application.js file :
/// <reference path="scripts/jquery-3.3.1.js" />
try {
var config = {
apiKey: "your api key",
authDomain: "you firebase domain",
databaseURL: "your firbase db url",
projectId: "your project id",
storageBucket: "",
messagingSenderId: "sender id"
};
firebase.initializeApp(config);
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is supported');
navigator.serviceWorker
.register('/firebase-messaging-sw.js')
.then((swReg) => {
firebase.messaging().useServiceWorker(swReg);
askForPermissioToReceiveNotifications();
})
.catch(function (error) {
console.error('Service Worker Error', error);
window.alert("Service Worker Error" + error);
})
} else {
console.warn('Push messaging is not supported');
window.alert("Push messaging is not supported " + (navigator.serviceWorker));
}
const askForPermissioToReceiveNotifications = async () => {
try {
const messaging = firebase.messaging();
console.log(messaging);
await messaging.requestPermission();
const token = await messaging.getToken();
if (token !== null || token !== 'undefined') {
await sendDeviceTokenToServerSide(token);
}
console.log('Got token : ' + token);
messaging.onMessage(function (payload) {
console.log('onMessage: ', payload);
setTimeout(() => {
navigator.serviceWorker.ready.then(function (registration) {
var notificationTitle = payload.notification.title;
var notificationOptions = {
body: payload.notification.body,
data: payload.data,
icon: payload.notification.icon,
image: payload.data.Image,
requireInteraction: payload.notification.requireInteraction,
tag: payload.notification.tag,
click_action: payload.data.click_action,
requireInteraction: true
};
registration.showNotification(notificationTitle, notificationOptions);
},50)
});
});
}
catch (e) { console.log('error in getting token: ' + e); window.alert("error in getting token: " + e); }
}
function sendDeviceTokenToServerSide(token) {
$.ajax({
type: 'POST',
url: '/Home/StoreToken',
timeout: 5000000,
data: { token: token },
success: function (success) {
console.log("device token is sent to server");
},
error: function (error) {
console.log("device error sending token to server : " + error);
window.alert("device error sending token to server : " + error);
}
});
}
} catch (e) {
window.alert("error: " + e);
}
function GetFcmUserToken(messaging) {
messaging.onTokenRefresh(function () {
messaging.getToken()
.then(function (refreshedToken) {
console.log('Token refreshed.');
return refreshedToken;
})
.catch(function (err) {
console.log('Unable to retrieve refreshed token ', err);
showToken('Unable to retrieve refreshed token ', err);
});
});
}
self.addEventListener('notificationclick', function (event) {
const clickedNotification = event.notification;
// Do something as the result of the notification click
const promiseChain = clients.openWindow(clickedNotification.data.Url);
event.waitUntil(promiseChain);
});
This code inside service worker js worked fine for me on chrome Desktop and Android.

How do I select a range and change font color of specific words in Outlook web add-ins

In Outlook web add-in, I want to select text from email body, keep it as tracked object and then apply styling on it like we do in Word web add-in. In the Word, I do it the following way:
//used to store current selected range
var selection=null;
$scope.getSelectedText = function () {
Word.run(function (context) {
if (selection) {
context.trackedObjects.remove(selection);
}
selection = context.document.getSelection();
context.trackedObjects.add(selection);
context.load(selection, 'text');
return context.sync()
.then(function () {
if (!selection.text) {
systemService.errorHandler("Please select any text from the document");
return false;
}
}).then(context.sync);
})
.catch(systemService.errorHandler);
};
//highlight Words in the selected range or body
$scope.highlightWords = function (item) {
var color = systemService.getColor(item.gradeText);
var filteredWords = JSON.parse(localStorage.getItem("List")) || [];
var promise = new OfficeExtension.Promise(function (resolve, reject) { resolve(null); });
Word.run(function (context) {
var selectedRange = (selection) ? selection : context.document.body;
selectedRange.load('text');
return context.sync().then(function () {
if (selectedRange.text == "") {
systemService.showNotification("Alert:", "No text found in the document!");
return;
}
filteredWords.forEach(function (word) {
promise = promise.then(function () {
return highlightRange(word, color);
})
});
});
}).catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
function highlightRange(word, color) {
return Word.run(selection, function (context) {
var range = (selection) ? selection : context.document.body;
var searchResults = null;
searchResults = range.search(word, { ignorePunct: true, matchCase: false, matchWholeWord: true });
searchResults.load('font');
return context.sync().then(function () {
for (var i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = color;
searchResults.items[i].font.bold = true;
}
}).then(context.sync());
}).catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
//highlight Words in the selected range or body
I have tried multiple ways to do but there is a problem while keeping track of the selected object. In Word, we can save the range and manipulate it overtime as needed, but in Outlook, the user have to keep the content selected in order to perform operation on it (like changing font color etc.). Also, do we have any font property available for outlook web add-ins like Word?

Chromecast Reciever App Error : '[goog.net.WebSocket] The WebSocket disconnected unexpectedly: undefined '

While creating a receiver application in chromecast we are getting a problem as :
[goog.net.WebSocket] The WebSocket disconnected unexpectedly: undefined
cast_receiver_framework.js:507 WebSocket connection to 'ws://localhost:8008/v2/ipc' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
Our receiver has simple code as in example provided by CAF receiver in chromecast documentation:
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const playbackConfig = new cast.framework.PlaybackConfig();
// Customize the license url for playback
playbackConfig.licenseUrl = 'https://wv-keyos.licensekeyserver.com/';
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
requestInfo.headers = {
'customdata': '<customdata>'
};
};
context.getPlayerManager().setMediaPlaybackInfoHandler((loadRequest, playbackConfig) => {
if (loadRequest.media.customData && loadRequest.media.customData.licenseUrl) {
playbackConfig.licenseUrl = loadRequest.media.customData.licenseUrl;
}
return playbackConfig;
});
function makeRequest (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(JSON.parse(xhr.response));
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
request => {
castDebugLogger.info('MyAPP.LOG', 'Intercepting LOAD request');
if (request.media && request.media.entity) {
request.media.contentId = request.media.entity;
}
return new Promise((resolve, reject) => {
if(request.media.contentType == 'video/mp4') {
return resolve(request);
}
// Fetch content repository by requested contentId
makeRequest('GET', 'https://tse-summit.firebaseio.com/content.json?orderBy=%22$key%22&equalTo=%22'+ request.media.contentId + '%22')
.then(function (data) {
var item = data[request.media.contentId];
if(!item) {
// Content could not be found in repository
castDebugLogger.error('MyAPP.LOG', 'Content not found');
reject();
} else {
// Adjusting request to make requested content playable
request.media.contentId = item.stream.hls;
request.media.contentType = 'application/x-mpegurl';
castDebugLogger.warn('MyAPP.LOG', 'Playable URL:', request.media.contentId);
// Add metadata
var metadata = new cast.framework.messages.MovieMediaMetadata();
metadata.metadataType = cast.framework.messages.MetadataType.MOVIE;
metadata.title = item.title;
metadata.subtitle = item.author;
request.media.metadata = metadata;
resolve(request);
}
});
});
});
/** Debug Logger **/
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
// Enable debug logger and show a warning on receiver
// NOTE: make sure it is disabled on production
castDebugLogger.setEnabled(false);
playerManager.addEventListener(
cast.framework.events.category.CORE,
event => {
castDebugLogger.info('ANALYTICS', 'CORE EVENT:', event);
});
// Set verbosity level for custom tags
castDebugLogger.loggerLevelByTags = {
'MyAPP.LOG': cast.framework.LoggerLevel.WARNING,
'ANALYTICS': cast.framework.LoggerLevel.INFO,
};
/** Optimizing for smart displays **/
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);
const touchControls = cast.framework.ui.Controls.getInstance();
let browseItems = getBrwoseItems();
function getBrwoseItems() {
let data = '"video": { \
<Encrypted Video>
}, \
"title": "Big Buck Bunny" \
}';
let browseItems = [];
for (let key in data) {
let item = new cast.framework.ui.BrowseItem();
item.entity = key;
item.title = data[key].title;
item.subtitle = data[key].description;
item.image = new cast.framework.messages.Image(data[key].poster);
item.imageType = cast.framework.ui.BrowseImageType.MOVIE;
browseItems.push(item);
}
return browseItems;
}
let browseContent = new cast.framework.ui.BrowseContent();
browseContent.title = 'Up Next';
browseContent.items = browseItems;
browseContent.targetAspectRatio =
cast.framework.ui.BrowseImageAspectRatio.LANDSCAPE_16_TO_9;
playerDataBinder.addEventListener(
cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
(e) => {
if (!e.value) return;
// Clear default buttons and re-assign
touchControls.clearDefaultSlotAssignments();
touchControls.assignButton(
cast.framework.ui.ControlsSlot.SLOT_1,
cast.framework.ui.ControlsButton.SEEK_BACKWARD_30
);
// Media browse
touchControls.setBrowseContent(browseContent);
});
context.start({playbackConfig: playbackConfig});
This code takes the input from sender but even if sender has application ID it shows the same message.

Gulp-concat: wrap all js in a unique $(document)

Is it possible to wrap all added js in a $(document).ready(function(){ as first line and }); as last line in cases using gulp-concat?
you can use gulp-concat-util for this
it does concat all files, and optionally add header line & footer line to concatenated text
var concat = require('gulp-concat-util');
gulp.task('concat:dist', function() {
gulp.src('scripts/{,*/}*.js')
.pipe(concat(pkg.name + '.js', {process: function(src) { return (src.trim() + '\n').replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1'); }}))
.pipe(concat.header('(function(window, document, undefined) {\n\'use strict\';\n'))
.pipe(concat.footer('\n})(window, document);\n'))
.pipe(gulp.dest('dist'));
});
I had the same question, and decided to hack something together. Maybe it will be helpful to you:
// remove jquery/use strict from independent files
// prior to concatenation, so all files are in the same context
function removeWrap( ) {
const readFileAsync = (filename) => {
return new Promise( (resolve) => {
fs.readFile('./src/js/' + filename, 'utf-8', function(err, content) {
let lines = content.split('\n')
lines.splice(0,2)
lines.splice(lines.length -2)
let newContent = lines.join('\n')
resolve( newContent)
})
})
}
fs.readdir('./src/js', function(err, filenames) {
filenames.forEach(function(filename) {
readFileAsync(filename).then( content => {
console.log( content[1] )
fs.writeFile('./src/js-temp/' + filename, content, ()=>{})
})
})
})
return Promise.resolve('the value is ignored');
}
// concat JS files
function concatFiles() {
return src('./src/js-temp/*.js')
.pipe(concat('main.js'))
.pipe(dest('./dist/js'));
}
function wrapMain() {
let header = `$(function() {
"use strict";\n`
let footer = `
});`
fs.readFile('./dist/js/main.js', 'utf-8', function(err, content) {
let newContent = header + content + footer
fs.writeFile('./dist/js/main.js', newContent, ()=>{})
})
return Promise.resolve('the value is ignored');
}
module.exports.devJs = series(removeWrap, concatFiles, wrapMain);

Node.JS convert Variable-length binary data to jpeg or png?

I was wondering how to convert Variable-length binary data(255216255224016747073700110010100255) to a jpeg or png to the web browser?
Example Code:
var Connection = require('tedious').Connection;
var config = {
"userName": "user#server.database.windows.net",
"password": "pswd",
"server": "server.database.windows.net",
"options": {
"database": "db",
"encrypt": true,
}
};
var connection = new Connection(config);
connection.on('connect', function(err) {
console.log("Connected");
}
);
var Request = require('tedious').Request;
var result,
resG;
function sendResponse() {
var vals;
// Convert to string then array
var resultA = result.toString().split(',');
// Now I can loop through the data returned
resultA.forEach(function(val, index, ar) {
if(vals == null) {
vals = val;
} else {
vals += val;
}
});
resG.writeHead(200, {'Content-Type': 'text/html', 'Content-Length': vals.length});
//console.log(vals);
//resG.end("<img src=\"data:image/jpg;base64," + vals + "\" />");
// Output data returned from db as string
resG.end("" + vals);
}
function executeStatement() {
request = new Request("SELECT Photos FROM dbo.tbl WHERE FarmerFirstName='someName' AND FarmerLastName='someLastName'", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
});
request.on('row', function(columns) {
columns.forEach(function(column) {
result = column.value;
});
});
request.on('doneInProc', function(rowCount, more) {
// Got everything needed from db move on to sending a response
sendResponse();
});
connection.execSql(request);
}
var http = require('http'),
director = require('director');
var router = new director.http.Router({
"/": {
get: executeStatement
}
});
var server = http.createServer(function (req, res) {
// set global res var
resG = res;
router.dispatch(req, res, function (err) {
if (err) {
res.writeHead(404);
res.end();
}
});
});
server.listen(80);
I'm using tedious for my db connector and director as my router.
The result is already an array of bytes for the Image. You do not need to do any fancy transformations on it. This should work.
function sendResponse() {
resG.writeHead(200, {'Content-Type': 'image/jpeg', 'Content-Length': result.length});
resG.end(new Buffer(result));
}
or if you want to serve it as part of an HTML page, this:
function sendResponse() {
resG.writeHead(200, {'Content-Type': 'text/html'});
var vals = (new Buffer(result)).toString('base64')
resG.end("<html><body>" +
"<img src=\"data:image/jpg;base64," + vals + "\" />" +
"</body></html>");
}

Resources