Node.js - Combine IBM Watson Discovery and Conversation Services - node.js

I know this may seem complicated but I have spent a week and a half trying to do this and can not figure it out. Hopefully someone can point me in the right direction. Thank you so much!
I am working with IBM Watson and Node.js to create a conversation bot. I have created the bot and used one of IBM's example programs (Conversation-Simple) to make a website to interact with the bot. Everything with it works. I am now trying to use Watson's Discovery to search documents and answer a question with the query response. I have Discovery working where you can query it and have a Node.js program to query it.
I am now trying to connect the two! When Conversation is ready to query Discovery it will move to the intent called query.
It looks like this is where Watson gives the response and the variable of the response is currentText. I could be wrong but it looks like it is.
function buildMessageDomElements(newPayload, isUser) {
var textArray = isUser ? newPayload.input.text : newPayload.output.text;
if (Object.prototype.toString.call( textArray ) !== '[object Array]') {
textArray = [textArray];
}
var messageArray = [];
textArray.forEach(function(currentText) {
if (currentText) {
var messageJson = {
// <div class='segments'>
'tagName': 'div',
'classNames': ['segments'],
'children': [{
// <div class='from-user/from-watson latest'>
'tagName': 'div',
'classNames': [(isUser ? 'from-user' : 'from-watson'), 'latest', ((messageArray.length === 0) ? 'top' : 'sub')],
'children': [{
// <div class='message-inner'>
'tagName': 'div',
'classNames': ['message-inner'],
'children': [{
// <p>{messageText}</p>
'tagName': 'p',
'text': currentText
}]
}]
}]
};
messageArray.push(Common.buildDomElement(messageJson));
}
});
return messageArray;
}
When it goes to respond here (or if it is somewhere else) how can I check if the intent is query and if it is query Watson Discovery?
This is how I currently query Discovery:
url2 = 'fakeURL'
var request = require("request");
var myJSON = require("JSON");
global var body1;
function getMyBody(url, callback) {
request(
{
url: url,
auth: {'user': 'fakeUsername','pass': 'fakePassword'},
json: true
},
function (error, response, body) {
if (error || response.statusCode !== 200) {
return callback(error || {statusCode: response.statusCode});
}
else{
callback(null, JSON.parse(JSON.stringify(body.results)));
}
});
}
getMyBody(url2, function test8(err, body) {
body1 = body[0];
console.log(body1)
}
);
This code currently prints:
{ id: 'a3990d05fee380f8d0e9b99fa87188a7',
score: 1.0697575,
os: { OperatingSystem: 'Windows 10 Professional' },
price: '174.99',
text: 'Lenovo ThinkCentre M58 Business Desktop Computer, Intel Core 2 Duo 30 GHz Processor, 8GB RAM, 2TB Hard Drive, DVD, VGA, Display Port, RJ45, Windows 10 Professional (Certified Refurbished)',
url: 'https://www.amazon.com/Lenovo-ThinkCentre-M58-Professional-Refurbished/dp/B01M4MD9C1?SubscriptionId=AKIAJXXNMXU323WLP4SQ&tag=creek0a-20&linkCode=xm2&camp=2025&creative=165953&creativeASIN=B01M4MD9C1',
highlight: { text: [Array] } }
The response to the user I want to be the value of text and the value of URL.
This is the whole program from IBM https://github.com/watson-developer-cloud/conversation-simple

