Created
November 25, 2018 19:17
-
-
Save jamesbulpin/8d75bae78232674c4b1ddab6d12196e8 to your computer and use it in GitHub Desktop.
Alexa skill handler for Cambridge bin collection query
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Set environment variable BIN_CALENDAR_URL to the specific ical calendar for your postcode | |
# https://refusecalendarapi.azurewebsites.net/calendar/ical/<id> where <id> can be found | |
# interactively using https://www.cambridge.gov.uk/check-when-your-bin-will-be-emptied | |
from urllib2 import urlopen | |
from datetime import datetime, timedelta | |
import os | |
from ask_sdk_core.skill_builder import SkillBuilder | |
from ask_sdk_core.dispatch_components import ( | |
AbstractRequestHandler, AbstractExceptionHandler, | |
AbstractRequestInterceptor, AbstractResponseInterceptor) | |
from ask_sdk_core.utils import is_request_type, is_intent_name | |
from ask_sdk_core.handler_input import HandlerInput | |
from ask_sdk_model.ui import SimpleCard | |
from ask_sdk_model import Response | |
# Quick and dirty ical parsing to find the next occurrence of each | |
# type of collection. This avoids needing the full ical library | |
# which relies on strftime format strings that don't work on all | |
# platforms. | |
def get_next_dates(url): | |
# We're only interested in dates from today onwards. Set our threshold to | |
# this time yesterday so that the "all-day" (midnight) timestamp for | |
# today shows as in the future | |
threshold = datetime.now() - timedelta(1) | |
ical = urlopen(url).read().decode('iso-8859-1') | |
earliest = {} | |
current_date = None | |
current_item = None | |
for line in ical.splitlines(): | |
if line.startswith("BEGIN:VEVENT"): | |
current_date = None | |
current_item = None | |
elif line.startswith("END:VEVENT"): | |
if current_date != None and current_item != None: | |
if current_date < threshold: | |
pass | |
elif earliest.has_key(current_item): | |
if current_date < earliest[current_item]: | |
earliest[current_item] = current_date | |
else: | |
earliest[current_item] = current_date | |
elif line.startswith("DTSTART;VALUE=DATE:"): | |
current_date = datetime.strptime(line.split(":")[1], "%Y%m%d") | |
elif line.startswith("SUMMARY:"): | |
current_item = line.split(":")[1] | |
return earliest | |
sb = SkillBuilder() | |
class BinDateIntentHandler(AbstractRequestHandler): | |
"""Handler for BinDateIntent.""" | |
def can_handle(self, handler_input): | |
# type: (HandlerInput) -> bool | |
return is_intent_name("BinDateIntent")(handler_input) | |
def handle(self, handler_input): | |
# type: (HandlerInput) -> Response | |
try: | |
dates = get_next_dates(os.environ["BIN_CALENDAR_URL"]) | |
slots = handler_input.request_envelope.request.intent.slots | |
if "BinColor" in slots: | |
color = slots["BinColor"].value | |
if color in ["green", "black", "blue"]: | |
item = color.title() + " Bin Collection" | |
if item in dates: | |
# Construct the response | |
resp = "The next " + item + " is " | |
if datetime.now().date() == dates[item].date(): | |
resp = resp + "today" | |
elif (datetime.now() + timedelta(1)).date() == dates[item].date(): | |
resp = resp + "tomorrow" | |
else: | |
delta_days = (dates[item].date() - datetime.now().date()).days | |
if delta_days < 7: | |
resp = resp + "this coming " | |
else: | |
resp = resp + "on " | |
resp = resp + dates[item].strftime("%A, %B") + " " | |
d = dates[item].strftime("%d").lstrip("0") | |
resp = resp + d | |
if d in ["1", "21", "31"]: resp = resp + "st" | |
if d in ["2", "22"]: resp = resp + "nd" | |
if d in ["3", "23"]: resp = resp + "rd" | |
handler_input.response_builder.speak(resp) | |
else: | |
handler_input.response_builder.speak("Sorry, I can't find the date for the " + item + " in the collection calendar.") | |
else: | |
handler_input.response_builder.speak("Sorry, I don't think that there are any " + color + " bin collections.") | |
else: | |
handler_input.response_builder.speak("Please specify which bin colour you'd like the schedule for.") | |
except: | |
handler_input.response_builder.speak("Sorry, something went wrong fetching the bin collection calendar.") | |
return handler_input.response_builder.response | |
sb.add_request_handler(BinDateIntentHandler()) | |
# Handler name that is used on AWS lambda | |
lambda_handler = sb.lambda_handler() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment