part two of favorites in node: added retrieve, delete and edit favorite

locations.
Added some refactoring to the general flow.
This commit is contained in:
Maha Badreldin 2017-02-21 11:37:24 +02:00
Родитель 5dab8211a7
Коммит 33672924d1
40 изменённых файлов: 836 добавлений и 278 удалений

Просмотреть файл

@ -4,22 +4,24 @@ var botbuilder_1 = require("botbuilder");
var common = require("./common");
var consts_1 = require("./consts");
var place_1 = require("./place");
var defaultLocationDialog = require("./dialogs/default-location-dialog");
var facebookLocationDialog = require("./dialogs/facebook-location-dialog");
var requiredFieldsDialog = require("./dialogs/required-fields-dialog");
var addFavoriteLocationDialog = require("./dialogs/add-favorite-location-dialog");
exports.LocationRequiredFields = requiredFieldsDialog.LocationRequiredFields;
exports.getFormattedAddressFromPlace = common.getFormattedAddressFromPlace;
var confirmDialog = require("./dialogs/confirm-dialog");
var retrieveLocationDialog = require("./dialogs/retrieve-location-dialog");
var requireFieldsDialog = require("./dialogs/require-fields-dialog");
var retrieveFavoriteLocationDialog = require("./dialogs/retrieve-favorite-location-dialog");
exports.LocationRequiredFields = requireFieldsDialog.LocationRequiredFields;
exports.getFormattedAddressFromLocation = common.getFormattedAddressFromLocation;
exports.Place = place_1.Place;
exports.createLibrary = function (apiKey) {
if (typeof apiKey === "undefined") {
throw "'apiKey' parameter missing";
}
var lib = new botbuilder_1.Library(consts_1.LibraryName);
requiredFieldsDialog.register(lib);
defaultLocationDialog.register(lib, apiKey);
facebookLocationDialog.register(lib, apiKey);
retrieveFavoriteLocationDialog.register(lib, apiKey);
retrieveLocationDialog.register(lib, apiKey);
requireFieldsDialog.register(lib);
addFavoriteLocationDialog.register(lib);
confirmDialog.register(lib);
lib.localePath(path.join(__dirname, 'locale/'));
lib.dialog('locationPickerPrompt', getLocationPickerPrompt());
return lib;
@ -33,36 +35,33 @@ exports.getLocation = function (session, options) {
};
function getLocationPickerPrompt() {
return [
function (session, args) {
function (session, args, next) {
session.dialogData.args = args;
if (args.useNativeControl && session.message.address.channelId == 'facebook') {
session.beginDialog('facebook-location-dialog', args);
if (!args.skipFavorites) {
botbuilder_1.Prompts.choice(session, session.gettext(consts_1.Strings.DialogStartBranchAsk), [session.gettext(consts_1.Strings.FavoriteLocations), session.gettext(consts_1.Strings.OtherLocation)], { listStyle: botbuilder_1.ListStyle.button, retryPrompt: session.gettext(consts_1.Strings.InvalidStartBranchResponse) });
}
else {
session.beginDialog('default-location-dialog', args);
}
},
function (session, results, next) {
if (results.response && results.response.place) {
session.beginDialog('required-fields-dialog', {
place: results.response.place,
requiredFields: session.dialogData.args.requiredFields
});
}
else {
next(results);
next();
}
},
function (session, results, next) {
if (results && results.response && results.response.entity === session.gettext(consts_1.Strings.FavoriteLocations)) {
session.beginDialog('retrieve-favorite-location-dialog', session.dialogData.args);
}
else {
session.beginDialog('retrieve-location-dialog', session.dialogData.args);
}
},
function (session, results, next) {
if (results.response && results.response.place) {
session.dialogData.place = results.response.place;
if (session.dialogData.args.skipConfirmationAsk) {
session.endDialogWithResult({ response: results.response.place });
next({ response: { confirmed: true } });
}
else {
var separator = session.gettext(consts_1.Strings.AddressSeparator);
var promptText = session.gettext(consts_1.Strings.ConfirmationAsk, common.getFormattedAddressFromPlace(results.response.place, separator));
session.dialogData.place = results.response.place;
botbuilder_1.Prompts.confirm(session, promptText, { listStyle: botbuilder_1.ListStyle.none });
var promptText = session.gettext(consts_1.Strings.ConfirmationAsk, common.getFormattedAddressFromLocation(results.response.place, separator));
session.beginDialog('confirm-dialog', { confirmationPrompt: promptText });
}
}
else {
@ -70,7 +69,8 @@ function getLocationPickerPrompt() {
}
},
function (session, results, next) {
if (!session.dialogData.args.skipFavorites && results.response && !results.response.reset) {
session.dialogData.confirmed = results.response.confirmed;
if (results.response && results.response.confirmed && !session.dialogData.args.skipFavorites) {
session.beginDialog('add-favorite-location-dialog', { place: session.dialogData.place });
}
else {
@ -78,12 +78,12 @@ function getLocationPickerPrompt() {
}
},
function (session, results, next) {
if (results.response && results.response.reset) {
if (!session.dialogData.confirmed || (results.response && results.response.reset)) {
session.send(consts_1.Strings.ResetPrompt);
session.replaceDialog('locationPickerPrompt', session.dialogData.args);
}
else {
next({ response: session.dialogData.place });
next({ response: common.processLocation(session.dialogData.place) });
}
}
];

Просмотреть файл

@ -18,7 +18,7 @@ function createBaseDialog(options) {
});
}
exports.createBaseDialog = createBaseDialog;
function processLocation(location, includeStreetAddress) {
function processLocation(location) {
var place = new place_1.Place();
place.type = location.entityType;
place.name = location.name;
@ -28,43 +28,25 @@ function processLocation(location, includeStreetAddress) {
place.locality = location.address.locality;
place.postalCode = location.address.postalCode;
place.region = location.address.adminDistrict;
if (includeStreetAddress) {
place.streetAddress = location.address.addressLine;
}
place.streetAddress = location.address.addressLine;
}
if (location.point && location.point.coordinates && location.point.coordinates.length == 2) {
place.geo = new place_1.Geo();
place.geo.latitude = location.point.coordinates[0];
place.geo.longitude = location.point.coordinates[1];
place.geo.latitude = location.point.coordinates[0].toString();
place.geo.longitude = location.point.coordinates[1].toString();
}
return place;
}
exports.processLocation = processLocation;
function buildPlaceFromGeo(latitude, longitude) {
var place = new place_1.Place();
place.geo = new place_1.Geo();
place.geo.latitude = latitude;
place.geo.longitude = longitude;
return place;
}
exports.buildPlaceFromGeo = buildPlaceFromGeo;
function getFormattedAddressFromPlace(place, separator) {
function getFormattedAddressFromLocation(location, separator) {
var addressParts = new Array();
if (place.streetAddress) {
addressParts.push(place.streetAddress);
if (location.address) {
addressParts = [location.address.addressLine,
location.address.locality,
location.address.adminDistrict,
location.address.postalCode,
location.address.countryRegion];
}
if (place.locality) {
addressParts.push(place.locality);
}
if (place.region) {
addressParts.push(place.region);
}
if (place.postalCode) {
addressParts.push(place.postalCode);
}
if (place.country) {
addressParts.push(place.country);
}
return addressParts.join(separator);
return addressParts.filter(function (i) { return i; }).join(separator);
}
exports.getFormattedAddressFromPlace = getFormattedAddressFromPlace;
exports.getFormattedAddressFromLocation = getFormattedAddressFromLocation;

Просмотреть файл

@ -10,17 +10,32 @@ exports.Strings = {
"ConfirmationAsk": "ConfirmationAsk",
"Country": "Country",
"DefaultPrompt": "DefaultPrompt",
"DeleteCommand": "DeleteCommand",
"DeleteFavoriteAbortion": "DeleteFavoriteAbortion",
"DeleteFavoriteConfirmationAsk": "DeleteFavoriteConfirmationAsk",
"DialogStartBranchAsk": "DialogStartBranchAsk",
"EditCommand": "EditCommand",
"EditFavoritePrompt": "EditFavoritePrompt",
"EnterNewFavoriteLocationName": "EnterNewFavoriteLocationName",
"FavoriteAddedConfirmation": "FavoriteAddedConfirmation",
"FavoriteDeletedConfirmation": "FavoriteDeletedConfirmation",
"FavoriteEdittedConfirmation": "FavoriteEdittedConfirmation",
"FavoriteLocations": "FavoriteLocations",
"HelpMessage": "HelpMessage",
"InvalidFavoriteLocationSelection": "InvalidFavoriteLocationSelection",
"InvalidLocationResponse": "InvalidLocationResponse",
"InvalidLocationResponseFacebook": "InvalidLocationResponseFacebook",
"InvalidStartBranchResponse": "InvalidStartBranchResponse",
"LocationNotFound": "LocationNotFound",
"Locality": "Locality",
"MultipleResultsFound": "MultipleResultsFound",
"NoFavoriteLocationsFound": "NoFavoriteLocationsFound",
"OtherComand": "OtherComand",
"OtherLocation": "OtherLocation",
"PostalCode": "PostalCode",
"Region": "Region",
"ResetPrompt": "ResetPrompt",
"SelectFavoriteLocationPrompt": "SelectFavoriteLocationPrompt",
"SingleResultFound": "SingleResultFound",
"StreetAddress": "StreetAddress",
"TitleSuffixFacebook": "TitleSuffixFacebook",

Просмотреть файл

@ -2,9 +2,7 @@
var common = require("../common");
var consts_1 = require("../consts");
var favorites_manager_1 = require("../services/favorites-manager");
var confirmDialog = require("./confirm-dialog");
function register(library) {
confirmDialog.register(library);
library.dialog('add-favorite-location-dialog', createDialog());
library.dialog('name-favorite-location-dialog', createNameFavoriteLocationDialog());
}

Просмотреть файл

@ -3,7 +3,7 @@ var common = require("../common");
var consts_1 = require("../consts");
var place_1 = require("../place");
function register(library) {
library.dialog('choice-dialog', createDialog());
library.dialog('choose-location-dialog', createDialog());
}
exports.register = register;
function createDialog() {
@ -21,8 +21,7 @@ function createDialog() {
if (match) {
var currentNumber = Number(match[0]);
if (currentNumber > 0 && currentNumber <= session.dialogData.locations.length) {
var place = common.processLocation(session.dialogData.locations[currentNumber - 1], true);
session.endDialogWithResult({ response: { place: place } });
session.endDialogWithResult({ response: { place: session.dialogData.locations[currentNumber - 1] } });
return;
}
}

Просмотреть файл

@ -1,8 +1,7 @@
"use strict";
var common = require("../common");
var consts_1 = require("../consts");
function register(library) {
library.dialog('single-location-confirm-dialog', createDialog());
library.dialog('confirm-single-location-dialog', createDialog());
}
exports.register = register;
function createDialog() {
@ -13,8 +12,7 @@ function createDialog() {
},
function (session, results, next) {
if (results.response && results.response.confirmed) {
var place = common.processLocation(session.dialogData.locations[0], true);
session.endDialogWithResult({ response: { place: place } });
session.endDialogWithResult({ response: { place: session.dialogData.locations[0] } });
}
else {
session.endDialogWithResult({ response: { reset: true } });

Просмотреть файл

@ -0,0 +1,32 @@
"use strict";
var consts_1 = require("../consts");
var favorites_manager_1 = require("../services/favorites-manager");
function register(library, apiKey) {
library.dialog('delete-favorite-location-dialog', createDialog());
}
exports.register = register;
function createDialog() {
return [
function (session, args) {
session.dialogData.args = args;
session.dialogData.toBeDeleted = args.toBeDeleted;
var deleteFavoriteConfirmationAsk = session.gettext(consts_1.Strings.DeleteFavoriteConfirmationAsk, args.toBeDeleted.name);
session.beginDialog('confirm-dialog', { confirmationPrompt: deleteFavoriteConfirmationAsk });
},
function (session, results, next) {
if (results.response && results.response.confirmed) {
var favoritesManager = new favorites_manager_1.FavoritesManager(session.userData);
favoritesManager.delete(session.dialogData.toBeDeleted);
session.send(session.gettext(consts_1.Strings.FavoriteDeletedConfirmation, session.dialogData.toBeDeleted.name));
session.replaceDialog('retrieve-favorite-location-dialog', session.dialogData.args);
}
else if (results.response && results.response.confirmed === false) {
session.send(session.gettext(consts_1.Strings.DeleteFavoriteAbortion));
session.replaceDialog('retrieve-favorite-location-dialog', session.dialogData.args);
}
else {
next(results);
}
}
];
}

Просмотреть файл

@ -0,0 +1,32 @@
"use strict";
var consts_1 = require("../consts");
var favorites_manager_1 = require("../services/favorites-manager");
function register(library, apiKey) {
library.dialog('edit-favorite-location-dialog', createDialog());
}
exports.register = register;
function createDialog() {
return [
function (session, args) {
session.dialogData.args = args;
session.dialogData.toBeEditted = args.toBeEditted;
session.send(session.gettext(consts_1.Strings.EditFavoritePrompt, args.toBeEditted.name));
session.beginDialog('retrieve-location-dialog', session.dialogData.args);
},
function (session, results, next) {
if (results.response && results.response.place) {
var favoritesManager = new favorites_manager_1.FavoritesManager(session.userData);
var newfavoriteLocation = {
location: results.response.place,
name: session.dialogData.toBeEditted.name
};
favoritesManager.update(session.dialogData.toBeEditted, newfavoriteLocation);
session.send(session.gettext(consts_1.Strings.FavoriteEdittedConfirmation, session.dialogData.toBeEditted.name));
session.endDialogWithResult({ response: { place: results.response.place } });
}
else {
next(results);
}
}
];
}

Просмотреть файл

@ -12,15 +12,15 @@ var LocationRequiredFields;
LocationRequiredFields[LocationRequiredFields["country"] = 16] = "country";
})(LocationRequiredFields = exports.LocationRequiredFields || (exports.LocationRequiredFields = {}));
function register(library) {
library.dialog('required-fields-dialog', createDialog());
library.dialog('require-fields-dialog', createDialog());
}
exports.register = register;
var fields = [
{ name: "streetAddress", prompt: consts_1.Strings.StreetAddress, flag: LocationRequiredFields.streetAddress },
{ name: "addressLine", prompt: consts_1.Strings.StreetAddress, flag: LocationRequiredFields.streetAddress },
{ name: "locality", prompt: consts_1.Strings.Locality, flag: LocationRequiredFields.locality },
{ name: "region", prompt: consts_1.Strings.Region, flag: LocationRequiredFields.region },
{ name: "adminDistrict", prompt: consts_1.Strings.Region, flag: LocationRequiredFields.region },
{ name: "postalCode", prompt: consts_1.Strings.PostalCode, flag: LocationRequiredFields.postalCode },
{ name: "country", prompt: consts_1.Strings.Country, flag: LocationRequiredFields.country },
{ name: "countryRegion", prompt: consts_1.Strings.Country, flag: LocationRequiredFields.country },
];
function createDialog() {
return common.createBaseDialog({ recognizeMode: botbuilder_1.RecognizeMode.onBegin })
@ -61,11 +61,11 @@ function createDialog() {
});
}
function completeFieldIfMissing(session, field) {
if ((field.flag & session.dialogData.requiredFieldsFlag) && !session.dialogData.place[field.name]) {
if ((field.flag & session.dialogData.requiredFieldsFlag) && !session.dialogData.place.address[field.name]) {
var prefix = "";
var prompt = "";
if (typeof session.dialogData.lastInput === "undefined") {
var formattedAddress = common.getFormattedAddressFromPlace(session.dialogData.place, session.gettext(consts_1.Strings.AddressSeparator));
var formattedAddress = common.getFormattedAddressFromLocation(session.dialogData.place, session.gettext(consts_1.Strings.AddressSeparator));
if (formattedAddress) {
prefix = session.gettext(consts_1.Strings.AskForPrefix, formattedAddress);
prompt = session.gettext(consts_1.Strings.AskForTemplate, session.gettext(field.prompt));

Просмотреть файл

@ -1,15 +1,14 @@
"use strict";
var common = require("../common");
var consts_1 = require("../consts");
var botbuilder_1 = require("botbuilder");
var map_card_1 = require("../map-card");
var locationService = require("../services/bing-geospatial-service");
var singleLocationConfirmDialog = require("./single-location-confirm-dialog");
var choiceDialog = require("./choice-dialog");
var confirmSingleLocationDialog = require("./confirm-single-location-dialog");
var chooseLocationDialog = require("./choose-location-dialog");
var location_card_builder_1 = require("../services/location-card-builder");
function register(library, apiKey) {
singleLocationConfirmDialog.register(library);
choiceDialog.register(library);
library.dialog('default-location-dialog', createDialog());
confirmSingleLocationDialog.register(library);
chooseLocationDialog.register(library);
library.dialog('resolve-bing-location-dialog', createDialog());
library.dialog('location-resolve-dialog', createLocationResolveDialog(apiKey));
}
exports.register = register;
@ -23,10 +22,10 @@ function createDialog() {
if (results.response && results.response.locations) {
var locations = results.response.locations;
if (locations.length == 1) {
session.beginDialog('single-location-confirm-dialog', { locations: locations });
session.beginDialog('confirm-single-location-dialog', { locations: locations });
}
else {
session.beginDialog('choice-dialog', { locations: locations });
session.beginDialog('choose-location-dialog', { locations: locations });
}
}
else {
@ -50,30 +49,10 @@ function createLocationResolveDialog(apiKey) {
}
var locationCount = Math.min(MAX_CARD_COUNT, locations.length);
locations = locations.slice(0, locationCount);
var reply = createLocationsCard(apiKey, session, locations);
var reply = new location_card_builder_1.LocationCardBuilder(apiKey).createHeroCards(session, locations);
session.send(reply);
session.endDialogWithResult({ response: { locations: locations } });
})
.catch(function (error) { return session.error(error); });
});
}
function createLocationsCard(apiKey, session, locations) {
var cards = new Array();
for (var i = 0; i < locations.length; i++) {
cards.push(constructCard(apiKey, session, locations, i));
}
return new botbuilder_1.Message(session)
.attachmentLayout(botbuilder_1.AttachmentLayout.carousel)
.attachments(cards);
}
function constructCard(apiKey, session, locations, index) {
var location = locations[index];
var card = new map_card_1.MapCard(apiKey, session);
if (locations.length > 1) {
card.location(location, index + 1);
}
else {
card.location(location);
}
return card;
}

Просмотреть файл

@ -4,7 +4,7 @@ var common = require("../common");
var botbuilder_1 = require("botbuilder");
var locationService = require("../services/bing-geospatial-service");
function register(library, apiKey) {
library.dialog('facebook-location-dialog', createDialog(apiKey));
library.dialog('retrive-facebook-location-dialog', createDialog(apiKey));
library.dialog('facebook-location-resolve-dialog', createLocationResolveDialog());
}
exports.register = register;
@ -16,11 +16,11 @@ function createDialog(apiKey) {
},
function (session, results, next) {
if (session.dialogData.args.reverseGeocode && results.response && results.response.place) {
locationService.getLocationByPoint(apiKey, results.response.place.geo.latitude, results.response.place.geo.longitude)
locationService.getLocationByPoint(apiKey, results.response.place.point.coordinates[0], results.response.place.point.coordinates[1])
.then(function (locations) {
var place;
if (locations.length) {
place = common.processLocation(locations[0], false);
place = locations[0];
}
else {
place = results.response.place;
@ -46,7 +46,7 @@ function createLocationResolveDialog() {
var entities = session.message.entities;
for (var i = 0; i < entities.length; i++) {
if (entities[i].type == "Place" && entities[i].geo && entities[i].geo.latitude && entities[i].geo.longitude) {
session.endDialogWithResult({ response: { place: common.buildPlaceFromGeo(entities[i].geo.latitude, entities[i].geo.longitude) } });
session.endDialogWithResult({ response: { place: buildLocationFromGeo(entities[i].geo.latitude, entities[i].geo.longitude) } });
return;
}
}
@ -66,3 +66,7 @@ function sendLocationPrompt(session, prompt) {
});
return session.send(message);
}
function buildLocationFromGeo(latitude, longitude) {
var coordinates = [latitude, longitude];
return { point: { coordinates: coordinates } };
}

Просмотреть файл

@ -0,0 +1,87 @@
"use strict";
var common = require("../common");
var consts_1 = require("../consts");
var location_card_builder_1 = require("../services/location-card-builder");
var favorites_manager_1 = require("../services/favorites-manager");
var deleteFavoriteLocationDialog = require("./delete-favorite-location-dialog");
var editFavoriteLocationDialog = require("./edit-fravorite-location-dialog");
function register(library, apiKey) {
library.dialog('retrieve-favorite-location-dialog', createDialog(apiKey));
deleteFavoriteLocationDialog.register(library, apiKey);
editFavoriteLocationDialog.register(library, apiKey);
}
exports.register = register;
function createDialog(apiKey) {
return common.createBaseDialog()
.onBegin(function (session, args) {
session.dialogData.args = args;
var favoritesManager = new favorites_manager_1.FavoritesManager(session.userData);
var userFavorites = favoritesManager.getFavorites();
if (userFavorites.length == 0) {
session.send(session.gettext(consts_1.Strings.NoFavoriteLocationsFound));
session.replaceDialog('retrieve-location-dialog', session.dialogData.args);
return;
}
session.dialogData.userFavorites = userFavorites;
var locations = [];
var names = [];
for (var i = 0; i < userFavorites.length; i++) {
locations.push(userFavorites[i].location);
names.push(userFavorites[i].name);
}
session.send(new location_card_builder_1.LocationCardBuilder(apiKey).createHeroCards(session, locations, true, names));
session.send(session.gettext(consts_1.Strings.SelectFavoriteLocationPrompt)).sendBatch();
}).onDefault(function (session) {
var text = session.message.text;
if (text === session.gettext(consts_1.Strings.OtherComand)) {
session.replaceDialog('retrieve-location-dialog', session.dialogData.args);
}
else {
var selection = tryParseCommandSelection(text, session.dialogData.userFavorites.length);
if (selection.command === "select") {
session.replaceDialog('require-fields-dialog', {
place: session.dialogData.userFavorites[selection.index - 1].location,
requiredFields: session.dialogData.args.requiredFields
});
}
else if (selection.command === session.gettext(consts_1.Strings.DeleteCommand)) {
session.dialogData.args.toBeDeleted = session.dialogData.userFavorites[selection.index - 1];
session.replaceDialog('delete-favorite-location-dialog', session.dialogData.args);
}
else if (selection.command === session.gettext(consts_1.Strings.EditCommand)) {
session.dialogData.args.toBeEditted = session.dialogData.userFavorites[selection.index - 1];
session.replaceDialog('edit-favorite-location-dialog', session.dialogData.args);
}
else {
session.send(session.gettext(consts_1.Strings.InvalidFavoriteLocationSelection)).sendBatch();
}
}
});
}
function tryParseNumberSelection(text) {
var tokens = text.trim().split(' ');
if (tokens.length == 1) {
var numberExp = /[+-]?(?:\d+\.?\d*|\d*\.?\d+)/;
var match = numberExp.exec(text);
if (match) {
return Number(match[0]);
}
}
return -1;
}
function tryParseCommandSelection(text, maxIndex) {
var tokens = text.trim().split(' ');
if (tokens.length == 1) {
var index = tryParseNumberSelection(text);
if (index > 0 && index <= maxIndex) {
return { index: index, command: "select" };
}
}
else if (tokens.length == 2) {
var index = tryParseNumberSelection(tokens[1]);
if (index > 0 && index <= maxIndex) {
return { index: index, command: tokens[0] };
}
}
return { command: "" };
}

Просмотреть файл

@ -0,0 +1,33 @@
"use strict";
var resolveBingLocationDialog = require("./resolve-bing-location-dialog");
var retrieveFacebookLocationDialog = require("./retrieve-facebook-location-dialog");
function register(library, apiKey) {
library.dialog('retrieve-location-dialog', createDialog());
resolveBingLocationDialog.register(library, apiKey);
retrieveFacebookLocationDialog.register(library, apiKey);
}
exports.register = register;
function createDialog() {
return [
function (session, args) {
session.dialogData.args = args;
if (args.useNativeControl && session.message.address.channelId == 'facebook') {
session.beginDialog('retrieve-facebook-location-dialog', args);
}
else {
session.beginDialog('resolve-bing-location-dialog', args);
}
},
function (session, results, next) {
if (results.response && results.response.place) {
session.beginDialog('require-fields-dialog', {
place: results.response.place,
requiredFields: session.dialogData.args.requiredFields
});
}
else {
next(results);
}
}
];
}

Просмотреть файл

@ -1,6 +1,6 @@
{
"AddressSeparator": ", ",
"AddToFavoritesAsk": "Do you want me to add this address to your favorite locations?",
"AddToFavoritesAsk": "Do you want me to add this address to your favorite locations?",
"AskForEmptyAddressTemplate": "Please provide the %s.",
"AskForPrefix": "OK %s.",
"AskForTemplate": " Please also provide the %s.",
@ -8,17 +8,32 @@
"ConfirmationAsk": "OK, I will ship to %s. Is that correct? Enter 'yes' or 'no'.",
"Country": "country",
"DefaultPrompt": "Where should I ship your order?",
"DeleteCommand": "delete",
"DeleteFavoriteAbortion": "OK, deletion aborted.",
"DeleteFavoriteConfirmationAsk": "Are you sure you want to delete %s from your favorite locations?",
"DialogStartBranchAsk": "How would you like to pick a location?",
"EditCommand": "edit",
"EditFavoritePrompt": "OK, let's edit %s. Enter a new address.",
"EnterNewFavoriteLocationName": "OK, please enter a friendly name for this address. You can use 'home', 'work' or any other name you prefer.",
"FavoriteAddedConfirmation": "OK, I added %s to your favorite locations.",
"HelpMessage": "Say or type a valid address when asked, and I will try to find it using Bing. You can provide the full address information (street no. / name, city, region, postal/zip code, country) or a part of it. If you want to change the address, say or type 'reset'. Finally, say or type 'cancel' to exit without providing an address.",
"FavoriteDeletedConfirmation": "OK, I deleted %s from your favorite locations.",
"FavoriteEdittedConfirmation": "OK, I editted %s in your favorite locations with this new address.",
"FavoriteLocations": "Favorite Locations",
"HelpMessage": "Say or type a valid address when asked, and I will try to find it using Bing. You can provide the full address information (street no. / name, city, region, postal/zip code, country) or a part of it. If you want to change the address, say or type 'reset'. Finally, say or type 'cancel' to exit without providing an address.",
"InvalidFavoriteLocationSelection": "Type or say a number to choose the address, enter 'other' to create a new favorite location, or enter 'cancel' to exit. You can also type or say 'edit' or 'delete' followed by a number to edit or delete the respective location.",
"InvalidLocationResponse": "Didn't get that. Choose a location or cancel.",
"InvalidLocationResponseFacebook": "Tap on Send Location to proceed; type or say cancel to exit.",
"InvalidStartBranchResponse": "Tap one of the options to proceed; type or say cancel to exit.",
"LocationNotFound": "I could not find this address. Please try again.",
"Locality": "city or locality",
"MultipleResultsFound": "I found these results. Type or say a number to choose the address, or enter 'other' to select another address.",
"NoFavoriteLocationsFound": "You do not seem to have any favorite locations at the moment. Enter an address and you will be able to save it to your favorite locations.",
"OtherComand": "other",
"OtherLocation": "Other Location",
"PostalCode": "zip or postal code",
"Region": "state or region",
"ResetPrompt": "OK, let's start over.",
"SelectFavoriteLocationPrompt": "Here are your favorite locations. Type or say a number to use the respective location, or 'other' to use a different location. You can also type or say 'edit' or 'delete' followed by a number to edit or delete the respective location.",
"SingleResultFound": "I found this result. Is this the correct address?",
"StreetAddress": "street address",
"TitleSuffix": " Type or say an address",

Просмотреть файл

@ -13,12 +13,15 @@ var MapCard = (function (_super) {
_this.apiKey = apiKey;
return _this;
}
MapCard.prototype.location = function (location, index) {
var indexText = "";
MapCard.prototype.location = function (location, index, locationName) {
var prefixText = "";
if (index !== undefined) {
indexText = index + ". ";
prefixText = index + ". ";
}
this.subtitle(indexText + location.address.formattedAddress);
if (locationName !== undefined) {
prefixText += locationName + ": ";
}
this.subtitle(prefixText + location.address.formattedAddress);
if (location.point) {
var locationUrl;
try {

Просмотреть файл

@ -0,0 +1,17 @@
"use strict";
var RawLocation = (function () {
function RawLocation() {
}
return RawLocation;
}());
exports.RawLocation = RawLocation;
var Address = (function () {
function Address() {
}
return Address;
}());
var Point = (function () {
function Point() {
}
return Point;
}());

Просмотреть файл

@ -2,16 +2,16 @@
var FavoritesManager = (function () {
function FavoritesManager(userData) {
this.userData = userData;
this.MAX_FAVORITE_COUNT = 5;
this.FAVORITES_KEY = 'favorites';
this.maxFavoriteCount = 5;
this.favoritesKey = 'favorites';
}
FavoritesManager.prototype.maxCapacityReached = function () {
return this.getFavorites().length >= this.MAX_FAVORITE_COUNT;
return this.getFavorites().length >= this.maxFavoriteCount;
};
FavoritesManager.prototype.isFavorite = function (location) {
var favorites = this.getFavorites();
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].location.formattedAddress === location.formattedAddress) {
if (this.areEqual(favorites[i].location, location)) {
return true;
}
}
@ -19,14 +19,37 @@ var FavoritesManager = (function () {
};
FavoritesManager.prototype.add = function (favoriteLocation) {
var favorites = this.getFavorites();
if (favorites.length >= this.MAX_FAVORITE_COUNT) {
if (favorites.length >= this.maxFavoriteCount) {
throw ('The max allowed number of favorite locations has already been reached.');
}
favorites.push(favoriteLocation);
this.userData[this.FAVORITES_KEY] = favorites;
this.userData[this.favoritesKey] = favorites;
};
FavoritesManager.prototype.delete = function (favoriteLocation) {
var favorites = this.getFavorites();
var newFavorites = [];
for (var i = 0; i < favorites.length; i++) {
if (!this.areEqual(favorites[i].location, favoriteLocation.location)) {
newFavorites.push(favorites[i]);
}
}
this.userData[this.favoritesKey] = newFavorites;
};
FavoritesManager.prototype.update = function (currentValue, newValue) {
var favorites = this.getFavorites();
var newFavorites = [];
for (var i = 0; i < favorites.length; i++) {
if (this.areEqual(favorites[i].location, currentValue.location)) {
newFavorites.push(newValue);
}
else {
newFavorites.push(favorites[i]);
}
}
this.userData[this.favoritesKey] = newFavorites;
};
FavoritesManager.prototype.getFavorites = function () {
var storedFavorites = this.userData[this.FAVORITES_KEY];
var storedFavorites = this.userData[this.favoritesKey];
if (storedFavorites) {
return storedFavorites;
}
@ -34,6 +57,9 @@ var FavoritesManager = (function () {
return [];
}
};
FavoritesManager.prototype.areEqual = function (location0, location1) {
return location0.address.formattedAddress === location1.address.formattedAddress;
};
return FavoritesManager;
}());
exports.FavoritesManager = FavoritesManager;

Просмотреть файл

@ -0,0 +1,35 @@
"use strict";
var botbuilder_1 = require("botbuilder");
var map_card_1 = require("../map-card");
var LocationCardBuilder = (function () {
function LocationCardBuilder(apiKey) {
this.apiKey = apiKey;
}
LocationCardBuilder.prototype.createHeroCards = function (session, locations, alwaysShowNumericPrefix, locationNames) {
var cards = new Array();
for (var i = 0; i < locations.length; i++) {
cards.push(this.constructCard(session, locations, i, alwaysShowNumericPrefix, locationNames));
}
return new botbuilder_1.Message(session)
.attachmentLayout(botbuilder_1.AttachmentLayout.carousel)
.attachments(cards);
};
LocationCardBuilder.prototype.constructCard = function (session, locations, index, alwaysShowNumericPrefix, locationNames) {
var location = locations[index];
var card = new map_card_1.MapCard(this.apiKey, session);
if (alwaysShowNumericPrefix || locations.length > 1) {
if (locationNames) {
card.location(location, index + 1, locationNames[index]);
}
else {
card.location(location, index + 1);
}
}
else {
card.location(location);
}
return card;
};
return LocationCardBuilder;
}());
exports.LocationCardBuilder = LocationCardBuilder;

5
Node/core/src/botbuilder-location.d.ts поставляемый
Просмотреть файл

@ -1,4 +1,5 @@
import * as builder from "botbuilder";
import { RawLocation } from "./rawLocation";
//=============================================================================
//
@ -130,7 +131,7 @@ export function getLocation(session: builder.Session, options: ILocationPromptOp
/**
* Gets a formatted address string.
* @param place Place object containing the address.
* @param location object containing the address.
* @param separator The string separating the address parts.
*/
export function getFormattedAddressFromPlace(place: Place, separator: string): string;
export function getFormattedAddressFromLocation(location: RawLocation, separator: string): string;

Просмотреть файл

@ -1,23 +1,25 @@
import * as path from 'path';
import { Library, Session, IDialogResult, Prompts, ListStyle } from 'botbuilder';
import { IDialogResult, IPromptOptions, Library, ListStyle, Prompts, Session} from 'botbuilder';
import * as common from './common';
import { Strings, LibraryName } from './consts';
import { Place } from './place';
import * as defaultLocationDialog from './dialogs/default-location-dialog';
import * as facebookLocationDialog from './dialogs/facebook-location-dialog'
import * as requiredFieldsDialog from './dialogs/required-fields-dialog';
import * as addFavoriteLocationDialog from './dialogs/add-favorite-location-dialog';
import * as confirmDialog from './dialogs/confirm-dialog';
import * as retrieveLocationDialog from './dialogs/retrieve-location-dialog'
import * as requireFieldsDialog from './dialogs/require-fields-dialog';
import * as retrieveFavoriteLocationDialog from './dialogs/retrieve-favorite-location-dialog'
export interface ILocationPromptOptions {
prompt: string;
requiredFields?: requiredFieldsDialog.LocationRequiredFields;
requiredFields?: requireFieldsDialog.LocationRequiredFields;
skipConfirmationAsk?: boolean;
useNativeControl?: boolean,
reverseGeocode?: boolean
reverseGeocode?: boolean,
skipFavorites?: boolean
}
exports.LocationRequiredFields = requiredFieldsDialog.LocationRequiredFields;
exports.getFormattedAddressFromPlace = common.getFormattedAddressFromPlace;
exports.LocationRequiredFields = requireFieldsDialog.LocationRequiredFields;
exports.getFormattedAddressFromLocation = common.getFormattedAddressFromLocation;
exports.Place = Place;
//=========================================================
@ -31,11 +33,11 @@ exports.createLibrary = (apiKey: string): Library => {
}
var lib = new Library(LibraryName);
requiredFieldsDialog.register(lib);
defaultLocationDialog.register(lib, apiKey);
facebookLocationDialog.register(lib, apiKey);
retrieveFavoriteLocationDialog.register(lib, apiKey);
retrieveLocationDialog.register(lib, apiKey);
requireFieldsDialog.register(lib);
addFavoriteLocationDialog.register(lib);
confirmDialog.register(lib);
lib.localePath(path.join(__dirname, 'locale/'));
lib.dialog('locationPickerPrompt', getLocationPickerPrompt());
@ -58,46 +60,50 @@ exports.getLocation = function (session: Session, options: ILocationPromptOption
function getLocationPickerPrompt() {
return [
// retrieve the location
(session: Session, args: ILocationPromptOptions) => {
// handle different ways of retrieving a location (favorite, other, etc)
(session: Session, args: ILocationPromptOptions, next: (results?: IDialogResult<any>) => void) => {
session.dialogData.args = args;
if (args.useNativeControl && session.message.address.channelId == 'facebook') {
session.beginDialog('facebook-location-dialog', args);
if (!args.skipFavorites) {
Prompts.choice(
session,
session.gettext(Strings.DialogStartBranchAsk),
[ session.gettext(Strings.FavoriteLocations), session.gettext(Strings.OtherLocation) ],
{ listStyle: ListStyle.button, retryPrompt: session.gettext(Strings.InvalidStartBranchResponse)});
}
else {
session.beginDialog('default-location-dialog', args);
next();
}
},
// complete required fields, if applicable
// retrieve location
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.place) {
session.beginDialog('required-fields-dialog', {
place: results.response.place,
requiredFields: session.dialogData.args.requiredFields
})
} else {
next(results);
if (results && results.response && results.response.entity === session.gettext(Strings.FavoriteLocations)) {
session.beginDialog('retrieve-favorite-location-dialog', session.dialogData.args);
}
else {
session.beginDialog('retrieve-location-dialog', session.dialogData.args);
}
},
// make final confirmation
// make final confirmation
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.place) {
session.dialogData.place = results.response.place;
if (session.dialogData.args.skipConfirmationAsk) {
session.endDialogWithResult({ response: results.response.place });
next({ response: { confirmed: true }});
}
else {
var separator = session.gettext(Strings.AddressSeparator);
var promptText = session.gettext(Strings.ConfirmationAsk, common.getFormattedAddressFromPlace(results.response.place, separator));
session.dialogData.place = results.response.place;
Prompts.confirm(session, promptText, { listStyle: ListStyle.none })
var promptText = session.gettext(Strings.ConfirmationAsk, common.getFormattedAddressFromLocation(results.response.place, separator));
session.beginDialog('confirm-dialog' , { confirmationPrompt: promptText });
}
} else {
}
else {
next(results);
}
},
// offer add to favorites, if applicable
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if(!session.dialogData.args.skipFavorites && results.response && !results.response.reset) {
session.dialogData.confirmed = results.response.confirmed;
if(results.response && results.response.confirmed && !session.dialogData.args.skipFavorites) {
session.beginDialog('add-favorite-location-dialog', { place : session.dialogData.place });
}
else {
@ -105,12 +111,12 @@ function getLocationPickerPrompt() {
}
},
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.reset) {
session.send(Strings.ResetPrompt)
if ( !session.dialogData.confirmed || (results.response && results.response.reset)) {
session.send(Strings.ResetPrompt);
session.replaceDialog('locationPickerPrompt', session.dialogData.args);
}
else {
next({ response: session.dialogData.place });
next({ response: common.processLocation(session.dialogData.place) });
}
}
];

Просмотреть файл

@ -1,6 +1,7 @@
import { Session, IntentDialog } from 'botbuilder';
import { Strings } from './consts';
import { Place, Geo } from './place';
import { RawLocation } from './rawLocation'
export function createBaseDialog(options?: any): IntentDialog {
return new IntentDialog(options)
@ -18,7 +19,7 @@ export function createBaseDialog(options?: any): IntentDialog {
});
}
export function processLocation(location: any, includeStreetAddress: boolean): Place {
export function processLocation(location: RawLocation): Place {
var place: Place = new Place();
place.type = location.entityType;
place.name = location.name;
@ -29,51 +30,28 @@ export function processLocation(location: any, includeStreetAddress: boolean): P
place.locality = location.address.locality;
place.postalCode = location.address.postalCode;
place.region = location.address.adminDistrict;
if (includeStreetAddress) {
place.streetAddress = location.address.addressLine;
}
place.streetAddress = location.address.addressLine;
}
if (location.point && location.point.coordinates && location.point.coordinates.length == 2) {
place.geo = new Geo();
place.geo.latitude = location.point.coordinates[0];
place.geo.longitude = location.point.coordinates[1];
place.geo.latitude = location.point.coordinates[0].toString();
place.geo.longitude = location.point.coordinates[1].toString();
}
return place;
}
export function buildPlaceFromGeo(latitude: string, longitude: string) {
var place = new Place();
place.geo = new Geo();
place.geo.latitude = latitude;
place.geo.longitude = longitude;
export function getFormattedAddressFromLocation(location: RawLocation, separator: string): string {
let addressParts: Array<string> = new Array();
return place;
}
export function getFormattedAddressFromPlace(place: Place, separator: string): string {
var addressParts: Array<any> = new Array();
if (place.streetAddress) {
addressParts.push(place.streetAddress);
if (location.address) {
addressParts = [ location.address.addressLine,
location.address.locality,
location.address.adminDistrict,
location.address.postalCode,
location.address.countryRegion];
}
if (place.locality) {
addressParts.push(place.locality);
}
if (place.region) {
addressParts.push(place.region);
}
if (place.postalCode) {
addressParts.push(place.postalCode);
}
if (place.country) {
addressParts.push(place.country);
}
return addressParts.join(separator);
return addressParts.filter(i => i).join(separator);
}

Просмотреть файл

@ -11,17 +11,32 @@ export const Strings = {
"ConfirmationAsk": "ConfirmationAsk",
"Country": "Country",
"DefaultPrompt": "DefaultPrompt",
"DeleteCommand": "DeleteCommand",
"DeleteFavoriteAbortion" : "DeleteFavoriteAbortion",
"DeleteFavoriteConfirmationAsk": "DeleteFavoriteConfirmationAsk",
"DialogStartBranchAsk": "DialogStartBranchAsk",
"EditCommand": "EditCommand",
"EditFavoritePrompt": "EditFavoritePrompt",
"EnterNewFavoriteLocationName": "EnterNewFavoriteLocationName",
"FavoriteAddedConfirmation": "FavoriteAddedConfirmation",
"FavoriteDeletedConfirmation": "FavoriteDeletedConfirmation",
"FavoriteEdittedConfirmation": "FavoriteEdittedConfirmation",
"FavoriteLocations": "FavoriteLocations",
"HelpMessage": "HelpMessage",
"InvalidFavoriteLocationSelection": "InvalidFavoriteLocationSelection",
"InvalidLocationResponse": "InvalidLocationResponse",
"InvalidLocationResponseFacebook": "InvalidLocationResponseFacebook",
"InvalidStartBranchResponse": "InvalidStartBranchResponse",
"LocationNotFound": "LocationNotFound",
"Locality": "Locality",
"MultipleResultsFound": "MultipleResultsFound",
"NoFavoriteLocationsFound": "NoFavoriteLocationsFound",
"OtherComand": "OtherComand",
"OtherLocation": "OtherLocation",
"PostalCode": "PostalCode",
"Region": "Region",
"ResetPrompt": "ResetPrompt",
"SelectFavoriteLocationPrompt" : "SelectFavoriteLocationPrompt",
"SingleResultFound": "SingleResultFound",
"StreetAddress": "StreetAddress",
"TitleSuffixFacebook": "TitleSuffixFacebook",

Просмотреть файл

@ -3,10 +3,8 @@ import * as common from '../common';
import { Strings } from '../consts';
import { FavoriteLocation } from '../favorite-location';
import { FavoritesManager } from '../services/favorites-manager';
import * as confirmDialog from './confirm-dialog';
export function register(library: Library): void {
confirmDialog.register(library);
library.dialog('add-favorite-location-dialog', createDialog());
library.dialog('name-favorite-location-dialog', createNameFavoriteLocationDialog());
}
@ -53,7 +51,7 @@ function createNameFavoriteLocationDialog() {
location: session.dialogData.place,
name : session.message.text
};
const favoritesManager = new FavoritesManager(session.userData);
const favoritesManager = new FavoritesManager(session.userData);
favoritesManager.add(favoriteLocation);
session.send(session.gettext(Strings.FavoriteAddedConfirmation, favoriteLocation.name));
session.endDialogWithResult({ response: {} });

Просмотреть файл

@ -4,7 +4,7 @@ import { Strings } from '../consts';
import { Place } from '../place';
export function register(library: Library): void {
library.dialog('choice-dialog', createDialog());
library.dialog('choose-location-dialog', createDialog());
}
function createDialog() {
@ -23,8 +23,7 @@ function createDialog() {
if (match) {
var currentNumber = Number(match[0]);
if (currentNumber > 0 && currentNumber <= session.dialogData.locations.length) {
var place = common.processLocation(session.dialogData.locations[currentNumber - 1], true);
session.endDialogWithResult({ response: { place: place } });
session.endDialogWithResult({ response: { place: session.dialogData.locations[currentNumber - 1] } });
return;
}
}

Просмотреть файл

@ -1,10 +1,8 @@
import { IDialogResult, Library, Session } from 'botbuilder';
import * as common from '../common';
import { Strings } from '../consts';
import * as confirmDialog from './confirm-dialog';
export function register(library: Library): void {
library.dialog('single-location-confirm-dialog', createDialog());
library.dialog('confirm-single-location-dialog', createDialog());
}
function createDialog() {
@ -16,8 +14,7 @@ function createDialog() {
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.confirmed) {
// User did confirm the single location offered
const place = common.processLocation(session.dialogData.locations[0], true);
session.endDialogWithResult({ response: { place: place } });
session.endDialogWithResult({ response: { place: session.dialogData.locations[0] } });
}
else {
// User said no

Просмотреть файл

@ -0,0 +1,35 @@
import { IDialogResult, Library, Session } from 'botbuilder';
import { Strings } from '../consts';
import { FavoritesManager } from '../services/favorites-manager';
export function register(library: Library, apiKey: string): void {
library.dialog('delete-favorite-location-dialog', createDialog());
}
function createDialog() {
return [
// Ask the user to confirm deleting the favorite location
(session: Session, args: any) => {
session.dialogData.args = args;
session.dialogData.toBeDeleted = args.toBeDeleted;
const deleteFavoriteConfirmationAsk = session.gettext(Strings.DeleteFavoriteConfirmationAsk, args.toBeDeleted.name)
session.beginDialog('confirm-dialog', { confirmationPrompt: deleteFavoriteConfirmationAsk });
},
// Check whether the user confirmed
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.confirmed) {
const favoritesManager = new FavoritesManager(session.userData);
favoritesManager.delete(session.dialogData.toBeDeleted);
session.send(session.gettext(Strings.FavoriteDeletedConfirmation, session.dialogData.toBeDeleted.name));
session.replaceDialog('retrieve-favorite-location-dialog', session.dialogData.args);
}
else if (results.response && results.response.confirmed === false) {
session.send(session.gettext(Strings.DeleteFavoriteAbortion));
session.replaceDialog('retrieve-favorite-location-dialog', session.dialogData.args);
}
else {
next(results);
}
}
]
}

Просмотреть файл

@ -0,0 +1,34 @@
import { IDialogResult, Library, Session } from 'botbuilder';
import { Strings } from '../consts';
import { FavoriteLocation } from '../favorite-location';
import { FavoritesManager } from '../services/favorites-manager';
export function register(library: Library, apiKey: string): void {
library.dialog('edit-favorite-location-dialog', createDialog());
}
function createDialog() {
return [
(session: Session, args: any) => {
session.dialogData.args = args;
session.dialogData.toBeEditted = args.toBeEditted;
session.send(session.gettext(Strings.EditFavoritePrompt, args.toBeEditted.name));
session.beginDialog('retrieve-location-dialog', session.dialogData.args);
},
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.place) {
const favoritesManager = new FavoritesManager(session.userData);
const newfavoriteLocation: FavoriteLocation = {
location: results.response.place,
name: session.dialogData.toBeEditted.name
};
favoritesManager.update(session.dialogData.toBeEditted, newfavoriteLocation);
session.send(session.gettext(Strings.FavoriteEdittedConfirmation, session.dialogData.toBeEditted.name));
session.endDialogWithResult({ response: { place: results.response.place } });
}
else {
next(results);
}
}
]
}

Просмотреть файл

@ -12,15 +12,15 @@ export enum LocationRequiredFields {
}
export function register(library: Library): void {
library.dialog('required-fields-dialog', createDialog());
library.dialog('require-fields-dialog', createDialog());
}
const fields: Array<any> = [
{ name: "streetAddress", prompt: Strings.StreetAddress, flag: LocationRequiredFields.streetAddress },
{ name: "addressLine", prompt: Strings.StreetAddress, flag: LocationRequiredFields.streetAddress },
{ name: "locality", prompt: Strings.Locality, flag: LocationRequiredFields.locality },
{ name: "region", prompt: Strings.Region, flag: LocationRequiredFields.region },
{ name: "adminDistrict", prompt: Strings.Region, flag: LocationRequiredFields.region },
{ name: "postalCode", prompt: Strings.PostalCode, flag: LocationRequiredFields.postalCode },
{ name: "country", prompt: Strings.Country, flag: LocationRequiredFields.country },
{ name: "countryRegion", prompt: Strings.Country, flag: LocationRequiredFields.country },
];
function createDialog() {
@ -68,12 +68,12 @@ function createDialog() {
}
function completeFieldIfMissing(session: Session, field: any) {
if ((field.flag & session.dialogData.requiredFieldsFlag) && !session.dialogData.place[field.name]) {
if ((field.flag & session.dialogData.requiredFieldsFlag) && !session.dialogData.place.address[field.name]) {
var prefix: string = "";
var prompt: string = "";
if (typeof session.dialogData.lastInput === "undefined") {
var formattedAddress: string = common.getFormattedAddressFromPlace(session.dialogData.place, session.gettext(Strings.AddressSeparator));
var formattedAddress: string = common.getFormattedAddressFromLocation(session.dialogData.place, session.gettext(Strings.AddressSeparator));
if (formattedAddress) {
prefix = session.gettext(Strings.AskForPrefix, formattedAddress);
prompt = session.gettext(Strings.AskForTemplate, session.gettext(field.prompt));

Просмотреть файл

@ -1,16 +1,15 @@
import * as common from '../common';
import { Strings } from '../consts';
import { Session, IDialogResult, Library, AttachmentLayout, HeroCard, CardImage, Message } from 'botbuilder';
import { Place } from '../Place';
import { MapCard } from '../map-card'
import { Session, IDialogResult, Library } from 'botbuilder';
import * as locationService from '../services/bing-geospatial-service';
import * as singleLocationConfirmDialog from './single-location-confirm-dialog';
import * as choiceDialog from './choice-dialog';
import * as confirmSingleLocationDialog from './confirm-single-location-dialog';
import * as chooseLocationDialog from './choose-location-dialog';
import { LocationCardBuilder } from '../services/location-card-builder';
export function register(library: Library, apiKey: string): void {
singleLocationConfirmDialog.register(library);
choiceDialog.register(library);
library.dialog('default-location-dialog', createDialog());
confirmSingleLocationDialog.register(library);
chooseLocationDialog.register(library);
library.dialog('resolve-bing-location-dialog', createDialog());
library.dialog('location-resolve-dialog', createLocationResolveDialog(apiKey));
}
@ -25,9 +24,9 @@ function createDialog() {
var locations = results.response.locations;
if (locations.length == 1) {
session.beginDialog('single-location-confirm-dialog', { locations: locations });
session.beginDialog('confirm-single-location-dialog', { locations: locations });
} else {
session.beginDialog('choice-dialog', { locations: locations });
session.beginDialog('choose-location-dialog', { locations: locations });
}
}
else {
@ -55,37 +54,11 @@ function createLocationResolveDialog(apiKey: string) {
var locationCount = Math.min(MAX_CARD_COUNT, locations.length);
locations = locations.slice(0, locationCount);
var reply = createLocationsCard(apiKey, session, locations);
var reply = new LocationCardBuilder(apiKey).createHeroCards(session, locations);
session.send(reply);
session.endDialogWithResult({ response: { locations: locations } });
})
.catch(error => session.error(error));
});
}
function createLocationsCard(apiKey: string, session: Session, locations: any) {
var cards = new Array();
for (var i = 0; i < locations.length; i++) {
cards.push(constructCard(apiKey, session, locations, i));
}
return new Message(session)
.attachmentLayout(AttachmentLayout.carousel)
.attachments(cards);
}
function constructCard(apiKey: string, session: Session, locations: Array<any>, index: number): HeroCard {
var location = locations[index];
var card = new MapCard(apiKey, session);
if (locations.length > 1) {
card.location(location, index + 1);
}
else {
card.location(location);
}
return card;
}

Просмотреть файл

@ -1,11 +1,11 @@
import { Strings } from '../consts';
import * as common from '../common';
import { Session, IDialogResult, Library, AttachmentLayout, HeroCard, CardImage, Message } from 'botbuilder';
import { Place } from '../Place';
import { Session, IDialogResult, Library, Message } from 'botbuilder';
import * as locationService from '../services/bing-geospatial-service';
import { RawLocation } from '../rawLocation';
export function register(library: Library, apiKey: string): void {
library.dialog('facebook-location-dialog', createDialog(apiKey));
library.dialog('retrive-facebook-location-dialog', createDialog(apiKey));
library.dialog('facebook-location-resolve-dialog', createLocationResolveDialog());
}
@ -17,11 +17,11 @@ function createDialog(apiKey: string) {
},
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (session.dialogData.args.reverseGeocode && results.response && results.response.place) {
locationService.getLocationByPoint(apiKey, results.response.place.geo.latitude, results.response.place.geo.longitude)
locationService.getLocationByPoint(apiKey, results.response.place.point.coordinates[0], results.response.place.point.coordinates[1])
.then(locations => {
var place: Place;
var place: RawLocation;
if (locations.length) {
place = common.processLocation(locations[0], false);
place = locations[0];
} else {
place = results.response.place;
}
@ -47,7 +47,7 @@ function createLocationResolveDialog() {
var entities = session.message.entities;
for (var i = 0; i < entities.length; i++) {
if (entities[i].type == "Place" && entities[i].geo && entities[i].geo.latitude && entities[i].geo.longitude) {
session.endDialogWithResult({ response: { place: common.buildPlaceFromGeo(entities[i].geo.latitude, entities[i].geo.longitude) } });
session.endDialogWithResult({ response: { place: buildLocationFromGeo(entities[i].geo.latitude, entities[i].geo.longitude) } });
return;
}
}
@ -69,4 +69,9 @@ function sendLocationPrompt(session: Session, prompt: string): Session {
});
return session.send(message);
}
function buildLocationFromGeo(latitude: string, longitude: string) {
let coordinates = [ latitude, longitude];
return { point : { coordinates : coordinates } };
}

Просмотреть файл

@ -0,0 +1,99 @@
import * as common from '../common';
import { Strings } from '../consts';
import { Session, Library } from 'botbuilder';
import { LocationCardBuilder } from '../services/location-card-builder';
import { FavoritesManager } from '../services/favorites-manager';
import * as deleteFavoriteLocationDialog from './delete-favorite-location-dialog';
import * as editFavoriteLocationDialog from './edit-fravorite-location-dialog';
import { RawLocation } from '../rawLocation';
export function register(library: Library, apiKey: string): void {
library.dialog('retrieve-favorite-location-dialog', createDialog(apiKey));
deleteFavoriteLocationDialog.register(library, apiKey);
editFavoriteLocationDialog.register(library, apiKey);
}
function createDialog(apiKey: string) {
return common.createBaseDialog()
.onBegin(function (session, args) {
session.dialogData.args = args;
const favoritesManager = new FavoritesManager(session.userData);
const userFavorites = favoritesManager.getFavorites();
// If the user has no favorite locations, switch to a normal location retriever dialog
if (userFavorites.length == 0) {
session.send(session.gettext(Strings.NoFavoriteLocationsFound));
session.replaceDialog('retrieve-location-dialog', session.dialogData.args);
return;
}
session.dialogData.userFavorites = userFavorites;
let locations: RawLocation[] = [];
let names: string[] = [];
for (let i = 0; i < userFavorites.length; i++) {
locations.push(userFavorites[i].location);
names.push(userFavorites[i].name);
}
session.send(new LocationCardBuilder(apiKey).createHeroCards(session, locations, true, names));
session.send(session.gettext(Strings.SelectFavoriteLocationPrompt)).sendBatch();
}).onDefault((session) => {
const text: string = session.message.text;
if (text === session.gettext(Strings.OtherComand)) {
session.replaceDialog('retrieve-location-dialog', session.dialogData.args);
}
else {
const selection = tryParseCommandSelection(text, session.dialogData.userFavorites.length);
if ( selection.command === "select" ) {
// complete required fields
session.replaceDialog('require-fields-dialog', {
place: session.dialogData.userFavorites[selection.index - 1].location,
requiredFields: session.dialogData.args.requiredFields
});
}
else if (selection.command === session.gettext(Strings.DeleteCommand) ) {
session.dialogData.args.toBeDeleted = session.dialogData.userFavorites[selection.index - 1];
session.replaceDialog('delete-favorite-location-dialog', session.dialogData.args);
}
else if (selection.command === session.gettext(Strings.EditCommand)) {
session.dialogData.args.toBeEditted = session.dialogData.userFavorites[selection.index - 1];
session.replaceDialog('edit-favorite-location-dialog', session.dialogData.args);
}
else {
session.send(session.gettext(Strings.InvalidFavoriteLocationSelection)).sendBatch();
}
}
});
}
function tryParseNumberSelection(text: string): number {
const tokens = text.trim().split(' ');
if (tokens.length == 1) {
const numberExp = /[+-]?(?:\d+\.?\d*|\d*\.?\d+)/;
const match = numberExp.exec(text);
if (match) {
return Number(match[0]);
}
}
return -1;
}
function tryParseCommandSelection(text: string, maxIndex: number): any {
const tokens = text.trim().split(' ');
if (tokens.length == 1) {
const index = tryParseNumberSelection(text);
if (index > 0 && index <= maxIndex) {
return { index: index, command: "select" };
}
}
else if (tokens.length == 2) {
const index = tryParseNumberSelection(tokens[1]);
if (index > 0 && index <= maxIndex) {
return { index: index, command: tokens[0] };
}
}
return { command: ""};
}

Просмотреть файл

@ -0,0 +1,34 @@
import { IDialogResult, Library, Session } from 'botbuilder';
import * as resolveBingLocationDialog from './resolve-bing-location-dialog';
import * as retrieveFacebookLocationDialog from './retrieve-facebook-location-dialog';
export function register(library: Library, apiKey: string): void {
library.dialog('retrieve-location-dialog', createDialog());
resolveBingLocationDialog.register(library, apiKey);
retrieveFacebookLocationDialog.register(library, apiKey);
}
function createDialog() {
return [
(session: Session, args: any) => {
session.dialogData.args = args;
if (args.useNativeControl && session.message.address.channelId == 'facebook') {
session.beginDialog('retrieve-facebook-location-dialog', args);
}
else {
session.beginDialog('resolve-bing-location-dialog', args);
}
},
// complete required fields, if applicable
(session: Session, results: IDialogResult<any>, next: (results?: IDialogResult<any>) => void) => {
if (results.response && results.response.place) {
session.beginDialog('require-fields-dialog', {
place: results.response.place,
requiredFields: session.dialogData.args.requiredFields
})
} else {
next(results);
}
}
]
}

Просмотреть файл

@ -1,6 +1,6 @@
import { Place } from './place';
import { RawLocation } from './rawLocation';
export class FavoriteLocation {
name: string;
location: Place;
location: RawLocation;
}

Просмотреть файл

@ -8,17 +8,32 @@
"ConfirmationAsk": "OK, I will ship to %s. Is that correct? Enter 'yes' or 'no'.",
"Country": "country",
"DefaultPrompt": "Where should I ship your order?",
"DeleteCommand": "delete",
"DeleteFavoriteAbortion": "OK, deletion aborted.",
"DeleteFavoriteConfirmationAsk": "Are you sure you want to delete %s from your favorite locations?",
"DialogStartBranchAsk": "How would you like to pick a location?",
"EditCommand": "edit",
"EditFavoritePrompt": "OK, let's edit %s. Enter a new address.",
"EnterNewFavoriteLocationName": "OK, please enter a friendly name for this address. You can use 'home', 'work' or any other name you prefer.",
"FavoriteAddedConfirmation": "OK, I added %s to your favorite locations.",
"FavoriteDeletedConfirmation": "OK, I deleted %s from your favorite locations.",
"FavoriteEdittedConfirmation": "OK, I editted %s in your favorite locations with this new address.",
"FavoriteLocations": "Favorite Locations",
"HelpMessage": "Say or type a valid address when asked, and I will try to find it using Bing. You can provide the full address information (street no. / name, city, region, postal/zip code, country) or a part of it. If you want to change the address, say or type 'reset'. Finally, say or type 'cancel' to exit without providing an address.",
"InvalidFavoriteLocationSelection": "Type or say a number to choose the address, enter 'other' to create a new favorite location, or enter 'cancel' to exit. You can also type or say 'edit' or 'delete' followed by a number to edit or delete the respective location.",
"InvalidLocationResponse": "Didn't get that. Choose a location or cancel.",
"InvalidLocationResponseFacebook": "Tap on Send Location to proceed; type or say cancel to exit.",
"InvalidStartBranchResponse": "Tap one of the options to proceed; type or say cancel to exit.",
"LocationNotFound": "I could not find this address. Please try again.",
"Locality": "city or locality",
"MultipleResultsFound": "I found these results. Type or say a number to choose the address, or enter 'other' to select another address.",
"NoFavoriteLocationsFound": "You do not seem to have any favorite locations at the moment. Enter an address and you will be able to save it to your favorite locations.",
"OtherComand": "other",
"OtherLocation": "Other Location",
"PostalCode": "zip or postal code",
"Region": "state or region",
"ResetPrompt": "OK, let's start over.",
"SelectFavoriteLocationPrompt": "Here are your favorite locations. Type or say a number to use the respective location, or 'other' to use a different location. You can also type or say 'edit' or 'delete' followed by a number to edit or delete the respective location.",
"SingleResultFound": "I found this result. Is this the correct address?",
"StreetAddress": "street address",
"TitleSuffix": " Type or say an address",

Просмотреть файл

@ -1,5 +1,6 @@
import { Session, HeroCard, CardImage } from 'botbuilder';
import { Place, Geo } from './place';
import { RawLocation } from './rawLocation'
import * as locationService from './services/bing-geospatial-service';
export class MapCard extends HeroCard {
@ -8,14 +9,18 @@ export class MapCard extends HeroCard {
super(session);
}
public location(location: any, index?: number): this {
var indexText = "";
public location(location: RawLocation, index?: number, locationName?: string): this {
var prefixText = "";
if (index !== undefined) {
indexText = index + ". ";
prefixText = index + ". ";
}
this.subtitle(indexText + location.address.formattedAddress)
if (locationName !== undefined) {
prefixText += locationName + ": ";
}
this.subtitle(prefixText + location.address.formattedAddress);
if (location.point) {
var locationUrl: string;
try {

Просмотреть файл

@ -0,0 +1,23 @@
export class RawLocation {
address: Address;
bbox: Array<number>;
confidence: string;
entityType: string;
name: string;
point: Point;
}
class Address {
addressLine: string;
adminDistrict: string;
adminDistrict2: string;
countryRegion: string;
formattedAddress: string;
locality: string;
postalCode: string;
}
class Point {
coordinates: Array<number>;
calculationMethod: string;
}

Просмотреть файл

@ -1,5 +1,6 @@
import * as rp from 'request-promise';
import { sprintf } from 'sprintf-js';
import { RawLocation } from '../rawLocation'
const formAugmentation = "&form=BTCTRL"
const findLocationByQueryUrl = "https://dev.virtualearth.net/REST/v1/Locations?" + formAugmentation;
@ -7,18 +8,18 @@ const findLocationByPointUrl = "https://dev.virtualearth.net/REST/v1/Locations/%
const findImageByPointUrl = "https://dev.virtualearth.net/REST/V1/Imagery/Map/Road/%1$s,%2$s/15?mapSize=500,280&pp=%1$s,%2$s;1;%3$s&dpi=1&logo=always" + formAugmentation;
const findImageByBBoxUrl = "https://dev.virtualearth.net/REST/V1/Imagery/Map/Road?mapArea=%1$s,%2$s,%3$s,%4$s&mapSize=500,280&pp=%5$s,%6$s;1;%7$s&dpi=1&logo=always" + formAugmentation;
export function getLocationByQuery(apiKey: string, address: string): Promise<Array<any>> {
export function getLocationByQuery(apiKey: string, address: string): Promise<Array<RawLocation>> {
var url = addKeyToUrl(findLocationByQueryUrl, apiKey) + "&q=" + encodeURIComponent(address);
return getLocation(url);
}
export function getLocationByPoint(apiKey: string, latitude: string, longitude: string): Promise<Array<any>> {
export function getLocationByPoint(apiKey: string, latitude: string, longitude: string): Promise<Array<RawLocation>> {
var url: string = sprintf(findLocationByPointUrl, latitude, longitude);
url = addKeyToUrl(url, apiKey) + "&q=";
return getLocation(url);
}
export function GetLocationMapImageUrl(apiKey: string, location: any, index?: number) {
export function GetLocationMapImageUrl(apiKey: string, location: RawLocation, index?: number) {
if (location && location.point && location.point.coordinates && location.point.coordinates.length == 2) {
var point = location.point;
@ -40,7 +41,7 @@ export function GetLocationMapImageUrl(apiKey: string, location: any, index?: nu
throw "Invalid Location Format: " + location;
}
function getLocation(url: string): Promise<Array<any>> {
function getLocation(url: string): Promise<Array<RawLocation>> {
const requestData = {
url: url,
json: true

Просмотреть файл

@ -1,23 +1,23 @@
import { FavoriteLocation } from '../favorite-location';
import { Place } from '../place';
import { RawLocation } from '../rawLocation';
export class FavoritesManager {
readonly MAX_FAVORITE_COUNT = 5;
readonly FAVORITES_KEY = 'favorites';
readonly maxFavoriteCount = 5;
readonly favoritesKey = 'favorites';
constructor (private userData : any) {
}
public maxCapacityReached(): boolean {
return this.getFavorites().length >= this.MAX_FAVORITE_COUNT;
return this.getFavorites().length >= this.maxFavoriteCount;
}
public isFavorite(location: Place) : boolean {
public isFavorite(location: RawLocation) : boolean {
let favorites = this.getFavorites();
for (let i = 0; i < favorites.length; i++) {
if (favorites[i].location.formattedAddress === location.formattedAddress) {
if (this.areEqual(favorites[i].location, location)) {
return true;
}
}
@ -28,16 +28,45 @@ export class FavoritesManager {
public add(favoriteLocation: FavoriteLocation): void {
let favorites = this.getFavorites();
if (favorites.length >= this.MAX_FAVORITE_COUNT) {
if (favorites.length >= this.maxFavoriteCount) {
throw ('The max allowed number of favorite locations has already been reached.');
}
favorites.push(favoriteLocation);
this.userData[this.FAVORITES_KEY] = favorites;
this.userData[this.favoritesKey] = favorites;
}
public delete(favoriteLocation: FavoriteLocation): void {
let favorites = this.getFavorites();
let newFavorites = [];
for (let i = 0; i < favorites.length; i++) {
if ( !this.areEqual(favorites[i].location, favoriteLocation.location)) {
newFavorites.push(favorites[i]);
}
}
this.userData[this.favoritesKey] = newFavorites;
}
public update(currentValue: FavoriteLocation, newValue: FavoriteLocation): void {
let favorites = this.getFavorites();
let newFavorites = [];
for (let i = 0; i < favorites.length; i++) {
if ( this.areEqual(favorites[i].location, currentValue.location)) {
newFavorites.push(newValue);
}
else {
newFavorites.push(favorites[i]);
}
}
this.userData[this.favoritesKey] = newFavorites;
}
public getFavorites(): FavoriteLocation[] {
let storedFavorites = this.userData[this.FAVORITES_KEY];
let storedFavorites = this.userData[this.favoritesKey];
if (storedFavorites) {
return storedFavorites;
@ -47,4 +76,12 @@ export class FavoritesManager {
return [];
}
}
private areEqual(location0: RawLocation, location1: RawLocation): boolean {
// Other attributes of a location such as its Confidence, BoundaryBox, etc
// should not be considered as distinguishing factors.
// On the other hand, attributes of a location that are shown to the users
// are what distinguishes one location from another.
return location0.address.formattedAddress === location1.address.formattedAddress;
}
}

Просмотреть файл

@ -0,0 +1,42 @@
import { AttachmentLayout, HeroCard, Message, Session } from 'botbuilder';
import { MapCard } from '../map-card'
import {RawLocation} from '../rawLocation'
export class LocationCardBuilder {
constructor (private apiKey : string) {
}
public createHeroCards(session: Session, locations: Array<RawLocation>, alwaysShowNumericPrefix?: boolean, locationNames?: Array<string>): Message {
let cards = new Array();
for (let i = 0; i < locations.length; i++) {
cards.push(this.constructCard(session, locations, i, alwaysShowNumericPrefix, locationNames));
}
return new Message(session)
.attachmentLayout(AttachmentLayout.carousel)
.attachments(cards);
}
private constructCard(session: Session, locations: Array<RawLocation>, index: number, alwaysShowNumericPrefix?: boolean, locationNames?: Array<string>): HeroCard {
const location = locations[index];
let card = new MapCard(this.apiKey, session);
if (alwaysShowNumericPrefix || locations.length > 1) {
if (locationNames)
{
card.location(location, index + 1, locationNames[index]);
}
else
{
card.location(location, index + 1);
}
}
else {
card.location(location);
}
return card;
}
}

Просмотреть файл

@ -40,7 +40,13 @@ bot.dialog("/", [
function (session, results) {
if (results.response) {
var place = results.response;
session.send("Thanks, I will ship to " + locationDialog.getFormattedAddressFromPlace(place, ", "));
var formattedAddress =
session.send("Thanks, I will ship to " + getFormattedAddressFromPlace(place, ", "));
}
}
]);
]);
function getFormattedAddressFromPlace(place, separator) {
var addressParts = [place.streetAddress, place.locality, place.region, place.postalCode, place.country];
return addressParts.filter(i => i).join(separator);
}