DOMException error while trying to register PUSH for push notification - node.js

I'm new with service workers and push notification, so I'm trying out this test project to create push notifications with service workers and node.js. I keep getting this DOMException error in my console while trying to register push.
This is my code
const publicVapidKey =
"BA6iGR7K3KfA_xn9XZTDqKMjUZcA6o8IsnDtnkvxtmkg4GwuTCsqNU-8YUoEIkfYCpuF7PraFKVI0zvNfNsPjy0";
//check for service worker
if ("serviceWorker" in navigator && "PushManager" in window) {
console.log("Service Worker and Push is supported");
send().catch(err => console.error(err));
}
// Register the service worker
async function send() {
console.log("Registering Service Worker...");
const register = await navigator.serviceWorker.register("/worker.js", {
scope: "/"
});
console.log("Service Worker Registered...");
//Register push
console.log("Registering Push...");
const subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
console.log("Push Registered...");
//send push notification
console.log("Sending Push... ");
await fetch("/subscribe", {
method: "POST",
body: JSON.stringify(subscription),
headers: {
"content-type": "application/json"
}
});
console.log("Push Sent...");
}
//
function urlBase64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}

Oh, it turns out I turned off notifications in my chrome browser settings. I turned it on now and everything works fine

Related

GCP provided code snippets to both subscribe and publish mqtt in the same app doesn't work

