diff --git a/AI Customer Service API-first-build-swagger.yaml b/AI Customer Service API-first-build-swagger.yaml new file mode 100644 index 00000000..f8980f01 --- /dev/null +++ b/AI Customer Service API-first-build-swagger.yaml @@ -0,0 +1,123 @@ +--- +swagger: "2.0" +info: + description: "AI Customer Service application, built during the Cloud and Big Data\ + \ course at New York University." + version: "1.0.0" + title: "AI Customer Service API" +host: "e0gqi81ueh.execute-api.us-east-1.amazonaws.com" +basePath: "/first-build" +schemes: +- "https" +paths: + /chatbot: + post: + tags: + - "NLU" + summary: "The endpoint for the Natural Language Understanding API." + description: "This API takes in one or more messages from the client and returns\n\ + one or more messages as a response. The API leverages the NLP\nbackend functionality,\ + \ paired with state and profile information\nand returns a context-aware reply.\n" + operationId: "sendMessage" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "BotRequest" + required: true + schema: + $ref: "#/definitions/BotRequest" + responses: + "200": + description: "A Chatbot response" + schema: + $ref: "#/definitions/BotResponse" + headers: + Access-Control-Allow-Origin: + type: "string" + "500": + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + "403": + description: "Unauthorized" + schema: + $ref: "#/definitions/Error" + options: + tags: + - "NLU" + summary: "The endpoint for the Natural Language Understanding API." + description: "This endpoints checks the API allowed methods.\n" + operationId: "checkMethods" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "BotRequest" + required: true + schema: + $ref: "#/definitions/BotRequest" + responses: + "200": + description: "A Chatbot response" + schema: + $ref: "#/definitions/BotResponse" + headers: + Access-Control-Allow-Origin: + type: "string" + Access-Control-Allow-Methods: + type: "string" + Access-Control-Allow-Headers: + type: "string" + "500": + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + "403": + description: "Unauthorized" + schema: + $ref: "#/definitions/Error" +definitions: + BotRequest: + type: "object" + properties: + messages: + type: "array" + items: + $ref: "#/definitions/Message" + Message: + type: "object" + properties: + type: + type: "string" + unstructured: + $ref: "#/definitions/UnstructuredMessage" + UnstructuredMessage: + type: "object" + properties: + id: + type: "string" + text: + type: "string" + timestamp: + type: "string" + format: "datetime" + BotResponse: + type: "object" + properties: + messages: + type: "array" + items: + $ref: "#/definitions/Message" + Error: + type: "object" + properties: + code: + type: "integer" + format: "int32" + message: + type: "string" diff --git a/assets/js/sdk/apigClient.js b/assets/js/sdk/apigClient.js old mode 100755 new mode 100644 index 10f0e7c4..4e28637b --- a/assets/js/sdk/apigClient.js +++ b/assets/js/sdk/apigClient.js @@ -40,7 +40,7 @@ apigClientFactory.newClient = function (config) { config.sessionToken = ''; } if(config.region === undefined) { - config.region = 'us-east-2'; + config.region = 'us-east-1'; } //If defaultContentType is not defined then default to application/json if(config.defaultContentType === undefined) { @@ -51,9 +51,9 @@ apigClientFactory.newClient = function (config) { config.defaultAcceptType = 'application/json'; } - + // extract endpoint and path from url - var invokeUrl = 'https://abc123.execute-api.us-east-1.amazonaws.com/v1'; + var invokeUrl = 'https://e0gqi81ueh.execute-api.us-east-1.amazonaws.com/first-build'; var endpoint = /(^https?:\/\/[^\/]+)/g.exec(invokeUrl)[1]; var pathComponent = invokeUrl.substring(endpoint.length); @@ -80,44 +80,44 @@ apigClientFactory.newClient = function (config) { }; var apiGatewayClient = apiGateway.core.apiGatewayClientFactory.newClient(simpleHttpClientConfig, sigV4ClientConfig); - - - + + + apigClient.chatbotPost = function (params, body, additionalParams) { if(additionalParams === undefined) { additionalParams = {}; } - + apiGateway.core.utils.assertParametersDefined(params, ['body'], ['body']); - - var nluPostRequest = { + + var chatbotPostRequest = { verb: 'post'.toUpperCase(), path: pathComponent + uritemplate('/chatbot').expand(apiGateway.core.utils.parseParametersToObject(params, [])), headers: apiGateway.core.utils.parseParametersToObject(params, []), queryParams: apiGateway.core.utils.parseParametersToObject(params, []), body: body }; - - - return apiGatewayClient.makeRequest(nluPostRequest, authType, additionalParams, config.apiKey); + + + return apiGatewayClient.makeRequest(chatbotPostRequest, authType, additionalParams, config.apiKey); }; - - + + apigClient.chatbotOptions = function (params, body, additionalParams) { if(additionalParams === undefined) { additionalParams = {}; } - - apiGateway.core.utils.assertParametersDefined(params, [], ['body']); - - var nluOptionsRequest = { + + apiGateway.core.utils.assertParametersDefined(params, ['body'], ['body']); + + var chatbotOptionsRequest = { verb: 'options'.toUpperCase(), path: pathComponent + uritemplate('/chatbot').expand(apiGateway.core.utils.parseParametersToObject(params, [])), headers: apiGateway.core.utils.parseParametersToObject(params, []), queryParams: apiGateway.core.utils.parseParametersToObject(params, []), body: body }; - - - return apiGatewayClient.makeRequest(nluOptionsRequest, authType, additionalParams, config.apiKey); + + + return apiGatewayClient.makeRequest(chatbotOptionsRequest, authType, additionalParams, config.apiKey); }; - + return apigClient; }; diff --git a/dynamoDBstreamtoES.py b/dynamoDBstreamtoES.py new file mode 100644 index 00000000..c6878c3a --- /dev/null +++ b/dynamoDBstreamtoES.py @@ -0,0 +1,31 @@ +import boto3 +import requests +from requests_aws4auth import AWS4Auth + +region = 'us-east-1' # e.g. us-east +service = 'es' +credentials = boto3.Session().get_credentials() +awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token) + +host = 'https://search-search-qwd3x3xfvps2llb4wwmiibd66q.us-east-1.es.amazonaws.com' # the OpenSearch Service domain, with https:// +index = 'restaurants' +type = 'restaurant' +url = host + '/' + index + '/' + type + '/' + +headers = { "Content-Type": "application/json" } + +def lambda_handler(event, context): + count = 0 + for record in event['Records']: + # Get the primary key for use as the OpenSearch ID + id = record['dynamodb']['Keys']['id']['S'] + + if record['eventName'] == 'REMOVE': + r = requests.delete(url + id, auth=awsauth) + else: + document = record['dynamodb']['NewImage'] + r = requests.put(url + id, auth=awsauth, json=document, headers=headers) + print(r.content,document,url+id,awsauth) + count += 1 + print("success") + return str(count) + ' records processed.' \ No newline at end of file diff --git a/lambda_function.py b/lambda_function.py new file mode 100644 index 00000000..6ed4d9a7 --- /dev/null +++ b/lambda_function.py @@ -0,0 +1,24 @@ +import json +def lambda_handler(event, context): + # TODO implement + message_string = { + "messages": [ + { + "type": "unstructured", + "unstructured": { + "id": "string", + "text": "Application under development. Search functionality will be implemented in Assignment 2", + "timestamp": "string" + } + } + ] + } + return { + 'statusCode': 200, + 'headers': { + 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'OPTIONS,POST' + }, + 'body': json.dumps(message_string) + } \ No newline at end of file diff --git a/lf0dining.py b/lf0dining.py new file mode 100644 index 00000000..12771615 --- /dev/null +++ b/lf0dining.py @@ -0,0 +1,22 @@ +import json +import boto3 +client = boto3.client('lex-runtime') +def lambda_handler(event, context): + usertext=event["messages"][0]["unstructured"]["text"] + response = client.post_text( + botName='diningbot', + botAlias='diningbot', + userId='1234567890', + sessionAttributes={ + }, + requestAttributes={ + }, + inputText=usertext, + activeContexts=[ + ] +) + print(response["message"]) + return { + 'statusCode': 200, + 'messages': [{'type': 'unstructured', 'unstructured': {'text': response["message"]}}] + } \ No newline at end of file diff --git a/lf1dining.py b/lf1dining.py new file mode 100644 index 00000000..bf3fee06 --- /dev/null +++ b/lf1dining.py @@ -0,0 +1,139 @@ +exports.handler = (event, context, callback) => { + console.log("incoming event details: " + JSON.stringify(event)); + const peoplecount = event.currentIntent.slots.PeopleCount; + const cuisine = event.currentIntent.slots.Cuisine; + const email = event.currentIntent.slots.Email; + const time = event.currentIntent.slots.Time; + const city = event.currentIntent.slots.City; + const date = event.currentIntent.slots.Date; + const outputSessionAttributes = event.sessionAttributes || {}; + let slots = event.currentIntent.slots; + if(city==null) + { + callback(null,delegate(outputSessionAttributes,slots)); + } + else if(!validate_loc(city)) + { + callback(null,elicitSlot(outputSessionAttributes, event.currentIntent.name,event.currentIntent.slots, "City", + { contentType: 'PlainText', content: 'We apologize but that is not a valid entry for city. Please make a new selection.' })); + } + else if(cuisine==null) + { + callback(null,delegate(outputSessionAttributes,slots)); + } + else if(!validate_cuisine(cuisine)) + { + callback(null,elicitSlot(outputSessionAttributes, event.currentIntent.name,event.currentIntent.slots, "Cuisine", + { contentType: 'PlainText', content: 'We dont have this cuisine. Can you choose from Chinese, American, Mexican, Korean,Japanese,Italian and French' })); + } + else if(peoplecount==null) + { + callback(null,delegate(outputSessionAttributes,slots)); + } + else if(!validate_peoplecount(peoplecount)) + { + callback(null,elicitSlot(outputSessionAttributes, event.currentIntent.name,event.currentIntent.slots, "PeopleCount", + { contentType: 'PlainText', content: 'Please enter between 1 and 19' })); + } + else if(date==null) + { + callback(null,delegate(outputSessionAttributes,slots)); + } + else if(!isValidDate(date)) + { + callback(null,elicitSlot(outputSessionAttributes, event.currentIntent.name,event.currentIntent.slots, "Date", + { contentType: 'PlainText', content: 'Invalid Date' })); + } + else if(time==null) + { + callback(null,delegate(outputSessionAttributes,slots)); + } + else if(!validate_time(time)) + { + callback(null,elicitSlot(outputSessionAttributes, event.currentIntent.name,event.currentIntent.slots, "Time", + { contentType: 'PlainText', content: 'Invalid Time. Please enter a proper time' })); + } + else if(email==null) + { + callback(null,delegate(outputSessionAttributes,slots)); + } + else if(!validate_email(email)) + { + callback(null,elicitSlot(outputSessionAttributes, event.currentIntent.name,event.currentIntent.slots, "Phone", + { contentType: 'PlainText', content: 'Invalid Phone Number. Please enter a valid phone number' })); + } + callback(null,delegate(outputSessionAttributes,slots)); +}; + +function delegate(sessionAttributes, slots) { + return { + sessionAttributes, + dialogAction: { + "type": 'Delegate', + "slots": slots + } + }; +} + +function validate_time(time) { +var qwerty=time.split(":"); + var now = new Date(); + var d = new Date(); +now.setHours(qwerty[0]); +now.setMinutes(qwerty[1]); +d.setHours(d.getHours()-4); +console.log(qwerty[0]+" "+qwerty[1]+" "+now+" "+d); +if (now >= d) { + return true; +} +return false; + } + +function isValidDate(dateString) { + var varDate = new Date(dateString); + varDate.setDate(varDate.getDate()+1); +var today = new Date(); +if(varDate.getTime() >= today.getTime()) { +return true; +} +return false; +} + +function validate_loc(loc){ + return loc.toLowerCase() == "manhattan" +} + +function validate_peoplecount(count){ + return !isNaN(count) && count>0 && count<20; +} + +function validate_cuisine(cuisine) +{ + cuisine=cuisine.toLowerCase(); + const cuisinetypes = ['chinese', 'american', 'mexican','korean','japanese','italian','french', 'indian'] + for(let i=0;i { + const outputSessionAttributes = event.sessionAttributes || {}; + callback(null,close(outputSessionAttributes,'Fulfilled',{'contentType': 'PlainText', + 'content': "You’re all set. Expect my suggestions shortly! Have a good day."})); + var pastsearch = { + TableName: 'conciergestate', + Item: { + 'userID' : '1234567890', + 'search': event.currentIntent.slots + } +}; +docClient.put(pastsearch, function(err, data) { + if (err) console.log(err); + else console.log(data); +}); + var params = { + MessageBody: JSON.stringify(event.currentIntent.slots), + QueueUrl: "https://sqs.us-east-1.amazonaws.com/************/diningmessage.fifo", + MessageGroupId: "diningsuggestions" + }; + sqs.sendMessage(params, function(err, data) { + if (err) { + console.log("Error", err); + } else { + console.log("Success", data.MessageId); + } + }); +} + +function close(session_attributes, fulfillment_state, message) { + return { + 'sessionAttributes': session_attributes, + 'dialogAction': { + 'type': 'Close', + 'fulfillmentState': fulfillment_state, + 'message': message + } + } +} diff --git a/lf3dining.py b/lf3dining.py new file mode 100644 index 00000000..5ea0376e --- /dev/null +++ b/lf3dining.py @@ -0,0 +1,63 @@ +import boto3 +import json +import requests +import random +import json +from requests_aws4auth import AWS4Auth + +region = 'us-east-1' +service = 'es' +credentials = boto3.Session().get_credentials() +awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token) +dynamo = boto3.client('dynamodb', region_name='us-east-1') +host = 'https://search-search-qwd3x3xfvps2llb4wwmiibd66q.us-east-1.es.amazonaws.com' +index = 'restaurants' +url = host + '/' + index + '/_search' +ses_client = boto3.client("ses", region_name="us-east-1") +def lambda_handler(event, context): + print(event) + print(context) + for record1 in event["Records"]: + record=json.loads(record1["body"]) + query = { "query": { "term":{ "category.S": record["Cuisine"] }}, "fields": ["_id"], "_source": False} + print(query) + headers = { "Content-Type": "application/json" } + r = requests.get(url, auth=awsauth, headers=headers, data=json.dumps(query)) + r=r.json() + print(r) + length=len(r["hits"]["hits"]) + print(length) + option1, option2, option3 = random.sample(range(0,length-1), 3) + one=r["hits"]["hits"][option1] + two=r["hits"]["hits"][option2] + three=r["hits"]["hits"][option3] + data1 = dynamo.get_item( TableName='yelp-restaurants', Key={ 'id': { 'S': one["_id"] }}) + data2 = dynamo.get_item( TableName='yelp-restaurants', Key={ 'id': { 'S': two["_id"] }}) + data3 = dynamo.get_item( TableName='yelp-restaurants', Key={ 'id': { 'S': three["_id"] }}) + print(data1["Item"]['name']) + result = "Hello! Here are my "+record["Cuisine"]+" restaurant suggestions for "+record["PeopleCount"]+" people, for "+record["Date"]+" at "+record["Time"]+" : 1."+data1["Item"]['name']['S']+",located at "+data1["Item"]['address']['S']+", 2."+data2["Item"]['name']['S']+",located at "+data2["Item"]['address']['S']+", 3."+data3["Item"]['name']['S']+",located at "+data3["Item"]['address']['S']+". Enjoy your meal!”" + print(result) + CHARSET = "UTF-8" + response = ses_client.send_email( + Destination={ + "ToAddresses": [ + record["Email"], + ], + }, + Message={ + "Body": { + "Text": { + "Charset": CHARSET, + "Data": result, + } + }, + "Subject": { + "Charset": CHARSET, + "Data": "Restaurant Suggestion" , + }, + }, + Source=record["Email"], + ) + + print(response) + return response \ No newline at end of file diff --git a/lf4dining_previous_state.py b/lf4dining_previous_state.py new file mode 100644 index 00000000..60805df7 --- /dev/null +++ b/lf4dining_previous_state.py @@ -0,0 +1,48 @@ +var AWS = require('aws-sdk'); +AWS.config.update({region: 'us-east-1'}); +var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'}); +var sqs = new AWS.SQS({apiVersion: '2012-11-05'}); +exports.handler = (event, context, callback) => { + var user="1234567890" + const outputSessionAttributes = event.sessionAttributes || {}; + var params = {TableName: 'conciergestate',Key:{"userID":{"S":user}}}; + console.log(params); + ddb.getItem(params, function(err, data) { + if (err) { + callback(null,close(outputSessionAttributes,'Fulfilled',{'contentType': 'PlainText', + 'content': "You didn't make any previous search"})); + } else { + params=data.Item.search.M; + console.log("Success", data.Item.search.M); + var params2={PeopleCount:params.PeopleCount.S, Cuisine:params.Cuisine.S, Time:params.Time.S, City:params.City.S, Phone:params.Phone.S, Date:params.Date.S}; + console.log(params2); + var params1 = { + MessageBody: JSON.stringify(params2), + QueueUrl: "https://sqs.us-east-1.amazonaws.com/************/diningmessage.fifo", + MessageGroupId: "diningsuggestions" + }; + sqs.sendMessage(params1, function(err, data) { + if (err) { + console.log("Error", err); + } else { + console.log("Success", data.MessageId); + } + }); + callback(null,close(outputSessionAttributes,'Fulfilled',{'contentType': 'PlainText', + 'content': "You’re all set. Expect my suggestions shortly! Have a good day."})); + } +}); + + return "fdv"; +} + +function close(session_attributes, fulfillment_state, message) { + return { + 'sessionAttributes': session_attributes, + 'dialogAction': { + 'type': 'Close', + 'fulfillmentState': fulfillment_state, + 'message': message + } + } +}