Like this example from IBM Developers: conversation-with-discovery. You can follow the same logic programming. But I really recommend check out the project and the video in the end of this answer.
Summary:
You can see in the workspace, the call for discovery is one action variable inside the Advanced JSON (Dialog node) called call_discovery.
"action":{"call_discovery: ""},
Basically, have one intent with the name out_of_scope, because if the user tells something that doesn't have any answer in the intents or some conditions in the Conversation, the call for discovery will happen and return one object with the documents according to the message from user.
Create one context variable inside the Conversation service:
{
"context": {
"callDiscovery": true
},
"output": {
"text": {
"values": [
"Wait for the response, I'll check out."
],
"selection_policy": "sequential"
}
}
}
After creates one context variable (Example: "callDiscovery": true) inside the Node in your Chatbot in the Advanced JSON for call the discovery service, you need to use code for identify if you arrived at the part where you need to make call for Discovery. Will be something like using the function updateMessage inside the conversation-simple example:
function updateMessage(input, response) {
var callDiscovery ;
if (!response.output) {
response.output = {};
//THE CONTEXT VARIABLE callDiscovery
} else if (response.context.callDiscovery === true){
//CREATE ONE NEW FUNCTION WITH YOUR CODE
getMyBody(url, callback, response); //response parameter for send the message
}
}
And, in your function getMyBody (THE FUNCTION that returns the prints in your Question[id, text, url, etc]), you need to send the message to the user, something like:
url2 = 'fakeURL'
var request = require("request");
var myJSON = require("JSON");
var body1;
function getMyBody(url, callback, response) {
request(
{
url: url,
auth: {'user': 'fakeUsername','pass': 'fakePassword'},
json: true
},
function (error, response, body) {
if (error || response.statusCode !== 200) {
return callback(error || {statusCode: response.statusCode});
}
else{
callback(null, JSON.parse(JSON.stringify(body.results)));
}
});
}
getMyBody(url2, function test8(err, body) {
body1 = body[0];
console.log(body1);
var sendMessage = ("I've find this results: "+ body1.text + "And you can see the url: "+ body1.url)// you said body1.text and body1.url
response.output.text[0] = sendMessage;
return res.json(response);
});
}
Note: According to the project conversation-simple, you need to use the response parameter for sending the message for the user, then, you need to set the parameter in your function that calls for Discovery, and to add the following code above in your function.
See the Official video by IBM Watson talking about this project and showing one good example to call the Discovery service and sends the result for the user.

Related

Using Twit to post a video with a bot

function fridayNight(){
const videoPath = "C:\\GitHub\\DivasLive\\DivasLive\\nsync.mp4";
console.log("It's Friday night and I just Got Paid!");
var b64content = fs.readFileSync(videoPath, { encoding: 'base64' });
var mediaType = MIM.getMIMEType(videoPath);
T.post('media/upload', { media_data: b64content, media_type: mediaType }, function (err, data,
response)
{
if(err)
{
console.log(err);
} else
{
console.log(data);
var mediaIdStr = data.media_id_string
var params = { status: "Just got paid!", media_id: [mediaIdStr] };
T.post('statuses/update', params, function (err, data, response)
{
console.log(data);
console.log(err);
});
};
});
};
I keep getting a 400: Media type unrecognized, but I'm trying to explicitly define it in line 88. Here's the full gist as well. https://gist.github.com/MetzinAround/25b5771564aa7de183391398db52dbef
For video and GIFs, you need to use the chunked media upload method - you cannot do it in a "single shot", it has multiple stages (INIT, APPEND, FINALIZE), per the Twitter docs.
Please note that to upload videos or GIFs (tweet_video, amplify_video, and tweet_gif), you need to use the chunked upload end-point.
It turns out that the twit module has a helper method for doing this, postMediaChunked - this also saves you having to tell Twitter the mime type of the data, which means you can remove the import of the mim module.
Here's a minimal example of just doing the media part - you just need to extract the media_id_string and use that in the statuses/update call:
// Create an Twitter object to connect to Twitter API
const Twit = require('twit')
// Making a Twit object for connection to the API
const T = new Twit(config)
var filePath = '/Users/myuser/Downloads/robot.mp4'
T.postMediaChunked({
file_path: filePath
}, function (err, data, response) {
console.log(data)
})
output:
{
media_id: 1379414276864151600,
media_id_string: '1379414276864151557',
media_key: '7_1379414276864151557',
size: 924669,
expires_after_secs: 86400,
processing_info: { state: 'pending', check_after_secs: 1 }
}
(note that, in JavaScript, you should always use the string versions of Twitter IDs - as you can see here, the numeric version in media_id does not match media_id_string, as JavaScript cannot properly handle long integers, and has mangled the numeric value)

Coinbase transfer between accounts returns "Not found"