In my Node.js app, I can successfully publish telemetry/state topics or subscribe to config/command topics, but can't both publish and subscribe.
Both Node.js code snippets that appear below are from
https://cloud.google.com/iot/docs/how-tos/mqtt-bridge
The subscribe code is as follows -
// const deviceId = `myDevice`;
// const registryId = `myRegistry`;
// const region = `us-central1`;
// const algorithm = `RS256`;
// const privateKeyFile = `./rsa_private.pem`;
// const serverCertFile = `./roots.pem`;
// const mqttBridgeHostname = `mqtt.googleapis.com`;
// const mqttBridgePort = 8883;
// const messageType = `events`;
// const numMessages = 5;
// The mqttClientId is a unique string that identifies this device. For Google
// Cloud IoT Core, it must be in the format below.
const mqttClientId = `projects/${projectId}/locations/${region}/registries/${registryId}/devices/${deviceId}`;
// With Google Cloud IoT Core, the username field is ignored, however it must be
// non-empty. The password field is used to transmit a JWT to authorize the
// device. The "mqtts" protocol causes the library to connect using SSL, which
// is required for Cloud IoT Core.
const connectionArgs = {
host: mqttBridgeHostname,
port: mqttBridgePort,
clientId: mqttClientId,
username: 'unused',
password: createJwt(projectId, privateKeyFile, algorithm),
protocol: 'mqtts',
secureProtocol: 'TLSv1_2_method',
ca: [readFileSync(serverCertFile)],
};
// Create a client, and connect to the Google MQTT bridge.
const iatTime = parseInt(Date.now() / 1000);
const client = mqtt.connect(connectionArgs);
// Subscribe to the /devices/{device-id}/config topic to receive config updates.
// Config updates are recommended to use QoS 1 (at least once delivery)
client.subscribe(`/devices/${deviceId}/config`, {qos: 1});
// Subscribe to the /devices/{device-id}/commands/# topic to receive all
// commands or to the /devices/{device-id}/commands/<subfolder> to just receive
// messages published to a specific commands folder; we recommend you use
// QoS 0 (at most once delivery)
client.subscribe(`/devices/${deviceId}/commands/#`, {qos: 0});
// The MQTT topic that this device will publish data to. The MQTT topic name is
// required to be in the format below. The topic name must end in 'state' to
// publish state and 'events' to publish telemetry. Note that this is not the
// same as the device registry's Cloud Pub/Sub topic.
const mqttTopic = `/devices/${deviceId}/${messageType}`;
client.on('connect', success => {
console.log('connect');
if (!success) {
console.log('Client not connected...');
} else if (!publishChainInProgress) {
publishAsync(mqttTopic, client, iatTime, 1, numMessages, connectionArgs);
}
});
client.on('close', () => {
console.log('close');
shouldBackoff = true;
});
client.on('error', err => {
console.log('error', err);
});
client.on('message', (topic, message) => {
let messageStr = 'Message received: ';
if (topic === `/devices/${deviceId}/config`) {
messageStr = 'Config message received: ';
} else if (topic.startsWith(`/devices/${deviceId}/commands`)) {
messageStr = 'Command message received: ';
}
messageStr += Buffer.from(message, 'base64').toString('ascii');
console.log(messageStr);
});
client.on('packetsend', () => {
// Note: logging packet send is very verbose
});
// Once all of the messages have been published, the connection to Google Cloud
// IoT will be closed and the process will exit. See the publishAsync method.
and the publish code is -
const publishAsync = (
mqttTopic,
client,
iatTime,
messagesSent,
numMessages,
connectionArgs
) => {
// If we have published enough messages or backed off too many times, stop.
if (messagesSent > numMessages || backoffTime >= MAXIMUM_BACKOFF_TIME) {
if (backoffTime >= MAXIMUM_BACKOFF_TIME) {
console.log('Backoff time is too high. Giving up.');
}
console.log('Closing connection to MQTT. Goodbye!');
client.end();
publishChainInProgress = false;
return;
}
// Publish and schedule the next publish.
publishChainInProgress = true;
let publishDelayMs = 0;
if (shouldBackoff) {
publishDelayMs = 1000 * (backoffTime + Math.random());
backoffTime *= 2;
console.log(`Backing off for ${publishDelayMs}ms before publishing.`);
}
setTimeout(() => {
const payload = `${argv.registryId}/${argv.deviceId}-payload-${messagesSent}`;
// Publish "payload" to the MQTT topic. qos=1 means at least once delivery.
// Cloud IoT Core also supports qos=0 for at most once delivery.
console.log('Publishing message:', payload);
client.publish(mqttTopic, payload, {qos: 1}, err => {
if (!err) {
shouldBackoff = false;
backoffTime = MINIMUM_BACKOFF_TIME;
}
});
const schedulePublishDelayMs = argv.messageType === 'events' ? 1000 : 2000;
setTimeout(() => {
const secsFromIssue = parseInt(Date.now() / 1000) - iatTime;
if (secsFromIssue > argv.tokenExpMins * 60) {
iatTime = parseInt(Date.now() / 1000);
console.log(`\tRefreshing token after ${secsFromIssue} seconds.`);
client.end();
connectionArgs.password = createJwt(
argv.projectId,
argv.privateKeyFile,
argv.algorithm
);
connectionArgs.protocolId = 'MQTT';
connectionArgs.protocolVersion = 4;
connectionArgs.clean = true;
client = mqtt.connect(connectionArgs);
client.on('connect', success => {
console.log('connect');
if (!success) {
console.log('Client not connected...');
} else if (!publishChainInProgress) {
publishAsync(
mqttTopic,
client,
iatTime,
messagesSent,
numMessages,
connectionArgs
);
}
});
client.on('close', () => {
console.log('close');
shouldBackoff = true;
});
client.on('error', err => {
console.log('error', err);
});
client.on('message', (topic, message) => {
console.log(
'message received: ',
Buffer.from(message, 'base64').toString('ascii')
);
});
client.on('packetsend', () => {
// Note: logging packet send is very verbose
});
}
publishAsync(
mqttTopic,
client,
iatTime,
messagesSent + 1,
numMessages,
connectionArgs
);
}, schedulePublishDelayMs);
}, publishDelayMs);
};
I am wondering if anyone has gotten their Node.js app to both successfully publish and subscribe with Google Cloud. If so, what might I be missing?

node js not behaving the same way on local and Google App Egine

