I am having difficulty getting twilio flex to show messages created using the API using node.js.
I am creating a channel, adding a member, creating a message on the channel using the member.
The flex dashboard shows the incoming chat request in the task list, I can answer the chat request but none of the messages I save to the channel are show.
The interesting thing is if I use the twilio-flex-webchat.min.js script and initiate a chat from a web page and then get the ChannelSid for that conversation (using https://chat.twilio.com/v2/Services/ISXXXX/Channels) I can use the APIs to create messages for this channel and they appear on the flex dashboard. But I need all this to work via node.js.
I compared the task, channel, reservation, member and message twilio objects both for chat conversations using the twilio-flex-webchat.min.js web library and the objects created by the node.js code. I cannot find any notable difference.
Does anybody have any insights?
Here is my code.
const accountSid = 'ACXXXXXXXXXX';
const authToken = 'XXXXXXXXXXXXXXXX';
const workspaceSid = 'WSXXXXXXXXXXXXXXXx';
const workFlowSid = 'WWXXXXXXXXXXXXXXXXXXXXXX';
const serviceSid = 'ISXXXXXXXXXXXXXXXXXXXXXX';
const client = require('twilio')(accountSid, authToken);
(async () => {
//create channel
let channel = await client.chat.services(serviceSid)
.channels
.create({
attributes: JSON.stringify({
status: "ACTIVE",
from: "Some Person",
channel_type: "web"
}),
workflowSid: workFlowSid,
taskChannel: 'chat',
friendlyName: 'Flex WebChat',
type: 'private'
});
//create a member in this channel
let member = await client.chat.services(serviceSid)
.channels(channel.sid)
.members
.create({ identity: 'WEB_CLIENT' });
//post a message to this channel from the member
let message = await client.chat.services(serviceSid)
.channels(channel.sid)
.messages.create({ body: 'This is a test message', to: channel.sid, from: 'WEB_CLIENT' });
//create a task for my programable chat channel and associate the channel sid for my current conversation
let task = await client.taskrouter.workspaces(workspaceSid)
.tasks
.create({
attributes: JSON.stringify({
channelSid: channel.sid,
channelType: "web"
}),
workflowSid: workFlowSid,
taskChannel: 'chat',
});
})();
Thanks
Before adding a message and creating a Task, you need to create a proxy session, add the members as participants and then update the channel attributes:
{
"status": "ACTIVE",
"forwarding": true,
"twilioNumber": firstParticipant.proxyIdentifier,
"serviceNumber": someIdentity,
"from": from,
"channel_type": "sms",
"proxySession": session.sid
}
Related
I am using botframework v4, but coming over from v3, I have not found any documentation that is similar to the code I use below but for v4, regarding sending proactive messages from Azure Function App
Below is the code I previously used but am having trouble adapting:
var builder = require('botbuilder');
// setup bot credentials
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
module.exports = function (context, req) {
if (req.body) {
var savedAddress = req.body.channelAddress;
var inMemoryStorage = new builder.MemoryBotStorage();
var bot = new builder.UniversalBot(connector).set('storage', inMemoryStorage);
sendProactiveMessage(savedAddress, bot)
}
};
function sendProactiveMessage(address, bot) {
var msg = new builder.Message().address(address);
msg.textLocale('en-US');
var img = {
attachments: [{
contentType: "image/jpg",
contentUrl: latestUrl,
}]
};
msg.addAttachment(img.attachments[0]);
msg.text('hello');
bot.send(msg);
}
This works fine with v3 but not v4.
If possible I would also like to find a way to log a user out:
await botAdapter.signOutUser(innerDc.context, this.connectionName);
This is how I do it in the bot itself, but doing so from Azure Functions again is proving difficult.
Any help would be appreciated.
Great that you are making the move from v3 to v4! Have you had a look at Send proactive notifications to users? This example is pretty straight forward and can be used within an Azure function.
First you retrieve the Conversation Reference in your bot by calling TurnContext.getConversationReference(context.activity);. This is the reference you could use in your proactive function to open the conversation. In your case you provide that via the request body to a proactive function, so I will do the same in my example.
My proactive endpoint example is written in Typescript, however it works the same way in plain Javascript. Create a HTTP trigger in Azure Functions and use the following code. I have added comments inline for clarity.
const { BotFrameworkAdapter } = require('botbuilder');
// Create adapter.
// If you need to share this adapter in multiple functions, you could
// instantiate it somewhere else and import it in every function.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
module.exports = async function (context, req) {
// Validate if request has a body
if (!req.body) {
context.res = {
status: 400,
body: "Please pass a conversation reference in the request body"
};
return;
}
// Retrieve conversation reference from POST body
const conversationReference = req.body;
// Open the conversation and retrieve a TurnContext
await adapter.continueConversation(conversationReference, async turnContext => {
// Send a text activity
// Depending on the channel, you might need to use https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity('Proactive hello');
});
context.res = {
body: 'Message sent!'
};
};
In the end you could make a request to this Azure Function, where you pass the Conversation Reference as body of the type application/json.
Extending this example with features like signOutUser is simple. You can call all functions within the continueConversation function, just as in a normal bot. You can even receive the adapter object there if you wish.
await adapter.continueConversation(conversationReference, async turnContext => {
// Sign user out
turnContext.adapter.signOutUser(turnContext, 'connection-name');
});
I have a TEAMS node.js bot running locally (with ngrok). I receive messages from TEAMS client and echo works
context.sendActivity(`You said '${context.activity.text}'`);
Now I want to send a 1to1 message to this user, but I receive
Error: Authorization has been denied for this request
when creating a conversation.
My code:
var sUserId = "29:1shb_5I6CkkerBVq4qPqcv5dGwDfkXx11Jbjc1UnGCIv"
var sServiceUrl = "https://smba.trafficmanager.net/emea/";
var sTenantId = "942369d2-208e-438b-894c-0d0e1510cf61";
var credentials = new BotConnector.MicrosoftAppCredentials({
appId: "xxxxxxx",
appPassword: "yyyyyyyy"
});
var connectorClient = new BotConnector.ConnectorClient(credentials, { baseUri: sServiceUrl });
const parameters = {
members: [ { id: sUserId } ],
isGroup: false,
channelData:
{
tenant: {
id: sTenantId
}
}
};
var conversationResource = await connectorClient.conversations.createConversation(parameters);
// I get the error here, next is not executed
await connectorClient.conversations.sendToConversation(conversationResource.id, {
type: "message",
from: { id: "xxxxxxx" },
recipient: { id: sUserId },
text: 'This a message from Bot Connector Client (NodeJS)'
});
appId & appPassword are valid (from .env file), if they are wrong I can not receive messages from TEAMS client
I have the same code to create a conversation in a .NET bot and it works for me:
var parameters = new ConversationParameters
{
Members = new[] { new ChannelAccount(sUserId) },
ChannelData = new TeamsChannelData
{
Tenant = new TenantInfo(sTenantId),
},
};
retValue = await connectorClient.Conversations.CreateConversationAsync(parameters);
What is wrong in my node.js code?
Thanks,
Diego
Have you trusted the service? It don't think so based on your code, and it's a classic cause of 401 in your case.
In node.js, do the following:
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
If you want more details around that, have a look to the documentation about getting 401 when sending proactive messages here
And also this SO answer about Teams and Proactive messaging, in particular last block.
Proactive messaging bot in Teams without mentioning the bot beforehand
Currently I have a webhook which is being called from Dialogflow (fulfillment) and checks which intent is currently applicable. (I do everything via JSON) once I know which Intent, I send back (via the webhook) a JSON response. I was wondering if it is possible to initiate a JSON object and send it to Dialogflow. (I want to send context without letting the user type first and Dialogflow receiving a fallback) (NodeJS Server -> Dialogflow) Currently the only way I can do it is (Dialogflow -> NodeJS Server (JSON object with contex) -> Dialogflow)
CURRENT:
var dialog_flow = {
// Create context using Google dialogflow
createContext: async function(projectId, sessionId, contextId, parameters, lifespanCount){
console.log("params to be sent: ", parameters);
//Instantiates client to create the actual context before sending
const contextsClient = new dialogflow.ContextsClient({
keyFilename: './dialogflow_credentials.json'
});
const sessionPath = contextsClient.sessionPath(projectId, sessionId);
const contextPath = contextsClient.contextPath(
projectId,
sessionId,
contextId
);
const createContextRequest = {
parent: sessionPath,
context: {
name: contextPath,
lifespanCount: lifespanCount,
parameters: parameters
},
};
const response = await contextsClient.createContext(createContextRequest);
console.log(`Created ${response[0].name}`);
}
};
PARAMETERS:
let testobj = {
firstname: 'test name',
lastname: 'itworks!!',
dd: 'fgfgf',
ff: 'fgfg'
}
You can call directly Dialogflow from Node.js, without the need of receiving a webhook. For that you will need to use the dialogflow package.
const sessionClient = new dialogflow.SessionsClient();
const session = sessionClient.sessionPath(projectId, sessionId);
const request = {
session,
queryInput: {
text: {
text: 'Some text'
}
},
queryParams: {
contexts: [context] // here you send the data
}
};
sessionClient.detectIntent(request)
.then(console.log)
.catch(console.error);
In order to send a context, with data, you will need to use dialogflow.ContextsClient
I give a detailed explanation about that in these other questions:
set input or output context dialogflow nodejs v2
Dialogflow pass parameters through NodeJS
And in order to authenticate the client you can check:
Dialogflow easy way for authorization
UPDATE: 2 weeks ago, Dialogflow, removed the structjson.js sample from the repository, but in favor of an npm package to handle the encoding/decoding of the protobuf struct: pb-util
I'm implementing a new channel for my bot's front end using the botframework-directlinejs NodeJS SDK. This channel will provide some custom back-channel functionality; however, my bot needs to know that the conversation it is communicating with is via this channel before it can construct an activity to use it.
From what I can gather from the 'Activity' object in the API, the channelId field should be set by the channel.
However,
myChannel.postActivity({
type: 'message',
text: 'hi',
from: {
id: "Node test user",
},
channelId: 'myChannel'
}).subscribe(
id => console.log("Posted activity, assigned ID ", id),
error => console.log("Error posting activity", error)
);
indeed sends the message 'hi' to my bot, but the channelId comes out as 'directline'.
Performing this same operation in Fiddler as a post to https://directline.botframework.com/v3/directline/conversations/<conversationID>/activities has the same response.
My suspicion is that the 'channelId' property of the Activity object is read-only, and that the API adds this value.
Is it possible to set a custom id of the channel?
No, it is not possible to set a custom channel id. There is a corresponding connector service for each channel type. If you're using Direct Line, the channelId should be directline.
You can send custom information through channel data though:
BotChat.App({
botConnection: Object.assign({}, dl, {
postActivity: activity => {
var newActivity = Object.assign({}, activity, { channelData: { "MyKey": "MyValue" } });
return dl.postActivity(newActivity);
}
}),
bot: bot,
user: user,
resize: 'detect',
}, document.getElementById('bot'));
Most node.js botframework examples involve the notion of the waterfall dialog model, which is a great way to structure conversations, but we don't need that since we have our own chat system. We simply receive messages via the webhook, process them and respond without the dialog system.
Also, and now we are getting to the heart of the matter;), the examples I have seen communicate back to the botframework in the web context:
var connector = new builder.ChatConnector({config});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());
bot.dialog('/', (session, args) => {
session.sendTyping();
session.send( 'Echo'+ session.message.text);
})
The above example simply responds with an 'echo' and before that set's the typing status.
Our system works asynchronous, the way we currently work is by bypassing the connector listen and dialog scheme. A simplified example of how we queue a botframework message.
server.post('/api/messages/:name', (req, res, next)=>{
queue.post('botframework',req.params.name,req.body)
.then(()=>res.sendStatus(200))
});
In the queue processing, we construct the botframework objects:
//the ':name' from the snippet above is used to identify
//the bot a retrieve credentials
const connector = new botbuilder.ChatConnector({
appId: bot.properties.botframework_id.value.appId,
appPassword: bot.properties.botframework_id.value.appPassword
});
const username=message.from.name.replace(/[\s-;:"';$]/g,"_")+"_skype";
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let msg = new botbuilder.Message();
//processing...
//create the outgoing message...
bot.send(msg);
The problem here for us is that we simply don't know how to create a session object from a raw webhook message which is needed for the typing indicator and to ensure the order of messages when many messages are sent in quick succession.
Here is what we wish to accomplish:
//the ':name' from the snippet above is used to identify
//the bot a retrieve credentials
//the context is non HHTP
const connector = new botbuilder.ChatConnector({
appId: bot.properties.botframework_id.value.appId,
appPassword: bot.properties.botframework_id.value.appPassword
});
const username=message.from.name.replace(/[\s-;:"';$]/g,"_")+"_skype";
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let session = bot.createSession();
session.sendTyping();
let message = getNextMessageFromChatServer(message);
session.send(message);
//more messages to be send?
//....
So the question: How can we create a session object from the raw data send to the botframework webhook?
You should be able to build a session using loadSession
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let msg = new botbuilder.Message();
bot.loadSession(address, (err, session) => {
session.send("Message");
})