Nodejs - IBM Watson Speech To Text Websocket connection error - node.js

I have been trying to follow this official guide from IBM (https://developer.ibm.com/tutorials/add-a-trigger-word-to-your-watson-assistant/) to build a Nodejs voice assistant that answer after it recognizes a "wake up word". That guide seems a little bit outdated so I decided to use it with Assistant V2 and ibm-watson 5.2.0 package from npm.
I am getting a WebSocket connection error with not much information on it, the issue seems to be on line 33 and with the params I am sending to the 'recognizeUsingWebsocket' method. Am I missing something along this parameters?
const AssistantV2 = require('ibm-watson/assistant/v2');
const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const SpeechToTextV1 = require('ibm-watson/speech-to-text/v1');
const { IamAuthenticator } = require('ibm-watson/auth');
const mic = require('mic');
const conversation = new AssistantV2({
authenticator: new IamAuthenticator({ apikey: '<api_key>' }),
url: 'https://gateway-lon.watsonplatform.net/assistant/api/',
version: '2018-09-19'
});
const speechToText = new SpeechToTextV1({
authenticator: new IamAuthenticator({ apikey: '<api_key>' }),
serviceUrl: 'https://gateway-lon.watsonplatform.net/speech-to-text/api'
});
const textToSpeech = new TextToSpeechV1({
authenticator: new IamAuthenticator({ apikey: '<api_key>' })
});
const micParams = {
rate: 44100,
channels: 2,
debug: true,
exitOnSilence: 6
};
const microphone = mic(micParams);
const micInputStream = microphone.getAudioStream();
const textStream = micInputStream
.pipe(
speechToText.recognizeUsingWebSocket({
accessToken:'<access_token>',
contentType: 'audio/l16; rate=44100; channels=2',
interimResults: true,
inactivityTimeout: -1
})
)
.setEncoding('utf8');
const speakResponse = (text) => {
var params = {
text: text,
accept: 'audio/wav',
voice: 'en-US_AllisonVoice'
};
var writeStream = fs.createWriteStream('output.wav');
textToSpeech
.synthesize(params)
.then((audio) => {
audio.pipe(writeStream);
})
.catch((err) => {
console.log('error:', err);
});
writeStream.on('finish', function() {
ffprobe('output.wav', function(err, probeData) {
if (probeData) {
pauseDuration = probeData.format.duration;
microphone.pause();
speaker.play('output.wav');
startTime = new Date();
}
});
});
writeStream.on('error', function(err) {
console.log('Text-to-speech streaming error: ' + err);
});
};
function printContext(header) {
if (debug) {
console.log(header);
if (context.system) {
if (context.system.dialog_stack) {
const util = require('util');
console.log(" dialog_stack: ['" + util.inspect(context.system.dialog_stack, false, null) + "']");
}
}
}
}
function watsonSays(response) {
if (typeof response !== 'undefined') {
console.log('Watson says:', response);
}
}
function isActive(text) {
var elapsedTime = new Date() - startTime;
if (elapsedTime > SLEEP_TIME) {
// go to sleep
startTime = new Date();
botIsActive = false;
}
if (botIsActive) {
// in active conversation, so stay awake
startTime = new Date();
return true;
} else {
// we are asleep - did we get a wake up call?
if (text.toLowerCase().indexOf(wakeWord) > -1) {
// time to wake up
console.log('App just woke up');
botIsActive = true;
} else {
// false alarm, go back to sleep
console.log('App needs the wake up command');
}
return botIsActive;
}
}
function performConversation() {
console.log('App is listening, you may speak now.');
textStream.on('data', (user_speech_text) => {
userSpeechText = user_speech_text.toLowerCase();
console.log('\n\nApp hears: ', user_speech_text);
if (isActive(user_speech_text)) {
conversation.message(
{
assistantId: process.env.ASSISTANT_ID,
sessionId: process.env.SESSION_ID,
input: { text: user_speech_text }
},
(err, response) => {
console.log(err);
context = response.context;
watson_response = response.output.text[0];
if (watson_response) {
speakResponse(watson_response);
}
watsonSays(watson_response);
}
);
}
});
}
microphone.start();
performConversation();

It seems that you do not use the right endpoint for websockets :
note : api endpoints have had a new version in dec 2019
you use :
https://gateway-lon.watsonplatform.net/speech-to-text/api
and it should be something like : (i think the prefix wss is key)
wss://api.{location}.speech-to-text.watson.cloud.ibm.com/instances/{instance_id}/v1/recognize
cf Api reference : https://cloud.ibm.com/apidocs/speech-to-text/speech-to-text#websocket_methods

Related

Microsoft bot framework save separately conversations or sessions

I got a Microsoft bot framework chatbot deployed on Azure and I´m using Tedious to save my conversations, thing is, bot it's being used on a web and many persons can open it to interact simultaneosly, but when I save a conversation from an user, it saves all the other interactions that have been made by other users at the same time, I need that each user has it's own conversation saved separately even if they are interacting with the chatbot at the same time...
Here's my code, maybe I'm missing something:
Bot.js
//SQL Connection
var Connection = require('tedious').Connection;
var config = {
server: 'myserver',
authentication: {
type: 'default',
options: {
userName: 'myuser',
password: 'mypass'
}
},
options: {
encrypt: true,
database: 'mydatabase'
}
};
const connection = new Connection(config);
connection.on('connect', function(err) {
console.log("Connected");
});
var Request = require('tedious').Request
var TYPES = require('tedious').TYPES;
// Function to save the conversation and bot ids
function executeConversationStatement(bot, cid, ulg ) {
request = new Request("INSERT INTO table (bot_id, conversationID, conversation_date, userlogged) VALUES (#bot, #cid, CURRENT_TIMESTAMP, #ulg); SELECT ##IDENTITY AS ID",function(err) {
if (err) {
console.log(err);}
});
request.addParameter('bot', TYPES.Int, bot);
request.addParameter('cid', TYPES.NVarChar, cid);
request.addParameter('ulg', TYPES.NVarChar, ulg);
request.on('row', function(columns) {
insertedcid = columns[0].value; // This is the id I pass later
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log("Conversation id of inserted item is " + column.value);
}
});
});
connection.execSql(request);
}
// Here on members added I save the conversation id generated by the framework
class BOT extends ActivityHandler {
constructor(conversationState,userState,telemetryClient) {
super();
this.conversationState = conversationState;
this.userState = userState;
this.dialogState = conversationState.createProperty("dialogState");
this.previousIntent = this.conversationState.createProperty("previousIntent");
this.conversationData = this.conversationState.createProperty('conservationData');
const qnaMaker = new QnAMaker({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
endpointKey: process.env.QnAEndpointKey,
host: process.env.QnAEndpointHostName
});
this.qnaMaker = qnaMaker;
this.onMessage(async (context, next) => {
await this.dispatchToIntentAsync(context);
await next();
});
this.onDialog(async (context, next) => {
await this.conversationState.saveChanges(context, false);
await this.userState.saveChanges(context, false);
await next();
});
this.onMembersAdded(async (context, next) => {
const { channelId, membersAdded } = context.activity;
actid = context._activity.id;
if (channelId === 'directline' || channelId === 'webchat') {
for (let member of membersAdded) {
if (member.id === context.activity.recipient.id) {
await context.sendActivity("Hi, I´m a chatbot to guide You");
try{
var saveqna = new executeConversationStatement(context._activity.id , 'Invitado');
}
catch{
console.log('Not saved');
}
}
}
}
await next();
});
}
//Finally, here I save the interaction:
async dispatchToIntentAsync(context) {
var result = await this.qnaMaker.getAnswers(context);
// Statement to save interaction with the insertedcid obtained above
var saveqnaint = new executeInteractionStatement(insertedcid, context._activity.text, result);
}
No matter if I use thet generated Id or the databse pk, I always keep the same identifier when multiple users are chatting, how can I got a separately Id for each session ?

Pub/Sub Lite GRPC - Error: 14 UNAVAILABLE: 502:Bad Gateway

I am trying to use the Google Cloud Pub/Sub Lite service in NodeJS using grpc. However, I am stuck with the following error : {"code":14,"details":"502:Bad Gateway","metadata":{"internalRepr":{},"options":{}}} when I try to publish a message in the queue.
I can't manage to make it works, can anyone help ?
Here is my code:
async function message() {
const protoDir = "node_modules/google-proto-files/google/cloud/pubsublite/v1/publisher.proto";
const packageDefinition = protoLoader.loadSync(protoDir, {
includeDirs: [
protos.getProtoPath('..'),
'node_modules/google-proto-files/google/cloud/pubsublite/v1/'
],
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const packageObject = grpc.loadPackageDefinition(packageDefinition);
const package = packageObject.google.cloud.pubsublite.v1;
const Publisher = package.PublisherService
var ssl_creds = grpc.credentials.createSsl();
const gAuth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
});
gAuth.getApplicationDefault(function(err, auth) {
var call_creds = grpc.credentials.createFromGoogleCredential(auth);
var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
const client = new Publisher('europe-west1-pubsublite.googleapis.com', combined_creds)
const initial = {
topic:'projects/projectId/locations/europe-west1-b/topics/poc-topic',
}
var pub = {
request_type: initial
}
const pubb = client.Publish(function(err, res) {
console.log("ERROR --> " + err)
});
pubb.on('data', function(data) {
console.log("Data --> " + data);
});
pubb.on('end', function() {
console.log('end ')
});
pubb.on('error', function(e) {
console.log('Error ' + e)
});
pubb.on('status', function(status) {
console.log('Status ' + JSON.stringify(status))
pubb.write(pub);
});
});
}

NodeJS Parse raw minecraft packet

I'm trying to create a minecraft proxy server to intercept and read packets between client and server.
I've already read the documentation about minecraft protocol here: https://wiki.vg , but my raw packets and the documentation didn't match...
Can someone explain me how i have to parse my packets to get length, id and data? Thanks ^^
packets examples:
1000d402093132372e302e302e3163dd0210000e4275726e47656d696f7333363433
bb01011035353730626633306336626363343266a20130819f300d06092a864886f70d010101050003818d0030818902818100a6f8df08dce2072565b5beef1bb27ed5c7cd02dd02e165019b9e4c4ae25a51a288c9474c643812036d07dc6715b0fc407824b6af1cb5b3efc1cf8739cb0a1f8e128b432f156ebe443bb69dd85c112d10426ab78ec99b0de8ab85b51140d0f5fc7b2d6b9c466b1272be09bc9551f1a0ab2121a0f35fd6bfc9d62b24cef2bb0e53020301000104051a9b77
8502018001584fe6b9c8388760fc20f4bab8bc3bb2c3026613eaff02d3cda41a541456d945b0e6dbdc62d5396b806e3ba5cda446ac6af681db1c584300fe7317d73a4310aea4f98dbecfcc71a1a0a6880ed0d7c6f7fa1b99c6b003226f38703c07871904574f855474cd41975e0ceccbd99b35a7da26121938af63484a26a8afb3772f947e800187a53861917ac19e8ffa04bb88e69a23a594ac22d268b25782e8de1b8f9dd3d8aff0c5222a286945a0de25c2ec75ee21c7bd49980910f9270d67f4290ae47b0aedc75f4e60406e406827bea309696fd9628aaf33270d7119292c10bcf0bbd955c54d97e57c7514e0080c373f8cad1fd3bbca6c90c6131c1592834446f28a1807
Edit:
my actual code:
const mc = require("minecraft-protocol");
const states = mc.states
const mcData = require('minecraft-data')('1.12.2');
const version = mcData.version
const Cserializer = mc.createSerializer({ state: states.PLAY, isServer: false, version: '1.12.2' })
const Sserializer = mc.createSerializer({ state: states.PLAY, isServer: true, version: '1.12.2' })
//var serialized = Cserializer.createPacketBuffer({ name: 'chat', params: { message: '!p' } })
//console.log("serialisé", serialized);
const Cdeserializer = mc.createDeserializer({ state: states.PLAY, isServer: false, version: '1.12.2' })
const Sdeserializer = mc.createDeserializer({ state: states.PLAY, isServer: true, version: '1.12.2' })
//console.log("deserialisé", Sdeserializer.parsePacketBuffer(Buffer.from("0c010d50455449542046445021 ", "hex")));
var net = require("net");
var fs = require("fs");
if(fs.existsSync("logs.txt"))fs.unlinkSync("logs.txt");
//packet format: [length][id][data...............]
function parsePacket(buffer, server){
let packet;
try {
if(server){
packet = Sdeserializer.parsePacketBuffer(buffer);
}else{
packet = Cdeserializer.parsePacketBuffer(buffer);
}
} catch (error) {
console.log("!");
}
return packet;
}
process.on("uncaughtException", function(error) {
console.error(error);
});
if (process.argv.length != 5) {
console.log("usage: %s <localport> <remotehost> <remoteport>", process.argv[1]);
process.exit();
}
var localport = process.argv[2];
var remotehost = process.argv[3];
var remoteport = process.argv[4];
var remotesocket = null;
var Clocalsocket = null;
var server = net.createServer(function (localsocket) {
Clocalsocket = localsocket;
remotesocket = new net.Socket();
remotesocket.connect(remoteport, remotehost);
localsocket.on('connect', function (data) {
console.log(">>> connection #%d from %s:%d",
server.connections,
localsocket.remoteAddress,
localsocket.remotePort
);
});
localsocket.on('data', function (data) {
let custom = doPacket(data, true);
if(!custom) custom = data;
fs.appendFileSync('logs.txt', '\n>>> remote >>> '+toHexString(data));
let p = parsePacket(data, true);
console.log(p);
if(custom != "cancel"){
var flushed = remotesocket.write(custom);
if (!flushed) {
console.log("remote not flushed; pausing local");
}
}
});
remotesocket.on('data', function(data) {
let custom = doPacket(data, false);
if(!custom) custom = data;
fs.appendFileSync('logs.txt', '\n>>> local >>> '+toHexString(data));
let p = parsePacket(data, false);
console.log(p);
if(custom != "cancel"){
var flushed = localsocket.write(custom);
if (!flushed) {
console.log("local not flushed; pausing remote");
}
}
});
localsocket.on('drain', function() {
console.log("%s:%d - resuming remote",
localsocket.remoteAddress,
localsocket.remotePort
);
remotesocket.resume();
});
remotesocket.on('drain', function() {
console.log("%s:%d - resuming local",
localsocket.remoteAddress,
localsocket.remotePort
);
localsocket.resume();
});
localsocket.on('close', function(had_error) {
console.log("%s:%d - closing remote",
localsocket.remoteAddress,
localsocket.remotePort
);
remotesocket.end();
});
remotesocket.on('close', function(had_error) {
console.log("%s:%d - closing local",
localsocket.remoteAddress,
localsocket.remotePort
);
localsocket.end();
});
});
server.listen(localport);
console.log("redirecting connections from 127.0.0.1:%d to %s:%d", localport, remotehost, remoteport);
function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
}