I'm developing an app to upload .las file to cesium ion.
I have modified this code https://github.com/CesiumGS/cesium-ion-rest-api-examples/blob/main/tutorials/rest-api/index.js
To pass a file from the browser.
It's working flawlessly when I run npm start on my local env.
When I try the same thing on the app Engine, I do not get the message about where the process is at. It's does upload the file though. It's just I can't monitor what is going on.
To explain what is going on below, I send a file from the client, then it's catch by app.post("/upload"
Then, it create asset on Ion, and then upload to S3, then it tell ion it's finished, then it monitor the tiling on Ion.
Then I call every second app.post("/progress" That is sending back the stat of things to the client.
I think there is probably a logic I'm missing, something basic I make totally wrong. I'm using one single service for both the backend and the frontend. Can this be a part of the problem ?
const express = require('express');
const app = express();
const port = process.env.PORT || 3001;
const fileUpload = require("express-fileupload");
const cors = require('cors');
var path = require('path');
const AWS = require('aws-sdk');
const fs = require('fs');
const rawdatafr = require('./lang/fr.json');
const rawdataen = require('./lang/en.json');
const axios = require('axios').default;
const accessToken = process.env.REACT_APP_ION_TOKEN;
const environment = process.env.NODE_ENV || 'production';
var urlLang = (environment === 'development') ? 'client/src/lang/' : 'lang/';
console.log('urlLang ? '+urlLang);
app.use(cors());
app.use(fileUpload({
useTempFiles: true,
safeFileNames: false,
preserveExtension: true,
tempFileDir: 'temp/'
}));
'use strict';
var messageFromLoc = rawdataen;
var input = null;
var filename = null;
var srcType = 'POINT_CLOUD';
var message = null;
var needMonitoring = false;
var assetMetadata = null;
var finished = null;
function resetGlobalvar(){
message = null;
needMonitoring = false;
assetMetadata = null;
finished = null;
input = null;
filename = null;
srcType = 'POINT_CLOUD';
}
async function creat_asset(){
finished = false;
message = 'create asset';
axios.post('https://api.cesium.com/v1/assets', {
name: filename,
description: '',
type: '3DTILES',
options: {
position:[ 2.29, 48.85, 0.1],
sourceType: srcType,
}
},{
headers: { Authorization: `Bearer ${accessToken}` }
})
.then(function (response) {
message = 'created successfully :> send to s3';
sendtos3(response.data);
})
.catch(function (error) {
console.log(error);
message = error;
});
}
async function sendtos3(response){
console.log('Asset created.');
message = 'send to s3';
try{
const uploadLocation = response.uploadLocation;
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
region: 'us-east-1',
signatureVersion: 'v4',
endpoint: uploadLocation.endpoint,
credentials: new AWS.Credentials(
uploadLocation.accessKey,
uploadLocation.secretAccessKey,
uploadLocation.sessionToken)
});
let params = {
Body: fs.createReadStream(input),
Bucket: uploadLocation.bucket,
Key: uploadLocation.prefix+filename
};
let s3Response = await s3.upload(params).on('httpUploadProgress', function (progress) {
message = `${messageFromLoc.upload}: ${((progress.loaded / progress.total) * 100).toFixed(2)}%`;
console.log(`Upload: ${((progress.loaded / progress.total) * 100).toFixed(2)}%`);
}).promise();
// request successed
console.log(`File uploaded to S3 at ${s3Response.Bucket} bucket. File location: ${s3Response.Location}`);
message = `File uploaded to S3 at ${s3Response.Bucket} bucket. File location: ${s3Response.Location}`;
step3(response);
// return s3Response.Location;
}
// request failed
catch (ex) {
console.error(ex);
message = ex;
}
}
async function step3(response){
const onComplete = response.onComplete;
assetMetadata = response.assetMetadata;
message = 'step3';
axios.post(onComplete.url, onComplete.fields,{
headers: { Authorization: `Bearer ${accessToken}` }
})
.then(function (response) {
message = 'step3 done';
monitorTiling(assetMetadata);
})
.catch(function (error) {
console.log(error);
message = error;
});
}
async function monitorTiling(assetMetadata){
// console.log(response);
const assetId = assetMetadata.id;
message = 'monitorTiling';
axios.get(`https://api.cesium.com/v1/assets/${assetId}`,{headers: { Authorization: `Bearer ${accessToken}` }})
.then(function (response) {
// handle success
console.log('monitorTiling - success');
var status = response.data.status;
message = 'Tiling - success';
if (status === 'COMPLETE') {
console.log('Asset tiled successfully');
console.log(`View in ion: https://cesium.com/ion/assets/${assetMetadata.id}`);
message = 'Asset tiled successfully';
needMonitoring = false;
finished = true;
} else if (status === 'DATA_ERROR') {
console.log('ion detected a problem with the uploaded data.');
message = 'ion detected a problem with the uploaded data.';
needMonitoring = false;
finished = true;
} else if (status === 'ERROR') {
console.log('An unknown tiling error occurred, please contact support#cesium.com.');
message = 'An unknown tiling error occurred, please contact support#cesium.com.';
needMonitoring = false;
finished = true;
} else {
needMonitoring = true;
if (status === 'NOT_STARTED') {
console.log('Tiling pipeline initializing.');
message = 'Tiling pipeline initializing.';
} else { // IN_PROGRESS
console.log(`Asset is ${assetMetadata.percentComplete}% complete.`);
message = `Asset is ${assetMetadata.percentComplete}% complete.`;
}
}
})
.catch(function (error) {
// handle error
console.log(error);
message =error;
})
}
/*------- LISTEN FOR CALL TO UPLOAD AND START THE UPLOAD PROCESS ----------*/
app.post("/upload", (req, res) => {
if (!req.files) {
res.send("File was not found");
message = 'File was not found';
return;
}
input = req.files.file.tempFilePath;
filename = req.files.file.name;
emptyTempFolder('temp', input.replace('temp/', ''));
var ext = path.extname(filename);
if(ext=='.zip'){
srcType = 'CITYGML';
}
/*------- START UPLOAD PROCESS ----------*/
creat_asset();
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
/*------- LISTEN FOR PROGRESS TO UPLOAD ASSET ----------*/
app.get("/progress", (req, res) => {
// lang = req.get('Accept-Language').substring(0, 2).toLowerCase();
// if(lang=='fr'){
// messageFromLoc = rawdatafr;
// }
console.log('message ='+message);
if(needMonitoring){
monitorTiling(assetMetadata);
}
res.json({ message: message, done: finished, myAssetMetadata: assetMetadata });
if(finished){
resetGlobalvar();
}
});
/*--------------STATIC ----------------*/
app.use(express.static( path.join(__dirname, 'build' )));
And my app.yaml is like this :
runtime: nodejs14
env: standard
includes:
- env_variables.yaml
instance_class: B1
service: my-app
basic_scaling:
max_instances: 25
idle_timeout: 60m
I think this is from your instance(s). You're using basic scaling with up to 25 instances.
It looks like a combination of the following is happening
a) When you send a request to /progress, a new instance of your App is created which means all of the global variables are starting from their default values (the initial value of message is null).
b) Other times, a request to /progress is handled by an existing instance which was already processing an upload request and that request has completed and so the message says completed
You don't have this problem on your local environment because only 1 instance runs.
To test this theory, modify your app.yaml and set max_instances: 1. This is supposed to force the App to only use 1 instance which means subsequent requests should use an existing instance (which has the updated state of your global variables)

