I'm building my first FB Messenger chat bot using Wit as the NLP engine. All my services are connected and seem to be working on the surface, but when I look at my Heroku logs it seems that my bot's responses are being sent back to Wit to be parsed as well as user inputted messages. This is obviously causing issues further through my conversation flow when it comes time to trigger actions.
How do I make it so that my bot only parses user input, then responds appropriately according to my story in Wit?
Messenger window:
Relevant part of my Wit conversation flow:
My logs:
As far as I can tell, this is the important code:
var actions = {
say (sessionId, context, message, cb) {
// Bot testing mode, run cb() and return
if (require.main === module) {
cb()
return
}
console.log('WIT HAS A CONTEXT:', context)
if (checkURL(message)) {
FB.newMessage(context._fbid_, message, true)
} else {
FB.newMessage(context._fbid_, message)
}
cb()
},
...
}
///
var read = function (sender, message, reply) {
console.log('READING LOG AAAAAAAAAAAAAAAAAAAAAA')
var sessionId = findOrCreateSession(sender)
console.log('READING LOG BBBBBBBBBBBBBBBBBBBBBB')
console.log(message)
// Let's forward the message to the Wit.ai bot engine
// This will run all actions until there are no more actions left to do
wit.runActions(
sessionId, // the user's current session by id
message, // the user's message
sessions[sessionId].context, // the user's session state
function (error, context) { // callback
console.log('READING LOG CCCCCCCCCCCCCC')
if (error) {
console.log('oops!', error)
} else {
// Wit.ai ran all the actions
// Now it needs more messages
console.log('READING LOG DDDDDDDDDDDDDDDD')
console.log('Waiting for further messages')
// Updating the user's current session state
sessions[sessionId].context = context
console.log('READING LOG EEEEEEEEEEEEEEEE')
}
})
}
///
app.post('/webhooks', function (req, res) {
var entry = FB.getMessageEntry(req.body)
// IS THE ENTRY A VALID MESSAGE?
if (entry && entry.message) {
if (entry.message.attachments) {
// NOT SMART ENOUGH FOR ATTACHMENTS YET
FB.newMessage(entry.sender.id, "That's interesting!")
} else {
// SEND TO BOT FOR PROCESSING
console.log('SENDING TO BOT FOR PROCESSING XXXXX')
Bot.read(entry.sender.id, entry.message.text, function (sender, reply) {
FB.newMessage(sender, reply)
return
})
console.log('SENDING TO BOT FOR PROCESSING YYYYY')
}
}
res.sendStatus(200)
})
When you create your Facebook messenger app, one of the webhooks events is message_echoes.
Make sure you you opt it out message_echoes for not receiving your own bot messages.
I used the 'is_echo' : true to discern wits messages from others and it's been working.
if (event.message.is_echo) {
console.log(`This sender is the wit bot.`);
return;
}
Related
for a while now I am stuck, trying to listen to the event where the agent sends a reply to the ticket. I have tried listening to ticket.comments.changed and ticket.conversation.changed but have not been successful.
I can't use the ticket.submit.[done|fail|always] or ticket.save because I don't have a way of knowing if it is the event I want or is being called with another event.
Maybe someone who knows of a configuration or some way that would allow me to do this, I would be very grateful.
You can configure triggers and webhooks to be added to your app as requirements. If you must use a ticket_sidebar app, you can listen to the following events:
var client = ZAFClient.init();
client.on('ticket.comments.changed', (e) => {
// Here is the latest comment
let comment = e[0];
console.log(comment);
// Check author role
console.log((comment.author.role !== "end-user") ? "Comment made by agent" : "Comment made by end user");
// Get ticket object if needed
client.get('ticket').then(
(res) => {
// Send ticket payload to my backend
},
(err) => {
console.error(err);
}
)
});
client.on('ticket.status.changed', (e) => {
// Here is the new status
console.log("Status changed to", e);
// Get ticket object if needed
client.get('ticket').then(
(res) => {
// Send ticket payload to my backend
},
(err) => {
console.error(err);
}
)
});
i am using graph api javascript example from here https://learn.microsoft.com/en-us/graph/api/user-list-joinedteams?view=graph-rest-beta&tabs=javascript
and my code is like:
async function(req, res) {
if (!req.isAuthenticated()) {
// Redirect unauthenticated requests to home page
res.redirect('/')
} else {
let params = {
active: { calendar: true }
};
// Get the access token
var accessToken;
try {
accessToken = await tokens.getAccessToken(req);
console.log("access token is:", accessToken)
} catch (err) {
req.flash('error_msg', {
message: 'Could not get access token. Try signing out and signing in again.',
debug: JSON.stringify(err)
});
}
if (accessToken && accessToken.length > 0) {
try {
console.log("vik testing stuff12 for teams")
const user = await graph.getTeams(accessToken)
console.log("graph me:::", user)
} catch (err) {
req.flash('error_msg', {
message: 'Could not fetch events',
debug: JSON.stringify(err)
});
}
} else {
req.flash('error_msg', 'Could not get an access token');
}
res.render('calendar', params);
}
}
getTeams is
getTeams: async function(accessToken) {
const client = getAuthenticatedClient(accessToken);
const events = await client
.api('/me/joinedTeams')
.version('beta')
.get();
return events;
}
this prints no results and no error. if I replace 'me/joinedTeams' to just 'me' then it returns logged in user details.
You can got a response successfully, so it seems no error with your code as you said if you call https://graph.microsoft.com/v1.0/me you can get user information.
And I tried to call this API using my account(my account hasn't joined any Teams), and got response like below, so if you got the same response as mine, perhaps you need to check if you have joined any Teams:
On the other hand, following the document, this API needs several permissions. So please obtain your access token when debug and use JWT tool to decrypt it to check if the access token have enough scope.
And I used the same request and got Teams information after adding my account to a team.
I'm sending message to my bot using Microsoft BotConnector but they are not being logged as normal messages. For logging messages to the DB I wrote custom logger :
class CustomLogger {
/**
* Log an activity to the transcript file.
* #param activity Activity being logged.
*/
constructor() {
this.conversations = {};
}
logActivity(activity) {
if (activity) {
console.log("Log information")
}
if (!activity) {
throw new Error("Activity is required.");
}
if (activity.conversation) {
var id = activity.conversation.id;
if (id.indexOf("|" !== -1)) {
id = activity.conversation.id.replace(/\|.*/, "");
}
}
if (activity.type === "message") {
Conv.create({
text: activity.text,
conv_id: activity.conversation.id,
from_type: activity.from.role,
message_id: activity.id || activity.replyToId
}).then(() => {
console.log("logged");
});
delete this.conversations[id];
}
}
}
it works great with normal messages but it is no working with the messages that are sent to
POST /v3/conversations/{conversationId}/activities
via microsoft bot connector.
When I send message using the the bot connector it doesn't log the request via activity.
Code that I'm using to send proactive msg:
/**
* Send message to the user.
*/
function sendMessage(token, conversation, name) {
var config = {
headers: { "Authorization": "Bearer " + token }
};
var bodyParameters = {
"type": "message",
"text": name
}
axios.post(
'https://smba.trafficmanager.net/apis/v3/conversations/29:XXXXXXXXXXXXXXX/activities',
bodyParameters,
config
).then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
});
}
let name = "Hey, How was your week?";
let conversation = "29:XXXXXXXXXXXXXXX";
run(conversation, name);
Instead of using the REST API to send proactive messages to users, I would recommend using the BotFramework Adapter to continue the conversation with the user. When you send the proactive message from the adapter, the activity passes through the logger middleware and gets saved to storage. If you would like to initiate the proactive message from an Azure Function, you can set up another messaging endpoint in the index file that you call from the function. Take a look at the code snippets below.
index.js
// Listen for incoming notifications and send proactive messages to user.
server.get('/api/notify/:conversationID', async (req, res) => {
const { conversationID } = req.params;
const conversationReference = conversationReferences[conversationID];
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity('proactive hello');
});
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.write('<html><body><h1>Proactive messages have been sent.</h1></body></html>');
res.end();
});
For more details I would take a look at this Proactive Messages Sample. It is in the samples-work-in-progress branch and might change slightly, but it is a great example of how to configure your project to send a proactive message from a Restify endpoint.
Hope this helps!
I am having trouble using the Parse Server JS SDK to edit and save a user.
I am signing in, logging in and retrieving the user just fine, I can call without exception user.set and add/edit any field I want, but when I try to save, even when using the masterKey, I get Error 206: Can t modify user <id>.
I also have tried to use save to direcly set the fields, same result.
A interesting thing is that in the DB, the User's Schema get updated with the new fields and types.
Here is my update function:
function login(user, callback) {
let username = user.email,
password = user.password;
Parse.User.logIn(username, password).then(
(user) => {
if(!user) {
callback('No user found');
} else {
callback(null, user);
}
},
(error) => {
callback(error.message, null);
}
);
}
function update(user, callback) {
login(user, (error, user) => {
if(error) {
callback('Can t find user');
} else {
console.log('save');
console.log('Session token: ' + user.getSessionToken());
console.log('Master key: ' + Parse.masterKey);
user.set('user', 'set');
user.save({key: 'test'}, {useMasterKey: true}).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR - ' + require('util').inspect(err));
callback(error.message);
}
);
}
});
}
And a exemple of the error:
update
save
Session token: r:c29b35a48d144f146838638f6cbed091
Master key: <my master key>
ERR- ParseError { code: 206, message: 'cannot modify user NPubttVAYv' }
How can I save correctly my edited user?
I had the exact same problem when using Parse Server with migrated data from an existing app.
The app was created before March 2015 when the new Enhanced Sessions was introduced. The app was still using legacy session tokens and the migration to the new revocable sessions system was never made. Parse Server requires revocable sessions tokens and will fail when encountering legacy session tokens.
In the app settings panel, the Require revocable sessions setting was not enabled before the migration and users sessions were not migrated to the new system when switching to Parse Server. The result when trying to edit a user was a 400 Bad Request with the message cannot modify user xxxxx (Code: 206).
To fix the issue, I followed the Session Migration Tutorial provided by Parse which explain how to upgrade from legacy session tokens to revocable sessions. Multiple methods are described depending on your needs like enableRevocableSession() to enable these sessions on a mobile app, if you're only having a web app, you can enforce that any API requests with a legacy session token to return an invalid session token error, etc.
You should also check if you're handling invalid session token error correctly during the migration to prompt the user to login again and therefore obtain a new session token.
I had the same error and neither useMasterKey nor sessionToken worked for me either. :(
Here's my code:
console.log("### attempt 1 sessionToken: " + request.user.getSessionToken());
var p1 = plan.save();
var p2 = request.user.save(null, {sessionToken: request.user.getSessionToken()});
return Parse.Promise.when([p1, p2]).then(function(savedPlan) {
...
}
I see the matching session token in log output:
2016-08-21T00:19:03.318662+00:00 app[web.1]: ### attempt 1 sessionToken: r:506deaeecf8a0299c9a4678ccac47126
my user object has the correct ACL values:
"ACL":{"*":{"read":true},"PC7AuAVDLY":{"read":true,"write":true}}
I also see a bunch of beforeSave and afterSave logs with user being "undefined". not sure whether that's related.
beforeSave triggered for _User for user undefined:
I'm running latest parser-server version 2.2.18 on Heroku (tried it on AWS and results are the same)
function login(logInfo, callback) {
let username = logInfo.email,
password = logInfo.password;
Parse.User.logIn(username, password).then(
(user) => {
if(!user) {
callback('No user found');
} else {
callback(null, user);
}
},
(error) => {
callback(error.message, null);
}
);
}
function update(userInfo, data, callback) {
login(userInfo, (error, user) => {
if(error) {
callback('Can t find user');
} else {
getUpdatedData(user.get('data'), data, (error, updateData) => {
if(error) {
callback(error);
} else {
user.save({data: updateData}, /*{useMasterKey: true}*/ {sessionToken: user.get("sessionToken")}).then(
(test) => {
callback();
}, (err) => {
callback(error.message);
}
);
}
});
}
});
}
For some reason, retrying to use sessionToken worked.
This is not how asynchronous functions work in JavaScript. When createUser returns, the user has not yet been created. Calling user.save kicks off the save process, but it isn't finished until the success or error callback has been executed. You should have createUser take another callback as an argument, and call it from the user.save success callback.
Also, you can't create a user with save. You need to use Parse.User.signUp.
The function returns long before success or error is called.
By default all tokens generated with the Twilio helper libraries expire after one hour. But you should configure this expiration to be as short as possible for your application.
I am trying to generate a new token each time a user attempts a new connection and try to setup Twilio device. But it creates new device each time. So all Twilio device get incoming call and i can see multiple notification for that. Multiple connections created an dmultiple dtmf sent. I want only one twilio device with fresh token.
Twilio.Device.destroy() method is there but it is not working. What are other option do I have?
How to release/destroy/stop/delete Twilio.Device?
After saving credentials globalTwilioSagaSetup() called and after 58min of that again token is generated and Twilio.Device setup is done.
function globalTwilioSagaSetup()
{
// Get Twilio credentials
// Get Twilio Token
// Setup Twilio Device
// For token re-generation before expire. 58min
setInterval(function(){globalTwilioSagaSetup();},3480000);
}
I've had similar issue although in current version of twilio lib (i.e. 1.2). The thing is that once setup is called ready event is fired but only after first call to the setup method. It means that even if one will initialize device with new token there will be problems with establishing new connection. Therefore calling Twilio.Device.destroy() then setup and then connect (via ready event) solved that issue for me. Here is an example:
srv.connectToTwilio = () => $q((resolve, reject) => {
var connection;
try {
connection = Twilio.Device.connect();
} catch (err) {
$log.debug('Device.connect(): throw', err);
}
if (connection) {
try {
connection.accept(() => {
$log.debug(`Twilio connection.accept.`);
resolve();
});
} catch (err) {
$log.debug('connection.accept(): throw', err);
}
} else {
reject(`Device.connect() did not return connection`);
}
});
srv.connect = (token) => {
return srv.setToken(token).then(() => srv.connectToTwilio());
};
srv.disconnect = () => {
shouldBeConnected = false;
try {
Twilio.Device.activeConnection().disconnect();
} catch (error) {
$log.debug(error);
} finally {
Twilio.Device.destroy();
}
$log.debug(`Twilio disconnect.`);
};