I'm trying to transfer BTC and BCH between accounts. Looking through the documentation I have found the following:
https://developers.coinbase.com/api/v2#transfer-money-between-accounts
First off I believe there is an error in their example, as the "currency" property is mandatory, it gives an error when not providing it.
But after adding the currency, every request I make returns with "Not found".
I'm using the node client, but I think the problem is higher up.
This is the request I'm doing:
const Client = require('coinbase').Client;
let client = new Client({'apiKey': 'xxxx', 'apiSecret': 'xxx'});
client.getAccounts({}, function(err, accounts) {
let btc_account = null;
let bch_account = null;
for(account of accounts) {
if(account.currency == "BTC") {
btc_account = account;
} else if(account.currency == "BCH") {
bch_account = account;
}
}
var options = {'to': bch_account.id, 'amount': btc_account.balance.amount, 'currency': "BTC"};
btc_account.transferMoney(options, function(err, tx) {
if(err) {
console.log("ERROR during transfer", err);
return
}
console.log(tx);
});
});
Doing this returns me with:
ERROR during transfer NotFound: Not found
After doing some debugging, I found it tries to do the request with the following options:
url: 'https://api.coinbase.com/v2/accounts/xxx/transactions'
body: '{"to":"xxx","amount":"0.00072256","currency":"BTC","type":"transfer"}'
(obfuscated the actual account.id's)
And the actual response from their API is:
{"errors":[{"id":"not_found","message":"Not found"}]}
Can anybody tell me what I'm doing wrong here?

Intergrating Bing Web Search API with QnA chat bot

I am making a chat bot using the QnA template (Microsoft Azure). Basically, a user asks a question and the bot will try and find the answer in an FAQ document. If it fails, I want it to run a Bing search with the user's query and replies with the most accurate answer. I found this example that uses Bing Web Search API: https://learn.microsoft.com/en-us/azure/cognitive-services/bing-web-search/quickstarts/nodejs. For now, I just want the bot to reply with the first link of the search for example.
However, I don't know how to merge the code in the link, with the generated code for the QnA Bot (in Node.js):
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require("botbuilder-azure");
var builder_cognitiveservices = require("botbuilder-cognitiveservices");
var request = require('request');
// 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 chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword,
openIdMetadata: process.env.BotOpenIdMetadata
});
// Listen for messages from users
server.post('/api/messages', connector.listen());
var tableName = 'botdata';
var azureTableClient = new botbuilder_azure.AzureTableClient(tableName, process.env['AzureWebJobsStorage']);
var tableStorage = new botbuilder_azure.AzureBotStorage({ gzipData: false }, azureTableClient);
// Create your bot with a function to receive messages from the user
var bot = new builder.UniversalBot(connector);
bot.set('storage', tableStorage);
// Recognizer and and Dialog for GA QnAMaker service
var recognizer = new builder_cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
authKey: process.env.QnAAuthKey || process.env.QnASubscriptionKey, // Backward compatibility with QnAMaker (Preview)
endpointHostName: process.env.QnAEndpointHostName
});
var basicQnAMakerDialog = new builder_cognitiveservices.QnAMakerDialog({
recognizers: [recognizer],
defaultMessage: 'Sorry, I cannot find anything on that topic',
qnaThreshold: 0.3
});
// Override the invokeAnswer function from QnAMakerDialog
builder_cognitiveservices.QnAMakerDialog.prototype.invokeAnswer = function (session, recognizeResult, threshold, noMatchMessage) {
var qnaMakerResult = recognizeResult;
session.privateConversationData.qnaFeedbackUserQuestion = session.message.text;
if (qnaMakerResult.score >= threshold && qnaMakerResult.answers.length > 0) {
if (this.isConfidentAnswer(qnaMakerResult) || this.qnaMakerTools == null) {
this.respondFromQnAMakerResult(session, qnaMakerResult);
this.defaultWaitNextMessage(session, qnaMakerResult);
}
else {
this.qnaFeedbackStep(session, qnaMakerResult);
}
}
else {
this.noMatch(session, noMatchMessage, qnaMakerResult);
}
};
// API call to Bing
basicQnAMakerDialog.noMatch = function (session, noMatchMessage, qnaMakerResult) {
var term = session.message.text;
var key = 'i hid it';
var options = {
url: "https://api.cognitive.microsoft.com/bing/v7.0/search?q=" + term,
method: 'GET',
headers : {
'Ocp-Apim-Subscription-Key' : key
}
};
request(options, function(err,res, body){
if(err){
console.error(err);
session.send(noMatchMessage);
}
body = JSON.parse(body);
session.send("I found a thing: " + body["webPages"]["value"][0]["name"]);
});
};
bot.dialog('basicQnAMakerDialog', basicQnAMakerDialog);
bot.dialog('/', //basicQnAMakerDialog);
[
function (session, results) {
session.replaceDialog('basicQnAMakerDialog');
},
]);
In the function inside the bot.dialog, I think I should add a condition such as: if the bot returns the default message, open a web page and let the "term" to search be the user's last message. However, I don't know how to code this exactly, and where to do so. Also, I don't know how to exit from the replaceDialog function in order to reply with something other than the default message or the answers in the FAQ.
PS: I don't have much experience with javascript or web development in general. Any help will be appreciated :)
What you are implementing involves two steps, extracting out the "noMatch" case to a method so you can make a full response message, and then the API call itself.
The "noMatch" method.
First, you will want to override the invokeAnswer function from QnAMakerDialog (This override changes nothing but adding a call to a separate method for the noMatch case.)
builder_cognitiveservices.QnAMakerDialog.prototype.invokeAnswer = function (session, recognizeResult, threshold, noMatchMessage) {
var qnaMakerResult = recognizeResult;
session.privateConversationData.qnaFeedbackUserQuestion = session.message.text;
if (qnaMakerResult.score >= threshold && qnaMakerResult.answers.length > 0) {
if (this.isConfidentAnswer(qnaMakerResult) || this.qnaMakerTools == null) {
this.respondFromQnAMakerResult(session, qnaMakerResult);
this.defaultWaitNextMessage(session, qnaMakerResult);
}
else {
this.qnaFeedbackStep(session, qnaMakerResult);
}
}
else {
this.noMatch(session, noMatchMessage, qnaMakerResult);
}
};
After this you will define your basicQnAMakerDialog as usual, but consider making the default message field something for an error case or "no results found". (This doesn't really matter, as you can arbitrarily decide what you send back within the noMatch method, but it's nice to have the default there)
var basicQnAMakerDialog = new builder_cognitiveservices.QnAMakerDialog({
recognizers: [recognizer],
defaultMessage: 'Sorry, I can't find anything on that topic'
qnaThreshold: 0.3
});
After this you can define the noMatch method seperately.
basicQnAMakerDialog.noMatch = function (session, noMatchMessage, qnaMakerResult) {
session.send(noMatchMessage); //Contains the default message
this.defaultWaitNextMessage(session, qnaMakerResult);
}
Within this method we can call the API or do basically whatever we want.
Credit to gliddell on this override
The API call.
Within the noMatch method, we will make an API call to Bing
basicQnAMakerDialog.noMatch = function (session, noMatchMessage, qnaMakerResult) {
var term = session.message.text;
var key = <Environment Var containing the Bing key>;
var options = {
url: "https://api.cognitive.microsoft.com/bing/v7.0/search?q=" + term,
method: 'GET',
headers : {
'Ocp-Apim-Subscription-Key' : key
}
};
request(options, function(err,res, body){
if(err){
console.error(err);
session.send(noMatchMessage);
}
body = JSON.parse(body);
session.send("I found a thing: " + body["webPages"]["value"][0]["name"]);
});
};
You can customize the message you are sending back as much as you like after you get the results from the search.