How to send push notification to Specific user in Web-push?

I have an existing web site , i just need to push notification to the site, for that i am using
Nodejs web-push package , I can able to receive notifications but i need to change it to User Specific,
For Example i want to send the notifications for the users based on the country
This is my code
client.js
const publicVapidKey = 'xxxxxx';
if ('serviceWorker' in navigator) {
console.log('Registering service worker');
run().catch(error => console.error(error));
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
async function run() {
console.log('Registering service worker');
const registration = await navigator.serviceWorker.
register('worker.js');
console.log('Registered service worker');
console.log('Registering push');
const subscription = await registration.pushManager.
subscribe({
userVisibleOnly: true,
// The `urlBase64ToUint8Array()` function is the same as in
// https://www.npmjs.com/package/web-push#using-vapid-key-for-applicationserverkey
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
// subscription.user = $('.header-user-name').find('span').text();
console.log('Registered push');
console.log('Sending push');
await fetch('http://localhost:3000/subscribe?user='+$('.header-user-name').find('span').text(), {
method: 'POST',
body: JSON.stringify(subscription),
headers: {
'content-type': 'application/json'
}
});
console.log('Sent push');
}
Worker.js
console.log('Loaded service worker!');
self.addEventListener('push', ev => {
const data = ev.data.json();
console.log('Got push', data);
ev.waitUntil(self.registration.showNotification(data.title, {
body: 'Hello, World!',
registration_ids: [$('.header-user-name').find('span').text()]
icon: 'http://mongoosejs.com/docs/images/mongoose5_62x30_transparent.png'
}));
});
Server Code (localhost:3000/push)
app.get('/push',function(req,res) {
const payload = JSON.stringify({ title: 'Hello '+ user.name +' ' + req.query.title, });
console.log(req.query);
console.log("yahooooooooooooooooooooooooooooooooo");
webpush.sendNotification(newSubscription, payload).catch(error => {
console.error(error.stack);
});
res.send({result : 'Success'});
});
After long gap, I got a solution for this problem.
Steps to be followed:
Create an ExpressJs api to store the user subscription in to a database.
Get the user based on countries from database (you can use your own backend language I have chosen Nodejs).
Create an api which can send push notifications using given parameter like (Country, Users, Etc).
Happy Coding.
You can use the users' IPs to look up their countries by using a service like ip-api.com. After you get the country info, you can include it in your request body along with the push subscription object and send to your backend. So, you will have the opportunity to segment your subscribers and send them push notifications with different content.

Use headless chrome to intercept image request data

I have a use case that needs to use Headless Chrome Network (https://chromedevtools.github.io/devtools-protocol/tot/Network/) to intercept all images requests and find out the image size before saving it (basically discard small images such as icons).
However, I am unable to figure out a way to load the image data in memory before saving it. I need to load it in Img object to get width and height. The Network.getResponseBody is taking requestId which I don't have access in Network.requestIntercepted. Also Network.loadingFinished always gives me "0" in encodedDataLength variable. I have no idea why. So my questions are:
How to intercept all responses from jpg/png request and get the image data? Without saving the file via URL string to the disk and load back.
BEST: how to get image dimension from header response? Then I don't have to read the data into memory at all.
My code is below:
const chromeLauncher = require('chrome-launcher');
const CDP = require('chrome-remote-interface');
const file = require('fs');
(async function() {
async function launchChrome() {
return await chromeLauncher.launch({
chromeFlags: [
'--disable-gpu',
'--headless'
]
});
}
const chrome = await launchChrome();
const protocol = await CDP({
port: chrome.port
});
const {
DOM,
Network,
Page,
Emulation,
Runtime
} = protocol;
await Promise.all([Network.enable(), Page.enable(), Runtime.enable(), DOM.enable()]);
await Network.setRequestInterceptionEnabled({enabled: true});
Network.requestIntercepted(({interceptionId, request, resourceType}) => {
if ((request.url.indexOf('.jpg') >= 0) || (request.url.indexOf('.png') >= 0)) {
console.log(JSON.stringify(request));
console.log(resourceType);
if (request.url.indexOf("/unspecified.jpg") >= 0) {
console.log("FOUND unspecified.jpg");
console.log(JSON.stringify(interceptionId));
// console.log(JSON.stringify(Network.getResponseBody(interceptionId)));
}
}
Network.continueInterceptedRequest({interceptionId});
});
Network.loadingFinished(({requestId, timestamp, encodedDataLength}) => {
console.log(requestId);
console.log(timestamp);
console.log(encodedDataLength);
});
Page.navigate({
url: 'https://www.yahoo.com/'
});
Page.loadEventFired(async() => {
protocol.close();
chrome.kill();
});
})();
This should get you 90% of the way there. It gets the body of each image request. You'd still need to base64decode, check size and save etc...
const CDP = require('chrome-remote-interface');
const sizeThreshold = 1024;
async function run() {
try {
var client = await CDP();
const { Network, Page } = client;
// enable events
await Promise.all([Network.enable(), Page.enable()]);
// commands
const _url = "https://google.co.za";
let _pics = [];
Network.responseReceived(async ({requestId, response}) => {
let url = response ? response.url : null;
if ((url.indexOf('.jpg') >= 0) || (url.indexOf('.png') >= 0)) {
const {body, base64Encoded} = await Network.getResponseBody({ requestId }); // throws promise error returning null/undefined so can't destructure. Must be different in inspect shell to app?
_pics.push({ url, body, base64Encoded });
console.log(url, body, base64Encoded);
}
});
await Page.navigate({ url: _url });
await sleep(5000);
// TODO: process _pics - base64Encoded, check body.length > sizeThreshold, save etc...
} catch (err) {
if (err.message && err.message === "No inspectable targets") {
console.error("Either chrome isn't running or you already have another app connected to chrome - e.g. `chrome-remote-interface inspect`")
} else {
console.error(err);
}
} finally {
if (client) {
await client.close();
}
}
}
function sleep(miliseconds = 1000) {
if (miliseconds == 0)
return Promise.resolve();
return new Promise(resolve => setTimeout(() => resolve(), miliseconds))
}
run();

Node.js TypeError: Wit is not a constructor

How to solve "Wit is not a constructor" error coming from Node.js while executing code given by node-wit and wit.ai documentation.
// Setting up our bot
const wit = new Wit(WIT_TOKEN, actions);
I tried all the ways by upgrading and downgrading npm/node versions, but no luck.
Update: Please find the index.js source I used,
Do I need to change anything in this?
module.exports = {
Logger: require('./lib/logger.js').Logger,
logLevels: require('./lib/logger.js').logLevels,
Wit: require('./lib/wit.js').Wit,
}
'use strict';
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
const Logger = require('node-wit').Logger;
const levels = require('node-wit').logLevels;
var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.listen((process.env.PORT || 3000));
//const Wit = require('node-wit').Wit;
const WIT_TOKEN = process.env.WIT_TOKEN;
const FB_PAGE_TOKEN = process.env.FB_PAGE_TOKEN;
const Wit = require('node-wit').Wit;
// Server frontpage
app.get('/', function (req, res) {
debugger;
res.send('This is TestBot Server');
});
// Messenger API specific code
// See the Send API reference
// https://developers.facebook.com/docs/messenger-platform/send-api-reference
const fbReq = request.defaults({
uri: 'https://graph.facebook.com/me/messages',
method: 'POST',
json: true,
qs: { access_token: FB_PAGE_TOKEN },
headers: {'Content-Type': 'application/json'},
});
const fbMessage = (recipientId, msg, cb) => {
const opts = {
form: {
recipient: {
id: recipientId,
},
message: {
text: msg,
},
},
};
fbReq(opts, (err, resp, data) => {
if (cb) {
cb(err || data.error && data.error.message, data);
}
});
};
// See the Webhook reference
// https://developers.facebook.com/docs/messenger-platform/webhook-reference
const getFirstMessagingEntry = (body) => {
const val = body.object == 'page' &&
body.entry &&
Array.isArray(body.entry) &&
body.entry.length > 0 &&
body.entry[0] &&
body.entry[0].id === FB_PAGE_ID &&
body.entry[0].messaging &&
Array.isArray(body.entry[0].messaging) &&
body.entry[0].messaging.length > 0 &&
body.entry[0].messaging[0]
;
return val || null;
};
// Wit.ai bot specific code
// This will contain all user sessions.
// Each session has an entry:
// sessionId -> {fbid: facebookUserId, context: sessionState}
const sessions = {};
const findOrCreateSession = (fbid) => {
var sessionId;
// Let's see if we already have a session for the user fbid
Object.keys(sessions).forEach(k => {
if (sessions[k].fbid === fbid) {
// Yep, got it!
sessionId = k;
}
});
if (!sessionId) {
// No session found for user fbid, let's create a new one
sessionId = new Date().toISOString();
sessions[sessionId] = {fbid: fbid, context: {}};
}
return sessionId;
};
// Our bot actions
const actions = {
say(sessionId, context, message, cb) {
// Our bot has something to say!
// Let's retrieve the Facebook user whose session belongs to
const recipientId = sessions[sessionId].fbid;
if (recipientId) {
// Yay, we found our recipient!
// Let's forward our bot response to her.
fbMessage(recipientId, message, (err, data) => {
if (err) {
console.log(
'Oops! An error occurred while forwarding the response to',
recipientId,
':',
err
);
}
// Let's give the wheel back to our bot
cb();
});
} else {
console.log('Oops! Couldn\'t find user for session:', sessionId);
// Giving the wheel back to our bot
cb();
}
},
merge(sessionId, context, entities, message, cb) {
cb(context);
},
error(sessionId, context, error) {
console.log(error.message);
},
// You should implement your custom actions here
// See https://wit.ai/docs/quickstart
};
const wit = new Wit(WIT_TOKEN, actions);
// Message handler
app.post('/webhook', (req, res) => {
// Parsing the Messenger API response
// Setting up our bot
//const wit = new Wit(WIT_TOKEN, actions);
const messaging = getFirstMessagingEntry(req.body);
if (messaging && messaging.message && messaging.message.text) {
// Yay! We got a new message!
// We retrieve the Facebook user ID of the sender
const sender = messaging.sender.id;
// We retrieve the user's current session, or create one if it doesn't exist
// This is needed for our bot to figure out the conversation history
const sessionId = findOrCreateSession(sender);
// We retrieve the message content
const msg = messaging.message.text;
const atts = messaging.message.attachments;
if (atts) {
// We received an attachment
// Let's reply with an automatic message
fbMessage(
sender,
'Sorry I can only process text messages for now.'
);
} else if (msg) {
// We received a text message
// Let's forward the message to the Wit.ai Bot Engine
// This will run all actions until our bot has nothing left to do
wit.runActions(
sessionId, // the user's current session
msg, // the user's message
sessions[sessionId].context, // the user's current session state
(error, context) => {
if (error) {
console.log('Oops! Got an error from Wit:', error);
} else {
// Our bot did everything it has to do.
// Now it's waiting for further messages to proceed.
console.log('Waiting for futher messages.');
// Based on the session state, you might want to reset the session.
// This depends heavily on the business logic of your bot.
// Example:
// if (context['done']) {
// delete sessions[sessionId];
// }
// Updating the user's current session state
sessions[sessionId].context = context;
}
}
);
}
}
res.sendStatus(200);
});
There are two typical causes of your issue, either forgetting to require your module or forgetting to npm install it. Check if you:
Forgot to require('node-wit') and obtain the constructor from the returned object:
const Wit = require('node-wit').Wit
Properly required Wit but forgot to npm install node-wit
For everyone who are using messenger.js as your index.js use this:
const Wit = require('./lib/wit');
const log = require('./lib/log');
Please check your node_modules directory for node-wit package.
If node-wit is present then please require it before trying to create its instance.
const {Wit} = require('node-wit');
witHandler = new Wit({
accessToken: accessToken
});

Resources