├── README.md └── src └── lambda └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # PillPal Alexa 2 | 3 | The code related to Alexa used for the [PillPal](https://github.com/CompSciLauren/pill-pal) project. 4 | -------------------------------------------------------------------------------- /src/lambda/index.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | exports.handler = function (event, context) { 3 | try { 4 | console.log("event.session.application.applicationId=" + event.session.application.applicationId); 5 | 6 | 7 | if (event.session.new) { 8 | onSessionStarted({requestId: event.request.requestId}, event.session); 9 | } 10 | 11 | if (event.request.type === "LaunchRequest") { 12 | onLaunch(event.request, 13 | event.session, 14 | function callback(sessionAttributes, speechletResponse) { 15 | context.succeed(buildResponse(sessionAttributes, speechletResponse)); 16 | }); 17 | } else if (event.request.type === "IntentRequest") { 18 | onIntent(event.request, 19 | event.session, 20 | function callback(sessionAttributes, speechletResponse) { 21 | context.succeed(buildResponse(sessionAttributes, speechletResponse)); 22 | }); 23 | } else if (event.request.type === "SessionEndedRequest") { 24 | onSessionEnded(event.request, event.session); 25 | context.succeed(); 26 | } 27 | } catch (e) { 28 | context.fail("Exception: " + e); 29 | } 30 | }; 31 | 32 | /** 33 | * Called when the session starts. 34 | */ 35 | function onSessionStarted(sessionStartedRequest, session) { 36 | console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId 37 | + ", sessionId=" + session.sessionId); 38 | 39 | // add any session init logic here 40 | } 41 | 42 | /** 43 | * Called when the user invokes the skill without specifying what they want. 44 | */ 45 | function onLaunch(launchRequest, session, callback) { 46 | console.log("onLaunch requestId=" + launchRequest.requestId 47 | + ", sessionId=" + session.sessionId); 48 | 49 | var cardTitle = "Hello, World!"; 50 | var speechOutput = "Welcome to Pill Pal! What would you like to do or know about?"; 51 | callback(session.attributes, 52 | buildSpeechletResponse(cardTitle, speechOutput, "", false)); 53 | } 54 | 55 | /** 56 | * Called when the user specifies an intent for this skill. 57 | */ 58 | function onIntent(intentRequest, session, callback) { 59 | console.log("onIntent requestId=" + intentRequest.requestId 60 | + ", sessionId=" + session.sessionId); 61 | 62 | var intent = intentRequest.intent, 63 | intentName = intentRequest.intent.name; 64 | 65 | // dispatch custom intents to handlers here 66 | switch(intentName) 67 | { 68 | case 'getName': 69 | giveName(intent, session, callback); 70 | break; 71 | case 'getPills': 72 | giveCurrentPills(intent, session, callback); 73 | break; 74 | case 'addPill': 75 | addPill(intent, session, callback); 76 | break; 77 | case 'removeOnePill': 78 | removeOnePill(intent, session, callback); 79 | break; 80 | case 'removeAllPills': 81 | removeAllPills(intent, session, callback); 82 | break; 83 | default: 84 | throw "Invalid intent"; 85 | } 86 | } 87 | 88 | /** 89 | * Called when the user ends the session. 90 | * Is not called when the skill returns shouldEndSession=true. 91 | */ 92 | function onSessionEnded(sessionEndedRequest, session) { 93 | console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId 94 | + ", sessionId=" + session.sessionId); 95 | 96 | // Add any cleanup logic here 97 | } 98 | 99 | function giveName(intent, session, callback) { 100 | http.get({ 101 | host: 'pillpal-app.de', 102 | path: '/User/0', 103 | }, function(res) { 104 | res.setEncoding('utf8'); 105 | // Continuously update stream with data 106 | var body = ''; 107 | res.on('data', function(d) { 108 | body += d; 109 | }); 110 | res.on('end', function() { 111 | 112 | try { 113 | // console.log(body); 114 | var parsed = JSON.parse(body); 115 | // callback(parsed.MRData); 116 | 117 | callback(session.attributes, 118 | buildSpeechletResponseWithoutCard("You are currently managing the account that belongs to " + parsed[0].First_Name + " " + parsed[0].Last_Name + ".", "", "true")); 119 | 120 | //return parsed.MRData; 121 | } catch (err) { 122 | console.error('Unable to parse response as JSON', err); 123 | throw(err); 124 | } 125 | }); 126 | }).on('error', function(err) { 127 | // handle errors with the request itself 128 | console.error('Error with the request:', err.message); 129 | throw(err); 130 | }); 131 | } 132 | 133 | function giveCurrentPills(intent, session, callback) { 134 | http.get({ 135 | host: 'pillpal-app.de', 136 | path: '/Takes/0', 137 | }, function(res) { 138 | res.setEncoding('utf8'); 139 | // Continuously update stream with data 140 | var body = ''; 141 | res.on('data', function(d) { 142 | body += d; 143 | }); 144 | res.on('end', function() { 145 | 146 | try { 147 | // console.log(body); 148 | var parsed = JSON.parse(body); 149 | // callback(parsed.MRData); 150 | if (parsed.length > 0) 151 | { 152 | let listOfMeds = ""; 153 | for (let i = 0; i < parsed.length; i++) 154 | { 155 | if (i == 0) 156 | { 157 | listOfMeds += parsed[i].Display_Name; 158 | } 159 | else 160 | { 161 | listOfMeds += " and " + parsed[i].Display_Name; 162 | } 163 | } 164 | callback(session.attributes, 165 | buildSpeechletResponseWithoutCard("You are currently taking " + listOfMeds + ".", "", "true")); 166 | } 167 | else 168 | { 169 | callback(session.attributes, 170 | buildSpeechletResponseWithoutCard("You are not currently taking any medication.", "", "true")); 171 | } 172 | 173 | //return parsed.MRData; 174 | } catch (err) { 175 | console.error('Unable to parse response as JSON', err); 176 | throw(err); 177 | } 178 | }); 179 | }).on('error', function(err) { 180 | // handle errors with the request itself 181 | console.error('Error with the request:', err.message); 182 | throw(err); 183 | }); 184 | } 185 | 186 | function addPill(intent, session, callback) { 187 | const request = require('request'); 188 | 189 | let medID = 'error'; 190 | switch(intent.slots.Pill.value) { 191 | case 'adderall': 192 | medID = 0; 193 | break; 194 | case 'birth control': 195 | medID = 1; 196 | break; 197 | case 'hydrocodone': 198 | medID = 2; 199 | break; 200 | case 'meloxicam': 201 | medID = 3; 202 | case 'methadone': 203 | medID = 4; 204 | break; 205 | case 'opioid': 206 | medID = 5; 207 | break; 208 | case 'oxycodone': 209 | medID = '6'; 210 | break; 211 | } 212 | 213 | var postData = { 214 | 'User_ID' : '0', 215 | 'Medication_ID': medID, 216 | 'Amount_Prescribed': intent.slots.Amount_Prescribed.value, 217 | 'Refills': intent.slots.Refills.value, 218 | 'Display_Name': intent.slots.Pill.value 219 | }; 220 | 221 | request.post('https://pillpal-app.de/Takes', {json: postData}, (error, response, body) => { 222 | console.log('ERROR: ' + error); 223 | console.log('BODY: ' + JSON.stringify(body.message)); 224 | console.log('RESPONSE: ' + JSON.stringify(response)); 225 | if (JSON.stringify(body.message).includes('ER_DUP_ENTRY')) 226 | { 227 | callback(session.attributes, buildSpeechletResponseWithoutCard('You already have ' + postData.Display_Name + ' as a current pill.', "", "true")); 228 | } 229 | else if (JSON.stringify(body.message).includes('ER_NO_REFERENCED_ROW_2')) 230 | { 231 | callback(session.attributes, buildSpeechletResponseWithoutCard('Sorry, ' + postData.Display_Name + ' is not currently registered with PillPal as an available medication. Go to the Help page in Settings on the PillPal app for more information or to report a problem.', "", "true")); 232 | } 233 | else 234 | { 235 | callback(session.attributes, buildSpeechletResponseWithoutCard('Okay, I\'ve added ' + postData.Display_Name + ' as a current pill. You need to take ' + postData.Amount_Prescribed + ' each day and you have ' + postData.Refills + ' refills remaining.', "", "true")); 236 | } 237 | }); 238 | } 239 | 240 | function removeOnePill(intent, session, callback) { 241 | const request = require('request'); 242 | 243 | let medID = '3'; 244 | switch(intent.slots.Pill.value) { 245 | case 'adderall': 246 | medID = 0; 247 | break; 248 | case 'birth control': 249 | medID = 1; 250 | break; 251 | case 'hydrocodone': 252 | medID = 2; 253 | break; 254 | case 'meloxicam': 255 | medID = 3; 256 | case 'methadone': 257 | medID = 4; 258 | break; 259 | case 'opioid': 260 | medID = 5; 261 | break; 262 | case 'oxycodone': 263 | medID = '6'; 264 | break; 265 | } 266 | 267 | var postData = { 268 | 'User_ID' : '0', 269 | 'Medication_ID': medID, 270 | 'Display_Name': intent.slots.Pill.value 271 | }; 272 | 273 | request.delete('https://pillpal-app.de/Takes/0/' + postData.Medication_ID, (error, response, body) => { 274 | console.log('ERROR: ' + error); 275 | console.log('BODY: ' + JSON.stringify(body)); 276 | console.log('RESPONSE: ' + JSON.stringify(response)); 277 | if (JSON.stringify(response.statusCode) == 404) 278 | { 279 | callback(session.attributes, 280 | buildSpeechletResponseWithoutCard(postData.Display_Name + ' is not one of your current pills.', "", "true")); 281 | } 282 | else 283 | { 284 | callback(session.attributes, 285 | buildSpeechletResponseWithoutCard('Okay, I\'ve deleted ' + postData.Display_Name + ' from your current pills.', "", "true")); 286 | } 287 | }); 288 | } 289 | 290 | function removeAllPills(intent, session, callback) { 291 | const request = require('request'); 292 | 293 | request.delete('https://pillpal-app.de/Takes/0', (error, response, body) => { 294 | console.log('ERROR: ' + error); 295 | console.log('BODY: ' + JSON.stringify(body)); 296 | console.log('RESPONSE: ' + JSON.stringify(response)); 297 | if(JSON.stringify(response.statusCode) == 404) 298 | { 299 | callback(session.attributes, 300 | buildSpeechletResponseWithoutCard('You are not currently taking any medication.', "", "true")); 301 | } 302 | else 303 | { 304 | callback(session.attributes, 305 | buildSpeechletResponseWithoutCard('Okay, all of your pills have been removed.', "", "true")); 306 | } 307 | }); 308 | } 309 | 310 | // ------- Helper functions to build responses ------- 311 | 312 | function buildSpeechletResponse(title, output, repromptText, shouldEndSession) { 313 | return { 314 | outputSpeech: { 315 | type: "PlainText", 316 | text: output 317 | }, 318 | card: { 319 | type: "Simple", 320 | title: title, 321 | content: output 322 | }, 323 | reprompt: { 324 | outputSpeech: { 325 | type: "PlainText", 326 | text: repromptText 327 | } 328 | }, 329 | shouldEndSession: shouldEndSession 330 | }; 331 | } 332 | 333 | function buildSpeechletResponseWithoutCard(output, repromptText, shouldEndSession) { 334 | return { 335 | outputSpeech: { 336 | type: "PlainText", 337 | text: output 338 | }, 339 | reprompt: { 340 | outputSpeech: { 341 | type: "PlainText", 342 | text: repromptText 343 | } 344 | }, 345 | shouldEndSession: shouldEndSession 346 | }; 347 | } 348 | 349 | function buildResponse(sessionAttributes, speechletResponse) { 350 | return { 351 | version: "1.0", 352 | sessionAttributes: sessionAttributes, 353 | response: speechletResponse 354 | }; 355 | } 356 | --------------------------------------------------------------------------------