telegram bot - keep questions and answers

My telegram bot is a dialog and it needs to keep the questions and the answers (like TriviaBot). What is the best (most efficient) method to do this? A database with the user id as key?
There is a lot of telegram bots, but where are examples with the source code to get ideas?
Your question is not really related to the telegram bot API. You are essentially asking: I have an application that has to keep an history of user interactions, how to do this (efficient)?
To answer that: you could use a database and insert an entry for each conversation using an unique identifier. Since telegram has a chat_id for each conversation, you could use that. Depending on what you are exactly trying to store, you should choose how to store it. (an entry for each answer, or for each conversation or ...)
If you program in python, you can use the python wrapper called python-telegram-bot to make things easier
Examples are here:
https://github.com/leandrotoledo/python-telegram-bot#examples
You can use force_reply and frequently ask whatever you want and store answers in any kind of database.
Please refer to its doc : refrence
and a simple answer on : force reply description
Well, I didn't found any example of force_reply, and had to ask an ai about it, honestly I dont know if it works, I just hope that it helps, here we go...
function survey(data) {
var Q1 = {
'chat_id': data.message.chat.id,
'text': 'how are you?'
}
var method = 'sendMessage';
var options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(Q1)
}
var response = UrlFetchApp.fetch('https://api.telegram.org/bot' + telegramToken + '/' + method, options);
var text = data.message.text;
// Start loop
for (var i = 0; i < 3; i++) {
if (i == 0) {
// First iteration
if (text == "") {
// Get response from user
currentstep = '3';
var dataForceReply = {
method: "post",
payload: {
method: "sendMessage",
chat_id: String(data.message.chat.id),
text: "how are you?",
reply_markup: JSON.stringify({
"force_reply": true
})
}
};
UrlFetchApp.fetch(telegramAPIURL + "/", dataForceReply);
}
} else if (i == 1) {
// Second iteration
if (text != "") {
// Get response from user
// Store response in variable
var response1 = text;
var dataForceReply2 = {
method: "post",
payload: {
method: "sendMessage",
chat_id: String(data.message.chat.id),
text: "What do you think about this?",
reply_markup: JSON.stringify({
"force_reply": true
})
}
};
UrlFetchApp.fetch(telegramAPIURL + "/", dataForceReply2);
}
} else if (i == 2) {
// Third iteration
if (text != "") {
// Get response from user
// Store response in variable
var response2 = text;
var dataForceReply3 = {
method: "post",
payload: {
method: "sendMessage",
chat_id: String(data.message.chat.id),
text: "Do you have any suggestions?",
reply_markup: JSON.stringify({
"force_reply": true
})
}
};
UrlFetchApp.fetch(telegramAPIURL + "/", dataForceReply3);
}
} else {
// Get response from user
// Store response in variable
var response3 = text;
// Store all responses in variables
var response1 = response1;
var response2 = response2;
var response3 = response3;
}
}
}

