forked from alexa-samples/skill-sample-nodejs-city-guide
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.js
More file actions
397 lines (341 loc) · 16.6 KB
/
index.js
File metadata and controls
397 lines (341 loc) · 16.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
// 1. Text strings =====================================================================================================
// Modify these strings and messages to change the behavior of your Lambda function
const data_en = {
"city" : "Düsseldorf",
"state" : "NRW",
"postcode" : "40000",
"restaurants" : [
{ "name":"Dauser",
"address":"Carlsplatz 4", "phone": "0211 486164",
"meals": "breakfast, lunch, dinner",
"description": "Very good civil dishes for a reasonable price."
},
{ "name":"Rösterei Vier",
"address":"Wallstraße 10", "phone": "0211 8693883",
"meals": "coffee, breakfast, lunch",
"description": "Cool caffe with a small selection of cakes and other sweet pastries."
},
{ "name":"Münstermann Kontor",
"address":"Hohe Straße 11", "phone": "0211 1300416",
"meals": "breakfast, lunch, dinner",
"description": "Delicious. A reservation is recommended."
},
{ "name":"Sternerestaurant Fritz's Frau Franzi",
"address":"Adersstraße 8", "phone": "0211 370750",
"meals": "lunch, dinner",
"description": "Friendly, relaxed and attentive service."
},
{ "name":"Stappen in Oberkassel",
"address":"Luegallee 50", "phone": "0211 93077600",
"meals": "lunch, dinner",
"description": "Fine German cuisine attentively served."
},
{ "name":"Das Coffe",
"address":"Benrather Straße 6B", "phone": "0171 7760800",
"meals": "coffee, breakfast, lunch",
"description": "One of the best cappuccinos in Düsseldorf."
},
],
"attractions":[
{
"name": "Medienhafen",
"description": "Perfect photo opportunity for photographers. The reflecting house must be seen.",
"distance": "0"
},
{
"name": "Düsselstrand",
"description": "Slides and attractions for all ages in a water park.",
"distance": "4"
},
{
"name": "Wildpark Grafenberger Wald",
"description": "Free park with native animals in natural-looking enclosures.",
"distance": "6"
},
{
"name": "Esprit Arena",
"description": "Home of the local soccer club Fortuna Düsseldorf.",
"distance": "10"
}
]
}
const data_de = {
"city" : "Düsseldorf",
"state" : "NRW",
"postcode" : "40000",
"restaurants" : [
{ "name":"Dauser",
"address":"Carlsplatz 4", "phone": "0211 486164",
"meals": "breakfast, lunch, dinner",
"description": "Sehr gute bürgerliche Gerichte zu einem vernünftigen Preis."
},
{ "name":"Rösterei Vier",
"address":"Wallstraße 10", "phone": "0211 8693883",
"meals": "coffee, breakfast, lunch",
"description": "Cooles Caffe mit kleinem Angebot an Kuchen und anderem süßem Gebäck."
},
{ "name":"Münstermann Kontor",
"address":"Hohe Straße 11", "phone": "0211 1300416",
"meals": "breakfast, lunch, dinner",
"description": "Super lecker. Eine Reservierung ist empfehlenswert."
},
{ "name":"Sternerestaurant Fritz's Frau Franzi",
"address":"Adersstraße 8", "phone": "0211 370750",
"meals": "lunch, dinner",
"description": "Freundlicher, lockerer und zuvorkommender Service."
},
{ "name":"Stappen in Oberkassel",
"address":"Luegallee 50", "phone": "0211 93077600",
"meals": "lunch, dinner",
"description": "Gehobene deutsche Küche aufmerksam serviert."
},
{ "name":"Das Coffe",
"address":"Benrather Straße 6B", "phone": "0171 7760800",
"meals": "coffee, breakfast, lunch",
"description": "Einer der besten Cappuccinos in Düsseldorf."
},
],
"attractions":[
{
"name": "Medienhafen",
"description": "Perfektes Fotomotiv für Fotografen. Das spiegelnde Haus muss man gesehen haben.",
"distance": "0"
},
{
"name": "Düsselstrand",
"description": "Rutschen und Attraktionen für alle Altersgruppen in einem Wasserpark.",
"distance": "4"
},
{
"name": "Wildpark Grafenberger Wald",
"description": "Kostenloser Park mit einheimischen Tieren in natürlich wirkenden Gehegen.",
"distance": "6"
},
{
"name": "Esprit Arena",
"description": "Heimat des lokalen Fußballclubs Fortuna Düsseldorf.",
"distance": "10"
}
]
}
const languageStrings = {
'en': {
'translation': {
'DATA' : data_en,
'WELCOME' : 'Welcome to Dusseldorf Guide! Say about, to hear more about the city, or say coffee, breakfast, lunch, or dinner, to hear local restaurant suggestions, or say recommend an attraction, or say, go outside.',
'HELP' : 'Say about, to hear more about the city, or say coffee, breakfast, lunch, or dinner, to hear local restaurant suggestions, or say recommend an attraction, or say, go outside.',
'ABOUT' : 'Dusseldorf is a city on the river rhine. A popular shopping destination, Dusseldorf has a rich history of beer and fashion.',
'STOP' : 'Okay, see you next time!',
'MORE' : 'Would you like to hear more?',
'COFFEE' : 'For a great coffee shop, I recommend {{name}}. Would you like to hear more?',
'BREAKFAST' : 'For breakfast, try this, {{name}}. Would you like to hear more?',
'LUNCH' : 'Lunch time! Here is a good spot. {{name}}. Would you like to hear more?',
'DINNER' : 'Enjoy dinner at {{name}}. Would you like to hear more?',
'ATTRACTION': 'Try {{name}}, which is kilomters away. Have fun! {{description}}',
'ATT_NEAR' : 'Try {{name}}, which is right downtown. {{description}}',
'ATT_NO' : 'I couldn\'t find anything in a {{distance}} kilomters radius.',
'DETAILS' : '{{name}} is located at {{address}}, the phone number is {{phone}}, and the description is, {{description}}. I have sent these details to the Alexa App on your phone. Enjoy your meal! <say-as interpret-as="interjection">bon appetit</say-as>',
'PHONE' : 'phone: ',
'GO_OUT' : 'It is {{localTime}} and the weather in {{city}} is {{currentTemp}} and {{currentCondition}}'
}
},
'de-DE': {
'translation': {
'DATA' : data_de,
'WELCOME' : 'Willkommen beim Düsseldorf Reiseführer! Sage über, um mehr über die Stadt zu erfahren, oder sage Kaffee, Frühstück, Mittag, oder Abendessen, um lokale Restaurantvorschläge zu erhalten, oder sage empfehle eine Attraktion, oder sage, geh nach draußen.',
'HELP' : 'Sage über, um mehr über die Stadt zu erfahren, oder sage Kaffee, Frühstück, Mittag, oder Abendessen, um lokale Restaurantvorschläge zu erhalten, oder sage empfehle eine Attraktion, oder sage, geh nach draußen.',
'ABOUT' : 'Düsseldorf ist eine Stadt am Rhein. Die Messe- und Sportstadt ist ein beliebtes Ausflugsziel zum Einkaufen und hat eine umfangreiche Geschichte rund um Bier und Mode.',
'STOP' : 'Okay, bis bald!',
'MORE' : 'Möchtest du mehr erfahren?',
'COFFEE' : 'Für einen guten Kaffee empfehle ich {{name}} Möchtest du mehr erfahren?',
'BREAKFAST' : 'Probiere {{name}} für ein Frühstück. Möchtest du mehr erfahren?',
'LUNCH' : 'Mittagszeit! Hier ist es gut: {{name}}. Möchtest du mehr erfahren?',
'DINNER' : 'Genieße das Abendessen bei {{name}}. Möchtest du mehr erfahren?',
'ATTRACTION': 'Versuche {{name}}, es ist {{description}} Kilomter entfernt. Viel Spaß! {{description}}',
'ATT_NEAR' : 'Versuche {{name}}, es ist in der Innenstadt. {{description}}',
'ATT_NO' : 'Ich konnte leider nichts im Umkreis von {{distance}} Kilomtern finden!',
'DETAILS' : '{{name}} findest du an der Adresse {{address}}, die Telefonnummer ist {{phone}}, und die Beschreibung ist, {{description}}. Ich habe diese Angaben an deine Alexa App geschickt. <say-as interpret-as="interjection">bon appetit</say-as>',
'PHONE' : 'Telefon: ',
'GO_OUT' : 'Es ist {{localTime}} und das Wetter in {{city}} ist {{currentTemp}} und {{currentCondition}}'
}
}
};
const SKILL_NAME = "Dusseldorf Guide";
// Weather courtesy of the Yahoo Weather API.
// This free API recommends no more than 2000 calls per day
// This concrete API only provides mph, fahrenheit, and english results.
const myAPI = {
host: 'query.yahooapis.com',
port: 443,
path: `/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22${encodeURIComponent(data_de.city)}%2C%20${data_de.state}%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`,
method: 'GET'
};
// 2. Skill Code =======================================================================================================
const Alexa = require('alexa-sdk');
exports.handler = function(event, context, callback) {
var alexa = Alexa.handler(event, context);
// alexa.appId = 'amzn1.echo-sdk-ams.app.1234';
///alexa.dynamoDBTableName = 'YourTableName'; // creates new table for session.attributes
alexa.resources = languageStrings;
alexa.registerHandlers(handlers);
alexa.execute();
};
const handlers = {
'LaunchRequest': function () {
this.response.speak(this.t('WELCOME')).listen(this.t('HELP'));
this.emit(':responseReady');
},
'AboutIntent': function () {
this.response.speak(this.t('ABOUT'));
this.emit(':responseReady');
},
'CoffeeIntent': function () {
var locale = this.event.request.locale;
var data;
if (locale === "de-DE") {
data = data_de;
} else {
data = data_en;
}
var restaurant = randomArrayElement(getRestaurantsByMeal('coffee', data));
this.attributes['restaurant'] = restaurant.name;
this.response.speak(this.t('COFFEE', {name: restaurant.name})).listen(this.t('MORE'));
this.emit(':responseReady');
},
'BreakfastIntent': function () {
var data = this.t('DATA');
var restaurant = randomArrayElement(getRestaurantsByMeal('breakfast', data));
this.attributes['restaurant'] = restaurant.name;
this.response.speak(this.t('BREAKFAST', {name: restaurant.name})).listen(this.t('MORE'));
this.emit(':responseReady');
},
'LunchIntent': function () {
var data = this.t('DATA');
var restaurant = randomArrayElement(getRestaurantsByMeal('lunch', data));
this.attributes['restaurant'] = restaurant.name;
this.response.speak(this.t('LUNCH', {name: restaurant.name})).listen(this.t('MORE'));
this.emit(':responseReady');
},
'DinnerIntent': function () {
var data = this.t('DATA');
var restaurant = randomArrayElement(getRestaurantsByMeal('dinner', data));
this.attributes['restaurant'] = restaurant.name;
this.response.speak(this.t('DINNER', {name: restaurant.name})).listen(this.t('MORE'));
this.emit(':responseReady');
},
'AMAZON.YesIntent': function () {
var data = this.t('DATA');
var restaurantName = this.attributes['restaurant'];
var restaurantDetails = getRestaurantByName(restaurantName, data);
var card = restaurantDetails.name + '\n' + restaurantDetails.address + '\n'
+ data.city + ', ' + data.state + ' ' + data.postcode
+ '\n' + this.t('PHONE') + restaurantDetails.phone + '\n';
this.response.cardRenderer(SKILL_NAME, card);
this.response.speak(this.t('DETAILS', {name: restaurantDetails.name, address: restaurantDetails.address, phone:restaurantDetails.phone, description: restaurantDetails.description}));
this.emit(':responseReady');
},
'AttractionIntent': function () {
var data = this.t('DATA');
var distance = 200;
if (this.event.request.intent.slots.distance.value) {
distance = this.event.request.intent.slots.distance.value;
}
var attraction = randomArrayElement(getAttractionsByDistance(distance, data));
if (!attraction) {
this.response.speak(this.t('ATT_NO', {distance: distance}));
} else if (attraction.distance == "0") {
this.response.speak(this.t('ATT_NEAR', {name: attraction.name, distance: attraction.distance, description: attraction.description}));
} else {
this.response.speak(this.t('ATTRACTION', {name: attraction.name, distance: attraction.distance, description: attraction.description}));
}
this.emit(':responseReady');
},
'GoOutIntent': function () {
// This concrete API only provides mph, fahrenheit, and english results.
getWeather( ( localTime, currentTemp, currentCondition) => {
// time format 10:34 PM
// currentTemp 72
// currentCondition, e.g. Sunny, Breezy, Thunderstorms, Showers, Rain, Partly Cloudy, Mostly Cloudy, Mostly Sunny
// sample API URL for Irvine, CA
// https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22irvine%2C%20ca%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys
var data = this.t('DATA');
this.response.speak(this.t('GO_OUT', {localTime: localTime, city:data.city, currentTemp:currentTemp, currentCondition:currentCondition}));
this.emit(':responseReady');
// TODO
// Decide, based on current time and weather conditions,
// whether to go out to a local beach or park;
// or recommend a movie theatre; or recommend staying home
});
},
'AMAZON.NoIntent': function () {
this.emit('AMAZON.StopIntent');
},
'AMAZON.HelpIntent': function () {
this.response.speak(this.t('HELP')).listen(this.t('HELP'));
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak(this.t('STOP'));
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.emit('SessionEndedRequest');
},
'SessionEndedRequest': function () {
this.response.speak(this.t('STOP'));
this.emit(':responseReady');
}
};
// END of Intent Handlers {} ========================================================================================
// 3. Helper Function =================================================================================================
function getRestaurantsByMeal(mealtype, data) {
var list = [];
for (var i = 0; i < data.restaurants.length; i++) {
if(data.restaurants[i].meals.search(mealtype) > -1) {
list.push(data.restaurants[i]);
}
}
return list;
}
function getRestaurantByName(restaurantName, data) {
var restaurant = {};
for (var i = 0; i < data.restaurants.length; i++) {
if(data.restaurants[i].name == restaurantName) {
restaurant = data.restaurants[i];
}
}
return restaurant;
}
function getAttractionsByDistance(maxDistance, data) {
var list = [];
for (var i = 0; i < data.attractions.length; i++) {
if(parseInt(data.attractions[i].distance) <= maxDistance) {
list.push(data.attractions[i]);
}
}
return list;
}
function getWeather(callback) {
var https = require('https');
var req = https.request(myAPI, res => {
res.setEncoding('utf8');
var returnData = "";
res.on('data', chunk => {
returnData = returnData + chunk;
});
res.on('end', () => {
var channelObj = JSON.parse(returnData).query.results.channel;
var localTime = channelObj.lastBuildDate.toString();
localTime = localTime.substring(17, 25).trim();
var currentTemp = channelObj.item.condition.temp;
var currentCondition = channelObj.item.condition.text;
callback(localTime, currentTemp, currentCondition);
});
});
req.end();
}
function randomArrayElement(array) {
var i = 0;
i = Math.floor(Math.random() * array.length);
return(array[i]);
}