ReactNative MQTT Listener

I have the following code excerpt:
client.on ('message', function (msg) {
// ....
});
Can someone tell me how I get the value of msg from this function and can access it from outside.
I tried but it doesn't work:
this.setState ({msg: msg})
You can make an MQTT manager and use it.
Example
module.exports = { // cached singleton instance
QOS: 1, // Only 0 and 1 supported by Rabbit
props: null,
create(userID, connectionProps = {}) {
if (userID && connectionProps) {
// http://www.hivemq.com/demos/websocket-client/
this.onConnectionOpened = this.onConnectionOpened.bind(this);
this.onConnectionClosed = this.onConnectionClosed.bind(this);
this.onError = this.onError.bind(this);
this.onMessageArrived = this.onMessageArrived.bind(this);
this.disconnect = this.disconnect.bind(this);
const deviceId = this.randIdCreator()
.replace(/[^a-zA-Z0-9]+/g, '');
this.conProps = _.extend({
clientId: `realtime.${userID}.${deviceId}`,
channelToUse: `mqtt-subscription-realtime.${userID}`,
auth: false,
clean: true, // clean session YES deletes the queue when all clients disconnect
}, connectionProps);
/* create mqtt client */
MQTT.createClient(this.conProps)
.then((client) => {
this.client = client;
client.on('closed', this.onConnectionClosed);
client.on('error', this.onError);
client.on('message', this.onMessageArrived);
client.on('connect', this.onConnectionOpened);
client.connect();
}).catch((err) => {
console.error(`MQTT.createtClient error: ${err}`);
});
}
},
...
onMessageArrived(message) {
if (message) {
console.log(`MQTT New message: ${JSON.stringify(message)}`)
}
}
...
Usage
import MqttNotificationsManager from './realtimeManager';
// init realtime
MqttNotificationsManager.create(
'bob',
{
uri: 'mqtt://test.mosquitto.org:1883',
},
);

IMAP Node.js executing twice, saving email twice and won't save email from emitter

I have a mailListenerWrapper.js where I define my imap class:
mailListenerWrapper.js:
const simpleParser = require("mailparser").simpleParser;
const Imap = require("imap");
const striptags = require("striptags");
const BlackListModel = require("./models/blacklist");
const RecEmailConfigModel = require("./models/emailConfig");
const ReceivedEmailModel = require("./models/receivedMail");
const Base64 = require("js-base64").Base64;
const _ = require("lodash");
const logger = require("./helpers/logger");
require("dotenv").config();
class MailListenerWrapper {
constructor() {
this.imap = null;
this.init();
}
async init() {
try {
const getRecEmailConfig = await RecEmailConfigModel.findOne({});
if (getRecEmailConfig) {
getRecEmailConfig.password = Base64.decode(getRecEmailConfig.password);
this.imap = new Imap({
user: getRecEmailConfig.user,
password: getRecEmailConfig.password,
host: getRecEmailConfig.host,
port: getRecEmailConfig.port,
tls: getRecEmailConfig.tls,
tlsOptions: {
rejectUnauthorized: false,
authTimeout: 10000
}
});
} else {
return false;
}
let lastMessage = 0;
const openInbox = cb => {
this.imap.openBox("INBOX", true, cb);
};
const regEmail = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/g;
this.imap.once("ready", () => {
openInbox((err, box) => {
if (err) {
logger.log({ level: "error", message: err });
return false;
}
lastMessage = box.messages.total;
this.imap.on("mail", numberOfNewMessages => {
const newEmail = {};
// const numberOfNewMessages = 0
lastMessage = lastMessage + numberOfNewMessages;
const f = this.imap.seq.fetch(lastMessage + ":" + lastMessage, {
bodies: ["HEADER.FIELDS (SUBJECT FROM)", ""],
struct: true
});
f.on("message", msg => {
msg.on("body", async stream => {
try {
const parsed = await simpleParser(stream);
if (parsed.headers.get("subject")) {
// ################ HEADER ################ \\
newEmail.title = parsed.headers.get("subject");
const getFrom = parsed.headers.get("from").text.split(" <");
// ############################ //
console.log(getFrom); // executes twice here already
// ########################### //
if (getFrom.length === 2) {
newEmail.name = getFrom[0];
newEmail.email = getFrom[1].match(regEmail).join("\n");
}
// ################ HEADER ################ \\
}
if (parsed.text) {
// ################ TEXT ################ \\
newEmail.text = striptags(parsed.text).toString("utf8");
// ################ TEXT ################ \\
}
if (
newEmail.name &&
newEmail.email &&
newEmail.title &&
newEmail.text
) {
const getEmailHostname = newEmail.email.replace(/.*#/, "");
const blacklists = await BlackListModel.find({
hostname: new RegExp(".*" + getEmailHostname + ".*", "i")
});
if (blacklists.length > 0) {
logger.log({
level: "info",
message:
"A BLACKLIST ENTRY WAS TRYING TO SUBMIT AN APPLICATION"
});
return false;
}
const savedEmail = new ReceivedEmailModel(newEmail);
await savedEmail.save();
logger.log({
level: "info",
message:
"SAVED RECEIVED EMAIL =>" + JSON.stringify(savedEmail)
});
}
} catch (err) {
logger.log({ level: "error", message: err });
}
}); // msg on body
});
}); // on mail
this.imap.once("error", err => {
logger.log({
level: "error",
message: err
});
}); // on error
}); // openInbox
}); // ready
this.imap.connect();
} catch (err) {
logger.log({
level: "error",
message: err
});
}
}
close() {
if (this.imap) this.imap.end();
}
}
module.exports = MailListenerWrapper;
In app.js:
global.mailListenerWrapper = new mailListenerWrapper();
The receivedEmail.js model:
const mongoose = require("mongoose");
// Schema variable
const Schema = mongoose.Schema;
// Tags
const receivedMailSchema = new Schema(
{
title: {
type: String,
required: true
},
from: {
name: {
type: String
},
email: {
type: String
}
},
text: {
type: String
}
},
{
collection: "receivedEmail",
timestamps: true
}
);
const ReceivedEmail = mongoose.model("ReceivedEmail", receivedMailSchema);
module.exports = ReceivedEmail;
THE PROBLEM
The console log executes twice and the email is saved on my database twice, and does not save the address that it was sent from.
I have no idea why this could be happening. Could there be some way that the imap class is being instantiated twice?
I guess I'm pretty late, but maybe it could still be useful for anyone else.
I experienced this issue as well. But I realized that node-imap documetation specifies the use of .once() instead of .on() in the sample code.
Try to change the event below:
msg.on("body", async stream => {
....
}
To:
msg.once("body", async stream => {
....
}
It did work for me!

Resources