handle response from telegram bot api

I am using this api: https://github.com/orzFly/node-telegram-bot
It should work like any other.
Now I want my Bot to have an option to update a string he keeps for some reason. so on "/update" the update function is called, where msg is a Message object (https://core.telegram.org/bots/api#message):
link = "something";
function update(msg) {
response = tg.sendMessage({
text: "Send a new URL, please",
chat_id: msg.chat.id,
reply_to_message_id: msg.message_id,
reply_markup: {
force_reply: true,
selective: true
}
});
console.log("response: " + response);
// on reply I want to update link
}
Now this bot asks me to provide a new string. The next answer in telegram is already an answer to the bots request, because of the force_reply.
How would I get this answer? 'response' here is a promise object and I don't know what to do with it.
After reading about Promises objects, I tried something like this:
response.then(function successHandler(result) {
tg.sendMessage({
text: "new URL is: I don't know",
chat_id: msg.chat.id
});
}, function failureHandler(error) {
colsole.log("error: " + error);
});
But it didn't work. In no way.
I just don't know where to get the reply Message object from.
I hope it's clear what I am asking. Otherwise let me know.
If I understood right, you're trying to get the next message from the user and treat it as the new string;
Problem is: response will contain the response from Telegram servers stating the result of the message you tried to send; it has nothing to do with the user's response to your message;
In order to do this, you need to control what was the last message your bot sent to a user and, based on that, decide how to handle that user's next message; it could look something like this:
link = "something";
states = {}
function update(msg) {
if (!states[msg.chat.id] || states[msg.chat.id] == 1) {
tg.sendMessage({
text: "Send a new URL, please",
chat_id: msg.chat.id,
reply_to_message_id: msg.message_id,
reply_markup: {
force_reply: true,
selective: true
}
}).then(() => {
states[msg.chat.id] = 2
console.log(`Asked a question to ${msg.chat.id}`);
});
} else {
link = msg.text;
tg.sendMessage({
text: `New URL is: ${link}`,
chat_id: msg.chat.id,
reply_to_message_id: msg.message_id
})
}
}
It looks like the result in the promise is the entire response from Telegram. So your result will be in result.result.text
The result variable will look like:
{
ok: true
result: {
message_id: x,
from: { ... }
chat: { ... }
date: x,
text: 'message'
}
}
This is unfortunate, I would have suggested the author only returns the result key.
var api = require('telegram-bot');
api = new api(<TOKEN>);
api.sendMessage({ chat_id: 0, text: 'test' }).then(function (result) {
console.log(result);
});
api.on('message', function (msg) {
console.log(msg); // <- will contain the reply
// msg.text
// msg.chat.id
// msg.from.id
});
api.start();

Resources