I have been trying for some time solve a scheduling problem for an App that I used to work at. This problem is as follows...
Example Scenario:
ContactA is running Our App in the background. He just got into his car for his commute home. So, he is in #Evening Commute status.
His Valet settings state that he only wants to be notified to make Phone Calls when in #Evening Commute status (no text or email reminders).
Once Our app sees ContactA is in #Evening Commute status, and that he is available to make Phone Calls, the app will scan ALL his contacts to find any contacts that he has specified as preferred to Call.
Th algorithm identifies 3 potential matches for ContactA in his Agent list:
ContactX
ContactY
ContactZ
How does it decide which one to prompt ContactA to connect with during his #Evening Commute?
First, it should look to see if any of those three are using The app too. For example, it identifies that ContactX and ContactY are both using The app, but ContactZ is not.
So, it will then look further at ContactX and ContactY to see if either of them are currently in a Valet status that is accepting Phone Calls.
If both are, it will look to see if either is a Favorite. If so, that person takes priority.
If neither are, it can look back at history to see which is the oldest in terms of last contacted to choose which one to contact first.
If both ContactX and ContactY are showing as “unavailable” right now, based on their App Valet settings, the app could choose to prompt me to call ContactZ.
Thanks in advance
Pseudocode (you did not ask for any specific language, but this is a simple implementation of your algorithm in code). contactsSpecified would be an array of all contacts "specified as preferred to Call".
var contactsToCall[];
//Add all contacts that are using the app and are available
foreach(contact in contactsSepcified) {
if(contact.isUsingApp() && contact.isAvailable()) {
contactsToCall.add(contact);
}
}
//Add all contacts not using the app if none of the app users have been added
if(contactsToCall.size() == 0) {
foreach(contact in contactsSepcified) {
if(!contact.isUsingApp()) {
contactsToCall.add(contact);
}
}
}
foreach(contact in conctactsToCall) {
if(conctact.isFavorite()) {
call(contact);
return;
}
}
suggestContacts(contactsToCall);
Related
I want to develop a Microsoft Teams Bot that when a user enters a keyword, such as 'pto' it will show an Adaptive Card form that the user can populate with information which will then get sent to a web service for processing.
Is it possible to display an Adaptive Card based on a user entering a text command?
Yes, absolutely possible. A "Command" is really just a regular message, and it's how you interpret / respond to the message. You can either detect these messages yourself by looking at the content, or you can use a conversational AI engine like LUIS.ai (part of Azure) which can more easily be configured to check for variants (e.g. "pto", "PTO", "POT", "[whatever 'pto' stands for']" etc. In this case, LUIS is basically building a ton of "if" statements to find a match, and the returning you an "intent" (e.g. "User is requesting whatever "PTO" means"). Because it's using AI to generate and maintain this "if" list, language conversational engines like LUIS are widely used in bots, but they're not -required-. It's why you see them in most samples though.
With regards sending an Adaptive Card, that's also pretty standard stuff in Teams bots, and they can be sent in response to a user's message, like you're trying here, as well as other ways to invoke them.
I actually cover both of these topics one after the other in a conference session earlier this year - see the video here: https://www.youtube.com/watch?v=mM7-fYdcJhw&t=1398s (the earlier parts might actually be of interest too).
Bot Framework can't handle the card prompts natively, so the best solution I've come up with is to
Display the card as a standard activity (i.e. before the prompt)
Provide a message indicating you are waiting for prompt input (e.g. waiting for selection...)
Validate that the prompt input is an object (e.g. input.match(/{.+/g))
Use the values from the object in your next step to call the webservice
So in code it ends up like this:
await step.context.sendActivity(CardHelper.datePicker());
return await step.prompt(DATE_PROMPT, `*waiting for selection...*`);
with a validator to make sure you are receiving an object like this:
async validateDateCard(prompt) {
prompt.activeDialog = await this.userDialogStateAccessor;
prompt.context = await prompt.context;
const input = prompt.recognized.value;
if (input.match(/{.+/g)) {
return true;
} else {
await prompt.context.sendActivity(`Please use the calendar widget above to enter the date.`);
return false;
}
}
In my case my widget just has a single field for date, but it works the same regardless of how many fields you have. They will all be in the submitted object. If you try to type something in manually, you are reprompted to use the widget. In general I find prompting for plain text values in sequence instead of using cards works fine and it not too cumbersome for users. But if you have a ton of inputs or need specialized controls like the above date widget, sometimes cards are the only way.
I have a bot which displays "No answers found" if the question that user has asked isn't present in the knowledge base.
I want to display a different message (e.g Contact support desk on some phone number 123456789) if the bot isn't able to answer on 3 consecutive attempts.
Something like as shown in the below image:
This is hard to answer without knowing exactly how you have QnA Maker set up, but what #JJ_Wailes suggested is the way to go. You need to keep track of the number of wrong answers, and then provide your alternative default message. To give myself more flexibility, I have always created my own default response logic instead of relying on QnA Maker. I'm going to make some assumptions that you are already good making the QnA Maker calls and dealing with user and conversation state. Instead of relying on the QnA Maker default answer, I check for the confidence score returned and create my own message activities for those cases. For simplicity I'm going to ignore prompts. Here is a sample of my typical QnA Maker flow. Unfortunately I have developed in nodejs only, but I think this should be similar enough that you can adapt for dotnet.
var MINIMUM_SCORE = 50;
const conversationData = await this.dialogState.get(context, {});
// Will use conversationData.qnaFailCount to track consecutive wrong answers
// Make the initial call
var qnaResult = await QnAServiceHelper.queryQnAService(activity.text);
var qnaAnswer = qnaResult[0].answer;
// Apply a confidence filter
if (qnaResult[0].score > MINIMUM_SCORE) {
outputActivity = MessageFactory.text(qnaAnswer);
conversationData.qnaFailCount = 0; // Reset counter when answer found
} else {
// If low confidence, increment counter
conversationData.qnaFailCount += 1;
if (conversationData.qnaFailCount < 3) {
outputActivity = MessageFactory.text(defaultAnswer);
} else {
// Send the escalation message for every consecutive "no answer" starting at 3
outputActivity = MessageFactory.text(escalation);
}
}
await this.conversationState.saveChanges(context); // Don't forget to save state!
return outputActivity;
So now you will increment the counter for each answer that is not found and reset it whenever an answer is found. In this way it will continue to give the escalation mesasge for the 4th, 5th, etc. query with no answer. Since you are using conversation state, when the user leaves the site and comes back, they will have a new conversation and the process will start over.
Note that in the event you are using this within Microsoft Teams, that is treated as one long conversation, so the message wouldn't reset even after multiple days, only with a correct answer. It seems like this is not an MS Teams use case but I wanted to mention that.
I would like to start a service that once in awhile on all platforms has checked is there a notification to appear or not. Is there any nuget to connect all platforms or some examples?
You can use the Device.StartTimer(TimeSpan minutes) method to start a background task that will repeat after the given time span. Here is a code example:
var minutes = TimeSpan.FromMinutes (3);
Device.StartTimer (minutes, () => {
// call your method to check for notifications here
// Returning true means you want to repeat this timer
return true;
});
This is included with Xamarin Forms, so you don't need any platform specific logic.
http://iosapi.xamarin.com/index.aspx?link=M%3AXamarin.Forms.Device.StartTimer(System.TimeSpan%2CSystem.Func%7BSystem.Boolean%7D)
I think that the best that you can do is following:
Unfortunately, the way that these two platforms have evolved to handle executing background code is completely different. As such, there is no way that we can abstract the backgrounding feature into the Xamarin.Forms library. Instead, we going to continue to rely on the native APIs to execute our shared background task.
Further information for this topic can be found here:
https://robgibbens.com/backgrounding-with-xamarin-forms/
We just upgraded our Heroku postgres database using the follower changeover method. We have over 50 dataclips attached to the old database, and now we need to move them over to the new database. However, doing them one by one will take a lot of time.
Is there a programatic way to update the database a dataclip is attached to, perhaps with the CLI tools?
At least once the old database has been deprovisioned, you can now (as of March 2016) reattach them to another database:
Go to https://dataclips.heroku.com/clips/recoverable. It will display your old database and a set of 'orphaned' dataclips and you can choose to transfer them to another database (in my case the promoted follower from the changeover).
Note that this only affects the dataclips that you created, it does not affect the dataclips one of your team members created and that you only had access to. So they will have to go through this process as well.
Official devcenter article: https://devcenter.heroku.com/articles/dataclips#dataclip-recovery
Thanks to Heroku CSRF measures, programmatically updating data clips is much more difficult than you might expect. You'll need to suck it up and start clicking buttons by hand, or beg their support team to do it for you, which is just as difficult.
There is no official support for programmatically moving the dataclips. That being said, you can script it out against their HTTP API.
The base URL is https://dataclips.heroku.com/api/v1/. There are three relevant endpoints:
clips /clips
resources (databases) /heroku_resources
move clip /clips/:slug/move
Find the slug of the clip you want to move, find the resource id of the new database, and make a post to the move clip endpoint:
POST /api/v1/clips/fjhwieufysdufnjqqueyuiewsr/move
Content-Type: application/json
{"heroku_resource_id":"resource123456789#heroku.com"}
I had over 300 dataclips to move. I used the following technique to update them all (essentially reverse engineering the dataclips API).
Open Chrome with Web Developer tools, Network tab.
Log into Heroku Dataclips
Observe the network call which returns all the dataclips, in JSON (https://dataclips.heroku.com/api/v1/clips). Take this response and extract out all dataclip slugs.
Update the database for one dataclip. Observe the network call which does this (https://dataclips.heroku.com/api/v1/clips/:slug/move). Right click, Copy as cURL. This is the easiest way to get all the correct parameters, since the API uses cookies for authentication.
Write a script that loops through each dataclip slug, and shells out to curl. In Ruby, this looks like:
slugs = <paste ids here>.split("\n")
slugs.each do |slug|
command = %Q(curl -v 'https://dataclips.heroku.com/api/v1/clips/#{slug}/move' -H 'Cookie: ...' --data '{"heroku_resource_id":"resource1234567#heroku.com"}')
puts command
system(command)
end
You can contact Heroku support, and they will bulk transfer the dataclips to your new database for you.
Batch working on dataclips
I've finally found a solution to work on my Dataclips as a batch using the javascript console and some scraping technique. I needed it to retrieve every dataclips. But it guess It can be updated as such:
// Go to the dataclip listing (https://data.heroku.com/dataclips).
// Then execute this script in your console.
// Be careful, this will focus a new window every 4 seconds, preventing
// you from working 4 seconds times the number of dataclips you have.
// Retrieve urls and titles
let dataclips = Array.
from(document.querySelectorAll('.rt-td:first-child a')).
map(el => ({ url: el.href, title: el.innerText }))
/**
* Allows waiting for a given timeout before execution.
* #param {number} seconds
*/
const timeout = function(seconds) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, seconds);
})
}
/**
* Here are all the changes you want to apply to every single
* dataclip.
* #param {object} window
*/
const applyChanges = function(window) {
}
// With a fast connection, 4 seconds is OK. Dial it down if you
// have errors.
const expectedLoadTime = 4000 // ms
// This is the main loop, windows are opened one by one to ensure focus and a
// correct loading time.
for (const dataclip of dataclips) {
// This opens another window from the script, having access to its DOM.
// See https://github.com/buonomo/kazoo for a funnier example usage!
// And don't be shy to star and share :D
const externWindow = window.open(dataclip.url)
// A hack to wait for loading, this could be improved for sure.
await timeout(expectedLoadTime)
applyChanges(externWindow)
externWindow.close()
}
You'd still have to implement applyChanges yourself which I conceed is a bit tedious and I don't have time to do it know (if one does, please share!). But at least it can be done on all of your dataclips in a single function.
For an example usage of this script, you can take a look at the gist I made to scrape every dataclips and related errors.
I ran into this issue just recently when I wanted to test my app with multiple live ids. I have an Azure Mobile Service that I use the live id with. Basically I remember being asked for whether the app can use my credentials or not at least once. Now the LiveAuthClient CanLogout variable is always false so I can't log my account out in order to sign in with another account.
I found some reference saying that I would need to remove some single sign on stuff by hand, but I could only find one thing in Credential Manager that was seemingly similar and removing it (MicrosoftAccount:sth) had no effect. Here's the relevant piece of my code:
LiveAuthClient liveIdClient = new LiveAuthClient("myserviceredirecturi");
LiveLoginResult liveAuthResult = await liveIdClient.InitializeAsync(new[] { "wl.basic" });
if (liveIdClient.CanLogout)
liveIdClient.Logout(); // Code never goes here, ever.
LiveLoginResult liveLoginResult = await liveIdClient.LoginAsync(new[] { "wl.basic" });
The last line logs me in with the current windows 8 live id. How can I test with multiple live ids?