Savage Garden Hotline

6b6e72d297aa0270654a0d4575f1287e?s=47 Mariatta
October 19, 2017

Savage Garden Hotline

A show and tell of my personal project.
I built a hotline that when called, caller will hear my favorite song from Savage Garden, and my twitter bot will tweet about it.
The project is built using Python 3.6. It uses aiohttp library, Twython library, Twilio service, and deployed to Heroku.

6b6e72d297aa0270654a0d4575f1287e?s=128

Mariatta

October 19, 2017
Tweet

Transcript

  1. SAVAGE GARDEN HOTLINE Python-powered @Mariatta Code and Coffee YVR -

    Oct 2017 +1-604-706-2558
  2. @Mariatta

  3. None
  4. NATIONAL RICK ASTLEY HOTLINE Paul Fenwick’s Australia: +61-3-8652-1453 New Zealand:

    +64-9-886-0565 UK: +44-11-7325-7425 USA: +1-760-706-7425 https://github.com/pjf/rickastley
  5. https://i.ytimg.com/vi/uxsWjeiQ76s/maxresdefault.jpg

  6. https://i.ytimg.com/vi/uxsWjeiQ76s/maxresdefault.jpg

  7. http://images.eil.com/large_image/SAVAGE_GARDEN_CRASH%2BAND%2BBURN-383118.jpg

  8. http://images.eil.com/large_image/SAVAGE_GARDEN_CRASH%2BAND%2BBURN-383118.jpg +1-604-706-2558

  9. Building the Hotline • Phone Number • MP3 • Webservice

    • Twitter bot
  10. Building the Hotline • Phone Number • MP3 • Webservice

    • Twitter bot Python 3.6
  11. Getting the Phone Number https://www.twilio.com/

  12. None
  13. https://www.twilio.com/docs/quickstart?filter-product=voice

  14. how it works

  15. how it works "

  16. how it works "

  17. how it works "

  18. how it works " ☁ request

  19. how it works " ☁ request response

  20. how it works " ☁ request response mp3

  21. Twilio Python API from twilio.twiml.voice_response import VoiceResponse def hello_world(): resp

    = VoiceResponse() resp.say("Hello world.") return str(resp)
  22. TwiML <?xml version="1.0" encoding="UTF-8"?> <Response> <Say>Hello world.</Say> </Response>

  23. aiohttp from aiohttp import web from twilio.twiml.voice_response import VoiceResponse async

    def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.say("Hello world.") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) web.run_app(app) http://aiohttp.readthedocs.io/ server.py
  24. web: python3 -m server Procfile

  25. web: python3 -m server Procfile ☠ 500

  26. web: python3 -m server Web process failed to bind to

    $PORT within 60 seconds of launch Procfile ☠ 500
  27. make it work server.py import os from aiohttp import web

    from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.say("Hello world.") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port)
  28. make it work server.py import os from aiohttp import web

    from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.say("Hello world.") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port) 200
  29. MP3

  30. MP3

  31. MP3 ⬆

  32. MP3 ⬆ ☁

  33. MP3 ⬆ ☁

  34. play the music server.py import os from aiohttp import web

    from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.say("Hello world.") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port)
  35. play the music server.py import os from aiohttp import web

    from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/ Savage+Garden+-+Crash+And+Burn.mp3") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port)
  36. play the music server.py import os from aiohttp import web

    from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/ Savage+Garden+-+Crash+And+Burn.mp3") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port) 200
  37. play the music server.py import os from aiohttp import web

    from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/ Savage+Garden+-+Crash+And+Burn.mp3") return web.Response(text=str(response)) app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port) 200
  38. … looks ok? <?xml version="1.0" encoding="UTF-8"?> <Response> <Play>https://s3.ca-central-1.amazonaws.com/ strangerelationship/Savage+Garden+- +Crash+And+Burn.mp3</Play>

    </Response>
  39. … looks ok? <?xml version="1.0" encoding="UTF-8"?> <Response> <Play>https://s3.ca-central-1.amazonaws.com/ strangerelationship/Savage+Garden+- +Crash+And+Burn.mp3</Play>

    </Response> Content-Type != xml
  40. make it work (for real) server.py import os from aiohttp

    import web from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/ Savage+Garden+-+Crash+And+Burn.mp3") return web.Response(text=str(response), content_type='application/xml') app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port)
  41. make it work (for real) server.py import os from aiohttp

    import web from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/ Savage+Garden+-+Crash+And+Burn.mp3") return web.Response(text=str(response), content_type='application/xml') app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port) 200
  42. make it work (for real) server.py import os from aiohttp

    import web from twilio.twiml.voice_response import VoiceResponse async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/ Savage+Garden+-+Crash+And+Burn.mp3") return web.Response(text=str(response), content_type='application/xml') app = web.Application() app.router.add_get('/', handle) port = os.environ.get("PORT") if port is not not None: port = int(port) web.run_app(app, port=port) 200
  43. the GET request at=info method=GET path="/? Called=%2B16047062558&ToState=BC&CallerCountry=CA&Direction=inbound&CallerStat e=BC&ToZip=&To=%2B16047062558&CallerZip=&ToCountry=CA&ApiVersion=2010-04-01&Ca lledZip=&CalledCity=Vancouver&CalledCountry=CA&CallerCity=NEW+WESTMINSTER&From Country=CA&ToCity=Vancouver&FromCity=NEW+WESTMINSTER&CalledState=BC&FromZip=&F

    romState=BC" host=crashandburnhotline.herokuapp.com request_id=e162d82f-94dc-43be-8b5b-f6d864161bb5 fwd="54.144.213.39" dyno=web.1 connect=0ms service=269ms status=200 bytes=370 protocol=https
  44. the GET request at=info method=GET path="/? Called=%2B16047062558&ToState=BC&CallerCountry=CA&Direction=inbound&CallerStat e=BC&ToZip=&To=%2B16047062558&CallerZip=&ToCountry=CA&ApiVersion=2010-04-01&Ca lledZip=&CalledCity=Vancouver&CalledCountry=CA&CallerCity=NEW+WESTMINSTER&From Country=CA&ToCity=Vancouver&FromCity=NEW+WESTMINSTER&CalledState=BC&FromZip=&F

    romState=BC" host=crashandburnhotline.herokuapp.com request_id=e162d82f-94dc-43be-8b5b-f6d864161bb5 fwd="54.144.213.39" dyno=web.1 connect=0ms service=269ms status=200 bytes=370 protocol=https
  45. twitter bot Register an APP at https://dev.twitter.com/apps Access level: Read

    and write Receive API token & secret
  46. Twython server.py from twython import Twython twitter = Twython( os.getenv("TW_API_KEY"),

    os.getenv("TW_API_SECRET"), os.getenv("TW_ACCESS_TOKEN"), os.getenv("TW_TOKEN_SECRET")) ) async def handle(request): """Respond to incoming requests.""" response = VoiceResponse() response.play("https://s3.ca-central-1.amazonaws.com/ strangerelationship/Savage+Garden+-+Crash+And+Burn.mp3") twitter.update_status(status="Thanks for dialing in.") return web.Response(text=str(response), content_type='application/xml') https://twython.readthedocs.io/
  47. @SG_hotline_bot

  48. THANK YOU! @Mariatta Code and Coffee YVR - Oct 2017

    Savage Garden Hotline: +1-604-706-2558 https://github.com/Mariatta/crash_and_burn_hotline/