We have built a teams app that can be used in the group chat. So, basically any user can do
#
At the server side, we want to get the sending user and respond to the sent text based on who sent it. The code to get users in the conversation looks like below:
const connector = context.adapter.createConnectorClient(context.activity.serviceUrl);
const response = await connector.conversations.getConversationMembers(context.activity.conversation.id);
functions.logger.log("conversation members are:", response)
The response returns an array of all the users in the conversation with below structure
[
{
"id": "29:1a-Xb7uPrMwC2XqjMEHCC7ytV2xb2VUCqTA-n_s-k5ZyMCTKIL-ku2XkgbE167D_5ZbmVaqQxJGIQ13vypSqu-A",
"name": "Neeti Sharma",
"objectId": "718ab805-860c-43ec-8d4e-4af0c543df75",
"givenName": "Neeti",
"surname": "Sharma",
"email": "xxx#xxxx.xxx",
"userPrincipalName": "xxxx#xxxx.xxx",
"tenantId": "xxx-xx-xx-xxxxxx-x",
"userRole": "user"
},
{
...
}
]
The above response does not indicate who is the sender of the message in the group chat. How do we find that?
I'm not sure the exact syntax for Node (I work mostly in C#), but basically on the context.activity object there is a from property (i.e. context.activity.from), which is of type ChannelAccount (DotNet reference here, but it's very similar for Node). That will give you, at least, Name and AadObjectId. What you're using right now is getConversationMembers, which gives you everyone in the entire Channel, not just that particular message/thread.
turnContext.Activity.From.Id is also unique to each user. You can use that property too. Email is tough to get in any other events than the MembersAdded event.
Related
I'm building an API in express and I have a few routes. I am also using express-validator to validate what the client sends. This gives an error if the client sends the right keys and the values for those keys fail the validation defined from the express-validator schema.
However, the issue I'm having now, is that I can't really check whether the client is sending a random key.
Here's an illustration. API can accept the following:
{
"firstName": "John",
"lastName": "Doe",
"email": "johndoe#example.com"
}
Validator will check if firstName, lastName, and email are valid and accept the data.
Now let's say I send the following:
{
"firstName": "John",
"lastName": "Doe"
}
This also works because the validator only requires firstName and lastName. email is optional, so it still goes through.
Now, let's try something else:
{
"firstName": "234839248923",
"lastName": "Doe",
"email": "johndoe#gmail.com"
}
In the above, the validation fails, and we get an error because firstName can't be just numbers.
So far, so good.
Now, here's another case:
{
"firstName": "John",
"lastName": "Doe",
"email": "johndoe#gmail.com",
"randomKeyIDontNeed": "I don't need this"
}
The above works perfectly and goes through, even though there is the randomKeyIDontNeed which is sent from the client.
I can't find a way with express-validator to check if random keys are sent and give an error.
Is there a way to check that only those 3 keys are allowed, whether required or optional, and give the client an error if they send the wrong keys?
Here's one way I am thinking of doing it, but I'm not sure if that makes sense:
Have an array of keys which are allowed. e.g arr = ["firstName,"lastName", "email"]
Get all the keys from req.body.
Check if keys from req.body are in array arr. If not, return an error.
Is there a simpler, cleaner way of doing this?
Right now, whenever a random key is sent, it's not being used in the back-end and it doesn't really affect anything. So, is there even a point to validate the random keys?
What are the repercussions of not doing the validation for the random keys?
You may want to look into using express-joi-validation. It does validation on types for the given keys, and prevents the user from passing other values.
Here is a file I have for my validators for some Stripe routes
const Joi = require('joi');
exports.postPaymentMethod = Joi.object({
paymentMethodId: Joi.string().required()
});
exports.deletePaymentMethod = Joi.object({
paymentMethodId: Joi.string().required()
});
And here is how I use them.
const validator = require('express-joi-validation').createValidator({});
router.delete('/payment-methods', validator.body(deletePaymentMethod), ash(async (req, res) => {
// validated body is passed here, safe to use w/o lots of error checking
var paymentMethodId = req.body.paymentMethodId;
}));
I am using bot framework and I am saving the session.messageAddress so I can send the user a msg later. I want to track each unique user I get. In case if they send me another msg again I will know I have already received a msg from them earlier. What unique ID should I use to identify the user?
I was thinking of using session.message.address.conversation.id, is this correct?
Refer to https://learn.microsoft.com/en-us/azure/bot-service/bot-service-resources-identifiers-guide?view=azure-bot-service-3.0
Every bot and user has an account within each channel. The account contains an identifier (id) and other informative bot non-structural data, like an optional name.
Example: "from": { "id": "john.doe#contoso.com", "name": "John Doe" }
You should use session.message.user.id to identify the user. Using the conversation id won’t work because the user can start a new conversation with the bot by simply reloading the web chat page and a new conversation id will be generated.
Edit
I mistakenly wrote session.from.id to identify the user. The correct way to do it is session.message.user.id!
Say that the user John Doe is chatting to the bot through Skype then message.user.id = "john.doe#contoso.com" and message.user.name = "John Doe". And there you have it! your unique user id!
The session object will look like this:
"session":
{
/*...*/
"message":
{
/*...*/
"user":
{
"id": "john.doe#contoso.com",
"name": "John Doe"
}
}
}
The google assistant does not show the suggestion chips sent into the webhook response.
{
"fulfillmentText": "Some text",
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "What number ?"
}
}
],
"suggestions": [
{
"title": "One"
},
{
"title": "Two"
}
]
}
}
},
"followupEventInput": {
"name": "numbers",
"parameters": {
"param1": "this is it"
}
}}
The interesting thing is that if I remove the "followupEventInput" field, the suggestion chips are displayed.
Can someone give me a hint on this behaviour ?
The JSON you're sending back doesn't do what you likely want it to do.
The followupEventInput means that the Intent is triggered immediately, rather than sending back the rest of the reply (including the suggestions). Instead, the reply from the followup event is sent back.
It sounds like you want to send back a reply and then, no matter what the user says or selects, their message is sent to a specific action. Keep in mind that Dialogflow Intents are triggered based on the user's actions and shaped based on the contexts that might be set.
In this case, it sounds like you may want to set an outputContext to influence which Intents will be examined when collecting the user's response. You can then have an Intent that takes this as an input Context and matches the possible phrases. If you truly want to get whatever the user says in the reply, you can use a Fallback Intent with the input Context set appropriately.
While you can redirect to another Intent to send output, usually this is unnecessary. Remember that Intents best represent the user's input rather than your agent's output. Particularly if you're using your webhook to generate and send a reply - just send the reply.
I'm writing my first NodeJS app for Google Home (using DialogFlow - formerly API.ai).
I'm looking at the doc on this page: https://developers.google.com/actions/reference/v1/dialogflow-webhook
but I don't see any way to set session variables.
My current test program sets speech like this:
speechText = "I'm not sure that character exists!";
callback(null, {"speech": speechText});
In DialogFlow, my JSON after running looks like this, and it looks like maybe the "contexts" is where the session state would go?
{
"id": "3a66f4d1-830e-48fb-b72d-12711ecb1937",
"timestamp": "2017-11-24T23:03:20.513Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "test word",
"action": "MyAction",
"actionIncomplete": false,
"parameters": {
"WordNumber": "400"
},
"contexts": [],
"metadata": {
"intentId": "a306b829-7c7a-46fb-ae1d-2feb1c309124",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"webhookResponseTime": 752,
"intentName": "MyIntentName"
},
"fulfillment": {
"messages": [{
"type": 0,
"speech": ""
}]
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success",
"webhookTimedOut": false
},
"sessionId": "fe0b7d9d-7a55-45db-9be9-75149ff084fe"
}
I just noticed from a chat bot course that I bought that you can set up Contexts like this, but still not sure exactly how the contexts get set and passed back and forth between the response of one call of my program to the request in the next call of my program (defined via "webhook").
When I added the contexts above, DialogFlow wouldn't recognize my utterance any longer and was giving me the DefaultFallback response. When I remove them, my AWS Lambda get's called.
For starters, the documentation page you're looking at refers to a deprecated version of the API. The page that talks about the current version of the api (v2) is https://developers.google.com/actions/dialogflow/webhook. The deprecated version will only be supported for another 6 months or so.
You're on the right track using Contexts! If you were using Google's actions-on-google node.js library, there would be some additional options - but they all use Contexts under the scenes. (And since they do use Contexts under the scenes - you should make sure you pick Context names that are different from theirs.) You can also use the sessionId and keep track of things in a local data store (such as DynamoDB) indexed against that SessionID. But enough about other options...
A Context consists of three elements:
A name.
A lifetime - for how many messages from the user will this context be sent back to you. (But see below about re-sending contexts.)
An object of key-value strings.
You'll set any contexts in the JSON that you return as an additional parameter named contextOut. This will be an array of contexts. So your response may look something like this:
var speechText = "I'm not sure that character exists!";
var sessionContext = {
name: "session_variables",
lifespan: 5,
parameters: {
"remember": "one",
"something": "two"
}
};
var contextOut = [sessionContext];
var response = {
speech: speechText,
contextOut: context
};
callback(null, response);
This will include a context named "session_variables" that stores two such variables. It will be returned for the next 5 messages sent to your webhook. You can, however, add this to every message you send, and the latest lifetime and parameters will be the ones that are sent back next time.
You'll get these contexts in the JSON sent to you in the result.contexts array.
The "Context" field on the Intent screen is used for an additional purpose in Dialogflow beyond just preserving session information. This indicates that the Intent is only triggered if the specified Context exists (lifetime > 0) when the phrase tries to be matched with it (or when handling a fallback intent). If you're using a webhook, the "Context Out" field is ignored if you send back contexts yourself.
This lets you do things like ask a particular question and set a Context (possibly with parameters) to indicates that some answers should be understood as being replies to the question you just asked.
I want to access the context variables saved in watson conversation JSON through an APP using node.js.
I have tried saving the whole conversation log into cloudant and fetched it from there.
Is there a easier way to access the context variables? I am thinking of sending a http request to server to fetch the right variables (I dont know which variables to access).
Depending on your needs you can store context in the browser session. This is what the conversation-simple app does.. https://github.com/watson-developer-cloud/conversation-simple
In this case the JSON context object is passed down to the browser, and passed back up again with subsequent requests.
The alternative is to store this info in a custom store such as Cloudant
The response that one get from Conversation service is in JSON format. So you can take out any context value that are available in the "context" param of this JSON response. Following is a simple response from the Conversation service.
{
"intents": [],
"entities": [],
"input": {
"text": ""
},
"output": {
"text": ["Hello MJ! How can I help you today?"],
"nodes_visited": ["Conversation Start"],
"log_messages": []
},
"context": {
"username": "MJ",
"conversation_id": "5835fa3b-6a1c-4ec5-92f9-22844684670e",
"system": {
"dialog_stack": [{
"dialog_node": "Conversation Start"
}],
"dialog_turn_counter": 1,
"dialog_request_counter": 1,
"_node_output_map": {
"Conversation Start": [0]
}
}
}
}
You will have all your context variables in the context key of the response. If you check the context parameter of this response you will see the "username": "MJ" entry. This is a custom value that I have added to the service context. You can format this response and use it in your application as per your need.