How to check whether the user is a new user or not in #assistant/conversation package - actions-on-google

In the actions-on-google package I have used the following logic to check whether the user is a new user or not.
conv.user.last.seen
But in the new #assistant/conversation package i have used the same logic but it fails stating that the seen is not found. I tried the following logic which shows the current date and time which makes the logic to pass all time.
conv.user.lastSeenTime
Does anyone tried to show whether the user is a new or not in the new #assistant/conversation package?

I ‘m dealing with a similar issue of how to differentiate between new vs returning users with google console’s action builder. The Main invocation is the part of the conversation I'm using this in. (Brand new to the forum so forgive me if I didn’t grasp your question.) I used the same logic as you did, and it did deploy correctly for me using the cloud function's inline editor for the webhook. So I ‘m not entirely sure what’s going on but here are some resources. The following tutorial and code was helpful/ worked for me (https://youtu.be/nVbwk4UKHWw?t=700 )
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
const app = conversation({debug:true});
app.handle('greeting', conv => {
let message = 'Welcome to App';
if (conv.user.lastSeenTime) {
message = 'Welcome back to App!';
}
conv.add(message);
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

Related

Need to show a card widget and after some delay automatically show another card widget regarding google workspace add-on creation

I need to show homeCard() and after I need to show settingsCard() automatically. Since I coudn't find a right method in app-script documentation I need some help for do this task.
Here I provided the code
function nevigateToUserSelectionPage(e) {
var navigation = CardService.newNavigation();
var builder = CardService.newActionResponseBuilder();
var userSelectionCardNavigation = navigation.pushCard(settingsCard());
return builder.setNavigation(userSelectionCardNavigation).build();
}
function homeCard() {
builder = CardService.newCardBuilder();
section = CardService.newCardSection();
let participantsText = CardService.newTextParagraph()
.setText("<u>Home card here</u>");
let blink = CardService
.newImage()
.setImageUrl('https://res.cloudinary.com/deez2bddk/image/upload/v1646709349/icons8-dots-loading_x9q7jv.gif');
section.addWidget(blink);
section.addWidget(participantsText);
section.addWidget(AddSplah);
builder.addSection(section);
console.log('home card triggered!!!');
return builder.build();
}
function settingsCard() {
//const myTimeout = setTimeout(5000);
Utilities.sleep(10000);
builder = CardService.newCardBuilder();
section = CardService.newCardSection();
console.log('Settings card triggered!!!');
let participantsText = CardService.newTextParagraph()
.setText("<u>This is Settings Page....</u>");
section.addWidget(participantsText);
section.addWidget(getAuthenticationStepperImage());
builder.addSection(section);
return builder.build();
}
in code.gs file
function mainController() {
return homeCard();
}
Above code blocks I need to execute homeCard() function and then , settingsCard() but I can`t find a proper solution in workspace add-on creation documentation provided by google.
After doing some research, I think the CardService does not provide a method for non-interactive updates.
You can update the view based on user click interaction, as you can see in the Cats Quickstart. When the user clicks the cat image changes the image updates due the URL has a new parameter via new Date().getTime().
Apart from this, you have the triggers provided by Google, such as: homepageTrigger for common use case or onItemsSelectedTrigger specifically for Drive. You can review the full list here.
In summary: I think that what are you trying to achieve actually is not currently feasible within CardService.
If you wish Google adds some kind of time driven trigger to Google Workspace Add-ons, request it via this form.
Remember that in the actual state, HTML/CSS is not allowed, maybe this would be another possible path for your Feature Request.

Microsoft Bot Framework - Multi turn context is lost after the first interaction

I recently moved my code from SDK v3 to v4 and I am trying to take advantage of the multi-turn features.
I have looked over the samples from GitHub. The samples work well for multi-turn but one issue I noticed is that it recognizes the context only if the prompt is clicked immediately after the initial answer (with prompts) is shown.
I would like to be able to identify, at any given time that a prompt is clicked. I am storing all the previous prompts in the state object (dialogInstance.State) already. I have a custom host, which sends the replytoid and using that I can get the appropriate state.
The problem is though, I am not able to get to a point where I can use the dialoginstance.State.
The sample code uses the DialogExtensions class. The "DialogExtensions" class tries to gather the previous context by checking if the result from the ContinueDialogAsync method returns null or not.
DialogExtensions class with multi-turn
When there is no previous context (no previous answer with prompts), then the call to the ContinueDialogAsync returns a result with Empty Status.
I am thinking this where I need to check the dialogstate and if the new message refers to any of the old messages at any given point, it can then start to continue the old conversation.
I am not sure if that is even possible.
Any help/pointers would be appreciated.
thanks,
I eventually ended up implementing something that will work for custom bot host/direct channel bot client.
The whole point is that the call to the qnamaker api should happen with the old context object, whenever an option is chosen, even if it is out-of-context.
First let me explain how it works in the current version of the code.
The way the bot code was trying to solve multi turn dialog, was by storing the current answer if it had prompts/options and returning the conversation state in "waiting" mode. When the next question is received, it would automatically assume that the new question is part of the prompts/options of the old question. It would then pass the oldstate along to the QnAMaker.
What I noticed is that, even if the question in the second turn is not part of the prompts/options (something the user has typed manually and is a completely different question), it would still send the oldstate object to the QnAMaker.
The QnAMaker api call seem to ignore oldstate if the new question is not part of the prompts/options of the oldstate. It will work correctly by fetching the answer for the new question that was typed manually.
This was the key. If we can focus on what gets to the qnamaker then we can solve our original problem.
I realized that having the bot return a waiting state is only a mechanism to create a condition to extract the oldstate in the next turn. However, if I can rebuild the oldstate anytime when there is an option chosen, then the call to the qnamaker would work equally well.
This is what I have done now.
In my custom bot host code (which is a direct line client), I am sending the ReplyToID field populated with the original question whenever a prompt is clicked. Then in the bot code, I have changed it so that if there is a replytoid present, then build a new oldstate object with the data from the reply to id. Below is the QnABotState class that represents the oldstate. its a very simple class containing previous qna question id and the question text.
public int PreviousQnaId { get; set; }
public string PreviousUserQuery { get; set; }
QnABoState class
Now, the problem was the Activity object contains ReplyToId but does not contain ReplyToQuery (or something like that). Activity object is used to send data from bot client to the bot. So, either I would have to use a different field or send the PreviousUserQuery as an empty string. I had a hunch that it would work with just the previousqnaid.
//starting the process to get the old context (create an object that will hold the Process function's current state from the dialog state)
//if there is replyToId field is present, then it is a direct channel request who is replying to an old context
//get the reply to id from summary field
var curReplyToId = "";
curReplyToId = dialogContext.Context.Activity.ReplyToId;
var curReplyToQuery = "";
var oldState = GetPersistedState(dialogContext.ActiveDialog);
//if oldstate is null also check if there is replytoid populated, if it is then it maybe a new conversation but it is actually an "out of turn option" selection.
if (oldState == null)
{
if (!string.IsNullOrEmpty(curReplyToId))
{
//curReplyToId is not empty. this is an option that was selected out-of-context
int prevQnaId = -1;
int.TryParse(curReplyToId, out prevQnaId);
oldState = new QnABotState() { PreviousQnaId = prevQnaId, PreviousUserQuery = curReplyToQuery };
}
}
With that in place, my call to the qnamaker api would receive an oldstate object even if it is called out-of-context.
I tried the code and it worked. Not having the previous qna query did not make a difference. It worked with just the PreviousQnaId field being populated.
However, please note, this will not work for other channels. It would work for channels where you can set the ReplyToId field, such as the Direct Channel Client.
here is the code from my bot host:
// to create a new message
Activity userMessage = new Activity
{
From = new ChannelAccount(User.Identity.Name),
Text = questionToBot,
Type = ActivityTypes.Message,
Value = paramChatCode,// + "|" + "ShahID-" + DateTime.Now.TimeOfDay,
Id = "ShahID-" + DateTime.Now.TimeOfDay,
ChannelData = botHostId//this will be added as the bot host identifier
};
//userMessage.Type = "imBack";
if (paramPreviousChatId > 0)
{
//we have a valid replytoid (as a part of dialog)
userMessage.ReplyToId = paramPreviousChatId.ToString();
}

Office JavaScript API: selecting a range in Word for Mac

I'm working on a side project using the Microsoft Office JavaScript APIs. I have some functionality working to select a range in order to scroll to a particular position within a document. This works as expected in Office for the web, but in Office for Mac I get the following error when calling context.sync().then():
Unhandled Promise Rejection: RichApi.Error: ItemNotFound
I can't find any documentation on that particular error, and I'm not sure how to troubleshoot what I might be doing wrong. What am I missing? Like I said, this works in the web interface.
Here is minimal sample of code that demonstrates the problem:
function UI(context) {
this.context = context;
}
UI.prototype.initialize = function() {
var paragraphs = this.context.document.body.paragraphs;
this.context.load(paragraphs);
document.querySelector('button').addEventListener('click', () => {
this.context.sync().then(() => {
this.goToRange(paragraphs.items[0]);
});
});
};
UI.prototype.goToRange = function(range) {
range.select();
this.context.sync();
};
document.addEventListener('DOMContentLoaded', () => {
Office.onReady(() => {
Word.run(context => {
return context.sync().then(() => {
new UI(context).initialize();
});
});
});
});
The only thing I can think of is that maybe the reference to the paragraph client object becomes "stale" in some sense, perhaps based on some resource limits that are lower in the Mac application than in the online interface? (That would be counterintuitive to me, but it's the only thing that comes to mind.)
I think I figured out the problem. I stumbled upon a hint while putting together the minimum code sample in the question; I removed a little too much code at first and encountered the following error:
Unhandled Promise Rejection: RichApi.Error: The batch function passed
to the ".run" method didn't return a promise. The function must return
a promise, so that any automatically-tracked objects can be released
at the completion of the batch operation.
I believe the issue is that, at least in Word for Mac, you can't use the context object provided by Word.run in an asynchronous event listener. I'm guessing this is because, as the above error states, some state has been released after resolving the promise returned. I can get the code to work by adding a dedicated call to Word.run (and using the fresh context provided) inside the event listener.
It is still a little odd that it works just fine in the browser. Presumably, the same state is not released as aggressively in the browser-based version.

How to use actions.intent.DATETIME?

It is "clearly" defined in the documentation, but I can find no example of how to use actions.intent.DATETIME.
Please provide an example of what is needed in the 'action.json' file, and how my code can get the date and time provided using the assistant SDK helper. I haven't been able to figure out how to use actions.intent.___ at all!
At the simplest level, I want my code to know whether it is morning or evening for the person since I need to give different information in each case. Someone might want to do this to respond "Good morning" or "Good evening".
Also to do with intents, at a more complex level, I also want to know their approximate location (lat/long). I figured that once I know how to work with DATETIME, I'd be able to apply the same code pattern to use getDeviceLocation.
There is some code at https://github.com/actions-on-google/actions-on-google-nodejs that uses the DATETIME intent, but it asks the user for any time. I want to simply know what their current time is.
The DateTime intent can be invoked using the askForDateTime() method from the ActionsSdkApp class in our client library. Simply call the method from an intent, and pass it some queries which clarify the prompting. Then listen for the response using a listener for the actions.intent.DATETIME intent, just as you would listen for the actions.intent.TEXT intent.
In the handler for the actions.intent.DATETIME intent, you can use the getDateTime() method to retrieve the data. Unfortunately this intent only works by asking for an exact date and time from the user, and it is a generic date and time, so there is no guarantee that it is their current datetime unless you structure your prompts in a way to guide the user towards that.
const app = new ActionsSdkApp({ request, response });
function welcomeIntent (app) {
app.askForDateTime('When do you want to come in?',
Which date works best for you?',
'What time of day works best for you?');
}
function datetime (app) {
app.tell({speech: 'Great see you at your appointment!',
displayText: 'Great, we will see you on '
+ app.getDateTime().date.month
+ '/' + app.getDateTime().date.day
+ ' at ' + app.getDateTime().time.hours
+ (app.getDateTime().time.minutes || '')});
}
const actionMap = new Map();
actionMap.set(app.StandardIntents.MAIN, welcomeIntent);
actionMap.set(app.StandardIntents.DATETIME, datetime);
app.handleRequest(actionMap);
As you mentioned you can invoke and handle the actions.intent.PERMISSION intent in a similar way to get a precise longitude and latitude location.
As a side note, if you are using API.AI, you can use their Date and Time system entities to do this, or use the askForDateTime() method in the ApiAiApp class from the client library.
You can em-bed the maps API location/time in your app JSON or, other open source. As far as location it is device and user settings specific so, whether or not you get a JSNODE response from the device/user depends on the user even if they are running the APP and, have different setting preferences.

trying to access Thunderbird-tabmail does not work

I want to open a new tab with a gloda conversation from inside calendar code.
I receive an error from error console:
window not defined (or document not defined), depending on which of the two I use to Access tabmail:
let tabmail = window.document.getElementById("tabmail");
let tabmail = document.getElementById("tabmail");
The code works fine if the js file is included in an overlay xul-file.
But I want to use it outside of xul in my code.
Somewhere in my calendar code (in my 'addevent'), the same code throws the error.
This code is originally called from a rightclick on an email, but several layers deep into calendar code.
In MDN, I read that window is global? So what do I Need to do to add an tab?
This part works if tabmail is properly referenced:
tabmail.openTab("glodaList", {
collection: queryCollection,
message: aCollection.items[0],
title: tabTitle,
background: false
});
So how do I get a reference for tabmail?
Any help is appreciated.
after trying and looking through code for really some time before posting, it took only ca. 20 minutes to accidentally find the solution after submitting the question..
While browsing mailutils on mxr for something else, I found the solution in some function:
mail3PaneWindow = Services.wm.getMostRecentWindow("mail:3pane");
if (mail3PaneWindow) var tabmail = mail3PaneWindow.document.getElementById("tabmail");