/// <summary> /// Handles all calls to the APIs and determines which is needed. /// </summary> /// <param name="args">The list of arguments that will determine the API call needed and give it the information required.</param> /// <returns>Key-Value paris of useful data retrived by the API.</returns> public IDictionary <string, string> ApiHandler(ApiArgs args) { IDictionary <string, string> output = new Dictionary <string, string>(); output.Add("output", "Error getting response."); using (HttpClient client = new HttpClient()) { using (HttpResponseMessage response = new HttpResponseMessage()) { //Set up headers for API call. client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("UserName", TEMP_USERNAME); client.DefaultRequestHeaders.Add("Password", TEMP_PASSWORD); client.DefaultRequestHeaders.Host = "sandbox.api.gnsvc.com"; client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("defate")); client.DefaultRequestHeaders.Add("AdvancedErrorCodes", "True"); //Holds temp attributes to be added to output IDictionary <string, string> attributes; switch (args.TYPE) { case ApiArgs.TYPES.Zip: TeesByZipArgs teesByZip = (TeesByZipArgs)args; attributes = GetFacilityByZip(client, response, teesByZip); attributes.ToList().ForEach(x => output[x.Key] = x.Value); break; case ApiArgs.TYPES.City: TeesByCityArgs teesByCity = (TeesByCityArgs)args; attributes = GetFacilityByCity(client, response, teesByCity); attributes.ToList().ForEach(x => output[x.Key] = x.Value); break; case ApiArgs.TYPES.Invoice: RateInvoiceArgs invoiceArgs = (RateInvoiceArgs)args; output = GetRateInvoice(client, response, invoiceArgs); break; case ApiArgs.TYPES.Login: LoginArgs loginArgs = (LoginArgs)args; output["output"] = "Loggging in..."; output["customerToken"] = GetCustToken(client, response, new User(loginArgs.Username, loginArgs.Password)); break; } } } if (output["output"] == "" || output == null || output["output"] == "Error getting response.") { output["output"] = "Sorry, no tee times were found with that information..."; } return(output); }
/// <summary> /// Returns the complete information of citties that could match the user's input. /// </summary> /// <param name="client">Object for making HTTP requests.</param> /// <param name="result">Object holding the response of the HTTP request.</param> /// <param name="city">The city information provided by the user.</param> /// <returns>List of cities that could match the given city name.</returns> private static List <City> getCityInfo(HttpClient client, HttpResponseMessage result, TeesByCityArgs city) { List <City> possibleMatchs = new List <City>(); //Get all country codes //If country code provided, use that. string[] countryCode; if (city.Country != null && city.Country.Length <= 2) { countryCode = new string[] { city.Country } } ; else { try { string fields = "CountryCode"; if (city.Country != null && city.Country.Length > 2) { fields += ",CountryName"; } result = client.GetAsync(ENDPOINT + "/channel/" + CHANNEL_ID + "/countries?fields=" + fields).Result; } catch (Exception e) { Console.WriteLine(e.Message); } var countries = JArray.Parse(result.Content.ReadAsStringAsync().Result); countryCode = new string[countries.Count]; for (int i = 0; i < countryCode.Length; i++) { //If user entered country is found, use only that and break out of loop. if (city.Country != null && (countries[i]["CountryName"].Value <string>() == city.Country || countries[i]["CountryCode"].Value <string>() == city.Country)) { countryCode = new string[] { countries[i]["CountryCode"].Value <string>() }; break; } string value = countries[i]["CountryCode"].Value <string>(); if (value == "US") { countryCode[i] = countryCode[0]; countryCode[0] = value; } else if (value == "CA") { countryCode[i] = countryCode[1]; countryCode[1] = value; } else { countryCode[i] = value; } } } //Search all states foreach (string country in countryCode) { //If state code provided, use that. string[] stateCode; if (city.State != null && city.State.Length <= 2) { stateCode = new string[] { city.State } } ; else { try { string fields = "StateProvinceCode"; if (city.State != null) { fields += ",StateProvinceName"; } result = client.GetAsync(ENDPOINT + "/channel/" + CHANNEL_ID + "/countries/" + country + "/state-provinces?fields=" + fields).Result; } catch (Exception e) { Console.WriteLine(e.Message); } var states = JArray.Parse(result.Content.ReadAsStringAsync().Result); stateCode = new string[states.Count]; for (int i = 0; i < stateCode.Length; i++) { //If user entered state is found, use only that and break out of loop. if (city.State != null && states[i]["StateProvinceName"].Value <string>() == city.State) { stateCode = new string[] { states[i]["StateProvinceCode"].Value <string>() }; break; } stateCode[i] = states[i]["StateProvinceCode"].Value <string>(); } } //search for cities foreach (string state in stateCode) { try { result = client.GetAsync(ENDPOINT + "/channel/" + CHANNEL_ID + "/countries/" + country + "/state-provinces/" + state + "/cities?fields=CityName").Result; } catch (Exception e) { Console.WriteLine(e.Message); } var cities = JArray.Parse(result.Content.ReadAsStringAsync().Result); string[] cityName = new string[cities.Count]; for (int i = 0; i < cityName.Length; i++) { if (cities[i]["CityName"].Value <string>().ToLower() == city.CityName.ToLower()) { possibleMatchs.Add(new City(city.CityName, state, country)); } } } } return(possibleMatchs); }
/// <summary> /// Gets facilities based around a given city, state(optional), and country (optional). /// </summary> /// <param name="client">Object for making HTTP requests.</param> /// <param name="result">Object holding the response of the HTTP request.</param> /// <param name="zip">the arguments for the city based API request. Ex. time, proximity, etc...</param> /// <returns>Key-Value paris of the returned TeeTimes by Facility.</returns> private static IDictionary <string, string> GetFacilityByCity(HttpClient client, HttpResponseMessage result, TeesByCityArgs city) { //All cities that could match the one entered by the user. List <City> possibleMatchs = getCityInfo(client, result, city); FacilitiesResponse facilities = new FacilitiesResponse(); //Iterate through the potentially matching cities and gather combine eligable facilites. foreach (City possibleCity in possibleMatchs) { result = client.GetAsync(ENDPOINT + $"/channel/{CHANNEL_ID}/facilities?q=country-city-state&country-code={possibleCity.CountryCode}&state-province-code={possibleCity.StateCode}&city={possibleCity.CityName}&players={city.NumOfPlayers}&fields=ID,name").Result; facilities.Facilities = facilities.Facilities.Concat(JsonConvert.DeserializeObject <List <Facility> >(result.Content.ReadAsStringAsync().Result)).ToList(); } //Send facilites to be searched for tee times. Return K/V pair array of results. return(getTeeTimesByFacilities(client, result, facilities, city.Time, city.NumOfPlayers)); }
/// <summary> /// Handles events from Lex /// </summary> /// <param name="lexEvent">Information about the lex event.</param> /// <param name="context">Lambda execution variables.</param> /// <returns>A response to the lex event</returns> public LexResponse FunctionHandler(LexEvent lexEvent, ILambdaContext context) { //Time slot entered by user. string time; //Number of players indicated by the user. string players; //The text of the response message. string textOut; //Key-Value pairs of options, response text, and other things returned by the API. IDictionary <string, string> attributes; //What (if any) information to populate slots with in the next intent. IDictionary <string, string> slots = new Dictionary <string, string>(); //The name of the next intent. string nextIntent; //The name of the next slot to elicit. string slotToElicit; //Detrimine the intent that triggered the logic. switch (lexEvent.CurrentIntent.Name) { case "login": string username; string password; string customerToken; if (!lexEvent.SessionAttributes.TryGetValue("username", out username)) { return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage { ContentType = "PlainText", Content = "Could not get retrieve username." })); } if (!lexEvent.SessionAttributes.TryGetValue("password", out password)) { return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage { ContentType = "PlainText", Content = "Could not get retrieve password." })); } lexEvent.SessionAttributes.Remove("password"); LoginArgs loginArgs = new LoginArgs(username, password); if (ApiConnectionBroker.Instance().ApiHandler(loginArgs).TryGetValue("customerToken", out customerToken)) { lexEvent.SessionAttributes["customerToken"] = customerToken; return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage { ContentType = "PlainText", Content = "Login Successful." })); } return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage { ContentType = "PlainText", Content = "Could not retrieve customer token." })); //An example case. case "ditto": //Example slot population. slots.Add("actor", "Bob Hoskins"); return(elicitSlot(lexEvent.SessionAttributes, "Test", slots, "time", new LexResponse.LexMessage { ContentType = "PlainText", Content = "This is the new intent!! Enter a time..." })); //User wants tee times via a given zip code. case "getTeeTimeByZip": string zip; nextIntent = "chooseOption"; slotToElicit = "option"; //Gather user inputs. if (!lexEvent.CurrentIntent.Slots.TryGetValue("zip", out zip)) { zip = "No zip..."; } if (!lexEvent.CurrentIntent.Slots.TryGetValue("time", out time)) { time = "No time..."; } if (!lexEvent.CurrentIntent.Slots.TryGetValue("players", out players)) { players = "1"; } //Colate arguments for the API. TeesByZipArgs teesByZip = new TeesByZipArgs(zip, "15", time, players); //Contact API and get resultes. attributes = ApiConnectionBroker.Instance().ApiHandler(teesByZip); //Seperate out the output text. textOut = attributes["output"]; attributes.Remove("output"); //add options to lexEvent Session Attribute vars that we will perserve. attributes.ToList().ForEach(x => lexEvent.SessionAttributes[x.Key] = x.Value); //Elicit the next intent to choose between options. return(elicitSlot(lexEvent.SessionAttributes, nextIntent, slots, slotToElicit, new LexResponse.LexMessage { ContentType = "PlainText", Content = textOut })); //User wants tee times via given city, state(optional), and country(optional). case "getTeeTimeByCity": string city; string state; string country; nextIntent = "chooseOption"; slotToElicit = "option"; //Gather user inputs. if (!lexEvent.CurrentIntent.Slots.TryGetValue("city", out city)) { city = "No city..."; } if (!lexEvent.CurrentIntent.Slots.TryGetValue("time", out time)) { time = "No time..."; } if (!lexEvent.CurrentIntent.Slots.TryGetValue("state", out state)) { state = "No state..."; } if (!lexEvent.CurrentIntent.Slots.TryGetValue("country", out country)) { country = "No country..."; } if (!lexEvent.CurrentIntent.Slots.TryGetValue("players", out players)) { players = "1"; } players = validatePlayers(players); //Colate arguments for the API. TeesByCityArgs teesByCity = new TeesByCityArgs(city, state, country, time, players); //Contact API and get resultes. attributes = ApiConnectionBroker.Instance().ApiHandler(teesByCity); //Seperate out the output text. textOut = attributes["output"]; attributes.Remove("output"); //add options to lexEvent Session Attribute vars that we will perserve. attributes.ToList().ForEach(x => lexEvent.SessionAttributes[x.Key] = x.Value); return(elicitSlot(lexEvent.SessionAttributes, nextIntent, slots, slotToElicit, new LexResponse.LexMessage { ContentType = "PlainText", Content = textOut })); //The user has been presented with options and has chosen one. case "chooseOption": //the index of the option chosen. string option; //Ensure an option was chosen. if (lexEvent.CurrentIntent.Slots.TryGetValue("option", out option)) { //Ensure the option selected exists. if (lexEvent.SessionAttributes["rateOption" + option] != "") { TeeTimeRateSelection selection = JsonConvert.DeserializeObject <TeeTimeRateSelection>(lexEvent.SessionAttributes["rateOption" + option]); RateInvoiceArgs args = new RateInvoiceArgs(selection.FacilityID, selection.RateID, lexEvent.SessionAttributes["players"]); attributes = ApiConnectionBroker.Instance().ApiHandler(args); //Seperate out the output text. textOut = attributes["output"]; attributes.Remove("output"); //textOut = "Rate found: " + selection.RateID + " at facility: " + selection.FacilityID; } else { textOut = "Rate not found."; } } else { textOut = "Rate number not found."; } return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage { ContentType = "PlainText", Content = textOut })); //The triggering event is unknown to this logic. default: return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage { ContentType = "PlainText", Content = "Chat Error: State Invalid." })); } }