def answer_for_postcode(loc: Tuple): # Send API request res = query_geocode_api_coords(loc[0], loc[1]) # Verify that we have at least one valid result if (not res or "results" not in res or not len(res["results"]) or not res["results"][0]): return None # Grab top result from API call top = res["results"][0] # TODO: Fall back on lower-ranked results from the API # if the top result doesn't even contain a locality. # Extract address info from top result (street, num, locality, postcode, country_code) = _addrinfo_from_api_result(top) # Only support Icelandic postcodes for now if country_code == "IS" and postcode: pc = postcodes.get(int(postcode)) pd = "{0} {1}".format(postcode, pc["stadur_nf"]) (response, answer, voice) = gen_answer(pd) voice = "Þú ert í {0}".format(pd) return response, answer, voice else: return gen_answer("Ég veit ekki í hvaða póstnúmeri þú ert.")
def get_currweather_answer(query, result): """ Handle queries concerning current weather conditions """ res = _curr_observations(query, result) if not res: return gen_answer(_API_ERRMSG) try: temp = int(round(float(res["T"]))) # Round to nearest whole number desc = res["W"].lower() windsp = float(res["F"]) except: logging.warning("Exception parsing weather API result: {0}".format(e)) return gen_answer(_API_ERRMSG) wind_desc = _wind_descr(windsp) temp_type = "hiti" if temp >= 0 else "frost" mdesc = ", " + desc + "," if desc else "" locdesc = result.get("subject") or "Úti" voice = "{0} er {1} stiga {2}{3} og {4}".format(locdesc.capitalize(), abs(temp), temp_type, mdesc, wind_desc) answer = "{0}°{1} og {2} ({3} m/s)".format( temp, mdesc, wind_desc, str(windsp).rstrip("0").rstrip(".")) response = dict(answer=answer) return response, answer, voice
def _answ_for_petrol_query(q: Query, result) -> AnswerTuple: req_distance = True location = q.location if location is None: return gen_answer("Ég veit ekki hvar þú ert") if result.qkey == "ClosestStation": station = _closest_petrol_station(location) answer = "{0} {1} ({2}, bensínverð {3})" desc = "Næsta bensínstöð" elif result.qkey == "CheapestStation": station = _cheapest_petrol_station() answer = "{0} {1} ({2}, bensínverð {3})" desc = "Ódýrasta bensínstöðin" req_distance = False elif result.qkey == "ClosestCheapestStation": station = _closest_cheapest_petrol_station(location) desc = "Ódýrasta bensínstöðin í grenndinni" else: raise ValueError("Unknown petrol query type") if (not station or "bensin95" not in station or "diesel" not in station or (req_distance and "distance" not in station)): return gen_answer(_ERRMSG) bensin_kr_desc = krona_desc(float(station["bensin95"])) diesel_kr_desc = krona_desc(float(station["diesel"])) if req_distance: answ_fmt = "{0} {1} ({2}, bensínverð {3}, díselverð {4})" voice_fmt = ("{0} er {1} {2} í um það bil {3} fjarlægð. " "Þar kostar bensínlítrinn {4} og dísel-lítrinn {5}.") dist_nf = distance_desc(station["distance"], case="nf") dist_þf = distance_desc(station["distance"], case="þf") answer = answ_fmt.format(station["company"], station["name"], dist_nf, bensin_kr_desc, diesel_kr_desc) voice = voice_fmt.format( desc, station["company"], station["name"], dist_þf, bensin_kr_desc, diesel_kr_desc, ) else: answ_fmt = "{0} {1} (bensínverð {2}, díselverð {3})" voice_fmt = "{0} er {1} {2}. Þar kostar bensínlítrinn {3} og dísel-lítrinn {4}." answer = answ_fmt.format(station["company"], station["name"], bensin_kr_desc, diesel_kr_desc) voice = voice_fmt.format(desc, station["company"], station["name"], bensin_kr_desc, diesel_kr_desc) response = dict(answer=answer) return response, answer, voice
def howlong_answ(q, result): """ Generate answer to a query about number of days since/until a given date. """ now = datetime.utcnow() target = result["target"] q.set_key("HowLongUntilDate" if "until" in result else "HowLongSinceDate") # Check if it's today if target.date() == now.date(): return q.set_answer( *gen_answer("Það er {0} í dag.".format(target.strftime("%-d. %B"))) ) # Check if it's tomorrow # TODO: Maybe return num hours until tomorrow? if target.date() == now.date() + timedelta(days=1): return q.set_answer( *gen_answer("Það er {0} á morgun.".format(target.strftime("%-d. %B"))) ) # Returns num days rounded down, so we increment by one. days = _date_diff(now, target, unit="days") + 1 # Diff. strings for singular vs. plural plural = is_plural(days) verb = "eru" if plural else "er" days_desc = "dagar" if plural else "dagur" # Format date fmt = "%-d. %B" if now.year == target.year else "%-d. %B %Y" tfmt = target.strftime(fmt) # Date asked about is in the past if days < 0: days = abs(days) passed = "liðnir" if plural else "liðinn" voice = "Það {0} {1} {2} {3} frá {4}.".format( verb, days, days_desc, passed, tfmt ) # Convert e.g. '25.' to 'tuttugasta og fimmta' voice = re.sub(r" \d+\. ", " " + _DAY_INDEX_DAT[target.day] + " ", voice) answer = "{0} {1}".format(days, days_desc) # It's in the future else: voice = "Það {0} {1} {2} þar til {3} gengur í garð.".format( verb, days, days_desc, tfmt ) # Convert e.g. '25.' to 'tuttugasti og fimmti' voice = re.sub(r" \d+\. ", " " + _DAY_INDEX_NOM[target.day] + " ", voice) answer = "{0} {1}".format(days, days_desc) response = dict(answer=answer) q.set_answer(response, answer, voice)
def _gen_curr_tv_program_answer(q: Query): """ Generate answer to query about current TV program """ sched = _query_tv_schedule_api() if not sched: return gen_answer(_TV_API_ERRMSG) prog = _curr_prog(sched) if not prog: return gen_answer("Það er engin dagskrá á RÚV núna.") title = prog["title"] ep = "" if "fréttir" in title.lower() else "" answ = "RÚV er að sýna {0}{1}. {2}.".format( ep, title, _clean_desc(prog["description"])) return gen_answer(answ)
def handle_plain_text(q: Query) -> bool: """ Handle a plain text query requesting a call to a telephone number. """ ql = q.query_lower.strip().rstrip("?") pfx = None number = None for rx in _PHONECALL_REGEXES: m = re.search(rx, ql) if m: pfx = m.group(1) telsubj = m.group(2).strip() break else: return False # Special handling if context if telsubj in _CONTEXT_SUBJ: ctx = q.fetch_context() if ctx is None or "phone_number" not in ctx: a = gen_answer("Ég veit ekki við hvern þú átt") else: q.set_url("tel:{0}".format(ctx["phone_number"])) answer = "Skal gert" a = (dict(answer=answer), answer, "") # Only number digits else: clean_num = re.sub(r"[^0-9]", "", telsubj).strip() if len(clean_num) < 3: # The number is clearly not a valid phone number a = gen_answer("{0} er ekki gilt símanúmer.".format(telsubj)) elif re.search(r"^[\d|\s]+$", clean_num): # At this point we have what looks like a legitimate phone number. # Send tel: url to trigger phone call in client q.set_url("tel:{0}".format(clean_num)) answer = "Skal gert" a = (dict(answer=answer), answer, "") q.set_beautified_query("{0}{1}".format(pfx, clean_num)) else: # This is a named subject subj_þgf = NounPhrase(telsubj.title()).dative or telsubj a = gen_answer("Ég veit ekki símanúmerið hjá {0}".format(subj_þgf)) q.set_answer(*a) q.set_qtype(_TELEPHONE_QTYPE) q.query_is_command() return True
def _gen_evening_tv_program_answer(q: Query) -> Tuple: """ Generate answer to query about the evening's TV programs """ sched = _query_tv_schedule_api() if not sched: return gen_answer(_TV_API_ERRMSG) prog = _evening_prog(sched) if not prog: return gen_answer("Það er enginn liður eftir á dagskrá RÚV.") answ = ["Klukkan"] for p in prog: answ.append("{0} : {1}.\n".format(p["startTime"][11:16], p["title"])) voice_answer = "".join(answ) answer = "".join(answ[1:]).replace(" : ", " ") return dict(answer=answer), answer, voice_answer
def sentence(state, result): """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" in result and "qkey" in result: # Successfully matched a query type q.set_qtype(result.qtype) q.set_key(result.qkey) # Asking for a location outside Iceland if q.location and not in_iceland(q.location): return gen_answer("Ég þekki ekki til veðurs utan Íslands") handler_func = _HANDLERS[result.qkey] try: r = handler_func(q, result) if r: q.set_answer(*r) except Exception as e: logging.warning( "Exception while processing weather query: {0}".format(e)) q.set_error("E_EXCEPTION: {0}".format(e)) raise else: q.set_error("E_QUERY_NOT_UNDERSTOOD")
def handle_plain_text(q): """ Handle a plain text query, contained in the q parameter """ ql = q.query_lower.rstrip("?") remote_loc = None for rx in _QDISTANCE_REGEXES: m = re.search(rx, ql) if m: remote_loc = m.group(1) handler = dist_answer_for_loc break else: # Nothing caught by regexes, bail return False # Look up in geo API try: if q.location: answ = handler(remote_loc, q) else: answ = gen_answer("Ég veit ekki hvar þú ert.") except Exception as e: logging.warning( "Exception generating answer from geocode API: {0}".format(e)) q.set_error("E_EXCEPTION: {0}".format(e)) answ = None if answ: q.set_qtype(_DISTANCE_QTYPE) q.set_answer(*answ) return True return False
def query_nearest_stop(query: Query, session, result): """ A query for the stop closest to the user """ # Retrieve the client location location = query.location if location is None: # No location provided in the query answer = "Staðsetning óþekkt" response = dict(answer=answer) voice_answer = "Ég veit ekki hvar þú ert." return response, answer, voice_answer if not in_iceland(location): # User's location is not in Iceland return gen_answer("Ég þekki ekki strætósamgöngur utan Íslands.") # Get the stop closest to the user stop = straeto.BusStop.closest_to(location) answer = stop.name # Use the same word for the bus stop as in the query stop_word = result.stop_word if "stop_word" in result else "stoppistöð" va = [ "Næsta", stop_word, "er", stop.name + ";", "þangað", "eru", voice_distance(straeto.distance(location, stop.location)), ] # Store a location coordinate and a bus stop name in the context query.set_context({"location": stop.location, "bus_stop": stop.name}) voice_answer = " ".join(va) + "." response = dict(answer=answer) return response, answer, voice_answer
def declension_answer_for_word(word: str, query: Query) -> AnswerTuple: """ Look up all morphological forms of a given word, construct natural language response. """ query.set_qtype("Declension") query.set_key(word) # Look up in BÍN forms = lookup_best_word(word) if not forms: return gen_answer(_NOT_IN_BIN_MSG.format(word)) answ = ", ".join(forms) response = dict(answer=answ) # TODO: Handle plural e.g. "Hér eru" cases_desc = "Hér er {0}, um {1}, frá {2}, til {3}".format(*forms) voice = "Orðið {0} beygist á eftirfarandi hátt: {1}.".format( icequote(word), cases_desc ) # Beautify by placing word in query within quotation marks bq = re.sub(word + r"\??$", icequote(word) + "?", query.beautified_query) query.set_beautified_query(bq) return response, answ, voice
def _gen_count(q: Query, result): num_range = None if result.qkey == "CountUp": num_range = list(range(1, result.first_num + 1)) elif result.qkey == "CountDown": num_range = list(range(0, result.first_num))[::-1] else: (fn, sn) = (result.first_num, result.second_num) if fn > sn: (fn, sn) = (sn, fn) num_range = list(range(fn, sn + 1)) if len(num_range) > _MAX_COUNT: return gen_answer("Ég nenni ekki að telja svona lengi.") answ = "{0}…{1}".format(num_range[0], num_range[-1]) response = dict(answer=answ) components = list() delay = result.get("delay", _DEFAULT_DELAY) for n in num_range: # Default delay results in roughly 1 sec per number in count components.append('{0} <break time="{1}s"/>'.format(n, delay)) voice = " ".join(components) return response, answ, voice
def sentence(state: QueryStateDict, result: Result) -> None: """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" in result and "qkey" in result and "subject_nom" in result: # Successfully matched a query type subj = result["subject_nom"] try: handlerfunc = _HANDLER_MAP[result.qkey] res: Optional[AnswerTuple] = None if q.location is not None: res = handlerfunc(subj, q.location, result.qkey) if res: q.set_answer(*res) q.set_source("Google Maps") else: errmsg = "Ekki tókst að fletta upp staðnum {0}".format( icequote(subj)) q.set_answer(*gen_answer(errmsg)) q.set_qtype(result.qtype) q.set_key(subj) except Exception as e: logging.warning("Exception answering places query: {0}".format(e)) q.set_error("E_EXCEPTION: {0}".format(e)) return else: q.set_error("E_QUERY_NOT_UNDERSTOOD")
def howlong_desc_answ(target): """ Generate answer to a query about length of period to a given date. """ now = datetime.utcnow() days = date_diff(now, target, unit="days") # Diff. strings for singular vs. plural plural = is_plural(days) verb = "eru" if plural else "er" days_desc = "dagar" if plural else "dagur" # Format date fmt = "%-d. %B" if now.year == target.year else "%-d. %B %Y" tfmt = target.strftime(fmt) # Date asked about is current date if days == 0: return gen_answer("Það er {0} í dag.".format(tfmt)) elif days < 0: # It's in the past days = abs(days) passed = "liðinn" if sing else "liðnir" voice = "Það {0} {1} {2} {3} frá {4}.".format(verb, days, days_desc, passed, tfmt) answer = "{0} {1}".format(days, days_desc) else: # It's in the future voice = "Það {0} {1} {2} þar til {3} gengur í garð.".format( verb, days, days_desc, tfmt) answer = "{0} {1}".format(days, days_desc) response = dict(answer=answer) return (response, answer, voice)
def get_forecast_answer(query, result): """ Handle weather forecast queries """ loc = query.location txt_id = _CAPITAL_FC_ID if ( loc and _near_capital_region(loc)) else _COUNTRY_FC_ID # Did the query mention a specific scope? if "location" in result: if result.location == "capital": txt_id = _CAPITAL_FC_ID elif result.location == "general": txt_id = _COUNTRY_FC_ID try: res = forecast_text(txt_id) except Exception as e: logging.warning("Failed to fetch weather text: {0}".format(e)) res = None if (not res or not "results" in res or not len(res["results"]) or "content" not in res["results"][0]): return gen_answer(_API_ERRMSG) answer = res["results"][0]["content"] response = dict(answer=answer) voice = _descr4voice(answer) return response, answer, voice
def get_currweather_answer(query, result): """ Handle queries concerning current weather conditions """ res = _curr_observations(query, result) if not res: return gen_answer(_API_ERRMSG) try: temp = int(round(float(res["T"]))) # Round to nearest whole number desc = res["W"].lower() windsp = float(res["F"]) except: logging.warning("Exception parsing weather API result: {0}".format(e)) return gen_answer(_API_ERRMSG) wind_desc = _wind_descr(windsp) wind_ms_str = str(windsp).rstrip("0").rstrip(".") temp_type = "hiti" if temp >= 0 else "frost" mdesc = ", " + desc + "," if desc else "" locdesc = result.get("subject") or "Úti" # Meters per second string for voice. Say nothing if "logn". voice_ms = ", {0} {1} á sekúndu".format( wind_ms_str, "metrar" if is_plural(wind_ms_str) else "metri") if wind_ms_str != "0" else "" # Format voice string voice = "{0} er {1} stiga {2}{3} og {4}{5}".format( locdesc.capitalize(), abs(temp), temp_type, mdesc, wind_desc, voice_ms, ) # Text answer answer = "{0}°{1} og {2} ({3} m/s)".format(temp, mdesc, wind_desc, wind_ms_str) response = dict(answer=answer) return response, answer, voice
def gen_repeat_answ(text: str, cmd_prefix: str, q: Query): atxt = text.strip() atxt = atxt[:1].upper() + atxt[1:] # Capitalize first character q.set_answer(*gen_answer(atxt)) q.set_qtype(_REPEAT_QTYPE) q.set_key(atxt) q.set_expires(datetime.utcnow() + timedelta(hours=24)) q.set_context(dict(subject=text)) # Beautify query by placing text to repeat within quotation marks q.set_beautified_query("{0}{1}".format(cmd_prefix.capitalize(), icequote(atxt.rstrip(".") + ".")))
def answ_address(placename: str, loc: LatLonTuple, qtype: str) -> AnswerTuple: """ Generate answer to a question concerning the address of a place. """ # Look up placename in places API res = query_places_api(placename, userloc=loc, fields="formatted_address,name,geometry") if (not res or res["status"] != "OK" or "candidates" not in res or not res["candidates"]): return gen_answer(_PLACES_API_ERRMSG) # Use top result in Iceland place = _top_candidate(res["candidates"]) if not place: return gen_answer(_NOT_IN_ICELAND_ERRMSG) # Remove superfluous "Ísland" in addr string addr = re.sub(r", Ísland$", "", place["formatted_address"]) # Get street name without number to get preposition street_name = addr.split()[0].rstrip(",") maybe_postcode = re.search(r"^\d\d\d", street_name) is not None prep = "í" if maybe_postcode else iceprep_for_street(street_name) # Split addr into street name w. number, and remainder street_addr = addr.split(",")[0] remaining = re.sub(r"^{0}".format(street_addr), "", addr) # Get street name in dative case addr_þgf = NounPhrase(street_addr).dative or street_addr # Assemble final address final_addr = "{0}{1}".format(addr_þgf, remaining) # Create answer answer = final_addr voice = "{0} er {1} {2}".format(placename, prep, numbers_to_neutral(final_addr)) response = dict(answer=answer) return response, answer, voice
def sentence(state: QueryStateDict, result: Result) -> None: """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" not in result or "subject_nom" not in result: q.set_error("E_QUERY_NOT_UNDERSTOOD") return # OK, we've successfully matched a query type subj: str = result["subject_nom"] answer = "Ég hef enga sérstaka skoðun í þeim efnum." q.set_answer(*gen_answer(answer)) q.set_qtype(_OPINION_QTYPE) q.set_context(dict(subject=subj)) q.set_key(subj) q.set_expires(datetime.utcnow() + timedelta(hours=24))
def get_currtemp_answer(query, result): """ Handle queries concerning temperature """ res = _curr_observations(query, result) if not res: return gen_answer(_API_ERRMSG) temp = int(round(float(res["T"]))) # Round to nearest whole number temp_type = "hiti" if temp >= 0 else "frost" locdesc = result.get("subject") or "Úti" voice = "{0} er {1} stiga {2}".format(locdesc.capitalize(), abs(temp), temp_type) answer = "{0}°".format(temp) response = dict(answer=answer) return response, answer, voice
def sentence(state, result): """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" in result and "qkey" in result: # Successfully matched a query type q.set_qtype(result.qtype) q.set_key(result.qkey) try: answ = None loc = q.location if loc: # Get relevant info about this location if result.qkey == "CurrentPostcode": answ = answer_for_postcode(loc) else: answ = answer_for_location(loc) if answ and loc is not None: # For uniformity, store the returned location in the context # !!! TBD: We might want to store an address here as well q.set_context({"location": loc}) else: # We either don't have a location or no info about # the location associated with the query answ = gen_answer("Ég veit ekki hvar þú ert.") ql = q.query_lower if ql.startswith("hvað er ég"): bq = re.sub( r"^hvað er ég", "Hvar er ég", q.beautified_query, flags=re.IGNORECASE, ) q.set_beautified_query(bq) q.set_answer(*answ) except Exception as e: logging.warning( "Exception while processing location query: {0}".format(e)) q.set_error("E_EXCEPTION: {0}".format(e)) raise else: q.set_error("E_QUERY_NOT_UNDERSTOOD")
def handle_plain_text(q: Query) -> bool: """ Handle a plain text query, contained in the q parameter. """ ql = q.query_lower.rstrip("?") matches = None handler = None # Distance queries for rx in _QDISTANCE_REGEXES: matches = re.search(rx, ql) if matches: handler = dist_answer_for_loc break # Travel time queries if not handler: for rx in _QTRAVELTIME_REGEXES: matches = re.search(rx, ql) if matches: handler = traveltime_answer_for_loc break # Nothing caught by regexes, bail if not handler or not matches: return False # Look up answer in geo API try: if q.location: answ = handler(matches, q) else: answ = gen_answer(_UNKNOWN_LOC_RESP) except Exception as e: logging.warning( "Exception gen. answer from geocode API: {0}".format(e)) q.set_error("E_EXCEPTION: {0}".format(e)) answ = None if answ: q.set_qtype(_DISTANCE_QTYPE) q.set_answer(*answ) q.set_source("Google Maps") return True return False
def _gen_most_mentioned_answer(q) -> bool: """ Answer questions about the most mentioned/talked about people in Icelandic news. """ top = top_persons(limit=_MOST_MENTIONED_COUNT, days=_MOST_MENTIONED_PERIOD) q.set_qtype(_STATS_QTYPE) q.set_key("MostMentioned") if not top: # No people for the period, empty scraper db? q.set_answer(*gen_answer("Engar manneskjur fundust í gagnagrunni")) return True answer = natlang_seq([t["name"] for t in top if "name" in t]) response = dict(answer=answer) voice = "Umtöluðustu einstaklingar síðustu daga eru {0}.".format(answer) q.set_expires(datetime.utcnow() + timedelta(hours=1)) q.set_answer(response, answer, voice) return True
def declension_answer_for_word(word, query): """ Look up all morphological forms of a given word, construct natural language response. """ # Look up in BÍN forms = lookup_best_word(word) if not forms: return gen_answer(_NOT_IN_BIN_MSG.format(word)) answ = ", ".join(forms) response = dict(answer=answ) # TODO: Handle plural e.g. "Hér eru" cases_desc = "Hér er {0}, um {1}, frá {2}, til {3}".format(*forms) voice = "Orðið '{0}' beygist á eftirfarandi hátt: {1}.".format( word, cases_desc) query.set_qtype("Declension") query.set_key(word) return response, answ, voice
def sentence(state, result): """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" not in result: q.set_error("E_QUERY_NOT_UNDERSTOOD") return # Successfully matched a query type, we're handling it... q.set_qtype(result.qtype) # Beautify query by fixing spelling of Wikipedia b = q.beautified_query for w in _WIKI_VARIATIONS: b = b.replace(w, _WIKIPEDIA_CANONICAL) b = b.replace(w.capitalize(), _WIKIPEDIA_CANONICAL) q.set_beautified_query(b) # Check for error in context ref if "error_context_reference" in result: q.set_answer(*gen_answer("Ég veit ekki til hvers þú vísar.")) return # We have a subject if "subject_nom" in result: # Fetch data from Wikipedia API subj = result["subject_nom"] answer = get_wiki_summary(subj) response = dict(answer=answer) voice = _clean_voice_answer(answer) # Set query answer q.set_answer(response, answer, voice) q.set_key(subj) q.set_context(dict(subject=subj)) q.set_source("Wikipedía") # Cache reply for 24 hours q.set_expires(datetime.utcnow() + timedelta(hours=24)) return q.set_error("E_QUERY_NOT_UNDERSTOOD")
def sentence(state, result): """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" in result: try: res = top_news_answer() if res: # We've successfully answered a query q.set_qtype(result.qtype) q.set_key("LatestNews") q.set_answer(*res) else: errmsg = "Ekki tókst að sækja fréttir" q.set_answer(*gen_answer(errmsg)) q.set_source("RÚV") except Exception as e: logging.warning("Exception answering news query '{0}': {1}".format( q, e)) q.set_error("E_EXCEPTION: {0}".format(e)) return else: q.set_error("E_QUERY_NOT_UNDERSTOOD")
def handle_plain_text(q): """ Handle a plain text query asking about user's current location. """ ql = q.query_lower.rstrip("?") if ql not in _WHERE_AM_I_QUERIES: return False answ = None loc = q.location if loc: # Get info about this location answ = answer_for_location(loc) if not answ: # We either don't have a location or no info about # the location associated with the query answ = gen_answer("Ég veit ekki hvar þú ert.") q.set_qtype(_LOC_QTYPE) q.set_key("CurrentPosition") q.set_answer(*answ) return True
def gen_random_answer(q, result): (num1, num2) = (1, 6) # Default if "numbers" in result: # Asking for a number between x and y if len(result.numbers) == 2: (num1, num2) = sorted(result.numbers) # Asking for the roll of an x-sided die else: if result.numbers[0] == 0: return gen_answer("Núll hliða teningar eru ekki til.") (num1, num2) = (1, result.numbers[0]) # Query key is random number range (e.g. 1-6) q.set_key("{0}-{1}".format(num1, num2)) answer = str(random.randint(num1, num2)) response = dict(answer=answer) if result.action == "dieroll": voice_answer = "Talan {0} kom upp á teningnum".format(answer) else: voice_answer = "Ég vel töluna {0}".format(answer) return response, answer, voice_answer
def sentence(state, result) -> None: """ Called when sentence processing is complete """ q: Query = state["query"] if "qtype" in result and "qkey" in result: # Successfully matched a query type try: loc = q.location if result.qkey == "CheapestStation" or loc: answ = _answ_for_petrol_query(q, result) else: # We need a location but don't have one answ = gen_answer("Ég veit ekki hvar þú ert.") if answ: q.set_qtype(result.qtype) q.set_key(result.qkey) q.set_answer(*answ) q.set_source("Gasvaktin") except Exception as e: logging.warning( "Exception while processing petrol query: {0}".format(e)) q.set_error("E_EXCEPTION: {0}".format(e)) raise else: q.set_error("E_QUERY_NOT_UNDERSTOOD")
def sentence(state: QueryStateDict, result: Result) -> None: """ Called when sentence processing is complete """ q = state["query"] if "qtype" not in result: q.set_error("E_QUERY_NOT_UNDERSTOOD") return # Connect smartdevice action if result.qtype == "ConnectSmartDevice": answer = "Skal gert" host = flask.request.host js = read_jsfile("connectHub.js") # Function from the javascript file needs to be called with # relevant variables js += "connectHub('{0}','{1}')".format(q.client_id, host) q.set_command(js) q.set_answer(*gen_answer(answer)) return # TODO hardcoded while only one device type is supported smartdevice_type = "smartlights" # Fetch relevant data from the device_data table to perform an action on the lights device_data = cast(Optional[DeviceData], q.client_data(smartdevice_type)) selected_light: Optional[str] = None hue_credentials: Optional[Dict[str, str]] = None if device_data is not None and smartdevice_type in device_data: dev = device_data[smartdevice_type] assert dev is not None selected_light = dev.get("selected_light") hue_credentials = dev.get("philips_hue") if not device_data: answer = "Snjalltæki hafa ekki verið sett upp" q.set_answer(*gen_answer(answer)) return # Light on or off action if (result.qtype == "LightOn" or result.qtype == "LightOff") and selected_light == "philips_hue": onOrOff = "true" if result.qtype == "LightOn" else "false" stofn: Optional[str] = None for token in q.token_list or []: if token.txt == result.subject and token.has_meanings: stofn = token.meanings[0].stofn assert stofn is not None stofn = _FIX_MAP.get(stofn, stofn) js = read_jsfile("lightService.js") js += "main('{0}','{1}','{2}', {3});".format( hue_credentials["ipAddress"], hue_credentials["username"], stofn, onOrOff) answer = "{0} {1}".format(stofn, onOrOff) q.set_answer(*gen_answer(answer)) q.set_command(js) return # Alter light dimmer action if result.qtype == "LightDim" and selected_light == "philips_hue": number = result.numbers[0] stofn = "" for token in q.token_list: if token.txt == result.subject: stofn = token.meanings[0].stofn stofn = _FIX_MAP.get(stofn, stofn) js = read_jsfile("lightService.js") js += "main('{0}','{1}','{2}', true, {3});".format( hue_credentials["ipAddress"], hue_credentials["username"], stofn, number) answer = stofn q.set_answer(*gen_answer(answer)) q.set_command(js) return # Alter light color action if result.qtype == "LightColor" and selected_light == "philips_hue": stofn_name = None stofn_color = None for token in q.token_list or []: if token.txt == result.subject and token.has_meanings: stofn_name = token.meanings[0].stofn stofn_name = _FIX_MAP.get(stofn_name) or stofn_name if token.txt == result.color: for word_variation in token.meanings: if (word_variation.ordfl == "lo" and word_variation.stofn in _COLOR_NAME_TO_CIE): stofn_color = word_variation.stofn break js = read_jsfile("lightService.js") js += "main('{0}','{1}','{2}', true, null, {3});".format( hue_credentials["ipAddress"], hue_credentials["username"], stofn_name, _COLOR_NAME_TO_CIE[stofn_color.lower()], ) answer = "{0} {1}".format(stofn_color, stofn_name) q.set_answer(*gen_answer(answer)) q.set_command(js) return # Connected lights info action if result.qtype == "HubInfo" and "selected_light" == "philips_hue": answer = "Skal gert" js = read_jsfile("lightInfo.js") q.set_answer(*gen_answer(answer)) q.set_command(js) return # Alter saturation action if result.qtype == "LightSaturation" and selected_light == "philips_hue": number = result.numbers[0] stofn = None for token in q.token_list or []: if token.txt == result.subject and token.has_meanings: stofn = token.meanings[0].stofn stofn = _FIX_MAP.get(stofn, stofn) js = read_jsfile("lightService.js") js += "main('{0}','{1}','{2}', undefined, undefined, undefined, {3});".format( hue_credentials["ipAddress"], hue_credentials["username"], stofn, number) answer = "{0} {1}".format(stofn, number) q.set_answer(*gen_answer(answer)) q.set_command(js) return # No command was applicable: give up q.set_error("E_QUERY_NOT_UNDERSTOOD")