I tried to run a script which creates a specific intent by using the Dialogflow API. The API is enabled already a couple of weeks.
After I called my script i received the following error:
Error: Dialogflow API has not been used in project usable-auth-library before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/dialogflow.googleapis.com/overview?project=usable-auth-library then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
at /Users/$USER"/Desktop/node_modules/grpc/src/client.js:554:15
code: 7,
metadata:
Metadata {
_internal_repr:
{ 'google.rpc.help-bin': [Array],
'grpc-status-details-bin': [Array],
'grpc-server-stats-bin': [Array] } } }
Here is my script.
// Imports the Dialogflow library
const dialogflow = require('dialogflow');
// Instantiates clients
const contextsClient = new dialogflow.ContextsClient();
const intentsClient = new dialogflow.IntentsClient();
projectId="1000-1505-ecom-conx-dev"
projectId="conrad-test"
// The path to identify the agent that owns the created intent.
const agentPath = intentsClient.projectAgentPath(projectId);
// Setup intents for ordering a pizza.
// First of all, let's create an intent that triggers pizza order flow.
// Output contexts for ordering pizza. They are used for matching follow-up
// intents. For pizza ordering intents, a "pizza" output context is used for
// hinting the conversation is about pizza ordering, not beer or something
// else. For the first intent, it returns responses asking users to provide
// size information, with a "size" output context for matching the intent
// asking for the size of the pizza.
// Note that session ID is unknown here, using asterisk.
const accountOutputContexts = [
{
name: contextsClient.contextPath(
projectId,
'*' /* sessionId */,
'EComerceAgent'
),
lifespanCount: 5,
},
];
// The result of the matched intent.
const accountResult = {
action: '',
parameters: [
{
displayName: 'Account for',
value: '$application',
entityTypeDisplayName: '#application',
mandatory: true,
prompts: [
'You need appliation access, please describe for which and which permissions do you need?',
'Would you like access to jirra?',
'Would you like access to confluence?',
'Would you like access to AEM?',
],
},
{
displayName: 'user',
value: '$user',
entityTypeDisplayName: '#user',
mandatory: true,
prompts: ['For wich user'],
isList: true,
},
{
displayName: 'permission',
value: '$permission',
// The API provides a built-in entity type #sys.address for addresses.
entityTypeDisplayName: 'permission',
mandatory: true,
prompts: ['Which permission do you need?'],
},
],
messages: [
{
text: {
text: [
'No problem. We will create an account on $application for $user with the following permission: $permission'
],
},
},
{
text: {
text: [
'Reply "check" to place your order. Reply "cancel" to cancel ' +
'your order. You can change your delivery address as well.',
],
},
},
{
quickReplies: {
title:
'No problem. We will create an account on $application for $user with the following permissions: $permission',
quickReplies: ['Create account', 'Cancel']
},
platform: 'PLATFORM_UNSPECIFIED',
},
],
outputContexts: accountOutputContexts,
};
// The phrases for training the linguistic model.
const accountPhrases = [
{type: 'TYPE_EXAMPLE', parts: [{text: 'Get account'}]},
{type: 'TYPE_EXAMPLE', parts: [{text: 'acction'}]},
{
type: 'TYPE_EXAMPLE',
parts: [
{text: 'Create an account '},
{text: 'for', entityType: '#application', alias: 'application'},
{text: ' '},
{text: 'for ', entityType: '#user', alias: 'user'},
{text: 'with the followin permissions ', entityType: '#permission', alias: 'permission'},
],
},
{
type: 'TYPE_EXAMPLE',
parts: [
{text: "I'd like to have access "},
{text: 'to', entityType: '#application', alias: 'application'},
],
}
];
// The intent to be created.
const accountIntent = {
displayName: 'Account',
events: ['create_account'],
// Webhook is disabled because we are not ready to call the webhook yet.
webhookState: 'WEBHOOK_STATE_DISABLED',
trainingPhrases: accountPhrases,
mlEnabled: true,
priority: 500000,
result: accountResult,
};
const accountRequest = {
parent: agentPath,
intent: accountIntent,
};
// Create the pizza intent
intentsClient
.createIntent(accountRequest)
.then(responses => {
console.log('Created account intent:');
logIntent(responses[0]);
})
.catch(err => {
console.error('ERROR:', err);
});
The error is not clear at all, it means that your are not authorized to access the remote ressource without credentials. Here is the quick solution that worked for me :
Go to https://console.cloud.google.com/apis/credentials/serviceaccountkey
Download json auth file (eg. foobar-123.json)
Add environement variable :
export GOOGLE_APPLICATION_CREDENTIALS="/home/me/secure/foobar-123.json"
And the full tutorial & documentation here : https://cloud.google.com/docs/authentication/getting-started
I reported this issue : https://github.com/dialogflow/dialogflow-nodejs-client-v2/issues/28
Related
My goal to create a looping process like this:
A user clicks button in a card in the chat launching a task module
A user submits task module inserting card in chat
Another user starts process over with that card
The sample code below allows the following flow, which breaks down at 4:
User A select a Messaging Extension action –composeExtension/fetchTask - app launches a task module
User A submits task module - composeExtension/submitAction - app adds card to compose box of chat
User B selects button of card in chat - task/fetch - app launches task module
User B submits task module - task/submit - app is unable to add card since this is not a Messaging Extension handler
Note that steps 1 & 2 can be replaced by, for example, a link unfurl.
What is the lowest friction way to allow a user at step 4 to submit a card back to the compose box / chat? Ideally this doesn't require the user to specifically authorize the app in the specific conversation; the user's action shows their intent.
class SimpleTeamsBot extends TeamsActivityHandler {
// Handle 'composeExtension/fetchTask'
handleTeamsMessagingExtensionFetchTask = async (
context: TurnContext,
taskModuleRequest: TaskModuleRequest
): Promise<MessagingExtensionActionResponse> =>
Promise.resolve({
task: {
type: "continue",
value: {
card: getTaskModuleCard(),
title: "Task Module",
},
},
});
// Handle 'composeExtension/submitAction'
handleTeamsMessagingExtensionSubmitAction = async (
context: TurnContext,
taskModuleRequest: TaskModuleRequest
): Promise<MessagingExtensionActionResponse> =>
Promise.resolve({
composeExtension: {
attachmentLayout: "list",
type: "result",
attachments: [getChatCard()],
},
});
// Handle 'task/fetch'
handleTeamsTaskModuleFetch = async (
context: TurnContext,
taskModuleRequest: TaskModuleRequest
): Promise<TaskModuleResponse> =>
Promise.resolve({
task: {
type: "continue",
value: {
card: getTaskModuleCard(),
title: "Task Module",
},
},
});
// Handle 'task/submit'
handleTeamsTaskModuleSubmit = async (
context: TurnContext,
taskModuleRequest: TaskModuleRequest
): Promise<TaskModuleResponse> =>
Promise.resolve({ task: { type: "message", value: "Thanks!" } });
// Link Unfurling.
handleTeamsAppBasedLinkQuery = async (
context: TurnContext,
query: AppBasedLinkQuery
): Promise<MessagingExtensionResponse> =>
Promise.resolve({
composeExtension: {
attachmentLayout: "list",
type: "result",
attachments: [getChatCard()],
},
});
}
const getChatCard = () =>
CardFactory.heroCard(
"Title",
"Subtitle",
[],
[
{
type: "invoke",
title: "Launch",
value: {
type: "task/fetch",
data: {},
},
},
]
);
const getTaskModuleCard = () =>
CardFactory.adaptiveCard({
type: "AdaptiveCard",
actions: [
{
type: "Action.Submit",
title: "Submit",
},
],
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
version: "1.4",
});
I want to intercept all the messages exchanged between user and bot. PFB code that I have used to intercept. I am using Bot Diaolog framework for webchat. Here the issue is I am not able to extract values from activities object which contains messages sent from bot to user.
adapter.use(async (turnContext, next) => {
// pre-processing of the current incoming activity
console.log("adapture use::turnContext::" + turnContext.activity.text);
// hook up a handler to process any outgoing activities sent during this turn
turnContext.onSendActivities(async (sendContext, activities, nextSend) => {
// pre-processing of outgoing activities
await nextSend();
console.log("adapture use::activities::POST##::" + flat.stringify(activities));
// post-processing outgoing activities
});
await next();
// post-processing of the current incoming activity
console.log(`Processing activity ${turnContext.activity.id} finishing. `);
});
To access the bot's responses, you can update the bot.js file (i.e. the file with the activity handlers defined) to something like the below.
In short, the onSendActivities() method keeps an array of the activities that have been passed in. You can cycle thru the activities pushing them into an array and then acting on a particular one. Or, react as each arrives. Further below are examples of each output.
const { ActivityHandler, MessageFactory } = require('botbuilder');
class EchoBot extends ActivityHandler {
constructor() {
super();
[...]
const transcript = [];
this.onTurn(async (context, next1) => {
let responses = {};
logActivity(transcript, cloneActivity(context.activity));
context.onSendActivities(async (ctx, activities, next2) => {
responses = await next2();
activities.forEach((a) => logActivity(transcript, cloneActivity(a)));
console.log('TRANSCRIPT ', activities);
return responses;
});
await next1();
});
}
}
const logActivity = (transcript, activity) => {
if (!activity.timestamp) {
activity.timestamp = new Date();
}
transcript.push(activity);
};
const cloneActivity = (activity) => {
return Object.assign({}, activity);
};
module.exports.EchoBot = EchoBot;
Logging the transcript array: Shows the list of activities.
[
{
type: 'conversationUpdate',
id: '1hEUP37Da8S',
timestamp: 2021-09-07T23:01:04.910Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: { id: 'dl_16310556645490.nc93iu9jr1' },
conversation: { id: '5JgOxxxxxxxxxxxxv6sw-g' },
recipient: { id: 'somebot#QaeuoeEamLg', name: 'Some Bot' },
membersAdded: [ [Object], [Object] ],
rawTimestamp: '2021-09-07T23:01:04.9109865Z',
callerId: 'urn:botframework:azure'
},
{
type: 'message',
text: 'Hello and welcome!',
inputHint: 'acceptingInput',
speak: 'Hello and welcome!',
channelId: 'directline',
locale: undefined,
serviceUrl: 'https://directline.botframework.com/',
conversation: { id: '5JgOxxxxxxxxxxxxv6sw-g' },
from: { id: 'somebot#QaeuoeEamLg', name: 'Some Bot' },
recipient: { id: 'dl_16310556645490.nc93iu9jr1' },
timestamp: 2021-09-07T23:01:06.547Z
},
{
type: 'message',
id: '5JgOxxxxxxxxxxxxv6sw-g|0000001',
timestamp: 2021-09-07T23:01:08.704Z,
localTimestamp: 2021-09-07T23:01:08.527Z,
localTimezone: 'America/Los_Angeles',
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: { id: 'dl_16310556645490.nc93iu9jr1', name: '' },
conversation: { id: '5JgOxxxxxxxxxxxxv6sw-g' },
recipient: { id: 'somebot#QaeuoeEamLg', name: 'Some Bot' },
textFormat: 'plain',
locale: 'en-US',
text: 'Hi',
entities: [ [Object] ],
channelData: {
clientActivityID: '1631055668527tzwhm47a4qd',
clientTimestamp: '2021-09-07T23:01:08.527Z'
},
rawTimestamp: '2021-09-07T23:01:08.704318Z',
rawLocalTimestamp: '2021-09-07T16:01:08.527-07:00',
callerId: 'urn:botframework:azure'
},
{
type: 'message',
text: 'Echo: Hi',
inputHint: 'acceptingInput',
speak: 'Echo: Hi',
channelId: 'directline',
locale: 'en-US',
serviceUrl: 'https://directline.botframework.com/',
conversation: { id: '5JgOxxxxxxxxxxxxv6sw-g' },
from: { id: 'somebot#QaeuoeEamLg', name: 'Some Bot' },
recipient: { id: 'dl_16310556645490.nc93iu9jr1', name: '' },
replyToId: '5JgOxxxxxxxxxxxxv6sw-g|0000001',
timestamp: 2021-09-07T23:01:09.147Z
}
]
Logging activities only: Shows the last item to have passed thru from either the bot or user.
[
{
type: 'message',
text: 'Echo: Hi',
inputHint: 'acceptingInput',
speak: 'Echo: Hi',
channelId: 'directline',
locale: 'en-US',
serviceUrl: 'https://directline.botframework.com/',
conversation: { id: 'AURKxxxxxxxxxJHpO-f' },
from: { id: 'somebot#QaeuoeEamLg', name: 'Some Bot' },
recipient: { id: 'dl_16310605730140.3nrm4evq6um', name: '' },
replyToId: 'AURKxxxxxxxxxJHpO-f|0000001'
}
]
Thank you Rajeesh Menoth. Posting your suggestions as an answer to help other community members.
For updating existing message we can pass NewActivityObject with the existing activity ID to the UpdateActivity method of the TurnContext Object
Below is the sample to pass new object ID
const newActivity = MessageFactory.text('The new text for the activity');
newActivity.id = activityId;
await turnContext.updateActivity(newActivity);
To update the existing card on button selection, you can use ReplyToId of incoming activity.
const message = MessageFactory.attachment(card);
message.id = context.activity.replyToId;
await context.updateActivity(message);
For Further information check Update Messages.
I am using docusign (production account) to allow users to sign documents. I am trying to add data to textTabs that I create through the docusign dashboard. So say if I add a text box and call it nbShares, the below code does not populate the box. All the boxes I add in the dashboard such as signature, text, checkboxes etc are not shown in the generated link. I get no API errors either. I also tried to create customFields, and pass data to them as textTab - however this did not work either.
I think I may misunderstand the flow, I add all recipients and signers programatically - is that why I cant see the placeholders/buttons I add? I have also allowed collaboration in the fields I add, made them mandatory - yet they still do not appear.
Would really appreciate some help on this. This is my envelope definition - Im using the node sdk along with ts types.
const makeEnvelopeDefinition = async (
templateName: string,
user: User,
dealId?: string,
): Promise<EnvelopeDefinition> => {
const personToSign: Signer = {
email: user.email,
name: user.name,
roleName: 'Signer',
// Should this work?
tabs: {
textTabs: [
{ tabLabel: 'nbShares', value: '1000' },
],
},
clientUserId: DOCUSIGN_CLIENT_USER_ID,
recipientId: '1',
}
const compositeTemplate: CompositeTemplate = {
serverTemplates: [
{ sequence: '1', templateId: 'remote-template-id' },
],
inlineTemplates: [
{
sequence: '1',
recipients: {
signers: [personToSign],
certifiedDeliveries: [
{
email: 'someemail#something.com',
recipientId: '77',
name: 'Receipt of transaction',
},
{
email: user.email,
recipientId: '771',
name: user.name,
},
],
},
},
],
}
// create the envelope definition
const envelope: EnvelopeDefinition = {
emailSubject: 'Review signed document',
status: 'sent',
compositeTemplates: [compositeTemplate],
}
return envelope
}
For me the solution was changing my signer to this
const personToSign: Signer = {
email: user.email,
name: user.name,
clientUserId: await getDocusignConfig().DOCUSIGN_CLIENT_USER_ID,
recipientId: '1',
routingOrder: '1', // Was missing this
roleName: 'Signer', // Must match signer specified on docusign dashboard
tabs: {
textTabs: [
{
xPosition: '150',
yPosition: '200',
name: 'First Name',
tabLabel: 'name', // Must match the dataLabel name specified on docusign
value: 'Test',
},
],
},
}
And enabling this setting 'When an envelope is sent, write the initial value of the field for all recipients' in 'Signing Settings' on docusign.
When I need a simple response having suggestion buttons, the following code works totally fine with v2 of the AoG SDK:
conv.ask(new SimpleResponse({
speech: 'blabla',
text: 'display text',
}), new Suggestions(['Suggestion 1', 'Suggestion 2']));
But: There are cases where I have the complete response prepared as JSON saved in an external file and want to use this as input parameter for ask():
conv.ask({
simpleResponse: {
textToSpeech: 'blabla',
displayText: 'display text',
},
suggestions: [
{ title: 'Suggestion 1' },
{ title: 'Suggestion 2' },
],
});
The problem is that the suggestion buttons are not displayed.
I am using TypeScript and the object passed to ask() has a valid Response type.
In v1 of the AoG SDK I could do the following (the argument ask() gets is no valid Response object and does not work in v2):
app.ask({
items: [{
simpleResponse: {
textToSpeech: 'blabla',
displayText: 'display text',
}
}],
suggestions: [
{ title: 'Suggestion 1' },
{ title: 'Suggestion 2' },
]
});
Does anyone know how I could do that for v2?
You can use the RichResponseOptions type to store a RichResponse as pure JSON using the raw GoogleActionsV2RichResponseItem type for RichResponse items.
response.json
{
"items": [
{
"simpleResponse": {
"textToSpeech": "blabla",
"displayText": "display text"
}
}
],
"suggestions": [
"Suggestion 1",
"Suggestion 2"
]
}
app.js
const { dialogflow, RichResponse } = require('actions-on-google')
// load the response as JSON
const response = require('./response.json')
const app = dialogflow()
app.intent('Default Welcome Intent', conv => {
conv.ask(new RichResponse(response))
})
By sending anonymous objects into conv.ask, the library interprets them as raw RichResponse items. So it does work when you are sending SimpleResponses. To send multiple raw RichResponse items, you would need separate elements for each item and call conv.ask multiple times or spread them into a single conv.ask.
conv.ask({
simpleResponse: {
textToSpeech: 'blabla',
displayText: 'display text',
}
})
conv.ask({
simpleResponse: {
textToSpeech: 'blabla',
displayText: 'display text',
}
})
But as you noticed, suggestions are not RichResponse items as they are directly part of the RichResponse object itself. You need to construct the entire RichResponse object in order to put suggestions in.
You can find the entire list of currently valid raw values for RichResponse items here.
Lets say i have the following model schema described as below to add some chat functionality between 2 users in my sails app. For instance if i have user A sending a message to user B, then user B will reply back to user A which will always create 2 conversations between both users. So my question is how can i query both conversations to get messages from A and B. I tried something like this but maybe theres a simple logic.
// User Model
attributes: {
username: {
type: 'string',
required: true,
unique: true,
},
conversations_sender: {
collection: conversation,
via: 'sender'
},
conversations_recipient: {
collection: conversation,
via: 'recipient'
}
}
// Conversation model
attributes: {
sender: {
model: user
},
recipient: {
model: user
},
messages: {
collection: 'message',
via: 'conversation'
}
}
// Message model
attributes: {
text: {
type: 'string'
},
conversation: {
model: 'conversation'
},
}
// Conversation Controller
get: function(req, res) {
var params = {
or : [
{
sender: req.param('sender'),
recipient: req.param('recipient')
},
{
sender: req.param('recipient'),
recipient: req.param('sender')
}
]
}
Conversation.find(params) ...
}
You should rethink your schema a bit, see this link that has a good database design for your needs:
http://www.9lessons.info/2013/05/message-conversation-database-design.html
You should be able then to fetch all the messages with the 2 user ids like this:
Conversation.findAll({sender: ..., receiver: ...})
Also you will need a timestamp for the messages, in the future you'll want to sort them somehow and also make the nice 'Read yesterday' feature